Flappy Plane

import pygame
import math
import random
import os
import time

pygame.init()

# ---------------- Paths ----------------
BASE_PATH = os.path.dirname(__file__)
BG_IMG = os.path.join(BASE_PATH, "backgrounds", "space_big.png")
PLAYER_IMG = os.path.join(BASE_PATH, "sprites", "player.png")
ENEMY_IMG = os.path.join(BASE_PATH, "sprites", "enemy.png")
A_ENEMY_IMG = os.path.join(BASE_PATH, "sprites", "a_enemy.png")
BOSS_IMG = os.path.join(BASE_PATH, "sprites", "boss.png")

# ---------------- Constants ----------------
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600
PLAYER_RADIUS = 20
ENEMY_RADIUS = 15
BOSS_RADIUS = 30
POWERUP_RADIUS = 10

PLAYER_SPEED = 5
PLAYER_MAX_HEALTH = 100
PLAYER_DAMAGE = 10
PLAYER_COOLDOWN = 0.2

ENEMY_SPEED_MIN = 1
ENEMY_SPEED_MAX = 2
ENEMY_HEALTH = 40
ENEMY_DAMAGE = 5
ENEMY_COOLDOWN = 2.5

BULLET_SPEED = 10
FIRE_BULLET_DAMAGE = 15

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)
RED = (255, 0, 0)
BLUE = (50, 50, 255)
ORANGE = (255, 165, 0)
GREEN = (0, 255, 0)

# ---------------- Load images ----------------
background_img = pygame.image.load(BG_IMG)
player_img_orig = pygame.image.load(PLAYER_IMG)
enemy_img_orig = pygame.image.load(ENEMY_IMG)
a_enemy_img_orig = pygame.image.load(A_ENEMY_IMG)
boss_img_orig = pygame.image.load(BOSS_IMG)

# ---------------- Classes ----------------
class Bullet:
def __init__(self, x, y, angle, is_enemy=False, fire=False):
self.x, self.y = x, y
self.angle = angle
self.is_enemy = is_enemy
self.fire = fire
self.speed = BULLET_SPEED
self.radius = 4

def update(self):
self.x += math.cos(math.radians(self.angle)) * self.speed
self.y += math.sin(math.radians(self.angle)) * self.speed

def draw(self, screen):
color = RED if self.is_enemy else (ORANGE if self.fire else YELLOW)
pygame.draw.circle(screen, color, (int(self.x), int(self.y)), self.radius)

def off_screen(self, width, height):
return self.x < -50 or self.x > width + 50 or self.y < -50 or self.y > height + 50

class Enemy:
def __init__(self, type_id=1, screen_size=(SCREEN_WIDTH, SCREEN_HEIGHT)):
width, height = screen_size
side = random.choice(["top", "bottom", "left", "right"])
if side == "top":
self.x, self.y = random.uniform(0, width), -20
elif side == "bottom":
self.x, self.y = random.uniform(0, width), height + 20
elif side == "left":
self.x, self.y = -20, random.uniform(0, height)
else:
self.x, self.y = width + 20, random.uniform(0, height)
self.speed = random.uniform(ENEMY_SPEED_MIN, ENEMY_SPEED_MAX)
self.angle = 0
self.type = type_id
self.radius = ENEMY_RADIUS
self.max_health = ENEMY_HEALTH
self.health = self.max_health
self.damage = ENEMY_DAMAGE
self.shoot_cd = random.uniform(0.5, ENEMY_COOLDOWN)

def update(self, px, py, dt):
dx, dy = px - self.x, py - self.y
self.angle = math.degrees(math.atan2(dy, dx))
self.x += math.cos(math.radians(self.angle)) * self.speed
self.y += math.sin(math.radians(self.angle)) * self.speed
self.shoot_cd -= dt

def can_shoot(self):
return self.shoot_cd <= 0def reset_shoot_timer(self): self.shoot_cd = ENEMY_COOLDOWNdef shoot(self): bx = self.x + math.cos(math.radians(self.angle)) * self.radius by = self.y + math.sin(math.radians(self.angle)) * self.radius self.reset_shoot_timer() return Bullet(bx, by, self.angle, True)def draw(self, screen): img = enemy_img_orig if self.type == 1 else a_enemy_img_orig img = pygame.transform.scale(img, (self.radius*2, self.radius*2)) rotated = pygame.transform.rotate(img, -self.angle) rect = rotated.get_rect(center=(self.x, self.y)) screen.blit(rotated, rect.topleft) # Health bar hp_percent = max(self.health/self.max_health, 0) bar_w = 30 bar_h = 4 pygame.draw.rect(screen, RED, (self.x - bar_w/2, self.y - self.radius - 8, bar_w, bar_h)) pygame.draw.rect(screen, GREEN, (self.x - bar_w/2, self.y - self.radius - 8, bar_w*hp_percent, bar_h))class Boss: def __init__(self, level): self.x, self.y = SCREEN_WIDTH/2, -BOSS_RADIUS*2 self.speed = 2 self.angle = 0 self.radius = BOSS_RADIUS self.damage = 10 self.max_health = 600 + ((level-1)//10)*100 self.health = self.max_health self.shoot_timer = 1.5 self.big_shoot_timer = 8 self.flash_timer = 0def update(self, px, py, dt): dx, dy = px - self.x, py - self.y self.angle = math.degrees(math.atan2(dy, dx)) self.x += math.cos(math.radians(self.angle)) * self.speed self.y += math.sin(math.radians(self.angle)) * self.speed if self.flash_timer > 0: self.flash_timer -= dt
self.shoot_timer -= dt
self.big_shoot_timer -= dt

def shoot(self):
if self.shoot_timer <= 0: self.shoot_timer = 1.5 bx = self.x + math.cos(math.radians(self.angle)) * self.radius by = self.y + math.sin(math.radians(self.angle)) * self.radius return Bullet(bx, by, self.angle, True) return Nonedef shoot_big(self): if self.big_shoot_timer <= 0: self.big_shoot_timer = 8 bullets = [] for offset in [-30, -15, 0, 15, 30]: bx = self.x + math.cos(math.radians(self.angle+offset)) * self.radius by = self.y + math.sin(math.radians(self.angle+offset)) * self.radius bullets.append(Bullet(bx, by, self.angle+offset, True)) return bullets return []def take_damage(self, dmg): self.health -= dmg self.flash_timer = 0.2 return self.health <= 0def draw(self, screen): img = pygame.transform.scale(boss_img_orig, (self.radius*2, self.radius*2)) rotated = pygame.transform.rotate(img, -self.angle) rect = rotated.get_rect(center=(self.x, self.y)) screen.blit(rotated, rect.topleft) # Health bar hp_percent = max(self.health/self.max_health,0) pygame.draw.rect(screen, RED, (screen.get_width()/2-150, 20, 300, 20)) pygame.draw.rect(screen, GREEN, (screen.get_width()/2-150, 20, 300*hp_percent, 20))class PowerUp: def __init__(self, screen_size=(SCREEN_WIDTH, SCREEN_HEIGHT)): self.x = random.randint(50, screen_size[0]-50) self.y = random.randint(50, screen_size[1]-50) self.radius = POWERUP_RADIUS self.type = random.choice(["fire","speed","shield","heal"])def draw(self, screen): color = ORANGE if self.type=="fire" else (YELLOW if self.type=="speed" else (BLUE if self.type=="shield" else RED)) pygame.draw.circle(screen, color, (int(self.x), int(self.y)), self.radius)def apply(self, player): if self.type=="heal": player["health"] = min(PLAYER_MAX_HEALTH, player["health"]+30) else: player[self.type] = 5# ---------------- Screens ---------------- def next_level_screen(screen, font, level, score): screen.fill(BLACK) msg = font.render(f"LEVEL {level} COMPLETE!", True, GREEN) msg2 = font.render("Press any key to continue...", True, WHITE) score_msg = font.render(f"Score: {score}", True, YELLOW) screen.blit(msg, (screen.get_width()//2 - 120, screen.get_height()//2 - 40)) screen.blit(score_msg, (screen.get_width()//2 - 50, screen.get_height()//2)) screen.blit(msg2, (screen.get_width()//2 - 140, screen.get_height()//2 + 40)) pygame.display.flip() wait = True while wait: for e in pygame.event.get(): if e.type == pygame.QUIT: pygame.quit(); exit() if e.type in [pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN]: wait=Falsedef game_over_screen(screen, font, score): screen.fill(BLACK) msg = font.render("GAME OVER", True, RED) msg2 = font.render("Press R to Restart", True, WHITE) score_msg = font.render(f"Score: {score}", True, YELLOW) screen.blit(msg, (screen.get_width()//2 - 80, screen.get_height()//2 - 40)) screen.blit(score_msg, (screen.get_width()//2 - 50, screen.get_height()//2)) screen.blit(msg2, (screen.get_width()//2 - 100, screen.get_height()//2 + 40)) pygame.display.flip() wait=True while wait: for e in pygame.event.get(): if e.type==pygame.QUIT: pygame.quit(); exit() if e.type==pygame.KEYDOWN and e.key==pygame.K_r: wait=False# ---------------- Main Game ---------------- def main(): screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.RESIZABLE) pygame.display.set_caption("Space Shooter - Multi Level") clock = pygame.time.Clock() font = pygame.font.SysFont(None,28) level = 1while True: # Setup level level_score_trigger = level*100 bullets = [] enemy_bullets = [] enemies = [Enemy(random.choice([1,2])) for _ in range(4)] powerups = [] player = {"x":SCREEN_WIDTH/2,"y":SCREEN_HEIGHT/2,"angle":0, "health":PLAYER_MAX_HEALTH,"bullet_cd":0,"fire":0,"speed":0,"shield":0} enemy_spawn_timer = 2 heal_timer = 0 hardcore_mode = level in [5,15,25,35,45,55] boss = None boss_defeated = False score = 0 game_over = Falsewhile not game_over: dt = clock.tick(60)/1000 width, height = screen.get_size() bg_scaled = pygame.transform.scale(background_img,(width,height)) screen.blit(bg_scaled,(0,0))for e in pygame.event.get(): if e.type == pygame.QUIT: pygame.quit(); return if e.type==pygame.KEYDOWN and e.key==pygame.K_r: return main()keys = pygame.key.get_pressed() mx,my = pygame.mouse.get_pos() player["angle"] = math.degrees(math.atan2(my - player["y"], mx - player["x"]))# Movement spd = PLAYER_SPEED*(1.5 if player["speed"]>0 else 1)
if keys[pygame.K_w]: player["y"] -= spd
if keys[pygame.K_s]: player["y"] += spd
if keys[pygame.K_a]: player["x"] -= spd
if keys[pygame.K_d]: player["x"] += spd
player["x"] = max(PLAYER_RADIUS,min(width-PLAYER_RADIUS,player["x"]))
player["y"] = max(PLAYER_RADIUS,min(height-PLAYER_RADIUS,player["y"]))

# Shooting
player["bullet_cd"] -= dt
if keys[pygame.K_SPACE] and player["bullet_cd"] <=0: bx = player["x"]+math.cos(math.radians(player["angle"]))*PLAYER_RADIUS by = player["y"]+math.sin(math.radians(player["angle"]))*PLAYER_RADIUS bullets.append(Bullet(bx,by,player["angle"],False,player["fire"]>0))
player["bullet_cd"] = PLAYER_COOLDOWN

# Passive healing
heal_timer += dt
if heal_timer >=2 and player["health"]0: player[attr]-=dt

# Enemy spawn
enemy_spawn_timer -= dt
if enemy_spawn_timer<=0 and not boss: enemies.append(Enemy(random.choice([1,2]),(width,height))) enemy_spawn_timer = 2# Update enemies for e in enemies[:]: e.update(player["x"],player["y"],dt) if math.hypot(e.x-player["x"],e.y-player["y"])=level_score_trigger and not boss and not boss_defeated:
boss = Boss(level)
if boss:
boss.update(player["x"],player["y"],dt)
bshot = boss.shoot()
if bshot: enemy_bullets.append(bshot)
bbshots = boss.shoot_big()
if bbshots: enemy_bullets.extend(bbshots)
boss.draw(screen)

# Powerups
if random.random()<0.001: powerups.append(PowerUp((width,height))) for pu in powerups[:]: pu.draw(screen) if math.hypot(pu.x-player["x"],pu.y-player["y"])0:
pygame.draw.circle(screen, BLUE, (int(player["x"]),int(player["y"])), PLAYER_RADIUS+5, 2)

# HUD
pygame.draw.rect(screen, RED,(20,20,200,20))
pygame.draw.rect(screen, GREEN,(20,20,200*(player["health"]/PLAYER_MAX_HEALTH),20))
score_text = font.render(f"Score: {score}",True,WHITE)
screen.blit(score_text,(20,45))

pygame.display.flip()

if player["health"]<=0: game_over_screen(screen,font,score) return main() # restart gameif boss_defeated: next_level_screen(screen,font,level,score) level+=1 breakif __name__=="__main__": main()

Scroll to Top