Skip to content
Merged
77 changes: 77 additions & 0 deletions examples/DecisionMaker/Main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include <plugin.h> // Plugin-SDK version 1002 from 2025-12-09 23:18:09
#include <CDecisionMakerTypes.h>
#include <CCivilianPed.h>
#include <CStreaming.h>
#include <CWorld.h>

using namespace plugin;

struct Main
{
eDecisionMakerType decisionMakerHandle = eDecisionMakerType::UNKNOWN;
CPed* ped = nullptr;

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

void OnGameRestart()
{
decisionMakerHandle = eDecisionMakerType::UNKNOWN; // game deletes the DM itself, just clear our reference
ped = nullptr; // game deletes the ped itself, just clear our reference
}

void OnGameProcess()
{
// create our decision maker if not exists yet
if (decisionMakerHandle == eDecisionMakerType::UNKNOWN)
{
// create decision maker
auto dmManager = CDecisionMakerTypes::GetInstance();
if (!dmManager) return; // try again later

CDecisionMaker templateDm; // empty
decisionMakerHandle = dmManager->AddDecisionMaker(&templateDm); // take note the game only has 10 slots for custom DMs

if (decisionMakerHandle == eDecisionMakerType::UNKNOWN) return; // failed to create DM, try again later

dmManager->AddEventResponse(decisionMakerHandle, eEventType::EVENT_GUN_AIMED_AT,
eTaskType::TASK_SIMPLE_HANDS_UP,
DecisionChances(4, 4, 4, 4), // 4/5 chance
DecisionContext(true, false)
);

dmManager->AddEventResponse(decisionMakerHandle, eEventType::EVENT_GUN_AIMED_AT,
eTaskType::TASK_SIMPLE_DUCK,
DecisionChances(1, 1, 1, 1), // 1/5 chance
DecisionContext(true, false)
);
}

// create ped
if (ped == nullptr)
{
constexpr auto MODEL = 70; // scientist
CStreaming::RequestModel(MODEL, eStreamingFlags::PRIORITY_REQUEST);
CStreaming::LoadAllRequestedModels(true);
ped = new CCivilianPed(PED_TYPE_CIVMALE, MODEL);

if (!ped) return; // failed to create ped, try again later

ped->SetPosn(FindPlayerPed()->TransformFromObjectSpace(CVector(0.0f, 3.0f, 0.0f))); // in front of the player
ped->SetOrientation(0.0f, 0.0f, 0.0f);
CWorld::Add(ped);
ped->PositionAnyPedOutOfCollision();

ped->m_fMaxHealth = ped->m_fHealth = 1000.0f; // stronger for tests

if (ped->m_pIntelligence)
{
ped->m_pIntelligence->SetPedDecisionMakerType(decisionMakerHandle);
}
}
}
} gInstance;
2 changes: 2 additions & 0 deletions examples/DecisionMaker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## Decision Maker
Shows how to create custom DecisionMaker and assign it to ped.
10 changes: 10 additions & 0 deletions examples/UnitTests/source/Test_CVector.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ UTEST(CVector, ctor_CVector2D)

// assignments

UTEST(CVector, Reset)
{
auto v = CVector(1.0f, 2.0f, 3.0f);

v.Reset();
EXPECT_EQ(v.x, 0.0f);
EXPECT_EQ(v.y, 0.0f);
EXPECT_EQ(v.z, 0.0f);
}

UTEST(CVector, Set_F)
{
auto v = CVector();
Expand Down
1 change: 1 addition & 0 deletions examples/examples.csv
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
PROJECT, TYPE, GTA2, GTA3, GTA-VC, GTA-SA, GTA4, DE-3, DE-VC, DE-SA, D3D
ColouredObjects, ASI, ---, ---, ---, YES, ---, ---, ---, ---, ---
CreateCar, ASI, ---, ---, ---, YES, ---, ---, ---, ---, ---
DecisionMaker, ASI, ---, ---, ---, YES, ---, ---, ---, ---, ---
DXFont, ASI, ---, YES, YES, YES, ---, ---, ---, ---, YES
FullNitrousControl, ASI, ---, ---, ---, YES, ---, ---, ---, ---, ---
GPS, ASI, ---, ---, ---, YES, ---, ---, ---, ---, YES
Expand Down
12 changes: 12 additions & 0 deletions plugin_sa/game_sa/CCollisionEventScanner.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
Plugin-SDK (Grand Theft Auto San Andreas) source file
Authors: GTA Community. See more here
https://github.com/DK22Pac/plugin-sdk
Do not delete this comment block. Respect others' work!
*/
#include "CCollisionEventScanner.h"

void CCollisionEventScanner::ScanForCollisionEvents(CPed* victim, CEventGroup* eventGroup)
{
plugin::CallMethod<0x604500, CCollisionEventScanner*, CPed*, CEventGroup*>(this, victim, eventGroup);
}
20 changes: 20 additions & 0 deletions plugin_sa/game_sa/CCollisionEventScanner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
Plugin-SDK (Grand Theft Auto San Andreas) header file
Authors: GTA Community. See more here
https://github.com/DK22Pac/plugin-sdk
Do not delete this comment block. Respect others' work!
*/
#pragma once

#include "PluginBase.h"

class CPed;
class CEventGroup;

class PLUGIN_API CCollisionEventScanner {
public:
bool m_bAlreadyHitByCar;

void ScanForCollisionEvents(CPed* victim, CEventGroup* eventGroup);
};
VALIDATE_SIZE(CCollisionEventScanner, 0x1);
11 changes: 11 additions & 0 deletions plugin_sa/game_sa/CDecision.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
Plugin-SDK (Grand Theft Auto San Andreas) source file
Authors: GTA Community. See more here
https://github.com/DK22Pac/plugin-sdk
Do not delete this comment block. Respect others' work!
*/
#include "CDecision.h"

CDecision::CDecision() {
plugin::CallMethod<0x6040C0, CDecision*>(this);
}
50 changes: 31 additions & 19 deletions plugin_sa/game_sa/CDecision.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,44 @@
*/
#pragma once
#include "PluginBase.h"
#include "eTaskType.h"

/*
https://www.gtamodding.com/wiki/Decision_Maker
*/
struct DecisionContext
{
bool onFoot = false;
bool inVehicle = false;

enum eDecisionTypes {
DECISION_ON_FOOT = 0,
DECISION_IN_VEHICLE = 1
DecisionContext() = default;
DecisionContext(bool onFoot, bool inVehicle) :
onFoot(onFoot), inVehicle(inVehicle)
{}
};
VALIDATE_SIZE(DecisionContext, 0x2);

struct DecisionChances
{
// weights that sums up to total value of all responses applicable to current event,
// then chance is weight/total
unsigned char toNeutral = 0;
unsigned char toPlayer = 0;
unsigned char toFriend = 0;
unsigned char toEnemy = 0;

enum eDecisionRelationship {
DECISION_RELATIONSHIP_NEUTRAL = 0,
DECISION_RELATIONSHIP_PLAYER = 1,
DECISION_RELATIONSHIP_FRIEND = 2,
DECISION_RELATIONSHIP_THREAT = 3
DecisionChances() = default;
DecisionChances(unsigned char toNeutral, unsigned char toPlayer, unsigned char toFriend, unsigned char toEnemy) :
toNeutral(toNeutral), toPlayer(toPlayer), toFriend(toFriend), toEnemy(toEnemy)
{}
};
VALIDATE_SIZE(DecisionChances, 0x4);

class PLUGIN_API CDecision {
public:
int m_anTaskTypes[6]; // see eTaskType
unsigned char m_anResponseChances[6][4]; // 4 different relationships : see eDecisionRelationship
unsigned char m_anTypeFlags[2][6]; // 2 different types : see eDecisionTypes
constexpr static auto RESPONSE_COUNT = 6; // max count of unique responses

inline CDecision() { // @0x6040C0
//SetDefault();
}
};
eTaskType task[RESPONSE_COUNT]; // response's task
DecisionChances chances[RESPONSE_COUNT]; // response's chances for each relationship type
DecisionContext context[RESPONSE_COUNT]; // situations the response applies to

VALIDATE_SIZE(CDecision, 0x3C);
CDecision();
};
VALIDATE_SIZE(CDecision, 0x3C);
11 changes: 11 additions & 0 deletions plugin_sa/game_sa/CDecisionMaker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
Plugin-SDK (Grand Theft Auto San Andreas) source file
Authors: GTA Community. See more here
https://github.com/DK22Pac/plugin-sdk
Do not delete this comment block. Respect others' work!
*/
#include "CDecisionMaker.h"

CDecisionMaker::CDecisionMaker() {
plugin::CallMethod<0x4650A0, CDecisionMaker*>(this);
}
11 changes: 3 additions & 8 deletions plugin_sa/game_sa/CDecisionMaker.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,8 @@

class PLUGIN_API CDecisionMaker {
public:
CDecision m_decision[DM_TOTAL_NUM_EVENTS];
CDecision m_decision[eDecisionMakerEvents::DM_TOTAL_NUM_EVENTS];

inline CDecisionMaker() { // @0x4650A0
for (unsigned int i = 0; i < DM_TOTAL_NUM_EVENTS; ++i) {
//m_decision[i].SetDefault();
}
}
CDecisionMaker();
};

VALIDATE_SIZE(CDecisionMaker, 0x99C);
VALIDATE_SIZE(CDecisionMaker, 0x99C);
46 changes: 46 additions & 0 deletions plugin_sa/game_sa/CDecisionMakerTypes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
Plugin-SDK (Grand Theft Auto San Andreas) source file
Authors: GTA Community. See more here
https://github.com/DK22Pac/plugin-sdk
Do not delete this comment block. Respect others' work!
*/
#include "CDecisionMakerTypes.h"

CDecisionMakerTypes* CDecisionMakerTypes::GetInstance() {
return plugin::CallAndReturn<CDecisionMakerTypes*, 0x4684F0>();
}

eDecisionMakerType CDecisionMakerTypes::AddDecisionMaker(CDecisionMaker* templateDm, eDecisionMakerType dm, bool bDecisionMakerForMission) {
return plugin::CallMethodAndReturn<eDecisionMakerType, 0x607050, CDecisionMakerTypes*, CDecisionMaker*, eDecisionMakerType, bool>(this, templateDm, dm, bDecisionMakerForMission);
}

void CDecisionMakerTypes::RemoveDecisionMaker(eDecisionMakerType dm) {
return plugin::Call<0x6043A0>(dm);
}

void CDecisionMakerTypes::AddEventResponse(eDecisionMakerType dm, eEventType eventType, eTaskType taskId, DecisionChances chances, DecisionContext context) {
float chancesF[4]; // internally converted back to byte
chancesF[0] = chances.toNeutral;
chancesF[1] = chances.toPlayer;
chancesF[2] = chances.toFriend;
chancesF[3] = chances.toEnemy;

plugin::CallMethod<0x6044C0, CDecisionMakerTypes*, eDecisionMakerType, eEventType, eTaskType, float*, DecisionContext&>(this, dm, eventType, taskId, chancesF, context);
}

void CDecisionMakerTypes::FlushDecisionMakerEventResponse(eDecisionMakerType dm, eEventType eventId) {
plugin::CallMethod<0x604490>(this, dm, eventId);
}

eTaskType CDecisionMakerTypes::MakeDecision(CPedGroup* pedGroup, eEventType eventType, int eventSourceType, bool bIsPedInVehicle, eTaskType taskId1, eTaskType taskId2, eTaskType taskId3, eTaskType taskId4) {
return plugin::CallMethodAndReturn<eTaskType, 0x606F80, CDecisionMakerTypes*, CPedGroup*, int, int, bool, int, int, int, int>(
this, pedGroup, eventType, eventSourceType, bIsPedInVehicle, taskId1, taskId2, taskId3, taskId4);
}

void CDecisionMakerTypes::MakeDecision(CPed* ped, eEventType eventType, int eventSourceType, bool bIsPedInVehicle, eTaskType taskTypeToAvoid1, eTaskType taskTypeToAvoid2, eTaskType taskTypeToAvoid3, eTaskType taskTypeToSeek, bool bUseInGroupDecisionMaker, short& taskType, short& facialTaskType) {
plugin::CallMethod<0x606E70>(this, ped, eventType, eventSourceType, bIsPedInVehicle, taskTypeToAvoid1, taskTypeToAvoid2, taskTypeToAvoid3, taskTypeToSeek, bUseInGroupDecisionMaker, &taskType, &facialTaskType);
}

void CDecisionMakerTypes::LoadEventIndices() {
plugin::CallMethod<0x600840>(this);
}
76 changes: 76 additions & 0 deletions plugin_sa/game_sa/CDecisionMakerTypes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
Plugin-SDK (Grand Theft Auto San Andreas) header file
Authors: GTA Community. See more here
https://github.com/DK22Pac/plugin-sdk
Do not delete this comment block. Respect others' work!
*/
#pragma once

#include "CDecision.h"
#include "CDecisionMaker.h"
#include "eEventType.h"
#include "eTaskType.h"

class CPed;
class CPedGroup;

enum class PLUGIN_API eDecisionMakerType : int {
UNKNOWN = -1,

PED_GROUPMEMBER,
PED_COP,
PED_RANDOM1,
PED_RANDOM2,
PED_RANDOM3,
PED_FIREMAN,
PED_EMPTY,
PED_INDOORS,

GROUP_RANDOM_AGGRESSIVE,
GROUP_RANDOM_PASSIVE,

MISSION0,
MISSION1,
MISSION2,
MISSION3,
MISSION4,
MISSION5,
MISSION6,
MISSION7,
MISSION8,
MISSION9,

COUNT_TOTAL,
COUNT_GAME_DM = MISSION0, // Number of built-in decision makers
};

class CDecisionMakerTypes {
public:
static constexpr auto NUM_TYPES = 20u;

static inline auto& ScriptReferenceIndex = *(std::array<unsigned short, NUM_TYPES>*)0xC0AFF4;
static inline auto& m_IsActive = *(std::array<bool, NUM_TYPES>*)0xC0B01C;

static CDecisionMakerTypes* GetInstance(); // get global Decision Maker manager

eDecisionMakerType AddDecisionMaker(CDecisionMaker* templateDm, eDecisionMakerType dm = eDecisionMakerType::UNKNOWN, bool bDecisionMakerForMission = false);
void RemoveDecisionMaker(eDecisionMakerType dm);

void AddEventResponse(eDecisionMakerType dm, eEventType eventType, eTaskType taskId, DecisionChances chances, DecisionContext context);
void FlushDecisionMakerEventResponse(eDecisionMakerType dm, eEventType eventId);

eTaskType MakeDecision(CPedGroup* pedGroup, eEventType eventType, int eventSourceType, bool bIsPedInVehicle, eTaskType taskId1, eTaskType taskId2, eTaskType taskId3, eTaskType taskId4);
void MakeDecision(CPed* ped, eEventType eventType, int eventSourceType, bool bIsPedInVehicle, eTaskType taskTypeToAvoid1, eTaskType taskTypeToAvoid2, eTaskType taskTypeToAvoid3, eTaskType taskTypeToSeek, bool bUseInGroupDecisionMaker, short& taskType, short& facialTaskType);

void LoadEventIndices();

public:
int m_NoOfDecisionMakers;
CDecisionMaker m_DecisionMakers[(size_t)eDecisionMakerType::COUNT_TOTAL];
int m_EventIndices[(size_t)eEventType::EVENT_TOTAL_NUM_EVENTS];
CDecisionMaker m_DefaultRandomPedDecisionMaker;
CDecisionMaker m_DefaultMissionPedDecisionMaker;
CDecisionMaker m_DefaultPlayerPedDecisionMaker;
CDecisionMaker m_DefaultRandomPedGroupDecisionMaker;
CDecisionMaker m_DefaultMissionPedGroupDecisionMaker;
};
17 changes: 17 additions & 0 deletions plugin_sa/game_sa/CMentalState.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
Plugin-SDK (Grand Theft Auto San Andreas) source file
Authors: GTA Community. See more here
https://github.com/DK22Pac/plugin-sdk
Do not delete this comment block. Respect others' work!
*/
#include "CMentalState.h"

// 0x421050
void CMentalState::IncrementAnger(int anger) {
plugin::CallMethod<0x421050, CMentalState*, int>(this, anger);
}

// 0x6008A0
void CMentalState::Process() {
plugin::CallMethod<0x6008A0, CMentalState*>(this);
}
Loading