Files
plateformer-evolve/game/game.py
T
2026-10-17 16:49:29 +02:00

167 lines
7.7 KiB
Python

"""
Represents the main game class.
This class is responsible for managing the game's logic.
It offers to set a callback function that will be called when waiting for a player input.
It provides the gameState object to access the current state of the game.
It provides the Map object to access the map.
TODO: It provides some stopGame conditions to stop the game like the max number of moves.
It provides public methods to start, stop and reset the game.
It provides public methods to change the current map.
"""
from game.blocks.block import Block
from game.gameState import GameState
from game.map import Map
from colorama import Style
class Game:
def __init__(self, map: Map, gameState: GameState):
self.map = map
self.gameState = gameState
self.waitingForInputCallback = None
self.positionUpdateCallback = None
self.gameStopCallback = None
def set_waiting_for_input_callback(self, callback: callable):
self.waitingForInputCallback = callback
def set_position_update_callback(self, callback: callable):
self.positionUpdateCallback = callback
def _position_update(self):
if self.positionUpdateCallback:
self.positionUpdateCallback()
def set_game_stop_callback(self, callback: callable):
self.gameStopCallback = callback
def start_game(self):
self.gameState.set_stop_game(False)
falling_steps = 0
# Spawn the player at the spawn point
spawn_point_position = self.map.get_first_block_type_position('spawn_point')
if spawn_point_position is None:
raise ValueError("No spawn point found in the map.")
self.gameState.set_player_position(spawn_point_position)
self._position_update()
while not self.gameState.stopGame:
if self.gameState.playerVelocity == -1 and self.waitingForInputCallback:
falling_steps = 0
self.gameState.set_player_velocity(self.waitingForInputCallback())
self.gameState.add_move_count()
elif self.gameState.playerVelocity != -1:
falling_steps += 1
if falling_steps > self.map.height:
self.gameState.set_game_won(False)
self.gameState.set_stop_game(True)
self._position_update()
continue
# Try to move the player according to his velocity
velocity_position = self.get_position_at_velocity()
if velocity_position is not None and not self.map.is_position_in_bounds(velocity_position):
self.gameState.set_stop_game(True)
self._position_update()
continue
block_at_velocity = None
if velocity_position is not None:
block_at_velocity = self.map.get_block_at(velocity_position[0], velocity_position[1])
if not block_at_velocity or not block_at_velocity.solid: # Empty space or non-solid block → Move the player to the new position
if velocity_position:
self.gameState.set_player_position(velocity_position)
if block_at_velocity:
block_at_velocity.on_collision(self.gameState) # Do something when the player is on the block, like add coins or stop the game
# Check if the player is on a solid block
player_position = self.gameState.get_player_position()
block_at_player_position = self.map.get_block_at(player_position[0], player_position[1] + 1)
if block_at_player_position and block_at_player_position.solid:
self.gameState.set_player_velocity(-1) # Stop the player from falling
self._update_player_velocity()
self._position_update() # Update the screen for example
if self.gameStopCallback and self.gameStopCallback(self.gameState):
self.gameState.set_stop_game(True)
break
def _update_player_velocity(self):
current_velocity = self.gameState.playerVelocity
if current_velocity == -1: # If no velocity, then the player is not moving, so we don't need to update the velocity
return
if current_velocity == "down": # Falling straight down → Still falling except if the player is on a block
if not self._get_block_at_velocity() == None and self._get_block_at_velocity().solid: # If block at where the player is going is solid, then the player will stop falling
self.gameState.set_player_velocity(-1)
return
if current_velocity == "up": # Going up → Going down
self.gameState.set_player_velocity("down")
return
if current_velocity in ["left_down", "right_down"]:
self.gameState.set_player_velocity("down")
return
if current_velocity in ["left", "right"]:
self.gameState.set_player_velocity(current_velocity + "_down")
block_at_velocity = self._get_block_at_velocity()
if not block_at_velocity == None and block_at_velocity.solid: # If block at where the player is going is solid, then the player will fall down
self.gameState.set_player_velocity("down")
return
if current_velocity in ["left_up", "right_up"]:
self.gameState.set_player_velocity(current_velocity.replace("_up", "_down"))
block_at_velocity = self._get_block_at_velocity()
if not block_at_velocity == None and block_at_velocity.solid: # If block at where the player is going is solid, then the player will fall down
self.gameState.set_player_velocity("down")
return
def get_position_at_velocity(self) -> tuple | None:
player_position = self.gameState.get_player_position()
current_velocity = self.gameState.playerVelocity
match current_velocity:
case -1:
return None
case "down":
return (player_position[0], player_position[1] + 1)
case "up":
return (player_position[0], player_position[1] - 1)
case "left_down":
return (player_position[0] - 1, player_position[1] + 1)
case "right_down":
return (player_position[0] + 1, player_position[1] + 1)
case "left":
return (player_position[0] - 1, player_position[1])
case "right":
return (player_position[0] + 1, player_position[1])
case "left_up":
return (player_position[0] - 1, player_position[1] - 1)
case "right_up":
return (player_position[0] + 1, player_position[1] - 1)
case _:
return None
return None
def _get_block_at_velocity(self) -> Block | None:
position_at_velocity = self.get_position_at_velocity()
if position_at_velocity is None:
return None
return self.map.get_block_at(position_at_velocity[0], position_at_velocity[1])
def stop_game(self):
self.gameState.set_stop_game(True)
def to_string(self):
"""
Returns a string representation of the game state and map with player position.
"""
player_position = self.gameState.get_player_position()
map_string_dict = self.map.to_string_dict()
map_string_dict[player_position[1]][player_position[0]] = f'{Style.BRIGHT}🦧'
return '\n'.join([''.join(character + Style.RESET_ALL for character in row) for row in map_string_dict])