вторник, 6 марта 2012 г.

Давайте сделаем рогалик. Глава 16: Оружие

Прежде чем перейти к реализации оружия, давайте взглянем на некоторые изменения в коде. Вместе с последним обновление появилась странная ошибка, в результате которой на карте появлялись фантомные предметы. На карту добавлялся идентификатор предмета и ничего больше. Глядя на код генерации предметов питания я обнаружил, что перечисление, используемое в генераторе случайных чисел, было изменено. Вместо того, чтобы верхняя граница задавалась supBottleOil (бутылка с маслом), почему то была указана броня armPlate. Я не знаю как это произошло, но это и являлось причиной создания фантомных предметов. Я исправил эту проблему и добавил в нескольких местах кода проверки, чтобы сразу знать что происходит нечто странное, если такая же ситуация повторится. (Примечание: бутылка масла была удалена из кода последующих глав, так как у нас не будет темных подземелий. Смотри раздел примечаний, где описаны идеи того, как можно добавить темные подземелья в игру).


inv.bi
Case Else
         inv.desc = "Uknown Supply"
         inv.supply.noise = 0
         inv.icon = Chr(63)
         inv.iconclr = fbYellow
         inv.supply.eval = FALSE
         inv.supply.use = useNone

В подпрограмму генерации предметов я добавил Case Else конструкцию, которая будет сигнализировать нам о том, что тип сгенерированного предмета не определился. Т. е. мы создадим предмет с описанием «Uknown Supply» что поможет нам в более быстром обнаружении места ошибки и, что главное, этот код нисколько не повлияет на производительность нашей программы.

Еще одно изменение я внес в функцию GetFullDesc, которая используется подпрограммой ProcessInspect. Раньше тут был ряд вложенных конструкций Select Case, но вложенный Select необходим только для предметов еды, поэтому можно упростить код.

inv.bi
'Возвращает подробное описание предмета.
  Sub GetFullDesc(lines() As String, inv As invtype)
    Dim As Integer idx = 0
  
    'Очистим массив.
    ReDim lines(0 To idx) As String
    'Убедимся что задан идентификатор типа предмета.
    If inv.classid <> clNone Then
      'Выберем предмет.
      Select Case inv.classid
         Case clSupplies 
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            lines(idx) = GetInvItemDesc(inv)
            Select Case inv.supply.id
               Case supHealingHerb
                  idx += 1
                  ReDim Preserve lines(0 to idx) As String
                  lines(idx) = "* Adds 50% max HP to current HP"
                  idx += 1
                  ReDim Preserve lines(0 to idx) As String
                  lines(idx) = "* Magic: Max healing"
               Case supHunkMeat
                  idx += 1
                  ReDim Preserve lines(0 to idx) As String
                  lines(idx) = "* Adds 25% max HP to current HP"
                  idx += 1
                  ReDim Preserve lines(0 to idx) As String
                  lines(idx) = "* Magic: Bonus to STR stat"
               Case supBread
                  idx += 1
                  ReDim Preserve lines(0 to idx) As String
                  lines(idx) = "* Adds 10% max HP to current HP"
                  idx += 1
                  ReDim Preserve lines(0 to idx) As String
                  lines(idx) = "* Magic: Cure poison"
               Case supBottleOil
                  idx += 1
                  ReDim Preserve lines(0 to idx) As String
                  lines(idx) = "* Fuel for lantern"
                  idx += 1
                  ReDim Preserve lines(0 to idx) As String
                  lines(idx) = "* Magic: See all tiles on map"
            End Select
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            If IsEval(inv) = TRUE Then
               lines(idx) = "* Item is evaluated"
            Else
               lines(idx) = "* Item is not evaluated"
            End If
         Case clArmor
            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.armor.dampct * 100) & "% damage reduction"
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            lines(idx) = "* Strength Required: " & inv.armor.struse
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            lines(idx) = "* Magic: Defense and Healing"
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            If IsEval(inv) = TRUE Then
               lines(idx) = "* Item is evaluated"
            Else
               lines(idx) = "* Item is not evaluated"
            End If
         Case clShield
            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.shield.dampct * 100) & "% damage reduction"
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            lines(idx) = "* Strength Required: " & inv.shield.struse
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            lines(idx) = "* Magic: Defense and Healing"
            idx += 1
            ReDim Preserve lines(0 to idx) As String
            If IsEval(inv) = TRUE Then
               lines(idx) = "* Item is evaluated"
            Else
               lines(idx) = "* Item is not evaluated"
            End If
         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
            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"
            Else
               lines(idx) = "* Item is not evaluated"
            End If
      End Select
    EndIf
  
  End Sub

Обратите внимание, что для доспехов, щитов и оружия нет вложенных Select-ов, так как они, на самом деле, не нужны. В результате код становится проще и его легче модифицировать. Теперь настала очередь оружия.

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

inv.bi
'Идентификаторы оружия.
  Enum weaponids
    wpNone
    wpClub            '1 ручное, dam 4 chr:33
    wpWarclub         '1 ручное, dam 4
    wpCudgel          '1 ручное, dam 5
    wpDagger          '1 ручное, dam 4 chr: 173
    wpLongknife       '1 ручное, dam 6
    wpSmallsword      '1 ручное, dam 4
    wpShortsword      '1 ручное, dam 6
    wpRapier          '1 ручное, dam 9
    wpBroadsword      '1 ручное, dam 12
    wpScimitar        '1 ручное, dam 10
    wpKatana          '2 ручное, dam 12
    wpLongsword       '2 ручное, dam 14
    wpClaymore        '2 ручное, dam 16
    wpGreatsword      '2 ручное, dam 18
    wpOdinsword       '2 ручное, dam 20
    wpHellguard       '2 ручное, dam 30
    wpQuarterstaff    '2 ручное, dam 4
    wpLongstaff       '2 ручное, dam 6
    wpPolearm         '2 ручное, dam 8
    wpLightspear      '2 ручное, dam 7 chr: 179
    wpHeavyspear      '2 ручное, dam 9
    wpTrident         '2 ручное, dam 10
    wpGlaive          '2 ручное, dam 12
    wpHandaxe         '1 ручное, dam 6 chr: 244
    wpBattleaxe       '1 ручное, dam 9
    wpGothicbattleaxe '2 ручное, dam 12
    wpWaraxe          '2 ручное, dam 14
    wpHalberd         '2 ручное, dam 16
    wpPoleaxe         '2 ручное, dam 18
    wpGreataxe        '2 ручное, dam 20
    wpSmallmace       '1 ручное, dam 6 chr: 226
    wpBattlemace      '1 ручное, dam 8
    wpSpikedmace      '1 ручное, dam 10
    wpDoubleballmace  '1 ручное, dam 12
    wpWarhammer       '1 ручное, dam 14
    wpMaul            '2 ручное, dam 16
    wpGreatMaul       '2 ручное, dam 20
    wpHellMaul        '2 ручное, dam 30
    wpBullwhip        '1 ручное, dam 4 chr: 231
    wpBallflail       '1 ручное, dam 6
    wpSpikedflail     '1 ручное, dam 8
    wpMorningstar     '1 ручное, dam 10
    wpBattleflail     '2 ручное, dam 12
    wpBishopsflail    '2 ручное, dam 14
    wpSling           '1 ручное, dam 2 chr: 125
    wpShortbow        '2 ручное, dam 8
    wpLongbow         '2 ручное, dam 10
    wpBonebow         '2 ручное, dam 14
    wpAdaminebow      '2 ручное, dam 20
    wpLightcrossbow   '2 ручное, dam 10 chr: 209
    wpHeavycrossbow   '2 ручное, dam 14
    wpBarrelcrossbow  '2 ручное, dam 18
    wpAdaminecrossbow '2 ручное, dam 25
  End Enum
  
  'Описание типа для оружия.
  Type weapontype
    id As weaponids      'Идентификатор
    evaldr As Integer    'Сложность опознания. Если > 0 то есть магия.
    eval As Integer      'Если предмет опознан.
    effect As integer    'Наложенное заклинание.
    sdesc As String * 30 'Секретное описание для магических предметов. Доступно если опознан.
    noise As Integer     'Кол-во генерируемого шума.
    use As itemuse       'Как используется.
    dam As integer       'Наносимые повреждения.
    hands As Integer     'Сколько рук необходимо для использования.
    wslot(1 To 2) As wieldpos 'Слоты для размещения когда используется.
  End Type

(В оригинальной книге в описании типа weapontype для параметра id был задан тип shieldids, но это, разумеется, ошибка, поэтому здесь я это исправил (примечание переводчика)).

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

Описание типа предмета для оружия, на данном этапе, должно быть вам уже хорошо знакомо. Оно такое же как и для остальных предметов, за исключением полей указывающих на количество повреждений, наносимых данным оружием и необходимое количество рук для его использование. Магическое оружие, как и магическая броня, может содержать какое либо заклинание, поэтому, когда мы дойдем до реализации магии, мы вернемся к коду оружия и доспехов чтобы добавить необходимые изменения. В настоящее время мы просто резервируем место для хранения заклинаний, связанных с оружием.

Я говорил в прошлой главе, что очень просто добавлять новые типы предметов в игру, и это на самом деле так. Все что нужно для добавления оружия, это добавить новые варианты выбора в команды Select — case или If в подпрограммах работы с предметами. Так как вы уже несколько раз видели подобный код, то я оставляю эту задачу для вас. Вам теперь должно быть очевидно, что каждый новый подраздел кода, это продолжение уже написанного ранее, и так все и останется, если мы не изменим основной код работы с инвентарем.

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

inv.bi
'Создаем оружие.
  Sub GenerateWeapon(inv As invtype, currlevel As Integer, wpid As weaponids = wpNone)
    Dim item As weaponids = wpid 
    Dim As Integer isMagic = ItemIsMagic(currlevel) 

    'Создадим тип предмета, если не указан.
    If item = wpNone Then
      item = RandomRange(wpClub, wpAdaminecrossbow)
    EndIf
    'Общее для всех типов оружия.
    inv.weapon.id = item
    inv.iconclr = fbCadmiumYellow
    inv.weapon.use = useWieldWear
    inv.weapon.eval = FALSE
    inv.weapon.wslot(1) = wPrimary
    inv.weapon.wslot(2) = wSecondary
    'Предмет содержит магию.
    If IsMagic = TRUE Then
      inv.weapon.evaldr = RandomRange(currlevel, currlevel * 2)
      inv.weapon.effect = 0
    EndIf
    'Заполним характеристики оружия.
    Select Case item
      Case wpClub            '1 ручное, dam 4 chr:33
         inv.desc = "Club"
         inv.icon = Chr(33)
         inv.weapon.noise = 1
         inv.weapon.dam = 4
         inv.weapon.hands = 1
      Case wpCudgel          '1 ручное, dam 5
         inv.desc = "Cudgel"
         inv.icon = Chr(33)
         inv.weapon.noise = 2
         inv.weapon.dam = 5
         inv.weapon.hands = 1
      Case wpWarclub         '1 ручное, dam 6
         inv.desc = "War Club"
         inv.icon = Chr(33)
         inv.weapon.noise = 3
         inv.weapon.dam = 6
         inv.weapon.hands = 1
      Case wpDagger          '1 ручное, dam 4 chr: 173
         inv.desc = "Dagger"
         inv.icon = Chr(173)
         inv.weapon.noise = 1
         inv.weapon.dam = 4
         inv.weapon.hands = 1
      Case wpLongknife       '1 ручное, dam 5
         inv.desc = "Dagger"
         inv.icon = Chr(173)
         inv.weapon.noise = 2
         inv.weapon.dam = 5
         inv.weapon.hands = 1
      Case wpSmallsword      '1 ручное, dam 6
         inv.desc = "Small Sword"
         inv.icon = Chr(173)
         inv.weapon.noise = 3
         inv.weapon.dam = 6
         inv.weapon.hands = 1
      Case wpShortsword      '1 ручное, dam 8
         inv.desc = "Short Sword"
         inv.icon = Chr(173)
         inv.weapon.noise = 4
         inv.weapon.dam = 8
         inv.weapon.hands = 1
      Case wpRapier          '1 ручное, dam 10
         inv.desc = "Rapier"
         inv.icon = Chr(173)
         inv.weapon.noise = 5
         inv.weapon.dam = 10
         inv.weapon.hands = 1
      Case wpBroadsword      '1 ручное, dam 12
         inv.desc = "Broadsword"
         inv.icon = Chr(173)
         inv.weapon.noise = 6
         inv.weapon.dam = 12
         inv.weapon.hands = 1
      Case wpScimitar        '1 ручное, dam 12
         inv.desc = "Scimitar"
         inv.icon = Chr(173)
         inv.weapon.noise = 7
         inv.weapon.dam = 12
         inv.weapon.hands = 1
      Case wpLongsword       '2 ручное, dam 14
         inv.desc = "Longsword"
         inv.icon = Chr(173)
         inv.weapon.noise = 8
         inv.weapon.dam = 16
         inv.weapon.hands = 2
      Case wpKatana          '2 ручное, dam 16
         inv.desc = "Katana"
         inv.icon = Chr(173)
         inv.weapon.noise = 9
         inv.weapon.dam = 16
         inv.weapon.hands = 2
      Case wpClaymore        '2 ручное, dam 18
         inv.desc = "Claynore"
         inv.icon = Chr(173)
         inv.weapon.noise = 12
         inv.weapon.dam = 18
         inv.weapon.hands = 2
      Case wpGreatsword      '2 ручное, dam 20
         inv.desc = "Greatsword"
         inv.icon = Chr(173)
         inv.weapon.noise = 16
         inv.weapon.dam = 20
         inv.weapon.hands = 2
      Case wpOdinsword       '2 ручное, dam 30
         inv.desc = "Odinsword"
         inv.icon = Chr(173)
         inv.weapon.noise = 20
         inv.weapon.dam = 30
         inv.weapon.hands = 2
      Case wpHellguard       '2 ручное, dam 40
         inv.desc = "Hellguard"
         inv.icon = Chr(173)
         inv.weapon.noise = 30
         inv.weapon.dam = 40
         inv.weapon.hands = 2
      Case wpQuarterstaff    '2 ручное, dam 4 chr: 179
         inv.desc = "Quarterstaff"
         inv.icon = Chr(179)
         inv.weapon.noise = 1
         inv.weapon.dam = 4
         inv.weapon.hands = 2
      Case wpLongstaff       '2 ручное, dam 6
         inv.desc = "Longstaff"
         inv.icon = Chr(179)
         inv.weapon.noise = 2
         inv.weapon.dam = 6
         inv.weapon.hands = 2
      Case wpLightspear      '2 ручное, dam 7 
         inv.desc = "Light Spear"
         inv.icon = Chr(179)
         inv.weapon.noise = 3
         inv.weapon.dam = 7
         inv.weapon.hands = 2
      Case wpPolearm         '2 ручное, dam 8
         inv.desc = "Polearm"
         inv.icon = Chr(179)
         inv.weapon.noise = 4
         inv.weapon.dam = 8
         inv.weapon.hands = 2
      Case wpHeavyspear      '2 ручное, dam 9
         inv.desc = "Heavy Spear"
         inv.icon = Chr(179)
         inv.weapon.noise = 5
         inv.weapon.dam = 9
         inv.weapon.hands = 2
      Case wpTrident         '2 ручное, dam 10
         inv.desc = "Trident"
         inv.icon = Chr(179)
         inv.weapon.noise = 6
         inv.weapon.dam = 10
         inv.weapon.hands = 2
      Case wpGlaive          '2 ручное, dam 12
         inv.desc = "Glaive"
         inv.icon = Chr(179)
         inv.weapon.noise = 7
         inv.weapon.dam = 12
         inv.weapon.hands = 2
      Case wpHandaxe         '1 ручное, dam 6 chr: 244
         inv.desc = "Hand Axe"
         inv.icon = Chr(244)
         inv.weapon.noise = 2
         inv.weapon.dam = 6
         inv.weapon.hands = 1
      Case wpBattleaxe       '1 ручное, dam 9
         inv.desc = "Battle Axe"
         inv.icon = Chr(244)
         inv.weapon.noise = 3
         inv.weapon.dam = 9
         inv.weapon.hands = 1
      Case wpGothicbattleaxe '2 ручное, dam 12
         inv.desc = "Gothic Battle Axe"
         inv.icon = Chr(244)
         inv.weapon.noise = 4
         inv.weapon.dam = 12
         inv.weapon.hands = 2
      Case wpWaraxe          '2 ручное, dam 14
         inv.desc = "War Axe"
         inv.icon = Chr(244)
         inv.weapon.noise = 5
         inv.weapon.dam = 14
         inv.weapon.hands = 2
      Case wpHalberd         '2 ручное, dam 16
         inv.desc = "Halberd"
         inv.icon = Chr(244)
         inv.weapon.noise = 6
         inv.weapon.dam = 16
         inv.weapon.hands = 2
      Case wpPoleaxe         '2 ручное, dam 18
         inv.desc = "Pole Axe"
         inv.icon = Chr(244)
         inv.weapon.noise = 8
         inv.weapon.dam = 18
         inv.weapon.hands = 2
      Case wpGreataxe        '2 ручное, dam 20
         inv.desc = "Great Axe"
         inv.icon = Chr(244)
         inv.weapon.noise = 10
         inv.weapon.dam = 20
         inv.weapon.hands = 2
      Case wpSmallmace       '1 ручное, dam 6 chr: 226
         inv.desc = "Small Mace"
         inv.icon = Chr(226)
         inv.weapon.noise = 1
         inv.weapon.dam = 6
         inv.weapon.hands = 1
      Case wpBattlemace      '1 ручное, dam 8
         inv.desc = "Battle Mace"
         inv.icon = Chr(226)
         inv.weapon.noise = 4
         inv.weapon.dam = 8
         inv.weapon.hands = 1
      Case wpSpikedmace      '1 ручное, dam 10
         inv.desc = "Spiked Mace"
         inv.icon = Chr(226)
         inv.weapon.noise = 6
         inv.weapon.dam = 10
         inv.weapon.hands = 1
      Case wpDoubleballmace  '1 ручное, dam 12
         inv.desc = "Double-Ball Mace"
         inv.icon = Chr(226)
         inv.weapon.noise = 8
         inv.weapon.dam = 12
         inv.weapon.hands = 1
      Case wpWarhammer       '1 ручное, dam 14
         inv.desc = "War Hammer"
         inv.icon = Chr(226)
         inv.weapon.noise = 10
         inv.weapon.dam = 14
         inv.weapon.hands = 1
      Case wpMaul            '2 ручное, dam 16
         inv.desc = "Maul"
         inv.icon = Chr(226)
         inv.weapon.noise = 12
         inv.weapon.dam = 16
         inv.weapon.hands = 2
      Case wpBullwhip        '1 ручное, dam 4 chr: 231
         inv.desc = "Bull Whip"
         inv.icon = Chr(231)
         inv.weapon.noise = 2
         inv.weapon.dam = 4
         inv.weapon.hands = 1
      Case wpBallflail       '1 ручное, dam 6
         inv.desc = "Ball Flail"
         inv.icon = Chr(231)
         inv.weapon.noise = 4
         inv.weapon.dam = 6
         inv.weapon.hands = 1
      Case wpSpikedflail     '1 ручное, dam 8
         inv.desc = "Spiked Flail"
         inv.icon = Chr(231)
         inv.weapon.noise = 6
         inv.weapon.dam = 8
         inv.weapon.hands = 1
      Case wpMorningstar     '1 ручное, dam 10
         inv.desc = "Morning Star"
         inv.icon = Chr(231)
         inv.weapon.noise = 8
         inv.weapon.dam = 10
         inv.weapon.hands = 1
      Case wpBattleflail     '2 ручное, dam 12
         inv.desc = "Battle Flail"
         inv.icon = Chr(231)
         inv.weapon.noise = 10
         inv.weapon.dam = 12
         inv.weapon.hands = 2
      Case wpBishopsflail    '2 ручное, dam 14
         inv.desc = "Bishop's Flail"
         inv.icon = Chr(231)
         inv.weapon.noise = 12
         inv.weapon.dam = 14
         inv.weapon.hands = 2
      Case wpSling           '1 ручное, dam 2 chr: 125
         inv.desc = "Sling"
         inv.icon = Chr(125)
         inv.weapon.noise = 1
         inv.weapon.dam = 2
         inv.weapon.hands = 1
      Case wpShortbow        '2 ручное, dam 8
         inv.desc = "Short Bow"
         inv.icon = Chr(125)
         inv.weapon.noise = 4
         inv.weapon.dam = 8
         inv.weapon.hands = 2
      Case wpLongbow         '2 ручное, dam 10
         inv.desc = "Long Bow"
         inv.icon = Chr(125)
         inv.weapon.noise = 6
         inv.weapon.dam = 10
         inv.weapon.hands = 2
      Case wpBonebow         '2 ручное, dam 14
         inv.desc = "Dragon Bone Bow"
         inv.icon = Chr(125)
         inv.weapon.noise = 10
         inv.weapon.dam = 14
         inv.weapon.hands = 2
      Case wpAdaminebow      '2 ручное, dam 20
         inv.desc = "Adamine Bow"
         inv.icon = Chr(125)
         inv.weapon.noise = 12
         inv.weapon.dam = 20
         inv.weapon.hands = 2
      Case wpLightcrossbow   '2 ручное, dam 10 chr: 209
         inv.desc = "Light Crossbow"
         inv.icon = Chr(209)
         inv.weapon.noise = 4
         inv.weapon.dam = 10
         inv.weapon.hands = 2
      Case wpHeavycrossbow   '2 ручное, dam 14
         inv.desc = "Heavy Crossbow"
         inv.icon = Chr(209)
         inv.weapon.noise = 8
         inv.weapon.dam = 14
         inv.weapon.hands = 2
      Case wpBarrelcrossbow  '2 ручное, dam 18
         inv.desc = "Barrel Crossbow"
         inv.icon = Chr(209)
         inv.weapon.noise = 12
         inv.weapon.dam = 18
         inv.weapon.hands = 2
      Case wpAdaminecrossbow '2 ручное, dam 25
         inv.desc = "Adamine Crossbow"
         inv.icon = Chr(209)
         inv.weapon.noise = 16
         inv.weapon.dam = 25
         inv.weapon.hands = 2
      Case Else
         inv.desc = "Unknown Weapon"
         inv.icon = Chr(63)
         inv.weapon.noise = 0
         inv.weapon.dam = 0
         inv.weapon.hands = 0
   End Select
   'Установим полное описание.
   If IsMagic = TRUE Then
      inv.weapon.sdesc = inv.desc 
   Else
      inv.weapon.sdesc = inv.desc
    EndIf
  End Sub

Как вы можете убедится, процесс такой же как для брони и щитов. Для того, чтобы мы могли создавать этой подпрограммой определенные предметы оружия у нее есть дополнительный параметр weaponids, так же как и в процедуре генерации доспехов. Мы этим воспользуемся, например, чтобы создать нож с которым персонаж начинает игру по умолчанию. Если этот параметр не задан, то используется значение по умолчанию wpNone и генерируется случайное оружие. Если вы сравните процедуры генерации различных типов предметов, то заметите, что их код идентичен. Все предметы имеют схожие свойства, которые задаются данными подпрограммами, в результате вы можете просто скопировать уже написанную процедуру генерации предметов и вставить ее в код для генерации предметов другого типа, внеся в нее лишь небольшие изменения. Однако, здесь нужно быть очень осторожным и не стоит этого вообще делать, если вы досконально не понимаете как данная процедура работает.

Нам осталось только внести изменения в команду инвентаря «Одеть/Взять в руки».

dod.bas
'Команда «Одеть/Взять в руки».
  Function ProcessEquip() As Integer
    Dim As String res, mask, desc, msg
    Dim As Integer i, iret, iitem, idx, ret = FALSE, freecnt
    Dim As invtype inv
    Dim As tWidgets.btnID btn
    Dim As tWidgets.tInputbox ib
    Dim As vec mvec
    Dim slot As Integer
  
    'Убедимся что есть что одеть/взять в руки.
    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) 'Получим индекс в инвентаре.
            'Получим предмет.
            pchar.GetInventoryItem iitem, inv
            'Получим описание предмета.
            desc = GetInvItemDesc(inv)
            iret = FALSE
            idx = 0
            'Проверим слот 1.
            slot = GetInvWSlot(inv, 1)
            If slot <> wNone Then
               'Проверим, свободен ли слот у персонажа.
               If pchar.HasInvItem(slot) = FALSE Then
                  idx = slot
                  iret = TRUE
               EndIf
            End If
            'Если первый занят.
            If iret = FALSE Then
               'Проверим слот 2.
               slot = GetInvWSlot(inv, 2)
               If slot <> wNone Then
                  'Проверим, свободен ли слот у персонажа.
                  If pchar.HasInvItem(slot) = FALSE Then
                     idx = slot
                     iret = TRUE
                  EndIf
               End If
            EndIf
            'Нет свободных слотов.
            If iret = FALSE Then
               msg = "No empty slots to equip " & desc & "."
            Else
               'Если это оружие и необходимо более одной свободной руки.
               If inv.classid = clWeapon then
                  freecnt = 0
                  'Проверим свободные слоты.
                  If pchar.HasInvItem(wPrimary) = FALSE Then
                     freecnt += 1
                  EndIf
                  If pchar.HasInvItem(wSecondary) = FALSE Then
                     freecnt += 1
                  EndIf
                  If inv.weapon.hands > freecnt Then
                     iret = FALSE
                     msg = "Not enough free hands to equip " & desc & "."
                  EndIf
               End If
               'Может ли персонаж воспользоваться щитом или оружием?
               If inv.classid = clArmor Or inv.classid = clShield Then
                  If pchar.CanWear(inv) = false Then
                     iret = FALSE
                     msg = "Not enough strength to equip " & desc & "."
                  End If
               EndIf
            End If
            'Пустой слот найден?
            If iret = TRUE Then
               'Поместим в него предмет.
               pchar.AddInvItem idx, inv
               'Уберем из инвентаря.
               ClearInv inv
               'Обновим состояние слотов.
               pchar.AddInvItem iitem, inv
               ret = TRUE
               desc &= " was equipped."
               ShowMsg "Equip Items", desc, tWidgets.MsgBoxType.gmbOK
            Else
               ShowMsg "Equip Items", msg, tWidgets.MsgBoxType.gmbOK
            End If
         Next
      EndIf
    EndIf
  
    Return ret
  End Function

Большая часть кода этой функции осталась прежней, мы просто сделали некоторую его реорганизацию для более удобной обработки условия использования двуручного оружия. Как и раньше, мы получаем список предметов, которые могут быть одеты или взяты в руки и предоставляем выбор игроку. После того, как выбор сделан, мы проверяем, может ли персонаж использовать этот предмет.

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

Следующее что нам необходимо сделать, это определить, является ли выбранный предмет доспехами, оружием или щитом. Оружие имеет требование на количество свободных рук, а броня и щиты — требование по показателю силы персонажа, так что нам необходимо проверить эти ограничения чтобы убедится, что персонаж может использовать выбранный предмет. Для оружия мы проверяем количество свободных рук у персонажа, для брони и щитов — значение параметра силы, как мы делали это и раньше. Если любая из проверок не удалась, то мы выводим об этом сообщение, чтобы дать понять игроку о не успешности попытки воспользоваться предметом. Если же проверки прошли успешно, то также как и раньше, мы убираем предмет из инвентаря персонажа и помещаем его в слот для используемых предметов.

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

2 комментария:

  1. Такое впечатление, что статьи как будто писались на русском. Отличный перевод. Спасибо.

    ОтветитьУдалить
  2. приятно видеть что это кому то полезно, да еще и нравиться перевод (мне просто книга понравилась и я, в основном, для себя захотел перевести). и это учитывая то, что не учил английский ни в школе. ни в универе. методом тыка пришлось выучить. сейчас вот работаю в компании, где ни одного русскоязычного человека нет. только арабский и английский (и то - не все английский знают), так что приходится на английском шпрехать, иногда вставляя слова на арабском :).

    ОтветитьУдалить