
inv.bi
'Идентификаторы эффектов.
Enum spellid
splNone 'Нет эффекта.
splMaxHealing 'Восстанавливает здоровье до максимума.
splStrongMeat 'Добавляет временный бонус к силе.
splBreadLife 'Лечит отравление.
splMaxMana 'Полностью восстанавливает ману.
splSerpentBite 'Оружие: Наносит урон ядом.
splRend 'Оружие: Уменьшает броню цели.
splSunder 'Оружие: Снижает наносимый целью урон оружием.
splReaper 'Оружие: Вызывает у монстра приступ страха.
splFire 'Оружие: Поджигает цель.
splGoliath 'Оружие: Добавляет к атаке дополнительную силу.
splStun 'Оружие: Обездвиживает цель на время.
splChaos 'Оружие: Добавляет случайные повреждения.
splWraith 'Оружие: Уменьшает случайную характеристику цели.
splThief 'Оружие: Забирает случайное количество от параметра цели и передает персонажу.
End EnumМы добавили несколько новых идентификаторов заклинаний к перечислению spellid. Первые пять вы уже видели, это заклинания расходных материалов, остальные — заклинания для оружия. Разместив заклинания для оружия друг за другом, мы сожем выбирать из списка случайное заклинание именно для оружия, указав первое и последнее заклинание в качестве параметров выбора. Таким образом мы в одно перечисление можем поместить все типы заклинаний. встречающихся в нашей игре, и нам не нужно будет отслеживать несколько разных перечислений.
inv.bi
'Определение типа информации о заклинании.
Type spelltype
id As spellid 'Идентификатор заклинания.
lvl As Integer 'Уровень заклинания.
splname As String * 30 'Название заклинания.
spldesc As String * 60 'Описание заклинания.
manacost As Integer 'Стоимость маны.
dam As Integer 'Кол-во возможных повреждений, наносимых заклинанием.
End TypeКак вы помните, для предметов мы использовали только один цифровой идентификатор заклинания. Однако, то что нам необходимо сделать, на самом деле, это создать общую структуру данных описывающую все заклинания, а не жестко привязать какой либо эффект к определенному предмету. Опять же, мы не хотим иметь кучу разных структур данных для разных типов заклинаний, нам нужна одна общая структура, что облегчит нам работу. Определение типа spelltype содержит все необходимые поля данных, которые нам могут понадобиться для заклинаний предметов, и заклинаний которые могут быть «вызваны» персонажем. Данная структура поможет нам написать подпрограммы для работы с заклинаниями, которые не зависят от типа самого заклинания.
Поле id содержит идентификатор заклинания из перечисления spellid. Поле lvl содержит уровень заклинания. Чем выше уровень заклинания, тем сильнее его эффект. Поля splname и spldesc используются подпрограммой работы с инвентарем персонажа и содержат название заклинания и его описание. Поле manacost указывает на количество маны, необходимой для использования данного заклинания. Мы не будем использовать данное поле для заклинаний оружия, так как данный тип заклинаний — это часть самого оружия и не требует чего либо еще, однако количество маны будет играть значительную роль при использовании заклинаний самим персонажем. Поле dam содержит количество урона, наносимого данным заклинанием, но, так как не все заклинания наносят урон, то мы будем использовать это поле и для других целей.
Так как теперь мы используем определение типа для заклинаний, а не целочисленное значение, то нам необходимо обновить определение типа оружия.
inv.bi
'Определение типа для оружия. Type weapontype id As weaponids 'Тип оружия evaldr As Integer 'Сложность определения. Evaldr > 0 для маг. предметов. eval As Integer 'Определен ли предмет. spell As spelltype 'Заклинание. noise As Integer 'Кол-во шума при использовании или переноске. use As itemuse 'Как используется. dam As integer 'Наносимые повреждения. hands As Integer 'Кол-во необходимых рук для использования. wslot(1 To 2) As wieldpos 'Какой слот занимает. Может занимать 2 слота. weapontype As weaptype 'Тип оружия: ближний бой, дистанционное. capacity As Integer 'Емкость обоймы. ammotype As ammoids 'Тип используемых боеприпасов. ammocnt As Integer 'Кол-во снарядов в обойме. iswand As Integer 'Если это «волшебная палочка». End Type
Единственное изменение заключается в том, что мы изменили тип поля spell на spelltype вместо spellid. Когда мы создаем новое оружие, нам необходимо заполнить данное поле данными, которые описывают прикрепленное к данному оружию заклинание. Также, мы должны добавить обработку данного поля для всех подпрограмм работы с оружием, так что давайте начнем, пожалуй, с подпрограммы создания нового предмета оружия.
inv.bi
'Создание оружия.
Sub GenerateWeapon(inv As invtype, currlevel As Integer, wpid As weaponids = wpNone)
Dim item As weaponids
Dim As Integer isMagic
isMagic = ItemIsMagic(currlevel)
item = wpid
'Создадим тип, если не задан.
If item = wpNone Then
item = RandomRange(wpClub, wpGoldWand)
EndIf
'Общее для всех предметов.
inv.classid = clWeapon
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
inv.weapon.iswand = FALSE
ClearSpell inv.weapon.spell
'Магический предмет.
If IsMagic = TRUE Then
inv.weapon.evaldr = GetScaledFactor(charint, currlevel) 'Сложность опознания.
inv.weapon.spell.id = RandomRange(splSerpentBite, splThief) 'Идентификатор заклинания.
GenerateSpell inv.weapon.spell, currlevel
EndIf
'Зададим значения по умолчанию для типа оружия и боеприпасов.
inv.weapon.weapontype = wtMelee
inv.weapon.ammotype = amNone
inv.weapon.ammocnt = 0
'Зададим тип оружия и количество.
Select Case item
...Как видите, нам пришлось совсем немного ее изменить для поддержки нового типа заклинаний. Хотя мы и добавили несколько новых подпрограмм. Подпрограмма ClearSpell просто очищает структуру данных заклинания. GenerateSpell заполняет структуру данных заклинания в соответствии с типом заклинания выбранного при помощи функции RandomRange. Здесь вы можете увидеть, почему мы хотим, чтобы идентификаторы заклинаний для оружия находились в одном месте. При добавлении нового заклинания, мы добавим его идентификатор между существующими идентификаторами задающими диапазон заклинаний, что позволит нам обойтись без изменения существующего кода в GenerateWeapon. Необходимо будет только добавить код для нового заклинания в процедуру GenerateSpell. Теперь давайте посмотрим на две новых подпрограммы для создания заклинаний.
inv.bi
'Очищает структуру заклинания Sub ClearSpell (spl As spelltype) 'Уберем тип заклинания. spl.id = splNone spl.lvl = 0 spl.splname = "" spl.spldesc = "" spl.manacost = 0 spl.dam = 0 End Sub
Процедура ClearSpell просто очищает структуру spelltype. Обратите внимание, что мы передаем в процедуру именно только те данные, которые необходимо очистить, а не всю структуру данных оружия. Это позволит нам использовать данную процедуру не только для оружия, но и для заклинаний других предметов, в также позволит избежать случайных изменений данных, которая данная процедура изменять не должна.
inv.bi
'Заполняем данные заклинаний.
Sub GenerateSpell(spl As spelltype, currlevel As Integer)
'Создаем заклинание основывая на его идентификаторе.
Select Case spl.id
Case splSerpentBite 'Оружие: Наносит урон ядом.
spl.lvl = currlevel 'Ткущий уровень используется для уровня заклинания.
spl.splname = GetSpellName(spl.id) 'Sets the spell name.
spl.spldesc = GetSpellEffect(spl.id) 'Sets the spell description.
spl.manacost = 0 'Cost in mana.
spl.dam = currlevel 'How much damage the spell does.
Case splRend 'Оружие: Уменьшает броню цели.
spl.lvl = currlevel
spl.splname = GetSpellName(spl.id)
spl.spldesc = GetSpellEffect(spl.id)
spl.manacost = 0
spl.dam = currlevel
Case splSunder 'Оружие: Снижает наносимый целью урон оружием.
spl.lvl = currlevel
spl.splname = GetSpellName(spl.id)
spl.spldesc = GetSpellEffect(spl.id)
spl.manacost = 0
spl.dam = currlevel
Case splReaper 'Оружие: Вызывает у монстра приступ страха.
spl.lvl = currlevel
spl.splname = GetSpellName(spl.id)
spl.spldesc = GetSpellEffect(spl.id)
spl.manacost = 0
spl.dam = currlevel
Case splFire 'Оружие: Поджигает цель.
spl.lvl = currlevel
spl.splname = GetSpellName(spl.id)
spl.spldesc = GetSpellEffect(spl.id)
spl.manacost = 0
spl.dam = currlevel
Case splGoliath 'Оружие: Добавляет к атаке дополнительную силу.
spl.lvl = currlevel
spl.splname = GetSpellName(spl.id)
spl.spldesc = GetSpellEffect(spl.id)
spl.manacost = 0
spl.dam = currlevel
Case splStun 'Оружие: Обездвиживает цель на время.
spl.lvl = currlevel
spl.splname = GetSpellName(spl.id)
spl.spldesc = GetSpellEffect(spl.id)
spl.manacost = 0
spl.dam = currlevel
Case splChaos 'Оружие: Добавляет случайные повреждения.
spl.lvl = currlevel
spl.splname = GetSpellName(spl.id)
spl.spldesc = GetSpellEffect(spl.id)
spl.manacost = 0
spl.dam = RandomRange(1, currlevel)
Case splWraith 'Оружие: Уменьшает случайную характеристику цели.
spl.lvl = currlevel
spl.splname = GetSpellName(spl.id)
spl.spldesc = GetSpellEffect(spl.id)
spl.manacost = 0
spl.dam = currlevel
Case splThief 'Оружие: Забирает случайное количество от параметра цели и передает персонажу.
spl.lvl = currlevel
spl.splname = GetSpellName(spl.id)
spl.spldesc = GetSpellEffect(spl.id)
spl.manacost = 0
spl.dam = currlevel
Case Else
spl.lvl = 0
spl.splname = "Unknown spell"
spl.spldesc = "Unknown spell effect."
spl.manacost = 0 'Cost in mana.
spl.dam = 0
End Select
End SubПодпрограмма заполняет структуру данных заклинания основываясь на его идентификаторе и текущей глубины подземелья. Глубина используется для задания силы заклинания. По мере спуска персонажа на более глубокие уровни подземелья, находимые им магические предметы будут становиться более сильными, предлагая игроку больше тактических решений в игре. У игрока появится выбор, оставить более сильное оружие со слабым заклинанием, или заменить его на более слабое, но с более сильными магическими эффектами. Возможность выбора делает игру более интересной и позволяет игроку пробовать разные варианты при прохождении игры.
Как видите, формат этой процедуры точно такой же, как и других процедур генерации. Я уже говорил это раньше, но стоит повторить еще раз: создание однотипного кода упрощает его, облегчает его поддержку и делает его самодокументируемым. Вернувшись к данной подпрограмме через месяцы, вы всегда сможете понять что она делает, так как все подпрограммы у вас написаны в одном стиле, что позволит вам добавлять новые предметы просто расширив шаблон. Это делает программу более надежной и позволяет избежать случайных ошибок при внесении изменений.
GenerateSpell довольно прост, он просто устанавливает базовую информацию для заклинаний. Здесь у нас появляются две новых функции, которые задают заклинаниям названия и описания. Сейчас мы их рассмотрим.
inv.bi
'Возвращает название заклинания.
Function GetSpellName(spl As spellid) As String
Dim As String ret = ""
Select Case spl
Case splMaxHealing 'Восстанавливает здоровье до максимума.
ret = "Spell of Maximum healing."
Case splStrongMeat 'Добавляет временный бонус к силе.
ret = "Spell of Enhance Strength."
Case splBreadLife 'Лечит отравление.
ret = "Spell of Cure Poison."
Case splMaxMana 'Полностью восстанавливает ману.
ret = "Spell of Restore Mana."
Case splSerpentBite 'Оружие: Наносит урон ядом.
ret = "Serpent's Bite"
Case splRend 'Оружие: Уменьшает броню цели.
ret = "Spell of Rend Armor"
Case splSunder 'Оружие: Снижает наносимый целью урон оружием.
ret = "Spell of Sunder"
Case splReaper 'Оружие: Вызывает у монстра приступ страха.
ret = "Spell of The Reaper"
Case splFire 'Оружие: Поджигает цель.
ret = "Spell of Fire"
Case splGoliath 'Оружие: Добавляет к атаке дополнительную силу.
ret = "The Strength of Goliath"
Case splStun 'Оружие: Обездвиживает цель на время.
ret = "Spell of Stun"
Case splChaos 'Оружие: Добавляет случайные повреждения.
ret = "Chaos Attack"
Case splWraith 'Оружие: Уменьшает случайную характеристику цели.
ret = "Hand of the Wraith"
Case splThief 'Оружие: Забирает случайное количество от параметра цели и передает персонажу.
ret = "Spell of The Phantom"
Case Else
ret = "No spell."
End Select
Return ret
End FunctionЭта функция просто смотрит на id заклинания и возвращает имя, связанное с этим идентификатором. Ничего сложного.
inv.bi
'Возвращает эффект заклиания. Используется в описании.
Function GetSpellEffect(spl As spellid) As String
Dim As String ret = ""
Select Case spl
Case splMaxHealing 'Восстанавливает здоровье до максимума.
ret = "Restore full health."
Case splStrongMeat 'Добавляет временный бонус к силе.
ret = "Adds bonus to strength."
Case splBreadLife 'Лечит отравление.
ret = "Cures poison."
Case splMaxMana 'Полностью восстанавливает ману.
ret = "Restore full mana."
Case splSerpentBite 'Оружие: Наносит урон ядом.
ret = "Inflicts poison damage over time."
Case splRend 'Оружие: Уменьшает броню цели.
ret = "Decreases armor of target."
Case splSunder 'Оружие: Снижает наносимый целью урон оружием.
ret = "Decreases target weapon damage."
Case splReaper 'Оружие: Вызывает у монстра приступ страха.
ret = "Causes monster to flee."
Case splFire 'Оружие: Поджигает цель.
ret = "Inflicts fire damage over time."
Case splGoliath 'Оружие: Добавляет к атаке дополнительную силу.
ret = "Adds additional strength to damage."
Case splStun 'Оружие: Обездвиживает цель на время.
ret = "Stuns target doing damage for a time."
Case splChaos 'Оружие: Добавляет случайные повреждения.
ret = "Adds random damage to attack."
Case splWraith 'Оружие: Уменьшает случайную характеристику цели.
ret = "Decreases random attribute of target."
Case splThief 'Оружие: Забирает случайное количество от параметра цели и передает персонажу.
ret = "Steals random attribute amount and adds to character."
Case Else
ret = "No effect."
End Select
Return ret
End FunctionGetSpellEffect возвращает описание заклинания. Оно нужно, чтобы игрок знал, какой эффект будет от использования того или иного заклинания. Название и описание заклинания отображается во время просмотра игроком описания предмета. Отображая описание заклинания мы избавим игрока от необходимости запоминать, что именно делает данное заклинание, что сделает игру более комфортной.
Давайте посмотрим, как мы показываем эту информацию игроку.
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 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
If IsEval(inv) = TRUE Then
If inv.weapon.spell.id <> splNone Then
idx += 1
ReDim Preserve lines(0 to idx) As String
lines(idx) = "* Spell: " & Trim(inv.weapon.spell.splname)
idx += 1
ReDim Preserve lines(0 to idx) As String
lines(idx) = "* Effect: " & Trim(inv.weapon.spell.spldesc)
End If
idx += 1
ReDim Preserve lines(0 to idx) As String
lines(idx) = "* Item is evaluated"
Else
idx += 1
ReDim Preserve lines(0 to idx) As String
lines(idx) = "* Item is not evaluated"
End If
...
End Select
EndIf
End SubДля ясности здесь показан только раздел с оружием. Мы получаем описание предмета и наносимый оружием урон, как делали это раньше. Затем мы проверяем, определен ли предмет, и если это так, то получаем название заклинания с его описанием и показываем их игроку. Если предмет не опознан, то мы не отображаем информацию о заклинании, а просто указываем игроку, что данный предмет еще не инспектировался. Игрок не будет знать, есть ли какие либо магические свойства у предмета, пока не определит его, что добавит дополнительный интерес игре.
Теперь наши заклинания на своем месте и мы должны добавить их в процедуры боя.
dod.bas
'Расчет ближнего боя.
Sub DoMeleeCombat(mx As Integer, my As Integer)
Dim As Integer cf, df, croll, mroll, dam, isdead, midx, mxp, xp
Dim As String txt, mname
Dim As Single marm
Dim As spelltype spl1, spl2
'Поучим фактор защиты монстра.
df = level.GetMonsterDefense(mx, my)
mname = level.GetMonsterName(mx, my)
'Получим здоровье монстра.
mxp = level.GetMonsterXP(mx, my)
'Убедимся что есть данные.
If df > 0 Then
'Возвращает фактор атаки основанный на том, чем вооружен персонаж.
cf = pchar.GetMeleeCombatFactor()
'Возьмем случайные значения.
croll = RandomRange(1, cf)
mroll = RandomRange(1, df)
'Если персонаж ударил монстра.
If croll > mroll Then
'Получим наносимое оружием повреждение.
dam = pchar.GetWeaponDamage()
'Получим броню монстра.
marm = level.GetMonsterArmor(mx, my)
'Рассчитаем повреждения, основанное на рейтинге брони.
dam = dam - (dam * marm)
If dam <= 0 Then dam = 1
'Проверим, есть ли опознанное магическое оружие в обоих слотах, .
spl1 = pchar.GetWeaponSpell(wPrimary)
spl2 = pchar.GetWeaponSpell(wSecondary)
'Проверим заклинание «голиаф» (добавляет силу).
If spl1.id = splGoliath Then dam += pchar.CurrStr
If spl2.id = splGoliath Then dam += pchar.CurrStr
'Изменим здоровье монстра.
isdead = level.ApplyDamage(mx, my, dam)
'Убедимся, что монстр жив.
If isdead = FALSE Then
'Применим заклинание.
If spl1.id <> splNone Then
isdead = DoWeaponSpell(spl1, mx, my)
EndIf
If isdead = FALSE Then
If spl2.id <> splNone Then
isdead = DoWeaponSpell(spl2, mx, my)
End If
EndIf
EndIf
'Если монстр умер.
If isdead = TRUE Then
'Добавим персонажу опыт.
xp = pchar.CurrXP
xp += mxp
pchar.CurrXP = xp
xp = pchar.TotXP
xp += mxp
pchar.TotXP = xp
'Напечатаем сообщение.
txt = pchar.CharName & " killed the " & mname & " with " & dam & " damage points."
PrintMessage txt
Else
txt = pchar.CharName & " hit the " & mname & " for " & dam & " damage points."
PrintMessage txt
EndIf
Else
txt = pchar.CharName & " missed the " & mname & "."
PrintMessage txt
EndIf
EndIf
End SubКак вы можете видеть, нам не пришлось добавлять много кода для реализации магической системы. Вот внесенные изменения:
dod.bas:DoMeleeCombat
'Проверим, есть ли опознанное магическое оружие в обоих слотах, .
spl1 = pchar.GetWeaponSpell(wPrimary)
spl2 = pchar.GetWeaponSpell(wSecondary)
'Проверим заклинание «голиаф» (добавляет силу).
If spl1.id = splGoliath Then dam += pchar.CurrStr
If spl2.id = splGoliath Then dam += pchar.CurrStr
'Изменим здоровье монстра.
isdead = level.ApplyDamage(mx, my, dam)
'Убедимся, что монстр жив.
If isdead = FALSE Then
'Применим заклинание.
If spl1.id <> splNone Then
isdead = DoWeaponSpell(spl1, mx, my)
EndIf
If isdead = FALSE Then
If spl2.id <> splNone Then
isdead = DoWeaponSpell(spl2, mx, my)
End If
EndIf
EndIfGetWeaponSpell возвращает информацию о заклинании во временную структуру. Нам необходима вся информация о заклинании, когда мы его применяем. Кроме того, нам нужно проверить оба слота персонажа, так как у него в руках могут быть два одноручных оружия. Теперь мы должны проверить — не является ли текущее заклинание, заклинанием «голиаф», так как оно добавляет силу к удару, а значит и урону наносимому текущим оружием, и его эффект должен быть применен здесь, а не позже, как у большинства заклинаний. Если после нанесения повреждений оружием монстр все еще жив, мы переменяем у нему эффекты заклинаний. Опять же мы должны проверить оба слота персонажа, так как у него в руках могут быть два магических оружия.
character.bi
'Возвращает информацию о заклинании
Function character.GetWeaponSpell(wslot As wieldpos) As spelltype
Dim ret As spelltype
'Очистим структуру.
ClearSpell ret
'Проверим слот на наличие в нем оружия.
If _cinfo.cwield(wslot).classid = clWeapon Then
'Проверим, опознано ли оно.
If _cinfo.cwield(wslot).weapon.eval = TRUE Then
'Вернем заклинание; возможно splNone.
ret = _cinfo.cwield(wslot).weapon.spell
End If
End If
Return ret
End FunctionФункция GetWeaponSpell является частью объекта персонажа и просто возвращает данные о заклинании оружия в определенном слоте персонажа, если оно было определено. Подпрограмма ClearSpell устанавливает возвращаемое значение в splNone, так что. Если оружие небыло определено, функция вернет splNone в качестве идентификатора заклинания. Что произойдет если оружие не содержит заклинания? Функция вернет splNone, так как мы вызывали функцию ClearSpell во время создания оружия до назначения ему идентификатора заклинания. Это гарантирует, что всегда вернется правильный идентификатор заклинания, какое бы оружие не находилось в руках у персонажа.
dod.bas
'Применяет активное оружейное заклинание.
Function DoWeaponSpell (spl As spelltype, mx As Integer, my As Integer) As Integer
Dim As Integer ret = FALSE, statamt, rstat
Dim As monStats mstat
Dim As String splname
If spl.id = splThief Then
'Получим случайную характеристику.
mstat = RandomRange(CombatFactor, MagicDefense)
statamt = level.GetMonsterStatAmt(mstat, mx, my)
rstat = RandomRange(1, statamt - 1)
'Добавим временное бонусное значение характеристики персонажу.
Select Case mstat
Case CombatFactor
pchar.BonAcf = rstat
pchar.BonAcfCnt = spl.lvl
Case CombatDefense
pchar.BonCdf = rstat
pchar.BonCdfCnt = spl.lvl
Case MagicCombat
pchar.BonMcf = rstat
pchar.BonMcfCnt = spl.lvl
Case MagicDefense
pchar.BonMdf = rstat
pchar.BonMdfCnt = spl.lvl
End Select
Else
'Применим заклинание к монстру.
ret = level.ApplySpell(spl, mx, my)
EndIf
Return ret
End FunctionDoWeaponSpell применяет эффект заклинания к монстру. Обратите внимание, что мы проверяем, является ли текущее заклинание. Заклинанием «Вор», так как оно добавляет бонус украденной у монстра характеристики персонажу. Различные боевые характеристики монстра перечислены в файле monster.bi.
monster.bi
'Характеристики монстров, используемых заклинаниями.
Enum monStats
CombatFactor = 1
CombatDefense
MagicCombat
MagicDefense
End EnumВначале случайным образом выбирается боевая характеристика монстра, а затем, при помощи функции GetMonsterStatAmt получаем ее значение.
monster.bi
'Возвращает текущее значение характеристики монстра
Function levelobj.GetMonsterStatAmt(stat As monStats, mx As Integer, my As Integer) As Integer
Dim As Integer midx, ret = 0
If _level.lmap(mx, my).monidx > 0 Then
midx = _level.lmap(mx, my).monidx
If stat = CombatFactor Then
ret = _level.moninfo(midx).cf
ElseIf stat = CombatDefense Then
ret = _level.moninfo(midx).cd
ElseIf stat = MagicCombat Then
ret = _level.moninfo(midx).mf
ElseIf stat = MagicDefense Then
ret = _level.moninfo(midx).md
EndIf
End If
Return ret
End FunctionФункция просто проверяет — значение какого параметра нам нужно и возвращает его. Так как список монстров у нас находится в объекте уровня подземелья. То данная функция также принадлежит объекту уровня.
Как только мы получили значение параметра, нам необходимо применить его к персонажу.
dod.bas:DoWeaponSpell
...
Select Case mstat
Case CombatFactor
pchar.BonAcf = rstat
pchar.BonAcfCnt = spl.lvl
...Значение записывается в поле бонуса соответствующего параметра персонажа, а его продолжительность зависит от уровня заклинания. Чем больше уровень подземелья, тем выше уровень у заклинания и тем больше продолжительность действия бонуса. Однако стоит иметь ввиду, что только один бонус может быть активным одновременно, что заставит игрока решать, какое оружие и с каким заклинанием ему стоит использовать в данный момент времени. Это добавляет игре еще больше тактических аспектов, а также не позволит ему становиться слишком сильным. Если бы бонусы имели накопительный эффект, то игра стала бы очень простой и скучной.
Если передаваемое в функцию DoWeaponSpell заклинание не является заклинанием «Вор», то мы должны применить его эффект на монстра, что мы и делаем при помощи функции объекта уровня ApplySpell.
map.bi
'Применить заклинание на монстра.
Function levelobj.ApplySpell(spl As spelltype, mx As Integer, my As Integer) As Integer
Dim As Integer ret, midx
Dim stat As monStats
Dim As String txt
'Убедимся что здесь есть монстр.
If _level.lmap(mx, my).monidx > 0 Then
'Получим индекс монстра.
midx = _level.lmap(mx, my).monidx
'Установим монстру флаг в зависимости от заклинания.
Select Case spl.id
Case splSerpentBite 'Оружие: Наносит урон ядом.
ret = ApplyDamage(mx, my, spl.dam)
'Если не умер, установим флаг времени.
If ret = FALSE Then
_level.moninfo(midx).effects(mePoison).cnt = spl.lvl
_level.moninfo(midx).effects(mePoison).dam = spl.dam
EndIf
txt = "Sperpent Bite Spell does " & spl.dam & " to " & _level.moninfo(midx).mname & "."
PrintMessage txt
Case splRend 'Оружие: Уменьшает броню цели.
_level.moninfo(midx).armval = _level.moninfo(midx).armval - (spl.dam / maxlevel)
If _level.moninfo(midx).armval < 0.0 Then
_level.moninfo(midx).armval = 0.0
EndIf
txt = "Rend Spell reduces armor to " & _level.moninfo(midx).armval & " for " & _level.moninfo(midx).mname & "."
PrintMessage txt
Case splSunder 'Оружие: Снижает наносимый целью урон оружием.
_level.moninfo(midx).atkdam = _level.moninfo(midx).atkdam - spl.dam
If _level.moninfo(midx).atkdam < 1 Then
_level.moninfo(midx).atkdam = 1
EndIf
txt = "Sunder Spell reduces attack damage to " & _level.moninfo(midx).atkdam & " for " & _level.moninfo(midx).mname & "."
PrintMessage txt
Case splReaper 'Оружие: Вызывает у монстра приступ страха.
_level.moninfo(midx).flee = TRUE
txt = "Reaper Spell is making " & _level.moninfo(midx).mname & " flee."
PrintMessage txt
Case splFire 'Оружие: Поджигает цель.
ret = ApplyDamage(mx, my, spl.dam)
'If not dead set the timed flag.
If ret = FALSE Then
_level.moninfo(midx).effects(meFire).cnt = spl.lvl
_level.moninfo(midx).effects(meFire).dam = spl.dam
EndIf
txt = "Fire Spell inflicted " & spl.dam & " to " & _level.moninfo(midx).mname & "."
PrintMessage txt
Case splStun 'Оружие: Обездвиживает цель на время.
_level.moninfo(midx).effects(meStun).cnt = spl.lvl
_level.moninfo(midx).effects(meStun).dam = 0
txt = "Stun Spell stunned " & _level.moninfo(midx).mname & "."
PrintMessage txt
Case splChaos 'Оружие: Добавляет случайные повреждения.
ret = ApplyDamage(mx, my, spl.dam)
txt = "Chaos Spell inflicted " & spl.dam & " additional damage to " & _level.moninfo(midx).mname & "."
PrintMessage txt
Case splWraith 'Оружие: Уменьшает случайную характеристику цели.
'Get the stat.
stat = RandomRange(CombatFactor, MagicDefense)
Select Case stat
Case CombatFactor
_level.moninfo(midx).cf = _level.moninfo(midx).cf - spl.dam
If _level.moninfo(midx).cf < 0 Then
_level.moninfo(midx).cf = 1
EndIf
txt = "Wraith Spell reduced " & _level.moninfo(midx).mname & " combat factor by " & spl.dam & "."
PrintMessage txt
Case CombatDefense
_level.moninfo(midx).cd = _level.moninfo(midx).cd - spl.dam
If _level.moninfo(midx).cd < 0 Then
_level.moninfo(midx).cd = 1
EndIf
txt = "Wraith Spell reduced " & _level.moninfo(midx).mname & " combat defense by " & spl.dam & "."
PrintMessage txt
Case MagicCombat
_level.moninfo(midx).mf = _level.moninfo(midx).mf - spl.dam
If _level.moninfo(midx).mf < 0 Then
_level.moninfo(midx).mf = 1
EndIf
txt = "Wraith Spell reduced " & _level.moninfo(midx).mname & " magic combat by " & spl.dam & "."
PrintMessage txt
Case MagicDefense
_level.moninfo(midx).md = _level.moninfo(midx).md - spl.dam
If _level.moninfo(midx).md < 0 Then
_level.moninfo(midx).md = 1
EndIf
txt = "Wraith Spell reduced " & _level.moninfo(midx).mname & " magic defense by " & spl.dam & "."
PrintMessage txt
End Select
End Select
End If
Return ret
End FunctionПервое что нужно сделать, это убедиться. Что монстр, на которого нужно применить заклинание, присутствует. Если это так, то мы применяем на наго эффект заклинания основываясь на его идентификаторе. Каждое заклинание делает что то свое и влияет на монстра по разному. Обратите внимание, что здесь мы имеем дело именно с самим заклинанием, а не его источником. Это позволит добавлять в эту функцию любые заклинания применимые к монстрам, а не только заклинания привязанные к оружию. Именно для этого нам нужно было обобщить заклинания, в результате чего мы будем использовать набор одних и тех же процедур для управления всеми заклинаниями в игре.
Изучив код вы можете увидеть что конкретно делает каждое заклинание. Обратите внимание, что поля lvl и dam в каждом заклинании используются по разному. Поле dam является универсальным и используется не только для нанесения повреждений, но, также, и для снижения атрибутов монстра. Некоторые заклинания влияют на монстров с течением времени. Например, заклинание «Жнец», заставляет монстра убегать, установив в TRUE специальный атрибут. Заклинание «Оглушить» временно замораживает монстра, заставляя его пропуска все атаки и передвижения. Продолжительность большинства эффектов зависит от уровня заклинания, поэтому чем выше уровень заклинания, тем дольше его эффект будет действовать на монстра.
Так как в нашей игре присутствует не только ближний бой, но и дистанционный, то нам необходимо добавить применение эффектов заклинаний и для дистанционных атак.
dod.bas
'Расчет дистанционных атак
Sub DoProjectileCombat(mx As Integer, my As Integer, wslot As wieldpos)
Dim As Integer cf, df, croll, mroll, dam, isdead, midx, mxp, xp
Dim As String txt, mname
Dim As Single marm
Dim As spelltype spl1, spl2
'Получим фактор защиты монстра.
If pchar.ProjectileIsWand(wslot) = TRUE Then
df = level.GetMonsterMagicDefense(mx, my)
Else
df = level.GetMonsterDefense(mx, my)
EndIf
'Получим имя монстра.
mname = level.GetMonsterName(mx, my)
'Получим здоровье монстра.
mxp = level.GetMonsterXP(mx, my)
'Убедимся что данные получены.
If df > 0 Then
'Получим файтор атаки, основанный на том, чем вооружен персонаж.
If pchar.ProjectileIsWand(wslot) = TRUE Then
cf = pchar.GetMagicCombatFactor()
Else
cf = pchar.GetProjectileCombatFactor()
EndIf
'Возьмем случайные значения.
croll = RandomRange(1, cf)
mroll = RandomRange(1, df)
'Если персонаж попал по монстру.
If croll > mroll Then
'Получим наночимы оружием повреждения.
dam = pchar.GetWeaponDamage(wslot)
'Получим рейтинг брони монстра.
marm = level.GetMonsterArmor(mx, my)
'Применим повреждения зависящие от брони.
dam = dam - (dam * marm)
If dam <= 0 Then dam = 1
'Проверим заклинания для оружия в обоих слотах. Если опознано.
spl1 = pchar.GetWeaponSpell(wPrimary)
spl2 = pchar.GetWeaponSpell(wSecondary)
'Если есть заклинание «голиаф».
If spl1.id = splGoliath Then dam += pchar.CurrStr
If spl2.id = splGoliath Then dam += pchar.CurrStr
'Изменим здоровье монстра.
isdead = level.ApplyDamage(mx, my, dam)
'Если монстр все еще жив.
If isdead = FALSE Then
'Применим эффект заклинания.
If spl1.id <> splNone Then
isdead = DoWeaponSpell(spl1, mx, my)
EndIf
If isdead = FALSE Then
If spl2.id <> splNone Then
isdead = DoWeaponSpell(spl2, mx, my)
End If
EndIf
EndIf
'Если монстр умер.
If isdead = TRUE Then
'Добавим опыт персонажу.
xp = pchar.CurrXP
xp += mxp
pchar.CurrXP = xp
xp = pchar.TotXP
xp += mxp
pchar.TotXP = xp
'Вывод сообщения.
txt = pchar.CharName & " killed the " & mname & " with " & dam & " damage points."
PrintMessage txt
Else
txt = pchar.CharName & " hit the " & mname & " for " & dam & " damage points."
PrintMessage txt
EndIf
Else
txt = pchar.CharName & " missed the " & mname & "."
PrintMessage txt
EndIf
EndIf
End SubЭто обновленный код расчета дистанционных атак. Как вы можете видеть, он почти в точности повторяет код атак ближнего боя. Мы получаем информацию о заклинаниях, если таковые имеются, проверяем наличие заклинания «голиаф». Наносим монстру повреждения от оружия, и, затем, применяем остальные эффекты. Это все что нам необходимо сделать, для добавления заклинаний для дистанционного оружия.
Нам достаточно много пришлось поработать, для реализации магической системы, но большая часть кода была очень простой, так как мы обобщили данные заклинаний. Теперь у нас есть базовая магическая система, к которой мы, достаточно легко, можем добавить заклинания для доспехов и ювелирных изделий. Так как мы будем использовать данную систему в качестве основы, то для добавления новых заклинаний нам потребуется просто обновить некоторые процедуры для применения и обработки новых эффектов.
Комментариев нет:
Отправить комментарий