Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
257 changes: 173 additions & 84 deletions examples/CreateCar/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,116 +4,205 @@
https://github.com/DK22Pac/plugin-sdk
Do not delete this comment block. Respect others' work!
*/
#include "plugin.h"
#include <plugin.h> // Plugin-SDK version 1003 from 2026-03-28 00:44:29
#include "common.h"
#include "CStreaming.h"
#include "CBmx.h"
#include "CBoat.h"
#include "CHeli.h"
#include "CMessages.h"
#include "CModelInfo.h"
#include "CMonsterTruck.h"
#include "CQuadBike.h"
#include "CHeli.h"
#include "CPlane.h"
#include "CBmx.h"
#include "CQuadBike.h"
#include "CStreaming.h"
#include "CTimer.h"
#include "CTheScripts.h"
#include "CTrailer.h"
#include "CBoat.h"
#include "CTrain.h"
#include "CWorld.h"
#include "CTheScripts.h"
#include "CTimer.h"

using namespace plugin;

class CreateCarExample {
public:
static unsigned int m_nLastSpawnedTime; // время последнего спавна ; last spawned time
struct Main
{
bool keyCarSpawnPressed = false;

Main()
{
// register event callbacks
Events::gameProcessEvent += []{ gInstance.OnGameProcess(); };
}

void OnGameProcess()
{
bool pressed;

// handle car spawning hotkey
pressed = KeyPressed(VK_TAB);
if (pressed && !keyCarSpawnPressed) // just pressed
{
SpawnRandomCar();
}
keyCarSpawnPressed = pressed;
}

void SpawnRandomCar()
{
auto player = FindPlayerPed();
if (!player || player->m_nAreaCode != 0)
{
return; // player not found or inside interior
}

// Вынесем создание авто в отдельную функцию SpawnVehicle ; Put everything related to car creation into SpawnVehicle function
static CVehicle *SpawnVehicle(unsigned int modelIndex, CVector position, float orientation) {
// Загружаем модель
unsigned int modelCount = MODEL_UTILTR1 - MODEL_LANDSTAL + 1;
std::srand(CTimer::m_snTimeInMilliseconds); // initialize random seed
unsigned int model = MODEL_LANDSTAL + (std::rand() % modelCount); // get random from range

CVector position = player->TransformFromObjectSpace({0.0f, 8.0f, -1.0f}); // get offset from player
float rotation = player->m_fCurrentRotation + DegToRad(120.0f);

auto vehicle = SpawnVehicle(model, position, rotation);

static char msg[128]; // static, as it must exist as long, as it is printed on the screen

if (!vehicle)
{
sprintf_s(msg, "~r~~h~Failed to spawn vehicle model: %d", model);
CMessages::AddMessageJumpQ(msg, 10000, 0);
return; // failed to spawn
}

sprintf_s(msg, "Vehicle model: %d~n~Base type: %d, Type: %d", model, vehicle->m_nVehicleClass, vehicle->m_nVehicleSubClass);
CMessages::AddMessageJumpQ(msg, 10000, 0);

// try modify components
if (!vehicle->m_pRwClump)
{
return; // graphical object not available
}

// get component by name
RwFrame* component = CClumpModelInfo::GetFrameFromName(vehicle->m_pRwClump, "bonnet_dummy");
if (component)
{
component->modelling.pos.z += 1.0f; // move up
}

// get component by index
if (vehicle->m_nVehicleSubClass == VEHICLE_AUTOMOBILE) // eCarNodes enum only applies to the CAutomobile class!
{
auto car = reinterpret_cast<CAutomobile*>(vehicle);

if (car->m_aCarNodes[eCarNodes::CAR_BUMP_FRONT]) // make sure the component is present
{
car->m_aCarNodes[eCarNodes::CAR_BUMP_FRONT]->modelling.pos.z += 1.0f; // move up
}
}
}

// pack everything related to car creation into SpawnVehicle function
static CVehicle* SpawnVehicle(unsigned int modelIndex, CVector position, float heading)
{
unsigned char oldFlags = CStreaming::ms_aInfoForModel[modelIndex].m_nFlags;

CStreaming::RequestModel(modelIndex, GAME_REQUIRED);
CStreaming::LoadAllRequestedModels(false);
if (CStreaming::ms_aInfoForModel[modelIndex].m_nLoadState == LOADSTATE_LOADED) {
if (!(oldFlags & GAME_REQUIRED)) {
CStreaming::SetModelIsDeletable(modelIndex);
CStreaming::SetModelTxdIsDeletable(modelIndex);
}
CVehicle *vehicle = nullptr;
// Выделяем обьект из пула
switch (reinterpret_cast<CVehicleModelInfo *>(CModelInfo::ms_modelInfoPtrs[modelIndex])->m_nVehicleType) {
case VEHICLE_MTRUCK:
vehicle = new CMonsterTruck(modelIndex, 1);
break;
case VEHICLE_QUAD:
vehicle = new CQuadBike(modelIndex, 1);
if (CStreaming::ms_aInfoForModel[modelIndex].m_nLoadState != LOADSTATE_LOADED)
{
return nullptr; // failed to load
}

if (!(oldFlags & GAME_REQUIRED))
{
CStreaming::SetModelIsDeletable(modelIndex);
CStreaming::SetModelTxdIsDeletable(modelIndex);
}

auto modelInfo = CModelInfo::GetModelInfo(modelIndex);
if (!modelInfo || modelInfo->GetModelType() != ModelInfoType::MODEL_INFO_VEHICLE)
{
return nullptr; // invalid model type
}
auto vehModelInfo = reinterpret_cast<CVehicleModelInfo*>(modelInfo);

CVehicle* vehicle = nullptr;
switch (vehModelInfo->m_nVehicleType)
{
case VEHICLE_BMX:
vehicle = new CBmx(modelIndex, eVehicleCreatedBy::RANDOM_VEHICLE);
break;
case VEHICLE_HELI:
vehicle = new CHeli(modelIndex, 1);

case VEHICLE_BIKE:
vehicle = new CBike(modelIndex, eVehicleCreatedBy::RANDOM_VEHICLE);
break;
case VEHICLE_PLANE:
vehicle = new CPlane(modelIndex, 1);

case VEHICLE_QUAD:
vehicle = new CQuadBike(modelIndex, eVehicleCreatedBy::RANDOM_VEHICLE);
break;
case VEHICLE_BIKE:
vehicle = new CBike(modelIndex, 1);
reinterpret_cast<CBike *>(vehicle)->m_nDamageFlags |= 0x10;

case VEHICLE_AUTOMOBILE:
vehicle = new CAutomobile(modelIndex, eVehicleCreatedBy::RANDOM_VEHICLE, true);
break;
case VEHICLE_BMX:
vehicle = new CBmx(modelIndex, 1);
reinterpret_cast<CBmx *>(vehicle)->m_nDamageFlags |= 0x10;

case VEHICLE_MTRUCK:
vehicle = new CMonsterTruck(modelIndex, eVehicleCreatedBy::RANDOM_VEHICLE);
break;

case VEHICLE_TRAILER:
vehicle = new CTrailer(modelIndex, 1);
vehicle = new CTrailer(modelIndex, eVehicleCreatedBy::RANDOM_VEHICLE);
break;

case VEHICLE_BOAT:
vehicle = new CBoat(modelIndex, 1);
vehicle = new CBoat(modelIndex, eVehicleCreatedBy::RANDOM_VEHICLE);
break;

case VEHICLE_HELI:
vehicle = new CHeli(modelIndex, eVehicleCreatedBy::RANDOM_VEHICLE);
break;

case VEHICLE_PLANE:
vehicle = new CPlane(modelIndex, eVehicleCreatedBy::RANDOM_VEHICLE);
break;

case VEHICLE_TRAIN:
vehicle = new CTrain(modelIndex, eVehicleCreatedBy::RANDOM_VEHICLE);
reinterpret_cast<CTrain*>(vehicle)->m_nTrainFlags.bNotOnARailRoad = true;
break;

default:
vehicle = new CAutomobile(modelIndex, 1, true);
// unsupported type
break;
}
if (vehicle) {
// Размещаем транспорт в игровом мире
vehicle->SetPosn(position);
vehicle->SetOrientation(0.0f, 0.0f, orientation);
vehicle->m_nStatus = 4;
vehicle->m_eDoorLock = DOORLOCK_UNLOCKED;
CWorld::Add(vehicle);
CTheScripts::ClearSpaceForMissionEntity(position, vehicle); // удаляем другие обьекты, которые находятся в этих координатах
if (vehicle->m_nVehicleClass == VEHICLE_BIKE)
reinterpret_cast<CBike *>(vehicle)->PlaceOnRoadProperly();
else if (vehicle->m_nVehicleClass != VEHICLE_BOAT)
reinterpret_cast<CAutomobile *>(vehicle)->PlaceOnRoadProperly();
return vehicle;
}
}
return nullptr;
}
if (!vehicle)
{
return nullptr; // failed to spawn
}

// Наша функция спавна ; Our spawning function
static void ProcessSpawn() {
if (KeyPressed(VK_TAB) && CTimer::m_snTimeInMilliseconds > (m_nLastSpawnedTime + 1000)) { // Если нажата клавиша и прошло больше секунды с момента последнего спавна
CPed *player = FindPlayerPed(-1); // находим игрока ; find player
if (player && !player->m_nAreaCode) { // если найден и не в интерьере ; if found
CVector position = FindPlayerPed(-1)->TransformFromObjectSpace(CVector(0.0f, 5.0f, 0.0f)); // получаем координаты по офсету от игрока ; get coords on offset from player
CAutomobile *automobile = reinterpret_cast<CAutomobile *>(SpawnVehicle(MODEL_INFERNUS, position, FindPlayerPed(-1)->m_fCurrentRotation + 1.5707964f)); // Создаём в этих координатах авто ; Create a car at these coords
if (automobile) { // если авто создано ; if car was created
m_nLastSpawnedTime = CTimer::m_snTimeInMilliseconds; // записываем новое время спавна ; store new spawning time
if (automobile->m_pRwClump) { // если создан графический обьект (RpClump) ; if graphical object was created (RpClump)
RwFrame *component = CClumpModelInfo::GetFrameFromName(automobile->m_pRwClump, (char*)"bonnet_dummy"); // находим компонент в иерархии ; find component in hierarchy
if (component) // если компонент есть в иерархии ; if component found
component->modelling.pos.z += 1.0f; // двигаем компонент вверх ; move component up

// Ещё один вариант управления компонентом - обращаемся к компонентам, которые записаны в структуре авто ; Another way to control components - work with components that are stored in car structure
if (automobile->m_aCarNodes[CAR_BUMP_FRONT]) // если компонент присутствует ; if component present
automobile->m_aCarNodes[CAR_BUMP_FRONT]->modelling.pos.z += 1.0f; // двигаем компонент вверх ; move component up
}
}
}
position.z += vehicle->GetHeightAboveRoad();
vehicle->SetOrientation(0.0f, 0.0f, heading);
vehicle->Teleport(position); // includes CWorld::Add

vehicle->m_nStatus = eEntityStatus::STATUS_ABANDONED;
vehicle->bEngineOn = false;
vehicle->m_eDoorLock = DOORLOCK_UNLOCKED;

switch (vehicle->m_nVehicleClass) // basic type
{
case VEHICLE_BIKE: // anything based on CBike class
reinterpret_cast<CBike*>(vehicle)->bOnSideStand = true;
reinterpret_cast<CBike*>(vehicle)->PlaceOnRoadProperly();
break;

case VEHICLE_AUTOMOBILE: // anything based on CAutomobile class
reinterpret_cast<CAutomobile*>(vehicle)->PlaceOnRoadProperly();
break;
}
}
vehicle->UpdateRwMatrix(); // make updated position visible right away

CreateCarExample() {
// Добавляем нашу функцию в gameProcessEvent; Add our function to gameProcessEvent
Events::gameProcessEvent.Add(ProcessSpawn); // Это то же, что и Events::gameProcessEvent += ProcessSpawn ; Same as Events::gameProcessEvent += ProcessSpawn
}
} example;
CTheScripts::ClearSpaceForMissionEntity(vehicle->GetPosition(), vehicle);

unsigned int CreateCarExample::m_nLastSpawnedTime = 0;
vehicle->ProcessControl(); // process suspension, ground brightness etc.

return vehicle;
}
} gInstance;
6 changes: 3 additions & 3 deletions examples/GPS/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ class GPS {
CRadar::TransformRadarPointToScreenSpace(posn, CVector2D(1.0f, 1.0f));
rect.right = static_cast<LONG>(posn.x - 2.0f);
rect.top = static_cast<LONG>(posn.y + 2.0f);
((IDirect3DDevice9*)GetD3DDevice)->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
((IDirect3DDevice9*)GetD3DDevice)->SetScissorRect(&rect);
((IDirect3DDevice9*)GetD3DDevice())->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
((IDirect3DDevice9*)GetD3DDevice())->SetScissorRect(&rect);
}

RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);
Expand Down Expand Up @@ -135,7 +135,7 @@ class GPS {
if (!FrontEndMenuManager.m_bDrawRadarOrMap
&& reinterpret_cast<D3DCAPS9 const*>(RwD3D9GetCaps())->RasterCaps & D3DPRASTERCAPS_SCISSORTEST)
{
((IDirect3DDevice9*)GetD3DDevice)->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
((IDirect3DDevice9*)GetD3DDevice())->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
}

gpsDistance += DistanceBetweenPoints(FindPlayerCoors(0), ThePaths.GetPathNode(resultNodes[0])->GetNodeCoors());
Expand Down
Loading
Loading