From af147c9e98dd384114f8a8d10dccad309af45523 Mon Sep 17 00:00:00 2001 From: ineedbots Date: Mon, 15 Jun 2026 22:52:01 -0600 Subject: [PATCH 1/2] fix(IW4-TU6): various bot fixes - fix bots interacting with objects - fix bots knife lunging - fix bots using alt mode weaponry - reduce SetDvar and localized strings spam in the console --- src/game/iw4/mp_tu6/components/patches.cpp | 7 +++++ src/game/iw4/mp_tu6/components/sv_bots.cpp | 36 ++++++++++++++++++++-- src/game/iw4/mp_tu6/structs.h | 28 +++++++++++++++++ src/game/iw4/mp_tu6/symbols.h | 12 ++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/game/iw4/mp_tu6/components/patches.cpp b/src/game/iw4/mp_tu6/components/patches.cpp index 28fb12a..faa94b3 100644 --- a/src/game/iw4/mp_tu6/components/patches.cpp +++ b/src/game/iw4/mp_tu6/components/patches.cpp @@ -36,6 +36,13 @@ void DisableDvarProtection() // Cheat-protected // .text:8230D6EC b __restgprlr_27 *(volatile uint32_t *)0x8230D6EC = 0x60000000; + + // default migration_dvarErrors off to prevent console spam + *(volatile uint8_t *)0x822828E7 = 0x0; + + // default loc_warnings off to prevent console spam + *(volatile uint8_t *)0x822CBDEB = 0x0; + } patches::patches() diff --git a/src/game/iw4/mp_tu6/components/sv_bots.cpp b/src/game/iw4/mp_tu6/components/sv_bots.cpp index 0c834bb..98ca1a9 100644 --- a/src/game/iw4/mp_tu6/components/sv_bots.cpp +++ b/src/game/iw4/mp_tu6/components/sv_bots.cpp @@ -13,6 +13,7 @@ struct BotMovementInfo_t bool active; int buttons; unsigned short weapon; + unsigned short primaryWeaponForAlt; bool has_move; char forwardmove; char rightmove; @@ -56,7 +57,32 @@ static Detour G_SelectWeaponIndex_Detour; static int *G_SelectWeaponIndex_Hook(int clientNum, int iWeaponIndex) { if (clientNum >= 0 && clientNum < IW4_MAX_CLIENTS) + { g_botai[clientNum].weapon = static_cast(iWeaponIndex); + g_botai[clientNum].primaryWeaponForAlt = 0; + + const WeaponCompleteDef* def = BG_GetWeaponCompleteDef(iWeaponIndex); + + if (def && def->weapDef->inventoryType == WEAPINVENTORY_ALTMODE) + { + const playerState_s* ps = &g_entities[clientNum].client->ps; + const int numWeaps = BG_GetNumWeapons(); + + for (auto i = 1; i < numWeaps; i++) + { + if (!BG_PlayerHasWeapon(ps, i)) + continue; + + const WeaponCompleteDef* thisDef = BG_GetWeaponCompleteDef(i); + + if (!thisDef || thisDef->altWeaponIndex != iWeaponIndex) + continue; + + g_botai[clientNum].primaryWeaponForAlt = static_cast(i); + break; + } + } + } return G_SelectWeaponIndex_Detour.GetOriginal()(clientNum, iWeaponIndex); } @@ -89,8 +115,9 @@ static void SV_BotUserMove_Stub(client_t *cl) cmd.weapon = g_botai[clientNum].weapon ? g_botai[clientNum].weapon : static_cast(level->clients[clientNum].ps.weapCommon.weapon); - cmd.primaryWeaponForAltMode = - static_cast(level->clients[clientNum].ps.weapCommon.primaryWeaponForAltMode); + cmd.primaryWeaponForAltMode = g_botai[clientNum].primaryWeaponForAlt + ? g_botai[clientNum].primaryWeaponForAlt + : static_cast(level->clients[clientNum].ps.weapCommon.primaryWeaponForAltMode); cmd.offHandIndex = static_cast(level->clients[clientNum].ps.weapCommon.offHandIndex); cmd.forwardmove = g_botai[clientNum].has_move ? g_botai[clientNum].forwardmove : 0; cmd.rightmove = g_botai[clientNum].has_move ? g_botai[clientNum].rightmove : 0; @@ -278,7 +305,7 @@ static void PlayerCmd_BotMeleeParams(scr_entref_t entref) Scr_Error("Usage: botMeleeParams(, );"); const float yaw = static_cast(Scr_GetFloat(0)); - const int dist = Scr_GetInt(1); + const int dist = static_cast(static_cast(Scr_GetFloat(1))); g_botai[entref.entnum].melee_charge_yaw = yaw; g_botai[entref.entnum].melee_charge_dist = ClampByte(dist); @@ -332,6 +359,9 @@ SVBots::SVBots() SV_CalcPings_Detour = Detour(SV_CalcPings, SV_CalcPings_Hook); SV_CalcPings_Detour.Install(); + // remove bot check inside of Player_ActivateHoldCmd + *(volatile uint32_t *)0x82263658 = 0x60000000; + Scr_AddFunction("addtestclient", GScr_AddTestClient, BUILTIN_ANY); Scr_AddMethod("botaction", PlayerCmd_BotAction, BUILTIN_ANY); diff --git a/src/game/iw4/mp_tu6/structs.h b/src/game/iw4/mp_tu6/structs.h index 62e357e..0957b31 100644 --- a/src/game/iw4/mp_tu6/structs.h +++ b/src/game/iw4/mp_tu6/structs.h @@ -908,6 +908,34 @@ static_assert(offsetof(level_locals_t, gentities) == 0x4, ""); static_assert(offsetof(level_locals_t, num_entities) == 0x8, ""); static_assert(offsetof(level_locals_t, maxclients) == 0x3A4, ""); +enum weapInventoryType_t +{ + WEAPINVENTORY_PRIMARY = 0x0, + WEAPINVENTORY_OFFHAND = 0x1, + WEAPINVENTORY_ITEM = 0x2, + WEAPINVENTORY_ALTMODE = 0x3, + WEAPINVENTORY_EXCLUSIVE = 0x4, + WEAPINVENTORY_SCAVENGER = 0x5, + WEAPINVENTORYCOUNT = 0x6, +}; + +struct WeaponDef +{ + char pad[0x38]; + weapInventoryType_t inventoryType; +}; +static_assert(offsetof(WeaponDef, inventoryType) == 0x38, ""); + +struct WeaponCompleteDef +{ + const char *szInternalName; + WeaponDef *weapDef; + char pad[0x38]; + int altWeaponIndex; +}; +static_assert(offsetof(WeaponCompleteDef, weapDef) == 0x4, ""); +static_assert(offsetof(WeaponCompleteDef, altWeaponIndex) == 0x40, ""); + struct weaponParms { float forward[3]; diff --git a/src/game/iw4/mp_tu6/symbols.h b/src/game/iw4/mp_tu6/symbols.h index df6c8da..e50e3a0 100644 --- a/src/game/iw4/mp_tu6/symbols.h +++ b/src/game/iw4/mp_tu6/symbols.h @@ -230,6 +230,18 @@ typedef void (*BG_CalculateViewMovement_Angles_Idle_t)(viewState_t *vs, float *a static BG_CalculateViewMovement_Angles_Idle_t BG_CalculateViewMovement_Angles_Idle = reinterpret_cast(0x82118198); +typedef WeaponCompleteDef*(*BG_GetWeaponCompleteDef_t)(unsigned int weaponIndex); +static BG_GetWeaponCompleteDef_t BG_GetWeaponCompleteDef = + reinterpret_cast(0x821142C0); + +typedef int(*BG_GetNumWeapons_t)(); +static BG_GetNumWeapons_t BG_GetNumWeapons = + reinterpret_cast(0x821143F0); + +typedef bool(*BG_PlayerHasWeapon_t)(const playerState_s* ps, unsigned int weaponIndex); +static BG_PlayerHasWeapon_t BG_PlayerHasWeapon = + reinterpret_cast(0x820FE5F0); + static auto Weapon_RocketLauncher_Fire = reinterpret_cast Date: Tue, 16 Jun 2026 11:49:55 +0100 Subject: [PATCH 2/2] chore: run clang-format --- src/game/iw4/mp_tu6/components/patches.cpp | 1 - src/game/iw4/mp_tu6/components/sv_bots.cpp | 13 +++++++------ src/game/iw4/mp_tu6/symbols.h | 15 ++++++--------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/game/iw4/mp_tu6/components/patches.cpp b/src/game/iw4/mp_tu6/components/patches.cpp index faa94b3..c098e77 100644 --- a/src/game/iw4/mp_tu6/components/patches.cpp +++ b/src/game/iw4/mp_tu6/components/patches.cpp @@ -42,7 +42,6 @@ void DisableDvarProtection() // default loc_warnings off to prevent console spam *(volatile uint8_t *)0x822CBDEB = 0x0; - } patches::patches() diff --git a/src/game/iw4/mp_tu6/components/sv_bots.cpp b/src/game/iw4/mp_tu6/components/sv_bots.cpp index 98ca1a9..1fe8ba0 100644 --- a/src/game/iw4/mp_tu6/components/sv_bots.cpp +++ b/src/game/iw4/mp_tu6/components/sv_bots.cpp @@ -61,11 +61,11 @@ static int *G_SelectWeaponIndex_Hook(int clientNum, int iWeaponIndex) g_botai[clientNum].weapon = static_cast(iWeaponIndex); g_botai[clientNum].primaryWeaponForAlt = 0; - const WeaponCompleteDef* def = BG_GetWeaponCompleteDef(iWeaponIndex); + const WeaponCompleteDef *def = BG_GetWeaponCompleteDef(iWeaponIndex); if (def && def->weapDef->inventoryType == WEAPINVENTORY_ALTMODE) { - const playerState_s* ps = &g_entities[clientNum].client->ps; + const playerState_s *ps = &g_entities[clientNum].client->ps; const int numWeaps = BG_GetNumWeapons(); for (auto i = 1; i < numWeaps; i++) @@ -73,7 +73,7 @@ static int *G_SelectWeaponIndex_Hook(int clientNum, int iWeaponIndex) if (!BG_PlayerHasWeapon(ps, i)) continue; - const WeaponCompleteDef* thisDef = BG_GetWeaponCompleteDef(i); + const WeaponCompleteDef *thisDef = BG_GetWeaponCompleteDef(i); if (!thisDef || thisDef->altWeaponIndex != iWeaponIndex) continue; @@ -115,9 +115,10 @@ static void SV_BotUserMove_Stub(client_t *cl) cmd.weapon = g_botai[clientNum].weapon ? g_botai[clientNum].weapon : static_cast(level->clients[clientNum].ps.weapCommon.weapon); - cmd.primaryWeaponForAltMode = g_botai[clientNum].primaryWeaponForAlt - ? g_botai[clientNum].primaryWeaponForAlt - : static_cast(level->clients[clientNum].ps.weapCommon.primaryWeaponForAltMode); + cmd.primaryWeaponForAltMode = + g_botai[clientNum].primaryWeaponForAlt + ? g_botai[clientNum].primaryWeaponForAlt + : static_cast(level->clients[clientNum].ps.weapCommon.primaryWeaponForAltMode); cmd.offHandIndex = static_cast(level->clients[clientNum].ps.weapCommon.offHandIndex); cmd.forwardmove = g_botai[clientNum].has_move ? g_botai[clientNum].forwardmove : 0; cmd.rightmove = g_botai[clientNum].has_move ? g_botai[clientNum].rightmove : 0; diff --git a/src/game/iw4/mp_tu6/symbols.h b/src/game/iw4/mp_tu6/symbols.h index e50e3a0..233ebd9 100644 --- a/src/game/iw4/mp_tu6/symbols.h +++ b/src/game/iw4/mp_tu6/symbols.h @@ -230,17 +230,14 @@ typedef void (*BG_CalculateViewMovement_Angles_Idle_t)(viewState_t *vs, float *a static BG_CalculateViewMovement_Angles_Idle_t BG_CalculateViewMovement_Angles_Idle = reinterpret_cast(0x82118198); -typedef WeaponCompleteDef*(*BG_GetWeaponCompleteDef_t)(unsigned int weaponIndex); -static BG_GetWeaponCompleteDef_t BG_GetWeaponCompleteDef = - reinterpret_cast(0x821142C0); +typedef WeaponCompleteDef *(*BG_GetWeaponCompleteDef_t)(unsigned int weaponIndex); +static BG_GetWeaponCompleteDef_t BG_GetWeaponCompleteDef = reinterpret_cast(0x821142C0); -typedef int(*BG_GetNumWeapons_t)(); -static BG_GetNumWeapons_t BG_GetNumWeapons = - reinterpret_cast(0x821143F0); +typedef int (*BG_GetNumWeapons_t)(); +static BG_GetNumWeapons_t BG_GetNumWeapons = reinterpret_cast(0x821143F0); -typedef bool(*BG_PlayerHasWeapon_t)(const playerState_s* ps, unsigned int weaponIndex); -static BG_PlayerHasWeapon_t BG_PlayerHasWeapon = - reinterpret_cast(0x820FE5F0); +typedef bool (*BG_PlayerHasWeapon_t)(const playerState_s *ps, unsigned int weaponIndex); +static BG_PlayerHasWeapon_t BG_PlayerHasWeapon = reinterpret_cast(0x820FE5F0); static auto Weapon_RocketLauncher_Fire = reinterpret_cast