Давайте сделаем рогалик (Инвентарь персонажа, ч. II)

В этой главе мы закончим код обработки наших предметов питания. Так что можно будет отбросить команды «Съесть/Выпить» и «Осмотреть». До этого момента, различные участки кода нашей программы работали, в основном, в изоляции. Теперь мы подошли к моменту, где участки кода, начинают взаимодействовать друг с другом, особенно в команде «Съесть/Выпить». Употребление лечащих трав вылечит персонажа, кусок мяса добавит здоровья и, возможно, даст временный бонус к атрибуту силы, а у хлеба есть возможность вылечить отравление ядом. С этой точки зрения, различные модули программы будут начинать все больше взаимодействовать друг с другом, по мере того, как мы будем развивать динамическую систему описывающую наш игровой мир. Это захватывающая часть процесса программирования, когда вы будете видеть, как различные модули начинают работать вместе, и мир, который мы создали, начинает оживать.

Прежде чем перейти к коду в этой главе, взгляните на главное меню программы. Теперь из него удалены команды инвентаря, так как они отображаются в инвентаре и работают при входе в инвентарь. Это помещает команды именно туда где они должны быть. Например, для команды «Выбросить» необходимо, чтобы игрок выбрал какой предмет нужно выбросить, а для этого ему нужно видеть список предметов в инвентаре. Говоря другими словами, отображаемая на экране контекст соответствует команде. Размещение команд в неверном контексте создает беспорядок и снижает эффективность представления информации. Хорошее представление предоставляет игроку в текущий момент только ту информацию, которая ему нужна в данный момент. Многие программы, не только игры, оказываются неэффективными просто потому, что плохо выполнено представление необходимой информации.

Вы заметите, что на главном экране список команд разбит на две части. Верхняя часть связана с другими крупными экранами программы: справки, инвентаря и улучшения характеристик персонажа. В нижней части размещены команды, которые относятся к действиям, происходящим на главном экране. Подняться или опуститься по лестнице, поиск, выбор врага и т. д. Это несколько очищает представление списка команд и позволяет игроку намного легче найти необходимую ему в данный момент команду. Группировка информации по логическим разделам сократит время «путешествия» глаз игрока, что облегчает процесс игры. Конечно, игрок этого не поймет, и, вероятно, даже не заметит, насколько это удобно. Это говорит о том, что мы сделали свою работу правильно. Вот если игрок начинает замечать различные мелочи, то это означает, что нам нужно остановиться и пересмотреть наши методы.

Предметы еды, которые у нас сейчас есть, это, в основном, предметы лечения, которые при потреблении влияют на здоровье персонажа. Если в предмете есть магия, то дополнительно добавляется бонус или какой либо эффект. Для реализации различных эффектов от употребления предметов, нам необходимо обновить структуру персонажа.

character.bi

'Определение типа данных характеристик персонажа.
Type characterinfo
    cname As String * 35 'Имя персонажа.
    stratt(3) As Integer 'Сила (0), бонус силы (1), продолжительность действия бонуса в ходах (2)
    staatt(3) As Integer 'Выносливость
    dexatt(3) As Integer 'Ловкость
    aglatt(3) As Integer 'Проворство
    intatt(3) As Integer 'Интеллект
    currhp As Integer    'Текущее здоровье
    maxhp As Integer     'Максимальное здоровье
    currmana As Integer  'Текущая мана
    maxmana As Integer   'Максимальная мана
    ucfsk(3) As Integer  'Рукопашный бой
    acfsk(3) As Integer  'Оружие ближнего боя
    pcfsk(3) As Integer  'Дистанционное оружие
    mcfsk(3) As Integer  'Магическая атака
    cdfsk(3) As Integer  'Защита
    mdfsk(3) As Integer  'Магическая защита
    currxp As Integer    'Текущий, расходуемый опыт.
    totxp As Integer     'Общая сумма опыта, за время жизни персонажа.
    currgold As Integer  'Текущее количество золота.
    totgold As Integer   'Общая сумма золота за время жизни персонажа.
    ploc As mcoord       'Ткущие x и y координаты персонажа.
    cinv(97 To 122) As invtype 'Инвентарь персонажа для индексации использует значения asii кодов.
    isPoisoned As Integer  'Флаг отравления. Истина = персонаж отравлен.
    PoisonStr As Integer   'Сила отравления. Отравление накапливается.
End Type


Вы заметили, что мы добавили новые поля isPoisoned и PoisonStr. Также нам необходимо добавить свойства объекта characterinfo для доступа к этим полям.

character.bi

Declare Property Poisoned() As Integer     'Возвращает флаг отравления.
Declare Property Poisoned(flag As Integer) 'Устанавливает флаг отравления.
Declare Property PoisonStr() As Integer    'Возвращает силу отравления.
Declare Property PoisonStr(amt As Integer) 'Устанавливает силу отравления.


Эффекты, как это принято, имеют временный характер, поэтому нам нужен способ изменять состояния эффектов на каждом ходу. Делаем мы это в подпрограмме DoTimedActions объекта персонажа.

character.bi

'Обновим счетчики всех бонусов, а также обработаем эффекты, такие как отравление и т.д.
Sub character.DoTimedActions ()
    Dim As Integer roll1, roll2, v1, v2

    'Эффект от отравления зависит от его силы.
    If _cinfo.IsPoisoned = TRUE Then
        'Поучим силу отравления.
        v1 = _cinfo.PoisonStr
        'Получим выносливость персонажа с бонусом
        v2 = _cinfo.staatt(0) + _cinfo.staatt(1)
        'Случайны значения от отравления и выносливости.
        roll1 = RandomRange(1, v1)
        roll2 = RandomRange(1, v2)
        'Отравление выиграло.
        If roll1 > roll2 Then
            'Уменьшим здоровье на 1.
            _cinfo.currhp = _cinfo.currhp - 1
        Endif
    Endif
    'Проверим счетчики для различных бонусов
    If _cinfo.stratt(2) > 0 Then
        'Уменьшим счетчик.
        _cinfo.stratt(2) -= 1
        If _cinfo.stratt(2) <= 0 Then
            'Удалим бонус.
            _cinfo.stratt(1) = 0
        Endif
    Endif

    If _cinfo.staatt(2) > 0 Then
        _cinfo.staatt(2) -= 1
        If _cinfo.staatt(2) <= 0 Then
            _cinfo.staatt(1) = 0
        End If
    Endif

    If _cinfo.dexatt(2) > 0 Then
        _cinfo.dexatt(2) -= 1
        If _cinfo.dexatt(2) <= 0 Then
            _cinfo.dexatt(1) = 0
        End If
    Endif

    If _cinfo.aglatt(2) > 0 Then
        _cinfo.aglatt(2) -= 1
        If _cinfo.aglatt(2) <= 0 Then
            _cinfo.aglatt(1) = 0
        End If
    Endif

    If _cinfo.intatt(2) > 0 Then
        _cinfo.intatt(2) -= 1
        If _cinfo.intatt(2) <= 0 Then
            _cinfo.intatt(1) = 0
        End If
    Endif

    If _cinfo.ucfsk(2) > 0 Then
        _cinfo.ucfsk(2) -= 1
        If _cinfo.ucfsk(2) <= 0 Then
            _cinfo.ucfsk(1) = 0
        End If
    Endif

    If _cinfo.acfsk(2) > 0 Then
        _cinfo.acfsk(2) -= 1
        If _cinfo.acfsk(2) <= 0 Then
            _cinfo.acfsk(1) = 0
        End If
    Endif

    If _cinfo.pcfsk(2) > 0 Then
        _cinfo.pcfsk(2) -= 1
        If _cinfo.pcfsk(2) <= 0 Then
            _cinfo.pcfsk(1) = 0
        End If
    Endif

    If _cinfo.mcfsk(2) > 0 Then
        _cinfo.mcfsk(2) -= 1
        If _cinfo.mcfsk(2) <= 0 Then
            _cinfo.mcfsk(1) = 0
        End If
    Endif

    If _cinfo.cdfsk(2) > 0 Then
        _cinfo.cdfsk(2) -= 1
        If _cinfo.cdfsk(2) <= 0 Then
            _cinfo.cdfsk(1) = 0
        End If
    Endif

    If _cinfo.mdfsk(2) > 0 Then
        _cinfo.mdfsk(2) -= 1
        If _cinfo.mdfsk(2) <= 0 Then
            _cinfo.mdfsk(1) = 0
        End If
    Endif
End Sub


Если персонаж отравлен, то мы берем два случайных числа, максимальное значение одного зависит от силы отравления, второго от выносливости персонажа. Далее мы сравниваем эти числа. Если победило отравление, то уменьшаем здоровье персонажа на единицу. Далее мы проходим по всем бонусам характеристик персонажа и если счетчики бонусов больше 0, то уменьшаем их на единицу. Если при этом счетчик достиг значения 0, то убираем данный бонус. Счетчики продолжительности бонусов содержаться в 3-м элементе массива характеристик, значение бонуса во 2-м, а в первом находится значение самой характеристики. Процедуру DoTimedActions необходимо вызывать из основном цикла программы.

dod.bas

If ckey <> "" Then

    ...

    'Если игрок нажал клавишу, то посчитать временные воздействия
    pchar.DoTimedActions
    'Проверить, вдур персонаж умер.
    If pchar.CurrHP <= 0 Then
        isdead = TRUE
    Endif
End If


Так как это пошаговая игра, то как только игрок нажимает клавишу, «время» приходит в игровой мир и мы вызываем подпрограмму DoTimeActions. Если игрок не наживает никаких клавиш, то «время» стоит на месте и мы ничего не делаем. Если бы игра была в режиме реального времени, то мы бы проверяли текущее время с каким либо временным интервалом, и когда интервал истечет мы выполняли бы действие, приуроченное к данному интервалу времени. После обработки временных воздействий мы проверяем, остался ли наш персонаж в живых. Мы обязаны сделать это, так как у нас теперь есть такие вещи как отравление, которые уменьшают здоровье персонажа. Если здоровье персонажа достигнет значения 0, то персонаж умирает и игра заканчивается.

Если персонаж умер, то мы просто сообщим об этом игроку.

dod.bas

'Напечатаем сообщение о смерти персонажа.
If isdead = TRUE Then
    Cls
    Print pchar.CharName & " has died."
    Sleep
Endif


Позже мы переделаем процедуру окончания игры, чтобы охватить как поражения, так и победы.

Также мы обновили подпрограмму ManageInventory которая запускает на выполнения различные действия с предметами из инвентаря.

dod.bas

'Управление инвентарем персонажа.
Sub ManageInventory()
    Dim As String kch, ich
    Dim As Integer ret

    DrawInventoryScreen
    Do
        kch = Inkey
        kch = Ucase(kch)
        'Проверим, была ли нажата какая либо клавиша.
        If kch <> "" Then
            'Команда распознавания предметов.
            If kch = "V" Then
                ret = ProcessEval()
                'Перерисуем экран, если изменен.
                If ret = TRUE Then
                    DrawInventoryScreen
                Endif
            Endif
            'Команда «Съесть/Выпить».
            If kch = "E" Then
                ret = ProcessEatDrink()
                'Перерисуем экран, если изменен.
                If ret = TRUE Then
                    DrawInventoryScreen
                Endif
            Endif
            'Команда «выбросить».
            If kch = "D" Then
                ret = ProcessDrop()
                'Перерисуем экран, если изменен.
                If ret = TRUE Then
                    DrawInventoryScreen
                Endif
            Endif
            'Команда «Осмотреть».
            If kch = "I" Then
                ret = ProcessInspect()
                'Перерисуем экран, если изменен.
                If ret = TRUE Then
                    DrawInventoryScreen
                Endif
            Endif
        Endif
        Sleep 1
    Loop Until kch = key_esc
    ClearKeys
End Sub


Мы изменили клавишу вызова процедуры распознавания предметов с «E» на «V», так как клавишу «E» мы задействовали для команды «Съесть/Выпить». Давайте рассмотрим новые функции, которые мы здесь добавили. Начнем с обработки команды «Осмотреть».

dod.bas

'Обрабатывает команду «Осмотреть».
Function ProcessInspect() As Integer
    Dim As String res, mask, desc
    Dim As Integer i, iitem, ret = FALSE
    Dim As invtype inv
    Dim As tWidgets.btnID btn
    Dim As tWidgets.tInputbox ib
    Dim lines() As String

    'Проверим, есть то что необходмо осмотреть.
    For i = pchar.LowInv To pchar.HighInv
        iitem = pchar.HasInvItem(i)
        If iitem = TRUE Then
            'Получим предмет.
            pchar.GetInventoryItem i, inv
            'Построим маску.
            mask &= Chr(i)
        Endif
    Next
    If Len(mask) = 0 Then
        ShowMsg "Inpsect Items", "Nothing to inspect.", tWidgets.MsgBoxType.gmbOK
    Else
        'Нарисуем поле для ввода.
        ib.Title = "Inspect Items"
        ib.Prompt = "Select item(s) to drop (" & mask & ")"
        ib.Row = 39
        ib.EditMask = mask
        ib.MaxLen = Len(mask)
        ib.InputLen = Len(mask)
        btn = ib.Inputbox(res)
        'если не нажали отмену, но что то ввели
        If (btn <> tWidgets.btnID.gbnCancel) And (Len(res) > 0) Then
            'Переберем все символы что были введены.
            For i = 1 To Len(res)
                iitem = Asc(res, i) 'Получим индекс для массва инвентаря персонажа.
                'Получим предмет из инвентаря.
                pchar.GetInventoryItem iitem, inv
                GetFullDesc lines(), inv
                If Ubound(lines) > 0 Then
                    ShowMsgLines "Inspect Items", lines(), tWidgets.MsgBoxType.gmbOK
                Endif
            Next
        Endif
    Endif

    Return ret
End Function


Метод, который здесь используется, идентичен тому, что мы реализовали в прошлой главе для команды «Распознать». Мы перебираем все слоты инвентаря, чтобы определить, содержится ли в них какой либо предмет, при помощи метода HasInvItem объекта персонажа. HasInvItem проверяет правильность индекса массива инвентаря и идентификатор класса предмета, чтобы убедиться, что в слоте инвентаря находится предмет. Если предмет присутствует, то мы добавляем индекс массива в маску mask. А затем выводим пользователю поля ввода, чтобы он мог сделать свой выбор.

После того, как игрок выбрал необходимые предметы, мы получаем о них информацию вызывая подпрограмму GetFullDesc, чтобы получить полное описание предмета. GetFullDesc получает массив строк переменной длины и заполняет его данными. Когда управление возвращается из подпрограммы в программу, мы можем проверить верхнюю границу массива, и если больше 0, то в массиве содержится информация, которую необходимо отобразить. Значение по индексу 0 в данном массиве не содержит данных, а просто сообщает нам, что массив пуст. Это также упрощает расширение массива, как мы увидим далее.

inv.bi

'Возвращает расширенное описание предметов.
Sub GetFullDesc(lines() As String, inv As invtype)
    Dim As Integer idx = 0

    'Очистим массив.
    Redim lines(0 To idx) As String
    'Убедимся что предмет есть в инвентаре.
    If inv.classid <> clNone Then
        'Выберем предмет.
        Select Case inv.classid
            Case clSupplies
                idx += 1
                Redim Preserve lines(0 To idx) As String
                lines(idx) = inv.desc
                Select Case inv.supply.id
                    Case supHealingHerb
                        idx += 1
                        Redim Preserve lines(0 To idx) As String
                        lines(idx) = "* Adds 50% max HP to current HP"
                        idx += 1
                        Redim Preserve lines(0 To idx) As String
                        lines(idx) = "* Magic: Max healing"
                    Case supHunkMeat
                        idx += 1
                        Redim Preserve lines(0 To idx) As String
                        lines(idx) = "* Adds 25% max HP to current HP"
                        idx += 1
                        Redim Preserve lines(0 To idx) As String
                        lines(idx) = "* Magic: Bonus to STR stat"
                    Case supBread
                        idx += 1
                        Redim Preserve lines(0 To idx) As String
                        lines(idx) = "* Adds 10% max HP to current HP"
                        idx += 1
                        Redim Preserve lines(0 To idx) As String
                        lines(idx) = "* Magic: Cure poison"
                    Case supBottleOil
                        idx += 1
                        Redim Preserve lines(0 To idx) As String
                        lines(idx) = "* Fuel for lantern"
                        idx += 1
                        Redim Preserve lines(0 To idx) As String
                        lines(idx) = "* Magic: See all tiles on map"
                End Select
                idx += 1
                Redim Preserve lines(0 To idx) As String
                If IsEval(inv) = TRUE Then
                    lines(idx) = "* Item is evaluated"
                Else
                    lines(idx) = "* Item is not evaluated"
                End If
        End Select
    Endif

End Sub


Первое что мы делаем в GetFullDesc, это очищаем полученный массив при помощи команды ReDim. Значение переменной idx при вызове ReDIM равно 0. поэтому, после ее выполнения, наш массив имеет верхнюю границу 0, а все содержимое, если оно имелось, удалено.

Затем мы проверяем ClassID предмета из инвентаря чтобы определить его тип. Так как сейчас у нас имеется только один тип предметов — расходные материалы, то проверяем только его. Затем мы проверяем идентификатор предмета, для того, чтобы точно определить осматриваемый предмет, и только после этого мы можем создать его описание. Обратите внимание на то, каким образом мы расширяем массив:

inv.bi

idx += 1
Redim Preserve lines(0 To idx) As String


Мы увеличиваем idx на единицу, и используем ReDim Preserve, чтобы увеличить массив на один элемент. Команда Preserve указывает на то, что не нужно очищать содержащиеся в массиве данные при изменении его размера. После того, как массив увеличен, мы добавляем строку с описанием и повторяем эту процедуру до тех пор, пока у нас не будет полное описание предмета. Мы используем динамический массив строк, так как от предмета к предмету, в их описании будет содержаться разный объем информации. Мы просто будем использовать 1 в качестве базового значения индекса, с которого будет начинаться описание предмета, а функция Ubound сообщит нам о том, сколько записей содержащих информацию в нем находится.

В подпрограмме ProcessInspect мы отображаем полученное описание предмета при помощи многострочного messagebox, кук это видно на изображении в начале этой главы. Следующая команда, которую мы рассмотрим, это «Выбросить».

dod.bas

'Обработка команды «Выбросить».
Function ProcessDrop() As Integer
    Dim As String res, mask, desc
    Dim As Integer i, iret, iitem, ret = FALSE
    Dim As invtype inv
    Dim As tWidgets.btnID btn
    Dim As tWidgets.tInputbox ib
    Dim As vec mvec

    'Создадим маску предметов, которые можно выбросиить.
    For i = pchar.LowInv To pchar.HighInv
        iitem = pchar.HasInvItem(i)
        If iitem = TRUE Then
            'Получим предмет инвентаря.
            pchar.GetInventoryItem i, inv
            'Построим маску.
            mask &= Chr(i)
        Endif
    Next
    If Len(mask) = 0 Then
        ShowMsg "Drop Items", "Nothing to drop.", tWidgets.MsgBoxType.gmbOK
    Else
        'Нарисуем поле для ввода.
        ib.Title = "Drop Items"
        ib.Prompt = "Select item(s) to drop (" & mask & ")"
        ib.Row = 39
        ib.EditMask = mask
        ib.MaxLen = Len(mask)
        ib.InputLen = Len(mask)
        btn = ib.Inputbox(res)
        'Если что то ввели, но не отмену.
        If (btn <> tWidgets.btnID.gbnCancel) And (Len(res) > 0) Then
            'Пройдемся по списку выбранных предметов.
            For i = 1 To Len(res)
                iitem = Asc(res, i) 'Получим индекс предмета из массива инвентаря.
                'Получим предмет.
                pchar.GetInventoryItem iitem, inv
                'Получим описание предмета.
                desc = GetInvItemDesc(inv)
                'Поищем пустое место на полу.
                iret = level.GetEmptySpot(mvec)
                'Еашли пустое место.
                If iret = TRUE Then
                    'Положим предмет на пол.
                    level.PutItemOnMap mvec.vx, mvec.vy, inv
                    'Очистим полученный из инвентаря предмет.
                    ClearInv inv
                    'Положим пустой предмет в инвентарь.
                    pchar.AddInvItem iitem, inv
                    ret = TRUE
                    desc &= " was dropped."
                    ShowMsg "Drop Items", desc, tWidgets.MsgBoxType.gmbOK
                Else
                    'Нет свободного места на полу.
                    desc = "No empty map tiles to drop item."
                    ShowMsg "Drop Items", desc, tWidgets.MsgBoxType.gmbOK
                    Exit For
                Endif
            Next
        Endif
    Endif

    Return ret
End Function


В обработчике команды «Выбросить» мы собираем все предметы в список, как делали раньше, и предоставляем этот список игроку. После того, как игрок сделает выбор мы перебираем выбранные предметы и пытаемся их выложить на пол. Очевидно, что для этого необходимо чтобы на полу было свободное место, которое мы ищем в функции GetEmptySpot объекта уровня подземелья.

map.bi

'Возвращает «Истина» если найдено пустое место на карте под или вокруг персонажа. Координаты места помещает в переменную vec.
Function levelobj.GetEmptySpot(v As vec) As Integer
    Dim As Integer ret = FALSE, hi
    Dim As vec ev
    Dim As terrainids tid

    'Проверим местность под персонажем.
    ev.vx = pchar.Locx
    ev.vy = pchar.Locy
    hi = HasItem(ev.vx, ev.vy)
    If  hi = FALSE Then
        ret = TRUE
        v = ev
    Else
        'Проверим все клетки карты вокруг персонажа.
        For i As compass = north To nwest
            ev.vx = pchar.Locx
            ev.vy = pchar.Locy
            ev += i
            'Получим тип местности.
            tid = GetTileID(ev.vx, ev.vy)
            'Проверим, есть ли на ячейке предмет.
            hi = HasItem(ev.vx, ev.vy)
            'Если это пол и на нем нет предметов, то пустое место найдено.
            If (tid = tfloor) And (hi = FALSE) Then
                v = ev
                ret = TRUE
                Exit For
            Endif
        Next
    Endif

    Return ret
End Function


Функция GetEmptySpot в качестве входного параметра получает объект типа вектор. Он определен в файле vec.bi, а также, в этом же файле, перегружены некоторые операции для данного векторного объекта, чтобы было легче манипулировать с координатами вектора. Например, перегруженный оператор += позволяет получить новые координаты в векторе просто добавив к нему одно из направлений сторон света. Это позволяет очень просто получить координаты всех ячеек карты вокруг персонажа. Мы просто перебираем их в обычном цикле FOR-NEX и выходим из него, если пустое место найдено.

Критерием определения пустой ячейки карты является то, что это пол и на на нем не лежит никаких предметов. Если персонаж стоит в коридоре, то возле него будут 3 возможных пустых ячейки. Это место на котором стоит персонаж, а также места впереди и позади его. Если персонаж находится в комнате, то будет 9 возможных мест: 1 — там где стоит персонаж и 8 клеток вокруг персонажа по направлению 8-ми сторон света. Стены, двери и те ячейки карты, на которых уже лежат предметы, не будут являться пустыми.

После того, как место для размещения предмета на полу будет найдено, функция помещает его координаты передаваемый параметр и возвращает «истина», что бы указать вызывающей программе, что пустая ячейка возле персонажа есть. Если пустой ячейки нет, то функция возвращает «ложно».

После того, как пустая ячейка определена, мы вызываем метод объекта карты PutItemOnMap

map.bi

'Добавить предмет inv на карту по координатам x, y.
Sub levelobj.PutItemOnMap(x As Integer, y As Integer, inv As invtype)
    ClearInv _level.linv(x, y)
    _level.linv(x, y) = inv
End Sub


Мы просто очищаем слот предмета для ячейки карты и помещаем туда предмет из переменной inv. На самом деле, очищать слот карты не обязательно, так как перед вызовом данной процедуры мы убедились что он пуст, но если нам понадобиться воспользоваться этой функцией по каким либо другим причинам, то на всякий случай очистим ячейку, в которую будем помещать предмет.

Когда предмет добавлен на карту, мы удаляем его из инвентаря персонажа и сообщаем игроку об успешности операции.

dod.bas:ProcessDrop

'Положим предмет на пол.
level.PutItemOnMap mvec.vx, mvec.vy, inv
'Очистим полученный из инвентаря предмет.
ClearInv inv
'Положим пустой предмет в инвентарь.
pchar.AddInvItem iitem, inv
ret = TRUE
desc &= " was dropped."
ShowMsg "Drop Items", desc, tWidgets.MsgBoxType.gmbOK


Когда игрок вернется на главный экран, он увидит выброшенный предмет на карте, как можно было ожидать.

Последняя процедура, которую нам осталось рассмотреть, это ProcessEatDrink.

dod.bas

...
For i = 1 To Len(res)
    iitem = Asc(res, i) 'Получим индекс в инвентаре персонажа.
    'Получим предмет.
    pchar.GetInventoryItem iitem, inv
    'Проверим, распознан или нет.
    evalstate =  IsEval(inv)
    'Проверим на магию.
    evalDR = GetEvalDR(inv)
    'Получим описание предметп.
    desc1 = GetInvItemDesc(inv)
    desc2 = ""
    'Используем предмет.
    If inv.supply.id = supHealingHerb Then
        'Опознанный магический предмет.
        If (evalstate = TRUE) And (evalDR > 0) Then
            pchar.CurrHP = pchar.MaxHP
            desc2 = " completely healed you!"
        Else
            'Добавляет 50% здоровья.
            pchar.CurrHP = pchar.CurrHP + (pchar.MaxHP * .5)
            If pchar.CurrHP > pchar.MaxHP Then
                pchar.CurrHP = pchar.MaxHP
            Endif
            pchar.CurrHP = pchar.MaxHP
            desc2 = " added some health!"
        Endif
    Elseif inv.supply.id = supHunkMeat Then
        'Добавляет 25% здоровья.
        pchar.CurrHP = pchar.CurrHP + (pchar.MaxHP * .25)
        If pchar.CurrHP > pchar.MaxHP Then
            pchar.CurrHP = pchar.MaxHP
            desc2 = " added some health!"
        Endif
        'Опознанный магический предмет.
        If (evalstate = TRUE) And (evalDR > 0) Then
            pchar.BonStr = RandomRange(1, pchar.CurrStr)
            pchar.BonStrCnt = RandomRange(1, 100)
            desc2 = " healed you and added some strength!"
        Endif
    Elseif inv.supply.id = supBread Then
        'Добавляет 10% здоровья.
        pchar.CurrHP = pchar.CurrHP + (pchar.MaxHP * .1)
        If pchar.CurrHP > pchar.MaxHP Then
            pchar.CurrHP = pchar.MaxHP
            desc2 = " added some health!"
        Endif
        'Опознанный магический предмет.
        If (evalstate = TRUE) And (evalDR > 0) Then
            'Лечит отравление ядом, если необходимо.
            If pchar.Poisoned = TRUE Then
                pchar.Poisoned = FALSE
                pchar.PoisonStr = 0
                desc2 = " added some health and cured your poison!"
            End If
        Endif
    Endif
    ShowMsg "Eat/Drink", desc1 & desc2, tWidgets.MsgBoxType.gmbOK
    'Clear the item.
    ClearInv inv
    'Put the item back into inventory.
    pchar.AddInvItem iitem, inv
Next
...


Так как процесс отбора предметов, которые можно употребить, совпадает с тем, что мы видели в предыдущих функциях, то мы рассмотрим только основной код исполнения команды «Съесть/Выпить». Обратите внимание, что мы получаем должны узнать — был ли предмет распознан evalstate = IsEval(inv), так как только тогда будут действовать его магические свойства. Конечно, для этого, так же необходимо, чтобы эти магические свойства присутствовали в предмете, что мы и узнаем проверяя рейтинг сложности опознания предмета evalDR = GetEvalDR(inv). Если evalDR больше 0, то магические свойства присутствуют и мы их применяем наряду с базовыми свойствами объекта, как показано в следующем примере.

dod.bas

...
Elseif inv.supply.id = supHunkMeat Then
'Добавляет 25% здоровья.
pchar.CurrHP = pchar.CurrHP + (pchar.MaxHP * .25)
If pchar.CurrHP > pchar.MaxHP Then
    pchar.CurrHP = pchar.MaxHP
    desc2 = " added some health!"
Endif
'Опознанный магический предмет.
If (evalstate = TRUE) And (evalDR > 0) Then
    pchar.BonStr = RandomRange(1, pchar.CurrStr)
    pchar.BonStrCnt = RandomRange(1, 100)
    desc2 = " healed you and added some strength!"
Endif
...


Исключение здесь являются лечащие травы, так как нет никакого смысла применять базовый эффект лечения 50% здоровья если присутствует магически — исцеление на 100% здоровья. Как и в других процедурах, в конце мы добавляем сообщение пользователю о результате употребления предмета.

Следующая команда, которую мы должны реализовать это «Взять в руки/Одеть», и для этого нам нужно добавить в игру доспехи и оружие. Есть и другие предметы, которые нам нужно добавить, например, такие как кольца, ожерелья и книги заклинаний, но мы работаем над нашей следующей важной вехой — основой игрового процесса. Как только мы получим броню и оружие, мы добавим несколько монстров, и у нас уже будет игра в которую можно играть! Это, действительно, заставит наш игровой мир ожить, а, так же, будет нам отличной наградой за огромную работу, которую мы уже проделали.

Перевод на русский: Fantik

содержание | назад | вперед