# Простейший плагин

Для написания плагина с plugin-sdk необходимо следующее:

* Установить Visual Studio (желательно последнюю версию - 2017). Подробнее - [здесь](https://github.com/DK22Pac/plugin-sdk/wiki/%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-%D1%81%D1%80%D0%B5%D0%B4%D1%8B-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D0%BA%D0%B8);
* Скачать plugin-sdk и настроить его. Подробнее - [здесь](https://github.com/DK22Pac/plugin-sdk/wiki/%D0%9D%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-plugin-sdk).

Итак, после того, как plugin-sdk настроен, мы можем создать плагин в Visual Studio. Для этого создаём новый проект и выбираем шаблон проекта Plugin-SDK в подразделе Visual C++ - Plugin-SDK:

[![Plugin Project Wizard](https://github.com/wmysterio/scm-scripting-lessons/raw/resources/_pu/2/87644493.png)](https://github.com/wmysterio/scm-scripting-lessons/raw/resources/_pu/2/87644493.png)

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

```cpp
#include "plugin.h"
#include "CMessages.h"
#include "CClock.h"

using namespace plugin;
```

Мы будем изменять игровое время (класс `CClock` отвечает за работу с игровыми "часами") и выводить сообщение (класс `CMessage` предоставляет функции для вывода сообщений).

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

Такой подход позволяет избавиться от явного использования функции `DllMain`, но, впрочем, никто вам не запрещает выполнять инициализацию плагина так, как Вам удобно.

```cpp
#include "plugin.h"
#include "CMessages.h"
#include "CClock.h"

using namespace plugin;

class TestPlugin {
public:
    TestPlugin() {
        // код инициализации плагина
    }
} test; // создание экземпляра класса TestPlugin в глобальной области видимости
```

### Инициализация плагина

В plugin-sdk реализована идея "событий" (events). Т.е., есть возможность выполнить свой код в какой-то определённый момент игры. Обращаться ко всем событиям можно из пространства имён `plugin::Events`. Вот как это выглядит:

```cpp
// Добавить вызов функции MyFunction, когда игра вызывает функцию CGame::Process
Events::gameProcessEvent.Add(MyFunction); 
```

Метод `Add` добавляет указанную функцию к конкретному игровому "событию". При этом, у метода `Add` есть альтернатива в виде оператора `+=`:

```cpp
// Добавить вызов функции MyFunction, когда игра вызывает функцию CGame::Process
Events::gameProcessEvent += MyFunction; 
```

Также, есть возможность указать когда именно вызывать нашу функцию: до "события" или после:

```cpp
// Добавить вызов функции MyFunction, перед вызовом CGame::Process
Events::gameProcessEvent.before += MyFunction; 

// Добавить вызов функции MyFunction, поле вызова CGame::Process
// (стандартное поведение - т.е. аналогично вызову "Events::gameProcessEvent +=")
Events::gameProcessEvent.after += MyFunction; 
```

Бывают ситуации, когда такая возможность может быть действительно полезной. Например, когда надо выполнить какие-то операции до и после отрисовки игрового субъекта (например, транспорта).

Ниже приведен список некоторых событий, доступных в plugin-sdk:

| Событие                 | Функция-прототип                      | Описание                            |
| ----------------------- | ------------------------------------- | ----------------------------------- |
| drawingEvent            | void()                                | Отрисовка в 2D                      |
| drawHudEvent            | void()                                | Отрисовка HUD-элементов             |
| drawRadarEvent          | void()                                | Отрисовка радара                    |
| drawBlipsEvent          | void()                                | Отрисовка иконок на радаре          |
| drawRadarOverlayEvent   | void(bool bInMenu)                    | Отрисовка зон влияния на радаре     |
| drawMenuBackgroundEvent | void(void \*menuManager)              | Отрисовка фона в меню               |
| initRwEvent             | void()                                | Инициализация графического движка   |
| shutdownRwEvent         | void()                                | Деинициализация графического движка |
| vehicleRenderEvent      | void(CVehicle\* vehicle)              | Рендер транспорта                   |
| pedRenderEvent          | void(CPed\* ped)                      | Рендер педа                         |
| objectRenderEvent       | void(CObject\* object)                | Рендер обьекта                      |
| vehicleSetModelEvent    | void(CVehicle\* vehicle, int modelId) | Установка модели транспорта         |
| pedSetModelEvent        | void(CPed\* ped, int modelId)         | Установка модели педа               |
| d3dResetEvent           | void()                                | Пересоздание D3D-девайса            |
| d3dLostEvent            | void()                                | Утерян доступ к D3D-девайсу         |
| gameProcessEvent        | void()                                | Обработка разных компонентов игры   |
| initGameEvent           | void()                                | Инициализация игры                  |
| reInitGameEvent         | void()                                | Повторная инициализация игры        |
| onPauseAllSounds        | void()                                | Остановка всех звуков в игре        |
| onResumeAllSounds       | void()                                | Включение всех звуков в игре        |
| initScriptsEvent        | void()                                | Инициализация скриптов              |
| processScriptsEvent     | void()                                | Обработка скриптов                  |

Используя лямбда-выражение в C++, мы можем записать добавление функции в таком виде:

```cpp
Events::gameProcessEvent += [] {
    // код нашей функции
};
```

### Код

Мы сделаем изменение времени после нажатия клавиши `Delete`. Вот так будет выглядеть наш код:

```cpp
#include "plugin.h"
#include "CMessages.h"
#include "CClock.h"

using namespace plugin;

class TestPlugin {
public:
    TestPlugin() {
        Events::gameProcessEvent += [] {
            // Если нажата клавиша Delete
            if (KeyPressed(VK_DELETE)) {
                // Устанавливаем время
                CClock::SetGameClock(12, 0, CClock::ms_nGameClockDayOfWeek);
                // Выводим сообщение
                CMessages::AddMessageJumpQ("You have set time to ~b~12:00", 3000, 0, false);
            }
        };
    }
} test;
```

Код готов, переключаемся в режим "Release" и компилируем проект (`F7`).

Итак, что мы получаем в случае использования Plugin SDK? Чистые хуки без явно видимых патчей и работы напрямую с адресами памяти.

***

Автор: **DK22Pac**


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lessons.sannybuilder.com/00100/00700/000100.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
