sep-pm-platformer/project/main.py

205 lines
8.2 KiB
Python

import sys
from typing import Optional
import pygame
from level.Level import Level
from level.LevelManager import LevelManager
from level.selection.LevelScreenManager import LevelScreenManager
from level.selection.LevelSelectionScreenManager import LevelSelectionScreenManager
from level.selection.MainMenuScreenManager import MainMenuScreenManager
from level.selection.OptionsScreenManager import OptionsScreenManager
from level.selection.ScreenManager import ScreenManager
from level.selection.WinScreenManager import WinScreenManager
from physics import ConstantsParser
from physics.SpriteManager import SpriteManager
from physics.TickData import TickData
from sprite.PositionScale import PositionScale
from sprite.SpritesheetManager import SpritesheetManager
from ui_elements.ClickEvent import ClickEvent
from ui_elements.KeyManager import KeyManager
BACKGROUND_IMAGES_UNSCALED = {
'tutorial': pygame.image.load('data/sprites/tutorial_bg.png'),
'castle': pygame.image.load('data/sprites/castle_bg.png'),
'cave': pygame.image.load('data/sprites/cave_bg.png'),
'menu': pygame.image.load('data/sprites/intro_bg.png'),
'level_select': pygame.image.load('data/sprites/level_select_bg.png'),
'win': pygame.image.load('data/sprites/win_bg.png'),
}
BACKGROUND_IMAGES_SCALED = {
'tutorial': pygame.transform.scale(BACKGROUND_IMAGES_UNSCALED['tutorial'], (1, 1)),
'castle': pygame.transform.scale(BACKGROUND_IMAGES_UNSCALED['castle'], (1, 1)),
'cave': pygame.transform.scale(BACKGROUND_IMAGES_UNSCALED['cave'], (1, 1)),
'menu': pygame.transform.scale(BACKGROUND_IMAGES_UNSCALED['menu'], (1, 1)),
'level_select': pygame.transform.scale(BACKGROUND_IMAGES_UNSCALED['level_select'], (1, 1)),
'win': pygame.transform.scale(BACKGROUND_IMAGES_UNSCALED['win'], (1, 1)),
}
def apply_frame_rate(number: float, frame_rate: float = 30):
"""
this function calculates a factor that will be multiplied with the
physics of the game to provide a constant speed
:param frame_rate: the frame rate of the game
:param number: The number to scale by the factor
:return: The scaled number
"""
return number / (frame_rate / 30)
class MainLoop:
def __init__(self):
self.GAME_STATE_MENU = 'main_menu'
self.GAME_STATE_LEVEL_SELECTION = 'level_selection'
self.GAME_STATE_LEVEL = 'level'
self.GAME_STATE_OPTIONS = 'options'
self.GAME_STATE_WIN = 'win'
self.screen_transform: PositionScale = PositionScale((0, 0), (1.5, 1.5))
self.window_size: tuple[float, float] = (1, 1)
pygame.init()
pygame.display.set_caption("Don't Get Busted")
self.update_position_scale(self.screen_transform)
self.screen = self.screen
self.clock = pygame.time.Clock()
self.frame_rate = 30
self.spritesheet_manager = SpritesheetManager("data/sprites", "data/sprites/sprites.json")
self.sprite_manager = SpriteManager()
self.key_manager = KeyManager()
self.parsed_levels_manager = LevelManager('data/levels')
self.parsed_levels_manager.load_from_config('data/levels/levels.json')
self.screen_manager: Optional[ScreenManager] = None
self.game_state = self.GAME_STATE_MENU
self.set_game_state(self.GAME_STATE_MENU)
self.level: Optional[Level] = None
def update_position_scale(self, position_scale: PositionScale):
self.screen_transform = position_scale
self.window_size = (
self.screen_transform.scale[0] * ConstantsParser.CONFIG.block_size[0] *
ConstantsParser.CONFIG.level_size[0],
self.screen_transform.scale[1] * ConstantsParser.CONFIG.block_size[1] *
ConstantsParser.CONFIG.level_size[1]
)
self.screen = pygame.display.set_mode((self.window_size[0], self.window_size[1]))
for key in BACKGROUND_IMAGES_UNSCALED:
BACKGROUND_IMAGES_SCALED[key] = pygame.transform.scale(
BACKGROUND_IMAGES_UNSCALED[key], self.window_size
)
def select_level(self, level: Level):
print('Loading level', level.name)
self.level = level
self.set_game_state(self.GAME_STATE_LEVEL)
def select_level_selection(self, theme: str = 'tutorial'):
self.set_game_state(self.GAME_STATE_LEVEL_SELECTION)
if self.screen_manager is not None and isinstance(self.screen_manager, LevelSelectionScreenManager):
self.screen_manager.select_theme(theme)
def player_death(self, level: Level):
self.select_level(level)
def player_success(self, level: Level):
next_level = self.parsed_levels_manager.find_next_level(level)
if next_level is not None:
self.select_level(next_level)
else:
self.set_game_state(self.GAME_STATE_WIN)
def select_main_menu(self):
self.set_game_state(self.GAME_STATE_MENU)
def select_options(self):
self.set_game_state(self.GAME_STATE_OPTIONS)
def set_game_state(self, game_state: str):
self.game_state = game_state
if self.screen_manager is not None:
self.screen_manager.destroy()
if self.game_state == self.GAME_STATE_MENU:
self.screen_manager = MainMenuScreenManager(
self.sprite_manager, self.spritesheet_manager, self
)
elif self.game_state == self.GAME_STATE_LEVEL:
self.screen_manager = LevelScreenManager(
self.sprite_manager, self.spritesheet_manager, self, self.level
)
elif self.game_state == self.GAME_STATE_LEVEL_SELECTION:
self.screen_manager = LevelSelectionScreenManager(
self.sprite_manager, self.spritesheet_manager, self, self.parsed_levels_manager
)
elif self.game_state == self.GAME_STATE_OPTIONS:
self.screen_manager = OptionsScreenManager(
self.sprite_manager, self.spritesheet_manager, self,
)
elif self.game_state == self.GAME_STATE_WIN:
self.screen_manager = WinScreenManager(
self.sprite_manager, self.spritesheet_manager, self,
)
else:
print('Invalid game state', self.game_state)
if self.screen_manager is not None:
self.screen_manager.initialize()
def main_loop(self):
while True:
self.clock.tick(self.frame_rate)
pygame_events: list[pygame.event.Event] = pygame.event.get()
self.key_manager.update_key_events(pygame_events)
click_events: list[ClickEvent] = ClickEvent.create_events(pygame_events, self.screen_transform)
for event in click_events:
for layer in self.sprite_manager.layers:
for sprite in self.sprite_manager.layers[layer]:
if sprite.get_bounding_box().contains_point(event.world_position):
sprite.click(event)
for event in pygame_events:
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
self.screen.fill((0, 0, 0))
tick_data = TickData(apply_frame_rate(1, self.frame_rate),
pygame_events,
self.key_manager,
click_events,
self.screen_transform)
self.screen_manager.tick(tick_data)
self.sprite_manager.tick(tick_data)
if self.level is not None and self.game_state == self.GAME_STATE_LEVEL:
self.screen.blit(BACKGROUND_IMAGES_SCALED[self.level.theme], (0, 0))
elif self.game_state == self.GAME_STATE_MENU:
self.screen.blit(BACKGROUND_IMAGES_SCALED['menu'], (0, 0))
elif self.game_state == self.GAME_STATE_LEVEL_SELECTION or self.game_state == self.GAME_STATE_OPTIONS:
self.screen.blit(BACKGROUND_IMAGES_SCALED['level_select'], (0, 0))
elif self.game_state == self.GAME_STATE_WIN:
self.screen.blit(BACKGROUND_IMAGES_SCALED['win'], (0, 0))
self.sprite_manager.draw(self.screen, self.screen_transform)
pygame.display.update()
main_loop: MainLoop = MainLoop()
main_loop.main_loop()