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

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

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

4. Работа с Gothic API. Кроссплатформенный плагин. Выводим текст на экран.

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.281
Благодарности
4.581
Баллы
625

Русский English


  • * В этом туторе будут использоваться препроцессорные команды для раздельной компиляции кроссплатформенного кода. Если это для Вас слишком сложно, добавлю внизу поста пример кода без них. Однако настоятельно рекомендую вникнуть в реализацию с использованием препроцессора.

    Для начала работы возьмем предыдущий плагин. Тестировать работу будем на Gothic II: NoTR, а затем распишем код для остальных.
    Установим конфигурацию проекта на G2A Release.

    Подготовка рабочего пространства
    Для удобства создадим новый исходный файл под каталогом Plugin:
    73955
    Подключим заголовок UnionAfx.h, а далее очень важно.

    Каждый интерфейс движка обернут под пространства имен (Gothic_I_Classic, Gothic_I_Addon, Gothic_II_Classic, Gothic_II_Addon соответственно). Поскольку мы пишем тестовый код под Gothic II NoTR, то определим пространство Gothic_II_Addon.

    В итоге получим файл примерно с такой начинкой:
    C++:
    #include <UnionAfx.h>
    
    namespace Gothic_II_Addon {
    
    }

    Однако если мы захотим изменить конфигурацию проекта под другой движок, компилятор выбьет ошибку, что код внутри Gothic_II_Addon ему непонятен.
    Поверх пространства допишем команду #ifdef __G2A. Благодаря этому компилятор сможет исключать из построения весь лишний код, когда тот будет не нужен.
    Эта конструкция будет базовой для всех кроссплатформенных проектов:
    C++:
    #include <UnionAfx.h>
    
    #ifdef __G2A
    namespace Gothic_II_Addon {
    
    }
    #endif



    Добавление циклической функции
    Самый простой способ вызывать функции из разных файлов исходного кода - ключевое слово extern. В конце нашего файла создадим функцию Loop, а в Application.cpp объявим ее через extern. Вызовем функцию из предопределенной функции Game_Loop.
    C++:
    // Example.cpp
    #include <UnionAfx.h>
    
    #ifdef __G2A
    namespace Gothic_II_Addon {
    
    }
    #endif
    
    void Loop() {
    
    }
    C++:
    // Application.cpp
    extern void Loop();
    
    void Game_Loop() {
      Loop();
    }



    Вывод текста на экран
    В Example.cpp в Gothic_II_Addon добавим функцию вывода текста. Текст, как можно догадаться, будет выводиться циклически вызовом из функции Loop.
    Вывод будем производить на вьюпорт screen.
    screen - это экземпляр zCView (можно посмотреть в файлах zView.h). Он имеет метод Print для вывода текста по координатам, PrintCX выравнивающий текст по горизонтали, PrintCY по вертикали и PrintCXY рисующий текст в центре экрана. Возьмем последнюю.
    Example: screen->PrintCXY( "Hello" );

    В качестве текста возьмем информацию об имени персонажа в фокусе игрока.
    Игрок определяется глобальной переменной player класса oCNpc.
    Класс имеет два подходящих метода GetFocusVob - берет любую сущность в фокусе и GetFocusNpc - берет конкретно нпс. Она нам и нужна.
    Example: oCNpc* focusNpc = playerGetFocusNpc();

    Далее необходимо получить имя npc. Вспоминаем скрипты - нпс имеет массив имен из 5 строк. Нам нужна первая. Воспользуемся методом GetName указав индекс строки 0.
    Example: zSTRING npcName = focusNpc ->GetName( 0 )

    Теперь собираем все воедино. Не забываем учесть, что npc не всегда присутствует в фокусе, по этому вывод необходимо производить когда указатель на focusNpc не равен null. Поэтому все операции вписываем под блок if( focusNpc )

    Код на данном этапе должен иметь такой вид:
    C++:
    #include <UnionAfx.h>
    
    #ifdef __G2A
    namespace Gothic_II_Addon {
    
      void PrintScreen() {
       
        oCNpc* focusNpc = player->GetFocusNpc();
        if( focusNpc ) {
    
          zSTRING npcName = focusNpc->GetName( 0 );
          screen->PrintCXY( npcName );
        }
      }
    
    }
    #endif
    
    void Loop() {
    
    }



    Расширение кода на все движки:
    Копируем код, меняя в нем пространство имен и определение препроцессора:
    73956

    В функции Loop добавляем код вызова функций PrintScreen.
    Чтобы вызывать только те функции, которые относятся к соответствующему движку - обратимся к экземпляру Union и запросим текущую версию движка методом GetEngineVersion.
    TEngineVersion engineVersion = Union.GetEngineVersion();

    Поставим проверки на соответствие и вызовем функции.
    C++:
    if( engineVersion == Engine_G1 )
        Gothic_I_Classic::PrintScreen();

    Но как говорилось ранее, собирая проект для одного движка - остальные будут выдавать ошибку:
    73957


    Поэтому также обернем вызовы под #ifdef, исключив из построения ненужные вызовы:
    C++:
    void Loop() {
      TEngineVersion engineVersion = Union.GetEngineVersion();
    
    #ifdef __G1
      if( engineVersion == Engine_G1 )
        Gothic_I_Classic::PrintScreen();
    #endif
    
    #ifdef __G1A
      if( engineVersion == Engine_G1A )
        Gothic_I_Addon::PrintScreen();
    #endif
    
    #ifdef __G2
      if( engineVersion == Engine_G2 )
        Gothic_II_Classic::PrintScreen();
    #endif
    
    #ifdef __G2A
      if( engineVersion == Engine_G2A )
        Gothic_II_Addon::PrintScreen();
    #endif
    }

    Тестовая компиляция и запуск
    Компилируем плагин в конфигурации G2A Release. Проект должен быстро собраться, поскольку отключены 3 движка. Запускаем плагин, заходим в игру и проверяем, чтобы при наведении фокуса на npc выводилось его имя.
    73958
    В центре экрана выводится дублирующее имя. Значит все работает исправно.



    Финальная компиляция и запуск:
    Включаем конфигурацию сборки Release. Замечаем, что стали активными все блоки, обернутые ранее под команду #ifdef. Это значит, что в игру вступили все движки и они готовы к работе в кроссплатформенном режиме. Компилируем проект, это займет значительно больше времени, чем при тестовом запуске.

    Результаты:
    Gothic I Classic
    73959

    Gothic I Addon
    73960

    Gothic II Classic
    73961

    Gothic II Addon
    73962





    Код без препроцессорных команд:
    C++:
    namespace Gothic_I_Classic {
    
      void PrintScreen() {
       
        oCNpc* focusNpc = player->GetFocusNpc();
        if( focusNpc ) {
    
          zSTRING npcName = focusNpc->GetName( 0 );
          screen->PrintCXY( npcName );
        }
      }
    
    }
    
    namespace Gothic_I_Addon {
    
      void PrintScreen() {
       
        oCNpc* focusNpc = player->GetFocusNpc();
        if( focusNpc ) {
    
          zSTRING npcName = focusNpc->GetName( 0 );
          screen->PrintCXY( npcName );
        }
      }
    
    }
    
    namespace Gothic_II_Classic {
    
      void PrintScreen() {
       
        oCNpc* focusNpc = player->GetFocusNpc();
        if( focusNpc ) {
    
          zSTRING npcName = focusNpc->GetName( 0 );
          screen->PrintCXY( npcName );
        }
      }
    
    }
    
    namespace Gothic_II_Addon {
    
      void PrintScreen() {
       
        oCNpc* focusNpc = player->GetFocusNpc();
        if( focusNpc ) {
    
          zSTRING npcName = focusNpc->GetName( 0 );
          screen->PrintCXY( npcName );
        }
      }
    
    }
    
    void Loop() {
      TEngineVersion engineVersion = Union.GetEngineVersion();
    
      if( engineVersion == Engine_G1 )
        Gothic_I_Classic::PrintScreen();
    
      if( engineVersion == Engine_G1A )
        Gothic_I_Addon::PrintScreen();
    
      if( engineVersion == Engine_G2 )
        Gothic_II_Classic::PrintScreen();
    
      if( engineVersion == Engine_G2A )
        Gothic_II_Addon::PrintScreen();
    }

  • * This tutorial will use preprocessor instructions to separately compile cross-platform code. If this is too complicated for you, I’ll add an example code without them at the bottom of the post. However, I strongly recommend that you delve into the implementation using a preprocessor.

    To get started, take the previous plugin. We will test the work on Gothic II: NoTR, and then we will write the code for the rest.
    Set the project configuration to G2A Release.

    Workspace preparation
    For convenience, create a new source file under the directory Plugin:
    73955
    We will include the header UnionAfx.h, and then it is very important.

    Each engine interface is wrapped under namespaces (Gothic_I_Classic, Gothic_I_Addon, Gothic_II_Classic, Gothic_II_Addon ). Because we are writing test code for Gothic II NoTR, we define the space Gothic_II_Addon.

    As a result, we get a file with something like this:
    C++:
    #include <UnionAfx.h>
    
    namespace Gothic_II_Addon {
    
    }

    However, if we want to change the configuration of the project for a different engine, the compiler will throw out an error that the code inside Gothic_II_Addon is unknown to it.
    We’ll add a command over namespace #ifdef __G2A. Due to it, the compiler will be able to exclude all unnecessary code from construction when it will not be needed.
    This code will be the base for all cross-platform projects:
    C++:
    #include <UnionAfx.h>
    
    #ifdef __G2A
    namespace Gothic_II_Addon {
    
    }
    #endif



    Adding a loop Function
    The easiest way to call functions from different source code files is to use the keyword extern. At the end of our file, create a function Loop, and in Application.cpp define it via extern. Call the function from a predefined function Game_Loop.
    C++:
    // Example.cpp
    #include <UnionAfx.h>
    
    #ifdef __G2A
    namespace Gothic_II_Addon {
    
    }
    #endif
    
    void Loop() {
    
    }
    C++:
    // Application.cpp
    extern void Loop();
    
    void Game_Loop() {
      Loop();
    }



    Display text
    In Example.cpp in Gothic_II_Addon let's add text output function. Текст, как можно догадаться, будет the text, as you might guess, will be displayed cyclically by calling from the function Loop.
    The output will be produced via the viewport.screen.
    screen - is an instance of zCView (you can see it in the file zView.h). It has the method Print for printing text with coordinates, PrintCX with text horizontal align, PrintCY with vertical align and PrintCXY text in the screen center. Let's use the last one.
    Example: screen->PrintCXY( "Hello" );

    As the text, we take information about the name of the character in the focus of the player.
    The player is defined in global variable player of the class oCNpc.
    The class has two methods GetFocusVob - any object in focus and GetFocusNpc - npc in focus
    Example: oCNpc* focusNpc = playerGetFocusNpc();

    Next, you need to get the name npc. NPC has an array of names of 5 lines. We need the first one. We use the method GetName with index 0.
    Example: zSTRING npcName = focusNpc ->GetName( 0 )

    Now we are putting it all together. Do not forget to take into account that npc is not always present in focus, therefore, the conclusion must be made when the pointer to focusNpc no equal null. Therefore, we write all operations under the blockif( focusNpc )

    The code at this stage should look like this:
    C++:
    #include <UnionAfx.h>
    
    #ifdef __G2A
    namespace Gothic_II_Addon {
    
      void PrintScreen() {
       
        oCNpc* focusNpc = player->GetFocusNpc();
        if( focusNpc ) {
    
          zSTRING npcName = focusNpc->GetName( 0 );
          screen->PrintCXY( npcName );
        }
      }
    
    }
    #endif
    
    void Loop() {
    
    }



    Code extension to all the engines
    We copy the code, changing the namespace and the definition of the preprocessor in it:
    73956

    In function Loop add function call codePrintScreen.
    To call only those functions that relate to the corresponding engine, we use the instance of Union and request the current version of the engine using the method GetEngineVersion.
    TEngineVersion engineVersion = Union.GetEngineVersion();

    We put the checks for compliance and call the function.
    C++:
    if( engineVersion == Engine_G1 )
        Gothic_I_Classic::PrintScreen();

    But as mentioned earlier, when collecting a project for one engine, the rest will give an error:
    73957


    Therefore, we also wrap the calls under #ifdef, eliminating unnecessary calls from the construction:
    C++:
    void Loop() {
      TEngineVersion engineVersion = Union.GetEngineVersion();
    
    #ifdef __G1
      if( engineVersion == Engine_G1 )
        Gothic_I_Classic::PrintScreen();
    #endif
    
    #ifdef __G1A
      if( engineVersion == Engine_G1A )
        Gothic_I_Addon::PrintScreen();
    #endif
    
    #ifdef __G2
      if( engineVersion == Engine_G2 )
        Gothic_II_Classic::PrintScreen();
    #endif
    
    #ifdef __G2A
      if( engineVersion == Engine_G2A )
        Gothic_II_Addon::PrintScreen();
    #endif
    }

    Test compilation and launch
    We compile the plugin in the G2A Release configuration. The project should be quickly assembled, since 3 engines are disabled. We launch the plugin, go into the game and check that when focusing on npc its name is displayed.
    73958
    A duplicate name is displayed in the center of the screen. So everything is working properly.



    Final compilation and launch:
    Enable build configuration Release. We notice that all blocks wrapped previously under the command have become active #ifdef. This means that all the engines have entered the game and they are ready to work in cross-platform mode. We compile the project, it will take much longer than with a test run.

    Results:
    Gothic I Classic
    73959

    Gothic I Addon
    73960

    Gothic II Classic
    73961

    Gothic II Addon
    73962





    Code without preprocessor commands:
    C++:
    namespace Gothic_I_Classic {
    
      void PrintScreen() {
       
        oCNpc* focusNpc = player->GetFocusNpc();
        if( focusNpc ) {
    
          zSTRING npcName = focusNpc->GetName( 0 );
          screen->PrintCXY( npcName );
        }
      }
    
    }
    
    namespace Gothic_I_Addon {
    
      void PrintScreen() {
       
        oCNpc* focusNpc = player->GetFocusNpc();
        if( focusNpc ) {
    
          zSTRING npcName = focusNpc->GetName( 0 );
          screen->PrintCXY( npcName );
        }
      }
    
    }
    
    namespace Gothic_II_Classic {
    
      void PrintScreen() {
       
        oCNpc* focusNpc = player->GetFocusNpc();
        if( focusNpc ) {
    
          zSTRING npcName = focusNpc->GetName( 0 );
          screen->PrintCXY( npcName );
        }
      }
    
    }
    
    namespace Gothic_II_Addon {
    
      void PrintScreen() {
       
        oCNpc* focusNpc = player->GetFocusNpc();
        if( focusNpc ) {
    
          zSTRING npcName = focusNpc->GetName( 0 );
          screen->PrintCXY( npcName );
        }
      }
    
    }
    
    void Loop() {
      TEngineVersion engineVersion = Union.GetEngineVersion();
    
      if( engineVersion == Engine_G1 )
        Gothic_I_Classic::PrintScreen();
    
      if( engineVersion == Engine_G1A )
        Gothic_I_Addon::PrintScreen();
    
      if( engineVersion == Engine_G2 )
        Gothic_II_Classic::PrintScreen();
    
      if( engineVersion == Engine_G2A )
        Gothic_II_Addon::PrintScreen();
    }
 
Последнее редактирование модератором:

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.039
Благодарности
1.816
Баллы
240
Копируем код, меняя в нем пространство имен и определение препроцессора:
Не думаю, что создавать копии кода, которые ничем не отличаются, кроме как названием неймспейса, хорошая идея. Либо версии кода для разных платформ практически идентичны и тогда можно этот код несколько раз заинклудить с разными значениями макроса NAMESPACE. Либо код от платформы к платформе отличается значительно и тогда действительно имеет смысл иметь разные реализации одной и той же функциональности, и эти реализации держать в разных файлах.

Пример, когда код отличается только неймспейсами:
C++:
// MegaHeader.h

#pragma once
#include "UnionAfx.h"

#ifdef __G1
#define NAMESPACE Gothic_I_Classic
#include "FirstClass.h"
#endif

#ifdef __G1A
#define NAMESPACE Gothic_I_Addon
#include "FirstClass.h"
#endif

#ifdef __G2
#define NAMESPACE Gothic_II_Classic
#include "FirstClass.h"
#endif

#ifdef __G2A
#define NAMESPACE Gothic_II_Addon
#include "FirstClass.h"
#endif
C++:
// MegaSource.cpp

#include "MegaHeader.h"

#ifdef __G1
#define NAMESPACE Gothic_I_Classic
#include "FirstClass.cpp"
#endif

#ifdef __G1A
#define NAMESPACE Gothic_I_Addon
#include "FirstClass.cpp"
#endif

#ifdef __G2
#define NAMESPACE Gothic_II_Classic
#include "FirstClass.cpp"
#endif

#ifdef __G2A
#define NAMESPACE Gothic_II_Addon
#include "FirstClass.cpp"
#endif
C++:
// FirstClass.h

namespace NAMESPACE
{
    class FirstClass
    {
    private:
        zCView* noUse;
    public:
        void Run();
    };
}
C++:
// FirstClass.cpp
// Этот файл не компилируется сам по себе. Он включается в MegaSource.cpp

#include "MegaHeader.h"

namespace NAMESPACE
{
    // Функция возвращает имя движка основываясь на его версию
    string GetEngineVersionName(TEngineVersion version) {
        switch (version) {
        case Engine_G1:  return "Gothic I Classic";
        case Engine_G1A: return "Gothic I Addon";
        case Engine_G2:  return "Gothic II Classic";
        case Engine_G2A: return "Gothic II Addon";
        }
        return "Unknown";
    }

    void FirstClass::Run()
    {
        Message::Info(string::Combine("Hello from %s", GetEngineVersionName(Union.GetEngineVersion())));
    }
}
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.281
Благодарности
4.581
Баллы
625
Не думаю, что создавать копии кода, которые ничем не отличаются, кроме как названием неймспейса, хорошая идея.
Платформы отличаются набором классов, размерами классов, порядком виртуальных таблиц и адресами. Но сам код, как правило, идентичен.
Не спорю, что заинклудить хидер намного удобнее и быстрее, но боюсь не каждый поймет принцип такой реализации. Тем более что и intellisense не всегда способна адекватно подсветить такой извилистый код, это введет новеньких в ступор.
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.039
Благодарности
1.816
Баллы
240
Но сам код, как правило, идентичен.
Тогда новичку проще кодить под одну платформу. А при необходимости отрефакторить под все. Потому что поддерживать исходники разных классов для разных платформ в согласованном состоянии нисколько не проще, ИМХО.
Тем более что и intellisense не всегда способна адекватно подсветить такой извилистый код
Может быть. У меня подрубалась как только редактируемый файл попадал в компилируемый исходник через цепочку инклудов.
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.281
Благодарности
4.581
Баллы
625
Тогда новичку проще кодить под одну платформу. А при необходимости отрефакторить под все. Потому что поддерживать исходники разных классов для разных платформ в согласованном состоянии нисколько не проще, ИМХО.
Я слишком часто контактирую со студентами программистами и, к сожалению, знаю о чем говорю. Им действительно проще вникнуть в более емкий, но примитивный код, чем в более короткий, но извилистый в реализации. Все придет с опытом, а пока важно дать базовое представление о механике плагинов.
 

neromont


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

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.281
Благодарности
4.581
Баллы
625
Сделал. Когда следующий урок? :D
Попробую сделать аналогичное для Item-ов.
Передохну сначала. Дальше напишу про виртуальные таблицы, будем наследовать класс oCNpc.
 

neromont


Модостроитель
Регистрация
12 Мар 2011
Сообщения
674
Благодарности
655
Баллы
245
Получилось и с Item. Радуюсь как младенец. :D

C++:
oCItem* focusItem = dynamic_cast<oCItem*>(player->GetFocusVob());
        if (focusItem) {
            zSTRING itemName = focusItem->GetName(0);
            screen->PrintCXY(itemName);
}
 

Вложения

  • Безымянный.png
    Безымянный.png
    844,2 KB · Просмотры: 336

neromont


Модостроитель
Регистрация
12 Мар 2011
Сообщения
674
Благодарности
655
Баллы
245
Я тут написал код для вывода названия первого элемента в сундуке, может есть какой-нибудь способ попроще?

C++:
oCMobContainer* focusContainer = dynamic_cast<oCMobContainer*>(player->GetFocusVob());
        if (focusContainer) {
            zSTRING firstItemName = focusContainer->containList.GetNextInList()->GetData()->GetName(0);
            screen->PrintCXY(firstItemName);
        }
 

Вложения

  • Безымянный.png
    Безымянный.png
    329 KB · Просмотры: 348
Последнее редактирование:

neromont


Модостроитель
Регистрация
12 Мар 2011
Сообщения
674
Благодарности
655
Баллы
245
Да, только нужно проверку сделать, на наличие объектов вообще, а то когда из сундука забираешь все вещи, игра виснет :D
 

MaGoth

★★★★★★★★★★★
Администратор
Регистрация
7 Янв 2003
Сообщения
19.371
Благодарности
7.802
Баллы
995
Да, только нужно проверку сделать, на наличие объектов вообще, а то когда из сундука забираешь все вещи, игра виснет
— Пилите Шура, пилите! © "Скора эхзамен.." *ded*
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.281
Благодарности
4.581
Баллы
625
Да, только нужно проверку сделать, на наличие объектов вообще, а то когда из сундука забираешь все вещи, игра виснет :D
Могу такой вариант предложить.
C++:
    // CastTo для производных от zObject эквивалентен zDYNAMIC_CAST
    oCMobContainer* focusContainer = player->GetFocusVob()->CastTo<oCMobContainer>();
    if( focusContainer ) {
      zCListSort<oCItem>* lstItems = focusContainer->containList.GetNextInList();

      // твоя проверка
      if( lstItems ) {
        oCItem* pItem = lstItems->GetData();
        zSTRING firstItemName = pItem->GetName( 0 );
        screen->PrintCXY( firstItemName );
      }
    }

Расшарить до цикла с выводом всего содержимого осилишь? ::)
 

neromont


Модостроитель
Регистрация
12 Мар 2011
Сообщения
674
Благодарности
655
Баллы
245
Попробуем :)
 

neromont


Модостроитель
Регистрация
12 Мар 2011
Сообщения
674
Благодарности
655
Баллы
245
Уверен что можно сделать элегантнее.

C++:
oCMobContainer* focusContainer = player->GetFocusVob()->CastTo<oCMobContainer>();
        if (focusContainer) {
            zCListSort<oCItem>* lstItems = focusContainer->containList.GetNextInList();
            zSTRING result;
            oCItem* pItem;
            // твоя проверка
            if (lstItems) {
                for (int i = 1;;i++) {
                    if (!lstItems) return;
                    pItem = lstItems->GetData();
                    result = pItem->GetName(0);
                    lstItems = lstItems->GetNextInList();
                    screen->PrintCX(i * 200, result);
                }
            }         
        }

Хотел объединить zSTRING через += или + он не знает такого.
Пробовал использовать string с последующим использованием GetVector().
Почему-то ничего не выводилось.
Возможно где-то ошибся.

Блин можно же через while.

А вообще чумовая вещь! *ecstatic**ecstatic**ecstatic*
 

Вложения

  • Безымянный.png
    Безымянный.png
    712,3 KB · Просмотры: 348
Последнее редактирование:

neromont


Модостроитель
Регистрация
12 Мар 2011
Сообщения
674
Благодарности
655
Баллы
245
Так можно добавить предмет инвентаря в сундук, если его открыть.

C++:
if (focusContainer->state) {

        focusContainer->CreateContents("ItMi_Gold");

}

1. Как сделать флажок для флага, что в этот сундук Item был уже добавлен и больше его не вставлять?
2. Как вставить несколько одинаковых предметов?

Понял, также как в Spacer.
C++:
focusContainer->CreateContents("ItMi_Gold:2, ItFo_Addon_Shellflesh:5");

В качестве флажка можно использовать неиспользуемое поле isAmbient, наверное :)
C++:
if (focusContainer) { // Лучше повесить на фокус, так как при проверке состояния Item добавляется после того как взяли существовавшие в сундуке предметы
                if (!focusContainer->bIsAmbientVob) {
                focusContainer->bIsAmbientVob = true;
                    focusContainer->CreateContents("ItMi_Gold:2, ItFo_Addon_Shellflesh:5");
                }
            }

Теперь возник естественный вопрос, а как получить список всех доступных в скриптах предметов инвентаря?
 
Последнее редактирование:

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.281
Благодарности
4.581
Баллы
625
1. Как сделать флажок для флага, что в этот сундук Item был уже добавлен и больше его не вставлять?
Вариант при наличии указателя на item
C++:
if( !focusContainer->IsIn( pItem->GetInstance() ) )
      focusContainer->CreateContents( pItem->GetInstanceName() );

Вариант через имя инстанции
C++:
    int instance = parser->GetIndex( "ITMI_GOLD" );
    if( instance  != -1 && !focusContainer->IsIn( instance ) )
      focusContainer->CreateContents( "ITMI_GOLD" );

Теперь возник естественный вопрос, а как получить список всех доступных в скриптах предметов инвентаря?
А хз, надо посмотреть как парсер хранит данные. Сейчас некогда пока.
 

Gratt


Модостроитель
Регистрация
14 Ноя 2014
Сообщения
3.281
Благодарности
4.581
Баллы
625
neromont
Вот заодно сверь чтобы цикл перебора списка был таким же
C++:
    zCListSort<oCItem>* lstItems = focusContainer->containList.GetNextInList();

      while( lstItems ) {
        oCItem* pItem = lstItems->GetData();
        zSTRING firstItemName = pItem->GetName( 0 );
        lstItems = lstItems->GetNextInList();
      }
 

neromont


Модостроитель
Регистрация
12 Мар 2011
Сообщения
674
Благодарности
655
Баллы
245
neromont
Вот заодно сверь чтобы цикл перебора списка был таким же
C++:
    zCListSort<oCItem>* lstItems = focusContainer->containList.GetNextInList();

      while( lstItems ) {
        oCItem* pItem = lstItems->GetData();
        zSTRING firstItemName = pItem->GetName( 0 );
        lstItems = lstItems->GetNextInList();
      }

А в этом случае в zSTRING не будут замещаться значения?

Отображается только название последнего предмета в инвентаре.
 
Последнее редактирование:
Сверху Снизу