Сейчас у нас есть разбросанные по карте предметы, и мы хотим добавить возможность персонажу их поднять. Нам необходимо добавить в нашу программу код инвентаря персонажа. Так же, как и с предметами на карте, вначале, мы добавим коллекцию предметов в объект описывающий нашего персонажа.
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 кодов. End Type
Последнее определение в описании персонажа, это наша коллекция предметов инвентаря. Обратите внимание на странный диапазон индексов. Это ASCII коды символов, заканчивающиеся буквой z. Персонаж будет иметь 26 слотов для предметов инвентаря, в дополнение к тем предмета, которые на нем одеты и которые он держит в руках. Так как индекс массива инвентаря у нас является кодом символов, то для выбора какого либо предмета нам необходимо только получить код символа клавиши клавиатуры, которую нажал пользователь. Здесь мы воспользуемся гибкостью языка FreeBasic и большую часть работы по выбору предметов из инвентаря за нас выполнить компилятор. Многие программисты не пользуются гибкостью языка, и, в конечном итоге, получают громоздкий, трудно поддерживаемый, код.
Что нам нужно сделать сейчас, это позволить игроку поднять предмет, находящийся на карте. Для этого мы добавляем действие «Поднять», если игрок нажимает клавишу «g».
dod.bas'Поднять предмет с пола и положить в инвентарь персонажа.
If ckey = "g" Then
'Inventory type.
Dim inv As invtype
'Если под персонажем есть предмет.
If level.HasItem(pchar.Locx, pchar.Locy) = TRUE Then
'проверить на залото. Добавляем золото и сколько всего было золота.
Dim iclass As classids = level.GetInvClassID(pchar.Locx, pchar.Locy)
If iclass = clGold Then
'Поднимаем золото с карты.
level.GetItemFromMap pchar.Locx, pchar.Locy, inv
'Добавим к золоту персонажа.
pchar.CurrGold = pchar.CurrGold + inv.gold.amt
pchar.TotGold = pchar.TotGold + inv.gold.amt
'Добавим опыт.
pchar.CurrXP = pchar.CurrXP + inv.gold.amt
pchar.TotXP = pchar.TotXP + inv.gold.amt
'Выведем сообщение для игрока.
PrintMessage inv.gold.amt & " gold coins collected."
DrawMainScreen
Else
'Проверим, есть ли свободный слот в инвентаре.
Dim As Integer cidx = pchar.GetFreeInventoryIndex
'Если слот найден, поместим туда предмет.
If cidx <> -1 Then
level.GetItemFromMap pchar.Locx, pchar.Locy, inv
'Добавим в инвентарь персонажа.
pchar.AddInvItem cidx, inv
PrintMessage "Item added to inventory."
Else
'Нет свободных слотов.
PrintMessage "No free inventory slots."
EndIf
EndIf
Else
PrintMessage "Nothing to get."
EndIf
EndIfКогда игрок нажимает клавишу «g», мы проверяем, вначале, стоит ли персонаж на предмете. Если предмет присутствует на той же ячейке карты, что и персонаж, то, далее, мы получаем идентификатор класса этого предмета. Это нам нужно для того, что бы отличить золото от других предметов. Золото не добавляется в инвентарь персонажа а изменяет значение переменно общего количество денег персонажа, а также, в качестве бонуса, добавляет некоторое количество опыта. Если предмет не золото, то мы добавляем его в инвентарь.
При добавлении предмета в инвентарь нам нужно проверить, если ли в нем свободное место. Мы делаем это в функции GetFreeInventoryIndex.
character.bi'Возвращает индекс свободного слота инвентаря или -1.
Function character.GetFreeInventoryIndex() As Integer
Dim As Integer ret = -1
'Ишем пустой слот.
For i As Integer = LBound(_cinfo.cinv) To UBound(_cinfo.cinv)
'Прверяем идентификатор класса.
If _cinfo.cinv(i).classid = clNone Then
'Пустой слот.
ret = i
Exit For
EndIf
Next
Return ret
End FunctionЭта функция перебирает массив предметов инвентаря и ищет clNone записанное в полн ClassID предмета, что говорит о том, что данная ячейка инвентаря пуста. Если функция находит свободный слот, то она возвращает его индекс, или -1, если все слоты заняты. Как только мы получили индекс свободного слота в инвентаре, мы можем добавить в него предмет, используя процедуру AddInvItem.
character.bi'Добавляет предмет в слот инвентаря персонажа.
Sub character.AddInvItem(idx As Integer, inv As invtype)
'Проверим правильность индекса массива.
If idx >= LBound(_cinfo.cinv) And idx <= UBound(_cinfo.cinv) Then
'Очистим слот инвентаря.
ClearInv _cinfo.cinv(idx)
'Добавим предмет.
_cinfo.cinv(idx) = inv
End If
End Sub
Мы проверяем, чтобы убедиться, что индекс валиден (так как данная процедура может быть вызвана из других мест программы), очистим текущий слот инвентаря (опять же, в случае, если процедура вызывается из другого места) и установим в слот инвентаря предмет, который игрок взял с карты. Чтобы сообщить игроку о том, что предмет успешно подобран, мы добавляем вывод сообщения об этом в основной код обработки команды «Поднять». Теперь, когда предмет находится в инвентаре персонажа, нам нужно иметь возможность его отобразить, а также другие предметы, которые персонаж подобрал ранее. Это подводит нас к экрану инвентаря персонажа.
dod.bas'Нарисуем экран инвентаря.
If ckey = "i" Then
ManageInventory
'Необходимо перерисовать фон.
DrawBackground mainback()
DrawMainScreen
EndIfКогда игрок наживает клавишу «i», мы вызываем подпрограмму ManageInventory, которая обрабатывает команды инвентаря. Как только игрок выходит из инвентаря, основной экран перерисовывается. Итак, все действия над инвентарем персонажа происходят в 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 = "E" Then
ret = ProcessEval()
'Экран изменен.
If ret = TRUE Then
DrawInventoryScreen
EndIf
EndIf
EndIf
Sleep 1
Loop Until kch = key_esc
ClearKeys
End SubВначале мы рисуем экран инвентаря персонажа, на котором и будут обрабатываться все команды работы с инвентарем. На данный момент активна только одна команда «оценить», которая вызывается при нажатии клавиши «e». После того, как игрок сделал все что ему нужно было сделать с инвентарем, мы возвращаем управление основной программе. Функцию ProcessEval() мы рассмотрим немного позже, а пока, давайте посмотрим на процедуру DrawInventoryScreen.
dod.bas'Выводит на экран инвентарь персонажа.
Sub DrawInventoryScreen()
Dim As Integer col, row, iitem, ret, srow, ssrow, cnt, i
Dim As String txt, txt2, desc
Dim As invtype inv
Dim As UInteger clr
ScreenLock
'Установим фон для экрана инвентаря.
DrawBackground leatherback()
'Добавим заголовок.
txt = "Current Inventory for " & Trim(pchar.CharName)
col = CenterX(txt)
row = 1
'Выведем заголовок с тенью.
PutTextShadow txt, row, col, fbYellowBright
'Добавми текущую экипировку.
col = 2
row += 3
txt = "1 Primary: "
PutTextShadow txt, row, col, fbWhite
col = txcols / 2
txt = "4 Neck: "
PutTextShadow txt, row, col, fbWhite
col = 2
row += 2
txt = "2 Secondary: "
PutTextShadow txt, row, col, fbWhite
col = txcols / 2
txt = "5 Ring Right: "
PutTextShadow txt, row, col, fbWhite
col = 2
row += 2
txt = "3 Armor: "
PutTextShadow txt, row, col, fbWhite
col = txcols / 2
txt = "6 Ring Left: "
PutTextShadow txt, row, col, fbWhite
'Разделительная черта.
row += 2
col = 1
txt = String(80, Chr(205))
Mid(txt, 2) = " Equipment (*) = Not Evaluated "
txt2 = " Gold: " & pchar.CurrGold & " "
Mid(txt, 80 - Len(txt2)) = txt2
PutTextShadow txt, row, col, fbYellowBright
row += 2
col = 2
srow = row
'Выведем предметы в инвентаре.
For i = pchar.LowInv To pchar.HighInv
'Проверим, есть ли предмет в слоте.
iitem = pchar.HasInvItem(i)
If iitem = TRUE Then
'Получим предмет из слота.
pchar.GetInventoryItem i, inv
'Проверим, опознан или нет.
ret = IsEval(inv)
'Получим описание предмета.
desc = GetInvItemDesc(inv)
'Получим цвет предмета.
clr = inv.iconclr
'Создадим строку с описанием.
txt = Chr(i) & " " & desc & " "
'Если предмет не опознан, то отметить его, чтобы игрок знал, что данный предмет нужно опознать.
If ret = FALSE Then
txt &= "(*)"
EndIf
Else
txt = Chr(i)
clr = fbWhite
EndIf
'Перейдем на другой столбец, если достигли середины списка инвентаря.
cnt += 1
If cnt = 14 Then
col = txcols / 2
'Сохраним номер последней строки, чтобы потом наисовать разделитель.
ssrow = row
row = srow
EndIf
'Выведем текст.
PutTextShadow txt, row, col, clr
row += 2
Next
'Выведем разделительную линию.
row = ssrow + 1
col = 1
txt = String(80, Chr(205))
Mid(txt, 2) = " Spells Learned "
PutTextShadow txt, row, col, fbYellowBright
'Нарисуем слоты заклинаний.
col = 2
row += 2
cnt = 0
srow = row
For i = 65 To 78
txt = Chr(i)
'Перейдем на следующую колонку если достигли середины заклинаний.
cnt += 1
If cnt = 8 Then
col = txcols / 2
'Сохраним номер последней строки. Чтобы знать, откуда продолжить.
ssrow = row
row = srow
EndIf
'Выведем текст.
PutTextShadow txt, row, col, clr
row += 2
Next
'Разделитель.
row = ssrow + 1
col = 1
txt = String(80, Chr(205))
Mid(txt, 2) = " Commands "
PutTextShadow txt, row, col, fbYellowBright
'Выведем список команд
row += 2
txt = "(D)rop - (E)val - (W)ield/Wear - (R)ead - (E)at/Drink - (I)spect"
col = CenterX(txt)
PutTextShadow txt, row, col, fbWhite
ScreenUnLock
End SubКак вы видите, процедура аналогична процедуре вывода главного экрана. Экран разделен на 3 области. В верхней части отображаются предметы одеты на персонаж, и те, что он держит в руках. Средняя часть, это инвентарь персонажа (представьте себе рюкзак, и вещи лежащие в нем), и, наконец, нижняя секция отображает заклинания, которые выучил персонаж читая книги заклинаний. Единственный раздел, который содержит какую либо информацию на данном этапе разработки, это рюкзак, отображающий предметы из инвентаря персонажа.
dod.bas:DrawInventoryScreen'Проверим, есть ли предмет в слоте.
iitem = pchar.HasInvItem(i)
If iitem = TRUE Then
'Получим предмет из слота.
pchar.GetInventoryItem i, inv
'Проверим, опознан или нет.
ret = IsEval(inv)
'Получим описание предмета.
desc = GetInvItemDesc(inv)
'Получим цвет предмета.
clr = inv.iconclr
'Создадим строку с описанием.
txt = Chr(i) & " " & desc & " "
'Если предмет не опознан, то отметить его, чтобы игрок знал, что данный предмет нужно опознать.
If ret = FALSE Then
txt &= "(*)"
EndIf
Else
txt = Chr(i)
clr = fbWhite
EndIfПри заполнения экрана, мы проверяем, есть ли в слоте инвентаря предмет, используя функцию персонажа HasInvItem, и, если она возвращает «истина», получаем предмет из данного слота инвентаря при помощи функции GetInventoryItem. Далее, необходимо проверить, опознан ли данный предмет, используя функцию IsEval, так как неопознанные предметы мы будем отмечать при их отображении, а также получаем описание предмета при помощи функции GetInvItemDesc. Цвет, используемый для вывода описания предмета, соответствует цвету символа предмета при отображении на карте. Это улучшит у игрока связь между предметом и его описанием. Мы рисуем предмет на экране в соответствующем слоте инвентаря. Если слот пуст, то будет отображен только номер слота, в нашем случае, это одна из букв, от 'a' до 'z'. Давайте пройдемся по каждой из функций, которые мы здесь называли.
character.bi'Возвращает «истина», если в слоте инвентаря есть предмет.
Function character.HasInvItem(idx As Integer) As Integer
'Проверим на правильность индекса.
If idx >= LBound(_cinfo.cinv) And idx <= UBound(_cinfo.cinv) Then
'Проверим идентификатор класса предмета.
If _cinfo.cinv(idx).classid = clNone Then
Return FALSE
Else
Return TRUE
EndIf
Else
Return FALSE
End If
End Function
Здесь необходимо небольшое пояснение. Мы проверяем, равен ли идентификатор класса предмета clNone и если это верно, то возвращаем «ложь», иначе «истина».
inv.bi'Возвращает «истина», если предмет опознан.
Function IsEval(inv As invtype) As Integer
Dim As Integer ret
'Если нечего помечать, как неопознанное.
If inv.classid = clNone Then
ret = TRUE
Else
'Проверим предметы различного типа.
Select Case inv.classid 'Золото ненужно распознавать.
Case clGold
ret = TRUE
Case clSupplies 'Вернуть значение поля eval.
ret = inv.supply.eval
End Select
EndIf
Return ret
End FunctionФункуия IsEval просто возвращает значение флага eval предмета из инвентаря. Так как сейчас у нас есть только предметы класса Supply, то мы проверяем только их. Позже мы будем расширять данную функцию.
character.bi
'Получить предмет из слота инвентаря.
Sub character.GetInventoryItem(idx As Integer, inv As invtype)
'Очистим переменную для предмета.
ClearInv inv
'Проверим правильность индекса.
If idx >= LBound(_cinfo.cinv) And idx <= UBound(_cinfo.cinv) Then
inv = _cinfo.cinv(idx)
End If
End Sub
Мы уже видели это ранее. Мы проверяем индекс, чтобы убедится что он лежит в нужном диапазоне, и возвращаем предмет, находящийся в слоте по этому индексу. Мы не проверяем — свободный слот или нет, чтобы использовать данную функцию как для получения значений из пустых слотов инвентаря (как мы делали это в предыдущей главе), так и содержащих предметы, как мы делаем здесь.
inv.bi'Возвращает описание предмета, находящегося по координатам x,y.
Function GetInvItemDesc(inv As invtype) As String
Dim As String ret = "None"
'Если идентификатор класса предмета clNone, то ничего не делаем.
If inv.classid <> clNone Then
'Возбмем описание для золота.
If inv.classid = clGold Then
ret = inv.desc
EndIf
EndIf
'Описание для «ады».
If inv.classid = clSupplies Then
'Если не опознано, то вернуть основное описание.
If inv.supply.eval = FALSE Then
ret = inv.desc
Else
'Вернем секретное описание.
ret = inv.supply.sdesc
EndIf
EndIf
Return ret
End FunctionОпять же, тут можно наблюдать уже знакомый вам код. Если элемент был опознан, то выводим тайное описание, в противном случае — основное. Для того чтобы освежить вашу память, напомню: магические предметы будут иметь секретное описание, описывающее их магические свойства, для не магических предметов секретное и основное описания совпадают.
После того, как все данные инвентаря будут отображены на экране, игрок может производить манипуляции с предметами нажимая клавиши соответствующие командам, которые перечислены в низу экрана. Единственная команда, которую мы реализовали на данный момент, это команда опознания предмета.
dod.bas'Команда распознания предмета.
Function ProcessEval() As Integer
Dim As String res, mask, desc
Dim As Integer i, iret, iitem, evaldr, pint, rollp, rolle, ret = FALSE
Dim As invtype inv
Dim As tWidgets.btnID btn
Dim As tWidgets.tInputbox ib
'Убедимся, что у нас есть что распознавать.
For i = pchar.LowInv To pchar.HighInv
iitem = pchar.HasInvItem(i)
If iitem = TRUE Then
'получим предмет инвентаря.
pchar.GetInventoryItem i, inv
'распознан ли он.
iret = IsEval(inv)
'необходимо распознать.
If iret = FALSE Then
'Построим маску слотов.
mask &= Chr(i)
End If
EndIf
Next
If Len(mask) = 0 Then
ShowMsg "Evaluate", "Nothing to evaluate.", tWidgets.MsgBoxType.gmbOK
Else
'Написовать строку ввода на экране.
ib.Title = "Evaluate"
ib.Prompt = "Select item(s) to evaluate (" & 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
'Получим сложность распознавания.
evalDR = GetEvalDR(inv)
'Используя интеллект получим возможность распознавания.
pint = pchar.CurrInt + pchar.BonInt
'Случайное число для сложности распознавания.
rolle = RandomRange(0, evalDR)
'Случайное сичло для возможности распознавания игроком.
rollp = RandomRange(0, pint)
'Получим описание предмета.
desc = GetInvItemDesc(inv)
'Если случайное число у игрока > числу необходимому для распознания - распознаем
If rollp > rolle Then
desc &= " was succesfully evaluated."
ShowMsg "Evaluate", desc, tWidgets.MsgBoxType.gmbOK
SetInvEval inv, TRUE 'Set eval to true.
pchar.AddInvItem iitem, inv 'Put item back into inv.
ret = TRUE 'Flag caller that screen has changed.
Else
desc &= " was not evaluated."
ShowMsg "Evaluate", desc, tWidgets.MsgBoxType.gmbOK
EndIf
Next
EndIf
EndIf
Return ret
End FunctionВо первых, вы заметите некоторые определения из пространства имен tWidgets. Они содержатся в файле tWidgets.bi. Twidgets содержит в себе набор виджетов, которые реализуют GUI (Графические Интерфейс Пользователя) в текстовом видео режиме. Я не буду обсуждать здесь виджеты, но они полностью описаны в разделе приложений.
Первое, что мы должны сделать, это проверить — есть ли предметы, которые должны быть распознаны. Список не распознанных предметов создается в переменной маски, для показа игроку, если же таких предметов нет, то отображается соответствующее сообщение.
Как только у нас есть список предметов, игрок выбирает предмет, который хочет распознать. Процесс распознавания выглядит следующим образом:
- Получить evalDR (рейтинг сложности) предмета для распознания: evalDR = GetEvalDR (INV).
- Получить значение интеллекта персонажа вместе с бонусом: pint = pchar.CurrInt + pchar.BonInt
- Получить случайное число от 0 до evalDR: rolle = RandomRange(0, evalDR)
- Получить случайное число от 0 до полученного интеллекта персонажа с бонусом: rollp = RandomRange(0, pint)
- Если число, выпавшее персонажу от его интеллекта больше числа, выпавшего от рейтинга сложности, распознавание прошло успешно. If rollp > rolle
- Если распознавание успешно, то выставим у предмета поле eval в состояние «истина» и информируем об этом игрока. SetInvEval inv, TRUE
- Если распознавание прошло не удачно, то сообщаем об этом игроку.
Мы получаем Рейтинг сложности используя функцию GetEvalDR
inv.bi'Возвразает рейтинг сложности распознания предмета.
Function GetEvalDR(inv As invtype) As Integer
Dim As Integer ret
'Если нечего распознавать, то вернем 0.
If inv.classid = clNone Then
ret = 0
Else
'Выберем тип предмета.
Select Case inv.classid
Case clGold 'Золото не нужно распознавать .
ret = 0
Case clSupplies
ret = inv.supply.evaldr 'Вернем значение evaldr для clSupplies.
End Select
EndIf
Return ret
End FunctionМы просто возвращаем значение поля evaldr если есть предмет, требующий распознавания, иначе возвращаем 0. После того, как предмет распознан, нам нужно изменить у него значение поля evaldr, для чего мы используем процедуру SetInvEval.
inv.bi'Устанавливает значение eval, для предмета inv.
Sub SetInvEval(inv As invtype, state As Integer)
'проверим, есть ли предмет
If inv.classid <> clNone Then
'Выберем предмет.
Select Case inv.classid
Case clSupplies
inv.supply.eval = state 'Установим значение eval.
End Select
EndIf
End SubЗдесь мы просто устанавливаем значение поля eval в то, которое передали в процедуру. Это дает нам возможность установить значение в «не распознано», если мы захотим реализовать эту функциональность. Мы могли бы это сделать, как результат заклинания монстра или воздействия чего либо еще. Мы пока еще не решили, нужно ли нам это, но лучше оставить свободу выбора, если позже данный функционал нам понадобиться.
Функция распознавания возвращает «истина» в 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 = "E" Then
ret = ProcessEval()
'Экран изменен.
If ret = TRUE Then
DrawInventoryScreen
EndIf
EndIf
EndIf
Sleep 1
Loop Until kch = key_esc
ClearKeys
End SubЕсть и другие команды для инвентаря, которые мы пока не реализовали, так как у нас пока ограниченный набор предметов. Например команда «Взять в руки/Одеть» относится только к оружию, броне, ожерельям и кольцам. Команда «Прочитать» действительна только для свитков и книг с заклинаниями, которых у нас также нет на данный момент. Тем не менее, команды «Выбросить», «Съесть/Выпить» и «Осмотреть» мы можем реализовать уже с теми предметами, которые у нас есть на данный момент. Этим мы и займемся в следующей главе.

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