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

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!
  • Внимание!
    — Требуется примерно по 3-5 человек на каждую из версий ОС:: - Windows® XP SP3, Windows® Vista SP2, Windows® 7 SP1, Windows® 8, Windows® 8.1, Windows® 10(build 10 1607) и Windows® 10(build 10 1703). Для стационарных ПК и ноутбуков. Заявку на участие можно оставить здесь...

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

Slavemaster

Участник форума
Регистрация
10 Июн 2019
Сообщения
118
Благодарности
179
Баллы
50
Здесь буду выкладывать свои плагины, не достойные отдельной темы (то есть все).

1. Плагин NoFocusFix. Для мододелов. Делает НПС с флагом NPC_FLAG_NFOCUS полностью прозрачными для фокуса ГГ.
2. Плагин AlterDamage. Меняет формулы урона. Добавляет всплывающие сообщения о нанесённом уроне.
3. Плагин Loops. Для мододелов. Добавляет методы для свободной индексации массивов, а так же циклы While и ForEach.
4. Плагин SpellFix. Устраняет некоторые баги в работе заклинаний.
5. Плагин SavesBackuper. Осуществляет резервное копирование сделанных сохранений.
6. Плагин Union_Hotbar_1.0f (Автор: Haart). Добавляет панель быстрого доступа для предметов из инвентаря.
7. Плагин MiscUtils. Добавляет отметки для непрочитанных документов и новых предметов в инвентаре. Также горящие факелы теперь сохраняются/загружаются, а фокусная надпись для некоторых предметов может быть заменена на более информативную.
 
Последнее редактирование:

ElderGamer


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

Slavemaster

Участник форума
Регистрация
10 Июн 2019
Сообщения
118
Благодарности
179
Баллы
50
Предполагается ли здесь обсуждение плагинов?
Да.
UPDATE: Исправил баг (не отображалось кол-во у обычных предметов) и перезалил.

Плагин NoFocusFix
Добавляет обработку флага static const int NPC_FLAG_NFOCUS = 1<<18;.
НПС с таким флагом становятся полностью прозрачными для фокуса ГГ.
C++:
#include "CGlobalEventSubscription.h"

namespace NAMESPACE
{
    oCNpc* AsNpc(const zCVob* vob)
    {
        if (!vob || vob->GetVobType() != zVOB_TYPE_NSC)
        {
            return nullptr;
        }

        return (oCNpc*)vob;
    }

    bool IsValidFocus(const zCVob* vob)
    {
        oCNpc* npc = AsNpc(vob);

        if (!player || !npc || !npc->HasFlag(NPC_FLAG_NFOCUS))
        {
            return true;
        }

        return false;
    }

    int __fastcall Hook_oCNpc_FocusCheck(oCNpc*, void*, zCVob*, int, int, float, float&);
    CInvoke<int(__thiscall*)(oCNpc*, zCVob*, int, int, float, float&)> Ivk_oCNpc_FocusCheck(nullptr, nullptr, IVK_DISABLED);
    int __fastcall Hook_oCNpc_FocusCheck(oCNpc* _this, void* vtable, zCVob* target, int a2, int a3, float a4, float& a5)
    {
        int result = Ivk_oCNpc_FocusCheck(_this, target, a2, a3, a4, a5);

        if (result && (_this == player) && !IsValidFocus(target))
        {
            return 0;
        }

        return result;
    }

    void __fastcall Hook_oCNpc_GetNearestValidVob(oCNpc*, void*, float);
    CInvoke<void(__thiscall*)(oCNpc*, float)> Ivk_oCNpc_GetNearestValidVob(nullptr, nullptr, IVK_DISABLED);
    void __fastcall Hook_oCNpc_GetNearestValidVob(oCNpc* _this, void* vtable, float target)
    {
        if (!player)
        {
            return Ivk_oCNpc_GetNearestValidVob(_this, target);
        }

        zCVob* oldFocus = player->GetFocusVob();

        Ivk_oCNpc_GetNearestValidVob(_this, target);

        if (!IsValidFocus(player->GetFocusVob()))
        {
            player->SetFocusVob(oldFocus);
        }
    }

    void __fastcall Hook_oCNpc_SetEnemy(oCNpc*, void*, oCNpc*);
    CInvoke<void(__thiscall*)(oCNpc*, oCNpc*)> Ivk_oCNpc_SetEnemy(nullptr, nullptr, IVK_DISABLED);
    void __fastcall Hook_oCNpc_SetEnemy(oCNpc* _this, void* vtable, oCNpc* target)
    {
        if (_this == player && !IsValidFocus(target))
        {
            return;
        }

        Ivk_oCNpc_SetEnemy(_this, target);
    }

    void OnLoop()
    {
        if (!player)
        {
            return;
        }

        if (!IsValidFocus(player->GetFocusVob()))
        {
            player->SetFocusVob(nullptr);
        }

        if (!IsValidFocus(player->enemy))
        {
            player->SetEnemy(nullptr);
        }
    }

    CGlobalEventSubscription* onLoop;
    CGlobalEventSubscription* onExit;

    void OnExit()
    {
        delete onLoop;
        delete onExit;
    }

    void Attach()
    {
        Ivk_oCNpc_FocusCheck.Attach(ADDRESS_OCNPC_FOCUSCHECK, &Hook_oCNpc_FocusCheck);
        Ivk_oCNpc_GetNearestValidVob.Attach(ADDRESS_OCNPC_GETNEARESTVALIDVOB, &Hook_oCNpc_GetNearestValidVob);
        Ivk_oCNpc_SetEnemy.Attach(ADDRESS_OCNPC_SETENEMY, &Hook_oCNpc_SetEnemy);

        onLoop = new CGlobalEventSubscription(CGlobalEventPublisher::GlobalEvent::LOOP, &OnLoop);
        onExit = new CGlobalEventSubscription(CGlobalEventPublisher::GlobalEvent::EXIT, &OnExit);
    }
}
 

Вложения

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

ElderGamer


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

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

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

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

Unsubdued

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

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

Slavemaster

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

Gratt


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

Slavemaster

Участник форума
Регистрация
10 Июн 2019
Сообщения
118
Благодарности
179
Баллы
50
Gratt, может сделать, чтобы юнион плагины в подпапках искал? Закинул все плагины в папку Data\Plugins и все.
 

Unsubdued

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

Slavemaster

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

ElderGamer


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

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

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

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

Slavemaster

Участник форума
Регистрация
10 Июн 2019
Сообщения
118
Благодарности
179
Баллы
50
Плагин AlterDamage
Меняет формулы наносимого урона. Добавляет стабилизатор рандома для промахов/попаданий. Добавляет урон от горения и всплывающие сообщения о нанесённом уроне.
Код:
[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

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

MinDamageVarName=#
; ... min damage variable name. If exists, must be valid integer.
Репозиторий на гитхабе: UnresolvedExternal/Union_AlterDamage
 

Вложения

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

Slavemaster

Участник форума
Регистрация
10 Июн 2019
Сообщения
118
Благодарности
179
Баллы
50
Плагин 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);
    }
}
 

Вложения

Phantom95

Участник форума
Регистрация
31 Июл 2014
Сообщения
1.887
Благодарности
1.398
Баллы
295
Плагин 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
Благодарности
55
Баллы
165
Will you add other fixes on github as well ? I like to study your source code.
 

YoungManRumble

Участник форума
Регистрация
20 Май 2019
Сообщения
6
Благодарности
1
Баллы
15
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
Сообщения
509
Благодарности
149
Баллы
100
Приветствую всех.
Плагин AlterDamage
Меняет формулы наносимого урона. Заменяет случайную последовательность промахов/попаданий в ближнем бою на детерминированную. Добавляет урон от горения и всплывающие сообщения о нанесённом уроне.
Объясните, пожалуйста, чуть понятнее и подробнее (для "НЕкодеров") что именно этот плагин делает?
Я буду промахиваться (наносить мин-й урон) только первые 3 удара, а на 4-й полноценно попадать, имея навык влад-я в 25%? Или как понимать "замену случайной последовательности на детерминированную"?
 
Последнее редактирование:
Сверху Снизу