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

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!
  • Друзья, спешите принять участие в поэтическом конкурсе "Весенние поэты 2024"!
    Ждем именно вас!

    Ссылка на конкурсную тему - тык

Вопросы по скриптингу

MaGoth

★★★★★★★★★★★
Администратор
Регистрация
7 Янв 2003
Сообщения
19.371
Благодарности
7.803
Баллы
995
  • Первое сообщение
  • #1
Прежде чем задавать вопросы, ознакомьтесь с документацией..
1) Читать онлайн
2) Архив с офлайн-версией(chm) во вложении
 

Вложения

  • Vam_tutor.rar
    171,6 KB · Просмотры: 514
Последнее редактирование модератором:

MW 7


Модостроитель
Регистрация
26 Мар 2004
Сообщения
1.930
Благодарности
929
Баллы
275
тогда попробуй rand не обнулять в начале функции
var int rand; //rand = 0;

RPD, а стрелка уникальная? только Никита ей пользуется?
 

MW 7


Модостроитель
Регистрация
26 Мар 2004
Сообщения
1.930
Благодарности
929
Баллы
275

RPD

Участник форума
Регистрация
13 Ноя 2023
Сообщения
76
Благодарности
2
Баллы
15
RPD, а стрелка уникальная? только Никита ей пользуется?
да
Пост автоматически объединён:

тогда попробуй rand не обнулять в начале функции
var int rand; //rand = 0;

RPD, а стрелка уникальная? только Никита ей пользуется?
не, ничего не меняется. Все равно как берет в руки призыв - сразу начинает его бесконечно перебирать (как будто система считает, что каст уже завершен и просто сразу вызывает return true), либо тормозить и держать в руках просто.
 

MW 7


Модостроитель
Регистрация
26 Мар 2004
Сообщения
1.930
Благодарности
929
Баллы
275
спелл_каст_стрелка
self.aivar[AIV_SelectSpell] = Hlp_Random(100) + 1;

в Spell_Cast_SummonNikita
self.aivar[AIV_SelectSpell] = Hlp_Random(100) + 1;


***
попробуй упростить
Код:
if  slf.aivar[AIV_SelectSpell] > 80
            {
                B_ReadySpell(slf,SPL_SummonNikita,13);
                return true;
            };
        };
     
        B_ReadySpell(slf,SPL_PierceArrow,0)

        return true;
 
Последнее редактирование:

RPD

Участник форума
Регистрация
13 Ноя 2023
Сообщения
76
Благодарности
2
Баллы
15
спелл_каст_стрелка
self.aivar[AIV_SelectSpell] = Hlp_Random(100) + 1;

в Spell_Cast_SummonNikita
self.aivar[AIV_SelectSpell] = Hlp_Random(100) + 1;


***
попробуй упростить
Код:
if  slf.aivar[AIV_SelectSpell] > 80
            {
                B_ReadySpell(slf,SPL_SummonNikita,13);
                return true;
            };
        };
    
        B_ReadySpell(slf,SPL_PierceArrow,0)

        return true;
точно также все
 

MW 7


Модостроитель
Регистрация
26 Мар 2004
Сообщения
1.930
Благодарности
929
Баллы
275
RPD, скинь свой скомпилированный датник
 

MW 7


Модостроитель
Регистрация
26 Мар 2004
Сообщения
1.930
Благодарности
929
Баллы
275
RPD, переписал. всё работает. файл в скрепке. Что у тебя там было в спеллкаст не знаю, поэтому сделал их лайтовыми для теста.

Daedalus:
func int B_SelectSpellNikita(var C_NPC slf, var C_NPC oth)
{   
    if (slf.guild != GIL_NIK)       { return false; };
    if (Npc_IsDrawingWeapon(slf))   { return TRUE;/*nichts tun wenn gerade am waffe ziehen*/    };
    
    var int rand;
    
    if (slf.aivar[AIV_SelectSpell] != rand)
    {
        //  Without your television
        rand = Hlp_Random(100) + 1;
        slf.aivar[AIV_SelectSpell] = rand;
    };
    
    if (rand > 95)
    {
        return B_ReadyRune(slf, oth, ItRu_SumNik); 
    };
    
    if (rand > 80)
    {   
        var c_npc npc;
        npc = Hlp_GetNpc(Nikita_for_Npc);
        
        if (!Hlp_IsValidNpc(npc))
        {
            // можно добавить доп проверки на расстояние или смерть
            return B_ReadyRune(slf, oth, ItRu_SumNik); 
        };
    };
    
    return B_ReadyRune(slf, oth, ItRu_PierceArrow);   
};
1702947488669.jpeg

MEG@VOLT, в Spell_Cast_Summon*** перезаписывается self после вызова Wld_SpawnNpcRange , поэтому self.aivar[AIV_SelectSpell] += 1; надо обновлять ДО Wld_Spawn ;-)

ПРАВИЛЬНОНЕПРАВИЛНО
Daedalus:
func void Spell_Cast_SummonSkeleton()
{
    if (Npc_GetActiveSpellIsScroll(self))
    {
        self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
    }
    else
    {
        self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_SummonSkeleton;
    };
 
    self.aivar[AIV_SelectSpell] += 1;
 
    if (Npc_IsPlayer(self))
    {    
        Wld_SpawnNpcRange    (self,    Summoned_Skeleton,            1,    500);
    }
    else
    {
        Wld_SpawnNpcRange    (self,    Skeleton,            1,    500);
    };
};
Daedalus:
func void Spell_Cast_SummonDemon()
{
    if (Npc_GetActiveSpellIsScroll(self))
    {
        self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
    }
    else
    {
        self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_SummonDemon;
    };

    if (Npc_IsPlayer(self))
    {    
        Wld_SpawnNpcRange (self, Summoned_Demon, 1, 1000);
    }
    else
    {
        Wld_SpawnNpcRange (self, Demon, 1, 1000);
    };
 
    self.aivar[AIV_SelectSpell] += 1;
};
 

Вложения

  • LaFemmeNikita.zip
    1,2 KB · Просмотры: 2
Последнее редактирование:

RPD

Участник форума
Регистрация
13 Ноя 2023
Сообщения
76
Благодарности
2
Баллы
15
RPD, переписал. всё работает. файл в скрепке. Что у тебя там было в спеллкаст не знаю, поэтому сделал их лайтовыми для теста.

Daedalus:
func int B_SelectSpellNikita(var C_NPC slf, var C_NPC oth)
{
    if (slf.guild != GIL_NIK)       { return false; };
    if (Npc_IsDrawingWeapon(slf))   { return TRUE;/*nichts tun wenn gerade am waffe ziehen*/    };
 
    var int rand;
 
    if (slf.aivar[AIV_SelectSpell] != rand)
    {
        //  Without your television
        rand = Hlp_Random(100) + 1;
        slf.aivar[AIV_SelectSpell] = rand;
    };
 
    if (rand > 95)
    {
        return B_ReadyRune(slf, oth, ItRu_SumNik);
    };
 
    if (rand > 80)
    {
        var c_npc npc;
        npc = Hlp_GetNpc(Nikita_for_Npc);
    
        if (!Hlp_IsValidNpc(npc))
        {
            // можно добавить доп проверки на расстояние или смерть
            return B_ReadyRune(slf, oth, ItRu_SumNik);
        };
    };
 
    return B_ReadyRune(slf, oth, ItRu_PierceArrow);
};
Посмотреть вложение 119854

MEG@VOLT, в Spell_Cast_Summon*** перезаписывается self после вызова Wld_SpawnNpcRange , поэтому self.aivar[AIV_SelectSpell] += 1; надо обновлять ДО Wld_Spawn ;-)

ПРАВИЛЬНОНЕПРАВИЛНО
Daedalus:
func void Spell_Cast_SummonSkeleton()
{
    if (Npc_GetActiveSpellIsScroll(self))
    {
        self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
    }
    else
    {
        self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_SummonSkeleton;
    };

    self.aivar[AIV_SelectSpell] += 1;

    if (Npc_IsPlayer(self))
    {
        Wld_SpawnNpcRange    (self,    Summoned_Skeleton,            1,    500);
    }
    else
    {
        Wld_SpawnNpcRange    (self,    Skeleton,            1,    500);
    };
};
Daedalus:
func void Spell_Cast_SummonDemon()
{
    if (Npc_GetActiveSpellIsScroll(self))
    {
        self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_Scroll;
    }
    else
    {
        self.attribute[ATR_MANA] = self.attribute[ATR_MANA] - SPL_Cost_SummonDemon;
    };

    if (Npc_IsPlayer(self))
    {
        Wld_SpawnNpcRange (self, Summoned_Demon, 1, 1000);
    }
    else
    {
        Wld_SpawnNpcRange (self, Demon, 1, 1000);
    };

    self.aivar[AIV_SelectSpell] += 1;
};

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

Я хотел бы сделать так, чтобы при закликивании НПС он мог что то типа взорваться, нанести урон и оглушить ненадолго противников. То есть отслеживать тот момент когда непись получает кучу ударов и сам ни одного не наносит, ну или редко. И вызывать функцию этого взрыва. Можно как то реализовать?
 

MW 7


Модостроитель
Регистрация
26 Мар 2004
Сообщения
1.930
Благодарности
929
Баллы
275
Можно как то реализовать?
тут есть две части:
  • первая это скрипт "взрыва", тут проще всего прописать какое то простое zs_состояние для жертв и перевести их в него через AI_SetNpcsToState.
  • второе это какой то скрипт-счётчик который будет понимать что НПС "закликан" и вызвать скрипт "взрыва". что такое "заклинивание" я не знаю так как ни играл ни разу в Готику с мышкой.
 

RPD

Участник форума
Регистрация
13 Ноя 2023
Сообщения
76
Благодарности
2
Баллы
15
тут есть две части:
  • первая это скрипт "взрыва", тут проще всего прописать какое то простое zs_состояние для жертв и перевести их в него через AI_SetNpcsToState.
  • второе это какой то скрипт-счётчик который будет понимать что НПС "закликан" и вызвать скрипт "взрыва". что такое "заклинивание" я не знаю так как ни играл ни разу в Готику с мышкой.
Закликивание - это я имею ввиду, что когда НПС просто сам не может ответить ударом, так как ему не дают - наносят кучу ударов враги. Сразу толпой.
 

MW 7


Модостроитель
Регистрация
26 Мар 2004
Сообщения
1.930
Благодарности
929
Баллы
275
в B_AssessDamage считать кол-во ударов записывая их в отдельный аивер НПС, а в ZS_Attack_Loop данный аивер обнулять каждые условно две секунды, если при этом цифра была высокая то запускать скрипт "взрыва".
 

RPD

Участник форума
Регистрация
13 Ноя 2023
Сообщения
76
Благодарности
2
Баллы
15
в B_AssessDamage считать кол-во ударов записывая их в отдельный аивер НПС, а в ZS_Attack_Loop данный аивер обнулять каждые условно две секунды, если при этом цифра была высокая то запускать скрипт "взрыва".
Хорошая идея, попробую придумать что-то
 

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.339
Благодарности
3.183
Баллы
525
почти полностью решило проблему застреваний при касте
Могу порекомендовать приём, помогающий найти причину не такого результата работы функций ИИ, какого тебе хотелось бы. Поскольку функции ИИ работают очень быстро, а внутри этих функций есть масса проверяемых условий, как правило, бывает трудно понять, какое из этих условий мешает получить нужный результат. И даже, в какой функции это условие находится. Разобраться помогает временное введение в функции ИИ строчек с выводом текстовых сообщений на экран. Это может быть название функции, чтобы убедиться, вызывается ли функция вообще. Это может быть надпись типа "Условие 1", "Условие 2" и т. д., чтобы понять, какое условие срабатывает, и это приводит к завершению работы функции. Это может быть значение аргументов функции или переменных. Например, при выборе заклинания в Г2 меняется значение переменной, определяющее очередь заклинаний. Зависание может быть связано с конкретным значением этой переменной.

данный аивер обнулять каждые условно две секунды, если при этом цифра была высокая то запускать скрипт "взрыва"
Возможно, здесь получится задействовать таймер отказа от диалога RefuseTalk.
 

RPD

Участник форума
Регистрация
13 Ноя 2023
Сообщения
76
Благодарности
2
Баллы
15
Могу порекомендовать приём, помогающий найти причину не такого результата работы функций ИИ, какого тебе хотелось бы. Поскольку функции ИИ работают очень быстро, а внутри этих функций есть масса проверяемых условий, как правило, бывает трудно понять, какое из этих условий мешает получить нужный результат. И даже, в какой функции это условие находится. Разобраться помогает временное введение в функции ИИ строчек с выводом текстовых сообщений на экран. Это может быть название функции, чтобы убедиться, вызывается ли функция вообще. Это может быть надпись типа "Условие 1", "Условие 2" и т. д., чтобы понять, какое условие срабатывает, и это приводит к завершению работы функции. Это может быть значение аргументов функции или переменных. Например, при выборе заклинания в Г2 меняется значение переменной, определяющее очередь заклинаний. Зависание может быть связано с конкретным значением этой переменной.


Возможно, здесь получится задействовать таймер отказа от диалога RefuseTalk.
А как именно работает этот RefuseTalk?
 

RPD

Участник форума
Регистрация
13 Ноя 2023
Сообщения
76
Благодарности
2
Баллы
15
Я так понимаю, что B_AssessDamage() вызывается после каждого удара, тогда проблем с отслеживанием нет, но как запускать некий таймер? И сбрасывать. Есть ли какие-то функции подходящие?
 
Последнее редактирование:

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.339
Благодарности
3.183
Баллы
525
А как именно работает этот RefuseTalk?
Из справки Соурсера:
Daedalus:
// установить счетчик отказа от диалога для НПС npc на timeSec секунд.
func void Npc_SetRefuseTalk(var C_NPC npc, var int timeSec);
 
// проверить, истек ли счетчик отказа от диалога для НПС npc,
// возвращает TRUE, если счетчик не истек, иначе – FALSE.
func int Npc_RefuseTalk(var C_NPC npc);

Я так понимаю, что B_AssessDamage() вызывается после каждого удара
Возможно, вызывается после блокирования удара. Нужно проверять.

но как запускать некий таймер? И сбрасывать.
Получил удар, проверил, установлен ли таймер. Если таймер не установлен, устанавливаешь таймер на 2 (к примеру) секунды, счётчик ударов равен 1. Получил следующий удар, проверил, установлен ли таймер. Если таймер установлен, снова устанавливаешь таймер на 2 секунды, значение счётчика ударов увеличиваешь на 1, проверяешь, не достигло ли его значение нужной величины, при которой происходит реакция на беспрерывную атаку. Если нужное значение счётчика ударов достигнуто, сбрасываешь значение счётчика в 0, устанавливаешь таймер в 0, запускаешь реакцию. Как-то так.
 

RPD

Участник форума
Регистрация
13 Ноя 2023
Сообщения
76
Благодарности
2
Баллы
15
Из справки Соурсера:
Daedalus:
// установить счетчик отказа от диалога для НПС npc на timeSec секунд.
func void Npc_SetRefuseTalk(var C_NPC npc, var int timeSec);
 
// проверить, истек ли счетчик отказа от диалога для НПС npc,
// возвращает TRUE, если счетчик не истек, иначе – FALSE.
func int Npc_RefuseTalk(var C_NPC npc);


Возможно, вызывается после блокирования удара. Нужно проверять.


Получил удар, проверил, установлен ли таймер. Если таймер не установлен, устанавливаешь таймер на 2 (к примеру) секунды, счётчик ударов равен 1. Получил следующий удар, проверил, установлен ли таймер. Если таймер установлен, снова устанавливаешь таймер на 2 секунды, значение счётчика ударов увеличиваешь на 1, проверяешь, не достигло ли его значение нужной величины, при которой происходит реакция на беспрерывную атаку. Если нужное значение счётчика ударов достигнуто, сбрасываешь значение счётчика в 0, устанавливаешь таймер в 0, запускаешь реакцию. Как-то так.
Написал черновой вариант для проверки. Вроде работает.

Daedalus:
func int B_BlastTimer(var C_NPC slf, var C_NPC oth)
{
    if(!Npc_RefuseTalk(slf))
    {
        Npc_SetRefuseTalk(slf, 5);
   
        slf.aivar[AIV_BlastCount] = 0;
    }
    else
    {
        if(slf.aivar[AIV_BlastCount] == 15)
        {
            oth.attribute[ATR_HITPOINTS] = 0;
            PrintScreen("Сработал взрыв!",-1,-1,"FONT_OLD_20_WHITE.TGA",1);
       
            slf.aivar[AIV_BlastCount] = 0;
            Npc_SetRefuseTalk(slf, 0);
       
            return true;
        };
    };
 
    slf.aivar[AIV_BlastCount] += 1;
 
    return true;
};
 
Последнее редактирование:

RPD

Участник форума
Регистрация
13 Ноя 2023
Сообщения
76
Благодарности
2
Баллы
15
Не понятно каким образом проигрывать анимацию отброса всех атакующих. То есть сразу чтобы все отлетали, как при касте кулака ветра или удара голема
 
Сверху Снизу