def __init__(self, screen:pygame.Surface, args: dict, parent):
        """
        This is the object ALL objects MUST inherit from to be used in a room.
        This is the parent of ALL objects.
        :param screen: The screen to draw the object to
        :param args: The dictionary of properties specified in the XML. NOTE: Any property defined in a tag, will have the '@' infront of it 
        :param parent: The room the object will live in 
        """
        self.screen = screen
        self.args = args
        self.parent = parent

        self.angle = 0

        self.should_center_width = get_mandatory(args, "@x", str) == "c"
        self.should_center_height = get_mandatory(args, "@y", str) == "c"

        self.locked = get_optional(args, "@locked", "false")

        self.screen_w, self.screen_h = self.screen.get_size()

        self.display = Color(screen, (255, 0, 255), 10, 10)
        self.w, self.h = self.display.get_size()

        if self.should_center_width:
            self.x = self.center_width()
        else:
            self.x = get_mandatory(args, "@x", int)

        if self.should_center_height:
            self.y = self.center_height()
        else:
            self.y = get_mandatory(args, "@y", int)

        self.on_screen_cache = True
        self.on_mouse_over_cache = False
        self.deleted = False

        self.ticker = Ticker()
        self.time_delta = self.ticker.tick

        self.states = {}
        self.state = None

        self.x_delta = 0
        self.y_delta = 0

        self.model_type = type(self).__name__

        self.debug_color = get_sys_var("debug-color")

        self.siblings = self.parent.props.array

        self.oncreate()
 def get_optional_arguement(self, key: str, default, return_type:type=None, is_literal_value=False, blank_means_unset=False):
     """
     Returns either the value in a dictionary, or a default value specified if the value is not in the dictionary
     :param key: The key to look for in the arguement dictionary
     :param default: The value to return if the value does not exits
     :param return_type: The datatype to cast the result to (regardless if it is found or not)
     :param is_literal_value: If the key specified is the literal key (True), or if it should try both the value, and the value preceded by the  @ sign
     :param blank_means_unset: If a blank value is found (""), treat it as unset. Default is False
     :return: Either the value in the dictionary, or the default value
     """
     return get_optional(self.args, key, default, return_type=return_type, is_literal_value=is_literal_value, blank_means_unset=blank_means_unset)
Beispiel #3
0
    def __init__(self, screen:pygame.Surface, args: dict, parent:'Room'):
        args["@x"] = 0
        args["@y"] = 0
        ObjectBase.__init__(self, screen, args, parent)

        self.src = get_mandatory(args, "@src", str)
        self.repetitions = get_optional(args, "@repeat", 0, int)
        self.channel = get_free_channel()

        self.playing = True

        self.repetition = -1
        self.sound = get_sound(self.src)
Beispiel #4
0
 def __init__(self, screen, data: dict, custom_objects: typing.List,
              parent):
     """
     The object which manages, and stores every object in a room
     NOTE: Only key functions are documented, which are available to each object via the "parent" variable in each class
     """
     ScreenBase.__init__(self, screen)
     self.tasks = []
     self.name = get_mandatory(data, "@name")
     self.props = ASQL()
     self.custom_objects = custom_objects
     self.parent = parent
     self.data = data
     self.background_color = convert_color(
         get_optional(data, "@color", self.parent.background_color))
     self.load_room()
Beispiel #5
0
def side_scroller(xml: str,
                  start_room: str,
                  images=None,
                  sprite_sheets=None,
                  sounds=None,
                  font=None,
                  development_screen_size: tuple = None,
                  refresh_rate: int = 60,
                  caption: str = "Python Side Scroller Engine",
                  icon: str = None,
                  loading_screen: callable = None,
                  min_loading_time: int = 0,
                  custom_objects: typing.List = None,
                  enable_alt_f4: bool = True,
                  initial_variables=None,
                  fullscreen: bool = True,
                  debug: bool = False,
                  debug_color: tuple = (255, 255, 255),
                  auto_scale: bool = True,
                  default_image: str = None,
                  default_spritesheet: str = None,
                  post_load: callable = None,
                  alt_side_scroller=None,
                  background_color: tuple = (0, 0, 0)):
    """
    This is the function which starts the engine.
    This is the single most important function in the entire system.
    :param xml: The XML data to build the game from
    :param start_room: The name of the room to start the player in
    :param images: A dictionary of the images, and their names to pre-load into the image cache. The value must be a dict object. Use "path" to specify the path to the image, "w" to specify the width to scale the image to (optional) and "h" to specify the height to scale the image to (optional)
    :param sprite_sheets: A dictionary of the Sprite Sheets, and their names to pre-load into the Sprite Sheets cache. Use "path" to specify the path to the sheet, "w" to specify the number of rows are in the sheet, "h" to specify the number of cols that are in the sheet, "duration" to specify the length of time to stay on each image, final_size as the size to scale each image to (format: [width, height]), and "invisible_color" as some RGB color which will be ignored. (in general, select a color not in any of the images)
    :param sounds: A dictionary of the sounds and their names to pre-load to the sound cache. Use "path" to specify the path to the sound, "volume" to specify the volume to play the sound at
    :param font: A dictionary of the fonts and their names to pre-load to the font cache. Use "path" to specify the path to the font, "size" to specify the font size, "bold" to specify if the font should be bold (default is False), and "italic" to specify if the font should be italicised (default is False)
    :param development_screen_size: The size of the screen you develop with. We recomend 800x500. The screen can be scaled to the user's screen with different configurations (see below)
    :param refresh_rate: The maximum refresh rate of the game. Note: all movement is time-based, and independent of the framerate
    :param caption: The text to be shown as the game window's title 
    :param icon: the path to the image to set as the window's icon
    :param loading_screen: The reference to the function to use as the loading animation. The function must take 1 arguement, which is the PyGame surface to do all of the drawing to
    :param min_loading_time: The mimumum amount of loading time the user will see on game launch. The game will load in the background. On completion, if there is still time left, your animation will continue until this time has been met
    :param custom_objects: The list of the refrences of the classes of your custom objects used in the XML. NOTE: Each object MUST inherit from the ObjectBase class (from SideScroller.Objects.ObjectBase import ObjectBase) 
    :param enable_alt_f4: If the window should be closable via the Alt+F4 keyboard shortcut
    :param initial_variables: A dictionary of the variables, and initial values (usefull when objects require variables to load)
    :param fullscreen: If the game should run in fullscreen mode
    :param debug: If the game should run in debug mode (every object is drawn with the hitbox showing)
    :param debug_color: The color to draw the debug hitbox (can be specified individually per Object) 
    :param auto_scale: If the engine should scale the screen to best fit the user's monitor 
    :param default_image: The name of the image (saved in the cache) to be used in the event of an unknown image requested from the cahce
    :param default_spritesheet: The name of the spritesheet (saved in the cache) to be used in the event of an unknown spritesheet requested from the cahce
    :param post_load: The funtion which will be called after everything has been loaded, but before the game starts
    :param alt_side_scroller: An alternate SideScroller class to use as the core engine. NOTE: MUST INHERIT FROM SideScroller CLASS in SideScroller/SideScroller.py
    :param background_color: The color of the screen's background (empty space where nothing is drawn). The default is black (0, 0, 0)
    """

    tmp = []
    for sublist in custom_objects:
        if type(sublist) is list:
            for item in sublist:
                tmp.append(item)
        else:
            tmp.append(sublist)
    custom_objects = tmp[:]
    del tmp

    set_sys_var("debug", debug)
    set_sys_var("debug-color", debug_color)

    def termanate():
        if game.room.quit_action():
            quit()

    if initial_variables is None:
        initial_variables = {}

    for name, value in initial_variables.items():
        set_var(name, value)

    if custom_objects is None:
        custom_objects = []

    if development_screen_size is None:
        development_screen_size = get_monitor_resolution()

    game_screen_size = get_monitor_resolution()

    x_scale = game_screen_size[0] / development_screen_size[0]
    y_scale = game_screen_size[1] / development_screen_size[1]

    scale_factor = 1

    if development_screen_size[0] * y_scale <= game_screen_size[0]:
        scale_factor = y_scale
    elif development_screen_size[1] * x_scale <= game_screen_size[1]:
        scale_factor = x_scale
    else:
        quit()

    scaled_size = (int(development_screen_size[0] * scale_factor),
                   int(development_screen_size[1] * scale_factor))
    scaled_pos = ((game_screen_size[0] / 2) - (scaled_size[0] / 2),
                  (game_screen_size[1] / 2) - (scaled_size[1] / 2))

    mode = 0
    if fullscreen: mode = pygame.FULLSCREEN

    if auto_scale:
        main_surf = pygame.display.set_mode(game_screen_size, mode)
    else:
        main_surf = pygame.display.set_mode(development_screen_size, mode)

    screen = pygame.Surface(development_screen_size)

    pygame.display.set_caption(caption)

    if loading_screen is not None:
        t = threading.Thread(target=loading_screen, args=(screen, ))
        t.setDaemon(True)
        t.start()

    load_start = time.time()

    if sprite_sheets is None:
        sprite_sheets = {}

    if images is None:
        images = {}

    if sounds is None:
        sounds = {}

    if font is None:
        font = {}

    if icon is not None:
        pygame.display.set_icon(pygame.image.load(icon))

    for name, props in images.items():
        set_image(name, props['path'], get_optional(props, "w", None),
                  get_optional(props, "h", None))

    for name, props in sprite_sheets.items():
        set_spritesheet(name, props["path"], props["w"], props["h"],
                        props["duration"],
                        get_optional(props, "final_size", None),
                        get_optional(props, "invisible_color", (0, 0, 1)))

    for name, props in sounds.items():
        set_sound(name, props["path"], get_optional(props, "volume", 1.0,
                                                    float))

    for name, props in font.items():
        set_font(name,
                 props["path"],
                 props["size"],
                 bold=get_optional(props, "bold", False, bool),
                 italic=get_optional(props, "italic", False, bool))

    set_default_image(default_image)
    set_default_spritesheet(default_spritesheet)

    clock = pygame.time.Clock()
    class_ref = PyGE
    if alt_side_scroller is not None:
        class_ref = alt_side_scroller
    game = class_ref(screen, xml, start_room, custom_objects)

    load_duration = time.time() - load_start
    if load_duration < min_loading_time:
        time.sleep(min_loading_time - load_duration)

    if post_load is not None:
        post_load()

    set_var("loaded", True)

    while True:
        events = pygame.event.get()
        for event in events:
            if event.type == pygame.QUIT:
                termanate()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_F4 and (
                        event.mod & pygame.KMOD_ALT
                        or event.mod & pygame.KMOD_RALT) and enable_alt_f4:
                    termanate()

        screen.fill(background_color)
        game.update(events)
        game.draw()

        if auto_scale:
            main_surf.blit(pygame.transform.scale(screen, scaled_size),
                           scaled_pos)
        else:
            main_surf.blit(screen, (0, 0))

        pygame.display.update()

        clock.tick(refresh_rate)
Beispiel #6
0
def pyge_application(
        level_data:str, start_room:str, images=None, sprite_sheets=None, sounds=None, font=None, videos=None,
        models=None, development_screen_size:tuple=None, refresh_rate:int=60,
        caption:str= "Python Game Engine Application", icon:str=None, loading_screen:callable=None,
        min_loading_time:int=0, custom_objects:list=None, enable_alt_f4:bool=True, initial_variables=None,
        fullscreen:bool=True, debug:bool=False, debug_color:tuple=(255, 255, 255), auto_scale:bool=True,
        default_image:str=None, default_spritesheet:str=None, post_load:callable=None, alt_side_scroller=None,
        background_color:tuple=(0, 0, 0), load_mode:int=0, audio_anaylasis_enabled:bool=False
):
    """
    This is the function which starts the engine.
    This is the single most important function in the entire system.
    :param level_data: The XML data to build the game from
    :param start_room: The name of the room to start the player in
    :param images: A dictionary of the images, and their names to pre-load into the image cache. The value must be a dict object. Use "path" to specify the path to the image, "w" to specify the width to scale the image to (optional) and "h" to specify the height to scale the image to (optional)
    :param sprite_sheets: A dictionary of the Sprite Sheets, and their names to pre-load into the Sprite Sheets cache. Use "path" to specify the path to the sheet, "w" to specify the number of rows are in the sheet, "h" to specify the number of cols that are in the sheet, "duration" to specify the length of time to stay on each image, final_size as the size to scale each image to (format: [width, height]), and "invisible_color" as some RGB color which will be ignored. (in general, select a color not in any of the images)
    :param sounds: A dictionary of the sounds and their names to pre-load to the sound cache. Use "path" to specify the path to the sound, "volume" to specify the volume to play the sound at
    :param font: A dictionary of the fonts and their names to pre-load to the font cache. Use "path" to specify the path to the font, "size" to specify the font size, "bold" to specify if the font should be bold (default is False), and "italic" to specify if the font should be italicised (default is False)
    :param videos: A dictionary of the videos and their names to pre-load to the font cache. Use "path" to specify the path to the file. See the Video page of the docs for the remainder of the options. (there are alot)
    :param models: A dictionary of the 3D Models and their names to pre-load to the 3D model cache. Use "path" to specify the path to the file. See the Video page of the docs for the remainder of the options. (there are alot)
    :param development_screen_size: The size of the screen you develop with. We recomend 800x500. The screen can be scaled to the user's screen with different configurations (see below)
    :param refresh_rate: The maximum refresh rate of the game. Note: all movement is time-based, and independent of the framerate
    :param caption: The text to be shown as the game window's title 
    :param icon: the path to the image to set as the window's icon
    :param loading_screen: The reference to the function to use as the loading animation. The function must take 1 arguement, which is the PyGame surface to do all of the drawing to
    :param min_loading_time: The mimumum amount of loading time the user will see on game launch. The game will load in the background. On completion, if there is still time left, your animation will continue until this time has been met
    :param custom_objects: The list of the refrences of the classes of your custom objects used in the XML. NOTE: Each object MUST inherit from the ObjectBase class (from SideScroller.Objects.ObjectBase import ObjectBase) 
    :param enable_alt_f4: If the window should be closable via the Alt+F4 keyboard shortcut
    :param initial_variables: A dictionary of the variables, and initial values (usefull when objects require variables to load)
    :param fullscreen: If the game should run in fullscreen mode
    :param debug: If the game should run in debug mode (every object is drawn with the hitbox showing)
    :param debug_color: The color to draw the debug hitbox (can be specified individually per Object) 
    :param auto_scale: If the engine should scale the screen to best fit the user's monitor 
    :param default_image: The name of the image (saved in the cache) to be used in the event of an unknown image requested from the cahce
    :param default_spritesheet: The name of the spritesheet (saved in the cache) to be used in the event of an unknown spritesheet requested from the cahce
    :param post_load: The funtion which will be called after everything has been loaded, but before the game starts
    :param alt_side_scroller: An alternate SideScroller class to use as the core engine. NOTE: MUST INHERIT FROM SideScroller CLASS in SideScroller/SideScroller.py
    :param background_color: The color of the screen's background (empty space where nothing is drawn). The default is black (0, 0, 0)
    :param load_mode: The mode ID of the method to interpret the level data as (0=XML, 1=JSON) WARNING: Experamantal
    :param audio_anaylasis_enabled: If the system should allow audio anaylasis. (You need to install 'aubio' first, which many people have issues with)
    """
    tmp = []

    logging.info("Initializing PyGE Application...")

    set_sys_var("audio-anaylasis-enabled", audio_anaylasis_enabled)

    if custom_objects is None:
        custom_objects = []

    for sublist in custom_objects:
        if type(sublist) is list:
            for item in sublist:
                tmp.append(item)
        else:
            tmp.append(sublist)
    custom_objects = tmp[:]
    del tmp

    set_sys_var("debug", debug)
    set_sys_var("debug-color", debug_color)

    def termanate():
        if game.room.quit_action():
            quit()

    if initial_variables is None:
        initial_variables = {}

    set_var("vertical_g", -9.80665)
    set_var("lateral_g", 0)

    logging.info("Calculating Screen Scaling...")

    for name, value in initial_variables.items():
        set_var(name, value)

    if custom_objects is None:
        custom_objects = []

    if development_screen_size is None:
        development_screen_size = get_monitor_resolution()

    game_screen_size = get_monitor_resolution()

    x_scale = game_screen_size[0] / development_screen_size[0]
    y_scale = game_screen_size[1] / development_screen_size[1]

    logging.info("Calculated Scale Factors As {}, {}...".format(x_scale, y_scale))

    scale_factor = 1

    if development_screen_size[0] * y_scale <= game_screen_size[0]:
        scale_factor = y_scale
        logging.info("Using Y-Scale Factor To Cause A Fullscreen Application...")
    elif development_screen_size[1] * x_scale <= game_screen_size[1]:
        scale_factor = x_scale
        logging.info("Using X-Scale Factor To Cause A Fullscreen Application...")
    else:
        logging.fatal("Could Not Calculate A Scale Factor To Match The User's Monitor")
        quit()

    scaled_size = (
        int(development_screen_size[0] * scale_factor),
        int(development_screen_size[1] * scale_factor)
    )
    scaled_pos = (
        (game_screen_size[0] / 2) - (scaled_size[0] / 2),
        (game_screen_size[1] / 2) - (scaled_size[1] / 2)
    )
    logging.info("The Scaled Screen Size Has Been Calculated As {}x{}".format(*scaled_size))
    logging.info("The Scaled Position Has Been Calculated As {}x{}".format(*scaled_pos))

    mode = 0
    if fullscreen:
        logging.info("Enabling Fullscreen Mode...")
        mode = pygame.FULLSCREEN

    logging.info("Window Interaction Mode Set To {}".format(mode))

    if auto_scale:
        logging.info("Scaling The Screen Up To The Specified Size. Using A Window Size Of {}x{}".format(*game_screen_size))
        main_surf = pygame.display.set_mode(game_screen_size, mode | pygame.DOUBLEBUF)
    else:
        logging.info("Skipping Screen Scaling. Using A Window Size Of {}x{}...".format(*development_screen_size))
        main_surf = pygame.display.set_mode(development_screen_size, mode | pygame.DOUBLEBUF)

    logging.info("Building The Buffered Screen For Application Drawing")
    screen = pygame.Surface(development_screen_size)

    pygame.display.set_caption(caption)

    if loading_screen is not None:
        logging.info("Spawning Loading Screen Thread")
        t = threading.Thread(
            target=loading_screen,
            args=(screen,)
        )
        t.setDaemon(True)
        t.start()
    else:
        logging.info("Skipping Loading Screen")

    load_start = time.time()

    if sprite_sheets is None:
        sprite_sheets = {}

    if images is None:
        images = {}

    if sounds is None:
        sounds = {}

    if font is None:
        font = {}

    if videos is None:
        videos = {}

    if models is None:
        models = {}

    if icon is not None:
        logging.info("Setting Application Icon")
        pygame.display.set_icon(pygame.image.load(icon))

    logging.info("Caching {} Images".format(len(images)))
    i = 1
    for name, props in images.items():
        logging.info("    Image {} - {} of {}".format(name, i, len(images)))
        set_image(name, props['path'], get_optional(props, "w", None), get_optional(props, "h", None))
        i += 1

    logging.info("Caching {} Sprite Sheets".format(len(sprite_sheets)))
    i = 1
    for name, props in sprite_sheets.items():
        logging.info("    Sprite Sheet {} - {} of {}".format(name, i, len(sprite_sheets)))
        set_spritesheet(name, props["path"], props["w"], props["h"], props["duration"], get_optional(props, "final_size", None), get_optional(props, "invisible_color", (10, 10, 10)))
        i += 1

    logging.info("Caching {} Sound Files".format(len(sounds)))
    i = 1
    for name, props in sounds.items():
        logging.info("    Sound File {} - {} of {}".format(name, i, len(sounds)))
        set_sound(name, props["path"], get_optional(props, "volume", 1.0, float))
        i += 1

    logging.info("Caching {} Font Files".format(len(font)))
    i = 1
    for name, props in font.items():
        logging.info("    Font File {} - {} of {}".format(name, i, len(font)))
        set_font(name, props["path"], props["size"], bold=get_optional(props, "bold", False, bool), italic=get_optional(props, "italic", False, bool))
        i += 1

    logging.info("Caching {} Video Files".format(len(videos)))
    i = 1
    for name, props in videos.items():
        logging.info("    Video File {} - {} of {}".format(name, i, len(videos)))
        set_video(
            name, props["path"],
            has_mask=get_optional(props, "has_mask", False, bool),
            audio=get_optional(props, "audio", True, bool),
            audio_buffersize=get_optional(props, "audio_buffersize", 200000, int),
            target_resolution=get_optional(props, "target_resolution", None, eval),
            resize_algorithm=get_optional(props, "resize_algorithm", 'bicubic', str),
            audio_fps=get_optional(props, "audio_fps", 44100, int),
            audio_nbytes=get_optional(props, "audio_nbytes", 2, int),
            verbose=get_optional(props, "verbose", False, bool),
            fps_source=get_optional(props, "fps_source", 'tbr', str)
        )
        i += 1

    for name, props in models.items():
        set_model(name, path=props["path"])

    logging.info("Performing General Initializations...")
    set_default_image(default_image)
    set_default_spritesheet(default_spritesheet)

    clock = pygame.time.Clock()
    class_ref = PyGE

    logging.info("Building Application Entities")
    if alt_side_scroller is not None:
        class_ref = alt_side_scroller
    game = class_ref(screen, level_data, start_room, custom_objects, load_mode, background_color)

    load_duration = time.time() - load_start
    logging.info("Application Initialization Took {}s".format(load_duration))
    if load_duration < min_loading_time:
        logging.info("Loading Time < {}s. Waiting {}s To Compensate".format(min_loading_time, min_loading_time - load_duration))
        time.sleep(min_loading_time - load_duration)

    if post_load is not None:
        logging.info("Executing Auxillary Loading Methods")
        post_load()

    set_var("loaded", True)

    logging.info("Entering Main Application Loop!")
    i = 1
    while True:
        events = pygame.event.get()
        for event in events:
            if event.type == pygame.QUIT:
                termanate()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_F4 and (event.mod & pygame.KMOD_ALT or event.mod & pygame.KMOD_RALT) and enable_alt_f4:
                    termanate()

        i += 1

        game.update(events)
        game.draw()

        if game.mouse_cursor is not None:
            game.mouse_cursor.draw(
                pygame.mouse.get_pos()[0] - (game.mouse_cursor.w / 2),
                pygame.mouse.get_pos()[1] - (game.mouse_cursor.h / 2)
            )

        if auto_scale:
            main_surf.blit(pygame.transform.scale(screen, scaled_size), scaled_pos)
        else:
            main_surf.blit(screen, (0, 0))

        pygame.display.update()

        clock.tick(refresh_rate)
    def __init__(self, screen:pygame.Surface, args: dict, parent):
        """
        This is the object ALL objects MUST inherit from to be used in a room.
        This is the parent of ALL objects.
        :param screen: The screen to draw the object to
        :param args: The dictionary of properties specified in the XML.
        :param parent: The room the object will live in 
        """
        self.screen = screen
        self.args = self.modify_args(args)
        self.parent = parent

        self.angle = 0

        # self.should_center_width = get_mandatory(args, "@x", str) == "c"
        # self.should_center_height = get_mandatory(args, "@y", str) == "c"

        self.locked = get_optional(args, "locked", "false")

        self.screen_w, self.screen_h = self.screen.get_size()

        self.display = Color(screen, (255, 0, 255), 10, 10)
        w, h = self.display.get_size()

        self.w = get_optional(args, "w", None)
        self.h = get_optional(args, "h", None)
        
        if self.w is None:
            self.w = w
            
        if self.h is None:
            self.h = h
            
        self.positional_vars = {}
        self.reload_vars()

        self.x = None
        self.y = None
        self.recalculate_position()

        self.on_screen_cache = True
        self.on_mouse_over_cache = False
        self.deleted = False

        self.ticker = Ticker()
        self.time_delta = self.ticker.tick

        self.states = {}
        self.state = None

        self.x_delta = 0
        self.y_delta = 0

        self.model_type = type(self).__name__

        self.debug_color = get_sys_var("debug-color")

        self.siblings = self.parent.props.array

        self.oncreate()

        self.frequency_monitor_thread = None

        self.zindex = 10