| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- import builtins
- import os
- import time
- from typing import Tuple, Type
- import pygame
- import jogai
- import jogai._globals as _globals
- import jogai.settings as settings
- from jogai import character, group, logger, scene, utils, widgets
- from jogai.translations import gettext as _
- from jogai.utils import _get_events, _get_keys
- # --------------- #
- # GLOBALS #
- # --------------- #
- running = True
- _predefined_attributes_allowed = [
- _('caption'),
- _('gravity'),
- ]
- # ------------------ #
- # FUNCTIONS #
- # ------------------ #
- def _load_scene(data: dict) -> 'scene.Scene':
- """
- Load scene from data schema.
- Parameters:
- data: input schema
- Returns:
- An object containing the scene
- """
- if _('scene') in data:
- return scene.Scene(data[_('scene')])
- return scene.Scene()
- def _load_attributes(data: dict):
- """
- Load attributes from data schema.
- Parameters:
- data: input schema
- """
- if not isinstance(data, dict):
- raise TypeError(
- _('The attributes in the input schema must be a dict.')
- )
- for attribute in data:
- # utils._convert_function_arguments_to_string()
- if attribute in _predefined_attributes_allowed:
- eval(f'set_{attribute}(data["{attribute}"])')
- def _load_characters(data: dict) -> list:
- """
- Load characters from data schema.
- Parameters:
- data: input schema
- Returns:
- A list containing all loaded characters
- """
- data_characters = {}
- if not _('characters') in data:
- return []
- data_characters = data[_('characters')]
- characters = []
- for character_name, character_data in data_characters.items():
- if 'filename' in character_data:
- character_filename = character_data['filename']
- else:
- character_filename = f'{character_name}.png'
- loaded_character = character.Character(
- character_filename,
- character_name,
- )
- loaded_character._load_data(character_data)
- characters.append(loaded_character)
- return characters
- def simulate_key_press(key: int | str, down: bool = True):
- """
- Post a key press simulation to pygame event module.
- Parameters:
- key: Key to simulate
- down: Keystroke direction. True for KEYDOWN, False for KEYUP
- Examples:
- >>> simulate_key_press('a') # doctest: +SKIP
- """
- key = utils.eval_key(key)
- if isinstance(down, bool) and not down:
- event_type = pygame.KEYUP
- else:
- event_type = pygame.KEYDOWN
- key_event = pygame.event.Event(
- event_type,
- key=key,
- )
- pygame.event.post(key_event)
- def key_pressed(key: int | str) -> bool:
- """
- Check if a key has been pressed.
- Parameters:
- key: The key to be checked
- Returns:
- True if the key is pressed, False otherwise.
- Examples:
- >>> key_pressed('a')
- False
- """
- key = utils.eval_key(key)
- return _get_keys()[key]
- def add_character(new_character: 'character.Character'):
- """
- Add a new character to the videogame.
- Parameters:
- new_character: the character to add
- Examples:
- >>> prepare()
- >>> p = character.Character('protagonist.png')
- >>> add_character(p)
- """
- if not isinstance(new_character, character.Character):
- raise TypeError(_('Only Character instances can be added.'))
- _globals.vg_characters.append(new_character)
- def add_chronometer(new_chronometer: 'widgets.Chronometer'):
- """
- Add a new chronometer to the videogame.
- Parameters:
- new_chronometer: the chronometer to be added
- Examples:
- >>> chrono = widgets.Chronometer(30)
- >>> add_chronometer(chrono) # doctest: +SKIP
- """
- if not isinstance(new_chronometer, widgets.Chronometer):
- raise TypeError(_('Only Chronometer instances can be added.'))
- _globals.vg_chronometers.append(new_chronometer)
- def add_group(new_group: 'group.SquareGroup'):
- """
- Add a new group to the videogame.
- Parameters:
- new_group: the character to be added
- Examples:
- >>> p = character.Character('protagonist.png')
- >>> rectangle = group.RectangleGroup(p, 4, 2)
- >>> add_group(rectangle) # doctest: +SKIP
- """
- if not isinstance(new_group, group.Group):
- raise TypeError(_('Only Group instances can be added.'))
- _globals.vg_groups.append(new_group)
- def add_levelbar(new_levelbar: 'widgets.LevelBar'):
- """
- Add a new level bar to the videogame.
- Parameters:
- new_levelbar: the level bar to be added
- Examples:
- >>> lb = widgets.LevelBar(3)
- >>> add_levelbar(lb) # doctest: +SKIP
- """
- if not isinstance(new_levelbar, widgets.LevelBar):
- raise TypeError(_('Only LevelBar instances can be added.'))
- _globals.vg_levelbars.append(new_levelbar)
- def add_scoreboard(new_text: 'widgets.Text'):
- """
- Add a new scoreboard to the videogame.
- Parameters:
- new_scoreboard: the scoreboard to be added
- Examples:
- >>> sc = widgets.Scoreboard('Test')
- >>> add_scoreboard(sc) # doctest: +SKIP
- """
- if not isinstance(new_scoreboard, widgets.Scoreboard):
- raise TypeError(_('Only Scoreboard instances can be added.'))
- _globals.vg_scobreboards.append(new_scoreboard)
- def add_text(new_text: 'widgets.Text'):
- """
- Add a new text to the videogame.
- Parameters:
- new_text: the text to be added
- Examples:
- >>> text = widgets.Text('Test')
- >>> add_text(text) # doctest: +SKIP
- """
- if not isinstance(new_text, widgets.Text):
- raise TypeError(_('Only Text instances can be added.'))
- _globals.vg_texts.append(new_text)
- def init():
- """
- Start the pygame engine if it is not started.
- Examples:
- >>> init()
- """
- if not pygame.get_init():
- pygame.init()
- _globals._window = pygame.display.set_mode(
- (settings.WINDOW_WIDTH, settings.WINDOW_HEIGHT)
- )
- def is_running() -> bool:
- """
- Check if the game is running. Useful in the main loop.
- Returns:
- True if the game is running. False otherwise.
- Examples:
- >>> state = is_running()
- """
- return running
- def listen_events():
- """
- Listen for events coming from the keyboard and window clicks.
- Examples:
- >>> simulate_key_press('ESCAPE')
- >>> listen_events()
- """
- _globals._events = _get_events()
- _globals._keys = _get_keys()
- for event in _globals._events:
- match event.type:
- case pygame.KEYDOWN:
- if event.key == pygame.K_ESCAPE:
- quit()
- case pygame.QUIT:
- quit()
- def get_color(x: int, y: int) -> Tuple[int, int, int]:
- """
- Get the RGB components for a point in the game window.
- Parameters:
- x: Position on the abscissa axis
- y: Position on the ordinate axis
- Returns:
- A tuple of integers with the RGB components
- Examples:
- >>> color = get_color(100, 200)
- """
- if not isinstance(x, int) or not isinstance(y, int):
- raise TypeError(
- _('The coordinates of the point must be a non-negative integers.')
- )
- if not (0 <= x <= settings.WINDOW_WIDTH) or not (
- 0 <= y <= settings.WINDOW_HEIGHT
- ):
- raise ValueError(
- _('The coordinates of the point must be a non-negative integers.')
- )
- return tuple(_globals._window.get_at((x, y))[:-1])
- def paint(
- sprite: Type['scene.Scene']
- | Type['character.Character']
- | Type['widgets.Text'],
- ):
- """
- Paint multiple sprite types on the main window.
- Parameters:
- sprite: The element to be painted.
- """
- # TODO: check sprite type
- if hasattr(sprite, 'background'):
- _globals._window.fill(sprite.background)
- if hasattr(sprite, 'image') and hasattr(sprite, 'rect'):
- _globals._window.blit(sprite.image, sprite.rect)
- def paint_group(group: Type['group.SquareGroup']):
- """
- Paint a group of sprites on the main window.
- Parameters:
- group: The group of sprites to be painted.
- """
- group.draw(_globals._window)
- def set_caption(caption: str):
- """
- Set the caption for the videogame.
- Parameters:
- caption: Caption to be setted
- Examples:
- >>> set_caption('My videogame')
- """
- if not isinstance(caption, str):
- raise TypeError(_('The caption for the videogame must be a string.'))
- pygame.display.set_caption(caption)
- def set_gravity(gravity: int):
- """
- Set the gravity in the videogame.
- Parameters:
- gravity: Gravity force to be setted.
- Examples:
- >>> set_gravity(5)
- """
- if not isinstance(gravity, int):
- raise TypeError(_('The gravity must be an integer.'))
- _globals._gravity = gravity
- def set_marks(
- x_gap: int = 0,
- y_gap: int = 0,
- x_color: tuple = (255, 180, 30),
- y_color: tuple = (0, 0, 255),
- width: int = 1,
- debug: bool = False,
- ):
- """
- Set distance mark lines and position information along both axis.
- Parameters:
- x_gap: The gap distance on abscissa axis.
- y_gap: The gap distance on ordinate axis.
- x_color: The line color on abscissa axis.
- y_color: The line color on ordinate axis.
- width: The line width.
- debug: If True, extra detailed information is shown.
- """
- _globals.marks = utils.Marks(x_gap, y_gap, x_color, y_color, width, debug)
- def prepare(data_filename: str = ''):
- """
- Prepare the game environment before entering the main loop.
- Examples:
- >>> init()
- >>> prepare()
- """
- if not _globals._window:
- init()
- pygame.key.set_repeat(settings.KEY_DELAY, settings.KEY_INTERVAL)
- if data_filename:
- vg_data = utils._load_yaml_from_filename(data_filename)
- else:
- vg_data = _globals._default_data
- _globals.vg_scene = _load_scene(vg_data)
- _globals.vg_characters = _load_characters(vg_data)
- _globals.vg_attributes = _load_attributes(vg_data)
- if _globals.vg_characters:
- _globals.character_focus = _globals.vg_characters[0]
- # TODO: Check and filter variable names added to builtins
- setattr(builtins, 'vgscene', _globals.vg_scene)
- for vg_character in _globals.vg_characters:
- setattr(builtins, vg_character.name, vg_character)
- def stop():
- """
- Finish the infinite videogame loop.
- """
- global running
- running = False
- def update():
- """
- Refresh the display surface and listen events.
- """
- if _globals.vg_scene:
- if _globals.vg_characters:
- _globals.vg_scene.set_position(_globals.character_focus.x)
- paint(_globals.vg_scene)
- if _globals.vg_characters:
- for pos, vg_character in enumerate(_globals.vg_characters):
- tracking = (
- True if vg_character == _globals.character_focus else False
- )
- vg_character._update_rect_position(tracking=tracking)
- paint(vg_character)
- vg_character.update()
- if _globals.vg_chronometers:
- for chronometers_item in _globals.vg_chronometers:
- paint(chronometers_item)
- chronometers_item.update()
- if _globals.vg_groups:
- for group_item in _globals.vg_groups:
- for sprite_item in group_item.sprites():
- sprite_item._update_rect_position()
- paint(sprite_item)
- sprite_item.update()
- if _globals.vg_levelbars:
- for levelbar_item in _globals.vg_levelbars:
- levelbar_item.draw()
- if _globals.vg_scoreboards:
- for scoreboard_item in _globals.vg_scoreboards:
- paint(scoreboard_item)
- if _globals.vg_texts:
- for texts_item in _globals.vg_texts:
- paint(texts_item)
- if _globals.marks:
- _globals.marks.draw()
- pygame.display.flip()
- _globals._clock.tick(settings.FPS)
- listen_events()
- def quit(wait: int = 0):
- """
- Exit the game.
- Parameters:
- wait: Seconds to wait before exiting
- Examples:
- >>> quit(1)
- """
- if isinstance(wait, int) and wait > 0:
- time.sleep(wait)
- pygame.quit()
- stop()
|