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

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!
  • Друзья, доброго времени суток!
    Стартовал новый литературный конкурс от "Ордена Хранителей" - "Пираты Миртанского моря".
    Каждый может принять в нём участие и снискать славу и уважение, а в случае занятия призового места ещё и получить награду. Дерзайте
  • Дорогие друзья, год подходит к концу, и пришло время подвести его итоги и наградить достойных

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

Gothic ½ Расширение возможностей парсера | zParserExtender [плагин для Union]

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.301
Благодарности
4.641
Баллы
625
  • Первое сообщение
  • #1
Мультиплатформенный плагин zParserExtender
Значительно увеличивает функциональность скриптов.
Для компиляции скриптов движком.



Доступно к скачиванию через Менеджер ресурсов в разделе 'Плагины'
- Скачать Менеджер Ресурсов - 1559419291725.png
- Скачать автономный установщик -

Требования: Union 1.0l или выше
Платформа: Gothic I, Gothic Sequel, Gothic II, Gothic II NoTR
Исходный код:
GitHub

Оглавнение:
HTML:
I. Общие функции для расширения и встраивания
  1. Внешние функции и переменные
  2. Цикл WHILE
  3. Пространства имен
  4. Функции-события
  5. Функции-триггеры (пространство имен zParserExtender)
  6. Прокси-классы движка (пространство имен zParserExtender)
II. Инъекции:
  1. API-скрипт
  2. Хуки
  3. Новые диалоги
  4. META свойства скрипта
  5. Операторы связывания
   5.1 Оператор test-else
   5.2 Оператор extern
III. Другие возможности
  1. Параметры ini
  2. Консольные команды MARVIN
  3. дополнительные параметры запуска
  4. Компилятор DAT и OU
  5. Индексирование строк
IV. Приложения
  Приложение 1 - список внешних функций и переменных
  Приложение 2 - коды клавиш
  Приложение 3 - теги и наименования парсеров/DAT файлов
  Приложение 4 - теги и наименования движков
V. Исправления

Содержание:
Плагин предлагает две области применения:
А. Для расширения возможностей DAT файлов во время компиляции.
Б. Для встраивания (инъекции) скриптов на лету.

В первую очередь плагин создан для варианта Б в качестве вспомогательного инструмента в реализации других плагинов. Но для каждого из них существуют свои полезные функции.
Итак, поговорим сначала о тех функциях, которые применимы к обеим областям.

I. Общие функции для расширения и встраивания:
1. Внешние функции и переменные:
Внешние функции - это функции, реализация которых определена самим движком. Например функция 'print' является внешней.
Внешние функции определяют сам функционал скриптового языка Daedalus, поскольку сам по себе язык ничего полезного не умеет.
Хотя движок содержит крайне большое количество внешних функций, периодически возникает их нехватка ввиду возрастающей сложности скриптов. Плагин предлагает целый рад вспомогательных функций, список которых можно посмотреть в 'приложении 1.
С актуальных версий плагина новые внешние функции называются 'динамическими внешними функциями'. Такое название происходит из-за того, что изменилась система их определения. Плагин не будет добавлять новые функции в скрипты, если А. уже существует одноименные символы непосредственно в скриптах Б. скрипты не обращаются ко внешним функциям. Таким образом плагин поддерживает чистоту DAT файла.
Если необходимо использовать новые внешние функции в GothicSourcer, необходима версия 3.15 или 3.16.
Внешние переменные - это переменные, значения которых определяется внутри плагина. Переменные могут быть заранее определены парсеров, либо они должны быть определены в скриптах (об этом будет явно сказано в описании). Так или иначе их значения будут определяться плагином. Список переменных все также можно найти в 'приложении 1'.

2. Цикл WHILE:
Эквивалент цикла 'while' на Си-подобных языках. В качестве условия может быть передано выражение, результат которого должен приводиться к логическому. Цикл выполняется до тех пор, пока результат выражения не равен нулю.
Поскольку цикл Си-подобный, в нем также поддерживаются операторы:
continue - оператор перехода к следующей итерации.
break - оператор прерывания цикла и выхода из него.
Daedalus:
  var int value; value = 10;
  while(value > 0)
  {
    if (value == 8)
    {
      continue;
    };
 
    if (value == 2)
    {
      break;
    };
  };

Примечания:
- Для активации цикла необходимо, чтобы в ini файле мода был задан параметр 'NativeWhile = 1'. Также можно переопределить данный параметр через блок META (см. блок META).
- Скомпилированный цикл может работать и без плагина на чистой игре.

3. Пространства имен:
Пространства имен эквивалентны пространствам современных языков, поэтому если вы не знакомы с этим понятием, то предлагается почитать подробнее на MSDN.
Пространство имен реализует внутри себя защищенную от переопределения область. Позволяет давать более простые и понятные имена объектам. Естественно, что имя самого пространства должно быть уникальным, обособленным от других. При необходимости допускается использование одинаковых названий пространств, если они входят в один проект. К тому же пространства имен неплохо уживаются с Ikarus.
Пространство по умолчанию называют 'глобальным пространством имен'.

Определение пространств имен:
А. Классический способ определения (он же и будет фигурировать во всех примерах далее) - это создание именованного блока через ключевое слово 'namespace':
Daedalus:
  namespace zTestNamespace
  {
    var int var01;
    func void func01() { };
  };

Б. Второй вариант допускается только во встраиваемых скриптах (инъекциях) - через блок META (см. блок META). Такой вариант эквивалентен предыдущему, но распространяется на весь документ:
Daedalus:
  META
  {
    Namespace = zTestNamespace;
  };
 
  var int var01;
  func void func01() { };

Пространства имен могут также быть вложены друг в друга, если это необходимо. Это касается и пространства, определенного через блок META, при этом такое пространство будет всегда иметь самый верхний уровень.
Daedalus:
  META
  {
    Namespace = zTestNamespace01;
  };
 
  namespace zTestNamespace02
  {
    namespace Definitions
    {
      var int var01;
    };
 
    func void func01() { };
  };

Для обращения к объекту из другого пространства имен используется оператор ':'. Этот оператор имеет два направления - А. вглубь (на уровень вниз) или Б. наружу (на уровень вверх).
А. Для углубления в пространство следует использовать имя пространства вместе с оператором ':'. Пример - три одноименные функции в разных пространствах имен. Вызов происходит из глобального пространства:
Daedalus:
  namespace zTestNamespace01
  {
    func void func01() { };
  };
 
  namespace zTestNamespace02
  {
    func void func01() { };
  };
 
  namespace zTestNamespace03
  {
    namespace zTestNamespace04
    {
      func void func01() { };
    };
  };
 
  func event GameInit()
  {
    // В данном случае обращение происходит из
    // глобального пространства имен к zTestNamespace
    zTestNamespace01:func01();
    zTestNamespace02:func01();
    zTestNamespace03:zTestNamespace04:func01();
  };

Б. Для выхода из пространства следует использовать оператор ':' без указания пространства, при этом количество пустых операторов определит, на сколько уровней следует подняться. Пример на одноименных функциях во вложенных пространствах имен. Вызов функций происходит из самого глубокого пространства:
Daedalus:
  func void func01()
  {
    Hlp_MessageBox("#1");
  };
 
  namespace zTestNamespace01
  {
    func void func01()
    {
      Hlp_MessageBox("#2");
    };
 
    namespace zTestNamespace02
    {
      func void func01()
      {
        Hlp_MessageBox("#3");
      };
 
      namespace zTestNamespace03
      {
        func void func01()
        {
          Hlp_MessageBox("#4");
        };
 
        func event GameInit()
        {
          :::func01(); // Вызвать функцию на 3 уровеня выше
          ::func01();  // Вызвать функцию на 2 уровеня выше
          :func01();   // Вызвать функцию на 1 уровень выше
          func01();    // Вызвать функцию из текущего пространства имен
        };
      };
    };
  };

Существует три случая, при которых пространство имен в обращении указывать не обязательно. При этом приоритет поиска символов будет соответствовать порядку примеров ниже.
А. Если объект находится в текущем пространстве имен, из которого происходит вызов:
Daedalus:
  namespace zTestNamespace01
  {
    func void func01()
    {
      Hlp_MessageBox("#1");
    };
 
    func event GameInit()
    {
      // Вызов функции из текущего пространства.
      func01();
    };
  };

Б. Если функция находится в пространстве имен выше уровнем. При этом взята будет функция ближайшая по уровню вложенности. В следующем примере будет взята функция, выводящая в сообщении '#2':
Daedalus:
  func void func01()
  {
    Hlp_MessageBox("#1");
  };
 
  namespace zTestNamespace01
  {
    func void func01()
    {
      Hlp_MessageBox("#2");
    };
 
    namespace zTestNamespace02
    {
      func event GameInit()
      {
        // Вызов функции пространства на один уровень выше.
        func01();
      };
    };
  };

В. Если в блоке мета указан параметр 'using' (см. блок META):
Daedalus:
  META
  {
    using = zTestNamespace01;
  };
 
  namespace zTestNamespace01
  {
    func void func01()
    {
      Hlp_MessageBox("#1");
    };
  };
 
  func event GameInit()
  {
    // Вызов функции пространства, указанного в
    // META как одно из пространств по умолчанию.
    func01();
  };

Пространство может указываться не только для обращения к существующим символам, но и для определения новых. В примере ниже представлено, как можно реализовать скриптовый хук на глобальную функцию (см. Хуки).
Чтобы совершить хук на объект, должна совпадать не только сигнатура перехватываемого и перехватывающего объекта, но и пространство имен. Чтобы перехватить функцию одного пространства из другого, синтаксически разрешается явно указать, к какому пространству имен будет принадлежать тот или иной объект.
Отсюда следует, что для определения функции 'ItAr_Pir_L_Addon' из пространства 'zTestNamespace01' в 'глобальное', необходимо поднять определение на уровень вверх оператором ':'. И хотя функция будет определена глобально, она по прежнему находится в блоке 'zTestNamespace01', а значит все обращения из этой функции будут локальными для данного пространства имен.
Daedalus:
  namespace zTestNamespace01
  {
    const string Var01 = "New instance name";
 
    // Хук глобальной инстанции
    instance :ItAr_Pir_L_Addon(C_Item)
    {
      ItAr_Pir_L_Addon_Old();
      name = Var01;
    };
  };

4. Функции-события:
События - это одноименные функции, которые могут быть определены множество раз, но по одному на файл. Такие функции полезны для реализации callback функций, поскольку при вызове события будут вызваны все одноименные экземпляры. Тип функции-события - 'func'. К особенностям можно отнести то, что события определяются глобально, не зависимо от того, в каком пространстве имен они находятся. Для вызова функции-события из скрипта, следует использовать внешнюю функцию Hlp_DoEvent(var string funcName) (см. Внешние функции).
Daedalus:
  func void GiveXP()
  {
    Hlp_DoEvent("OnGiveXP");
  };
 
  func event OnGiveXP()
  {
    // TODO
  };
Стандартными событиями плагина являются функции 'func event GameInit()' - происходит в момент входа в игровое меню; 'func event GameLoop()' - происходит каждый кадр при загруженном мире. Определите их в любых файлах и они будут вызваны автоматически.

5. Функции-триггеры:
Триггеры представляют собой циклические функции, которые вызываются самостоятельно через заданный промежуток времени (не путать с триггерами, создаваемыми средствами zen миров). Важная особенность триггеров - они могут хранить пользовательскую информацию. Пользователю доступно 16 числовых переменных для каждого триггера, а так же self, other и victim (скрытые параметры).
Для определения триггера используется класс C_Trigger, в котором описаны его основные свойства:
HTML:
  - Delay           - определяет частоту (мс), с которой будет вызываться функция.
  - Enabled         - определяет, активен ли триггер. Если значение равно нулю, то триггер уничтожается.
  - AIVariables[16] - пользовательские данные, которые можно задать самостоятельно при создании триггера (да, можно писать туда абсолютно все, что душе угодно).

Также триггер использует 4 скрытых параметра:
HTML:
  - Func   - функция, которую будет вызывать триггер.
  - Self   - NPC, который при вызове функции будет помещаться в self.
  - Other  - NPC, который при вызове функции будет помещаться в other.
  - Victim - NPC, который при вызове функции будет помещаться в victim.

Для создания триггера используются две внешние функции:
HTML:
  - AI_StartTriggerScript(имя функции, задержка)                        - функция создает триггер без привязки в ней NPC (self, other или victim)
  - AI_StartTriggerScriptEx(имя функции, задержка, self, other, victim) - расширенная функция, если необходимо указать в нее определенных участников

В результате выполнения любой из функций выше будет возвращен экземпляр C_Trigger. При необходимости триггер после создания настраивается. Например можно заполнить поля 'AIVariables'.
Триггер-функция, которую будет вызывать сам триггер, должна иметь формат `func int()`. Она должна возвращать значение, определяющее состояние цикла - если функция возвращает Loop_end, то триггер будет моментально остановлен и удален. Если же Loop_continue - функция будет вызвана повторно через Delay миллисекунд.

Для того, чтобы внутри триггер-функции получить данные текущего или другого триггера, существуют две глобальные переменные:
HTML:
  - C_Trigger FirstTrigger - экземпляр самого первого триггера в списке триггеров.
  - C_Trigger SelfTrigger  - экземпляр текущего вызывающего триггера.

Чтобы показать простоту и возможности триггеров, ниже представлен код простого триггера, имитирующего отравление:
Daedalus:
  // реализация триггера, имитирующего эффект отравления:
  // Создадим триггер на функцию 'c_loop' с интервалом вызова 1 секунда.
  // При вызове функции в self будет помещена инстанция hero (хотя при желании это может быть любой другой NPC).
  // Остальные инстанции оставляем null (не используются).
  var C_Trigger trigger;
  trigger = AI_StartTriggerScriptEx("c_loop", 1000, hero, null, null);
  trigger.AIVariables[0] = 15; // Укажем, сколько раз функция должна быть вызвана
  trigger.AIVariables[1] = 5;  // зададим, сколько HP следует отнимать каждый вызов

Создание триггер-функции:
Daedalus:
  func int c_loop()
  {
    // Создадим проверку на окончание цикла. Если количество
    // доступных итераций подошло к концу, то останавливаем
    // триггер возвратом значения Loop_end.
    if (SelfTrigger.AIVariables[0] <= 0)
    {
      return Loop_end;
    };
 
    SelfTrigger.Delay -= 20;                                     // Ускоряем цикл каждый вызов на 20 мс
    SelfTrigger.AIVariables[0] -= 1;                             // Уменьшаем количество оставшихся повторов
    self.Attribute[ATR_HITPOINTS] -= SelfTrigger.AIVariables[1]; // Отнимаем здоровье у указанного нами self
    return Loop_continue;
  };

Область работы триггера:
Триггеры можно поделить на два типа. . .
А. Глобальный триггер (AI_StartTriggerScript) - такой триггер работает во всех мирах. Триггер по умолчанию считается глобальным, если для него не было представлено ни 'self', ни 'other', ни 'victim'.
Б. Локальный триггер (AI_StartTriggerScriptEx) - а этот триггер работает только в том мире, в которым был создан. Триггер считается локальным, если ему был представлен хотя бы один NPC в 'self', 'other' или 'victim' (не null). Если требуется создать триггер, но без привязки с какому-либо NPC, рекомендуется просто представить триггеру hero в качестве 'self'.

Сохранение:
Для сохранения триггеров используется отдельный архив, который никак не конфликтует с оригинальными файлами игры. В архив пишутся все данные о триггерах - переменных, NPC и т.д.

Дополнительно:
Для поиска конкретного триггера, к примеру, по NPC, используются функции перебора триггеров (см. Внешние функции).
Daedalus:
  // Пример отключения всех триггеров для hero.
  var C_Trigger trigget = FirstTrigger;
  var C_Trigger trigget_saved;
  while (!Hlp_IsNULL(trigget))
  {
    trigget_saved = trigger;
    trigger = AI_GetNextTriggerBySelf(hero);
    trigget_saved.Enabled = false;
  };

6. Прокси-классы движка:
Прокси-классы позволяют получать или задавать свойства объектов движка. Это грубые классы, которые содержат общую информацию о данных объектов, образованных от следующих классов: zCOLOR, zVEC3, zCVob, zCVobLight, oCMOB, oCMobInter, oCMobLockable.
Для получения указателей на объекты следует использовать внешние функции из группы Cast, Hlp и Wld. Для получения или задания значений объектам используются getter-setter функции из приложения IV Внешние функции: VOB.
Список классов и их свойства, определенные в zUnionVob.d (API скрипт. Пространство имен - zParserExtender):
Daedalus:
C_VOB - базовый указатель на объект игрового мира

C_COLOR - цвет в формате RGBA
  int R - значение красного канала
  int G - значение зеленого канала
  int B - значение синего канала
  int A - значение альфа канала
 
C_POSITION - координата
  int X - координата по оси X
  int Y - координата по оси Y
  int Z - координата по оси Z

Следующие классы определяют свойства объекта C_VOB или унаследованных от него классов

C_VOB_DATA - универсальные данные класса zCVob
  string Name               - имя объекта
  float VisualAlpha         - прозрачность объекта (от 0 до 1)
  int ShowVisual            - отобажает модель
  int DrawBBox3D            - отображает границы объекта
  int VisualAlphaEnabled    - разрешает прозрачность объекта
  int PhysicsEnabled        - активирует физику объекта
  int IgnoredByTraceRay     - запрещает любые коллизии с объектом
  int CollDetectionStatic   - разрешает коллизии со статическими полигонами мира
  int CollDetectionDynamic  - разрешает коллизии с динамическими объектами мира
  int CastDynShadow         - отображает тень объекта
  int LightColorStatDirty   - Разрешает статическое освещение объекта
  int LightColorDynDirty    - Разрешает динамическое освещение объекта
  int SleepingMode          - определяет режим активности объекта (0 - неактивен, 1 - активен, 2 - разрешено только выполнение AI)
  int DontWriteIntoArchive  - запрещает сохранять этот объект в .sav файл

C_LIGHT_DATA - данные для источников света zCVobLight
  int R                - интенсивность красного света
  int G                - интенсивность зеленого света
  int B                - интенсивность синего света
  int Range            - радиус
  int RangeInv         -
  int RangeBackup      -
  int RangeAniActFrame - текущий кадр анимации света для радиуса
  int RangeAniFPS      - скорость анимации света для радиуса
  int ColorAniActFrame - текущий кадр анимации света для цвета
  int ColorAniFPS      - скорость анимации света для цвета
  int SpotConeAngleDeg - угол конусообразного источника света
  int IsStatic         - является ли источник статическим
  int RangeAniSmooth   - [UNUSED]
  int RangeAniLoop     - [UNUSED]
  int ColorAniSmooth   - разрешает мягкие переходы между цветами
  int ColorAniLoop     - [UNUSED]
  int IsTurnedOn       - включен ли источник света
  int LightQuality     - качество источника (при статической компиляции света) (0 - высокое, 1 - среднее, 2 - низкое)
  int LightType        - тип источника (при статической компиляции света) (0 - точечный, 1 - конусообразный)
 
C_MOB_DATA - данные для используемого объекта oCMOB
  string VisibleName     - отображаемое над объектом имя
  int Hitpoints          - количество 'здоровья'
  int Damage             - урон, который может наносить объект
  int IsDestroyed        - уничтожен ли объект
  int Moveable           - может ли объект быть перемещен
  int Takeable           - может ли объект быть взят
  int FocusOverride      - будет ли объект переопределять фокус в режиме боя
  int SndMat             - материал объекта (0 - дерево, 1 - камень, 2 - металл, 3 - кожа, 4 - глина, 5 - стекло)
  string VisualDestroyed - модель, когда объект уничтожен
  string OwnerStr        - имя инстанции хозяина объекта
  string OwnerGuildStr   - имя гильдии объекта
  int Owner              - инстанция хозяина
  int OwnerGuild         - инстанция гильдии
  int FocusNameIndex     - скриптовая строка отображаемого имени
 
C_MOBINTER_DATA - данные для интерактивного объекта oCMobInter
  string TriggerTarget   - имя объекта, которое срабатывает при OnTrigger
  string UseWithItem     - имя инстанции объекта, который необходим для взаимодействия
  string Sceme           - имя сцены, которая соответствует анимациям объекта и персонажа
  string ConditionFunc   - скриптовое условие, при котором взаимодействие может быть совершено
  string OnStateFuncName - шаблон имени функций, которые будут вызываться при изменении состояния объекта
  int State              - текущее состояние объекта
  int State_num          - количество состояний объекта
  int State_target       - текущее состояние объекта
  int Rewind             - запрещает обновление объекта
  int MobStateAni        - текущая анимация обоъекта
  int NpcStateAni        - текущая анимация персонажа
 
C_MOBLOCKABLE_DATA - данные для запираемого интерактивного объекта oCMobLockable
  int Locked         - заперт ли объект
  int AutoOpen       - [UNUSED]
  int PickLockNr     - текущий номер поворота отмышки
  string KeyInstance - имя инстанции ключа для данного объекта
  string PickLockStr - комбинация для вскрытия объекта ( "LRRLR" )

II. Инъекции:
Далее речь пойдет о том, что поддерживают только инъекции.
Само понятие инъекции означает внедрение скриптов в игру минуя DAT файлы. То есть скрипты будут загружаться в игру напрямую. Такой способ необходим для реализации вспомогательных плагину скриптов, создания хот-фиксов или просто если нужно что-то по быстрому поменять в скриптах.
Для внедрения скрипта достаточно положить .d или .src файл в папку Gothic/System/Autorun и запустить игру. Однако автоматическая инъекция не распространяется на вложенные в Autorun/* подкаталоги. Но скрипты по вложенных каталогах можно использовать в двух сценариях:
А. Через .src файл указать относительные пути до скриптов
Б. Если скрипты являются API-скриптами

1. API-скрипт:
Это скрипт, лежащий в подкаталогах Autorun/* и использующийся лишь в качестве внешней зависимости.
Предполагается, что API-скрипт не вызывается самостоятельно (в тч из .src). Такой файл служит в качестве внешней зависимости, то есть может использоваться множеством скриптов для предоставления им готовых решений (классов, функций, констант и тп). Во встраиваемых скриптах подключение зависимостей происходит через тег 'After' (см. блок META), который позволяет подключать скрипты и выстраивать очередь их сборки.
Следует обратить внимание на замечание к тегу 'After' - если внешняя зависимость не существует, то и текущий скрипт не будет загружен - раз нет зависимости, то и выполнение текущего скрипта заведомо невозможно. А из этого следует, что внешнюю зависимость имеет смысл распространять рядом с инъекцией (при соблюдении оригинальной иерархии), в том же томе, что встраиваемый скрипт. Даже если это скрипт из какого-то другого проекта.

* API скрипты zUnionTrigger.d и zUnionVob.d обернуты в пространство имен zParserExtender. При использовании этих скриптов, рекомендуется использовать META тег Using=zParserExtender, чтобы обращаться к объектам напрямую.

2. Хуки:
Хуки являются одной из наиболее важных функций в инъекциях.
Под хуком понимается механизм, позволяющий заменять любые скриптовые объекты на новые. Для этого необходимо определить новый объект с тем же типом, именем и в том же пространстве имен.
Замена будет произведена, если для текущего скрипта активен параметр 'MergeMode'. Глобальный параметр будет находиться в ini мода, по умолчанию он равен TRUE.
Если данный параметр отключен, то при попытке создания хука на объект произойдет ошибка 'переопределения'. Для избежания несогласованности в самом скрипте можно определить аналогичный параметр через блок META (см. блок META).
Когда старый объект будет подменен, к его имени добавится окончание '_old' (прим. myFunc01 -> myFunc01_old). По этому окончанию всегда можно обращаться к оригинальным объектам.
К примеру хук на функцию будет выглядеть следующим образом:
Daedalus:
func void ZS_Attack_Loop()
{
  // Если враг - игрок и без оружия, то
  // тоже складываем оружие в ножны
  if (Npc_IsPlayer(other) && !Npc_HasReadiedWeapon(other))
  {
    return LOOP_END;
  };
 
  // Иначе возвращаемся в оригинальную функцию
  return ZS_Attack_Loop_Old();
};

Такая подмена работает и другими типами объектов, будь то переменная или инстанция. Однако на инстанциях следует остановиться отдельно.
Сначала хотелось бы пояснить как работает инстанция, унаследованная от прототипа. Когда движок вызывает инстанцию, вначале происходит обращение к прототипу как в функции. Это значит, что если мы определим такой хук:
Daedalus:
instance pc_hero(Npc_Default)
{
  pc_hero_old();
  name = "Ivan";
};

то прототип `Npc_Default` будет вызван дважды. Обосновывается это тем, что вначале вызывается новая инстанция `pc_hero`, а из нее - прототип. Затем вызывается оригинальная реализация `pc_hero_old`, и в ней опять - прототип. Такой код может быть небезопасен, поэтому если в перехваченной инстанции предполагается вызов оригинальной функции, то настоятельно рекомендуется в новой инстанции прототип не указывать. Тогда код получит следующий вид:
Daedalus:
instance pc_hero(C_Npc) // no Npc_Default
{
  pc_hero_old();
  name = "Ivan";
};

3. Новые диалоги:
Механизм предназначен для внедрения новых диалогов в игру, а также их подмены средствами хуков. Скриптер может создавать новых торговцев, квесты, диалоги, а так же привязывать к ним svm фразы.
Все новые или замененные диалоги сразу же станут доступны, в том числе из сохранений. В случае, если новые диалоги будут отключены (удален плагин или скрипт), движок продолжит удерживать их в файле сохранений, что позволит вернуть диалоги в любой момент с тем же состоянием, в котором они находились в последний раз.

4. META свойства скрипта:
Этот тег предназначен для настройки встраиваемых скриптов. Часть этих параметров переопределяет параметры из ini и имеют над ним высший приоритет.
META тег необязателен, однако в случае его использования важно учесть, что его необходимо указывать в САМОМ начале документа (без отступов и комментариев).
Тег может принимать следующие параметры:
HTML:
  А. Parser      - указывает плагину для какого парсера (DAT) будет собран скрипт. Список тегов парсера см. в приложении 3
  Б. MergeMode   - указывает режим переопределения. 0 - при переопределении будет ошибка, 1 - при переопределении выполнится хук
  В. Engine      - указывает плагину, для которых может быть собран скрипт (указываются через запятую). Список тегов движка см. в приложении 4
  Г. NativeWhile - Указывает плагину, будут ли компилироваться блоки цикла while. 0 - выкл, 1 - вкл
  Д. Namespace   - указывает пространство имен документа по умолчанию
  Е. Using       - указывает, какие пространства имен откроют список своих членов для прямого доступа из глобального пространства (указываются через запятую)
  Ж. Mod         - указывает, при наличии каких vdf/mod скрипт может быть скомпилирован (указываются через запятую)
  З. After       - указывает, после каких скриптов может быть выполнен текущий скрипт (указываются через запятую). Если указанных скриптов нет в очереди запуска, то плагин попытается их запустить самоятоятельно (внешние зависимости).

Пример того, как может выглядеть тег META:
Daedalus:
META
{
  Parser = Menu;
  After  = TestScript.d, HelpFunctions.d;
  Engine = G2, G2A;
  Mod    = GothicGame.mod, LHiver.mod;
};

// your code . . .

5.1 Оператор связывания test-else
Оператор связывания test-else может использоваться для определения участков исходного кода, подлежащего компиляции. Если исходный код находится в границах неактивной ветки test-else, то он не будет скомпилирован. Данный оператор может принимать на вход значения, которые преобразуются в логические. Например если передать в качестве аргумента объект, то парсер проверит его существование. Если это тег движка, то будет возвращен результат соответствия текущего движка с тегом:
- имя символов (PC_HERO, ...)
- теги движка (G1, G1A, G2, G2A)
- активность Steam Overlay (Steam)

Результат может комбинироваться из нескольких аргументов, разделенных скобками (...) для задания приоритета и логическими операторами инверсии (!), логического И (&&) и ИЛИ (||).

Оператор может использоваться в любом участке файла скрипта. Синтаксически схож с if-else, но при однострочных операциях можно не указывать фигурные скобки. Например:
Daedalus:
  test Steam var const SteamActivated = 1;
 
  test SteamActivated && G2A {
    // TODO
  }
  else {
    // TODO
  }

5.2 Оператор связывания extern
Оператор связывания extern позволяет обезопасить Ваш код от переопределенных или неопределенных символов. Указанное слово extern перед объявлением означает, что при наличии одноименного объекта следует использоваться исходный. А при отсутствии - будет создан новый.
Daedalus:
extern instance PC_Hero(C_Npc) {
  // TODO
};

III. Другие возможности:
1. Параметры ini:
Выбор ini файла зависит от того, каким образом была запущена игра. Если запуск произошел с Gothic.exe, то параметры будут читаться из SystemPack.ini. Если через GothicStarter.exe, то из ini мода.
Некоторые из этих параметров могут переопределяться через блок META (см. блок META).
HTML:
- LoadScript [устарел] - указывает скрипт формата parser-script для запуска скриптов. В настоящее время параметр неактуален.
- MergeMode            - определяет, будут ли в инъекциях производиться хуки. По умолчанию True.
- CompileDat           - определяет, будет ли создана копия ИЗМЕНЕННОГО инъекциями DAT файла. По умолчанию False.
- CompileOU            - определяет, будет ли создана копия ИЗМЕНЕННОГО инъекциями OU файла. По умолчанию False.
- NativeWhile          - определяет, будет ли компилироваться цикл WHILE. По умолчанию False (для лучшей совместимости с Ninja).
- MessagesLevel        - определяет уровень вывода сообщений. Чем выше уровень, тем больше информации будет выведено в консоль Union. По умолчанию 1.
- StringIndexingMode   - определяет режим индексирования строк (см. индексирование строк). По умолчанию -1.

2. Консольные команды MARVIN:
Следующие консольные команды предназначены для сохранения КОПИЙ DAT и OU файлов в ручном режиме и списка строковых переменных.
Примечание: если используется Ikarus, то следует использовать параметр CompileDat (см. параметры ini), поскольку при использовании консоли может произойти критическая ошибка.
HTML:
Parser SaveDat OU        - скомпилировать OU.Edited.bin
Parser SaveDat Game      - скомпилировать Gothic.Edited.dat
Parser SaveDat SFX       - скомпилировать SFX.Edited.dat
Parser SaveDat PFX       - скомпилировать ParticleFX.Edited.dat
Parser SaveDat VFX       - скомпилировать VisualFX.Edited.dat
Parser SaveDat Camera    - скомпилировать Camera.Edited.dat
Parser SaveDat Menu      - скомпилировать Menu.Edited.dat
Parser SaveDat Music     - скомпилировать Music.Edited.dat
Parser Export Stringlist - экспортировать список строковых символов в Scripts\Exports\StringList.d

3. дополнительные параметры запуска:
Параметры следует передавать движку в качестве текста командной строки. Это можно сделать, например, при помощи GothicStarter_Mod.
Примечание: для сборки OU необходимо, чтобы. . .
А. Был задан оригинальный параметр -zReparse (пересобрать все скрипты)
Б. Были одновременно заданы параметры -zReparse_Game и -zReparse_OU, поскольку в одиночку второй параметр не работает

Все параметры:
HTML:
-zReparse_OU     - скомпилировать OU.bin
-zReparse_Game   - скомпилировать Gothic.dat
-zReparse_SFX    - скомпилировать SFX.dat
-zReparse_PFX    - скомпилировать ParticleFX.dat
-zReparse_VFX    - скомпилировать VisualFX.dat
-zReparse_Camera - скомпилировать Camera.dat
-zReparse_Menu   - скомпилировать Menu.dat
-zReparse_Music  - скомпилировать Music.dat

4. Компилятор DAT и OU:
А. Плагин имеет механизм создания OU (.bin и .csl). При этом созданная библиотека чище, чем собранная средствами Spacer или GothicSourcer (см. дополнительные параметры запуска).
Б. Плагин может пересобирать DAT файлы по отдельности (см. дополнительные параметры запуска).
В. Плагин может сохранить новый DAT и OU после их изменения через инъекции (см. параметры ini и консольные команды), при этом имена таких файлов будут иметь суффикс '.edited', то есть плагин не перезаписывает оригинальные файлы.

5. Индексирование строк:
Движок при сборке скриптов назначает безымянным строкам имя, соответствующее ее порядковому номеру среди других безымянных строк, где нумерация начинается внутри имени с '10000'.
Если до начала инъекций DAT файл не компилировался, то плагин должен будет самостоятельно определить количество безымянных строк для выставления точки отсчета. В зависимости от режима, плагин попутно может "починить" нумерацию безымянных строк, если порядок был нарушен. Режим индексирования настраивается в ini в параметре StringIndexingMode:
HTML:
Default   = -1 - на данный момент умолчанием служит режим Repair.
Disabled  =  0 - ничего не делать с индексами.
TopSymbol =  1 - плагин находит самую верхнюю безымянную строку и выставляет по ней счетчик.
Repair    =  2 - плагин перебирает всю таблицу символов и, если порядок индексации нарушен, проставляет правильные имена. Счетчик выставляется на основе перебора.

IV. Приложения:
Приложение 1 - список внешних функций:
Функции преобразования - CAST
Daedalus:
// Преобразует адрес address указателя в instance
func instance Cast_PointerToInstance( var int address )

// Преобразует object в адрес
func int Cast_InstanceToPointer( var instance object )

// Преобразует адрес address указателя в npc
func C_NPC Cast_PointerToNpc( var int address )

// Преобразует адрес address указателя в item
func C_ITEM Cast_PointerToItem( var int address )

// Определяет является ли объект object персонажем
func int Cast_InstanceIsNpc( var instance object )

// Определяет является ли объект object предметом
func int Cast_InstanceIsItem( var instance object )

// Определяет является ли объект object мобом
func int Cast_InstanceIsMob( var instance object )

// Возвращает индекс инстанции объекта, иначе -1
func int Cast_GetInstanceIndex( var instance object )

// Возвращает идентификатор класса класса по его имени
func int Cast_GetClassID( var string className )

// Возвращает идентификатор класса zCObject воба
func int Cast_GetVobClassID( var instance object )

// Проверяет является ли класс classId родительским для объекта object
func int Cast_CheckVobClassID( var int classId, var instance object )
Функции вспомогательные - HLP
Daedalus:
// Определяет существует ли для нпс npc объект в фокусе
func int Hlp_HasFocusVob( var C_NPC npc )

// Возвращает объект в фокусе персонажа npc
func instance Hlp_GetFocusVob( var C_NPC npc )

// Возвращает имя воба в фокусе персонажа npc
func string Hlp_GetFocusVobName( var C_NPC npc )

// Возвращает длину строки str
func int Hlp_GetStringLength( var string str )

// Определяет является ли значение value неопределенностью
func int IsNAN( var float value )

// Определяет была ли только что нажата кнопка key
func int Hlp_KeyToggled( var int key )

// Определяет зажата ли кнопка key
func int Hlp_KeyPressed( var int key )

// Определяет была ли нажата логическая кнопка key
func int Hlp_LogicalKeyToggled( var int key )

// Определяет стоит ли игра на паузе
func int Hlp_GameOnPause()

// Выводит сообщение message на экран
func void Hlp_MessageBox( var string message )

// Выводит информацию message в консольное окно Union'а
func void Hlp_PrintConsole(var string message)

// Считывает целочисленное значение параметра из ini
// файла optName ("Gothic", "Mod", "SystemPack"), секции section,
// вхождения entry, где значением по умолчанию считыется default
func int Hlp_ReadOptionInt(var string optName, var string section, var string entry, var int default)

// Считывает вещественное значение параметра из ini
// файла optName ("Gothic", "Mod", "SystemPack"), секции section,
// вхождения entry, где значением по умолчанию считыется default
func float Hlp_ReadOptionFloat(var string optName, var string section, var string entry, var float default)

// Считывает строковое значение параметра из ini
// файла optName ("Gothic", "Mod", "SystemPack"), секции section,
// вхождения entry, где значением по умолчанию считыется default
func string Hlp_ReadOptionString(var string optName, var string section, var string entry, var string default)

// Определяет существует ли в ini
// файле optName ("Gothic", "Mod", "SystemPack") секция section
// (при entry = "") или вхождение entry (тогда entry != "")
func int Hlp_OptionIsExists(var string optName, var string section, var string entry)

// Записывает целочисленное значение value в ini
// файл optName ("Gothic", "Mod", "SystemPack")
// в секцию section, вхождение entry
func void Hlp_WriteOptionInt(var string optName, var string section, var string entry, var int value)

// Записывает вещественное значение value в ini
// файл optName ("Gothic", "Mod", "SystemPack")
// в секцию section, вхождение entry
func void Hlp_WriteOptionFloat(var string optName, var string section, var string entry, var float value)

// Записывает строковое значение value в ini
// файл optName ("Gothic", "Mod", "SystemPack")
// в секцию section, вхождение entry
func void Hlp_WriteOptionString(var string optName, var string section, var string entry, var string value)

// Возвращает имя текущего пользователя Steam (иначе пустую строку)
func string Hlp_GetSteamPersonalName()
Функции действий над миром - WLD
Daedalus:
// Меняет активный мир world и ставит hero на заданный waypoint
func void Wld_ChangeLevel( var string world , var string waypoint )

// Ищет воб по его имени vobname и возвращает адрес , иначе -1
func instance Wld_FindVob( var string vobname )

// Воспроизводит эффект effect для воба pvob, с уровнем эффекта level,
// силой урона damage, типом урона damage_type и скоростью damage_speed
func void Wld_PlayEffectVob(
                             var string effect ,
                             var instance pvob ,
                             var int level ,
                             var int damage ,
                             var int damage_type ,
                             var int damage_speed )

// Воспроизводит эффект effect в точке coord, с уровнем эффекта level,
// силой урона damage, типом урона damage_type и скоростью damage_speed
func void Wld_PlayEffectAt(
                             var string effect ,
                             var instance coord ,
                             var int level ,
                             var int damage ,
                             var int damage_type ,
                             var int damage_speed )

// Вызывает эффект дождя силой weight и продолжительностью time
func void Wld_ToggleRain( var float weight , var flaot time )

// Устанавливает тип осадков // дождь или снег. Работает только в G2 & G2A
func void Wld_SetWeatherType( var int type )

// Возвращает тип осадков, иначе -1. работает только в G2 & G2A
func int Wld_GetWeatherType()
Функции действий над моделью - MDL
Daedalus:
// Получает индекс анимации по имени ani_name, иначе -1
func int Mdl_GetAnimationIndex( var C_NPC npc , var string ani_name )

// Получает имя анимации по индексу ani_index, иначе пустая строка
func string Mdl_GetAnimationName( var C_NPC npc , var int ani_index )

// Определяет существует ли анимация по заданному индексу ani_index
func int Mdl_AnimationIsExists( var C_NPC npc , var int ani_index )

// Определяет активна ли у персонажа npc анимация ani_index
func int Mdl_AnimationIsActive( var C_NPC npc , var int ani_index )

// Изменяет скорость всех анимаций персонажа npc на fps
func float Mdl_SetAllAnimationsFPS( var C_NPC npc , var float fps )

// Сбрасывает скорость всех анимаций персонажа npc
func float Mdl_ResetAllAnimationsFPS( var C_NPC npc )

// Изменяет скорость заданной анимации idx персонажа npc на fps
func float Mdl_SetAnimationFPS( var C_NPC npc , var int idx , var float fps )

// Сбрасывает скорость заданной анимации ani_index персонажа npc
func float Mdl_ResetAnimationFPS( var C_NPC npc , var int ani_index )

// Отображает модель для npc, если isVisible истина, иначе скрывает
func void Mdl_SetVisible( var C_NPC npc , var int isVisible )

// Применяет или делает оверлей верхним в списке оверлеев
func void Mdl_ApplyOverlayMds_AtFirst( var string mdsName )

// Задает скорость анимаций multiplier для персонажа npc (1.0 нормальная скорость)
func void Mdl_SetNpcSpeedMultiplier( var C_Npc npc, var float multiplier )

// Сбрасывает скорость анимаций для персонажа npc
func void Mdl_ResetNpcSpeedMultiplier( var C_Npc npc )
Функции действий над персонажами - NPC
Daedalus:
// Изменяет главного героя на персонажа npc
func void Npc_SetAsHero( var C_NPC npc )

// Открывает основной инвентарь персонажа npc
func void Npc_OpenInventory( var C_NPC npc )

// Открывает инвентарь персонажа, который находится в фокусе npc
func void Npc_OpenInventorySteal( var C_NPC npc )

// Открывает основной интвентарь персонажа npc для торговли
func void Npc_OpenInventoryTrade( var C_NPC npc )

// Возвращает предмет, который находится в левой руке персонажа npc
func C_Item Npc_GetLeftHandItem( var C_Npc npc )

// Возвращает предмет, который находится в правой руке персонажа npc
func C_Item Npc_GetRightHandItem( var C_Npc npc )

// Возвращает предмет, который находится в слоте slotName персонажа npc
func C_Item Npc_GetSlotItem( var C_Npc npc )

// Помещает экземпляр класса oCVob (в тч item, npc) object в слот slotName персонажа npc.
// Параметр copyInInv определяет, должна ли копия предмета оставаться в инвентаре персонажа.
func void Npc_PutInSlot(var C_Npc npc, var string slotName, var instance object, var int copyInInv)

// Удаляет объект из слота slotName персонажа npc. Параметр dropIt в Gothic 2 определяет,
// должен ли объект выпасть из слота. В Gothic 1 параметр зарезервирован и должен быть 0.
func void Npc_RemoveFromSlot(var C_Npc npc, var string slotName, var int dropIt)
Функции действий над интерактивными объектами - MOB
Daedalus:
// Ломает объект object
func void Mob_Destroy( var instance object )

// Удаляет из контейнера моба object предмет item
func void Mob_RemoveItem( var instance object , var int item )

// Удаляет из контейнера моба object предметы item в количестве cnt
func void Mob_RemoveItems( var instance object , var int item , var int cnt )

// Добавляет в контейнер моба object предмет item
func void Mob_InsertItem( var instance object , var int item )

// Добавляет в контейнер моба object предметы item в количестве cnt
func void Mob_InsertItems( var instance object , var int item , var int cnt )

// Возвращает комбинацию взлома для моба object
func string Mob_GetLockCombination( var instance object )

// Устанавливает комбинацию взлома comb для моба object
func void Mob_SetLockCombination( var instance object , var string comb )

// Определяет заперт ли моб object
func int Mob_IsLocked( var instance object )

// Устанавливает значение locked будет ли моб object заперт
func void Mob_SetLocked( var instance object , var int locked )

// Возвращает инстанцию ключа для object, иначе null
func instance Mob_GetKeyInstance( var instance object )

// Устанавливает инстанцию ключа key для object, иначе -1
func void Mob_SetKeyInstance( var instance object , var int key )
Функции для работы с AI
Daedalus:
// Ставит вызов функции funcName в AI очередь
// и при ее вызове помещает значения slf & oth
// в глобальные self & other (соотвественно)
func void AI_CallScript(var string funcName, var C_Npc slf, var C_Npc oth)

// Создает циклический триггер, вызывающий функцию
// funcName каждые delay миллисекунд. В глобальную переменную
// SelfTrigger будет помещен триггер, вызывающий текущую функцию funcName
func C_Trigger AI_StartTriggerScript(var string funcName, var int delay)

// Таже функция, принимающая три дополнительных
// агрумента - slf, oth и vct, которые при вызове функции
// funcName будут помещены в глобальные self, other и victim (соответственно)
func C_Trigger AI_StartTriggerScriptEx(var string funcName, var int delay, var C_Npc slf, var C_Npc oth, var C_Npc vct)

// Рекомандуемые константы для функции AI_GetTriggerNPC
const int selfID   = 0;
const int otherID  = 1;
const int victimID = 2;

// Проверяет, является ли указатель pointer нулевым
func int Hlp_IsNULL(var instance pointer);

// Возвращает триггер из массива активных триггеров по индексу ID
func C_Trigger AI_GetTriggerByID(var int ID);

// Возвращает количество триггеров в массиве активных триггеров
func int AI_GetTriggersNum();

// Возвращает связанного с триггером NPC, соторый соответствует индексу 0 = self, 1 = other, 2 = victim
func C_Trigger AI_GetTriggerNPC(var int npcID);

// Возвращает связанную с триггером trigger функцию
func func AI_GetTriggerFunc(var C_Trigger trigger);

// Возвращает имя связанной с триггером trigger функции
func string AI_GetTriggerFuncName(var C_Trigger trigger);

// Возвращает следующий за startTrigger триггер, функция которого соответствует function
AI_GetNextTriggerByFunc(var C_Trigger startTrigger, var func function);

// Возвращает следующий за startTrigger триггер, имя функции которого соответствует functionName
AI_GetNextTriggerByFuncName(var C_Trigger startTrigger, var string functionName);

// Возвращает следующий за startTrigger триггер, Self-NPC которого соответствует self
AI_GetNextTriggerBySelf(var C_Trigger startTrigger, var C_Npc self);

// Возвращает следующий за startTrigger триггер, Other-NPC которого соответствует other
AI_GetNextTriggerByOther(var C_Trigger startTrigger, var C_Npc other);

// Возвращает следующий за startTrigger триггер, Victim-NPC которого соответствует victim
AI_GetNextTriggerByVictim(var C_Trigger startTrigger, var C_Npc victim);

// Возвращает следующий за startTrigger триггер, связанные NPC которого соответствуют self, other & victim соответственно
AI_GetNextTriggerByNPCs(var C_Trigger startTrigger, var C_Npc self, var C_Npc other, var C_Npc victim);
Функции для работы с парсерами - PAR
Daedalus:
// Возвращает ID парсера по его имени parName
// (см. теги и наименования парсеров/DAT файлов)
func int Par_GetParserID(var string parName);

// Возвращает ID символа symName из парсера parId
func int Par_GetSymbolID(var int parId, var string symName);

// Возвращает количество элементов символа symId парсера parId
// (если количество больше 1, то символ является массивом)
func int Par_GetSymbolLength(var int parId, var int symId);

// Возвращает int значение символа symId парсера parId
func int Par_GetSymbolValueInt(var int parId, var int symId);

// Возвращает float значение символа symId парсера parId
func float Par_GetSymbolValueFloat(var int parId, var int symId);

// Возвращает string значение символа symId парсера parId
func string Par_GetSymbolValueString(var int parId, var int symId);

// Возвращает instance значение символа symId парсера parId
func instance Par_GetSymbolValueInstance(var int parId, var int symId);

// Возвращает int значение символа symId по индексу arrayId парсера parId
func int Par_GetSymbolValueIntArray(var int parId, var int symId, var int arrayId);

// Возвращает float значение символа symId по индексу arrayId парсера parId
func float Par_GetSymbolValueFloatArray(var int parId, var int symId, var int arrayId);

// Возвращает string значение символа symId по индексу arrayId парсера parId
func string Par_GetSymbolValueStringArray(var int parId, var int symId, var int arrayId);

// Задает int значение value символу symId парсера parId
func void Par_SetSymbolValueInt(var int value, var int parId, var int symId, var int arrayId);

// Задает float значение value символу symId парсера parId
func void Par_SetSymbolValueFloat(var float value, var int parId, var int symId, var int arrayId);

// Задает string значение value символу symId парсера parId
func void Par_SetSymbolValueString(var string value, var int parId, var int symId, var int arrayId);

// Задает instance значение value символу symId парсера parId
func void Par_SetSymbolValueInstance(var instance value, var int parId, var int symId, var int arrayId);

// Задает int значение value символу symId по индексу arrayId парсера parId
func void Par_SetSymbolValueIntArray(var int value, var int parId, var int symId, var int arrayId);

// Задает float значение value символу symId по индексу arrayId парсера parId
func void Par_SetSymbolValueFloatArray(var float value, var int parId, var int symId, var int arrayId);

// Задает string значение value символу symId по индексу arrayId парсера parId
func void Par_SetSymbolValueStringArray(var string value, var int parId, var int symId, var int arrayId);
Функции для работы со строками - STR
Daedalus:
// Возвращает отформатированную строку format, в которую подставлены указанные в ней аргументы.
// Передача аргумента в строку format осуществляется литералом %, который может принимать значения:
// %s - подставляет строку
// %i - подставляет целое число
// %x - подставляет целое число в 16-ой системе
// %f - подставляет число с плавающей запятой
// %b - подставляет логическое выражение
// %p - подставляет адрес инстанции
// в других случаях подставляет следующий символ
func string Str_Format( var string format, ... )

// Возвращает строку, которая соответствует текущему языку, иначе английский вариант.
// Аргументы должны быть представлены в кодировке UTF-8! Результат будет преобразован в ANSI строку.
func string Str_GetLocalizedString( var string russian, var string english, var string german, var string polish )

// Возвращает строку, которая соответствует текущему языку, иначе английский вариант.
// Аргументы должны быть представлены в кодировке UTF-8! Результат будет преобразован в ANSI строку.
func string Str_GetLocalizedStringEx( var string russian, var string english,  var string german,  var string polish,
                                      var string czech,   var string romanian, var string italian, var string spanish );

// Преобразует строку utf8 в кодировке UTF-8 в строку ANSI с кодовой страницей codePade
func string Str_UTF8_to_ANSI( var string utf8, var int codePade )

// Возвращает кодовую страницу, которая соответствует текущему языку в системе Union
func int Str_GetCurrentCP()

// Возвращает длину строки str
func int Str_GetLength( var int str )
Функции работы с дневником - LOG
Daedalus:
// Возвращает статус записи в дневнике:
// Отсутствует - (-1)
// Free        - 0
// Running     - 1
// Success     - 2
// Failure     - 3
// Obsolete    - 4
func int Log_GetTopicStatus(var string topic)

// Возвращает страницу на которой расположена запись в дневнике.
// Отсутствует - (-1)
// Missions    - 0
// Notes       - 1
// All         - 2
func int Log_GetTopicSection(var string topic)
Функции работы с вобами (прокси-классы) - VOB
Daedalus:
// Возвращает текущую позицию объекта в мире
func C_Position Vob_GetVobPosition( var C_Vob vob )

// Задает текущую позицию объекта в мире
func void Vob_SetVobPosition( var C_Vob vob, var C_Position pos )

// Возвращает универсальные данные объекта zCVob
func C_Vob_Data Vob_GetVobData( var C_Vob vob )

// Задает универсальные данные объекта zCVob
func void Vob_SetVobData( var C_Vob vob, var C_Vob_Data data )

// Возвращает данные объекта zCVobLight
func C_Light_Data Vob_GetLightData( var C_Vob vobLight )

// Задает данные объекта zCVobLight
func void Vob_SetLightData( var C_Vob vobLight, var C_Light_Data data )

// Очищает список цветов анимации для источника света
func void Vob_ClearLightAniList( var C_Vob vobLight )

// Добавляет позицию в список цветов
func void Vob_AddLightAniColor( var C_Vob vobLight, var C_Color col )

// Добавляет позицию в список цветов
func void Vob_AddLightAniColorRGB( var C_Vob vobLight, var int r, var int g, var int b )

// Возвращает данные объекта oCMOB
func C_Mob_Data Vob_GetMobData( var C_Vob mob )

// Задает данные объекта oCMOB
func void Vob_SetMobData( var C_Vob mob, var C_Mob_Data data )

// Возвращает данные объекта oCMobInter
func C_MobInter_Data Vob_GetMobInterData( var C_Vob mobInter )

// Задает данные объекта oCMobInter
func void Vob_SetMobInterData( var C_Vob mobInter, var C_MobInter_Data data )

// Возвращает данные объекта oCMobLockable
func C_MobLockable_Data Vob_GetMobInterData( var C_Vob mobLock )

// Задает данные объекта oCMobLockable
func void Vob_SetMobInterData( var C_Vob mobLock, var C_MobLockable_Data data )
Остальные функции
Daedalus:
// Находит все объекты C_MenuItem по маске имени ptr и автоматически помещает в текущую инстанцию меню
func void Menu_SearchItems( var string ptr )

// Открывает ссылку в браузере по умолчанию или в Steam Overlay (если доступен)
func void Open_Link( var string url )
Внешние переменные
var int DIA_CurrentInstance - содержит ID текущей инстанции C_Info. Может значительно упростить код или сделать его универсальнее. Следует определить в скриптах. Сценарии использования:
Daedalus:
Info_ClearChoices(DIA_CurrentInstance)
Info_AddChoice(DIA_CurrentInstance, ..., ...)
Npc_KnowsInfo(hero, DIA_CurrentInstance)
Создание функции-обертки на основе этой переменной:
Daedalus:
func int C_HeroKnowsCurrentInfo()
{
    return Npc_KnowsInfo(hero, DIA_CurrentInstance);
};

var string DIA_CurrentName - содержит имя текущей инстанции C_Info. Может быть полезно в отладочных целях. Следует определить в скриптах. Сценарии использования:
Daedalus:
Hlp_PrintConsole(DIA_CurrentName)
Hlp_PrintConsole(Str_Format("%s[%s]", DIA_CurrentName, self.name))
Hlp_StrCmp(DIA_CurrentName, "DIA_DiegoOw_Teach")
Вспомогательные возможности
Daedalus:
// Если функция определена в скриптах, то она будет вызываться каждый кадр
func event GameLoop()

// А эта функция будет вызвана при входе в игровое меню
func event GameInit()

Приложение 2 - коды клавиш:
Виртуальные клавиши
KEY_ESCAPE
KEY_1
KEY_2
KEY_3
KEY_4
KEY_5
KEY_6
KEY_7
KEY_8
KEY_9
KEY_0
KEY_MINUS
KEY_EQUALS
KEY_BACK
KEY_TAB
KEY_Q
KEY_W
KEY_E
KEY_R
KEY_T
KEY_Y
KEY_U
KEY_I
KEY_O
KEY_P
KEY_LBRACKET
KEY_RBRACKET
KEY_RETURN
KEY_LCONTROL
KEY_A
KEY_S
KEY_D
KEY_F
KEY_G
KEY_H
KEY_J
KEY_K
KEY_L
KEY_SEMICOLON
KEY_APOSTROPHE
KEY_GRAVE
KEY_LSHIFT
KEY_BACKSLASH
KEY_Z
KEY_X
KEY_C
KEY_V
KEY_B
KEY_N
KEY_M
KEY_COMMA
KEY_PERIOD
KEY_SLASH
KEY_RSHIFT
KEY_MULTIPLY
KEY_LMENU
KEY_SPACE
KEY_CAPITAL
KEY_F1
KEY_F2
KEY_F3
KEY_F4
KEY_F5
KEY_F6
KEY_F7
KEY_F8
KEY_F9
KEY_F10
KEY_NUMLOCK
KEY_SCROLL
KEY_NUMPAD7
KEY_NUMPAD8
KEY_NUMPAD9
KEY_SUBTRACT
KEY_NUMPAD4
KEY_NUMPAD5
KEY_NUMPAD6
KEY_ADD
KEY_NUMPAD1
KEY_NUMPAD2
KEY_NUMPAD3
KEY_NUMPAD0
KEY_DECIMAL
KEY_OEM_102
KEY_F11
KEY_F12
KEY_F13
KEY_F14
KEY_F15
KEY_KANA
KEY_ABNT_C1
KEY_CONVERT
KEY_NOCONVERT
KEY_YEN
KEY_ABNT_C2
KEY_NUMPADEQUALS
KEY_PREVTRACK
KEY_AT
KEY_COLON
KEY_UNDERLINE
KEY_KANJI
KEY_STOP
KEY_AX
KEY_UNLABELED
KEY_NEXTTRACK
KEY_NUMPADENTER
KEY_RCONTROL
KEY_MUTE
KEY_CALCULATOR
KEY_PLAYPAUSE
KEY_MEDIASTOP
KEY_VOLUMEDOWN
KEY_VOLUMEUP
KEY_WEBHOME
KEY_NUMPADCOMMA
KEY_DIVIDE
KEY_SYSRQ
KEY_RMENU
KEY_PAUSE
KEY_HOME
KEY_UP
KEY_PRIOR
KEY_LEFT
KEY_RIGHT
KEY_END
KEY_DOWN
KEY_NEXT
KEY_INSERT
KEY_DELETE
KEY_LWIN
KEY_RWIN
KEY_APPS
KEY_POWER
KEY_SLEEP
KEY_WAKE
KEY_WEBSEARCH
KEY_WEBFAVORITES
KEY_WEBREFRESH
KEY_WEBSTOP
KEY_WEBFORWARD
KEY_WEBBACK
KEY_MYCOMPUTER
KEY_MAIL
KEY_MEDIASELECT
Логические клавиши
GAME_LEFT
GAME_RIGHT
GAME_UP
GAME_DOWN
GAME_ACTION
GAME_SLOW
GAME_ACTION2
GAME_WEAPON
GAME_SMOVE
GAME_SMOVE2
GAME_SHIFT
GAME_END
GAME_INVENTORY
GAME_LOOK
GAME_SNEAK
GAME_STRAFELEFT
GAME_STRAFERIGHT
GAME_SCREEN_STATUS
GAME_SCREEN_LOG
GAME_SCREEN_MAP
GAME_LOOK_FP
GAME_LOCK_TARGET
GAME_PARADE
GAME_ACTIONLEFT
GAME_ACTIONRIGHT
GAME_LAME_POTION
GAME_LAME_HEAL

Приложение 3 - теги и наименования парсеров/DAT файлов:
Эти теги используются в блоке META и консольных командах.
Используйте ПЕРВЫЙ столбец в качестве аргумента.
HTML:
Тег     Парсер            Имя DAT
---     ------            -------
Game    parser            Gothic.dat
SFX     parserSoundFX     SFX.dat
PFX     parserParticleFX  ParticleFX.dat
VFX     parserVisualFX    VisualFX.dat
Camera  parserCamera      Camera.dat
Menu    parserMenu        Menu.dat
Music   parserMusic       Music.dat

Приложение 4 - теги и наименования движков:
Эти теги используются в блоке META и консольных командах.
Используйте ПЕРВЫЙ столбец в качестве аргумента.
HTML:
Тег   Движок          Альтернативное название
---   -----           -----------------------
G1    Gothic I        Gothic I Classic
G1A   Gothic Sequel   Gothic I Addon
G2    Gothic II       Gothic II Classic
G2A   Gothic II NoTR  Gothic II Addon

V. Исправления:
Несколько исправлений оригинальной игры:
1. При создании инстанции в item помещается текущий предмет
2. При вызове on_equip и on_unequip в item помещается текущий предмет
3. При загрузке DAT файла плагин восстанавливает иерархию символов
4. При загрузке сохранения плагин пропускает несуществующие в скриптах символы
 
Последнее редактирование:

N1kX


Модостроитель
Регистрация
13 Ноя 2009
Сообщения
6.180
Благодарности
5.708
Баллы
910
Тогда нужен ещё поиск и доступ к вобу типо такого
Daedalus:
    var zCMover ValleyTree;
    ValleyTree = _^(MEM_SearchVobByName("VALLEY_SHOWCASE_MOVER_01")); //функция из Ikarus

А то сейчас инстанцию непися можно указать, а воба нет. Можно указать, если смотришь или взял в фокус этот воб.

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

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.301
Благодарности
4.641
Баллы
625
N1kX, Wld_FindVob чем не устроил?
 

STARK

Участник форума
Регистрация
20 Янв 2011
Сообщения
566
Благодарности
142
Баллы
220
N1kX, Разница в том, что код выглядел бы так:
C++:
  int Mdl_SetVisible() {
    zCParser* par = zCParser::GetParser();
    zCVob* vob;
    int visible;
    par->GetParameter( visible );
    vob = (zCVob*)par->GetInstance();
    vob->showVisual = visible;
    return 0;
  }
Где при false коллизии не считались бы по умолчанию.
Я скомпилил плагин с этим кодом. Но вопрос, как описать тип Воб'а в дедале?
Поставил знаки вопроса там, где неясно:)

func void Mdl_SetVisible( var ????? vob, var int isVisible );

????? vob1 = Wld_FindVob( vobname );
Mdl_SetVisible( vob1 , 0 );
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.301
Благодарности
4.641
Баллы
625
Daedalus:
Суй ему че хочешь. Главное чтобы был экземпляром класса:

class C_OBJECT { };
var C_OBJECT vob ...
 

STARK

Участник форума
Регистрация
20 Янв 2011
Сообщения
566
Благодарности
142
Баллы
220
Gratt, прошёл дальше, теперь пишет: Неопределенная функция.
В этом месте: vob1 = Wld_FindVob(vobname);
Прототип в Externals есть.
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.086
Благодарности
1.910
Баллы
320
Gratt, прошёл дальше, теперь пишет: Неопределенная функция.
В этом месте: vob1 = Wld_FindVob(vobname);
Прототип в Externals есть.
У меня сурсер компилит:
Daedalus:
class C_OBJECT { };

func void Wtf()
{
    var C_OBJECT vob;
    var string vobName;
    vobName = "WTF";
    vob = Wld_FindVob(vobName);
};

// Externals.d
func instance Wld_FindVob(var string vobName);
 

STARK

Участник форума
Регистрация
20 Янв 2011
Сообщения
566
Благодарности
142
Баллы
220
Можно ещё вот так:
vob1 = Wld_FindVob("vobname");

Теперь ругается на Mdl_SetVisible(vob1,1);
Пишет - ожидается ')' , но со скобками то всё нормально.
Прототип:
func void Mdl_SetVisible( var instance vob , var int isVisible );
 

N1kX


Модостроитель
Регистрация
13 Ноя 2009
Сообщения
6.180
Благодарности
5.708
Баллы
910
Можно ещё вот так:
vob1 = Wld_FindVob("vobname");

Теперь ругается на Mdl_SetVisible(vob1,1);
Пишет - ожидается ')' , но со скобками то всё нормально.
Прототип:
func void Mdl_SetVisible( var instance vob , var int isVisible );
Подожди обновление плагина. Там уже все готово.
Даже пример будет для тебя на Г1.
Примерно вот так можно скрывать вобы/мобы
Не забудь, что vobname должно быть определено в мире заранее
На примере при нажатии кнопки 9, будет скрываться из мира лебедка в самом начале. Фокусное имя пропадет, когда сохранишься/запишешься.
Тебе же останешься просто взять часть из под функции Hlp_KeyToggled
Daedalus:
// zCVob
class C_VOB { };

// zCVob
class C_VOB_DATA
{
  var string Name;
  var float VisualAlpha;
  var int ShowVisual;
  var int DrawBBox3D;
  var int VisualAlphaEnabled;
  var int PhysicsEnabled;
  var int IgnoredByTraceRay;
  var int CollDetectionStatic;
  var int CollDetectionDynamic;
  var int CastDynShadow;
  var int LightColorStatDirty;
  var int LightColorDynDirty;
  var int SleepingMode;
  var int DontWriteIntoArchive;
};

// oCMob
class C_MOB_DATA
{
  var string VisibleName;
  var int Hitpoints;
  var int Damage;
  var int IsDestroyed;
  var int Moveable;
  var int Takeable;
  var int FocusOverride;
  var int SndMat;
  var string VisualDestroyed;
  var string OwnerStr;
  var string OwnerGuildStr;
  var int Owner;
  var int OwnerGuild;
  var int FocusNameIndex;
};

//функция, которая работает каждый кадр в игре
func void GameLoop()
{
    //EVT_OW_STARTLOCKOUT_MOVER
    //EVT_OW_LOCKOUT_TRIGGER

    //если нажата кнопка 9
    if (Hlp_KeyToggled(10))
    {
        //определяем воб и ищем его в игре
        var C_VOB vob; vob = Wld_FindVob("EVT_OW_LOCKOUT_TRIGGER");
        
        //получаем данные от найденного воба
        var C_MOB_DATA mobData; mobData = Vob_GetMobData(vob);
        var C_VOB_DATA vobData; vobData = Vob_GetVobData(vob);
        
        //отладка
        PrintScreen    (inttostring(vobData.ShowVisual), 15,15,"FONT_OLD_10_WHITE.TGA",3);
        PrintScreen    (inttostring(vobData.CollDetectionStatic), 15,17,"FONT_OLD_10_WHITE.TGA",3);
        PrintScreen    (inttostring(vobData.CollDetectionDynamic), 15,19,"FONT_OLD_10_WHITE.TGA",3);
        PrintScreen    (mobData.VisibleName, 15,21,"FONT_OLD_10_WHITE.TGA",3);
        
        //убираем видимость воба
        vobData.ShowVisual = !vobData.ShowVisual;
        
        //убираем показ фокусного имени
        mobData.VisibleName = "";
        
        //записываем изменения в игру
        Vob_SetVobData(vob, vobData);
        Vob_SetMobData(vob, mobData);
    };
};
 
Последнее редактирование:

STARK

Участник форума
Регистрация
20 Янв 2011
Сообщения
566
Благодарности
142
Баллы
220
N1kX, хотелось простой команды, а тут выросло до такого:)
 

STARK

Участник форума
Регистрация
20 Янв 2011
Сообщения
566
Благодарности
142
Баллы
220
У меня вопрос по поводу AI_StartTriggerScript("func1", 60000) . Как прекратить циклический вызов функции, если цель была достигнута?
 

N1kX


Модостроитель
Регистрация
13 Ноя 2009
Сообщения
6.180
Благодарности
5.708
Баллы
910
У меня вопрос по поводу AI_StartTriggerScript("func1", 60000) . Как прекратить циклический вызов функции, если цель была достигнута?

Пока что можешь просто прекратить его работу в теле функции
Daedalus:
if (действие == TRUE)
{
    return Loop_end; //завершить работу
};

Пример же можешь скачать с сообщения 8
Там правда под Г2, но смысл как написать ясен
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.301
Благодарности
4.641
Баллы
625
Добавлены пространства имен. Само понятие пространства имен можно почитать тут.
Плагин предоставляет аналог для скриптового языка Daedalus.

Общий смысл использования пространств имен заключается в том, чтобы создать внедряемым скриптам собственную защищенную от переопределения область, чтобы создавать в ней объекты с любыми удобными именами не боясь, что такие объекты уже кто-то определил. Имя пространства должно быть уникальным и максимально полно отражать суть Вашего модуля. Это гарантирует, что данные внутри модуля останутся целыми.
Вы можете использовать как разные пространства имен, так и единое для всех Ваших модулей.

1. Объявление пространства имен
A. Классический вариант с явным определением блока
Daedalus:
namespace zTestNamespace
{
    var int Variable01;
 
    func void Func01() { };
};


Б. Через блок META с указанием пространства имен по умолчанию для всего документа.
Технически ничем не отличается от классического варианта.
Daedalus:
META
{
    Namespace = zTestNamespace;
};

var int Variable01;

func void Func01() { };


В. Определение вложенных пространств друг в друга
Daedalus:
META
{
    Namespace = zTestNamespace;
};

namespace Definitions
{
    namespace Variables
    {
        var int Variable01;
    };
 
    func void Func01()
    {
 
    };
};

2. Обращение к данным пространств имен. Обращение К данным пространства и ИЗ него происходит оператором ':'
А. Обращение к данным пространства
Daedalus:
namespace zTestNamespace01
{
    func void Func01()
    {
        Hlp_MessageBox("#1");
    };
};

namespace zTestNamespace02
{
    func void Func01()
    {
        Hlp_MessageBox("#2");
        zTestNamespace01:Func01(); // Вызов функции из другого пространства имен
    };
};

func event GameInit()
{
    // Вызов одноименных функций
    // от разных пространств имен
    zTestNamespace01:Func01();
    zTestNamespace02:Func01();
};


Б. Обращение к вышестоящим данным происходит при помощи добавления ':' перед именем объекта.
Daedalus:
func void Func01()
{
    Hlp_MessageBox("#1");
};

namespace zTestNamespace01
{
    func void Func01()
    {
        Hlp_MessageBox("#2");
    };

    namespace zTestNamespace02
    {
        func void Func01()
        {
            Hlp_MessageBox("#3");
        };
 
        namespace zTestNamespace03
        {
            func void Func01()
            {
                Hlp_MessageBox("#4");
            };
   
            func void ShowMessages()
            {
                :::Func01(); // Вызвать функцию на 3 уровня выше
                ::Func01();  // Вызвать функцию на 2 уровня выше
                :Func01();   // Вызвать функцию на 1 уровень выше
                Func01();    // Вызвать функцию того же уровня
            };
        };
    };
};

func event GameInit()
{
    zTestNamespace01:zTestNamespace02:zTestNamespace03:ShowMessages();
};


В. Обращение к глобальным данным при отсутствии одноименных в пространстве имен. У пространств имен есть приоритеты, по которым происходит выборка данных, к которым следует обращаться. Сначала ищутся данные внутри текущего пространства. А затем - глобальные.
Daedalus:
func void Func01()
{
    Hlp_MessageBox("#1");
};

namespace zTestNamespace01
{
    func void ShowMessages()
    {
        Func01(); // Вызов глобальной или вышестоящей функции без оператора ':'
    };
};

func event GameInit()
{
    zTestNamespace01:ShowMessages();
};

3. Особенности областей видимости
А. Объявление объекта внутри пространства имен, но физически на уровень выше.
Такой подход может пригодиться для хуков на данные из глобального пространства не выходя за пределы текущего пространства имен.
К тому же такой объект будет видим из глобального пространства.
Несмотря на то, что такой объект физически лежит в пространстве на уровень выше, внутри его тела область видимости по прежнему останется в пределах текущего пространства имен.
Daedalus:
namespace zTestNamespace01
{
    const string Test_ItemName = "Одежда пирата - перехваченная";

    // Объект объявленный оператором : будет помещен в
    // пространство на столько уровней выше, сколько
    // операторов указано в объявлении.
    instance :ITAR_PIR_L_Addon(C_Item)
    {
        ITAR_PIR_L_Addon_Old();
        name = Test_ItemName; // не смотря на то, что инстанция объявлена в глобальное
                              // пространство, мы все еще можем свободно обращаться к
                              // данным в текущем пространстве имен
    };
};

Б. Пространства имен - это абстракция. Их задача привести имена объектов к общему виду. Можно без проблем обратиться к объекту из другого пространства имен без явного обращения к этому пространству, если его имя эквивалентно текущему.
Daedalus:
namespace zTestNamespace01
{
    namespace zTestNamespace02
    {
        func void Test01()
        {
            Hlp_MessageBox("#1");
        };
    };
};

namespace zTestNamespace01
{
    namespace zTestNamespace02
    {
        func void ShowMessages()
        {
            Test01();
        };
    };
};

func event GameInit()
{
    zTestNamespace01:zTestNamespace02:ShowMessages();
};


В. Функции-события всегда определяются в глобальном пространстве, поскольку должны поддерживать постоянный постоянный уровень видимости объекта. Тем не менее, по правилу, описанному выше, такой объект может без проблем использовать объекты в пространстве, в котором был объявлен.
Daedalus:
namespace zTestNamespace01
{
    func void ShowMessages()
    {
        Hlp_MessageBox("#1");
    };

    func event GameInit()
    {
        ShowMessages();
    };
};
 

Fabiosek

Участник форума
Регистрация
17 Июн 2017
Сообщения
147
Благодарности
47
Баллы
190
Externals like "Wld_ChangeLevel" have been removed in the new version? MarvinHelper doesn't detect them, so I don't think they are there.
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.301
Благодарности
4.641
Баллы
625
In the new version externals are not created if they are not used in scripts. Sad for MarvinHelper...
Аа, учту. Добавь пока GetProcAddress("LoadExternalFunc"), чуть позже обновлю с этой функцией, сигнатура будет такая:
bool LoadExternalFunc( const string & funcName )
Вызывай ее перед GetIndex, либо если он вернет -1
 
Сверху Снизу