import asyncio import os #os.environ["SDL_VIDEODRIVER"] = "dummy" import pygame import sys import random import numpy as np import math class StateManager: def __init__(self): # height of the gameplay screen self.SCREEN_HEIGHT = int(1000) # width of the gameplay screen self.SCREEN_WIDTH = int(1000) # fps of the gameplay screen self.FPS = int(30) # the score of the (human) player self.score = int(0) # a boolean variable indicating whether the game has ended or not self.game_over = bool(False) # The X position of the character self.character_x = int(500) # The Y position of the character self.character_y = int(950) # The speed at which the character moves horizontally self.character_speed = int(10) # The change in Y position of the character due to gravity and jumps self.gravity = float(0.5) # The upward speed during the beginning of a jump self.jump_speed = int(20) # The vertical speed of the character self.vertical_speed = float(0) # The size of the character self.character_size = tuple(tuple((50, 50))) # The color of the character, not white as to contrast with the background self.character_color = tuple(tuple((255, 0, 0))) # A boolean indicating whether the character is on the ground self.on_ground = bool(True) # Ground level's Y position self.ground_y = int(980) # Height of the ground self.ground_height = int(20) # Color of the ground self.ground_color = tuple(tuple((0, 255, 0))) # List of stationary platforms with their position and size self.platforms = list([{'x': 200, 'y': 800, 'width': 100, 'height': 20, 'color': tuple((0, 0, 255))}, {'x': 400, 'y': 600, 'width': 100, 'height': 20, 'color': tuple((0, 0, 255))}, {'x': 600, 'y': 400, 'width': 100, 'height': 20, 'color': tuple((0, 0, 255))}, {'x': 800, 'y': 200, 'width': 100, 'height': 20, 'color': tuple((0, 0, 255))}, {'x': 700, 'y': 700, 'width': 100, 'height': 20, 'color': tuple((0, 0, 255))}]) # The character bottom edge y-coordinate, calculated based on character_y and character_size to determine if the character stands on ground or platforms self.character_bottom_edge_y = int(950) # The character left edge x-coordinate, calculated based on character_x to determine if the character is within the screen's horizontal bounds self.character_left_edge_x = int(500) # The character right edge x-coordinate, calculated based on character_x and character_size to determine if the character is within the screen's horizontal bounds self.character_right_edge_x = int(550) # Score awarded for rescuing the princess self.rescue_score = int(50) # The color of the princess, not white to contrast with the background self.princess_color = tuple(tuple((255, 105, 180))) # The size of the princess self.princess_size = tuple(tuple((50, 50))) # A boolean indicating if the princess had been rescued during the current transition self.princess_rescued = bool(False) # The index of the current platform where the princess is located self.princess_platform_index = int(0) # A boolean indicating if the character is in range to rescue the princess self.in_rescue_range = bool(False) # X position of the princess, it should be the center of a platform for placement. self.princess_x = int(200) # Y position of the princess, it should be just above a platform self.princess_y = int(720) # Color of the princess, distinguished from the background and other elements self.princess_color = tuple(tuple((255, 105, 180))) # Size of the princess, to help in rendering her size on the screen self.princess_size = tuple(tuple((50, 50))) # Boolean indicating whether the princess has been rescued in the current transition self.princess_rescued = bool(False) # Boolean indicating if the character is in range to rescue the princess self.in_rescue_range = bool(False) # List of active fireballs with their position, speed, and size self.fireballs = list([{'x': 100, 'y': 10, 'speed_y': 5, 'width': 30, 'height': 30, 'color': tuple((255, 69, 0))}]) # The number of lives the player has before the game ends self.lives = int(3) # Points to be deducted when the character touches a fireball self.fireball_penalty = int(25) # The Y position where fireballs reset after moving off-screen self.fireball_reset_y = int(-50) # HUD display color, to contrast with the background self.hud_color = tuple(tuple((0, 0, 0))) # HUD font size self.hud_font_size = int(30) # HUD margin from the edge of the screen self.hud_margin = int(10) # Color of the coins, not white to contrast with the background self.coin_color = tuple(tuple((255, 223, 0))) # Size of the coins, to help in rendering their size on the screen self.coin_size = tuple(tuple((25, 25))) # Positions of coins to be collected by the player self.coin_positions = list([{'x': 50, 'y': 150, 'visible': True, 'value': 5}, {'x': 150, 'y': 250, 'visible': True, 'value': 5}, {'x': 250, 'y': 350, 'visible': True, 'value': 5}, {'x': 350, 'y': 450, 'visible': True, 'value': 5}, {'x': 450, 'y': 550, 'visible': True, 'value': 5}, {'x': 550, 'y': 650, 'visible': True, 'value': 5}, {'x': 650, 'y': 750, 'visible': True, 'value': 5}, {'x': 750, 'y': 850, 'visible': True, 'value': 5}, {'x': 850, 'y': 950, 'visible': True, 'value': 5}, {'x': 950, 'y': 50, 'visible': True, 'value': 5}]) # HUD to display score self.hud_score_display = str('Score: 0') # HUD to display player lives self.hud_lives_display = str('Lives: 3') # Scattered coins with random positions across the game window self.coin_positions = list([{'x': 50, 'y': 150, 'visible': True, 'value': 5}, {'x': 150, 'y': 250, 'visible': True, 'value': 5}, {'x': 250, 'y': 350, 'visible': True, 'value': 5}, {'x': 350, 'y': 450, 'visible': True, 'value': 5}, {'x': 450, 'y': 550, 'visible': True, 'value': 5}, {'x': 550, 'y': 650, 'visible': True, 'value': 5}, {'x': 650, 'y': 750, 'visible': True, 'value': 5}, {'x': 750, 'y': 850, 'visible': True, 'value': 5}, {'x': 850, 'y': 950, 'visible': True, 'value': 5}, {'x': 950, 'y': 50, 'visible': True, 'value': 5}]) # Height of coins to maintain a standard and to help in rendering their size on the screen self.coin_height = int(25) # Width of coins to maintain a standard and to help in rendering their size on the screen self.coin_width = int(25) # Height of coins to maintain a standard size for rendering self.coin_height = int(25) # Width of coins to maintain a standard size for rendering self.coin_width = int(25) # Height of a coin, used to help in rendering its size on the screen self.coin_height = int(25) # Width of a coin, used to help in rendering its size on the screen self.coin_width = int(25) # Maximum number of lives the player can have before the game ends self.max_lives = int(15) # Message to be displayed on the Game Over screen self.game_over_message = str("""Game Over! Press R to Restart""") # Message color for the Game Over screen self.game_over_message_color = tuple(tuple((255, 0, 0))) # Restart option message color self.restart_message_color = tuple(tuple((0, 255, 0))) # Conditions to show the 'Game Over' screen self.show_game_over = bool(False) def handle_character_input(state_manager, event): if event.type == pygame.KEYDOWN: if event.key == pygame.K_a: state_manager.character_x -= state_manager.character_speed elif event.key == pygame.K_d: state_manager.character_x += state_manager.character_speed elif event.key == pygame.K_w and state_manager.on_ground: state_manager.vertical_speed = -state_manager.jump_speed state_manager.on_ground = False def handle_coin_collection(state_manager, event): #if event.type == pygame.KEYDOWN: # Placeholder for player position, to be replaced with actual player position #player_position = {'x': 0, 'y': 0} player_position = {'x': state_manager.character_x, 'y': state_manager.character_y} for coin in state_manager.coin_positions: if coin['visible']: coin_rect = pygame.Rect(coin['x'], coin['y'], state_manager.coin_width, state_manager.coin_height) player_rect = pygame.Rect(player_position['x'], player_position['y'], state_manager.character_size[0], state_manager.character_size[1]) if coin_rect.colliderect(player_rect): coin['visible'] = False state_manager.score += coin['value'] def handle_game_restart(state_manager, event): if state_manager.game_over and event.type == pygame.KEYDOWN and event.key == pygame.K_r: state_manager.score = 0 state_manager.lives = state_manager.max_lives state_manager.game_over = False state_manager.show_game_over = False def update_character_state(state_manager): if not state_manager.on_ground: # Apply gravity to the vertical speed state_manager.vertical_speed += state_manager.gravity else: # Reset the vertical speed if the character is on the ground state_manager.vertical_speed = 0 # Update the character's Y position state_manager.character_y += state_manager.vertical_speed # Prevent character from moving below the ground if state_manager.character_y > state_manager.SCREEN_HEIGHT - state_manager.character_size[1]: state_manager.character_y = state_manager.SCREEN_HEIGHT - state_manager.character_size[1] state_manager.vertical_speed = 0 state_manager.on_ground = True # Placeholder for input handling - this should set jump_now to True if a jump is initiated jump_now = False # This will be handled by the actual input event in the game loop if jump_now and state_manager.on_ground: # Handle jumping logic state_manager.vertical_speed = -state_manager.jump_speed state_manager.on_ground = False def handle_character_collision_and_screen_bounds(state_manager): # Horizontal bounds state_manager.character_left_edge_x = state_manager.character_x state_manager.character_right_edge_x = state_manager.character_x + state_manager.character_size[0] # Prevent character from moving off the screen if state_manager.character_left_edge_x < 0: state_manager.character_x = 0 elif state_manager.character_right_edge_x > state_manager.SCREEN_WIDTH: state_manager.character_x = state_manager.SCREEN_WIDTH - state_manager.character_size[0] # Vertical bounds and collision with ground or platforms state_manager.character_bottom_edge_y = state_manager.character_y + state_manager.character_size[1] if state_manager.character_bottom_edge_y >= state_manager.ground_y: state_manager.character_y = state_manager.ground_y - state_manager.character_size[1] state_manager.vertical_speed = 0 state_manager.on_ground = True else: # Check collision with platforms collision_found = False for platform in state_manager.platforms: platform_top = platform['y'] platform_bottom = platform['y'] + platform['height'] platform_left = platform['x'] platform_right = platform['x'] + platform['width'] if (state_manager.character_bottom_edge_y > platform_top and state_manager.character_y < platform_bottom and state_manager.character_right_edge_x > platform_left and state_manager.character_left_edge_x < platform_right): state_manager.character_y = platform_top - state_manager.character_size[1] state_manager.vertical_speed = 0 state_manager.on_ground = True collision_found = True break if not collision_found: state_manager.on_ground = False def rescue_princess(state_manager): # Detect if character is within rescue range of the princess if (abs(state_manager.character_x - state_manager.princess_x) < state_manager.character_size[0] and abs(state_manager.character_y - state_manager.princess_y) < state_manager.character_size[1]): state_manager.in_rescue_range = True else: state_manager.in_rescue_range = False # Rescue the princess and update game state accordingly if state_manager.in_rescue_range and not state_manager.game_over: # Increment score for rescuing the princess state_manager.score += state_manager.rescue_score state_manager.princess_rescued = True # Randomly reposition the princess to a different platform state_manager.princess_platform_index = random.randint(0, len(state_manager.platforms) - 1) new_platform = state_manager.platforms[state_manager.princess_platform_index] state_manager.princess_x = new_platform['x'] + new_platform['width'] // 2 state_manager.princess_y = new_platform['y'] - state_manager.princess_size[1] else: state_manager.princess_rescued = False def update_fireballs_and_check_collisions(state_manager): for fireball in state_manager.fireballs: fireball['y'] += fireball['speed_y'] for platform in state_manager.platforms: if platform['y'] < fireball['y'] + fireball['height'] < platform['y'] + platform['height'] and platform['x'] < fireball['x'] + fireball['width']//2 < platform['x'] + platform['width']: fireball['y'] = platform['y'] - fireball['height'] break if fireball['y'] >= state_manager.SCREEN_HEIGHT: fireball['y'] = state_manager.fireball_reset_y if state_manager.character_x < fireball['x'] + fireball['width'] and state_manager.character_x + state_manager.character_size[0] > fireball['x'] and state_manager.character_y < fireball['y'] + fireball['height'] and state_manager.character_y + state_manager.character_size[1] > fireball['y']: state_manager.score -= state_manager.fireball_penalty state_manager.lives -= 1 if state_manager.lives <= 0: state_manager.game_over = True def update_hud_display(state_manager): # Format the score and lives for the HUD display state_manager.hud_score_display = f'Score: {state_manager.score}' state_manager.hud_lives_display = f'Lives: {state_manager.lives}' # Check for coins collection for coin in state_manager.coin_positions: if not coin['visible']: #state_manager.score += coin['value'] # Update the HUD display after score change state_manager.hud_score_display = f'Score: {state_manager.score}' #coin['visible'] = True # Reset the visibility for this example, real game might handle differently def check_game_over_conditions(state_manager): if state_manager.lives <= 0: state_manager.game_over = True state_manager.show_game_over = True if state_manager.game_over and pygame.key.get_pressed()[pygame.K_r]: state_manager.score = 0 state_manager.lives = 3 state_manager.game_over = False state_manager.show_game_over = False def render_sprite(state_manager, sprite_image_path, x, y, width, height): image = pygame.image.load(sprite_image_path) image = pygame.transform.scale(image, (width, height)) # Get the rectangle object from the image for positioning rect = image.get_rect(topleft=(x, y)) # Blit the image onto the screen state_manager.screen.blit(image, rect) def render_character(state_manager): #character_rect = pygame.Rect(state_manager.character_x, state_manager.character_y, state_manager.character_size[0], state_manager.character_size[1]) #pygame.draw.rect(state_manager.screen, state_manager.character_color, character_rect) render_sprite(state_manager, 'player.png', state_manager.character_x, state_manager.character_y, state_manager.character_size[0], state_manager.character_size[1]) def render_environment(state_manager): # Render the ground #ground_rect = pygame.Rect(0, state_manager.ground_y, state_manager.SCREEN_WIDTH, state_manager.ground_height) #pygame.draw.rect(state_manager.screen, state_manager.ground_color, ground_rect) #render_sprite(state_manager, 'platform.png', # 0, state_manager.ground_y, state_manager.SCREEN_WIDTH, state_manager.ground_height) for i in range(state_manager.SCREEN_WIDTH//100+1): render_sprite(state_manager, "platform.png", i*100, state_manager.ground_y, 100, state_manager.ground_height) # Render the platforms for platform in state_manager.platforms: #platform_rect = pygame.Rect(platform['x'], platform['y'], platform['width'], platform['height']) #pygame.draw.rect(state_manager.screen, platform['color'], platform_rect) render_sprite(state_manager, 'platform.png', platform['x'], platform['y'], platform['width'], platform['height']) def render_princess(state_manager): #princess_rect = pygame.Rect(state_manager.princess_x, state_manager.princess_y, state_manager.princess_size[0], state_manager.princess_size[1]) #pygame.draw.rect(state_manager.screen, state_manager.princess_color, princess_rect) render_sprite(state_manager, 'princess.png', state_manager.princess_x, state_manager.princess_y, state_manager.princess_size[0], state_manager.princess_size[1]) def render_fireballs(state_manager): for fireball in state_manager.fireballs: #fireball_rect = pygame.Rect(fireball['x'], fireball['y'], fireball['width'], fireball['height']) #pygame.draw.rect(state_manager.screen, fireball['color'], fireball_rect) render_sprite(state_manager, 'fireball.png', fireball['x'], fireball['y'], fireball['width'], fireball['height']) def render_coins_and_hud(state_manager): # Render Coins for coin in state_manager.coin_positions: if coin['visible']: #coin_rect = pygame.Rect(coin['x'], coin['y'], state_manager.coin_size[0], state_manager.coin_size[1]) #pygame.draw.ellipse(state_manager.screen, state_manager.coin_color, coin_rect) render_sprite(state_manager, 'coin.png', coin['x'], coin['y'], state_manager.coin_size[0], state_manager.coin_size[1]) # Render HUD hud_font = pygame.font.Font(None, state_manager.hud_font_size) score_surface = hud_font.render(state_manager.hud_score_display, True, state_manager.hud_color) lives_surface = hud_font.render(state_manager.hud_lives_display, True, state_manager.hud_color) state_manager.screen.blit(score_surface, (state_manager.hud_margin, state_manager.hud_margin)) state_manager.screen.blit(lives_surface, (state_manager.hud_margin, state_manager.hud_margin + score_surface.get_height())) def render_game_over_screen(state_manager): if state_manager.game_over: game_over_font = pygame.font.Font(None, state_manager.hud_font_size) game_over_surface = game_over_font.render(state_manager.game_over_message, True, state_manager.game_over_message_color) restart_prompt_surface = game_over_font.render('Press R to Restart', True, state_manager.restart_message_color) game_over_x = state_manager.SCREEN_WIDTH // 2 - game_over_surface.get_width() // 2 game_over_y = state_manager.SCREEN_HEIGHT // 2 - game_over_surface.get_height() // 2 restart_x = state_manager.SCREEN_WIDTH // 2 - restart_prompt_surface.get_width() // 2 restart_y = game_over_y + game_over_surface.get_height() + 20 state_manager.screen.blit(game_over_surface, (game_over_x, game_over_y)) state_manager.screen.blit(restart_prompt_surface, (restart_x, restart_y)) class Game(): def __init__(self): self.state_manager = StateManager() self.state_manager.screen = pygame.display.set_mode((self.state_manager.SCREEN_WIDTH, self.state_manager.SCREEN_HEIGHT)) def run(self, event): state_manager = self.state_manager if event.type == pygame.QUIT: return False # Detect key presses to update character movement state. If 'a' is pressed, set a flag to move the character left. If 'd' is pressed, set a flag to move the character right. If 'w' is pressed and the character is on the ground, set flags to initiate a jump. handle_character_input(state_manager, event) # Check for collision between the player character and coins. If a collision is detected, the corresponding coin's visibility state should be set to false and the score should be incremented by the coin's value. handle_coin_collection(state_manager, event) # This function should detect if the R key is pressed when the game is over. If the R key is pressed, it should reset the necessary state variables to restart the game, including setting the score to 0, resetting lives to the maximum number of lives, and ensuring the game_over state is set to False. handle_game_restart(state_manager, event) # call all the logics # Update character's position based on input flags, apply gravity to the character's vertical velocity when not on the ground, and handle jumping logic by updating the vertical velocity and resetting the on_ground flag. update_character_state(state_manager) # Update the character's position according to its current velocity and apply collision detection logic to prevent the character from moving off the screen or through platforms. The function checks if the character is standing on the ground or any platform by iterating through each platform and checking for collisions using the character's bottom edge y-coordinate. When the character's bottom edge y-coordinate overlaps with the top of a platform within the X boundaries of any platform or ground level, the character's y position is adjusted to stand on that surface, the vertical speed is set to 0, and the on_ground state is set to True. If no collision is detected with the ground or a platform, set on_ground to False, allowing gravity to affect the character. Also, check if the character's left or right edges are going beyond the screen width and if so, adjust the x position to remain within the bounds. handle_character_collision_and_screen_bounds(state_manager) # Move the princess to a different platform when the character reaches her and award points. Set in_rescue_range to true if the character is colliding with the princess. If in_rescue_range is true, update score, set princess_rescued to true, randomly select a new platform index for the princess, and update the princess' position to the new platform's location. rescue_princess(state_manager) # This function should iterate over each fireball in the state_manager.fireballs list, updating each fireball's y-coordinate based on its speed_y. It should check for collisions with each platform to prevent fireballs from passing through them and reset fireball positions to the top off-screen when a fireball moves below the screen height. Additionally, the function should detect if the character touches a fireball, in which case the player's score is reduced by fireball_penalty, and a life is deducted. If all lives are lost, set game_over to True. update_fireballs_and_check_collisions(state_manager) # Update the display strings for the HUD to reflect the current score and remaining lives. If a coin is collected, reflect the score change. No transition logic is needed for coins scattering as their positions are predetermined. update_hud_display(state_manager) # This function should check if the player's lives have reached 0. If so, it should set the game_over flag to True and set the show_game_over flag to True to display the 'Game Over' screen. It should also handle a state transition when the game is restarted by resetting relevant game data. check_game_over_conditions(state_manager) # Fill the screen with white state_manager.screen.fill((255, 255, 255)) # Draw the character on the screen at its current position with the specified size and color. render_character(state_manager) # This function should render the ground as a rectangular area at the bottom of the screen using the ground_color, ground_y, and ground_height variables specified in the StateManager class. Additionally, it should iterate through the list of platform dictionaries in the state_manager.platforms list and render each platform as a rectangle at the positions specified in the dictionary using their associated colors. render_environment(state_manager) # Draw the princess at her current position with the specified size and color. Use princess_x, princess_y, princess_size, and princess_color from the StateManager class to create a Rect defining the bounds of the princess and render it onto the screen. render_princess(state_manager) # This function should iterate through all active fireballs in the state_manager.fireballs list and render each fireball as a rectangle on the screen at its current location with the specified size and color from the fireball's dictionary. render_fireballs(state_manager) # Render visible coins at their respective positions using the predefined coin dimensions and color. Additionally, render the updated HUD with the score and lives information onto the screen. render_coins_and_hud(state_manager) # This function should render the 'Game Over' screen when the game_over flag is True. It should display the game_over_message with the specified color and provide the text indicating that the player can press R to restart. If the game is not over, this rendering should be skipped. render_game_over_screen(state_manager) return not state_manager.game_over async def tmp(): game = Game() pygame.init() global event running = True while running: event = pygame.event.poll() running = game.run(event) pygame.display.flip() pygame.quit() async def main(): game = Game() pygame.init() global event running = True while running: event = pygame.event.poll() running = game.run(event) pygame.display.flip() await asyncio.sleep(0.00) pygame.quit() asyncio.run(main())