Зелья являются одними из основных составляющих любой РПГ игры, и было бы непростительно, если бы мы не включили их Подземелье Судьбы. Есть несколько способов, которыми вы можете реализовать зелья, использовать их как дополнительный магический механизм или влиять ими на характеристики персонажа. Перед употреблением, персонажу имеет смысл изучить эффекты, которые оказывает то или иное зелье. В нашем Подземелье Судьбы зелья будут изменять атрибуты персонажа.В нашей ролевой системе есть 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Сейчас мы дошли до места в разработке нашей игры, где нам необходимо добавить много новых эффектов. Поэтому, для упрощения добавления новых эффектов, их применение лучше всего вынести в отделенную подпрограмму. Мы поместили ее в объект персонажа, что даст нам доступ к структуре данных объекта и упростит написание кода результата применения эффекта.
Обратите внимание, что эффекты от зелий действует согласно заданным нами в начале главы правилам. Эффекты, изменяющие базовые атрибуты персонажа действуют постоянно, в то время как эффекты, влияющие на факторы боя, применяются на некоторое время. Вы также можете видеть, что после обновления индексов массива атрибутов мы упростили здесь код и сделали его более читаемым.
Данных модификаций достаточно для добавления зелий в игру, что добавит еще один уровень стратегии и, хотя по большей части, зелья вносят положительные дополнения, есть все же небольшой шанс отрицательного эффекта, что остановит игрока от питья всего что ему попадается под руку.

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