Давайте сделаем рогалик (Зелья)
Зелья являются одними из основных составляющих любой РПГ игры, и было бы непростительно, если бы мы не включили их Подземелье Судьбы. Есть несколько способов, которыми вы можете реализовать зелья, использовать их как дополнительный магический механизм или влиять ими на характеристики персонажа. Перед употреблением, персонажу имеет смысл изучить эффекты, которые оказывает то или иное зелье. В нашем Подземелье Судьбы зелья будут изменять атрибуты персонажа.В нашей ролевой системе есть 2 типа атрибутов, это базовые атрибуты — такие как сила или выносливость, и рассчитываемые боевые факторы, зависящие от них. В результате зелья у нас будут влиять на разные типы характеристик по разному. Для базовых типов параметров персонажа эффект зелья будет постоянным. Если персонаж выпьет «Зелье силы», то его сила увеличится перманентно. Эффект от зелий, влияющих на боевые атрибуты будет длиться временно — определенное количество ходов. Я думаю, что это будет хорошо работать с созданной нами системой характеристик персонажа.
Помните, что у нас может быть только один активный эффект для боевых факторов в один и тот же момент времени. Если на персонажа действует эффект +10 к фактору ближнего боя на 20 ходов и он выпьет зелье «Вооруженный Бой», которое дает +5 к фактору ближнего боя на 10 ходов, то эффект наносимый последним выпитым зельем заменит предыдущий. Это позволит избежать накопления эффектов и предотвратит внесение дисбаланса в игру.
С основными правилами разберемся на месте. Давайте рассмотрим следующий код.
inv.bi
'Эффект от применения зелий. Enum poteffect potEffectNone potStrength potStamina potDexterity potAgility potIntelligence potUCF potACF potPCF potMCF potCDF potMDF potHealing potMana End Enum 'Идентификаторы зелий. Enum potionids potNone potWhite 'str potBlack 'sta potBlue 'dex potGreen 'agl potCyan 'int potRed 'ucf potMagenta 'acf potYellow 'pcf potGray 'mcf potSilver 'cdf potGold 'mdf potOrange 'healing potPink 'mana End Enum 'Типы зелий. Type pottype id As potionids potname As String * 30 'Название зелья. Скрыто до распознания. evaldr As Integer 'Сложность распознания. Используется для определения, машический ли предмет: 0 = не магический. eval As Integer 'Истина, если опознан. noise As Integer 'Количество генерируемого шума. use As itemuse 'Как используется. amt As Integer 'Сила эффекта. cnt As Integer 'Продолжительность эффекта. effect As poteffect 'Тип эффекта. End Type
Здесь у нас 2 перечисления, poteffect — в котором перечислены эффекты, и
potionids, содержащий список самих зелий. Использование цвета в названии зелья
является одним из правил их именования в рогаликах, поэтому мы также будем его
использовать. Тип pottype описывает структуру данных для зелий. Она идентична
структурам данных для других предметов. Поле effect содержит один из
перечисленных выше эффектов, которое мы добавили для удобства применения эффекта
при употреблении зелья.
inv.bi
'Создание зелий. Sub GeneratePotion(inv As invtype, currlevel As Integer, potid As potionids = potNone) Dim item As potionids Dim As Integer effmax = 100 item = potid 'Генерируем предмет, если не задан. If item = potNone Then item = RandomRange(potWhite, potPink) Endif 'Общие для всех зелий параметры. inv.classid = clPotion inv.potion.id = item inv.potion.use = useEatDrink inv.potion.eval = FALSE inv.potion.evaldr = RandomRange(currlevel, currlevel * 2) inv.icon = Chr(147) inv.potion.noise = 10 inv.potion.amt = RandomRange(-1, 10) If inv.potion.amt = 0 Then inv.potion.amt = 1 inv.potion.cnt = 0 'Заданим описание, эффект и иконку. Select Case item Case potWhite 'сила inv.iconclr = fbWhite inv.potion.potname = "Potion of Strength" inv.desc = "White Potion" inv.potion.effect = potStrength Case potBlack 'выностивость inv.iconclr = fbBlack inv.potion.potname = "Potion of Stamina" inv.desc = "Black Potion" inv.potion.effect = potStamina Case potBlue 'ловкость inv.iconclr = fbBlue inv.potion.potname = "Potion of Dexterity" inv.desc = "Blue Potion" inv.potion.effect = potDexterity Case potGreen 'подвижность inv.iconclr = fbGreen inv.potion.potname = "Potion of Agility" inv.desc = "Green Potion" inv.potion.effect = potAgility Case potCyan 'интеллект inv.iconclr = fbCyan inv.potion.potname = "Potion of Intelligence" inv.desc = "Cyan Potion" inv.potion.effect = potIntelligence Case potRed 'безоружный бой inv.iconclr = fbRed inv.potion.potname = "Potion of Unarmed Comabt" inv.desc = "Red Potion" inv.potion.cnt = RandomRange(1, effmax) inv.potion.effect = potUCF Case potMagenta 'ближний бой inv.iconclr = fbMagenta inv.potion.potname = "Potion of Armed Combat" inv.desc = "Magenta Potion" inv.potion.cnt = RandomRange(1, effmax) inv.potion.effect = potACF Case potYellow 'дистанционная атака inv.iconclr = fbYellow inv.potion.potname = "Potion of Projectile Combat" inv.desc = "Yellow Potion" inv.potion.cnt = RandomRange(1, effmax) inv.potion.effect = potPCF Case potGray 'магическая атака inv.iconclr = fbGray inv.potion.potname = "Potion of Magic Comabt" inv.desc = "Gray Potion" inv.potion.cnt = RandomRange(1, effmax) inv.potion.effect = potMCF Case potSilver 'физическая защита inv.iconclr = fbSilver inv.potion.potname = "Potion of Combat Defense" inv.desc = "Silver Potion" inv.potion.cnt = RandomRange(1, effmax) inv.potion.effect = potCDF Case potGold 'магическая защита inv.iconclr = fbGold inv.potion.potname = "Potion of Magic Defense" inv.desc = "Gold Potion" inv.potion.cnt = RandomRange(1, effmax) inv.potion.effect = potMDF Case potOrange 'лечение inv.iconclr = fbOrange inv.potion.potname = "Potion of Healing" inv.desc = "Orange Potion" inv.potion.effect = potHealing Case potPink 'мана inv.iconclr = fbPink inv.potion.potname = "Potion of Mana" inv.desc = "Pink Potion" inv.potion.effect = potMana Case Else inv.iconclr = fbWhite inv.icon = "?" inv.potion.potname = "Unknown Potion" inv.desc = inv.potion.potname inv.potion.effect = potEffectNone End Select End Sub
Здесь все кажется знакомым, так как идентично генерации остальных
предметов, однако есть одна особенность. Это значение поля amt (сила эффекта).
Задаваемый диапазон включает в себя небольшую отрицательную величину и может
принимать значение от -1 (и 0) до 10. Значение -1 кажется небольшим, но может
сыграть большую роль в критический момент игры. Эффекты зелий применяются
независимо от того, опознано оно или нет, так что возможность небольшого
отрицательного эффекта заставит игрока задуматься перед его использованием. Это
добавляет элемент случайности в игру, наряду с некоторым интересом. Позже,
возможно, нам понадобиться вернуться к этому коду и поправить значение силы
эффекта, но пока оставим все как и есть.
Другое поле в описании зелья, вызывающее интерес, это поде desc, содержащее описание предмета. Зелья являются «магическими предметами», поэтому фактические названия зелий, такие как «Зелье маны», не раскрываются до его опознания, поэтому мы будем отображать в инвентаре только цвет зелья, а истинное название игрок увидит только после опознания.
Рассмотрим изменения в подпрограмме 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 'Установим значение. Case clArmor inv.armor.eval = state Case clShield inv.shield.eval = state Case clWeapon inv.weapon.eval = state Case clAmmo inv.ammo.eval = TRUE Case clPotion inv.potion.eval = state 'установим истинное название зелья. If state = TRUE Then inv.desc = inv.potion.potname Endif End Select Endif End Sub
Когда устанавливается состояние True, то мы переходим к «тайному»
описанию зелья, которое содержится в параметре potname.
Так как мы добавили новый тип предметов в игру, то мы должны обойти все подпрограммы работы с инвентарем и внести в них соответствующие изменения для работы с зельями. Мы проделывали данную процедуру несколько раз ранее, поэтому я не буду останавливаться на этом. Процедура идентична тем что мы проделывали до этого для всех новых типов предметов.
Зелья являются расходным материалом, так что нам нужно еще обновить команду «Съесть/Выпить» основной программы. Давайте рассмотрим внесенные изменения.
dod.bas
'Обработка команды «Съесть/Выпить». Function ProcessEatDrink() 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 'Убедимся, что в инвентаре есть необходимые предметы. For i = pchar.LowInv To pchar.HighInv iitem = pchar.HasInvItem(i) If iitem = TRUE Then 'Получим предмет инвентаря. pchar.GetInventoryItem i, inv 'проверим на соответствие iret = MatchUse(inv, useEatDrink) 'Если соответствует. If iret = TRUE Then 'Построим маску. mask &= Chr(i) End If Endif Next If Len(mask) = 0 Then ShowMsg "Eat/Drink", "Nothing to consume.", tWidgets.MsgBoxType.gmbOK Else 'Draws an input box on screen. ib.Title = "Eat/Drink" ib.Prompt = "Select item(s) to eat/drink (" & 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 ret = TRUE 'Переберем все предметы из списка. For i = 1 To Len(res) iitem = Asc(res, i) 'Get index into character inventory. 'Получим предмет инвентаря. pchar.GetInventoryItem iitem, inv desc = pchar.ApplyInvItem(inv) ShowMsg "Eat/Drink", desc, tWidgets.MsgBoxType.gmbOK 'Очистим предмет. ClearInv inv 'Поместим назад в инвентарь. pchar.AddInvItem iitem, inv Next Endif Endif Return ret End Function
Если вы помните, ранее в данное процедуре мы обрабатывали эффект
предмета, теперь же применение эффекта перенесено в новый метод ApplyInvItem
объекта персонажа.
character.bi
'Применить предмет из инвентаря на персонажа. Function character.ApplyInvItem(inv As invtype) As String Dim As Integer evalstate, evaldr, amt, amt2, amt3 Dim As String ret 'опознан ли предмет. evalstate = IsEval(inv) 'проверка для магических предметов. evalDR = GetEvalDR(inv) If inv.classid = clSupplies Then 'Применяем предмет на персонажа. If inv.supply.id = supHealingHerb Then 'Опознанный машический предмет. If (evalstate = TRUE) And (evalDR > 0) Then CurrHP = MaxHP ret = "The Healing Herb completely healed you!" Else 'лечим 50% здоровья . amt = MaxHP * .5 'Рассчитаем значение. CurrHP = CurrHP + amt If CurrHP > MaxHP Then CurrHP = MaxHP Endif ret = "The Healing Herb added " & amt & " health!" Endif Elseif inv.supply.id = supHunkMeat Then 'лечим 25% здоровья. amt = MaxHP * .25 CurrHP = CurrHP + amt If CurrHP > MaxHP Then CurrHP = MaxHP Endif ret = "The Hunk of Meat added " & amt & " health!" 'Опознанный магический предмет. If (evalstate = TRUE) And (evalDR > 0) Then amt2 = RandomRange(1, CurrStr) BonStr = amt2 amt3 = RandomRange(1, 100) BonStrCnt = amt3 ret = "The Hunk of Meat added " & amt & " health and added " & amt2 & " strength for " & amt3 & " turns!" Endif Elseif inv.supply.id = supBread Then 'Лечим 10% здоровья. amt = MaxHP * .1 CurrHP = CurrHP + amt If CurrHP > MaxHP Then CurrHP = MaxHP Endif ret = "The Bread added " & amt & " health!" 'Опознанный магический предмет. If (evalstate = TRUE) And (evalDR > 0) Then 'Вылечим отравление, если необходимо. If Poisoned = TRUE Then Poisoned = FALSE PoisonStr = 0 ret = "The Bread added " & amt & " health and cured your poison!" End If Endif Elseif inv.supply.id = supManaOrb Then 'Добавим 25% маны. amt = MaxMana * .25 CurrMana = CurrMana + amt If CurrMana > MaxMana Then CurrMana = MaxMana Endif ret = "The Mana Orb added " & amt & " mana!" 'Опознанный магический предмет. If (evalstate = TRUE) And (evalDR > 0) Then 'Вылечим отравление, если необходимо. If CurrMana = MaxMana Then ret = "The Mana Orb restored all your mana!" End If Endif Endif Elseif inv.classid = clPotion Then Select Case inv.potion.effect Case potStrength ChangeStrength inv.potion.amt If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " strength!" Else ret = "You lost " & inv.potion.amt & " strength!" Endif Case potStamina ChangeStamina inv.potion.amt If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " stamina!" Else ret = "You lost " & inv.potion.amt & " stamina!" Endif Case potDexterity ChangeDexterity inv.potion.amt If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " dexterity!" Else ret = "You lost " & inv.potion.amt & " dexterity!" Endif Case potAgility ChangeAgility inv.potion.amt If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " agility!" Else ret = "You lost " & inv.potion.amt & " agility!" Endif Case potIntelligence ChangeIntelligence inv.potion.amt If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " intelligence!" Else ret = "You lost " & inv.potion.amt & " intelligence!" Endif Case potUCF BonUcf = inv.potion.amt BonUcfCnt = inv.potion.cnt If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " Unarmed Combat for " & inv.potion.cnt & " turns!" Else ret = "You lost " & inv.potion.amt & " Unarmed Combat for " & inv.potion.cnt & " turns!" Endif Case potACF BonAcf = inv.potion.amt BonAcfCnt = inv.potion.cnt If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " Armed Combat for " & inv.potion.cnt & " turns!" Else ret = "You lost " & inv.potion.amt & " Armed Combat for " & inv.potion.cnt & " turns!" Endif Case potPCF BonPcf = inv.potion.amt BonPcfCnt = inv.potion.cnt If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " Projectile Combat for " & inv.potion.cnt & " turns!" Else ret = "You lost " & inv.potion.amt & " Projectile Combat for " & inv.potion.cnt & " turns!" Endif Case potMCF BonMcf = inv.potion.amt BonMcfCnt = inv.potion.cnt If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " Magic Combat for " & inv.potion.cnt & " turns!" Else ret = "You lost " & inv.potion.amt & " Magic Combat for " & inv.potion.cnt & " turns!" Endif Case potCDF BonCdf = inv.potion.amt BonCdfCnt = inv.potion.cnt If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " Combat Defense for " & inv.potion.cnt & " turns!" Else ret = "You lost " & inv.potion.amt & " Combat Defense for " & inv.potion.cnt & " turns!" Endif Case potMDF BonMdf = inv.potion.amt BonMdfCnt = inv.potion.cnt If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " Magic Defense for " & inv.potion.cnt & " turns!" Else ret = "You lost " & inv.potion.amt & " Magic Defense for " & inv.potion.cnt & " turns!" Endif Case potHealing CurrHP = CurrHP + inv.potion.amt If CurrHP > MaxHP Then CurrHP = MaxHP If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " health!" Else ret = "You lost " & inv.potion.amt & " health!" Endif Case potMana CurrMana = CurrMana + inv.potion.amt If CurrMana > MaxMana Then CurrMana = MaxMana If inv.potion.amt > 0 Then ret = "You gained " & inv.potion.amt & " mana!" Else ret = "You lost " & inv.potion.amt & " mana!" Endif End Select Endif Return ret End Function
Сейчас мы дошли до места в разработке нашей игры, где нам необходимо
добавить много новых эффектов. Поэтому, для упрощения добавления новых эффектов,
их применение лучше всего вынести в отделенную подпрограмму. Мы поместили ее в
объект персонажа, что даст нам доступ к структуре данных объекта и упростит
написание кода результата применения эффекта.
Обратите внимание, что эффекты от зелий действует согласно заданным нами в начале главы правилам. Эффекты, изменяющие базовые атрибуты персонажа действуют постоянно, в то время как эффекты, влияющие на факторы боя, применяются на некоторое время. Вы также можете видеть, что после обновления индексов массива атрибутов мы упростили здесь код и сделали его более читаемым.
Данных модификаций достаточно для добавления зелий в игру, что добавит еще один уровень стратегии и, хотя по большей части, зелья вносят положительные дополнения, есть все же небольшой шанс отрицательного эффекта, что остановит игрока от питья всего что ему попадается под руку.
Перевод на русский: Fantik
содержание | назад | вперед