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

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

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

Union - мини плагины

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.086
Благодарности
1.904
Баллы
320
Здесь будут собираться плагины, создание отдельной темы для которых было бы излишним.


Плагины, распространяемые с помощью exe-инсталлятора, можно деинсталлировать им же.
Плагины, распространяемые в виде vdf тома, следует помещать в папку Data/Plugins/.
Каждый плагин, если не указано иного, должен работать на любых версиях игры: Gothic I Classic (G1), Gothic I Sequel (G1A), Gothic II Classic (G2), Gothic II Addon (G2A).
Код плагинов и сами плагины, автором которых являюсь я, можно распространять и использовать без ограничений.


При наличии нескольких одноименных вложений в одном посте следует скачивать последнее. Удалять старые вложения прав нет.

  1. Плагин zNoFocusFlag (Автор: Slavemaster). Для модостроителей. Делает NPC с флагом NPC_FLAG_NFOCUS полностью прозрачными для фокуса ГГ.
  2. Плагин AlterDamage (Автор: Slavemaster). Есть проблемы с совместимостью. Меняет формулы урона. Добавляет всплывающие сообщения о нанесённом уроне.
  3. Плагин zSavesBackuper (Автор: Slavemaster). Осуществляет резервное копирование сделанных сохранений.
  4. Плагин Union_Hotbar_1.0k (Автор: Haart). Добавляет панель быстрого доступа для предметов из инвентаря.
  5. Плагин QuickLoot (Автор: Slavemaster). Быстрый сбор предметов при помощи правой кнопки мыши.
  6. Плагин MarvinHelper (Автор: Slavemaster). Дополнительные возможности для консоли разработчика.
  7. Плагин MunitionInfo (Автор: Xeдин). Отображает кол-во стрел/болтов в инвентаре.
  8. Плагин zAsyncSaveLoad. (Автор: Slavemaster).Плагин делает окно игры отзывчивым к сочетанию Alt+Tab во время загрузки, сохранения и просмотра видео.
  9. Плагин Union_SaveLoadManager_i (Автор: Haart). Позволяет сохраняться в разные слоты без лишних усилий.
  10. Плагин zBugFixes (Автор: Slavemaster). Плагин содержит исправления некоторых ошибок движка Готики.
  11. Плагин zTorchControl (Автор: Slavemaster). Плагин предотвращает удаление горящих факелов при загрузке, а также позволяет их использовать по горячей клавише.
  12. Плагин zMarkItems (Автор: Slavemaster).Плагин осуществляет маркировку предметов внутри ячеек инвентаря.
  13. Плагин zMiscUtils (Автор: Slavemaster). Плагин реализует множество функций, не объединённых единой тематикой.
  14. Плагин SimpleRegen (Автор: Xeдин). Плагин реализует простой алгоритм регенерации жизни и/или маны.
  15. Плагин Union_Thief_Helper (Автор: Xeдин). Помощник вора. Маркировка не обворованных и обворованных NPC и подсказки при взломе.
  16. Плагин Union_XP_Bar (Автор: Xeдин). Плагин для отображения полоски опыта.
  17. Плагин OdyAlt (Автор: N1kX). Подробное многостраничное меню навыков для мода Одиссея 2.6.4.
  18. Плагин OdyAltMenu (Автор: N1kX). Подробное многостраничное меню навыков для мода Одиссея 2.7.0+.
  19. Плагин zUtilities (Автор: Franisz). Несколько удобных функций.
  20. Плагин oDamageHelper (Автор: Gratt). Для модостроителей. Позволяет менять наносимый урон с помощью скриптов.
  21. Плагин Union_DamageInfo (Автор: Xeдин). Плагин выводит урон по врагам и по главному герою.
  22. Плагин Union_RandomRainDX11 (Автор: Haart). При использовании с DX11 дождь будет начинаться в случайное время суток.
  23. Плагин Union_Ext_Stat (Автор: Xeдин). Небольшая дополнительная статистика по количеству убитых, выпитых зелий, квестам и времени игры.
  24. Плагин Union_Show_FPS (Автор: Xeдин). Простенький плагин на показ FPS для всех версий Готики, с возможностью отключения в меню и задания позиции отображения.
  25. Плагин zImprovedLegacyFrying (Автор: Slavemaster). Добавляет диалоги жарки мяса для костра и плиты.
  26. Плагин Union_Keep_Last_Save (Авторы: Gratt, Xeдин). Плагин для удобной работы с последним слотом записи.
  27. Плагин EquippedWeapon (Автор: MEG@VOLT). Для модостроителей. Добавляет функции экипировки / деэкипировки оружия.
  28. Плагин TradeMissItems (Автор: MEG@VOLT). Плагин создан для защиты от случайной продажи квестовых предметов.
  29. Плагин zUnstuckSlidingPlayer (Автор: Slavemaster). Автоматическая телепортация игрока при длительном скольжении.
  30. Плагин KillMeatBugs (Автор: MEG@VOLT). Позволяет затаптывать мясных жуков.
  31. Плагин ezFistMode (Автор: fyryNy). Хоткей для перехода в режим кулачного боя.
  32. Плагин Union_2hSplash (Автор: Strange). Урон по площади для двуручного оружия.
 
Последнее редактирование:

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.409
Благодарности
3.239
Баллы
525
Предполагается ли здесь обсуждение плагинов?
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.086
Благодарности
1.904
Баллы
320
zNoFocusFlag
Позволяет делать персонажей прозрачными для фокуса игрока
Google Drive
Функции плагина
  • Добавляет обработку флага var int NPC_FLAG_NFOCUS = 1 << 18
  • Меняет обработку поля C_NPC.noFocus (Готика II)
 
Последнее редактирование:

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.409
Благодарности
3.239
Баллы
525
Я к чему спросил, если будет иметь место обсуждение, а плагины будут появляться регулярно, то первый пост темы нужно оставить для списка плагинов, иначе искать их по теме будет не удобно.

Предложение. Если мини-плагинов будет много, то неплохо бы прибавлять к названию vdf-тома определённый префикс. Например, Union_ (Union_FullFocusName.vdf, Union_NoFocusFix.vdf и т. д.).

Можно ли как-то отключать некоторые фиксы внутри одного мини-плагина? Например, как по мне, название предмета в фокусе, взятое из описания предмета, не всегда уместно. В описании иногда встречается довольно длинная надпись, что мало подходит для надписи в фокусе. А вот то, что появилась надпись у валяющегося на земле горящего факела - это здорово.

Кстати, о горящем факеле. Движок в оригинале не отображает его название в фокусе. Это связано с тем, что визуалом является zen-файл, а не 3ds, как обычно. У тебя в плагине учитывается именно название инстанции горящего факела, и по нему происходит подмена, или как? Это я к тому, что моддеры могут использовать и другие предметы с визуалом типа zen.
 

Test Level

Участник форума
Регистрация
1 Ноя 2011
Сообщения
1.770
Благодарности
557
Баллы
275
Я к чему спросил, если будет иметь место обсуждение, а плагины будут появляться регулярно, то первый пост темы нужно оставить для списка плагинов, иначе искать их по теме будет не удобно.
Я бы к этому еще добавил краткое описание того, что делает плагин.

Slavemaster, вообще, там 2-3 в один объединить реально?
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.086
Благодарности
1.904
Баллы
320
а плагины будут появляться регулярно,
Ну вот этого не могу обещать. Ковыряние движка методом тыка - занятие не быстрое и не всегда успешное. Тут уж как получится. Я просто не хотел плодить темы с околонулевым обсуждением, поэтому создал эту. А шапку отредактировал.
@Slavemaster, вообще, там 2-3 в один объединить реально?
Я не проверял, но по идее нужно просто в один vdf/mod все перепаковать.
 
Последнее редактирование:

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.301
Благодарности
4.638
Баллы
625
Насчёт вдф я думаю может тогда автоупаковщик сделать? Выбираешь нужные плагины и он сует их в том. А ненужные исключает. Только надо удобный интерфейс придумать....
 

Test Level

Участник форума
Регистрация
1 Ноя 2011
Сообщения
1.770
Благодарности
557
Баллы
275
Я не проверял, но по идее нужно просто в один vdf/mod все перепаковать.
Это я уже скорее для себя спросил. Допустим, мне надо плагин a, b, d,k и т.д - это же задница, делать столько томов в моде.
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.086
Благодарности
1.904
Баллы
320
В описании иногда встречается довольно длинная надпись, что мало подходит для надписи в фокусе.
Может сделать проверку на длину описания? Если слишком большое, то использовать name.
У тебя в плагине учитывается именно название инстанции горящего факела, и по нему происходит подмена, или как?
У факела тоже дескрипшн брал. Движок даже не пытается имя горящего факела выводить, поэтому отрисовал надпись самостоятельно
Это я к тому, что моддеры могут использовать и другие предметы с визуалом типа zen.
По-моему у факела был нулевой размер bbox-а. Если это характерная особенность zen-визуалов, то можно ввести проверку и выводить надписи для таких предметов. Однако надо будет определить, где выводить надпись, ведь, вероятно, реальный размер объекта я определить не смогу.
 
Последнее редактирование:

ElderGamer


Модостроитель
Регистрация
16 Апр 2008
Сообщения
4.409
Благодарности
3.239
Баллы
525
По-моему у факела был нулевой размер bbox-а.
Если вставить горящий факел в Спейсере, то у zenа действительно отсутствует бокс.
TorchBurning-1.jpg

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

Если это характерная особенность zen-визуалов, то можно ввести проверку и выводить надписи для таких предметов.
Скорее всего, это именно особенность таких предметов.

Однако надо будет определить, где выводить надпись, ведь, вероятно, реальный размер объекта я определить не смогу.
Гм, логичное построение zenа - это когда один объект, составляющий его, взят в качестве материнского, а остальные привязаны к нему. В случае с горящим факелом, материнским объектом является меш факела. Можно ли при выводе надписи отталкиваться от бокса такого объекта?
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.086
Благодарности
1.904
Баллы
320
Union_AlterDamage
Изменение расчёта урона
Google Drive
Из-за обширных изменений несовместим со многими модами
Функции плагина
  • Изменение формулы урона
  • Отображение нанесённого урона
  • Стабилизирование вероятности нанесения критического удара
Конфигурация
INI:
[UNION_ALTERDAMAGE]
EnablePopupDamage=1
; ... активирует (1) или деактивирует (0) сообщения о нанесённом уроне

RequiredAttrScaling=0
; ... активирует (1) или деактивирует (0) скалирование арбалетов от силы
; ... и шпаг от ловкости

HumanFistDamageMult=0.5
; ... сила ударов кулаками умножается на это значение

MeleeRawDamageMult=1
; ... значение для скалируемых типов урона умножаются
; ... на это значения

MeleeMissMult=0.5
; ... скалируемые типы урона умножаются на это значения
; ... если шанс от владения оружием не сработал

ArmorSoftCap=0.600000024
BeyondSoftCapArmorScaling=0.300000012
; ... когда защита достигает значения ArmorSoftCap * <входящий урон>,
; ... каждая дополнительная единица защиты снижает урон на
; ... BeyondSoftCapArmorScaling, а не на 1 как в оригинале

TwoHandedMult=1.35000002
; ... итоговый урон от двуручного оружия умножается на это значение

ComboChanceAdd=8
; ... дополнительный шанс крита за каждый удар из комбинации (процент от базового владения)

ComboDamageAdd=0.0799999982
; ... увеличение итогового множителя за каждый удар из комбинации

RandomStability=0.5
; ... текущая последовательность промахов/попаданий влияет на успешность следующего удара
; ... поставьте 0, чтобы работал обычный рандом
; ... поставьте 100 и тогда даже 1% нечестности определит судьбу последующего удара
; ... (пример) когда установлено 0.5 промах с 20% владения увеличит успешность следующего удара до 30% (20 + 20 * RandomStability)

FireDotDamageMult=0.5
FireDotDuration=5
; ... огненные заклинания наносят дополнительный урон в размере
; ... FireDotDamageMult * <damage dealt> в течении FireDotDuration секунд

MinDamageVarName=#
; ... переменная для минимального урона. Если существует, должна иметь
; ...  корректное целочисленное значение
INI:
[UNION_ALTERDAMAGE]
EnablePopupDamage=1
; ... enables (1) or disables (0) popup damage messages

RequiredAttrScaling=0
; ... enables (1) or disables (0) crossbows scaling from strength and
; ... smallswords scaling from dexterity

HumanFistDamageMult=0.5
; ... human's fist attack power is premultiplied by this value

MeleeRawDamageMult=1
; ... scalable types of damage dealed by attacks with melee weapon
; ... are premultiplied by this value

MeleeMissMult=0.5
; ... when hitchance roll fails, scalable types of damage dealed by attacks
; ... with melee weapon are premultiplied by this value

ArmorSoftCap=0.600000024
BeyondSoftCapArmorScaling=0.300000012
; ... when armor exceeds ArmorSoftCap * <incoming damage>,
; ... each additional armor point
; ... give BeyondSoftCapArmorScaling protection, instead of 1

TwoHandedMult=1.35000002
; ... power of attacks with twohanded weapon is postmultiplied by this value

ComboChanceAdd=8
; ... hitchance bonus per combo hit (based on base hitchance)

ComboDamageAdd=0.0799999982
; ... damage postmultiplier addition per combo hit

RandomStability=0.5
; ... current hit/miss sequence unfairness affects the next hit chance
; ... set to 0 for usual vanilla random
; ... set to 100 and even 1% of unfairness will determine the next hit/miss
; ... (ex.) when set to 0.5 the miss with 20% hitchance will increase
; ... the next hitchance to 30%

FireDotDamageMult=0.5
FireDotDuration=5
; ... fire spells apply additional damage in an amount equal to
; ... FireDotDamageMult * <damage dealt> during FireDotDuration seconds
; ... при FireDotDuration = -1 активируется оригинальная система горения

MinDamageVarName=#
; ... min damage variable name. If exists, must be valid integer.
Исходные коды
 
Последнее редактирование:

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.086
Благодарности
1.904
Баллы
320
Плагин Loops
Добавляет методы для свободной индексации массивов, а так же циклы While и ForEach.
C++:
int GetArrInt(string,int);
void SetArrInt(string,int,int);
float GetArrFloat(string,int);
void SetArrFloat(string,int,float);
string GetArrStr(string,int);
void SetArrStr(string,int,string);
void While(func);
void FillArrInt(string,int);
void FillArrFloat(string,float);
void FillArrStr(string,string);
void ForEachInt(string,func);
void ForEachFloat(string,func);
void ForEachStr(string,func);
void AlterEachInt(string,func);
void AlterEachFloat(string,func);
void AlterEachStr(string,func);
Единственный тест, который я провел... Платформа Готика2 НВ, компиляция GothicSourcer-ом:
C++:
var string ATR_NAMES[ATR_INDEX_MAX];
var float FLOAT_ARR[8];
var int INDEXES[ATR_INDEX_MAX];
var int SORTED;
var string SINGLE_STRING[1];
var float SINGLE_FLOAT[1];
var int SINGLE_INT[1];
var int PrintY;

func void Swap(var int leftIndex, var int rightIndex)
{
    var int tempIndex;
    tempIndex = GetArrInt("INDEXES", leftIndex);
    SetArrInt("INDEXES", leftIndex, GetArrInt("INDEXES", rightIndex));
    SetArrInt("INDEXES", rightIndex, tempIndex);
};

func int LoopBody_FillIndexes(var int index)
{
    if (index == ATR_INDEX_MAX)
    {
        return FALSE;
    };
    
    SetArrInt("INDEXES", index, index);
    return TRUE;
};

func int LoopBody_SortImpl(var int index)
{
    if (index == ATR_INDEX_MAX)
    {
        return FALSE;
    };
    
    if (index > 0)
    {
        if (GetArrInt("hero.attribute", GetArrInt("INDEXES", index - 1)) < GetArrInt("hero.attribute", GetArrInt("INDEXES", index)))
        {
            Swap(index - 1, index);
            SORTED = FALSE;
        };
    };
    
    return TRUE;
};

func int LoopBody_Sort(var int index)
{
    SORTED = TRUE;
    While(LoopBody_SortImpl);
    return !SORTED;
};

func void LoopBody_PrintAttributes(var int index, var int realIndex)
{
    var string text;
    text = GetArrStr("ATR_NAMES", realIndex);
    text = ConcatStrings(text, ": ");
    text = ConcatStrings(text, IntToString(GetArrInt("hero.attribute", realIndex)));
    
    PrintScreen(text, -1, PrintY, FONT_ScreenSmall, 20);    
    PrintY += 2;
};

func string LoopBody_AlterNames(var int index, var string name)
{
    name = ConcatStrings("+", name);
    return ConcatStrings(name, "+");
};

// check local instance access
func void SetAttributes(var C_NPC who)
{
    FillArrInt("who.attribute", 50);
    SetArrInt("who.attribute", Hlp_Random(ATR_INDEX_MAX), Hlp_Random(100));
    SetArrInt("who.attribute", Hlp_Random(ATR_INDEX_MAX), Hlp_Random(100));
    SetArrInt("who.attribute", Hlp_Random(ATR_INDEX_MAX), Hlp_Random(100));
    SetArrInt("who.attribute", Hlp_Random(ATR_INDEX_MAX), Hlp_Random(100));
    SetArrInt("who.attribute", Hlp_Random(ATR_INDEX_MAX), Hlp_Random(100));
};

func float LoopBody_Mulf2(var int index, var float value)
{
    // but there is no multiply function :(
    
    if (index == 2)
    {
        return 666.666;
    };
    
    return value;
};

func void LoopBody_PrintFloat(var int index, var float value)
{
    PrintScreen(FloatToString(value), -1, PrintY, FONT_ScreenSmall, 20);
    PrintY += 2;
};

func int LoopBody_Mul2(var int index, var int value)
{
    return value * 2;
};

func void LoopBody_PrintString(var int index, var string value)
{
    PrintScreen(value, -1, PrintY, FONT_ScreenSmall, 20);
    PrintY += 2;
};

func void B_LogEntry(var string topic,var string entry)
{
    PrintY = 0;
    SetAttributes(hero);
    FillArrStr("ATR_NAMES", "Неизвестный параметр");
    
    ATR_NAMES[ATR_DEXTERITY] = "Ловкость";
    ATR_NAMES[ATR_STRENGTH] = "Сила";
    ATR_NAMES[ATR_HITPOINTS_MAX] = "Максимальное здоровье";
    ATR_NAMES[ATR_HITPOINTS] = "Текущее здоровье";
    
    AlterEachStr("ATR_NAMES", LoopBody_AlterNames);
    While(LoopBody_FillIndexes);
    While(LoopBody_Sort);
    ForEachInt("INDEXES", LoopBody_PrintAttributes);
    
    FillArrFloat("FLOAT_ARR", 15.68);
    SetArrFloat("FLOAT_ARR", 1, 0.7);
    SetArrFloat("FLOAT_ARR", 5, 0.995);
    SetArrFloat("FLOAT_ARR", 6, 3.777);
    AlterEachFloat("FLOAT_ARR", LoopBody_Mulf2);
    ForEachFloat("FLOAT_ARR", LoopBody_PrintFloat);
    
    SetArrStr("SINGLE_STRING", 0, "abacaba");
    SetArrInt("SINGLE_INT", 0, 2);
    SetArrFloat("SINGLE_FLOAT", 0, 0.555);
    
    AlterEachStr("SINGLE_STRING", LoopBody_AlterNames);
    AlterEachInt("SINGLE_INT", LoopBody_Mul2);
    AlterEachFloat("SINGLE_FLOAT", LoopBody_Mulf2);
    
    ForEachInt("SINGLE_INT", LoopBody_PrintAttributes);
    ForEachStr("SINGLE_STRING", LoopBody_PrintString);
    ForEachFloat("SINGLE_FLOAT", LoopBody_PrintFloat);
    
    AlterEachInt("hero.HitChance", LoopBody_Mul2);
    
    Log_AddEntry(topic,entry);
    PrintScreen(PRINT_NewLogEntry,-1,YPOS_LOGENTRY,FONT_ScreenSmall,2);
    Snd_Play("LogEntry");
};
C++:
#include <vector>
#include <algorithm>

namespace NAMESPACE
{
    void PushInt(const int& value)
    {
        parser->datastack.Push(value);
        parser->datastack.Push(zPAR_TOK_PUSHINT);
    }

    void PushString(const zSTRING& value)
    {
        parser->datastack.Push(reinterpret_cast<int>(&value));
        parser->datastack.Push(zPAR_TOK_PUSHVAR);
    }

    void PushFloat(const float& value)
    {
        parser->datastack.Push(reinterpret_cast<const int&>(value));
        parser->datastack.Push(zPAR_TOK_PUSHINT);
    }

    void Call(const int& funcIndex)
    {
        parser->DoStack(parser->GetSymbol(funcIndex)->single_intdata);
    }

    int GetFuncAddress(zCPar_Symbol* sym)
    {
        if (!sym->HasFlag(zPAR_FLAG_CONST) || sym->HasFlag(zPAR_FLAG_EXTERNAL) || sym->HasFlag(zPAR_FLAG_MERGED))
        {
            return -1;
        }

        if (sym->type != zPAR_TYPE_FUNC && sym->type != zPAR_TYPE_INSTANCE && sym->type != zPAR_TYPE_PROTOTYPE)
        {
            return -1;
        }

        const char* name = sym->name.ToChar();

        for (int i = 0; name[i] != '\0' && name[i] != '\n'; i++)
        {
            if (name[i] == '.')
            {
                return -1;
            }
        }

        return sym->single_intdata;
    }

    struct FuncEntity
    {
        int index;
        int address;

        FuncEntity(int index, int address) :
            index(index),
            address(address)
        {

        }
    };

    std::vector<FuncEntity> functions;
    bool initialized = false;

    void InitFunctions()
    {
        if (initialized)
        {
            return;
        }

        for (int i = 0; i < parser->symtab.GetNumInList(); i++)
        {
            int address = GetFuncAddress(parser->GetSymbol(i));

            if (address == -1)
            {
                continue;
            }

            functions.push_back(FuncEntity(i, address));
        }

        std::sort(functions.begin(), functions.end(), [](const FuncEntity & x, const FuncEntity & y) { return x.address < y.address; });
        initialized = true;
    }

    const zSTRING& GetFuncNameByAddress(int address)
    {
        auto it = std::lower_bound(functions.begin(), functions.end(), FuncEntity(-1, address), [](const FuncEntity& x, const FuncEntity& y) { return x.address <= y.address; });
        it--;
        return parser->GetSymbol(it->index)->name;
    }

    void TerminateGame()
    {
        gameMan->ExitGame();
    }

    void ExtractSegments(string name, zCArray<string>& segments)
    {
        int nextStart = 0;

        for (size_t i = 0; i <= name.Length(); i++)
        {
            if (i == name.Length() || name[i] == '.')
            {
                segments.InsertEnd(name.Copy(nextStart, i - nextStart));
                nextStart = i + 1;
            }
        }
    }

    bool ValidateSegments(const zCArray<string> & segments)
    {
        if (segments.GetNum() < 1 || segments.GetNum() > 2)
        {
            return false;
        }

        for (int i = 0; i < segments.GetNum(); i++)
        {
            if (segments[i].IsEmpty())
            {
                return false;
            }
        }

        return true;
    }

    void ShowParserError(string data)
    {
        const zSTRING& func = GetFuncNameByAddress(parser->stack.stackptr - parser->stack.stack);
        Message::Error((string)"Error in function " + func.ToChar() + ".\n" + data + ".", "Loops Error");
        TerminateGame();
    }

    bool ValidateArrayType(int type)
    {
        switch (type)
        {
        case zPAR_TYPE_INT:
        case zPAR_TYPE_FLOAT:
        case zPAR_TYPE_FUNC:
        case zPAR_TYPE_STRING:
            return true;
        default:
            return false;
        }
    }

    zSTRING& GetInstanceClassName(zCPar_Symbol * instance)
    {
        while (instance->GetParent())
        {
            instance = instance->GetParent();
        }

        return instance->name;
    }

    void GetArray(zSTRING& arrayName, zCPar_Symbol*& arrayDesc, void*& pointer, int& size)
    {
        InitFunctions();

        arrayDesc = nullptr;
        pointer = nullptr;
        size = 0;

        string name = arrayName.ToChar();
        name.Upper();

        zCArray<string> segments;
        ExtractSegments(name, segments);

        if (!ValidateSegments(segments))
        {
            return ShowParserError((string)"Invalid array name: " + arrayName.ToChar());
        }

        string funcName = GetFuncNameByAddress(parser->stack.stackptr - parser->stack.stack).ToChar();

        zCPar_Symbol* symbol = parser->GetSymbol((funcName + "." + segments[0]).GetVector());

        if (!symbol)
        {
            symbol = parser->GetSymbol(segments[0].GetVector());
        }

        if (!symbol)
        {
            return ShowParserError((string)"Variable not found: " + segments[0]);
        }

        if (segments.GetNum() == 1)
        {
            if (!ValidateArrayType(symbol->type))
            {
                return ShowParserError((string)"Invalid array type: " + symbol->type);
            }

            arrayDesc = symbol;
            size = arrayDesc->ele;

            switch (arrayDesc->type)
            {
            case zPAR_TYPE_INT:
            case zPAR_TYPE_FUNC:
                pointer = (size == 1) ? &arrayDesc->single_intdata : arrayDesc->intdata;
                break;
            case zPAR_TYPE_FLOAT:
                pointer = (size == 1) ? &arrayDesc->single_floatdata : arrayDesc->floatdata;
                break;
            case zPAR_TYPE_STRING:
                pointer = arrayDesc->stringdata;
                break;
            }

            return;
        }

        if (symbol->type != zPAR_TYPE_INSTANCE)
        {
            return ShowParserError(segments[0] + " is not instance");
        }

        string className = GetInstanceClassName(symbol).ToChar();
        string classVarName = className + "." + segments[1];

        zCPar_Symbol* classVarSymbol = parser->GetSymbol(classVarName.GetVector());

        if (!classVarSymbol)
        {
            return ShowParserError((string)"Can't find class member: " + classVarName);
        }

        if (!ValidateArrayType(classVarSymbol->type))
        {
            return ShowParserError((string)"Invalid array type: " + classVarSymbol->type);
        }

        arrayDesc = classVarSymbol;
        pointer = (byte*)symbol->GetInstanceAdr() + arrayDesc->GetOffset();
        size = arrayDesc->ele;
    }

    void GetIntArray(zSTRING& arrayName, zCPar_Symbol*& arrayDesc, int*& pointer, int& size)
    {
        void* ptr;
        GetArray(arrayName, arrayDesc, ptr, size);
        pointer = (int*)ptr;

        if (!pointer)
        {
            ShowParserError("GetArray failed");
            return;
        }

        if (arrayDesc->type != zPAR_TYPE_INT && arrayDesc->type != zPAR_TYPE_FUNC)
        {
            ShowParserError((string)"Array type mistmatch.\nExpected: " + zPAR_TYPE_INT + " or " + zPAR_TYPE_FUNC +
                ".\nGot: " + arrayDesc->type);
            return;
        }
    }

    void GetFloatArray(zSTRING& arrayName, zCPar_Symbol*& arrayDesc, float*& pointer, int& size)
    {
        void* ptr;
        GetArray(arrayName, arrayDesc, ptr, size);
        pointer = (float*)ptr;

        if (!pointer)
        {
            ShowParserError("GetArray failed");
            return;
        }

        if (arrayDesc->type != zPAR_TYPE_FLOAT)
        {
            ShowParserError((string)"Array type mistmatch.\nExpected: " + zPAR_TYPE_FLOAT +
                ".\nGot: " + arrayDesc->type);
            return;
        }
    }

    void GetStringArray(zSTRING& arrayName, zCPar_Symbol*& arrayDesc, zSTRING*& pointer, int& size)
    {
        void* ptr;
        GetArray(arrayName, arrayDesc, ptr, size);
        pointer = (zSTRING*)ptr;

        if (!pointer)
        {
            ShowParserError("GetArray failed");
            return;
        }

        if (arrayDesc->type != zPAR_TYPE_STRING)
        {
            ShowParserError((string)"Array type mistmatch.\nExpected: " + zPAR_TYPE_STRING +
                ".\nGot: " + arrayDesc->type);
            return;
        }
    }

    void ValidateIndex(int index, int size)
    {
        if (index < 0 || index >= size)
        {
            ShowParserError((string)"Index out of range: " + index + ".\nArray size is " + size);
            ogame->Done();
        }
    }

    int __cdecl GetArrInt()
    {
        int index;
        zSTRING arrayName;

        parser->GetParameter(index);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        int* pointer;
        int size;

        GetIntArray(arrayName, arrayDesc, pointer, size);
        ValidateIndex(index, size);

        parser->SetReturn(pointer[index]);
        return 0;
    }

    int __cdecl SetArrInt()
    {
        int value;
        int index;
        zSTRING arrayName;

        parser->GetParameter(value);
        parser->GetParameter(index);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        int* pointer;
        int size;

        GetIntArray(arrayName, arrayDesc, pointer, size);
        ValidateIndex(index, size);

        pointer[index] = value;
        return 0;
    }

    int __cdecl GetArrFloat()
    {
        int index;
        zSTRING arrayName;

        parser->GetParameter(index);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        float* pointer;
        int size;

        GetFloatArray(arrayName, arrayDesc, pointer, size);
        ValidateIndex(index, size);

        parser->SetReturn(pointer[index]);
        return 0;
    }

    int __cdecl SetArrFloat()
    {
        float value;
        int index;
        zSTRING arrayName;

        parser->GetParameter(value);
        parser->GetParameter(index);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        float* pointer;
        int size;

        GetFloatArray(arrayName, arrayDesc, pointer, size);
        ValidateIndex(index, size);

        pointer[index] = value;
        return 0;
    }

    int __cdecl GetArrStr()
    {
        int index;
        zSTRING arrayName;

        parser->GetParameter(index);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        zSTRING* pointer;
        int size;

        GetStringArray(arrayName, arrayDesc, pointer, size);
        ValidateIndex(index, size);

        parser->SetReturn(pointer[index]);
        return 0;
    }

    int __cdecl SetArrStr()
    {
        zSTRING value;
        int index;
        zSTRING arrayName;

        parser->GetParameter(value);
        parser->GetParameter(index);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        zSTRING* pointer;
        int size;

        GetStringArray(arrayName, arrayDesc, pointer, size);
        ValidateIndex(index, size);

        pointer[index] = value;
        return 0;
    }

    // While(LoopBody_Func);
    int __cdecl While()
    {
        int funcIndex;
        parser->GetParameter(funcIndex);

        int index = 0;

        while (true)
        {
            int result = *(int*)parser->CallFunc(funcIndex, index++);

            if (!result)
            {
                return 0;
            }
        }
    }

    // FillArrInt("self.HitChance", 95);
    int __cdecl FillArrInt()
    {
        int value;
        zSTRING arrayName;

        parser->GetParameter(value);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        int* pointer;
        int size;

        GetIntArray(arrayName, arrayDesc, pointer, size);

        for (int i = 0; i < size; i++)
        {
            pointer[i] = value;
        }

        return 0;
    }

    int __cdecl FillArrFloat()
    {
        float value;
        zSTRING arrayName;

        parser->GetParameter(value);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        float* pointer;
        int size;

        GetFloatArray(arrayName, arrayDesc, pointer, size);

        for (int i = 0; i < size; i++)
        {
            pointer[i] = value;
        }

        return 0;
    }

    int __cdecl FillArrStr()
    {
        zSTRING value;
        zSTRING arrayName;

        parser->GetParameter(value);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        zSTRING* pointer;
        int size;

        GetStringArray(arrayName, arrayDesc, pointer, size);

        for (int i = 0; i < size; i++)
        {
            pointer[i] = value;
        }

        return 0;
    }

    int __cdecl ForEachInt()
    {
        int funcIndex;
        zSTRING arrayName;

        parser->GetParameter(funcIndex);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        int* pointer;
        int size;

        GetIntArray(arrayName, arrayDesc, pointer, size);

        for (int i = 0; i < size; i++)
        {
            PushInt(i);
            PushInt(pointer[i]);
            Call(funcIndex);
        }

        return 0;
    }

    int __cdecl ForEachFloat()
    {
        int funcIndex;
        zSTRING arrayName;

        parser->GetParameter(funcIndex);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        float* pointer;
        int size;

        GetFloatArray(arrayName, arrayDesc, pointer, size);

        for (int i = 0; i < size; i++)
        {
            PushInt(i);
            PushFloat(pointer[i]);
            Call(funcIndex);
        }

        return 0;
    }

    int __cdecl ForEachStr()
    {
        int funcIndex;
        zSTRING arrayName;

        parser->GetParameter(funcIndex);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        zSTRING* pointer;
        int size;

        GetStringArray(arrayName, arrayDesc, pointer, size);

        for (int i = 0; i < size; i++)
        {
            PushInt(i);
            PushString(pointer[i]);
            Call(funcIndex);
        }

        return 0;
    }

    int __cdecl AlterEachInt()
    {
        int funcIndex;
        zSTRING arrayName;

        parser->GetParameter(funcIndex);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        int* pointer;
        int size;

        GetIntArray(arrayName, arrayDesc, pointer, size);

        for (int i = 0; i < size; i++)
        {
            PushInt(i);
            PushInt(pointer[i]);
            Call(funcIndex);
            parser->GetParameter(pointer[i]);
        }

        return 0;
    }

    int __cdecl AlterEachFloat()
    {
        int funcIndex;
        zSTRING arrayName;

        parser->GetParameter(funcIndex);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        float* pointer;
        int size;

        GetFloatArray(arrayName, arrayDesc, pointer, size);

        for (int i = 0; i < size; i++)
        {
            PushInt(i);
            PushFloat(pointer[i]);
            Call(funcIndex);
            parser->GetParameter(pointer[i]);
        }

        return 0;
    }

    int __cdecl AlterEachStr()
    {
        int funcIndex;
        zSTRING arrayName;

        parser->GetParameter(funcIndex);
        parser->GetParameter(arrayName);

        zCPar_Symbol* arrayDesc;
        zSTRING* pointer;
        int size;

        GetStringArray(arrayName, arrayDesc, pointer, size);

        for (int i = 0; i < size; i++)
        {
            PushInt(i);
            PushString(pointer[i]);
            Call(funcIndex);
            parser->GetParameter(pointer[i]);
        }

        return 0;
    }

    void Attach()
    {
        parser->DefineExternal("GetArrInt", &GetArrInt, zPAR_TYPE_STRING, zPAR_TYPE_INT, 0);
        parser->DefineExternal("SetArrInt", &SetArrInt, zPAR_TYPE_STRING, zPAR_TYPE_INT, zPAR_TYPE_INT, 0);

        parser->DefineExternal("GetArrFloat", &GetArrFloat, zPAR_TYPE_STRING, zPAR_TYPE_INT, 0);
        parser->DefineExternal("SetArrFloat", &SetArrFloat, zPAR_TYPE_STRING, zPAR_TYPE_INT, zPAR_TYPE_FLOAT, 0);

        parser->DefineExternal("GetArrStr", &GetArrStr, zPAR_TYPE_STRING, zPAR_TYPE_INT, 0);
        parser->DefineExternal("SetArrStr", &SetArrStr, zPAR_TYPE_STRING, zPAR_TYPE_INT, zPAR_TYPE_STRING, 0);

        parser->DefineExternal("While", &While, zPAR_TYPE_FUNC, 0);

        parser->DefineExternal("FillArrInt", &FillArrInt, zPAR_TYPE_INT, 0);
        parser->DefineExternal("FillArrFloat", &FillArrFloat, zPAR_TYPE_FLOAT, 0);
        parser->DefineExternal("FillArrStr", &FillArrStr, zPAR_TYPE_STRING, 0);

        parser->DefineExternal("ForEachInt", &ForEachInt, zPAR_TYPE_STRING, zPAR_TYPE_FUNC, 0);
        parser->DefineExternal("ForEachFloat", &ForEachFloat, zPAR_TYPE_STRING, zPAR_TYPE_FUNC, 0);
        parser->DefineExternal("ForEachStr", &ForEachStr, zPAR_TYPE_STRING, zPAR_TYPE_FUNC, 0);

        parser->DefineExternal("AlterEachInt", &AlterEachInt, zPAR_TYPE_STRING, zPAR_TYPE_FUNC, 0);
        parser->DefineExternal("AlterEachFloat", &AlterEachFloat, zPAR_TYPE_STRING, zPAR_TYPE_FUNC, 0);
        parser->DefineExternal("AlterEachStr", &AlterEachStr, zPAR_TYPE_STRING, zPAR_TYPE_FUNC, 0);
    }
}
 

Вложения

  • Union_Loops.vdf
    68,4 KB · Просмотры: 441

Phantom95

Участник форума
Регистрация
31 Июл 2014
Сообщения
2.227
Благодарности
1.911
Баллы
370
Плагин AlterDamage
Меняет формулы наносимого урона. Заменяет случайную последовательность промахов/попаданий в ближнем бою на детерминированную. Добавляет урон от горения и всплывающие сообщения о нанесённом уроне.
Код:
[ALTERDAMAGE]
EnablePopupDamage=1
; ... enables (1) or disables (0) popup damage messages

HumanFistDamageMult=0.5
; ... human's fist attack power is premultiplied by this value

MeleeRawDamageMult=1
; ... scalable types of damage dealed by attacks with melee weapon are premultiplied by this value

MeleeMissMult=0.5
; ... when hitchance roll fails, scalable types of damage dealed by attacks with melee weapon are multiplied by this value

ArmorSoftCap=0.60
BeyondSoftCapArmorScaling=0.300000012
; ... when armor exceeds ArmorSoftCap * <incoming damage>, each additional armor point give BeyondSoftCapArmorScaling protection, instead of 1

TwoHandedMult=1.35000002
; ... power of attacks with twohanded weapon is postmultiplied by this value

FireDotDamageMult=0.5
FireDotDuration=5
; ... fire spells apply additional damage in an amount equal to FireDotDamageMult * <real damage> during FireDotDuration seconds
Репозиторий на гитхабе: UnresolvedExternal/Union_AlterDamage
Можно ли добавить минимальный наносимый гг урон также и для движков 1.08k и 1.12f ? (Должен соответствовать константе установленной в скриптах, в готике 1 - 1, в готике сиквел - 2)
 

Carnage/Mark56

Участник форума
Регистрация
27 Дек 2014
Сообщения
16
Благодарности
56
Баллы
165
Will you add other fixes on github as well ? I like to study your source code.
 

YoungManRumble

Участник форума
Регистрация
20 Май 2019
Сообщения
12
Благодарности
2
Баллы
155
Hi, about the AlterDamage plugin, where can I change the settings? It seems that the vdf file contains 2 files and I can't see an .ini file.
Edit: Nevermind, I found it at gothic.ini in system folder.
 

KirTheSeeker

Участник форума
Регистрация
18 Авг 2017
Сообщения
1.931
Благодарности
560
Баллы
275
Приветствую всех.
Плагин AlterDamage
Меняет формулы наносимого урона. Заменяет случайную последовательность промахов/попаданий в ближнем бою на детерминированную. Добавляет урон от горения и всплывающие сообщения о нанесённом уроне.
Объясните, пожалуйста, чуть понятнее и подробнее (для "НЕкодеров") что именно этот плагин делает?
Я буду промахиваться (наносить мин-й урон) только первые 3 удара, а на 4-й полноценно попадать, имея навык влад-я в 25%? Или как понимать "замену случайной последовательности на детерминированную"?
 
Последнее редактирование:
Сверху Снизу