From db866ef67669f436d43d56786ee73aa6e7a8d2e6 Mon Sep 17 00:00:00 2001 From: luisgulo Date: Mon, 5 Oct 2026 22:59:28 +0200 Subject: [PATCH] Version final del juego --- tower_sentry_version-sin-intro.py | 465 ------------------------------ 1 file changed, 465 deletions(-) delete mode 100644 tower_sentry_version-sin-intro.py diff --git a/tower_sentry_version-sin-intro.py b/tower_sentry_version-sin-intro.py deleted file mode 100644 index a6dea91..0000000 --- a/tower_sentry_version-sin-intro.py +++ /dev/null @@ -1,465 +0,0 @@ -import math -import random -import pygame - -# ----------------------------------------------------------------------------- -# CONFIGURACIÓN Y CONSTANTES -# ----------------------------------------------------------------------------- -WIDTH, HEIGHT = 1024, 768 -GRID_SIZE = 36 # Base amplia -TILE_SIZE = 22.0 # Proporción para encajar todo el mapa - -# Reglas de alcance y línea de visión -MAX_REACH_DISTANCE = 12.0 # Distancia máxima en casillas para interactuar - -pygame.init() -pygame.font.init() -screen = pygame.display.set_mode((WIDTH, HEIGHT)) -pygame.display.set_caption("Tower Sentry - Sistema de Niveles Infinitos") -clock = pygame.time.Clock() - -# Paleta de Colores -COLOR_SKY = (125, 165, 205) - -COLOR_WATER = (45, 85, 125) -COLOR_SAND = (195, 180, 140) -COLOR_GRASS_LOW = (80, 140, 65) -COLOR_GRASS_HIGH = (110, 155, 75) -COLOR_ROCK = (125, 120, 110) -COLOR_SNOW = (240, 245, 250) - -COLOR_TARGET = (255, 220, 80) -COLOR_TARGET_INVALID = (220, 80, 80) -COLOR_BLOCK_TOP = (205, 175, 130) -COLOR_BLOCK_SIDE = (130, 105, 75) -COLOR_CLONE_MAUVE = (175, 140, 195) -COLOR_CLONE_DARK = (130, 100, 150) - -# Costes y Valores de Energía -COST_BLOCK = 2 -COST_CLONE = 3 -VAL_TREE = 1 -VAL_SENTINEL = 10 - -# ----------------------------------------------------------------------------- -# GENERACIÓN DE TERRENO Y LÓGICA DE NIVELES -# ----------------------------------------------------------------------------- -def noise(x, y): - xi, yi = int(x), int(y) - xf, yf = x - xi, y - yi - - u = xf * xf * (3 - 2 * xf) - v = yf * yf * (3 - 2 * yf) - - def hash_pt(px, py): - n = px * 157 + py * 113 - return math.sin(n) * 43758.5453123 - math.floor(math.sin(n) * 43758.5453123) - - g00 = hash_pt(xi, yi) - g10 = hash_pt(xi + 1, yi) - g01 = hash_pt(xi, yi + 1) - g11 = hash_pt(xi + 1, yi + 1) - - x1 = g00 + u * (g10 - g00) - x2 = g01 + u * (g11 - g01) - return x1 + v * (x2 - x1) - -def generate_realistic_heightmap(size): - grid = [[0.0 for _ in range(size)] for _ in range(size)] - seed_x, seed_y = random.uniform(10, 100), random.uniform(10, 100) - - for x in range(size): - for y in range(size): - nx, ny = (x + seed_x) * 0.07, (y + seed_y) * 0.07 - - h_base = noise(nx, ny) * 6.0 - h_detail = noise(nx * 2.0, ny * 2.0) * 2.0 - - total_h = h_base + h_detail - total_h = math.pow(max(0.0, total_h) / 5.0, 1.3) * 10.0 - - if 3.5 < total_h < 5.0: - total_h = 3.5 - elif 5.0 <= total_h < 6.5: - total_h = 6.5 - - grid[x][y] = round(total_h, 1) - - return grid - -# Variables Globales de Estado del Juego -current_level = 1 -player_energy = 10 - -base_grid = [] -has_block_grid = [] -sentinels_pos = set() -main_sentinel_pos = (0, 0) -player_start_pos = (0, 0) -player_x, player_y = (0, 0) -trees = set() -clones = set() -map_angle = 0.0 - -def load_level(level): - global base_grid, has_block_grid, sentinels_pos, main_sentinel_pos - global player_start_pos, player_x, player_y, trees, clones, map_angle - - base_grid = generate_realistic_heightmap(GRID_SIZE) - has_block_grid = [[0 for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)] - clones = set() - trees = set() - map_angle = 0.0 - - # Ordenar casillas por altura - height_list = [] - for x in range(GRID_SIZE): - for y in range(GRID_SIZE): - height_list.append((base_grid[x][y], (x, y))) - - height_list.sort(key=lambda item: item[0], reverse=True) - - # El Centinela Principal es la cima más alta - main_sentinel_pos = height_list[0][1] - - # Número de centinelas según el nivel actual - num_sentinels = level - sentinels_pos = set() - sentinels_pos.add(main_sentinel_pos) - - # Añadir centinelas adicionales en las siguientes cimas elevadas - idx = 1 - while len(sentinels_pos) < num_sentinels and idx < len(height_list): - pos = height_list[idx][1] - if base_grid[pos[0]][pos[1]] > 3.0: - sentinels_pos.add(pos) - idx += 1 - - # El Jugador empieza en la zona más baja - player_start_pos = height_list[-1][1] - player_x, player_y = player_start_pos - - # Generación de Árboles - while len(trees) < 32: - tx, ty = random.randint(0, GRID_SIZE - 1), random.randint(0, GRID_SIZE - 1) - if (tx, ty) not in sentinels_pos and (tx, ty) != player_start_pos and base_grid[tx][ty] > 1.8: - trees.add((tx, ty)) - -load_level(current_level) - -def get_effective_height(gx, gy): - if 0 <= gx < GRID_SIZE and 0 <= gy < GRID_SIZE: - return base_grid[gx][gy] + (1.2 if has_block_grid[gx][gy] == 1 else 0.0) - return 0.0 - -# ----------------------------------------------------------------------------- -# COMPROBACIÓN DE DISTANCIA Y LÍNEA DE VISIÓN -# ----------------------------------------------------------------------------- -def is_in_range_and_visible(px, py, tx, ty): - dist = math.hypot(tx - px, ty - py) - if dist > MAX_REACH_DISTANCE: - return False, "Fuera de alcance (demasiado lejos)" - - steps = int(max(abs(tx - px), abs(ty - py))) - if steps > 1: - p_height = get_effective_height(px, py) + 1.5 - t_height = get_effective_height(tx, ty) - - for i in range(1, steps): - cx = px + (tx - px) * (i / steps) - cy = py + (ty - py) * (i / steps) - - los_h = p_height + (t_height - p_height) * (i / steps) - terrain_h = get_effective_height(int(cx), int(cy)) - - if terrain_h > los_h: - return False, "Sin línea de visión (terreno bloquea la vista)" - - return True, "En alcance" - -# ----------------------------------------------------------------------------- -# PROYECCIÓN -# ----------------------------------------------------------------------------- -CENTER_GRID = GRID_SIZE / 2.0 - -def project(gx, gy, gz, angle): - rx = gx - CENTER_GRID - ry = gy - CENTER_GRID - - rot_x = rx * math.cos(angle) - ry * math.sin(angle) - rot_y = rx * math.sin(angle) + ry * math.cos(angle) - - screen_x = (rot_x - rot_y) * (TILE_SIZE / 2.0) + (WIDTH / 2.0) - screen_y = (rot_x + rot_y) * (TILE_SIZE / 4.0) - (gz * 6.5) + (HEIGHT / 2.0) + 30 - - return int(screen_x), int(screen_y), rot_y - -def get_realistic_color(height, normal_shade=1.0): - if height < 1.8: - c = COLOR_WATER - elif height < 3.0: - c = COLOR_SAND - elif height < 6.5: - t = (height - 3.0) / 3.5 - c = ( - int(COLOR_GRASS_LOW[0] * (1 - t) + COLOR_GRASS_HIGH[0] * t), - int(COLOR_GRASS_LOW[1] * (1 - t) + COLOR_GRASS_HIGH[1] * t), - int(COLOR_GRASS_LOW[2] * (1 - t) + COLOR_GRASS_HIGH[2] * t), - ) - elif height < 9.0: - c = COLOR_ROCK - else: - c = COLOR_SNOW - - r = max(0, min(255, int(c[0] * normal_shade))) - g = max(0, min(255, int(c[1] * normal_shade))) - b = max(0, min(255, int(c[2] * normal_shade))) - return (r, g, b) - -# ----------------------------------------------------------------------------- -# DIBUJO DE ENTIDADES -# ----------------------------------------------------------------------------- -def draw_humanoid(surface, cx, cy, color_main, color_shadow, is_player=False): - pygame.draw.ellipse(surface, (40, 50, 40, 100), (cx - 6, cy - 2, 12, 4)) - - pygame.draw.line(surface, color_shadow, (cx - 2, cy - 2), (cx - 1, cy - 7), 2) - pygame.draw.line(surface, color_shadow, (cx + 2, cy - 2), (cx + 1, cy - 7), 2) - - pygame.draw.polygon(surface, color_main, [ - (cx - 4, cy - 7), (cx + 4, cy - 7), - (cx + 3, cy - 15), (cx - 3, cy - 15) - ]) - - pygame.draw.line(surface, color_shadow, (cx - 3, cy - 14), (cx - 5, cy - 8), 2) - pygame.draw.line(surface, color_shadow, (cx + 3, cy - 14), (cx + 5, cy - 8), 2) - - pygame.draw.circle(surface, color_main, (cx, cy - 18), 3) - - if is_player: - pygame.draw.circle(surface, (100, 255, 200), (cx + 1, cy - 18), 2) - -def draw_sentinel(surface, cx, cy, time_ticks, is_main=True): - pygame.draw.ellipse(surface, (60, 60, 70), (cx - 8, cy - 2, 16, 5)) - - pygame.draw.polygon(surface, (40, 45, 55), [(cx - 6, cy - 3), (cx + 6, cy - 3), (cx + 4, cy - 22), (cx - 4, cy - 22)]) - pygame.draw.polygon(surface, (70, 75, 90), [(cx - 3, cy - 3), (cx + 3, cy - 3), (cx + 2, cy - 22), (cx - 2, cy - 22)]) - - pygame.draw.rect(surface, (30, 35, 45), (cx - 5, cy - 27, 10, 6)) - - eye_offset = int(math.sin(time_ticks * 0.05) * 2) - eye_color = (255, 40, 40) if is_main else (255, 140, 0) - pygame.draw.circle(surface, eye_color, (cx + eye_offset, cy - 24), 2) - -def draw_realistic_tree(surface, cx, cy): - pygame.draw.ellipse(surface, (30, 40, 30), (cx - 5, cy - 1, 10, 3)) - pygame.draw.polygon(surface, (65, 45, 25), [(cx - 1, cy - 1), (cx + 1, cy - 1), (cx + 1, cy - 6), (cx - 1, cy - 6)]) - pygame.draw.polygon(surface, (35, 85, 45), [(cx, cy - 18), (cx - 5, cy - 9), (cx + 5, cy - 9)]) - pygame.draw.polygon(surface, (45, 105, 55), [(cx, cy - 14), (cx - 4, cy - 5), (cx + 4, cy - 5)]) - -# ----------------------------------------------------------------------------- -# BUCLE PRINCIPAL -# ----------------------------------------------------------------------------- -font = pygame.font.SysFont("Courier", 13, bold=True) -selected_tile = None -level_transition_timer = 0 - -running = True -while running: - clock.tick(60) - time_ticks = pygame.time.get_ticks() - screen.fill(COLOR_SKY) - - mouse_x, mouse_y = pygame.mouse.get_pos() - - for event in pygame.event.get(): - if event.type == pygame.QUIT: - running = False - - elif event.type == pygame.KEYDOWN and level_transition_timer == 0: - if selected_tile: - gx, gy = selected_tile - can_reach, _ = is_in_range_and_visible(player_x, player_y, gx, gy) - - if can_reach: - # Absorber ('A') - if event.key == pygame.K_a: - if (gx, gy) in trees: - trees.remove((gx, gy)) - player_energy += VAL_TREE - elif (gx, gy) in sentinels_pos: - if get_effective_height(player_x, player_y) >= get_effective_height(gx, gy): - sentinels_pos.remove((gx, gy)) - player_energy += VAL_SENTINEL - elif (gx, gy) in clones and (gx, gy) != (player_x, player_y): - clones.remove((gx, gy)) - player_energy += COST_CLONE - elif has_block_grid[gx][gy] == 1 and (gx, gy) != (player_x, player_y): - has_block_grid[gx][gy] = 0 - player_energy += COST_BLOCK - - # Crear Bloque ('B') - elif event.key == pygame.K_b: - if player_energy >= COST_BLOCK and has_block_grid[gx][gy] == 0: - if (gx, gy) != (player_x, player_y) and (gx, gy) not in trees and (gx, gy) not in sentinels_pos: - has_block_grid[gx][gy] = 1 - player_energy -= COST_BLOCK - - # Crear Clon ('C') - elif event.key == pygame.K_c: - if player_energy >= COST_CLONE and (gx, gy) != (player_x, player_y): - if (gx, gy) not in trees and (gx, gy) not in sentinels_pos: - clones.add((gx, gy)) - player_energy -= COST_CLONE - - # Teletransporte (ESPACIO) - elif event.key == pygame.K_SPACE: - target_h = get_effective_height(gx, gy) - curr_h = get_effective_height(player_x, player_y) - - if (gx, gy) in clones: - player_x, player_y = gx, gy - - # Condición de Victoria: Estar en la peana del Centinela Principal y que todos los centinelas hayan sido eliminados - if (gx, gy) == main_sentinel_pos and len(sentinels_pos) == 0: - level_transition_timer = time_ticks - - elif has_block_grid[gx][gy] == 1 and target_h <= curr_h: - player_x, player_y = gx, gy - - # Transición al siguiente nivel - if level_transition_timer > 0 and time_ticks - level_transition_timer > 2000: - current_level += 1 - load_level(current_level) - level_transition_timer = 0 - - # Rotación - keys = pygame.key.get_pressed() - if keys[pygame.K_LEFT]: - map_angle -= 0.025 - if keys[pygame.K_RIGHT]: - map_angle += 0.025 - - polygons_to_draw = [] - closest_tile = None - min_dist = float('inf') - - for gy in range(GRID_SIZE): - for gx in range(GRID_SIZE): - z0 = get_effective_height(gx, gy) - - p0, y0 = project(gx, gy, z0, map_angle)[:2], project(gx, gy, z0, map_angle)[2] - p1 = project(gx + 1, gy, get_effective_height(min(gx + 1, GRID_SIZE - 1), gy), map_angle)[:2] - p2 = project(gx + 1, gy + 1, get_effective_height(min(gx + 1, GRID_SIZE - 1), min(gy + 1, GRID_SIZE - 1)), map_angle)[:2] - p3 = project(gx, gy + 1, get_effective_height(gx, min(gy + 1, GRID_SIZE - 1)), map_angle)[:2] - - cx = (p0[0] + p1[0] + p2[0] + p3[0]) // 4 - cy = (p0[1] + p1[1] + p2[1] + p3[1]) // 4 - - dist = math.hypot(mouse_x - cx, mouse_y - cy) - if dist < min_dist and dist < 18: - min_dist = dist - closest_tile = (gx, gy) - - shade = max(0.8, min(1.1, 1.0 + (z0 - 5.0) * 0.03)) - polygons_to_draw.append((y0, [p0, p1, p2, p3], z0, gx, gy, (cx, cy), shade)) - - polygons_to_draw.sort(key=lambda item: item[0]) - selected_tile = closest_tile - - # Renderizado - for depth, poly, avg_z, gx, gy, center_pt, shade in polygons_to_draw: - is_selected = (selected_tile == (gx, gy)) - - if is_selected: - can_reach, _ = is_in_range_and_visible(player_x, player_y, gx, gy) - base_col = COLOR_TARGET if can_reach else COLOR_TARGET_INVALID - else: - base_col = get_realistic_color(avg_z, shade) - - pygame.draw.polygon(screen, base_col, poly) - pygame.draw.polygon(screen, (35, 45, 40), poly, 1) - - cx, cy = center_pt - - if has_block_grid[gx][gy] == 1: - top_b = [(cx - 9, cy - 4), (cx + 9, cy - 4), (cx + 9, cy - 1), (cx - 9, cy - 1)] - side_b = [(cx - 9, cy - 1), (cx + 9, cy - 1), (cx + 9, cy + 3), (cx - 9, cy + 3)] - pygame.draw.polygon(screen, COLOR_BLOCK_SIDE, side_b) - pygame.draw.polygon(screen, COLOR_BLOCK_TOP, top_b) - pygame.draw.polygon(screen, (40, 30, 20), top_b, 1) - - if (gx, gy) in trees: - draw_realistic_tree(screen, cx, cy) - - if (gx, gy) in clones and (gx, gy) != (player_x, player_y): - draw_humanoid(screen, cx, cy, COLOR_CLONE_MAUVE, COLOR_CLONE_DARK, is_player=False) - - if (gx, gy) in sentinels_pos: - is_main = ((gx, gy) == main_sentinel_pos) - draw_sentinel(screen, cx, cy, time_ticks, is_main) - - if (gx, gy) == (player_x, player_y): - draw_humanoid(screen, cx, cy, (60, 140, 210), (30, 70, 120), is_player=True) - - # ----------------------------------------------------------------------------- - # INTERFAZ HUD - # ----------------------------------------------------------------------------- - pygame.draw.circle(screen, (255, 255, 255), (mouse_x, mouse_y), 3) - - p_h = get_effective_height(player_x, player_y) - action_msg = "Apunta a una casilla" - msg_color = (20, 20, 20) - - if selected_tile: - sgx, sgy = selected_tile - sh = get_effective_height(sgx, sgy) - can_reach, reach_reason = is_in_range_and_visible(player_x, player_y, sgx, sgy) - - if not can_reach: - action_msg = f"CASILLA INALCANZABLE: {reach_reason}" - msg_color = (180, 40, 40) - else: - if (sgx, sgy) in sentinels_pos: - if p_h >= sh: - action_msg = "¡CENTINELA EN RANGO! Pulsa 'A' para absorberlo (+10 energía)" - msg_color = (0, 140, 0) - else: - action_msg = f"Centinela elevado ({sh:.1f}m > {p_h:.1f}m). Iguala su altura con bloques." - msg_color = (180, 30, 30) - elif (sgx, sgy) == main_sentinel_pos and (sgx, sgy) not in sentinels_pos: - if len(sentinels_pos) > 0: - action_msg = f"Cima Principal despejada. Elimina los {len(sentinels_pos)} centinelas restantes." - msg_color = (180, 110, 0) - else: - if (sgx, sgy) in clones: - action_msg = "¡PEANA LISTA! Pulsa ESPACIO para conquistar el nivel." - msg_color = (0, 150, 220) - else: - action_msg = "Cima lista. Crea un clon ('C') y desplázate a él para ganar." - msg_color = (0, 140, 0) - elif (sgx, sgy) in trees: - action_msg = "Árbol en alcance. Pulsa 'A' para absorber (+1 energía)" - msg_color = (0, 120, 0) - elif (sgx, sgy) in clones: - action_msg = "Clon en alcance. Pulsa ESPACIO para teletransportarte | 'A' para absorber" - msg_color = (110, 70, 150) - elif has_block_grid[sgx][sgy] == 1: - action_msg = "Bloque en alcance. Pulsa ESPACIO para subir | 'C' para Clon | 'A' absorber" - msg_color = (130, 90, 0) - - pygame.draw.rect(screen, (235, 238, 242), (10, 10, WIDTH - 20, 45)) - - txt_hud = font.render(f"TOWER SENTRY | NIVEL: {current_level} | ENERGIA: {player_energy} | CENTINELAS: {len(sentinels_pos)}", True, (20, 20, 20)) - txt_action = font.render(action_msg, True, msg_color) - - screen.blit(txt_hud, (15, 14)) - screen.blit(txt_action, (15, 32)) - - if level_transition_timer > 0: - pygame.draw.rect(screen, (15, 20, 30), (WIDTH // 2 - 250, HEIGHT // 2 - 30, 500, 60)) - w_surf = font.render(f"¡NIVEL {current_level} SUPERADO! CARGANDO NIVEL {current_level + 1}...", True, (255, 215, 0)) - screen.blit(w_surf, (WIDTH // 2 - 230, HEIGHT // 2 - 8)) - - pygame.display.flip() - -pygame.quit()