Инвентарь со счетчиком

В Renpy есть неприятная особенность. Если высота инвентаря больше высоты экрана, то клик по нижним предметам не срабатывает. Можно вместо бегунка сделать кнопки для пролистывания инвентаря. Заодно добавим возможность группировки предметов с одинаковыми изображениями. Для этого удобно пользоваться словарями. Количество рядов и колонок настраивается переменными columns и rows.



init python:
    # окно игры в центре экрана
    import os
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    # автоматическое объявление изображений
    config.automatic_images_minimum_components = 1
    config.automatic_images = [' ', '_', '/']
    config.automatic_images_strip = ['images']

    # размеры сетки с инвентарем настраиваются
    columns = 3
    rows = 3
    # размеры иконок в инвентаре
    side = 64

    # сам инвентарь
    inventory = {}
    # текущее положение скролла инвентаря
    from_i = 0

    # получить количество предметов
    def get_count(str):
        s = str.split(":")
        if len(s) > 0:
            return int(s[0])
        return 0
    # получить подсказку по предмету
    def get_hint(str):
        s = str.split(":")
        if len(s) > 1:
            return s[1]
        return ""
    # получить описание предмета
    def get_about(str):
        s = str.split(":")
        if len(s) > 2:
            return s[2]
        return "Описание отсутствует."

    # изменение количества предметов в ячейке на plus единиц
    # если не было, то будут созданы
    # если количество < 1, то удалены вовсе
    def iplus(item_name, plus = 1, hint = None, about = None):
        if plus:
            global inventory
            i = get_count(inventory.get(item_name, "0"))
            if hint == None:
                hint = get_hint(inventory.get(item_name, "0"))
            if about == None:
                about = get_about(inventory.get(item_name, "0"))
            i += plus
            if i < 1:
                # предметы количеством < 1 удаляются из инвентаря
                i = 0
                if item_name in inventory:
                    inventory.pop(item_name)
            else:
                inventory[item_name] = str(i) + ":" + hint + ":" + about
        else:
            # если plus == None, то уничтожаем все единицы предмета
            if item_name in inventory:
                inventory.pop(item_name)
        # изменения на экран
        renpy.restart_interaction()
        # вручную сохраняем измененные данные инвентаря
        # так как renpy сохраняет данные только
        # при смене текста в текстбоксе
        renpy.retain_after_load()

    # скролл вверх
    class iUp(Action, DictEquality):
        def __call__(self):
            global from_i
            from_i -= columns
            if from_i < 0:
                from_i = 0
            from_i = (from_i / columns) * columns
            renpy.restart_interaction()
        # кликабельность кнопки
        def get_sensitive(self):
            return from_i >= columns
    # скролл вниз
    class iDn(Action, DictEquality):
        def __call__(self):
            global from_i
            from_i += columns
            if from_i < 0:
                from_i = 0
            from_i = (from_i / columns) * columns
            renpy.restart_interaction()
        # кликабельность кнопки
        def get_sensitive(self):
            return from_i + columns * rows < len(inventory)

    # обработчик кликов,
    # чтобы не пихать гору кода в action кнопок
    def iclick(i):
        # например, вызовем окно применения предмета
        renpy.show_screen("scr_use", i)
        renpy.restart_interaction()

    # кнопка применения предмета
    def accept(i):
        # уменьшаем количество предметов на 1
        iplus(inventory.keys()[i], -1)

    # превращаем функции в action
    iClick = renpy.curry(iclick)
    Accept = renpy.curry(accept)

init:
    image empty = Null(side, side)

# необязательное окно прменения предмета
screen scr_use(i):
    modal True
    frame:
        background "#100b"
        align(.5, .5)
        xminimum 500
        has vbox:
            xalign .5
        text " "
        add inventory.keys()[i] xalign .5
        text get_hint(inventory.values()[i]) xalign .5
        text get_about(inventory.values()[i]) xalign .5
        text " "
        hbox:
            xminimum 500
            textbutton "Применить" xalign .5 xminimum 150 action [Hide("scr_use"), Accept(i)]
            textbutton "Отмена"    xalign .5 xminimum 150 action Hide("scr_use")
        text " "

# сам экран инвентаря
screen scr_inventory:
    zorder 111
    default tt = Tooltip(" ")
    button:
        align (.95, .05)
        background "#0018" # фон инвентаря
        action NullAction()
        # заполняем ячейки
        vbox:
            text "Инвентарь" xalign .5
            textbutton _("↑") xminimum side*columns action iUp() # скролл вверх
            for y in range(0, rows):
                hbox:
                    for x in range(0, columns):
                        # индекс ячейки
                        $ i = from_i + x + y * columns
                        # если не за грницами списка предметов
                        if i < len(inventory):
                            button:
                                # размеры предмета
                                minimum (side, side)
                                maximum (side, side)
                                background inventory.keys()[i] # картинка предмета
                                hovered tt.Action(get_hint(inventory.values()[i]))
                                # количество
                                text "{size=16}" + str(get_count(inventory.values()[i])) align(1.0, 1.0)
                                action iClick(i) # действе по клику
                        else:
                            # пустые ячейки
                            button:
                                minimum (side, side)
                                background "empty"
                                text " "
                                action NullAction()
            textbutton _("↓") xminimum side*columns action iDn() # скролл вниз
            text tt.value xalign .5

# чтобы использовать инвентарь нужно только
# доработать под себя фунцию для обработки клика по предмету в инвентаре iclick
label start:
    # если сначала объявить переменную прямо в скрипте,
    # то renpy будет сам сохранять ее содержимое
    $ inventory = {}
    scene bg
    # заполним инвентарь всеми доступными пузырьками в 1 экземпляре
    $ i = 1
    while i <= 12:
        # вторым параметром можно указать стартовое количество хреновин
        # третьим - подсказку, появляющуюся при наведении курсора
        # а можно ничего не указывать, только имя картинки - 1й параметр
        $ iplus('pot' + str(i), 1, "Хреновина №" + str(i))
        $ i += 1
    # покажем экран инвентаря
    show screen scr_inventory
    "Можно кликнуть по хреновинам в инвентаре."
    # пример изменения инвентаря из скрипта
    # iplus(спрайт, [количество, подсказка, описание])
    $ iplus('pot9', 2)
    "{image=pot9} Добавлено еще 2 вот таких хреновины."
    return

Комментарии

  1. Руслан, добрый день! Подскажите, пжл, как можно определить название предмета, по которому был выбран клик? Что-то не могу сообразить. Т.е., например, мне нужно, чтобы если было использовано зеленое зелье, убавилось его кол-во + появилась надпись типа "Вы выпили супер-пупер зелье!".

    ОтветитьУдалить
    Ответы
    1. можно в функцию «def accept(i)» добавить строку
      last_item_name = get_hint(inventory.values()[i])
      а там, где нужно сообщить об изменениях, проверять содержимое этой переменной. к примеру:
      if last_item_name:
      __"Потрачено [last_item_name]"
      __$ last_item_name = ""

      Удалить
    2. Почему-то last_item_name не присваивается значение get_hint(inventory.values()[i])

      Удалить
    3. забыл сказать, что в функциях нужно указать на то, что переменная глобальная с помощью команды global:
      global last_item_name
      last_item_name = ***

      Удалить
    4. Спасибо, Руслан. Я в Ren'Py ещё новичок. А Google не понимал, что я от него хочу. Поэтому Ваш ответ был последней надеждой :D

      Удалить
  2. А можно, как то сделать независимые друг от друга предметы со своим описанием, именем и переменной?

    ОтветитьУдалить
    Ответы
    1. Например, что бы в инвентаре например были деньги или некий предмет и при выборе из инвентаря изымались деньги и добавлялся предмет.

      Удалить
    2. можно, я разрешаю))
      это уже магазин, а не инвентарь.
      но кто мешает в функции-обработчике клика сделать проверку на тип предмета, его добавление и уменьшение денег? добавление описано в конце скрипта, деньги любой сам может убавить. дерзайте.

      Удалить
    3. Я просто хочу сделать так, что когда ты нажимаешь на предмет, то, скажем, у тебя,у некого персонажа, повышалась привязанность.

      Удалить
    4. да я не против, делайте.
      нужный код требуется вписать в def accept(i)
      либо в def iclick(i), если не нужно подтверждение действия. читайте комментарии к коду и дополняйте его своим.

      Удалить
  3. Не понял, что означает DictEquality в классе. Не подскажете?

    ОтветитьУдалить
  4. Gracias por tus aportes, son muy buenos, me gustaria saber si este script se puede usar en la ultima versión de RenPy.

    ОтветитьУдалить

Отправить комментарий