import pygame import math import random import sys pygame.init() # Constants WIDTH, HEIGHT = 1000, 600 FPS = 60 BLIP_FADE_TIME = 4000 # milliseconds # Init screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Polished Submarine Simulator") clock = pygame.time.Clock() # Colors BLACK = (10, 10, 20) DARK_GRAY = (40, 40, 60) GRAY = (100, 100, 120) LIGHT_GRAY = (160, 160, 180) WHITE = (255, 255, 255) GREEN = (0, 255, 0) RED = (255, 100, 100) YELLOW = (255, 255, 0) BLUE = (10, 40, 80) CYAN = (0, 255, 255) SONAR_BLIP = (0, 255, 180) # Fonts font = pygame.font.SysFont("consolas", 14) # Submarine state sub_x = 500 sub_depth = 100.0 speed = 0.0 heading = 0.0 # Terrain terrain_width = 5000 seafloor = [] def generate_seafloor(): y = 300 for _ in range(terrain_width): y += random.uniform(-20, 20) y = max(100, min(500, y)) seafloor.append(y) generate_seafloor() # Sonar system sonar_angle = 0 sonar_radius = 120 sonar_center = (WIDTH - 180, HEIGHT - 180) sonar_speed = 90 blips = [] sonar_mode = "active" # can be 'active' or 'passive' # Player system interior_rect = pygame.Rect(50, 50, 600, 200) pilot_seat = pygame.Rect(90, 210, 30, 30) player = pygame.Rect(150, 210, 20, 40) player_vel = 100 sitting = False # Personal stats oxygen = 100.0 stamina = 100.0 health = 100.0 room_sealed = True # Draw gauges def draw_gauge(x, y, radius, label, value, min_val, max_val, unit, color): pygame.draw.circle(screen, GRAY, (x, y), radius) pygame.draw.circle(screen, BLUE, (x, y), radius - 6) for i in range(11): angle = math.radians(135 - i * 27) dx = math.cos(angle) * (radius - 10) dy = math.sin(angle) * (radius - 10) pygame.draw.line(screen, WHITE, (x + int(dx), y + int(dy)), (x + int(dx / 2), y + int(dy / 2)), 2) pct = (value - min_val) / (max_val - min_val) angle = math.radians(135 - pct * 270) dx = math.cos(angle) * (radius - 15) dy = math.sin(angle) * (radius - 15) pygame.draw.line(screen, color, (x, y), (x + int(dx), y + int(dy)), 4) screen.blit(font.render(label, True, WHITE), (x - 25, y - radius + 5)) screen.blit(font.render(f"{value:.0f} {unit}", True, WHITE), (x - 30, y + radius - 20)) # Main loop running = True while running: dt = clock.tick(FPS) / 1000.0 now = pygame.time.get_ticks() for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_e and not sitting and player.colliderect(pilot_seat): sitting = True player.topleft = pilot_seat.topleft elif event.key == pygame.K_ESCAPE and sitting: sitting = False elif event.key == pygame.K_TAB: sonar_mode = "passive" if sonar_mode == "active" else "active" keys = pygame.key.get_pressed() if not sitting: if keys[pygame.K_a] or keys[pygame.K_LEFT]: player.x -= int(player_vel * dt) if keys[pygame.K_d] or keys[pygame.K_RIGHT]: player.x += int(player_vel * dt) player.x = max(interior_rect.left, min(interior_rect.right - player.width, player.x)) if not room_sealed: oxygen = max(0, oxygen - 10 * dt) else: if keys[pygame.K_w]: speed += 20 * dt if keys[pygame.K_s]: speed -= 20 * dt if keys[pygame.K_q]: heading -= 90 * dt if keys[pygame.K_e]: heading += 90 * dt if keys[pygame.K_r]: sub_depth -= 50 * dt if keys[pygame.K_f]: sub_depth += 50 * dt # Clamp values speed = max(0, min(speed, 30)) sub_depth = max(0, min(sub_depth, 500)) heading %= 360 sub_x += math.cos(math.radians(heading)) * speed * dt # Sonar logic sonar_angle += sonar_speed * dt sonar_angle %= 360 sweep_radians = math.radians(sonar_angle) dx = math.cos(sweep_radians) dy = math.sin(sweep_radians) if sonar_mode == "active": for i in range(20, sonar_radius): ray_x = sub_x + dx * i ray_y = sub_depth + dy * i if 0 <= int(ray_x) < len(seafloor): if ray_y >= seafloor[int(ray_x)]: blips.append((sonar_angle, i, now)) break elif sonar_mode == "passive": for _ in range(2): angle = random.uniform(0, 360) distance = random.randint(30, sonar_radius) rad = math.radians(angle) px = sub_x + math.cos(rad) * distance py = sub_depth + math.sin(rad) * distance if 0 <= int(px) < len(seafloor) and py >= seafloor[int(px)]: blips.append((angle, distance, now)) # Clean expired blips blips = [(a, d, t) for a, d, t in blips if now - t < BLIP_FADE_TIME] # Draw everything screen.fill(BLACK) pygame.draw.rect(screen, DARK_GRAY, interior_rect, border_radius=15) pygame.draw.rect(screen, LIGHT_GRAY, pilot_seat, border_radius=5) pygame.draw.rect(screen, (220, 220, 255), player) pygame.draw.line(screen, GRAY, (interior_rect.left + 10, interior_rect.bottom), (interior_rect.right - 10, interior_rect.bottom), 2) pygame.draw.circle(screen, GRAY, (pilot_seat.centerx, pilot_seat.top - 10), 4) # UI overlays if sitting: draw_gauge(100, HEIGHT - 120, 60, "Depth", sub_depth, 0, 500, "m", GREEN) draw_gauge(220, HEIGHT - 120, 60, "Speed", speed, 0, 30, "kt", RED) draw_gauge(340, HEIGHT - 120, 60, "Heading", heading, 0, 360, "°", YELLOW) else: draw_gauge(100, HEIGHT - 120, 60, "O₂", oxygen, 0, 100, "%", GREEN) draw_gauge(220, HEIGHT - 120, 60, "Stamina", stamina, 0, 100, "%", YELLOW) draw_gauge(340, HEIGHT - 120, 60, "Health", health, 0, 100, "%", RED) # Sonar pygame.draw.circle(screen, (0, 60, 100), sonar_center, sonar_radius) pygame.draw.circle(screen, CYAN, sonar_center, sonar_radius, 2) for angle, dist, timestamp in blips: fade = max(0, min(255, 255 - ((now - timestamp) / BLIP_FADE_TIME) * 255)) rad = math.radians(angle) bx = sonar_center[0] + math.cos(rad) * dist by = sonar_center[1] + math.sin(rad) * dist surf = pygame.Surface((4, 4), pygame.SRCALPHA) pygame.draw.circle(surf, SONAR_BLIP + (int(fade),), (2, 2), 2) screen.blit(surf, (bx - 2, by - 2)) end_x = sonar_center[0] + math.cos(sweep_radians) * sonar_radius end_y = sonar_center[1] + math.sin(sweep_radians) * sonar_radius if sonar_mode == "active": pygame.draw.line(screen, CYAN, sonar_center, (end_x, end_y), 2) pygame.draw.circle(screen, CYAN, sonar_center, 3) else: pygame.draw.circle(screen, CYAN, sonar_center, 1) mode_text = font.render(f"Sonar: {sonar_mode.upper()} (TAB)", True, WHITE) screen.blit(mode_text, (sonar_center[0] - 60, sonar_center[1] + sonar_radius + 10)) # Prompts if not sitting and player.colliderect(pilot_seat): msg = font.render("Press E to sit", True, WHITE) screen.blit(msg, (pilot_seat.x, pilot_seat.y - 20)) elif sitting: msg = font.render("Press ESC to stand", True, WHITE) screen.blit(msg, (pilot_seat.x, pilot_seat.y - 20)) pygame.display.flip() pygame.quit() sys.exit()