SurvivorBot::UpdateEscapePath
0x00941670 · 4343 bytes · __thiscall
_ZN11SurvivorBot16UpdateEscapePathEv
Decompiled
undefined (1 param)
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */
/* SurvivorBot::UpdateEscapePath() */
void __thiscall SurvivorBot::UpdateEscapePath(SurvivorBot *this)
{
PathFollower *this_00;
INextBot *this_01;
uint *puVar1;
float fVar2;
CNavArea CVar3;
uint uVar4;
char cVar5;
bool bVar6;
int *piVar7;
TerrorNavArea *pTVar8;
TerrorNavArea *pTVar9;
int iVar10;
Vector *pVVar11;
int iVar12;
Vector *pVVar13;
CNavArea *pCVar14;
CNavArea *pCVar15;
CBaseEntity *pCVar16;
undefined4 uVar17;
Vector *pVVar18;
undefined *puVar19;
SurvivorBot *pSVar20;
TerrorNavArea *this_02;
longdouble lVar21;
bool local_85;
TerrorNavArea *local_70;
CNavArea *local_60;
undefined4 local_5c;
undefined4 local_58;
undefined4 local_50;
undefined4 local_4c;
int local_48;
float local_44;
float local_40;
float local_3c;
undefined4 local_38;
undefined4 local_34;
undefined4 local_30;
SurvivorBot *local_2c;
TerrorNavArea *local_28;
undefined4 local_24;
undefined1 local_20;
undefined1 local_1f;
lVar21 = (longdouble)CountdownTimer::Now();
if ((float)lVar21 < *(float *)(this + 0x1aa80)) {
return;
}
piVar7 = (int *)(**(code **)(*(int *)this + 0x9a0))(this);
cVar5 = (**(code **)(*piVar7 + 0x118))(piVar7);
if (cVar5 != '\0') {
return;
}
piVar7 = (int *)(**(code **)(*(int *)this + 0x9a0))(this);
cVar5 = (**(code **)(*piVar7 + 200))(piVar7);
if (cVar5 != '\0') {
return;
}
lVar21 = (longdouble)RandomFloat(0x3f800000,0x40000000);
fVar2 = (float)lVar21;
lVar21 = (longdouble)CountdownTimer::Now();
if ((float)lVar21 + fVar2 != *(float *)(this + 0x1aa80)) {
(**(code **)(*(int *)(this + 0x1aa78) + 4))(this + 0x1aa78,this + 0x1aa80);
*(float *)(this + 0x1aa80) = (float)lVar21 + fVar2;
}
if (fVar2 != *(float *)(this + 0x1aa7c)) {
(**(code **)(*(int *)(this + 0x1aa78) + 4))(this + 0x1aa78,this + 0x1aa7c);
*(float *)(this + 0x1aa7c) = fVar2;
}
pTVar8 = (TerrorNavArea *)(**(code **)(*(int *)this + 0x52c))(this);
this_00 = (PathFollower *)(this + 0x16298);
if (pTVar8 == (TerrorNavArea *)0x0) {
PathFollower::Invalidate(this_00);
cVar5 = INextBot::IsDebugging((INextBot *)(this + 0x4028),4);
if (cVar5 == '\0') {
return;
}
uVar17 = (**(code **)(*(int *)this + 0x9b8))(this);
DevMsg("%3.2f: %s: UpdateEscapePath failed: No last known area.\n",
(double)*(float *)(gpGlobals + 0xc),uVar17);
return;
}
if ((0 < *(int *)(this + 0x1a69c)) &&
(cVar5 = (**(code **)(*(int *)(this + 0x16298) + 0x44))(this_00), cVar5 != '\0')) {
cVar5 = (**(code **)(*(int *)(this + 0x16298) + 0x44))(this_00);
pSVar20 = (SurvivorBot *)0x0;
if (cVar5 != '\0') {
pSVar20 = this + 0x1629c;
}
if (pTVar8 == *(TerrorNavArea **)pSVar20) {
return;
}
}
this_01 = (INextBot *)(this + 0x4028);
cVar5 = INextBot::IsDebugging(this_01,4);
if (cVar5 != '\0') {
uVar17 = (**(code **)(*(int *)this + 0x9b8))(this);
DevMsg("%3.2f: %s: UpdateEscapePath REPATH.\n",(double)*(float *)(gpGlobals + 0xc),uVar17);
}
CNavArea::m_masterMarker = CNavArea::m_masterMarker + 1;
if (CNavArea::m_masterMarker == 0) {
CNavArea::m_masterMarker = 1;
}
*(int *)(pTVar8 + 0x3c) = CNavArea::m_masterMarker;
local_70 = (TerrorNavArea *)0x0;
this_02 = pTVar8;
while (((pTVar9 = (TerrorNavArea *)
TerrorNavArea::GetNextEscapeStep(this_02,(NavTraverseType *)0x0),
pTVar9 != (TerrorNavArea *)0x0 && (*(int *)(pTVar9 + 0x3c) != CNavArea::m_masterMarker))
&& ((float)local_70 < 2000.0))) {
piVar7 = (int *)(**(code **)(*(int *)this + 0x9a0))(this);
cVar5 = (**(code **)(*piVar7 + 0x150))(piVar7,pTVar9);
if (cVar5 == '\0') break;
*(int *)(pTVar9 + 0x3c) = CNavArea::m_masterMarker;
local_70 = (TerrorNavArea *)
(SQRT((*(float *)(pTVar9 + 0x30) - *(float *)(this_02 + 0x30)) *
(*(float *)(pTVar9 + 0x30) - *(float *)(this_02 + 0x30)) +
(*(float *)(pTVar9 + 0x2c) - *(float *)(this_02 + 0x2c)) *
(*(float *)(pTVar9 + 0x2c) - *(float *)(this_02 + 0x2c)) +
(*(float *)(pTVar9 + 0x34) - *(float *)(this_02 + 0x34)) *
(*(float *)(pTVar9 + 0x34) - *(float *)(this_02 + 0x34))) + (float)local_70);
this_02 = pTVar9;
}
local_70 = this_02;
if (this_02 == pTVar8) {
local_2c = this;
local_28 = (TerrorNavArea *)0x0;
local_24 = 0;
iVar10 = CBaseEntity::GetTeamNumber((CBaseEntity *)this);
if (((byte)this[0x14d] & 8) != 0) {
CBaseEntity::CalcAbsolutePosition((CBaseEntity *)this);
}
SearchSurroundingAreas<HighestFlowAreaScan>
((CNavArea *)this_02,(Vector *)(this + 0x2e0),(HighestFlowAreaScan *)&local_2c,1000.0,
0,iVar10);
local_70 = local_28;
if (local_28 == (TerrorNavArea *)0x0) {
local_70 = this_02;
}
}
local_2c = (SurvivorBot *)&PTR_operator___00d09770;
local_28 = (TerrorNavArea *)this_01;
local_24 = (**(code **)(*(int *)(this + 0x4028) + 0xc0))(this_01);
piVar7 = (int *)(**(code **)(*(int *)(this + 0x4028) + 200))(this_01);
iVar10 = (**(code **)(*piVar7 + 0xb8))(piVar7,this_01);
local_1f = 0;
local_20 = iVar10 != 0;
local_5c = 0;
local_58 = 0;
pVVar11 = (Vector *)(local_70 + 0x2c);
if (_towlower == 0) {
local_48 = 0;
}
else {
(**(code **)(_towlower + 0x50))
(_towlower,&local_5c,0,0,0,0,"/data/src/game/server/nextbot/path/nextbotpath.h",0x113,
&Path::
Compute<SurvivorBotPathCost>(INextBot*,Vector_const&,SurvivorBotPathCost&,float)::
tm_fmt,"(%s)%s","NextBotSpiky","Path::Compute(goal)");
local_48 = _towlower;
}
iVar10 = _DAT_0105db00;
local_50 = local_5c;
local_4c = local_58;
/* try { // try from 009419bd to 009419c1 has its CatchHandler @ 00942785 */
if (((_DAT_0105d154 != 0) || (DAT_0105d158 == '\0')) &&
(iVar12 = ThreadGetCurrentId(), iVar10 == iVar12)) {
if ((char *)*_DAT_0105d15c != "Path::Compute(goal)") {
/* try { // try from 00941fe5 to 00942008 has its CatchHandler @ 00942785 */
_DAT_0105d15c =
(int *)CVProfNode::GetSubNode((char *)_DAT_0105d15c,0xbfa1b7,(char *)0x0,0xbfa1cb);
}
puVar1 = (uint *)(_DAT_0105d1e8 + 4 + _DAT_0105d15c[0x1c] * 8);
*puVar1 = *puVar1 | 4;
CVProfNode::EnterScope();
DAT_0105d158 = '\0';
}
/* try { // try from 009419d6 to 00941c97 has its CatchHandler @ 0094279a */
(**(code **)(*(int *)(this + 0x16298) + 0x48))(this_00);
pVVar13 = (Vector *)(**(code **)(*(int *)(this + 0x4028) + 0xd4))(this_01);
piVar7 = (int *)(**(code **)(*(int *)(this + 0x4028) + 0xb4))(this_01);
pCVar14 = (CNavArea *)(**(code **)(*piVar7 + 0x52c))(piVar7);
local_85 = false;
if (pCVar14 == (CNavArea *)0x0) {
/* try { // try from 00942030 to 0094205a has its CatchHandler @ 0094279a */
(**(code **)(*(int *)(this + 0x16298) + 100))(this_00,this_01);
}
else {
pCVar15 = (CNavArea *)CNavMesh::GetNearestNavArea(TheNavMesh,pVVar11,true,200.0,true,true,false)
;
if (pCVar14 == pCVar15) {
/* try { // try from 00942151 to 00942155 has its CatchHandler @ 0094279a */
Path::BuildTrivialPath((Path *)this_00,this_01,pVVar11);
local_85 = true;
}
else {
local_44 = *(float *)(local_70 + 0x2c);
local_40 = *(float *)(local_70 + 0x30);
local_3c = *(float *)(local_70 + 0x34);
if (pCVar15 == (CNavArea *)0x0) {
CNavMesh::GetGroundHeight(TheNavMesh,(Vector *)&local_44,&local_3c,(Vector *)0x0);
}
else {
lVar21 = (longdouble)CNavArea::GetZ(pCVar15,local_44,local_40);
local_3c = (float)lVar21;
}
pCVar16 = (CBaseEntity *)(**(code **)(*(int *)(this + 0x4028) + 0xb4))(this_01);
if (((byte)pCVar16[0x14d] & 8) != 0) {
CBaseEntity::CalcAbsolutePosition(pCVar16);
}
local_38 = *(undefined4 *)(pCVar16 + 0x2e0);
local_34 = *(undefined4 *)(pCVar16 + 0x2e4);
local_30 = *(undefined4 *)(pCVar16 + 0x2e8);
local_60 = (CNavArea *)0x0;
pCVar16 = (CBaseEntity *)(**(code **)(*(int *)(this + 0x4028) + 0xb4))(this_01);
iVar10 = CBaseEntity::GetTeamNumber(pCVar16);
bVar6 = NavAreaBuildPath<SurvivorBotPathCost>
(pCVar14,pCVar15,(Vector *)&local_38,pVVar11,
(SurvivorBotPathCost *)&local_2c,&local_60,0.0,iVar10,false);
if (local_60 != (CNavArea *)0x0) {
if (pCVar14 == local_60) {
LAB_00942077:
/* try { // try from 0094208b to 0094208f has its CatchHandler @ 0094279a */
Path::BuildTrivialPath((Path *)this_00,this_01,pVVar11);
local_85 = bVar6;
}
else {
iVar10 = 1;
pCVar15 = local_60;
do {
pCVar15 = *(CNavArea **)(pCVar15 + 0x78);
if (pCVar15 == (CNavArea *)0x0) break;
iVar10 = iVar10 + 1;
} while (pCVar14 != pCVar15);
if (iVar10 < 0x100) {
if (iVar10 == 1) goto LAB_00942077;
*(int *)(this + 0x1a69c) = iVar10;
}
else {
*(undefined4 *)(this + 0x1a69c) = 0xff;
iVar10 = 0xff;
}
pCVar14 = local_60;
iVar12 = iVar10;
pSVar20 = this + (iVar10 + -1) * 0x44 + 0x1629c;
do {
*(CNavArea **)pSVar20 = pCVar14;
iVar12 = iVar12 + -1;
CVar3 = pCVar14[0x7c];
*(undefined4 *)(pSVar20 + 0x18) = 0;
*(uint *)(pSVar20 + 4) = (byte)CVar3 & 0xf;
pCVar14 = *(CNavArea **)(pCVar14 + 0x78);
if (pCVar14 == (CNavArea *)0x0) break;
pSVar20 = pSVar20 + -0x44;
} while (iVar12 != 0);
iVar10 = iVar10 * 0x44;
*(CNavArea **)(this + iVar10 + 0x1629c) = local_60;
*(float *)(this_00 + iVar10 + 0xc) = local_44;
*(float *)(this_00 + iVar10 + 0x10) = local_40;
*(float *)(this_00 + iVar10 + 0x14) = local_3c;
iVar10 = *(int *)(this + 0x1a69c);
*(undefined4 *)(this + iVar10 * 0x44 + 0x162b0) = 0;
*(undefined4 *)(this + iVar10 * 0x44 + 0x162a0) = 9;
*(undefined4 *)(this + iVar10 * 0x44 + 0x162b4) = 0;
*(int *)(this + 0x1a69c) = iVar10 + 1;
cVar5 = Path::ComputePathDetails((Path *)this_00,this_01,pVVar13);
if (cVar5 == '\0') {
(**(code **)(*(int *)(this + 0x16298) + 0x48))(this_00);
(**(code **)(*(int *)(this + 0x16298) + 100))(this_00,this_01);
}
else {
/* try { // try from 00941eed to 00941f1a has its CatchHandler @ 0094279a */
Path::Optimize((INextBot *)this_00);
Path::PostProcess((Path *)this_00);
(**(code **)(*(int *)(this + 0x16298) + 100))(this_00,this_01);
local_85 = bVar6;
}
}
}
}
}
iVar10 = _DAT_0105db00;
if (((DAT_0105d158 == '\0') || (_DAT_0105d154 != 0)) &&
(iVar12 = ThreadGetCurrentId(), iVar10 == iVar12)) {
cVar5 = CVProfNode::ExitScope();
if (cVar5 != '\0') {
_DAT_0105d15c = (int *)_DAT_0105d15c[0x19];
}
DAT_0105d158 = _DAT_0105d15c == (int *)0x105d160;
}
if (local_48 != 0) {
(**(code **)(local_48 + 0x54))(local_48,local_50,local_4c,0,0,0);
}
if (local_85 != false) goto LAB_00941cfe;
cVar5 = INextBot::IsDebugging(this_01,4);
if (cVar5 != '\0') {
uVar17 = (**(code **)(*(int *)this + 0x9b8))(this);
DevMsg("%3.2f: %s: UpdateEscapePath failed: Pathfind failed.\n",
(double)*(float *)(gpGlobals + 0xc),uVar17);
}
iVar10 = GetTeamSituation(this);
uVar4 = *(uint *)(iVar10 + 0x54);
if ((((uVar4 == 0xffffffff) ||
(puVar19 = g_pEntityList + (uVar4 & 0xfff) * 0x10, puVar19 == (undefined *)0xfffffffc)) ||
(*(uint *)(puVar19 + 8) != uVar4 >> 0xc)) ||
(pCVar16 = *(CBaseEntity **)(puVar19 + 4), pCVar16 == (CBaseEntity *)0x0)) {
cVar5 = INextBot::IsDebugging(this_01,4);
if (cVar5 != '\0') {
uVar17 = (**(code **)(*(int *)this + 0x9b8))(this);
DevMsg("%3.2f: %s: UpdateEscapePath failed: Pathfind to failed, but using it anyway.\n",
(double)*(float *)(gpGlobals + 0xc),uVar17);
}
goto LAB_00941cfe;
}
if (((byte)pCVar16[0x14d] & 8) != 0) {
CBaseEntity::CalcAbsolutePosition(pCVar16);
}
pVVar13 = (Vector *)(pCVar16 + 0x2e0);
local_5c = 0;
local_58 = 0;
if (_towlower == 0) {
local_48 = 0;
}
else {
(**(code **)(_towlower + 0x50))
(_towlower,&local_5c,0,0,0,0,"/data/src/game/server/nextbot/path/nextbotpath.h",0x113,
&Path::
Compute<SurvivorBotPathCost>(INextBot*,Vector_const&,SurvivorBotPathCost&,float)::
tm_fmt,"(%s)%s","NextBotSpiky","Path::Compute(goal)");
local_48 = _towlower;
}
iVar10 = _DAT_0105db00;
local_50 = local_5c;
local_4c = local_58;
/* try { // try from 0094222d to 00942231 has its CatchHandler @ 009427aa */
if (((_DAT_0105d154 != 0) || (DAT_0105d158 == '\0')) &&
(iVar12 = ThreadGetCurrentId(), iVar10 == iVar12)) {
if ((char *)*_DAT_0105d15c != "Path::Compute(goal)") {
/* try { // try from 009426ad to 009426d0 has its CatchHandler @ 009427aa */
_DAT_0105d15c =
(int *)CVProfNode::GetSubNode((char *)_DAT_0105d15c,0xbfa1b7,(char *)0x0,0xbfa1cb);
}
puVar1 = (uint *)(_DAT_0105d1e8 + 4 + _DAT_0105d15c[0x1c] * 8);
*puVar1 = *puVar1 | 4;
CVProfNode::EnterScope();
DAT_0105d158 = '\0';
}
/* try { // try from 00942246 to 00942522 has its CatchHandler @ 009427ac */
(**(code **)(*(int *)(this + 0x16298) + 0x48))(this_00);
pVVar18 = (Vector *)(**(code **)(*(int *)(this + 0x4028) + 0xd4))(this_01);
piVar7 = (int *)(**(code **)(*(int *)(this + 0x4028) + 0xb4))(this_01);
pCVar14 = (CNavArea *)(**(code **)(*piVar7 + 0x52c))(piVar7);
if (pCVar14 == (CNavArea *)0x0) {
/* try { // try from 009426f8 to 00942722 has its CatchHandler @ 009427ac */
(**(code **)(*(int *)(this + 0x16298) + 100))(this_00,this_01);
}
else {
pCVar15 = (CNavArea *)CNavMesh::GetNearestNavArea(TheNavMesh,pVVar13,true,200.0,true,true,false)
;
if (pCVar14 == pCVar15) {
Path::BuildTrivialPath((Path *)this_00,this_01,pVVar13);
local_85 = true;
}
else {
local_44 = *(float *)(pCVar16 + 0x2e0);
local_40 = *(float *)(pCVar16 + 0x2e4);
local_3c = *(float *)(pCVar16 + 0x2e8);
if (pCVar15 == (CNavArea *)0x0) {
CNavMesh::GetGroundHeight(TheNavMesh,(Vector *)&local_44,&local_3c,(Vector *)0x0);
}
else {
lVar21 = (longdouble)CNavArea::GetZ(pCVar15,local_44,local_40);
local_3c = (float)lVar21;
}
pCVar16 = (CBaseEntity *)(**(code **)(*(int *)(this + 0x4028) + 0xb4))(this_01);
if (((byte)pCVar16[0x14d] & 8) != 0) {
CBaseEntity::CalcAbsolutePosition(pCVar16);
}
local_38 = *(undefined4 *)(pCVar16 + 0x2e0);
local_34 = *(undefined4 *)(pCVar16 + 0x2e4);
local_30 = *(undefined4 *)(pCVar16 + 0x2e8);
local_60 = (CNavArea *)0x0;
pCVar16 = (CBaseEntity *)(**(code **)(*(int *)(this + 0x4028) + 0xb4))(this_01);
iVar10 = CBaseEntity::GetTeamNumber(pCVar16);
bVar6 = NavAreaBuildPath<SurvivorBotPathCost>
(pCVar14,pCVar15,(Vector *)&local_38,pVVar13,
(SurvivorBotPathCost *)&local_2c,&local_60,0.0,iVar10,false);
if (local_60 != (CNavArea *)0x0) {
if (pCVar14 == local_60) {
LAB_00942735:
/* try { // try from 00942749 to 00942778 has its CatchHandler @ 009427ac */
Path::BuildTrivialPath((Path *)this_00,this_01,pVVar13);
local_85 = bVar6;
}
else {
iVar10 = 1;
pCVar15 = local_60;
do {
pCVar15 = *(CNavArea **)(pCVar15 + 0x78);
if (pCVar15 == (CNavArea *)0x0) break;
iVar10 = iVar10 + 1;
} while (pCVar14 != pCVar15);
if (iVar10 < 0x100) {
if (iVar10 == 1) goto LAB_00942735;
*(int *)(this + 0x1a69c) = iVar10;
}
else {
*(undefined4 *)(this + 0x1a69c) = 0xff;
iVar10 = 0xff;
}
pCVar14 = local_60;
iVar12 = iVar10;
pSVar20 = this + (iVar10 + -1) * 0x44 + 0x1629c;
do {
*(CNavArea **)pSVar20 = pCVar14;
iVar12 = iVar12 + -1;
CVar3 = pCVar14[0x7c];
*(undefined4 *)(pSVar20 + 0x18) = 0;
*(uint *)(pSVar20 + 4) = (byte)CVar3 & 0xf;
pCVar14 = *(CNavArea **)(pCVar14 + 0x78);
if (pCVar14 == (CNavArea *)0x0) break;
pSVar20 = pSVar20 + -0x44;
} while (iVar12 != 0);
iVar10 = iVar10 * 0x44;
*(CNavArea **)(this + iVar10 + 0x1629c) = local_60;
*(float *)(this_00 + iVar10 + 0xc) = local_44;
*(float *)(this_00 + iVar10 + 0x10) = local_40;
*(float *)(this_00 + iVar10 + 0x14) = local_3c;
iVar10 = *(int *)(this + 0x1a69c);
*(undefined4 *)(this + iVar10 * 0x44 + 0x162b0) = 0;
*(undefined4 *)(this + iVar10 * 0x44 + 0x162a0) = 9;
*(undefined4 *)(this + iVar10 * 0x44 + 0x162b4) = 0;
*(int *)(this + 0x1a69c) = iVar10 + 1;
cVar5 = Path::ComputePathDetails((Path *)this_00,this_01,pVVar18);
if (cVar5 == '\0') {
(**(code **)(*(int *)(this + 0x16298) + 0x48))(this_00);
(**(code **)(*(int *)(this + 0x16298) + 100))(this_00,this_01);
}
else {
/* try { // try from 0094260e to 0094263b has its CatchHandler @ 009427ac */
Path::Optimize((INextBot *)this_00);
Path::PostProcess((Path *)this_00);
(**(code **)(*(int *)(this + 0x16298) + 100))(this_00,this_01);
local_85 = bVar6;
}
}
}
}
}
iVar10 = _DAT_0105db00;
if (((DAT_0105d158 == '\0') || (_DAT_0105d154 != 0)) &&
(iVar12 = ThreadGetCurrentId(), iVar10 == iVar12)) {
cVar5 = CVProfNode::ExitScope();
if (cVar5 != '\0') {
_DAT_0105d15c = (int *)_DAT_0105d15c[0x19];
}
DAT_0105d158 = _DAT_0105d15c == (int *)0x105d160;
}
if (local_48 != 0) {
(**(code **)(local_48 + 0x54))(local_48,local_50,local_4c,0,0,0);
}
if (local_85 == false) {
PathFollower::Invalidate(this_00);
cVar5 = INextBot::IsDebugging(this_01,4);
if (cVar5 != '\0') {
uVar17 = (**(code **)(*(int *)this + 0x9b8))(this);
DevMsg("%3.2f: %s: UpdateEscapePath failed: Pathfind to leader human also failed.\n",
(double)*(float *)(gpGlobals + 0xc),uVar17);
}
}
LAB_00941cfe:
if (*(int *)(SurvivorBotDebugTeamMovement._28_4_ + 0x30) != 0) {
fVar2 = *(float *)(this + 0x1aa80);
lVar21 = (longdouble)CountdownTimer::Now();
if (((byte)this[0x14d] & 8) != 0) {
CBaseEntity::CalcAbsolutePosition((CBaseEntity *)this);
}
NDebugOverlay::Line((Vector *)(this + 0x2e0),pVVar11,0,0xff,0,true,fVar2 - (float)lVar21);
(**(code **)(*(int *)local_70 + 0x50))(local_70);
}
return;
}
SourceMod gamedata
paste intoaddons/sourcemod/gamedata/<your_plugin>.txt
used as the SourceMod key — change to fit your plugin's convention
SourceMod library name (server / engine / matchmaking)
Just a Signatures{} block, ready to drop into a gamedata
file. Wire it up in your plugin however you like - SDKCall, DHooks,
raw memory ops, or anything else.
Signatures + Functions block. The plugin uses
DHookCreateFromConf - DHooks reads return type, this-type,
calling convention, and argument types straight from the gamedata. Less
boilerplate in the plugin, types travel with the gamedata.
Gamedata KeyValues
DHooks plugin example
Type inference is best-effort from the C signature - sanity-check the DHooks types before shipping.