cosmic-pixels/main.py

1162 lines
49 KiB
Python

import pygame
import random
import sys
# --- Initialize Pygame and Mixer ---
pygame.init()
pygame.mixer.init()
# --- Screen Settings ---
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Cosmic Pixels")
clock = pygame.time.Clock()
# --- Colors ---
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
CYAN = (0, 255, 255)
YELLOW = (255, 255, 0)
PLAYER_COLOR = (0, 200, 255) # Fallback color
RED = (255, 0, 0)
GREEN = (0, 255, 0)
ORANGE = (255, 165, 0)
# --- Load Assets (Font and Music) ---
try:
title_font = pygame.font.Font('pixel_font.ttf', 72)
prompt_font = pygame.font.Font('pixel_font.ttf', 30)
score_font = pygame.font.Font('pixel_font.ttf', 24)
except FileNotFoundError:
title_font = pygame.font.SysFont('Consolas', 72, bold=True)
prompt_font = pygame.font.SysFont('Consolas', 30)
score_font = pygame.font.SysFont('Consolas', 24)
# --- Load Sound Effects ---
try:
shoot_sound = pygame.mixer.Sound('laser_shoot.wav')
explosion_sound = pygame.mixer.Sound('explosion.wav')
death_sound = pygame.mixer.Sound('player_death.wav')
print("Sound effects loaded.")
except pygame.error:
print("Warning: One or more sound effect files are missing.")
shoot_sound = None
explosion_sound = None
death_sound = None
# --- Starfield Setup ---
stars = []
for _ in range(200):
x = random.randrange(0, SCREEN_WIDTH)
y = random.randrange(0, SCREEN_HEIGHT)
speed = random.randint(1, 3)
stars.append([x, y, speed])
# --- Bullet Class ---
class Bullet:
def __init__(self, x, y, dx, dy):
self.rect = pygame.Rect(x, y, 10, 5)
self.dx = dx
self.dy = dy
def update(self):
self.rect.x += self.dx
self.rect.y += self.dy
# --- Letter Class for Animation ---
class Letter:
def __init__(self, char, font, color, start_pos, end_pos, anim_duration=0.5):
self.surface = font.render(char, True, color)
self.start_pos = pygame.Vector2(start_pos)
self.end_pos = pygame.Vector2(end_pos)
self.current_pos = pygame.Vector2(start_pos)
self.anim_duration_ms = anim_duration * 1000
self.anim_start_time = 0
self.is_animating = False
self.is_finished = False
def start_animation(self):
self.is_animating = True
self.anim_start_time = pygame.time.get_ticks()
def update(self):
if not self.is_animating: return
elapsed_time = pygame.time.get_ticks() - self.anim_start_time
progress = min(elapsed_time / self.anim_duration_ms, 1.0)
progress = 1 - (1 - progress) ** 3
self.current_pos = self.start_pos.lerp(self.end_pos, progress)
if progress >= 1.0:
self.is_animating = False
self.is_finished = True
def draw(self, screen):
screen.blit(self.surface, self.current_pos)
# --- Game Loop Function (Level 1) ---
def game_loop():
"""This function runs the main gameplay for Level 1."""
pygame.mixer.music.stop()
try:
pygame.mixer.music.load('level1.wav')
pygame.mixer.music.play(-1)
except pygame.error:
print("Warning: Music file 'level1.wav' not found.")
try:
player_image = pygame.image.load('layer.png').convert_alpha()
player_image = pygame.transform.scale(player_image, (80, 40))
except pygame.error:
player_image = None
player_rect = player_image.get_rect() if player_image else pygame.Rect(0, 0, 80, 40)
player_rect.x = 50
player_rect.centery = SCREEN_HEIGHT / 2
PLAYER_SPEED = 5
player_health = 100
max_health = 100
player_dead = False
death_timer_start = 0
score = 0
kill_count = 0
kills_for_boss = 20
boss_fight_active = False
boss_defeated = False
player_bullets = []
enemy_bullets = []
boss_bullets = []
try:
enemy_image = pygame.image.load('enemy_ship.png').convert_alpha()
enemy_image = pygame.transform.scale(enemy_image, (50, 50))
except pygame.error:
enemy_image = None
enemies = []
ENEMY_SPEED = 3
ENEMY_SPAWN_EVENT = pygame.USEREVENT + 1
pygame.time.set_timer(ENEMY_SPAWN_EVENT, 1000)
try:
boss_image = pygame.image.load('boss_level1.png').convert_alpha()
boss_image = pygame.transform.scale(boss_image, (150, 100))
except pygame.error:
boss_image = None
boss_rect = None
boss_health = 200
boss_max_health = 200
boss_speed = 2
boss_direction = 1
boss_is_entering = False
thruster_length = 10
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN and not boss_defeated and not player_dead:
if event.key == pygame.K_SPACE:
player_bullets.append(Bullet(player_rect.right, player_rect.centery - 2, 10, 0))
if shoot_sound: shoot_sound.play()
if event.type == ENEMY_SPAWN_EVENT and not boss_fight_active:
enemy_y_pos = random.randint(50, SCREEN_HEIGHT - 50)
enemy_rect = enemy_image.get_rect(midleft=(SCREEN_WIDTH + 20, enemy_y_pos)) if enemy_image else pygame.Rect(SCREEN_WIDTH + 20, enemy_y_pos, 50, 50)
enemies.append(enemy_rect)
if not boss_defeated and not player_dead:
keys = pygame.key.get_pressed()
is_moving_forward = False
if keys[pygame.K_LEFT]: player_rect.x -= PLAYER_SPEED
if keys[pygame.K_RIGHT]:
player_rect.x += PLAYER_SPEED
is_moving_forward = True
if keys[pygame.K_UP]: player_rect.y -= PLAYER_SPEED
if keys[pygame.K_DOWN]: player_rect.y += PLAYER_SPEED
thruster_length = 25 if is_moving_forward else 10
if not player_dead:
if player_rect.left < 0: player_rect.left = 0
if player_rect.right > SCREEN_WIDTH and not boss_defeated: player_rect.right = SCREEN_WIDTH
if player_rect.top < 0: player_rect.top = 0
if player_rect.bottom > SCREEN_HEIGHT: player_rect.bottom = SCREEN_HEIGHT
for bullet in player_bullets: bullet.update()
for bullet in enemy_bullets: bullet.x -= 10
for bullet in boss_bullets: bullet.x -= 12
for enemy in enemies:
enemy.x -= ENEMY_SPEED
if random.randint(0, 150) == 1:
enemy_bullet = pygame.Rect(enemy.left, enemy.centery - 2, 10, 5)
enemy_bullets.append(enemy_bullet)
if not player_dead:
for bullet in player_bullets[:]:
bullet_removed = False
for enemy in enemies[:]:
if bullet.rect.colliderect(enemy):
enemies.remove(enemy)
player_bullets.remove(bullet)
score += 10
kill_count += 1
if explosion_sound: explosion_sound.play()
bullet_removed = True
break
if bullet_removed: continue
for enemy in enemies[:]:
if player_rect.colliderect(enemy):
enemies.remove(enemy)
player_health -= 25
if explosion_sound: explosion_sound.play()
for bullet in enemy_bullets[:] + boss_bullets[:]:
if player_rect.colliderect(bullet):
if bullet in enemy_bullets: enemy_bullets.remove(bullet)
if bullet in boss_bullets: boss_bullets.remove(bullet)
player_health -= 10
if boss_fight_active and boss_rect and not boss_defeated:
if boss_is_entering:
entry_target_x = SCREEN_WIDTH - boss_rect.width - 20
if boss_rect.x > entry_target_x: boss_rect.x -= 5
else: boss_is_entering = False
else:
boss_rect.y += boss_speed * boss_direction
if boss_rect.top <= 0 or boss_rect.bottom >= SCREEN_HEIGHT: boss_direction *= -1
if random.randint(0, 40) == 1:
boss_bullet = pygame.Rect(boss_rect.left, boss_rect.centery, 15, 8)
boss_bullets.append(boss_bullet)
for bullet in player_bullets[:]:
if bullet.rect.colliderect(boss_rect):
player_bullets.remove(bullet)
boss_health -= 5
if boss_health <= 0:
boss_defeated = True
if explosion_sound: explosion_sound.play()
pygame.mixer.music.fadeout(2000)
boss_rect = None
enemy_bullets.clear()
boss_bullets.clear()
break
if kill_count >= kills_for_boss and not boss_fight_active:
boss_fight_active = True
boss_is_entering = True
enemies.clear()
pygame.time.set_timer(ENEMY_SPAWN_EVENT, 0)
print("BOSS FIGHT STARTED!")
boss_rect = boss_image.get_rect(midleft=(SCREEN_WIDTH, SCREEN_HEIGHT / 2)) if boss_image else pygame.Rect(SCREEN_WIDTH, SCREEN_HEIGHT/2 - 50, 150, 100)
if player_health <= 0 and not player_dead:
player_dead = True
if death_sound: death_sound.play()
death_timer_start = pygame.time.get_ticks()
pygame.mixer.music.stop()
if player_dead:
if pygame.time.get_ticks() - death_timer_start > 2000:
fade_surface = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
fade_surface.fill(BLACK)
for alpha in range(0, 255, 5):
fade_surface.set_alpha(alpha)
screen.blit(fade_surface, (0,0))
pygame.display.flip()
pygame.time.delay(30)
died_text = title_font.render("YOU DIED", True, RED)
died_rect = died_text.get_rect(center=(SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
screen.blit(died_text, died_rect)
pygame.display.flip()
pygame.time.wait(3000)
return
if boss_defeated:
player_rect.x += PLAYER_SPEED
if player_rect.left > SCREEN_WIDTH:
fade_surface = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
fade_surface.fill(BLACK)
for alpha in range(0, 255, 5):
fade_surface.set_alpha(alpha)
screen.blit(fade_surface, (0,0))
pygame.display.flip()
pygame.time.delay(30)
level2_text = title_font.render("LEVEL 2", True, WHITE)
level2_rect = level2_text.get_rect(center=(SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
screen.blit(level2_text, level2_rect)
pygame.display.flip()
pygame.time.wait(3000)
level_2_loop(player_health, score)
running = False
screen.fill(BLACK)
for star in stars:
star[0] -= star[2]
if star[0] < 0: star[0] = SCREEN_WIDTH; star[1] = random.randrange(0, SCREEN_HEIGHT)
pygame.draw.rect(screen, WHITE, (star[0], star[1], star[2], star[2]))
player_bullets = [b for b in player_bullets if b.rect.left < SCREEN_WIDTH]
for bullet in player_bullets: pygame.draw.rect(screen, YELLOW, bullet.rect)
enemy_bullets = [b for b in enemy_bullets if b.right > 0]
for bullet in enemy_bullets: pygame.draw.rect(screen, RED, bullet)
boss_bullets = [b for b in boss_bullets if b.right > 0]
for bullet in boss_bullets: pygame.draw.rect(screen, RED, bullet)
enemies = [e for e in enemies if e.right > 0]
for enemy in enemies:
if enemy_image: screen.blit(enemy_image, enemy)
else: pygame.draw.rect(screen, RED, enemy)
if boss_rect:
if boss_image: screen.blit(boss_image, boss_rect)
else: pygame.draw.rect(screen, CYAN, boss_rect)
if not player_dead:
if boss_defeated:
thruster_length = 25 # Keep thrust on during fly-off
thruster_rect = pygame.Rect(player_rect.left - thruster_length, player_rect.centery - 5, thruster_length, 10)
pygame.draw.rect(screen, ORANGE, thruster_rect)
if player_image: screen.blit(player_image, player_rect)
else: pygame.draw.rect(screen, PLAYER_COLOR, player_rect)
health_bar_width = max(0, (player_health / max_health) * 200)
pygame.draw.rect(screen, RED, (10, 10, 200, 20))
pygame.draw.rect(screen, GREEN, (10, 10, health_bar_width, 20))
score_text = score_font.render(f"Score: {score}", True, WHITE)
screen.blit(score_text, score_text.get_rect(center=(SCREEN_WIDTH / 2, 20)))
if boss_fight_active and not boss_defeated and boss_rect:
boss_health_width = max(0, (boss_health / boss_max_health) * 200)
pygame.draw.rect(screen, RED, (SCREEN_WIDTH - 210, 10, 200, 20))
pygame.draw.rect(screen, GREEN, (SCREEN_WIDTH - 210, 10, boss_health_width, 20))
pygame.display.flip()
clock.tick(60)
def level_2_loop(player_health, score):
pygame.mixer.music.stop()
try:
pygame.mixer.music.load('level2.wav')
pygame.mixer.music.play(-1)
except pygame.error:
print("Warning: Music file 'level2.wav' not found.")
try:
player_image = pygame.image.load('layer.png').convert_alpha()
player_image = pygame.transform.scale(player_image, (80, 40))
except pygame.error:
player_image = None
player_rect = player_image.get_rect() if player_image else pygame.Rect(0, 0, 80, 40)
player_rect.x = 50
player_rect.centery = SCREEN_HEIGHT / 2
PLAYER_SPEED = 5
max_health = 100
player_dead = False
death_timer_start = 0
player_has_triple_shot = False
powerup_spawned = False
powerup_active_start_time = 0
POWERUP_DURATION = 30000
try:
powerup_image = pygame.image.load('powerup_triple_shot.png').convert_alpha()
powerup_image = pygame.transform.scale(powerup_image, (40, 40))
except pygame.error:
powerup_image = None
powerup_rect = None
try:
mine_image = pygame.image.load('space_mine.png').convert_alpha()
mine_image = pygame.transform.scale(mine_image, (40, 40))
except pygame.error:
mine_image = None
mines = []
MINE_SPAWN_EVENT = pygame.USEREVENT + 2
pygame.time.set_timer(MINE_SPAWN_EVENT, 2000)
try:
enemy_image = pygame.image.load('enemy_ship.png').convert_alpha()
enemy_image = pygame.transform.scale(enemy_image, (50, 50))
except pygame.error:
enemy_image = None
enemies = []
ENEMY_SPEED_L2 = 4
ENEMY_SPAWN_EVENT_L2 = pygame.USEREVENT + 3
pygame.time.set_timer(ENEMY_SPAWN_EVENT_L2, 800)
kill_count = 0
kills_for_powerup = 10
kills_for_boss = 40
boss_fight_active = False
boss_defeated = False
try:
boss_image = pygame.image.load('boss_level2.png').convert_alpha()
boss_image = pygame.transform.scale(boss_image, (170, 120))
except pygame.error:
print("Warning: 'boss_level2.png' not found.")
boss_image = None
boss_rect = None
boss_health = 400
boss_max_health = 400
boss_speed = 3
boss_direction = 1
boss_is_entering = False
player_bullets = []
enemy_bullets = []
boss_bullets = []
thruster_length = 10
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN and not boss_defeated and not player_dead:
if event.key == pygame.K_SPACE:
if player_has_triple_shot:
player_bullets.append(Bullet(player_rect.right, player_rect.centery - 2, 10, -2))
player_bullets.append(Bullet(player_rect.right, player_rect.centery - 2, 10, 0))
player_bullets.append(Bullet(player_rect.right, player_rect.centery - 2, 10, 2))
else:
player_bullets.append(Bullet(player_rect.right, player_rect.centery - 2, 10, 0))
if shoot_sound: shoot_sound.play()
if event.type == MINE_SPAWN_EVENT and not boss_fight_active:
mine_y = random.randint(20, SCREEN_HEIGHT - 20)
mine_rect = mine_image.get_rect(center=(SCREEN_WIDTH + 20, mine_y)) if mine_image else pygame.Rect(SCREEN_WIDTH + 20, mine_y, 40, 40)
mines.append(mine_rect)
if event.type == ENEMY_SPAWN_EVENT_L2 and not boss_fight_active:
enemy_y_pos = random.randint(50, SCREEN_HEIGHT - 50)
enemy_rect = enemy_image.get_rect(midleft=(SCREEN_WIDTH + 20, enemy_y_pos)) if enemy_image else pygame.Rect(SCREEN_WIDTH + 20, enemy_y_pos, 50, 50)
enemies.append(enemy_rect)
if not boss_defeated and not player_dead:
keys = pygame.key.get_pressed()
is_moving_forward = False
if keys[pygame.K_LEFT]: player_rect.x -= PLAYER_SPEED
if keys[pygame.K_RIGHT]:
player_rect.x += PLAYER_SPEED
is_moving_forward = True
if keys[pygame.K_UP]: player_rect.y -= PLAYER_SPEED
if keys[pygame.K_DOWN]: player_rect.y += PLAYER_SPEED
thruster_length = 25 if is_moving_forward else 10
if not player_dead:
if player_rect.left < 0: player_rect.left = 0
if player_rect.right > SCREEN_WIDTH and not boss_defeated: player_rect.right = SCREEN_WIDTH
if player_rect.top < 0: player_rect.top = 0
if player_rect.bottom > SCREEN_HEIGHT: player_rect.bottom = SCREEN_HEIGHT
if kill_count >= kills_for_powerup and not powerup_spawned:
powerup_spawned = True
powerup_rect = powerup_image.get_rect(center=(SCREEN_WIDTH + 30, SCREEN_HEIGHT / 2)) if powerup_image else pygame.Rect(SCREEN_WIDTH + 30, SCREEN_HEIGHT / 2, 40, 40)
if player_has_triple_shot:
elapsed_time = pygame.time.get_ticks() - powerup_active_start_time
if elapsed_time > POWERUP_DURATION:
player_has_triple_shot = False
for bullet in player_bullets: bullet.update()
for bullet in enemy_bullets: bullet.x -= 10
for bullet in boss_bullets: bullet.update()
for enemy in enemies:
enemy.x -= ENEMY_SPEED_L2
if random.randint(0, 120) == 1:
enemy_bullet = pygame.Rect(enemy.left, enemy.centery - 2, 10, 5)
enemy_bullets.append(enemy_bullet)
for mine in mines: mine.x -= 2
if powerup_rect: powerup_rect.x -= 3
if not player_dead:
for mine in mines[:]:
if player_rect.colliderect(mine):
mines.remove(mine)
player_health -= 20
if explosion_sound: explosion_sound.play()
for bullet in player_bullets[:]:
bullet_removed = False
for enemy in enemies[:]:
if bullet.rect.colliderect(enemy):
enemies.remove(enemy)
player_bullets.remove(bullet)
score += 15
kill_count += 1
if explosion_sound: explosion_sound.play()
bullet_removed = True
break
if bullet_removed: continue
for enemy in enemies[:]:
if player_rect.colliderect(enemy):
enemies.remove(enemy)
player_health -= 25
if explosion_sound: explosion_sound.play()
for bullet in enemy_bullets[:] + [b.rect for b in boss_bullets]:
if player_rect.colliderect(bullet):
if isinstance(bullet, pygame.Rect) and bullet in enemy_bullets:
enemy_bullets.remove(bullet)
else:
for b in boss_bullets[:]:
if b.rect == bullet:
boss_bullets.remove(b)
break
player_health -= 10
if powerup_rect and player_rect.colliderect(powerup_rect):
player_has_triple_shot = True
powerup_active_start_time = pygame.time.get_ticks()
powerup_rect = None
if kill_count >= kills_for_boss and not boss_fight_active:
boss_fight_active = True
boss_is_entering = True
enemies.clear()
mines.clear()
pygame.time.set_timer(ENEMY_SPAWN_EVENT_L2, 0)
pygame.time.set_timer(MINE_SPAWN_EVENT, 0)
boss_rect = boss_image.get_rect(midleft=(SCREEN_WIDTH, SCREEN_HEIGHT / 2)) if boss_image else pygame.Rect(SCREEN_WIDTH, SCREEN_HEIGHT/2-60, 170, 120)
if boss_fight_active and boss_rect and not boss_defeated:
if boss_is_entering:
entry_target_x = SCREEN_WIDTH - boss_rect.width - 20
if boss_rect.x > entry_target_x:
boss_rect.x -= 5
else:
boss_is_entering = False
else:
boss_rect.y += boss_speed * boss_direction
if boss_rect.top <= 0 or boss_rect.bottom >= SCREEN_HEIGHT:
boss_direction *= -1
if random.randint(0, 50) == 1:
boss_bullets.append(Bullet(boss_rect.left, boss_rect.centery, -12, -2))
boss_bullets.append(Bullet(boss_rect.left, boss_rect.centery, -12, 0))
boss_bullets.append(Bullet(boss_rect.left, boss_rect.centery, -12, 2))
for bullet in player_bullets[:]:
if bullet.rect.colliderect(boss_rect):
player_bullets.remove(bullet)
boss_health -= 5
if boss_health <= 0:
boss_defeated = True
if explosion_sound: explosion_sound.play()
pygame.mixer.music.fadeout(2000)
boss_rect = None
break
if player_health <= 0 and not player_dead:
player_dead = True
if death_sound: death_sound.play()
death_timer_start = pygame.time.get_ticks()
pygame.mixer.music.stop()
if player_dead:
if pygame.time.get_ticks() - death_timer_start > 2000:
fade_surface = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
fade_surface.fill(BLACK)
for alpha in range(0, 255, 5):
fade_surface.set_alpha(alpha)
screen.blit(fade_surface, (0,0))
pygame.display.flip()
pygame.time.delay(30)
died_text = title_font.render("YOU DIED", True, RED)
died_rect = died_text.get_rect(center=(SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
screen.blit(died_text, died_rect)
pygame.display.flip()
pygame.time.wait(3000)
return
if boss_defeated:
player_rect.x += PLAYER_SPEED
if player_rect.left > SCREEN_WIDTH:
fade_surface = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
fade_surface.fill(BLACK)
for alpha in range(0, 255, 4):
fade_surface.set_alpha(alpha)
screen.blit(fade_surface, (0,0))
pygame.display.flip()
pygame.time.delay(30)
level3_text = title_font.render("LEVEL 3", True, WHITE)
level3_rect = level3_text.get_rect(center=(SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
screen.blit(level3_text, level3_rect)
pygame.display.flip()
pygame.time.wait(3000)
level_3_loop(player_health, score)
running = False
screen.fill(BLACK)
for star in stars:
star[0] -= star[2]
if star[0] < 0: star[0] = SCREEN_WIDTH; star[1] = random.randrange(0, SCREEN_HEIGHT)
pygame.draw.rect(screen, WHITE, (star[0], star[1], star[2], star[2]))
for bullet in player_bullets: pygame.draw.rect(screen, YELLOW, bullet.rect)
for bullet in enemy_bullets: pygame.draw.rect(screen, RED, bullet)
for bullet in boss_bullets: pygame.draw.rect(screen, RED, bullet.rect)
for enemy in enemies:
if enemy_image: screen.blit(enemy_image, enemy)
else: pygame.draw.rect(screen, RED, enemy)
for mine in mines:
if mine_image: screen.blit(mine_image, mine)
else: pygame.draw.rect(screen, RED, mine)
if powerup_rect:
if powerup_image: screen.blit(powerup_image, powerup_rect)
else: pygame.draw.rect(screen, CYAN, powerup_rect)
if boss_rect:
if boss_image: screen.blit(boss_image, boss_rect)
else: pygame.draw.rect(screen, CYAN, boss_rect)
if not player_dead:
if boss_defeated:
thruster_length = 25
thruster_rect = pygame.Rect(player_rect.left - thruster_length, player_rect.centery - 5, thruster_length, 10)
pygame.draw.rect(screen, ORANGE, thruster_rect)
if player_image: screen.blit(player_image, player_rect)
else: pygame.draw.rect(screen, PLAYER_COLOR, player_rect)
health_bar_width = max(0, (player_health / max_health) * 200)
pygame.draw.rect(screen, RED, (10, 10, 200, 20))
pygame.draw.rect(screen, GREEN, (10, 10, health_bar_width, 20))
score_text = score_font.render(f"Score: {score}", True, WHITE)
screen.blit(score_text, score_text.get_rect(center=(SCREEN_WIDTH / 2, 20)))
level_text = score_font.render("Level 2", True, WHITE)
screen.blit(level_text, level_text.get_rect(topright=(SCREEN_WIDTH - 10, 10)))
if player_has_triple_shot:
powerup_icon = pygame.transform.scale(powerup_image, (20, 20)) if powerup_image else None
if powerup_icon: screen.blit(powerup_icon, (10, 35))
elapsed_time = pygame.time.get_ticks() - powerup_active_start_time
remaining_ratio = 1 - (elapsed_time / POWERUP_DURATION)
timer_bar_width = max(0, remaining_ratio * 175)
pygame.draw.rect(screen, RED, (35, 35, 175, 20))
pygame.draw.rect(screen, YELLOW, (35, 35, timer_bar_width, 20))
if boss_fight_active and not boss_defeated and boss_rect:
boss_health_width = max(0, (boss_health / boss_max_health) * 200)
pygame.draw.rect(screen, RED, (SCREEN_WIDTH - 210, 10, 200, 20))
pygame.draw.rect(screen, GREEN, (SCREEN_WIDTH - 210, 10, boss_health_width, 20))
pygame.display.flip()
clock.tick(60)
def level_3_loop(player_health, score, cheat_mode=False):
pygame.mixer.music.stop()
try:
pygame.mixer.music.load('level3.wav')
pygame.mixer.music.play(-1)
except pygame.error:
print("Warning: Music file 'level3.wav' not found.")
try:
player_image = pygame.image.load('layer.png').convert_alpha()
player_image = pygame.transform.scale(player_image, (80, 40))
except pygame.error:
player_image = None
player_rect = player_image.get_rect() if player_image else pygame.Rect(0, 0, 80, 40)
player_rect.x = 50
player_rect.centery = SCREEN_HEIGHT / 2
PLAYER_SPEED = 5
max_health = 100
player_dead = False
death_timer_start = 0
if cheat_mode: player_health = 999999
player_has_triple_shot = False
powerup_spawned = False
powerup_active_start_time = 0
POWERUP_DURATION = 30000
try:
powerup_image = pygame.image.load('powerup_triple_shot.png').convert_alpha()
powerup_image = pygame.transform.scale(powerup_image, (40, 40))
except pygame.error:
powerup_image = None
powerup_rect = None
heal_powerup_spawned = False
try:
heal_image = pygame.image.load('powerup_heal.png').convert_alpha()
heal_image = pygame.transform.scale(heal_image, (40, 40))
except pygame.error:
heal_image = None
heal_rect = None
try:
asteroid_image = pygame.image.load('asteroid.png').convert_alpha()
except pygame.error:
asteroid_image = None
asteroids = []
ASTEROID_SPAWN_EVENT = pygame.USEREVENT + 4
pygame.time.set_timer(ASTEROID_SPAWN_EVENT, 1500)
try:
enemy_image = pygame.image.load('enemy_ship.png').convert_alpha()
enemy_image = pygame.transform.scale(enemy_image, (50, 50))
except pygame.error:
enemy_image = None
enemies = []
ENEMY_SPEED_L3 = 5
ENEMY_SPAWN_EVENT_L3 = pygame.USEREVENT + 5
pygame.time.set_timer(ENEMY_SPAWN_EVENT_L3, 700)
kill_count = 0
kills_for_powerup = 15
kills_for_heal = 30
kills_for_boss = 50
boss_fight_active = False
boss_defeated = False
try:
boss_image = pygame.image.load('boss_level3.png').convert_alpha()
boss_image = pygame.transform.scale(boss_image, (200, 150))
except pygame.error:
boss_image = None
boss_rect = None
boss_health = 600
boss_max_health = 600
boss_speed = 4
boss_direction = 1
boss_is_entering = False
player_bullets = []
enemy_bullets = []
boss_bullets = []
thruster_length = 10
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN and not boss_defeated and not player_dead:
if event.key == pygame.K_SPACE:
if player_has_triple_shot:
player_bullets.append(Bullet(player_rect.right, player_rect.centery - 2, 10, -2))
player_bullets.append(Bullet(player_rect.right, player_rect.centery - 2, 10, 0))
player_bullets.append(Bullet(player_rect.right, player_rect.centery - 2, 10, 2))
else:
player_bullets.append(Bullet(player_rect.right, player_rect.centery - 2, 10, 0))
if shoot_sound: shoot_sound.play()
if event.type == ASTEROID_SPAWN_EVENT and not boss_fight_active:
size = random.randint(30, 60)
asteroid_y = random.randint(0, SCREEN_HEIGHT - size)
asteroid_rect = pygame.Rect(SCREEN_WIDTH + size, asteroid_y, size, size)
asteroids.append({'rect': asteroid_rect, 'speed': random.randint(2, 4)})
if event.type == ENEMY_SPAWN_EVENT_L3 and not boss_fight_active:
enemy_y_pos = random.randint(50, SCREEN_HEIGHT - 50)
enemy_rect = enemy_image.get_rect(midleft=(SCREEN_WIDTH + 20, enemy_y_pos)) if enemy_image else pygame.Rect(SCREEN_WIDTH + 20, enemy_y_pos, 50, 50)
enemies.append(enemy_rect)
if not boss_defeated and not player_dead:
keys = pygame.key.get_pressed()
is_moving_forward = False
if keys[pygame.K_LEFT]: player_rect.x -= PLAYER_SPEED
if keys[pygame.K_RIGHT]:
player_rect.x += PLAYER_SPEED
is_moving_forward = True
if keys[pygame.K_UP]: player_rect.y -= PLAYER_SPEED
if keys[pygame.K_DOWN]: player_rect.y += PLAYER_SPEED
thruster_length = 25 if is_moving_forward else 10
if not player_dead:
if player_rect.left < 0: player_rect.left = 0
if player_rect.right > SCREEN_WIDTH and not boss_defeated: player_rect.right = SCREEN_WIDTH
if player_rect.top < 0: player_rect.top = 0
if player_rect.bottom > SCREEN_HEIGHT: player_rect.bottom = SCREEN_HEIGHT
if kill_count >= kills_for_powerup and not powerup_spawned:
powerup_spawned = True
powerup_rect = powerup_image.get_rect(center=(SCREEN_WIDTH + 30, SCREEN_HEIGHT / 2)) if powerup_image else pygame.Rect(SCREEN_WIDTH + 30, SCREEN_HEIGHT / 2, 40, 40)
if kill_count >= kills_for_heal and not heal_powerup_spawned:
heal_powerup_spawned = True
heal_rect = heal_image.get_rect(center=(SCREEN_WIDTH + 30, SCREEN_HEIGHT / 3)) if heal_image else pygame.Rect(SCREEN_WIDTH+30, SCREEN_HEIGHT/3, 40, 40)
if player_has_triple_shot:
elapsed_time = pygame.time.get_ticks() - powerup_active_start_time
if elapsed_time > POWERUP_DURATION:
player_has_triple_shot = False
for bullet in player_bullets: bullet.update()
for bullet in enemy_bullets: bullet.x -= 10
for bullet in boss_bullets: bullet.update()
for enemy in enemies:
enemy.x -= ENEMY_SPEED_L3
if random.randint(0, 100) == 1:
enemy_bullet = pygame.Rect(enemy.left, enemy.centery - 2, 10, 5)
enemy_bullets.append(enemy_bullet)
for asteroid in asteroids: asteroid['rect'].x -= asteroid['speed']
if powerup_rect: powerup_rect.x -= 3
if heal_rect: heal_rect.x -= 3
if not player_dead:
for bullet in player_bullets[:]:
bullet_removed = False
for asteroid_dict in asteroids[:]:
if bullet.rect.colliderect(asteroid_dict['rect']):
try:
asteroids.remove(asteroid_dict)
player_bullets.remove(bullet)
if explosion_sound: explosion_sound.play()
bullet_removed = True
except ValueError:
pass
break
if bullet_removed: continue
for enemy in enemies[:]:
if bullet.rect.colliderect(enemy):
try:
enemies.remove(enemy)
player_bullets.remove(bullet)
score += 20
kill_count += 1
if explosion_sound: explosion_sound.play()
except ValueError:
pass
break
for asteroid_dict in asteroids[:]:
if player_rect.colliderect(asteroid_dict['rect']):
asteroids.remove(asteroid_dict)
if not cheat_mode: player_health -= 35
if explosion_sound: explosion_sound.play()
for enemy in enemies[:]:
if player_rect.colliderect(enemy):
enemies.remove(enemy)
if not cheat_mode: player_health -= 25
if explosion_sound: explosion_sound.play()
for bullet in enemy_bullets[:] + [b.rect for b in boss_bullets]:
if player_rect.colliderect(bullet):
if isinstance(bullet, pygame.Rect) and bullet in enemy_bullets:
enemy_bullets.remove(bullet)
else:
for b in boss_bullets[:]:
if b.rect == bullet:
boss_bullets.remove(b)
break
if not cheat_mode: player_health -= 10
if powerup_rect and player_rect.colliderect(powerup_rect):
player_has_triple_shot = True
powerup_active_start_time = pygame.time.get_ticks()
powerup_rect = None
if heal_rect and player_rect.colliderect(heal_rect):
player_health = min(max_health, player_health + 25)
heal_rect = None
if kill_count >= kills_for_boss and not boss_fight_active:
boss_fight_active = True
boss_is_entering = True
enemies.clear()
asteroids.clear()
pygame.time.set_timer(ENEMY_SPAWN_EVENT_L3, 0)
pygame.time.set_timer(ASTEROID_SPAWN_EVENT, 0)
boss_rect = boss_image.get_rect(midleft=(SCREEN_WIDTH, SCREEN_HEIGHT / 2)) if boss_image else pygame.Rect(SCREEN_WIDTH, SCREEN_HEIGHT/2-75, 200, 150)
if boss_fight_active and boss_rect and not boss_defeated:
if boss_is_entering:
entry_target_x = SCREEN_WIDTH - boss_rect.width - 20
if boss_rect.x > entry_target_x:
boss_rect.x -= 5
else:
boss_is_entering = False
else:
boss_rect.y += boss_speed * boss_direction
if boss_rect.top <= 0 or boss_rect.bottom >= SCREEN_HEIGHT:
boss_direction *= -1
if random.randint(0, 30) == 1:
boss_bullets.append(Bullet(boss_rect.left, boss_rect.centery, -15, -3))
boss_bullets.append(Bullet(boss_rect.left, boss_rect.centery, -15, 0))
boss_bullets.append(Bullet(boss_rect.left, boss_rect.centery, -15, 3))
for bullet in player_bullets[:]:
if bullet.rect.colliderect(boss_rect):
player_bullets.remove(bullet)
boss_health -= 5
if boss_health <= 0:
boss_defeated = True
if explosion_sound: explosion_sound.play()
pygame.mixer.music.fadeout(2000)
boss_rect = None
break
if player_health <= 0 and not player_dead:
player_dead = True
if death_sound: death_sound.play()
death_timer_start = pygame.time.get_ticks()
pygame.mixer.music.stop()
if player_dead:
if pygame.time.get_ticks() - death_timer_start > 2000:
fade_surface = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
fade_surface.fill(BLACK)
for alpha in range(0, 255, 5):
fade_surface.set_alpha(alpha)
screen.blit(fade_surface, (0,0))
pygame.display.flip()
pygame.time.delay(30)
died_text = title_font.render("YOU DIED", True, RED)
died_rect = died_text.get_rect(center=(SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
screen.blit(died_text, died_rect)
pygame.display.flip()
pygame.time.wait(3000)
return
if boss_defeated:
player_rect.x += PLAYER_SPEED
if player_rect.left > SCREEN_WIDTH:
fade_surface = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
fade_surface.fill(BLACK)
for alpha in range(0, 255, 4):
fade_surface.set_alpha(alpha)
screen.blit(fade_surface, (0,0))
pygame.display.flip()
pygame.time.delay(30)
win_text = title_font.render("YOU WIN!", True, WHITE)
win_rect = win_text.get_rect(center=(SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
screen.blit(win_text, win_rect)
pygame.display.flip()
pygame.time.wait(3000)
credits_screen()
running = False
screen.fill(BLACK)
for star in stars:
star[0] -= star[2]
if star[0] < 0: star[0] = SCREEN_WIDTH; star[1] = random.randrange(0, SCREEN_HEIGHT)
pygame.draw.rect(screen, WHITE, (star[0], star[1], star[2], star[2]))
for bullet in player_bullets: pygame.draw.rect(screen, YELLOW, bullet.rect)
for bullet in enemy_bullets: pygame.draw.rect(screen, RED, bullet)
for bullet in boss_bullets: pygame.draw.rect(screen, RED, bullet.rect)
for asteroid_dict in asteroids:
if asteroid_image:
scaled_asteroid = pygame.transform.scale(asteroid_image, (asteroid_dict['rect'].width, asteroid_dict['rect'].height))
screen.blit(scaled_asteroid, asteroid_dict['rect'])
else:
pygame.draw.rect(screen, WHITE, asteroid_dict['rect'])
for enemy in enemies:
if enemy_image: screen.blit(enemy_image, enemy)
else: pygame.draw.rect(screen, RED, enemy)
if powerup_rect:
if powerup_image: screen.blit(powerup_image, powerup_rect)
else: pygame.draw.rect(screen, CYAN, powerup_rect)
if heal_rect:
if heal_image: screen.blit(heal_image, heal_rect)
else: pygame.draw.rect(screen, GREEN, heal_rect)
if boss_rect:
if boss_image: screen.blit(boss_image, boss_rect)
else: pygame.draw.rect(screen, CYAN, boss_rect)
if not player_dead:
if boss_defeated:
thruster_length = 25
thruster_rect = pygame.Rect(player_rect.left - thruster_length, player_rect.centery - 5, thruster_length, 10)
pygame.draw.rect(screen, ORANGE, thruster_rect)
if player_image: screen.blit(player_image, player_rect)
else: pygame.draw.rect(screen, PLAYER_COLOR, player_rect)
health_bar_width = max(0, (player_health / max_health) * 200) if not cheat_mode else 200
pygame.draw.rect(screen, RED, (10, 10, 200, 20))
pygame.draw.rect(screen, GREEN, (10, 10, health_bar_width, 20))
score_text = score_font.render(f"Score: {score}", True, WHITE)
screen.blit(score_text, score_text.get_rect(center=(SCREEN_WIDTH / 2, 20)))
level_text = score_font.render("Level 3", True, WHITE)
screen.blit(level_text, level_text.get_rect(topright=(SCREEN_WIDTH - 10, 10)))
if player_has_triple_shot:
powerup_icon = pygame.transform.scale(powerup_image, (20, 20)) if powerup_image else None
if powerup_icon: screen.blit(powerup_icon, (10, 35))
elapsed_time = pygame.time.get_ticks() - powerup_active_start_time
remaining_ratio = 1 - (elapsed_time / POWERUP_DURATION)
timer_bar_width = max(0, remaining_ratio * 175)
pygame.draw.rect(screen, RED, (35, 35, 175, 20))
pygame.draw.rect(screen, YELLOW, (35, 35, timer_bar_width, 20))
if boss_fight_active and not boss_defeated and boss_rect:
boss_health_width = max(0, (boss_health / boss_max_health) * 200)
pygame.draw.rect(screen, RED, (SCREEN_WIDTH - 210, 10, 200, 20))
pygame.draw.rect(screen, GREEN, (SCREEN_WIDTH - 210, 10, boss_health_width, 20))
pygame.display.flip()
clock.tick(60)
def credits_screen():
pygame.mixer.music.stop()
try:
pygame.mixer.music.load('credits.wav')
pygame.mixer.music.play(-1)
pygame.mixer.music.set_volume(0.5)
except pygame.error:
print("Warning: 'credits.wav' not found.")
credit_lines = [ "Credits", "", "Coding: Google Gemini", "Graphics: Google Gemini", "Music: Suno.com", "", "Pygame", "", "Other then me (Michael Howard)", "putting it all together", "A.I. created this game", "", "Thanks for playing" ]
credit_surfaces = []
line_height = 60 # Increased spacing
start_y = SCREEN_HEIGHT + 50
for i, line in enumerate(credit_lines):
font = title_font if i == 0 else prompt_font
text_surface = font.render(line, True, WHITE)
text_rect = text_surface.get_rect(center=(SCREEN_WIDTH / 2, start_y + i * line_height))
credit_surfaces.append({'surf': text_surface, 'rect': text_rect})
credit_stars = [[random.uniform(-SCREEN_WIDTH / 2, SCREEN_WIDTH / 2), random.uniform(-SCREEN_HEIGHT / 2, SCREEN_HEIGHT / 2), random.uniform(1, SCREEN_WIDTH)] for _ in range(400)]
scroll_speed = 1.0 # Slower scroll
credits_finished_scrolling = False
prompt_alpha = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN and credits_finished_scrolling:
return # Go back to main menu loop
screen.fill(BLACK)
for star in credit_stars:
star[2] -= 2
if star[2] < 1:
star[2] = SCREEN_WIDTH
star[0], star[1] = random.uniform(-SCREEN_WIDTH / 2, SCREEN_WIDTH / 2), random.uniform(-SCREEN_HEIGHT / 2, SCREEN_HEIGHT / 2)
k = 128.0 / star[2]
sx, sy = int(star[0] * k + SCREEN_WIDTH / 2), int(star[1] * k + SCREEN_HEIGHT / 2)
if 0 < sx < SCREEN_WIDTH and 0 < sy < SCREEN_HEIGHT:
size = (1 - star[2] / SCREEN_WIDTH) * 4
pygame.draw.rect(screen, WHITE, (sx, sy, size, size))
last_rect_bottom = 0
if not credits_finished_scrolling:
for item in credit_surfaces:
item['rect'].y -= scroll_speed
screen.blit(item['surf'], item['rect'])
last_rect_bottom = item['rect'].bottom
if last_rect_bottom < -50:
credits_finished_scrolling = True
else:
if prompt_alpha < 255:
prompt_alpha = min(prompt_alpha + 3, 255)
prompt_surface = prompt_font.render("Press any key to continue", True, WHITE)
prompt_surface.set_alpha(prompt_alpha)
prompt_rect = prompt_surface.get_rect(center=(SCREEN_WIDTH / 2, SCREEN_HEIGHT - 50))
screen.blit(prompt_surface, prompt_rect)
pygame.display.flip()
clock.tick(60)
def main_menu():
try:
pygame.mixer.music.load('CosmicPixels.wav')
pygame.mixer.music.play(-1)
except pygame.error:
print("Warning: Music file 'CosmicPixels.wav' not found.")
game_title = "COSMIC PIXELS"
full_title_surface = title_font.render(game_title, True, CYAN)
total_width = full_title_surface.get_width()
initial_x = (SCREEN_WIDTH - total_width) / 2
title_letters = []
current_x = initial_x
for char in game_title:
char_surface = title_font.render(char, True, CYAN)
end_pos = (current_x, SCREEN_HEIGHT / 4)
start_pos = (current_x, SCREEN_HEIGHT)
letter = Letter(char, title_font, CYAN, start_pos, end_pos, anim_duration=0.7)
title_letters.append(letter)
current_x += char_surface.get_width()
next_letter_index, stagger_delay_ms = 0, 80
last_letter_time = pygame.time.get_ticks()
title_animation_complete = False
prompt_alpha, prompt_fade_speed = 0, 3
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN and title_animation_complete:
if event.key == pygame.K_0:
level_3_loop(100, 0, cheat_mode=True)
else:
game_loop()
# After a game, the loops will return here, and we just let the main menu loop continue
screen.fill(BLACK)
for star in stars:
star[0] -= star[2]
if star[0] < 0:
star[0] = SCREEN_WIDTH; star[1] = random.randrange(0, SCREEN_HEIGHT)
pygame.draw.rect(screen, WHITE, (star[0], star[1], star[2], star[2]))
current_time = pygame.time.get_ticks()
if not title_animation_complete and next_letter_index < len(title_letters):
if current_time - last_letter_time > stagger_delay_ms:
title_letters[next_letter_index].start_animation()
next_letter_index += 1
last_letter_time = current_time
all_letters_finished = True
for letter in title_letters:
letter.update(); letter.draw(screen)
if not letter.is_finished: all_letters_finished = False
if all_letters_finished: title_animation_complete = True
if title_animation_complete:
if prompt_alpha < 255: prompt_alpha = min(prompt_alpha + prompt_fade_speed, 255)
prompt_surface = prompt_font.render("PRESS ANY KEY TO START", True, YELLOW)
prompt_surface.set_alpha(prompt_alpha)
prompt_rect = prompt_surface.get_rect(center=(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2))
screen.blit(prompt_surface, prompt_rect)
pygame.display.flip()
clock.tick(60)
if __name__ == "__main__":
while True:
main_menu()