This commit is contained in:
ineed bots
2023-04-19 21:44:24 -06:00
9 changed files with 1230 additions and 118 deletions

522
src/component/ai.cpp Normal file
View File

@ -0,0 +1,522 @@
#include <stdinc.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "component/gsc.hpp"
#include <json.hpp>
#include <utils/io.hpp>
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace ai
{
namespace
{
/*
int __cdecl Path_AStarAlgorithm_CustomSearchInfo_FindPath_(path_t *pPath, team_t eTeam, const float *vStartPos, pathnode_t *pNodeFrom, const float *vGoalPos, int bAllowNegotiationLinks, CustomSearchInfo_FindPath *searchInfo, int bIgnoreBadplaces, pathnode_t *nodeTo)
{
float *returnVGoaPosl; // edx
int nodeLinkIndex; // ebx
pathnode_t *returnStartNode; // ecx
pathnode_t *returnEndNode; // edi
pathlink_s *endNodeLinks; // eax
int nodeNum; // esi
float *endNodeLinkDist; // eax
pathnode_t *pSuccessor; // esi
float v17; // xmm0_4
pathnode_t *v18; // eax
pathnode_t *v19; // eax
float v20; // xmm1_4
long double v21; // st7
float v22; // xmm3_4
float v23; // xmm0_4
float v24; // xmm1_4
float v25; // xmm2_4
float v26; // xmm0_4
float v27; // xmm1_4
float v28; // xmm1_4
pathnode_t *pInsert; // eax
pathnode_t *v30; // ecx
pathnode_t *v31; // eax
int success; // eax
float v33; // [esp+10h] [ebp-8Ch]
int linkCount; // [esp+14h] [ebp-88h]
float negotiationOverlapCost; // [esp+18h] [ebp-84h]
pathnode_t topParent; // [esp+1Ch] [ebp-80h] BYREF
returnVGoaPosl = (float *)vGoalPos;
nodeLinkIndex = 0;
if ( vGoalPos )
{
g_pathAttemptGoalPos[0] = *vGoalPos;
g_pathAttemptGoalPos[1] = vGoalPos[1];
g_pathAttemptGoalPos[2] = vGoalPos[2];
}
else
{
g_pathAttemptGoalPos[0] = 0.0;
g_pathAttemptGoalPos[1] = 0.0;
g_pathAttemptGoalPos[2] = 0.0;
}
returnStartNode = pNodeFrom;
pNodeFrom->transient.iSearchFrame = ++level.iSearchFrame;
returnEndNode = pNodeFrom;
pNodeFrom->transient.pParent = &topParent;
pNodeFrom->transient.pNextOpen = 0;
pNodeFrom->transient.pPrevOpen = &topParent;
pNodeFrom->transient.fCost = 0.0;
topParent.transient.pNextOpen = pNodeFrom;
while ( returnEndNode != searchInfo->m_pNodeTo )
{
topParent.transient.pNextOpen = returnEndNode->transient.pNextOpen;
if ( topParent.transient.pNextOpen )
topParent.transient.pNextOpen->transient.pPrevOpen = &topParent;
linkCount = 0;
if ( returnEndNode->dynamic.wLinkCount > 0 )
{
while ( 1 )
{
if ( bIgnoreBadplaces || !returnEndNode->constant.Links[nodeLinkIndex].ubBadPlaceCount[eTeam] )
{
endNodeLinks = returnEndNode->constant.Links;
nodeNum = endNodeLinks[nodeLinkIndex].nodeNum;
endNodeLinkDist = &endNodeLinks[nodeLinkIndex].fDist;
pSuccessor = &gameWorldCurrent->path.nodes[nodeNum];
if ( returnEndNode->constant.type != NODE_NEGOTIATION_BEGIN || pSuccessor->constant.type != NODE_NEGOTIATION_END || !returnEndNode->dynamic.wOverlapCount && !pSuccessor->dynamic.wOverlapCount )
{
if ( pSuccessor->transient.iSearchFrame != level.iSearchFrame )
{
pSuccessor->transient.iSearchFrame = level.iSearchFrame;
negotiationOverlapCost = searchInfo->negotiationOverlapCost;
v20 = vGoalPos[1] - pSuccessor->constant.vOrigin[1];
v21 = sqrtf((float)((float)(*vGoalPos - pSuccessor->constant.vOrigin[0]) * (float)(*vGoalPos - pSuccessor->constant.vOrigin[0])) + (float)(v20 * v20));
v22 = pSuccessor->constant.minUseDistSq;
if ( v22 <= 1.0 || (v23 = searchInfo->startPos[0] - pSuccessor->constant.vOrigin[0], v24 = searchInfo->startPos[1] - pSuccessor->constant.vOrigin[1], v25 = searchInfo->startPos[2] - pSuccessor->constant.vOrigin[2], v22 <= (float)((float)((float)(v23 * v23) + (float)(v24 * v24)) + (float)(v25 * v25))) )
{
v26 = v21 + (double)pSuccessor->dynamic.userCount * negotiationOverlapCost;
}
else
{
v33 = v21 + (double)pSuccessor->dynamic.userCount * negotiationOverlapCost;
v26 = negotiationOverlapCost + v33;
}
pSuccessor->transient.fHeuristic = v26;
v17 = returnEndNode->constant.Links[nodeLinkIndex].fDist + returnEndNode->transient.fCost;
LABEL_25:
v27 = pSuccessor->transient.fHeuristic;
pSuccessor->transient.pParent = returnEndNode;
pSuccessor->transient.fCost = v17;
v28 = v27 + v17;
pInsert = &topParent;
if ( topParent.transient.pNextOpen )
{
do
{
v30 = pInsert->transient.pNextOpen;
if ( (float)(v30->transient.fHeuristic + v30->transient.fCost) >= v28 )
break;
pInsert = pInsert->transient.pNextOpen;
}
while ( v30->transient.pNextOpen );
}
pSuccessor->transient.pPrevOpen = pInsert;
pSuccessor->transient.pNextOpen = pInsert->transient.pNextOpen;
pInsert->transient.pNextOpen = pSuccessor;
v31 = pSuccessor->transient.pNextOpen;
if ( v31 )
v31->transient.pPrevOpen = pSuccessor;
goto LABEL_30;
}
v17 = returnEndNode->transient.fCost + *endNodeLinkDist;
if ( v17 < pSuccessor->transient.fCost )
{
v18 = pSuccessor->transient.pPrevOpen;
if ( v18 )
{
v18->transient.pNextOpen = pSuccessor->transient.pNextOpen;
v19 = pSuccessor->transient.pNextOpen;
if ( v19 )
v19->transient.pPrevOpen = pSuccessor->transient.pPrevOpen;
}
goto LABEL_25;
}
}
}
LABEL_30:
++nodeLinkIndex;
if ( ++linkCount >= returnEndNode->dynamic.wLinkCount )
{
returnVGoaPosl = (float *)vGoalPos;
returnStartNode = pNodeFrom;
break;
}
}
}
nodeLinkIndex = 0;
returnEndNode->transient.pPrevOpen = 0;
returnEndNode = topParent.transient.pNextOpen;
if ( !topParent.transient.pNextOpen )
return 0;
}
if ( pPath )
success = Path_GeneratePath(pPath, eTeam, vStartPos, returnVGoaPosl, returnStartNode, returnEndNode, 1, bAllowNegotiationLinks);
else
success = 1;
return success;
}
*/
int Path_AStarAlgorithm_CustomSearchInfo_FindPath_custom(game::path_t* pPath, game::team_t eTeam, const float* vStartPos, game::pathnode_t* pNodeFrom, const float* vGoalPos, int bAllowNegotiationLinks, game::CustomSearchInfo_FindPath* searchInfo, int bIgnoreBadplaces, game::pathnode_t* nodeTo)
{
float* returnVGoaPosl; // edx
int nodeLinkIndex; // ebx
game::pathnode_t* returnStartNode; // ecx
game::pathnode_t* returnEndNode; // edi
game::pathlink_s* endNodeLinks; // eax
int nodeNum; // esi
float* endNodeLinkDist; // eax
game::pathnode_t* pSuccessor; // esi
float v17; // xmm0_4
game::pathnode_t* v18; // eax
game::pathnode_t* v19; // eax
float v20; // xmm1_4
long double v21; // st7
float v22; // xmm3_4
float v23; // xmm0_4
float v24; // xmm1_4
float v25; // xmm2_4
float v26; // xmm0_4
float v27; // xmm1_4
float v28; // xmm1_4
game::pathnode_t* pInsert; // eax
game::pathnode_t* v30; // ecx
game::pathnode_t* v31; // eax
int success; // eax
float v33; // [esp+10h] [ebp-8Ch]
int linkCount; // [esp+14h] [ebp-88h]
float negotiationOverlapCost; // [esp+18h] [ebp-84h]
game::pathnode_t topParent; // [esp+1Ch] [ebp-80h] BYREF
returnVGoaPosl = (float*)vGoalPos;
nodeLinkIndex = 0;
if (vGoalPos)
{
*game::g_pathAttemptGoalPos[0] = vGoalPos[0];
*game::g_pathAttemptGoalPos[1] = vGoalPos[1];
*game::g_pathAttemptGoalPos[2] = vGoalPos[2];
}
else
{
*game::g_pathAttemptGoalPos[0] = 0.0f;
*game::g_pathAttemptGoalPos[1] = 0.0f;
*game::g_pathAttemptGoalPos[2] = 0.0f;
}
returnStartNode = pNodeFrom;
pNodeFrom->transient.iSearchFrame = ++game::level->iSearchFrame;
returnEndNode = pNodeFrom;
pNodeFrom->transient.pParent = &topParent;
pNodeFrom->transient.pNextOpen = 0;
pNodeFrom->transient.pPrevOpen = &topParent;
pNodeFrom->transient.fCost = 0.0;
topParent.transient.pNextOpen = pNodeFrom;
while (returnEndNode != searchInfo->m_pNodeTo)
{
topParent.transient.pNextOpen = returnEndNode->transient.pNextOpen;
if (topParent.transient.pNextOpen)
topParent.transient.pNextOpen->transient.pPrevOpen = &topParent;
linkCount = 0;
if (returnEndNode->dynamic.wLinkCount > 0)
{
while (1)
{
if (bIgnoreBadplaces || !returnEndNode->constant.Links[nodeLinkIndex].ubBadPlaceCount[eTeam])
{
endNodeLinks = returnEndNode->constant.Links;
nodeNum = endNodeLinks[nodeLinkIndex].nodeNum;
endNodeLinkDist = &endNodeLinks[nodeLinkIndex].fDist;
pSuccessor = &(*game::gameWorldCurrent)->path.nodes[nodeNum];
if (returnEndNode->constant.type != game::NODE_NEGOTIATION_BEGIN || pSuccessor->constant.type != game::NODE_NEGOTIATION_END || !returnEndNode->dynamic.wOverlapCount && !pSuccessor->dynamic.wOverlapCount)
{
if (pSuccessor->transient.iSearchFrame != game::level->iSearchFrame)
{
pSuccessor->transient.iSearchFrame = game::level->iSearchFrame;
negotiationOverlapCost = searchInfo->negotiationOverlapCost;
v20 = vGoalPos[1] - pSuccessor->constant.vOrigin[1];
v21 = sqrtf((float)((float)(*vGoalPos - pSuccessor->constant.vOrigin[0]) * (float)(*vGoalPos - pSuccessor->constant.vOrigin[0])) + (float)(v20 * v20));
v22 = pSuccessor->constant.minUseDistSq;
if (v22 <= 1.0 || (v23 = searchInfo->startPos[0] - pSuccessor->constant.vOrigin[0], v24 = searchInfo->startPos[1] - pSuccessor->constant.vOrigin[1], v25 = searchInfo->startPos[2] - pSuccessor->constant.vOrigin[2], v22 <= (float)((float)((float)(v23 * v23) + (float)(v24 * v24)) + (float)(v25 * v25))))
{
v26 = v21 + (double)pSuccessor->dynamic.userCount * negotiationOverlapCost;
}
else
{
v33 = v21 + (double)pSuccessor->dynamic.userCount * negotiationOverlapCost;
v26 = negotiationOverlapCost + v33;
}
pSuccessor->transient.fHeuristic = v26;
v17 = returnEndNode->constant.Links[nodeLinkIndex].fDist + returnEndNode->transient.fCost;
LABEL_25:
v27 = pSuccessor->transient.fHeuristic;
pSuccessor->transient.pParent = returnEndNode;
pSuccessor->transient.fCost = v17;
v28 = v27 + v17;
pInsert = &topParent;
if (topParent.transient.pNextOpen)
{
do
{
v30 = pInsert->transient.pNextOpen;
if ((float)(v30->transient.fHeuristic + v30->transient.fCost) >= v28)
break;
pInsert = pInsert->transient.pNextOpen;
} while (v30->transient.pNextOpen);
}
pSuccessor->transient.pPrevOpen = pInsert;
pSuccessor->transient.pNextOpen = pInsert->transient.pNextOpen;
pInsert->transient.pNextOpen = pSuccessor;
v31 = pSuccessor->transient.pNextOpen;
if (v31)
v31->transient.pPrevOpen = pSuccessor;
goto LABEL_30;
}
v17 = returnEndNode->transient.fCost + *endNodeLinkDist;
if (v17 < pSuccessor->transient.fCost)
{
v18 = pSuccessor->transient.pPrevOpen;
if (v18)
{
v18->transient.pNextOpen = pSuccessor->transient.pNextOpen;
v19 = pSuccessor->transient.pNextOpen;
if (v19)
v19->transient.pPrevOpen = pSuccessor->transient.pPrevOpen;
}
goto LABEL_25;
}
}
}
LABEL_30:
++nodeLinkIndex;
if (++linkCount >= returnEndNode->dynamic.wLinkCount)
{
returnVGoaPosl = (float*)vGoalPos;
returnStartNode = pNodeFrom;
break;
}
}
}
nodeLinkIndex = 0;
returnEndNode->transient.pPrevOpen = 0;
returnEndNode = topParent.transient.pNextOpen;
if (!topParent.transient.pNextOpen)
return 0;
}
if (pPath)
success = game::Path_GeneratePath(pPath, eTeam, vStartPos, returnVGoaPosl, returnStartNode, returnEndNode, 1, bAllowNegotiationLinks);
else
success = 1;
return success;
}
/*
//Original
int __userpurge Path_FindPathFromTo@<eax>(float *startPos@<eax>, pathnode_t *pNodeTo@<edx>, path_t *pPath, team_t eTeam, pathnode_t *pNodeFrom, float *vGoalPos, int bAllowNegotiationLinks, int bIgnoreBadplaces)
{
int v8; // xmm0_4
CustomSearchInfo_FindPath info; // [esp+0h] [ebp-14h] BYREF
v8 = ai_pathNegotiationOverlapCost->current.integer;
info.m_pNodeTo = pNodeTo;
LODWORD(info.negotiationOverlapCost) = v8;
info.startPos[0] = *startPos;
info.startPos[1] = startPos[1];
info.startPos[2] = startPos[2];
return Path_AStarAlgorithm_CustomSearchInfo_FindPath_(pPath, eTeam, startPos, pNodeFrom, vGoalPos, bAllowNegotiationLinks, &info, bIgnoreBadplaces, pNodeTo);
}
*/
int Path_FindPathFromTo_custom(float* startPos, game::pathnode_t* pNodeTo, game::path_t* pPath, game::team_t eTeam, game::pathnode_t* pNodeFrom, float* vGoalPos, int bAllowNegotiationLinks, int bIgnoreBadplaces)
{
int overlapCost; // xmm0_4
game::CustomSearchInfo_FindPath info = {}; // [esp+0h] [ebp-14h] BYREF
int result;
overlapCost = (*game::ai_pathNegotiationOverlapCost)->current.integer;
info.m_pNodeTo = pNodeTo;
info.negotiationOverlapCost = overlapCost;
info.startPos[0] = startPos[0];
info.startPos[1] = startPos[1];
info.startPos[2] = startPos[2];
result = Path_AStarAlgorithm_CustomSearchInfo_FindPath_custom(pPath, eTeam, startPos, pNodeFrom, vGoalPos, bAllowNegotiationLinks, &info, bIgnoreBadplaces, pNodeTo);
return result;
}
/*
//Original
int __userpurge Path_FindPath@<eax>(path_t* pPath@<ecx>, team_t eTeam@<edx>, float* vStartPos, float* vGoalPos, int bAllowNegotiationLinks)
{
pathnode_t* pNodeTo; // esi
int result; // eax
pathnode_t* pNodeFrom; // eax
int a9; // [esp+2Ch] [ebp-304h] BYREF
int a4[192]; // [esp+30h] [ebp-300h] BYREF
pNodeTo = Path_NearestNodeNotCrossPlanes(NAN, COERCE_FLOAT(64), (int)vGoalPos, (int)a4, 192.0, 0, 0, 0, (int)&a9, 0);
if (pNodeTo && (pNodeFrom = Path_NearestNodeNotCrossPlanes(NAN, COERCE_FLOAT(64), (int)vStartPos, (int)a4, 192.0, 0, 0, 0, (int)&a9, 0)) != 0)
result = Path_FindPathFromTo(vStartPos, pNodeTo, pPath, eTeam, pNodeFrom, vGoalPos, bAllowNegotiationLinks, 0);
else
result = 0;
return result;
}
*/
int Path_FindPath_custom(game::path_t* pPath, game::team_t eTeam, float* vStartPos, float* vGoalPos, int bAllowNegotiationLinks)
{
int result; // eax
int returnCount = 0; // [esp+2Ch] [ebp-304h] BYREF
std::unique_ptr<game::pathsort_t[], void (*)(game::pathsort_t*)> nodes(new game::pathsort_t[64], [](game::pathsort_t* ptr) { delete[] ptr; });
const int maxNodes = 64;
game::pathnode_t* pNodeTo = game::Path_NearestNodeNotCrossPlanes(-2, maxNodes, vGoalPos, nodes.get(), 192.0f, 0.0f, 0.0f, 0.0f, &returnCount, game::NEAREST_NODE_DO_HEIGHT_CHECK);
if (!pNodeTo)
{
printf("Couldn't find the node to\n");
}
game::pathnode_t* pNodeFrom = game::Path_NearestNodeNotCrossPlanes(-2, maxNodes, vStartPos, nodes.get(), 192.0f, 0.0f, 0.0f, 0.0f, &returnCount, game::NEAREST_NODE_DO_HEIGHT_CHECK);
if (pNodeTo && pNodeFrom)
{
result = Path_FindPathFromTo_custom(vStartPos, pNodeTo, pPath, eTeam, pNodeFrom, vGoalPos, bAllowNegotiationLinks, 0);
}
else
{
result = 0;
}
return result;
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
//utils::hook::jump(0x4CF280, Path_FindPath_stub);
gsc::method::add("getlinkednodes", [](game::scr_entref_s ent)
{
if (ent.classnum != game::CLASS_NUM_PATHNODE)
{
game::Scr_Error("Not a pathnode", game::SCRIPTINSTANCE_SERVER, false);
return;
}
auto primary_node = &(*game::gameWorldCurrent)->path.nodes[ent.entnum];
game::Scr_MakeArray(game::SCRIPTINSTANCE_SERVER);
for (auto i = 0; i < primary_node->constant.totalLinkCount; i++)
{
auto linked_node = &(*game::gameWorldCurrent)->path.nodes[primary_node->constant.Links[i].nodeNum];
game::Scr_AddPathnode(game::SCRIPTINSTANCE_SERVER, linked_node);
game::Scr_AddArray(game::SCRIPTINSTANCE_SERVER);
}
});
gsc::method::add("getnodenumber", [](game::scr_entref_s ent)
{
if (ent.classnum != game::CLASS_NUM_PATHNODE)
{
game::Scr_Error("Not a pathnode", game::SCRIPTINSTANCE_SERVER, false);
return;
}
auto node = &(*game::gameWorldCurrent)->path.nodes[ent.entnum];
auto entnum = node - (*game::gameWorldCurrent)->path.nodes;
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, entnum);
});
gsc::function::add("getnodebynumber", []()
{
auto node_num = game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 0);
if (node_num == game::g_path->actualNodeCount)
{
game::Scr_AddUndefined(game::SCRIPTINSTANCE_SERVER);
return;
}
if (node_num < 0 || node_num > game::g_path->actualNodeCount)
{
game::Scr_Error(utils::string::va("Number %d is not valid for a node", node_num), game::SCRIPTINSTANCE_SERVER, false);
return;
}
auto node = &(*game::gameWorldCurrent)->path.nodes[node_num];
game::Scr_AddPathnode(game::SCRIPTINSTANCE_SERVER, node);
});
gsc::function::add("generatepath", []()
{
auto path = std::make_unique<game::path_t>();
float start_pos[3] = {};
float goal_pos[3] = {};
auto team = "neutral"s;
auto allow_negotiation_links = false;
game::Scr_GetVector(game::SCRIPTINSTANCE_SERVER, 0, start_pos);
game::Scr_GetVector(game::SCRIPTINSTANCE_SERVER, 1, goal_pos);
if (game::Scr_GetNumParam(game::SCRIPTINSTANCE_SERVER) >= 3)
{
if (game::Scr_GetType(game::SCRIPTINSTANCE_SERVER, 2) != game::VAR_UNDEFINED)
{
team = game::Scr_GetString(game::SCRIPTINSTANCE_SERVER, 2);
}
if (game::Scr_GetNumParam(game::SCRIPTINSTANCE_SERVER) >= 4)
{
allow_negotiation_links = game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 3);
}
}
if (!game::team_map.contains(team))
{
game::Scr_Error(utils::string::va("Team %s is not valid", team.data()), game::SCRIPTINSTANCE_SERVER, false);
return;
}
auto eTeam = game::team_map.at(team);
auto success = Path_FindPath_custom(path.get(), eTeam, start_pos, goal_pos, allow_negotiation_links);
if (!success)
{
game::Scr_AddUndefined(game::SCRIPTINSTANCE_SERVER);
return;
}
game::Scr_MakeArray(game::SCRIPTINSTANCE_SERVER);
//Reverse the order of the array so index 0 is from the starting point instead of the end
for (auto i = path->wPathLen; i >= 0; i--)
{
//Return the number of the node instead of the node itself because of spooky GSC VM corruption
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, path->pts[i].iNodeNum);
game::Scr_AddArray(game::SCRIPTINSTANCE_SERVER);
}
});
}
private:
};
}
REGISTER_COMPONENT(ai::component)

0
src/component/debug.cpp Normal file
View File

View File

@ -259,118 +259,26 @@ namespace gsc
game::Scr_AddEntity(game::SCRIPTINSTANCE_SERVER, ent2);
});
method::add("getlinkednodes", [](game::scr_entref_s ent)
{
if (ent.classnum != game::CLASS_NUM_PATHNODE)
{
game::Scr_Error("Not a pathnode", game::SCRIPTINSTANCE_SERVER, false);
return;
}
auto primary_node = &(*game::gameWorldCurrent)->path.nodes[ent.entnum];
game::Scr_MakeArray(game::SCRIPTINSTANCE_SERVER);
for (auto i = 0; i < primary_node->constant.totalLinkCount; i++)
{
auto linked_node = &(*game::gameWorldCurrent)->path.nodes[primary_node->constant.Links[i].nodeNum];
game::Scr_AddPathnode(game::SCRIPTINSTANCE_SERVER, linked_node);
game::Scr_AddArray(game::SCRIPTINSTANCE_SERVER);
}
});
method::add("getnodenumber", [](game::scr_entref_s ent)
{
if (ent.classnum != game::CLASS_NUM_PATHNODE)
{
game::Scr_Error("Not a pathnode", game::SCRIPTINSTANCE_SERVER, false);
return;
}
auto node = &(*game::gameWorldCurrent)->path.nodes[ent.entnum];
auto entnum = node - (*game::gameWorldCurrent)->path.nodes;
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, entnum);
});
function::add("getnodebynumber", []()
{
auto node_num = game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 0);
if (node_num == game::g_path->actualNodeCount)
{
game::Scr_AddUndefined(game::SCRIPTINSTANCE_SERVER);
return;
}
if (node_num < 0 || node_num > game::g_path->actualNodeCount)
{
game::Scr_Error(utils::string::va("Number %d is not valid for a node", node_num), game::SCRIPTINSTANCE_SERVER, false);
return;
}
auto node = &(*game::gameWorldCurrent)->path.nodes[node_num];
game::Scr_AddPathnode(game::SCRIPTINSTANCE_SERVER, node);
});
function::add("generatepath", []()
{
auto path = std::make_unique<game::path_t>();
float start_pos[3] = {};
float goal_pos[3] = {};
auto team = "neutral"s;
auto allow_negotiation_links = false;
game::Scr_GetVector(game::SCRIPTINSTANCE_SERVER, 0, start_pos);
game::Scr_GetVector(game::SCRIPTINSTANCE_SERVER, 1, goal_pos);
if (game::Scr_GetNumParam(game::SCRIPTINSTANCE_SERVER) >= 3)
{
if (game::Scr_GetType(game::SCRIPTINSTANCE_SERVER, 2) != game::VAR_UNDEFINED)
{
team = game::Scr_GetString(game::SCRIPTINSTANCE_SERVER, 2);
}
if (game::Scr_GetNumParam(game::SCRIPTINSTANCE_SERVER) >= 4)
{
allow_negotiation_links = game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 3);
}
}
if (!game::team_map.contains(team))
{
game::Scr_Error(utils::string::va("Team %s is not valid", team.data()), game::SCRIPTINSTANCE_SERVER, false);
return;
}
auto eTeam = game::team_map.at(team);
auto success = game::Path_FindPath(path.get(), eTeam, start_pos, goal_pos, allow_negotiation_links);
if (!success)
{
game::Scr_AddUndefined(game::SCRIPTINSTANCE_SERVER);
return;
}
game::Scr_MakeArray(game::SCRIPTINSTANCE_SERVER);
for (auto i = 0; i < path->wPathLen; i++)
{
//Return the number of the node instead of the node itself because of spooky GSC VM corruption
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, path->pts[i].iNodeNum);
game::Scr_AddArray(game::SCRIPTINSTANCE_SERVER);
}
});
function::add("writefile", []()
{
const auto path = game::Scr_GetString(game::SCRIPTINSTANCE_SERVER, 0);
const auto data = game::Scr_GetString(game::SCRIPTINSTANCE_SERVER, 1);
auto append = false;
if (game::Scr_GetNumParam(game::SCRIPTINSTANCE_SERVER) > 2)
{
append = game::Scr_GetInt(game::SCRIPTINSTANCE_SERVER, 2);
}
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, utils::io::write_file(path, data, append));
});
gsc::function::add("readfile", []()
{
const auto path = game::Scr_GetString(game::SCRIPTINSTANCE_SERVER, 0);
game::Scr_AddString(game::SCRIPTINSTANCE_SERVER, utils::io::read_file(path).c_str());
});
}
private:

21
src/component/gsc.hpp Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <stdinc.hpp>
namespace gsc
{
namespace
{
}
namespace function
{
void add(const std::string& name, const game::BuiltinFunction function);
}
namespace method
{
void add(const std::string& name, const game::BuiltinMethod method);
}
}

View File

@ -5,14 +5,57 @@
#include <utils/io.hpp>
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace test
{
utils::hook::detour gscr_spawn_hook;
namespace
{
game::dvar_s* custom_dvar;
game::dvar_s* custom_string_dvar;
void print_script_callstack(const char* msg, game::scriptInstance_t inst)
{
int i; // [esp+4h] [ebp-4h]
printf("******* Callstack for %s *******\n", msg);
game::Scr_PrintPrevCodePos(game::function_stack[inst].pos, inst, game::CON_CHANNEL_DONT_FILTER, 0);
auto function_count = game::gScrVmPub[inst].function_count;
if (function_count)
{
for (i = function_count - 1; i >= 1; --i)
{
printf("Info: called from:\n");
game::Scr_PrintPrevCodePos(
game::gScrVmPub[inst].function_frame_start[i].fs.pos,
inst,
game::CON_CHANNEL_DONT_FILTER,
game::gScrVmPub[inst].function_frame_start[i].fs.localId == 0);
}
printf("started from:\n");
game::Scr_PrintPrevCodePos(game::gScrVmPub[inst].function_frame_start[0].fs.pos, inst, game::CON_CHANNEL_DONT_FILTER, 1u);
}
printf("************************************\n");
}
void gscr_spawn_stub()
{
auto classname = game::Scr_GetConstString(game::SCRIPTINSTANCE_SERVER, 0);
float origin[3] = {};
game::Scr_GetVector(game::SCRIPTINSTANCE_SERVER, 1, origin);
if (classname == game::scr_const->script_origin)
{
print_script_callstack(utils::string::va("GScr_Spawn() classname: script_origin at: (%f, %f, %f)", origin[0], origin[1], origin[2]), game::SCRIPTINSTANCE_SERVER);
}
gscr_spawn_hook.invoke<void>();
}
}
bool our_funny_call(game::pathnode_t* begin_node, game::pathnode_t* end_node, bool allowNeg)
{
if (!allowNeg && begin_node->constant.type == game::NODE_NEGOTIATION_BEGIN && end_node->constant.type == game::NODE_NEGOTIATION_END)
@ -81,6 +124,17 @@ namespace test
//printf("Biggie Spam McCheese\n");
});
gscr_spawn_hook.create(0x517630, gscr_spawn_stub);
//Disable AI print spam
utils::hook::nop(0x4BAB7D, 5);
utils::hook::nop(0x4BAAFA, 5);
//Disable asset loading print spam
utils::hook::nop(0x48D9D9, 5);
//Disable unknown dvar spam
utils::hook::nop(0x5F04AF, 5);
// fix NEGOTIATION links
utils::hook::jump(0x4D3296, our_funny_hook);