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

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

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

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

MaGoth

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

Вложения

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

Vlad_Torop

Участник форума
Регистрация
2 Май 2014
Сообщения
868
Благодарности
498
Баллы
230
надо что-то изменить в восприятиях AssessPlayer и/или AssessTalk?
В C_RefuseTalk(AI\Human\C_Human) пропиши:
if((other.guild == GIL_Sheep) && ((Hlp_GetInstanceID(slf) != Hlp_GetInstanceID(Лобарт) || (Hlp_GetInstanceID(slf) != Hlp_GetInstanceID(Онар)))
{
return TRUE;
};
 
Последнее редактирование:

Dimus

★★★★★★★★★
Супермодератор
Регистрация
19 Июл 2010
Сообщения
5.562
Благодарности
4.153
Баллы
915
Спасибо тебе за подсказку - теперь NPC не начинают разговор с ГГ в облике овцы. Заодно исправил замеченную недоработку (вместо self должно быть slf):
Код:
func int C_RefuseTalk(var C_Npc slf,var C_Npc oth)
{
    if(Npc_RefuseTalk(slf) && C_NpcIsGateGuard(slf) && (slf.aivar[AIV_Guardpassage_Status] == GP_NONE))
    {
        return TRUE;
    };
    if((slf.aivar[AIV_TalkedToPlayer] == FALSE) && (Npc_GetDistToWP(slf,slf.wp) > 500))
    {
        if(Hlp_GetInstanceID(slf) == Hlp_GetInstanceID(Lothar))
        {
            return TRUE;
        };
    };
    if(C_PlayerHasFakeGuild(slf,oth) && (slf.flags != NPC_FLAG_IMMORTAL))
    {
        return TRUE;
    };
    if(oth.guild == GIL_SHEEP)
    {
        return TRUE;
    };
    return FALSE;
};
 
Последнее редактирование:

Gor

Участник форума
Регистрация
26 Дек 2009
Сообщения
977
Благодарности
543
Баллы
245
Подскажите, в чем ошибка. Почему переменной типа testclass нельзя присвоить инстанцию этого типа? К ВАМу не отсылайте, т.к.
instance - пока сказать нечего. (с)VAM
Код:
class testclass
{
    var int m1;
};
instance testinst(testclass)
{
    m1 = 1;
};

var testclass tci;

func void f1()
{
    tci = testinst;
};
 

HeDeDe

Участник форума
Регистрация
17 Авг 2009
Сообщения
203
Благодарности
79
Баллы
180
Подскажите, в чем ошибка. Почему переменной типа testclass нельзя присвоить инстанцию этого типа? К ВАМу не отсылайте, т.к.
Во-первых, свои классы без помощи хаков (т.е. Икаруса) создавать нельзя.
Во-вторых — "var Class" является указателем на экземпляр класса, в то время как instance — это что-то вроде конструктора класса. Instance — это на самом деле функция, которая инициализирует экземпляр класса.
 

Gor

Участник форума
Регистрация
26 Дек 2009
Сообщения
977
Благодарности
543
Баллы
245
Во-первых, свои классы без помощи хаков (т.е. Икаруса) создавать нельзя.
Это вопрос по возможностям языка, не для скриптинга игры.
Во-вторых — "var Class" является указателем на экземпляр класса, в то время как instance — это что-то вроде конструктора класса. Instance — это на самом деле функция, которая инициализирует экземпляр класса.
Ага, тогда прототип это как бы базовый конструктор, который по умолчанию вызывает instance первой командой.
Еще вот вопрос на примере:
c_npc Hlp_GetNpc (int instanceName); - возвращает ссылку на НПС, принадлежащему к типу instanceName.
Код:
class c_npc {...};
instance BDT_1090_Addon_Raven(Npc_Default){...};
var c_npc Raven;
Raven = Hlp_GetNpc(BDT_1090_Addon_Raven);
Здесь переменной Raven типа c_npc присваивается ссылка на инстанцию BDT_1090_Addon_Raven того же типа посредством функции движка. Выходит это единственный способ? Неожиданно...
 

HeDeDe

Участник форума
Регистрация
17 Авг 2009
Сообщения
203
Благодарности
79
Баллы
180
Это вопрос по возможностям языка, не для скриптинга игры.
Сам язык не дает возможности создать экземпляр класса, это делается посредством обращения к функциям движка.

Ага, тогда прототип это как бы базовый конструктор, который по умолчанию вызывает instance первой командой.
Верно.

Здесь переменной Raven типа c_npc присваивается ссылка на инстанцию BDT_1090_Addon_Raven того же типа посредством функции движка. Выходит это единственный способ? Неожиданно...
На самом деле у инстанции два назначения: Помимо конструктора, инстанция хранит указатель на последний созданный класс.

Код:
zBOOL zCParser::CreateInstance(int instanceId, void* mem)
{
    zCPar_Symbol *symbol = symtab->GetSymbol(instanceId);
    if (!symbol || symbol->type != zPAR_TYPE_INSTANCE)
        return 0;

    // Запись указателя на эксземпляр класса
    symbol->SetOffset(symbol, int(mem));

    int func_offset;
    zCPar_Symbol::SetUseInstance(symbol);
    symbol->GetStackPos(func_offset, 0);

    // Вызов конструктора instance
    DoStack(func_offset);
    return 1;
}

Функция Hlp_GetNpc проверяет, ссылается ли указатель, записанный в instance, на класс oCNpc, и если да, возвращает его.
(На самом деле она сложнее, и похоже, принимает не только имя instance).
Код:
int Hlp_GetNpc()
{
    zCParser *parser = zCParser::GetParser();

    oCNpc* npc = nullptr;

    int instanceId;
    parser->GetParameter(instanceId);
    zCPar_Symbol* symbol = parser->GetSymbol(instanceId);

    if ( symbol ) {
        if (symbol->type == zPAR_TYPE_INSTANCE) {
            zCVob* adr = (zCVob*)symbol->GetInstanceAdr();
            npc = dynamic_cast<oCNpc*>(adr);
        }

        if (!npc) {
            zCWorld* world = ogame->GetGameWorld();
            npc = world->SearchVobByName(symbol->name);

            zCClassDef classdef = npc->GetClassDef();
            if (!zCObject::CheckInheritance(&oCNpc::classDef, classdef))
                npc = nullptr;
        }
    }

    zCParser::SetReturn(parser, npc);
    return 0;
}
 
Последнее редактирование:

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.331
Благодарности
3.178
Баллы
525
Кстати, господа-спецы, раз уж речь зашла о ссылках и подразумевает возможность заглядывания в движок. Поясните если можно, как происходит обработка ссылок, и как с ними правильно работать?

Вот, например, непись экипирован мечом определённой инстанции, а в его инвентаре лежит ещё такой же меч. С точки зрения движка это одна ссылка, или каждый из экземпляров будет иметь свою ссылку? Можно ли получить ссылку на предмет, который непись держит в руке, например, на горящий факел?
 

Gor

Участник форума
Регистрация
26 Дек 2009
Сообщения
977
Благодарности
543
Баллы
245
Вопрос: Правильно ли я понял создание объекта в игре?
Код:
// класс
class c_npc {};
// прототип (базовый конструктор объекта)
prototype default_npc {};
// инстанция (конструктор объекта)
instance BDT_1090_Addon_Raven {};
// создаем объект
Wld_InsertNpc(BDT_1090_Addon_Raven,"ADW_ADANOSTEMPEL_RAVEN_11");
// переменная, которая будет указывать на объект
var c_npc raven;
// получаем ссылку на объект
raven = Hlp_GetNpc(BDT_1090_Addon_Raven);
// теперь имеем доступ к полям объекта
 

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.331
Благодарности
3.178
Баллы
525
// получаем ссылку на объект raven = Hlp_GetNpc(BDT_1090_Addon_Raven); // теперь имеем доступ к полям объекта
Но это при условии, что данный непись уникален в игровом мире. В случае с неписями клонами (монстры) данный механизм получения ссылки имеет изъян.
 

Gor

Участник форума
Регистрация
26 Дек 2009
Сообщения
977
Благодарности
543
Баллы
245
Но это при условии, что данный непись уникален в игровом мире. В случае с неписями клонами (монстры) данный механизм получения ссылки имеет изъян.
Согласен, но объект все же создается функцией движка. Насколько я знаю ссылки на все созданные объекты находятся в дереве вобов и в списке. Доступ к ним скорее всего возможен с помощью икаруса и аст.
instance в игре имеет два понятия.
1) инстанция - экземпляр_класса - объект
2) инстанция - функция - конструктор
То что мы записываем как instance имя (){}; это фактически особого вида функция (конструктор), которая передается в функцию движка для создания объекта. Создание клонов лишь подтверждает это (инстанция (конструктор) одна, а клонов много). Прототип тоже особого вида функция. Ее вызывает инстанция первой командой байт-кода, если в инстанции указывали прототип.

Это мои предположения. Данных пока не хватает.
 
Последнее редактирование:

HeDeDe

Участник форума
Регистрация
17 Авг 2009
Сообщения
203
Благодарности
79
Баллы
180
Вопрос: Правильно ли я понял создание объекта в игре?

Создание происходит седующим образом:
  • Wld_InsertNpc обращается к методу oSpawnManager::SpawnNpc
  • oCSpawnManager вызывает world->CreateVob. Эта функция ещё не вставляет NPC в игру, а лишь вызывает oCObjectFactory::CreateNpc
  • oCObjectFactory::CreateNpc создает NPC, и инициализирует его, npc->InitByScript. Тот, в свою очередь, обращается к парсеру, который и вызывает конструктор instance (См. zCParser::CreateInstance в посте выше).
  • При успешном создании NPC, oCSpawnManager вставляет NPC в мир.
Кстати, господа-спецы, раз уж речь зашла о ссылках и подразумевает возможность заглядывания в движок. Поясните если можно, как происходит обработка ссылок, и как с ними правильно работать?

Вот, например, непись экипирован мечом определённой инстанции, а в его инвентаре лежит ещё такой же меч. С точки зрения движка это одна ссылка, или каждый из экземпляров будет иметь свою ссылку? Можно ли получить ссылку на предмет, который непись держит в руке, например, на горящий факел?
Да, каждый из предметов имеет свою ссылку. Ссылка — это адрес в памяти, завернутый в int.
Получить ссылку на предмет в руке можно, но только с помощью Ikarus'a, обратившись к методу oCNpc::GetInvSlot(npc, "ZS_LEFTHAND").

А можно по подробнее. Где можно посмотреть создание клонов?
Например, всякие монстры и бандиты. Как я говорил, instance хранит в себе только указатель на последний созданный экземпляр.

1) инстанция - экземпляр_класса - объект
2) инстанция - функция - конструктор
Здесь всё просто: у каждого символа есть поле offset, которое имеет разное значение в зависимости от типа (для функций это возвращаемое значение, для полей класса — смещение от начала класса, а для инстанций — указатель на последний созданный экземпляр).

Прототип тоже особого вида функция. Ее вызывает инстанция первой командой байт-кода, если в инстанции указывали прототип.
Верно. Причем вызов добавляется ещё на этапе компиляции.
 

Gor

Участник форума
Регистрация
26 Дек 2009
Сообщения
977
Благодарности
543
Баллы
245
Создание происходит седующим образом:
  • Wld_InsertNpc обращается к методу oSpawnManager::SpawnNpc
  • oCSpawnManager вызывает world->CreateVob. Эта функция ещё не вставляет NPC в игру, а лишь вызывает oCObjectFactory::CreateNpc
  • oCObjectFactory::CreateNpc создает NPC, и инициализирует его, npc->InitByScript. Тот, в свою очередь, обращается к парсеру, который и вызывает конструктор instance (См. zCParser::CreateInstance в посте выше).
  • При успешном создании NPC, oCSpawnManager вставляет NPC в мир.
Все подтверждается. Объект создается движком, который вызывает конструктор instance. Но в CAMERA.DAT находятся одни инстанции и никаких вызовов функций движка. Возможно двиг сам это все обрабатывает? То есть он по разному поступает в зависимости от датников.
 

HeDeDe

Участник форума
Регистрация
17 Авг 2009
Сообщения
203
Благодарности
79
Баллы
180
Все подтверждается. Объект создается движком, который вызывает конструктор instance. Но в CAMERA.DAT находятся одни инстанции и никаких вызовов функций движка. Возможно двиг сам это все обрабатывает?
Да, движок берет определенные функции/инстанции/константы из датников.

То есть он по разному поступает в зависимости от датников.
А точнее, в функциях движка жестко прописано, что и из каких датников брать.
 

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.331
Благодарности
3.178
Баллы
525
Да, каждый из предметов имеет свою ссылку. Ссылка — это адрес в памяти, завернутый в int.
Пытаясь написать функцию очистки инвентаря торговцев в Г1, столкнулся с тем, что возникает проблема при удалении предметов, не складывающихся в пачки. Если у непися есть несколько таких предметов одной инстанции, то удалить их разом с помощью функции Npc_RemoveInvItems не получается. Можно удалить только один экземпляр с помощью функции Npc_RemoveInvItem или Npc_RemoveInvItems с указанным количеством "1". Если в функции Npc_RemoveInvItems указать количество больше "1", то вообще ничего не удаляется. Похоже, всё дело в разных ссылках у данных предметов. Можно ли как-то решить эту проблему?
 

Myxomop

Почетный форумчанин
Регистрация
28 Май 2005
Сообщения
3.235
Благодарности
2.575
Баллы
455
Можно ли как-то решить эту проблему?
Ну если так не работает: Npc_RemoveInvItems(slf,ItMi_xxx,Npc_HasItems(slf,ItMi_xxx));
можно попробовать написать рекурсивную функцию или через зацикленный триггер-скрипт удалять поштучно до победы.
 

HeDeDe

Участник форума
Регистрация
17 Авг 2009
Сообщения
203
Благодарности
79
Баллы
180
Пытаясь написать функцию очистки инвентаря торговцев в Г1, столкнулся с тем, что возникает проблема при удалении предметов, не складывающихся в пачки. Если у непися есть несколько таких предметов одной инстанции, то удалить их разом с помощью функции Npc_RemoveInvItems не получается. Можно удалить только один экземпляр с помощью функции Npc_RemoveInvItem или Npc_RemoveInvItems с указанным количеством "1". Если в функции Npc_RemoveInvItems указать количество больше "1", то вообще ничего не удаляется. Похоже, всё дело в разных ссылках у данных предметов. Можно ли как-то решить эту проблему?
Это странно, так как Npc_RemoveInvItems должна удалять такие предметы в цикле по одному. При этом сама инстанция никак не трогается — проверяется лишь, сопадает ли имя инстанции предмета в инвентаре с именем переданной инстанции.

Код:
    oCItem* item = 0;
    if ( npc )
        item = npc->IsInInv(instanceItem, 1);

    if ( item ) {
        Script_SaveNpcGlobals();
        if (item->MultiSlot() && npc->IsInInv(instanceItem, amount))
        {
            npc->RemoveFromInv(instanceItem, amount);
        }
        else
        {
            for ( i = 0; i < amount; ++i ) {
                if ( !npc->RemoveFromInv(instanceItem, 1) )
                    break;
            }
        }
        ret = 1;
        Script_RestoreNpcGlobals();
    }

Стоп. Я говорю про Г2.
Г1 я не трогал, но похоже, что IsInInv и RemoveFromInv от Г1 к Г2 не изменились, а в Г2 была исправлена сама функция Npc_RemoveInvItems.


Код:
if ( npc->IsInInv(instanceItem, amount) )
       npc->RemoveFromInv(instanceItem, amount);

Следовательно, причина бага тогда в следующем: IsInInv проверяет каждый слот инвентаря, и возвращает истину тогда и только тогда, когда в слоте содержится предмет заданной инстанции, и количество предметов >= заданному. Так как нескладываемые предметы всегда имеют количество равное 1, выполняется только половина условия (в слоте лежит нужный нам предмет, но количество его меньше необходимого), таким образом IsInInv проверяет весь инвентарь, и не найдя предметов с необходимым количеством, возвращает 0.
 

neromont


Модостроитель
Регистрация
12 Мар 2011
Сообщения
674
Благодарности
655
Баллы
245
Подскажите пожалуйста. Класс C_FightAI отвечает за поведение существ во время боя?
 

neromont


Модостроитель
Регистрация
12 Мар 2011
Сообщения
674
Благодарности
655
Баллы
245
Еще один вопрос. Просто разбираюсь с классами. Функция Item.magic за что отвечает. Нашел ответ на немецком "VAR FUNC magic ; // Parserfunktion zum "Magie Header"", но что-то никак не пойму о чем это они.
 
Последнее редактирование:
Сверху Снизу