суббота, 3 марта 2012 г.

Давайте сделаем рогалик. Глава 14: Инвентарь персонажа, ч. 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% здоровья. Как и в других процедурах, в конце мы добавляем сообщение пользователю о результате употребления предмета.

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

Комментариев нет:

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