""" ----------------------------------------------------- Project: Lifeblood platformer Standard: 91883 (AS1.7) v.1 School: Tauranga Boys' College Author: Tyson Allerby Date: June 2025 Python: 3.11.9 64-bit (system) ----------------------------------------------------- """ import pygame import sys import os pygame.init() # === Delete death count on launch === if os.path.exists("deaths.txt"): os.remove("deaths.txt") # === Screen Setup === SCREEN_WIDTH = 800 SCREEN_HEIGHT = 540 BOTTOM_MARGIN = 100 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT + BOTTOM_MARGIN)) pygame.display.set_caption('----------------------------------------- ⌐╖╖═─ Lifeblood ⌐╖╖═─ ---------------------------------------') # === Colors === WHITE = (255, 255, 255) BLACK = (0, 0, 0) GRAY = (150, 150, 150) BG_COLOR = WHITE BOTTOM_COLOR = BLACK # === Clock & Font === clock = pygame.time.Clock() font = pygame.font.SysFont('Arial', 48) # === Trail Surface === trail_surf = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT + BOTTOM_MARGIN), pygame.SRCALPHA) trail_surf.fill((255, 255, 255, 0)) # === Player Settings === player_size = 40 player_start_x = SCREEN_WIDTH // 2 - player_size // 2 player_start_y = 100 player_x = player_start_x player_y = player_start_y player_speed = 5 player_vel_y = 0 gravity = 0.5 jump_strength = -10 ground_y = SCREEN_HEIGHT - player_size player_active = False can_jump = False # === Load/Save Death Count === def load_deaths(): if os.path.exists("deaths.txt"): with open("deaths.txt", "r") as file: return int(file.read()) return 0 def save_deaths(count): with open("deaths.txt", "w") as file: file.write(str(count)) death_count = load_deaths() death_screen_active = False game_complete = False # === Levels === levels = [ { "platforms": [ pygame.Rect(0, 0, 40, 40), pygame.Rect(760, 0, 40, 40), pygame.Rect(0, 500, 40, 40), pygame.Rect(760, 500, 40, 40), pygame.Rect(680, 500, 40, 40) ], "kill_zone": pygame.Rect(720, 540, 40, 40) }, { "platforms": [ pygame.Rect(0, 0, 40, 40), pygame.Rect(760, 0, 40, 40), pygame.Rect(100, 450, 80, 20), pygame.Rect(300, 375, 80, 20), pygame.Rect(500, 300, 80, 20), pygame.Rect(760, 500, 40, 40), pygame.Rect(0, 500, 40, 40), ], "kill_zone": pygame.Rect(740, 180, 40, 40) }, { "platforms": [ pygame.Rect(0, 0, 40, 40), pygame.Rect(760, 0, 40, 40), pygame.Rect(100, 475, 100, 20), pygame.Rect(250, 400, 100, 20), pygame.Rect(400, 325, 100, 20), pygame.Rect(550, 250, 100, 20), pygame.Rect(700, 175, 80, 20), pygame.Rect(0, 500, 40, 40), ], "kill_zone": pygame.Rect(797, 537, 1, 1) } ] level_index = 0 platforms = levels[level_index]["platforms"] kill_zone = levels[level_index]["kill_zone"] # === Sounds === jump_sound = pygame.mixer.Sound("jump.wav") death_sound = pygame.mixer.Sound("death.wav") # === Button Class === class Button: def __init__(self, text, x, y, w, h): self.text = text self.rect = pygame.Rect(x, y, w, h) self.color = (0, 0, 0) self.hover_color = (50, 50, 50) self.alpha = 0 self.surf = pygame.Surface((w, h), pygame.SRCALPHA) def draw(self, surface): mouse_pos = pygame.mouse.get_pos() is_hovered = self.rect.collidepoint(mouse_pos) color = self.hover_color if is_hovered else self.color self.surf.fill((255, 255, 255, 0)) pygame.draw.rect(self.surf, (*color, self.alpha), self.surf.get_rect(), border_radius=10) text_surf = font.render(self.text, True, (255, 255, 255)) text_surf.set_alpha(self.alpha) text_rect = text_surf.get_rect(center=self.rect.center) surface.blit(self.surf, self.rect.topleft) surface.blit(text_surf, text_rect) def fade_in(self, step=5): if self.alpha < 255: self.alpha += step if self.alpha > 255: self.alpha = 255 def is_clicked(self, event): return event.type == pygame.MOUSEBUTTONDOWN and event.button == 1 and self.rect.collidepoint(event.pos) # === Fade Overlay === def fade_overlay(to_black=True, speed=10): overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT + BOTTOM_MARGIN)) overlay.fill((0, 0, 0)) for alpha in range(0, 256, speed): draw_base() screen.blit(trail_surf, (0, 0)) overlay.set_alpha(alpha if to_black else 255 - alpha) screen.blit(overlay, (0, 0)) pygame.display.update() clock.tick(60) # === Death Animation === def death_animation(): for _ in range(3): pygame.draw.rect(screen, (255, 0, 0), (player_x, player_y, player_size, player_size)) pygame.display.update() pygame.time.delay(100) draw_base() pygame.display.update() pygame.time.delay(100) # === Draw Background === def draw_base(): screen.fill(BG_COLOR) pygame.draw.rect(screen, BOTTOM_COLOR, (0, SCREEN_HEIGHT, SCREEN_WIDTH, BOTTOM_MARGIN)) # === Buttons === begin_button = Button("BEGIN", SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 - 40, 200, 80) replay_button = Button("REPLAY", SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 + 20, 200, 80) # === Begin Game Sequence === def fade_in_begin_button(): global player_active, player_x, player_y fading = True # Bounce intro animation intro_y = -100 velocity = 0 gravity = 1 bounce_height = SCREEN_HEIGHT // 2 - 100 bouncing = True while fading: draw_base() if bouncing: velocity += gravity intro_y += velocity if intro_y >= bounce_height: intro_y = bounce_height velocity = -velocity * 0.6 if abs(velocity) < 2: bouncing = False intro_y = bounce_height pygame.draw.rect(screen, BLACK, (SCREEN_WIDTH // 2 - player_size // 2, int(intro_y), player_size, player_size)) if not bouncing: begin_button.fade_in() begin_button.draw(screen) # Draw subtitle if begin_button.alpha >= 255: sub_font = pygame.font.SysFont('Arial', 32) subtitle = sub_font.render("Time to die", True, BLACK) subtitle_rect = subtitle.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 + 60)) screen.blit(subtitle, subtitle_rect) counter_text = font.render(f"Deaths: {death_count}", True, BLACK) screen.blit(counter_text, (10, SCREEN_HEIGHT + 30)) pygame.display.update() clock.tick(60) for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if not bouncing and begin_button.is_clicked(event): fading = False player_active = True fade_overlay(False) # === Death Screen === def show_death_screen(): global death_screen_active, player_active, level_index, platforms, kill_zone, game_complete fade_overlay(to_black=True) while death_screen_active: draw_base() overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT + BOTTOM_MARGIN), pygame.SRCALPHA) overlay.fill((0, 0, 0, 180)) screen.blit(overlay, (0, 0)) if level_index >= len(levels) - 1: win_text = font.render("CONGRATULATIONS!", True, WHITE) screen.blit(win_text, win_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 - 50))) pygame.display.update() pygame.time.delay(3000) pygame.quit() sys.exit() death_text = font.render("YOU DIED", True, (255, 0, 0)) screen.blit(death_text, death_text.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 - 50))) replay_button.fade_in() replay_button.draw(screen) counter_text = font.render(f"Deaths: {death_count}", True, WHITE) screen.blit(counter_text, (10, SCREEN_HEIGHT + 30)) pygame.display.update() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if replay_button.is_clicked(event): death_screen_active = False player_active = True level_index += 1 if level_index >= len(levels): game_complete = True return platforms = levels[level_index]["platforms"] kill_zone = levels[level_index]["kill_zone"] fade_overlay(False) # === Start Game === fade_in_begin_button() # === Game Loop === run = True while run: draw_base() for event in pygame.event.get(): if event.type == pygame.QUIT: run = False # Draw platforms and kill zone for platform in platforms: pygame.draw.rect(screen, BLACK, platform) pygame.draw.rect(screen, GRAY, kill_zone) # Draw death counter death_display = font.render(f"Deaths: {death_count}", True, BLACK) screen.blit(death_display, (10, SCREEN_HEIGHT + 30)) keys = pygame.key.get_pressed() if player_active: # Horizontal movement if keys[pygame.K_LEFT]: new_rect = pygame.Rect(player_x - player_speed, player_y, player_size, player_size) if not any(new_rect.colliderect(p) for p in platforms): player_x -= player_speed if keys[pygame.K_RIGHT]: new_rect = pygame.Rect(player_x + player_speed, player_y, player_size, player_size) if not any(new_rect.colliderect(p) for p in platforms): player_x += player_speed # Keep player in bounds player_x = max(0, min(SCREEN_WIDTH - player_size, player_x)) # Jump if keys[pygame.K_SPACE] and can_jump: player_vel_y = jump_strength can_jump = False jump_sound.play() # Apply gravity player_vel_y += gravity player_y += player_vel_y player_rect = pygame.Rect(player_x, player_y, player_size, player_size) # Platform collision on_ground = False for platform in platforms: if player_rect.colliderect(platform): if player_vel_y > 0 and player_rect.bottom - player_vel_y <= platform.top: player_y = platform.top - player_size player_vel_y = 0 on_ground = True elif player_vel_y < 0 and player_rect.top - player_vel_y >= platform.bottom: player_y = platform.bottom player_vel_y = 0 if player_y >= ground_y: player_y = ground_y player_vel_y = 0 on_ground = True can_jump = on_ground # Death if player_rect.colliderect(kill_zone): death_count += 1 save_deaths(death_count) death_sound.play() death_animation() player_active = False death_screen_active = True show_death_screen() if game_complete: run = False continue player_x = player_start_x player_y = player_start_y player_vel_y = 0 pygame.draw.rect(screen, BLACK, (player_x, int(player_y), player_size, player_size)) pygame.display.update() clock.tick(60) pygame.quit()