init:

    image bg pong field = "pong_field.png"

    python:

        class PongDisplayable(renpy.Displayable):

            def __init__(self):

                renpy.Displayable.__init__(self)
                
                # Используемые визуальные объекты.
                self.paddle = Image("pong.png")
                self.ball = Image("pong_ball.png")
                self.player = Text(povname, size=36)
                self.eileen = Text(u"Эйлин", size=36)
                self.ctb = Text("Click to Begin", size=36)
                
                # Размер некоторых изображений.
                self.PADDLE_WIDTH = 8
                self.PADDLE_HEIGHT = 79
                self.BALL_WIDTH = 15
                self.BALL_HEIGHT = 15
                self.COURT_TOP = 108
                self.COURT_BOTTOM = 543

                # Прилеплен ли мячик к платформе.
                self.stuck = True
                
                # Позиции платформ.
                self.playery = (self.COURT_BOTTOM - self.COURT_TOP) / 2
                self.computery = self.playery

                # Скорость компьютерного игрока.
                self.computerspeed = 350.0

                # Позиция, направление и скорость мячика
                self.bx = 88
                self.by = self.playery
                self.bdx = .5
                self.bdy = .5
                self.bspeed = 300.0

                # Время с предыдущего кадра.
                self.oldst = None

                # победитель.
                self.winner = None

            def visit(self):
                return [ self.paddle, self.ball, self.player, self.eileen, self.ctb ]
                
            # Перечитывает позицию мячика, организует отскоки, и
            # отрисовывает экран.
            def render(self, width, height, st, at):

                # Объект Render, в котором будет вестись отрисовка.
                r = renpy.Render(width, height)
                
                # Сколько времени прошло с предыдущего кадра.
                if self.oldst is None:
                    self.oldst = st
                    
                dtime = st - self.oldst
                self.oldst = st

                # Где будет мячик.
                speed = dtime * self.bspeed
                oldbx = self.bx

                if self.stuck:
                    self.by = self.playery
                else:                    
                    self.bx += self.bdx * speed
                    self.by += self.bdy * speed

                # Перемещает платформу компьютерного игрока. Стремится к self.by, 
                # но может быть ограничен скоростью реакции.
                cspeed = self.computerspeed * dtime
                if abs(self.by - self.computery) <= cspeed:
                    self.computery = self.by
                else:
                    self.computery += cspeed * (self.by - self.computery) / abs(self.by - self.computery)

                # Организация отскоков.
                
                # от верхнего края.
                ball_top = self.COURT_TOP + self.BALL_HEIGHT / 2                
                if self.by < ball_top:
                    self.by = ball_top + (ball_top - self.by)
                    self.bdy = -self.bdy
                    renpy.sound.play("pong_beep.wav", channel=0)
                    
                # От нижнего края.
                ball_bot = self.COURT_BOTTOM - self.BALL_HEIGHT / 2
                if self.by > ball_bot:
                    self.by = ball_bot - (self.by - ball_bot)
                    self.bdy = -self.bdy
                    renpy.sound.play("pong_beep.wav", channel=0)

                # Отрисовывает платформы, и организует отскоки от них.                       
                def paddle(px, py, hotside):

                    # Отрисовка изображеия платформы. мы даём ему область 800x600
                    # для отрисовки, зная, что изображения отрисуются по своему размеру.
                    # (Это истинно не для всех визуальных объектов. Solid, Frame,
                    # и Fixed расширяются, чтобы занять всё область.)
                    # Также передаём st и at.
                    pi = renpy.render(self.paddle, 800, 600, st, at)

                    # renpy.render возвращает объект Render, который мы можем
                    # blit к Render'y мы создаём.
                    r.blit(pi, (int(px), int(py - self.PADDLE_HEIGHT / 2)))

                    if py - self.PADDLE_HEIGHT / 2 <= self.by <= py + self.PADDLE_HEIGHT / 2:

                        hit = False
                        
                        if oldbx >= hotside >= self.bx:
                            self.bx = hotside + (hotside - self.bx)
                            self.bdx = -self.bdx
                            hit = True
                            
                        elif oldbx <= hotside <= self.bx:
                            self.bx = hotside - (self.bx - hotside)
                            self.bdx = -self.bdx
                            hit = True
                            
                        if hit:
                            renpy.sound.play("pong_boop.wav", channel=1)
                            self.bspeed *= 1.10
                            
                # Рисует две платформы.
                paddle(68, self.playery, 68 + self.PADDLE_WIDTH)
                paddle(724, self.computery, 724)

                # Рисует мячик.
                ball = renpy.render(self.ball, 800, 600, st, at)
                r.blit(ball, (int(self.bx - self.BALL_WIDTH / 2),
                              int(self.by - self.BALL_HEIGHT / 2)))

                # Отображает имя игрока.                
                player = renpy.render(self.player, 800, 600, st, at)
                r.blit(player, (20, 25))

                # Отображает имя Эйлин.
                eileen = renpy.render(self.eileen, 800, 600, st, at)
                ew, eh = eileen.get_size()
                r.blit(eileen, (790 - ew, 25))
                
                # Отображает "Click to Begin".
                if self.stuck:
                    ctb = renpy.render(self.ctb, 800, 600, st, at)
                    cw, ch = ctb.get_size()
                    r.blit(ctb, (400 - cw / 2, 30)) 
                    
                
                # Выясняет победителя.
                if self.bx < -200:
                    self.winner = "eileen"

                    # Нужно для того, чтобы быть уверенным в том
                    # что событие вызвано.
                    renpy.timeout(0)

                elif self.bx > 1000:
                    self.winner = "player"
                    renpy.timeout(0)

                # Просит перерисовать как можно скорее, дабы
                # мы могли отобразить следующий кадр.
                renpy.redraw(self, 0)

                # Возвращает объект Render.
                return r
                
            # Обработка событий.
            def event(self, ev, x, y, st):

                import pygame

                # Нажатие кнопки мыши == начало игры, путём установки
                # self.stuck = False.
                if ev.type == pygame.MOUSEBUTTONDOWN and ev.button == 1:
                    self.stuck = False
                    
                # Установка платформ игроков.
                y = max(y, self.COURT_TOP)
                y = min(y, self.COURT_BOTTOM)
                self.playery = y

                # Если есть победитель, он возвращается основной игре. 
                # В противном случае текущее событие игнорируется.
                if self.winner:
                    return self.winner
                else:
                    raise renpy.IgnoreEvent()
            

label demo_minigame:

    e "У Вас может возникнуть желание добавить в визуальную новеллу другую форму игры. Есть несколько путей достичь этого."

    e "Во-первых, с использованием функций UI, которые могут быть использованны для создания довольно сложного интерфейса на основе кнопок и меню."

    e "Этого часто достаточно для многих симуляционных игр."

    e "Есть ещё два пути расширения игровых возможностей Ren'Py. Оба требуют опыта программирования на Python'e и потому не для слабых духом."

    e "{i}Renpygame{/i} - это библиотека, позволяющая запускать игры на основе pygame внутри Ren'Py."

    e "При использовании {i}renpygame{/i} Ren'Py отступает в сторону и дает Вам полный контроль над взаимодействием с игроком."

    e "Скачать {i}renpygame{/i} можно со страницы Frameworks веб-сайта Ren'Py."
    
    e "Если же есть желание интегрировать Ваш код с Ren'Py, Вы можете написать визуальный объект, определяемый пользователем."

    e "Визуальные объекты, определяемые пользователем, в некотором смысле более ограниченны, но лучше сопрягаются с остальными частями Ren'Py."

    e "К примеру, при использовании визуального объекта, определяемого пользователем, поддерживаются обычные сохранение и загрузка."

    e "А сейчас сыграем в понг."

label demo_minigame_pong:

    #Выводим фон понга, обычным путём.
    scene bg pong field

    # Запускаем мини-игру "Понг", и определяем победителя.
    python:
        ui.add(PongDisplayable())
        winner = ui.interact(suppress_overlay=True, suppress_underlay=True)

    
    scene bg washington
    show eileen vhappy
        
    if winner == "eileen":
    
        e "Победа за мной!"

    else:

        e "Вы победили! Поздравляю."


    show eileen happy
        
    menu:
        e "Сыграем ещё раз?"

        "Конечно!":
            jump demo_minigame_pong
        "Не в этот раз.":
            pass        

        
    e "Будте осторожны, добавляя мини-игры в визуальные новеллы. Не каждый любитель визуальных новелл захочет проявлять мастерство в аркадных играх."

    return
