import pygame
import math
# --- 1. SETTINGS & MATH ---
WIDTH, HEIGHT = 1100, 700
CEILING_FT = 70000
DERATE_FT = 60000
THIRTY_K_FT_Y = HEIGHT - (30000 * (HEIGHT / CEILING_FT))
JET_SPEED_BASE = 6
MISSILE_SPEED = JET_SPEED_BASE * 1.1
JET_TURN_RATE = 5
MISSILE_TURN_RATE = JET_TURN_RATE / 1.2
class Entity:
def __init__(self, x, y, speed, turn_rate, color, fuel, is_missile=False):
self.start_pos = pygame.Vector2(x, y)
self.pos = pygame.Vector2(x, y)
self.angle = 0
self.speed = speed
self.turn_rate = turn_rate
self.color = color
self.max_fuel = fuel
self.fuel = fuel
self.is_missile = is_missile
self.falling = False # For the "out of fuel" tumble
def reset(self):
self.pos = pygame.Vector2(self.start_pos.x, self.start_pos.y)
self.fuel = self.max_fuel
self.angle = 0
self.falling = False
# --- 2. INITIALIZE ---
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
font_small = pygame.font.SysFont("Courier New", 20, bold=True)
font_big = pygame.font.SysFont("Arial", 60, bold=True)
# 67% Fuel Logic: Jet = 100 units, Missile = 67 units
jet = Entity(100, 200, JET_SPEED_BASE, JET_TURN_RATE, (30, 60, 150), 100)
missile = None
game_active = True
dodged_count = 0
frame_count = 0
def draw_jet(surface, color, pos, angle):
rad = math.radians(angle)
points = [(pos.x + 25 * math.cos(rad), pos.y + 25 * math.sin(rad)),
(pos.x + 15 * math.cos(rad + 2.4), pos.y + 15 * math.sin(rad + 2.4)),
(pos.x + 5 * math.cos(rad + 3.14), pos.y + 5 * math.sin(rad + 3.14)),
(pos.x + 15 * math.cos(rad - 2.4), pos.y + 15 * math.sin(rad - 2.4))]
pygame.draw.polygon(surface, color, points)
def draw_missile(surface, pos, angle, fuel):
rad = math.radians(angle)
color = (255, 0, 0) if fuel > 0 else (100, 100, 100) # Turns grey when empty
points = [(pos.x + 18 * math.cos(rad), pos.y + 18 * math.sin(rad)),
(pos.x + 12 * math.cos(rad + 2.8), pos.y + 12 * math.sin(rad + 2.8)),
(pos.x + 6 * math.cos(rad + 3.14), pos.y + 6 * math.sin(rad + 3.14)),
(pos.x + 12 * math.cos(rad - 2.8), pos.y + 12 * math.sin(rad - 2.8))]
pygame.draw.polygon(surface, color, points)
if fuel > 0: # Rocket flame
pygame.draw.circle(surface, (255, 255, 0), (int(pos.x - 8 * math.cos(rad)), int(pos.y - 8 * math.sin(rad))), 4)
def reset_game():
global missile, game_active, dodged_count
jet.reset()
missile = None
game_active = True
dodged_count = 0
# --- 3. MAIN LOOP ---
running = True
while running:
frame_count += 1
current_alt = int(((HEIGHT - jet.pos.y) / HEIGHT) * CEILING_FT)
# Derated Speed Logic
current_speed = jet.speed * 0.9 if current_alt > DERATE_FT else jet.speed
is_derated = current_alt > DERATE_FT
screen.fill((10, 20, 50) if current_alt > 50000 else (100, 149, 237))
for event in pygame.event.get():
if event.type == pygame.QUIT: running = False
if event.type == pygame.KEYDOWN and not game_active and event.key == pygame.K_r:
reset_game()
if game_active:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]: jet.angle -= jet.turn_rate
if keys[pygame.K_RIGHT]: jet.angle += jet.turn_rate
if keys[pygame.K_UP] and jet.pos.y > 10: jet.pos.y -= 3
if keys[pygame.K_DOWN]: jet.pos.y += 3
# Update Jet
j_vel = pygame.Vector2(); j_vel.from_polar((current_speed, jet.angle))
jet.pos += j_vel
jet.fuel -= 0.012
# Missile System (67% Fuel Logic)
if missile is None and current_alt < 30000:
missile = Entity(jet.pos.x % WIDTH, HEIGHT, MISSILE_SPEED, MISSILE_TURN_RATE, (255, 0, 0), 67, True)
if missile:
if missile.fuel > 0:
dx, dy = (jet.pos.x % WIDTH) - missile.pos.x, jet.pos.y - missile.pos.y
target_angle = math.degrees(math.atan2(dy, dx))
angle_diff = (target_angle - missile.angle + 180) % 360 - 180
missile.angle += max(min(angle_diff, missile.turn_rate), -missile.turn_rate)
m_vel = pygame.Vector2(); m_vel.from_polar((missile.speed, missile.angle))
missile.pos += m_vel
missile.fuel -= 0.08
if missile.pos.distance_to(pygame.Vector2(jet.pos.x % WIDTH, jet.pos.y)) < 18:
game_active = False
else:
# Tumble Out of Fuel
missile.pos.y += 5
missile.angle += 10 # Spin
if missile.pos.y > HEIGHT:
missile = None
dodged_count += 1
if current_alt <= 0 or jet.fuel <= 0: game_active = False
# --- 4. DRAWING ---
pygame.draw.line(screen, (255, 255, 255), (0, THIRTY_K_FT_Y), (WIDTH, THIRTY_K_FT_Y), 1)
draw_jet(screen, jet.color, pygame.Vector2(jet.pos.x % WIDTH, jet.pos.y), jet.angle)
if missile: draw_missile(screen, missile.pos, missile.angle, missile.fuel)
# HUD
screen.blit(font_small.render(f"ALTITUDE: {max(0, current_alt)} FT", True, (255,255,255)), (20, 20))
screen.blit(font_small.render(f"DODGED: {dodged_count}", True, (255,255,255)), (20, 50))
if is_derated: screen.blit(font_small.render("DERATED", True, (255,255,0)), (20, 80))
# Fuel Bars
pygame.draw.rect(screen, (0,255,100), (WIDTH-120, 20, jet.fuel, 15)) # Jet
if missile and missile.fuel > 0:
pygame.draw.rect(screen, (255,0,0), (WIDTH-120, 45, (missile.fuel/67)*100, 10))
if not game_active:
screen.blit(font_big.render("MISSION FAILED", True, (255,0,0)), (WIDTH//2-200, HEIGHT//2))
screen.blit(font_small.render("Press 'R' to Restart", True, (255,255,255)), (WIDTH//2-110, HEIGHT//2+70))
pygame.display.flip()
clock.tick(60)
pygame.quit()