diff --git a/previos/dragonfly_game_v3.0.py b/previos/dragonfly_game_v3.0.py deleted file mode 100644 index 2b8d994..0000000 --- a/previos/dragonfly_game_v3.0.py +++ /dev/null @@ -1,555 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.2, 0.2) # Mosquitos (Rojo) -COLOR_FLY = (1.0, 0.9, 0.0) # Moscas (Amarillo/Dorado) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 # Estanque de 1800u de superficie -STREAM_LEN = 900.0 # Longitud de cada arroyo -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) # -2700.0 -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) # 2700.0 -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - # Juncos en islas del estanque principal - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - # Juncos en el arroyo inferior - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - # Juncos en el arroyo superior - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - """Devuelve el centro X del arroyo fuera de los límites del estanque central.""" - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - """Comprueba si unas coordenadas (x, y) están dentro del agua.""" - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - """Genera una posición inicial aleatoria sobre el agua.""" - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: # Fly - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - if dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - # Cuerpo - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - # Alas - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DEL ESCENARIO -# ========================================== -def draw_water_surface(time_counter): - """Dibuja una malla 3D animada para el agua.""" - glColor3f(*COLOR_WATER) - - grid_size = 30 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 20): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 15): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - """Dibuja el terreno del mapa.""" - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - # Orilla Izquierda - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - # Orilla Derecha - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - """Dibuja todo el mapa.""" - draw_terrain() - draw_water_surface(time_counter) - - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - for rx, ry, rz, rh in REEDS: - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - glVertex3f(rx, ry, rz) - glVertex3f(rx - 1.5, ry + 0.5, rz + rh * 0.7) - glVertex3f(rx, ry, rz) - glVertex3f(rx + 1.5, ry - 0.5, rz + rh * 0.8) - glEnd() - -# ========================================== -# MODELO 3D DE LA LIBÉLULA -# ========================================== -def draw_dragonfly(wing_angle, caught_insect=None): - """Dibuja la libélula y el insecto atrapado en sus patas si tiene uno.""" - glColor3f(*COLOR_DRAGONFLY) - - # Cuerpo - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - # Patas (Visibles sujetando al insecto) - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - # Cabeza - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - # Alas - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - # Si lleva un insecto atrapado, se dibuja sujeto bajo su tórax - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) # Posición bajo las patas - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# BUCLE PRINCIPAL Y LÓGICA -# ========================================== -def main(): - pygame.init() - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - # Estado de Caza - caught_insect = None # Insecto sujeto actualmente en las patas - eaten_mosquitos = 0 # Contador de mosquitos comidos - eaten_flies = 0 # Contador de moscas comidas - - def update_title(): - status = " (Llevando presa a un junco...)" if caught_insect else "" - pygame.display.set_caption( - f"Libélula 3D | Mosquitos cazados: {eaten_mosquitos} | Moscas cazadas: {eaten_flies}{status}" - ) - - update_title() - - # Inicializar Insectos - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - - keys = pygame.key.get_pressed() - - # CONTROLES Y MOVIMIENTO DE LA LIBÉLULA - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_UP]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - else: - if keys[K_UP]: - speed = min(speed + acceleration, max_speed) - elif keys[K_DOWN]: - speed = max(speed - deceleration, 0.0) - - # Intentar posarse en la punta de un junco - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 8.0 and abs(pz - (rz + rh)) < 8.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - # Si lleva una presa al posarse, se la come - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - update_title() - break - - target_pitch = 0.0 - if keys[K_w]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_s]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 60.0 if speed > 0.1 else 30.0 - wing_angle = math.sin(wing_timer) * 35.0 - - # ACTUALIZACIÓN DE INSECTOS - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - # Detección de contacto/choque con la libélula - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: # Rozamiento o choque - caught_insect = insect - insects.remove(insect) - update_title() - - # Mantener poblaciones dentro del rango - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - # RENDERIZADO - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - # Dibujar Insectos en el mapa - for insect in insects: - insect.draw() - - # Dibujar Libélula (con el insecto en las patas si lo ha capturado) - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v4.1.py b/previos/dragonfly_game_v4.1.py deleted file mode 100644 index 4567d4f..0000000 --- a/previos/dragonfly_game_v4.1.py +++ /dev/null @@ -1,702 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.2, 0.2) # Mosquitos (Rojo) -COLOR_FLY = (1.0, 0.9, 0.0) # Moscas (Amarillo/Dorado) -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 # Estanque de 1800u de superficie -STREAM_LEN = 900.0 # Longitud de cada arroyo -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) # -2700.0 -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) # 2700.0 -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - # Juncos en islas del estanque principal - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - # Juncos en el arroyo inferior - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - # Juncos en el arroyo superior - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - """Devuelve el centro X del arroyo fuera de los límites del estanque central.""" - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - """Comprueba si unas coordenadas (x, y) están dentro del agua.""" - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - """Genera una posición inicial aleatoria sobre el agua.""" - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: # Fly - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - if dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 30 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 20): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 15): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - for rx, ry, rz, rh in REEDS: - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - glVertex3f(rx, ry, rz) - glVertex3f(rx - 1.5, ry + 0.5, rz + rh * 0.7) - glVertex3f(rx, ry, rz) - glVertex3f(rx + 1.5, ry - 0.5, rz + rh * 0.8) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / RADAR / HUD -# ========================================== -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - """Dibuja el Radar central y los contadores en 2D superpuestos.""" - glDisable(GL_DEPTH_TEST) - - # Cambiar a proyección 2D ortogonal - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # ---------------------------------- - # 1. RADAR / MIRILLA (Centro Superior) - # ---------------------------------- - cx, cy = WIDTH // 2, HEIGHT - 70 - radius = 45 - - # Dibujar Círculo exterior - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(32): - angle = 2.0 * math.pi * i / 32 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - # Dibujar Cruz Central y Horizonte en línea discontinua - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 10) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 10) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 10, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 10, cy) - glEnd() - - # Cruz pequeña central - glBegin(GL_LINES) - glVertex2f(cx - 4, cy) - glVertex2f(cx + 4, cy) - glVertex2f(cx, cy - 4) - glVertex2f(cx, cy + 4) - glEnd() - - # Buscar el insecto más cercano al frente - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - rad_yaw = math.radians(yaw) - # Vector dirección del jugador - dir_x = -math.sin(rad_yaw) - dir_y = math.cos(rad_yaw) - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - # Calcular ángulo relativo respecto a hacia dónde mira la libélula - angle_to_insect = math.degrees(math.atan2(dy, dx)) - # Ajuste de ángulo respecto al YAW - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - # Solo mostrar si está en un cono frontal de visión (120 grados) - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz # Por encima (+) o debajo (-) - - # Si hay una presa cercana al frente, dibujarla en la mirilla del radar - if closest_insect: - # Mapear posición horizontal (-60° a +60° -> -radius a +radius) - radar_x = cx - (relative_angle / 60.0) * (radius - 8) - # Mapear altura (-20u a +20u de diferencia -> -radius a +radius) - radar_y = cy + (diff_z / 20.0) * (radius - 8) - - # Clampear dentro del círculo del radar - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 6): - factor = (radius - 6) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - # Color del punto según la especie - if closest_insect.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - # Dibujar punto e indicador de la presa - glBegin(GL_TRIANGLE_FAN) - for i in range(12): - ang = 2.0 * math.pi * i / 12 - glVertex2f(radar_x + 4 * math.cos(ang), radar_y + 4 * math.sin(ang)) - glEnd() - - # Pequeñas alas en el radar - glBegin(GL_LINES) - glVertex2f(radar_x - 7, radar_y) - glVertex2f(radar_x + 7, radar_y) - glEnd() - - # ---------------------------------- - # 2. ICONOS Y CONTADORES HUD - # ---------------------------------- - # Marcador Izquierdo: Mosquitos - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - # Dibujar pequeño ícono de mosquito - glVertex2f(25, HEIGHT - 25) - glVertex2f(25, HEIGHT - 45) - glVertex2f(25, HEIGHT - 35) - glVertex2f(10, HEIGHT - 35) - glVertex2f(25, HEIGHT - 35) - glVertex2f(40, HEIGHT - 35) - glEnd() - - # Marcador Derecho: Moscas - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - # Dibujar pequeño ícono de mosca - glVertex2f(WIDTH - 45, HEIGHT - 25) - glVertex2f(WIDTH - 45, HEIGHT - 45) - glVertex2f(WIDTH - 45, HEIGHT - 35) - glVertex2f(WIDTH - 60, HEIGHT - 35) - glVertex2f(WIDTH - 45, HEIGHT - 35) - glVertex2f(WIDTH - 30, HEIGHT - 35) - glEnd() - - # Restaurar matrices 3D - glPopMatrix() - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glEnable(GL_DEPTH_TEST) - - # Renderizar el texto de los contadores mediante imágenes PyGame - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 60, 60)) - txt_flies = font.render(f"x {eaten_flies}", True, (255, 230, 0)) - - surface = pygame.display.get_surface() - - # Crear textura rápida para el texto en pantalla - draw_text_pygame(txt_mosq, 50, 20) - draw_text_pygame(txt_flies, WIDTH - 110, 20) - -def draw_text_pygame(text_surface, x, y): - """Dibuja texto de PyGame en la pantalla de OpenGL.""" - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - glRasterPos2d(x, HEIGHT - y - h) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -# ========================================== -# BUCLE PRINCIPAL Y LÓGICA -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 26, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - # Estado de Caza - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - # Inicializar Insectos - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - - keys = pygame.key.get_pressed() - - # CONTROLES Y MOVIMIENTO DE LA LIBÉLULA - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_UP]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - else: - if keys[K_UP]: - speed = min(speed + acceleration, max_speed) - elif keys[K_DOWN]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 8.0 and abs(pz - (rz + rh)) < 8.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_w]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_s]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 60.0 if speed > 0.1 else 30.0 - wing_angle = math.sin(wing_timer) * 35.0 - - # ACTUALIZACIÓN DE INSECTOS - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - # RENDERIZADO - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - # DIBUJAR INTERFAZ (HUD) Y RADAR - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v4.3.py b/previos/dragonfly_game_v4.3.py deleted file mode 100644 index 768da4e..0000000 --- a/previos/dragonfly_game_v4.3.py +++ /dev/null @@ -1,675 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 # Estanque de 1800u de superficie -STREAM_LEN = 900.0 # Longitud de cada arroyo -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) # -2700.0 -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) # 2700.0 -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - # Juncos en islas del estanque principal - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - # Juncos en el arroyo inferior - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - # Juncos en el arroyo superior - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - """Devuelve el centro X del arroyo fuera de los límites del estanque central.""" - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - """Comprueba si unas coordenadas (x, y) están dentro del agua.""" - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - """Genera una posición inicial aleatoria sobre el agua.""" - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: # Fly - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - if dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 30 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 20): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 15): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - for rx, ry, rz, rh in REEDS: - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - glVertex3f(rx, ry, rz) - glVertex3f(rx - 1.5, ry + 0.5, rz + rh * 0.7) - glVertex3f(rx, ry, rz) - glVertex3f(rx + 1.5, ry - 0.5, rz + rh * 0.8) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / RADAR REDUCIDO / HUD -# ========================================== -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - """Dibuja el Radar reducido y los contadores en 2D superpuestos fijados arriba.""" - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # ---------------------------------- - # 1. RADAR REDUCIDO A LA MITAD (Centro Superior) - # ---------------------------------- - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 # Tamaño reducido a la mitad (era 45) - - # Dibujar Círculo exterior - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - # Dibujar Cruz Central - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - # Cruz pequeña central - glBegin(GL_LINES) - glVertex2f(cx - 2, cy) - glVertex2f(cx + 2, cy) - glVertex2f(cx, cy - 2) - glVertex2f(cx, cy + 2) - glEnd() - - # Buscar el insecto más cercano al frente - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - rad_yaw = math.radians(yaw) - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - # Si hay una presa cercana al frente, se dibuja un pequeño círculo genérico blanco - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - # Círculo vacío blanco (genérico) - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # ---------------------------------- - # 2. ICONOS Y CONTADORES HUD FIJOS - # ---------------------------------- - # Marcador Izquierdo: Mosquitos (Amarillo) - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 20) - glVertex2f(25, HEIGHT - 40) - glVertex2f(25, HEIGHT - 30) - glVertex2f(12, HEIGHT - 30) - glVertex2f(25, HEIGHT - 30) - glVertex2f(38, HEIGHT - 30) - glEnd() - - # Marcador Derecho: Moscas (Rojo-Marrón) - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 45, HEIGHT - 20) - glVertex2f(WIDTH - 45, HEIGHT - 40) - glVertex2f(WIDTH - 45, HEIGHT - 30) - glVertex2f(WIDTH - 58, HEIGHT - 30) - glVertex2f(WIDTH - 45, HEIGHT - 30) - glVertex2f(WIDTH - 32, HEIGHT - 30) - glEnd() - - glPopMatrix() - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glEnable(GL_DEPTH_TEST) - - # Textos fija de conteo - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) # Texto amarillo - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) # Texto rojo-marrón - - draw_text_pygame(txt_mosq, 48, 18) - draw_text_pygame(txt_flies, WIDTH - 110, 18) - -def draw_text_pygame(text_surface, x, y): - """Dibuja texto de PyGame en la pantalla de OpenGL.""" - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - glRasterPos2d(x, HEIGHT - y - h) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -# ========================================== -# BUCLE PRINCIPAL Y LÓGICA -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - # Estado de Caza - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - # Inicializar Insectos - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - - keys = pygame.key.get_pressed() - - # CONTROLES Y MOVIMIENTO DE LA LIBÉLULA - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_UP]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - else: - if keys[K_UP]: - speed = min(speed + acceleration, max_speed) - elif keys[K_DOWN]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 8.0 and abs(pz - (rz + rh)) < 8.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_w]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_s]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 60.0 if speed > 0.1 else 30.0 - wing_angle = math.sin(wing_timer) * 35.0 - - # ACTUALIZACIÓN DE INSECTOS - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - # RENDERIZADO - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - # DIBUJAR INTERFAZ (HUD) Y RADAR - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v5.0.py b/previos/dragonfly_game_v5.0.py deleted file mode 100644 index f849066..0000000 --- a/previos/dragonfly_game_v5.0.py +++ /dev/null @@ -1,668 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - if dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 30 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 20): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 15): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - for rx, ry, rz, rh in REEDS: - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - glVertex3f(rx, ry, rz) - glVertex3f(rx - 1.5, ry + 0.5, rz + rh * 0.7) - glVertex3f(rx, ry, rz) - glVertex3f(rx + 1.5, ry - 0.5, rz + rh * 0.8) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / RADAR Y TEXTOS FIJOS EN PANTALLA -# ========================================== -def draw_text_2d(text_surface, x, y): - """Renderiza texto de PyGame fijado en coordenadas 2D absolutas de pantalla.""" - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - #glRasterPos utiliza la matriz 2D activa - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - """Dibuja todo el HUD fijado en el plano 2D de la pantalla.""" - glDisable(GL_DEPTH_TEST) - - # 1. Configurar proyección 2D ortogonal estricta - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # ---------------------------------- - # A. RADAR REDUCIDO (Centro Superior) - # ---------------------------------- - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - # Círculo del radar - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - # Cruz - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - # Buscar presa cercana al frente - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - rad_yaw = math.radians(yaw) - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - # Círculo blanco genérico en radar - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # ---------------------------------- - # B. ÍCONOS SUPERIORES FIJOS - # ---------------------------------- - # Ícono Mosquito (Izquierda - Amarillo) - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 20) - glVertex2f(25, HEIGHT - 40) - glVertex2f(25, HEIGHT - 30) - glVertex2f(12, HEIGHT - 30) - glVertex2f(25, HEIGHT - 30) - glVertex2f(38, HEIGHT - 30) - glEnd() - - # Ícono Mosca (Derecha - Rojo/Marrón) - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 85, HEIGHT - 20) - glVertex2f(WIDTH - 85, HEIGHT - 40) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 98, HEIGHT - 30) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 72, HEIGHT - 30) - glEnd() - - # ---------------------------------- - # C. RENDERIZADO DE TEXTOS 2D FIJOS - # ---------------------------------- - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - # Posicionar texto exacto en coordenadas de pantalla 2D - draw_text_2d(txt_mosq, 45, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 65, HEIGHT - 40) - - # Restaurar matrices - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - # Estado de Caza - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - # Inicializar Insectos - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - - keys = pygame.key.get_pressed() - - # CONTROLES Y MOVIMIENTO DE LA LIBÉLULA - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_UP]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - else: - if keys[K_UP]: - speed = min(speed + acceleration, max_speed) - elif keys[K_DOWN]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 8.0 and abs(pz - (rz + rh)) < 8.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_w]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_s]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 60.0 if speed > 0.1 else 30.0 - wing_angle = math.sin(wing_timer) * 35.0 - - # ACTUALIZACIÓN DE INSECTOS - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - # RENDERIZADO - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - # DIBUJAR INTERFAZ (HUD) Y TEXTOS FIJOS EN 2D - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v5.3.py b/previos/dragonfly_game_v5.3.py deleted file mode 100644 index 5e7312e..0000000 --- a/previos/dragonfly_game_v5.3.py +++ /dev/null @@ -1,649 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: # Fly - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - # VENTAJA TÁCTICA: Si la libélula vuela por encima de ellos (pz > self.z), - # los mosquitos NO la detectan y no intentan huir. - can_detect = (pz <= self.z) - - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - # Las moscas sí detectan siempre a la libélula desde cualquier ángulo/altura - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 30 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 20): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 15): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - for rx, ry, rz, rh in REEDS: - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - glVertex3f(rx, ry, rz) - glVertex3f(rx - 1.5, ry + 0.5, rz + rh * 0.7) - glVertex3f(rx, ry, rz) - glVertex3f(rx + 1.5, ry - 0.5, rz + rh * 0.8) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / RADAR Y TEXTOS FIJOS -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - rad_yaw = math.radians(yaw) - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # ÍCONOS SUPERIORES - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 20) - glVertex2f(25, HEIGHT - 40) - glVertex2f(25, HEIGHT - 30) - glVertex2f(12, HEIGHT - 30) - glVertex2f(25, HEIGHT - 30) - glVertex2f(38, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 85, HEIGHT - 20) - glVertex2f(WIDTH - 85, HEIGHT - 40) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 98, HEIGHT - 30) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 32, HEIGHT - 30) - glEnd() - - # TEXTOS - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 45, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 65, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_UP]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - else: - if keys[K_UP]: - speed = min(speed + acceleration, max_speed) - elif keys[K_DOWN]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 8.0 and abs(pz - (rz + rh)) < 8.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_w]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_s]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 60.0 if speed > 0.1 else 30.0 - wing_angle = math.sin(wing_timer) * 35.0 - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v5.4.py b/previos/dragonfly_game_v5.4.py deleted file mode 100644 index 702c37b..0000000 --- a/previos/dragonfly_game_v5.4.py +++ /dev/null @@ -1,676 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: # Fly - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 30 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 20): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 15): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - for rx, ry, rz, rh in REEDS: - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - glVertex3f(rx, ry, rz) - glVertex3f(rx - 1.5, ry + 0.5, rz + rh * 0.7) - glVertex3f(rx, ry, rz) - glVertex3f(rx + 1.5, ry - 0.5, rz + rh * 0.8) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / RADAR Y TEXTOS FIJOS -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - rad_yaw = math.radians(yaw) - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # ÍCONOS SUPERIORES - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 20) - glVertex2f(25, HEIGHT - 40) - glVertex2f(25, HEIGHT - 30) - glVertex2f(12, HEIGHT - 30) - glVertex2f(25, HEIGHT - 30) - glVertex2f(38, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 85, HEIGHT - 20) - glVertex2f(WIDTH - 85, HEIGHT - 40) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 98, HEIGHT - 30) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 32, HEIGHT - 30) - glEnd() - - # TEXTOS - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 45, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 65, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - is_diving = False # Estado de picado automático - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - # Activar movimiento automático de PICADO al pulsar la barra ESPACIO - elif event.type == KEYDOWN and event.key == K_SPACE and not is_perched: - is_diving = True - - keys = pygame.key.get_pressed() - - if is_perched: - # En el junco, las flechas izquierda/derecha giran - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - # Despegar con W (Acelerar) - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - is_diving = False - else: - # ---------------------------------- - # CONTROLES Y MOVIMIENTO REORDENADOS - # ---------------------------------- - if is_diving: - # MODO PICADO AUTOMÁTICO (Caída a 45º a máxima velocidad) - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - # Desactivar picado al tocar suelo/agua o soltar control - if pz <= min_altitude: - pz = min_altitude - is_diving = False - else: - # Controles estándar - # W: Acelerar / S: Frenar (hasta vuelo estático) - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - # Si está casi detenida, intentar posarse en un junco cercano - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 8.0 and abs(pz - (rz + rh)) < 8.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - # Flecha Arriba: Subir altitud / Flecha Abajo: Bajar altitud - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - # Transición suave de cabeceo (Pitch) - pitch += (target_pitch - pitch) * 0.15 - - # Flecha Izquierda / Derecha: Giro lateral + Alabeo (Roll) - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - # Traslación hacia adelante según la dirección (Yaw) - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - # Muros invisibles - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - # Bucle continuo en arroyos - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if is_diving else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v5.5.py b/previos/dragonfly_game_v5.5.py deleted file mode 100644 index 1726e49..0000000 --- a/previos/dragonfly_game_v5.5.py +++ /dev/null @@ -1,694 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: # Fly - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 30 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 20): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 15): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - for rx, ry, rz, rh in REEDS: - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - glVertex3f(rx, ry, rz) - glVertex3f(rx - 1.5, ry + 0.5, rz + rh * 0.7) - glVertex3f(rx, ry, rz) - glVertex3f(rx + 1.5, ry - 0.5, rz + rh * 0.8) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / RADAR Y TEXTOS FIJOS -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - rad_yaw = math.radians(yaw) - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # ÍCONOS SUPERIORES - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 20) - glVertex2f(25, HEIGHT - 40) - glVertex2f(25, HEIGHT - 30) - glVertex2f(12, HEIGHT - 30) - glVertex2f(25, HEIGHT - 30) - glVertex2f(38, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 85, HEIGHT - 20) - glVertex2f(WIDTH - 85, HEIGHT - 40) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 98, HEIGHT - 30) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 32, HEIGHT - 30) - glEnd() - - # TEXTOS - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 45, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 65, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - # Estado de Picado y Recuperación - dive_phase = "none" # "none", "diving" o "recovering" - pre_dive_alt = 15.0 # Almacena la altitud previa al picado - pre_dive_speed = 0.0 # Almacena la velocidad previa al picado - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - # Activar movimiento automático de PICADO al pulsar ESPACIO - elif event.type == KEYDOWN and event.key == K_SPACE and not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - else: - # ---------------------------------- - # GESTIÓN DE FASES DE PICADO Y RECUPERACIÓN - # ---------------------------------- - if dive_phase == "diving": - # FASE 1: CAÍDA EN PICADO A 45º Y MÁXIMA VELOCIDAD - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - # Pasa a recuperación si toca agua/suelo o alcanza el límite inferior - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - # FASE 2: RECUPERACIÓN AUTOMÁTICA HASTA ALTITUD Y VELOCIDAD PREVIAS - target_pitch = 30.0 # Inclinación ascendente - pz += 0.8 - - # Ajustar suavemente la velocidad a la inicial antes del picado - speed += (pre_dive_speed - speed) * 0.1 - - # Al alcanzar la altitud original o superior, finaliza la maniobra - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - # VUELO ESTÁNDAR - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 8.0 and abs(pz - (rz + rh)) < 8.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - # Transición suave del ángulo Pitch - pitch += (target_pitch - pitch) * 0.15 - - # Giros laterales con Flechas Izquierda / Derecha - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - # Traslación - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - # Muros invisibles - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if dive_phase != "none" else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - # SI CAZA MIENTRAS ESTÁ EN PICADO, INICIA INMEDIATAMENTE LA RECUPERACIÓN DE ALTITUD - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v6.1.py b/previos/dragonfly_game_v6.1.py deleted file mode 100644 index 41f4293..0000000 --- a/previos/dragonfly_game_v6.1.py +++ /dev/null @@ -1,696 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espiga marrón oscuro de los juncos -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 30 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 20): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 15): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - # Juncos realistas con múltiples varas en distintas direcciones y espigas marrones (cattails) - for rx, ry, rz, rh in REEDS: - # Tallo principal vertical - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - - # Varas secundarias inclinadas en diferentes direcciones - offsets = [(-1.8, 0.8), (1.5, -0.7), (-0.8, -1.6), (1.2, 1.4)] - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.8) - glEnd() - - # Espigas / cilindros marrón oscuro en la punta de cada vara - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - # Punta principal - glVertex3f(rx, ry, rz + rh - 3.5) - glVertex3f(rx, ry, rz + rh) - # Puntas secundarias - for ox, oy in offsets: - glVertex3f(rx + ox, ry + oy, (rz + rh * 0.8) - 3.0) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.8) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / RADAR Y TEXTOS FIJOS (LIMPIA) -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # 1. RADAR REDUCIDO (Centro Superior) - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - rad_yaw = math.radians(yaw) - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # 2. ÍCONOS SUPERIORES (SIN CRUCES, MÁS LIMPIO) - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 30) - glVertex2f(12, HEIGHT - 30) - glVertex2f(25, HEIGHT - 30) - glVertex2f(38, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 98, HEIGHT - 30) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 72, HEIGHT - 30) - glEnd() - - # 3. TEXTOS - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 45, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 65, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN and event.key == K_SPACE and not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - - keys = pygame.key.get_pressed() - - if is_perched: - # Flecha Izquierda / Derecha para girar posada - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - # W para acelerar y despegar - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - else: - if dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - # W: Acelerar / S: Frenar - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - # Radio de detección ampliado y ajustado a la altura del junco para facilitar el aterrizaje - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - # Flecha Arriba: Subir / Flecha Abajo: Bajar - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - pitch += (target_pitch - pitch) * 0.15 - - # Flecha Izquierda / Derecha: Moverse lateralmente o girar en vuelo - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if dive_phase != "none" else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v6.2.py b/previos/dragonfly_game_v6.2.py deleted file mode 100644 index 9f1571d..0000000 --- a/previos/dragonfly_game_v6.2.py +++ /dev/null @@ -1,736 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 30 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 20): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 15): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # ÍCONOS SUPERIORES - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 30) - glVertex2f(12, HEIGHT - 30) - glVertex2f(25, HEIGHT - 30) - glVertex2f(38, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 98, HEIGHT - 30) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 72, HEIGHT - 30) - glEnd() - - # TEXTOS - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 45, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 65, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN and event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.type == KEYDOWN and event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v6.3.py b/previos/dragonfly_game_v6.3.py deleted file mode 100644 index 0b8c8b8..0000000 --- a/previos/dragonfly_game_v6.3.py +++ /dev/null @@ -1,797 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.1, 0.9, 0.3) # Rana verde decorativa -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS Y RANA DECORATIVA -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - # Seleccionar dos puntos aleatorios de inicio y destino entre islas y juncos - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.5 - self.speed = 0.4 - self.yaw = 0.0 - - def pick_new_destination(self): - # Destinos posibles: islas o posiciones de juncos - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-20, 20), dest[1] + random.uniform(-20, 20) - - def update(self): - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 5.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self): - glColor3f(*COLOR_FROG) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - # Pequeño modelo alámbrico de rana - glBegin(GL_LINES) - # Cuerpo - glVertex3f(0, 2.0, 0) - glVertex3f(0, -2.0, 0) - glVertex3f(-1.5, 1.0, 0) - glVertex3f(1.5, 1.0, 0) - glVertex3f(-1.5, -1.0, 0) - glVertex3f(1.5, -1.0, 0) - # Ojos saltones - glVertex3f(-0.8, 1.8, 0.5) - glVertex3f(-0.8, 1.8, 0) - glVertex3f(0.8, 1.8, 0.5) - glVertex3f(0.8, 1.8, 0) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - """Malla de agua con mayor densidad de líneas para mayor realismo visual.""" - glColor3f(*COLOR_WATER) - - grid_size = 15 # Reducido de 30 a 15 para duplicar la densidad de líneas - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # ÍCONOS SUPERIORES - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 30) - glVertex2f(12, HEIGHT - 30) - glVertex2f(25, HEIGHT - 30) - glVertex2f(38, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 98, HEIGHT - 30) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 72, HEIGHT - 30) - glEnd() - - # TEXTOS - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 45, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 65, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - # Instanciar elementos y animales - frog = Frog() - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN and event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.type == KEYDOWN and event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - # Actualizar rana y ambiente - frog.update() - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - # Dibujar rana sobre el agua - frog.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v6.4.py b/previos/dragonfly_game_v6.4.py deleted file mode 100644 index 1489d4c..0000000 --- a/previos/dragonfly_game_v6.4.py +++ /dev/null @@ -1,828 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS Y RANA DECORATIVA -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self): - glColor3f(*COLOR_FROG) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - # Modelo 3D de rana ampliado y detallado (Anatomía reconocible) - glBegin(GL_LINES) - # Tronco / Cuerpo robusto y ancho - glVertex3f(0.0, 5.0, 0.5) - glVertex3f(0.0, -4.0, 0.5) - glVertex3f(-3.0, 2.0, 0.5) - glVertex3f(3.0, 2.0, 0.5) - glVertex3f(-3.5, -2.0, 0.5) - glVertex3f(3.5, -2.0, 0.5) - - # Conexión lomo - glVertex3f(-3.0, 2.0, 0.5) - glVertex3f(0.0, 5.0, 0.5) - glVertex3f(3.0, 2.0, 0.5) - glVertex3f(0.0, 5.0, 0.5) - - # Cabeza ancha - glVertex3f(-2.5, 5.0, 0.6) - glVertex3f(2.5, 5.0, 0.6) - glVertex3f(-2.5, 5.0, 0.6) - glVertex3f(0.0, 7.5, 0.6) - glVertex3f(2.5, 5.0, 0.6) - glVertex3f(0.0, 7.5, 0.6) - - # Ojos saltones prominentes - glVertex3f(-2.0, 6.5, 1.8) - glVertex3f(-2.0, 6.5, 0.8) - glVertex3f(2.0, 6.5, 1.8) - glVertex3f(2.0, 6.5, 0.8) - - # Patas delanteras flexionadas - glVertex3f(-3.0, 2.0, 0.5) - glVertex3f(-5.0, 3.5, 0.0) - glVertex3f(-5.0, 3.5, 0.0) - glVertex3f(-4.5, 4.5, 0.0) - - glVertex3f(3.0, 2.0, 0.5) - glVertex3f(5.0, 3.5, 0.0) - glVertex3f(5.0, 3.5, 0.0) - glVertex3f(4.5, 4.5, 0.0) - - # Patas traseras musculosas y plegadas - glVertex3f(-3.5, -2.0, 0.5) - glVertex3f(-6.5, -4.0, 0.0) - glVertex3f(-6.5, -4.0, 0.0) - glVertex3f(-4.0, -6.0, 0.0) - - glVertex3f(3.5, -2.0, 0.5) - glVertex3f(6.5, -4.0, 0.0) - glVertex3f(6.5, -4.0, 0.0) - glVertex3f(4.0, -6.0, 0.0) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # ÍCONOS SUPERIORES - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 30) - glVertex2f(12, HEIGHT - 30) - glVertex2f(25, HEIGHT - 30) - glVertex2f(38, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 98, HEIGHT - 30) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 72, HEIGHT - 30) - glEnd() - - # TEXTOS - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 45, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 65, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN and event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.type == KEYDOWN and event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - frog.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v6.5.py b/previos/dragonfly_game_v6.5.py deleted file mode 100644 index 96fe80b..0000000 --- a/previos/dragonfly_game_v6.5.py +++ /dev/null @@ -1,834 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS Y RANA DECORATIVA -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - # Apuntar la cabeza hacia la dirección del movimiento (ajustado 90 grados para orientar el frente del modelo) - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self): - glColor3f(*COLOR_FROG) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - # Offset de animación de las patas traseras para simular el nado - leg_offset = math.sin(self.swim_timer) * 1.5 - - # Modelo 3D de rana orientado al frente y con patas traseras móviles - glBegin(GL_LINES) - # Tronco / Cuerpo robusto - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - # Conexión lomo - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - # Cabeza (apuntando hacia adelante / eje Y positivo) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - # Ojos saltones prominentes - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - # Patas delanteras estáticas - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - # Patas traseras móviles simulando brazada/patada de natación - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-3.0 - leg_offset, -6.0, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(3.0 + leg_offset, -6.0, 0.0) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # ÍCONOS SUPERIORES - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 30) - glVertex2f(12, HEIGHT - 30) - glVertex2f(25, HEIGHT - 30) - glVertex2f(38, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 98, HEIGHT - 30) - glVertex2f(WIDTH - 85, HEIGHT - 30) - glVertex2f(WIDTH - 72, HEIGHT - 30) - glEnd() - - # TEXTOS - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 45, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 65, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN and event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.type == KEYDOWN and event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - frog.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v7.0.py b/previos/dragonfly_game_v7.0.py deleted file mode 100644 index fd28e52..0000000 --- a/previos/dragonfly_game_v7.0.py +++ /dev/null @@ -1,830 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 10, 20 -FLY_MIN, FLY_MAX = 2, 7 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS Y RANA DECORATIVA -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self): - glColor3f(*COLOR_FROG) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - leg_offset = math.sin(self.swim_timer) * 1.5 - - glBegin(GL_LINES) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-3.0 - leg_offset, -6.0, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(3.0 + leg_offset, -6.0, 0.0) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # DIBUJOS ESTÁTICOS 2D DE LOS INSECTOS EN EL HUD - # 1. Icono de Mosquito (Izquierda - Amarillo) - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 33) - glVertex2f(25, HEIGHT - 27) - glVertex2f(21, HEIGHT - 30) - glVertex2f(29, HEIGHT - 30) - glEnd() - - # 2. Icono de Mosca (Derecha - Rojo/Marrón) - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - # Cuerpo y alas estáticas de la mosca - glVertex2f(WIDTH - 82, HEIGHT - 33) - glVertex2f(WIDTH - 82, HEIGHT - 27) - glVertex2f(WIDTH - 86, HEIGHT - 28) - glVertex2f(WIDTH - 78, HEIGHT - 32) - glVertex2f(WIDTH - 86, HEIGHT - 32) - glVertex2f(WIDTH - 78, HEIGHT - 28) - glEnd() - - # TEXTOS DE CONTADORES - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 38, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 68, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN and event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.type == KEYDOWN and event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - frog.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v7.2.py b/previos/dragonfly_game_v7.2.py deleted file mode 100644 index fa3d899..0000000 --- a/previos/dragonfly_game_v7.2.py +++ /dev/null @@ -1,827 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos (Aumentadas en +7 el mínimo y máximo) -MOSQUITO_MIN, MOSQUITO_MAX = 17, 27 -FLY_MIN, FLY_MAX = 9, 14 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS Y RANA DECORATIVA -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self): - glColor3f(*COLOR_FROG) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - leg_offset = math.sin(self.swim_timer) * 1.5 - - glBegin(GL_LINES) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-3.0 - leg_offset, -6.0, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(3.0 + leg_offset, -6.0, 0.0) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # DIBUJOS ESTÁTICOS 2D DE LOS INSECTOS EN EL HUD - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 33) - glVertex2f(25, HEIGHT - 27) - glVertex2f(21, HEIGHT - 30) - glVertex2f(29, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 82, HEIGHT - 33) - glVertex2f(WIDTH - 82, HEIGHT - 27) - glVertex2f(WIDTH - 86, HEIGHT - 28) - glVertex2f(WIDTH - 78, HEIGHT - 32) - glVertex2f(WIDTH - 86, HEIGHT - 32) - glVertex2f(WIDTH - 78, HEIGHT - 28) - glEnd() - - # TEXTOS DE CONTADORES - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 38, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 68, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN and event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.type == KEYDOWN and event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - frog.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/dragonfly_game_v8.0.py b/previos/dragonfly_game_v8.0.py deleted file mode 100644 index fa3d899..0000000 --- a/previos/dragonfly_game_v8.0.py +++ /dev/null @@ -1,827 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos (Aumentadas en +7 el mínimo y máximo) -MOSQUITO_MIN, MOSQUITO_MAX = 17, 27 -FLY_MIN, FLY_MAX = 9, 14 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS Y RANA DECORATIVA -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self): - glColor3f(*COLOR_FROG) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - leg_offset = math.sin(self.swim_timer) * 1.5 - - glBegin(GL_LINES) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-3.0 - leg_offset, -6.0, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(3.0 + leg_offset, -6.0, 0.0) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # DIBUJOS ESTÁTICOS 2D DE LOS INSECTOS EN EL HUD - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 33) - glVertex2f(25, HEIGHT - 27) - glVertex2f(21, HEIGHT - 30) - glVertex2f(29, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 82, HEIGHT - 33) - glVertex2f(WIDTH - 82, HEIGHT - 27) - glVertex2f(WIDTH - 86, HEIGHT - 28) - glVertex2f(WIDTH - 78, HEIGHT - 32) - glVertex2f(WIDTH - 86, HEIGHT - 32) - glVertex2f(WIDTH - 78, HEIGHT - 28) - glEnd() - - # TEXTOS DE CONTADORES - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 38, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 68, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Simulador de Vuelo de Libélula 3D") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - running = True - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN and event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.type == KEYDOWN and event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - - frog.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/libelula_v8.2.py b/previos/libelula_v8.2.py deleted file mode 100644 index f4c9cfa..0000000 --- a/previos/libelula_v8.2.py +++ /dev/null @@ -1,968 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 17, 27 -FLY_MIN, FLY_MAX = 9, 14 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS Y RANA DECORATIVA -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self): - glColor3f(*COLOR_FROG) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - leg_offset = math.sin(self.swim_timer) * 1.5 - - glBegin(GL_LINES) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-3.0 - leg_offset, -6.0, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(3.0 + leg_offset, -6.0, 0.0) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD Y PANTALLA INICIAL -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_start_screen(font_title, font_subtitle, font_text, wing_angle): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - # TÍTULO PRINCIPAL - txt_title = font_title.render("LIBÉLULA", True, (0, 220, 255)) - draw_text_2d(txt_title, WIDTH // 2 - 130, HEIGHT - 100) - - # DIBUJO ANIMADO DE LA LIBÉLULA EN LA PANTALLA INICIAL - glPushMatrix() - glTranslatef(WIDTH // 2 + 150, HEIGHT - 75, 0.0) - glScalef(12.0, 12.0, 1.0) - - # Dibujar libélula estática en 2D para el menú - glColor3f(*COLOR_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - - # Alas aleteando - rad_w = math.radians(wing_angle) - w_off = math.sin(rad_w) * 1.5 - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_off) - glVertex3f(-3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_off) - glVertex3f(3.8, 0.2, w_off) - glEnd() - glPopMatrix() - - # OBJETIVO DEL JUEGO - obj_line1 = font_text.render("Objetivo: Vuela libre por el estanque y sus arroyos capturando moscas", True, (255, 255, 255)) - obj_line2 = font_text.render("y mosquitos. Una vez cazados debes devorarlos posado en un junco.", True, (255, 255, 255)) - draw_text_2d(obj_line1, 70, HEIGHT - 170) - draw_text_2d(obj_line2, 70, HEIGHT - 195) - - # CONTROLES Y TECLAS - controls = [ - "CONTROLES:", - "W / S : Acelerar / Frenar", - "Flecha Arriba / Abajo : Cambiar altitud", - "Flecha Izquierda / Derecha : Girar / Virar", - "ESPACIO : Picado rápido para caza", - "P : Aterrizaje automático en junco cercano", - "ESC : Salir del juego" - ] - - y_pos = HEIGHT - 250 - for ctrl in controls: - is_title = ctrl.endswith(":") - col = (255, 220, 50) if is_title else (200, 200, 200) - t_surf = font_subtitle.render(ctrl, True, col) if is_title else font_text.render(ctrl, True, col) - draw_text_2d(t_surf, 70, y_pos) - y_pos -= 25 - - # LEYENDA VISUAL INFERIOR (Mosquito, Mosca y Junco) - y_icon = 90 - - # Icono Mosquito - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(100, y_icon + 5) - glVertex2f(100, y_icon - 5) - glVertex2f(95, y_icon) - glVertex2f(105, y_icon) - glEnd() - txt_m = font_text.render("Mosquito", True, (255, 230, 0)) - draw_text_2d(txt_m, 120, y_icon - 8) - - # Icono Mosca - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(280, y_icon + 5) - glVertex2f(280, y_icon - 5) - glVertex2f(275, y_icon - 3) - glVertex2f(285, y_icon + 3) - glVertex2f(275, y_icon + 3) - glVertex2f(285, y_icon - 3) - glEnd() - txt_f = font_text.render("Mosca", True, (200, 65, 40)) - draw_text_2d(txt_f, 300, y_icon - 8) - - # Icono Junco - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex2f(450, y_icon - 10) - glVertex2f(450, y_icon + 15) - glEnd() - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - glVertex2f(450, y_icon + 8) - glVertex2f(450, y_icon + 15) - glEnd() - txt_r = font_text.render("Junco", True, (100, 220, 100)) - draw_text_2d(txt_r, 470, y_icon - 8) - - # MENSAJE PARA COMENZAR - start_msg = font_subtitle.render("Pulsa INTRO o cualquier tecla para comenzar...", True, (100, 255, 100)) - draw_text_2d(start_msg, WIDTH // 2 - 200, 30) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # DIBUJOS ESTÁTICOS 2D DE LOS INSECTOS EN EL HUD[cite: 4] - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 33) - glVertex2f(25, HEIGHT - 27) - glVertex2f(21, HEIGHT - 30) - glVertex2f(29, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 82, HEIGHT - 33) - glVertex2f(WIDTH - 82, HEIGHT - 27) - glVertex2f(WIDTH - 86, HEIGHT - 28) - glVertex2f(WIDTH - 78, HEIGHT - 32) - glVertex2f(WIDTH - 86, HEIGHT - 32) - glVertex2f(WIDTH - 78, HEIGHT - 28) - glEnd() - - # TEXTOS DE CONTADORES[cite: 4] - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 38, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 68, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - font_title = pygame.font.SysFont("Arial", 48, bold=True) - font_subtitle = pygame.font.SysFont("Arial", 20, bold=True) - font_text = pygame.font.SysFont("Arial", 16, bold=False) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Libélula") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - in_start_screen = True - running = True - - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - wing_timer += 60.0 - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN: - if in_start_screen: - in_start_screen = False # Salir de la pantalla de inicio con cualquier tecla - else: - if event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - if in_start_screen: - # Renderizar fondo del mundo de forma estática para ambientar el menú - cam_x, cam_y, cam_z = 0.0, -100.0, 30.0 - gluLookAt(cam_x, cam_y, cam_z, 0, 0, 15, 0, 0, 1) - draw_map(water_timer) - frog.draw() - - # Dibujar la pantalla de inicio 2D encima - wing_angle = math.sin(wing_timer * 0.5) * 35.0 - draw_start_screen(font_title, font_subtitle, font_text, wing_angle) - else: - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - frog.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/libelula_v8.3.py b/previos/libelula_v8.3.py deleted file mode 100644 index 631455b..0000000 --- a/previos/libelula_v8.3.py +++ /dev/null @@ -1,982 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 17, 27 -FLY_MIN, FLY_MAX = 9, 14 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS Y RANA DECORATIVA -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self): - glColor3f(*COLOR_FROG) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - leg_offset = math.sin(self.swim_timer) * 1.5 - - glBegin(GL_LINES) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-3.0 - leg_offset, -6.0, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(3.0 + leg_offset, -6.0, 0.0) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - # Par de alas delanteras - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - # Par de alas traseras (añadidas para completar las 4 alas) - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD Y PANTALLA INICIAL -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_start_screen(font_title, font_subtitle, font_text, wing_angle): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - # TÍTULO PRINCIPAL - txt_title = font_title.render("LIBÉLULA", True, (0, 220, 255)) - draw_text_2d(txt_title, WIDTH // 2 - 130, HEIGHT - 100) - - # DIBUJO ANIMADO DE LA LIBÉLULA DE 4 ALAS EN LA PANTALLA INICIAL - glPushMatrix() - glTranslatef(WIDTH // 2 + 150, HEIGHT - 75, 0.0) - glScalef(12.0, 12.0, 1.0) - - glColor3f(*COLOR_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - - # 4 alas aleteando en el menú - rad_w = math.radians(wing_angle) - w_off = math.sin(rad_w) * 1.5 - - # Alas delanteras - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_off) - glVertex3f(-3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_off) - glVertex3f(3.8, 0.2, w_off) - glEnd() - - # Alas traseras - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_off) - glVertex3f(-3.0, -0.8, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_off) - glVertex3f(3.0, -0.8, w_off) - glEnd() - - glPopMatrix() - - # OBJETIVO DEL JUEGO - obj_line1 = font_text.render("Objetivo: Vuela libre por el estanque y sus arroyos capturando moscas", True, (255, 255, 255)) - obj_line2 = font_text.render("y mosquitos. Una vez cazados debes devorarlos posado en un junco.", True, (255, 255, 255)) - draw_text_2d(obj_line1, 70, HEIGHT - 170) - draw_text_2d(obj_line2, 70, HEIGHT - 195) - - # CONTROLES Y TECLAS - controls = [ - "CONTROLES:", - "W / S : Acelerar / Frenar", - "Flecha Arriba / Abajo : Cambiar altitud", - "Flecha Izquierda / Derecha : Girar / Virar", - "ESPACIO : Picado rápido para caza", - "P : Aterrizaje automático en junco cercano", - "ESC : Salir del juego" - ] - - y_pos = HEIGHT - 250 - for ctrl in controls: - is_title = ctrl.endswith(":") - col = (255, 220, 50) if is_title else (200, 200, 200) - t_surf = font_subtitle.render(ctrl, True, col) if is_title else font_text.render(ctrl, True, col) - draw_text_2d(t_surf, 70, y_pos) - y_pos -= 25 - - # LEYENDA VISUAL INFERIOR (Mosquito, Mosca y Junco) - y_icon = 90 - - # Icono Mosquito - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(100, y_icon + 5) - glVertex2f(100, y_icon - 5) - glVertex2f(95, y_icon) - glVertex2f(105, y_icon) - glEnd() - txt_m = font_text.render("Mosquito", True, (255, 230, 0)) - draw_text_2d(txt_m, 120, y_icon - 8) - - # Icono Mosca - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(280, y_icon + 5) - glVertex2f(280, y_icon - 5) - glVertex2f(275, y_icon - 3) - glVertex2f(285, y_icon + 3) - glVertex2f(275, y_icon + 3) - glVertex2f(285, y_icon - 3) - glEnd() - txt_f = font_text.render("Mosca", True, (200, 65, 40)) - draw_text_2d(txt_f, 300, y_icon - 8) - - # Icono Junco - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex2f(450, y_icon - 10) - glVertex2f(450, y_icon + 15) - glEnd() - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - glVertex2f(450, y_icon + 8) - glVertex2f(450, y_icon + 15) - glEnd() - txt_r = font_text.render("Junco", True, (100, 220, 100)) - draw_text_2d(txt_r, 470, y_icon - 8) - - # MENSAJE PARA COMENZAR - start_msg = font_subtitle.render("Pulsa INTRO o cualquier tecla para comenzar...", True, (100, 255, 100)) - draw_text_2d(start_msg, WIDTH // 2 - 200, 30) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - # RADAR REDUCIDO - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - # DIBUJOS ESTÁTICOS 2D DE LOS INSECTOS EN EL HUD - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 33) - glVertex2f(25, HEIGHT - 27) - glVertex2f(21, HEIGHT - 30) - glVertex2f(29, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 82, HEIGHT - 33) - glVertex2f(WIDTH - 82, HEIGHT - 27) - glVertex2f(WIDTH - 86, HEIGHT - 28) - glVertex2f(WIDTH - 78, HEIGHT - 32) - glVertex2f(WIDTH - 86, HEIGHT - 32) - glVertex2f(WIDTH - 78, HEIGHT - 28) - glEnd() - - # TEXTOS DE CONTADORES - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 38, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 68, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - font_title = pygame.font.SysFont("Arial", 48, bold=True) - font_subtitle = pygame.font.SysFont("Arial", 20, bold=True) - font_text = pygame.font.SysFont("Arial", 16, bold=False) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Libélula") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Estado de la Libélula - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - in_start_screen = True - running = True - - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - wing_timer += 60.0 - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN: - if in_start_screen: - in_start_screen = False # Salir de la pantalla de inicio con cualquier tecla - else: - if event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - if in_start_screen: - cam_x, cam_y, cam_z = 0.0, -100.0, 30.0 - gluLookAt(cam_x, cam_y, cam_z, 0, 0, 15, 0, 0, 1) - draw_map(water_timer) - frog.draw() - - wing_angle = math.sin(wing_timer * 0.5) * 35.0 - draw_start_screen(font_title, font_subtitle, font_text, wing_angle) - else: - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - frog.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/libelula_v8.4.py b/previos/libelula_v8.4.py deleted file mode 100644 index 66793d8..0000000 --- a/previos/libelula_v8.4.py +++ /dev/null @@ -1,972 +0,0 @@ -import sys -import math -import random -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula alámbrica (Cian) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 17, 27 -FLY_MIN, FLY_MAX = 9, 14 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# CLASES DE INSECTOS Y RANA DECORATIVA -# ========================================== -class Insect: - def __init__(self, insect_type): - self.type = insect_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - - if self.type == "mosquito": - self.speed = 1.8 - self.size = 1.2 - else: - self.speed = 0.9 - self.size = 1.6 - - def update(self, player_pos): - self.timer += 0.05 - px, py, pz = player_pos - dist_to_player = math.hypot(self.x - px, self.y - py) - - if self.type == "mosquito": - can_detect = (pz <= self.z) - if can_detect and dist_to_player < 100: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw += (escape_angle - self.yaw) * 0.05 - - self.yaw += math.sin(self.timer * 3) * 1.5 - self.z += math.sin(self.timer * 1.5) * 0.2 - - elif self.type == "fly": - if dist_to_player < 120: - escape_angle = math.degrees(math.atan2(self.y - py, self.x - px)) - self.yaw = escape_angle + random.uniform(-45, 45) - self.z += random.choice([-1.5, 1.5]) - else: - if random.random() < 0.08: - self.yaw += random.uniform(-60, 60) - - self.yaw += math.sin(self.timer * 6) * 4.0 - self.z += math.cos(self.timer * 4) * 0.6 - - self.z = max(3.0, min(50.0, self.z)) - - rad = math.radians(self.yaw) - next_x = self.x - math.sin(rad) * self.speed - next_y = self.y + math.cos(rad) * self.speed - - if is_over_water(next_x, next_y): - self.x, self.y = next_x, next_y - else: - self.yaw += 140.0 + random.uniform(-20, 20) - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self): - glColor3f(*COLOR_FROG) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - leg_offset = math.sin(self.swim_timer) * 1.5 - - glBegin(GL_LINES) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - # Patas traseras con los pies mirando hacia el exterior (más realismo)[cite: 4] - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-6.5 - leg_offset, -3.2, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(6.5 + leg_offset, -3.2, 0.0) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - # Par de alas delanteras - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - # Par de alas traseras - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD Y PANTALLA INICIAL -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_start_screen(font_title, font_subtitle, font_text, wing_angle): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - # TÍTULO PRINCIPAL - txt_title = font_title.render("LIBÉLULA", True, (0, 220, 255)) - draw_text_2d(txt_title, WIDTH // 2 - 130, HEIGHT - 100) - - # DIBUJO ANIMADO DE LA LIBÉLULA DE 4 ALAS EN LA PANTALLA INICIAL - glPushMatrix() - glTranslatef(WIDTH // 2 + 150, HEIGHT - 75, 0.0) - glScalef(12.0, 12.0, 1.0) - - glColor3f(*COLOR_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - - rad_w = math.radians(wing_angle) - w_off = math.sin(rad_w) * 1.5 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_off) - glVertex3f(-3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_off) - glVertex3f(3.8, 0.2, w_off) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_off) - glVertex3f(-3.0, -0.8, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_off) - glVertex3f(3.0, -0.8, w_off) - glEnd() - - glPopMatrix() - - # OBJETIVO DEL JUEGO - obj_line1 = font_text.render("Objetivo: Vuela libre por el estanque y sus arroyos capturando moscas", True, (255, 255, 255)) - obj_line2 = font_text.render("y mosquitos. Una vez cazados debes devorarlos posado en un junco.", True, (255, 255, 255)) - draw_text_2d(obj_line1, 70, HEIGHT - 170) - draw_text_2d(obj_line2, 70, HEIGHT - 195) - - # CONTROLES Y TECLAS - controls = [ - "CONTROLES:", - "W / S : Acelerar / Frenar", - "Flecha Arriba / Abajo : Cambiar altitud", - "Flecha Izquierda / Derecha : Girar / Virar", - "ESPACIO : Picado rápido para caza", - "P : Aterrizaje automático en junco cercano", - "ESC : Salir del juego" - ] - - y_pos = HEIGHT - 250 - for ctrl in controls: - is_title = ctrl.endswith(":") - col = (255, 220, 50) if is_title else (200, 200, 200) - t_surf = font_subtitle.render(ctrl, True, col) if is_title else font_text.render(ctrl, True, col) - draw_text_2d(t_surf, 70, y_pos) - y_pos -= 25 - - # LEYENDA VISUAL INFERIOR - y_icon = 90 - - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(100, y_icon + 5) - glVertex2f(100, y_icon - 5) - glVertex2f(95, y_icon) - glVertex2f(105, y_icon) - glEnd() - txt_m = font_text.render("Mosquito", True, (255, 230, 0)) - draw_text_2d(txt_m, 120, y_icon - 8) - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(280, y_icon + 5) - glVertex2f(280, y_icon - 5) - glVertex2f(275, y_icon - 3) - glVertex2f(285, y_icon + 3) - glVertex2f(275, y_icon + 3) - glVertex2f(285, y_icon - 3) - glEnd() - txt_f = font_text.render("Mosca", True, (200, 65, 40)) - draw_text_2d(txt_f, 300, y_icon - 8) - - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex2f(450, y_icon - 10) - glVertex2f(450, y_icon + 15) - glEnd() - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - glVertex2f(450, y_icon + 8) - glVertex2f(450, y_icon + 15) - glEnd() - txt_r = font_text.render("Junco", True, (100, 220, 100)) - draw_text_2d(txt_r, 470, y_icon - 8) - - start_msg = font_subtitle.render("Pulsa INTRO o cualquier tecla para comenzar...", True, (100, 255, 100)) - draw_text_2d(start_msg, WIDTH // 2 - 200, 30) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 33) - glVertex2f(25, HEIGHT - 27) - glVertex2f(21, HEIGHT - 30) - glVertex2f(29, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 82, HEIGHT - 33) - glVertex2f(WIDTH - 82, HEIGHT - 27) - glVertex2f(WIDTH - 86, HEIGHT - 28) - glVertex2f(WIDTH - 78, HEIGHT - 32) - glVertex2f(WIDTH - 86, HEIGHT - 32) - glVertex2f(WIDTH - 78, HEIGHT - 28) - glEnd() - - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 38, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 68, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.font.init() - font = pygame.font.SysFont("Arial", 22, bold=True) - font_title = pygame.font.SysFont("Arial", 48, bold=True) - font_subtitle = pygame.font.SysFont("Arial", 20, bold=True) - font_text = pygame.font.SysFont("Arial", 16, bold=False) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Libélula") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - in_start_screen = True - running = True - - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - wing_timer += 60.0 - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN: - if in_start_screen: - in_start_screen = False - else: - if event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - if in_start_screen: - cam_x, cam_y, cam_z = 0.0, -100.0, 30.0 - gluLookAt(cam_x, cam_y, cam_z, 0, 0, 15, 0, 0, 1) - draw_map(water_timer) - frog.draw() - - wing_angle = math.sin(wing_timer * 0.5) * 35.0 - draw_start_screen(font_title, font_subtitle, font_text, wing_angle) - else: - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - current_mosquitos = 0 - current_flies = 0 - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - if not caught_insect: - dist_to_dragonfly = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_dragonfly < 6.0: - caught_insect = insect - insects.remove(insect) - - if dive_phase == "diving": - dive_phase = "recovering" - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - frog.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/libelula_v9.0.py b/previos/libelula_v9.0.py deleted file mode 100644 index 0073f8a..0000000 --- a/previos/libelula_v9.0.py +++ /dev/null @@ -1,1139 +0,0 @@ -import sys -import math -import random -import struct -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula del jugador (Cian) -COLOR_PURPLE_DRAGONFLY = (0.7, 0.2, 0.8) # Libélulas decorativas (Morado) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 17, 27 -FLY_MIN, FLY_MAX = 9, 14 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# GENERADOR DE AUDIO PROCEDURAL (SIN ARCHIVOS) -# ========================================== -def generate_buzz_sound(): - try: - sample_rate = 22050 - duration = 0.3 - num_samples = int(sample_rate * duration) - buffer = bytearray() - for i in range(num_samples): - t = i / sample_rate - val = int(32767 * 0.25 * math.sin(2 * math.pi * 180 * t + math.sin(2 * math.pi * 25 * t) * 3)) - buffer.extend(struct.pack(' STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class PurpleDragonfly: - def __init__(self, behavior_type): - self.behavior = behavior_type # 1: Círculos, 2: Patrulla lineal, 3: Zig-zag errático - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - self.wing_timer = random.uniform(0, 100) - self.is_perched = False - self.perch_timer = 0 - - def update(self): - self.wing_timer += 60.0 - if self.is_perched: - self.perch_timer -= 1 - if self.perch_timer <= 0: - self.is_perched = False - return - - self.timer += 0.05 - if self.behavior == 1: - self.yaw += 1.2 - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.1 - self.y += math.cos(rad) * 1.1 - elif self.behavior == 2: - if random.random() < 0.02: - self.yaw += random.uniform(-40, 40) - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.3 - self.y += math.cos(rad) * 1.3 - else: - self.yaw += math.sin(self.timer * 3) * 3.5 - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.4 - self.y += math.cos(rad) * 1.4 - - self.z = 15.0 + math.sin(self.timer * 2) * 6.0 - - # Posibilidad de posarse en un junco de vez en cuando - if random.random() < 0.0015 and REEDS: - reed = random.choice(REEDS) - self.x, self.y, self.z = reed[0], reed[1], reed[2] + reed[3] - self.is_perched = True - self.perch_timer = random.randint(300, 600) - - if not is_over_water(self.x, self.y): - self.yaw += 150.0 - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self): - glColor3f(*COLOR_PURPLE_DRAGONFLY) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - wing_angle = 0.0 if self.is_perched else math.sin(self.wing_timer) * 35.0 - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self): - glColor3f(*COLOR_FROG) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - leg_offset = math.sin(self.swim_timer) * 1.5 - - glBegin(GL_LINES) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - # Pies hacia el exterior - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-6.5 - leg_offset, -3.2, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(6.5 + leg_offset, -3.2, 0.0) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD Y PANTALLA INICIAL -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_start_screen(font_title, font_subtitle, font_text, wing_angle): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - txt_title = font_title.render("LIBÉLULA", True, (0, 220, 255)) - draw_text_2d(txt_title, WIDTH // 2 - 130, HEIGHT - 100) - - glPushMatrix() - glTranslatef(WIDTH // 2 + 150, HEIGHT - 75, 0.0) - glScalef(12.0, 12.0, 1.0) - - glColor3f(*COLOR_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - - rad_w = math.radians(wing_angle) - w_off = math.sin(rad_w) * 1.5 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_off) - glVertex3f(-3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_off) - glVertex3f(3.8, 0.2, w_off) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_off) - glVertex3f(-3.0, -0.8, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_off) - glVertex3f(3.0, -0.8, w_off) - glEnd() - - glPopMatrix() - - obj_line1 = font_text.render("Objetivo: Vuela libre por el estanque y sus arroyos capturando moscas", True, (255, 255, 255)) - obj_line2 = font_text.render("y mosquitos. Una vez cazados debes devorarlos posado en un junco.", True, (255, 255, 255)) - draw_text_2d(obj_line1, 70, HEIGHT - 170) - draw_text_2d(obj_line2, 70, HEIGHT - 195) - - controls = [ - "CONTROLES:", - "W / S : Acelerar / Frenar", - "Flecha Arriba / Abajo : Cambiar altitud", - "Flecha Izquierda / Derecha : Girar / Virar", - "ESPACIO : Picado rápido para caza", - "P : Aterrizaje automático en junco cercano", - "ESC : Salir del juego" - ] - - y_pos = HEIGHT - 250 - for ctrl in controls: - is_title = ctrl.endswith(":") - col = (255, 220, 50) if is_title else (200, 200, 200) - t_surf = font_subtitle.render(ctrl, True, col) if is_title else font_text.render(ctrl, True, col) - draw_text_2d(t_surf, 70, y_pos) - y_pos -= 25 - - y_icon = 90 - - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(100, y_icon + 5) - glVertex2f(100, y_icon - 5) - glVertex2f(95, y_icon) - glVertex2f(105, y_icon) - glEnd() - txt_m = font_text.render("Mosquito", True, (255, 230, 0)) - draw_text_2d(txt_m, 120, y_icon - 8) - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(280, y_icon + 5) - glVertex2f(280, y_icon - 5) - glVertex2f(275, y_icon - 3) - glVertex2f(285, y_icon + 3) - glVertex2f(275, y_icon + 3) - glVertex2f(285, y_icon - 3) - glEnd() - txt_f = font_text.render("Mosca", True, (200, 65, 40)) - draw_text_2d(txt_f, 300, y_icon - 8) - - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex2f(450, y_icon - 10) - glVertex2f(450, y_icon + 15) - glEnd() - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - glVertex2f(450, y_icon + 8) - glVertex2f(450, y_icon + 15) - glEnd() - txt_r = font_text.render("Junco", True, (100, 220, 100)) - draw_text_2d(txt_r, 470, y_icon - 8) - - start_msg = font_subtitle.render("Pulsa INTRO o cualquier tecla para comenzar...", True, (100, 255, 100)) - draw_text_2d(start_msg, WIDTH // 2 - 200, 30) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 33) - glVertex2f(25, HEIGHT - 27) - glVertex2f(21, HEIGHT - 30) - glVertex2f(29, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 82, HEIGHT - 33) - glVertex2f(WIDTH - 82, HEIGHT - 27) - glVertex2f(WIDTH - 86, HEIGHT - 28) - glVertex2f(WIDTH - 78, HEIGHT - 32) - glVertex2f(WIDTH - 86, HEIGHT - 32) - glVertex2f(WIDTH - 78, HEIGHT - 28) - glEnd() - - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 38, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 68, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.mixer.init(frequency=22050, size=-16, channels=1) - pygame.font.init() - - font = pygame.font.SysFont("Arial", 22, bold=True) - font_title = pygame.font.SysFont("Arial", 48, bold=True) - font_subtitle = pygame.font.SysFont("Arial", 20, bold=True) - font_text = pygame.font.SysFont("Arial", 16, bold=False) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Libélula") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - # Generar efectos de sonido procedurales - buzz_sound = generate_buzz_sound() - wind_sound = generate_wind_sound() - - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - # Instanciar las 3 libélulas moradas decorativas con comportamientos distintos - purple_dragonflies = [ - PurpleDragonfly(1), - PurpleDragonfly(2), - PurpleDragonfly(3) - ] - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - in_start_screen = True - running = True - - buzz_timer = 0 - - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - wing_timer += 60.0 - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN: - if in_start_screen: - in_start_screen = False - else: - if event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - if in_start_screen: - cam_x, cam_y, cam_z = 0.0, -100.0, 30.0 - gluLookAt(cam_x, cam_y, cam_z, 0, 0, 15, 0, 0, 1) - draw_map(water_timer) - frog.draw() - for p_df in purple_dragonflies: - p_df.update() - p_df.draw() - - wing_angle = math.sin(wing_timer * 0.5) * 35.0 - draw_start_screen(font_title, font_subtitle, font_text, wing_angle) - else: - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - if wind_sound and not pygame.mixer.get_busy(): - wind_sound.play() - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - if speed > 2.0 and wind_sound and random.random() < 0.1 and not pygame.mixer.get_busy(): - wind_sound.play() - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - for p_df in purple_dragonflies: - p_df.update() - - current_mosquitos = 0 - current_flies = 0 - near_insect = False - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - dist_to_df = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_df < 40.0: - near_insect = True - - if not caught_insect: - if dist_to_df < 6.0: - caught_insect = insect - insects.remove(insect) - if dive_phase == "diving": - dive_phase = "recovering" - - # Reproducir zumbido si estamos cerca de insectos - if near_insect and buzz_sound: - buzz_timer += 1 - if buzz_timer > 25 and not pygame.mixer.get_busy(): - buzz_sound.play() - buzz_timer = 0 - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - frog.draw() - - for p_df in purple_dragonflies: - p_df.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/libelula_v9.2.py b/previos/libelula_v9.2.py deleted file mode 100644 index b3aab8e..0000000 --- a/previos/libelula_v9.2.py +++ /dev/null @@ -1,1199 +0,0 @@ -import sys -import math -import random -import struct -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula del jugador (Cian) -COLOR_PURPLE_DRAGONFLY = (0.7, 0.2, 0.8) # Libélulas decorativas (Morado) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 17, 27 -FLY_MIN, FLY_MAX = 9, 14 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# GENERADOR DE AUDIO PROCEDURAL (SIN ARCHIVOS) -# ========================================== -def generate_buzz_sound(): - try: - sample_rate = 22050 - duration = 0.3 - num_samples = int(sample_rate * duration) - buffer = bytearray() - for i in range(num_samples): - t = i / sample_rate - val = int(32767 * 0.25 * math.sin(2 * math.pi * 180 * t + math.sin(2 * math.pi * 25 * t) * 3)) - buffer.extend(struct.pack(' STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class PurpleDragonfly: - def __init__(self, behavior_type): - self.behavior = behavior_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - self.wing_timer = random.uniform(0, 100) - self.is_perched = False - self.perch_timer = 0 - - def update(self): - self.wing_timer += 60.0 - if self.is_perched: - self.perch_timer -= 1 - if self.perch_timer <= 0: - self.is_perched = False - return - - self.timer += 0.05 - if self.behavior == 1: - self.yaw += 1.2 - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.1 - self.y += math.cos(rad) * 1.1 - elif self.behavior == 2: - if random.random() < 0.02: - self.yaw += random.uniform(-40, 40) - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.3 - self.y += math.cos(rad) * 1.3 - else: - self.yaw += math.sin(self.timer * 3) * 3.5 - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.4 - self.y += math.cos(rad) * 1.4 - - self.z = 15.0 + math.sin(self.timer * 2) * 6.0 - - if random.random() < 0.0015 and REEDS: - reed = random.choice(REEDS) - self.x, self.y, self.z = reed[0], reed[1], reed[2] + reed[3] - self.is_perched = True - self.perch_timer = random.randint(300, 600) - - if not is_over_water(self.x, self.y): - self.yaw += 150.0 - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self): - glColor3f(*COLOR_PURPLE_DRAGONFLY) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - wing_angle = 0.0 if self.is_perched else math.sin(self.wing_timer) * 35.0 - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self, custom_x=None, custom_y=None, custom_z=None, scale=1.0, menu_icon=False): - glColor3f(*COLOR_FROG) - glPushMatrix() - if custom_x is not None and custom_y is not None and custom_z is not None: - glTranslatef(custom_x, custom_y, custom_z) - glScalef(scale, scale, scale) - else: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - leg_offset = 0.0 if menu_icon else math.sin(self.swim_timer) * 1.5 - - glBegin(GL_LINES) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - # Patas traseras con los pies hacia el exterior - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-6.5 - leg_offset, -3.2, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(6.5 + leg_offset, -3.2, 0.0) - glEnd() - - # Pequeño cuadrado/rectángulo central para representar el cuerpo sólido en el icono del menú - if menu_icon: - glBegin(GL_LINE_LOOP) - glVertex3f(-1.5, -1.5, 0.4) - glVertex3f(1.5, -1.5, 0.4) - glVertex3f(1.5, 1.5, 0.4) - glVertex3f(-1.5, 1.5, 0.4) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(-POND_RADIUS) + int(POND_RADIUS)*2, grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD Y PANTALLA INICIAL -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_start_screen(font_title, font_subtitle, font_text, wing_angle, frog_instance): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - # TÍTULO PRINCIPAL Y LIBÉLULA SUPERIOR - txt_title = font_title.render("LIBÉLULA", True, (0, 220, 255)) - draw_text_2d(txt_title, WIDTH // 2 - 130, HEIGHT - 100) - - glPushMatrix() - glTranslatef(WIDTH // 2 + 150, HEIGHT - 75, 0.0) - glScalef(12.0, 12.0, 1.0) - - glColor3f(*COLOR_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - - rad_w = math.radians(wing_angle) - w_off = math.sin(rad_w) * 1.5 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_off) - glVertex3f(-3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_off) - glVertex3f(3.8, 0.2, w_off) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_off) - glVertex3f(-3.0, -0.8, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_off) - glVertex3f(3.0, -0.8, w_off) - glEnd() - - glPopMatrix() - - # OBJETIVO DEL JUEGO - obj_line1 = font_text.render("Objetivo: Vuela libre por el estanque y sus arroyos capturando moscas", True, (255, 255, 255)) - obj_line2 = font_text.render("y mosquitos. Una vez cazados debes devorarlos posado en un junco.", True, (255, 255, 255)) - draw_text_2d(obj_line1, 70, HEIGHT - 170) - draw_text_2d(obj_line2, 70, HEIGHT - 195) - - # CONTROLES Y TECLAS - controls = [ - "CONTROLES:", - "W / S : Acelerar / Frenar", - "Flecha Arriba / Abajo : Cambiar altitud", - "Flecha Izquierda / Derecha : Girar / Virar", - "ESPACIO : Picado rápido para cazar", - "P : Aterrizaje automático en junco cercano", - "ESC : Salir del juego" - ] - - y_pos = HEIGHT - 250 - for ctrl in controls: - is_title = ctrl.endswith(":") - col = (255, 220, 50) if is_title else (200, 200, 200) - t_surf = font_subtitle.render(ctrl, True, col) if is_title else font_text.render(ctrl, True, col) - draw_text_2d(t_surf, 70, y_pos) - y_pos -= 25 - - # LEYENDA VISUAL INFERIOR (Mosquito, Mosca, Junco, Rana y Libélula) - y_icon = 90 - - # 1. Mosquito - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(60, y_icon + 5) - glVertex2f(60, y_icon - 5) - glVertex2f(55, y_icon) - glVertex2f(65, y_icon) - glEnd() - txt_m = font_text.render("Mosquito", True, (255, 230, 0)) - draw_text_2d(txt_m, 75, y_icon - 8) - - # 2. Mosca - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(210, y_icon + 5) - glVertex2f(210, y_icon - 5) - glVertex2f(205, y_icon - 3) - glVertex2f(215, y_icon + 3) - glVertex2f(205, y_icon + 3) - glVertex2f(215, y_icon - 3) - glEnd() - txt_f = font_text.render("Mosca", True, (200, 65, 40)) - draw_text_2d(txt_f, 225, y_icon - 8) - - # 3. Junco - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex2f(340, y_icon - 10) - glVertex2f(340, y_icon + 15) - glEnd() - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - glVertex2f(340, y_icon + 8) - glVertex2f(340, y_icon + 15) - glEnd() - txt_r = font_text.render("Junco", True, (100, 220, 100)) - draw_text_2d(txt_r, 355, y_icon - 8) - - # 4. Rana (Con su cuadradito de cuerpo sólido incorporado) - frog_instance.draw(custom_x=475, custom_y=y_icon, custom_z=0.0, scale=3.5, menu_icon=True) - txt_frog = font_text.render("Rana", True, (100, 255, 100)) - draw_text_2d(txt_frog, 505, y_icon - 8) - - # 5. Libélula (Con alas animadas) - glPushMatrix() - glTranslatef(625, y_icon, 0.0) - glScalef(6.0, 6.0, 1.0) - glColor3f(*COLOR_PURPLE_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_off) - glVertex3f(-3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_off) - glVertex3f(3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_off) - glVertex3f(-3.0, -0.8, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_off) - glVertex3f(3.0, -0.8, w_off) - glEnd() - glPopMatrix() - - txt_df = font_text.render("Libélula", True, (0, 220, 255)) - draw_text_2d(txt_df, 654, y_icon - 8) - - # MENSAJE PARA COMENZAR - start_msg = font_subtitle.render("Pulsa INTRO o cualquier tecla para comenzar...", True, (100, 255, 100)) - draw_text_2d(start_msg, WIDTH // 2 - 200, 30) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 33) - glVertex2f(25, HEIGHT - 27) - glVertex2f(21, HEIGHT - 30) - glVertex2f(29, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 82, HEIGHT - 33) - glVertex2f(WIDTH - 82, HEIGHT - 27) - glVertex2f(WIDTH - 86, HEIGHT - 28) - glVertex2f(WIDTH - 78, HEIGHT - 32) - glVertex2f(WIDTH - 86, HEIGHT - 32) - glVertex2f(WIDTH - 78, HEIGHT - 28) - glEnd() - - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 38, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 68, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.mixer.init(frequency=22050, size=-16, channels=1) - pygame.font.init() - - font = pygame.font.SysFont("Arial", 22, bold=True) - font_title = pygame.font.SysFont("Arial", 48, bold=True) - font_subtitle = pygame.font.SysFont("Arial", 20, bold=True) - font_text = pygame.font.SysFont("Arial", 16, bold=False) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Libélula") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - buzz_sound = generate_buzz_sound() - wind_sound = generate_wind_sound() - - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - purple_dragonflies = [ - PurpleDragonfly(1), - PurpleDragonfly(2), - PurpleDragonfly(3) - ] - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - in_start_screen = True - running = True - - buzz_timer = 0 - - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - wing_timer += 60.0 - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN: - if in_start_screen: - in_start_screen = False - else: - if event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - if in_start_screen: - cam_x, cam_y, cam_z = 0.0, -200.0, 50.0 - gluLookAt(cam_x, cam_y, cam_z, 0, 0, 15, 0, 0, 1) - - draw_map(water_timer) - frog.update() - frog.draw() - for p_df in purple_dragonflies: - p_df.update() - p_df.draw() - - wing_angle = math.sin(wing_timer * 0.5) * 35.0 - draw_start_screen(font_title, font_subtitle, font_text, wing_angle, frog) - else: - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - if wind_sound and not pygame.mixer.get_busy(): - wind_sound.play() - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - if speed > 2.0 and wind_sound and random.random() < 0.1 and not pygame.mixer.get_busy(): - wind_sound.play() - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - for p_df in purple_dragonflies: - p_df.update() - - current_mosquitos = 0 - current_flies = 0 - near_insect = False - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - dist_to_df = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_df < 40.0: - near_insect = True - - if not caught_insect: - if dist_to_df < 6.0: - caught_insect = insect - insects.remove(insect) - if dive_phase == "diving": - dive_phase = "recovering" - - if near_insect and buzz_sound: - buzz_timer += 1 - if buzz_timer > 25 and not pygame.mixer.get_busy(): - buzz_sound.play() - buzz_timer = 0 - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - frog.draw() - - for p_df in purple_dragonflies: - p_df.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/libelula_v9.3.py b/previos/libelula_v9.3.py deleted file mode 100644 index 4817603..0000000 --- a/previos/libelula_v9.3.py +++ /dev/null @@ -1,1199 +0,0 @@ -import sys -import math -import random -import struct -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula del jugador (Cian) -COLOR_PURPLE_DRAGONFLY = (0.7, 0.2, 0.8) # Libélulas decorativas (Morado) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 17, 27 -FLY_MIN, FLY_MAX = 9, 14 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# GENERADOR DE AUDIO PROCEDURAL (SIN ARCHIVOS) -# ========================================== -def generate_buzz_sound(): - try: - sample_rate = 22050 - duration = 0.3 - num_samples = int(sample_rate * duration) - buffer = bytearray() - for i in range(num_samples): - t = i / sample_rate - val = int(32767 * 0.25 * math.sin(2 * math.pi * 180 * t + math.sin(2 * math.pi * 25 * t) * 3)) - buffer.extend(struct.pack(' STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class PurpleDragonfly: - def __init__(self, behavior_type): - self.behavior = behavior_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - self.wing_timer = random.uniform(0, 100) - self.is_perched = False - self.perch_timer = 0 - - def update(self): - self.wing_timer += 60.0 - if self.is_perched: - self.perch_timer -= 1 - if self.perch_timer <= 0: - self.is_perched = False - return - - self.timer += 0.05 - if self.behavior == 1: - self.yaw += 1.2 - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.1 - self.y += math.cos(rad) * 1.1 - elif self.behavior == 2: - if random.random() < 0.02: - self.yaw += random.uniform(-40, 40) - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.3 - self.y += math.cos(rad) * 1.3 - else: - self.yaw += math.sin(self.timer * 3) * 3.5 - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.4 - self.y += math.cos(rad) * 1.4 - - self.z = 15.0 + math.sin(self.timer * 2) * 6.0 - - if random.random() < 0.0015 and REEDS: - reed = random.choice(REEDS) - self.x, self.y, self.z = reed[0], reed[1], reed[2] + reed[3] - self.is_perched = True - self.perch_timer = random.randint(300, 600) - - if not is_over_water(self.x, self.y): - self.yaw += 150.0 - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self): - glColor3f(*COLOR_PURPLE_DRAGONFLY) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - wing_angle = 0.0 if self.is_perched else math.sin(self.wing_timer) * 35.0 - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self, custom_x=None, custom_y=None, custom_z=None, scale=1.0, menu_icon=False): - glColor3f(*COLOR_FROG) - glPushMatrix() - if custom_x is not None and custom_y is not None and custom_z is not None: - glTranslatef(custom_x, custom_y, custom_z) - glScalef(scale, scale, scale) - else: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - leg_offset = 0.0 if menu_icon else math.sin(self.swim_timer) * 1.5 - - glBegin(GL_LINES) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - # Patas traseras con los pies hacia el exterior - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-6.5 - leg_offset, -3.2, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(6.5 + leg_offset, -3.2, 0.0) - glEnd() - - # Pequeño cuadrado/rectángulo central para representar el cuerpo sólido en el icono del menú - if menu_icon: - glBegin(GL_LINE_LOOP) - glVertex3f(-1.5, -1.5, 0.4) - glVertex3f(1.5, -1.5, 0.4) - glVertex3f(1.5, 1.5, 0.4) - glVertex3f(-1.5, 1.5, 0.4) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(-POND_RADIUS) + int(POND_RADIUS)*2, grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD Y PANTALLA INICIAL -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_start_screen(font_title, font_subtitle, font_text, wing_angle, frog_instance): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - # TÍTULO PRINCIPAL Y LIBÉLULA SUPERIOR - txt_title = font_title.render("LIBÉLULA", True, (0, 220, 255)) - draw_text_2d(txt_title, WIDTH // 2 - 130, HEIGHT - 100) - - glPushMatrix() - glTranslatef(WIDTH // 2 + 150, HEIGHT - 75, 0.0) - glScalef(12.0, 12.0, 1.0) - - glColor3f(*COLOR_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - - rad_w = math.radians(wing_angle) - w_off = math.sin(rad_w) * 1.5 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_off) - glVertex3f(-3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_off) - glVertex3f(3.8, 0.2, w_off) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_off) - glVertex3f(-3.0, -0.8, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_off) - glVertex3f(3.0, -0.8, w_off) - glEnd() - - glPopMatrix() - - # OBJETIVO DEL JUEGO - obj_line1 = font_text.render("Objetivo: Vuela libre por el estanque y sus arroyos capturando moscas", True, (255, 255, 255)) - obj_line2 = font_text.render("y mosquitos. Una vez cazados debes devorarlos posado en un junco.", True, (255, 255, 255)) - draw_text_2d(obj_line1, 70, HEIGHT - 170) - draw_text_2d(obj_line2, 70, HEIGHT - 195) - - # CONTROLES Y TECLAS - controls = [ - "CONTROLES:", - "W / S : Acelerar / Frenar", - "Flecha Arriba / Abajo : Cambiar altitud", - "Flecha Izquierda / Derecha : Girar / Virar", - "ESPACIO : Picado rápido para cazar", - "P : Aterrizaje automático en junco cercano", - "ESC : Salir del juego" - ] - - y_pos = HEIGHT - 250 - for ctrl in controls: - is_title = ctrl.endswith(":") - col = (255, 220, 50) if is_title else (200, 200, 200) - t_surf = font_subtitle.render(ctrl, True, col) if is_title else font_text.render(ctrl, True, col) - draw_text_2d(t_surf, 70, y_pos) - y_pos -= 25 - - # LEYENDA VISUAL INFERIOR (Mosquito, Mosca, Junco, Rana y Libélula) - y_icon = 90 - - # 1. Mosquito - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(60, y_icon + 5) - glVertex2f(60, y_icon - 5) - glVertex2f(55, y_icon) - glVertex2f(65, y_icon) - glEnd() - txt_m = font_text.render("Mosquito", True, (255, 230, 0)) - draw_text_2d(txt_m, 75, y_icon - 8) - - # 2. Mosca - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(210, y_icon + 5) - glVertex2f(210, y_icon - 5) - glVertex2f(205, y_icon - 3) - glVertex2f(215, y_icon + 3) - glVertex2f(205, y_icon + 3) - glVertex2f(215, y_icon - 3) - glEnd() - txt_f = font_text.render("Mosca", True, (200, 65, 40)) - draw_text_2d(txt_f, 225, y_icon - 8) - - # 3. Junco - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex2f(340, y_icon - 10) - glVertex2f(340, y_icon + 15) - glEnd() - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - glVertex2f(340, y_icon + 8) - glVertex2f(340, y_icon + 15) - glEnd() - txt_r = font_text.render("Junco", True, (100, 220, 100)) - draw_text_2d(txt_r, 355, y_icon - 8) - - # 4. Rana (Con su cuadradito de cuerpo sólido incorporado) - frog_instance.draw(custom_x=475, custom_y=y_icon, custom_z=0.0, scale=3.5, menu_icon=True) - txt_frog = font_text.render("Rana", True, (100, 255, 100)) - draw_text_2d(txt_frog, 505, y_icon - 8) - - # 5. Libélula (Con alas animadas) - glPushMatrix() - glTranslatef(625, y_icon, 0.0) - glScalef(6.0, 6.0, 1.0) - glColor3f(*COLOR_PURPLE_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_off) - glVertex3f(-3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_off) - glVertex3f(3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_off) - glVertex3f(-3.0, -0.8, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_off) - glVertex3f(3.0, -0.8, w_off) - glEnd() - glPopMatrix() - - txt_df = font_text.render("Libélulas", True, (178, 51, 204)) - draw_text_2d(txt_df, 654, y_icon - 8) - - # MENSAJE PARA COMENZAR - start_msg = font_subtitle.render("Pulsa INTRO o cualquier tecla para comenzar...", True, (100, 255, 100)) - draw_text_2d(start_msg, WIDTH // 2 - 200, 30) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 33) - glVertex2f(25, HEIGHT - 27) - glVertex2f(21, HEIGHT - 30) - glVertex2f(29, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 82, HEIGHT - 33) - glVertex2f(WIDTH - 82, HEIGHT - 27) - glVertex2f(WIDTH - 86, HEIGHT - 28) - glVertex2f(WIDTH - 78, HEIGHT - 32) - glVertex2f(WIDTH - 86, HEIGHT - 32) - glVertex2f(WIDTH - 78, HEIGHT - 28) - glEnd() - - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 38, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 68, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.mixer.init(frequency=22050, size=-16, channels=1) - pygame.font.init() - - font = pygame.font.SysFont("Arial", 22, bold=True) - font_title = pygame.font.SysFont("Arial", 48, bold=True) - font_subtitle = pygame.font.SysFont("Arial", 20, bold=True) - font_text = pygame.font.SysFont("Arial", 16, bold=False) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Libélula") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - buzz_sound = generate_buzz_sound() - wind_sound = generate_wind_sound() - - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - purple_dragonflies = [ - PurpleDragonfly(1), - PurpleDragonfly(2), - PurpleDragonfly(3) - ] - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - in_start_screen = True - running = True - - buzz_timer = 0 - - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - wing_timer += 60.0 - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN: - if in_start_screen: - in_start_screen = False - else: - if event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - if in_start_screen: - cam_x, cam_y, cam_z = 0.0, -200.0, 50.0 - gluLookAt(cam_x, cam_y, cam_z, 0, 0, 15, 0, 0, 1) - - draw_map(water_timer) - frog.update() - frog.draw() - for p_df in purple_dragonflies: - p_df.update() - p_df.draw() - - wing_angle = math.sin(wing_timer * 0.5) * 35.0 - draw_start_screen(font_title, font_subtitle, font_text, wing_angle, frog) - else: - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - if wind_sound and not pygame.mixer.get_busy(): - wind_sound.play() - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - if speed > 2.0 and wind_sound and random.random() < 0.1 and not pygame.mixer.get_busy(): - wind_sound.play() - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - for p_df in purple_dragonflies: - p_df.update() - - current_mosquitos = 0 - current_flies = 0 - near_insect = False - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - dist_to_df = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_df < 40.0: - near_insect = True - - if not caught_insect: - if dist_to_df < 6.0: - caught_insect = insect - insects.remove(insect) - if dive_phase == "diving": - dive_phase = "recovering" - - if near_insect and buzz_sound: - buzz_timer += 1 - if buzz_timer > 25 and not pygame.mixer.get_busy(): - buzz_sound.play() - buzz_timer = 0 - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - frog.draw() - - for p_df in purple_dragonflies: - p_df.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/libelula_v9.4.py b/previos/libelula_v9.4.py deleted file mode 100644 index 3398d7d..0000000 --- a/previos/libelula_v9.4.py +++ /dev/null @@ -1,1204 +0,0 @@ -import sys -import math -import random -import struct -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula del jugador (Cian) -COLOR_PURPLE_DRAGONFLY = (0.7, 0.2, 0.8) # Libélulas decorativas (Morado) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 17, 27 -FLY_MIN, FLY_MAX = 9, 14 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# GENERADOR DE AUDIO PROCEDURAL (SIN ARCHIVOS) -# ========================================== -def generate_buzz_sound(): - try: - sample_rate = 22050 - duration = 0.3 - num_samples = int(sample_rate * duration) - buffer = bytearray() - for i in range(num_samples): - t = i / sample_rate - val = int(32767 * 0.25 * math.sin(2 * math.pi * 180 * t + math.sin(2 * math.pi * 25 * t) * 3)) - buffer.extend(struct.pack(' STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class PurpleDragonfly: - def __init__(self, behavior_type): - self.behavior = behavior_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - self.wing_timer = random.uniform(0, 100) - self.is_perched = False - self.perch_timer = 0 - - def update(self): - self.wing_timer += 60.0 - if self.is_perched: - self.perch_timer -= 1 - if self.perch_timer <= 0: - self.is_perched = False - return - - self.timer += 0.05 - if self.behavior == 1: - self.yaw += 1.2 - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.1 - self.y += math.cos(rad) * 1.1 - elif self.behavior == 2: - if random.random() < 0.02: - self.yaw += random.uniform(-40, 40) - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.3 - self.y += math.cos(rad) * 1.3 - else: - self.yaw += math.sin(self.timer * 3) * 3.5 - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.4 - self.y += math.cos(rad) * 1.4 - - self.z = 15.0 + math.sin(self.timer * 2) * 6.0 - - if random.random() < 0.0015 and REEDS: - reed = random.choice(REEDS) - self.x, self.y, self.z = reed[0], reed[1], reed[2] + reed[3] - self.is_perched = True - self.perch_timer = random.randint(300, 600) - - if not is_over_water(self.x, self.y): - self.yaw += 150.0 - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self): - glColor3f(*COLOR_PURPLE_DRAGONFLY) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - wing_angle = 0.0 if self.is_perched else math.sin(self.wing_timer) * 35.0 - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self, custom_x=None, custom_y=None, custom_z=None, scale=1.0, menu_icon=False): - glColor3f(*COLOR_FROG) - glPushMatrix() - if custom_x is not None and custom_y is not None and custom_z is not None: - glTranslatef(custom_x, custom_y, custom_z) - glScalef(scale, scale, scale) - else: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - leg_offset = 0.0 if menu_icon else math.sin(self.swim_timer) * 1.5 - - glBegin(GL_LINES) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - # Patas traseras con los pies hacia el exterior - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-6.5 - leg_offset, -3.2, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(6.5 + leg_offset, -3.2, 0.0) - glEnd() - - if menu_icon: - glBegin(GL_LINE_LOOP) - glVertex3f(-1.5, -1.5, 0.4) - glVertex3f(1.5, -1.5, 0.4) - glVertex3f(1.5, 1.5, 0.4) - glVertex3f(-1.5, 1.5, 0.4) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(-POND_RADIUS) + int(POND_RADIUS)*2, grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD Y PANTALLA INICIAL -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_start_screen(font_title, font_subtitle, font_text, wing_angle, menu_wing_angle, frog_instance): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - # TÍTULO PRINCIPAL - txt_title = font_title.render("LIBÉLULA", True, (0, 220, 255)) - draw_text_2d(txt_title, WIDTH // 2 - 130, HEIGHT - 100) - - # LIBÉLULA SUPERIOR AL LADO DEL TÍTULO (CON AMPLITUD DE ALAS AMPLIADA) - glPushMatrix() - glTranslatef(WIDTH // 2 + 150, HEIGHT - 75, 0.0) - glScalef(12.0, 12.0, 1.0) - - glColor3f(*COLOR_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - - # Amplitud mayor amplificada para que se aprecie con claridad el aleteo - rad_menu_w = math.radians(menu_wing_angle) - menu_w_off = math.sin(rad_menu_w) * 3.5 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, menu_w_off) - glVertex3f(-3.8, 0.2, menu_w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, menu_w_off) - glVertex3f(3.8, 0.2, menu_w_off) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, menu_w_off) - glVertex3f(-3.0, -0.8, menu_w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, menu_w_off) - glVertex3f(3.0, -0.8, menu_w_off) - glEnd() - - glPopMatrix() - - # OBJETIVO DEL JUEGO - obj_line1 = font_text.render("Objetivo: Vuela libre por el estanque y sus arroyos capturando moscas", True, (255, 255, 255)) - obj_line2 = font_text.render("y mosquitos. Una vez cazados debes devorarlos posado en un junco.", True, (255, 255, 255)) - draw_text_2d(obj_line1, 70, HEIGHT - 170) - draw_text_2d(obj_line2, 70, HEIGHT - 195) - - # CONTROLES Y TECLAS - controls = [ - "CONTROLES:", - "W / S : Acelerar / Frenar", - "Flecha Arriba / Abajo : Cambiar altitud", - "Flecha Izquierda / Derecha : Girar / Virar", - "ESPACIO : Picado rápido para caza", - "P : Aterrizaje automático en junco cercano", - "ESC : Salir del juego" - ] - - y_pos = HEIGHT - 250 - for ctrl in controls: - is_title = ctrl.endswith(":") - col = (255, 220, 50) if is_title else (200, 200, 200) - t_surf = font_subtitle.render(ctrl, True, col) if is_title else font_text.render(ctrl, True, col) - draw_text_2d(t_surf, 70, y_pos) - y_pos -= 25 - - # LEYENDA VISUAL INFERIOR - y_icon = 90 - - # 1. Mosquito - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(60, y_icon + 5) - glVertex2f(60, y_icon - 5) - glVertex2f(55, y_icon) - glVertex2f(65, y_icon) - glEnd() - txt_m = font_text.render("Mosquito", True, (255, 230, 0)) - draw_text_2d(txt_m, 75, y_icon - 8) - - # 2. Mosca - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(210, y_icon + 5) - glVertex2f(210, y_icon - 5) - glVertex2f(205, y_icon - 3) - glVertex2f(215, y_icon + 3) - glVertex2f(205, y_icon + 3) - glVertex2f(215, y_icon - 3) - glEnd() - txt_f = font_text.render("Mosca", True, (200, 65, 40)) - draw_text_2d(txt_f, 225, y_icon - 8) - - # 3. Junco - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex2f(340, y_icon - 10) - glVertex2f(340, y_icon + 15) - glEnd() - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - glVertex2f(340, y_icon + 8) - glVertex2f(340, y_icon + 15) - glEnd() - txt_r = font_text.render("Junco", True, (100, 220, 100)) - draw_text_2d(txt_r, 355, y_icon - 8) - - # 4. Rana - frog_instance.draw(custom_x=475, custom_y=y_icon, custom_z=0.0, scale=3.5, menu_icon=True) - txt_frog = font_text.render("Rana", True, (100, 255, 100)) - draw_text_2d(txt_frog, 505, y_icon - 8) - - # 5. Libélulas (Moradas) - rad_w = math.radians(wing_angle) - w_off = math.sin(rad_w) * 1.5 - - glPushMatrix() - glTranslatef(625, y_icon, 0.0) - glScalef(6.0, 6.0, 1.0) - glColor3f(*COLOR_PURPLE_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_off) - glVertex3f(-3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_off) - glVertex3f(3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_off) - glVertex3f(-3.0, -0.8, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_off) - glVertex3f(3.0, -0.8, w_off) - glEnd() - glPopMatrix() - - txt_df = font_text.render("Libélulas", True, (178, 51, 204)) - draw_text_2d(txt_df, 654, y_icon - 8) - - # MENSAJE PARA COMENZAR - start_msg = font_subtitle.render("Pulsa INTRO o cualquier tecla para comenzar...", True, (100, 255, 100)) - draw_text_2d(start_msg, WIDTH // 2 - 200, 30) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 33) - glVertex2f(25, HEIGHT - 27) - glVertex2f(21, HEIGHT - 30) - glVertex2f(29, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 82, HEIGHT - 33) - glVertex2f(WIDTH - 82, HEIGHT - 27) - glVertex2f(WIDTH - 86, HEIGHT - 28) - glVertex2f(WIDTH - 78, HEIGHT - 32) - glVertex2f(WIDTH - 86, HEIGHT - 32) - glVertex2f(WIDTH - 78, HEIGHT - 28) - glEnd() - - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 38, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 68, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.mixer.init(frequency=22050, size=-16, channels=1) - pygame.font.init() - - font = pygame.font.SysFont("Arial", 22, bold=True) - font_title = pygame.font.SysFont("Arial", 48, bold=True) - font_subtitle = pygame.font.SysFont("Arial", 20, bold=True) - font_text = pygame.font.SysFont("Arial", 16, bold=False) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Libélula") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - buzz_sound = generate_buzz_sound() - wind_sound = generate_wind_sound() - - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - purple_dragonflies = [ - PurpleDragonfly(1), - PurpleDragonfly(2), - PurpleDragonfly(3) - ] - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - in_start_screen = True - running = True - - buzz_timer = 0 - - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - wing_timer += 60.0 - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN: - if in_start_screen: - in_start_screen = False - else: - if event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - if in_start_screen: - cam_x, cam_y, cam_z = 0.0, -200.0, 50.0 - gluLookAt(cam_x, cam_y, cam_z, 0, 0, 15, 0, 0, 1) - - draw_map(water_timer) - frog.update() - frog.draw() - for p_df in purple_dragonflies: - p_df.update() - p_df.draw() - - wing_angle = math.sin(wing_timer * 0.5) * 35.0 - menu_wing_angle = math.sin(wing_timer * 0.8) * 45.0 - draw_start_screen(font_title, font_subtitle, font_text, wing_angle, menu_wing_angle, frog) - else: - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - if wind_sound and not pygame.mixer.get_busy(): - wind_sound.play() - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - if speed > 2.0 and wind_sound and random.random() < 0.1 and not pygame.mixer.get_busy(): - wind_sound.play() - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - for p_df in purple_dragonflies: - p_df.update() - - current_mosquitos = 0 - current_flies = 0 - near_insect = False - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - dist_to_df = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_df < 40.0: - near_insect = True - - if not caught_insect: - if dist_to_df < 6.0: - caught_insect = insect - insects.remove(insect) - if dive_phase == "diving": - dive_phase = "recovering" - - if near_insect and buzz_sound: - buzz_timer += 1 - if buzz_timer > 25 and not pygame.mixer.get_busy(): - buzz_sound.play() - buzz_timer = 0 - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - frog.draw() - - for p_df in purple_dragonflies: - p_df.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main() diff --git a/previos/libelula_v9.5.py b/previos/libelula_v9.5.py deleted file mode 100644 index 24a09b5..0000000 --- a/previos/libelula_v9.5.py +++ /dev/null @@ -1,1204 +0,0 @@ -import sys -import math -import random -import struct -import pygame -from pygame.locals import * -from OpenGL.GL import * -from OpenGL.GLU import * - -# ========================================== -# CONFIGURACIÓN Y CONSTANTES -# ========================================== -WIDTH, HEIGHT = 800, 600 - -# Colores (R, G, B) -COLOR_BG = (0.12, 0.15, 0.12) # Fondo ambiente -COLOR_WATER = (0.0, 0.5, 0.85) # Agua animada (Cian/Azul) -COLOR_LAND = (0.45, 0.32, 0.15) # Tierra / Relieve -COLOR_LAND_RIVER = (0.40, 0.28, 0.12) # Tierra en riachuelos -COLOR_ISLAND = (0.5, 0.38, 0.18) # Islas -COLOR_REED = (0.15, 0.85, 0.25) # Juncos verdes -COLOR_CATTAIL = (0.35, 0.18, 0.08) # Espigas marrones densas -COLOR_DRAGONFLY = (0.0, 1.0, 1.0) # Libélula del jugador (Cian) -COLOR_PURPLE_DRAGONFLY = (0.7, 0.2, 0.8) # Libélulas decorativas (Morado) -COLOR_MOSQUITO = (1.0, 0.9, 0.0) # Mosquitos (Amarillo) -COLOR_FLY = (0.8, 0.25, 0.15) # Moscas (Rojo-Marrón) -COLOR_FROG = (0.15, 0.85, 0.35) # Rana verde detallada -COLOR_HUD = (1.0, 1.0, 1.0) # Retícula HUD (Blanco) - -# Dimensiones del Mundo -POND_RADIUS = 1800.0 -STREAM_LEN = 900.0 -STREAM_MIN_Y = -(POND_RADIUS + STREAM_LEN) -STREAM_MAX_Y = (POND_RADIUS + STREAM_LEN) -WORLD_LEN = STREAM_MAX_Y - STREAM_MIN_Y - -# Cantidades de insectos -MOSQUITO_MIN, MOSQUITO_MAX = 17, 27 -FLY_MIN, FLY_MAX = 9, 14 - -# ========================================== -# ELEMENTOS DEL MAPA -# ========================================== -REEDS = [ - (-250, 350, 0, 20), (-160, -500, 0, 22), (500, 200, 0, 24), (-700, 200, 0, 21), (200, -800, 0, 23), - (-900, -300, 0, 25), (760, -600, 0, 22), (900, 500, 0, 26), (-500, 900, 0, 23), (600, -1000, 0, 22), - (-1200, 400, 0, 24), (1300, -300, 0, 20), (-1400, -700, 0, 25), (1100, 1200, 0, 23), (-360, -1300, 0, 22), - (-90, -1950, 0, 20), (-110, -2150, 0, 23), (-80, -2350, 0, 19), (-100, -2550, 0, 22), - (90, 1950, 0, 22), (110, 2150, 0, 20), (85, 2350, 0, 24), (105, 2550, 0, 21) -] - -ISLANDS = [ - (-250, 350, 90), (-160, -500, 85), (500, 200, 100), (-700, 200, 110), (200, -800, 95), - (-900, -300, 120), (760, -600, 90), (900, 500, 105), (-500, 900, 115), (600, -1000, 100) -] - -def get_river_center(y): - if y < -POND_RADIUS: - dy = y + POND_RADIUS - return -50 + 100 * math.sin(dy * 0.008) - elif y > POND_RADIUS: - dy = y - POND_RADIUS - return 50 + 100 * math.sin(dy * 0.008) - else: - return (y / POND_RADIUS) * 50.0 - -def is_over_water(x, y): - dist_center = math.hypot(x, y) - if dist_center < POND_RADIUS - 50: - return True - if abs(y) >= POND_RADIUS: - cx = get_river_center(y) - if abs(x - cx) < 80: - return True - return False - -def get_random_water_position(): - while True: - if random.random() < 0.7: - rad = random.uniform(0, math.pi * 2) - r = random.uniform(100, POND_RADIUS - 150) - x = r * math.cos(rad) - y = r * math.sin(rad) - else: - y = random.choice([random.uniform(POND_RADIUS, STREAM_MAX_Y), random.uniform(STREAM_MIN_Y, -POND_RADIUS)]) - cx = get_river_center(y) - x = cx + random.uniform(-60, 60) - - if is_over_water(x, y): - z = random.uniform(5.0, 35.0) - return x, y, z - -# ========================================== -# GENERADOR DE AUDIO PROCEDURAL (SIN ARCHIVOS) -# ========================================== -def generate_buzz_sound(): - try: - sample_rate = 22050 - duration = 0.3 - num_samples = int(sample_rate * duration) - buffer = bytearray() - for i in range(num_samples): - t = i / sample_rate - val = int(32767 * 0.25 * math.sin(2 * math.pi * 180 * t + math.sin(2 * math.pi * 25 * t) * 3)) - buffer.extend(struct.pack(' STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self, attached=False): - if self.type == "mosquito": - glColor3f(*COLOR_MOSQUITO) - else: - glColor3f(*COLOR_FLY) - - glPushMatrix() - if not attached: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - s = self.size - glBegin(GL_LINES) - glVertex3f(0, s, 0) - glVertex3f(0, -s, 0) - w_offset = 0.0 if attached else math.sin(self.timer * 30) * (s * 0.8) - glVertex3f(0, 0, 0) - glVertex3f(-s * 1.5, 0, w_offset) - glVertex3f(0, 0, 0) - glVertex3f(s * 1.5, 0, w_offset) - glEnd() - - glPopMatrix() - -class PurpleDragonfly: - def __init__(self, behavior_type): - self.behavior = behavior_type - self.x, self.y, self.z = get_random_water_position() - self.yaw = random.uniform(0, 360) - self.timer = random.uniform(0, 100) - self.wing_timer = random.uniform(0, 100) - self.is_perched = False - self.perch_timer = 0 - - def update(self): - self.wing_timer += 60.0 - if self.is_perched: - self.perch_timer -= 1 - if self.perch_timer <= 0: - self.is_perched = False - return - - self.timer += 0.05 - if self.behavior == 1: - self.yaw += 1.2 - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.1 - self.y += math.cos(rad) * 1.1 - elif self.behavior == 2: - if random.random() < 0.02: - self.yaw += random.uniform(-40, 40) - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.3 - self.y += math.cos(rad) * 1.3 - else: - self.yaw += math.sin(self.timer * 3) * 3.5 - rad = math.radians(self.yaw) - self.x += -math.sin(rad) * 1.4 - self.y += math.cos(rad) * 1.4 - - self.z = 15.0 + math.sin(self.timer * 2) * 6.0 - - if random.random() < 0.0015 and REEDS: - reed = random.choice(REEDS) - self.x, self.y, self.z = reed[0], reed[1], reed[2] + reed[3] - self.is_perched = True - self.perch_timer = random.randint(300, 600) - - if not is_over_water(self.x, self.y): - self.yaw += 150.0 - - if self.y > STREAM_MAX_Y: - self.y -= WORLD_LEN - elif self.y < STREAM_MIN_Y: - self.y += WORLD_LEN - - def draw(self): - glColor3f(*COLOR_PURPLE_DRAGONFLY) - glPushMatrix() - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - wing_angle = 0.0 if self.is_perched else math.sin(self.wing_timer) * 35.0 - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - glPopMatrix() - -class Frog: - def __init__(self): - self.pick_new_destination() - self.x, self.y = self.target_x, self.target_y - self.z = 0.6 - self.speed = 0.35 - self.yaw = 0.0 - self.swim_timer = 0.0 - - def pick_new_destination(self): - destinations = [(rx, ry) for rx, ry, _, _ in REEDS] + [(ix, iy) for ix, iy, _ in ISLANDS] - dest = random.choice(destinations) - self.target_x, self.target_y = dest[0] + random.uniform(-30, 30), dest[1] + random.uniform(-30, 30) - - def update(self): - self.swim_timer += 0.15 - dx = self.target_x - self.x - dy = self.target_y - self.y - dist = math.hypot(dx, dy) - - if dist < 6.0: - self.pick_new_destination() - else: - self.yaw = math.degrees(math.atan2(dy, dx)) - 90.0 - self.x += (dx / dist) * self.speed - self.y += (dy / dist) * self.speed - - def draw(self, custom_x=None, custom_y=None, custom_z=None, scale=1.0, menu_icon=False): - glColor3f(*COLOR_FROG) - glPushMatrix() - if custom_x is not None and custom_y is not None and custom_z is not None: - glTranslatef(custom_x, custom_y, custom_z) - glScalef(scale, scale, scale) - else: - glTranslatef(self.x, self.y, self.z) - glRotatef(self.yaw, 0, 0, 1) - - leg_offset = 0.0 if menu_icon else math.sin(self.swim_timer) * 1.5 - - glBegin(GL_LINES) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(0.0, -5.0, 0.5) - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(2.5, -2.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(0.0, 0.0, 0.5) - - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(-2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - glVertex3f(2.0, 2.0, 0.6) - glVertex3f(0.0, 4.5, 0.6) - - glVertex3f(-1.5, 3.5, 1.5) - glVertex3f(-1.5, 3.5, 0.6) - glVertex3f(1.5, 3.5, 1.5) - glVertex3f(1.5, 3.5, 0.6) - - glVertex3f(-2.0, 2.0, 0.5) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-4.0, 3.0, 0.0) - glVertex3f(-3.5, 4.0, 0.0) - - glVertex3f(2.0, 2.0, 0.5) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(4.0, 3.0, 0.0) - glVertex3f(3.5, 4.0, 0.0) - - # Patas traseras con los pies hacia el exterior - glVertex3f(-2.5, -2.0, 0.5) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-5.0 - leg_offset, -4.0, 0.0) - glVertex3f(-6.5 - leg_offset, -3.2, 0.0) - - glVertex3f(2.5, -2.0, 0.5) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(5.0 + leg_offset, -4.0, 0.0) - glVertex3f(6.5 + leg_offset, -3.2, 0.0) - glEnd() - - # Cuadrado vacío central simulando el cuerpo sólido con el borde del color de las patas - if menu_icon: - glBegin(GL_LINE_LOOP) - glVertex3f(-2.0, -2.5, 0.4) - glVertex3f(2.0, -2.5, 0.4) - glVertex3f(2.0, 1.5, 0.4) - glVertex3f(-2.0, 1.5, 0.4) - glEnd() - - glPopMatrix() - -# ========================================== -# RENDERIZADO DE ESCENARIO Y MODELOS -# ========================================== -def draw_water_surface(time_counter): - glColor3f(*COLOR_WATER) - - grid_size = 15 - for x in range(int(-POND_RADIUS), int(POND_RADIUS), grid_size * 2): - glBegin(GL_LINE_STRIP) - for y in range(int(-POND_RADIUS), int(-POND_RADIUS) + int(POND_RADIUS)*2, grid_size): - dist = math.hypot(x, y) - if dist < POND_RADIUS - 30: - z = 1.0 * math.sin(dist * 0.015 - time_counter * 3.0) + 0.5 * math.cos(x * 0.01 + time_counter) - glVertex3f(x, y, z) - glEnd() - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 15): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - glBegin(GL_LINE_STRIP) - for dx in range(-100, 110, 10): - x = cx + dx - z = 0.5 * math.sin(y * 0.03 + dx * 0.08 + time_counter * 4.0) - glVertex3f(x, y, z) - glEnd() - -def draw_terrain(): - glColor3f(*COLOR_LAND) - - grid_step = 120 - for y in range(int(STREAM_MIN_Y - 300), int(STREAM_MAX_Y + 300), grid_step): - glBegin(GL_LINE_STRIP) - for x in range(-2500, 2500, grid_step): - z = 1.5 * math.sin(x * 0.005) + 1.5 * math.cos(y * 0.005) - glVertex3f(x, y, z) - glEnd() - - width_river = 100.0 - margin_land = 70.0 - glColor3f(*COLOR_LAND_RIVER) - - for y in range(int(STREAM_MIN_Y), int(STREAM_MAX_Y), 30): - if abs(y) < POND_RADIUS: - continue - cx = get_river_center(y) - - glBegin(GL_LINE_STRIP) - for dx in range(int(-width_river - margin_land), int(-width_river), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - glBegin(GL_LINE_STRIP) - for dx in range(int(width_river), int(width_river + margin_land), 15): - x = cx + dx - z = 0.6 * math.sin(y * 0.02 + x * 0.02) + 0.3 - glVertex3f(x, y, z) - glEnd() - - for ix, iy, ir in ISLANDS: - glColor3f(*COLOR_ISLAND) - for layer in range(1, 4): - rad_layer = ir * (1 - layer * 0.25) - height = layer * 1.2 - glBegin(GL_LINE_LOOP) - for i in range(16): - angle = 2.0 * math.pi * i / 16 - dx = rad_layer * math.cos(angle) - dy = rad_layer * math.sin(angle) - glVertex3f(ix + dx, iy + dy, height) - glEnd() - -def draw_map(time_counter): - draw_terrain() - draw_water_surface(time_counter) - - offsets = [(-1.5, 0.6), (1.3, -0.6), (-0.7, -1.4), (1.1, 1.2), (0.0, 1.6), (-1.2, 0.0)] - - for rx, ry, rz, rh in REEDS: - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex3f(rx, ry, rz) - glVertex3f(rx, ry, rz + rh) - for ox, oy in offsets: - glVertex3f(rx, ry, rz) - glVertex3f(rx + ox, ry + oy, rz + rh * 0.85) - glEnd() - - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - for d in [-0.2, 0.0, 0.2]: - glVertex3f(rx + d, ry, rz + rh - 4.5) - glVertex3f(rx + d, ry, rz + rh) - for ox, oy in offsets: - for d in [-0.15, 0.15]: - glVertex3f(rx + ox + d, ry + oy, (rz + rh * 0.85) - 3.8) - glVertex3f(rx + ox + d, ry + oy, rz + rh * 0.85) - glEnd() - -def draw_dragonfly(wing_angle, caught_insect=None): - glColor3f(*COLOR_DRAGONFLY) - - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glVertex3f(-0.3, -0.6, 0) - glVertex3f(0.3, -0.6, 0) - - glVertex3f(-0.3, 0.5, 0) - glVertex3f(-0.2, 0.2, -0.6) - glVertex3f(0.3, 0.5, 0) - glVertex3f(0.2, 0.2, -0.6) - glVertex3f(-0.2, -0.2, 0) - glVertex3f(-0.1, -0.3, -0.6) - glVertex3f(0.2, -0.2, 0) - glVertex3f(0.1, -0.3, -0.6) - glEnd() - - glBegin(GL_LINE_LOOP) - for i in range(8): - angle = 2.0 * math.pi * i / 8 - glVertex3f(0.3 * math.cos(angle), 1.4 + 0.3 * math.sin(angle), 0) - glEnd() - - rad_wing = math.radians(wing_angle) - w_z = math.sin(rad_wing) * 1.2 if wing_angle != 0 else 0.0 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_z) - glVertex3f(-3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_z) - glVertex3f(3.8, 0.2, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_z) - glVertex3f(-3.0, -0.8, w_z) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_z) - glVertex3f(3.0, -0.8, w_z) - glEnd() - - if caught_insect: - glPushMatrix() - glTranslatef(0.0, 0.2, -0.6) - caught_insect.draw(attached=True) - glPopMatrix() - -# ========================================== -# INTERFAZ 2D / HUD Y PANTALLA INICIAL -# ========================================== -def draw_text_2d(text_surface, x, y): - text_data = pygame.image.tostring(text_surface, "RGBA", True) - w, h = text_surface.get_size() - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - glRasterPos2i(x, y) - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, text_data) - glDisable(GL_BLEND) - -def draw_start_screen(font_title, font_subtitle, font_text, wing_angle, menu_wing_angle, frog_instance): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - # TÍTULO PRINCIPAL - txt_title = font_title.render("LIBÉLULA", True, (0, 220, 255)) - draw_text_2d(txt_title, WIDTH // 2 - 130, HEIGHT - 100) - - # LIBÉLULA SUPERIOR - glPushMatrix() - glTranslatef(WIDTH // 2 + 150, HEIGHT - 75, 0.0) - glScalef(12.0, 12.0, 1.0) - - glColor3f(*COLOR_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - - rad_menu_w = math.radians(menu_wing_angle) - menu_w_off = math.sin(rad_menu_w) * 3.5 - - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, menu_w_off) - glVertex3f(-3.8, 0.2, menu_w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, menu_w_off) - glVertex3f(3.8, 0.2, menu_w_off) - glEnd() - - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, menu_w_off) - glVertex3f(-3.0, -0.8, menu_w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, menu_w_off) - glVertex3f(3.0, -0.8, menu_w_off) - glEnd() - - glPopMatrix() - - # OBJETIVO DEL JUEGO - obj_line1 = font_text.render("Objetivo: Vuela libre por el estanque y sus arroyos capturando moscas", True, (255, 255, 255)) - obj_line2 = font_text.render("y mosquitos. Una vez cazados debes devorarlos posado en un junco.", True, (255, 255, 255)) - draw_text_2d(obj_line1, 70, HEIGHT - 170) - draw_text_2d(obj_line2, 70, HEIGHT - 195) - - # CONTROLES Y TECLAS - controls = [ - "CONTROLES:", - "W / S : Acelerar / Frenar", - "Flecha Arriba / Abajo : Cambiar altitud", - "Flecha Izquierda / Derecha : Girar / Virar", - "ESPACIO : Picado rápido para cazar", - "P : Aterrizaje automático en junco cercano", - "ESC : Salir del juego" - ] - - y_pos = HEIGHT - 250 - for ctrl in controls: - is_title = ctrl.endswith(":") - col = (255, 220, 50) if is_title else (200, 200, 200) - t_surf = font_subtitle.render(ctrl, True, col) if is_title else font_text.render(ctrl, True, col) - draw_text_2d(t_surf, 70, y_pos) - y_pos -= 25 - - # LEYENDA VISUAL INFERIOR - y_icon = 90 - - # 1. Mosquito - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(60, y_icon + 5) - glVertex2f(60, y_icon - 5) - glVertex2f(55, y_icon) - glVertex2f(65, y_icon) - glEnd() - txt_m = font_text.render("Mosquito", True, (255, 230, 0)) - draw_text_2d(txt_m, 75, y_icon - 8) - - # 2. Mosca - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(210, y_icon + 5) - glVertex2f(210, y_icon - 5) - glVertex2f(205, y_icon - 3) - glVertex2f(215, y_icon + 3) - glVertex2f(205, y_icon + 3) - glVertex2f(215, y_icon - 3) - glEnd() - txt_f = font_text.render("Mosca", True, (200, 65, 40)) - draw_text_2d(txt_f, 225, y_icon - 8) - - # 3. Junco - glColor3f(*COLOR_REED) - glBegin(GL_LINES) - glVertex2f(340, y_icon - 10) - glVertex2f(340, y_icon + 15) - glEnd() - glColor3f(*COLOR_CATTAIL) - glBegin(GL_LINES) - glVertex2f(340, y_icon + 8) - glVertex2f(340, y_icon + 15) - glEnd() - txt_r = font_text.render("Junco", True, (100, 220, 100)) - draw_text_2d(txt_r, 355, y_icon - 8) - - # 4. Rana (Con el cuadrado central de cuerpo sólido) - frog_instance.draw(custom_x=475, custom_y=y_icon, custom_z=0.0, scale=3.5, menu_icon=True) - txt_frog = font_text.render("Rana", True, (100, 255, 100)) - draw_text_2d(txt_frog, 505, y_icon - 8) - - # 5. Libélulas (Moradas) - rad_w = math.radians(wing_angle) - w_off = math.sin(rad_w) * 1.5 - - glPushMatrix() - glTranslatef(625, y_icon, 0.0) - glScalef(6.0, 6.0, 1.0) - glColor3f(*COLOR_PURPLE_DRAGONFLY) - glBegin(GL_LINES) - glVertex3f(0, 1.5, 0) - glVertex3f(0, -3.0, 0) - glVertex3f(-0.4, 0.6, 0) - glVertex3f(0.4, 0.6, 0) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(-4.5, 1.0, w_off) - glVertex3f(-3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, 0.6, 0) - glVertex3f(4.5, 1.0, w_off) - glVertex3f(3.8, 0.2, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(-3.8, -0.2, w_off) - glVertex3f(-3.0, -0.8, w_off) - glEnd() - glBegin(GL_LINE_LOOP) - glVertex3f(0, -0.1, 0) - glVertex3f(3.8, -0.2, w_off) - glVertex3f(3.0, -0.8, w_off) - glEnd() - glPopMatrix() - - txt_df = font_text.render("Libélulas", True, (178, 51, 204)) - draw_text_2d(txt_df, 654, y_icon - 8) - - # MENSAJE PARA COMENZAR - start_msg = font_subtitle.render("Pulsa INTRO o cualquier tecla para comenzar...", True, (100, 255, 100)) - draw_text_2d(start_msg, WIDTH // 2 - 200, 30) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -def draw_hud(player_pos, yaw, insects, eaten_mosquitos, eaten_flies, font): - glDisable(GL_DEPTH_TEST) - - glMatrixMode(GL_PROJECTION) - glPushMatrix() - glLoadIdentity() - gluOrtho2D(0, WIDTH, 0, HEIGHT) - - glMatrixMode(GL_MODELVIEW) - glPushMatrix() - glLoadIdentity() - - px, py, pz = player_pos - - cx, cy = WIDTH // 2, HEIGHT - 35 - radius = 22 - - glColor3f(*COLOR_HUD) - glBegin(GL_LINE_LOOP) - for i in range(24): - angle = 2.0 * math.pi * i / 24 - glVertex2f(cx + radius * math.cos(angle), cy + radius * math.sin(angle)) - glEnd() - - glBegin(GL_LINES) - glVertex2f(cx, cy + radius) - glVertex2f(cx, cy + radius - 5) - glVertex2f(cx, cy - radius) - glVertex2f(cx, cy - radius + 5) - glVertex2f(cx - radius, cy) - glVertex2f(cx - radius + 5, cy) - glVertex2f(cx + radius, cy) - glVertex2f(cx + radius - 5, cy) - glEnd() - - closest_insect = None - min_dist = 999999.0 - relative_angle = 0.0 - diff_z = 0.0 - - for insect in insects: - dx = insect.x - px - dy = insect.y - py - dist = math.hypot(dx, dy) - - if dist < min_dist and dist > 0.1: - angle_to_insect = math.degrees(math.atan2(dy, dx)) - heading = (angle_to_insect - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - - if abs(heading) < 60: - min_dist = dist - closest_insect = insect - relative_angle = heading - diff_z = insect.z - pz - - if closest_insect: - radar_x = cx - (relative_angle / 60.0) * (radius - 4) - radar_y = cy + (diff_z / 20.0) * (radius - 4) - - dist_from_center = math.hypot(radar_x - cx, radar_y - cy) - if dist_from_center > (radius - 3): - factor = (radius - 3) / dist_from_center - radar_x = cx + (radar_x - cx) * factor - radar_y = cy + (radar_y - cy) * factor - - glColor3f(1.0, 1.0, 1.0) - glBegin(GL_LINE_LOOP) - for i in range(8): - ang = 2.0 * math.pi * i / 8 - glVertex2f(radar_x + 3 * math.cos(ang), radar_y + 3 * math.sin(ang)) - glEnd() - - glColor3f(*COLOR_MOSQUITO) - glBegin(GL_LINES) - glVertex2f(25, HEIGHT - 33) - glVertex2f(25, HEIGHT - 27) - glVertex2f(21, HEIGHT - 30) - glVertex2f(29, HEIGHT - 30) - glEnd() - - glColor3f(*COLOR_FLY) - glBegin(GL_LINES) - glVertex2f(WIDTH - 82, HEIGHT - 33) - glVertex2f(WIDTH - 82, HEIGHT - 27) - glVertex2f(WIDTH - 86, HEIGHT - 28) - glVertex2f(WIDTH - 78, HEIGHT - 32) - glVertex2f(WIDTH - 86, HEIGHT - 32) - glVertex2f(WIDTH - 78, HEIGHT - 28) - glEnd() - - txt_mosq = font.render(f"x {eaten_mosquitos}", True, (255, 230, 0)) - txt_flies = font.render(f"x {eaten_flies}", True, (200, 65, 40)) - - draw_text_2d(txt_mosq, 38, HEIGHT - 40) - draw_text_2d(txt_flies, WIDTH - 68, HEIGHT - 40) - - glMatrixMode(GL_PROJECTION) - glPopMatrix() - glMatrixMode(GL_MODELVIEW) - glPopMatrix() - glEnable(GL_DEPTH_TEST) - -# ========================================== -# BUCLE PRINCIPAL -# ========================================== -def main(): - pygame.init() - pygame.mixer.init(frequency=22050, size=-16, channels=1) - pygame.font.init() - - font = pygame.font.SysFont("Arial", 22, bold=True) - font_title = pygame.font.SysFont("Arial", 48, bold=True) - font_subtitle = pygame.font.SysFont("Arial", 20, bold=True) - font_text = pygame.font.SysFont("Arial", 16, bold=False) - - pygame.display.set_mode((WIDTH, HEIGHT), DOUBLEBUF | OPENGL) - pygame.display.set_caption("Libélula") - - glEnable(GL_DEPTH_TEST) - glClearColor(*COLOR_BG, 1.0) - - glMatrixMode(GL_PROJECTION) - gluPerspective(50, (WIDTH / HEIGHT), 0.1, 5000.0) - glMatrixMode(GL_MODELVIEW) - - buzz_sound = generate_buzz_sound() - wind_sound = generate_wind_sound() - - px, py, pz = 0.0, 0.0, 15.0 - yaw = 0.0 - roll = 0.0 - pitch = 0.0 - max_roll = 30.0 - max_pitch = 20.0 - - speed = 0.0 - max_speed = 3.0 - acceleration = 0.06 - deceleration = 0.03 - - min_altitude = 1.0 - max_altitude = 120.0 - - wing_timer = 0.0 - water_timer = 0.0 - is_perched = False - - dive_phase = "none" - pre_dive_alt = 15.0 - pre_dive_speed = 0.0 - - auto_landing = False - target_perch = None - - caught_insect = None - eaten_mosquitos = 0 - eaten_flies = 0 - - frog = Frog() - - purple_dragonflies = [ - PurpleDragonfly(1), - PurpleDragonfly(2), - PurpleDragonfly(3) - ] - - insects = [] - num_mosquitos = random.randint(MOSQUITO_MIN, MOSQUITO_MAX) - num_flies = random.randint(FLY_MIN, FLY_MAX) - - for _ in range(num_mosquitos): - insects.append(Insect("mosquito")) - for _ in range(num_flies): - insects.append(Insect("fly")) - - clock = pygame.time.Clock() - - in_start_screen = True - running = True - - buzz_timer = 0 - - while running: - dt = clock.tick(60) / 1000.0 - water_timer += dt - wing_timer += 60.0 - - for event in pygame.event.get(): - if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE): - running = False - elif event.type == KEYDOWN: - if in_start_screen: - in_start_screen = False - else: - if event.key == K_SPACE: - if not is_perched and dive_phase == "none": - dive_phase = "diving" - pre_dive_alt = pz - pre_dive_speed = speed - auto_landing = False - elif event.key == K_p: - if not is_perched and dive_phase == "none": - best_reed = None - min_d = 999.0 - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 35.0: - angle_to_reed = math.degrees(math.atan2(ry - py, rx - px)) - heading = (angle_to_reed - (90 - yaw)) % 360 - if heading > 180: - heading -= 360 - if abs(heading) < 45 and dist < min_d: - min_d = dist - best_reed = (rx, ry, rz + rh) - - if best_reed: - target_perch = best_reed - auto_landing = True - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - - if in_start_screen: - cam_x, cam_y, cam_z = 0.0, -200.0, 50.0 - gluLookAt(cam_x, cam_y, cam_z, 0, 0, 15, 0, 0, 1) - - draw_map(water_timer) - frog.update() - frog.draw() - for p_df in purple_dragonflies: - p_df.update() - p_df.draw() - - wing_angle = math.sin(wing_timer * 0.5) * 35.0 - menu_wing_angle = math.sin(wing_timer * 0.8) * 45.0 - draw_start_screen(font_title, font_subtitle, font_text, wing_angle, menu_wing_angle, frog) - else: - keys = pygame.key.get_pressed() - - if is_perched: - if keys[K_LEFT]: - yaw += 3.0 - if keys[K_RIGHT]: - yaw -= 3.0 - - if keys[K_w]: - is_perched = False - speed = 0.5 - - wing_angle = 0.0 - pitch = 0.0 - roll = 0.0 - dive_phase = "none" - auto_landing = False - else: - if auto_landing and target_perch: - tx, ty, tz = target_perch - dx = tx - px - dy = ty - py - dist_to_target = math.hypot(dx, dy) - - speed = 1.2 - px += (dx / dist_to_target) * speed if dist_to_target > 0.1 else 0 - py += (dy / dist_to_target) * speed if dist_to_target > 0.1 else 0 - pz += (tz - pz) * 0.1 - - if dist_to_target < 2.5 and abs(pz - tz) < 2.5: - px, py, pz = target_perch - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - auto_landing = False - target_perch = None - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - - elif dive_phase == "diving": - speed = max_speed * 1.5 - pz -= 1.2 - target_pitch = -45.0 - if wind_sound and not pygame.mixer.get_busy(): - wind_sound.play() - - if pz <= min_altitude: - pz = min_altitude - dive_phase = "recovering" - - elif dive_phase == "recovering": - target_pitch = 30.0 - pz += 0.8 - speed += (pre_dive_speed - speed) * 0.1 - - if pz >= pre_dive_alt: - pz = pre_dive_alt - speed = pre_dive_speed - dive_phase = "none" - else: - if keys[K_w]: - speed = min(speed + acceleration, max_speed) - if speed > 2.0 and wind_sound and random.random() < 0.1 and not pygame.mixer.get_busy(): - wind_sound.play() - elif keys[K_s]: - speed = max(speed - deceleration, 0.0) - - if speed < 0.3: - for rx, ry, rz, rh in REEDS: - dist = math.hypot(px - rx, py - ry) - if dist < 10.0 and abs(pz - (rz + rh)) < 10.0: - px, py, pz = rx, ry, rz + rh - speed = 0.0 - roll = 0.0 - pitch = 0.0 - is_perched = True - - if caught_insect: - if caught_insect.type == "mosquito": - eaten_mosquitos += 1 - else: - eaten_flies += 1 - caught_insect = None - break - - target_pitch = 0.0 - if keys[K_UP]: - pz = min(pz + 0.8, max_altitude) - target_pitch = max_pitch - elif keys[K_DOWN]: - pz = max(pz - 0.8, min_altitude) - target_pitch = -max_pitch - - if not auto_landing: - pitch += (target_pitch - pitch) * 0.15 - - target_roll = 0.0 - if keys[K_LEFT]: - yaw += 2.8 - target_roll = -max_roll - elif keys[K_RIGHT]: - yaw -= 2.8 - target_roll = max_roll - - roll += (target_roll - roll) * 0.15 - - rad_yaw = math.radians(yaw) - px += -math.sin(rad_yaw) * speed - py += math.cos(rad_yaw) * speed - - if abs(py) < POND_RADIUS: - max_limit_x = POND_RADIUS + 200.0 - else: - cx = get_river_center(py) - max_limit_x = abs(cx) + 200.0 - - px = max(-max_limit_x, min(max_limit_x, px)) - - if py > STREAM_MAX_Y: - py -= WORLD_LEN - elif py < STREAM_MIN_Y: - py += WORLD_LEN - - wing_timer += 80.0 if (dive_phase != "none" or auto_landing) else (60.0 if speed > 0.1 else 30.0) - wing_angle = math.sin(wing_timer) * 35.0 - - frog.update() - - for p_df in purple_dragonflies: - p_df.update() - - current_mosquitos = 0 - current_flies = 0 - near_insect = False - - for insect in insects[:]: - insect.update((px, py, pz)) - - if insect.type == "mosquito": - current_mosquitos += 1 - else: - current_flies += 1 - - dist_to_df = math.sqrt((px - insect.x)**2 + (py - insect.y)**2 + (pz - insect.z)**2) - if dist_to_df < 40.0: - near_insect = True - - if not caught_insect: - if dist_to_df < 6.0: - caught_insect = insect - insects.remove(insect) - if dive_phase == "diving": - dive_phase = "recovering" - - if near_insect and buzz_sound: - buzz_timer += 1 - if buzz_timer > 25 and not pygame.mixer.get_busy(): - buzz_sound.play() - buzz_timer = 0 - - if current_mosquitos < MOSQUITO_MIN: - insects.append(Insect("mosquito")) - if current_flies < FLY_MIN: - insects.append(Insect("fly")) - - cam_dist = 18.0 - cam_height = 8.0 - rad_yaw = math.radians(yaw) - - cam_x = px + math.sin(rad_yaw) * cam_dist - cam_y = py - math.cos(rad_yaw) * cam_dist - cam_z = pz + cam_height - - gluLookAt(cam_x, cam_y, cam_z, - px, py, pz + 0.5, - 0, 0, 1) - - draw_map(water_timer) - frog.draw() - - for p_df in purple_dragonflies: - p_df.draw() - - for insect in insects: - insect.draw() - - glPushMatrix() - glTranslatef(px, py, pz) - glRotatef(yaw, 0, 0, 1) - glRotatef(pitch, 1, 0, 0) - glRotatef(roll, 0, 1, 0) - draw_dragonfly(wing_angle, caught_insect) - glPopMatrix() - - draw_hud((px, py, pz), yaw, insects, eaten_mosquitos, eaten_flies, font) - - pygame.display.flip() - - pygame.quit() - sys.exit() - -if __name__ == "__main__": - main()