fadiinf-sose26/fertigesSpiel/flucht_aus_der_tiefsee.py

559 lines
20 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import sys
import math
import random
import pygame
# =====================================================================
# FLUCHT AUS DER TIEFSEE
# Steuerung: Pfeiltasten
# Start / Neustart: Leertaste
# =====================================================================
BREITE = 1100
HOEHE = 760
HUD_HOEHE = 125
FPS = 60
PAPIER = (247, 240, 222)
TINTE = (25, 42, 52)
WASSER_OBEN = (173, 224, 245)
WASSER_UNTEN = (91, 183, 224)
WASSER_LICHT = (210, 239, 250)
GRUEN = (117, 170, 105)
GRUEN_HELL = (151, 195, 126)
GRUEN_DUNKEL = (49, 104, 66)
PERLE = (231, 63, 145)
PERLE_HELL = (255, 157, 206)
QUALLE = (172, 139, 231)
QUALLE_HELL = (211, 195, 249)
UBOOT = (246, 180, 45)
UBOOT_HELL = (255, 213, 87)
TAUCHER = (42, 163, 166)
SPIELFELD = pygame.Rect(45, HUD_HOEHE + 18, BREITE - 90, HOEHE - HUD_HOEHE - 55)
random.seed(8)
# =====================================================================
# Hilfsfunktionen
# =====================================================================
def runde_box(flaeche, rect, farbe, randfarbe=TINTE, radius=14, rand=3):
pygame.draw.rect(flaeche, farbe, rect, border_radius=radius)
pygame.draw.rect(flaeche, randfarbe, rect, width=rand, border_radius=radius)
def text(flaeche, inhalt, schrift, farbe, pos, mitte=False):
bild = schrift.render(inhalt, True, farbe)
rect = bild.get_rect()
if mitte:
rect.center = pos
else:
rect.topleft = pos
flaeche.blit(bild, rect)
def zeichne_wellenlinie(flaeche, punkte, farbe, breite=3):
if len(punkte) >= 2:
pygame.draw.lines(flaeche, farbe, False, punkte, breite)
def organische_form(rect, seed):
"""Erzeugt eine unregelmäßige, aber geschlossene Fels-/Algenform."""
rng = random.Random(seed)
x, y, w, h = rect
punkte = []
abstand = 22
for px in range(x, x + w + 1, abstand):
punkte.append((px, y + rng.randint(-7, 8)))
for py in range(y + abstand, y + h + 1, abstand):
punkte.append((x + w + rng.randint(-8, 8), py))
for px in range(x + w - abstand, x - 1, -abstand):
punkte.append((px, y + h + rng.randint(-8, 8)))
for py in range(y + h - abstand, y, -abstand):
punkte.append((x + rng.randint(-8, 8), py))
return punkte
# =====================================================================
# Spielfiguren und Objekte
# =====================================================================
class Taucher:
def __init__(self, x, y):
self.start_x = x
self.start_y = y
self.x = x
self.y = y
self.breite = 42
self.hoehe = 54
self.geschwindigkeit = 3.5
self.leben = 3
self.sauerstoff = 100
self.perlen_gesammelt = 0
self.unverwundbar = False
self.unverwundbar_timer = 0
def rechteck(self):
return pygame.Rect(int(self.x), int(self.y), self.breite, self.hoehe)
def bewegen(self, dx, dy, spielfeld, hindernisse):
neues_x = self.x + dx * self.geschwindigkeit
test = pygame.Rect(int(neues_x), int(self.y), self.breite, self.hoehe)
if spielfeld.contains(test) and not any(h.rechteck().colliderect(test) for h in hindernisse):
self.x = neues_x
neues_y = self.y + dy * self.geschwindigkeit
test = pygame.Rect(int(self.x), int(neues_y), self.breite, self.hoehe)
if spielfeld.contains(test) and not any(h.rechteck().colliderect(test) for h in hindernisse):
self.y = neues_y
def update(self):
if self.unverwundbar:
self.unverwundbar_timer -= 1
if self.unverwundbar_timer <= 0:
self.unverwundbar = False
def leben_verlieren(self):
if not self.unverwundbar:
self.leben -= 1
self.unverwundbar = True
self.unverwundbar_timer = 90
def zuruecksetzen(self):
self.x = self.start_x
self.y = self.start_y
self.leben = 3
self.sauerstoff = 100
self.perlen_gesammelt = 0
self.unverwundbar = False
self.unverwundbar_timer = 0
def zeichnen(self, flaeche):
if self.unverwundbar and (self.unverwundbar_timer // 5) % 2 == 0:
return
x, y = int(self.x), int(self.y)
# Sauerstoffflasche
pygame.draw.rect(flaeche, (241, 176, 43), (x - 6, y + 18, 10, 28), border_radius=4)
pygame.draw.rect(flaeche, TINTE, (x - 6, y + 18, 10, 28), 2, border_radius=4)
# Körper
pygame.draw.rect(flaeche, TAUCHER, (x + 9, y + 25, 25, 23), border_radius=7)
pygame.draw.rect(flaeche, TINTE, (x + 9, y + 25, 25, 23), 2, border_radius=7)
# Helm
pygame.draw.circle(flaeche, TAUCHER, (x + 22, y + 18), 17)
pygame.draw.circle(flaeche, TINTE, (x + 22, y + 18), 17, 3)
# Visier
pygame.draw.ellipse(flaeche, (193, 231, 236), (x + 11, y + 9, 23, 17))
pygame.draw.ellipse(flaeche, TINTE, (x + 11, y + 9, 23, 17), 2)
pygame.draw.circle(flaeche, TINTE, (x + 18, y + 17), 2)
pygame.draw.circle(flaeche, TINTE, (x + 27, y + 17), 2)
# Arme
pygame.draw.line(flaeche, TINTE, (x + 10, y + 31), (x + 1, y + 38), 4)
pygame.draw.line(flaeche, TINTE, (x + 34, y + 31), (x + 42, y + 37), 4)
# Beine und Flossen
pygame.draw.line(flaeche, TINTE, (x + 16, y + 46), (x + 12, y + 53), 4)
pygame.draw.line(flaeche, TINTE, (x + 28, y + 46), (x + 31, y + 53), 4)
pygame.draw.polygon(flaeche, TAUCHER, [(x + 12, y + 52), (x + 3, y + 55), (x + 12, y + 47)])
pygame.draw.polygon(flaeche, TAUCHER, [(x + 31, y + 52), (x + 40, y + 55), (x + 31, y + 47)])
class Perle:
def __init__(self, x, y):
self.x, self.y = x, y
self.groesse = 26
self.eingesammelt = False
def rechteck(self):
return pygame.Rect(self.x, self.y, self.groesse, self.groesse)
def zeichnen(self, flaeche):
if self.eingesammelt:
return
mitte = (self.x + self.groesse // 2, self.y + self.groesse // 2)
pygame.draw.circle(flaeche, TINTE, mitte, 14)
pygame.draw.circle(flaeche, PERLE, mitte, 12)
pygame.draw.circle(flaeche, PERLE_HELL, (mitte[0] - 3, mitte[1] - 3), 7)
pygame.draw.circle(flaeche, (255, 245, 250), (mitte[0] - 5, mitte[1] - 6), 3)
class Hindernis:
def __init__(self, x, y, breite, hoehe, seed):
self._rechteck = pygame.Rect(x, y, breite, hoehe)
self.punkte = organische_form((x, y, breite, hoehe), seed)
self.seed = seed
def rechteck(self):
return self._rechteck.inflate(-10, -10)
def zeichnen(self, flaeche):
pygame.draw.polygon(flaeche, GRUEN_DUNKEL, self.punkte)
innen = []
cx, cy = self._rechteck.center
for px, py in self.punkte:
innen.append((int(px + (cx - px) * 0.08), int(py + (cy - py) * 0.08)))
pygame.draw.polygon(flaeche, GRUEN, innen)
rng = random.Random(self.seed)
for _ in range(max(5, self._rechteck.width * self._rechteck.height // 8000)):
px = rng.randint(self._rechteck.left + 12, self._rechteck.right - 12)
py = rng.randint(self._rechteck.top + 12, self._rechteck.bottom - 12)
pygame.draw.circle(flaeche, GRUEN_HELL, (px, py), rng.randint(3, 7))
pygame.draw.circle(flaeche, GRUEN_DUNKEL, (px, py), rng.randint(1, 3), 1)
# kleine Algenknubbel am Rand
for i, (px, py) in enumerate(self.punkte[::2]):
r = 5 + (i % 3)
pygame.draw.circle(flaeche, GRUEN_HELL, (px, py), r)
pygame.draw.circle(flaeche, GRUEN_DUNKEL, (px, py), r, 2)
class Qualle:
def __init__(self, start_x, end_x, y, geschwindigkeit):
self.start_x, self.end_x = start_x, end_x
self.x, self.y = start_x, y
self.groesse = 46
self.geschwindigkeit = geschwindigkeit
self.richtung = 1
self.phase = random.random() * math.tau
def update(self):
self.x += self.geschwindigkeit * self.richtung
if self.x >= self.end_x or self.x <= self.start_x:
self.richtung *= -1
self.phase += 0.06
def rechteck(self):
return pygame.Rect(int(self.x), int(self.y), self.groesse, self.groesse + 12)
def zeichnen(self, flaeche):
x, y = int(self.x), int(self.y + math.sin(self.phase) * 4)
kuppel = pygame.Rect(x, y, self.groesse, 31)
pygame.draw.ellipse(flaeche, TINTE, kuppel)
pygame.draw.ellipse(flaeche, QUALLE, kuppel.inflate(-4, -4))
pygame.draw.ellipse(flaeche, QUALLE_HELL, (x + 8, y + 5, 19, 11))
# Unterkante etwas flacher abdecken
pygame.draw.rect(flaeche, QUALLE, (x + 3, y + 17, self.groesse - 6, 12))
pygame.draw.line(flaeche, TINTE, (x + 3, y + 29), (x + self.groesse - 3, y + 29), 2)
# Gesicht
pygame.draw.circle(flaeche, TINTE, (x + 16, y + 18), 2)
pygame.draw.circle(flaeche, TINTE, (x + 30, y + 18), 2)
pygame.draw.arc(flaeche, TINTE, (x + 19, y + 18, 9, 7), 0.1, math.pi - 0.1, 2)
# gewellte Tentakel
for i in range(4):
tx = x + 8 + i * 10
punkte = []
for s in range(6):
punkte.append((tx + int(math.sin(self.phase + s) * 3), y + 30 + s * 5))
zeichne_wellenlinie(flaeche, punkte, TINTE, 2)
class Uboot:
def __init__(self, x, y):
self._rechteck = pygame.Rect(x, y, 145, 78)
def rechteck(self):
return self._rechteck
def zeichnen(self, flaeche):
x, y, w, h = self._rechteck
# Propeller
pygame.draw.line(flaeche, TINTE, (x + w - 2, y + h // 2), (x + w + 17, y + h // 2), 4)
pygame.draw.polygon(flaeche, UBOOT, [(x + w + 15, y + 17), (x + w + 25, y + 38), (x + w + 15, y + 58)])
pygame.draw.polygon(flaeche, TINTE, [(x + w + 15, y + 17), (x + w + 25, y + 38), (x + w + 15, y + 58)], 2)
# Körper
pygame.draw.ellipse(flaeche, TINTE, self._rechteck)
pygame.draw.ellipse(flaeche, UBOOT, self._rechteck.inflate(-5, -5))
pygame.draw.ellipse(flaeche, UBOOT_HELL, (x + 24, y + 8, 80, 25))
# Nase
pygame.draw.circle(flaeche, (130, 196, 214), (x + 17, y + 39), 15)
pygame.draw.circle(flaeche, TINTE, (x + 17, y + 39), 15, 3)
# Bullaugen
for px in (50, 77, 104):
pygame.draw.circle(flaeche, TINTE, (x + px, y + 39), 10)
pygame.draw.circle(flaeche, (213, 239, 244), (x + px, y + 39), 7)
# Turm und Periskop
pygame.draw.rect(flaeche, UBOOT, (x + 59, y - 13, 31, 18), border_radius=6)
pygame.draw.rect(flaeche, TINTE, (x + 59, y - 13, 31, 18), 3, border_radius=6)
pygame.draw.line(flaeche, TINTE, (x + 74, y - 13), (x + 74, y - 31), 4)
pygame.draw.line(flaeche, TINTE, (x + 74, y - 31), (x + 90, y - 31), 4)
# Flossen
pygame.draw.polygon(flaeche, UBOOT, [(x + 56, y + 66), (x + 43, y + 83), (x + 78, y + 80)])
pygame.draw.polygon(flaeche, TINTE, [(x + 56, y + 66), (x + 43, y + 83), (x + 78, y + 80)], 2)
# =====================================================================
# Level
# =====================================================================
def erstelle_level():
taucher = Taucher(70, HOEHE - 110)
hindernisse = [
Hindernis(95, 165, 300, 55, 1),
Hindernis(350, 165, 55, 250, 2),
Hindernis(95, 345, 280, 55, 3),
Hindernis(95, 345, 55, 210, 4),
Hindernis(390, 470, 300, 58, 5),
Hindernis(335, 505, 62, 135, 6),
# Die beiden senkrechten Algenwände sind bewusst kürzer.
# Dadurch entstehen unten zwei ausreichend breite Durchgänge:
# einer zur oberen Perle und einer weiter zum U-Boot.
Hindernis(665, 165, 58, 170, 7),
Hindernis(665, 400, 240, 58, 8),
Hindernis(845, 165, 58, 170, 9),
# Rechts bleibt neben dieser Wand ein freier Weg nach oben.
Hindernis(930, 455, 58, 130, 10),
]
perlen = [
Perle(190, 225),
Perle(165, 430),
Perle(455, 235),
Perle(760, 310),
Perle(820, 585),
Perle(590, 575),
Perle(360, 650),
]
quallen = [
Qualle(280, 470, 255, 1.4),
Qualle(745, 865, 300, 1.6),
Qualle(230, 340, 505, 1.2),
]
uboot = Uboot(895, 175)
return taucher, hindernisse, perlen, quallen, uboot
# =====================================================================
# Hintergrund und HUD
# =====================================================================
def zeichne_wasser(flaeche, blasen, pflanzen):
for y in range(HUD_HOEHE, HOEHE):
t = (y - HUD_HOEHE) / (HOEHE - HUD_HOEHE)
farbe = tuple(int(WASSER_OBEN[i] + (WASSER_UNTEN[i] - WASSER_OBEN[i]) * t) for i in range(3))
pygame.draw.line(flaeche, farbe, (0, y), (BREITE, y))
# dezente Papier-/Kreidestruktur
for bx, by, r in blasen:
pygame.draw.circle(flaeche, WASSER_LICHT, (bx, by), r, 1)
for x, y, hoehe in pflanzen:
for versatz in (-8, 0, 8):
punkte = []
for s in range(6):
punkte.append((x + versatz + int(math.sin(s * 0.9 + versatz) * 4), y - s * hoehe // 6))
zeichne_wellenlinie(flaeche, punkte, (51, 149, 141), 3)
def zeichne_rahmen(flaeche):
pygame.draw.rect(flaeche, PAPIER, (0, 0, BREITE, HUD_HOEHE))
pygame.draw.rect(flaeche, PAPIER, (0, HOEHE - 25, BREITE, 25))
pygame.draw.rect(flaeche, TINTE, (10, 10, BREITE - 20, HOEHE - 20), 3)
pygame.draw.rect(flaeche, TINTE, SPIELFELD, 3)
def zeichne_hud(flaeche, taucher, gesamt, schrift_titel, schrift_hud, schrift_klein):
text(flaeche, "Flucht aus der Tiefsee", schrift_titel, TINTE, (BREITE // 2, 36), mitte=True)
boxen = [
(pygame.Rect(40, 72, 245, 45), f"● Perlen: {taucher.perlen_gesammelt} / {gesamt}", PERLE),
(pygame.Rect(305, 72, 245, 45), f"▣ Sauerstoff: {taucher.sauerstoff}", (45, 140, 220)),
(pygame.Rect(570, 72, 190, 45), f"♥ Leben: {taucher.leben}", (220, 63, 52)),
]
for rect, beschriftung, farbe in boxen:
runde_box(flaeche, rect, PAPIER)
text(flaeche, beschriftung, schrift_hud, farbe, (rect.x + 14, rect.y + 9))
ziel_box = pygame.Rect(780, 72, 285, 45)
runde_box(flaeche, ziel_box, PAPIER)
text(flaeche, "★ Sammle alle Perlen!", schrift_klein, TINTE, (ziel_box.x + 13, ziel_box.y + 11))
def zeichne_start_ziel(flaeche, schrift):
text(flaeche, "↑ START", schrift, (20, 108, 96), (72, HOEHE - 52))
text(flaeche, "← ZIEL", schrift, (208, 79, 34), (992, 158))
def zeichne_overlay(flaeche, titel, untertitel, hinweis, gross, mittel):
overlay = pygame.Surface((BREITE, HOEHE), pygame.SRCALPHA)
overlay.fill((13, 31, 44, 175))
flaeche.blit(overlay, (0, 0))
box = pygame.Rect(BREITE // 2 - 330, HOEHE // 2 - 115, 660, 230)
runde_box(flaeche, box, PAPIER, TINTE, 22, 4)
text(flaeche, titel, gross, TINTE, (BREITE // 2, box.y + 55), mitte=True)
text(flaeche, untertitel, mittel, TINTE, (BREITE // 2, box.y + 115), mitte=True)
text(flaeche, hinweis, mittel, (30, 111, 143), (BREITE // 2, box.y + 165), mitte=True)
# =====================================================================
# Hauptprogramm
# =====================================================================
def main():
pygame.init()
pygame.display.set_caption("Flucht aus der Tiefsee")
bildschirm = pygame.display.set_mode((BREITE, HOEHE))
uhr = pygame.time.Clock()
schrift_titel = pygame.font.SysFont("Comic Sans MS", 38, bold=True)
schrift_gross = pygame.font.SysFont("Comic Sans MS", 44, bold=True)
schrift_mittel = pygame.font.SysFont("Comic Sans MS", 22)
schrift_hud = pygame.font.SysFont("Comic Sans MS", 21, bold=True)
schrift_klein = pygame.font.SysFont("Comic Sans MS", 18, bold=True)
taucher, hindernisse, perlen, quallen, uboot = erstelle_level()
blasen = [
(random.randint(SPIELFELD.left + 10, SPIELFELD.right - 10),
random.randint(SPIELFELD.top + 10, SPIELFELD.bottom - 10),
random.choice((1, 1, 2, 2, 3)))
for _ in range(95)
]
pflanzen = [
(140, 690, 50), (245, 655, 38), (525, 300, 52),
(735, 685, 45), (865, 635, 58), (965, 320, 42),
(610, 210, 40), (470, 655, 35)
]
zustand = "start"
end_nachricht = ""
sauerstoff_zaehler = 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
if zustand == "start":
zustand = "laeuft"
elif zustand in ("gewonnen", "verloren"):
taucher.zuruecksetzen()
for perle in perlen:
perle.eingesammelt = False
for qualle in quallen:
qualle.x = qualle.start_x
qualle.richtung = 1
sauerstoff_zaehler = 0
end_nachricht = ""
zustand = "laeuft"
if zustand == "laeuft":
tasten = pygame.key.get_pressed()
dx = tasten[pygame.K_RIGHT] - tasten[pygame.K_LEFT]
dy = tasten[pygame.K_DOWN] - tasten[pygame.K_UP]
taucher.bewegen(dx, dy, SPIELFELD, hindernisse)
taucher.update()
for qualle in quallen:
qualle.update()
sauerstoff_zaehler += 1
if sauerstoff_zaehler >= 42:
taucher.sauerstoff = max(0, taucher.sauerstoff - 1)
sauerstoff_zaehler = 0
for perle in perlen:
if not perle.eingesammelt and perle.rechteck().colliderect(taucher.rechteck()):
perle.eingesammelt = True
taucher.perlen_gesammelt += 1
for qualle in quallen:
if qualle.rechteck().colliderect(taucher.rechteck()):
taucher.leben_verlieren()
alle_perlen = all(perle.eingesammelt for perle in perlen)
if alle_perlen and uboot.rechteck().colliderect(taucher.rechteck()):
zustand = "gewonnen"
end_nachricht = "Du bist sicher im U-Boot angekommen!"
if taucher.sauerstoff <= 0:
zustand = "verloren"
end_nachricht = "Dir ist der Sauerstoff ausgegangen!"
elif taucher.leben <= 0:
zustand = "verloren"
end_nachricht = "Die Quallen waren dieses Mal schneller."
bildschirm.fill(PAPIER)
zeichne_wasser(bildschirm, blasen, pflanzen)
for hindernis in hindernisse:
hindernis.zeichnen(bildschirm)
for perle in perlen:
perle.zeichnen(bildschirm)
for qualle in quallen:
qualle.zeichnen(bildschirm)
uboot.zeichnen(bildschirm)
taucher.zeichnen(bildschirm)
zeichne_rahmen(bildschirm)
zeichne_hud(bildschirm, taucher, len(perlen), schrift_titel, schrift_hud, schrift_klein)
zeichne_start_ziel(bildschirm, schrift_klein)
if zustand == "start":
zeichne_overlay(
bildschirm,
"FLUCHT AUS DER TIEFSEE",
"Sammle alle Perlen und erreiche danach das U-Boot.",
"Mit LEERTASTE starten bewegen mit den PFEILTASTEN",
schrift_gross,
schrift_mittel,
)
elif zustand == "gewonnen":
zeichne_overlay(
bildschirm,
"GESCHAFFT!",
end_nachricht,
"Leertaste für ein neues Spiel",
schrift_gross,
schrift_mittel,
)
elif zustand == "verloren":
zeichne_overlay(
bildschirm,
"SPIEL VORBEI",
end_nachricht,
"Leertaste für einen neuen Versuch",
schrift_gross,
schrift_mittel,
)
pygame.display.flip()
uhr.tick(FPS)
if __name__ == "__main__":
main()