Game

class pygamelib.engine.Game(name: str = 'Game', player: Player = None, boards={}, current_level=None, enable_partial_display=False, partial_display_viewport=None, partial_display_focus=None, mode=EngineMode.MODE_TURN_BY_TURN, user_update=None, input_lag=0.01, user_update_paused=None)

Bases: PglBaseObject

A class that serve as a game engine.

This object is the central system that allow the management of a game. It holds boards (see pygamelib.engine.Board), associate it to level, takes care of level changing, etc.

Note

The game object has an object_library member that is always an empty array except just after loading a board. In this case, if the board have a “library” field, it is going to be used to populate object_library. This library is accessible through the Game object mainly so people have access to it across different Boards during level design in the editor. That architecture decision is debatable.

Note

The constructor of Game takes care of initializing the terminal to properly render the colors on Windows.

Important

The Game object automatically assumes ownership over the Player.

__init__(name: str = 'Game', player: Player = None, boards={}, current_level=None, enable_partial_display=False, partial_display_viewport=None, partial_display_focus=None, mode=EngineMode.MODE_TURN_BY_TURN, user_update=None, input_lag=0.01, user_update_paused=None)
Parameters:
  • name (str) – The Game name.

  • boards (dict) – A dictionary of boards with the level number as key and a board reference as value.

  • current_level (int) – The current level.

  • enable_partial_display (bool) – A boolean to tell the Game object to enable or not partial display of boards. Default: False.

  • partial_display_viewport (list) – A 2 int elements array that gives the radius of the partial display in number of row and column. Please see display_around().

  • partial_display_focus (BoardItem) – The object that is going to be the center of the view when the board is displayed.

  • mode (EngineMode) – The mode parameter configures the way the run() method is going to behave. The default value is EngineMode.MODE_TURN_BY_TURN. In that mode, the Game object wait for an user input before looping. Exactly like when you wait for user input with get_key(). The other possible value is EngineMode.MODE_TURN_BY_TURN. RT stands for “Real Time”. In that mode, the Game object waits for a minimal amount of time (0.01 i.e 100 FPS, configurable through the input_lag parameter) in order to get the input from the user and call the update function right away. This parameter is only useful if you use Game.run().

  • user_update (function) – A reference to the main program update function. The update function is called for each new frame. It is called with 3 parameters: the game object, the user input (can be None) and the elapsed time since last frame.

  • user_update_paused (function) – A reference to the update function called when the game is paused. It is called with the same 3 parameters than the regular update function: the game object, the user input (can be None) and the elapsed time since last frame. If not specified, the regular update function is called but nothing is done regarding NPCs, projectiles, animations, etc.

  • input_lag (float|int) – The amount of time the run() function is going to wait for a user input before returning None and calling the update function. Default is 0.01.

Methods

__init__([name, player, boards, ...])

param name:

The Game name.

actuate_npcs(level_number[, elapsed_time])

Actuate all NPCs on a given level

actuate_projectiles(level_number[, elapsed_time])

Actuate all Projectiles on a given level

add_board(level_number, board)

Add a board for the level number.

add_npc(level_number, npc[, row, column, ...])

Add a NPC to the game.

add_projectile(level_number, projectile[, ...])

Add a Projectile to the game.

animate_items(level_number[, elapsed_time])

That method goes through all the BoardItems of a given map and call Animation.next_frame().

attach(observer)

Attach an observer to this instance.

change_level(level_number)

Change the current level, load the board and place the player to the right place.

clear_screen()

Clear the whole screen (i.e: remove everything written in terminal)

clear_session_logs()

Delete all the log lines from the logs.

config([section])

Get the content of a previously loaded configuration section.

create_config(section)

Initialize a new config section.

current_board()

This method return the board object corresponding to the current_level.

delete_all_levels()

Delete all boards and their associated levels from the game object.

delete_level([lvl_number])

Delete a level and its associated Board from the game object.

detach(observer)

Detach an observer from this instance.

display_board()

Display the current board.

display_player_stats([life_model, void_model])

Display the player name and health.

get_board(level_number)

This method returns the board associated with a level number.

get_key()

Reads the next key-stroke returning it as a string.

handle_notification(subject[, attribute, value])

A virtual method that needs to be implemented by the observer.

insert_board(level_number, board)

Insert a board for the level number.

instance(*args, **kwargs)

Returns the instance of the Game object

load_board(filename[, lvl_number])

Load a saved board

load_config(filename[, section])

Load a configuration file from the disk.

move_player(direction[, step])

Easy wrapper for Board.move().

neighbors([radius, obj])

Get a list of neighbors (non void item) around an object.

notify([modifier, attribute, value])

Notify all the observers that a change occurred.

pause()

Set the game engine state to PAUSE.

remove_npc(level_number, npc)

This methods remove the NPC from the level in parameter.

run()

New in version 1.2.0.

save_board(lvl_number, filename)

Save a board to a JSON file

save_config([section, filename, append])

Save a configuration section.

session_log(line)

Add a line to the session logs.

session_logs()

Return the complete session logs since instantiation.

start()

Set the game engine state to RUNNING.

stop()

Set the game engine state to STOPPED.

store_screen_position(row, column)

Store the screen position of the object.

Attributes

screen_column

A property to get/set the screen column.

screen_row

A property to get/set the screen row.

state

Get/set the state of the game.

actuate_npcs(level_number, elapsed_time=0.0)

Actuate all NPCs on a given level

This method actuate all NPCs on a board associated with a level. At the moment it means moving the NPCs but as the Actuators become more capable this method will evolve to allow more choice (like attack use objects, etc.)

When all NPCs have been successfully actuated, the observers are notified of the change with the pygamelib.engine.Game.actuate_npcs:npcs_actuated event. Their is value passed for that event.

Parameters:
  • level_number (int) – The number of the level to actuate NPCs in.

  • elapsed_time (float) – The amount of time that passed since last call. This parameter is not mandatory.

Example:

mygame.actuate_npcs(1)

Note

This method only move NPCs when their actuator state is RUNNING. If it is PAUSED or STOPPED, the NPC is not moved.

Note

Since version 1.2.0 it’s possible for a Movable item to have different vertical and horizontal movement steps, so actuate_npc respect that by integrating the steps with a unit direction vector. It should be completely transparent and you should not expect any change. Just more movement freedom. If you do experience issues, please report a bug.

Note

Since version 1.2.0 and the appearance of the realtime mode, we have to account for movement speed. This method does it.

actuate_projectiles(level_number, elapsed_time=0.0)

Actuate all Projectiles on a given level

This method actuate all Projectiles on a board associated with a level. This method differs from actuate_npcs() as some logic is involved with projectiles that NPC do not have. This method decrease the available range by projectile.step each time it’s called. It also detects potential collisions. If the available range falls to 0 or a collision is detected the projectile hit_callback is called.

This method respects the Projectile.collision_exclusions parameter and does not register collisions with objects of a type present in that list.

Important

In this method, projectiles do not collide with overlappable items. If you want to detect collisions with overlappable objects, please implement your own projectile actuation method.

Parameters:
  • level_number (int) – The number of the level to actuate Projectiles in.

  • elapsed_time (float) – The amount of time that passed since last call. This parameter is not mandatory.

When all Projectiles have been successfully actuated, the observers are notified of the change with the pygamelib.engine.Game.actuate_projectiles:projectiles_actuated event. Their is value passed for that event.

Example:

mygame.actuate_projectiles(1)

Note

This method only move Projectiles when their actuator state is RUNNING. If it is PAUSED or STOPPED, the Projectile is not moved.

Important

Please have a look at the pygamelib.board_items.Projectile.hit() method for more information on the projectile hit mechanic.

add_board(level_number: int, board: Board) None

Add a board for the level number.

This method associate a Board (pygamelib.engine.Board) to a level number.

If the partial display is enabled at Game level (i.e: partial_display_viewport is not None and enable_partial_display is True), this method propagate the settings to the board automatically. Same for partial_display_focus.

Example:

game.add_board(1,myboard)
Parameters:
  • level_number (int) – the level number to associate the board to.

  • board (pygamelib.engine.Board) – a Board object corresponding to the level number.

Raises:

PglInvalidTypeException – If either of these parameters are not of the correct type.

add_npc(level_number, npc, row=None, column=None, layer=None, auto_layer=True)

Add a NPC to the game. It will be placed on the board corresponding to the level_number. If row and column are not None, the NPC is placed at these coordinates. Else, it’s randomly placed in an empty cell.

Example:

game.add_npc(1,my_evil_npc,5,2)
Parameters:
  • level_number (int) – the level number of the board.

  • npc (pygamelib.board_items.NPC) – the NPC to place.

  • row (int) – the row coordinate to place the NPC at.

  • column (int) – the column coordinate to place the NPC at.

If either of these parameters are not of the correct type, a PglInvalidTypeException exception is raised.

Important

If the NPC does not have an actuator, this method is going to affect a pygamelib.actuators.RandomActuator() to npc.actuator. And if npc.step == None, this method sets it to 1

add_projectile(level_number, projectile, row=None, column=None)

Add a Projectile to the game. It will be placed on the board corresponding to level_number. Neither row nor column can be None.

Example:

game.add_projectile(1, fireball, 5, 2)
Parameters:
  • level_number (int) – the level number of the board.

  • projectile (Projectile) – the Projectile to place.

  • row (int) – the row coordinate to place the Projectile at.

  • column (int) – the column coordinate to place the Projectile at.

If either of these parameters are not of the correct type, a PglInvalidTypeException exception is raised.

Important

If the Projectile does not have an actuator, this method is going to affect pygamelib.actuators.RandomActuator(moveset=[RIGHT]) to projectile.actuator. And if projectile.step == None, this method sets it to 1.

animate_items(level_number, elapsed_time=0.0)

That method goes through all the BoardItems of a given map and call Animation.next_frame().

When all items have been successfully animated, the observers are notified of the change with the pygamelib.engine.Game.animate_items:items_animated event. Their is value passed for that event.

Parameters:
  • level_number (int) – The number of the level to animate items in.

  • elapsed_time (float) – The amount of time that passed since last call. This parameter is not mandatory.

Raise:

PglInvalidLevelException PglInvalidTypeException

Example:

mygame.animate_items(1)
attach(observer)

Attach an observer to this instance. It means that until it is detached, it will be notified every time that a notification is issued (usually on changes).

An object cannot add itself to the list of observers (to avoid infinite recursions).

Parameters:

observer (PglBaseObject) – An observer to attach to this object.

Returns:

True or False depending on the success of the operation.

Return type:

bool

Example:

myboard = Board()
screen = Game.instance().screen
# screen will be notified of all changes in myboard
myboard.attach(screen)
change_level(level_number: int) None

Change the current level, load the board and place the player to the right place.

Example:

game.change_level(1)
Parameters:

level_number (int) – the level number to change to.

Raises:

base.PglInvalidTypeException – If parameter is not an int.

clear_screen()

Clear the whole screen (i.e: remove everything written in terminal)

Deprecated since version 1.2.0: Starting 1.2.0 we are using the pygamelib.engine.Screen object to manage the screen. That function is a simple forward and is kept for backward compatibility only. You should use Game.screen.clear()

clear_session_logs() None

Delete all the log lines from the logs.

Example:

game = Game.instance()
game.clear_logs()

Note

The session log system is nothing more than a list to do your “debug prints”. If you want a real logging system, please use Python logging module.

config(section: str = 'main') dict

Get the content of a previously loaded configuration section.

Parameters:

section (str) – The name of the section.

Example:

if mygame.config('main')['pgl-version-required'] < 10200:
    print('The pygamelib version 1.2.0 or greater is required.')
    exit()
create_config(section: str) None

Initialize a new config section.

The new section is a dictionary.

Parameters:

section (str) – The name of the new section.

Example:

if mygame.config('high_scores') is None:
    mygame.create_config('high_scores')
mygame.config('high_scores')['first_place'] = mygame.player.name
current_board() Board

This method return the board object corresponding to the current_level.

Example:

game.current_board().display()

If current_level is set to a value with no corresponding board a PglException exception is raised with an invalid_level error.

delete_all_levels()

Delete all boards and their associated levels from the game object.

You might want to think twice before using that function…

Example:

game.delete_all_levels()
delete_level(lvl_number: int = None)

Delete a level and its associated Board from the game object.

Both the level and the board can’t be used after that (unless they are reloaded or replaced of course).

Parameters:

lvl_number (int) – The number of the level to remove.

Raises:

Example:

my_game.delete_level(1)
detach(observer)

Detach an observer from this instance. If observer is not in the list this returns False.

Parameters:

observer (PglBaseObject) – An observer to detach from this object.

Returns:

True or False depending on the success of the operation.

Return type:

bool

Example:

# screen will no longer be notified of the changes in myboard.
myboard.detach(screen)
display_board()

Display the current board.

The behavior of that function is dependant on how you configured this object. If you set enable_partial_display to True AND partial_display_viewport is set to a correct value, it will call Game.current_board().display_around() with the correct parameters. The partial display will be centered on the player (Game.player). Otherwise it will just call Game.current_board().display().

If the player is not set or is set to EngineConstant.NO_PLAYER partial display won’t activate automatically.

Example:

mygame.enable_partial_display = True
# Number of rows, number of column (on each side, total viewport
# will be 20x20 in that case).
mygame.partial_display_viewport = [10, 10]
# This will call Game.current_board().display_around()
mygame.display()
mygame.enable_partial_display = False
# This will call Game.current_board().display()
mygame.display()
display_player_stats(life_model='\x1b[41m \x1b[0m', void_model='\x1b[40m \x1b[0m')

Display the player name and health.

Deprecated since version This: method is completely deprecated and not even compatible with the Screen Buffer system. It will be removed in 1.4.0.

This method print the Player name, a health bar (20 blocks of life_model). When life is missing the complement (20-life missing) is printed using void_model. It also display the inventory value as “Score”.

Parameters:
  • life_model (str) – The character(s) that should be used to represent the remaining life.

  • void_model (str) – The character(s) that should be used to represent the lost life.

Note

This method might change in the future. Particularly it could take a template of what to display.

get_board(level_number: int) Board

This method returns the board associated with a level number. :param level_number: The number of the level. :type level_number: int

Raises:

PglInvalidTypeException – if the level_number is not an int.

Example:

level1_board = mygame.get_board(1)
static get_key()

Reads the next key-stroke returning it as a string.

Example:

key = Utils.get_key()
if key == Utils.key.UP:
    print("Up")
elif key == "q"
    exit()

Note

See readkey documentation in readchar package.

handle_notification(subject, attribute=None, value=None)

A virtual method that needs to be implemented by the observer. By default it does nothing but each observer needs to implement it if something needs to be done when notified.

This method always receive the notifying object as first parameter. The 2 other parameters are optional and can be None.

You can use the attribute and value as you see fit. You are free to consider attribute as an event and value as the event’s value.

Parameters:
  • subject (PglBaseObject) – The object that has changed.

  • attribute (str) – The attribute that has changed, it is usually a “FQDN style” string. This can be None.

  • value (Any) – The new value of the attribute. This can be None.

insert_board(level_number: int, board: Board) None

Insert a board for the level number.

This method does basically the same thing than add_board() except that if the level number is already associated it re-affect the numbers down.

Example:

game.insert_board(1,myboard_1)
# level number 1 is associated with myboard_1
game.insert_board(2,myboard_2)
# level number 1 is associated with myboard_1
# level number 2 is associated with myboard_2
game.insert_board(2,myboard_3)
# level number 1 is associated with myboard_1
# level number 2 is now associated with myboard_3
# level number 3 is associated with myboard_2
Parameters:
  • level_number (int) – the level number to associate the board to.

  • board (pygamelib.engine.Board) – a Board object corresponding to the level number.

Raises:

PglInvalidTypeException – If either of these parameters are not of the correct type.

classmethod instance(*args, **kwargs)

Returns the instance of the Game object

Creates a Game object on first call an then returns the same instance on further calls

Returns:

Instance of Game object

load_board(filename, lvl_number=0)

Load a saved board

Load a Board saved on the disk as a JSON file. This method creates a new Board object, populate it with all the elements (except a Player) and then return it.

If the filename argument is not an existing file, the open function is going to raise an exception.

This method, load the board from the JSON file, populate it with all BoardItem included, check for sanity, init the board with BoardItemVoid and then associate the freshly created board to a lvl_number. It then create the NPCs and add them to the board.

Parameters:
  • filename (str) – The file to load

  • lvl_number (int) – The level number to associate the board to. Default is 0.

Returns:

a newly created board (see pygamelib.engine.Board)

Example:

mynewboard = game.load_board( 'awesome_level.json', 1 )
game.change_level( 1 )
load_config(filename: str, section: str = 'main') dict

Load a configuration file from the disk. The configuration file must respect the INI syntax. The goal of these methods is to simplify configuration files management.

Parameters:
  • filename (str) – The filename to load. does not check for existence.

  • section (str) – The section to put the read config file into. This allow for multiple files for multiple purpose. Section is a human readable unique identifier.

Raises:
  • FileNotFoundError – If filename is not found on the disk.

  • json.decoder.JSONDecodeError – If filename could not be decoded as JSON.

Returns:

The parsed data.

Return type:

dict

Warning

breaking changes: before v1.1.0 that method use to load file using the configparser module. This have been dumped in favor of json files. Since that methods was apparently not used, there is no backward compatibility.

Example:

mygame.load_config('game_controls.json','game_control')
move_player(direction, step=1)

Easy wrapper for Board.move().

Example:

mygame.move_player(Direction.RIGHT,1)
neighbors(radius=1, obj=None)

Get a list of neighbors (non void item) around an object.

This method returns a list of objects that are all around an object between the position of an object and all the cells at radius.

Parameters:
  • radius (int) – The radius in which non void item should be included

  • object (pygamelib.board_items.BoardItem) – The central object. The neighbors are calculated for that object. If None, the player is the object.

Returns:

A list of BoardItem. No BoardItemVoid is included.

Raises:

PglInvalidTypeException – If radius is not an int.

Example:

for item in game.neighbors(2):
    print(f'{item.name} is around player at coordinates '
        '({item.pos[0]},{item.pos[1]})')
notify(modifier=None, attribute: str = None, value: Any = None) None

Notify all the observers that a change occurred.

Parameters:
  • modifier (PglBaseObject) – An optional parameter that identify the modifier object to exclude it from the notified objects.

  • attribute (str) – An optional parameter that identify the attribute that has changed.

  • value (Any) – An optional parameter that identify the new value of the attribute.

Example:

# This example is silly, you would usually notify other objects from inside
# an object that changes a value that's important for the observers.
color = Color(255,200,125)
color.attach(some_text_object)
color.notify()
pause()

Set the game engine state to PAUSE.

Example:

mygame.pause()
remove_npc(level_number, npc)

This methods remove the NPC from the level in parameter.

Parameters:
  • level (int) – The number of the level from where the NPC is to be removed.

  • npc (NPC) – The NPC object to remove.

Example:

mygame.remove_npc(1, dead_npc)
run()

New in version 1.2.0.

The run() method act as the main game loop and does a number of things for you:

  1. It grabs the user input. If the Game object is configured with MODE_TBT (the default), nothing happen until the user hit a key. If the mode is set to MODE_RT, it will wait for input_lag secondes for a user input before going to step 3.

  2. It calculate the elapsed time between 2 frames.

  3. Accumulates the elapsed time in the player dtmove variable (if there is a player object configured)

  4. It sets the cursor position to 0,0 (meaning that your user_update function will draw on top of the previously drawn window). The Board.display() and Board.display_around() method clean the end of their line.

  5. It calls the user_update function with 3 parameters: the game object, the key hit by the user (it can be None) and the elapsed time between to calls.

  6. Clears the end of the screen.

  7. Actuates NPCs (If there is at least one Board manage by Game).

  8. Actuates projectiles (If there is at least one Board manage by Game).

  9. Animates items (If there is at least one Board manage by Game).

On the subject of particle emitters, the Board object automatically update the ones that are attached to BoardItems. For all other particle emitters you need to call the update method of the emitters yourself (for now).

In version 1.2.X, there was a bug when the game was paused. In that case nothing was happening anymore. The user update function was not called and events were not processed. On top of that it was impossible to use run() without associating a board object with a level. Starting with version 1.3.0, it is now possible to use run() without associating a board object with a level. There is also a new parameter to the constructor (user_update_paused) that allows you to specify a function that will be called when the game is paused. This function will be called with the same 3 parameters than the regular update function: the game object, the user input (can be None) and the elapsed time since last frame.

Important

If you try to set the game state to PAUSED and the user_update_paused function is not defined, a notification will be issued and the game will continue to run. The notification message is pygamelib.engine.Game.run:PauseNotAvailable

Raises:

PglInvalidTypeException, PglInvalidTypeException

Example:

mygame.run()
save_board(lvl_number, filename)

Save a board to a JSON file

This method saves a Board and everything in it but the BoardItemVoid.

Not check are done on the filename, if anything happen you get the exceptions from open().

Parameters:
  • lvl_number (int) – The level number to get the board from.

  • filename (str) – The path to the file to save the data to.

Raises:

Example:

game.save_board( 1, 'hac-maps/level1.json')

If Game.object_library is not an empty array, it will be saved also.

Warning

In version 1.3.0 the Board class changed a lot and a layer system has been added. Therefor, boards saved from version 1.3.0+ are not compatible with previous version. Previous boards can be loaded (Game.load_board() is backward compatible), but when saved they will be converted to the new format.

save_config(section: str = None, filename: str = None, append: bool = False) None

Save a configuration section.

Parameters:
  • section (str) – The name of the section to save on disk.

  • filename (str) – The file to write in. If not provided it will write in the file that was used to load the given section. If section was not loaded from a file, save will raise an exception.

  • append (bool) – Do we need to append to the file or replace the content (True = append, False = replace)

Example:

mygame.save_config('game_controls', 'data/game_controls.json')
property screen_column: int

A property to get/set the screen column.

Parameters:

value (int) – the screen column

Return type:

int

property screen_row: int

A property to get/set the screen row.

Parameters:

value (int) – the screen row

Return type:

int

session_log(line: str) None

Add a line to the session logs.

Session logs needs to be activated first.

Parameters:

line (str) – The line to add to the logs.

Example:

game = Game.instance()
game.ENABLE_SESSION_LOGS = True
game.session_log('Game engine initialized')

Note

The session log system is nothing more than a list to do your “debug prints”. If you want a real logging system, please use Python logging module.

session_logs() list

Return the complete session logs since instantiation.

Example:

game = Game.instance()
game.ENABLE_SESSION_LOGS = True
for line in game.logs():
    print(line)

Note

The session log system is nothing more than a list to do your “debug prints”. If you want a real logging system, please use Python logging module.

start()

Set the game engine state to RUNNING.

The game has to be RUNNING for actuate_npcs() and move_player() to do anything.

Example:

mygame.start()
property state

Get/set the state of the game.

Parameters:

value (State) – The new state of the game (from the constants module).

Returns:

The state of the game.

Return type:

State

The observers are notified of a change of state with the pygamelib.engine.Game.state event. The new state is passed as the value of the event.

stop()

Set the game engine state to STOPPED.

Example:

mygame.stop()
store_screen_position(row: int, column: int) bool

Store the screen position of the object.

This method is automatically called by Screen.place().

Parameters:
  • row (int) – The row (or y) coordinate.

  • column (int) – The column (or x) coordinate.

Example:

an_object.store_screen_coordinate(3,8)