summaryrefslogtreecommitdiff
path: root/sourcemod/scripting/gokz-replays/playback.sp
diff options
context:
space:
mode:
Diffstat (limited to 'sourcemod/scripting/gokz-replays/playback.sp')
-rw-r--r--sourcemod/scripting/gokz-replays/playback.sp1501
1 files changed, 0 insertions, 1501 deletions
diff --git a/sourcemod/scripting/gokz-replays/playback.sp b/sourcemod/scripting/gokz-replays/playback.sp
deleted file mode 100644
index b3f6865..0000000
--- a/sourcemod/scripting/gokz-replays/playback.sp
+++ /dev/null
@@ -1,1501 +0,0 @@
-/*
- Bot replay playback logic and processes.
-
- The recorded files are read and their information and tick data
- stored into variables. A bot is then used to playback the recorded
- data by setting it's origin, velocity, etc. in OnPlayerRunCmd.
-*/
-
-
-
-static int preAndPostRunTickCount;
-
-static int playbackTick[RP_MAX_BOTS];
-static ArrayList playbackTickData[RP_MAX_BOTS];
-static bool inBreather[RP_MAX_BOTS];
-static float breatherStartTime[RP_MAX_BOTS];
-
-// Original bot caller, needed for OnClientPutInServer callback
-static int botCaller[RP_MAX_BOTS];
-// Original bot name after creation by bot_add, needed for bot removal
-static char botName[RP_MAX_BOTS][MAX_NAME_LENGTH];
-static bool botInGame[RP_MAX_BOTS];
-static int botClient[RP_MAX_BOTS];
-static bool botDataLoaded[RP_MAX_BOTS];
-static int botReplayType[RP_MAX_BOTS];
-static int botReplayVersion[RP_MAX_BOTS];
-static int botSteamAccountID[RP_MAX_BOTS];
-static int botCourse[RP_MAX_BOTS];
-static int botMode[RP_MAX_BOTS];
-static int botStyle[RP_MAX_BOTS];
-static float botTime[RP_MAX_BOTS];
-static int botTimeTicks[RP_MAX_BOTS];
-static char botAlias[RP_MAX_BOTS][MAX_NAME_LENGTH];
-static bool botPaused[RP_MAX_BOTS];
-static bool botPlaybackPaused[RP_MAX_BOTS];
-static int botKnife[RP_MAX_BOTS];
-static int botWeapon[RP_MAX_BOTS];
-static int botJumpType[RP_MAX_BOTS];
-static float botJumpDistance[RP_MAX_BOTS];
-static int botJumpBlockDistance[RP_MAX_BOTS];
-
-static int timeOnGround[RP_MAX_BOTS];
-static int timeInAir[RP_MAX_BOTS];
-static int botTeleportsUsed[RP_MAX_BOTS];
-static int botCurrentTeleport[RP_MAX_BOTS];
-static int botButtons[RP_MAX_BOTS];
-static MoveType botMoveType[RP_MAX_BOTS];
-static float botTakeoffSpeed[RP_MAX_BOTS];
-static float botSpeed[RP_MAX_BOTS];
-static float botLastOrigin[RP_MAX_BOTS][3];
-static bool hitBhop[RP_MAX_BOTS];
-static bool hitPerf[RP_MAX_BOTS];
-static bool botJumped[RP_MAX_BOTS];
-static bool botIsTakeoff[RP_MAX_BOTS];
-static bool botJustTeleported[RP_MAX_BOTS];
-static float botLandingSpeed[RP_MAX_BOTS];
-
-
-
-// =====[ PUBLIC ]=====
-
-// Returns the client index of the replay bot, or -1 otherwise
-int LoadReplayBot(int client, char[] path)
-{
- // Safeguard Check
- if (GOKZ_GetCoreOption(client, Option_Safeguard) > Safeguard_Disabled && GOKZ_GetTimerRunning(client) && GOKZ_GetValidTimer(client))
- {
- if (!GOKZ_GetPaused(client) && !GOKZ_GetCanPause(client))
- {
- GOKZ_PrintToChat(client, true, "%t", "Safeguard - Blocked");
- GOKZ_PlayErrorSound(client);
- return -1;
- }
- }
- int bot;
- if (GetBotsInUse() < RP_MAX_BOTS)
- {
- bot = GetUnusedBot();
- }
- else
- {
- GOKZ_PrintToChat(client, true, "%t", "No Bots Available");
- GOKZ_PlayErrorSound(client);
- return -1;
- }
-
- if (bot == -1)
- {
- LogError("Unused bot could not be found even though only %d out of %d are known to be in use.",
- GetBotsInUse(), RP_MAX_BOTS);
- GOKZ_PlayErrorSound(client);
- return -1;
- }
-
- if (!LoadPlayback(client, bot, path))
- {
- GOKZ_PlayErrorSound(client);
- return -1;
- }
-
- ServerCommand("bot_add");
- botCaller[bot] = client;
- return botClient[bot];
-}
-
-// Passes the current state of the replay into the HUDInfo struct
-void GetPlaybackState(int client, HUDInfo info)
-{
- int bot, i;
- for(i = 0; i < RP_MAX_BOTS; i++)
- {
- bot = botClient[i] == client ? i : bot;
- }
- if (i == RP_MAX_BOTS + 1) return;
-
- if (playbackTickData[bot] == INVALID_HANDLE)
- {
- return;
- }
-
- info.TimerRunning = botReplayType[bot] == ReplayType_Jump ? false : true;
- if (botReplayVersion[bot] == 1)
- {
- info.Time = playbackTick[bot] * GetTickInterval();
- }
- else if (botReplayVersion[bot] == 2)
- {
- if (playbackTick[bot] < preAndPostRunTickCount)
- {
- info.Time = 0.0;
- }
- else if (playbackTick[bot] >= playbackTickData[bot].Length - preAndPostRunTickCount)
- {
- info.Time = botTime[bot];
- }
- else if (playbackTick[bot] >= preAndPostRunTickCount)
- {
- info.Time = (playbackTick[bot] - preAndPostRunTickCount) * GetTickInterval();
- }
- }
- info.TimerRunning = true;
- info.TimeType = botTeleportsUsed[bot] > 0 ? TimeType_Nub : TimeType_Pro;
- info.Speed = botSpeed[bot];
- info.Paused = false;
- info.OnLadder = (botMoveType[bot] == MOVETYPE_LADDER);
- info.Noclipping = false;
- info.OnGround = Movement_GetOnGround(client);
- info.Ducking = botButtons[bot] & IN_DUCK > 0;
- info.ID = botClient[bot];
- info.Jumped = botJumped[bot];
- info.HitBhop = hitBhop[bot];
- info.HitPerf = hitPerf[bot];
- info.Buttons = botButtons[bot];
- info.TakeoffSpeed = botTakeoffSpeed[bot];
- info.IsTakeoff = botIsTakeoff[bot] && !Movement_GetOnGround(client);
- info.CurrentTeleport = botCurrentTeleport[bot];
-}
-
-int GetBotFromClient(int client)
-{
- for (int bot = 0; bot < RP_MAX_BOTS; bot++)
- {
- if (botClient[bot] == client)
- {
- return bot;
- }
- }
- return -1;
-}
-
-bool InBreather(int bot)
-{
- return inBreather[bot];
-}
-
-bool PlaybackPaused(int bot)
-{
- return botPlaybackPaused[bot];
-}
-
-void PlaybackTogglePause(int bot)
-{
- if(botPlaybackPaused[bot])
- {
- botPlaybackPaused[bot] = false;
- }
- else
- {
- botPlaybackPaused[bot] = true;
- }
-}
-
-void PlaybackSkipForward(int bot)
-{
- if (playbackTick[bot] + RoundToZero(RP_SKIP_TIME / GetTickInterval()) < playbackTickData[bot].Length)
- {
- PlaybackSkipToTick(bot, playbackTick[bot] + RoundToZero(RP_SKIP_TIME / GetTickInterval()));
- }
-}
-
-void PlaybackSkipBack(int bot)
-{
- if (playbackTick[bot] < RoundToZero(RP_SKIP_TIME / GetTickInterval()))
- {
- PlaybackSkipToTick(bot, 0);
- }
- else
- {
- PlaybackSkipToTick(bot, playbackTick[bot] - RoundToZero(RP_SKIP_TIME / GetTickInterval()));
- }
-}
-
-int PlaybackGetTeleports(int bot)
-{
- return botCurrentTeleport[bot];
-}
-
-void TrySkipToTime(int client, int seconds)
-{
- if (!IsValidClient(client))
- {
- return;
- }
-
- int tick = seconds * 128 + preAndPostRunTickCount;
- int bot = GetBotFromClient(GetObserverTarget(client));
-
- if (tick >= 0 && tick < playbackTickData[bot].Length)
- {
- PlaybackSkipToTick(bot, tick);
- }
- else
- {
- GOKZ_PrintToChat(client, true, "%t", "Replay Controls - Invalid Time");
- }
-}
-
-float GetPlaybackTime(int bot)
-{
- if (playbackTick[bot] < preAndPostRunTickCount)
- {
- return 0.0;
- }
- if (playbackTick[bot] >= playbackTickData[bot].Length - preAndPostRunTickCount)
- {
- return botTime[bot];
- }
- if (playbackTick[bot] >= preAndPostRunTickCount)
- {
- return (playbackTick[bot] - preAndPostRunTickCount) * GetTickInterval();
- }
-
- return 0.0;
-}
-
-
-
-// =====[ EVENTS ]=====
-
-void OnClientPutInServer_Playback(int client)
-{
- if (!IsFakeClient(client) || IsClientSourceTV(client))
- {
- return;
- }
-
- // Check if an unassigned bot has joined, and assign it
- for (int bot; bot < RP_MAX_BOTS; bot++)
- {
- // Also check if the bot was created by us.
- if (!botInGame[bot] && botCaller[bot] != 0)
- {
- botInGame[bot] = true;
- botClient[bot] = client;
- GetClientName(client, botName[bot], sizeof(botName[]));
- // The bot won't receive its weapons properly if we don't wait a frame
- RequestFrame(SetBotStuff, bot);
- if (IsValidClient(botCaller[bot]))
- {
- MakePlayerSpectate(botCaller[bot], botClient[bot]);
- botCaller[bot] = 0;
- }
- break;
- }
- }
-}
-
-void OnClientDisconnect_Playback(int client)
-{
- for (int bot; bot < RP_MAX_BOTS; bot++)
- {
- if (botClient[bot] != client)
- {
- continue;
- }
-
- botInGame[bot] = false;
- if (playbackTickData[bot] != null)
- {
- playbackTickData[bot].Clear(); // Clear it all out
- botDataLoaded[bot] = false;
- }
- }
-}
-
-void OnPlayerRunCmd_Playback(int client, int &buttons, float vel[3], float angles[3])
-{
- if (!IsFakeClient(client))
- {
- return;
- }
-
- for (int bot; bot < RP_MAX_BOTS; bot++)
- {
- // Check if not the bot we're looking for
- if (!botInGame[bot] || botClient[bot] != client || !botDataLoaded[bot])
- {
- continue;
- }
-
- switch (botReplayVersion[bot])
- {
- case 1: PlaybackVersion1(client, bot, buttons);
- case 2: PlaybackVersion2(client, bot, buttons, vel, angles);
- }
- break;
- }
-}
-
-void OnPlayerRunCmdPost_Playback(int client)
-{
- for (int bot; bot < RP_MAX_BOTS; bot++)
- {
- // Check if not the bot we're looking for
- if (!botInGame[bot] || botClient[bot] != client || !botDataLoaded[bot])
- {
- continue;
- }
- if (botReplayVersion[bot] == 2)
- {
- PlaybackVersion2Post(client, bot);
- }
- break;
- }
-}
-
-void GOKZ_OnOptionsLoaded_Playback(int client)
-{
- for (int bot = 0; bot < RP_MAX_BOTS; bot++)
- {
- if (botClient[bot] == client)
- {
- // Reset its movement options as it might be wrongfully changed
- GOKZ_SetCoreOption(client, Option_Mode, botMode[bot]);
- GOKZ_SetCoreOption(client, Option_Style, botStyle[bot]);
- }
- }
-}
-// =====[ PRIVATE ]=====
-
-// Returns false if there was a problem loading the playback e.g. doesn't exist
-static bool LoadPlayback(int client, int bot, char[] path)
-{
- if (!FileExists(path))
- {
- GOKZ_PrintToChat(client, true, "%t", "No Replay Found");
- return false;
- }
-
- File file = OpenFile(path, "rb");
-
- // Check magic number in header
- int magicNumber;
- file.ReadInt32(magicNumber);
- if (magicNumber != RP_MAGIC_NUMBER)
- {
- LogError("Failed to load invalid replay file: \"%s\".", path);
- delete file;
- return false;
- }
-
- // Check replay format version
- int formatVersion;
- file.ReadInt8(formatVersion);
- switch(formatVersion)
- {
- case 1:
- {
- botReplayVersion[bot] = 1;
- if (!LoadFormatVersion1Replay(file, bot))
- {
- return false;
- }
- }
- case 2:
- {
- botReplayVersion[bot] = 2;
- if (!LoadFormatVersion2Replay(file, client, bot))
- {
- return false;
- }
- }
-
- default:
- {
- LogError("Failed to load replay file with unsupported format version: \"%s\".", path);
- delete file;
- return false;
- }
- }
-
- return true;
-}
-
-static bool LoadFormatVersion1Replay(File file, int bot)
-{
- // Old replays only support runs, not jumps
- botReplayType[bot] = ReplayType_Run;
-
- int length;
-
- // GOKZ version
- file.ReadInt8(length);
- char[] gokzVersion = new char[length + 1];
- file.ReadString(gokzVersion, length, length);
- gokzVersion[length] = '\0';
-
- // Map name
- file.ReadInt8(length);
- char[] mapName = new char[length + 1];
- file.ReadString(mapName, length, length);
- mapName[length] = '\0';
-
- // Some integers...
- file.ReadInt32(botCourse[bot]);
- file.ReadInt32(botMode[bot]);
- file.ReadInt32(botStyle[bot]);
-
- // Old replays don't store the weapon information
- botKnife[bot] = CS_WeaponIDToItemDefIndex(CSWeapon_KNIFE);
- botWeapon[bot] = (botMode[bot] == Mode_Vanilla) ? -1 : CS_WeaponIDToItemDefIndex(CSWeapon_USP_SILENCER);
-
- // Time
- int timeAsInt;
- file.ReadInt32(timeAsInt);
- botTime[bot] = view_as<float>(timeAsInt);
-
- // Some integers...
- file.ReadInt32(botTeleportsUsed[bot]);
- file.ReadInt32(botSteamAccountID[bot]);
-
- // SteamID2
- file.ReadInt8(length);
- char[] steamID2 = new char[length + 1];
- file.ReadString(steamID2, length, length);
- steamID2[length] = '\0';
-
- // IP
- file.ReadInt8(length);
- char[] IP = new char[length + 1];
- file.ReadString(IP, length, length);
- IP[length] = '\0';
-
- // Alias
- file.ReadInt8(length);
- file.ReadString(botAlias[bot], sizeof(botAlias[]), length);
- botAlias[bot][length] = '\0';
-
- // Read tick data
- file.ReadInt32(length);
-
- // Setup playback tick data array list
- if (playbackTickData[bot] == null)
- {
- playbackTickData[bot] = new ArrayList(IntMax(RP_V1_TICK_DATA_BLOCKSIZE, sizeof(ReplayTickData)), length);
- }
- else
- { // Make sure it's all clear and the correct size
- playbackTickData[bot].Clear();
- playbackTickData[bot].Resize(length);
- }
-
- // The replay has no replay data, this shouldn't happen normally,
- // but this would cause issues in other code, so we don't even try to load this.
- if (length == 0)
- {
- delete file;
- return false;
- }
-
- any tickData[RP_V1_TICK_DATA_BLOCKSIZE];
- for (int i = 0; i < length; i++)
- {
- file.Read(tickData, RP_V1_TICK_DATA_BLOCKSIZE, 4);
- playbackTickData[bot].Set(i, view_as<float>(tickData[0]), 0); // origin[0]
- playbackTickData[bot].Set(i, view_as<float>(tickData[1]), 1); // origin[1]
- playbackTickData[bot].Set(i, view_as<float>(tickData[2]), 2); // origin[2]
- playbackTickData[bot].Set(i, view_as<float>(tickData[3]), 3); // angles[0]
- playbackTickData[bot].Set(i, view_as<float>(tickData[4]), 4); // angles[1]
- playbackTickData[bot].Set(i, view_as<int>(tickData[5]), 5); // buttons
- playbackTickData[bot].Set(i, view_as<int>(tickData[6]), 6); // flags
- }
-
- playbackTick[bot] = 0;
- botDataLoaded[bot] = true;
-
- delete file;
- return true;
-}
-
-static bool LoadFormatVersion2Replay(File file, int client, int bot)
-{
- int length;
-
- // Replay type
- int replayType;
- file.ReadInt8(replayType);
-
- // GOKZ version
- file.ReadInt8(length);
- char[] gokzVersion = new char[length + 1];
- file.ReadString(gokzVersion, length, length);
- gokzVersion[length] = '\0';
-
- // Map name
- file.ReadInt8(length);
- char[] mapName = new char[length + 1];
- file.ReadString(mapName, length, length);
- mapName[length] = '\0';
- if (!StrEqual(mapName, gC_CurrentMap))
- {
- GOKZ_PrintToChat(client, true, "%t", "Replay Menu - Wrong Map", mapName);
- delete file;
- return false;
- }
-
- // Map filesize
- int mapFileSize;
- file.ReadInt32(mapFileSize);
-
- // Server IP
- int serverIP;
- file.ReadInt32(serverIP);
-
- // Timestamp
- int timestamp;
- file.ReadInt32(timestamp);
-
- // Player Alias
- file.ReadInt8(length);
- file.ReadString(botAlias[bot], sizeof(botAlias[]), length);
- botAlias[bot][length] = '\0';
-
- // Player Steam ID
- int steamID;
- file.ReadInt32(steamID);
-
- // Mode
- file.ReadInt8(botMode[bot]);
-
- // Style
- file.ReadInt8(botStyle[bot]);
-
- // Player Sensitivity
- int intPlayerSensitivity;
- file.ReadInt32(intPlayerSensitivity);
- float playerSensitivity = view_as<float>(intPlayerSensitivity);
-
- // Player MYAW
- int intPlayerMYaw;
- file.ReadInt32(intPlayerMYaw);
- float playerMYaw = view_as<float>(intPlayerMYaw);
-
- // Tickrate
- int tickrateAsInt;
- file.ReadInt32(tickrateAsInt);
- float tickrate = view_as<float>(tickrateAsInt);
- if (tickrate != RoundToZero(1 / GetTickInterval()))
- {
- GOKZ_PrintToChat(client, true, "%t", "Replay Menu - Wrong Tickrate", tickrate, (RoundToZero(1 / GetTickInterval())));
- delete file;
- return false;
- }
-
- // Tick Count
- int tickCount;
- file.ReadInt32(tickCount);
-
- // The replay has no replay data, this shouldn't happen normally,
- // but this would cause issues in other code, so we don't even try to load this.
- if (tickCount == 0)
- {
- delete file;
- return false;
- }
-
- // Equipped Weapon
- file.ReadInt32(botWeapon[bot]);
-
- // Equipped Knife
- file.ReadInt32(botKnife[bot]);
-
- // Big spit to console
- PrintToConsole(client, "Replay Type: %d\nGOKZ Version: %s\nMap Name: %s\nMap Filesize: %d\nServer IP: %d\nTimestamp: %d\nPlayer Alias: %s\nPlayer Steam ID: %d\nMode: %d\nStyle: %d\nPlayer Sensitivity: %f\nPlayer m_yaw: %f\nTickrate: %f\nTick Count: %d\nWeapon: %d\nKnife: %d", replayType, gokzVersion, mapName, mapFileSize, serverIP, timestamp, botAlias[bot], steamID, botMode[bot], botStyle[bot], playerSensitivity, playerMYaw, tickrate, tickCount, botWeapon[bot], botKnife[bot]);
-
- switch(replayType)
- {
- case ReplayType_Run:
- {
- // Time
- int timeAsInt;
- file.ReadInt32(timeAsInt);
- botTime[bot] = view_as<float>(timeAsInt);
- botTimeTicks[bot] = RoundToNearest(botTime[bot] * tickrate);
-
- // Course
- file.ReadInt8(botCourse[bot]);
-
- // Teleports Used
- file.ReadInt32(botTeleportsUsed[bot]);
-
- // Type
- botReplayType[bot] = ReplayType_Run;
-
- // Finish spit to console
- PrintToConsole(client, "Time: %f\nCourse: %d\nTeleports Used: %d", botTime[bot], botCourse[bot], botTeleportsUsed[bot]);
- }
- case ReplayType_Cheater:
- {
- // Reason
- int reason;
- file.ReadInt8(reason);
-
- // Type
- botReplayType[bot] = ReplayType_Cheater;
-
- // Finish spit to console
- PrintToConsole(client, "AC Reason: %s", gC_ACReasons[reason]);
- }
- case ReplayType_Jump:
- {
- // Jump Type
- file.ReadInt8(botJumpType[bot]);
-
- // Distance
- file.ReadInt32(view_as<int>(botJumpDistance[bot]));
-
- // Block Distance
- file.ReadInt32(botJumpBlockDistance[bot]);
-
- // Strafe Count
- int strafeCount;
- file.ReadInt8(strafeCount);
-
- // Sync
- float sync;
- file.ReadInt32(view_as<int>(sync));
-
- // Pre
- float pre;
- file.ReadInt32(view_as<int>(pre));
-
- // Max
- float max;
- file.ReadInt32(view_as<int>(max));
-
- // Airtime
- int airtime;
- file.ReadInt32(airtime);
-
- // Type
- botReplayType[bot] = ReplayType_Jump;
-
- // Finish spit to console
- PrintToConsole(client, "Jump Type: %s\nJump Distance: %f\nBlock Distance: %d\nStrafe Count: %d\nSync: %f\n Pre: %f\nMax: %f\nAirtime: %d",
- gC_JumpTypes[botJumpType[bot]], botJumpDistance[bot], botJumpBlockDistance[bot], strafeCount, sync, pre, max, airtime);
- }
- }
-
- // Tick Data
- // Setup playback tick data array list
- if (playbackTickData[bot] == null)
- {
- playbackTickData[bot] = new ArrayList(IntMax(RP_V1_TICK_DATA_BLOCKSIZE, sizeof(ReplayTickData)));
- }
- else
- {
- playbackTickData[bot].Clear();
- }
-
- // Read tick data
- preAndPostRunTickCount = RoundToZero(RP_PLAYBACK_BREATHER_TIME / GetTickInterval());
- any tickDataArray[RP_V2_TICK_DATA_BLOCKSIZE];
- for (int i = 0; i < tickCount; i++)
- {
- file.ReadInt32(tickDataArray[RPDELTA_DELTAFLAGS]);
-
- for (int index = 1; index < sizeof(tickDataArray); index++)
- {
- int currentFlag = (1 << index);
- if (tickDataArray[RPDELTA_DELTAFLAGS] & currentFlag)
- {
- file.ReadInt32(tickDataArray[index]);
- }
- }
-
- ReplayTickData tickData;
- TickDataFromArray(tickDataArray, tickData);
- // HACK: Jump replays don't record proper length sometimes. I don't know why.
- // This leads to oversized replays full of 0s at the end.
- // So, we do this horrible check to dodge that issue.
- if (tickData.origin[0] == 0 && tickData.origin[1] == 0 && tickData.origin[2] == 0 && tickData.angles[0] == 0 && tickData.angles[1] == 0)
- {
- break;
- }
- playbackTickData[bot].PushArray(tickData);
- }
-
- playbackTick[bot] = 0;
- botDataLoaded[bot] = true;
-
- delete file;
-
- return true;
-}
-
-static void PlaybackVersion1(int client, int bot, int &buttons)
-{
- int size = playbackTickData[bot].Length;
- float repOrigin[3], repAngles[3];
- int repButtons, repFlags;
-
- // If first or last frame of the playback
- if (playbackTick[bot] == 0 || playbackTick[bot] == (size - 1))
- {
- // Move the bot and pause them at that tick
- repOrigin[0] = playbackTickData[bot].Get(playbackTick[bot], 0);
- repOrigin[1] = playbackTickData[bot].Get(playbackTick[bot], 1);
- repOrigin[2] = playbackTickData[bot].Get(playbackTick[bot], 2);
- repAngles[0] = playbackTickData[bot].Get(playbackTick[bot], 3);
- repAngles[1] = playbackTickData[bot].Get(playbackTick[bot], 4);
- TeleportEntity(client, repOrigin, repAngles, view_as<float>( { 0.0, 0.0, 0.0 } ));
-
- if (!inBreather[bot])
- {
- // Start the breather period
- inBreather[bot] = true;
- breatherStartTime[bot] = GetEngineTime();
- if (playbackTick[bot] == (size - 1))
- {
- GOKZ_EmitSoundToClientSpectators(client, gC_ModeEndSounds[GOKZ_GetCoreOption(client, Option_Mode)], _, "Timer End");
- }
- }
- else if (GetEngineTime() > breatherStartTime[bot] + RP_PLAYBACK_BREATHER_TIME)
- {
- // End the breather period
- inBreather[bot] = false;
- botPlaybackPaused[bot] = false;
- if (playbackTick[bot] == 0)
- {
- GOKZ_EmitSoundToClientSpectators(client, gC_ModeStartSounds[GOKZ_GetCoreOption(client, Option_Mode)], _, "Timer Start");
- }
- // Start the bot if first tick. Clear bot if last tick.
- playbackTick[bot]++;
- if (playbackTick[bot] == size)
- {
- playbackTickData[bot].Clear(); // Clear it all out
- botDataLoaded[bot] = false;
- CancelReplayControlsForBot(bot);
- ServerCommand("bot_kick %s", botName[bot]);
- }
- }
- }
- else
- {
- // Check whether somebody is actually spectating the bot
- int spec;
- for (spec = 1; spec < MAXPLAYERS + 1; spec++)
- {
- if (IsValidClient(spec) && GetObserverTarget(spec) == botClient[bot])
- {
- break;
- }
- }
- if (spec == MAXPLAYERS + 1 && !IsReplayBotControlled(bot, botClient[bot]))
- {
- playbackTickData[bot].Clear();
- botDataLoaded[bot] = false;
- CancelReplayControlsForBot(bot);
- ServerCommand("bot_kick %s", botName[bot]);
- return;
- }
-
- // Load in the next tick
- repOrigin[0] = playbackTickData[bot].Get(playbackTick[bot], 0);
- repOrigin[1] = playbackTickData[bot].Get(playbackTick[bot], 1);
- repOrigin[2] = playbackTickData[bot].Get(playbackTick[bot], 2);
- repAngles[0] = playbackTickData[bot].Get(playbackTick[bot], 3);
- repAngles[1] = playbackTickData[bot].Get(playbackTick[bot], 4);
- repButtons = playbackTickData[bot].Get(playbackTick[bot], 5);
- repFlags = playbackTickData[bot].Get(playbackTick[bot], 6);
-
- // Check if the replay is paused
- if (botPlaybackPaused[bot])
- {
- TeleportEntity(client, repOrigin, repAngles, view_as<float>( { 0.0, 0.0, 0.0 } ));
- return;
- }
-
- // Set velocity to travel from current origin to recorded origin
- float currentOrigin[3], velocity[3];
- Movement_GetOrigin(client, currentOrigin);
- MakeVectorFromPoints(currentOrigin, repOrigin, velocity);
- ScaleVector(velocity, 128.0); // Hard-coded 128 tickrate
- TeleportEntity(client, NULL_VECTOR, repAngles, velocity);
-
- // We need the velocity directly from the replay to calculate the speeds
- // for the HUD.
- MakeVectorFromPoints(botLastOrigin[bot], repOrigin, velocity);
- ScaleVector(velocity, 128.0); // Hard-coded 128 tickrate
- CopyVector(repOrigin, botLastOrigin[bot]);
-
- botSpeed[bot] = GetVectorHorizontalLength(velocity);
- buttons = repButtons;
- botButtons[bot] = repButtons;
-
- // Should the bot be ducking?!
- if (repButtons & IN_DUCK || repFlags & FL_DUCKING)
- {
- buttons |= IN_DUCK;
- }
-
- // If the replay file says the bot's on the ground, then fine! Unless you're going too fast...
- // Note that we don't mind if replay file says bot isn't on ground but the bot is.
- if (repFlags & FL_ONGROUND && Movement_GetSpeed(client) < SPEED_NORMAL * 2)
- {
- if (timeInAir[bot] > 0)
- {
- botLandingSpeed[bot] = botSpeed[bot];
- timeInAir[bot] = 0;
- botIsTakeoff[bot] = false;
- botJumped[bot] = false;
- hitBhop[bot] = false;
- hitPerf[bot] = false;
- if (!Movement_GetOnGround(client))
- {
- timeOnGround[bot] = 0;
- }
- }
-
- SetEntityFlags(client, GetEntityFlags(client) | FL_ONGROUND);
- Movement_SetMovetype(client, MOVETYPE_WALK);
-
- timeOnGround[bot]++;
- botTakeoffSpeed[bot] = botSpeed[bot];
- }
- else
- {
- if (timeInAir[bot] == 0)
- {
- botIsTakeoff[bot] = true;
- botJumped[bot] = botButtons[bot] & IN_JUMP > 0;
- hitBhop[bot] = (timeOnGround[bot] <= RP_MAX_BHOP_GROUND_TICKS) && botJumped[bot];
-
- if (botMode[bot] == Mode_SimpleKZ)
- {
- hitPerf[bot] = timeOnGround[bot] < 3 && botJumped[bot];
- }
- else
- {
- hitPerf[bot] = timeOnGround[bot] < 2 && botJumped[bot];
- }
-
- if (hitPerf[bot])
- {
- if (botMode[bot] == Mode_SimpleKZ)
- {
- botTakeoffSpeed[bot] = FloatMin(botLandingSpeed[bot], (0.2 * botLandingSpeed[bot] + 200));
- }
- else if (botMode[bot] == Mode_KZTimer)
- {
- botTakeoffSpeed[bot] = FloatMin(botLandingSpeed[bot], 380.0);
- }
- else
- {
- botTakeoffSpeed[bot] = FloatMin(botLandingSpeed[bot], 286.0);
- }
- }
- }
- else
- {
- botJumped[bot] = false;
- botIsTakeoff[bot] = false;
- }
-
- timeInAir[bot]++;
- Movement_SetMovetype(client, MOVETYPE_NOCLIP);
- }
-
- playbackTick[bot]++;
- }
-}
-void PlaybackVersion2(int client, int bot, int &buttons, float vel[3], float angles[3])
-{
- int size = playbackTickData[bot].Length;
- ReplayTickData prevTickData;
- ReplayTickData currentTickData;
-
- // If first or last frame of the playback
- if (playbackTick[bot] == 0 || playbackTick[bot] == (size - 1))
- {
- // Move the bot and pause them at that tick
- playbackTickData[bot].GetArray(playbackTick[bot], currentTickData);
- playbackTickData[bot].GetArray(IntMax(playbackTick[bot] - 1, 0), prevTickData);
- TeleportEntity(client, currentTickData.origin, currentTickData.angles, view_as<float>( { 0.0, 0.0, 0.0 } ));
-
- if (!inBreather[bot])
- {
- // Start the breather period
- inBreather[bot] = true;
- breatherStartTime[bot] = GetEngineTime();
- }
- else if (GetEngineTime() > breatherStartTime[bot] + RP_PLAYBACK_BREATHER_TIME)
- {
- // End the breather period
- inBreather[bot] = false;
- botPlaybackPaused[bot] = false;
-
- // Start the bot if first tick. Clear bot if last tick.
- playbackTick[bot]++;
- if (playbackTick[bot] == size)
- {
- playbackTickData[bot].Clear(); // Clear it all out
- botDataLoaded[bot] = false;
- CancelReplayControlsForBot(bot);
- ServerCommand("bot_kick %s", botName[bot]);
- }
- }
- }
- else
- {
- // Check whether somebody is actually spectating the bot
- int spec;
- for (spec = 1; spec < MAXPLAYERS + 1; spec++)
- {
- if (IsValidClient(spec) && GetObserverTarget(spec) == botClient[bot])
- {
- break;
- }
- }
- if (spec == MAXPLAYERS + 1 && !IsReplayBotControlled(bot, botClient[bot]))
- {
- playbackTickData[bot].Clear();
- botDataLoaded[bot] = false;
- CancelReplayControlsForBot(bot);
- ServerCommand("bot_kick %s", botName[bot]);
- return;
- }
-
- // Load in the next tick
- playbackTickData[bot].GetArray(playbackTick[bot], currentTickData);
- playbackTickData[bot].GetArray(IntMax(playbackTick[bot] - 1, 0), prevTickData);
-
- // Check if the replay is paused
- if (botPlaybackPaused[bot])
- {
- TeleportEntity(client, currentTickData.origin, currentTickData.angles, view_as<float>( { 0.0, 0.0, 0.0 } ));
- return;
- }
-
- // Play timer start/end sound, if necessary. Reset teleports
- if (playbackTick[bot] == preAndPostRunTickCount && botReplayType[bot] == ReplayType_Run)
- {
- GOKZ_EmitSoundToClientSpectators(client, gC_ModeStartSounds[GOKZ_GetCoreOption(client, Option_Mode)], _, "Timer Start");
- botCurrentTeleport[bot] = 0;
- }
- if (playbackTick[bot] == botTimeTicks[bot] + preAndPostRunTickCount && botReplayType[bot] == ReplayType_Run)
- {
- GOKZ_EmitSoundToClientSpectators(client, gC_ModeEndSounds[GOKZ_GetCoreOption(client, Option_Mode)], _, "Timer End");
- }
- // We use the previous position/velocity data to recreate sounds accurately.
- // This might not be necessary as we already did do this in OnPlayerRunCmdPost of last tick,
- // but we do it again just in case the values don't match up somehow (eg. collision with moving objects?)
- TeleportEntity(client, NULL_VECTOR, prevTickData.angles, prevTickData.velocity);
- // TeleportEntity does not set the absolute origin and velocity so we need to do it
- // to prevent inaccurate eye position interpolation.
- SetEntPropVector(client, Prop_Data, "m_vecVelocity", prevTickData.velocity);
- SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", prevTickData.velocity);
-
- SetEntPropVector(client, Prop_Data, "m_vecAbsOrigin", prevTickData.origin);
- SetEntPropVector(client, Prop_Data, "m_vecOrigin", prevTickData.origin);
-
-
- // Set buttons and potential inputs.
- int newButtons;
- if (currentTickData.flags & RP_IN_ATTACK)
- {
- newButtons |= IN_ATTACK;
- }
- if (currentTickData.flags & RP_IN_ATTACK2)
- {
- newButtons |= IN_ATTACK2;
- }
- if (currentTickData.flags & RP_IN_JUMP)
- {
- newButtons |= IN_JUMP;
- }
- if (currentTickData.flags & RP_IN_DUCK || currentTickData.flags & RP_FL_DUCKING)
- {
- newButtons |= IN_DUCK;
- }
- // Few assumptions here because the replay doesn't track them: Player doesn't use +klook or +strafe.
- // If the assumptions are wrong we will just end up with wrong sound prediction, no big deal.
- if (currentTickData.flags & RP_IN_FORWARD)
- {
- newButtons |= IN_FORWARD;
- vel[0] += RP_PLAYER_ACCELSPEED;
- }
- if (currentTickData.flags & RP_IN_BACK)
- {
- newButtons |= IN_BACK;
- vel[0] -= RP_PLAYER_ACCELSPEED;
- }
- if (currentTickData.flags & RP_IN_MOVELEFT)
- {
- newButtons |= IN_MOVELEFT;
- vel[1] -= RP_PLAYER_ACCELSPEED;
- }
- if (currentTickData.flags & RP_IN_MOVERIGHT)
- {
- newButtons |= IN_MOVERIGHT;
- vel[1] += RP_PLAYER_ACCELSPEED;
- }
- if (currentTickData.flags & RP_IN_LEFT)
- {
- newButtons |= IN_LEFT;
- }
- if (currentTickData.flags & RP_IN_RIGHT)
- {
- newButtons |= IN_RIGHT;
- }
- if (currentTickData.flags & RP_IN_RELOAD)
- {
- newButtons |= IN_RELOAD;
- }
- if (currentTickData.flags & RP_IN_SPEED)
- {
- newButtons |= IN_SPEED;
- }
- buttons = newButtons;
- botButtons[bot] = buttons;
- // The angles might be wrong if the player teleports, but this should only affect sound prediction.
- angles = currentTickData.angles;
-
- // Set the bot's MoveType
- MoveType replayMoveType = view_as<MoveType>(prevTickData.flags & RP_MOVETYPE_MASK);
- botMoveType[bot] = replayMoveType;
- if (replayMoveType == MOVETYPE_WALK)
- {
- Movement_SetMovetype(client, MOVETYPE_WALK);
- }
- else if (replayMoveType == MOVETYPE_LADDER)
- {
- botPaused[bot] = false;
- Movement_SetMovetype(client, MOVETYPE_LADDER);
- }
- else
- {
- Movement_SetMovetype(client, MOVETYPE_NOCLIP);
- }
- // Set some variables
- if (currentTickData.flags & RP_TELEPORT_TICK)
- {
- botJustTeleported[bot] = true;
- botCurrentTeleport[bot]++;
- }
-
- if (currentTickData.flags & RP_TAKEOFF_TICK)
- {
- hitPerf[bot] = currentTickData.flags & RP_HIT_PERF > 0;
- botIsTakeoff[bot] = true;
- botTakeoffSpeed[bot] = GetVectorHorizontalLength(currentTickData.velocity);
- }
-
- if ((currentTickData.flags & RP_SECONDARY_EQUIPPED) && !IsCurrentWeaponSecondary(client))
- {
- int item = GetPlayerWeaponSlot(client, CS_SLOT_SECONDARY);
- if (item != -1)
- {
- char name[64];
- GetEntityClassname(item, name, sizeof(name));
- FakeClientCommand(client, "use %s", name);
- }
- }
- else if (!(currentTickData.flags & RP_SECONDARY_EQUIPPED) && IsCurrentWeaponSecondary(client))
- {
- int item = GetPlayerWeaponSlot(client, CS_SLOT_KNIFE);
- if (item != -1)
- {
- char name[64];
- GetEntityClassname(item, name, sizeof(name));
- FakeClientCommand(client, "use %s", name);
- }
- }
-
- #if defined DEBUG
- if(!botPlaybackPaused[bot])
- {
- PrintToServer("Tick: %d", playbackTick[bot]);
- PrintToServer("X %f \nY %f \nZ %f\nPitch %f\nYaw %f", currentTickData.origin[0], currentTickData.origin[1], currentTickData.origin[2], currentTickData.angles[0], currentTickData.angles[1]);
- if(currentTickData.flags & RP_MOVETYPE_MASK == view_as<int>(MOVETYPE_WALK)) PrintToServer("MOVETYPE_WALK");
- if(currentTickData.flags & RP_MOVETYPE_MASK == view_as<int>(MOVETYPE_LADDER)) PrintToServer("MOVETYPE_LADDER");
- if(currentTickData.flags & RP_MOVETYPE_MASK == view_as<int>(MOVETYPE_NOCLIP)) PrintToServer("MOVETYPE_NOCLIP");
- if(currentTickData.flags & RP_MOVETYPE_MASK == view_as<int>(MOVETYPE_NOCLIP)) PrintToServer("MOVETYPE_NONE");
-
- if(currentTickData.flags & RP_IN_ATTACK) PrintToServer("IN_ATTACK");
- if(currentTickData.flags & RP_IN_ATTACK2) PrintToServer("IN_ATTACK2");
- if(currentTickData.flags & RP_IN_JUMP) PrintToServer("IN_JUMP");
- if(currentTickData.flags & RP_IN_DUCK) PrintToServer("IN_DUCK");
- if(currentTickData.flags & RP_IN_FORWARD) PrintToServer("IN_FORWARD");
- if(currentTickData.flags & RP_IN_BACK) PrintToServer("IN_BACK");
- if(currentTickData.flags & RP_IN_LEFT) PrintToServer("IN_LEFT");
- if(currentTickData.flags & RP_IN_RIGHT) PrintToServer("IN_RIGHT");
- if(currentTickData.flags & RP_IN_MOVELEFT) PrintToServer("IN_MOVELEFT");
- if(currentTickData.flags & RP_IN_MOVERIGHT) PrintToServer("IN_MOVERIGHT");
- if(currentTickData.flags & RP_IN_RELOAD) PrintToServer("IN_RELOAD");
- if(currentTickData.flags & RP_IN_SPEED) PrintToServer("IN_SPEED");
- if(currentTickData.flags & RP_IN_USE) PrintToServer("IN_USE");
- if(currentTickData.flags & RP_IN_BULLRUSH) PrintToServer("IN_BULLRUSH");
-
- if(currentTickData.flags & RP_FL_ONGROUND) PrintToServer("FL_ONGROUND");
- if(currentTickData.flags & RP_FL_DUCKING ) PrintToServer("FL_DUCKING");
- if(currentTickData.flags & RP_FL_SWIM) PrintToServer("FL_SWIM");
- if(currentTickData.flags & RP_UNDER_WATER) PrintToServer("WATERLEVEL!=0");
- if(currentTickData.flags & RP_TELEPORT_TICK) PrintToServer("TELEPORT");
- if(currentTickData.flags & RP_TAKEOFF_TICK) PrintToServer("TAKEOFF");
- if(currentTickData.flags & RP_HIT_PERF) PrintToServer("PERF");
- if(currentTickData.flags & RP_SECONDARY_EQUIPPED) PrintToServer("SECONDARY_WEAPON_EQUIPPED");
- PrintToServer("==============================================================");
- }
- #endif
- }
-}
-
-void PlaybackVersion2Post(int client, int bot)
-{
- if (botPlaybackPaused[bot])
- {
- return;
- }
- int size = playbackTickData[bot].Length;
- if (playbackTick[bot] != 0 && playbackTick[bot] != (size - 1))
- {
- ReplayTickData currentTickData;
- ReplayTickData prevTickData;
- playbackTickData[bot].GetArray(playbackTick[bot], currentTickData);
- playbackTickData[bot].GetArray(IntMax(playbackTick[bot] - 1, 0), prevTickData);
-
- // TeleportEntity does not set the absolute origin and velocity so we need to do it
- // to prevent inaccurate eye position interpolation.
- SetEntPropVector(client, Prop_Data, "m_vecVelocity", currentTickData.velocity);
- SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", currentTickData.velocity);
-
- SetEntPropVector(client, Prop_Data, "m_vecAbsOrigin", currentTickData.origin);
- SetEntPropVector(client, Prop_Data, "m_vecOrigin", currentTickData.origin);
-
- SetEntPropFloat(client, Prop_Send, "m_angEyeAngles[0]", currentTickData.angles[0]);
- SetEntPropFloat(client, Prop_Send, "m_angEyeAngles[1]", currentTickData.angles[1]);
-
- MoveType replayMoveType = view_as<MoveType>(currentTickData.flags & RP_MOVETYPE_MASK);
- botMoveType[bot] = replayMoveType;
- int entityFlags = GetEntityFlags(client);
- if (replayMoveType == MOVETYPE_WALK)
- {
- if (currentTickData.flags & RP_FL_ONGROUND)
- {
- SetEntityFlags(client, entityFlags | FL_ONGROUND);
- botPaused[bot] = false;
- // The bot is on the ground, so there must be a ground entity attributed to the bot.
- int groundEnt = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity");
- if (groundEnt == -1 && botJustTeleported[bot])
- {
- SetEntPropFloat(client, Prop_Send, "m_flFallVelocity", 0.0);
- float endPosition[3], mins[3], maxs[3];
- GetEntPropVector(client, Prop_Send, "m_vecMaxs", maxs);
- GetEntPropVector(client, Prop_Send, "m_vecMins", mins);
- endPosition = currentTickData.origin;
- endPosition[2] -= 2.0;
- TR_TraceHullFilter(currentTickData.origin, endPosition, mins, maxs, MASK_PLAYERSOLID, TraceEntityFilterPlayers);
-
- // This should always hit.
- if (TR_DidHit())
- {
- groundEnt = TR_GetEntityIndex();
- SetEntPropEnt(client, Prop_Data, "m_hGroundEntity", groundEnt);
- }
- }
- }
- else
- {
- botJustTeleported[bot] = false;
- }
- }
-
- if (currentTickData.flags & RP_UNDER_WATER)
- {
- SetEntityFlags(client, entityFlags | FL_INWATER);
- }
-
- botSpeed[bot] = GetVectorHorizontalLength(currentTickData.velocity);
- playbackTick[bot]++;
- }
-}
-
-// Set the bot client's GOKZ options, clan tag and name based on the loaded replay data
-static void SetBotStuff(int bot)
-{
- if (!botInGame[bot] || !botDataLoaded[bot])
- {
- return;
- }
-
- int client = botClient[bot];
-
- // Set its movement options just in case it could negatively affect the playback
- GOKZ_SetCoreOption(client, Option_Mode, botMode[bot]);
- GOKZ_SetCoreOption(client, Option_Style, botStyle[bot]);
-
- // Clan tag and name
- SetBotClanTag(bot);
- SetBotName(bot);
-
- // Bot takes one tick after being put in server to be able to respawn.
- RequestFrame(RequestFrame_SetBotStuff, GetClientUserId(client));
-}
-
-public void RequestFrame_SetBotStuff(int userid)
-{
- int client = GetClientOfUserId(userid);
- if (!client)
- {
- return;
- }
- int bot;
- for (bot = 0; bot <= RP_MAX_BOTS; bot++)
- {
- if (botClient[bot] == client)
- {
- break;
- }
- else if (bot == RP_MAX_BOTS)
- {
- return;
- }
- }
- // Set the bot's team based on if it's NUB or PRO
- if (botReplayType[bot] == ReplayType_Run
- && GOKZ_GetTimeTypeEx(botTeleportsUsed[bot]) == TimeType_Pro)
- {
- GOKZ_JoinTeam(client, CS_TEAM_CT, .forceBroadcast = true);
- }
- else
- {
- GOKZ_JoinTeam(client, CS_TEAM_CT, .forceBroadcast = true);
- }
- // Set bot weapons
- // Always start by removing the pistol and knife
- int currentPistol = GetPlayerWeaponSlot(client, CS_SLOT_SECONDARY);
- if (currentPistol != -1)
- {
- RemovePlayerItem(client, currentPistol);
- }
-
- int currentKnife = GetPlayerWeaponSlot(client, CS_SLOT_KNIFE);
- if (currentKnife != -1)
- {
- RemovePlayerItem(client, currentKnife);
- }
-
- char weaponName[128];
- // Give the bot the knife stored in the replay
- /*
- if (botKnife[bot] != 0)
- {
- CS_WeaponIDToAlias(CS_ItemDefIndexToID(botKnife[bot]), weaponName, sizeof(weaponName));
- Format(weaponName, sizeof(weaponName), "weapon_%s", weaponName);
- GivePlayerItem(client, weaponName);
- }
- else
- {
- GivePlayerItem(client, "weapon_knife");
- }
- */
- // We are currently not doing that, as it would require us to disable the
- // FollowCSGOServerGuidelines failsafe if the bot has a non-standard knife.
- GivePlayerItem(client, "weapon_knife");
-
- // Give the bot the pistol stored in the replay
- if (botWeapon[bot] != -1)
- {
- CS_WeaponIDToAlias(CS_ItemDefIndexToID(botWeapon[bot]), weaponName, sizeof(weaponName));
- Format(weaponName, sizeof(weaponName), "weapon_%s", weaponName);
- GivePlayerItem(client, weaponName);
- }
-
- botCurrentTeleport[bot] = 0;
-}
-
-static void SetBotClanTag(int bot)
-{
- char tag[MAX_NAME_LENGTH];
-
- if (botReplayType[bot] == ReplayType_Run)
- {
- if (botCourse[bot] == 0)
- {
- // KZT PRO
- FormatEx(tag, sizeof(tag), "%s %s",
- gC_ModeNamesShort[botMode[bot]], gC_TimeTypeNames[GOKZ_GetTimeTypeEx(botTeleportsUsed[bot])]);
- }
- else
- {
- // KZT B2 PRO
- FormatEx(tag, sizeof(tag), "%s B%d %s",
- gC_ModeNamesShort[botMode[bot]], botCourse[bot], gC_TimeTypeNames[GOKZ_GetTimeTypeEx(botTeleportsUsed[bot])]);
- }
- }
- else if (botReplayType[bot] == ReplayType_Jump)
- {
- // KZT LJ
- FormatEx(tag, sizeof(tag), "%s %s",
- gC_ModeNamesShort[botMode[bot]], gC_JumpTypesShort[botJumpType[bot]]);
- }
- else
- {
- // KZT
- FormatEx(tag, sizeof(tag), "%s",
- gC_ModeNamesShort[botMode[bot]]);
- }
-
- CS_SetClientClanTag(botClient[bot], tag);
-}
-
-static void SetBotName(int bot)
-{
- char name[MAX_NAME_LENGTH];
-
- if (botReplayType[bot] == ReplayType_Run)
- {
- // DanZay (01:23.45)
- FormatEx(name, sizeof(name), "%s (%s)",
- botAlias[bot], GOKZ_FormatTime(botTime[bot]));
- }
- else if (botReplayType[bot] == ReplayType_Jump)
- {
- if (botJumpBlockDistance[bot] == 0)
- {
- // DanZay (291.44)
- FormatEx(name, sizeof(name), "%s (%.2f)",
- botAlias[bot], botJumpDistance[bot]);
- }
- else
- {
- // DanZay (291.44 on 289 block)
- FormatEx(name, sizeof(name), "%s (%.2f on %d block)",
- botAlias[bot], botJumpDistance[bot], botJumpBlockDistance[bot]);
- }
- }
- else
- {
- // DanZay
- FormatEx(name, sizeof(name), "%s",
- botAlias[bot]);
- }
-
- gB_HideNameChange = true;
- SetClientName(botClient[bot], name);
-}
-
-// Returns the number of bots that are currently replaying
-static int GetBotsInUse()
-{
- int botsInUse = 0;
- for (int bot; bot < RP_MAX_BOTS; bot++)
- {
- if (botInGame[bot] && botDataLoaded[bot])
- {
- botsInUse++;
- }
- }
- return botsInUse;
-}
-
-// Returns a bot that isn't currently replaying, or -1 if no unused bots found
-static int GetUnusedBot()
-{
- for (int bot = 0; bot < RP_MAX_BOTS; bot++)
- {
- if (!botInGame[bot])
- {
- return bot;
- }
- }
- return -1;
-}
-
-static void PlaybackSkipToTick(int bot, int tick)
-{
- if (botReplayVersion[bot] == 1)
- {
- // Load in the next tick
- float repOrigin[3], repAngles[3];
- repOrigin[0] = playbackTickData[bot].Get(tick, 0);
- repOrigin[1] = playbackTickData[bot].Get(tick, 1);
- repOrigin[2] = playbackTickData[bot].Get(tick, 2);
- repAngles[0] = playbackTickData[bot].Get(tick, 3);
- repAngles[1] = playbackTickData[bot].Get(tick, 4);
-
- TeleportEntity(botClient[bot], repOrigin, repAngles, view_as<float>( { 0.0, 0.0, 0.0 } ));
- }
- else if (botReplayVersion[bot] == 2)
- {
- // Load in the next tick
- ReplayTickData currentTickData;
- playbackTickData[bot].GetArray(tick, currentTickData);
-
- TeleportEntity(botClient[bot], currentTickData.origin, currentTickData.angles, view_as<float>( { 0.0, 0.0, 0.0 } ));
-
- int direction = tick < playbackTick[bot] ? -1 : 1;
- for (int i = playbackTick[bot]; i != tick; i += direction)
- {
- playbackTickData[bot].GetArray(i, currentTickData);
- if (currentTickData.flags & RP_TELEPORT_TICK)
- {
- botCurrentTeleport[bot] += direction;
- }
- }
-
- #if defined DEBUG
- PrintToServer("X %f \nY %f \nZ %f\nPitch %f\nYaw %f", currentTickData.origin[0], currentTickData.origin[1], currentTickData.origin[2], currentTickData.angles[0], currentTickData.angles[1]);
- if(currentTickData.flags & RP_MOVETYPE_MASK == view_as<int>(MOVETYPE_WALK)) PrintToServer("MOVETYPE_WALK");
- if(currentTickData.flags & RP_MOVETYPE_MASK == view_as<int>(MOVETYPE_LADDER)) PrintToServer("MOVETYPE_LADDER");
- if(currentTickData.flags & RP_MOVETYPE_MASK == view_as<int>(MOVETYPE_NOCLIP)) PrintToServer("MOVETYPE_NOCLIP");
- if(currentTickData.flags & RP_MOVETYPE_MASK == view_as<int>(MOVETYPE_NONE)) PrintToServer("MOVETYPE_NONE");
-
- if(currentTickData.flags & RP_IN_ATTACK) PrintToServer("IN_ATTACK");
- if(currentTickData.flags & RP_IN_ATTACK2) PrintToServer("IN_ATTACK2");
- if(currentTickData.flags & RP_IN_JUMP) PrintToServer("IN_JUMP");
- if(currentTickData.flags & RP_IN_DUCK) PrintToServer("IN_DUCK");
- if(currentTickData.flags & RP_IN_FORWARD) PrintToServer("IN_FORWARD");
- if(currentTickData.flags & RP_IN_BACK) PrintToServer("IN_BACK");
- if(currentTickData.flags & RP_IN_LEFT) PrintToServer("IN_LEFT");
- if(currentTickData.flags & RP_IN_RIGHT) PrintToServer("IN_RIGHT");
- if(currentTickData.flags & RP_IN_MOVELEFT) PrintToServer("IN_MOVELEFT");
- if(currentTickData.flags & RP_IN_MOVERIGHT) PrintToServer("IN_MOVERIGHT");
- if(currentTickData.flags & RP_IN_RELOAD) PrintToServer("IN_RELOAD");
- if(currentTickData.flags & RP_IN_SPEED) PrintToServer("IN_SPEED");
- if(currentTickData.flags & RP_FL_ONGROUND) PrintToServer("FL_ONGROUND");
- if(currentTickData.flags & RP_FL_DUCKING ) PrintToServer("FL_DUCKING");
- if(currentTickData.flags & RP_FL_SWIM) PrintToServer("FL_SWIM");
- if(currentTickData.flags & RP_UNDER_WATER) PrintToServer("WATERLEVEL!=0");
- if(currentTickData.flags & RP_TELEPORT_TICK) PrintToServer("TELEPORT");
- if(currentTickData.flags & RP_TAKEOFF_TICK) PrintToServer("TAKEOFF");
- if(currentTickData.flags & RP_HIT_PERF) PrintToServer("PERF");
- if(currentTickData.flags & RP_SECONDARY_EQUIPPED) PrintToServer("SECONDARY_WEAPON_EQUIPPED");
- PrintToServer("==============================================================");
- #endif
- }
-
- Movement_SetMovetype(botClient[bot], MOVETYPE_NOCLIP);
- playbackTick[bot] = tick;
-}
-
-static bool IsCurrentWeaponSecondary(int client)
-{
- int activeWeaponEnt = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");
- int secondaryEnt = GetPlayerWeaponSlot(client, CS_SLOT_SECONDARY);
- return activeWeaponEnt == secondaryEnt;
-}
-
-static void MakePlayerSpectate(int client, int bot)
-{
- GOKZ_JoinTeam(client, CS_TEAM_SPECTATOR);
- SetEntProp(client, Prop_Send, "m_iObserverMode", 4);
- SetEntPropEnt(client, Prop_Send, "m_hObserverTarget", bot);
-
- int clientUserID = GetClientUserId(client);
- DataPack data = new DataPack();
- data.WriteCell(clientUserID);
- data.WriteCell(GetClientUserId(bot));
- CreateTimer(0.1, Timer_UpdateBotName, GetClientUserId(bot));
- EnableReplayControls(client);
-}
-
-public Action Timer_UpdateBotName(Handle timer, int botUID)
-{
- Event e = CreateEvent("spec_target_updated");
- e.SetInt("userid", botUID);
- e.Fire();
- return Plugin_Continue;
-} \ No newline at end of file