diff --git a/src/game/iw3/mp/components/cj_tas.cpp b/src/game/iw3/mp/components/cj_tas.cpp index e754597..146e644 100644 --- a/src/game/iw3/mp/components/cj_tas.cpp +++ b/src/game/iw3/mp/components/cj_tas.cpp @@ -2,9 +2,6 @@ #include "events.h" #include "cj_tas.h" -#define ANGLE2SHORT(x) ((int)((x) * 65536 / 360) & 65535) -#define SHORT2ANGLE(x) ((x) * (360.0 / 65536)) - namespace iw3 { namespace mp @@ -97,7 +94,7 @@ void Cmd_Startplayback_f() is_playing = true; playback_start_time = 0; // Will be set on first UpdateCommand recording_start_time = current_recording[0].serverTime; - CG_GameMessage(0, "Playback ^2started\n"); + // CG_GameMessage(0, "Playback ^2started\n"); } void Cmd_Stopplayback_f() @@ -110,7 +107,7 @@ void Cmd_Stopplayback_f() play_frame = 0; is_playing = false; - CG_GameMessage(0, "Playback ^1stopped\n"); + // CG_GameMessage(0, "Playback ^1stopped\n"); } bool IsPlayback() @@ -404,14 +401,14 @@ cj_tas::cj_tas() cj_tas_rpg_lookdown_pitch = Dvar_RegisterInt("cj_tas_rpg_lookdown_pitch", 70, -70, 70, 0, "RPG lookdown pitch angle"); - Events::OnCG_DrawActive( - []() - { - if (cj_tas::TAS_Enabled()) - { - CG_DrawTAS(); - } - }); + // Events::OnCG_DrawActive( + // []() + // { + // if (cj_tas::TAS_Enabled()) + // { + // CG_DrawTAS(); + // } + // }); Events::OnCG_Init( []() diff --git a/src/game/iw3/mp/components/sv_bots.cpp b/src/game/iw3/mp/components/sv_bots.cpp index bb0a793..d252882 100644 --- a/src/game/iw3/mp/components/sv_bots.cpp +++ b/src/game/iw3/mp/components/sv_bots.cpp @@ -32,6 +32,8 @@ namespace mp struct BotMovementInfo_t { int buttons; + bool is_mirroring_client; + int mirror_client_num; }; BotMovementInfo_t g_botai[MAX_CLIENTS]; @@ -60,6 +62,23 @@ void SV_BotUserMove_Stub(client_t *cl) if (g_clients[clientNum].sess.archiveTime == 0) { cmd.buttons = g_botai[clientNum].buttons; + + // Handle mirrored mode + // TODO: fix angles? + if (g_botai[clientNum].is_mirroring_client) + { + const int mirror_num = g_botai[clientNum].mirror_client_num; + if (mirror_num < MAX_CLIENTS) + { + const usercmd_s &lastUsercmd = svsHeader->clients[mirror_num].lastUsercmd; + cmd.buttons = lastUsercmd.buttons; + cmd.angles[PITCH] = lastUsercmd.angles[PITCH]; + cmd.angles[YAW] = lastUsercmd.angles[YAW]; + // Ignore ROLL + cmd.forwardmove = lastUsercmd.forwardmove; + cmd.rightmove = lastUsercmd.rightmove; + } + } } cl->header.deltaMessage = cl->header.netchan.outgoingSequence - 1; @@ -121,6 +140,29 @@ static void Scr_BotStop(scr_entref_t entref) Scr_Error("Usage: botStop();"); g_botai[entref.entnum].buttons = 0; + g_botai[entref.entnum].is_mirroring_client = false; +} + +static void Scr_BotMirror(scr_entref_t entref) +{ + // Validate self is a player entity + GetPlayerEntity(entref); + + if (Scr_GetNumParam() != 1) + Scr_Error("Usage: BotMirror();"); + + const gentity_s *targetEntity = Scr_GetEntity(0); + if (!targetEntity->client) + Scr_Error("not a player"); + + const int clientNum = targetEntity->client - g_clients; + if (entref.entnum == clientNum) + { + Scr_Error("BotMirror: a bot cannot mirror itself."); + } + + g_botai[entref.entnum].is_mirroring_client = true; + g_botai[entref.entnum].mirror_client_num = clientNum; } sv_bots::sv_bots() @@ -129,6 +171,7 @@ sv_bots::sv_bots() SV_BotUserMove_Detour.Install(); Scr_AddMethod("botaction", Scr_BotAction, 0); + Scr_AddMethod("botmirror", Scr_BotMirror, 0); Scr_AddMethod("botstop", Scr_BotStop, 0); Events::OnVMShutdown(CleanBotArray); diff --git a/src/game/iw3/mp/symbols.h b/src/game/iw3/mp/symbols.h index e42e4aa..36dd31f 100644 --- a/src/game/iw3/mp/symbols.h +++ b/src/game/iw3/mp/symbols.h @@ -6,6 +6,9 @@ namespace iw3 { namespace mp { +#define ANGLE2SHORT(x) ((int)((x) * 65536 / 360) & 65535) +#define SHORT2ANGLE(x) ((x) * (360.0 / 65536)) + // Functions static auto AngleDelta = reinterpret_cast(0x821DABC0); static auto AngleNormalize180 = reinterpret_cast(0x820A0088); @@ -135,7 +138,7 @@ static auto Scr_AddArray = reinterpret_cast(0x82210538); static auto Scr_AddInt = reinterpret_cast(0x822111C0); static auto Scr_AddString = reinterpret_cast(0x82210F28); static auto Scr_Error = reinterpret_cast(0x8220F6F0); -static auto Scr_GetEntity = reinterpret_cast(0x8224EE68); +static auto Scr_GetEntity = reinterpret_cast(0x8224EE68); static auto Scr_GetFunction = reinterpret_cast(0x82256ED0); static auto Scr_GetInt = reinterpret_cast(0x8220FD10); static auto Scr_GetString = reinterpret_cast(0x82211390);