Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
50 changes: 50 additions & 0 deletions classes/GameManager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import json
import os
#change
class CoinMemento:
"""Stores the coin count as a memento."""
def __init__(self, coin_count):
self.coin_count = coin_count # Make sure this is publicly accessible

def get_coin_count(self):
"""Returns the stored coin count."""
return self.coin_count

class CoinCaretaker:
"""Manages saving and loading of coin mementos."""
SAVE_FILE = "entities/data/coin_data.json" # Path to JSON storage file

@staticmethod
def save_memento(memento):
"""Saves the coin count to a JSON file."""
os.makedirs(os.path.dirname(CoinCaretaker.SAVE_FILE), exist_ok=True) # Create folder if missing
with open(CoinCaretaker.SAVE_FILE, "w") as file:
json.dump({"coin_count": memento.get_coin_count()}, file)

@staticmethod
def load_memento():
"""Loads the saved coin count from a JSON file, with error handling."""
if os.path.exists(CoinCaretaker.SAVE_FILE):
try:
with open(CoinCaretaker.SAVE_FILE, "r") as file:
data = json.load(file)
return CoinMemento(data.get("coin_count", 0))
except (json.JSONDecodeError, IOError):
print("⚠️ Error loading coin data. Resetting to 0.")
return CoinMemento(0)
return CoinMemento(0) # Default if file doesn't exist

class GameManager:
"""Manages the coin collection system."""
def __init__(self):
self.coin_memento = CoinCaretaker.load_memento()

def save_coin_count(self, coin_count):
"""Saves the current coin count."""
self.coin_memento = CoinMemento(coin_count)
CoinCaretaker.save_memento(self.coin_memento)

def load_coin_count(self):
"""Loads the saved coin count."""
self.coin_memento = CoinCaretaker.load_memento()
return self.coin_memento.coin_count # Access the correct attribute
34 changes: 33 additions & 1 deletion classes/Level.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@
from entities.CoinBox import CoinBox
from entities.RandomBox import RandomBox

#new classes needed for win condition implementation
from classes.Observer import Subject
from entities.EndGoal import Goal

class Level:
#level class is now a subject for the observer design pattern
class Level(Subject):
def __init__(self, screen, sound, dashboard):
Subject.__init__(self)
self.sprites = Sprites()
self.dashboard = dashboard
self.sound = sound
Expand All @@ -22,14 +27,23 @@ def __init__(self, screen, sound, dashboard):
self.levelLength = 0
self.entityList = []

#added for the win condition implemetation
self.currentLevelName = ""
self.completed_levels = set()

def loadLevel(self, levelname):

with open("./levels/{}.json".format(levelname)) as jsonData:
data = json.load(jsonData)
self.loadLayers(data)
self.loadObjects(data)
self.loadEntities(data)
self.levelLength = data["length"]

#added for win condition implementation
goal_x = self.levelLength - 2
self.addGoal(goal_x, 8)

def loadEntities(self, data):
try:
[self.addCoinBox(x, y) for x, y in data["level"]["entities"]["CoinBox"]]
Expand Down Expand Up @@ -203,3 +217,21 @@ def addRedMushroom(self, x, y):
self.entityList.append(
RedMushroom(self.screen, self.sprites.spriteCollection, x, y, self, self.sound)
)



#added for win condition implementation
def addGoal(self, x, y):
#add flag to the level end point to show where the goal is
self.entityList.append(
Goal(self.screen, self.sprites.spriteCollection, x, y, self)
)

def mark_level_complete(self):
#add the current level to completed levels
self.completed_levels.add(self.currentLevelName)
#need to notify observers about level completion
self.notify_observers("level_complete", level_name=self.currentLevelName)

def is_level_completed(self, level_name):
return level_name in self.completed_levels
7 changes: 7 additions & 0 deletions classes/Menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,16 @@ def drawLevelChooser(self):
if i < 3:
self.dashboard.drawText(levelName, 175*i+textOffset, 100, 12)
self.drawBorder(175*i+offset, 55, 125, 75, color, 5)

#to show the level is completed
if self.level.is_level_completed(levelName):
self.dashboard.drawText("★", 175*i+textOffset+45, 130, 18)
else:
self.dashboard.drawText(levelName, 175*j+textOffset, 250, 12)
self.drawBorder(175*j+offset, 210, 125, 75, color, 5)
#to show level is completed
if self.level.is_level_completed(levelName):
self.dashboard.drawText("★", 175*j+textOffset+45, 280, 18)
j += 1

def loadLevelNames(self):
Expand Down
23 changes: 23 additions & 0 deletions classes/Observer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#added for group 32 project
#this is the observer class, to implement the observer design pattern
#for the level win conditions

class Subject:
def __init__(self):
self.observers = []

def register_observer(self, observer):
if observer not in self.observers:
self.observers.append(observer)

def remove_observer(self, observer):
if observer in self.observers:
self.observers.remove(observer)

def notify_observers(self, *args, **kwargs):
for observer in self.observers:
observer.notify(*args, **kwargs)

class Observer:
def notify(self, *args, **kwargs):
raise NotImplementedError("subclass must use this notify method")
34 changes: 34 additions & 0 deletions classes/VictoryScreen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#added for group 32 project
import pygame

class VictoryScreen:
def __init__(self, screen, dashboard, sound, level_id):
self.screen = screen
self.dashboard = dashboard
self.sound = sound
self.level_id = level_id
self.active = False
self.timeout = 0
self.return_to_menu = False

def activate(self):
self.active = True
self.timeout = pygame.time.get_ticks() + 3000 #show screen for 3 seconds

def update(self):
if self.active:
#creating the background
overlay = pygame.Surface((640, 480))
overlay.set_alpha(200)
overlay.fill((0, 0, 0))
self.screen.blit(overlay, (0, 0))

#victory message
self.dashboard.drawText("Congratulations!", 180, 200, 24)
self.dashboard.drawText("You Won!", 190, 250, 36)

if pygame.time.get_ticks() > self.timeout:
self.active = False
self.return_to_menu = True

return self.return_to_menu
12 changes: 10 additions & 2 deletions entities/Coin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from copy import copy

#edit-for coin new branch for merging
from entities.EntityBase import EntityBase

from classes.GameManager import GameManager # Import GameManager to manage coins

class Coin(EntityBase):
def __init__(self, screen, spriteCollection, x, y, gravity=0):
Expand All @@ -12,6 +12,14 @@ def __init__(self, screen, spriteCollection, x, y, gravity=0):
self.type = "Item"

def update(self, cam):
"""Updates coin animation."""
if self.alive:
self.animation.update()
self.screen.blit(self.animation.image, (self.rect.x + cam.x, self.rect.y))

def collect(self, mario):
"""Removes coin and updates GameManager."""
self.alive = False # Remove coin from game
mario.dashboard.coins += 1 # Increase Mario's coin count
GameManager().save_coin_count(mario.dashboard.coins) # Persist coin count

24 changes: 24 additions & 0 deletions entities/EndGoal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#added for group 32 project

import pygame

class Goal(pygame.sprite.Sprite):
def __init__(self, screen, sprite_collection, x, y, level):
pygame.sprite.Sprite.__init__(self)
self.sprite_collection = sprite_collection
self.image = self.sprite_collection.get("pipeR").image
self.rect = pygame.Rect(x * 32, y * 32, 32, 64)
self.screen = screen
self.x = x
self.y = y
self.level = level
self.triggered = False
self.type = "Goal"

def update(self, camera):
if not self.triggered:
self.screen.blit(
self.image,
((self.x + camera.pos.x) * 32, self.y * 32)
)

69 changes: 61 additions & 8 deletions entities/Mario.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import pygame

import sys
import os
# Add the project root directory to sys.path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from classes.Animation import Animation
from classes.Camera import Camera
from classes.Collider import Collider
Expand All @@ -12,6 +15,10 @@
from traits.go import GoTrait
from traits.jump import JumpTrait
from classes.Pause import Pause
from classes.GameManager import GameManager # Import GameManager to handle coins
#needed for win condition
from classes.Observer import Observer
from classes.VictoryScreen import VictoryScreen

spriteCollection = Sprites().spriteCollection
smallAnimation = Animation(
Expand All @@ -33,8 +40,8 @@
spriteCollection["mario_big_jump"].image,
)


class Mario(EntityBase):
#mario needs to be the observer for win condition
class Mario(EntityBase, Observer):
def __init__(self, x, y, level, screen, dashboard, sound, gravity=0.8):
super(Mario, self).__init__(x, y, gravity)
self.camera = Camera(self.rect, self)
Expand All @@ -51,17 +58,32 @@ def __init__(self, x, y, level, screen, dashboard, sound, gravity=0.8):
}

self.levelObj = level
self.levelObj.register_observer(self) #for win condition, register mario as observer of level
self.collision = Collider(self, level)
self.screen = screen
self.EntityCollider = EntityCollider(self)
self.dashboard = dashboard
self.restart = False
self.pause = False
self.pauseObj = Pause(screen, self, dashboard)
# Load saved coin count from GameManager
self.dashboard.coins = GameManager().load_coin_count()
#for victory screen
self.victory = False
self.victory_screen = VictoryScreen(screen, dashboard, sound, level.currentLevelName)


def update(self):
if self.invincibilityFrames > 0:
self.invincibilityFrames -= 1

#for victory screen, check if victory is active
if self.victory:
#updates the victory screen and check if we should return to menu
if self.victory_screen.update():
self.restart = True
return

self.updateTraits()
self.moveMario()
self.camera.move()
Expand All @@ -85,12 +107,35 @@ def checkEntityCollision(self):
self._onCollisionWithBlock(ent)
elif ent.type == "Mob":
self._onCollisionWithMob(ent, collisionState)

#goal, for checking win condition
elif ent.type == "Goal":
self._onCollisionWithGoal(ent)

#for win condition
def _onCollisionWithGoal(self, goal):
if not self.victory:
#stop moveing
self.vel.x = 0
self.vel.y = 0

goal.triggered = True

#level is completed
self.levelObj.mark_level_complete()

#open victory screen
self.victory = True
self.victory_screen.activate()
#edit-for coin new branch for merging
def _onCollisionWithItem(self, item):
self.levelObj.entityList.remove(item)
self.dashboard.points += 100
self.dashboard.coins += 1
self.sound.play_sfx(self.sound.coin)
"""Handles collision with items (coins, power-ups, etc.)."""
if isinstance(item, EntityBase) and item.type == "Item":
self.levelObj.entityList.remove(item)
self.dashboard.points += 100
self.dashboard.coins += 1 # Increment coin count
self.sound.play_sfx(self.sound.coin)
# Save the updated coin count using GameManager
GameManager().save_coin_count(self.dashboard.coins) # Pass the coin count here

def _onCollisionWithBlock(self, block):
if not block.triggered:
Expand Down Expand Up @@ -186,3 +231,11 @@ def powerup(self, powerupID):
self.traits['goTrait'].updateAnimation(bigAnimation)
self.rect = pygame.Rect(self.rect.x, self.rect.y-32, 32, 64)
self.invincibilityFrames = 20


#for win condition, observer
def notify(self, event_type, **kwargs):
if event_type == "level_complete":
pass


1 change: 1 addition & 0 deletions entities/data/coin_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"coin_count": 60}
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pygame==2.0.0.dev10
pygame==2.0.0.dev12
scipy==1.4.1
21 changes: 21 additions & 0 deletions venv/Include/site/python3.11/pygame/_blit_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#define NO_PYGAME_C_API
#include "_surface.h"

/* The structure passed to the low level blit functions */
typedef struct {
int width;
int height;
Uint8 *s_pixels;
int s_pxskip;
int s_skip;
Uint8 *d_pixels;
int d_pxskip;
int d_skip;
SDL_PixelFormat *src;
SDL_PixelFormat *dst;
Uint8 src_blanket_alpha;
int src_has_colorkey;
Uint32 src_colorkey;
SDL_BlendMode src_blend;
SDL_BlendMode dst_blend;
} SDL_BlitInfo;
Loading