Следующим нашим шагом будет добавление в игру предметов брони и щитов. Также, чтобы они у нас работали, нам необходимо реализовать команду инвентаря «Взять в руки/Одеть». Тут мы сталкиваемся с небольшой проблемой, так как нам нужно уметь не только одевать предметы, но и снимать их. Так как мы имеем дело с двумя действиями, то имеет смысл реализовать для них две команды: «Взять в руки/Одеть» и «Снять». Этим мы и займемся в этой главе.
Добавление новых предметов инвентаря — самая простая часть, так как у нас уже есть все методы работы с инвентарем и нам необходимо только добавить определение новых типов предметов и у нас будут щиты и броня. Тем не менее нам придется обновить объект персонажа для их хранения, а также существующие функции работы с инвентарем — для обработки новых типов предметов. Так что в у нас будет достаточно работы, но когда мы закончим, то добавление в игру новых предметов будет простой задачей.
Давайте посмотрим на определение брони и щитов:
inv.bi'Идентификаторы доспехов
Enum armorids
armArmorNone
armCloth 'Плотная ткань. Cloth armor: поглощает 1% повреждений
armLeather 'Кожаная. Leather armor: поглощает 5% повреждений
armCuirboli 'Прошитая кожаная. Cuirboli armor: поглощает 10 % повреждений
armRing 'Кожа с металлическими кольцами. Ring armor: поглощает 20% повреждений
armBrigantine 'Полная кожаная? Brigantine armor: поглощает 30% повреждений
armChain 'Кольчуга. Chain armor: поглощает 50 % повреждений
armScale 'Чешуйчатая броня. Scale armor: поглощает 70% повреждений
armPlate 'Пластинчатая броня. Plate armor: поглощает 90% повреждений
End Enum
'Идентификаторы щитов
Enum shieldids
shldShieldNone
shldLeather 'Значения для щитов такие же как и для доспехов.
shldCuirboli
shldRing
shldBrigantine
shldChain
shldScale
shldPlate
End EnumЭто типы доспехов и щитов, которые будут у нас в игре. Как вы можете видеть из комментариев, броня в подземелье судьбы ведет себя несколько иначе, чем в других ролевых системах. Броня не дает способность персонажу перенести удар полностью без последствий, а просто поглощает часть урона. Так или иначе, повреждения, наносимые персонажу при ударе, зависят от различных факторов защиты. Если персонажа ударили, то броня уменьшает повреждения на определенный процент. Броня из ткани уменьшит его на 1%, а пластинчатые доспехи на 90%. Однако, чем больше повреждений поглощает броня, тем больше у нее требования к параметру силы персонажа для возможности ее использования. Также требование по параметру силы персонажа зависит от генератора случайных чисел, так что удача игрока здесь также немаловажный параметр.
inv.bi'Слоты инвентаря на персонаже (предметы в руках и т.д.).
Enum wieldpos
wNone
wPrimary
wSecondary
wArmor
wNeck
wRingRt
wRingLt
End Enum
'Описание типа для брони
Type armortype
id As armorids 'Тип брони
evaldr As Integer 'Сложность опознания. Если больше > 0 то магический предмет.
eval As Integer 'Если предмет опознан.
effect As Integer 'Магический эффект (заклинание).
sdesc As String * 30 'Секретное название для магических предметов. Отображается если он опознан.
noise As Integer 'Производимый шум при использовании или если лежит в инвентаре.
use As itemuse 'Как предмет используется.
dampct As Single 'Процент поглощаемых повреждений.
struse As Integer 'Сколько необходимо значение силы для использования.
wslot(1 To 2) As wieldpos 'Слоты, занимаемые предметом. Может занимать одновременно до двух слотов.
End Type
'Описание типа для щитов
Type shieldtype
id As shieldids 'Тип щита
evaldr As Integer 'Сложность опознания. Ксли больше > 0 то магический.
eval As Integer 'Если предмет опознан.
effect As integer 'Магический эффект.
sdesc As String * 30 'Секретное название для магических предметов. Отображается если он опознан.
noise As Integer 'Сколько шума производит при использовании и при нахождении в инвентаре.
use As itemuse 'Как используется.
dampct As Single 'Процент поглощаемых повреждений.
struse As Integer 'Сила, необходимая для использования
wslot(1 To 2) As wieldpos 'Слоты, занимаемые предметом. Может занимать одновременно до двух слотов.
End TypeОписание типов для брони и щитов содержит довольно много информации. Некоторые поля вы видели раньше, такие как идентификатор (id), опознание (eval), генерируемый предметом шум (noise) и использование (use). Новое поле dampct указывает на количество поглощаемых повреждений, struse — минимальное значение параметры силы персонажа для возможности использования данного предмета и массив, указывающий на занимаемые предметом слоты когда персонаж его использует. Вы у видите как это работает когда мы дойдем до реализации команд «Взять в руки/Одеть» и «Снять».
В Подземелье Судьбы магические броня и оружие содержат заклинания, которые на них могут быть наложены, а не просто являются магическими сами по себе, как это было у нас с едой. Поскольку мы еще не дошли до реализации магии, то пока просто зарезервируем в описании наших предметов место для заклинаний, которые они могу содержать и которое мы сможем заполнить позже, с развитием нашего проекта.
Для генерации новых типов предметов, нам придется обновить каждую из существующих подпрограмм в inv.bi. Процесс очень прост, для брони и щитов мы добавим новый раздел в каждую процедуру. Например, подпрограмма ClearInv обновляется путем добавления нового условия в секцию Select — Case.
inv.bi:ClearInvCase clArmor
inv.armor.id = armArmorNone
inv.armor.evaldr = 0
inv.armor.eval = FALSE
inv.armor.effect = 0
inv.armor.sdesc = ""
inv.armor.noise = 0
inv.armor.use = UseNone
inv.armor.dampct = 0
inv.armor.struse = 0
inv.armor.wslot(1) = wNone
inv.armor.wslot(2) = wNone
Case clShield
inv.shield.id = shldShieldNone
inv.shield.evaldr = 0
inv.shield.eval = FALSE
inv.shield.effect = 0
inv.shield.sdesc = ""
inv.shield.noise = 0
inv.shield.use = UseNone
inv.shield.dampct = 0
inv.shield.struse = 0
inv.shield.wslot(1) = wNone
inv.shield.wslot(2) = wNoneДругие подпрограммы обновляются подобным образом. Для создания доспехов мы вызываем подпрограмму GenerateArmor.
inv.bi'Создает предмет брони.
Sub GenerateArmor(inv As invtype, currlevel As Integer, arid As armorids = armArmorNone)
Dim item As armorids
Dim As Integer isMagic = ItemIsMagic(currlevel)
'Общее для всех предметов.
If arid = armArmorNone Then
item = RandomRange(armCloth, armPlate)
inv.armor.id = item
Else
item = arid
inv.armor.id = item
End If
inv.icon = Chr(234)
inv.iconclr = fbEmeraldGreen
inv.armor.use = useWieldWear
inv.armor.eval = FALSE
inv.armor.wslot(1) = wArmor
'Магический предмет.
If IsMagic = TRUE Then
inv.armor.evaldr = RandomRange(currlevel, currlevel * 2)
inv.armor.effect = 0
EndIf
'Зададим тип брони и параметры.
Select Case item
Case armCloth
inv.desc = "Cloth Armor" 'Cloth armor: 1% damage reduction.
inv.armor.noise = 1
inv.armor.dampct = .01
inv.armor.struse = 10
Case armLeather 'Leather armor: 5% damage reduction
inv.desc = "Leather Armor"
inv.armor.noise = 5
inv.armor.dampct = .05
inv.armor.struse = 50
Case armCuirboli 'Cuirboli armor: 10 % damage reduction
inv.desc = "Cuirboli Armor"
inv.armor.noise = 10
inv.armor.dampct = .10
inv.armor.struse = 100
Case armRing 'Ring armor: 20% damage reduction
inv.desc = "Ring Armor"
inv.armor.noise = 15
inv.armor.dampct = .20
inv.armor.struse = 150
Case armBrigantine 'Brigantine armor: 30% dam reduction
inv.desc = "Brigantine Armor"
inv.armor.noise = 20
inv.armor.dampct = .30
inv.armor.struse = 200
Case armChain 'Chain armor: 50 % dam reduction
inv.desc = "Chain Armor"
inv.armor.noise = 25
inv.armor.dampct = .50
inv.armor.struse = 250
Case armScale 'Scale armor: 70% dam reduction
inv.desc = "Scale Armor"
inv.armor.noise = 30
inv.armor.dampct = .70
inv.armor.struse = 300
Case armPlate 'Plate armor: 90% dam reduction
inv.desc = "Plate Armor"
inv.armor.noise = 35
inv.armor.dampct = .90
inv.armor.struse = 350
End Select
'Зададим секретное описание.
If IsMagic = TRUE Then
inv.armor.sdesc = inv.desc
Else
inv.armor.sdesc = inv.desc
EndIf
End SubДанная процедура почти идентична процедуре генерации предметов еды, но генерирует параметры для доспехов. В нее же мы могли добавить и генерацию щитов. Так как щиты у нас это просто еще один вид брони, но для наглядности я создал для брони и щитов две разных функции чтобы лучше показать ход выполнения программы. Посмотрев на код в inv.bi вы обнаружите, что точно такой же алгоритм используется во всех подпрограммах работы с инвентарем. По аналогии с тем, как мы добавили броню, мы будем добавлять в игру все новые предметы. Которые бы нам ни потребовались.
После того, как броня и щиты созданы и размещены на карте, мы должны дать игроку возможность их использовать, что подводит нас к реализации команд инвентаря «Одеть» и «Снять».
В подпрограмме ManageInventory файла dod.bas для данных команд мы добавили новую возможность выбора.
dod.bas'«Одеть/Взыть в руки» предмет.
If kch = "U" Then
ret = ProcessUnequip()
'Screen changed.
If ret = TRUE Then
DrawInventoryScreen
EndIf
EndIf
'«Снять».
If kch = "Q" Then
ret = ProcessEquip()
'Screen changed.
If ret = TRUE Then
DrawInventoryScreen
EndIf
EndIfСодержание этих двух новых подпрограмм аналогично уже существующим подпрограммам инвентаря, поэтому их код должен быть вам знаком.
dod.bas'Реализация команды «Одеть/Взять в руки».
Function ProcessEquip() As Integer
Dim As String res, mask, desc
Dim As Integer i, iret, iitem, idx, ret = FALSE
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) 'Get index into wield inventory.
'Получим предмет инвентаря.
pchar.GetInventoryItem iitem, inv
'Получим описание предмета.
desc = GetInvItemDesc(inv)
iret = FALSE
idx = 0
'Проверим первый слот.
slot = GetInvWSlot(inv, 1)
If slot <> wNone Then
'Проверим, что данный слот персонажа не занят.
If pchar.HasInvItem(slot) = FALSE Then
idx = slot
iret = TRUE
EndIf
Else
'Проверим второй слот.
slot = GetInvWSlot(inv, 2)
If slot <> wNone Then
'Проверим, что данный слот персонажа не занят.
If pchar.HasInvItem(slot) = FALSE Then
idx = slot
iret = TRUE
EndIf
End If
EndIf
'Есть свободное место?
If iret = TRUE Then
'Убедимся что персонаж может использовать предмет.
If pchar.CanWear(inv) = 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", "Not enough strength to equip " & desc & ".",tWidgets.MsgBoxType.gmbOK
End if
Else
'Нет свободного места.
desc = "No empty slots to equip item."
ShowMsg "Equip Items", desc, tWidgets.MsgBoxType.gmbOK
Exit For
EndIf
Next
EndIf
EndIf
Return ret
End FunctionТак же, как мы делали в подпрограмме опознания предметов, мы проверяем предметы из инвентаря — какие из них могут быть одеты или взяты в руки персонажем. Как только список таких предметов сформирован, мы предоставляем го пользователю, чтобы он смог сделать выбор. После того, как игрок выбрал предмет, мы должны проверить в какой слот этот предмет может быть размещен и не занят ли он уже. Каждый из «одеваемых» предметов может занимать определенное место в слотах персонажа, которое описано перечислением, определенным в inv.bi. Мы получаем этот слот вызывая функцию GetInvWSlot.
inv.bi'Возвращает слоты инвентаря для одеваемых предметов.
Function GetInvWSlot(inv As invtype, slotnum As Integer) As Integer
Dim As Integer ret = wNone
If inv.classid = clArmor Then
If slotnum >= LBound(inv.armor.wslot) And slotnum <= UBound(inv.armor.wslot) Then
ret = inv.armor.wslot(slotnum)
End If
ElseIf inv.classid = clShield Then
If slotnum >= LBound(inv.shield.wslot) And slotnum <= UBound(inv.shield.wslot) Then
ret = inv.shield.wslot(slotnum)
End If
EndIf
Return ret
End Function
Параметр slotnum является индексом массива wslot в описании типов для доспехов и щитов. У нас каждый предмет может находится максимум в каких либо 2-х разных слотах, например оружие — может находится в правой и в левой руке (первичный и вторичный слот). Для того, чтобы получить слот в котором должен быть размещен предмет мы передаем индекс в эту функцию. Доспехи у нас могут располагаться только в слоте для доспехов, поэтому у них в массиве wslot по индексу 1 будет содержаться wArmor, по индексу 2 — wNone или 0. Функция возвращает wNone — если слот не задан в массиве wslot.
Что означают цифры указывающие на слот? Это индекс в массиве слотов содержащемся в объекте персонажа.
character.bicwield(wPrimary To wRingLt) As invtype 'Активный предмет: 1 = главное оружие, 2 = второе оружие/щит, 3 = броня, 4 = ожерелье, 5 = кольцо пр., 6 = кольцо лр.
Также они используются для меню выбора одеваемого/снимаемого предмета инвентаря, как это показано на изображении в начале главы. Связав меню выбора предмета с реальным размером массива мы упрощаем выбор слота одеваемого или снимаемого предмета. Мы можем наблюдать это в коде функции ProcessEquip который «одевает» предмет.
dod.bas'проверим слот 1.
slot = GetInvWSlot(inv, 1)
If slot <> wNone Then
'Проверим персонажа чтобы убедится что слот свободен.
If pchar.HasInvItem(slot) = FALSE Then
idx = slot
iret = TRUE
EndIf
Else
'Проверим слот 2.
slot = GetInvWSlot(inv, 2)
If slot <> wNone Then
'Проверим персонажа чтобы убедится что слот свободен .
If pchar.HasInvItem(slot) = FALSE Then
idx = slot
iret = TRUE
EndIf
End If
EndIfОбратите внимание, что мы используем индекс, получаемый из GetInvWSlot чтобы использовать его в функции персонажа HasInvItem, которую мы обновили для проверки: занят или свободен конкретный слот для «одеваемых» предметов.
character.bi'Возвращает True если предмет находится в инвентаре.
Function character.HasInvItem(idx As Integer) As Integer
'Проверим одетые предметы.
If idx >= LBound(_cinfo.cwield) And idx <= UBound(_cinfo.cwield) Then
'Проверим идентификатор класса предмета.
If _cinfo.cwield(idx).classid = clNone Then
Return FALSE
Else
Return TRUE
EndIf
Else
'Проверка индекса.
If idx >= LBound(_cinfo.cinv) And idx <= UBound(_cinfo.cinv) Then
'Проверка идентификатора класса.
If _cinfo.cinv(idx).classid = clNone Then
Return FALSE
Else
Return TRUE
EndIf
Else
Return FALSE
End If
EndIf
End Function
Первый оператор If проверяет, попадает ли индекс в диапазон массива одеваемых на персонажа предметов. Поскольку id занимаемого места у нас соответствует индексу массива слотов, то далее мы просто проверяем массив по этому индексу чтобы узнать — занят слот или свободен. Это соответствие индексов расположения и индексов массива слотов позволяет упростить код и сделать его более понятным и простым в отладке и обслуживании.
Если слот пуст, то мы просто помещаем в него предмет и удаляем его из массива инвентаря персонажа.
dod.bas:ProcessEquip'Пустой слот найден?
If iret = TRUE Then
'Убедимся, может ли персонаж пользоваться предметом.
If pchar.CanWear(inv) = 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", "Not enough strength to equip " & desc & ".",tWidgets.MsgBoxType.gmbOK
End if
Else
'Нет свободного слота.
desc = "No empty slots to equip item."
ShowMsg "Equip Items", desc, tWidgets.MsgBoxType.gmbOK
Exit For
EndIfПеред тем как одеть предмет, необходимо проверить — достаточно ли у персонажа показатель силы для использования данного щита или доспехов. Для данной проверки мы добавили новую функцию в объект персонажа: CanWear.
character.bi'Возвращает True если персонаж может использовать предмет
Function character.CanWear(inv As invtype) As Integer
Dim As Integer ret = TRUE
If inv.classid = clArmor Then
If inv.armor.struse > _cinfo.stratt(0) Then
ret = FALSE
EndIf
EndIf
If inv.classid = clShield Then
If inv.shield.struse > _cinfo.stratt(0) Then
ret = FALSE
EndIf
EndIf
Return ret
End FunctionЗдесь у брони и щитов проверяется поле struse, для того чтобы определить — достаточно ли сил у персонажа. Обратите внимание, что возвращаемое по умолчанию значение — True. Только предметы определенного типа могут изменить значение на False (в нашем случае броня и щиты), а для всех остальных мы будем возвращать True.
Для команды «снять» мы делаем противоположные действия.
dod.bas'Обработаем команду «снять».
Function ProcessUnEquip() 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
Dim As vec mvec
'Проверим предметы которые можно снять.
For i = wPrimary To wRingLt
iitem = pchar.HasInvItem(i)
If iitem = TRUE Then
'Построим маску.
mask &= Str(i)
EndIf
Next
If Len(mask) = 0 Then
ShowMsg "Unequip Items", "Nothing to unequip.", tWidgets.MsgBoxType.gmbOK
Else
'Нарисуем поле для ввода.
ib.Title = "Unequip Items"
ib.Prompt = "Select item(s) to unequip (" & 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 = Val(Mid(res, i, 1)) 'Получим индекс слота.
'Получим предмет инвентаря.
pchar.GetInventoryItem iitem, inv
'Получим описание.
desc = GetInvItemDesc(inv)
'Ищем пустой слот инвентаря.
iret = pchar.GetFreeInventoryIndex
'Нашелся пустой слот?
If iret > -1 Then
'Поместим предмет в инвентарь.
pchar.AddInvItem iret, inv
'Очистим предмет.
ClearInv inv
'Обновим одеваемые слоты.
pchar.AddInvItem iitem, inv
ret = TRUE
desc &= " was unequipped."
ShowMsg "Unequip Items", desc, tWidgets.MsgBoxType.gmbOK
Else
'Нет места в инвентаре.
desc = "No empty inventory slots to unequip item."
ShowMsg "Unequip Items", desc, tWidgets.MsgBoxType.gmbOK
Exit For
EndIf
Next
EndIf
EndIf
Return ret
End FunctionВместо того, чтобы перебирать предметы из инвентаря персонажыа, мы перебираем массив «одеваемых» слотов: For i = wPrimary To wRingLt. Вызывая HasInvItem, мы проверяем — занят ли слот, и если занят, то добавляем предмет в список. Другое отличие здесь в том, что вместо использования ASCII колов мы используем в маске фактические номера слотов: mask &= Str(i).
После того как сок список создан и игрок сделал свой выбор, мы обрабатываем его почти также, как и список выбрасываемых предметов. Опять же, разница в том, что мы должны обрабатывать номер индекса массива а не ASCII код: iitem = Val(Mid(res, i, 1)). За исключением этих отличий, алгоритм почти такой же, как и в команде «выбросить», мы перемещаем предмет из слота одетых предметов в слот инвентаря персонажа. Используя функцию GetFreeInventoryIndex мы получаем индекс свободного слота в инвентаре персонажа, так же, как мы это делали в команде «Поднять». Свободный слот нам нужен для того. Чтобы положить туда снятый с персонажа предмет.
Теперь нам необходимо отобразить на экране одетые на персонажа предметы. Мы обновили код в DrawMainScreen чтобы любые одетые предметы отображались на информационной панели.
dod.bas'Проверим одетые предметы.
If pchar.HasInvItem(wPrimary) = TRUE Then
pchar.GetInventoryItem wPrimary, inv
idesc = GetInvItemDesc(inv)
Else
idesc = ""
EndIf
PutText "Primary: " & idesc, row, col
row += 1
If pchar.HasInvItem(wSecondary) = TRUE Then
pchar.GetInventoryItem wSecondary, inv
idesc = GetInvItemDesc(inv)
Else
idesc = ""
EndIf
PutText "Secondary: " & idesc, row, col
row += 1
If pchar.HasInvItem(wArmor) = TRUE Then
pchar.GetInventoryItem wArmor, inv
idesc = GetInvItemDesc(inv)
Else
idesc = ""
EndIf
PutText "Armor: " & idesc, row, col
row += 1
If pchar.HasInvItem(wNeck) = TRUE Then
pchar.GetInventoryItem wNeck, inv
idesc = GetInvItemDesc(inv)
Else
idesc = ""
EndIf
PutText "Neck: " & idesc, row, col
row += 1
If pchar.HasInvItem(wRingRt) = TRUE Then
pchar.GetInventoryItem wRingRt, inv
idesc = GetInvItemDesc(inv)
Else
idesc = ""
EndIf
PutText "Ring RT: " & idesc, row, col
row += 1
If pchar.HasInvItem(wRingLt) = TRUE Then
pchar.GetInventoryItem wRingLt, inv
idesc = GetInvItemDesc(inv)
Else
idesc = ""
EndIf
PutText "Ring LT: " & idesc, row, colЗдесь мы просто проверяем все слоты для одетых предметов и если какой либо слот не пуст, то получаем название предмета который в нем находится и выводим его на экран.
Последнее что нам осталось сделать, это чтобы персонаж начинал игру с какой нибудь броней. Для этого необходимо добавить код в подпрограмму GenerateCharacter.
character.bi'Добавим персонаджу броню из обычной ткани.
inv.classid = clArmor
GenerateArmor inv, 1, armCloth
SetInvEval inv, TRUE
AddInvItem wArmor, inv
'Добавим персонажу ножКак только персонаж создан, мы создаем для него доспехи из ткани и помещаем в слот для доспехов. Обратите внимание, что мы отметили место вставки кода для добавление персонажу ножа. Наш персонаж будет начинать с ним игру, чтобы не быть совсем уж беззащитным.
Теперь у нас есть доспехи, но еще нам необходимо и оружие, которое мы добавим в следующей главе.

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