воскресенье, 22 апреля 2012 г.

Давайте сделаем рогалик. Глава 24: Волшебные жезлы

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

Во первых давайте определим некоторые правила для жезлов. У нас будут различные типы жезлов, основанные на материале из которого они изготовлены, такие как железо или, например, золото. Редкость металла, из которого изготовлен жезл, будет отвечать за его силу. Железные жезлы будут самыми слабыми, а золотые — самыми сильными. Жезлы будет иметь определенное количество зарядов и чтобы их сбалансировать, мы дадим более слабым жезлам больше зарядов, а более сильным — меньше. Когда заряды жезла закончатся, то его нельзя будет «перезарядить» как оружие. Когда мы доберемся до реализации магии, то мы добавим заклинания для подзарядки жезлов, но сейчас они у нас будут одноразовыми. Хотя механизм использования жезлов такой же как и для дистанционного орудия, мы будем классифицировать их как магические предметы и при их применении будем использовать факторы магической атаки и магической зашиты, а не факторы для обычных и дистанционных атак.

Первое что нужно сделать, это добавить несколько жезлов в наш список оружия.

inv.bi
'идентификаторы оружия.
 Enum weaponids
 ...
   wpAdaminecrossbow '2 ручное, повр. 25
   wpIronWand        '1 ручное, повр. 10
   wpBrassWand       '1 ручное, повр. 20
   wpCopperWand      '1 ручное, повр. 40
   wpSilverWand      '1 ручное, повр. 80
   wpGoldWand        '1 ручное, повр. 100
 End Enum

Этим мы добавили в игру несколько магических жезлов. Как вы можете видеть по комментариями, они достаточно мощные, поэтому нам нужно ограничить их использование в игре.

Следующее что необходимо сделать, это добавить жезлы в подпрограмму генерации оружия — GenerateWeapon.

inv.bi
Case wpIronWand '1 ручное, повр. 10
         inv.desc = "Iron Wand"
         inv.icon = Chr(139)
         inv.weapon.noise = 1
         inv.weapon.dam = 10
         inv.weapon.hands = 1
         inv.weapon.weapontype = wtProjectile
         inv.weapon.capacity = RandomRange(1, 100)
         inv.weapon.ammotype =  amNone
         inv.weapon.ammocnt = inv.weapon.capacity
         inv.weapon.iswand = TRUE
      Case wpBrassWand '1 hand, dam 20
         inv.desc = "Brass Wand" 
         inv.icon = Chr(139)
         inv.weapon.noise = 1
         inv.weapon.dam = 20
         inv.weapon.hands = 1
         inv.weapon.weapontype = wtProjectile
         inv.weapon.capacity = RandomRange(1, 80)
         inv.weapon.ammotype =  amNone
         inv.weapon.ammocnt = inv.weapon.capacity 
         inv.weapon.iswand = TRUE
      Case wpCopperWand '1 ручное, повр. 40
         inv.desc = "Copper Wand"
         inv.icon = Chr(139)
         inv.weapon.noise = 1
         inv.weapon.dam = 40
         inv.weapon.hands = 1
         inv.weapon.weapontype = wtProjectile
         inv.weapon.capacity = RandomRange(1, 40)
         inv.weapon.ammotype =  amNone
         inv.weapon.ammocnt = inv.weapon.capacity 
         inv.weapon.iswand = TRUE
      Case wpSilverWand '1 ручное, повр. 80
         inv.desc = "Silver Wand"
         inv.icon = Chr(139)
         inv.weapon.noise = 1
         inv.weapon.dam = 80
         inv.weapon.hands = 1
         inv.weapon.weapontype = wtProjectile
         inv.weapon.capacity = RandomRange(1, 20)
         inv.weapon.ammotype =  amNone
         inv.weapon.ammocnt = inv.weapon.capacity 
         inv.weapon.iswand = TRUE
      Case wpGoldWand '1 ручное, повр. 100
         inv.desc = "Gold Wand"
         inv.icon = Chr(139)
         inv.weapon.noise = 1
         inv.weapon.dam = 100
         inv.weapon.hands = 1
         inv.weapon.weapontype = wtProjectile
         inv.weapon.capacity = RandomRange(1, 10)
         inv.weapon.ammotype = amNone
         inv.weapon.ammocnt = inv.weapon.capacity 
         inv.weapon.iswand = TRUE

Обратите внимание, что в определение типа оружия мы добавили новое поле: iswand. Мы будем использовать его для расчета факторов атаки и зашиты. Для большинства видов оружия оно будет равно false. Мы устанавливаем его в true для жезлов во время их создания.

Ключевым полем для жезлов является емкость (capacity). Для железных жезлов оно принимает значение от 1 до 100, для золотых — от 1 до 10. Как только мы получили емкость жезла, мы используем это значение чтобы зарядить жезл необходимым количеством зарядов, заполняя значение поля ammocnt. Жезлы не могут быть перезаряжены, поэтому тип боеприпасов для них мы устанавливаем в amNone.

Этого достаточно, чтобы получить жезлы как новый тип предмета инвентаря, но для их использования необходимо добавить некоторые изменения в процедуру пользования дистанционного оружия.

dod.bas:DoProjectileCombat
...
   'Получим фактор зашиты монстра.
   If pchar.ProjectileIsWand(wslot) = TRUE Then
      df = level.GetMonsterMagicDefense(mx, my)
   Else
      df = level.GetMonsterDefense(mx, my)
   EndIf
 
...
 
      'Возвращает фактор атаки зависящий от используемого оружия.
      If pchar.ProjectileIsWand(wslot) = TRUE Then
         cf = pchar.GetMagicCombatFactor()
      Else
         cf = pchar.GetProjectileCombatFactor()
      EndIf
 ...

Функция ProjectileIsWand просто возвращает значение из поля iswand объекта оружия. Если это так, то мы получаем значения магической защиты монстра и магической атаки персонажа используя функции GetMonsterMagicDefense и GetMagicCombatFactor. Верьте или нет, но это единственные изменения, которые необходимо в нести для модификации удаленных атак.

Давайте взглянем на две новые функции:

level.bi
'Возвращает магическую защиту монстра.
 Function levelobj.GetMonsterMagicDefense(mx As Integer, my As Integer) As Integer
   Dim As Integer ret = 0, midx = 0
   
   'Убедимся что монстр здесь.
   If _level.lmap(mx, my).monidx > 0 Then
      midx = _level.lmap(mx, my).monidx
      ret = _level.moninfo(midx).md
   EndIf
   
   Return ret
 End Function

Мы просто проверяем, находится ли монстр по указанным координатам и возвращаем значение поля md описания монстра, которое содержит его фактор магической защиты.

Получить фактор магической атаки персонажа также просто:

character.bi
'Возвращает фактор магической атаки.
 Function character.GetMagicCombatFactor() As Integer
   Return CurrMCF + BonMcf
 End Function

Просто возвращаем значение фактора магической атаки вместе с бонусом.

Все что нам осталось сделать, это обновить часть подпрограммы описания объектов, добавив в нее обработку новых полей.

inv.bi
...
         Case clWeapon
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            lines(idx) = GetInvItemDesc(inv)
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            lines(idx) = "* " & inv.weapon.dam & " weapon damage"
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            lines(idx) = "* Hands Required: " & inv.weapon.hands
            If inv.weapon.weapontype = wtProjectile Then
               idx += 1
               ReDim Preserve lines(0 to idx) As String
               lines(idx) = "* Capacity: " & inv.weapon.capacity
               idx += 1
               ReDim Preserve lines(0 to idx) As String
               lines(idx) = "* Ammo Cnt: " & inv.weapon.ammocnt
            EndIf
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            lines(idx) = "* Magic: Offense and Defense"
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            If IsEval(inv) = TRUE Then
               lines(idx) = "* Item is evaluated"
               idx += 1
               ReDim Preserve lines(0 to idx) As String
               lines(idx) = "* Spell: " & GetSpellDescriptions(inv.weapon.spell)
            Else
               lines(idx) = "* Item is not evaluated"
            End If
 ...

Мы добавили секцию IF, чтобы отображать количество зарядов в оружии, если необходимо. Это будет работать не только с жезлами, но и с любым другим дистанционным оружием.

Это все что необходимо для добавления магических жезлов в игру. Как вы можете видеть, мы использовали код реализации дистанционного оружия для добавления оружия нового типа, хотя оно и является достаточно экзотическим. Ключевым моментом является использование поля емкости обоймы (capacity) метательного оружия. Без него мы не могли бы добавить жезлы так же легко. Но наличие этого поля и кода, связанного с ним, позволило нам очень малыми силами добавить даже такое экзотическое оружие как магические жезлы.

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

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