Давайте сделаем рогалик (Допиливаем игру 2)
Существует одна вещь, которую мы обязательно должны сделать, это убедиться что при нажатии на клавишу Escape игрок действительно хочет выйти из игры. Нет способа расстроить игрока легче, чем закрыть нашу игру, когда игрок случайно, в пылу сражения, нажмет клавишу Escape. Мы можем избежать этого путем простой замены.
'Проверим нажатие клавиши Escape. If ckey = key_esc Then Dim As tWidgets.tMsgbox conf Dim As tWidgets.btnID btn 'Спросим игрока, действительно ли он хочет выйти из игры. conf.MessageStyle = tWidgets.MsgBoxType.gmbYesNo conf.Title = "Confirm Exit" btn = conf.MessageBox("Do you wish to quit?") If btn = tWidgets.btnID.gbnYes Then done = TRUE Endif Endif
Здесь мы просто выводим окно сообщения с вопросом о подтверждении выхода из игры, и даем возможность игроку подтвердить выход, выбрав «Yes» или отменить «No». Если игрок подтвердит свое желаний выйти то мы устанавливаем флаг выхода, иначе, просто продолжим главный цикл. Это позволит предотвратить случайны выходы из из игры испортив игроку настроение. Мы также будем использовать этот код, для того чтобы сохранить игру при выходе, так что, добавив его, мы сразу убьем двух зайцев.
Наш следующий апгрейд не только косметический, но и позволит немного упростить код работы с инвентарем, что даст нам некоторые варианты для более поздних усовершенствований. Сейчас, когда персонаж берет в руки двуручное оружие, в одном слоте экипировки будет содержаться название этого оружия, а второй будет отображаться как пустой, хотя и будет не доступен. Это может ввести в заблуждение новых игроков, поэтому, было бы неплохо, отметить этот слот как недоступный. Мы не только можем это сделать, но, заодно, и упростим немного наш код.
Мы начнем с добавления нового класса предмета инвентаря — clUnavailable.
inv.bi'Идентификаторы классов предметов. Enum classids clNone clGold clSupplies clArmor clShield clWeapon clAmmo clPotion clRing clNecklace clSpellBook clSpell clUnavailable End Enum
Как вы можете догадаться, новый класс предмета будет вести себя точно также, как и остальные предметы инвентаря. В результате нам не придется делать дополнительных проверок на невозможность использования второго слота, все будет обрабатываться обычными функция для работы с инвентарем, что сделает наш код более прямолинейным и надежным. Так как мы добавили новый объект инвентаря, нам нужна для него программа генерации.
inv.bi'Генерирует «недоступный» инвентарный обхект. Sub GenerateUnavail (inv As invtype) ClearInv inv inv.classid = clUnavailable inv.desc = "Unavailable" inv.iconclr = fbWhite End Sub
На самом деле не очень много параметров. Все что нам нужно, это указать идентификатор типа предмета, его описание и цвет значка, необходимые для отображения этого предмета на главном экране и экране инвентаря.
Единственное место, где сейчас будет использоваться этот предмет, это во время экипировки двуручного оружия. Для этого необходимо внести небольшое изменение в метод AddInvItem объекта персонажа.
character.bi'Добавляет предмет из инвентаря в экипировочный слот персонажа. Sub character.AddInvItem(idx As Integer, inv As invtype) Dim As invtype inv2 'Проверим в допустимых ли рамках индекс. If idx >= Lbound(_cinfo.cwield) And idx <= Ubound(_cinfo.cwield) Then 'Очистим слот инвентаря. ClearInv _cinfo.cwield(idx) 'Назначим предмет в слот экипировки. _cinfo.cwield(idx) = inv 'Проверим на двуручный предмет и если это так, установим во вторую руку «недоступный» предмет. If inv.classid = clWeapon Then 'Если двуручное оружие, займем вторую руку. If inv.weapon.hands = 2 Then 'Получим «недоступный» предмет. GenerateUnavail inv2 'Поместим предмет в слот. If idx = wPrimary Then ClearInv _cinfo.cwield(wSecondary) _cinfo.cwield(wSecondary) = inv2 Else ClearInv _cinfo.cwield(wPrimary) _cinfo.cwield(wPrimary) = inv2 Endif Endif End If Else 'Проверим индексы инвентаря. If idx >= Lbound(_cinfo.cinv) And idx <= Ubound(_cinfo.cinv) Then 'Очистим слот инвентаря. ClearInv _cinfo.cinv(idx) 'Назначим предмет в слот. _cinfo.cinv(idx) = inv End If Endif End Sub
Вначале мы проверяем, оружие ли это, и если это так, то смотрим — сколько рук необходимо для его использования. Если оружие двуручное, то во второй слот мы помещаем «недоступный» предмет. В результате в первом слоте у нас будет отображаться название используемого оружия, во втором: «Недоступно».
И как же это нам поможет упростить работу с инвентарем? Давайте взглянем на код функции ProcessEquip.
dod.bas'Экипировка предметов. Function ProcessEquip() As Integer Dim As String res, mask, msg Dim As Integer i, iitem, iret, ret = FALSE Dim As invtype inv Dim As tWidgets.btnID btn Dim As tWidgets.tInputbox ib Dim As wieldpos slot 'Получим список вещей, которые можно экипировать. For i = pchar.LowInv To pchar.HighInv iitem = pchar.HasInvItem(i) If iitem = TRUE Then 'Получим предмет инвентаря. pchar.GetInventoryItem i, inv 'Проверим, может ли быть экипирован. iret = MatchUse(inv, useWieldWear) 'Предмет может быть экипирован. If iret = TRUE Then 'Построим маску. mask &= Chr(i) End If Endif Next If Len(mask) = 0 Then ShowMsg "Equip Items", "Nothing to equip.", tWidgets.MsgBoxType.gmbOK Else 'Написуем строку для ввода. ib.Title = "Equip Items" ib.Prompt = "Select item(s) to equip (" & 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) 'Get index into inventory. 'Получим предмет инвентаря. pchar.GetInventoryItem iitem, inv 'Получим свободный слот. slot = pchar.GetFreeSlot(inv, msg) 'Если есть свободный слот. If slot <> wNone Then 'Поместим предмет в слот экипировки. pchar.AddInvItem slot, inv 'Очистим предмет. ClearInv inv 'Обновим слот инвентаря. pchar.AddInvItem iitem, inv ret = TRUE msg &= " was equipped." End If ShowMsg "Equip Items", msg, tWidgets.MsgBoxType.gmbOK Next Endif Endif Return ret End Function
Если вы помните старый код этой функции, то он был намного сложнее. Там, где сейчас у нас вызывается функция GetFreeSlot, раньше был сложный код на проверку слотов принимая во внимание необходимое количество рук и т. д. Теперь все это не нужно, функция GetFreeSlot возвращает нам слот, который мы можем использовать. Давайте взглянем на новый код для получения свободного слота.
character.bi'Возвращает свободный слот или wNone. Function character.GetFreeSlot(inv As invtype, msg As String) As wieldpos Dim As wieldpos rslot Dim As String desc Dim As invtype inv2 'Получим описание предмета. desc = GetInvItemDesc(inv) msg = desc 'Получим слот для предмета инвентаря. rslot = GetInvWSlot(inv, 1) If rslot <> wNone Then 'Проверим, занят ли слот. If HasInvItem(rslot) = TRUE Then rslot = wNone Endif End If 'Проверим второй слот. If rslot = wNone Then rslot = GetInvWSlot(inv, 2) If rslot <> wNone Then 'Проверим, занят ли он. If HasInvItem(rslot) = TRUE Then rslot = wNone End If Endif Endif 'Ищем свободный слот. If rslot <> wNone Then 'Проверим, оружие ли это, и сколько рук нужно для использования. If inv.classid = clWeapon Then If inv.weapon.hands = 2 Then If (HasInvItem(wPrimary) = TRUE) Or (HasInvItem(wSecondary) = TRUE) Then msg = "Not enough free hands to equip " & desc & "." rslot = wNone Endif Endif Elseif (inv.classid = clArmor) Or (inv.classid = clShield) Then If CanWear(inv) = FALSE Then msg = "Not enough strength to equip " & desc & "." rslot = wNone End If End If Else msg = "No empty slots to equip " & desc & "." Endif Return rslot End Function
Эта функция возвратит свободный слот, в который можно экипировать предмет, или wNone — если свободных слотов нет. Поскольку теперь у нас при двуручном оружии во второй руку находится «недоступный» предмет, то все что нам нужно сделать, это проверить — заняты ли слоты рук функцией HasInvItem. HasInvItem возвращает False, если в слоте содержится идентификатор класса clNone. Поскольку «недоступный» предмет имеет идентификатор класса clUnavailable, то HasInvItem вернет True, как будто слот занят и, следовательно, недоступен.
Если персонаж, в настоящий момент, держит в руках двуручный меч, то при попытке экипировать, например, одноручный меч, отобразится сообщение о том, что нет свободных слотов, т. к. во вторая рука будет занята «недоступным» предметом. Мы получим то что хотели. Нам не нужно будет делать дополнительных проверок — держит ли персонаж двуручное оружие или нет, достаточно будет проверить слоты при помощи HasInvItem.
Это удобно тем, что не влияет на остальные инвентарные слоты. Функция GetInvWSlot все также вернет wPrimary или wSecondary для оружия, wNeck для ожерелья и т. д. Это работает также как и раньше, поэтому мы упростили наш код избавившись от неприятных исключений.
Введение нового «недоступного» предмета, открывает нам некоторые возможности для дальнейших усовершенствований. Например, если мы захотим ограничить объем инвентаря в соответствии с показателем силы персонажа. Все что нам нужно, это рассчитать, сколько слотов персонажу доступно в соответствии с текущей силой, и заполнить «лишние» слоты инвентаря «недоступными» предметами. По мере увеличения силы персонажа — убирать из инвентаря эти предметы, увеличивая тем самым его объем. Есть ряд других способов использования этих предметов, например для критических ударов или магических эффектов. Любое из этих усовершенствований будет намного легче реализовать, если у нас есть «недоступный» предмет инвентаря.
Этими двумя небольшими изменениями мы добились очень многого. Обновления добавляют косметические усовершенствования, упрощают наш код и открывают пути для новых усовершенствований. Мы добились того, для чего, обычно, и нужно «допиливание» - получили максимум функциональности из уже существующего кода.
Перевод на русский: Fantik
содержание | назад | вперед