GTA Builder Форум

GTA Vice City => Программирование => Тема начата: DK от Май 02, 2016, 09:49:53 pm

Название: Написание плагина. Настройка проекта
Отправлено: DK от Май 02, 2016, 09:49:53 pm
В этой статье я опишу создание плагина для GTA. Основное внимание уделю созданию и настройке проекта.
Я использую Visual Studio 2017. В открытом доступе есть Community версия этого продукта. Впрочем, найти Pro или Enterprise версию тоже не сложно.
Ах, да... Что требуется от вас:
-Наличие игры
-Умение писать код (если вы до этого не занимались скриптингом - возможно, лучше стоит начать с него)
-Базовые знания C/C++ (хотя, можно и без этого, как я в своё время  :P)
-Желание познавать и... время.


Напомню, что ASI-плагин - это динамическая библиотека с расширением .asi, которая подгружается (ASI-Loader'ом) и прикрепляется к процессу игры.
Итак, поехали.

Инструкция (https://github.com/DK22Pac/plugin-sdk/wiki)
Название: Re: Написание плагина. Настройка проекта
Отправлено: Shagg_E от Май 03, 2016, 12:46:24 am
Класс! Скорее всего, как будет возможность - перейду с VS 2010 на 2015, ибо первый у меня глючит.

Хотелось бы туториал по вызову перед стартом игры окна с выбором одного из 3х пунктов.
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Май 03, 2016, 10:55:49 am
Shaggy, для этого нужно использовать кастомный ASI-Loader (он загружает библиотеки при загрузке приложения).
Название: Re: Написание плагина. Настройка проекта
Отправлено: Shagg_E от Май 03, 2016, 11:50:59 am
Это уже есть, и я даже реализовал(спс Sector-у и xanser-у) то, что мне нужно(подмена имен загружаемых файлов и пара мелочей), всё работает, однако через ini, а хотелось бы научиться создавать окно с выбором пункта, ибо в ini у меня всего лишь один пункт настройки(глупо создавать ini ради одной, пускай и самой важной настройки) и вообще - по многим причинам нужно именно окно, а не ini.
Нужно окно по типу такого (http://gta.nick7.com/programs/videomode/videomode-003.png).
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Май 03, 2016, 11:57:31 am
Shaggy, ты имеешь ввиду окно Windows или настройку как пункт в меню?
Название: Re: Написание плагина. Настройка проекта
Отправлено: Shagg_E от Май 03, 2016, 11:58:03 am
Окно Windows (http://gta.nick7.com/programs/videomode/videomode-003.png)
Название: Re: Написание плагина. Настройка проекта
Отправлено: xanser от Май 03, 2016, 06:03:46 pm
А я бы хотел полноценное окно для самой игры. Мне кажется widescreen вайсу не подходит и приходится использовать олдскульный квадратный экран, да и удобно было бы следить за происходящим в системном трее.

DK можно уточнить? Раньше плагин подключался в виде функции в такой конструкции:
Код: C++
  1. BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
  2. {
  3.         switch (ul_reason_for_call)
  4.         {
  5.         case DLL_PROCESS_ATTACH:
  6.                 LoadViceCityPlugin();
  7.                 break;
  8.         case DLL_PROCESS_DETACH:
  9.                 break;
  10.         }
  11.         return TRUE;
  12. }

В твоем варианте это все уже не нужно и достаточно создать экземпляр класса, что автоматически вызовет конструктор и по пути все пропатчит?
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Май 03, 2016, 07:03:38 pm
Shaggy, конкретно по этому не планировал делать туториал. Но можно глянуть, как это сделано в примерах RenderWare. Наверняка там MFC используется.
xanser, да. Так тоже можно. А если надо что-то сделать при детаче - нужно писать это в деструктор.
Код: C++
  1. class MyPlugin{
  2. public:
  3.     MyPlugin(){
  4.         // on attach
  5.     }
  6.  
  7.     ~MyPlugin(){
  8.         // on detach
  9.     }
  10. };
  11.  
  12. MyPlugin myPlugin; // создали экземпляр класса в глобальном пространстве (глобальный обьект)
Цитировать
Объявление функции в глобальной области видимости вводит глобальную функцию, а объявление переменной – глобальный объект. Глобальный объект существует на протяжении всего времени выполнения программы. Время жизни глобального объекта начинается с момента запуска программы и заканчивается с ее завершением.
Т.е. обьект будет создан при аттаче dll (будет вызван конструктор), а разрушен - при детаче (будет вызван деструктор).
Название: Re: Написание плагина. Настройка проекта
Отправлено: Sektor от Май 03, 2016, 09:25:47 pm
xanser, да потому-что объект будет создан, так сказать компилятором и уничтожен тоже, иначе говоря компилятор позаботиться обо всем сам, в данном случае.
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Май 07, 2016, 02:36:19 pm
Вынесено в шапку.
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Июнь 11, 2016, 10:36:17 am
Создал шаблон для Vice City.
Правда, там идёт привязка к несуществующему plugin_vc.
https://github.com/DK22Pac/plugin-sdk/tree/master/extra/VS%20templates
Название: Re: Написание плагина. Настройка проекта
Отправлено: Shagg_E от Июнь 11, 2016, 11:05:15 am
Круто! Жаль, у меня 2010, но я постараюсь за лето на 2015 пересесть)
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Июнь 19, 2016, 03:43:30 pm
Я уже пишу плагины для VC c новым sdk  :P
Код: C++
  1. #include <plugin_vc.h>
  2. #include <d3dx8.h>
  3. #include <game_vc\RenderWare.h>
  4.  
  5. using namespace plugin;
  6.  
  7. class DXFont {
  8. public:
  9.     static ID3DXFont *m_pD3DXFont;
  10.  
  11.     static void InitFont() {
  12.         HFONT hFont = CreateFont(48, 0, 0, 0, FW_NORMAL, FALSE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
  13.             CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_SWISS, "arial");
  14.         if (FAILED(D3DXCreateFont(_RwD3DDevice, hFont, &m_pD3DXFont)))
  15.             Error("Failed to create D3DX font!");
  16.     }
  17.  
  18.     static void DestroyFont() {
  19.         if (m_pD3DXFont) {
  20.             m_pD3DXFont->Release();
  21.             m_pD3DXFont = NULL;
  22.         }
  23.     }
  24.  
  25.     static void Draw() {
  26.         if (m_pD3DXFont) {
  27.             RECT rect;
  28.             rect.left = 0;
  29.             rect.top = 0;
  30.             rect.bottom = RsGlobal.maximumHeight;
  31.             rect.right = RsGlobal.maximumWidth;
  32.             m_pD3DXFont->DrawTextA("D3DXFont example for GTA Vice City", -1, &rect, DT_CENTER|DT_VCENTER,
  33.                 D3DCOLOR_RGBA(255, 255, 0, 255));
  34.         }
  35.     }
  36.  
  37.     DXFont() {
  38.         m_pD3DXFont = NULL;
  39.         Events::drawingEvent += Draw;
  40.         Events::initRwEvent += InitFont;
  41.         Events::shutdownRwEvent += DestroyFont;
  42.         Events::d3dLostEvent += DestroyFont;
  43.         Events::d3dResetEvent += InitFont;
  44.     }
  45. } dxFont;
  46.  
  47. ID3DXFont *DXFont::m_pD3DXFont;
Название: Re: Написание плагина. Настройка проекта
Отправлено: Shagg_E от Июнь 19, 2016, 05:34:21 pm
Шикарно! Таким макаром, наверное можно и стандартные тексты выдавать windows-шрифтами!
Жаль правда, что с rwd3d9 (http://gtaforums.com/topic/819551-rwd3d9-d3d9-extension-for-rw/) конфликтует: при активированном в global.ini UseD3D8to9=1 иногда на пару секунд происходит нечто:
(http://funkyimg.com/p/2daho.jpg) (http://funkyimg.com/view/2daho)
(http://funkyimg.com/p/2dahp.jpg) (http://funkyimg.com/view/2dahp)
Закономерностей не заметил - просто изредка на секунду-другую весь GUI слетает и творится ад. Небо же начинает сохранять "следы" динамических объектов(видно на скринах). Если выставить UseD3D8to9=0 - такого не наблюдается. Очевидно, идет какой-то конфликт из-за того, что оба плагина(Ultimate asi loader (https://github.com/ThirteenAG/Ultimate-ASI-Loader/releases) с rwd3d9 и этот плагин с текстом) юзают какие-то функции dxd8
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Июнь 19, 2016, 10:40:15 pm
Наверное, для d3d8tod3d9 стоит использовать DX9 sdk.
Название: Re: Написание плагина. Настройка проекта
Отправлено: Shagg_E от Июнь 19, 2016, 11:57:52 pm
А есть и такой?
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Июль 29, 2016, 09:48:42 am
Дмитрий, во-первых, хочу сказать спасибо за проделанную работу по plugin-sdk, во-вторых, хочу сказать, что есть большое желание освоить написание плагинов для GTA. Года два назад я начал было, но инфы по написанию плагинов очень мало, написал один простой плагин и на этом как-то изучение остановилось. При написании клео скриптов мне в своё время очень помогли исходники скриптов Дениса. По ним-то я и научился писать высокоуровневый код, а начинал с написания в меточном стиле. Так вот к чему это я всё - хорошо бы здесь написать несколько простых примеров для SA, например, как создавать транспорт с определённой моделью в определённых координатах, работу с транспортом и компонентами транспорта и т.д. Хорошо бы, как сделано здесь http://ru-script.3dn.ru/publ/gta_iv/gta_iv/obzor_klassa_vehicles/35-1-0-177 (http://ru-script.3dn.ru/publ/gta_iv/gta_iv/obzor_klassa_vehicles/35-1-0-177) - тот же код в примере клео скрипта. Заранее спасибо.

Кстати, плагин для VC не собирается. У меня отсутствует dxd8.h. Можно заменить строчку на dxd9.h или где взять dxd8.h 
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Июль 30, 2016, 12:44:50 am
kenking, уже поправил это (зависимость от d3d8) в VC, скачай репозиторий заново, или удали зависимость вручную (нужно убрать обьявление _D3D_INCLUDE в настройках проекта plugin_vc):
(http://i.imgur.com/sDFJmi5.png)
Plugin-sdk для VC пока что малофункционален, там даже нет классов CPed и CVehicle.

По работе с компонентами я сделаю примеры.

А пока что - такое видео.
https://www.youtube.com/watch?v=PVfC7YSOEQs (https://www.youtube.com/watch?v=PVfC7YSOEQs)
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Июль 30, 2016, 10:27:08 am
Цитировать
или удали зависимость вручную (нужно убрать обьявление _D3D_INCLUDE в настройках проекта plugin_vc):
Удалил. Плагин VC собрался. А пример VC_DXFont не идёт. Там же #include <d3dx8.h>

Цитировать
уже поправил это (зависимость от d3d8) в VC, скачай репозиторий заново
Скачал и новую версию. Здесь плагин VC не собирается. Вот
(http://savepic.net/8279914m.png) (http://savepic.net/8279914.png)


За пример в видео спасибо, но я имел ввиду создание транспорта в определённых координатах, с загрузкой модели
00A5: 0@ = create_car #PONY at 0.0 0.0 0.0
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Июль 30, 2016, 06:05:42 pm
Цитировать
А пример VC_DXFont не идёт. Там же #include <d3dx8.h>

Всё верно. Чтобы его собрать нужен DX8 SDK.
https://drive.google.com/uc?export=download&id=0B3pQzS44FafdajNJcy1Cb2haUkk (https://drive.google.com/uc?export=download&id=0B3pQzS44FafdajNJcy1Cb2haUkk)
Включи обратно _D3D_INCLUDE в проекте plugin_vc, и настрой пути в Configuration Properties > VC++ Directories > Include Directories
http://i.imgur.com/n3DzDfe.png (http://i.imgur.com/n3DzDfe.png)
Это же надо сделать и для проекта VC_DXFont, +изменить путь вот тут к своей папке
http://i.imgur.com/cmL3Qlb.png (http://i.imgur.com/cmL3Qlb.png)

Скачал и новую версию. Здесь плагин VC не собирается. ВотУже добавил эти файлы.

За пример в видео спасибо, но я имел ввиду создание транспорта в определённых координатах, с загрузкой моделиОпкод 00A5 вызывает функцию CCarCtrl::CreateCarForScript. Пока что её нету в sdk (класс CCarCtrl пока что добавлен только частично), но я добавлю.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Июль 31, 2016, 08:04:23 am
Теперь при сборке VC_DXFont выходит:
1>------ Перестроение всех файлов начато: проект: VC_DXFont, Конфигурация: Release Win32 ------
1>  Main.cpp
1>Main.obj : error LNK2001: неразрешенный внешний символ ""struct IDirect3DDevice8 * & _RwD3DDevice" (?_RwD3DDevice@@3AAPAUIDirect3DDevice8@@A)"
1>D:\Games\GTA Vice City 1.0\VC_DXFont.asi : fatal error LNK1120: неразрешенных внешних элементов: 1
========== Перестроение всех проектов: успешно: 0, с ошибками: 1, пропущено: 0 ==========
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Июль 31, 2016, 08:21:13 am
kenking, plugin_vc пересобрал с _D3D_INCLUDE?

PS прямо сейчас занимаюсь классом CCarCtrl, после покажу пример создания транспорта и манипулирования им.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Июль 31, 2016, 10:31:35 am
Цитировать
plugin_vc пересобрал с _D3D_INCLUDE?
Да.

Цитировать
прямо сейчас занимаюсь классом CCarCtrl, после покажу пример создания транспорта и манипулирования им.
Жду с нетерпением. Спасибо, что подробно всё разъясняешь.

Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Июль 31, 2016, 05:54:42 pm
Да.
Попробуй именно пересобрать (не Build, а Rebuild).
А после этого пересобери VC_DXFont.

Пример по спавну авто и управлению компонентом уже добавил.
https://github.com/DK22Pac/plugin-sdk/blob/master/examples/SA_CreateCar/Main.cpp

Спрашивай если что-то не понятно.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Июль 31, 2016, 07:51:08 pm
Цитировать
Попробуй именно пересобрать (не Build, а Rebuild).
А после этого пересобери VC_DXFont.
Так и делал. Пересобрал ещё раз - тоже самое.

Цитировать
Пример по спавну авто и управлению компонентом уже добавил.
За пример с подробными комментариями огромное спасибо!

Цитировать
Спрашивай если что-то не понятно.
Как вращать компоненты (CMatrix__rotateAroundX, CMatrix__rotateAroundY, CMatrix__rotateAroundZ)? Попробовал так:
if (automobile->m_aCarNodes[CAR_DOOR_RR])
automobile->m_aCarNodes[CAR_DOOR_RR]->modelling.at.z += 1.57f;
и так:
if (automobile->m_aCarNodes[CAR_DOOR_RF])
automobile->m_aCarNodes[CAR_DOOR_RF]->modelling.up.z += 1.57f;
Вот что вышло:  ;D
(http://savepic.net/8321684m.jpg) (http://savepic.net/8321684.jpg)

Сделай пожалуйста пример вращения компонента в цикле на транспорте игрока.
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Июль 31, 2016, 09:24:17 pm
Тут всё очень просто - метод CVehicle::SetComponentRotation(RwFrame* компонент, int ось, float угол(радианы), bool ресет_позиции_после_вращения).
Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\common.h"
  3. #include "game_sa\CAutomobile.h"
  4.  
  5. using namespace plugin;
  6.  
  7. class RotateDoorExample {
  8. public:
  9.     static float m_angle;
  10.  
  11.     RotateDoorExample() {
  12.         Events::gameProcessEvent.Add([] {
  13.             CVehicle *vehicle = FindPlayerVehicle(0, false); // Находим авто игрока (0 - это индекс игрока)
  14.             if (vehicle && vehicle->m_dwVehicleClass == VEHICLE_AUTOMOBILE) { // если транспорт найден и его тип - автомобиль
  15.                 m_angle += 0.01f; // прибавляем 0.01 каждый фрейм
  16.                 if (m_angle > (_RW_pi * 2.0f)) // 2 pi = ~ 6,28
  17.                     m_angle = 0.0f; // обнуляем если дошли до максимума
  18.                 CAutomobile *automobile = reinterpret_cast<CAutomobile *>(vehicle); // приводим тип, чтобы обращаться к транспорту как к CAutomobile (CAutomobile - дочерний класс CVehicle)
  19.                 // Этот массив компонентов (m_aCarNodes @0x648) - уникален для CAutomobile. Имея указатель на CVehicle,
  20.                 // мы не сможем к нему обратиться - поэтому и приводим тип, предварительно убедившись, что транспорт - автомобиль.
  21.                 // У CBike, CTrain - также есть свои таблицы компонентов.
  22.                 if (automobile->m_aCarNodes[CAR_DOOR_RF])
  23.                     automobile->SetComponentRotation(automobile->m_aCarNodes[CAR_DOOR_RF], 0, m_angle, true); // здесь 0 - это ось (0,1,2 - соотв. X,Y,Z)
  24.                 if (automobile->m_aCarNodes[CAR_DOOR_LF])
  25.                     automobile->SetComponentRotation(automobile->m_aCarNodes[CAR_DOOR_LF], 0, m_angle, true);
  26.             }
  27.         });
  28.     }
  29. } example;
  30.  
  31. float RotateDoorExample::m_angle = 0.0f; // начальное значение = 0.0
Также есть функции RwMatrixRotate/RwFrameRotate, которые вращают матрицу/фрейм вокруг оси (ось можно задать своим вектором).
Ну и ещё вариант - вращение методами CMatrix: RotateX,RotateY,RotateZ и другими (см. файл CMatrix.h) Eсли будет нужно, то покажу на примере.

Вот тут более сложный вариант, с возможностью вращения по трём осям с помощью кватерниона.
https://github.com/DK22Pac/plugin-sdk/blob/master/examples/SA_RotateDoor/Main.cpp (https://github.com/DK22Pac/plugin-sdk/blob/master/examples/SA_RotateDoor/Main.cpp)
Вращение устанавливается методом
quat.Set(m_angle, 0.0f, 0.0f);где 3 параметра - вращение в радианах по осям y, z, x, соответственно.


По поводу VC_DXFont - в plugin_vc открой в студии файл game_vc/RenderWare.cpp и глянь, не закомменчена ли строка
LPDIRECT3DDEVICE8 &_RwD3DDevice = *(LPDIRECT3DDEVICE8 *)0x7897A8;как у меня на скрине
(http://i.imgur.com/paKRub2.png)
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 01, 2016, 08:05:13 am
Цитировать
По поводу VC_DXFont - в plugin_vc открой в студии файл game_vc/RenderWare.cpp и глянь, не закомменчена ли строка
Вот:
(http://savepic.net/8307367m.png) (http://savepic.net/8307367.png)

С примерами буду разбираться, спасибо.

Ещё вопросы:
1) При обновлении sdk как лучше обновить у себя? Достаточно ли будет копировать новые файлы с заменой старых и пересобрать плагин? Шаблон пересобирать надо или нет?
2) В SA_CreateCar CStreaming::RequestModel(modelId, 2); цифра 2 что означает?
а в SA_PedSpawner стоит 0
CStreaming::RequestModel(modelID, 0);
UPD:
Вот здесь
void CVehicle::OpenDoor(CPed* ped, int componentId, eDoors door, float doorOpenRatio, bool playSound)eDoors door - что ставить? В клео я ставил 0-5, а здесь пишет аргумент типа int несовместим с параметром типа "eDoors".
Вот код моего скрипта (для GTA3, но не суть), не пойму, как сделать такое в плагине (для SA). Цикл for в частности, как реализовать wait 0? Можно пример? Пожалуйста  ???

{$CLEO .cs}
05E5: 15@ = game_version
if
  15@ == 0
then
    6@ = 0x52E660
    7@ = 0x545930
    8@ = 0x52EF90
    9@ = 0x52E750
else
    if
      15@ == 1
    then 
        6@ = 0x52E8A0
        7@ = 0x545B30
        8@ = 0x52F1D0
        9@ = 0x52E990
    else
        05DC: end_custom_thread
    end   
end
var
   12@: float
end

while true
wait 0
    if
      Player.Defined($PLAYER_CHAR)
    then
        if and
          $ONMISSION == 0
          Actor.Driving($PLAYER_ACTOR)
          80DE: not player $PLAYER_CHAR driving_vehicle_type #PREDATOR
          80DE: not player $PLAYER_CHAR driving_vehicle_type #SPEEDER
          80DE: not player $PLAYER_CHAR driving_vehicle_type #REEFER
          80DE: not player $PLAYER_CHAR driving_vehicle_type #GHOST
        then
            if
              05EE: key_pressed 219 // [
            then
                0@ = 17 // ComponentID
                1@ = 0  // eDoors
                gosub @open_component // капот
                repeat
                  wait 0
                until 85EE: not key_pressed 219 // [ 
            end
            if
              05EE: key_pressed 221 // ]
            then
                0@ = 18
                1@ = 1
                gosub @open_component // багажник
                repeat
                  wait 0
                until 85EE: not key_pressed 221 // ] 
            end
            if and
              05EE: key_pressed 186 // ;
              05EE: key_pressed 187 // =
            then
                0@ = 15
                1@ = 2
                gosub @open_component // левая передняя дверь
                repeat
                  wait 0
                until 85EE: not key_pressed 187 // = 
            end
            if and
              05EE: key_pressed 222 // '
              05EE: key_pressed 187 // =
            then
                0@ = 11
                1@ = 3
                gosub @open_component // правая передняя дверь
                repeat
                  wait 0
                until 85EE: not key_pressed 187 // = 
            end
            if and
              05EE: key_pressed 186 // ;
              05EE: key_pressed 189 // -
            then
                0@ = 16
                1@ = 4
                gosub @open_component // левая задняя дверь
                repeat
                  wait 0
                until 85EE: not key_pressed 189 // - 
            end
            if and
              05EE: key_pressed 222 // '
              05EE: key_pressed 189 // -
            then
                0@ = 12
                1@ = 5
                gosub @open_component // правая задняя дверь
                repeat
                  wait 0
                until 85EE: not key_pressed 189 // - 
            end
        end
    end
end

:open_component
if
  Actor.Driving($PLAYER_ACTOR)
then 
    03C0: 10@ = actor $PLAYER_ACTOR car
    05E7: 10@ = car 10@ struct
    0085: 11@ = 10@
    05E4: call_function_method 6@ struct 11@ num_params 1 pop 0 0@ {1..19} 2@ // IsComponentPresent
    if
      2@ == 1
    then
        0085: 3@ = 10@
        3@ += 0x288 // CDamageManager
        05E4: call_function_method 7@ struct 3@ num_params 1 pop 0 1@ {0..5} 4@ // GetDoorStatus
        if
          4@ <> 3
        then
            05E4: call_function_method 8@ struct 10@ num_params 1 pop 0 1@ {0..5} 5@ // IsDoorFullyOpen
            if
              5@ == 0
            then
                for 12@ = 0.0 TO 1.0 step 0.1
                   05E3: call_function_method 9@ struct 10@ num_params 3 pop 0 12@ 1@ 0@ {1..19} // CAutomobile__OpenDoor
                   wait 0
                end
            else
                for 12@ = 1.0 DOWNTO 0.0 step 0.1
                   05E3: call_function_method 9@ struct 10@ num_params 3 pop 0 12@ 1@ 0@ {1..19} // CAutomobile__OpenDoor
                   wait 0
                end
            end
        end 
    end 
end
return         
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 01, 2016, 10:02:53 pm
Вот:

Ты что-то не так сделал.
Открой настройки plugin_vc, должно быть определение _D3D_INCLUDE вот тут:
http://i.imgur.com/WyptPrQ.png (http://i.imgur.com/WyptPrQ.png)
У тебя проект plugin_vc сейчас скомпилирован без переменной _RwD3DDevice. Поэтому при попытке компилировать VC_DXFont ты получаешь ошибку с сообщением, что переменная не найдена.

1) При обновлении sdk как лучше обновить у себя? Достаточно ли будет копировать новые файлы с заменой старых и пересобрать плагин? Шаблон пересобирать надо или нет?

Можно и так. Скопировать, заменить, пересобрать plugin-sdk, пересобрать свои проекты. Шаблон менять не надо, если он не изменялся в репозитории.
Но я бы советовал использовать специальные утилиты для обновления/изменения:
Git for Windows (https://git-scm.com)
TortoiseGit (http://"https://tortoisegit.org)

Как с этим работать:
Скачать Git, установить, скачать TortoiseGit, установить. После этого в контекстном меню Windows появятся новые элементы.
Открыть страницу plugin-sdk на github, нажать кнопку "Clone or Download", скопировать ссылку:
(http://i.imgur.com/CdMvdeP.png)

Зайти в папку своих проектов (если там уже есть папка plugin-sdk - удалить её), нажать ПКМ, далее "Git Clone":
(http://i.imgur.com/LTKDVAc.png)

Если ссылка правильно скопировалась, то программа сама всё настроит:
(http://i.imgur.com/T2dizUI.png)

Нажать "ОК", после загрузки - "Close".

Теперь у тебя будет папка-репозитарий plugin-sdk.  :P
(http://i.imgur.com/pNlgHaz.png)

Как с ней работать:
Нажать ПКМ на папке (plugin-sdk), далее выбрать "Git Sync...":
(http://i.imgur.com/6Uyyx86.png)

Если надо обновить (перед этой процедурой желательно закрыть студию):
Нажимаешь кл. Pull
(http://i.imgur.com/z16OA7H.png)
После апдейтинга, ты увидишь список "коммитов", которые были загружены:
(http://i.imgur.com/FgIom7r.png)
Нажать Close.

Если ты внес изменения (например, добавил какую-то функцию, класс, что-то поправил, или даже добавил свой пример) и хочешь внести их в репозиторий:
Создать аккаунт на github.
Сообщить мне свой никнейм (мне нужно будет добавить его в список редакторов).

Нажать кл. Commit
(http://i.imgur.com/G5fQpd6.png)
Далее в окне "коммита" дать ему какое-то название (обычно описание изменений) и выбрать все файлы, которые ты изменял (по дефолту, новодобобавленные файлы не отмаечаются автоматом):
(http://i.imgur.com/arrNtDD.png)
Нажать Commit & Push (на кнопке Commit есть справа стрелка, открывающая доп. опции), после отправки нажать Close.

2) В SA_CreateCar CStreaming::RequestModel(modelId, 2); цифра 2 что означает?
а в SA_PedSpawner стоит 0
CStreaming::RequestModel(modelID, 0);

Это флаги загрузки и стриминга модели. Честно скажу - сам пока что не очень разобрался, как правильно их использовать. Примерно я их наименовал так:
enum eStreamingFlags {
    GAME_REQUIRED = 0x2, // Модель нужна игре?  (Сохранять в памяти, пока не будет вызвана SetModelIsDeletable, дальше - пусть игра сама решает, когда удалить модель)
    MISSION_REQUIRED = 0x4, // Модель нужна миссии? (Сохранять в памяти, пока не будет вызвана SetMissionDoesntRequireModel, дальше - пусть игра сама решает, когда удалить модель)
    KEEP_IN_MEMORY = 0x8, // Сохранять в памяти, пока мы сами не выгрузим модель (RemoveModel)
    PRIORITY_REQUEST = 0x10 // Особый приоритет загруки (в функции LoadAllRequestedModels есть параметр на загрузку только таких моделей)
};
Эти описания - примерные, я их только вчера составил, и не было времени всё проверить. Раньше этого перечисления я вообще нигде не находил :)
Вот здесь
void CVehicle::OpenDoor(CPed* ped, int componentId, eDoors door, float doorOpenRatio, bool playSound)eDoors door - что ставить? В клео я ставил 0-5, а здесь пишет аргумент типа int несовместим с параметром типа "eDoors".

Типы, которые начинаются с "e" - это enumerations (перечисления).
Тип eDoors обьявлен, но он пустой (в CDamageManager.h):
(http://i.imgur.com/xuwNt2Z.png)
Если ты знаешь значения этого перечисления - можешь их добавить в файле CDamageManager.h, и далее использовать их при вызове (напр. DOOR_FRONT_LEFT).
Если нужно использовать integer вместо enum - нужно привести тип:
static_cast<eDoors>(5)
Вот код моего скрипта (для GTA3, но не суть), не пойму, как сделать такое в плагине (для SA). Цикл for в частности, как реализовать wait 0? Можно пример? Пожалуйста  ???

Wait вообще не нужен (есть аналог в виде Sleep(), но использовать его где-либо не желательно). Ты же внедряешь свой код в код игры. Сделаешь где-то задержку - и игра тоже её сделает.
Тебе нужно мыслить по-другому - ты "вклиниваешься" в процесс каждый фрейм. Твой код (функция) будет вызываться один раз за фрейм - допустим, где-то в CGame::Process (если используется gameProcessEvent). Что делать в такой ситуации? Тут есть несколько вариантов. Самое простое - разделить код на отдельные процессы (состояния) и создать переменную, которая будет хранить id поточного процесса (состояния) (и которой будем переключать процессы (состояния))
Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\common.h"
  3. #include "game_sa\CAutomobile.h"
  4. #include "game_sa\CTimer.h"
  5. #include "game_sa\CMessages.h"
  6.  
  7. using namespace plugin;
  8.  
  9. class OpenDoorExample {
  10. public:
  11.     enum eOpenDoorState {
  12.         STATE_OPENING, // сейчас мы что-то открываем
  13.         STATE_CLOSING, // закрываем
  14.         STATE_WAITING  // ждём указаний
  15.     };
  16.  
  17.     static eOpenDoorState m_currentState; // наш статус
  18.     static CAutomobile *m_pCurrentCar; // авто, с которым сейчас что-то делаем
  19.     static float m_openingState; // статус открытия/закрытия
  20.     static int m_currentComponentId;
  21.     static eDoors m_currentDoorId;
  22.     static unsigned int m_nLastTimeWhenAnyActionWasEnabled;
  23.  
  24.     static void OpenComponent(CAutomobile *automobile, int componentId, eDoors doorId) {
  25.         if (automobile->IsComponentPresent(componentId)) {
  26.             if (automobile->m_damageManager.GetDoorStatus(doorId) != 3) { // 3 - это тоже член какого-то перечисления, напр. STATUS_DAMAGED
  27.                 m_pCurrentCar = automobile; // сохраняем все данные в переменные, чтобы потом заюзать при обработке STATE_OPENING/STATE_CLOSING (см. функцию Process)
  28.                 m_currentComponentId = componentId;
  29.                 m_currentDoorId = doorId;
  30.                 if (!automobile->IsDoorFullyOpen(doorId)) {
  31.                     m_currentState = STATE_OPENING; // переключаем статус
  32.                     m_openingState = 0.0f; // и ресетим статус открытия
  33.                 }
  34.                 else {
  35.                     m_currentState = STATE_CLOSING; // переключаем статус
  36.                     m_openingState = 1.0f; // и ресетим статус открытия
  37.                 }
  38.                 m_nLastTimeWhenAnyActionWasEnabled = CTimer::m_snTimeInMilliseconds; // запоминаем время
  39.             }
  40.         }
  41.     }
  42.  
  43.     static void Process() { // Обрабатываем поточное состояние - ждём нажатия клавиши ИЛИ открываем ИЛИ закрываем компонент
  44.         switch (m_currentState) {
  45.         case STATE_OPENING: // Что-то открываем. Обрати внимание, что ранее мы записали поточное авто и ид компонентов в статические переменные класса (по сути, это глобальные переменные)
  46.             m_pCurrentCar->OpenDoor(0, m_currentComponentId, m_currentDoorId, m_openingState, true);
  47.             m_openingState += 0.05f;
  48.             if (m_openingState > 1.0f) { // Если полностью открыли
  49.                 m_currentState = STATE_WAITING; // Переключаем статус на "ожидание"
  50.                 m_pCurrentCar->OpenDoor(0, m_currentComponentId, m_currentDoorId, 1.0f, true); // Полностью открываем
  51.             }
  52.             break;
  53.         case STATE_CLOSING: // Что-то закрываем
  54.             m_pCurrentCar->OpenDoor(0, m_currentComponentId, m_currentDoorId, m_openingState, true);
  55.             m_openingState -= 0.05f;
  56.             if (m_openingState < 0.0f) { // Если полностью закрыли
  57.                 m_currentState = STATE_WAITING; // Переключаем статус на "ожидание"
  58.                 m_pCurrentCar->OpenDoor(0, m_currentComponentId, m_currentDoorId, 0.0f, true); // Полностью закрываем
  59.             }
  60.             break;
  61.         case STATE_WAITING:
  62.             if (CTimer::m_snTimeInMilliseconds > (m_nLastTimeWhenAnyActionWasEnabled + 500)) { // если прошло 500 мс с того времени, как мы начали открывать/закрывать что-то
  63.                 CVehicle *vehicle = FindPlayerVehicle(0, false);
  64.                 if (vehicle && vehicle->m_dwVehicleClass == VEHICLE_AUTOMOBILE) {
  65.                     CAutomobile *automobile = reinterpret_cast<CAutomobile *>(vehicle); // опять же, приведение типов. Т.к. мы будет юзать damageManager, нам нужно убедиться, что транспорт - это автомобиль (CAutomobile)
  66.                     if (KeyPressed(219)) // [
  67.                         OpenComponent(automobile, CAR_BONNET, static_cast<eDoors>(0)); // капот
  68.                     else if (KeyPressed(221)) // ]
  69.                         OpenComponent(automobile, CAR_BOOT, static_cast<eDoors>(1)); // багажник
  70.                     else if (KeyPressed(186) && KeyPressed(187)) // ; =
  71.                         OpenComponent(automobile, CAR_DOOR_LF, static_cast<eDoors>(2)); // левая передняя дверь
  72.                     else if (KeyPressed(222) && KeyPressed(187)) // ' =
  73.                         OpenComponent(automobile, CAR_DOOR_RF, static_cast<eDoors>(3)); // правая передняя дверь
  74.                     else if (KeyPressed(186) && KeyPressed(189)) // ; -
  75.                         OpenComponent(automobile, CAR_DOOR_LR, static_cast<eDoors>(4)); // левая задняя дверь
  76.                     else if (KeyPressed(222) && KeyPressed(189)) // ' -
  77.                         OpenComponent(automobile, CAR_DOOR_RR, static_cast<eDoors>(5)); // правая задняя дверь
  78.                 }
  79.             }
  80.         }
  81.  
  82.         switch (m_currentState) { // Для наглядности выведем поточный статус на экран
  83.         case STATE_OPENING:
  84.             CMessages::AddMessageJumpQ("State: OPENING", 100, 0, false);
  85.             break;
  86.         case STATE_CLOSING:
  87.             CMessages::AddMessageJumpQ("State: CLOSING", 100, 0, false);
  88.             break;
  89.         case STATE_WAITING:
  90.             CMessages::AddMessageJumpQ("State: WAITING", 100, 0, false);
  91.             break;
  92.         }
  93.     }
  94.  
  95.     OpenDoorExample() {
  96.         Events::gameProcessEvent.Add(Process);
  97.     }
  98. } example;
  99.  
  100. OpenDoorExample::eOpenDoorState OpenDoorExample::m_currentState = STATE_WAITING;
  101. CAutomobile *OpenDoorExample::m_pCurrentCar;
  102. float OpenDoorExample::m_openingState;
  103. int OpenDoorExample::m_currentComponentId;
  104. eDoors OpenDoorExample::m_currentDoorId;
  105. unsigned int OpenDoorExample::m_nLastTimeWhenAnyActionWasEnabled = 0;
Не стал делать проверку нажатия/отжатия клавиши, чтобы не усложнять  :D
Заметь, что и в твоём скрипте, и в этом примере есть небольшая проблема - это выполнение действий над авто в цикле, без доп. проверки на существование этого авто. Чисто теоретически - пока обрабатывается открытие/закрытие, с авто может что-то случиться :) Но это уже мелочи.
Можно было бы вообще разделить процессы открытия/закрытия и обработки клавиш (чтобы эти процессы выполнялись одновременно). Но для базовой реализации подойдёт и так.

По for и другим циклам лучше найти какой-то урок/туториал по C++ (например этот (https://code-live.ru/post/cpp-loops))
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 02, 2016, 09:11:07 am
С plugin_vc и VC_DXFont разобрался, спасибо.
Как я понял, и для SA, и для VC плагины идут только под версию exe 1.0. Ну для SA то ладно, а для VC хорошо бы и версию 1.1 учитывать.
А есть ли в планах и для GTA3 плагин сделать?

Цитировать
Но я бы советовал использовать специальные утилиты для обновления/изменения:
Git for Windows
TortoiseGit
Скачал, установил.

Цитировать
Создать аккаунт на github.
Сообщить мне свой никнейм (мне нужно будет добавить его в список редакторов).
kenkingGitHub (никнейм kenking уже кто-то занял, наглость какая  ;D)

Цитировать
Тебе нужно мыслить по-другому - ты "вклиниваешься" в процесс каждый фрейм.
Буду пробовать, после клео как-то непривычно. Ещё раз большое спасибо за подробные пояснения!

Цитировать
Заметь, что и в твоём скрипте, и в этом примере есть небольшая проблема - это выполнение действий над авто в цикле, без доп. проверки на существование этого авто. Чисто теоретически - пока обрабатывается открытие/закрытие, с авто может что-то случиться
Есть такое дело. Я проверку там в общем-то сознательно не поставил, потому как при написании такого скрипта для VC во время процесса открывания/закрывания компонента специально взрывал авто и вылета не было, поскольку авто сразу не удаляется. Ну так-то проверку лучше ставить, согласен.
Название: Re: Написание плагина. Настройка проекта
Отправлено: Shagg_E от Август 02, 2016, 10:11:36 am
для VC хорошо бы и версию 1.1 учитывать
Не думаю, что ради этого стоит напрягаться - сейчас мало кто учитывает 1.1: даже MVL работает лишь с 1.0
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 02, 2016, 10:18:05 am
Не думаю, что ради этого стоит напрягаться - сейчас мало кто учитывает 1.1 - даже MVL работает лишь с 1.0
Я в клео скриптах учитываю. В библиотеке клео тоже учитывали это дело. Не думаю, что это большой напряг для того, кто разбирается в этом. Ну автору, конечно, решать.

UPD:
По коду открытия компонентов есть непонятка. Вот здесь:
if (automobile->m_damageManager.GetDoorStatus(doorId) != 3)Для VC и GTA3 значение 3 - это компонент отсутствует, т.е. он повредился и отвалился. Для SA такое состояние компонента будет 4. Однако при замене 3 на 4 плагин перестаёт работать, т.е. компоненты не открываются и не закрываются. Это как понимать?

Цитировать
Если ты знаешь значения этого перечисления - можешь их добавить в файле CDamageManager.h, и далее использовать их при вызове (напр. DOOR_FRONT_LEFT).
Добавил:
enum eDoors
{
BONNET = 0,
BOOT = 1,
DOOR_FRONT_LEFT = 2,
DOOR_FRONT_RIGHT = 3,
DOOR_REAR_LEFT = 4,
DOOR_REAR_RIGHT = 5
};

А также:
enum ePanels
{
WING_FRONT_LEFT = 0,
WING_FRONT_RIGHT = 1,
WINDSCREEN = 4,
BUMP_FRONT = 5,
BUMP_REAR = 6
};
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 02, 2016, 08:59:56 pm
А есть ли в планах и для GTA3 плагин сделать?
Нет. Даже на plugin_vc у меня времени почти нет.
kenkingGitHub (никнейм kenking уже кто-то занял, наглость какая  ;D)
Кинул тебе инвайт.
Не думаю, что это большой напряг для того, кто разбирается в этом.
Я не скажу, что это очень сложно. Это просто требует времени. Много времени. Сейчас в plugin-sdk (SA) более 2700 адресов.
Поиск всех этих адресов в exe какой-либо другой версии может занять месяцы (с моим графиком). Я для себя вообще отбросил этот вариант.
Для VC и GTA3 значение 3 - это компонент отсутствует, т.е. он повредился и отвалился. Для SA такое состояние компонента будет 4. Однако при замене 3 на 4 плагин перестаёт работать, т.е. компоненты не открываются и не закрываются. Это как понимать?
Действительно, не работает.
UPD: разобрался, это ошибка в sdk. Дело в том, что есть 2 функции GetDoorStatus. Одна в качестве аргумента принимает перечисление eDoors, вторая - id компонента. В sdk эти функции были перепутаны. Уже исправил.
Добавил:
Как будет время - можешь попробовать сделать коммит.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 03, 2016, 07:45:50 am
Цитировать
Нет. Даже на plugin_vc у меня времени почти нет.
Жаль, было бы хорошо собрать плагин для GTA3, VC и SA в один "инструмент". А если ещё сделать для IV и V - это была бы самая крутая вещь для скриптинга GTA.

Цитировать
Я не скажу, что это очень сложно. Это просто требует времени. Много времени. Сейчас в plugin-sdk (SA) более 2700 адресов.
Поиск всех этих адресов в exe какой-либо другой версии может занять месяцы (с моим графиком). Я для себя вообще отбросил этот вариант.
Да для SA это в общем-то без надобности. Я скрипты писал тоже только под exe 1.0. Да и остальные скриптёры тоже.

Цитировать
Действительно, не работает.
UPD: разобрался, это ошибка в sdk. Дело в том, что есть 2 функции GetDoorStatus. Одна в качестве аргумента принимает перечисление eDoors, вторая - id компонента. В sdk эти функции были перепутаны. Уже исправил.
Вот теперь всё отлично!

Как я понял, после каждого обновления надо пересобирать плагин?

В plugin-sdk файлы отмечены разными значками:
(http://savepic.net/8320524m.png) (http://savepic.net/8320524.png)
Что означают эти предупреждения и "кирпичи". Какие нужны действия?

Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 03, 2016, 05:53:33 pm
Как я понял, после каждого обновления надо пересобирать плагин?
Да.
Что означают эти предупреждения и "кирпичи". Какие нужны действия?
Красным отмечены файлы, которые ты изменил (и которые, по идее, нужно "закоммитить"). Зелёный - совпадает с текущим (на репозитарии). Серый - файлы игнорируются (не включаются в коммит и их нету в репозитории).
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 04, 2016, 11:56:28 am
В в CDamageManager.h есть ещё "пустые" перечисления
tComponent
tComponentGroup
eWheels

Ну с eWheels вроде, как понятно - это 6 колёс, а остальные что означают? 

Цитировать
Красным отмечены файлы, которые ты изменил (и которые, по идее, нужно "закоммитить")
При сборке примеров у меня выходило сообщение компилятора:
Цитировать
Main.obj : найден .netmodule MSIL или модуль, откомпилированный с параметром /GL; перезапускается компоновка с параметром /LTCG; добавьте /LTCG в командную строку компоновки для повышения производительности компоновщика
В общем я добавил во все примеры. Также по незнанию, я начал было экспериментировать в этом решении. ??? "Откатил" всё назад. Теперь буду учиться в своём решении, а не в plugin-sdk.

Цитировать
Как будет время - можешь попробовать сделать коммит.
Вот и сделал... :(
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 04, 2016, 06:25:16 pm
В в CDamageManager.h есть ещё "пустые" перечисления
tComponent
tComponentGroup
eWheels

Ну с eWheels вроде, как понятно - это 6 колёс, а остальные что означают?
Тоже какие-то перечисления. Надо смотреть код функций
Код: C++
  1. bool GetComponentGroup(tComponent component, tComponentGroup* group, unsigned char* damageCompId);
  2. bool ApplyDamage(CAutomobile* car, tComponent component, float intensity, float arg3);
  3.  

Чтобы разобраться.

UPD: Нашел их у себя в базе, добавил.

Вот и сделал... :(
Ты о чём? Вроде всё правильно сделал.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 04, 2016, 07:08:30 pm
Цитировать
Ты о чём? Вроде всё правильно сделал.
Да там https://github.com/DK22Pac/plugin-sdk/tree/master/examples (https://github.com/DK22Pac/plugin-sdk/tree/master/examples) теперь везде

Цитировать
CDamageManager: added enum ePanels and enum eDoors

ну так-то ничего страшного, теперь протестил, что да как делать при коммите, в следующий раз всё будет как надо.

Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 05, 2016, 06:22:26 pm
Потихоньку разбираюсь. Есть вопросы:
1) Как от найденного компонента перейти к child, next, root, а также прочитать их названия (+ 172 // NodeName)?
2) Какой алгоритм добавления новой функции в плагин?
Допустим, я знаю такой адрес:
:007F0ED0     ; RwFrame *__cdecl RwFrameScale(RwFrame *frame, RwV3D *v, int combine)Как добавить в плагин? 
3) Планируешь ли добавить в примеры код IVF? Не устаю повторять - это лучший мод на эту тему, когда-либо сделанный для SA! Хорошо бы с комментариями добавить его в примеры (ну или выслать в ЛС, если можно). Кстати, (я про это писал уже на GCU форуме) в версии IVF 2.1 на прицепах поворотники не работают.
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 05, 2016, 06:38:30 pm
1) Как от найденного компонента перейти к child, next, root, а также прочитать их названия (+ 172 // NodeName)?
Если у тебя есть указатель на RwFrame, используй оператор -> для доступа к членам структуры:
Код: C++
  1. RwFrame *pNext = component->next;
Можешь посмотртеть структуру в файле rw\rwcore.h.

Для перебора всех child'ов есть специальная функция
Код: C++
  1. RwFrameForAllChildren(RwFrame* фрейм, RwFrameCallBack функция_коллбэк, void* данные_для_передачи_в_коллбэк);
2) Какой алгоритм добавления новой функции в плагин?
Допустим, я знаю такой адрес:
:007F0ED0     ; RwFrame *__cdecl RwFrameScale(RwFrame *frame, RwV3D *v, int combine)Как добавить в плагин? 
Эта функция уже есть в sdk.
Вообще, я добавил почти все функции RW, которые есть в exe.

Обычно, для добавления функции, нужно её обьявить (обычно в файле-хедере .h) и определить (обычно в .cpp файле).
Можешь посмотреть на примере той же RwFrameScale (в файлах RenderWare.h и RenderWare.cpp).

Большинство процедур в exe - это методы различных классов.
Для добавления таких процедур нужно также обьявить класс, членами которого являются методы.
Пример - посмотри, например, CMatrix.h и CMatrix.cpp.
3) Планируешь ли добавить в примеры код IVF? Не устаю повторять - это лучший мод на эту тему, когда-либо сделанный для SA! Хорошо бы с комментариями добавить его в примеры (ну или выслать в ЛС, если можно). Кстати, (я про это писал уже на GCU форуме) в версии IVF 2.1 на прицепах поворотники не работают.
Для начала нужно весь код IVF адаптировать под новый sdk. Тогда, возможно, выложу исходники на github.

UPD: Добавил файлы с функциями NodeName.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 05, 2016, 07:54:25 pm
Цитировать
Эта функция уже есть в sdk.
Это я для примера. А как её правильно вызывать? Для клео ты мне тогда объяснял, теперь, что-то не пойму здесь как надо.

Цитировать
Если у тебя есть указатель на RwFrame, используй оператор -> для доступа к членам структуры:
А название компонента, как прочитать? В клео по смещению + 172 читалось имя компонента. Я делал скрипт для дверей автобусов, там в модель добавлялись вспомогательные компоненты и их названия были величины углов, на которые нужно вращать двери (у разных моделей разные значения), потом я считывал название этого компонента (child, next) и переводил из string в float, получал значение угла вращения.

Цитировать
Тогда, возможно, выложу исходники на github.
Было бы здорово!
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 05, 2016, 08:01:20 pm
Дело в том, что имя компонента (NodeName) не является частью RenderWare - поэтому его нету в структуре RwFrame. Это "надстройка" от создателей GTA.
Для доступа к имени есть специальне фунеции, я их добавлю в скором времени (файлы NodeName.h, NodeName.cpp).
Ну и пример - вывод иерархии транспорта на экран.
Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\common.h"
  3. #include "game_sa\NodeName.h"
  4. #include "game_sa\CFont.h"
  5.  
  6. using namespace plugin;
  7.  
  8. class VehicleHierarchy {
  9. public:
  10.     static RwFrame *DrawFrameHierarchyNames(RwFrame *frame, void *data) {
  11.         CVector2D *offset = reinterpret_cast<CVector2D *>(data);
  12.         // сначала выводим имя самого фрейма
  13.         char *name = GetFrameNodeName(frame);
  14.         CFont::PrintString(offset->x, offset->y, name[0] ? name : "<no_name>");
  15.         offset->y += 20.0f; //опускаемся ниже
  16.         // Теперь выводим имена всех дочерних компонентов
  17.         offset->x += 20.0f; // детей выводим правее
  18.         RwFrameForAllChildren(frame, DrawFrameHierarchyNames, offset); // для всех дочерних фреймов вызываем DrawFrameHierarchyNames. Таким образом, мы переберём всех детей, детей всех детей, и т.д. Это называется рекурсией.
  19.         offset->x -= 20.0f; // вывели всех детей
  20.         return frame;
  21.     }
  22.  
  23.     VehicleHierarchy() {
  24.         Events::drawingEvent += [] {
  25.             CVehicle *vehicle = FindPlayerVehicle(-1, false);
  26.             if (vehicle && vehicle->m_pRwObject) {
  27.                 // Настраивем вывод текста
  28.                 CFont::SetScale(0.5f, 1.0f);
  29.                 CFont::SetColor(CRGBA(255, 255, 255, 255));
  30.                 CFont::SetAlignment(ALIGN_LEFT);
  31.                 CFont::SetOutlinePosition(1);
  32.                 CFont::SetDropColor(CRGBA(0, 0, 0, 255));
  33.                 CFont::SetBackground(false, false);
  34.                 CFont::SetFontStyle(FONT_SUBTITLES);
  35.                 CFont::SetProp(true);
  36.                 CFont::SetWrapx(600.0f);
  37.                 // Теперь выводим, начиная с корневого фрейма (vehicle->m_pRwObject->parent) (член "parent" имеет тип void*, поэтому приводим к RwFrame*.
  38.                 CVector2D offset = {10.0f, 10.0f}; // Начальные координаты
  39.                 DrawFrameHierarchyNames(reinterpret_cast<RwFrame *>(vehicle->m_pRwObject->parent), &offset);
  40.             }
  41.         };
  42.     }
  43. } vehicleHierarchy;
Вроде как всё очень просто, не так ли?

Ещё раз:
RwFrameForAllChildren(frame - родительский фрейм (доступ к детям которого надо получить
                                  DrawFrameHierarchyNames - функция (функция-коллбэк), которую надо выполнить для каждого ребёнка,
                                  offset - наши данные, которые будут переданы в функцию-коллбэк );
Можно расшифровать так:
Выполнить функцию DrawFrameHierarchyNames для всех детей frame, с данными offset. Данные offset (2д-вектор) в примере мы использовали для сохранения координат вывода - ведь нам нужно опускать текст после каждого компонента и двигать его вправо для детей. Мы передавали эти данные в функцию обработки каждого отдельного компонента.

UPD: Добавил файлы.

Цитировать
А как её правильно вызывать? Для клео ты мне тогда объяснял, теперь, что-то не пойму здесь как надо.

Когда напечатаешь имя функции в Visual Studio и откроешь скобку, увидишь список параметров:
(http://i.imgur.com/cGoKHIs.png)

Пример вызова
Код: C++
  1. RwV3d savedPosn = automobile->m_aCarNodes[CAR_DOOR_LF]->modelling.pos;
  2. RwV3d scale = { 2.0f, 2.0f, 2.0f };
  3. RwMatrixScale(&automobile->m_aCarNodes[CAR_DOOR_LF]->modelling, &scale, rwCOMBINEREPLACE);
  4. automobile->m_aCarNodes[CAR_DOOR_LF]->modelling.pos = savedPosn;

Ещё это почитать можно:
Рекурсия https://acmp.ru/article.asp?id_sec=1&id_text=1333 (https://acmp.ru/article.asp?id_sec=1&id_text=1333)
Тернарный оператор http://cppstudio.com/post/304/ (http://cppstudio.com/post/304/)

Ещё один пример, показывает как использовать VehicleExtendedData.
Код: C++
  1. #include <plugin.h>
  2.  
  3. using namespace plugin;
  4.  
  5. class VehicleExtendedExample {
  6. public:
  7.     class VehicleComponents { // Класс, который представляет наши данные (можно сказать, что эти данные "прикрепляются" к структуре транспорта)
  8.     public:
  9.         RwFrame *m_pDoorLF;
  10.         RwFrame *m_pDoorRF;
  11.         RwFrame *m_pDoorLR;
  12.         RwFrame *m_pDoorRR;
  13.  
  14.         VehicleComponents(CVehicle *) { // Конструктор этого класса будет вызван при вызове конструктора транспорта (CVehicle::CVehicle)
  15.             m_pDoorLF = m_pDoorRF = m_pDoorLR = m_pDoorRR = nullptr; // устанавливаем все указатели в 0
  16.         }
  17.     };
  18.  
  19.     VehicleExtendedExample() {
  20.         static VehicleExtendedData<VehicleComponents> vehComps; // Создаем экземпляр нашего расширения. vehComps - это переменная, через которую мы будем
  21.                                                                 // обращаться к нашим данным (используя метод Get(CVehicle *транспорт) )
  22.  
  23.         Events::vehicleSetModelEvent += [](CVehicle *vehicle, int modelIndex) { // Выполняем нашу функцию, когда игра устанавливает модель транспорту
  24.             if (vehicle->m_pRwClump) { // Если создан графический обьект модели (RpClump)
  25.                 vehComps.Get(vehicle).m_pDoorLF = CClumpModelInfo::GetFrameFromName(vehicle->m_pRwClump, "door_lf_dummy"); // Находим компоненты в иерархии
  26.                 vehComps.Get(vehicle).m_pDoorRF = CClumpModelInfo::GetFrameFromName(vehicle->m_pRwClump, "door_rf_dummy"); // и записываем их в наш класс
  27.                 vehComps.Get(vehicle).m_pDoorLR = CClumpModelInfo::GetFrameFromName(vehicle->m_pRwClump, "door_lr_dummy");
  28.                 vehComps.Get(vehicle).m_pDoorRR = CClumpModelInfo::GetFrameFromName(vehicle->m_pRwClump, "door_rr_dummy");
  29.             }
  30.             else // иначе устанавливаём всё в 0
  31.                 vehComps.Get(vehicle).m_pDoorLF = vehComps.Get(vehicle).m_pDoorRF = vehComps.Get(vehicle).m_pDoorLR = vehComps.Get(vehicle).m_pDoorRR = nullptr;
  32.         };
  33.  
  34.         Events::vehicleRenderEvent += [](CVehicle *vehicle) { // Выполняем нашу функцию, когда игра рендерит транспорт
  35.             if (vehComps.Get(vehicle).m_pDoorLF)
  36.                 vehComps.Get(vehicle).m_pDoorLF->modelling.pos.z = 1.0f; // Сдвигаем компонент вверх
  37.             if (vehComps.Get(vehicle).m_pDoorRF)
  38.                 vehComps.Get(vehicle).m_pDoorRF->modelling.pos.z = 1.0f;
  39.             if (vehComps.Get(vehicle).m_pDoorLR)
  40.                 vehComps.Get(vehicle).m_pDoorLR->modelling.pos.z = 1.0f;
  41.             if (vehComps.Get(vehicle).m_pDoorRR)
  42.                 vehComps.Get(vehicle).m_pDoorRR->modelling.pos.z = 1.0f;
  43.         };
  44.     }
  45. } vehicleExtendedExample;
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 06, 2016, 03:52:41 pm
Дмитрий, ты волшебник.  ;)

Спасибо за очередные примеры и пояснения. Чем больше будет примеров, тем быстрей я освоюсь в написании плагинов. За ссылки на уроки тоже спасибо. Я сейчас читаю книгу
Прохоренок Н.А. Программирование на С++ в Visual Studio 2010 Express (2010) и дополнительный материал будет кстати.

По примерам:
с первым примером разобрался, всё нормально. Для теста написал небольшой код, глянь верно ли? Можно, что-то оптимизировать? Хотел ещё вывести скорость авто, но что-то не вышло, вот это CVector GetSpeed(CVector direction) оно или нет? С вектором не понял, как работать. Также не понятно, как вывести название транспорта. Пожалуйста пример.
#include <plugin.h>
#include "game_sa\common.h"
#include "game_sa\CFont.h"

using namespace plugin;

class VehicleGetPosition {
public:

VehicleGetPosition() {
Events::drawingEvent += [] {
CVehicle *vehicle = FindPlayerVehicle(-1, false);
if (vehicle) {
// Настраивем вывод текста
CFont::SetScale(0.5f, 1.0f);
CFont::SetColor(CRGBA(255, 255, 255, 255));
CFont::SetAlignment(ALIGN_LEFT);
CFont::SetOutlinePosition(1);
CFont::SetDropColor(CRGBA(0, 0, 0, 255));
CFont::SetBackground(false, false);
CFont::SetFontStyle(FONT_SUBTITLES);
CFont::SetProp(true);
CFont::SetWrapx(600.0f);
CVector vspeed = vehicle->m_vLinearVelocity;
float speed = sqrt(vspeed.x*vspeed.x + vspeed.y*vspeed.y + vspeed.z*vspeed.z);
speed *= 180;
float vhealth = vehicle->m_fHealth;
float vmass = vehicle->m_fMass;
CVector posn = vehicle->GetPosition();
char t_speed[16];
char t_health[16];
char t_vmass[16];
char t_posnx[16];
char t_posny[16];
char t_posnz[16];
sprintf(t_speed, "speed %.0f km/h", speed);
sprintf(t_health, "health %.0f \%\%", vhealth/10.0f);
sprintf(t_vmass, "massa %.1f kg", vmass);
sprintf(t_posnx, "posn x %.2f", posn.x);
sprintf(t_posny, "posn y %.2f", posn.y);
sprintf(t_posnz, "posn z %.2f", posn.z);
CFont::PrintString(5.0f, 5.0f, t_speed);
CFont::PrintString(5.0f, 25.0f, t_health);
CFont::PrintString(5.0f, 45.0f, t_vmass);
CFont::PrintString(5.0f, 65.0f, t_posnx);
CFont::PrintString(5.0f, 85.0f, t_posny);
CFont::PrintString(5.0f, 105.0f, t_posnz);
}
};
}
} vehicleGetPosition;


во втором примере пишет
Цитировать
namespase "plugin::Events" не содержит члена "vehicleSetModelEvent"

Цитировать
Пример вызова
Спасибо, теперь ясно. Но есть одно но - при RwMatrixScale если вращать компонент, например дверь, то она принимает стандартный размер. В клео я поэтому вызывал RwFrameScale - тогда компонент при вращении остаётся запланированного масштаба. По примеру попробовал вызвать RwFrameScale так:
RwFrameScale(automobile->m_aCarNodes[CAR_DOOR_LR], &scale, rwCOMBINEPRECONCAT);Не знаю правильно или нет, но компонент масштабируется, но при вращении опять становится стандартного размера. А при rwCOMBINEREPLACE так и вовсе оказывается в центре модели (как и при вращении с этим параметром после RwMatrixScale). В клео нормально работало, что я делаю не так?

UPD:
    RwV3d savedPosn = automobile->m_aCarNodes[CAR_DOOR_LF]->modelling.pos;
    RwV3d scale = { 2.0f, 2.0f, 2.0f };
    RwMatrixScale(&automobile->m_aCarNodes[CAR_DOOR_LF]->modelling, &scale, rwCOMBINEREPLACE);
    automobile->m_aCarNodes[CAR_DOOR_LF]->modelling.pos = savedPosn;
Можно, как ты написал до этого:
RwV3d scale = { 2.0f, 2.0f, 2.0f };
RwMatrixScale(&automobile->m_aCarNodes[CAR_DOOR_LR]->modelling, &scale, rwCOMBINEPRECONCAT);
только combine поменять на rwCOMBINEPRECONCAT

Но всё равно в обоих вариантах после вращения масштабирование сбрасывается в стандарт.


UPD2:
Обновил sdk, пересобрал плагин, теперь
Цитировать
пример, показывает как использовать VehicleExtendedData.
работает.

А практически для чего например можно это использовать в плагине?

UPD3:Со скоростью "разобрался" так:
CVector vspeed = vehicle->m_vLinearVelocity;
float speed = sqrt(vspeed.x*vspeed.x + vspeed.y*vspeed.y + vspeed.z*vspeed.z);
speed *= 180;
Верно?

Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 07, 2016, 12:31:25 pm
Со скоростью "разобрался" так:
Верно?

Да.
В опкоде 02E3 скорость считается вот так (длина вектора MoveForce умножается на 50):
(http://i.imgur.com/Qo8YfWx.png)

PS Я поменял названия некоторых членов в CPhysical (коммит 99 (https://github.com/DK22Pac/plugin-sdk/commit/8d18c41a2af20588e50c9eb6d32c0878dbd3222b#diff-16e208f83bec7d0b8058b273b70b9f7d)).

Также не понятно, как вывести название транспорта.

Находишь gxt-ключ названия в структуре модели транспорта (CVehicleModelInfo) и переводишь его в текст (CText::Get(char* ключ))

Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\common.h"
  3. #include "game_sa\CFont.h"
  4. #include "game_sa\CModelInfo.h"
  5. #include "game_sa\CText.h"
  6.  
  7. using namespace plugin;
  8.  
  9. class VehicleGetPosition {
  10. public:
  11.     VehicleGetPosition() {
  12.         Events::drawingEvent += [] {
  13.             CVehicle *vehicle = FindPlayerVehicle(-1, false);
  14.             if (vehicle) {
  15.                 // Настраивем вывод текста
  16.                 CFont::SetScale(0.5f, 1.0f);
  17.                 CFont::SetColor(CRGBA(255, 255, 255, 255));
  18.                 CFont::SetAlignment(ALIGN_LEFT);
  19.                 CFont::SetOutlinePosition(1);
  20.                 CFont::SetDropColor(CRGBA(0, 0, 0, 255));
  21.                 CFont::SetBackground(false, false);
  22.                 CFont::SetFontStyle(FONT_SUBTITLES);
  23.                 CFont::SetProp(true);
  24.                 CFont::SetWrapx(600.0f);
  25.                 static char str[32];
  26.                 CVehicleModelInfo *vehModel = reinterpret_cast<CVehicleModelInfo *>(CModelInfo::ms_modelInfoPtrs[vehicle->m_wModelIndex]);
  27.                 sprintf(str, "vehicle %s", TheText.Get(vehModel->m_szGameName));
  28.                 CFont::PrintString(5.0f, 25.0f, str);
  29.                 sprintf(str, "health %.0f%%", vehicle->m_fHealth / 10.0f);
  30.                 CFont::PrintString(5.0f, 45.0f, str);
  31.                 sprintf(str, "mass %.1f kg", vehicle->m_fMass);
  32.                 CFont::PrintString(5.0f, 65.0f, str);
  33.                 sprintf(str, "posn x %.2f", vehicle->GetPosition().x);
  34.                 CFont::PrintString(5.0f, 85.0f, str);
  35.                 sprintf(str, "posn y %.2f", vehicle->GetPosition().y);
  36.                 CFont::PrintString(5.0f, 105.0f, str);
  37.                 sprintf(str, "posn z %.2f", vehicle->GetPosition().z);
  38.                 CFont::PrintString(5.0f, 125.0f, str);
  39.                 sprintf(str, "speed %.2f", vehicle->m_vecMoveForce.Magnitude() * 50.0f);
  40.                 CFont::PrintString(5.0f, 145.0f, str);
  41.             }
  42.         };
  43.     }
  44. } vehicleGetPosition;

В клео я поэтому вызывал RwFrameScale - тогда компонент при вращении остаётся запланированного масштаба.

Можешь показать свой код в клео?

PS Для вывода кода C++ используй
[code=cpp][/code
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 07, 2016, 02:25:54 pm
Цитировать
Можешь показать свой код в клео?

Я немного ввел тебя в заблуждение, прошу прощения. Дело в том, что при вызове RwFrameScale я масштабировал все компоненты в том числе и chassis_dummy в который вложены подвижные компоненты (двери, капот и багажник). Так вот в этом случае масштаб сохраняется при вращении этих компонентов, а если вызывать RwFrameScale отдельно для вращающегося компонента, то при вращении масшаб сбрасывается в стандартный. Вывод - для таких компонентов надо масштабировать parent. Вот код клео:
{$CLEO .cs}
20@ = 0.5 // X
21@ = 0.5 // Y
22@ = 0.5 // Z

while true
wait 0
    if
      Player.Defined($PLAYER_CHAR)
    then
        if and
          Actor.Driving($PLAYER_ACTOR)
          0AB0: key_pressed 53
        then
            03C0: 0@ = actor $PLAYER_ACTOR car
            0A97: 0@ = car 0@ struct
            0A8E: 0@ = 0@ + 0x18
            0A8D: 0@ = read_memory 0@ size 4 virtual_protect 0
            0AA7: call_function 0x4C5400 num_params 2 pop 2 _nodename "chassis_dummy" _rwObject 0@ _store_to 1@
            if
              1@ > 0
            then
                0AA5: call {RwFrameScale} 0x7F0ED0 num_params 3 pop 3 _combine 1 _v 20@v _frame 1@
            end
            repeat
              wait 0
            until 8AB0: not key_pressed 53
        end
    end
end       
(http://savepic.net/8372643m.jpg) (http://savepic.net/8372643.jpg) (http://savepic.net/8373667m.jpg) (http://savepic.net/8373667.jpg)


Как правильно вызвать RwFrameScale в плагине?
Код: C++
  1. RwFrameScale(automobile->m_aCarNodes[CAR_CHASSIS], &scale, rwCOMBINEPRECONCAT);
Тот вариант, что я написал похоже неверный, поскольку вложенные компоненты не масштабируются вместе с CAR_CHASSIS, как в случае с клео. Происходит ровно тоже, что при вызове RwMatrixScale.

(http://savepic.net/8349090m.jpg) (http://savepic.net/8349090.jpg)

Цитировать
Находишь gxt-ключ названия в структуре модели транспорта (CVehicleModelInfo) и переводишь его в текст (CText::Get(char* ключ))
Спасибо.

UPD:В базе есть такой адрес:
.text:0043A570     _cheatSpawnTankerTruck proc nearВ sdk ты его не добавил? Я во всяком случае в CCheat его не нашёл. Ведь по-идеи он там должен быть? Или в других файлах? Попробовал туда добавить так:
Код: C++
  1. #include "CCheat.h"
  2.  
  3. char *CCheat::m_CheatString = (char *)0x969110;
  4.  
  5. CVehicle *CCheat::VehicleCheat(int vehicleId) {
  6.     return ((CVehicle *(__cdecl *)(int))0x43A0B0)(vehicleId);
  7. }
  8.  
  9. CVehicle *CCheat::TankerTruck() {
  10.         return ((CVehicle *(__cdecl *)())0x43A570)();
  11. }
Код: C++
  1. #pragma once
  2. #include "plbase/PluginBase_SA.h"
  3.  
  4. class PLUGIN_API CCheat {
  5. public:
  6.     // static char m_CheatString[30]
  7.     static char *m_CheatString;
  8.  
  9.     static class CVehicle *VehicleCheat(int vehicleId);
  10.         static class CVehicle *TankerTruck();
  11. };

Верно ли?
Ну по крайне мере вызывается правильно, бензовоз с прицепом спунятся.

Давно задаюсь вопросом - можно ли переписать эту функцию так, чтобы считывать пары ID тягача и ID прицепа из текстового файла (ну в крайнем случае в сам плагин набрать это сочетание, ну лучше считывать из текстового файла) и потом аналогичным образом спаунить другие тягачи с прицепами, ведь нормального спаунера авто с прицепами так и не сделали.

UPD2:
 :D Вот:
(http://savepic.net/8375770m.jpg) (http://savepic.net/8375770.jpg)
Код: C++
  1. if (automobile->m_aCarNodes[CAR_CHASSIS]) {
  2.    if (automobile->m_aCarNodes[CAR_DOOR_LF])
  3.       RwFrameAddChild(automobile->m_aCarNodes[CAR_CHASSIS], automobile->m_aCarNodes[CAR_DOOR_LF]);
  4.                
  5.    RwV3d scale = { 0.5f, 0.5f, 0.5f };
  6.    RwFrameScale(automobile->m_aCarNodes[CAR_CHASSIS], &scale, rwCOMBINEREPLACE);
  7. }
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 08, 2016, 07:35:51 pm
CCheat я особо не разбирал.
В sdk была только функция VehicleChear для спавна авто.
Давно задаюсь вопросом - можно ли переписать эту функцию так, чтобы считывать пары ID тягача и ID прицепа из текстового файла (ну в крайнем случае в сам плагин набрать это сочетание, ну лучше считывать из текстового файла) и потом аналогичным образом спаунить другие тягачи с прицепами, ведь нормального спаунера авто с прицепами так и не сделали.
Можно полноситью заменить оригинальную функцию своей.
Покажу пример, как доделаю полностью иерархию клаасов CVehicle, включая CTrailer.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 09, 2016, 08:55:17 am
Цитировать
CCheat я особо не разбирал.
В sdk была только функция VehicleChear для спавна авто.
А добавил-то я правильно?

Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 09, 2016, 09:19:20 pm
Цитировать
CCheat я особо не разбирал.
В sdk была только функция VehicleChear для спавна авто.
А добавил-то я правильно?
Да.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 10, 2016, 09:40:39 am
1)
Цитировать
Можно было бы вообще разделить процессы открытия/закрытия и обработки клавиш (чтобы эти процессы выполнялись одновременно).
Т.е. нажал клавишу, дверь открывается\закрывается и в это же время нажал другую клавишу и одновременно пошло открытие\закрытие другой двери? И т.д. Можно это показать для этого же примера? (SA_OpenDoorExample) https://github.com/DK22Pac/plugin-sdk/blob/master/examples/SA_OpenDoorExample/Main.cpp (https://github.com/DK22Pac/plugin-sdk/blob/master/examples/SA_OpenDoorExample/Main.cpp)

2) Какой загрузчик asi использовать для VC, чтобы скрипты загружались не из корневого каталога, а из \scripts ?

3) Будет ли в плагине VC реализован поиск компонента по имени?
Код: C++
  1. RwFrame *component = CClumpModelInfo::GetFrameFromName(automobile->m_pRwClump, "bonnet_dummy");
Название: Re: Написание плагина. Настройка проекта
Отправлено: Shagg_E от Август 10, 2016, 04:23:52 pm
Ох - наконец-то я что-то полезное оставлю в этой теме :D
Цитировать
2) Какой загрузчик asi использовать для VC, чтобы скрипты загружались не из корневого каталога, а из \scripts ?
Ultimate ASI Loader (https://github.com/ThirteenAG/Ultimate-ASI-Loader/releases)
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 10, 2016, 10:51:45 pm
kenking, покажу позже.

По VC ничего не могу сказать. Если есть свободное время - дорабатываю версию для SA.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 11, 2016, 06:47:53 pm
kenking, покажу позже.
Хорошо. Ещё интересует создание спецактёра. Там же отличается (в клео по-крайне мере) загрузка модели, создание актёра, выгрузка модели и т.д. Также интересна установка (и удаление) деталей тюнинга на транспорт. Такие примеры были бы тоже кстати. 

Ultimate ASI Loader (https://github.com/ThirteenAG/Ultimate-ASI-Loader/releases)
Спасибо.
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 11, 2016, 11:51:29 pm
Т.е. нажал клавишу, дверь открывается\закрывается и в это же время нажал другую клавишу и одновременно пошло открытие\закрытие другой двери? И т.д. Можно это показать для этого же примера?
Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\common.h"
  3. #include "game_sa\CAutomobile.h"
  4. #include "game_sa\CTimer.h"
  5.  
  6. using namespace plugin;
  7.  
  8. const float ACTION_TIME_STEP = 0.005f;
  9. const unsigned int TIME_FOR_KEYPRESS = 500;
  10.  
  11. class DoorsExample {
  12. public:
  13.     static int componentByDoorId[6]; // Таблица перевода eDoors в Id компонента
  14.  
  15.     static int m_nLastTimeWhenAnyActionWasEnabled; // Последнее время запуска события
  16.  
  17.     enum eDoorEventType { // Тип события
  18.         DOOR_EVENT_OPEN,
  19.         DOOR_EVENT_CLOSE
  20.     };
  21.  
  22.     class DoorEvent { // Класс события
  23.     public:
  24.         bool m_active;
  25.         eDoorEventType m_type;
  26.         float m_openingState;
  27.        
  28.         DoorEvent() {
  29.             m_active = false;
  30.             m_type = DOOR_EVENT_CLOSE;
  31.         }
  32.     };
  33.  
  34.     class VehicleDoors {
  35.     public:
  36.         DoorEvent events[6]; // События для всех 6 дверей
  37.  
  38.         VehicleDoors(CVehicle *) {}
  39.     };
  40.  
  41.     static VehicleExtendedData<VehicleDoors> VehDoors; // Наше расширение
  42.  
  43.     static void EnableDoorEvent(CAutomobile *automobile, eDoors doorId) { // Включить событие двери
  44.         if (automobile->IsComponentPresent(componentByDoorId[doorId])) {
  45.             if (automobile->m_damageManager.GetDoorStatus(doorId) != DAMSTATE_NOTPRESENT) {
  46.                 DoorEvent &event = VehDoors.Get(automobile).events[doorId];
  47.                 if (event.m_type == DOOR_EVENT_OPEN)
  48.                     event.m_type = DOOR_EVENT_CLOSE; // Если последнее событие - открытие, то закрываем
  49.                 else
  50.                     event.m_type = DOOR_EVENT_OPEN; // Если последнее событие закрытие - то открываем
  51.                 event.m_active = true; // Включаем обработку
  52.                 m_nLastTimeWhenAnyActionWasEnabled = CTimer::m_snTimeInMilliseconds;
  53.             }
  54.         }
  55.     }
  56.  
  57.     static void ProcessDoors(CVehicle *vehicle) { // Обработка событий для конкретного авто
  58.         if (vehicle->m_dwVehicleSubClass == VEHICLE_AUTOMOBILE) {
  59.             CAutomobile *automobile = reinterpret_cast<CAutomobile *>(vehicle);
  60.             for (unsigned int i = 0; i < 6; i++) { // Обрабатываем все события
  61.                 eDoors doorId = static_cast<eDoors>(i);
  62.                 DoorEvent &event = VehDoors.Get(automobile).events[doorId];
  63.                 if (event.m_active) { // Если событие активно
  64.                     if (event.m_type == DOOR_EVENT_OPEN) {
  65.                         event.m_openingState += ACTION_TIME_STEP;
  66.                         if (event.m_openingState > 1.0f) { // Если полностью открыли
  67.                             event.m_active = false; // Отключаем обработку
  68.                             automobile->OpenDoor(0, componentByDoorId[doorId], doorId, 1.0f, true); // Полностью открываем
  69.                             event.m_openingState = 1.0f;
  70.                         }
  71.                         else
  72.                             automobile->OpenDoor(0, componentByDoorId[doorId], doorId, event.m_openingState, true);
  73.                     }
  74.                     else {
  75.                         event.m_openingState -= ACTION_TIME_STEP;
  76.                         if (event.m_openingState < 0.0f) { // Если полностью открыли
  77.                             event.m_active = false; // Отключаем обработку
  78.                             automobile->OpenDoor(0, componentByDoorId[doorId], doorId, 0.0f, true); // Полностью открываем
  79.                             event.m_openingState = 0.0f;
  80.                         }
  81.                         else
  82.                             automobile->OpenDoor(0, componentByDoorId[doorId], doorId, event.m_openingState, true);
  83.                     }
  84.                 }
  85.             }
  86.         }
  87.     }
  88.  
  89.     static void MainProcess() { // Обработка нажатия клавиш и запуск событий
  90.         if (CTimer::m_snTimeInMilliseconds > (m_nLastTimeWhenAnyActionWasEnabled + TIME_FOR_KEYPRESS)) { // если прошло 500 мс с того времени, как мы начали открывать/закрывать что-то
  91.             CVehicle *vehicle = FindPlayerVehicle(0, false);
  92.             if (vehicle && vehicle->m_dwVehicleClass == VEHICLE_AUTOMOBILE) {
  93.                 CAutomobile *automobile = reinterpret_cast<CAutomobile *>(vehicle); // опять же, приведение типов. Т.к. мы будет юзать damageManager, нам нужно убедиться, что транспорт - это автомобиль (CAutomobile)
  94.                 if (KeyPressed(219)) // [
  95.                     EnableDoorEvent(automobile, BONNET); // капот
  96.                 else if (KeyPressed(221)) // ]
  97.                     EnableDoorEvent(automobile, BOOT); // багажник
  98.                 else if (KeyPressed(186) && KeyPressed(187)) // ; =
  99.                     EnableDoorEvent(automobile, DOOR_FRONT_LEFT); // левая передняя дверь
  100.                 else if (KeyPressed(222) && KeyPressed(187)) // ' =
  101.                     EnableDoorEvent(automobile, DOOR_FRONT_RIGHT); // правая передняя дверь
  102.                 else if (KeyPressed(186) && KeyPressed(189)) // ; -
  103.                     EnableDoorEvent(automobile, DOOR_REAR_LEFT); // левая задняя дверь
  104.                 else if (KeyPressed(222) && KeyPressed(189)) // ' -
  105.                     EnableDoorEvent(automobile, DOOR_REAR_RIGHT); // правая задняя дверь
  106.             }
  107.         }
  108.     }
  109.  
  110.     DoorsExample() {
  111.         Events::gameProcessEvent += MainProcess; // Тут обрабатываем нажатия и запускаем события
  112.         Events::vehicleRenderEvent += ProcessDoors; // Тут обрабатываем события, а также выключаем их
  113.     }
  114. } example;
  115.  
  116. int DoorsExample::componentByDoorId[6] = { CAR_BONNET, CAR_BOOT, CAR_DOOR_LF, CAR_DOOR_RF, CAR_DOOR_LR, CAR_DOOR_RR };
  117. int DoorsExample::m_nLastTimeWhenAnyActionWasEnabled = 0;
  118. VehicleExtendedData<DoorsExample::VehicleDoors> DoorsExample::VehDoors;
В этом примере процессы обработки нажатия клавиш и открытия/закрытия компонентов разделены.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 12, 2016, 03:58:18 pm
В этом примере процессы обработки нажатия клавиш и открытия/закрытия компонентов разделены.
Круто! Спасибо за пример.

Вопросы:
1) Названия классов могут совпадать в разных плагинах? Никакого конфликта не будет? Или всё, что делается в одном плагине остальных никак не касается?
2) В конце кода это зачем?
Код: C++
  1. int DoorsExample::componentByDoorId[6] = { CAR_BONNET, CAR_BOOT, CAR_DOOR_LF, CAR_DOOR_RF, CAR_DOOR_LR, CAR_DOOR_RR };
  2. int DoorsExample::m_nLastTimeWhenAnyActionWasEnabled = 0;
  3. VehicleExtendedData<DoorsExample::VehicleDoors> DoorsExample::VehDoors;
3) В примере спаунера транспорта https://github.com/DK22Pac/plugin-sdk/blob/master/examples/SA_VehicleSpawner/Main.cpp (https://github.com/DK22Pac/plugin-sdk/blob/master/examples/SA_VehicleSpawner/Main.cpp) при вызове модели типа train происходит вылет игры. Видимо эта функция не предназначена для работы с таким типом транспорта.

Прошло две недели с начала моего обучения, пора показать первый результат. Переписал скрипт Дениса Car Spawner 3 на С++. Посмотри пожалуйста. Можно ли, что-то улучшить, оптимизировать?
Код клео
{$CLEO}  // 0 - 48 , 9 -57
var
    0@: array 5 of Integer  // digits     0 1 2 3 4
    17@: Integer             // index
    6@: Integer             // current_digit
    7@: Integer             // SUM
    8@: Integer             // KEY_HOLD
    15@: Integer             // KEY
end
const
    DIGIT = 0@
    i = 17@
    CURRENT_DIGIT = 6@
    SUM = 7@
    KEY_HOLD = 8@
    KEY = 15@
end
CURRENT_DIGIT = 1
while true
    wait 0
    if or
        not player.Defined($PLAYER_CHAR)
        $ONMISSION <> 0
    then   
        CURRENT_DIGIT = 1
        SUM = 0
        KEY_HOLD = False
        for i = 0 to 4
            0006: DIGIT[i] = 0 
        end
        continue
    end
    if
        8AB0: not key_pressed 2
    then
        if and
            SUM > 0
            SUM < 19010
        then
            if
                07DE: model SUM exists // versionB
            then
                0AA7: call_function 0x4C5AA0 num_params 1 pop 1 SUM 9@ // isModelCar
                0AA7: call_function 0x4C5B60 num_params 1 pop 1 SUM 10@ // isModelBike
                0AA7: call_function 0x4C5C20 num_params 1 pop 1 SUM 11@ // isModelBmx
                0AA7: call_function 0x4C5BF0 num_params 1 pop 1 SUM 12@ // isModelQuad
                0AA7: call_function 0x4C5C50 num_params 1 pop 1 SUM 13@ // isModelTrailer
                0AA7: call_function 0x4C5BC0 num_params 1 pop 1 SUM 14@ // isModelMTruck
                if or
                    081E:   model SUM boat
                    081F:   model SUM plane
                    0820:   model SUM heli
                    9@ == True
                then
                    0ADD: spawn_car_with_model SUM at_player_location
                else
                    if or
                        10@ == 8766721 // True
                        11@ == 8766721 // True
                        12@ == 8766721 // True
                        13@ == 8766721 // True
                        14@ == True
                    then
                        0ADD: spawn_car_with_model SUM at_player_location
                    end
                end
            end
        end
        CURRENT_DIGIT = 1
        SUM = 0
        KEY_HOLD = False
        for i = 0 to 4
            0006: DIGIT[i] = 0 
        end
        continue
    end    // key pressed 2
    if
        KEY_HOLD == False
    then
        if
            0AB0: key_pressed 8
        then
            KEY_HOLD = True
            if
                CURRENT_DIGIT > 1
            then
                CURRENT_DIGIT /= 10
                008F: 16@ = integer CURRENT_DIGIT to_float
                0AEF: 16@ = log 16@ base 10.0 //all floats
                SUM = 0
                0092: 16@ = float 16@ to_integer
                16@ -= 1
                for i = 0 to 16@
                    0016: DIGIT[i] /= 10
                    005A: SUM += DIGIT[i] // (int)
                end
            end
        else
            if
                SUM >= 19010
            then
                continue
            end
            KEY = 48
            for i = 0 to 9
                if
                    0AB0: key_pressed KEY
                then
                    008F: 16@ = integer CURRENT_DIGIT to_float
                    0AEF: 16@ = log 16@ base 10.0 //all floats
                    SUM = 0
                    0092: 16@ = float 16@ to_integer
                    0085: DIGIT[16@] = i   // int
                    for i = 0 to 16@
                        if
                            001D:   16@ > i // (int)
                        then
                            0012: DIGIT[i] *= 10
                        end
                        005A: SUM += DIGIT[i] // (int)
                    end
                    CURRENT_DIGIT *= 10
                    KEY_HOLD = True
                    break
                end
                KEY += 1
            end
        end
    else  //  KEY_HOLD == True
        if and
            8AB0: not key_pressed 48
            8AB0: not key_pressed 49
            8AB0: not key_pressed 50
            8AB0: not key_pressed 51
            8AB0: not key_pressed 52
        then
            if and
                8AB0: not key_pressed 53
                8AB0: not key_pressed 54
                8AB0: not key_pressed 55
                8AB0: not key_pressed 56
                8AB0: not key_pressed 57
                8AB0: not key_pressed 8
            then
                KEY_HOLD = False
            end
        end
    end  // KEY_HOLD == False
    if
        CURRENT_DIGIT > 1
    then
        03F0: enable_text_draw 1
        045A: draw_text_1number 320.0 240.0 GXT 'NUMBER' number SUM
    end
end

Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\common.h"
  3. #include "game_sa\CFont.h"
  4. #include "game_sa\CText.h"
  5. #include "game_sa\CCheat.h"
  6. #include "game_sa\eModelID.h"
  7. #include "game_sa\CModelInfo.h"
  8.  
  9. // originally made by Den_spb
  10.  
  11. using namespace plugin;
  12.  
  13. class MoreVehiclesSpawner {
  14. public:
  15.     MoreVehiclesSpawner() {
  16.     static int digits[] = { 0, 0, 0, 0, 0 };
  17.     static int current_digit = 1;
  18.     static unsigned int sum = 0;
  19.     static bool key_hold = false;
  20.     static int key = 48;
  21.     static int i_16 = 0;
  22.     static float f_16 = 0.0f;
  23.  
  24.         Events::drawingEvent += [] {
  25.             CPed *playa = FindPlayerPed();
  26.             if (playa && playa->IsAlive()) {
  27.                 if (KeyPressed(2)) {
  28.                     // вывод на экран
  29.                     if (current_digit > 1) {
  30.                         CFont::SetScale(0.5f, 1.0f);
  31.                         CFont::SetColor(CRGBA(255, 255, 255, 255));
  32.                         CFont::SetAlignment(ALIGN_LEFT);
  33.                         CFont::SetOutlinePosition(1);
  34.                         CFont::SetDropColor(CRGBA(0, 0, 0, 255));
  35.                         CFont::SetBackground(false, false);
  36.                         CFont::SetFontStyle(FONT_SUBTITLES);
  37.                         CFont::SetProp(true);
  38.                         CFont::SetWrapx(600.0f);
  39.                         char text[16];
  40.                         sprintf(text, "%d", sum);
  41.                         CFont::PrintString(300.0f, 10.0f, text);
  42.                     }
  43.                     if (key_hold == false) {
  44.                         // корректировка цифр
  45.                         if (KeyPressed(8)) {
  46.                             key_hold = true;
  47.                             if (current_digit > 1) {
  48.                                 current_digit /= 10;
  49.                                 f_16 = log10(static_cast<float>(current_digit));
  50.                                 sum = 0;
  51.                                 i_16 = static_cast<int>(f_16);
  52.                                 i_16 -= 1;
  53.                                 for (int i = 0; i <= i_16; i++) {
  54.                                     digits[i] /= 10;
  55.                                     sum += digits[i];
  56.                                 }
  57.                             }
  58.                         }
  59.                         // набор цифр
  60.                         else {
  61.                             if (sum >= 19010) {
  62.                                 sum = 0;
  63.                                 current_digit = 1;
  64.                                 key_hold = false;
  65.                                 for (int i = 0; i <= 4; i++) {
  66.                                     digits[i] = 0;
  67.                                 }
  68.                             }
  69.                             key = 48;
  70.                             for (int i = 0; i <= 9; i++) {
  71.                                 if (KeyPressed(key)) {
  72.                                     f_16 = log10(static_cast<float>(current_digit));
  73.                                     sum = 0;
  74.                                     i_16 = static_cast<int>(f_16);
  75.                                     digits[i_16] = i;
  76.                                     for (int i = 0; i <= i_16; i++) {
  77.                                         if (i_16 > i)
  78.                                             digits[i] *= 10;
  79.                                         sum += digits[i];
  80.                                     }
  81.                                     current_digit *= 10;
  82.                                     key_hold = true;
  83.                                     break;
  84.                                 }
  85.                                 key++;
  86.                             }
  87.                         }
  88.                     }
  89.                     else {
  90.                         if (!KeyPressed(8) && !KeyPressed(48) && !KeyPressed(49)
  91.                             && !KeyPressed(50) && !KeyPressed(51) && !KeyPressed(52)
  92.                             && !KeyPressed(53) && !KeyPressed(54) && !KeyPressed(55)
  93.                             && !KeyPressed(56) && !KeyPressed(57))
  94.                             key_hold = false;
  95.                     }
  96.                 }
  97.                 // спавн транспорта
  98.                 else {
  99.                     if (sum > 0 && sum < 19010) {
  100.                         CVehicleModelInfo *typModel = reinterpret_cast<CVehicleModelInfo *>(CModelInfo::IsVehicleModelType(sum));
  101.                         if (reinterpret_cast<int>(typModel) != -1
  102.                             && reinterpret_cast<int>(typModel) != 6)
  103.                             CCheat::VehicleCheat(sum);
  104.                     }
  105.                                                
  106.                     sum = 0;
  107.                     current_digit = 1;
  108.                     key_hold = false;
  109.                     for (int i = 0; i <= 4; i++) {
  110.                         digits[i] = 0;
  111.                     }
  112.                 }
  113.             }
  114.         };
  115.     }
  116. } moreVehiclesSpawner;
  117.  
   
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 12, 2016, 08:03:45 pm
Цитировать
Круто! Спасибо за пример.
Пример не самый простой получился... Для полного понимания нужно ознакомиться с понятиями
-класса
-конструктора класса
-ссылками
в С++.
В plugin-sdk реализована возможность "прицепить" свой класс к классу транспорта (CVehicle).
Для наглядности я сделал ещё один пример.
Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\CSprite.h"
  3. #include "game_sa\CFont.h"
  4.  
  5. using namespace plugin;
  6.  
  7. class DistanceExample {
  8. public:
  9.     class DistanceInfo {
  10.     public:
  11.         // данные нашего класса
  12.         float distance; // пройденная дистанция
  13.         bool startedPositionRecording; // если уже начали отслеживать позицию
  14.         CVector lastPosition; // последняя позиция
  15.  
  16.         DistanceInfo(CVehicle *vehicle) {
  17.             // Контсруктор нашего класса. Должен быть обязательно обьявлен. Принимает один параметр -
  18.             // транспорт, к которому "прицепляется" этот класс.
  19.             // Этот конструктор будет вызван на этапе конструирования CVehicle (CVehicle::CVehicle).
  20.             // При этом, обращаться к каким-либо членам CVehicle запрещено (обьект CVehicle ещё не построен до конца).
  21.             distance = 0.0f; // устанавливаем начальное значение
  22.             startedPositionRecording = false;
  23.         }
  24.     };
  25.  
  26.     DistanceExample() {
  27.         static VehicleExtendedData<DistanceInfo> VehDistance; // Наше расширение
  28.  
  29.         Events::vehicleRenderEvent += [](CVehicle *vehicle) {
  30.             DistanceInfo &info = VehDistance.Get(vehicle); // Получаем наши данные для этого транспорта
  31.             if (info.startedPositionRecording) // если info.lastPosition не "пустой"
  32.                 info.distance += DistanceBetweenPoints(info.lastPosition, vehicle->GetPosition()); // добавляем расстояние
  33.             else
  34.                 info.startedPositionRecording = true;
  35.             info.lastPosition = vehicle->GetPosition();
  36.         };
  37.  
  38.         Events::drawingEvent += [] {
  39.             for (int i = 0; i < CPools::ms_pVehiclePool->m_Size; i++) {
  40.                 CVehicle *vehicle = CPools::ms_pVehiclePool->GetAt(i);
  41.                 if (vehicle && vehicle->GetIsOnScreen()) {
  42.                     CVector &posn = vehicle->GetPosition();
  43.                     RwV3d rwp = { posn.x, posn.y, posn.z + 1.0f };
  44.                     RwV3d screenCoors; float w, h;
  45.                     if (CSprite::CalcScreenCoors(rwp, &screenCoors, &w, &h, true, true) && w > 10.0f) {
  46.                         CFont::SetAlignment(ALIGN_CENTER);
  47.                         CFont::SetColor(CRGBA(255, 255, 255, 255));
  48.                         CFont::SetOutlinePosition(1);
  49.                         CFont::SetDropColor(CRGBA(0, 0, 0, 255));
  50.                         CFont::SetBackground(false, false);
  51.                         CFont::SetCentreSize(300.0f);
  52.                         CFont::SetScale(w * 0.015f, h * 0.03f);
  53.                         CFont::SetFontStyle(FONT_PRICEDOWN);
  54.                         CFont::SetProp(true);
  55.                         static char text[16];
  56.                         sprintf(text, "%.2f", VehDistance.Get(vehicle).distance);
  57.                         CFont::PrintString(screenCoors.x, screenCoors.y, text);
  58.                     }
  59.                 }
  60.             }
  61.         };
  62.     }
  63. } example;
Цитировать
1) Названия классов могут совпадать в разных плагинах? Никакого конфликта не будет? Или всё, что делается в одном плагине остальных никак не касается?
Да, можешь использовать одинаковые названия.
Цитировать
2) В конце кода это зачем?
Эти переменные были обьявлены как статические внутри класса.
Такие переменные надо дополнительно определить за пределами класса, в глобальной области видимости.
https://msdn.microsoft.com/ru-ru/library/b1b5y48f.aspx
Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\CMessages.h"
  3.  
  4. using namespace plugin;
  5.  
  6. class ShowValue {
  7. public:
  8.     static unsigned int value; // Переменная обьявлена внутри класса.
  9.                                // Внутри класса мы можем обращаться к этой переменной вот так: value
  10.  
  11.     ShowValue() {
  12.         Events::gameProcessEvent += [] {
  13.             CMessages::AddMessageJumpQWithNumber("~1~", 100, 0, value, -1, -1, -1, -1, -1, false);
  14.         };
  15.     }
  16. } example;
  17.  
  18. unsigned int ShowValue::value = 12345; // А определяем в глобальной области.
  19.                                        // За пределами класса мы можем обращаться к этой переменной
  20.                                        // вот так: ShowValue::value
  21.  
  22. class AnotherClass {
  23. public:
  24.     AnotherClass() {
  25.         Events::gameProcessEvent += [] {
  26.             CMessages::AddBigMessageWithNumberQ("~1~", 100, 0, ShowValue::value, -1, -1, -1, -1, -1);
  27.         };
  28.     }
  29. } another;
Вообще, это всё - на твоё личное усмотрение :) Мне просто удобнее так - когда всё "упаковано" в класс, а конкретные переменные ассоциируются с конкретным классом.
Никто не запрещает обьявлять глобальные переменные вне класса.
Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\CMessages.h"
  3.  
  4. using namespace plugin;
  5.  
  6. // Переменная обьявлена и определена в глобальной области.
  7. // Мы можем использовать эту переменную где угодно.
  8. unsigned int value = 12345;
  9.  
  10. class ShowValue {
  11. public:
  12.     ShowValue() {
  13.         Events::gameProcessEvent += [] {
  14.             CMessages::AddMessageJumpQWithNumber("~1~", 100, 0, value, -1, -1, -1, -1, -1, false);
  15.         };
  16.     }
  17. } example;
Можно обьявлять статические переменные и в теле функций. Такие переменные не нужно определять вне класса. Но обращаться к таким переменным можно только внутри функции (в которой они обьявлены)!
Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\CMessages.h"
  3.  
  4. using namespace plugin;
  5.  
  6. class ShowValue {
  7. public:
  8.     ShowValue() {
  9.         static unsigned int value = 12345; // Переменная обьявлена и определена внутри конструктора.
  10.                                            // Мы можем обращаться к этой переменной только в конструкторе.
  11.  
  12.         Events::gameProcessEvent += [] { // Здесь мы используем лямба-выражение - формируем функцию "на лету"
  13.             CMessages::AddMessageJumpQWithNumber("~1~", 100, 0, value, -1, -1, -1, -1, -1, false);
  14.         };
  15.     }
  16.  
  17.     static void MyDrawingFunction() {
  18.         // а вот тут мы уже не можем получить доступ к value
  19.     }
  20. } example;

По коду Den_spb могу точно сказать, что здесь у тебя что-то не так:
Код: C++
  1. CVehicleModelInfo *typModel = reinterpret_cast<CVehicleModelInfo *>(CModelInfo::IsVehicleModelType(sum));
  2. if (reinterpret_cast<int>(typModel) != -1
  3.     && reinterpret_cast<int>(typModel) != 6)
CModelInfo::IsVehicleModelType возвращает boolean (true/false), зачем переводить его в VehicleModelInfo, и сравнивать с -1 и 6 - непонятно.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 13, 2016, 08:02:44 am
Спасибо за очередной пример и пояснения!

Цитировать
CModelInfo::IsVehicleModelType возвращает boolean (true/false), зачем переводить его в VehicleModelInfo, и сравнивать с -1 и 6 - непонятно.
После перевода получается тип модели (0=car, 9=bike, 11=trailer и т.д.). А если модель не является транспортом или вообще нет такой модели, то получается значение -1, а 6 - это модель типа train. Вот я и проверяю, что набранное число - это ID модели транспорта и при этом не является моделью train. Потому как, если модель train, то происходит вылет игры. По другому я не придумал, как проверить. Денис проверял, что модель есть (в моём коде сравнение с -1) и она является любой из моделей, кроме train (в моём коде сравнение с 6). Так-то плагин в работе я проверил, всё работает. Просто по самому коду - может, где-то можно, что-то оптимизировать?
                if
                07DE: model SUM exists // versionB
            then
                0AA7: call_function 0x4C5AA0 num_params 1 pop 1 SUM 9@ // isModelCar
                0AA7: call_function 0x4C5B60 num_params 1 pop 1 SUM 10@ // isModelBike
                0AA7: call_function 0x4C5C20 num_params 1 pop 1 SUM 11@ // isModelBmx
                0AA7: call_function 0x4C5BF0 num_params 1 pop 1 SUM 12@ // isModelQuad
                0AA7: call_function 0x4C5C50 num_params 1 pop 1 SUM 13@ // isModelTrailer
                0AA7: call_function 0x4C5BC0 num_params 1 pop 1 SUM 14@ // isModelMTruck
                if or
                    081E:   model SUM boat
                    081F:   model SUM plane
                    0820:   model SUM heli
                    9@ == True
                then
                    0ADD: spawn_car_with_model SUM at_player_location
                else
                    if or
                        10@ == 8766721 // True
                        11@ == 8766721 // True
                        12@ == 8766721 // True
                        13@ == 8766721 // True
                        14@ == True
                    then
                        0ADD: spawn_car_with_model SUM at_player_location
                    end
                end
            end


UPD:
Что-то я действительно намудрил.  :)
CModelInfo::IsVehicleModelType(sum) как раз и вернёт тип модели (0=car, 9=bike, 11=trailer и т.д.). А если модель не является транспортом или вообще нет такой модели, то получается значение -1.
Вот так надо:
Код: C++
  1. if (CModelInfo::IsVehicleModelType(sum) != -1 && CModelInfo::IsVehicleModelType(sum) != 6)
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 13, 2016, 06:23:22 pm
Да, это я не досмотрел.
CModelInfo::IsVehicleModelType возвращает тип модели транспорта.
Но переводить его CVehicleModelInfo* не надо.
Код: C++
  1. if (sum > 0 && sum < 19010) {
  2.     int modelType = CModelInfo::IsVehicleModelType(sum);
  3.     if (modelType != -1 && modelType != 6)
  4.         CCheat::VehicleCheat(sum);
  5. }
Цитировать
Видимо эта функция не предназначена для работы с таким типом транспорта.

Действительно, в CCheat::VehicleCheat (которая также используется в опкоде 0ADD), нет варианта с созданием поезда. Вместо этого можно поэкспериментировать с функцией CTrain::CreateMissionTrain.
По поводу скрипта Дениса:
Я бы вообще переделал обработку набора числа.
В C++ можно сделать всё намного "чище".

KeyCheck.h
Код: C++
  1. #pragma once
  2.  
  3. class KeyCheck {
  4.     static unsigned char currStates[256];
  5.     static unsigned char prevStates[256];
  6.     static unsigned int timeDelayPressed[256];
  7. public:
  8.     static void Update(); // апдейт клавиш. Нужно вызывать эту функцию один раз за фрейм, перед тем, как проверять клавиши
  9.  
  10.     static bool Check(unsigned int key); // Проверить, нажата ли сейчас клавиша
  11.     static bool CheckJustDown(unsigned int key); // Проверить, была ли нажата клавиша прямо сейчас
  12.     static bool CheckJustUp(unsigned int key); // Проверить, была ли отпущена клавиша прямо сейчас
  13.     static bool CheckWithDelay(unsigned int key, unsigned int time); // Проверить нажата ли клавиша, с интервалом проверки
  14. };

KeyCheck.cpp
Код: C++
  1. #include "KeyCheck.h"
  2. #include "plugin.h"
  3. #include "game_sa\CTimer.h"
  4.  
  5. unsigned char KeyCheck::currStates[256] = {};
  6. unsigned char KeyCheck::prevStates[256] = {};
  7. unsigned int KeyCheck::timeDelayPressed[256] = {};
  8.  
  9. void KeyCheck::Update() {
  10.     memcpy(prevStates, currStates, 256);
  11.     GetKeyboardState(currStates);
  12. }
  13.  
  14. bool KeyCheck::Check(unsigned int key) {
  15.     return key < 256 && (currStates[key] & 0x80);
  16. }
  17.  
  18. bool KeyCheck::CheckJustDown(unsigned int key) {
  19.     return key < 256 && (currStates[key] & 0x80) && !(prevStates[key] & 0x80);
  20. }
  21.  
  22. bool KeyCheck::CheckJustUp(unsigned int key) {
  23.     return key < 256 && !(currStates[key] & 0x80) && (prevStates[key] & 0x80);
  24. }
  25.  
  26. bool KeyCheck::CheckWithDelay(unsigned int key, unsigned int time) {
  27.     if (key < 256 && (currStates[key] & 0x80)) {
  28.         if (!(prevStates[key] & 0x80) || CTimer::m_snTimeInMilliseconds >(timeDelayPressed[key] + time)) {
  29.             timeDelayPressed[key] = CTimer::m_snTimeInMilliseconds;
  30.             return true;
  31.         }
  32.     }
  33.     return false;
  34. }

Main.cpp
Код: C++
  1. #include <string>
  2. #include "plugin.h"
  3. #include "KeyCheck.h"
  4. #include "game_sa\common.h"
  5. #include "game_sa\CModelInfo.h"
  6. #include "game_sa\CCheat.h"
  7. #include "game_sa\CTimer.h"
  8. #include "game_sa\CFont.h"
  9. #include "game_sa\CSprite2d.h"
  10.  
  11. using namespace plugin;
  12.  
  13. class MoreVehiclesSpawner {
  14. public:
  15.     static std::string typedBuffer;
  16.     static std::string errorMessage;
  17.     static std::string errorMessageBuffer;
  18.     static unsigned int errorMessageTimer;
  19.     static bool enabled;
  20.  
  21.     static void ReportAudioEvent(int audioEventId, float volume, float speed) { // с классами, связанными со звуком, в sdk пока что не очень
  22.         CallMethod<NoRet, 0x506EA0, unsigned int, int, float, float>(0xB6BC90, audioEventId, volume, speed);
  23.     }
  24.  
  25.     static void Update() {
  26.         KeyCheck::Update(); // апдейтим состояния клавиш
  27.         if (FindPlayerPed() && FindPlayerPed()->IsAlive()) {
  28.             if (KeyCheck::CheckJustDown(VK_TAB)) { // Если нажата Tab - включаем или выключаем консоль
  29.                 enabled = !enabled;
  30.                 typedBuffer.clear();
  31.                 errorMessageBuffer.clear();
  32.             }
  33.             if (enabled) {
  34.                 errorMessage.clear();
  35.                 if (KeyCheck::CheckWithDelay(VK_BACK, 200)) { // Если нажат Backspace - убираем последний символ в строке
  36.                     if (typedBuffer.size() > 0) {
  37.                         typedBuffer.pop_back();
  38.                         ReportAudioEvent(3, 0.0f, 1.0f);
  39.                     }
  40.                 }
  41.                 else {
  42.                     for (int i = 0; i <= 9; i++) {
  43.                         if (KeyCheck::CheckWithDelay(i + 48, 200)) {
  44.                             if (typedBuffer.size() == 5)
  45.                                 errorMessage = "Too many digits!";
  46.                             else {
  47.                                 typedBuffer.push_back(i + 48); // Добавляем символ в конец строки
  48.                                 ReportAudioEvent(3, 0.0f, 1.0f);
  49.                             }
  50.                             break;
  51.                         }
  52.                     }
  53.                 }
  54.                 if (KeyCheck::CheckJustDown(VK_RETURN)) { // Если нажата Return - спавним транспорт
  55.                     if (typedBuffer.size() > 0) {
  56.                         unsigned int modelId = std::stoi(typedBuffer);
  57.                         if (modelId < 19010) {
  58.                             int modelType = CModelInfo::IsVehicleModelType(modelId);
  59.                             if (modelType != -1) {
  60.                                 if (modelType != 6) {
  61.                                     CCheat::VehicleCheat(modelId);
  62.                                     ReportAudioEvent(12, 0.0f, 1.0f);
  63.                                     errorMessageBuffer.clear(); // убираем надпись об ошибке (если она была на экране)
  64.                                 }
  65.                                 else
  66.                                     errorMessage = "Can't spawn a train model";
  67.                             }
  68.                             else
  69.                                 errorMessage = "This model is not a vehicle!";
  70.                         }
  71.                         else
  72.                             errorMessage = "ID is too big!";
  73.                     }
  74.                     else
  75.                         errorMessage = "Please enter model Id!";
  76.                 }
  77.             }
  78.         }
  79.         else
  80.             enabled = false;
  81.     }
  82.  
  83.     static void Render() {
  84.         if (enabled) {
  85.             CSprite2d::DrawRect(CRect(100.0f, 100.0f, 470.0f, 200.0f), CRGBA(0, 0, 0, 100));
  86.             CSprite2d::DrawRect(CRect(250.0f, 140.0f, 340.0f, 142.0f), CRGBA(255, 255, 255, 255));
  87.             CFont::SetScale(0.8f, 1.9f);
  88.             CFont::SetColor(CRGBA(255, 255, 255, 255));
  89.             CFont::SetAlignment(ALIGN_LEFT);
  90.             CFont::SetOutlinePosition(0);
  91.             CFont::SetBackground(false, false);
  92.             CFont::SetFontStyle(FONT_SUBTITLES);
  93.             CFont::SetProp(true);
  94.             CFont::SetWrapx(600.0f);
  95.             CFont::PrintString(105.0f, 105.0f, "Model ID:");
  96.             if (typedBuffer.size() > 0)
  97.                 CFont::PrintString(250.0f, 105.0f, const_cast<char*>(typedBuffer.c_str()));
  98.             if (errorMessage.size() > 0) {
  99.                 errorMessageBuffer = errorMessage;
  100.                 errorMessageTimer = CTimer::m_snTimeInMilliseconds;
  101.                 ReportAudioEvent(4, 0.0f, 1.0f);
  102.             }
  103.             if (errorMessageBuffer.size() > 0 && CTimer::m_snTimeInMilliseconds < (errorMessageTimer + 2000)) {
  104.                 CFont::SetColor(CRGBA(255, 0, 0, 255));
  105.                 CFont::PrintString(105.0f, 150.0f, const_cast<char*>(errorMessageBuffer.c_str()));
  106.             }
  107.         }
  108.     }
  109.  
  110.     MoreVehiclesSpawner() {
  111.         Events::gameProcessEvent += Update;
  112.         Events::drawingEvent += Render;
  113.     };
  114. } moreVehiclesSpawner;
  115.  
  116. std::string MoreVehiclesSpawner::typedBuffer;
  117. std::string MoreVehiclesSpawner::errorMessage;
  118. std::string MoreVehiclesSpawner::errorMessageBuffer;
  119. unsigned int MoreVehiclesSpawner::errorMessageTimer = 0;
  120. bool MoreVehiclesSpawner::enabled = false;

Цитировать
Давно задаюсь вопросом - можно ли переписать эту функцию так, чтобы считывать пары ID тягача и ID прицепа из текстового файла (ну в крайнем случае в сам плагин набрать это сочетание, ну лучше считывать из текстового файла) и потом аналогичным образом спаунить другие тягачи с прицепами, ведь нормального спаунера авто с прицепами так и не сделали.

Для прямой работы с памятью в sdk есть класс patch.
Для подмены функций можно использовать:
plugin::patch::RedirectCall(адрес_места_вызова, своя_функция);
plugin::patch::RedirectJump(адрес_функции, своя_функция);
При этом, новая функция должна совпадать с оригинальной - параметрами и соглашением вызова.
Выбор способа патча зависит от ситуации.
В большинстве случаев, если нужно полностью заменить тело функции, я использую RedirectJump.

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

(http://i.imgur.com/7zxL7Le.png)

Вот что получилось:
Код: C++
  1. #include "plugin.h"
  2. #include "game_sa\CStreaming.h"
  3. #include "game_sa\CCheat.h"
  4. #include "game_sa\CTrailer.h"
  5. #include "game_sa\CWorld.h"
  6.  
  7. using namespace plugin;
  8.  
  9. unsigned int truckModelIds[3] = { MODEL_LINERUN, MODEL_PETRO, MODEL_RDTRAIN };
  10. unsigned int trailerModelIds[4] = { MODEL_ARTICT1, MODEL_ARTICT2, MODEL_PETROTR, MODEL_ARTICT3 };
  11.  
  12. class SpawnTruckCheat {
  13. public:
  14.     static void Spawn() {
  15.         CVehicle *vehicle = CCheat::VehicleCheat(truckModelIds[rand() % 3]); // спавним тягач
  16.         if (vehicle) {
  17.             unsigned int modelId = trailerModelIds[rand() % 4]; // получаем случайный ID
  18.             CStreaming::RequestModel(modelId, 0); // 0 - значит, что игра может удалить эту модель когда она больше нигде не будет нужна
  19.             CStreaming::LoadAllRequestedModels(false);
  20.             if (CStreaming::ms_aInfoForModel[modelId].m_loadState == LOADSTATE_LOADED) {
  21.                 CTrailer *trailer = new CTrailer(modelId, 1); // создаём прицеп //--+
  22.                 if (trailer) {                                                  //  | По сути, функция CCheat::VehicleCheat
  23.                     trailer->SetPosn(vehicle->GetPosition());                   //  | делает всё то же самое - создаёт обьект
  24.                     trailer->SetOrientation(0.0f, 0.0f, 3.4906585f);            //  | CAutomobile/CBike/CBoat/CHeli и т.д.,
  25.                     trailer->m_nStatus = 4; // что это за статус - без понятия  //  | устанавливает ему позицию и вращение,
  26.                     CWorld::Add(trailer);                                       //  | добавляет в "мир", и т.д.
  27.                     trailer->SetTowLink(vehicle, true);                         //--+
  28.                 }
  29.             }
  30.         }
  31.     }
  32.    
  33.     SpawnTruckCheat() {
  34.         patch::RedirectJump(0x43A570, Spawn);
  35.     };
  36. } spawnTruck;
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 14, 2016, 11:13:48 am
Цитировать
KeyCheck.h
KeyCheck.cpp
Добавил в \plugin_sa\game_sa или надо было в другое место добавлять?

Цитировать
По поводу скрипта Дениса:
Я бы вообще переделал обработку набора числа.
В C++ можно сделать всё намного "чище".
Ну да, так лучше, чем в моём варианте.  :)

Цитировать
Вот что получилось:
Класс! Спасибо за пример и пояснения! Есть один момент - при повторном спауне первый тягач благополучно удаляется, а вот первый прицеп нет и получается ко второму тягачу цепляются два прицепа, через некоторое время они взрываются. Для прицепа нужен RemoveReferences. Как добавить?

И ещё сразу по прицепам есть вопрос - можно ли в трафике в процессе создания транспорта проверить, если модель определённого ID (тягач), то цеплять к нему прицеп. Цеплять не на весь транспорт с таким ID, а выборочно, ну скажем через определённое время? Цеплять разные прицепы?

Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 14, 2016, 11:34:17 am
Добавил в \plugin_sa\game_sa или надо было в другое место добавлять?

Добавляй в проект твоего плагина.
(http://i.imgur.com/fbVCB2c.png)

Есть один момент - при повторном спауне первый тягач благополучно удаляется, а вот первый прицеп нет и получается ко второму тягачу цепляются два прицепа, через некоторое время они взрываются. Для прицепа нужен RemoveReferences. Как добавить?

Тут нужен не RemoveReference.
В функции CCheat::VehicleCheat ещё вызывается CTheScripts::ClearSpaceForMissionEntity, которая удаляет все обьекты на месте спавна транспорта.
После
Код: C++
  1. trailer->SetTowLink(vehicle, true);
Добавь
Код: C++
  1. Call<NoRet, 0x486B00, CVector const&, CEntity *>(trailer->GetPosition(), trailer);
Класс CTheScripts я постараюсь добавить позже.

И ещё сразу по прицепам есть вопрос - можно ли в трафике в процессе создания транспорта проверить, если модель определённого ID (тягач), то цеплять к нему прицеп. Цеплять не на весь транспорт с таким ID, а выборочно, ну скажем через определённое время? Цеплять разные прицепы?

Можно, надо разбирать CCarCtrl::GenerateOneRandomCar.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 14, 2016, 12:23:45 pm
Цитировать
Добавь
Код: C++
  1. Call<NoRet, 0x486B00, CVector const&, CEntity *>(trailer->GetPosition(), trailer);
Теперь нормально, спасибо!

Цитировать
trailer->m_nStatus = 4; // что это за статус - без понятия
Проверил, с значениями 0-2 прицеп становится поверх тягача, 3 тоже, что и 4 получился результат.

Цитировать
Добавляй в проект твоего плагина.
Упс.. а я добавил в \plugin_sa\game_sa да ещё и коммит сделал...  поторопился... Убрать из \plugin_sa\game_sa и сделать новый коммит или ты сам исправишь?
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 14, 2016, 12:54:03 pm
Надо удалить файлы из папки, а также убрать из проекта (в Solution-explorer'e).
Можешь сделать, я позже буду коммиты делать.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 16, 2016, 11:30:39 am
Я с новой порцией вопросов.
1) Почитал про классы в С++. Написано:
Код: C++
  1. // объявление классов в С++
  2. class /*имя класса*/
  3. {
  4.   private:
  5.   /* список свойств и методов для использования внутри класса */
  6.   public:
  7.   /* список методов доступных другим функциям и объектам программы */
  8.   protected:
  9.   /*список средств, доступных при наследовании*/
  10. };

В примерах sdk в конце класса перед ; ставится ещё слово (здесь, например, playerTest, в других примерах другое, при этом я заметил, что с названием класса оно вообще может не совпадать). Для чего это слово?
Код: C++
  1. class PlayerTest {
  2. public:
  3.         PlayerTest() {
  4.                
  5.         }
  6. } playerTest;

2) Имеет ли значение в какой процесс "вклиниваться"?
3) Имеет ли значение каким способом это делать? Так
Код: C++
  1. Events::gameProcessEvent += [] {
  2.                        
  3. };
или так
Код: C++
  1. OpenDoorExample() {
  2.     Events::gameProcessEvent.Add(Process);
  3. }

4) Как я понял, если надо, чтобы два действия обрабатывались одновременно, надо "вклиниваться" в два разных процесса?
5) Как в плагине проверить $ONMISSION == 0
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 16, 2016, 07:35:27 pm
Код: C++
  1. class PlayerTest {
  2. public:
  3.         PlayerTest() {
  4.                
  5.         }
  6. } playerTest;
Это то же, что и
Код: C++
  1. class PlayerTest {
  2. public:
  3.         PlayerTest() {
  4.                
  5.         }
  6. };
  7.  
  8. PlayerTest playerTest;
Просто первый вариант выглядит компактнее.
Ну а это
Код: C++
  1. PlayerTest playerTest;
Обявление переменной playerTest с типом PlayerTest.
Причем обьявление происходит в глобальной области.
А все глобальные обьекты конструируются (т.е. вызывается их конструктор) при загрузке dll (asi).
Поэтому мы и определяем конструктор и добавляем туда инжект наших функций.

Уже говорил об этом тут (http://forum.gtabuilder.ru/index.php?topic=337.msg2155#msg2155) и тут (http://i.imgur.com/rFDIFbP.png)

2) Имеет ли значение в какой процесс "вклиниваться"?

Имеет. Например, ты не сможешь выводить что-либо на экран из эвента gameProcess. Для этого есть drawingEvent и подобные.
Или, например, тебе нужно загрузить текстуру, которая бы постоянно была в памяти (аналог в оригинальной игре - текстуры из hud.txt). Тут можно использовать rwInitEvent, а для выгрузки - rwShutdownEvent.
Для рисования на радаре есть drawRadarEvent. Ну и эвенты, связанные с игровыми субьектами - напр. vehicle/ped/object RenderEvent.
3) Имеет ли значение каким способом это делать? Так
Код: C++
  1. Events::gameProcessEvent += [] {
  2.                        
  3. };
или так
Код: C++
  1. OpenDoorExample() {
  2.     Events::gameProcessEvent.Add(Process);
  3. }

Нет. Просто в первом варинте используется лямбда-выражение (https://msdn.microsoft.com/uk-ua/library/dd293608.aspx).
А во втором - отдельно создается функция Process.
4) Как я понял, если надо, чтобы два действия обрабатывались одновременно, надо "вклиниваться" в два разных процесса?

Не обязательно. Просто с примером с открыванием компонентов удобнее было сделать именно так.
5) Как в плагине проверить $ONMISSION == 0

Есть функция CTheScripts::IsPlayerOnAMission().
Про этот класс я уже говорил.
Пока что вызвать можно так:
Код: C++
  1. plugin::Call<bool, 0x464D50>();
Или добавить её в класс.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 20, 2016, 01:12:47 pm
Понятно. Спасибо.

Решил переписать свой скрипт "Дизель" на С++
{$CLEO .cs}
0000:
const
   AVTO = 0@          // авто
   MODEL_AVTO = 1@    // модель авто
   CLASS_AVTO = 2@    // класс авто
   STRUCT_AVTO = 3@   // структура авто                         
   ENGINE_TYPE = 4@   // тип двигателя
   EXHAUST = 5@       // тип выхлопа
   PARTICLE_1 = 6@    // эффект_1
   PARTICLE_2 = 7@    // эффект_2
   DOUBLE = 8@        // вспомогательная переменная
   X_1 = 9@           // координата Х_1
   Y_1 = 10@          // координата Y_1
   Z_1 = 11@          // координата Z_1
   X_2 = 12@          // координата Х_2
end

var
   X_1: Float
   X_2: Float
end

while true
    wait 0
    if
      Player.Defined($PLAYER_CHAR)
    then
        if 
          actor.Driving($PLAYER_ACTOR)
        then
            03C0: AVTO = actor $PLAYER_ACTOR car // транспорт игрока
            if
              01C1: car AVTO stopped // транспорт стоит
            then
                if 
                  00E1: key_pressed 0 16
                then 
                    0441: MODEL_AVTO = car AVTO model   // модель транспорта
                    08EC: CLASS_AVTO = car AVTO type   // класс транспорта
                    if and
                      0A01: model MODEL_AVTO car // транспорт = авто
                      04A4: CLASS_AVTO == 4  // @ == any // класс авто worker
                    then
                        0A97: STRUCT_AVTO = car AVTO struct // структура авто
                        STRUCT_AVTO += 0x384
                        0A8D: STRUCT_AVTO = read_memory STRUCT_AVTO size 4 virtual_protect 0 // получили указатель на handling-структуру
                        STRUCT_AVTO += 0x75
                        0A8D: ENGINE_TYPE = read_memory STRUCT_AVTO size 1 virtual_protect 0 // получили тип двигателя
                        if
                          ENGINE_TYPE == 68 // тип двигателя = дизель
                        then 
                            MODEL_AVTO *= 4
                            MODEL_AVTO += 0xA9B0C8
                            0A8D: STRUCT_AVTO = read_memory MODEL_AVTO size 4 virtual_protect 0 // CModel
                            STRUCT_AVTO += 0x5C                                       
                            0A8D: STRUCT_AVTO = read_memory STRUCT_AVTO size 4 virtual_protect 0 // vehicle struct
                            STRUCT_AVTO += 0x48
                            0A8D: X_1 = read_memory STRUCT_AVTO size 4 virtual_protect 0 // X_1
                            STRUCT_AVTO += 4
                            0A8D: Y_1 = read_memory STRUCT_AVTO size 4 virtual_protect 0 // Y_1
                            STRUCT_AVTO += 4
                            0A8D: Z_1 = read_memory STRUCT_AVTO size 4 virtual_protect 0 // Z_1
                            //---определение второго глушителя---
                            0A97: STRUCT_AVTO = car AVTO struct
                            STRUCT_AVTO += 0x384
                            0A8D: STRUCT_AVTO = read_memory STRUCT_AVTO size 4 virtual_protect 0
                            STRUCT_AVTO += 0xCC
                            0A8D: EXHAUST = read_memory STRUCT_AVTO size 4 virtual_protect 0
                            if
                              08B7: test EXHAUST bit 13
                            then
                                DOUBLE = 2
                                X_2 = X_1
                                X_2 *= -1.0
                            else
                                DOUBLE = 1
                            end
                            //----------------------------------
                            066C: PARTICLE_1 = attach_particle "riot_smoke" to_car AVTO with_offset X_1 Y_1 Z_1 rotation 0.0 0.0 0.0 flag 1
                            064C: make_particle PARTICLE_1 visible
                            if
                              DOUBLE == 2
                            then
                                066C: PARTICLE_2 = attach_particle "riot_smoke" to_car AVTO with_offset X_2 Y_1 Z_1 rotation 0.0 0.0 0.0 flag 1
                                064C: make_particle PARTICLE_2 visible
                            end
                            wait 500
                            0650: destroy_particle PARTICLE_1
                            if
                              DOUBLE == 2
                            then
                                0650: destroy_particle PARTICLE_2
                            end
                        end     
                    end     
                end
            end
        end
    end
end 

Возникли вопросы:
1) Не нашёл в sdk замены
01C1: car AVTO stoppedПосмотрел опкод в базе и нашёл функцию
.text:004861F0     CScriptEngine__isVehicleStopped proc nearДобавил в CVehicle
Код: C++
  1. //CVehicle.h
  2. bool CVehicle::IsStopped();
  3. //CVehicle.cpp
  4. // Converted from thiscall bool CVehicle::IsStopped(void) 0x4861F0
  5. bool CVehicle::IsStopped()
  6. {
  7.   return ((bool(__cdecl *)(CVehicle*))0x4861F0)(this);
  8. }
Ну и вроде как этот вопрос решился.
2) Не нашёл в sdk замены (нашёл только перечисление ePadButton)
00E1: key_pressed 0 16В базе нашёл функцию
.text:00485B10     _CScriptThread__getPlayerKeyState proc nearТолько куда и как её добавить в sdk не разобрался.
3) Не могу понять как считать значение m_nEngineType из cTransmission.h
4) Также не понятно как проверить наличие второго глушителя у модели
m_bDoubleExhaust из tHandlingData.h

5) Работу с партициклами в sdk я тоже не нашёл
066C: PARTICLE_1 = attach_particle "riot_smoke" to_car AVTO with_offset X_1 Y_1 Z_1 rotation 0.0 0.0 0.0 flag 1
064C: make_particle PARTICLE_1 visible
0650: destroy_particle PARTICLE_1
Пока набросал примерный код без учёта непонятных моментов. Создание партицикла заменил на создание короны.
Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\common.h"
  3. #include "game_sa\CTimer.h"
  4. #include "game_sa\CModelInfo.h"
  5. #include "game_sa\CVehicle.h"
  6. #include "game_sa\tHandlingData.h"
  7. #include "game_sa\CCoronas.h"
  8.  
  9. using namespace plugin;
  10.  
  11. class Diesel {
  12. public:
  13.     Diesel() {
  14.     static bool m_currentState = true;
  15.     static unsigned int m_nLastTimeWhenAnyActionWasEnabled = 0;
  16.     static tHandlingData *hanlData;
  17.  
  18.         Events::gameProcessEvent += [] {
  19.             CVehicle *vehicle = FindPlayerVehicle(-1, false);
  20.             if (vehicle && vehicle->m_dwVehicleClass == VEHICLE_AUTOMOBILE && vehicle->IsStopped() && KeyPressed(87) && m_currentState) {
  21.                 CVehicleModelInfo *vehModel = reinterpret_cast<CVehicleModelInfo *>(CModelInfo::ms_modelInfoPtrs[vehicle->m_wModelIndex]);
  22.                 hanlData = vehicle->m_pHandlingData;
  23.                 if (vehModel->m_nClass == 4 && hanlData->m_transmissionData.m_nEngineType == 68) {
  24.                     m_currentState = false;
  25.                     m_nLastTimeWhenAnyActionWasEnabled = CTimer::m_snTimeInMilliseconds;
  26.                 }
  27.             } else if (!m_currentState) {
  28.                        if (CTimer::m_snTimeInMilliseconds < (m_nLastTimeWhenAnyActionWasEnabled + 2000)) {
  29.                            CVector posn = reinterpret_cast<CVehicleModelInfo *>(CModelInfo::ms_modelInfoPtrs[vehicle->m_wModelIndex])->m_pVehicleStruct->m_avDummyPosn[6];
  30.                            CCoronas::RegisterCorona(reinterpret_cast<unsigned int>(vehicle) + 50 + 6 + 0, vehicle, 255, 128, 0, 255, posn, 0.3f, 150.0f, CORONATYPE_SHINYSTAR, 0, false, false, 0, 0.0f, false, 0.5f, 0, 50.0f, false, true);
  31.                                if (hanlData->m_bDoubleExhaust) {
  32.                                    posn.x *= -1.0f;
  33.                                    CCoronas::RegisterCorona(reinterpret_cast<unsigned int>(vehicle) + 50 + 6 + 1, vehicle, 255, 128, 0, 255, posn, 0.3f, 150.0f, CORONATYPE_SHINYSTAR, 0, false, false, 0, 0.0f, false, 0.5f, 0, 50.0f, false, true);
  34.                                }
  35.                        } else
  36.                              m_currentState = true;
  37.               }
  38.         };
  39.     }
  40. } example;

Просьба прояснить эти вопросы.
6) Ещё в sdk есть перечисление eCommandName. Как можно использовать в написании плагина?


UPD:
С вопросами 3 и 4 разобрался, эти вопросы снимаются.
Код подредактировал.

Остальные вопросы всё ещё неразрешимы пока. Просьба помочь разобраться.
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 24, 2016, 06:12:21 pm
.text:004861F0     CScriptEngine__isVehicleStopped proc near
Это CTheScripts::IsVehicleStopped(CVehicle *). Вообще, класс CScriptEngine (который в старой базе) - это CTheScripts ("Скрипты"). CScriptThread из старой базы - CRunningScript ("Исполняемый скрипт").
Я эту функцию и ещё некоторые (включая IsPlayerOnMission) добавил в последнем коммите.

2) Не нашёл в sdk замены (нашёл только перечисление ePadButton)
00E1: key_pressed 0 16В базе нашёл функцию
.text:00485B10     _CScriptThread__getPlayerKeyState proc near
Это
short CRunningScript::GetPadState(unsigned short player, unsigned short key)Тебе надо смотреть в CPad.
По идее, так должно работать:
Код: C++
  1. if (CPad::GetPad()->NewState.RightStickX)

5) Работу с партициклами в sdk я тоже не нашёл
Да, по партиклам почти ничего не было. Теперь есть.
Для создания и управления над партиклам есть классы FxManager_c, Fx_c, FxSystem_c.
Например, для создания можно использовать функции
Код: C++
  1. FxSystem_c* FxManager_c::CreateFxSystem(char* name, RwMatrixTag* transform, RwMatrixTag* objectMatrix, unsigned char ignoreBoundingChecks);
  2. FxSystem_c* FxManager_c::CreateFxSystem(char* name, RwV3d* position, RwMatrixTag* objectMatrix, unsigned char ignoreBoundingChecks);
А для удаления
Код: C++
  1. void FxSystem_c::Kill()
Вообще, рекомендую глянуть, как в оригинале создаются партиклы эффекта нитро, можно по аналогии прицеплять свои партиклы.
Детальнее чуть позже расскажу, может и пример сделаю.

Ещё в sdk есть перечисление eCommandName. Как можно использовать в написании плагина?
Это просто перечисление опкодов.
Использование вызова опкодов в плагинах не желательно (лучше просто посмотреть, как реализован опкод).

PS Добавил ссылку на свою базу в подпись. Для открытия нужна IDA 6.8.

UPD: Вот ещё немножко инфы.
Опкод 066C создаёт партикл примерно вот так (direction - в стром описании опкода это rotation, ignoreBoundingChecks - flag):
Код: C++
  1. g_fx.CreateMatFromVec(&rotationMat, &offset, &direction);
  2. FxSystem_c *fxSystem = g_fxMan.CreateFxSystem(name, &rotationMat, &vehicle->m_pRwObject->object.parent->modelling, ignoreBoundingChecks);
Опкод 064C делает вот это:
Код: C++
  1. fxSystem->Play()
Опкод 064E
Код: C++
  1. fxSystem->Stop()
Опкод 064F
Код: C++
  1. fxSystem->PlayAndKill()
Опкод 0650
Код: C++
  1. fxSystem->Kill()

Код: C++
  1. #include "plugin.h"
  2. #include "game_sa\CTheScripts.h"
  3. #include "game_sa\Fx_c.h"
  4. #include "game_sa\CTimer.h"
  5. #include "game_sa\CModelInfo.h"
  6. #include "game_sa\CPad.h"
  7. #include "game_sa\common.h"
  8.  
  9. using namespace plugin;
  10.  
  11. class Diesel {
  12. public:
  13.     class DieselData {
  14.     public:
  15.         FxSystem_c *m_apParticles[2];
  16.         unsigned int m_nCreationTime;
  17.  
  18.         DieselData(CVehicle *) {
  19.             m_apParticles[0] = m_apParticles[1] = nullptr;
  20.             m_nCreationTime = 0;
  21.         }
  22.  
  23.         ~DieselData() {
  24.             for (unsigned int i = 0; i < 2; i++) {
  25.                 if (m_apParticles[i]) {
  26.                     m_apParticles[i]->Kill();
  27.                     m_apParticles[i] = nullptr;
  28.                 }
  29.             }
  30.         }
  31.     };
  32.  
  33.     Diesel() {
  34.         static VehicleExtendedData<DieselData> VehDiesel;
  35.  
  36.         Events::gameProcessEvent += [] {
  37.             CVehicle *vehicle = FindPlayerVehicle(-1, false);
  38.             if (vehicle && vehicle->m_dwVehicleClass == VEHICLE_AUTOMOBILE && CTheScripts::IsVehicleStopped(vehicle) && CPad::GetPad()->NewState.ButtonCross) {
  39.                 CVehicleModelInfo *vehModel = reinterpret_cast<CVehicleModelInfo *>(CModelInfo::ms_modelInfoPtrs[vehicle->m_wModelIndex]);
  40.                 if (vehModel->m_nClass == 4 && vehicle->m_pHandlingData->m_transmissionData.m_nEngineType == 'D') {
  41.                     DieselData &dieselData = VehDiesel.Get(vehicle);
  42.                     dieselData.m_nCreationTime = CTimer::m_snTimeInMilliseconds;
  43.                     RwV3d offset = *reinterpret_cast<RwV3d*>(&vehModel->m_pVehicleStruct->m_avDummyPosn[6]);
  44.                     if (!dieselData.m_apParticles[0]) {
  45.                         dieselData.m_apParticles[0] = g_fxMan.CreateFxSystem("riot_smoke", &offset, &reinterpret_cast<RwFrame*>(vehicle->m_pRwObject->parent)->modelling, true);
  46.                         if (dieselData.m_apParticles[0])
  47.                             dieselData.m_apParticles[0]->Play();
  48.                     }
  49.                     if (vehicle->m_pHandlingData->m_bDoubleExhaust) {
  50.                         if (!dieselData.m_apParticles[1]) {
  51.                             offset.x *= -1.0f;
  52.                             dieselData.m_apParticles[1] = g_fxMan.CreateFxSystem("riot_smoke", &offset, &reinterpret_cast<RwFrame*>(vehicle->m_pRwObject->parent)->modelling, true);
  53.                             if (dieselData.m_apParticles[1])
  54.                                 dieselData.m_apParticles[1]->Play();
  55.                         }
  56.                     }
  57.                 }
  58.             }
  59.         };
  60.  
  61.         Events::vehicleRenderEvent += [](CVehicle *vehicle) {
  62.             DieselData &dieselData = VehDiesel.Get(vehicle);
  63.             for (unsigned int i = 0; i < 2; i++) {
  64.                 if (dieselData.m_apParticles[i] && CTimer::m_snTimeInMilliseconds > (dieselData.m_nCreationTime + 500)) {
  65.                     dieselData.m_apParticles[i]->Kill();
  66.                     dieselData.m_apParticles[i] = nullptr;
  67.                 }
  68.             }
  69.         };
  70.     }
  71. } diesel;
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 26, 2016, 09:41:52 am
Спасибо за пояснения и новую базу. Буду разбираться.

Когда-то меня просили написать скрипт для SA, чтобы у дополнительно установленных моделей типа zr350, фары открывались на разный угол. Ну т.е. задавать каждой модели в .ini файле свой угол открытия фар, т.к. есть модели у которых он значительно отличается и получается при использовании плагина Александра GTA SA Vehicle Special Abilities Editor и новых моделей, фары у некоторых моделей открываются не полностью или наоборот поворачиваются на лишние градусы. Для VC и GTA III я такую возможность сделал (только там не считывание угла с .ini для каждой модели, а значение угла "зашивается" в саму модель путём названия вспомогательного дамми, т.е. получается надо редактировать саму модель).
 (http://www.youtube.com/watch?v=QWWcvHoUvRk&feature=youtu.be#)

Для SA хотелось бы это сделать без редактирования самой модели. Можно ли это реализовать с помощью плагина?

И ещё, касаемо плагина Александра GTA SA Vehicle Special Abilities Editor - как известно, плагин
Цитировать
позволяет ставить модели авто,обладающие особыми способностями, к примеру фары у ZR350, ковш у Dozer'a ... на лю6ые другие свободные ID с сохранением всех рабочих деталей

так вот, если можно, то просьба показать, как это реализовать на С++ на примере хотя бы возможности цеплять прицепы к моделям, где изначально эта возможность не имелась.
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 30, 2016, 12:23:20 am
Если я ничего не путаю, то проверка по ID модели для тягачей производится в функции CAutomobile::GetTowBarPos (функция, которая получает позицию крепления).
Тут есть 2 варианта:
1) Переписать функцию GetTowBarPos.
2) Изменить функцию GetTowBarPos частично, внедрением в ту часть, где идёт проверка модели.
Пример замены функции я уже показывал (SpawnTruck).
Второй вариант сложнее - нужно будет писать код на ассемблере.

Первый вариант, конечно, предпочтительнее. Тем более, функция не такая уж и большая.

Вместо ini я бы использовал такой формат
Код: C++
  1. section
  2. 1,2,3,4
  3. end
По-моему, так намного удобнее.

Код: C++
  1. #include "plugin.h"
  2. #include <vector>
  3. #include <string>
  4. #include <sstream>
  5. #include <fstream>
  6. #include "game_sa\CModelInfo.h"
  7. #include "game_sa\CAutomobile.h"
  8. #include "game_sa\CCoronas.h"
  9.  
  10. using namespace plugin;
  11.  
  12. std::vector<unsigned int> truckIDs;
  13.  
  14. #define Z_POS_FOR_MODELS_WITHOUT_NODE 1.3f
  15.  
  16. #define g_TOWTRUCK_HOIST_DOWN_LIMIT *(unsigned short *)0x8D313C
  17.  
  18. class VehicleAdvanced {
  19. public:
  20.     static void ReadSettingsFile() {
  21.         std::ifstream stream("vehicle_advanced.dat");
  22.         for (std::string line; getline(stream, line); ) {
  23.             if (line[0] != ';' && line[0] != '#') {
  24.                 if (!line.compare("trucks")) {
  25.                     while (line.compare("end") && getline(stream, line)) {
  26.                         if (line[0] != ';' && line[0] != '#') {
  27.                             std::stringstream ss(line);
  28.                             int i;
  29.                             while (ss >> i) {
  30.                                 truckIDs.push_back(i);
  31.                                 if (ss.peek() == ',')
  32.                                     ss.ignore();
  33.                             }
  34.                         }
  35.                     }
  36.                 }
  37.             }
  38.         }
  39.     }
  40.  
  41.     static bool IsTruckModel(unsigned int modelId) {
  42.         for (unsigned int i : truckIDs) {
  43.             if (i == modelId)
  44.                 return true;
  45.         }
  46.         return false;
  47.     }
  48.  
  49.     static bool __fastcall MyGetTowBarPos(CAutomobile *automobile, int, CVector &outPos, bool ignoreModelType, CVehicle *attachTo) {
  50.         if (automobile->m_wModelIndex == MODEL_TOWTRUCK || automobile->m_wModelIndex == MODEL_TRACTOR) {
  51.             float yOffset = -1.05f;
  52.             if (automobile->m_wModelIndex == MODEL_TRACTOR) {
  53.                 if (attachTo && attachTo->m_dwVehicleSubClass == VEHICLE_TRAILER && attachTo->m_wModelIndex != MODEL_FARMTR1)
  54.                     return false;
  55.                 yOffset = -0.6f;
  56.             }
  57.             else if (attachTo && attachTo->m_dwVehicleSubClass == VEHICLE_TRAILER)
  58.                 return false;
  59.             outPos.x = 0.0f;
  60.             outPos.y = yOffset + CModelInfo::ms_modelInfoPtrs[automobile->m_wModelIndex]->m_pColModel->m_boundBox.m_vSup.y;
  61.             outPos.z = (1.0f - static_cast<float>(automobile->m_wMiscComponentAngle) / static_cast<float>(g_TOWTRUCK_HOIST_DOWN_LIMIT)) * 0.5f + 0.5f - automobile->m_fFrontHeightAboveRoad;
  62.             outPos = *automobile->m_matrix * outPos;
  63.             return true;
  64.         }
  65.         if (IsTruckModel(automobile->m_wModelIndex)
  66.             || automobile->m_wModelIndex == MODEL_UTILITY && attachTo && attachTo->m_wModelIndex == MODEL_UTILTR1
  67.             || (automobile->m_wModelIndex == MODEL_BAGGAGE || automobile->m_wModelIndex == MODEL_TUG || automobile->m_wModelIndex == MODEL_BAGBOXA || automobile->m_wModelIndex == MODEL_BAGBOXB)
  68.             && attachTo
  69.             && (attachTo->m_wModelIndex == MODEL_BAGBOXA
  70.                 || attachTo->m_wModelIndex == MODEL_BAGBOXB
  71.                 || attachTo->m_wModelIndex == MODEL_TUGSTAIR))
  72.         {
  73.             if (automobile->m_aCarNodes[CAR_MISC_A]) {
  74.                 RwMatrix *ltm = RwFrameGetLTM(automobile->m_aCarNodes[CAR_MISC_A]);
  75.                 outPos.x = ltm->pos.x;
  76.                 outPos.y = ltm->pos.y;
  77.                 outPos.z = ltm->pos.z;
  78.             }
  79.             else {
  80.                 outPos.x = 0.0f;
  81.                 outPos.y = CModelInfo::ms_modelInfoPtrs[automobile->m_wModelIndex]->m_pColModel->m_boundBox.m_vSup.y - 0.5f;
  82.                 outPos.z = Z_POS_FOR_MODELS_WITHOUT_NODE - automobile->m_fFrontHeightAboveRoad;
  83.                 outPos = *automobile->m_matrix * outPos;
  84.             }
  85.             return true;
  86.         }
  87.         if (ignoreModelType) {
  88.             outPos.x = 0.0f;
  89.             outPos.y = CModelInfo::ms_modelInfoPtrs[automobile->m_wModelIndex]->m_pColModel->m_boundBox.m_vSup.y - 0.5f;
  90.             outPos.z = 0.5f - automobile->m_fFrontHeightAboveRoad;
  91.             outPos = *automobile->m_matrix * outPos;
  92.             return true;
  93.         }
  94.         return false;
  95.     }
  96.  
  97.     static void Test() {
  98.         for (int i = 0; i < CPools::ms_pVehiclePool->m_Size; i++) {
  99.             CVehicle *vehicle = CPools::ms_pVehiclePool->GetAt(i);
  100.             if (vehicle) {
  101.                 if (IsTruckModel(vehicle->m_wModelIndex)) {
  102.                     if (vehicle->m_dwVehicleSubClass == VEHICLE_AUTOMOBILE && reinterpret_cast<CAutomobile *>(vehicle)->m_aCarNodes[CAR_MISC_A]) {
  103.                         CVector *posn = reinterpret_cast<CVector *>(&RwFrameGetLTM(reinterpret_cast<CAutomobile *>(vehicle)->m_aCarNodes[CAR_MISC_A])->pos);
  104.                         CCoronas::RegisterCorona(reinterpret_cast<unsigned int>(vehicle) + 100, 0, 0, 255, 0, 255, *posn, 1.0f, 150.0f, CORONATYPE_SHINYSTAR,
  105.                             0, false, false, 0, 0.0f, false, 0.1f, 0, 15.0f, false, false);
  106.                     }
  107.                     else {
  108.                         CCoronas::RegisterCorona(reinterpret_cast<unsigned int>(vehicle) + 100, vehicle, 255, 0, 0, 255, CVector(0.0f,
  109.                             CModelInfo::ms_modelInfoPtrs[vehicle->m_wModelIndex]->m_pColModel->m_boundBox.m_vSup.y - 0.5f,
  110.                             1.3f - reinterpret_cast<CAutomobile *>(vehicle)->m_fFrontHeightAboveRoad), 1.0f, 150.0f, CORONATYPE_SHINYSTAR,
  111.                             0, false, false, 0, 0.0f, false, 0.1f, 0, 15.0f, false, false);
  112.                     }
  113.                 }
  114.             }
  115.         }
  116.     }
  117.  
  118.     VehicleAdvanced() {
  119.         patch::RedirectJump(0x6AF250, MyGetTowBarPos);
  120.         Events::gameProcessEvent += Test;
  121.         ReadSettingsFile();
  122.     }
  123. } vehAdv;

vehicle_advanced.dat
Код: C++
  1. trucks
  2. 400,401,402,403,404,405,406,407,408,409
  3. 410
  4. 411
  5. end

(http://i.imgur.com/RiNWFzjm.png) (http://i.imgur.com/RiNWFzj.png)

PS Соединять LANDSTALKER и ARTICT2 не желательно  :P Наверное, лучше ещё добавить настройку офсета под каждую модель.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 30, 2016, 09:11:22 am
 :( Студия ругается на строчки
Код: C++
  1. outPos = *automobile->m_matrix * outPos;

Цитировать
1>VehicleAdvanced.cpp(62): error C2676: бинарный "*": "CMatrixLink" не определяет этот оператор или преобразование к типу приемлемо к встроенному оператору
1>VehicleAdvanced.cpp(83): error C2676: бинарный "*": "CMatrixLink" не определяет этот оператор или преобразование к типу приемлемо к встроенному оператору
1>VehicleAdvanced.cpp(91): error C2676: бинарный "*": "CMatrixLink" не определяет этот оператор или преобразование к типу приемлемо к встроенному оператору


http://savepic.net/8355422.png (http://savepic.net/8355422.png)

Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Август 30, 2016, 05:57:49 pm
Обнови проект.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Август 30, 2016, 08:01:28 pm
Теперь нормально.
Что сказать? Круто! Спасибо за пример.
С каждым новым примером я всё больше убеждаюсь в том, что надо было мне раньше заняться изучением С++. Ну рано или поздно это должно было произойти, в рамках клео скриптов давно стало "тесно".  :)

Нашёл, кстати, исходник на Delphi того плагина Александра. Вот часть с тягачами:
{TRAILER_HOOKs}
  Reset(F);

  {FIRST'n'LAST Hook}
  CodePtr := VirtualAlloc(0, 1000 , MEM_COMMIT , PAGE_READWRITE) ;
  VirtualProtect(ptr($6AF26C),5,PAGE_READWRITE,OldProtect);
  PByte($6AF26C)^:= $E9;     // jmp
  PInteger($6AF26D)^:= Integer(CodePtr) - $6AF26C - 5;   // Alloced place

  While not EOF(F) do
  begin
    ReadLn(F,StrName);
    if not (strpos(PChar(StrName),'[TRAILER_HOOKs]')=nil) then break;
  end;

  Count:= ReadIntValue('MAIN','TRAILER_HOOKs');

  For i:= 0 to Count-1 do
  begin
    ReadLn(F,S[i]);
    PByte(Integer(CodePtr)+i*6)^:= $66;
    PByte(Integer(CodePtr)+i*6+1)^:= $3D;
    PSmallInt(Integer(CodePtr)+i*6+2)^:= StrToInt(S[i]);
    PByte(Integer(CodePtr)+i*6+4)^:= $74;
    PByte(Integer(CodePtr)+i*6+5)^:= (Count-i-1)*6 + $A;
  end;

  PByte(Integer(CodePtr)+Count*6)^:= $66;  // cmp
  PByte(Integer(CodePtr)+Count*6+1)^:= $3D; // ax
  PByte(Integer(CodePtr)+Count*6+2)^:= $02;   // id
  PByte(Integer(CodePtr)+Count*6+3)^:= $02;   // 2 b
  PByte(Integer(CodePtr)+Count*6+4)^:= $0F;  // jnz
  PByte(Integer(CodePtr)+Count*6+5)^:= $85;  // $6AF284
  PInteger(Integer(CodePtr)+Count*6+6)^:= $6AF272 - (Integer(CodePtr)+Count*6+10);
  PByte(Integer(CodePtr)+Count*6+10)^:= $E9;   // jmp $6AF2CC
  PInteger(Integer(CodePtr)+Count*6+11)^:= $6AF2CC - (Integer(CodePtr)+Count*6+15);

  CloseFile(F);
{TRAILER_HOOKs_END}
Название: Re: Написание плагина. Настройка проекта
Отправлено: mfisto от Август 31, 2016, 12:31:25 am
Есть плагин fastman92 лимит-аджастер. Там тоже есть попытка сделать дополнительные модели. Проект отличный, можно расширять карту, воду, кол-во авто итд. Так вот к чему это я, может вам объединиться с ним касательно части новых фишек для авто. Возможно его дело пойдет быстрее.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Сентябрь 01, 2016, 12:41:19 pm
Цитировать
Есть плагин fastman92 лимит-аджастер. Там тоже есть попытка сделать дополнительные модели. Проект отличный, можно расширять карту, воду, кол-во авто итд. Так вот к чему это я, может вам объединиться с ним касательно части новых фишек для авто. Возможно его дело пойдет быстрее.
С меня в данный момент польза для fastman'а вряд ли будет, я только начал изучать.  ;D

Цитировать
Вместо ini я бы использовал такой формат
Код: C++
  1. section
  2. 1,2,3,4
  3. end
Цитировать
По-моему, так намного удобнее.

Да, разобрал, что к чему. Удобная вещь.
Есть вопросы:
1) Есть ли ограничение по количеству элементов записываемых в truckIDs?
2) Длина строки в .dat ограничена в 256 символа или никакого ограничения нет?
3) Как бы ещё организовать запись/считывание пары значений. Например, одного типа - ID тягача и ID прицепа? А разных типов? Например, ID дополнительной модели типа zr350 и угол, на который должны открываться у неё фары?
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Сентябрь 01, 2016, 09:31:39 pm
Нет, ограничений нету.

Я бы сделал так, по аналогии с vehicles.ide:
Код: C++
  1. trucks
  2. 400, 500, 0.0
  3. 401, 505, 1.0
  4. end
Или вообще всё в отдельный файл и убрать метки trucks и end.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Сентябрь 02, 2016, 02:11:22 pm
Цитировать
Я бы сделал так, по аналогии с vehicles.ide:
Код: C++
  1. trucks
  2. 400, 500, 0.0
  3. 401, 505, 1.0
  4. end
Цитировать
Или вообще всё в отдельный файл и убрать метки trucks и end.
Надо ещё один объект std::stringstream и std::vector для float значений? А как сопоставлять их значения? Или как-то по другому? Вот, допустим, первой колонкой идёт набор ID каких-то моделей транспорта, второй колонкой идёт какое-то значение float (допустим, угол поворота фар). Как их правильно считать и сопоставить угол и ID?
Код: C++
  1. trucks
  2. 500, 1.5
  3. 501, 1.71
  4. 502, 1.35
  5. end



Решил попробовать сделать неоновую подсветку для автомобилей в плагине. Посмотрел скрипт Дениса для SA, что-то там сложновато. Глянул твой скрипт для GTA III - тут всё понятно, можно взять за основу. Посмотрел в базе опкод 016F, там вызывается  CShadows__registerShadowByType, глянул в sdk - есть такое дело в CShadows.
Код: C++
  1. static void StoreShadowToBeRendered(unsigned char type, RwTexture* texture, CVector* posn, float x1, float y1, float x2, float y2, short intensity, unsigned char red, unsigned char green, unsigned char blue, float zDistance, bool bDrawOnWater, float scale, CRealTimeShadow* shadowData, bool bDrawOnBuildings);


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

Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Сентябрь 02, 2016, 05:14:35 pm
Обьяви структуру, которая будет содержать все нужные параметры, и создай вектор для хранения этих структур.
https://ru.wikipedia.org/wiki/Vector_(C%2B%2B) (https://ru.wikipedia.org/wiki/Vector_(C%2B%2B))
Ну и для чтения можно заюзать sscanf.
Код: C++
  1. #include "plugin.h"
  2. #include <vector>
  3. #include <string>
  4. #include <fstream>
  5.  
  6. using namespace plugin;
  7. using namespace std;
  8.  
  9. class SettingsTest {
  10. public:
  11.     struct MyData {
  12.         unsigned int baseModelId;
  13.         unsigned int linkedModelId;
  14.         float value;
  15.     };
  16.  
  17.     static vector<MyData> entries;
  18.  
  19.     static void ReadSettingsFile() {
  20.         std::ifstream stream("vehicle_advanced.dat");
  21.         for (std::string line; getline(stream, line); ) {
  22.             if (line[0] != ';' && line[0] != '#') {
  23.                 if (!line.compare("trucks")) {
  24.                     while (getline(stream, line) && line.compare("end")) {
  25.                         if (line[0] != ';' && line[0] != '#') {
  26.                             MyData entry;
  27.                             if (sscanf(line.c_str(), "%d, %d, %f", &entry.baseModelId, &entry.linkedModelId, &entry.value) == 3)
  28.                                 entries.push_back(entry);
  29.                             else
  30.                                 Error("Failed to scan line: %s", line.c_str());
  31.                         }
  32.                     }
  33.                 }
  34.             }
  35.         }
  36.     }
  37.  
  38.     MyData *GetDataInfoForModel(unsigned int BaseModelId) {
  39.         for (unsigned int i = 0; i < entries.size(); i++) {
  40.             if (entries[i].baseModelId == BaseModelId)
  41.                 return &entries[i];
  42.         }
  43.         return nullptr;
  44.     }
  45.  
  46.     SettingsTest() {
  47.         ReadSettingsFile();
  48.         for (unsigned int i = 0; i < entries.size(); i++)
  49.             Error("Entry %d: %d %d %.2f", i + 1, entries[i].baseModelId, entries[i].linkedModelId, entries[i].value);
  50.  
  51.         MyData *entryModel400 = GetDataInfoForModel(400);
  52.         if (entryModel400) {
  53.             Error("Test for model 400: %d %d %.2f", entryModel400->baseModelId, entryModel400->linkedModelId, entryModel400->value);
  54.         }
  55.     }
  56. } test;

Код: C++
  1. #include "plugin.h"
  2. #include "game_sa\CClock.h"
  3. #include "game_sa\CShadows.h"
  4. #include "game_sa\common.h"
  5. #include "KeyCheck.h"
  6.  
  7. using namespace plugin;
  8. using namespace std;
  9.  
  10. RwTexture *&pExpTex = *(RwTexture **)0xC403F4;
  11.  
  12. class NeonLights {
  13. public:
  14.     enum eNeonColor {
  15.         NEON_YELLOW, NEON_GREEN, NEON_RED, NEON_BLUE, NEON_PURPLE
  16.     };
  17.  
  18.     class Neon {
  19.     public:
  20.         unsigned char color;
  21.         bool activated;
  22.         bool processed;
  23.  
  24.         Neon(CVehicle *) {
  25.             activated = processed = false;
  26.         }
  27.  
  28.         void Enable(eNeonColor Color) {
  29.             color = Color;
  30.             activated = true;
  31.         }
  32.  
  33.         void Disable() {
  34.             activated = false;
  35.         }
  36.     };
  37.  
  38.     static VehicleExtendedData<Neon> VehNeon;
  39.  
  40.     static bool CanEnableNeonOnThisVehicle(CVehicle *vehicle) {
  41.         return CClock::GetIsTimeInRange(23, 5) && vehicle->m_pDriver && (vehicle->m_dwVehicleSubClass == VEHICLE_AUTOMOBILE ||
  42.             vehicle->m_dwVehicleSubClass == VEHICLE_MTRUCK || vehicle->m_dwVehicleSubClass == VEHICLE_QUAD);
  43.     }
  44.  
  45.     static void ProcessNpcVehicle(CVehicle *vehicle) {
  46.         if (CanEnableNeonOnThisVehicle(vehicle) && !VehNeon.Get(vehicle).processed) {
  47.             VehNeon.Get(vehicle).processed = true;
  48.             if (rand() % 11 == 10) {
  49.                 VehNeon.Get(vehicle).activated = true;
  50.                 VehNeon.Get(vehicle).color = rand() % 5;
  51.             }
  52.         }
  53.     }
  54.  
  55.     static void ProcessVehicles() {
  56.         KeyCheck::Update();
  57.         CVehicle *playaVeh = FindPlayerVehicle(0, false);
  58.         if (playaVeh) {
  59.             if (KeyCheck::Check(VK_SHIFT)) {
  60.                 if (KeyCheck::CheckJustDown('1'))
  61.                     VehNeon.Get(playaVeh).Enable(NEON_YELLOW);
  62.                 else if (KeyCheck::CheckJustDown('2'))
  63.                     VehNeon.Get(playaVeh).Enable(NEON_GREEN);
  64.                 else if (KeyCheck::CheckJustDown('3'))
  65.                     VehNeon.Get(playaVeh).Enable(NEON_RED);
  66.                 else if (KeyCheck::CheckJustDown('4'))
  67.                     VehNeon.Get(playaVeh).Enable(NEON_BLUE);
  68.                 else if (KeyCheck::CheckJustDown('5'))
  69.                     VehNeon.Get(playaVeh).Enable(NEON_PURPLE);
  70.                 else if (KeyCheck::CheckJustDown('0'))
  71.                     VehNeon.Get(playaVeh).Disable();
  72.             }
  73.         }
  74.         for (int i = 0; i < CPools::ms_pVehiclePool->m_Size; i++) {
  75.             CVehicle *vehicle = CPools::ms_pVehiclePool->GetAt(i);
  76.             if (vehicle && vehicle != playaVeh)
  77.                 ProcessNpcVehicle(vehicle);
  78.         }
  79.     }
  80.  
  81.     static void RenderNeonForVehicle(CVehicle *vehicle) {
  82.         if (VehNeon.Get(vehicle).activated) {
  83.             unsigned char r, g, b;
  84.             switch (VehNeon.Get(vehicle).color) {
  85.             case NEON_YELLOW:
  86.                 r = 255; g = 200; b = 0;
  87.                 break;
  88.             case NEON_GREEN:
  89.                 r = 0; g = 255; b = 0;
  90.                 break;
  91.             case NEON_RED:
  92.                 r = 255; g = 0; b = 0;
  93.                 break;
  94.             case NEON_BLUE:
  95.                 r = 0; g = 0; b = 255;
  96.                 break;
  97.             case NEON_PURPLE:
  98.                 r = 255; g = 0; b = 255;
  99.                 break;
  100.             }
  101.             CShadows::StoreShadowToBeRendered(2, pExpTex, &vehicle->GetPosition(), 2.5f, 0.0f, 0.0f, -2.5f, 255, r, g, b, 2.0f, false, 1.0f, 0, true);
  102.         }
  103.     }
  104.  
  105.     NeonLights() {
  106.         Events::gameProcessEvent += ProcessVehicles;
  107.         Events::vehicleRenderEvent += RenderNeonForVehicle;
  108.     }
  109. } neon;
  110.  
  111. VehicleExtendedData<NeonLights::Neon> NeonLights::VehNeon;
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Сентябрь 03, 2016, 10:48:35 am
Спасибо за примеры.

По первому примеру студия ругается на:
Код: C++
  1. 1>------ Перестроение всех файлов начато: проект: testVector, Конфигурация: Release Win32 ------
  2. 1>  Source.cpp
  3. 1>Source.obj : error LNK2001: неразрешенный внешний символ ""public: static class std::vector<struct SettingsTest::MyData,class std::allocator<struct SettingsTest::MyData> > SettingsTest::entries" (?entries@SettingsTest@@2V?$vector@UMyData@SettingsTest@@V?$allocator@UMyData@SettingsTest@@@std@@@std@@A)"
  4. 1>D:\Games\GTA San Andreas\scripts\testVector.asi : fatal error LNK1120: неразрешенных внешних элементов: 1
  5. ========== Перестроение всех проектов: успешно: 0, с ошибками: 1, пропущено: 0 ==========

По второму примеру всё отлично, за исключением того, что проекция на поверхности стоит под одним и тем же углом, т.е. не поворачивается вместе с авто. Это заметно, если сделать проекцию овальной, а не круглой или заменить текстуру на квадратную. Можно это, как-то исправить? У Дениса в скрипте проекция поворачивается вместе с авто.
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Сентябрь 03, 2016, 01:06:38 pm
В первом примере надо определить переменную entries.
Код: C++
  1. vector<SettingsTest::MyData> SettingsTest::entries;
По второму - смотри этот пост.
http://modsforgta.ucoz.ru/forum/9-22-3523-16-1333570000 (http://modsforgta.ucoz.ru/forum/9-22-3523-16-1333570000)
Код: C++
  1. CVector center = vehicle->TransformFromObjectSpace(CVector(0.0f, -3.0f, 0.0f));
  2. CVector up = vehicle->TransformFromObjectSpace(CVector(0.0f, -2.0f, 0.0f)) - center;
  3. CVector right = vehicle->TransformFromObjectSpace(CVector(2.0f, -3.0f, 0.0f)) - center;
  4. CShadows::StoreShadowToBeRendered(2, pExpTex, &center, up.x, up.y, right.x, right.y, 255, r, g, b, 2.0f, false, 1.0f, 0, true);
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Сентябрь 03, 2016, 07:00:36 pm
Разобрался, спасибо! Есть только один момент:
Код: C++
  1. RwTexture *&pExpTex = *(RwTexture **)0xC403F4;
Такие записи (*& и **) ранее не встречались. Просьба разъяснить. Где про это почитать?

Немного изменил и дополнил код:
1) Ограничил для транспорта трафика по m_nClass (оставил для poorfamily, richfamily и executive).
2) Ограничил для транспорта игрока по m_dwVehicleSubClass.
3) Изменил случайный выбор для транспорта в трафике.
4) Взял другую (прямоугольную) текстуру.
5) Размер проекции "привязал" на габариты модели.
6) Так как текстура с ровными краями, сделал мигание проекции, чтобы это сильно "не бросалось в глаза".

Код: C++
  1. #include "plugin.h"
  2. #include "game_sa\CClock.h"
  3. #include "game_sa\CShadows.h"
  4. #include "game_sa\common.h"
  5. #include "KeyCheck.h"
  6. #include "game_sa\CModelInfo.h"
  7. #include "game_sa\CTimer.h"
  8.  
  9. #define TURN_ON_OFF_DELAY 500
  10.  
  11. using namespace plugin;
  12. using namespace std;
  13.  
  14. RwTexture *&pWhiteTex = *(RwTexture **)0xB4E3EC;
  15.  
  16. class NeonLights {
  17. public:
  18.     enum eNeonColor {
  19.         NEON_YELLOW, NEON_GREEN, NEON_RED, NEON_BLUE, NEON_PURPLE
  20.     };
  21.  
  22.     class Neon {
  23.     public:
  24.         unsigned char color;
  25.         bool activated;
  26.         bool processed;
  27.  
  28.         Neon(CVehicle *) {
  29.             activated = processed = false;
  30.         }
  31.  
  32.         void Enable(eNeonColor Color) {
  33.             color = Color;
  34.             activated = true;
  35.         }
  36.  
  37.         void Disable() {
  38.             activated = false;
  39.         }
  40.     };
  41.  
  42.     static VehicleExtendedData<Neon> VehNeon;
  43.  
  44.     static bool CanEnableNeonOnThisVehicle(CVehicle *vehicle) {
  45.         CVehicleModelInfo *vehModel = reinterpret_cast<CVehicleModelInfo *>(CModelInfo::ms_modelInfoPtrs[vehicle->m_wModelIndex]);
  46.         return CClock::GetIsTimeInRange(22, 5) && vehicle->m_pDriver && (vehModel->m_nClass == 1 || vehModel->m_nClass == 2 || vehModel->m_nClass == 3) && (vehicle->m_dwVehicleSubClass == VEHICLE_AUTOMOBILE ||
  47.                         vehicle->m_dwVehicleSubClass == VEHICLE_MTRUCK || vehicle->m_dwVehicleSubClass == VEHICLE_QUAD);
  48.     }
  49.  
  50.     static void ProcessNpcVehicle(CVehicle *vehicle) {
  51.         if (CanEnableNeonOnThisVehicle(vehicle) && !VehNeon.Get(vehicle).processed) {
  52.             VehNeon.Get(vehicle).processed = true;
  53.             if (rand() % 3 == 1) {
  54.                 VehNeon.Get(vehicle).activated = true;
  55.                 VehNeon.Get(vehicle).color = rand() % 5;
  56.             }
  57.         }
  58.     }
  59.  
  60.     static void ProcessVehicles() {
  61.         KeyCheck::Update();
  62.         CVehicle *playaVeh = FindPlayerVehicle(0, false);
  63.         if (playaVeh && (playaVeh->m_dwVehicleSubClass == VEHICLE_AUTOMOBILE ||
  64.                         playaVeh->m_dwVehicleSubClass == VEHICLE_MTRUCK || playaVeh->m_dwVehicleSubClass == VEHICLE_QUAD)) {
  65.             if (KeyCheck::Check(VK_SHIFT)) {
  66.                 if (KeyCheck::CheckJustDown('1'))
  67.                     VehNeon.Get(playaVeh).Enable(NEON_YELLOW);
  68.                 else if (KeyCheck::CheckJustDown('2'))
  69.                     VehNeon.Get(playaVeh).Enable(NEON_GREEN);
  70.                 else if (KeyCheck::CheckJustDown('3'))
  71.                     VehNeon.Get(playaVeh).Enable(NEON_RED);
  72.                 else if (KeyCheck::CheckJustDown('4'))
  73.                     VehNeon.Get(playaVeh).Enable(NEON_BLUE);
  74.                 else if (KeyCheck::CheckJustDown('5'))
  75.                     VehNeon.Get(playaVeh).Enable(NEON_PURPLE);
  76.                 else if (KeyCheck::CheckJustDown('0'))
  77.                     VehNeon.Get(playaVeh).Disable();
  78.             }
  79.         }
  80.         for (int i = 0; i < CPools::ms_pVehiclePool->m_Size; i++) {
  81.             CVehicle *vehicle = CPools::ms_pVehiclePool->GetAt(i);
  82.             if (vehicle && vehicle != playaVeh)
  83.                 ProcessNpcVehicle(vehicle);
  84.         }
  85.     }
  86.  
  87.     static void RenderNeonForVehicle(CVehicle *vehicle) {
  88.         if (VehNeon.Get(vehicle).activated) {
  89.             unsigned char r, g, b;
  90.             switch (VehNeon.Get(vehicle).color) {
  91.             case NEON_YELLOW:
  92.                 r = 255; g = 200; b = 0;
  93.                 break;
  94.             case NEON_GREEN:
  95.                 r = 0; g = 255; b = 0;
  96.                 break;
  97.             case NEON_RED:
  98.                 r = 255; g = 0; b = 0;
  99.                 break;
  100.             case NEON_BLUE:
  101.                 r = 0; g = 0; b = 255;
  102.                 break;
  103.             case NEON_PURPLE:
  104.                 r = 255; g = 0; b = 255;
  105.                 break;
  106.             }
  107.             if (CTimer::m_snTimeInMilliseconds % (TURN_ON_OFF_DELAY + 250) < TURN_ON_OFF_DELAY) {
  108.                 CVector Pos = CModelInfo::ms_modelInfoPtrs[vehicle->m_wModelIndex]->m_pColModel->m_boundBox.m_vSup;
  109.                 CVector center = vehicle->TransformFromObjectSpace(CVector(0.0f, 0.0f, 0.0f));
  110.                 CVector up = vehicle->TransformFromObjectSpace(CVector(0.0f, -Pos.y - 0.5f, 0.0f)) - center;
  111.                 CVector right = vehicle->TransformFromObjectSpace(CVector(Pos.x + 0.2f, 0.0f, 0.0f)) - center;
  112.                 CShadows::StoreShadowToBeRendered(2, pWhiteTex, &center, up.x, up.y, right.x, right.y, 255, r, g, b, 2.0f, false, 1.0f, 0, true);
  113.             }
  114.         }
  115.     }
  116.  
  117.     NeonLights() {
  118.         Events::gameProcessEvent += ProcessVehicles;
  119.         Events::vehicleRenderEvent += RenderNeonForVehicle;
  120.     }
  121. } neon;
  122.  
  123. VehicleExtendedData<NeonLights::Neon> NeonLights::VehNeon;
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Сентябрь 03, 2016, 10:07:05 pm
Такая конструкция используется для определения игровых переменных.
Читать про ссылки, указатели, разыменование.

Допустим, мы знаем, что по адресу 0x863984 находится значение гравитации (float, дефолтное значение - 0.008).
Как работать с этим адресом (записать, прочитать)? (функции класса patch не трогаем)
Например, вот так
Код: C++
  1. // Чтение
  2. float gravity = *(float *)0x863984; // C-шный стиль; представляем 0x863984 как указатель на float и разыменовываем его
  3. float gravity = *reinterpret_cast<float *>0x863984; // C++ -шный type casting
  4. // Запись
  5. *(float *)0x863984 = 1.0f;
  6. *reinterpret_cast<float *>0x863984 = 1.0f;
Отлично. Но выглядит не очень. Что делать?
Можно сделать так
Код: C++
  1. #define GRAVITY *(float *)0x863984
  2.  
  3. /* ... */
  4.  
  5. GRAVITY = 1.0f;
А ещё лучше - создать переменную-указатель, и читать/записывать значение через неё:
Код: C++
  1. float *pGravity = (float *)0x863984;
  2.  
  3. /* ... */
  4.  
  5. *pGravity = 1.0f; // предварительно разыменовываем
  6. float gravity = *pGravity; // предварительно разыменовываем
Всё вроде хорошо... Но можно сделать ещё лучше, если вспомнить о ссылках.
Код: C++
  1. float &gGravity = *(float *)0x863984; // создаём ссылку на значение по адресу 0x863984
  2.  
  3. /* ... */
  4.  
  5. gGravity = 1.0f;
Ситуация с RwTexture* - аналогична.
По адресу 0xB4E3EC находится указатель на обьект текстуры графического движка игры (RwTexture).
Код: C++
  1. RwTexture *&pWhiteTex = *(RwTexture **)0xB4E3EC; // создаём ссылку на RwTexture* (указатель на обьект текстуры) по адресу 0xB4E3EC
  2.  
  3. /* ... */
  4.  
  5. plugin::Error("Texture \"%s\" size: %dx%d", pWhiteTex->name, pWhiteTex->raster->width, pWhiteTex->raster->height);
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Сентябрь 04, 2016, 12:53:26 pm
Спасибо за разъяснения.

По sdk для VC пока никакого продвижения нет? А по SA много ещё осталось не разобранного?
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Сентябрь 04, 2016, 03:18:26 pm
По VC нету.
По SA - да, много.
В последнем коммите я обновил класс CShadows, добавил все функции класса и перечисления.
Ну и переменные тоже.

Добавил все функции класса CGeneral, теперь там есть такое:
Код: C++
  1. static unsigned int GetRandomNumberInRange(int min, int max); // returns random int in range [min;max)
  2. static float GetRandomNumberInRange(float min, float max); // returns random float in range [min;max)
Эти функции используются в опкодах 0208 и 0209.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Сентябрь 04, 2016, 04:34:14 pm
Добавил все функции класса CGeneral, теперь там есть такое:
Эти функции используются в опкодах 0208 и 0209.
Это хорошо. В скриптах я частенько пользовался этими опкодами.


И ещё сразу по прицепам есть вопрос - можно ли в трафике в процессе создания транспорта проверить, если модель определённого ID (тягач), то цеплять к нему прицеп. Цеплять не на весь транспорт с таким ID, а выборочно, ну скажем через определённое время? Цеплять разные прицепы?
Можно, надо разбирать CCarCtrl::GenerateOneRandomCar.
Как будет свободное время разбери пожалуйста это дело. Вопрос с прицепами интересен многим пользователям. Было написано несколько скриптов на эту тему (в том числе и у меня есть такой скрипт), но это всё не то.
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Сентябрь 10, 2016, 09:19:14 am
Тем временем в plugin-sdk появились все классы из иерархии CVehicle (CAutomobile, CMonsterTruck, CQuadBike, CTrain, CHeli, CPlane, CBike, CBmx, CTrailer, CBoat).
Обновился пример CreateCar, теперь вместо CCheat::VehicleCheat там используется своя функция спавна.
Название: Re: Написание плагина. Настройка проекта
Отправлено: kenking от Сентябрь 10, 2016, 10:43:02 am
Обновился пример CreateCar, теперь вместо CCheat::VehicleCheat там используется своя функция спавна.
Просьба пояснить эти строчки:
Код: C++
  1. reinterpret_cast<CBike *>(vehicle)->m_nDamageFlags |= 0x10;
  2. reinterpret_cast<CBmx *>(vehicle)->m_nDamageFlags |= 0x10;

и эти:
Код: C++
  1. reinterpret_cast<CBike *>(vehicle)->PlaceOnRoadProperly();
  2. reinterpret_cast<CAutomobile *>(vehicle)->PlaceOnRoadProperly();

Пробую работу с выводом текстур на экран. Почитал твой урок http://ru-script.3dn.ru/publ/programirovanie/sozdanie_dll_bibliotek/sa_124_c_plugin_sdk_risuem_chast_1/18-1-0-206 (http://ru-script.3dn.ru/publ/programirovanie/sozdanie_dll_bibliotek/sa_124_c_plugin_sdk_risuem_chast_1/18-1-0-206)

Перенёс пример на sdk:
Код: C++
  1. #include <plugin.h>
  2. #include "game_sa\CSprite2d.h"
  3. #include "game_sa\CTxdStore.h"
  4.  
  5. using namespace plugin;
  6.  
  7. class MyPlugin {
  8. public:
  9.     static CSprite2d mySprite;
  10.  
  11.     MyPlugin() {
  12.         Events::initRwEvent += [] {
  13.             // Добавляем новый слот для нашего txd
  14.             int txd = CTxdStore::AddTxdSlot("mytxd");
  15.             // Загружаем наш txd в выделенный слот
  16.             CTxdStore::LoadTxd(txd, "MODELS\\MYTXD.TXD");
  17.             // Увеличиваем счётчик использований для созданного txd
  18.             CTxdStore::AddRef(txd);
  19.             // Сохраняем текущий txd
  20.             CTxdStore::PushCurrentTxd();
  21.             // Устанавливаем наш txd как текущий
  22.             CTxdStore::SetCurrentTxd(txd);
  23.             // Назначаем текстру нашему спрайту (имя текстуры, имя альфа-маски для текстуры)
  24.             mySprite.SetTexture("skipicon", "skipicona");
  25.             // Восстанавливаем сохранённый txd
  26.             CTxdStore::PopCurrentTxd();
  27.         };
  28.  
  29.         Events::drawingEvent += [] {
  30.             // Рисуем наш спрайт (указываем параметры - позиция, ширина, высота, цвет)
  31.             mySprite.Draw(20.0, 20.0, 150.0, 150.0, CRGBA(255, 255, 255, 255));
  32.         };
  33.     }
  34.  
  35.     ~MyPlugin() {
  36.         Events::shutdownRwEvent += [] {
  37.             // Удаляем наш спрайт
  38.             mySprite.Delete();
  39.             // Удаляем наш txd
  40.             CTxdStore::RemoveTxdSlot(CTxdStore::FindTxdSlot("mytxd"));
  41.         };
  42.     }
  43. } myPlugin;
  44.  
  45. CSprite2d MyPlugin::mySprite;

Верно ли?

Дальше задумался над кодом для спидометра, но не нашёл функции для вращения текстуры стрелки.
Название: Re: Написание плагина. Настройка проекта
Отправлено: DK от Сентябрь 10, 2016, 01:46:27 pm
Просьба пояснить эти строчки:
Код: C++
  1. reinterpret_cast<CBike *>(vehicle)->m_nDamageFlags |= 0x10;
  2. reinterpret_cast<CBmx *>(vehicle)->m_nDamageFlags |= 0x10;

Для мотоциклов и велосипедов надо установить этот флаг после создания. Что это за флаг - не знаю (0x10 = 5ый бит).
и эти:
Код: C++
  1. reinterpret_cast<CBike *>(vehicle)->PlaceOnRoadProperly();
  2. reinterpret_cast<CAutomobile *>(vehicle)->PlaceOnRoadProperly();

Название функции PlaceOnRoadProperly говорит само за себя - "ставит" транспорт на землю.
Верно ли?

Нет. Добавлять функции в эвенты надо в конструкторе. Конструктор (глобального обьекта) выполняется, когда ASI прицепляется к процессу игры.
Деструктор (глобального обьекта) выполняется, когда ASI открепляется от процесса игры.
Дальше задумался над кодом для спидометра, но не нашёл функции для вращения текстуры стрелки.