Модуль ачивок для нового Renpy

Если не указывать config.steam_appid, то будут отображаться локальные ачивки. Для этого нужно только закинуть в проект модуль ach.rpy и при необходимости задать свои параметры (желательно не в самом модуле на случай обновлений и багофиксов). По умолчанию фон окна сообщений создаётся из круглого символа, без картинок. Есть своя галерея достижений. К тому же имеется функция notify для сообщений в стиле ачивок. А ещё модуль при старте игры проверяет целостность ачивок в стиме и переоткрывает их, если они повреждены.

# все настройки можно скопировать в любой файл
# в init выше -9999 и там поменять
# чтобы модуль остался неизменным для копирования в другие проекты

# id вашего приложения в steam
# если None, то локальные ачивки будут отображаться
# иначе локальные будут скрыты, но будут синхронизироваться со стимом
define config.steam_appid = None

# галерея ачивок вызывается из игрового или главного меню так:
# textbutton _("Достижения") action ShowMenu("achgallery")

# спрятать окно сообщений/ачивок
# $ achhide()

# открыть ачивку с идентификатором id
# $ ach(id)

# показать сообщение в окошке ачивки
# необязательные параметры - заголовок и картинка
# $ notify(txt, caption=None, img=None, xalign=1., xmaximum=achw)

# удалить ачивку id (не равную None)
# или все открытые ачивки, если не указывать id
# $ achdel(id=None)

# переменные из этого блока лучше переназначать в любом init pyton
# в другом файле на случай, если модуль придётся менять на более новый
init -9999 python:
    # данные ачивок в формате словаря
    # id: (иконка, заголовок, текст)
    # иконки могут совпадать или отсутствовать вовсе
    # то же самое и с последующими пунктами
    # _(str) - конструкция для перевода на другие языки
    # achdata = {
        # "id1": ("image 1", _("Заголовок 1"), _("Текст 1")),
        # "id2": (None, _("Заголовок 2"), _("Текст 2"))
    # }
    achdata = {}

    # сколько должна быть видна ачивка при получении
    # включая время появления и исчезновения
    achtime = 4

    # врямя появления (или исчезновения) окошка с ачивкой
    achshowtime = achtime * .2

    # размеры окошка с ачивкой
    achw, achh = 720, 180

    # расстояние между картинкой и заголовком с текстом
    achxspacing = 8

    # вертикальный отступ от заголовка до текста
    achyspacing = 2

    # радиус скругления углов и внутренний отступ от краёв рамки
    achround = 50

    # цвет фона окошка
    achcolor = "#128b"

    # толщина рамки для окошка
    achoutline = 2

    # цвет обводки у окошка
    achoutlinecolor = "#fff"

    # цвет текста
    achtextcolor = "#fff"

    # цвет заголовка
    achcaptioncolor = "#def"

    # список полученных ачивок
    if persistent.ach is None:
        persistent.ach = []

init -9998:
    # фон для окошка с ачивкой
    # желательно, чтобы он растягивался с помощью Frame
    # вместо Text() можно поставить картинку
    # и фиксировать размеры углов, которые не должны растягиваться
    # а это вариант сплошного окошка с круглыми углами и белой рамочкой
    # (⚫, ◉)
    image achbg = Frame(Text( "⚫", color=achcolor, font="DejaVuSans.ttf", size=achround, outlines=[(achoutline, achoutlinecolor, 0, 0)]), int(achround/2), int(achround/2))

init python:
    # синхронизация при старте, если нужен стим
    if config.steam_appid:
        achievement.sync()

    # проверить целостность ачивок
    # id ачивок в стиме и в achdata должны совпадать!
    def achcheck():
        # если только нужен стим
        if not config.steam_appid:
            return
        # если какие-то открыты на устройстве, но не в стиме,
        # то удалить и открыть снова на случай, если они там повреждены
        for id in persistent.ach:
            if not achievement.has(id):
                achievement.clear(id)
                achievement.grant(id)
        # или если открыты в стиме, то открываем и на устройстве
        ids = list(achdata.keys())
        for id in ids:
            if achievement.has(id):
                if not id in persistent.ach:
                    persistent.ach.append(id)

    # проверяем целостность ачивок при запуске игры
    achcheck()

    # спрятать окно сообщений/ачивок
    def achhide():
        # скрыть уже показанное сообщение,
        # если оно ещё на экране
        if renpy.get_screen("achscr"):
            renpy.hide_screen("achscr")
            renpy.pause(achshowtime)

    # открыть ачивку с идентификатором id
    def ach(id):
        # если ачивка уже открыта, то ничего не делаем
        if id in persistent.ach:
            return
        # добавить в открытые
        if not (id in persistent.ach):
            persistent.ach.append(id)
        # если есть привязка к стиму, то добавляем туда
        if config.steam_appid:
            achievement.grant(id)
        # иначе показываем на экране
        else:
            achhide()
            # получить данные ачивки
            img, caption, txt = achdata[id]
            # показать сообщение о получении ачивки
            renpy.show_screen("achscr", img, caption, txt)

    # превращаем функцию def в action для экранов screen
    Ach = renpy.curry(ach)

    # для старых версий
    def aa(id):
        ach(id)

    # показать сообщение в окошке ачивки
    # необязательные параметры - заголовок и картинка
    def notify(txt, caption=None, img=None, xalign=1., xmaximum=achw):
        # скрыть уже показанное сообщение,
        # если оно ещё на экране
        if renpy.get_screen("achscr"):
            renpy.hide_screen("achscr")
            renpy.pause(achshowtime)
        # показать сообщение
        renpy.show_screen("achscr", img, caption, txt, xalign)
    Notify = renpy.curry(notify)

    # удалить ачивку id (не равную None)
    # или все открытые ачивки, если не указывать id
    def achdel(id=None):
        # если открыта, то закрываем
        if id in persistent.ach:
            persistent.ach.remove(id)
        # если id == None, то закрываем все
        elif id is None:
            persistent.ach = []

init:
    # стили можно поменять в init 1 и выше

    # стиль для окошка
    style achframe is frame:
        # фон
        background "achbg"            
        # размеры окошка
        xmaximum achw
        yminimum achh
        # внутренние отступы
        xpadding achround
        ypadding achround

    # стиль для текста ачивки
    style achtext is text:
        bold False
        size gui.text_size
        xalign .5
        layout "subtitle"
        text_align .5

    # стиль для заголовка ачивки
    style achcaption is achtext:
        bold True
        size gui.name_text_size

    # прозрачность
    transform alpha(alpha=.75):
        alpha alpha

    # показать/спрятать ачивку из-за верхнего края экрана
    # по умолчанию с правой стороны экрана
    transform achshowhide(t=achshowtime):
        # положение сообщения об ачивке
        yalign .0 alpha 0
        # появляется
        on show:
            yanchor 1.
            ease_back t alpha 1 yanchor .0
        # исчезает
        on hide:
            ease_back t alpha 0 yanchor 1.

# экран сообщения о получении ачивки
screen achscr(img=None, caption=None, txt=". . .", xalign=1., xmaximum=achw):
    # таймер для убирания ачивки с экрана
    timer achtime action Hide("achscr")

    # пустое окно
    window:
        style "empty"
        xalign xalign
        # анимация срабатывает при появлении или исчезновении окна
        at achshowhide()
        frame:
            style "achframe"
            xmaximum xmaximum
            hbox:
                yalign .5
                spacing achxspacing
                # отступ от левого края
                null width achxspacing
                # картинка в углу
                if img:
                    add img yalign .5
                # заголовок с текстом
                vbox:
                    align (.5, .5)
                    xfill True
                    spacing achyspacing
                    # заголовок
                    if caption:
                        text caption style "achcaption" xalign .5 color achcaptioncolor
                    # текст
                    if txt:
                        text txt style "achtext" xalign .5 color achtextcolor
                # отступ от правого края
                null width achxspacing

# экран галереи ачивок
screen achgallery():
    tag menu

    use game_menu(""):

        vbox:
            # контейнер
            frame:
                style "empty"
                xmaximum 960 ymaximum 960

                # окошко для скролла списка ачивок
                viewport:
                    yinitial 0
                    scrollbars "vertical"
                    mousewheel True
                    draggable True
                    id "achid"
                    
                    # рамка со всеми кнопками
                    frame:
                        style "empty"
                        # background "#0124"
                        vbox:
                            spacing 8
                            # в списках ключи расположены как попало, лучше отсортировать по алфавиту
                            for id in achdata.keys():
                                $ img, caption, txt = achdata[id]
                                button:
                                    style "achframe"
                                    hbox:
                                        yalign .5
                                        spacing achxspacing
                                        # отступ от левого края
                                        null width achxspacing
                                        # картинка в углу
                                        if img:
                                            add img yalign .5
                                        # заголовок с текстом
                                        vbox:
                                            align (.5, .5)
                                            xfill True
                                            spacing achyspacing
                                            # заголовок
                                            if caption:
                                                text caption style "achcaption" color achcaptioncolor
                                            # текст
                                            if txt:
                                                text txt style "achtext" color achtextcolor
                                        # отступ от правого края
                                        null width achxspacing
                                    # если не открыта, то прозрачная
                                    if not id in persistent.ach:
                                        at alpha(.25)

Комментарии