diff --git a/src/game/iw4/mp_tu6/components/patches.cpp b/src/game/iw4/mp_tu6/components/patches.cpp index 28fb12a..c098e77 100644 --- a/src/game/iw4/mp_tu6/components/patches.cpp +++ b/src/game/iw4/mp_tu6/components/patches.cpp @@ -36,6 +36,12 @@ 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..1fe8ba0 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); } @@ -90,7 +116,9 @@ static void SV_BotUserMove_Stub(client_t *cl) ? g_botai[clientNum].weapon : static_cast(level->clients[clientNum].ps.weapCommon.weapon); cmd.primaryWeaponForAltMode = - static_cast(level->clients[clientNum].ps.weapCommon.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 +306,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 +360,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..233ebd9 100644 --- a/src/game/iw4/mp_tu6/symbols.h +++ b/src/game/iw4/mp_tu6/symbols.h @@ -230,6 +230,15 @@ 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