Merge pull request #68 from ineedbots/develop

Compatibility and improvements for Bot Warfare
This commit is contained in:
Louve 2023-12-13 10:00:22 +01:00 committed by GitHub
commit 984c63532e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 13 deletions

View File

@ -27,6 +27,9 @@ namespace Components
std::int8_t forward; std::int8_t forward;
std::int8_t right; std::int8_t right;
std::uint16_t weapon; std::uint16_t weapon;
std::uint16_t lastAltWeapon;
std::uint8_t meleeDist;
float meleeYaw;
bool active; bool active;
}; };
@ -291,6 +294,23 @@ namespace Components
g_botai[entref.entnum].right = static_cast<int8_t>(rightInt); g_botai[entref.entnum].right = static_cast<int8_t>(rightInt);
g_botai[entref.entnum].active = true; g_botai[entref.entnum].active = true;
}); });
GSC::Script::AddMethod("BotMeleeParams", [](const Game::scr_entref_t entref) // Usage: <bot> BotMeleeParams(<float>, <float>);
{
const auto* ent = GSC::Script::Scr_GetPlayerEntity(entref);
if (!Game::SV_IsTestClient(ent->s.number))
{
Game::Scr_Error("BotMeleeParams: Can only call on a bot!");
return;
}
const auto yaw = Game::Scr_GetFloat(0);
const auto dist = std::clamp<int>(static_cast<int>(Game::Scr_GetFloat(1)), std::numeric_limits<unsigned char>::min(), std::numeric_limits<unsigned char>::max());
g_botai[entref.entnum].meleeYaw = yaw;
g_botai[entref.entnum].meleeDist = static_cast<int8_t>(dist);
g_botai[entref.entnum].active = true;
});
} }
void Bots::BotAiAction(Game::client_s* cl) void Bots::BotAiAction(Game::client_s* cl)
@ -300,8 +320,10 @@ namespace Components
return; return;
} }
auto clientNum = cl - Game::svs_clients;
// Keep test client functionality // Keep test client functionality
if (!g_botai[cl - Game::svs_clients].active) if (!g_botai[clientNum].active)
{ {
Game::SV_BotUserMove(cl); Game::SV_BotUserMove(cl);
return; return;
@ -312,10 +334,13 @@ namespace Components
userCmd.serverTime = *Game::svs_time; userCmd.serverTime = *Game::svs_time;
userCmd.buttons = g_botai[cl - Game::svs_clients].buttons; userCmd.buttons = g_botai[clientNum].buttons;
userCmd.forwardmove = g_botai[cl - Game::svs_clients].forward; userCmd.forwardmove = g_botai[clientNum].forward;
userCmd.rightmove = g_botai[cl - Game::svs_clients].right; userCmd.rightmove = g_botai[clientNum].right;
userCmd.weapon = g_botai[cl - Game::svs_clients].weapon; userCmd.weapon = g_botai[clientNum].weapon;
userCmd.primaryWeaponForAltMode = g_botai[clientNum].lastAltWeapon;
userCmd.meleeChargeYaw = g_botai[clientNum].meleeYaw;
userCmd.meleeChargeDist = g_botai[clientNum].meleeDist;
userCmd.angles[0] = ANGLE2SHORT((cl->gentity->client->ps.viewangles[0] - cl->gentity->client->ps.delta_angles[0])); userCmd.angles[0] = ANGLE2SHORT((cl->gentity->client->ps.viewangles[0] - cl->gentity->client->ps.delta_angles[0]));
userCmd.angles[1] = ANGLE2SHORT((cl->gentity->client->ps.viewangles[1] - cl->gentity->client->ps.delta_angles[1])); userCmd.angles[1] = ANGLE2SHORT((cl->gentity->client->ps.viewangles[1] - cl->gentity->client->ps.delta_angles[1]));
@ -339,11 +364,38 @@ namespace Components
} }
} }
void Bots::G_SelectWeaponIndex(int clientNum, int iWeaponIndex) void Bots::G_SelectWeaponIndex(int clientNum, unsigned int iWeaponIndex)
{ {
if (g_botai[clientNum].active) if (g_botai[clientNum].active)
{ {
g_botai[clientNum].weapon = static_cast<uint16_t>(iWeaponIndex); g_botai[clientNum].weapon = static_cast<uint16_t>(iWeaponIndex);
g_botai[clientNum].lastAltWeapon = 0;
auto* def = Game::BG_GetWeaponCompleteDef(iWeaponIndex);
if (def && def->weapDef->inventoryType == Game::WEAPINVENTORY_ALTMODE)
{
auto* ps = &Game::g_entities[clientNum].client->ps;
auto numWeaps = Game::BG_GetNumWeapons();
for (auto i = 1u; i < numWeaps; i++)
{
if (!Game::BG_PlayerHasWeapon(ps, i))
{
continue;
}
auto* thisDef = Game::BG_GetWeaponCompleteDef(i);
if (!thisDef || thisDef->altWeaponIndex != iWeaponIndex)
{
continue;
}
g_botai[clientNum].lastAltWeapon = static_cast<uint16_t>(i);
break;
}
}
} }
} }
@ -472,6 +524,11 @@ namespace Components
}); });
} }
bool Bots::Player_UpdateActivate_stub(int)
{
return false;
}
Bots::Bots() Bots::Bots()
{ {
AssertOffset(Game::client_s, bIsTestClient, 0x41AF0); AssertOffset(Game::client_s, bIsTestClient, 0x41AF0);
@ -489,6 +546,9 @@ namespace Components
Utils::Hook(0x441B80, G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick(); Utils::Hook(0x441B80, G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick();
// fix bots using objects (SV_IsClientBot)
Utils::Hook(0x4D79C5, Player_UpdateActivate_stub, HOOK_CALL).install()->quick();
Utils::Hook(0x459654, SV_GetClientPing_Hk, HOOK_CALL).install()->quick(); Utils::Hook(0x459654, SV_GetClientPing_Hk, HOOK_CALL).install()->quick();
sv_randomBotNames = Game::Dvar_RegisterBool("sv_randomBotNames", false, Game::DVAR_NONE, "Randomize the bots' names"); sv_randomBotNames = Game::Dvar_RegisterBool("sv_randomBotNames", false, Game::DVAR_NONE, "Randomize the bots' names");

View File

@ -32,9 +32,11 @@ namespace Components
static void BotAiAction(Game::client_s* cl); static void BotAiAction(Game::client_s* cl);
static void SV_BotUserMove_Hk(); static void SV_BotUserMove_Hk();
static void G_SelectWeaponIndex(int clientNum, int iWeaponIndex); static void G_SelectWeaponIndex(int clientNum, unsigned int iWeaponIndex);
static void G_SelectWeaponIndex_Hk(); static void G_SelectWeaponIndex_Hk();
static bool Player_UpdateActivate_stub(int);
static int SV_GetClientPing_Hk(int clientNum); static int SV_GetClientPing_Hk(int clientNum);
static bool IsFull(); static bool IsFull();

View File

@ -30,12 +30,20 @@ namespace Components::GSC
std::filesystem::path IO::BuildPath(const char* path) std::filesystem::path IO::BuildPath(const char* path)
{ {
const std::filesystem::path fsGame = (*Game::fs_gameDirVar)->current.string; const std::filesystem::path fsGame = (*Game::fs_gameDirVar)->current.string;
if (!fsGame.empty())
// check if we need to append scriptdata folder, for backwards compat
std::string spath = path;
if (!spath.starts_with(Game::SCRIPTDATA_DIR + "/"s) && !spath.starts_with(Game::SCRIPTDATA_DIR + "\\"s))
{ {
return fsGame / "scriptdata"s / path; spath = Game::SCRIPTDATA_DIR + "/"s + spath;
} }
return DefaultDestPath / "scriptdata"s / path; if (!fsGame.empty())
{
return fsGame / spath;
}
return DefaultDestPath / spath;
} }
void IO::GScr_OpenFile() void IO::GScr_OpenFile()
@ -154,7 +162,7 @@ namespace Components::GSC
return; return;
} }
file = file.substr(0, 1024 - 1); // 1024 is the max string size for the SL system file = file.substr(0, (1 << 16) - 1); // 65535 is the max string size for the SL system
Game::Scr_AddString(file.data()); Game::Scr_AddString(file.data());
}); });

View File

@ -84,12 +84,12 @@ namespace Components::GSC
const nlohmann::json json = Data; const nlohmann::json json = Data;
FileSystem::FileWriter("scriptdata/scriptstorage.json").write(json.dump()); FileSystem::FileWriter(Game::SCRIPTDATA_DIR + "/scriptstorage.json"s).write(json.dump());
}); });
Script::AddFunction("StorageLoad", [] // gsc: StorageLoad(); Script::AddFunction("StorageLoad", [] // gsc: StorageLoad();
{ {
FileSystem::File storageFile("scriptdata/scriptstorage.json"); FileSystem::File storageFile(Game::SCRIPTDATA_DIR + "/scriptstorage.json"s);
if (!storageFile.exists()) if (!storageFile.exists())
{ {
return; return;

View File

@ -11,4 +11,6 @@ namespace Game
BG_IsWeaponValid_t BG_IsWeaponValid = BG_IsWeaponValid_t(0x415BA0); BG_IsWeaponValid_t BG_IsWeaponValid = BG_IsWeaponValid_t(0x415BA0);
BG_GetEquippedWeaponIndex_t BG_GetEquippedWeaponIndex = BG_GetEquippedWeaponIndex_t(0x4D8BA0); BG_GetEquippedWeaponIndex_t BG_GetEquippedWeaponIndex = BG_GetEquippedWeaponIndex_t(0x4D8BA0);
BG_GetEquippedWeaponState_t BG_GetEquippedWeaponState = BG_GetEquippedWeaponState_t(0x4E79E0); BG_GetEquippedWeaponState_t BG_GetEquippedWeaponState = BG_GetEquippedWeaponState_t(0x4E79E0);
BG_PlayerHasWeapon_t BG_PlayerHasWeapon = BG_PlayerHasWeapon_t(0x4AB530);
BG_GetWeaponCompleteDef_t BG_GetWeaponCompleteDef = BG_GetWeaponCompleteDef_t(0x44CE00);
} }

View File

@ -28,4 +28,10 @@ namespace Game
typedef PlayerEquippedWeaponState*(*BG_GetEquippedWeaponState_t)(playerState_s* ps, unsigned int weaponIndex); typedef PlayerEquippedWeaponState*(*BG_GetEquippedWeaponState_t)(playerState_s* ps, unsigned int weaponIndex);
extern BG_GetEquippedWeaponState_t BG_GetEquippedWeaponState; extern BG_GetEquippedWeaponState_t BG_GetEquippedWeaponState;
typedef int*(*BG_PlayerHasWeapon_t)(playerState_s* ps, unsigned int weaponIndex);
extern BG_PlayerHasWeapon_t BG_PlayerHasWeapon;
typedef Game::WeaponCompleteDef*(*BG_GetWeaponCompleteDef_t)(unsigned int weaponIndex);
extern BG_GetWeaponCompleteDef_t BG_GetWeaponCompleteDef;
} }

View File

@ -221,6 +221,8 @@ namespace Game
constexpr auto LOCAL_VAR_STACK_SIZE = 64; constexpr auto LOCAL_VAR_STACK_SIZE = 64;
constexpr auto SCRIPTDATA_DIR = "scriptdata";
extern void IncInParam(); extern void IncInParam();
extern void Scr_AddBool(int value); extern void Scr_AddBool(int value);