1. Уважаемые гости и новички, приветствуем Вас на нашем форуме
    Здесь вы можете найти ответы практически на все свои вопросы о серии игр «Готика» (в том числе различных модах на нее), «Ведьмак», «Ризен», «Древние свитки», «Эра дракона» и о многих других играх. Можете также узнать свежие новости о разработке новых проектов, сыграть в увлекательные ФРПГ, восхититься творчеством наших форумчан, либо самим показать, что вы умеете. Ну и наконец, можете обсудить общие увлечения или просто весело пообщаться с посетителями «Таверны».

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!
    Скрыть объявление
  2. Форум аддона "Возвращение" 2.0:
    — Обсудить игру, почитать о прохождениях и/или разрешить свои вопросы по игре вы можете в одной из тем одноименного форума. Посетить...
    — Прочитать историю изменения и/или скачать последнюю версию аддона "Возвращение", вы можете на страницах наших ресурсов. Скачать...
    Скрыть объявление

Важно Тутор по созданию стрелы массового поражения

Тема в разделе "Скриптинг", создана пользователем MEG@VOLT, 8 дек 2014.

Статус темы:
Закрыта.
  1. MEG@VOLT

    MEG@VOLT ★★★★★★★
    Модератор

    Регистрация:
    24 мар 2006
    Сообщения:
    1.652
    Благодарности:
    721
    Баллы:
    290
    Пишу этот тутор, чтобы показать, что есть ещё порох в пороховницах Готы, и можно найти интересные скрытые от глаз вещи.
    Итак, приступим.

    Цель: создать тип заряда лука - Взрывная стрела
    Особенности заряда: При попадании в жертву окружающие НПСы,находящиеся на расстоянии 6 метров,получают осколочный магический урон в 40 пунктов.
    И на них проигрывается эффект горения.
    (Это очень похоже на действие заклинания Огненный шторм
    Также сама жертва получает дополнительный магический урон в 50 пунктов.
    Пункт 1.
    Создайте новую инстанцию стрелы, например, с именем ItRw_Addon_ExplosiveArrow. Можно просто скопировать обычную стрелу и переименовать.

    Пункт 2.
    Во-первых, надо прописать визуальный эффект "огненного шторма" при попадании в жертву. Для этого создадим маленькую функцию:
    Код:
    func void B_ArrowBonusDamage(var C_Npc oth,var C_Npc slf)
    {
    var C_Item readyweap; //Текущее оружие в руках
    readyweap = Npc_GetReadiedWeapon(oth); //Текущее оружие в руках
    //Если типом заряда текущего оружия является "Взрывная стрела", то
    if(readyweap.munition == ItRw_Addon_ExplosiveArrow)
    {
    Проигрываем на цель эффект "огненного шторма"
    Wld_PlayEffect("spellFX_Firestorm_SPREAD&quot ;,slf,slf,0,0,0,FALSE);
    Далее делаем проверку, является ли цель бессмертным НПСом.
    if(slf.flags == 0) //на бессмертных не действует
    {
    Помимо физического урона, расчитанного движком, добавляется урон магией.
    if(slf.protection[PROT_MAGIC] < 50)
    {
    slf.attribute[ATR_HITPOINTS] = slf.attribute[ATR_HITPOINTS] - (50 - slf.protection[PROT_MAGIC]);
    };
    };
    Не забываем и про самого стрелка, если он находится в зоне радиуса осколков.
    Наносим стрелку осколочный урон в 40 магических пунктов и проигрываем эффект горения.
    if(Npc_GetDistToNpc(slf,oth) <= 600)
    {
    Wld_PlayEffect("VOB_MAGICBURN",oth,oth,0 ,0,0,FALSE);
    if(slf.flags == 0) //на бессмертных не действует
    {
    if(slf.protection[PROT_MAGIC]) < 40)
    {
    slf.attribute[ATR_HITPOINTS] = slf.attribute[ATR_HITPOINTS] - (40 - slf.protection[PROT_MAGIC]);
    };
    };
    Если осколочный урон окажется фатальным, то чтобы избежать глюков, проигрываем анимацию смерти.
    if(oth.attribute[ATR_HITPOINTS] <= 0)
    {
    AI_PlayAni(oth,"T_DEAD"
    };
    };
    };
    Даем опыт герою, если противник умрёт после эффекта стрелы.
    if((slf.attribute[ATR_HITPOINTS] <= 0) && Npc_IsPlayer(oth))
    {
    B_GivePlayerXP(slf.level*XP_PER_VICTORY);
    };
    };
    
    
    И Добавим наше творение в начало функции B_AssessDamage - описывающую восприятие повреждения жертвы.
    Код:
    func void B_AssessDamage()
    {
    B_ArrowBonusDamage(other,self);
    ...Продолжение...
    };
    Пункт 2.
    И Самый главный. Как заставить осколки нанести урон окружающим НПСам.
    Моей первой попыткой было использование функции :
    void AI_SetNpcsToState (c_npc self, func aiStateFunc, int radius); - переводит всех НПС, находящихся от НПС self на расстоянии radius сантиметров, в соответствующее состояние, описанное функцией aiStateFunc.
    Этой функцией aiStateFunc я сделал состояние ZS_Fire. НЕ буду приводить её ввиду недоделанности.
    Смысл в том её был, что после попадания взрывной стрелы все НПСы в радиусе поражения осколков получали урон и переходили в состояние горения.
    Но главной проблемой при тесте было то, что эта встроенная функция AI_SetNpcsToState почему-то не всегда срабатывала, да и из состояния ZS_Fire НПСы преходили как-то неграмотно. Благо мне в голову вовремя пришло другое решение.
    Но вы можете попробывать и вышеуказанный способ.

    Так что же пришло мне в голову. Покажу сразу решение.
    У всех НПСов есть восприятия. Описаны они в функции Perception.d
    Их описание указано в туторе Vam'а. Так что не буду на этом останавливаться.
    Так вот. Если посмотреть внимательно, то у монстров есть такое восприятие как PERC_ASSESSOTHERSDAMAGE.
    Код:
    func void Perception_Set_Monster_Rtn()
    {
    Npc_PercEnable(self, PERC_ASSESSOTHERSDAMAGE, B_MM_AssessOthersDamage);
    };
    Этим восприятием является реакция на урон "жертвы - соседа". В нашем случае жертвой-соседом будет цель попадания стрелы.

    И описана она функцией B_MM_AssessOthersDamage.
    У человека такой функции нет ни в наборе стандартых восприятий, ни в минимальном.
    Ну чтож активируем эту функцию, добавив строчку в минимальный и нормальный набор восприятий.
    Код:
    func void Perception_Set_Normal()
    {
    self.senses = SENSE_HEAR | SENSE_SEE;
    self.senses_range = PERC_DIST_ACTIVE_MAX;
    if(Npc_KnowsInfo(self,1) || C_NpcIsGateGuard(self))
    {
    Npc_SetPercTime(self,0.3);
    }
    else
    {
    Npc_SetPercTime(self,1);
    };
    Npc_PercEnable(self,PERC_ASSESSPLAYER,B_AssessPlay er);
    Npc_PercEnable(self,PERC_ASSESSENEMY,B_AssessEnemy );
    Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic );
    Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_AssessDama ge);
    ******Добавил*****
    Npc_PercEnable(self,PERC_ASSESSOTHERSDAMAGE,B_Asse ssOthersDamage);
    ******************
    Npc_PercEnable(self,PERC_ASSESSMURDER,B_AssessMurd er);
    Npc_PercEnable(self,PERC_ASSESSTHEFT,B_AssessTheft );
    Npc_PercEnable(self,PERC_ASSESSUSEMOB,B_AssessUseM ob);
    Npc_PercEnable(self,PERC_ASSESSENTERROOM,B_AssessP ortalCollision);
    Npc_PercEnable(self,PERC_ASSESSTHREAT,B_AssessThre at);
    Npc_PercEnable(self,PERC_DRAWWEAPON,B_AssessDrawWe apon);
    Npc_PercEnable(self,PERC_ASSESSFIGHTSOUND,B_Assess FightSound);
    Npc_PercEnable(self,PERC_ASSESSQUIETSOUND,B_Assess QuietSound);
    Npc_PercEnable(self,PERC_ASSESSWARN,B_AssessWarn);
    Npc_PercEnable(self,PERC_ASSESSTALK,B_AssessTalk);
    Npc_PercEnable(self,PERC_MOVEMOB,B_MoveMob);
    };
    
    Код:
    func void Perception_Set_Minimal()
    {
    self.senses = SENSE_HEAR | SENSE_SEE;
    self.senses_range = PERC_DIST_ACTIVE_MAX;
    Npc_PercEnable(self,PERC_ASSESSMAGIC,B_AssessMagic );
    Npc_PercEnable(self,PERC_ASSESSDAMAGE,B_AssessDama ge);
    ******Добавил*****
    Npc_PercEnable(self,PERC_ASSESSOTHERSDAMAGE,B_Asse ssOthersDamage);
    ******************
    Npc_PercEnable(self,PERC_ASSESSMURDER,B_AssessMurd er);
    Npc_PercEnable(self,PERC_ASSESSTHEFT,B_AssessTheft );
    Npc_PercEnable(self,PERC_ASSESSUSEMOB,B_AssessUseM ob);
    Npc_PercEnable(self,PERC_ASSESSENTERROOM,B_AssessP ortalCollision);
    };
    Теперь надо описать саму функцию B_AssessOtherDamage.
    Код:
    func void B_AssessOthersDamage()
    {
    //************************************************** **************************
    var C_Item readyweap;
    readyweap = Npc_GetReadiedWeapon(other);
    //************************************************** ****************************
    //************************************************** ****************************
    if(readyweap.munition == ItRw_Addon_ExplosiveArrow)
    {
    if((Npc_GetDistToNpc(self,victim) <= 600) && (readyweap.munition == ItRw_Addon_ExplosiveArrow))
    {
    Wld_PlayEffect("VOB_MAGICBURN",self,self ,0,0,0,FALSE);
    if(slf.flags == 0) //на бессмертных не действует
    {
    if(slf.protection[PROT_MAGIC]) < 40
    {
    slf.attribute[ATR_HITPOINTS] = slf.attribute[ATR_HITPOINTS] - (40 - slf.protection[PROT_MAGIC]);
    };
    };
    if((self.attribute[ATR_HITPOINTS] <= 0) && Npc_IsPlayer(other))
    {
    B_GivePlayerXP((self.level * XP_PER_VICTORY));
    };
    if(self.aivar[AIV_PARTYMEMBER] == TRUE)
    {
    if(Npc_IsPlayer(victim))
    {
    Npc_ClearAIQueue(self);
    B_ClearPerceptions(self);
    Npc_SetTarget(self,other);
    AI_StartState(self,ZS_Attack,0,""
    return;
    };
    if(Npc_IsPlayer(other) && !Npc_IsDead(victim))
    {
    Npc_ClearAIQueue(self);
    B_ClearPerceptions(self);
    Npc_SetTarget(self,victim);
    AI_StartState(self,ZS_Attack,0,""
    return;
    };
    };
    if(Wld_GetGuildAttitude(self.guild,other.guild) != ATT_FRIENDLY)
    {
    Npc_ClearAIQueue(self);
    B_ClearPerceptions(self);
    //Npc_SetTarget(self,other);
    B_Attack(self,other,AR_ReactToDamage,0);
    return;
    };
    };
    if(Npc_IsInState(self,ZS_Attack))
    {
    return;
    };
    };
    return;
    };
    
    
    На этом вроде бы кажется всё... Но не всё так быстро.
    Первым глюком при тесте будет то, что если физический урон стрелы окажется выше текущих хитпойнтов жертвы стрелы, то визуальный эффект огненного шторма просто не воспроизведется. Смысл заключается в том, что жертва сразу перейдёт в состояние ZS_Dead и функция B_AssessDamage просто не сработает. Это одна из ошибок, которую допускают некоторые модостроители.
    Добавим следующие строчки в функцию ZS_Dead
    Код:
    func void ZS_Dead()
    {
    var C_Item readyweap;
    readyweap = Npc_GetReadiedWeapon(other);
    ......................Продолжение...... ..................
    if(readyweap.munition == ItRw_Addon_ExplosiveArrow)
    {
    Wld_PlayEffect("spellFX_Firestorm_SPREAD&quot ;,self,self,0,0,0,FALSE);
    if(Npc_GetDistToNpc(self,other) <= 600)
    {
    Wld_PlayEffect("VOB_MAGICBURN",other,oth er,0,0,0,FALSE);
    if(other.flags == 0) //на бессмертных не действует
    {
    if(other.protection[PROT_MAGIC]) < 40)
    {
    other.attribute[ATR_HITPOINTS] = other.attribute[ATR_HITPOINTS] - (40 - other.protection[PROT_MAGIC]);
    };
    };
    Если осколочный урон окажется фатальным, то чтобы избежать глюков, проигрываем анимацию смерти.
    if(other.attribute[ATR_HITPOINTS] <= 0)
    {
    AI_PlayAni(other,"T_DEAD"
    };
    };
    };
    .................................................. ...........
    }
    Следующим косяком при тесте окажется то , в ситуации, когда рядом с жертвой-целью в радиусе осколков окажется человек и физический урон стрелы окажется выше текущих хитпойнтов жертвы стрелы, то осколочный урон не достигнет человека.
    Этот косяк исправляется тем, что практически всё тело функции добавляется в B_AssessMurder - функцию, описывающую реакцию человека на убийство соседа.
    Код:
    func void B_AssessMurder()
    {
    //************************************************** ********
    var C_Item readyweap;
    readyweap = Npc_GetReadiedWeapon(other);
    //*********************************************
    if(Hlp_GetInstanceID(self) == Hlp_GetInstanceID(other))
    {
    return;
    };
    if((Npc_GetDistToNpc(self,other) > PERC_DIST_INTERMEDIAT) && (Npc_GetDistToNpc(self,victim) > PERC_DIST_INTERMEDIAT))
    {
    return;
    };
    if((Npc_GetHeightToNpc(self,other) > PERC_DIST_HEIGHT) && (Npc_GetHeightToNpc(self,victim) > PERC_DIST_HEIGHT))
    {
    return;
    };
    if(!Npc_CanSeeNpcFreeLOS(self,other) && !Npc_CanSeeNpcFreeLOS(self,victim))
    {
    return;
    };
    //*****************НАШе ТЕЛО**************************************
    if(readyweap.munition == ItRw_Addon_ExplosiveArrow)
    {
    if((Npc_GetDistToNpc(self,victim) <= 600) && (readyweap.munition == ItRw_Addon_ExplosiveArrow))
    {
    Wld_PlayEffect("VOB_MAGICBURN",self,self ,0,0,0,FALSE);
    if(self.flags == 0) //на бессмертных не действует
    {
    if(self.protection[PROT_MAGIC]) < 40)
    {
    self.attribute[ATR_HITPOINTS] = slf.attribute[ATR_HITPOINTS] - (40 - slf.protection[PROT_MAGIC]);
    };
    };
    if((self.attribute[ATR_HITPOINTS] <= 0) && Npc_IsPlayer(other))
    {
    B_GivePlayerXP((self.level * XP_PER_VICTORY));
    };
    };
    };
    //************************************************** **********************
    if(B_AssessEnemy())
    {
    return;
    };
    };
    Осталось только не забыть монстров и добавить в функцию B_MM_AssessDamage некоторые дополнения и одно исправление.
    Код:
    func void B_MM_AssessOthersDamage()
    {
    //************************************************** ******************
    var C_Item readyweap;
    readyweap = Npc_GetReadiedWeapon(other);
    
    //************************************************** ********************
    if((Npc_GetDistToNpc(self,victim) > PERC_DIST_INTERMEDIAT) && (Npc_GetDistToNpc(self,other) > PERC_DIST_INTERMEDIAT))
    {
    return;
    };
    if(!Npc_CanSeeNpcFreeLOS(self,victim))
    {
    return;
    };
    //Закоментированно мной и изменено в связи с тем, что у монстров нет уникальных имен и осколки не сработают на соседа, если ID соседа будет равно ID жертвы.
    /*
    if((Hlp_GetInstanceID(victim) == Hlp_GetInstanceID(self)) || (Hlp_GetInstanceID(other) == Hlp_GetInstanceID(self)))
    {
    return;
    };
    */
    //Измененный вариант
    if(Hlp_GetInstanceID(other) == Hlp_GetInstanceID(self))
    {
    return;
    };
    //**************************************
    if((Npc_GetDistToNpc(self,victim) <= 600) && (readyweap.munition == ItRw_Addon_ExplosiveArrow))
    {
    Wld_PlayEffect("VOB_MAGICBURN",self,self ,0,0,0,FALSE);
    if(slf.flags == 0) //на бессмертных не действует
    {
    if(slf.protection[PROT_MAGIC]) < 40
    {
    slf.attribute[ATR_HITPOINTS] = slf.attribute[ATR_HITPOINTS] - (40 - slf.protection[PROT_MAGIC]);
    };
    };
    if((self.attribute[ATR_HITPOINTS] <= 0) && Npc_IsPlayer(other))
    {
    B_GivePlayerXP((self.level * XP_PER_VICTORY));
    };
    };
    ................................
    };
    
    
    ************************************************** *****************************
    ИТОГО
    Основной целью тутора было не написание за вас скриптов, а показать интересный способ и недопущение многих ошибок. Если встретите какие-то мелкие ошибки и недочеты, то это скорей всего связано с написанием самого тутора.
    Исходя из всего вышеизложенного вы можете создать не только взрывные стрелы, но всё, что может вообразить ваш мозг, как то:
    Стрела с эффектом ледяной волны, Стрела с эффектом Огненного дождя, Волны смерти и любого другого средства массового поражения.
    Дерзайте.Если что-то непонятно по тексту и самому тутору, спрашивайте...
    За сим откланиваюсь...

    P.S. Особая благодарность выражается Lev-Lion'у за предоставленную идею и совместную работу и тестирование взрывной стрелы.

    Автор - redleha
     
Статус темы:
Закрыта.

Поделиться этой страницей