2023-03-29 15:06:18 +02:00
|
|
|
import sys
|
2023-03-29 10:45:48 +02:00
|
|
|
from typing import Optional
|
2023-03-24 17:41:48 +01:00
|
|
|
|
|
|
|
import pygame
|
|
|
|
|
2023-03-29 10:45:48 +02:00
|
|
|
from level.Level import Level
|
2023-03-25 15:41:32 +01:00
|
|
|
from level.LevelManager import LevelManager
|
2023-03-29 10:45:48 +02:00
|
|
|
from level.selection.LevelScreenManager import LevelScreenManager
|
|
|
|
from level.selection.LevelSelectionScreenManager import LevelSelectionScreenManager
|
|
|
|
from level.selection.MainMenuScreenManager import MainMenuScreenManager
|
|
|
|
from level.selection.ScreenManager import ScreenManager
|
2023-03-27 14:38:52 +02:00
|
|
|
from physics import ConstantsParser
|
2023-03-29 10:45:48 +02:00
|
|
|
from physics.SpriteManager import SpriteManager
|
2023-03-26 12:46:41 +02:00
|
|
|
from physics.TickData import TickData
|
2023-03-25 15:41:32 +01:00
|
|
|
from sprite.PositionScale import PositionScale
|
2023-03-28 12:30:12 +02:00
|
|
|
from sprite.SpritesheetManager import SpritesheetManager
|
2023-03-26 11:13:34 +02:00
|
|
|
from ui_elements.ClickEvent import ClickEvent
|
2023-03-26 15:01:58 +02:00
|
|
|
from ui_elements.KeyManager import KeyManager
|
2023-03-24 17:41:48 +01:00
|
|
|
|
2023-03-29 14:14:45 +02:00
|
|
|
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'),
|
|
|
|
}
|
|
|
|
|
|
|
|
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)),
|
2023-03-29 14:01:55 +02:00
|
|
|
}
|
2023-03-28 19:35:42 +02:00
|
|
|
|
2023-03-29 10:45:48 +02:00
|
|
|
|
|
|
|
def apply_frame_rate(number: float, frame_rate: float = 30):
|
2023-03-25 17:18:43 +01:00
|
|
|
"""
|
|
|
|
this function calculates a factor that will be multiplied with the
|
|
|
|
physics of the game to provide a constant speed
|
2023-03-29 10:45:48 +02:00
|
|
|
:param frame_rate: the frame rate of the game
|
2023-03-25 17:18:43 +01:00
|
|
|
:param number: The number to scale by the factor
|
|
|
|
:return: The scaled number
|
|
|
|
"""
|
|
|
|
return number / (frame_rate / 30)
|
|
|
|
|
2023-03-24 17:41:48 +01:00
|
|
|
|
2023-03-29 10:45:48 +02:00
|
|
|
class MainLoop:
|
|
|
|
def __init__(self):
|
|
|
|
self.GAME_STATE_MENU = 'main_menu'
|
|
|
|
self.GAME_STATE_LEVEL_SELECTION = 'level_selection'
|
|
|
|
self.GAME_STATE_LEVEL = 'level'
|
|
|
|
|
|
|
|
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("PM GAME")
|
|
|
|
|
|
|
|
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_LEVEL_SELECTION)
|
|
|
|
|
2023-03-29 13:50:48 +02:00
|
|
|
self.level: Optional[Level] = None
|
|
|
|
|
2023-03-29 10:45:48 +02:00
|
|
|
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]))
|
|
|
|
|
2023-03-29 14:14:45 +02:00
|
|
|
for key in BACKGROUND_IMAGES_UNSCALED:
|
|
|
|
BACKGROUND_IMAGES_SCALED[key] = pygame.transform.scale(
|
|
|
|
BACKGROUND_IMAGES_UNSCALED[key], self.window_size
|
|
|
|
)
|
|
|
|
|
2023-03-29 10:45:48 +02:00
|
|
|
def select_level(self, level: Level):
|
2023-03-29 13:50:48 +02:00
|
|
|
print('Loading level', level.name)
|
|
|
|
self.level = level
|
2023-03-29 10:45:48 +02:00
|
|
|
self.set_game_state(self.GAME_STATE_LEVEL)
|
|
|
|
|
2023-03-29 14:29:36 +02:00
|
|
|
def select_level_selection(self, theme: str = 'tutorial'):
|
2023-03-29 13:50:48 +02:00
|
|
|
self.set_game_state(self.GAME_STATE_LEVEL_SELECTION)
|
2023-03-29 14:29:36 +02:00
|
|
|
if self.screen_manager is not None and isinstance(self.screen_manager, LevelSelectionScreenManager):
|
|
|
|
self.screen_manager.select_theme(theme)
|
2023-03-29 13:50:48 +02:00
|
|
|
|
2023-03-29 10:45:48 +02:00
|
|
|
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(
|
2023-03-29 13:50:48 +02:00
|
|
|
self.sprite_manager, self.spritesheet_manager, self, self.level
|
2023-03-29 10:45:48 +02:00
|
|
|
)
|
|
|
|
elif self.game_state == self.GAME_STATE_LEVEL_SELECTION:
|
|
|
|
self.screen_manager = LevelSelectionScreenManager(
|
|
|
|
self.sprite_manager, self.spritesheet_manager, self, self.parsed_levels_manager
|
|
|
|
)
|
|
|
|
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()
|
2023-03-29 15:06:18 +02:00
|
|
|
sys.exit()
|
2023-03-29 10:45:48 +02:00
|
|
|
|
|
|
|
self.screen.fill((0, 0, 0))
|
2023-03-29 13:50:48 +02:00
|
|
|
|
|
|
|
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)
|
2023-03-29 14:29:36 +02:00
|
|
|
if self.level is not None and self.game_state == self.GAME_STATE_LEVEL:
|
2023-03-29 14:14:45 +02:00
|
|
|
self.screen.blit(BACKGROUND_IMAGES_SCALED[self.level.theme], (0, 0))
|
2023-03-29 10:45:48 +02:00
|
|
|
self.sprite_manager.draw(self.screen, self.screen_transform)
|
|
|
|
|
|
|
|
pygame.display.update()
|
|
|
|
|
|
|
|
|
|
|
|
main_loop: MainLoop = MainLoop()
|
|
|
|
main_loop.main_loop()
|