view pytouhou/ui/gamerunner.pyx @ 694:3ff1af76e413

anm0: only use recoverable errors, no panics except for anm0 asserts.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Fri, 23 Aug 2019 02:24:08 +0200
parents 80687f258001
children
line wrap: on
line source

# -*- encoding: utf-8 -*-
##
## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##

cimport cython

from pytouhou.lib.gui cimport EXIT, PAUSE, SCREENSHOT, RESIZE, FULLSCREEN

from .window cimport Window, Runner
from .music import BGMPlayer, SFXPlayer
from pytouhou.game.game cimport Game
from pytouhou.game.music cimport MusicPlayer


cdef class GameRunner(Runner):
    cdef object background, con, resource_loader, keys, replay_level, common
    cdef Game game
    cdef Window window
    cdef list save_keystates
    cdef bint skip

    # Since we want to support multiple renderers, don’t specify its type.
    #TODO: find a way to still specify its interface.
    cdef object renderer

    def __init__(self, Window window, renderer, common, resource_loader,
                 bint skip=False, con=None):
        self.renderer = renderer
        self.common = common
        self.resource_loader = resource_loader

        self.window = window
        self.replay_level = None
        self.skip = skip
        self.con = con

        self.width = common.interface.width
        self.height = common.interface.height


    def load_game(self, Game game, background=None, bgms=None, replay=None,
                  save_keystates=None):
        self.game = game
        self.background = background

        if self.renderer is not None:
            self.renderer.load_textures(self.resource_loader.instanced_anms)
            self.renderer.load_background(background)

        self.set_input(replay)
        if replay and replay.levels[game.stage - 1]:
            game.players[0].lives = self.replay_level.lives
            game.players[0].power = self.replay_level.power
            game.players[0].bombs = self.replay_level.bombs
            game.difficulty = self.replay_level.difficulty

        self.save_keystates = save_keystates

        null_player = MusicPlayer()
        if bgms is not None:
            game.music = BGMPlayer(self.resource_loader, bgms)
            game.music.play(0)
        else:
            game.music = null_player

        game.sfx_player = SFXPlayer(self.resource_loader) if not self.skip else null_player


    cdef bint set_input(self, replay=None) except True:
        if not replay or not replay.levels[self.game.stage-1]:
            self.replay_level = None
        else:
            self.replay_level = replay.levels[self.game.stage-1]
            self.keys = self.replay_level.iter_keystates()


    @cython.cdivision(True)
    cdef bint set_renderer_size(self, long width, long height) except True:
        if self.renderer is not None:
            runner_width = float(self.width)
            runner_height = float(self.height)

            scale = min(width / runner_width,
                        height / runner_height)

            new_width = <long>(runner_width * scale)
            new_height = <long>(runner_height * scale)

            x = (width - new_width) // 2
            y = (height - new_height) // 2

            self.renderer.size = x, y, new_width, new_height


    cdef bint start(self) except True:
        if self.renderer is not None:
            self.set_renderer_size(self.width, self.height)
            self.renderer.start(self.common)


    cdef bint capture(self) except True:
        if self.renderer is not None:
            filename = 'screenshot/frame%06d.ppm' % self.game.frame
            self.renderer.capture(filename, self.width, self.height)


    cpdef bint update(self, bint render) except -1:
        cdef long keystate
        capture = False

        if self.background is not None:
            self.background.update(self.game.frame)
        for event, args in self.window.get_events():
            if event == EXIT:
                return False
            elif event == PAUSE:
                return False  # TODO: implement the pause.
            elif event == FULLSCREEN:
                self.window.toggle_fullscreen()
            elif event == SCREENSHOT:
                capture = True
            elif event == RESIZE:
                width, height = args
                self.set_renderer_size(width, height)
                if self.window is not None:
                    self.window.set_size(width, height)
        if self.replay_level is None:
            keystate = self.window.get_keystate()
        else:
            try:
                keystate = self.keys.next()
            except StopIteration:
                keystate = 0
                if self.skip:
                    self.set_input()
                    self.skip = False
                    self.game.sfx_player = SFXPlayer(self.resource_loader)

        if self.save_keystates is not None:
            self.save_keystates.append(keystate)

        if self.con is not None:
            self.con.run_iter(self.game, keystate)
        else:
            self.game.run_iter([keystate])

        labels = self.game.interface.labels
        if self.window is not None and 'framerate' in labels:
            labels['framerate'].set_text('%.2ffps' % self.window.get_fps())

        if render and not self.skip and self.renderer is not None:
            self.renderer.render(self.game)

        if capture:
            self.capture()

        return True