Compare commits

...

14 Commits
dev ... main

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 962 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 KiB

@ -0,0 +1,68 @@
import pygame
import settings
from settings import MyColors
class GameOver:
def __init__(self, screen):
self.screen = screen
self.background = pygame.image.load(settings.BACKGROUND_IMG_COMP).convert_alpha()
icon_controller_retry = pygame.image.load(settings.icons.controller.viereck).convert_alpha()
self.icon_controller_retry = pygame.transform.scale(icon_controller_retry, (50, 48))
icon_controller_menu = pygame.image.load(settings.icons.controller.dreieck).convert_alpha()
self.icon_controller_menu = pygame.transform.scale(icon_controller_menu, (50, 48))
self.font = pygame.font.Font(settings.fonts.PressStart2P, 60)
self.small_font = pygame.font.Font(settings.fonts.PressStart2P, 24)
self.retry_button = pygame.Rect(390, 400, 200, 50)
self.menu_button = pygame.Rect(690, 400, 200, 50)
def draw(self):
# self.screen.blit(self.background, (0, 0))
overlay = pygame.Surface((settings.SCREEN_WIDTH, settings.SCREEN_HEIGHT)) # Neues Surface in Bildschirmgröße
overlay.set_alpha(50) # 0 = komplett durchsichtig, 255 = komplett undurchsichtig
overlay.fill((0, 0, 0)) # Schwarz färben
self.screen.blit(overlay, (0, 0)) # Overlay auf
# Score-Text
score_text = self.font.render(f"GAME OVER", True, (255, 255, 255)) # z.B. Gold
self.screen.blit(score_text, (settings.SCREEN_WIDTH // 2 - score_text.get_width() // 2, 200))
time_text = self.small_font.render(f"Das wird schon wieder", True, (255, 255, 255))
self.screen.blit(time_text, (settings.SCREEN_WIDTH // 2 - time_text.get_width() // 2, 300))
pygame.draw.rect(self.screen, MyColors.yellow, self.retry_button)
pygame.draw.rect(self.screen, MyColors.yellow, self.menu_button)
nochmal_text = self.small_font.render("Nochmal", True, MyColors.brown)
menu_text = self.small_font.render("Menü", True, MyColors.brown)
self.screen.blit(nochmal_text, (self.retry_button.x + 15, self.retry_button.y + 10))
self.screen.blit(menu_text, (self.menu_button.x + 50, self.menu_button.y + 10))
self.screen.blit(self.icon_controller_retry, (self.retry_button.x + 70, self.retry_button.y + 40))
self.screen.blit(self.icon_controller_menu, ((self.menu_button.x + 70), self.menu_button.y + 40))
return self.retry_button, self.menu_button
def handle_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
print("MOUSEBUTTONDOWN") if settings.DEBUG else None
if self.retry_button.collidepoint(event.pos):
return "retry"
elif self.menu_button.collidepoint(event.pos):
return "menu"
if event.type == pygame.MOUSEBUTTONUP:
if self.retry_button.collidepoint(event.pos):
return "retry"
elif self.menu_button.collidepoint(event.pos):
return "menu"
if event.type == pygame.JOYBUTTONDOWN:
if event.button == 3:
return "menu"
elif event.button == 2:
return "retry"
return None

Binary file not shown.

@ -0,0 +1,17 @@
import pygame
import random
import settings
class Item(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.image.load(settings.world.items.beer.img).convert_alpha()
self.image = pygame.transform.scale(self.image, settings.world.items.beer.size)
self.rect = self.image.get_rect(topleft=(x, y))
print(f'create item {self.rect}') if settings.DEBUG else None
def draw(self, screen, camera_offset):
screen.blit(self.image, (self.rect.x - camera_offset, self.rect.y))
def get_rect(self):
return self.rect

@ -0,0 +1,92 @@
import pygame
import settings
from settings import MyColors
import sqlite3
class Endscreen:
def __init__(self, screen, score=0, time_passed=0, player_name="Spieler1"):
self.screen = screen
self.score = score
self.player_name = player_name
self.background = pygame.image.load(settings.BACKGROUND_IMG_COMP).convert_alpha()
icon_controller_retry = pygame.image.load(settings.icons.controller.viereck).convert_alpha()
self.icon_controller_retry = pygame.transform.scale(icon_controller_retry, (50, 48))
icon_controller_menu = pygame.image.load(settings.icons.controller.dreieck).convert_alpha()
self.icon_controller_menu = pygame.transform.scale(icon_controller_menu, (50, 48))
self.font = pygame.font.Font(settings.fonts.PressStart2P, 60)
self.small_font = pygame.font.Font(settings.fonts.PressStart2P, 24)
self.retry_button = pygame.Rect(390, 400, 200, 50)
self.menu_button = pygame.Rect(690, 400, 200, 50)
self.time_passed = time_passed
self.save_score_to_db()
def save_score_to_db(self):
connection = sqlite3.connect('highscores.db')
cursor = connection.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS highscores (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
score INTEGER NOT NULL,
time REAL NOT NULL
)
''')
cursor.execute('''
INSERT INTO highscores (name, score, time)
VALUES (?, ?, ?)
''', (self.player_name, self.score, self.time_passed))
connection.commit()
connection.close()
def draw(self):
self.screen.blit(self.background, (0, 0))
# Score-Text
score_text = self.font.render(f"Dein Score: {self.score}", True, (255, 255, 255)) # z.B. Gold
self.screen.blit(score_text, (settings.SCREEN_WIDTH // 2 - score_text.get_width() // 2, 200))
time_text = self.small_font.render(f"Deine Zeit: {self.time_passed:.2f} Sekunden", True, (255, 255, 255))
self.screen.blit(time_text, (settings.SCREEN_WIDTH // 2 - time_text.get_width() // 2, 300))
pygame.draw.rect(self.screen, MyColors.yellow, self.retry_button)
pygame.draw.rect(self.screen, MyColors.yellow, self.menu_button)
nochmal_text = self.small_font.render("Nochmal", True, MyColors.brown)
menu_text = self.small_font.render("Menü", True, MyColors.brown)
self.screen.blit(nochmal_text, (self.retry_button.x + 15, self.retry_button.y + 10))
self.screen.blit(menu_text, (self.menu_button.x + 50, self.menu_button.y + 10))
self.screen.blit(self.icon_controller_retry, (self.retry_button.x + 70, self.retry_button.y + 40))
self.screen.blit(self.icon_controller_menu, ((self.menu_button.x + 70), self.menu_button.y + 40))
return self.retry_button, self.menu_button
def handle_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN:
print("MOUSEBUTTONDOWN") if settings.DEBUG else None
if self.retry_button.collidepoint(event.pos):
return "retry"
elif self.menu_button.collidepoint(event.pos):
return "menu"
if event.type == pygame.MOUSEBUTTONUP:
if self.retry_button.collidepoint(event.pos):
return "retry"
elif self.menu_button.collidepoint(event.pos):
return "menu"
if event.type == pygame.JOYBUTTONDOWN:
if event.button == 3:
return "menu"
elif event.button == 2:
return "retry"
return None

@ -4,15 +4,25 @@ import sys
from menu import Menu # Menü importieren
import settings
from world import World
from level_complete_screen import Endscreen
from gameover import GameOver
def main():
pygame.joystick.init()
joysticks = []
for i in range(pygame.joystick.get_count()):
joystick = pygame.joystick.Joystick(i)
joystick.init()
joysticks.append(joystick)
pygame.init()
screen = pygame.display.set_mode((settings.SCREEN_WIDTH, settings.SCREEN_HEIGHT))
pygame.display.set_caption("The Last Ollie - A Fred Story")
clock = pygame.time.Clock()
menu = Menu(screen)
world = World(screen)
world = World(screen, menu.player_name)
endscreen = Endscreen(screen, 000) if settings.DEBUG_ENDSCREEN else Endscreen(screen, world.save_score)
current_scene = settings.START_POINT
@ -26,6 +36,7 @@ def main():
menu.handle_event(event)
if menu.ready_to_start_game:
menu.ready_to_start_game = False # Reset!
world = World(screen, menu.player_name)
current_scene = "world"
if current_scene == "world":
@ -36,11 +47,49 @@ def main():
menu.show_main_buttons = True
menu.ready_to_start_game = False
if current_scene == "endscreen":
action = endscreen.handle_event(event)
if action == "retry":
world = World(screen, menu.player_name)
current_scene = "world"
elif action == "menu":
current_scene = "menu"
menu.input_active = False
menu.show_main_buttons = True
menu.ready_to_start_game = False
menu.player_name = ""
if current_scene == "gameover":
action = endscreen.handle_event(event)
if action == "retry":
world = World(screen, menu.player_name)
current_scene = "world"
elif action == "menu":
current_scene = "menu"
menu.input_active = False
menu.show_main_buttons = True
menu.ready_to_start_game = False
menu.player_name = ""
if current_scene == "menu":
menu.draw()
elif current_scene == "world":
world.player.update(670)
if not world.player.game_paused and not world.player.gameOver:
world.draw()
elif world.player.gameOver:
gameover = GameOver(screen)
current_scene = "gameover"
else:
endscreen = Endscreen(screen, world.save_score, world.time_passed, world.player_name)
current_scene = "endscreen"
if settings.DEBUG and not hasattr(world, 'level_end_printed'):
print("Level beendet!")
world.level_end_printed = True
elif current_scene == "endscreen":
endscreen.draw()
elif current_scene == "gameover":
gameover.draw()
pygame.display.flip()
clock.tick(60)

@ -70,7 +70,7 @@ class Menu:
if self.start_button.collidepoint(event.pos):
self.input_active = True
self.show_main_buttons = False
if self.highscore_button.collidepoint(event.pos) and settings.DEBUG:
if settings.DEBUG:
print("Highscores anzeigen (später!)")
if self.input_active:
if self.back_button.collidepoint(event.pos):
@ -85,6 +85,21 @@ class Menu:
if settings.DEBUG:
print(f"Spielstart mit Spielername: {self.player_name}")
if event.type == pygame.JOYBUTTONDOWN:
if event.button == 2: # Button 2 auf den meisten Controllern ist Index 1
if self.show_main_buttons:
self.input_active = True
self.show_main_buttons = False
if settings.DEBUG:
print("Start gedrückt mit Controller")
elif self.input_active:
if self.player_name: # Name muss eingegeben sein
self.input_active = False
self.show_main_buttons = False
self.ready_to_start_game = True
if settings.DEBUG:
print(f"Spielstart mit Controller: {self.player_name}")
if event.type == pygame.KEYDOWN and self.input_active:
if event.key == pygame.K_RETURN:
if self.player_name: # Nur wenn ein Name eingegeben wurde

@ -1,10 +1,18 @@
import pygame
import settings
import random
class Obstacle:
def __init__(self, x, y, image_path):
self.image = pygame.image.load(image_path).convert_alpha()
self.image = pygame.transform.scale(self.image, (300, 150)) # Größe anpassen
def __init__(self, x):
type = random.randint(1,2)
if type == 1:
self.image = pygame.image.load(settings.world.obstacle.box).convert_alpha()
self.image = pygame.transform.scale(self.image, (400, 120)) # Größe anpassen
y = 530
if type == 2:
self.image = pygame.image.load(settings.world.obstacle.stone).convert_alpha()
self.image = pygame.transform.scale(self.image, (90, 80)) # Größe anpassen
y = 575
self.rect = self.image.get_rect(topleft=(x, y))
def draw(self, screen, camera_offset):

@ -1,6 +1,11 @@
import pygame
import settings
# Sound für das Einsammeln von Items laden
pygame.mixer.init()
pickup_sound = pygame.mixer.Sound("../assets/sounds/coin.mp3")
pickup_sound.set_volume(0.5)
class Player:
def __init__(self, x, y):
# PLAYER BILDER
@ -16,38 +21,81 @@ class Player:
self.image = self.image_normal # Anfangszustand
self.rect = self.image.get_rect(topleft=(x, y))
self.velocity_y = 0
self.speed = settings.player.speed
self.gravity = settings.player.gravity
self.jump_strength = settings.player.jump_strength
self.on_ground = False
def update(self, ground_y, obstacles=[]):
self.game_paused = False
self.gameOver = False
self.broken_bones = 0
self.last_collision_obstacle = None
def update(self, ground_y=80, obstacles=[], items=[], score=0, input_allowed=False):
keys = pygame.key.get_pressed()
joysticks = [pygame.joystick.Joystick(x) for x in range(pygame.joystick.get_count())]
move_left = keys[pygame.K_LEFT]
move_right = keys[pygame.K_RIGHT]
jump = keys[pygame.K_SPACE]
# Bewegung seitlich
if keys[pygame.K_LEFT]:
self.rect.x -= self.speed
if keys[pygame.K_RIGHT]:
self.rect.x += self.speed
# Controller-Eingaben prüfen
for joystick in joysticks:
if joystick.get_axis(0) < -0.5:
move_left = True
if joystick.get_axis(0) > 0.5:
move_right = True
if joystick.get_button(0):
jump = True
# Sprung
if keys[pygame.K_SPACE] and self.on_ground:
# --- Horizontale Bewegung
dx = 0
if move_left and input_allowed:
dx = -settings.player.speed
if move_right and input_allowed:
dx = settings.player.speed
self.rect.x += dx
for obs in obstacles:
if self.rect.colliderect(obs.get_rect()):
if dx > 0: # Bewegung nach rechts
self.rect.right = obs.get_rect().left - 10
if self.last_collision_obstacle != obs:
self.broken_bones += 1
self.last_collision_obstacle = obs
elif dx < 0: # Bewegung nach links
self.rect.left = obs.get_rect().right - 10
if self.last_collision_obstacle != obs:
self.broken_bones += 1
self.last_collision_obstacle = obs
# --- Vertikale Bewegung
if jump and self.on_ground and input_allowed:
self.velocity_y = self.jump_strength
self.on_ground = False
# Gravitation
self.velocity_y += self.gravity
self.rect.y += self.velocity_y
self.on_ground = False # zurücksetzen
self.on_ground = False
# Plattform-Kollision prüfen
for obs in obstacles:
if self.rect.colliderect(obs.get_rect()):
# nur wenn Spieler von oben kommt
if self.velocity_y > 0 and self.rect.bottom <= obs.get_rect().top + 20:
if self.velocity_y > 0: # Fallend
self.rect.bottom = obs.get_rect().top
self.velocity_y = 0
self.on_ground = True
self.last_collision_obstacle = None
elif self.velocity_y < 0: # Aufsteigend
self.rect.top = obs.get_rect().bottom
self.velocity_y = 0
# ITEM-KOLLISION
for item in items[:]:
if self.rect.colliderect(item.get_rect()):
items.remove(item)
score += 10
pickup_sound.play()
# Boden-Kollision
if self.rect.bottom >= ground_y:
@ -55,15 +103,25 @@ class Player:
self.velocity_y = 0
self.on_ground = True
# Bildlogik
# Ziel-Flagge erreicht
if not self.game_paused and self.rect.left > settings.world.goal.position_x:
self.game_paused = True
# CANCEL nach zu vielen Brüche
if self.broken_bones == 5:
self.gameOver = True
# Bild-Logik
if not self.on_ground:
self.image = self.image_jump
elif keys[pygame.K_LEFT]:
elif keys[pygame.K_LEFT] or move_left:
self.image = self.image_left
elif keys[pygame.K_RIGHT]:
elif keys[pygame.K_RIGHT] or move_right:
self.image = self.image_normal
else:
self.image = self.image_normal
return score
def draw(self, screen):
screen.blit(self.image, self.rect)

@ -1,11 +1,16 @@
import random
# Einstellungen
SCREEN_WIDTH = 1280
SCREEN_HEIGHT = 720
FPS = 60
DEBUG = True
DEBUG = False
DEBUG_KEY_INPUT = False
DEBUG_ENDSCREEN = False
Mute = True
START_POINT = "world" # world, menu
START_POINT = "menu" # world, menu
BACKGROUND_IMG = "../assets/backgrounds/background_III.png"
BACKGROUND_IMG_COMP = "../assets/backgrounds/background-complete.png"
# Schriftarten
class fonts:
@ -13,9 +18,9 @@ class fonts:
class player:
size = (110, 200)
speed = 12 if DEBUG else 5
gravity = 0.8
jump_strength = -20
speed = 25 if DEBUG else 17
gravity = 1.7
jump_strength = -30
class fred:
normal = "../assets/images/player/fred.png"
@ -23,6 +28,8 @@ class player:
left = "../assets/images/player/fred_back.png"
class world:
countdown = 3 #sekunden
brokenBone_image = "../assets/images/broken_bone.png"
class ground:
img = "../assets/world/ground_II.png"
crop_top = 500
@ -32,7 +39,30 @@ class world:
crop_scale_right = 1400
class obstacle:
amount = 15
box = "../assets/obstacles/box.png"
stone = "../assets/obstacles/stone.png"
class items:
amount = 23
class beer:
img = "../assets/images/items/bier.png"
size = (70, 85)
class goal:
img = "../assets/world/GoalFlag.png"
size = (110,160)
position_x = 29800
class icons:
class controller:
kreis = "../assets/images/controller/kreis.png"
viereck = "../assets/images/controller/viereck.png"
dreieck = "../assets/images/controller/dreieck.png"
kreuz = "../assets/images/controller/kreuz.png"
# Farben
class MyColors:

@ -3,15 +3,35 @@ import settings
from player import Player
from obstacle import Obstacle
import random
from item import Item
class World:
def __init__(self, screen):
def __init__(self, screen, player_name=""):
self.start_ticks = pygame.time.get_ticks()
self.level_started = False
self.level_start_time = None
self.time_passed = 0
# Hindernisse
self.obstacles = []
obstacle_count = random.randint(2, 3)
for _ in range(obstacle_count):
x = random.randint(500, 4000) # X-Position irgendwo in der Welt
self.obstacles.append(Obstacle(x, 520, settings.world.obstacle.box))
while len(self.obstacles) < settings.world.obstacle.amount:
x = random.randint(500, 29500)
# Prüfen, ob der neue x weit genug entfernt ist von bestehenden Hindernissen
if all(abs(x - obs.rect.x) > 800 for obs in self.obstacles):
self.obstacles.append(Obstacle(x))
# ITEMS
self.items = []
while len(self.items) < settings.world.items.amount:
x = random.randint(200, 29500)
y = random.randint(100, 480)
# Prüfen, ob der neue x weit genug entfernt ist von bestehenden Hindernissen
if all(abs(x - item.rect.x) > 200 for item in self.items):
self.items.append(Item(x, y))
# Welt Generierung
self.screen = screen
@ -19,7 +39,7 @@ class World:
self.bg_width = self.background.get_width()
# Boden
self.ground_rect = pygame.Rect(0, 650, 5000 * 2.7, 80) # Boden
self.ground_rect = pygame.Rect(0, 650, 5000 * 6, 80) # Boden
full_image = pygame.image.load(settings.world.ground.img).convert_alpha()
# --- Ausschneiden des Bereichs
cropped = pygame.Surface((settings.world.ground.crop_scale_right, settings.world.ground.crop_height), pygame.SRCALPHA)
@ -28,17 +48,42 @@ class World:
self.ground_image = pygame.transform.scale(cropped, (settings.world.ground.scale_width, settings.world.ground.scale_height))
self.tile_width = self.ground_image.get_width()
# ZIEL FLAGGEN
goal_flag_image = pygame.image.load(settings.world.goal.img).convert_alpha()
self.goal_flag = pygame.transform.scale(goal_flag_image, settings.world.goal.size)
# Knochenbrüche
brokenBone_image = pygame.image.load(settings.world.brokenBone_image).convert_alpha()
self.brokeBoneImage = pygame.transform.scale(brokenBone_image, (50,50))
# Spieler
self.player = Player(200, 500) # Spieler
# SCORE
self.save_score = 0
# Spielername
self.player_name = player_name
def draw(self):
self.screen.fill((0, 0, 0)) # Bildschirm löschen, bevor gezeichnet wird
camera_offset = self.player.rect.centerx - settings.SCREEN_WIDTH // 2
world_width = max(self.bg_width, max([obs.rect.right for obs in self.obstacles] + [self.ground_rect.right]))
camera_offset = max(0, min(camera_offset, world_width - settings.SCREEN_WIDTH))
self.player.update(self.ground_rect.top, self.obstacles)
score = self.player.update(
ground_y=self.ground_rect.top,
obstacles=self.obstacles,
items=self.items,
score=0,
input_allowed=self.level_started
)
if score != 0:
self.save_score += 10
print(self.save_score) if settings.DEBUG else None
bg_offset = int(camera_offset * 0.3) # Parallax: langsamer bewegen
bg_offset = int(camera_offset * 0.125) # Parallax: langsamer bewegen
self.screen.blit(self.background, (-bg_offset, 0))
# Boden kacheln ohne Parallax
start_tile = (camera_offset // self.tile_width) * self.tile_width
@ -52,14 +97,68 @@ class World:
for obs in self.obstacles:
obs.draw(self.screen, camera_offset)
for items in self.items:
items.draw(self.screen, camera_offset)
player_screen_x = self.player.rect.x - camera_offset
# ZIEL FLAGGEN BACK
self.screen.blit(self.goal_flag, (settings.world.goal.position_x - camera_offset, self.ground_rect.top-160))
# PLAYER
self.screen.blit(self.player.image, (player_screen_x, self.player.rect.y))
# ANZEIGE
font = pygame.font.Font(settings.fonts.PressStart2P, 32)
name_surface = font.render(self.player_name, True, (255, 255, 255))
score_surface = font.render(f"{self.save_score}", True, (255,255,0))
self.screen.blit(name_surface, (20, 20))
self.screen.blit(score_surface, (20, 120))
# KNOCHENBRÜCHE
if self.player.broken_bones < 5:
self.screen.blit(self.brokeBoneImage, (20, 50))
if self.player.broken_bones < 4:
self.screen.blit(self.brokeBoneImage, (70, 50))
if self.player.broken_bones < 3:
self.screen.blit(self.brokeBoneImage, (120, 50))
if self.player.broken_bones < 2:
self.screen.blit(self.brokeBoneImage, (170, 50))
if self.player.broken_bones < 1:
self.screen.blit(self.brokeBoneImage, (220, 50))
current_ticks = pygame.time.get_ticks()
elapsed_seconds = (current_ticks - self.start_ticks) // 1000
if not self.level_started:
countdown = max(0, settings.world.countdown - elapsed_seconds)
font = pygame.font.Font(settings.fonts.PressStart2P, 90)
if countdown > 0:
countdown_surface = font.render(str(countdown), True, settings.MyColors.yellow)
else:
countdown_surface = font.render("GO!", True, (0, 255, 0))
text_rect = countdown_surface.get_rect(center=(settings.SCREEN_WIDTH // 2, settings.SCREEN_HEIGHT // 2))
self.screen.blit(countdown_surface, text_rect)
if countdown == 0:
if self.level_start_time is None:
self.level_start_time = pygame.time.get_ticks()
if elapsed_seconds >= settings.world.countdown:
self.level_started = True
return # Solange Countdown läuft, alles andere nicht zeichnen
else:
self.time_passed = (pygame.time.get_ticks() - self.level_start_time) / 1000 # in Sekunden
def handle_event(self, event):
if not self.level_started:
return None # Keine Events verarbeiten, solange Countdown läuft
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
if settings.DEBUG:
print("ESC-Pressed")
return "menu"
# self.player.handle_event(event)
return None
Loading…
Cancel
Save

Powered by TurnKey Linux.