Уважаемые гости и новички, приветствуем Вас на нашем форуме
Здесь вы можете найти ответы практически на все свои вопросы о серии игр «Готика» (в том числе различных модах на нее), «Ведьмак», «Ризен», «Древние свитки», «Эра дракона» и о многих других играх. Можете также узнать свежие новости о разработке новых проектов, восхититься творчеством наших форумчан, либо самим показать, что вы умеете. Ну и наконец, можете обсудить общие увлечения или просто весело пообщаться с посетителями «Таверны».
Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
Удачи!
Друзья, доброго времени суток!
Стартовал новый литературный конкурс от "Ордена Хранителей" - "Пираты Миртанского моря". Каждый может принять в нём участие и снискать славу и уважение, а в случае занятия призового места ещё и получить награду. Дерзайте
Дорогие друзья, год подходит к концу, и пришло время подвести его итоги и наградить достойных
Не ленитесь, голосуйте в этой теме за тех форумчан, которые по вашему мнению больше всех проявили себя в этом году
По желанию, аргументировать свой выбор можете в теме обсуждения голосования.
Вы используете устаревший браузер. Этот и другие сайты могут отображаться в нём некорректно. Вам необходимо обновить браузер или попробовать использовать другой.
Orc Hunter, ну если чего-то сложного не надо, то вот скриптовый модификатор урона. Попробуй.
В скриптах необходимо определить функцию func int OnDamage_Hit(var int damageTotal)
Плагин автоматически вызовет скриптовую функцию и установит следующие значения:
self - атакующий
victim - атакуемый
item - орудие убийства (может быть null)
Параметры функции:
damageTotal - рассчитанное количество урона, которое предполагается снять с victim
Возвращаемое значение:
Новое значение урона, которое будет снято с victim
Примечание:
Скриптовый модификатор урона не будет вызван, если victim неуязвим.
Пример:
Daedalus:
func int OnDamage_Hit(var int damageTotal)
{
if (Npc_IsPlayer(self) && Hlp_GetInstanceID(DJG_713_Biff_DI) == Hlp_GetInstanceID(victim))
{
return 10000;
};
return damageTotal;
};
Orc Hunter, там будет Null.
Расширил инфу о дамаге. Определи в скрипте структуру C_DamageInfo и производную от нее инстанцию damageinfo.
В инстанцию будет писаться дополнительная инфа:
Daedalus:
class C_DamageInfo
{
var int ItemID;
var int IsSpellDamage;
var int SpellID;
var int SpellLevel;
};
instance damageinfo(C_DamageInfo) { };
- ItemID - номер инстанции предмета, которым нанесен урон. Если предмета нет или это был спелл, то номер будет равен -1.
- IsSpellDamage - значение, определяющее, нанесен ли урон спелом.
- SpellID - идентификатор спела, например SPL_FIREBALL.
- SpellLevel - уровень спела, например при касте огненной бури.
Пример использования функции. Например вы хотите полностью поменять расчёт урона для рапир и шпаг.
В OnDamage_Hit производится проверка на то был ли удар нанесён Рапирой или Шпагой, после рассчитываем новый урон или возвращаем старый. Проверку и расчёт урона я для удобства вынес в отдельный файл.
Daedalus:
func int OnDamage_Hit(var int damageTotal)
{
if (Npc_IsEquippedItMw1hRPR(item) == true)
{
return DMG_ItMw1hRPR(self, victim, item);
};
return damageTotal;
};
damageTotal - общая величина урона
такую проверку можно использовать для того что бы определить насколько "мощное оружие". например if (item.damageTotal >=130)мы понимаем что оружие "мощное" и можем пересчитать урон, уменьшив например влияние защиты жертвы или дав шанс нанести такому оружию дополнительный ущерб. или шанс проигнорировать защиту жертву или прикрутить какой то эффект.
cond_value - минимальное значение атрибута, которым должен обладать Npc для экипировки предмета (с)
давайте условно разделим всё оружие на то у которого ущерб больше чем требуется атрибута, равен или меньше. и мы получим условно три типа: плохое, обычное и хорошее.
условный тип
пример проверки
примечание
"плохое" оружие
Daedalus:
if (item.cond_value[2] > item.damageTotal)
грубые, потёртые, ржавые, старое и т.п. оружие. некоторое дробящее оружие.
"обычное " оружие
Daedalus:
if (item.cond_value[2] == item.damageTotal)
большинство оружие
"хорошее" оружие
Daedalus:
if (item.cond_value[2] < item.damageTotal)
все рудные клинки, самокованное оружие, прочее
cond_atr - массив атрибутов Npc необходимых для экипировки предмета (с)
можно использовать как дополнительную проверку что бы например вычленить оружие ближнего боя требующую ловкость.[/I][/I][/I]
атакующий НПС держит оружие ближнего боя которое требует ловкость
Daedalus:
if (Npc_HasReadiedMeleeWeapon(self)
&& item.cond_atr[2] == ATR_DEXTERITY)
это будет шпага, шпага мастера, бетти, рапира и бандитская шпага
атакующий НПС держит оружие дальнего боя которое требует силу
Daedalus:
if (Npc_HasReadiedRangedWeapon(self)
&& item.cond_atr[2] == ATR_STRENGTH)
получатся все арбалеты
так же проверку на cond_atr можно использовать в связки с проверкой по флагу и/или главному флагу, результат в 99 случаев из 100 будет такой же
value - стоимость предмета (с) в классе C_Item.
цена предмета не всегда отображает его св-ва. впрочем вы можете использовать проверку на цену с сочетанием с реакцией на удар. например если нпс1 бьет нпс2 дорогим клинком, то НПС2 может дать какой то комментарий относительно того что он сделает с этим оружием после победы
ownerGuild -какой гильдии принадлежит предмет(с). в классе C_Item. Проверка на гильдию которой принадлежит предмет прекрасна, к сожалению в оригинальной игре это не используется, но если вы делаете мод то может назначить некоторым оружиям гильдию паладинов и через такую проверку определить такое оружие.
id - так же не используется в игре, но прекрасно подойдёт для модов: так легко создать некий класс оружия назначив им общий айди. так же можно использовать для какого то оружия с большим кол-вом инстанций, например Коготь Белиара.
nameID Проверка по nameID так же можно использовать в модах
Используем проверку на mainflag например с проверкой на cond_atr
оружие ближнего боя которое требует ловкость
if (item.mainflag & ITEM_KAT_NF)
&& item.cond_atr[2] == ATR_DEXTERITY)
это будет шпага, шпага мастера, бетти, рапира и бандитская шпага
оружие дальнего боя которое требует силу
if (item.mainflag & ITEM_KAT_FF)
&& item.cond_atr[2] == ATR_STRENGTH)
получатся все арбалеты
flags -Тип предмета и его свойства (с)
в случае с оружием это флаги для 1р, 2р , луков и арбалетов
оружие ближнего боя
ITEM_SWD
if (item.flags & ITEM_SWD)
оружие дальнего боя
ITEM_AXE
if (item.flags & ITEM_AXE)
Двуручный меч
ITEM_2HD_SWD
if (item.flags & ITEM_2HD_SWD)
Двуручный топор
ITEM_2HD_AXE
if (item.flags & ITEM_2HD_AXE)
Щит
ITEM_SHIELD
if (item.flags & ITEM_SHIELD)
Лук
ITEM_BOW
if (item.flags & ITEM_BOW)
Арбалет
ITEM_CROSSBOW
if (item.flags & ITEM_CROSSBOW)
Давайте в качестве примера разделим 1р топоры и 1р булавы через доп проверку на тип урона
flags
пример проверки
примечание
1р булава
ITEM_AXE
if (item.damageType == DAM_BLUNT)
&& (item.flags & ITEM_AXE)
1р топор
ITEM_AXE
if (item.damageType == DAM_EDGE)
&& (item.flags & ITEM_AXE)
2р посох
ITEM_2HD_AXE
if (item.damageType == DAM_BLUNT)
&& (item.flags & ITEM_2HD_AXE)
проверка по названию instance
Заголовок 1
Заголовок 2
Daedalus:
var int weaponInstance; weaponInstance = Hlp_GetInstanceID(item);
// посохи магов воды
if (weaponInstance == ItMW_Addon_Stab03)
|| (weaponInstance == ItMW_Addon_Stab05)
Daedalus:
//
// посохи магов огня
if (Hlp_IsItem(item, ITMW_ADDON_STAB01) == TRUE)
|| (Hlp_IsItem(item, ITMW_ADDON_STAB04) == TRUE)
проверка по range
наверное можно и тут что-то придумать , разделив условно оружие на короткое , среднее и длинное
** дальше попробую написать что можно сделать после того как выполнена проверка: *заново провести расчет урона *сделать проверку на то кто жертва и увеличить или уменьшить урон *сделать рандом и нанести дополнительный (критический) урон *перевести в какое то состояние после нанесения урона
***
Gratt можешь нам кратко про проверку на флаги рассказать, даёт она там какую то доп нагрузку?
Каких-то немыслимых истин у меня нет. В скриптах арифметикологические (не считая сложных типа умножения) операции над целыми числами работают равнозначно быстро. Вопрос только в их количестве и количестве любых других проверок, так как daedalus прослойка слишком высокого уровня и вместо выполнения команд напрямую на процессоре происходит ещё куча неявного мусора. Чем меньше выполняется проверок на пути к целевому коду, тем эффективнее он будет. То же относится к выражениям. Чем меньше в выражении операторов, и чем операторы проще, тем быстрее оно выполнится.
Будем разбавлять теоретическую часть практическими занятиями. Напишем простенький код который будет добавлять «криты» оркам.
Для начало наверное нам следует определить, кто атакует орки или нет самый простой способ (но далеко не единственный) это сделать через проверку по гильдии if (self.guild == GIL_ORC), стоит обратить внимание на то что орк в данном случае атакует , а значит он не жертва, а агрессор то есть self.
Далее мы определяем некий шанс на то будет крит или нет if (DamageRandy < 33) и если крит «проходит» увеличиваем ущерб. В данном примере я увеличу ущерб на величину урона оружия, что соответствует механики Готики damageTotal += item.damageTotal. Таким образом логически ход мысли можно записать в примерное такой вот код.
Daedalus:
func int OnDamage_Hit(var int damageTotal)
{
if (self.guild == GIL_ORC)
{
Hlp_PrintConsole ("OnDamage_Hit: self.guild = GIL_ORC ");
var int DamageRandy; DamageRandy = Hlp_Random(100);
if (DamageRandy < 33)
{
var string msg;
msg = Str_Format("OnDamage_Hit: Critical hit [%i --> %i]",damageTotal, damageTotal+ item.damageTotal);
Hlp_PrintConsole (msg);
damageTotal += item.damageTotal;
return damageTotal;
};
};
return damageTotal;
};
Запустив игру я увидел в Минентале такой вот результат
Как видите код получился довольно простой и я старался его именно таким и сделать, что бы отразить возможность модернизировать урон на основе старого. Главный минус данного решения в том что криты начнут «пробивать» сильно защищённого НПС, что не соответствует механики Готики :-(
Попробуем дописать код проведя расчет нового урона с "чистого листа". Всю формулу можно сделать в одну стразу и сразу отдать, но большинству понятнее когда всё идёт поэтапно: умножим урон оружия на два, затем добавим силу и отнимем броню. При этом конечно же надо помнить что PROT_EDGE и PROT_BLUNT могут отличаться. В оригинальной игре такую ситуацию связанную с орками сложно представить, но в моде вполне даже может быть
Прежде чем сделать шаг вперед отскочим в бок и еще раз поговорим про криты от ударов орков. Как писалось ранее отфильтровать Орков проще всего по гильдии, но можно сделать и через фильтр по оружию. В таком случае крит будут наносить не только Орки, но и брутальные люди, настоящие мужчины Конечно же самый просто вариант пометить оружие орков прописав какую то метку в ID инстанции. Но для данного примера, лучше пойти дальним путём и попытаться классифицировать оружие орков по неким признакам «брутальности»: во первых оружие орков двуручное, а во вторых оно требует много силы, точнее требует силы сильно больше чем наносит урона. То есть оно как бы «грубое». И вот как раз про такой тип я писал ранее в вводной теоретической части. Таким образом у нас получается следующие два условия
Daedalus:
if (item.cond_value[2] > item.damageTotal)
&& (item.flags & ITEM_2HD_AXE)
И вот у нас уже получился целый подкласс двуручных топоров и если в игре будут ещё какие то «брутальные» топоры они тоже дадут крит.
Заменим новыми условиями старое и получим примерной такой код
Daedalus:
func int OnDamage_Hit(var int OldDamageTotal)
{
//if (self.guild == GIL_ORC)
if (item.cond_value[2] > item.damageTotal)
&& (item.flags & ITEM_2HD_AXE)
{
var string msg;
var int DamageRandy; DamageRandy = Hlp_Random(100);
if (DamageRandy < 13)
{
var int NewDamage;
NewDamage = item.damageTotal * 2;
NewDamage += self.attribute[ATR_STRENGTH];
if (item.damagetype == DAM_EDGE)
{
NewDamage -= victim.protection[PROT_EDGE];
}
else if (item.damagetype == DAM_BLUNT)
{
NewDamage -= victim.protection[PROT_BLUNT];
};
msg = Str_Format("OnDamage_Hit: %s Critical hit [%i --> %i]", item.name, OldDamageTotal, NewDamage);
Hlp_PrintConsole(msg);
return NewDamage;
};
msg = Str_Format("OnDamage_Hit: %s hit [%i]", item.name, OldDamageTotal);
Hlp_PrintConsole(msg);
};
return OldDamageTotal;
};
запустим игру и проверим как оно будет работать
OnDamage_Hit: Краш Агаш hit [9]
OnDamage_Hit: Краш Агаш hit [100]
OnDamage_Hit: Краш Агаш hit [9]
OnDamage_Hit: Краш Уррок Critical hit [90 --> 150]
OnDamage_Hit: Краш Агаш hit [100]
OnDamage_Hit: Краш Уррок hit [90]
OnDamage_Hit: Краш Агаш hit [100]
OnDamage_Hit: Краш Агаш hit [100]
OnDamage_Hit: Краш Агаш Critical hit [100 --> 170]
OnDamage_Hit: Краш Агаш hit [9]
OnDamage_Hit: Краш Уррок hit [8]
OnDamage_Hit: Краш Уррок hit [8]
OnDamage_Hit: Краш Уррок hit [90]
OnDamage_Hit: Краш Уррок Critical hit [90 --> 150]
Итак у нас уже жертва victim, которая вот вот получает урон. У нас есть задача провести сегрегацию.
В игре достаточно много проверок написанных пираньями. Сегрегацию авторы проводили по всем каноном: жёстко и принудительно.
Нежить может получать доп ущерб от чего то или получить сильно меньше ущерба от чего то. Или «уклоняться» с какой вероятности от удара/выстрела/магии.
C_NpcIsEvil
if (C_NpcIsEvil(victim) == true)
жертва Зло( враги Паладина)
Такая проверка стоит на магии паладина и вы вполне её можете использовать на то что бы наносить доп урон / эффекты при использование клинков паладина или каких то других клинков прошедших освещение или что то там, что придумает автор.
C_NpcIsHero
if (C_NpcIsHero(victim) == true)
жертва герой
C_NpcIsDown
if (C_NpcIsDown(victim))
жертва лежит на земле. Без сознания, убита или магический сон.
посохи магов огня/воды с доп уроном по ледяным/огненным монстрам
будем продолжать подкреплять теорию практикой. рассмотрим простой пример того как добавить доп урон посохам магов/огня против ледяных/огненных монстров.
Но стоило мне скопировать код как я тут же заметил, что забыл добавить проверку на минимальный урон :-( если урон будет отрицательный , то жертва будет получать не "-", а "+", то есть лечиться. поэтому перед тем как отдать новый урон надо сделать еще одну проверку и сравнить его с нулём. ай нет, с минимальным
Daedalus:
if (NewDamage < NPC_MINIMAL_DAMAGE)
{
NewDamage = NPC_MINIMAL_DAMAGE;
};
немного самоконтроля и отдаём новый урон ледяным монстрам и возвращаем старый всем остальным.
будем исправлять эти моменты в будущем. потому что.. потому что в капитализме нельзя выпустить первый планшет с камерой, ведь тогда продажи второго будут низкие ;-)
---
Здесь, в Пустыне, у нас нет бумаги для контрактов, женщина. ...
Мужчина обещал — и это уже контракт.
И, как вождь, я связываю своим словом и моих людей.
в четвёртых любой кто занимается писанием профессионально или стремится к этому обратит внимание на то что расчёт урона не вынесен в отдельную функцию, а значит.. в практическом плане это значит что ошибки и огрехи будут множится и их нужно будет исправлять одно и тоже по много раз будем исправлять эти моменты в будущем.
итак критический ущерб уже использовался трижды и пора его вынести в отдельную func int, которая будет производить расчёт урона от критического удара и возвращать нам уже итоговый ущерб. технически это сделать просто в связи с чем этим делом часто злоупотребляют вынося всё подряд в отдельные функции поэтому прежде чем такое делать надо чётко ответить себе на вопрос "зачем" и что даст это в практическомсмысле. Чистый / красивый / "правильный" код это так себе аргумент, так как лучше иметь портянку которые вы сами можете читать и править, чем иметь чистый / красивый / "правильный" код который вы не способны поправить без посторонней помощи. я считаю урон от критического удара трижды в разных местах и хочу минимизировать кол-во ошибок при его расчёте.
Итак для того что бы посчитать ущерб мне нужны self, item и victim. Старый ущерб мне не нужен, но пусть он у меня тоже будет, так как он пригодится мне как минимум для отладки и самоконтроля.
естественно как творец я обязан что нибудь улучшить. добавлю на свякий случай проверку на "колющий" ущерб. так же я вижу в формуле цифру два. лезу в код Готики 1 и нахожу константу DAM_CRITICAL_MULTIPLIER проверяю есть ли она в коде НВ и .. она там есть!
Daedalus:
CONST INT DAM_CRITICAL_MULTIPLIER = 2 ; //Obsolet, da Treffer-%-CHance CriticalHit ersetzt hat
значит её то я и использую в формуле, вместо цифры "2".
.. в начале файла я сразу напишу для себя пару комментариев, что бы через неделю было понятно, что это не мой новодел, а берётся из первоисточника ;-)
Daedalus:
// DAM_CRITICAL_MULTIPLIER - задаётся в _intern\Constants.d
// NPC_MINIMAL_DAMAGE - задается в AI\AI_Intern\AI_Constants.d
// ФОРМУЛА КРИТА (itm.damageTotal * DAM_CRITICAL_MULTIPLIER) + slf.attribute[ATR_STRENGTH] - vctim.protection[PROT_****]
ну и в новой func int тоже подпишу что есть что
Daedalus:
func int onDamage_CriticalHit(var C_Npc slf, // агрессор
var C_Npc vctim, // жертва
var C_Item itm, // оружие агрессора
var int OldDamageTotal) // старый ущерб
и наконец сам расчёт урона..
Daedalus:
{
var string msg;
var int NewDamage;
NewDamage = (itm.damageTotal * DAM_CRITICAL_MULTIPLIER);
NewDamage += slf.attribute[ATR_STRENGTH];
if (itm.damagetype == DAM_EDGE)
{
NewDamage -= vctim.protection[PROT_EDGE];
}
else if (itm.damagetype == DAM_BLUNT)
{
NewDamage -= vctim.protection[PROT_BLUNT];
}
else if (itm.damagetype == DAM_POINT)
{
NewDamage -= vctim.protection[PROT_POINT];
};
if (NewDamage < NPC_MINIMAL_DAMAGE)
{
NewDamage = NPC_MINIMAL_DAMAGE;
};
msg = Str_Format("OnDamage_Hit[%s]: Critical hit %s", vctim.name, itm.name);
msg = Str_Format("%s [%i --> %i]", msg, OldDamageTotal, NewDamage);
Hlp_PrintConsole(msg);
return NewDamage;
};
сохраняю своё творение в файл onDamage_CriticalHit.d
осталось поправить файл с функцией OnDamage_Hitдля посохов магов огня / воды. кода теперь в нём будет сильно меньше
Daedalus:
META
{
after = onDamage_CriticalHit.d;
};
func int OnDamage_Hit(var int OldDmgTotal)
{
var string msg;
var int NewDamage;
// посохи магов огня
if (Hlp_IsItem(item, ITMW_ADDON_STAB01) == TRUE)
|| (Hlp_IsItem(item, ITMW_ADDON_STAB04) == TRUE)
{
if (victim.guild == GIL_ICEGOLEM)
|| (victim.aivar[AIV_MM_REAL_ID] == ID_DRAGON_ICE)
|| (victim.aivar[AIV_MM_REAL_ID] == ID_ICEWOLF)
{
return onDamage_CriticalHit(self, victim, item, OldDmgTotal);
};
return OldDmgTotal;
};
var int weaponInstance; weaponInstance = Hlp_GetInstanceID(item);
// посохи магов воды
if (weaponInstance == ItMW_Addon_Stab03)
|| (weaponInstance == ItMW_Addon_Stab05)
{
// огненные существа
if (victim.guild == GIL_FIREGOLEM)
|| (victim.aivar[AIV_MM_REAL_ID] == ID_FIREWARAN)
|| (victim.aivar[AIV_MM_REAL_ID] == ID_DRAGON_FIRE)
|| (victim.guild == GIL_GARGOYLE)
{
return onDamage_CriticalHit(self, victim, item, OldDmgTotal);
};
return OldDmgTotal;
};
return OldDmgTotal;
};
обратите внимание что ввиду того что это инъекция я использовал META в zParserExtender что бы определить что данный файл идёт после onDamage_CriticalHit.
не пренебрегайте(!) подобными вещами периодически чётко указывая машине кто тут главный ;-)
функция расчёта урона с учётом бонусов от силы и/или ловкости
предположим мы хотим разным типам оружия давать разные бонусы от силы / ловкости персонажа. в таком случае нам понадобится какая то func int которая будет рассчитывать урон и возвращать его. читайте так же функция расчёта урона от критического удара.
для расчёта урона понадобится отдать self, item и victim, а так же коэффициенты для бонуса(процент) от силы и ловкости
Daedalus:
func int onDamage_ScalinglHit(var C_Npc slf, // агрессор
var C_Npc victm, // жертва
var C_Item itm, // оружие агрессора
var int str, // процент для силы
var int dex) // процент для ловкости
для начало посчитаем бонусы от силы и ловкости и добавим их к урону оружия
Daedalus:
var int NewDmgTotal;
var int ScalingStr;
var int ScalingDex;
var string msg;
ScalingStr = (slf.attribute[ATR_STRENGTH] * str / 100);
ScalingDex = (slf.attribute[ATR_DEXTERITY] * dex / 100);
NewDmgTotal = (itm.DamageTotal + ScalingStr + ScalingDex);
msg = Str_Format("onDamage_ScalinglHit: %s[%i]", itm.name, itm.DamageTotal);
msg = Str_Format("%s + ScalingStr[%i] + ScalingDex[%i]", msg, ScalingStr, ScalingDex);
Hlp_PrintConsole(msg);
теперь можно определить навык владения оружием которым НПС наносит удар
Daedalus:
var int SlfHitChance;
if (itm.flags & ITEM_DAG) { SlfHitChance = slf.HitChance[NPC_TALENT_1H]; }
else if (itm.flags & ITEM_SWD) { SlfHitChance = slf.HitChance[NPC_TALENT_1H]; }
else if (itm.flags & ITEM_AXE) { SlfHitChance = slf.HitChance[NPC_TALENT_1H]; }
else if (itm.flags & ITEM_2HD_SWD) { SlfHitChance = slf.HitChance[NPC_TALENT_2H]; }
else if (itm.flags & ITEM_2HD_AXE) { SlfHitChance = slf.HitChance[NPC_TALENT_2H]; }
else if (itm.flags & ITEM_BOW) { SlfHitChance = slf.HitChance[NPC_TALENT_BOW]; }
else if (itm.flags & ITEM_CROSSBOW) { SlfHitChance = slf.HitChance[NPC_TALENT_CROSSBOW]; };
посчитаем шанс нанесения полного урона. Здесь используется CoefficientDamage что бы в моде можно было изменить формулу расчёта полного / неполного урона.
На данном сайте используются файлы cookie, чтобы персонализировать контент и сохранить Ваш вход в систему, если Вы зарегистрируетесь.
Продолжая использовать этот сайт, Вы соглашаетесь на использование наших файлов cookie.