diff options
| author | aura <nw@moneybot.cc> | 2026-02-17 23:42:09 +0100 |
|---|---|---|
| committer | aura <nw@moneybot.cc> | 2026-02-17 23:42:09 +0100 |
| commit | 5e2eb7d67ae933b7566f1944d0bb7744da03d586 (patch) | |
| tree | 054acff1113270a9cd07933df760f3768c1b6853 /sourcemod/scripting/gokz-replays/playback.sp | |
| parent | 341db13a008dc12bb22ceb50452d93d01476308c (diff) | |
move source stuff to its own folder
Diffstat (limited to 'sourcemod/scripting/gokz-replays/playback.sp')
| -rw-r--r-- | sourcemod/scripting/gokz-replays/playback.sp | 1501 |
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 |
