Advertisement
Guest User

hekkeld

a guest
Jan 27th, 2020
180
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 53.97 KB | None | 0 0
  1. #pragma newdecls required
  2. #pragma semicolon 1
  3.  
  4. #include <sourcemod>
  5. #include <cstrike>
  6. //#include <slidy-timer>
  7. #include <influx/core>
  8. #include <influx/zones>
  9. #include <influx/recording>
  10.  
  11. #define MAX_TEAMS 12
  12. #define MAX_TEAM_MEMBERS 10
  13. #define MAX_STYLES 12
  14.  
  15.  
  16. float g_flAirAccelerate;
  17.  
  18.  
  19. // CONVARS
  20. ConVar g_ConVar_AirAccelerate;
  21. ConVar g_ConVar_EnableBunnyhopping;
  22. ConVar g_ConVar_AutoBhop;
  23.  
  24. ConVar g_ConVar_Auto_AirAccelerate;
  25.  
  26. ConVar g_cvMaxPasses;
  27. ConVar g_cvMaxUndos;
  28.  
  29. int MODE_TAGTEAM = 12;
  30.  
  31. // invite system
  32. int g_iInviteStyle[MAXPLAYERS + 1];
  33. bool g_bCreatingTeam[MAXPLAYERS + 1];
  34. ArrayList g_aInvitedPlayers[MAXPLAYERS + 1];
  35. bool g_bInvitedPlayer[MAXPLAYERS + 1][MAXPLAYERS + 1];
  36. int g_nDeclinedPlayers[MAXPLAYERS + 1];
  37. ArrayList g_aAcceptedPlayers[MAXPLAYERS + 1];
  38.  
  39. // teams system
  40. bool g_bAllowReset[MAXPLAYERS + 1];
  41. bool g_bAllowStyleChange[MAXPLAYERS + 1];
  42.  
  43. int g_nUndoCount[MAX_TEAMS];
  44. bool g_bDidUndo[MAX_TEAMS];
  45. any g_LastCheckpoint[MAX_TEAMS][eCheckpoint];
  46.  
  47. char g_cTeamName[MAX_TEAMS][MAX_NAME_LENGTH];
  48. int g_nPassCount[MAX_TEAMS];
  49. int g_nRelayCount[MAX_TEAMS];
  50. int g_iCurrentPlayer[MAX_TEAMS];
  51. bool g_bTeamTaken[MAX_TEAMS];
  52. int g_nTeamPlayerCount[MAX_TEAMS];
  53.  
  54. int g_iTeamIndex[MAXPLAYERS + 1] = { -1, ... };
  55. int g_iNextTeamMember[MAXPLAYERS + 1];
  56. char g_cPlayerTeamName[MAXPLAYERS + 1][MAX_NAME_LENGTH];
  57.  
  58. // records system
  59. ArrayList g_aCurrentSegmentStartTicks[MAX_TEAMS];
  60. ArrayList g_aCurrentSegmentPlayers[MAX_TEAMS];
  61.  
  62. StringMap g_smSegmentPlayerNames[MAX_RUNS][MAX_STYLES];
  63.  
  64. ArrayList g_aMapTopRecordIds[MAX_RUNS][MAX_STYLES];
  65. ArrayList g_aMapTopTimes[MAX_RUNS][MAX_STYLES];
  66. ArrayList g_aMapTopNames[MAX_RUNS][MAX_STYLES];
  67.  
  68. int g_iRecordId[MAXPLAYERS + 1][MAX_RUNS][MAX_STYLES];
  69. float g_fPersonalBest[MAXPLAYERS + 1][MAX_RUNS][MAX_STYLES];
  70.  
  71. // segments loading
  72. bool g_bFinishedInsertingSegments[MAX_RUNS][MAX_STYLES];
  73. bool g_bLoadSegmentsAfterInserting[MAX_RUNS][MAX_STYLES];
  74.  
  75. Database        g_hDatabase;
  76. char g_cMapName[PLATFORM_MAX_PATH];
  77.  
  78. public Plugin myinfo =
  79. {
  80.     name = "Slidy's Timer - Tagteam relay",
  81.     author = "SlidyBat ~ celly",
  82.     description = "",
  83.     version = "1.3.3.7",
  84.     url = ""
  85. }
  86.  
  87. public APLRes AskPluginLoad2( Handle myself, bool late, char[] error, int err_max )
  88. {
  89.     CreateNative( "Timer_IsClientInTagTeam", Native_IsClientInTagTeam );
  90.     CreateNative( "Timer_GetClientTeamIndex", Native_GetClientTeamIndex );
  91.     CreateNative( "Timer_GetTeamName", Native_GetTeamName );
  92.  
  93.     RegPluginLibrary( "timer-tagteam" );
  94.    
  95.     return APLRes_Success;
  96. }
  97.  
  98. public void OnPluginStart()
  99. {
  100.      if ( (g_ConVar_AirAccelerate = FindConVar( "sv_airaccelerate" )) == null )
  101.     {
  102.         SetFailState( INF_CON_PRE..."Couldn't find handle for sv_airaccelerate!" );
  103.     }
  104.    
  105.     if ( (g_ConVar_EnableBunnyhopping = FindConVar( "sv_enablebunnyhopping" )) == null )
  106.     {
  107.         SetFailState( INF_CON_PRE..."Couldn't find handle for sv_enablebunnyhopping!" );
  108.     }
  109.    
  110.    
  111.     if ( (g_ConVar_AutoBhop = FindConVar( "sv_autobunnyhopping" )) == null )
  112.     {
  113.         SetFailState( INF_CON_PRE..."Couldn't find handle for sv_autobunnyhopping!" );
  114.     }
  115.    
  116.     g_ConVar_AutoBhop.Flags &= ~(FCVAR_REPLICATED | FCVAR_NOTIFY);
  117.    
  118.    
  119.    
  120.     g_ConVar_Auto_AirAccelerate = CreateConVar( "influx_auto_airaccelerate", "1000", "", FCVAR_NOTIFY );
  121.     g_ConVar_Auto_AirAccelerate.AddChangeHook( E_CvarChange_Auto_AA );
  122.    
  123.     g_flAirAccelerate = g_ConVar_Auto_AirAccelerate.FloatValue;
  124.  
  125.     for( int i = 0; i < MAX_RUNS; i++ )
  126.     {
  127.         for( int j = 0; j < MAX_STYLES; j++ )
  128.         {
  129.             g_aMapTopRecordIds[i][j] = new ArrayList();
  130.             g_aMapTopTimes[i][j] = new ArrayList();
  131.             g_aMapTopNames[i][j] = new ArrayList( ByteCountToCells( MAX_NAME_LENGTH ) );
  132.         }
  133.     }
  134.  
  135.     g_cvMaxPasses = CreateConVar( "sm_timer_tagteam_maxpasses", "-1", "Maximum number of passes a team can make or -1 for unlimited passes", _, true, -1.0, false );
  136.     g_cvMaxUndos = CreateConVar( "sm_timer_tagteam_maxundos", "3", "Maximum number of undos a team can make or -1 for unlimited undos", _, true, -1.0, false );
  137.     AutoExecConfig( true, "tagteam", "SlidyTimer" );
  138.    
  139.     RegConsoleCmd( "sm_teamname", Command_TeamName );
  140.     RegConsoleCmd( "sm_exitteam", Command_ExitTeam );
  141.     RegConsoleCmd( "sm_pass", Command_Pass );
  142.     RegConsoleCmd( "sm_undo", Command_Undo );
  143.  
  144.     char error[256];
  145.     g_hDatabase = SQL_Connect( "Slidy-Timer", true, error, sizeof(error) );
  146.  
  147.     SQL_CreateTables();
  148.    
  149.     GetCurrentMap( g_cMapName, sizeof(g_cMapName) );
  150. }
  151. public void OnAllPluginsLoaded()
  152. {
  153.     AddMode();
  154. }
  155.  
  156. public void OnPluginEnd()
  157. {
  158.     Influx_RemoveMode( MODE_TAGTEAM );
  159.    
  160.    
  161.     g_ConVar_AutoBhop.Flags |= (FCVAR_REPLICATED | FCVAR_NOTIFY);
  162. }
  163.  
  164. public void Influx_OnRequestModes()
  165. {
  166.     AddMode();
  167. }
  168.  
  169. stock void AddMode()
  170. {
  171.     if ( !Influx_AddMode( MODE_TAGTEAM, "Tagteam", "team", "team", 260.0 ) )
  172.     {
  173.         SetFailState( INF_CON_PRE..."Couldn't add mode! (%i)", MODE_TAGTEAM );
  174.     }
  175. }
  176.  
  177. public void OnMapStart()
  178. {
  179.     GetCurrentMap( g_cMapName, sizeof(g_cMapName) );
  180. }
  181.  
  182. public void Timer_OnMapLoaded( int mapid )
  183. {
  184.     if( g_hDatabase != null )
  185.     {
  186.         SQL_LoadAllMapRecords();
  187.     }
  188. }
  189.  
  190. public void Timer_OnDatabaseLoaded()
  191. {
  192.     if( g_hDatabase == null )
  193.     {
  194.         char error[256];
  195.         g_hDatabase = SQL_Connect( "Slidy-Timer", true, error, sizeof(error) );
  196.         SQL_CreateTables();
  197.     }
  198. }
  199.  
  200.  
  201. /*
  202. public void Timer_OnReplayLoadedPost( int track, int style, float time, int recordid, ArrayList frames )
  203. {
  204.     delete g_smSegmentPlayerNames[track][style];
  205.    
  206.     if( Timer_StyleHasSetting( style, "tagteam" ) )
  207.     {
  208.         SQL_LoadSegments( track, style, recordid );
  209.     }
  210. }
  211. */
  212. public Action Influx_OnTimerFinish( int client, int runid, int mode, int style, float time, int flags, char[] errormsg, int error_len )
  213. {
  214.     // not tagteam, dont bother
  215.     int targetmode = MODE_INVALID;
  216.     int recordid = 1;
  217.     targetmode = Influx_GetClientMode( client );
  218.     if(targetmode != MODE_TAGTEAM)
  219.     {
  220.         return;
  221.     }
  222.     //OnTimerFinishCustom();
  223.  
  224.     if( g_bFinishedInsertingSegments[runid][mode] )
  225.     {
  226.         ////Timer_DebugPrint( "Timer_OnReplaySavedPost: Loading segments" );
  227.         SQL_LoadSegments( runid, mode, recordid );
  228.     }
  229.     else
  230.     {
  231.         ////Timer_DebugPrint( "Timer_OnReplaySavedPost: Waiting for segments to be inserted" );
  232.         g_bLoadSegmentsAfterInserting[runid][mode] = true;
  233.     }
  234. }
  235.  
  236. public void OnClientPutInServer( int client )
  237. {
  238.     for( int i = 0; i < MAX_RUNS; i++ )
  239.     {
  240.         for( int j = 0; j < MAX_STYLES; j++ )
  241.         {
  242.             g_fPersonalBest[client][i][j] = 0.0;
  243.             g_iRecordId[client][i][j] = -1;
  244.         }
  245.     }
  246.    
  247.     Format( g_cPlayerTeamName[client], sizeof(g_cPlayerTeamName[]), "Team %N", client );
  248. }
  249.  
  250. public void Timer_OnClientLoaded( int client, int playerid, bool newplayer )
  251. {
  252.     if( !newplayer )
  253.     {
  254.         SQL_LoadAllPlayerTimes( client );
  255.     }
  256. }
  257.  
  258. public void OnClientDisconnect( int client )
  259. {
  260.     if( !IsFakeClient( client ) && g_iTeamIndex[client] != -1 )
  261.     {
  262.         ExitTeam( client );
  263.     }
  264. }
  265. /*
  266. public Action OnPlayerRunCmd( int client )
  267. {
  268.     int tick = Timer_GetReplayBotCurrentFrame( client );
  269.     int track = Influx_GetReplayRunId( client );
  270.     int style = Timer_GetReplayBotStyle( client );
  271.    
  272.     // not a valid replay bot or not currently replaying
  273.     if( tick == -1 || track == -1 || style == -1 )
  274.     {
  275.         return Plugin_Continue;
  276.     }
  277.    
  278.     if( g_smSegmentPlayerNames[track][style] == null )
  279.     {
  280.         return Plugin_Continue;
  281.     }
  282.    
  283.     char sTick[8];
  284.     IntToString( tick, sTick, sizeof(sTick) );
  285.     char name[MAX_NAME_LENGTH];
  286.     if( !g_smSegmentPlayerNames[track][style].GetString( sTick, name, sizeof(name) ) )
  287.     {
  288.         return Plugin_Continue;
  289.     }
  290.    
  291.     for( int i = 1; i <= MaxClients; i++ )
  292.     {
  293.         if( i == client )
  294.         {
  295.             continue;
  296.         }
  297.        
  298.         if( IsClientInGame( i ) && GetClientObserverTarget( i ) == client )
  299.         {
  300.             PrintToChat( i, "{primary}Current section by: {name}%s", name );
  301.         }
  302.     }
  303.    
  304.     return Plugin_Continue;
  305. }
  306. */
  307.  
  308. public Action Influx_OnClientModeChange( int client, int mode, int lastmode )
  309. {
  310.     if ( mode == MODE_TAGTEAM )
  311.     {
  312.         if( g_iTeamIndex[client] == -1 ) // not in a team, make them create or join one before changing style
  313.         {
  314.             OpenInviteSelectMenu( client, 0, true );
  315.             PrintToChat( client, "{primary}Created {secondary}%s{primary}! Use {secondary}!teamname {primary}to set your team name.", g_cPlayerTeamName[client] );
  316.             return Plugin_Handled;
  317.         }
  318.        
  319.         UnhookThinks( client );
  320.        
  321.        
  322.         if ( !Inf_SDKHook( client, SDKHook_PreThinkPost, E_PreThinkPost_Client ) )
  323.         {
  324.             return Plugin_Handled;
  325.         }
  326.        
  327.         if ( !Inf_SDKHook( client, SDKHook_PostThinkPost, E_PostThinkPost_Client ) )
  328.         {
  329.             UnhookThinks( client );
  330.             return Plugin_Handled;
  331.         }
  332.        
  333.        
  334.         Inf_SendConVarValueFloat( client, g_ConVar_AirAccelerate, g_ConVar_Auto_AirAccelerate.FloatValue );
  335.         Inf_SendConVarValueBool( client, g_ConVar_EnableBunnyhopping, true );
  336.         Inf_SendConVarValueBool( client, g_ConVar_AutoBhop, true );
  337.         return Plugin_Continue;
  338.     }
  339.     else if ( lastmode == MODE_TAGTEAM )
  340.     {
  341.         if( g_iTeamIndex[client] != -1 && !g_bAllowStyleChange[client] )
  342.         {
  343.             PrintToChat( client, "{primary}You cannot change style until you leave the team! Type {secondary}!exitteam {primary}to leave your team" );
  344.             return Plugin_Handled;
  345.         }
  346.         if( g_bAllowStyleChange[client] )
  347.         {
  348.             g_bAllowStyleChange[client] = false;
  349.         }
  350.        
  351.         UnhookThinks( client );
  352.        
  353.         Inf_SendConVarValueBool( client, g_ConVar_AutoBhop, false );
  354.         return Plugin_Continue;
  355.     }
  356.    
  357.     return Plugin_Continue;
  358. }
  359.  
  360. stock void UnhookThinks( int client )
  361. {
  362.     SDKUnhook( client, SDKHook_PreThinkPost, E_PreThinkPost_Client );
  363.     SDKUnhook( client, SDKHook_PostThinkPost, E_PostThinkPost_Client );
  364. }
  365.  
  366. public void E_CvarChange_Auto_AA( ConVar convar, const char[] oldval, const char[] newval )
  367. {
  368.     g_flAirAccelerate = convar.FloatValue;
  369. }
  370.  
  371. public void E_PreThinkPost_Client( int client )
  372. {
  373. #if defined DEBUG_THINK
  374.     PrintToServer( INF_DEBUG_PRE..."PreThinkPost - Auto CS:GO (aa: %.0f)", g_flAirAccelerate );
  375. #endif
  376.    
  377.     g_ConVar_AirAccelerate.FloatValue = g_flAirAccelerate;
  378.     g_ConVar_EnableBunnyhopping.BoolValue = true;
  379.     g_ConVar_AutoBhop.BoolValue = true;
  380. }
  381.  
  382. public void E_PostThinkPost_Client( int client )
  383. {
  384. #if defined DEBUG_THINK
  385.     PrintToServer( INF_DEBUG_PRE..."PostThinkPost - Auto CS:GO (aa: %.0f)", g_flAirAccelerate );
  386. #endif
  387.  
  388.     g_ConVar_AutoBhop.BoolValue = false;
  389. }
  390.  
  391. /* Chat notify
  392. public Action Timer_OnTimerFinishPre( int client, int track, int style, float time )
  393. {
  394.     if( Timer_StyleHasSetting( style, "tagteam" ) )
  395.     {
  396.         char sTime[64];
  397.         Timer_FormatTime( time, sTime, sizeof(sTime) );
  398.    
  399.         char sZoneTrack[64];
  400.         Timer_GetZoneTrackName( track, sZoneTrack, sizeof(sZoneTrack) );
  401.    
  402.         char sStyleName[32];
  403.         Timer_GetStyleName( style, sStyleName, sizeof(sStyleName) );
  404.        
  405.         PrintToChatAll( "[{secondary}%s{white}] {name}%s {primary}finished on {secondary}%s {primary}timer in {secondary}%ss", sStyleName, g_cTeamName[g_iTeamIndex[client]], sZoneTrack, sTime );
  406.        
  407.         return Plugin_Changed;
  408.     }
  409.    
  410.     return Plugin_Continue;
  411. }*/
  412.  
  413. /* sm_r here xddd
  414. public Action Timer_OnClientTeleportToZonePre( int client, int zoneType, int zoneTrack, int subindex )
  415. {
  416.     if( g_iTeamIndex[client] != -1 && !g_bAllowReset[client] && !(g_nRelayCount[g_iTeamIndex[client]] == 0 && g_iCurrentPlayer[g_iTeamIndex[client]] == client) )
  417.     {
  418.         PrintToChat( client, "{primary}You cannot reset or teleport until you leave the team! Type {secondary}!exitteam {primary}to leave your team" );
  419.         return Plugin_Handled;
  420.     }
  421.     if( g_bAllowReset[client] )
  422.     {
  423.         g_bAllowReset[client] = false;
  424.     }
  425.    
  426.     return Plugin_Continue;
  427. }
  428. */
  429. void OpenInviteSelectMenu( int client, int firstItem, bool reset = false )
  430. {
  431.     if( reset )
  432.     {
  433.         for( int i = 1; i <= MaxClients; i++ )
  434.         {
  435.             g_bInvitedPlayer[client][i] = false;
  436.         }
  437.        
  438.         g_bCreatingTeam[client] = true;
  439.         g_iInviteStyle[client] = MODE_TAGTEAM;
  440.         g_nDeclinedPlayers[client] = 0;
  441.        
  442.         delete g_aAcceptedPlayers[client];
  443.         g_aAcceptedPlayers[client] = new ArrayList();
  444.        
  445.         delete g_aInvitedPlayers[client];
  446.         g_aInvitedPlayers[client] = new ArrayList();
  447.     }
  448.  
  449.     Menu menu = new Menu( InviteSelectMenu_Handler );
  450.     menu.SetTitle( "Select players to invite:\n \n" );
  451.    
  452.     for( int i = 1; i <= MaxClients; i++ )
  453.     {
  454.         if( i == client || !IsClientInGame( i ) || IsFakeClient( i ) || g_iTeamIndex[i] != -1 )
  455.         {
  456.             continue;
  457.         }
  458.    
  459.         char name[MAX_NAME_LENGTH + 32];
  460.         Format( name, sizeof(name), "[%s] %N", g_bInvitedPlayer[client][i] ? "X" : " ", i );
  461.    
  462.         char userid[8];
  463.         IntToString( GetClientUserId( i ), userid, sizeof(userid) );
  464.        
  465.         menu.AddItem( userid, name );
  466.     }
  467.    
  468.     menu.AddItem( "send", "Send Invites!", g_aInvitedPlayers[client].Length == 0 ? ITEMDRAW_DISABLED : ITEMDRAW_DEFAULT );
  469.    
  470.     menu.DisplayAt( client, firstItem, MENU_TIME_FOREVER );
  471. }
  472.  
  473. public int InviteSelectMenu_Handler( Menu menu, MenuAction action, int param1, int param2 )
  474. {
  475.     if( action == MenuAction_Select )
  476.     {
  477.         char info[8];
  478.         menu.GetItem( param2, info, sizeof(info) );
  479.        
  480.         if( StrEqual( info, "send" ) ) // send the invites!
  481.         {
  482.             int length = g_aInvitedPlayers[param1].Length;
  483.             for( int i = 0; i < length; i++ )
  484.             {
  485.                 SendInvite( param1, GetClientOfUserId( g_aInvitedPlayers[param1].Get( i ) ) );
  486.             }
  487.            
  488.             PrintToChat( param1, "Invites sent!" );
  489.            
  490.             OpenLobbyMenu( param1 );
  491.         }
  492.         else
  493.         {
  494.             int userid = StringToInt( info );
  495.             int target = GetClientOfUserId( userid );
  496.             if( 0 < target <= MaxClients )
  497.             {
  498.                 g_bInvitedPlayer[param1][target] = !g_bInvitedPlayer[param1][target];
  499.                 if( g_bInvitedPlayer[param1][target] )
  500.                 {
  501.                     g_aInvitedPlayers[param1].Push( userid );
  502.                 }
  503.                 else
  504.                 {
  505.                     int idx = g_aInvitedPlayers[param1].FindValue( userid );
  506.                     if( idx != -1 )
  507.                     {
  508.                         g_aInvitedPlayers[param1].Erase( idx );
  509.                     }
  510.                 }
  511.             }
  512.            
  513.             OpenInviteSelectMenu( param1, (param2 / 6) * 6 );
  514.         }
  515.     }
  516.     else if( action == MenuAction_End )
  517.     {
  518.         delete menu;
  519.     }
  520. }
  521.  
  522. void SendInvite( int client, int target )
  523. {
  524.     Menu menu = new Menu( InviteMenu_Handler );
  525.    
  526.     char buffer[256];
  527.     Format( buffer, sizeof(buffer), "%N has invited you to play tagteam!\nAccept?\n \n", client );
  528.     menu.SetTitle( buffer );
  529.    
  530.     char userid[8];
  531.     IntToString( GetClientUserId( client ), userid, sizeof(userid) );
  532.    
  533.     menu.AddItem( userid, "Yes" );
  534.     menu.AddItem( userid, "No" );
  535.    
  536.     menu.Display( target, 20 );
  537. }
  538.  
  539. public int InviteMenu_Handler( Menu menu, MenuAction action, int param1, int param2 )
  540. {
  541.     if( action == MenuAction_Select )
  542.     {
  543.         char info[8];
  544.         menu.GetItem( param2, info, sizeof(info) );
  545.        
  546.         int client = GetClientOfUserId( StringToInt( info ) );
  547.         if( !( 0 < client <= MaxClients ) )
  548.         {
  549.             return 0;
  550.         }
  551.    
  552.         if( param2 == 0 ) // yes
  553.         {
  554.             if( !g_bCreatingTeam[client] )
  555.             {
  556.                 PrintToChat( param1, "The team has been cancelled or has already started the run" );
  557.             }
  558.             if( g_aAcceptedPlayers[client].Length >= MAX_TEAM_MEMBERS )
  559.             {
  560.                 PrintToChat( param1, "The team is now full, cannot join" );
  561.             }
  562.             else
  563.             {
  564.                 g_aAcceptedPlayers[client].Push( GetClientUserId( param1 ) );
  565.                 OpenLobbyMenu( client );
  566.             }
  567.         }
  568.         else // no
  569.         {
  570.             g_nDeclinedPlayers[client]++;
  571.             PrintToChat( client, "{name}%N {primary}has declined your invite", param1 );
  572.         }
  573.        
  574.         //Timer_DebugPrint( "InviteMenu_Handler: %i + %i, %i", g_aAcceptedPlayers[client].Length, g_nDeclinedPlayers[client], g_aInvitedPlayers[client].Length );
  575.         if( g_aAcceptedPlayers[client].Length + g_nDeclinedPlayers[client] == g_aInvitedPlayers[client].Length ) // everyone responded
  576.         {
  577.             FinishInvite( client );
  578.         }
  579.     }
  580.     else if( action == MenuAction_End )
  581.     {
  582.         delete menu;
  583.     }
  584.    
  585.     return 0;
  586. }
  587.  
  588. void OpenLobbyMenu( int client )
  589. {
  590.     Menu menu = new Menu( LobbyMenu_Handler );
  591.    
  592.     char buffer[512];
  593.     Format( buffer, sizeof(buffer), "%s\n \nMembers:\n%N\n", g_cPlayerTeamName[client], client );
  594.    
  595.     int length = g_aAcceptedPlayers[client].Length;
  596.     if( length == 0 )
  597.     {
  598.         Format( buffer, sizeof(buffer), "%s \n", buffer );
  599.     }
  600.    
  601.     for( int i = 0; i < length; i++ )
  602.     {
  603.         Format( buffer, sizeof(buffer), "%s%N\n", buffer, GetClientOfUserId( g_aAcceptedPlayers[client].Get( i ) ) );
  604.        
  605.         if( i == length - 1 )
  606.         {
  607.             Format( buffer, sizeof(buffer), "%s \n", buffer );
  608.         }
  609.     }
  610.    
  611.     menu.SetTitle( buffer );
  612.    
  613.     menu.AddItem( "start", "Start", (length > 0) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED );
  614.     menu.AddItem( "cancel", "Cancel" );
  615.    
  616.     menu.ExitButton = false;
  617.     menu.Display( client, MENU_TIME_FOREVER );
  618. }
  619.  
  620. public int LobbyMenu_Handler( Menu menu, MenuAction action, int param1, int param2 )
  621. {
  622.     if( action == MenuAction_Select )
  623.     {
  624.         if( param1 == 0 ) // start
  625.         {
  626.             FinishInvite( param1 );
  627.         }
  628.         else if( param1 == 1 ) // cancel
  629.         {
  630.             CancelInvite( param1 );
  631.         }
  632.     }
  633.     else if( action == MenuAction_End )
  634.     {
  635.         delete menu;
  636.     }
  637. }
  638.  
  639. void FinishInvite( int client )
  640. {
  641.     g_bCreatingTeam[client] = false;
  642.  
  643.     int length = g_aAcceptedPlayers[client].Length;
  644.    
  645.     if( length < 1 )
  646.     {
  647.         PrintToChat( client, "{primary}Not enough players to create a team" );
  648.         return;
  649.     }
  650.    
  651.     int[] members = new int[length + 1];
  652.    
  653.     members[0] = client;
  654.     for( int i = 0; i < length; i++ )
  655.     {
  656.         members[i + 1] = GetClientOfUserId( g_aAcceptedPlayers[client].Get( i ) );
  657.     }
  658.    
  659.     CreateTeam( members, length + 1, g_iInviteStyle[client] );
  660.    
  661.     int letters;
  662.     char buffer[512];
  663.     for( int i = 0; i <= length; i++ )
  664.     {
  665.         letters += Format( buffer, sizeof(buffer), "%s{name}%N{primary}, ", buffer, members[i] );
  666.     }
  667.     buffer[letters - 3] = '\0';
  668.    
  669.     PrintToTeam( g_iTeamIndex[client], "{secondary}%s {primary}has been assembled! Members: %s", g_cTeamName[g_iTeamIndex[client]], buffer );
  670. }
  671.  
  672. void CancelInvite( int client )
  673. {
  674.     g_bCreatingTeam[client] = false;
  675. }
  676.  
  677. void CreateTeam( int[] members, int memberCount, int style )
  678. {
  679.     //Timer_DebugPrint( "CreateTeam: memberCount=%i", memberCount );
  680.  
  681.     int teamindex = -1;
  682.     for( int i = 0; i < MAX_TEAMS; i++ )
  683.     {
  684.         if( !g_bTeamTaken[i] )
  685.         {
  686.             teamindex = i;
  687.             break;
  688.         }
  689.     }
  690.    
  691.     if( teamindex == -1 )
  692.     {
  693.         LogError( "Not enough teams" );
  694.         return;
  695.     }
  696.    
  697.     g_nUndoCount[teamindex] = 0;
  698.     g_nPassCount[teamindex] = 0;
  699.     g_nRelayCount[teamindex] = 0;
  700.     g_bTeamTaken[teamindex] = true;
  701.     g_nTeamPlayerCount[teamindex] = memberCount;
  702.     strcopy( g_cTeamName[teamindex], sizeof(g_cTeamName[]), g_cPlayerTeamName[members[0]] );
  703.    
  704.     delete g_aCurrentSegmentStartTicks[teamindex];
  705.     g_aCurrentSegmentStartTicks[teamindex] = new ArrayList();
  706.     delete g_aCurrentSegmentPlayers[teamindex];
  707.     g_aCurrentSegmentPlayers[teamindex] = new ArrayList();
  708.    
  709.     g_aCurrentSegmentStartTicks[teamindex].Push( 2 ); // not zero so that it doesnt spam print during first tick freeze time
  710.     g_aCurrentSegmentPlayers[teamindex].Push( Influx_GetClientId( members[0] ) );
  711.    
  712.     int next = members[0];
  713.     for( int i = memberCount - 1; i >= 0; i-- )
  714.     {
  715.         //Timer_DebugPrint( "CreateTeam: Adding member %N", members[i] );
  716.    
  717.         g_iNextTeamMember[members[i]] = next;
  718.         next = members[i];
  719.        
  720.         g_iTeamIndex[members[i]] = teamindex;
  721.        
  722.         g_bAllowStyleChange[members[i]] = true;
  723.         Timer_ClearClientCheckpoints( members[i] );
  724.         Influx_SetClientMode( members[i], MODE_TAGTEAM);
  725.     }
  726.     Influx_SetClientRun( members[0], 1 );
  727.     Influx_TeleportToStart( members[0] );
  728.     //TeleportClientToZone( members[0], Zone_Start, ZoneTrack_Main );
  729.     Timer_OpenCheckpointsMenu( members[0] );
  730.     g_iCurrentPlayer[teamindex] = members[0];
  731.    
  732.     for( int i = 1; i < memberCount; i++ )
  733.     {
  734.         //Timer_DebugPrint( "CreateTeam: Moving %N to spec %N", members[i], members[0] );
  735.    
  736.         ChangeClientTeam( members[i], CS_TEAM_SPECTATOR );
  737.         SetEntPropEnt( members[i], Prop_Send, "m_hObserverTarget", members[0] );
  738.         SetEntProp( members[i], Prop_Send, "m_iObserverMode", 4 );
  739.     }
  740. }
  741.  
  742. bool ExitTeam( int client )
  743. {
  744.     if( g_iTeamIndex[client] == -1 )
  745.     {
  746.         Influx_SetClientMode( client, MODE_AUTO );
  747.         Influx_TeleportToStart( client );
  748.         return false;
  749.     }
  750.    
  751.     int teamidx = g_iTeamIndex[client];
  752.     g_iTeamIndex[client] = -1;
  753.    
  754.     g_nTeamPlayerCount[teamidx]--;
  755.     if( g_nTeamPlayerCount[teamidx] <= 1 )
  756.     {
  757.         g_bTeamTaken[teamidx] = false;
  758.         for( int i = 1; i <= MaxClients; i++ )
  759.         {
  760.             if( i != client && g_iTeamIndex[i] == teamidx )
  761.             {
  762.                 PrintToChat( i, "{primary}All your team members have left, your team has been disbanded!" );
  763.                 ExitTeam( i );
  764.                 break;
  765.             }
  766.         }
  767.     }
  768.     else
  769.     {
  770.         for( int i = 1; i <= MaxClients; i++ )
  771.         {
  772.             if( g_iNextTeamMember[i] == client )
  773.             {
  774.                 g_iNextTeamMember[i] = g_iNextTeamMember[client];
  775.             }
  776.         }
  777.     }
  778.    
  779.     g_iNextTeamMember[client] = -1;
  780.    
  781.     Influx_SetClientMode( client, MODE_AUTO );
  782.     Influx_TeleportToStart( client );
  783.    
  784.     return true;
  785. }
  786.  
  787. public Action Timer_OnCPLoadedPre( int client, int idx )
  788. {
  789.     if( g_iTeamIndex[client] != -1 && g_iCurrentPlayer[g_iTeamIndex[client]] != client )
  790.     {
  791.         PrintToChat( client, "{primary}Cannot load when it is not your turn" );
  792.         return Plugin_Handled;
  793.     }
  794.  
  795.     return Plugin_Continue;
  796. }
  797.  
  798. public Action Timer_OnCPSavedPre( int client, int target, int idx )
  799. {
  800.     if( g_iTeamIndex[client] != -1 && client != target )
  801.     {
  802.         PrintToChat( client, "{primary}Cannot save when it is not your turn" );
  803.         return Plugin_Handled;
  804.     }
  805.    
  806.     return Plugin_Continue;
  807. }
  808.  
  809. public void Timer_OnCPSavedPost( int client, int target, int idx )
  810. {
  811.     if( g_iTeamIndex[client] != -1 )
  812.     {
  813.         int teamidx = g_iTeamIndex[client];
  814.        
  815.         if( !g_bDidUndo[teamidx] )
  816.         {
  817.             delete g_LastCheckpoint[teamidx][CP_ReplayFrames];
  818.         }
  819.         Timer_GetClientCheckpoint( client, 0, g_LastCheckpoint[teamidx] );
  820.        
  821.         // 'frames' will be deleted by PassToNext
  822.         ArrayList frames = g_LastCheckpoint[teamidx][CP_ReplayFrames];
  823.         g_LastCheckpoint[teamidx][CP_ReplayFrames] = frames.Clone();
  824.        
  825.         g_nRelayCount[teamidx]++;
  826.         int next = g_iNextTeamMember[client];
  827.    
  828.         any checkpoint[eCheckpoint];
  829.         Timer_GetClientCheckpoint( client, idx, checkpoint );
  830.        
  831.         g_aCurrentSegmentStartTicks[teamidx].Push( checkpoint[CP_ReplayFrames].Length - 1 );
  832.         g_aCurrentSegmentPlayers[teamidx].Push( Influx_GetClientId( next ) );
  833.        
  834.         PassToNext( client, next, checkpoint );
  835.        
  836.         g_bDidUndo[teamidx] = false;
  837.     }
  838. }
  839.  
  840. // Commands
  841.  
  842. public Action Command_TeamName( int client, int args )
  843. {
  844.     GetCmdArgString( g_cPlayerTeamName[client], sizeof(g_cPlayerTeamName[]) );
  845.     if( g_iTeamIndex[client] != -1 )
  846.     {
  847.         strcopy( g_cTeamName[g_iTeamIndex[client]], sizeof(g_cTeamName[]), g_cPlayerTeamName[client] );
  848.     }
  849.    
  850.     ReplyToCommand( client, "{primary}Team name set to: {secondary}%s", g_cPlayerTeamName[client] );
  851.    
  852.     return Plugin_Handled;
  853. }
  854.  
  855. public Action Command_ExitTeam( int client, int args )
  856. {
  857.     if( !ExitTeam( client ) )
  858.     {
  859.         ReplyToCommand( client, "{primary}You are not currently in a team" );
  860.     }
  861.    
  862.     return Plugin_Handled;
  863. }
  864.  
  865. public Action Command_Pass( int client, int args )
  866. {
  867.     if( g_iTeamIndex[client] == -1 )
  868.     {
  869.         ReplyToCommand( client, "{primary}You are not currently in a team" );
  870.         return Plugin_Handled;
  871.     }
  872.    
  873.     int teamidx = g_iTeamIndex[client];
  874.     int maxPasses = g_cvMaxPasses.IntValue;
  875.    
  876.     if( maxPasses > -1 && g_nPassCount[teamidx] >= maxPasses )
  877.     {
  878.         ReplyToCommand( client, "{primary}Your team has used all %i passes", maxPasses );
  879.         return Plugin_Handled;
  880.     }
  881.    
  882.     if( g_iCurrentPlayer[teamidx] != client )
  883.     {
  884.         ReplyToCommand( client, "{primary}You cannot pass when it is not your turn" );
  885.         return Plugin_Handled;
  886.     }
  887.    
  888.     g_nPassCount[teamidx]++;
  889.    
  890.     any checkpoint[eCheckpoint];
  891.     bool usecp = Timer_GetTotalCheckpoints( client ) > 0;
  892.    
  893.     if( usecp )
  894.     {
  895.         Timer_GetClientCheckpoint( client, 0, checkpoint );
  896.     }
  897.    
  898.     PassToNext( client, g_iNextTeamMember[client], checkpoint, usecp );
  899.    
  900.     int lastidx = g_aCurrentSegmentPlayers[teamidx].Length - 1;
  901.     g_aCurrentSegmentPlayers[teamidx].Set( lastidx, Influx_GetClientId( g_iNextTeamMember[client] ) );
  902.    
  903.     if( maxPasses > -1 )
  904.     {
  905.         PrintToTeam( teamidx, "{name}%N {primary}has passed! It is now {name}%N{primary}'s turn. {secondary}%i/%i {primary}passes used.", client, g_iNextTeamMember[client], g_nPassCount[teamidx], maxPasses );
  906.     }
  907.     else
  908.     {
  909.         PrintToTeam( teamidx, "{name}%N {primary}has passed! It is now {name}%N{primary}'s turn.", client, g_iNextTeamMember[client] );
  910.     }
  911.    
  912.     return Plugin_Handled;
  913. }
  914.  
  915. public Action Command_Undo( int client, int args )
  916. {
  917.     if( g_iTeamIndex[client] == -1 )
  918.     {
  919.         ReplyToCommand( client, "{primary}You are not currently in a team" );
  920.         return Plugin_Handled;
  921.     }
  922.    
  923.     int teamidx = g_iTeamIndex[client];
  924.    
  925.     int maxUndos = g_cvMaxUndos.IntValue;
  926.     if( maxUndos == -1 || g_nUndoCount[teamidx] >= maxUndos )
  927.     {
  928.         ReplyToCommand( client, "{primary}Your team has already used all %i undos", maxUndos );
  929.         return Plugin_Handled;
  930.     }
  931.    
  932.     if( g_iCurrentPlayer[teamidx] != client )
  933.     {
  934.         ReplyToCommand( client, "{primary}You cannot undo when it is not your turn" );
  935.         return Plugin_Handled;
  936.     }
  937.    
  938.     if( g_nRelayCount[teamidx] == 0 )
  939.     {
  940.         ReplyToCommand( client, "{primary}Cannot undo when no one has saved!" );
  941.         return Plugin_Handled;
  942.     }
  943.    
  944.     if( g_bDidUndo[teamidx] )
  945.     {
  946.         ReplyToCommand( client, "{primary}Your team has already undo-ed this turn" );
  947.         return Plugin_Handled;
  948.     }
  949.    
  950.     int last = -1;
  951.    
  952.     for( int i = 1; i <= MaxClients; i++ )
  953.     {
  954.         if( g_iNextTeamMember[i] == client )
  955.         {
  956.             last = i;
  957.             break;
  958.         }
  959.     }
  960.    
  961.     if( last == -1 )
  962.     {
  963.         LogError( "Failed to find last player" );
  964.         return Plugin_Handled;
  965.     }
  966.    
  967.     PassToNext( client, last, g_LastCheckpoint[teamidx] );
  968.     g_aCurrentSegmentStartTicks[teamidx].Erase( g_aCurrentSegmentStartTicks[teamidx].Length - 1 );
  969.     g_aCurrentSegmentPlayers[teamidx].Erase( g_aCurrentSegmentPlayers[teamidx].Length - 1 );
  970.     g_bDidUndo[teamidx] = true;
  971.     g_nUndoCount[teamidx]++;
  972.    
  973.     if( maxUndos > -1 )
  974.     {
  975.         PrintToTeam( teamidx, "{name}%N {primary}used an undo! It is now {name}%N{primary}'s turn again. {secondary}%i/%i {primary}undos used.", client, last, g_nUndoCount[teamidx], maxUndos );
  976.     }
  977.     else
  978.     {
  979.         PrintToTeam( teamidx, "{name}%N {primary}used an undo! It is now {name}%N{primary}'s turn again.", client, last );
  980.     }
  981.     return Plugin_Handled;
  982. }
  983.  
  984. // Records/Database stuff
  985.  
  986. void OnTimerFinishCustom( int client, int track, int style, float time, Handle fwdInsertedPre, Handle fwdInsertedPost, Handle fwdUpdatedPre, Handle fwdUpdatedPost, Handle fwdWRBeaten )
  987. {
  988.     if( g_iTeamIndex[client] == -1 )
  989.     {
  990.         LogError( "%N finished on tagteam without a team!", client );
  991.         return;
  992.     }
  993.    
  994.     float wr = g_aMapTopTimes[track][style].Length ? g_aMapTopTimes[track][style].Get( 0 ) : 0.0;
  995.     if( wr == 0.0 || time < wr )
  996.     {
  997.         Call_StartForward( fwdWRBeaten );
  998.         Call_PushCell( client );
  999.         Call_PushCell( track );
  1000.         Call_PushCell( style );
  1001.         Call_PushCell( time );
  1002.         Call_PushCell( wr );
  1003.         Call_Finish();
  1004.     }
  1005.    
  1006.     ArrayList frames = Influx_GetClientReplayFrames( client );
  1007.    
  1008.     for( int i = 1; i <= MaxClients; i++ )
  1009.     {
  1010.         if( i != client && g_iTeamIndex[i] == g_iTeamIndex[client] )
  1011.         {
  1012.             Influx_SetClientReplayFrames( i, frames );
  1013.         }
  1014.     }
  1015.    
  1016.     delete frames;
  1017.    
  1018.     SQL_InsertRecord( client, g_iTeamIndex[client], track, style, time, fwdInsertedPre, fwdInsertedPost, fwdUpdatedPre, fwdUpdatedPost );
  1019. }
  1020.  
  1021. void SQL_CreateTables()
  1022. {
  1023.     if( g_hDatabase == null )
  1024.     {
  1025.         return;
  1026.     }
  1027.    
  1028.     Transaction txn = new Transaction();
  1029.    
  1030.     // makes a table that links multiple players to 1 record
  1031.     char query[512];
  1032.     Format( query, sizeof(query), "CREATE TABLE IF NOT EXISTS `t_tagteam_records` ( tt_recordid INT NOT NULL AUTO_INCREMENT, \
  1033.                                                                                     teamname CHAR(64) NOT NULL, \
  1034.                                                                                     mapid INT NOT NULL, \
  1035.                                                                                     timestamp INT NOT NULL, \
  1036.                                                                                     time FLOAT NOT NULL, \
  1037.                                                                                     track INT NOT NULL, \
  1038.                                                                                     style INT NOT NULL, \
  1039.                                                                                     jumps INT NOT NULL, \
  1040.                                                                                     strafes INT NOT NULL, \
  1041.                                                                                     sync FLOAT NOT NULL, \
  1042.                                                                                     strafetime FLOAT NOT NULL, \
  1043.                                                                                     ssj INT NOT NULL, \
  1044.                                                                                     PRIMARY KEY (`tt_recordid`) );" );
  1045.                                                                                    
  1046.     txn.AddQuery( query );
  1047.    
  1048.     Format( query, sizeof(query), "CREATE TABLE IF NOT EXISTS `t_tagteam_pb` ( tt_timeid INT NOT NULL AUTO_INCREMENT, \
  1049.                                                                                 playerid INT NOT NULL, \
  1050.                                                                                 tt_recordid INT NOT NULL, \
  1051.                                                                                 PRIMARY KEY (`tt_timeid`) );" );
  1052.    
  1053.     txn.AddQuery( query );
  1054.    
  1055.     Format( query, sizeof(query), "CREATE TABLE IF NOT EXISTS `t_tagteam_segments` ( tt_segmentid INT NOT NULL AUTO_INCREMENT, \
  1056.                                                                                     playerid INT NOT NULL, \
  1057.                                                                                     recordid INT NOT NULL, \
  1058.                                                                                     starttick INT NOT NULL, \
  1059.                                                                                     PRIMARY KEY (`tt_segmentid`) );" );
  1060.    
  1061.     txn.AddQuery( query );
  1062.    
  1063.     g_hDatabase.Execute( txn, CreateTableSuccess_Callback, CreateTableFailure_Callback, _, DBPrio_High );
  1064. }
  1065.  
  1066. public void CreateTableSuccess_Callback( Database db, any data, int numQueries, DBResultSet[] results, any[] queryData )
  1067. {
  1068.     if( Influx_GetCurrentMapId() > -1 )
  1069.     {
  1070.         SQL_LoadAllMapRecords();
  1071.     }
  1072.    
  1073.     for( int i = 1; i <= MaxClients; i++ )
  1074.     {
  1075.         if( Influx_IsClientCached( i ) )
  1076.         {
  1077.             SQL_LoadAllPlayerTimes( i );
  1078.         }
  1079.     }
  1080. }
  1081.  
  1082. public void CreateTableFailure_Callback( Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData )
  1083. {
  1084.     SetFailState( "[SQL ERROR] (CreateTableFailure_Callback) - %s", error );
  1085. }
  1086.  
  1087. void SQL_LoadAllPlayerTimes( int client )
  1088. {
  1089.     SQL_LoadPlayerTime( client, 1, MODE_TAGTEAM );
  1090.     SQL_LoadPlayerTime( client, 2, MODE_TAGTEAM );
  1091. }
  1092.  
  1093. void SQL_LoadPlayerTime( int client, int track, int style )
  1094. {
  1095.     char query[512];
  1096.     Format( query, sizeof(query), "SELECT r.tt_recordid, r.time FROM `t_tagteam_records` r \
  1097.                                 JOIN `t_tagteam_pb` t ON r.tt_recordid = t.tt_recordid \
  1098.                                 WHERE t.playerid = '%i' AND r.mapid = '%i' AND r.track = '%i' AND r.style = '%i'",
  1099.                                 Influx_GetClientId( client ),
  1100.                                 Influx_GetCurrentMapId(),
  1101.                                 track,
  1102.                                 style );
  1103.    
  1104.     DataPack pack = new DataPack();
  1105.     pack.WriteCell( GetClientUserId( client ) );
  1106.     pack.WriteCell( track );
  1107.     pack.WriteCell( style );
  1108.    
  1109.     g_hDatabase.Query( LoadPlayerTime_Callback, query, pack );
  1110. }
  1111.  
  1112. public void LoadPlayerTime_Callback( Database db, DBResultSet results, const char[] error, DataPack pack )
  1113. {
  1114.     if( results == null )
  1115.     {
  1116.         LogError( "[SQL ERROR] (LoadPlayerTime_Callback) - %s", error );
  1117.         delete pack;
  1118.         return;
  1119.     }
  1120.    
  1121.     pack.Reset();
  1122.     int client = GetClientOfUserId( pack.ReadCell() );
  1123.     int track = pack.ReadCell();
  1124.     int style = pack.ReadCell();
  1125.     delete pack;
  1126.    
  1127.     if( !( 0 < client <= MaxClients ) )
  1128.     {
  1129.         return;
  1130.     }
  1131.    
  1132.    
  1133.     if( results.FetchRow() )
  1134.     {
  1135.         g_iRecordId[client][track][style] = results.FetchInt( 0 );
  1136.         g_fPersonalBest[client][track][style] = results.FetchFloat( 1 );
  1137.         //Timer_DebugPrint( "LoadPlayerTime_Callback: %N recordid=%i pb=%f", client, g_iRecordId[client][track][style], g_fPersonalBest[client][track][style] );
  1138.     }
  1139. }
  1140.  
  1141. void SQL_InsertRecord( int client, int teamidx, int track, int style, float time, Handle fwdInsertedPre, Handle fwdInsertedPost, Handle fwdUpdatedPre, Handle fwdUpdatedPost )
  1142. {
  1143.     char query[512];
  1144.     Format( query, sizeof(query), "INSERT INTO `t_tagteam_records` (teamname, mapid, timestamp, time, track, style, jumps, strafes, sync, strafetime, ssj) \
  1145.                                 VALUES ('%s', '%i', '%i', '%f', '%i', '%i', '%i', '%i', '%f', '%f', '%i')",
  1146.                                 g_cTeamName[teamidx],
  1147.                                 Influx_GetCurrentMapId(),
  1148.                                 GetTime(),
  1149.                                 time,
  1150.                                 track,
  1151.                                 style,
  1152.                                 Timer_GetClientCurrentJumps( client ),
  1153.                                 Timer_GetClientCurrentStrafes( client ),
  1154.                                 Timer_GetClientCurrentSync( client ),
  1155.                                 Timer_GetClientCurrentStrafeTime( client ),
  1156.                                 Timer_GetClientCurrentSSJ( client ) );
  1157.    
  1158.     DataPack pack = new DataPack();
  1159.     pack.WriteCell( teamidx );
  1160.     pack.WriteCell( track );
  1161.     pack.WriteCell( style );
  1162.     pack.WriteFloat( time );
  1163.     pack.WriteCell( fwdInsertedPre );
  1164.     pack.WriteCell( fwdInsertedPost );
  1165.     pack.WriteCell( fwdUpdatedPre );
  1166.     pack.WriteCell( fwdUpdatedPost );
  1167.    
  1168.     g_hDatabase.Query( InsertRecord_Callback, query, pack );
  1169. }
  1170.  
  1171. public void InsertRecord_Callback( Database db, DBResultSet results, const char[] error, DataPack pack )
  1172. {
  1173.     if( results == null )
  1174.     {
  1175.         LogError( "[SQL ERROR] (InsertRecord_Callback) - %s", error );
  1176.         delete pack;
  1177.         return;
  1178.     }
  1179.    
  1180.     int recordid = results.InsertId;
  1181.    
  1182.     pack.Reset();
  1183.     int teamidx = pack.ReadCell();
  1184.     int track = pack.ReadCell();
  1185.     int style = pack.ReadCell();
  1186.     float time = pack.ReadFloat();
  1187.     Handle fwdInsertedPre = pack.ReadCell();
  1188.     Handle fwdInsertedPost = pack.ReadCell();
  1189.     Handle fwdUpdatedPre = pack.ReadCell();
  1190.     Handle fwdUpdatedPost = pack.ReadCell();
  1191.     delete pack;
  1192.    
  1193.     for( int i = 1; i <= MaxClients; i++ )
  1194.     {
  1195.         if( g_iTeamIndex[i] == teamidx )
  1196.         {
  1197.             if( g_fPersonalBest[i][track][style] == 0.0 || time <= g_fPersonalBest[i][track][style] )
  1198.             {
  1199.                 if( g_fPersonalBest[i][track][style] == 0.0 )
  1200.                 {
  1201.                     SQL_InsertPlayerTime( i, track, style, time, recordid, fwdInsertedPre, fwdInsertedPost );
  1202.                 }
  1203.                 else
  1204.                 {
  1205.                     SQL_UpdatePlayerTime( i, track, style, time, recordid, fwdUpdatedPre, fwdUpdatedPost );
  1206.                 }
  1207.             }
  1208.         }
  1209.     }
  1210.    
  1211.     g_bLoadSegmentsAfterInserting[track][style] = false;
  1212.     g_bFinishedInsertingSegments[track][style] = false;
  1213.     SQL_InsertSegments( teamidx, track, style, recordid );
  1214.    
  1215.     SQL_LoadMapRecords( track, style );
  1216. }
  1217.  
  1218. void SQL_InsertPlayerTime( int client, int track, int style, float time, int recordid, Handle fwdInsertedPre, Handle fwdInsertedPost )
  1219. {
  1220.     //Timer_DebugPrint( "Inserting time for %N", client );
  1221.  
  1222.     any result = Plugin_Continue;
  1223.     Call_StartForward( fwdInsertedPre );
  1224.     Call_PushCell( client );
  1225.     Call_PushCell( track );
  1226.     Call_PushCell( style );
  1227.     Call_PushFloat( time );
  1228.     Call_Finish( result );
  1229.    
  1230.     if( result == Plugin_Handled || result == Plugin_Stop )
  1231.     {
  1232.         return;
  1233.     }
  1234.  
  1235.     g_iRecordId[client][track][style] = recordid;
  1236.     g_fPersonalBest[client][track][style] = time;
  1237.    
  1238.     char query[512];
  1239.     Format( query, sizeof(query), "INSERT INTO `t_tagteam_pb` (playerid, tt_recordid) \
  1240.                                 VALUES ('%i', '%i')",
  1241.                                 Influx_GetClientId( client ), recordid );
  1242.    
  1243.     DataPack pack = new DataPack();
  1244.     pack.WriteCell( GetClientUserId( client ) );
  1245.     pack.WriteCell( track );
  1246.     pack.WriteCell( style );
  1247.     pack.WriteFloat( time );
  1248.     pack.WriteCell( recordid );
  1249.     pack.WriteCell( fwdInsertedPost );
  1250.    
  1251.     g_hDatabase.Query( InsertPlayerTime_Callback, query, pack );
  1252. }
  1253.  
  1254. public void InsertPlayerTime_Callback( Database db, DBResultSet results, const char[] error, DataPack pack )
  1255. {
  1256.     if( results == null )
  1257.     {
  1258.         LogError( "[SQL ERROR] (InsertPlayerTime_Callback) - %s", error );
  1259.         delete pack;
  1260.         return;
  1261.     }
  1262.    
  1263.     pack.Reset();
  1264.     int client = GetClientOfUserId( pack.ReadCell() );
  1265.     int track = pack.ReadCell();
  1266.     int style = pack.ReadCell();
  1267.     float time = pack.ReadFloat();
  1268.     int recordid = pack.ReadCell();
  1269.     Handle fwdInsertedPost = pack.ReadCell();
  1270.     delete pack;
  1271.    
  1272.     if( !( 0 < client <= MaxClients ) )
  1273.     {
  1274.         return;
  1275.     }
  1276.    
  1277.     Call_StartForward( fwdInsertedPost );
  1278.     Call_PushCell( client );
  1279.     Call_PushCell( track );
  1280.     Call_PushCell( style );
  1281.     Call_PushFloat( time );
  1282.     Call_PushCell( recordid );
  1283.     Call_Finish();
  1284. }
  1285.  
  1286. void SQL_UpdatePlayerTime( int client, int track, int style, float time, int recordid, Handle fwdUpdatedPre, Handle fwdUpdatedPost )
  1287. {
  1288.     any result = Plugin_Continue;
  1289.     Call_StartForward( fwdUpdatedPre );
  1290.     Call_PushCell( client );
  1291.     Call_PushCell( track );
  1292.     Call_PushCell( style );
  1293.     Call_PushFloat( time );
  1294.     Call_Finish( result );
  1295.    
  1296.     if( result == Plugin_Handled || result == Plugin_Stop )
  1297.     {
  1298.         return;
  1299.     }
  1300.  
  1301.     char query[512];
  1302.     Format( query, sizeof(query), "UPDATE `t_tagteam_pb` SET tt_recordid = '%i' \
  1303.                                 WHERE playerid = '%i' AND tt_recordid = '%i'",
  1304.                                 recordid, Influx_GetClientId( client ), g_iRecordId[client] );
  1305.    
  1306.     DataPack pack = new DataPack();
  1307.     pack.WriteCell( GetClientUserId( client ) );
  1308.     pack.WriteCell( track );
  1309.     pack.WriteCell( style );
  1310.     pack.WriteFloat( time );
  1311.     pack.WriteCell( recordid );
  1312.     pack.WriteCell( fwdUpdatedPost );
  1313.    
  1314.     g_hDatabase.Query( UpdatePlayerTime_Callback, query, pack );
  1315.    
  1316.     g_iRecordId[client][track][style] = recordid;
  1317.     g_fPersonalBest[client][track][style] = time;
  1318. }
  1319.  
  1320. public void UpdatePlayerTime_Callback( Database db, DBResultSet results, const char[] error, DataPack pack )
  1321. {
  1322.     if( results == null )
  1323.     {
  1324.         LogError( "[SQL ERROR] (UpdatePlayerTime_Callback) - %s", error );
  1325.         delete pack;
  1326.         return;
  1327.     }
  1328.    
  1329.     pack.Reset();
  1330.     int client = GetClientOfUserId( pack.ReadCell() );
  1331.     int track = pack.ReadCell();
  1332.     int style = pack.ReadCell();
  1333.     float time = pack.ReadFloat();
  1334.     int recordid = pack.ReadCell();
  1335.     Handle fwdUpdatedPost = pack.ReadCell();
  1336.     delete pack;
  1337.    
  1338.     if( !( 0 < client <= MaxClients ) )
  1339.     {
  1340.         return;
  1341.     }
  1342.    
  1343.     Call_StartForward( fwdUpdatedPost );
  1344.     Call_PushCell( client );
  1345.     Call_PushCell( track );
  1346.     Call_PushCell( style );
  1347.     Call_PushFloat( time );
  1348.     Call_PushCell( recordid );
  1349.     Call_Finish();
  1350. }
  1351.  
  1352. void SQL_LoadAllMapRecords()
  1353. {
  1354.     SQL_LoadMapRecords( 1, MODE_TAGTEAM );
  1355.     SQL_LoadMapRecords( 2, MODE_TAGTEAM );
  1356. }
  1357.  
  1358. void SQL_LoadMapRecords( int track, int style )
  1359. {
  1360.     //Timer_DebugPrint( "SQL_LoadMapRecords: Loading map records" );
  1361.  
  1362.     char query[512];
  1363.     Format( query, sizeof(query), "SELECT tt_recordid, time, teamname FROM `t_tagteam_records` \
  1364.                                     WHERE mapid = '%i' AND track = '%i' AND style = '%i' \
  1365.                                     ORDER BY time ASC",
  1366.                                     Influx_GetCurrentMapId(), track, style );
  1367.    
  1368.     DataPack pack = new DataPack();
  1369.     pack.WriteCell( track );
  1370.     pack.WriteCell( style );
  1371.    
  1372.     g_hDatabase.Query( LoadMapRecords_Callback, query, pack, DBPrio_High );
  1373. }
  1374.  
  1375. public void LoadMapRecords_Callback( Database db, DBResultSet results, const char[] error, DataPack pack )
  1376. {
  1377.     if( results == null )
  1378.     {
  1379.         LogError( "[SQL ERROR] (LoadMapRecords_Callback) - %s", error );
  1380.         delete pack;
  1381.         return;
  1382.     }
  1383.    
  1384.     pack.Reset();
  1385.     int track = pack.ReadCell();
  1386.     int style = pack.ReadCell();
  1387.     delete pack;
  1388.    
  1389.     g_aMapTopRecordIds[track][style].Clear();
  1390.     g_aMapTopTimes[track][style].Clear();
  1391.     g_aMapTopNames[track][style].Clear();
  1392.    
  1393.    
  1394.     //Timer_DebugPrint( "LoadMapRecords_Callback: Got %i rows", results.RowCount );
  1395.    
  1396.     char name[MAX_NAME_LENGTH];
  1397.     while( results.FetchRow() )
  1398.     {
  1399.         g_aMapTopRecordIds[track][style].Push( results.FetchInt( 0 ) );
  1400.         g_aMapTopTimes[track][style].Push( results.FetchFloat( 1 ) );
  1401.         results.FetchString( 2, name, sizeof(name) );
  1402.         g_aMapTopNames[track][style].PushString( name );
  1403.     }
  1404. }
  1405.  
  1406. void SQL_InsertSegments( int teamidx, int track, int style, int recordid )
  1407. {
  1408.     char query[256];
  1409.  
  1410.     int length = g_aCurrentSegmentStartTicks[teamidx].Length;
  1411.     for( int i = 0; i < length; i++ )
  1412.     {  
  1413.         Format( query, sizeof(query), "INSERT INTO `t_tagteam_segments` (playerid, recordid, starttick) VALUES ('%i', '%i', '%i')",
  1414.                                         g_aCurrentSegmentPlayers[teamidx].Get( i ),
  1415.                                         recordid,
  1416.                                         g_aCurrentSegmentStartTicks[teamidx].Get( i ) );
  1417.        
  1418.         DataPack pack = null;
  1419.         if( i == length - 1 )
  1420.         {
  1421.             pack = new DataPack();
  1422.             pack.WriteCell( track );
  1423.             pack.WriteCell( style );
  1424.         }
  1425.        
  1426.         g_hDatabase.Query( InsertSegments_Callback, query, pack );
  1427.     }
  1428. }
  1429.  
  1430. public void InsertSegments_Callback( Database db, DBResultSet results, const char[] error, DataPack pack )
  1431. {
  1432.     if( results == null )
  1433.     {
  1434.         LogError( "[SQL ERROR] (InsertSegments_Callback) - %s", error );
  1435.         delete pack;
  1436.         return;
  1437.     }
  1438.    
  1439.     if( pack == null )
  1440.     {
  1441.         return;
  1442.     }
  1443.    
  1444.     pack.Reset();
  1445.     int track = pack.ReadCell();
  1446.     int style = pack.ReadCell();
  1447.     delete pack;
  1448.    
  1449.     if( g_bLoadSegmentsAfterInserting[track][style] )
  1450.     {
  1451.         int recordid = Timer_GetReplayRecordId( track, style );
  1452.         //Timer_DebugPrint( "InsertSegments_Callback: Loading segments (recordid=%i)", recordid );
  1453.         if( recordid > -1 )
  1454.         {
  1455.             SQL_LoadSegments( track, style, recordid );
  1456.         }
  1457.     }
  1458.     else
  1459.     {
  1460.         //Timer_DebugPrint( "InsertSegments_Callback: Finished inserting segments" );
  1461.         g_bFinishedInsertingSegments[track][style] = true;
  1462.     }
  1463. }
  1464.  
  1465. void SQL_LoadSegments( int track, int style, int recordid )
  1466. {
  1467.     char query[512];
  1468.     Format( query, sizeof(query), "SELECT s.starttick, p.lastname FROM `t_tagteam_segments` s \
  1469.                                 JOIN `t_players` p ON p.playerid = s.playerid \
  1470.                                 WHERE s.recordid = '%i' \
  1471.                                 ORDER BY s.starttick ASC", recordid );
  1472.    
  1473.     DataPack pack = new DataPack();
  1474.     pack.WriteCell( track );
  1475.     pack.WriteCell( style );
  1476.    
  1477.     g_hDatabase.Query( LoadSegments_Callback, query, pack );
  1478. }
  1479.  
  1480. public void LoadSegments_Callback( Database db, DBResultSet results, const char[] error, DataPack pack )
  1481. {
  1482.     if( results == null )
  1483.     {
  1484.         LogError( "[SQL ERROR] (LoadSegments_Callback) - %s", error );
  1485.         delete pack;
  1486.         return;
  1487.     }
  1488.    
  1489.     pack.Reset();
  1490.     int track = pack.ReadCell();
  1491.     int style = pack.ReadCell();
  1492.     delete pack;
  1493.    
  1494.     delete g_smSegmentPlayerNames[track][style];
  1495.     g_smSegmentPlayerNames[track][style] = new StringMap();
  1496.    
  1497.     char sStartTick[8];
  1498.     char name[MAX_NAME_LENGTH];
  1499.     while( results.FetchRow() )
  1500.     {
  1501.         IntToString( results.FetchInt( 0 ), sStartTick, sizeof(sStartTick) );
  1502.         results.FetchString( 1, name, sizeof(name) );
  1503.        
  1504.         g_smSegmentPlayerNames[track][style].SetString( sStartTick, name );
  1505.     }
  1506. }
  1507.  
  1508. public Action Timer_OnClientRankRequested( int client, int track, int style, int& rank )
  1509. {
  1510.     if( Timer_StyleHasSetting( style, "tagteam" ) )
  1511.     {
  1512.         rank = GetClientMapRank( client, track, style );
  1513.         return Plugin_Handled;
  1514.     }
  1515.    
  1516.     return Plugin_Continue;
  1517. }
  1518.  
  1519. public Action Timer_OnClientPBTimeRequested( int client, int track, int style, float& time )
  1520. {
  1521.     if( Timer_StyleHasSetting( style, "tagteam" ) )
  1522.     {
  1523.         time = g_fPersonalBest[client][track][style];
  1524.         return Plugin_Handled;
  1525.     }
  1526.    
  1527.     return Plugin_Continue;
  1528. }
  1529.  
  1530. public Action Timer_OnWRTimeRequested( int track, int style, float& time )
  1531. {
  1532.     if( Timer_StyleHasSetting( style, "tagteam" ) )
  1533.     {
  1534.         if( g_aMapTopTimes[track][style].Length > 0 )
  1535.         {
  1536.             time = g_aMapTopTimes[track][style].Get( 0 );
  1537.         }
  1538.         else
  1539.         {
  1540.             time = 0.0;
  1541.         }
  1542.         return Plugin_Handled;
  1543.     }
  1544.    
  1545.     return Plugin_Continue;
  1546. }
  1547.  
  1548. public Action Timer_OnWRNameRequested( int track, int style, char name[MAX_NAME_LENGTH] )
  1549. {
  1550.     if( Timer_StyleHasSetting( style, "tagteam" ) )
  1551.     {
  1552.         g_aMapTopNames[track][style].GetString( 0, name, sizeof(name) );
  1553.         return Plugin_Handled;
  1554.     }
  1555.    
  1556.     return Plugin_Continue;
  1557. }
  1558.  
  1559. public Action Timer_OnRecordsCountRequested( int track, int style, int& recordcount )
  1560. {
  1561.     if( Timer_StyleHasSetting( style, "tagteam" ) )
  1562.     {
  1563.         recordcount = g_aMapTopTimes[track][style].Length;
  1564.         return Plugin_Handled;
  1565.     }
  1566.    
  1567.     return Plugin_Continue;
  1568. }
  1569.  
  1570. public Action Timer_OnLeaderboardRequested( int client, int track, int style )
  1571. {
  1572.     if( Timer_StyleHasSetting( style, "tagteam" ) )
  1573.     {
  1574.         int length = g_aMapTopTimes[track][style].Length;
  1575.        
  1576.         if( length == 0 )
  1577.         {
  1578.             PrintToChat( client, "{primary}No records found" );
  1579.             return Plugin_Handled;
  1580.         }
  1581.        
  1582.         Menu menu = new Menu( Leaderboard_Handler );
  1583.         menu.SetTitle( "Tagteam leaderboard\n \n" );
  1584.        
  1585.         char buffer[256];
  1586.        
  1587.         int max = length > 50 ? 50 : length;
  1588.         for( int i = 0; i < max; i++ )
  1589.         {
  1590.             char name[MAX_NAME_LENGTH];
  1591.             g_aMapTopNames[track][style].GetString( i, name, sizeof(name) );
  1592.            
  1593.             float time = g_aMapTopTimes[track][style].Get( i );
  1594.             char sTime[32];
  1595.             Timer_FormatTime( time, sTime, sizeof(sTime) );
  1596.            
  1597.             char sRecordId[8];
  1598.             IntToString( g_aMapTopRecordIds[track][style].Get( i ), sRecordId, sizeof(sRecordId) );
  1599.            
  1600.             Format( buffer, sizeof(buffer), "[#%i] %s (%s)", i + 1, name, sTime );
  1601.            
  1602.             menu.AddItem( sRecordId, buffer );
  1603.         }
  1604.        
  1605.         menu.Display( client, MENU_TIME_FOREVER );
  1606.        
  1607.         return Plugin_Handled;
  1608.     }
  1609.    
  1610.     return Plugin_Continue;
  1611. }
  1612.  
  1613. public int Leaderboard_Handler( Menu menu, MenuAction action, int param1, int param2 )
  1614. {
  1615.     if( action == MenuAction_Select )
  1616.     {
  1617.         char info[8];
  1618.         menu.GetItem( param2, info, sizeof(info) );
  1619.        
  1620.         int recordid = StringToInt( info );
  1621.         SQL_ShowTeamStats( param1, recordid );
  1622.     }
  1623.     else if( action == MenuAction_End )
  1624.     {
  1625.         delete menu;
  1626.     }
  1627. }
  1628.  
  1629. void SQL_ShowTeamStats( int client, int recordid )
  1630. {
  1631.     // tt_recordid INT NOT NULL AUTO_INCREMENT,
  1632.     // teamname CHAR(64) NOT NULL,
  1633.     // mapid INT NOT NULL,
  1634.     // timestamp INT NOT NULL,
  1635.     // time FLOAT NOT NULL,
  1636.     // track INT NOT NULL,
  1637.     // style INT NOT NULL,
  1638.     // jumps INT NOT NULL,
  1639.     // strafes INT NOT NULL,
  1640.     // sync FLOAT NOT NULL,
  1641.     // strafetime FLOAT NOT NULL,
  1642.     // ssj INT NOT NULL,
  1643.  
  1644.     DataPack pack = new DataPack();
  1645.     pack.WriteCell( GetClientUserId( client ) );
  1646.     pack.WriteCell( recordid );
  1647.    
  1648.     char query[1024];
  1649.     Format( query, sizeof(query), "SELECT pb.playerid, player.lastname, r.teamname, map.mapname, r.timestamp, r.time, r.track, r.style, r.jumps, r.strafes, r.sync, r.strafetime, r.ssj \
  1650.                                 FROM `t_tagteam_records` r \
  1651.                                 JOIN `t_tagteam_pb` pb ON pb.tt_recordid = r.tt_recordid \
  1652.                                 JOIN `t_players` player ON pb.playerid = player.playerid \
  1653.                                 JOIN `t_maps` map ON r.mapid = map.mapid \
  1654.                                 WHERE r.tt_recordid = '%i'",
  1655.                                 recordid );
  1656.    
  1657.     g_hDatabase.Query( ShowTeamStats_Callback, query, pack );
  1658. }
  1659.  
  1660. public void ShowTeamStats_Callback( Database db, DBResultSet results, const char[] error, DataPack pack )
  1661. {
  1662.     if( results == null )
  1663.     {
  1664.         LogError( "[SQL ERROR] (ShowTeamStats_Callback) - %s", error );
  1665.         delete pack;
  1666.         return;
  1667.     }
  1668.    
  1669.     pack.Reset();
  1670.     int client = GetClientOfUserId( pack.ReadCell() );
  1671.     int recordid = pack.ReadCell();
  1672.     delete pack;
  1673.    
  1674.     if( !( 0 < client <= MaxClients ) )
  1675.     {
  1676.         return;
  1677.     }
  1678.    
  1679.     ArrayList playerids = new ArrayList();
  1680.     ArrayList playernames = new ArrayList( ByteCountToCells( MAX_NAME_LENGTH ) );
  1681.    
  1682.     char buffer[512];
  1683.     char teamname[64];
  1684.     char mapname[PLATFORM_MAX_PATH];
  1685.     int timestamp;
  1686.     float time;
  1687.     int track;
  1688.     int style;
  1689.     int jumps;
  1690.     int strafes;
  1691.     float sync;
  1692.     float strafetime;
  1693.     int ssj;
  1694.    
  1695.     bool fetchedStats = false;
  1696.     while( results.FetchRow() )
  1697.     {
  1698.         playerids.Push( results.FetchInt( 0 ) );
  1699.         results.FetchString( 1, buffer, sizeof(buffer) );
  1700.         playernames.PushString( buffer );
  1701.        
  1702.         // kind of aids, but i dont wan't to split this up into 2 queries.
  1703.         // just get the info once at the start, all rows should have the same stats
  1704.         if( !fetchedStats )
  1705.         {
  1706.             results.FetchString( 2, teamname, sizeof(teamname) );
  1707.             results.FetchString( 3, mapname, sizeof(mapname) );
  1708.             timestamp = results.FetchInt( 4 );
  1709.             time = results.FetchFloat( 5 );
  1710.             track = results.FetchInt( 6 );
  1711.             style = results.FetchInt( 7 );
  1712.             jumps = results.FetchInt( 8 );
  1713.             strafes = results.FetchInt( 9 );
  1714.             sync = results.FetchFloat( 10 );
  1715.             strafetime = results.FetchFloat( 11 );
  1716.             ssj = results.FetchInt( 12 );
  1717.            
  1718.             fetchedStats = true;
  1719.         }
  1720.     }
  1721.  
  1722.     Menu menu = new Menu( ShowTeamStats_Handler );
  1723.  
  1724.     char date[128];
  1725.     FormatTime( date, sizeof(date), "%d/%m/%Y - %H:%M:%S", timestamp );
  1726.     char sTime[64];
  1727.     Timer_FormatTime( time, sTime, sizeof(sTime) );
  1728.    
  1729.     char sTrack[16];
  1730.     Timer_GetZoneTrackName( track, sTrack, sizeof(sTrack) );
  1731.    
  1732.     any settings[styleSettings];
  1733.     Timer_GetStyleSettings( style, settings );
  1734.    
  1735.     char sSync[10];
  1736.     if( settings[Sync] )
  1737.     {
  1738.         Format( sSync, sizeof(sSync), "(%.2f)", sync );
  1739.     }
  1740.    
  1741.     Format( buffer, sizeof(buffer), "%s - %s %s\n \n%s:", mapname, sTrack, settings[StyleName], teamname);
  1742.     menu.SetTitle( buffer );
  1743.    
  1744.     char sInfo[8];
  1745.    
  1746.     int playerCount = playerids.Length;
  1747.     for( int i = 0; i < playerCount; i++ )
  1748.     {
  1749.         IntToString( playerids.Get( i ), sInfo, sizeof(sInfo) );
  1750.         playernames.GetString( i, buffer, sizeof(buffer) );
  1751.        
  1752.         if( i == playerCount - 1 )
  1753.         {
  1754.             Format( buffer, sizeof(buffer), "%s\n \n", buffer );
  1755.             Format( buffer, sizeof(buffer), "%sDate: %s\n", buffer, date );
  1756.             Format( buffer, sizeof(buffer), "%sTime: %s\n \n", buffer, sTime );
  1757.             Format( buffer, sizeof(buffer), "%sJumps: %i\n", buffer, jumps );
  1758.             Format( buffer, sizeof(buffer), "%sStrafes: %i %s\n", buffer, strafes, sSync );
  1759.             Format( buffer, sizeof(buffer), "%sStrafe Time %: %.2f\n", buffer, strafetime );
  1760.             Format( buffer, sizeof(buffer), "%sSSJ: %i\n \n", buffer, ssj );
  1761.         }
  1762.        
  1763.         menu.AddItem( sInfo, buffer );
  1764.     }
  1765.    
  1766.     if( CheckCommandAccess( client, "delete_time", ADMFLAG_RCON ) )
  1767.     {
  1768.         Format( buffer, sizeof(buffer), "delete;%i;%i;%i", track, style, recordid );
  1769.         menu.AddItem( buffer, "Delete Time" );
  1770.     }
  1771.    
  1772.     menu.Display( client, MENU_TIME_FOREVER );
  1773.    
  1774.     delete playerids;
  1775.     delete playernames;
  1776. }
  1777.  
  1778. public int ShowTeamStats_Handler( Menu menu, MenuAction action, int param1, int param2 )
  1779. {
  1780.     if( action == MenuAction_Select )
  1781.     {
  1782.        
  1783.         char sInfo[64];
  1784.         menu.GetItem( param2, sInfo, sizeof(sInfo) );
  1785.        
  1786.         // user pressed delete
  1787.         if( StrContains( sInfo, "delete" ) )
  1788.         {
  1789.             char sRecordData[4][32];
  1790.             ExplodeString( sInfo, ";", sRecordData, sizeof(sRecordData), sizeof(sRecordData[]) );
  1791.            
  1792.             int track = StringToInt( sRecordData[1] );
  1793.             int style = StringToInt( sRecordData[2] );
  1794.             int recordid = StringToInt( sRecordData[3] );
  1795.             SQL_DeleteRecord( track, style, recordid );
  1796.         }
  1797.         // user pressed player name
  1798.         else
  1799.         {
  1800.             // TODO: show player stats once its implemented in timer-records
  1801.         }
  1802.     }
  1803.     else if( action == MenuAction_End )
  1804.     {
  1805.         delete menu;
  1806.     }
  1807. }
  1808.  
  1809. void SQL_DeleteRecord( int track, int style, int recordid )
  1810. {
  1811.     char query[512];
  1812.  
  1813.     {
  1814.         DataPack pack = new DataPack();
  1815.         pack.WriteCell( track );
  1816.         pack.WriteCell( style );
  1817.         pack.WriteCell( recordid );
  1818.        
  1819.         Format( query, sizeof(query), "DELETE FROM `t_tagteam_records` WHERE tt_recordid = '%i'", recordid );
  1820.         g_hDatabase.Query( DeleteRecord_Callback, query, pack );
  1821.     }
  1822.    
  1823.     {
  1824.         DataPack pack = new DataPack();
  1825.         pack.WriteCell( track );
  1826.         pack.WriteCell( style );
  1827.         pack.WriteCell( recordid );
  1828.    
  1829.         Format( query, sizeof(query), "DELETE FROM `t_tagteam_pb` WHERE tt_recordid = '%i'", recordid );
  1830.         g_hDatabase.Query( DeletePB_Callback, query, recordid );
  1831.     }
  1832.    
  1833.     {
  1834.         Format( query, sizeof(query), "DELETE FROM `t_tagteam_segments` WHERE tt_recordid = '%i'", recordid );
  1835.         g_hDatabase.Query( DeleteSegments_Callback, query );
  1836.     }
  1837. }
  1838.  
  1839. public void DeleteRecord_Callback( Database db, DBResultSet results, const char[] error, DataPack pack )
  1840. {
  1841.     if( results == null )
  1842.     {
  1843.         LogError( "[SQL ERROR] (DeleteRecord_Callback) - %s", error );
  1844.         delete pack;
  1845.         return;
  1846.     }
  1847.    
  1848.     pack.Reset();
  1849.     int track = pack.ReadCell();
  1850.     int style = pack.ReadCell();
  1851.     int recordid = pack.ReadCell();
  1852.     delete pack;
  1853.    
  1854.     Timer_CallOnRecordDeleted( track, style, recordid );
  1855.    
  1856.     SQL_LoadMapRecords( track, style );
  1857. }
  1858.  
  1859. public void DeletePB_Callback( Database db, DBResultSet results, const char[] error, DataPack pack )
  1860. {
  1861.     if( results == null )
  1862.     {
  1863.         LogError( "[SQL ERROR] (DeletePB_Callback) - %s", error );
  1864.         delete pack;
  1865.         return;
  1866.     }
  1867.    
  1868.     pack.Reset();
  1869.     int track = pack.ReadCell();
  1870.     int style = pack.ReadCell();
  1871.     int recordid = pack.ReadCell();
  1872.     delete pack;
  1873.    
  1874.     for( int i = 1; i <= MaxClients; i++ )
  1875.     {
  1876.         if( g_iRecordId[i][track][style] == recordid )
  1877.         {
  1878.             g_iRecordId[i][track][style] = -1;
  1879.             g_fPersonalBest[i][track][style] = 0.0;
  1880.         }
  1881.     }
  1882. }
  1883.  
  1884. public void DeleteSegments_Callback( Database db, DBResultSet results, const char[] error, any data )
  1885. {
  1886.     if( results == null )
  1887.     {
  1888.         LogError( "[SQL ERROR] (DeleteSegments_Callback) - %s", error );
  1889.         return;
  1890.     }
  1891. }
  1892.  
  1893. int GetRankForTime( float time, int track, int style )
  1894. {
  1895.     if( time == 0.0 )
  1896.     {
  1897.         return 0;
  1898.     }
  1899.    
  1900.     int nRecords = g_aMapTopTimes[track][style].Length;
  1901.    
  1902.     if( nRecords == 0 )
  1903.     {
  1904.         return 1;
  1905.     }
  1906.    
  1907.     for( int i = 0; i < nRecords; i++ )
  1908.     {
  1909.         float maptime = g_aMapTopTimes[track][style].Get( i );
  1910.         if( time < maptime )
  1911.         {
  1912.             return i + 1;
  1913.         }
  1914.     }
  1915.    
  1916.     return nRecords + 1;
  1917. }
  1918.  
  1919. int GetClientMapRank( int client, int track, int style )
  1920. {
  1921.     // subtract 1 because when times are equal, it counts as next rank
  1922.     return GetRankForTime( g_fPersonalBest[client][track][style], track, style ) - 1;
  1923. }
  1924.  
  1925. // natives
  1926.  
  1927. public int Native_IsClientInTagTeam( Handle handler, int numParams )
  1928. {
  1929.     int client = GetNativeCell( 1 );
  1930.     int teamidx = GetNativeCell( 2 );
  1931.  
  1932.     if( teamidx == -1 )
  1933.     {
  1934.         return g_iTeamIndex[client] != -1;
  1935.     }
  1936.    
  1937.     return g_iTeamIndex[client] == teamidx;
  1938. }
  1939.  
  1940. public int Native_GetClientTeamIndex( Handle handler, int numParams )
  1941. {
  1942.     return g_iTeamIndex[GetNativeCell( 1 )];
  1943. }
  1944.  
  1945. public int Native_GetTeamName( Handle handler, int numParams )
  1946. {
  1947.     int teamidx = GetNativeCell( 1 );
  1948.     if( !g_bTeamTaken[teamidx] )
  1949.     {
  1950.         return false;
  1951.     }
  1952.    
  1953.     SetNativeString( 2, g_cTeamName[teamidx], GetNativeCell( 3 ) );
  1954.    
  1955.     return true;
  1956. }
  1957.  
  1958. // helper functions
  1959.  
  1960. void TeleportClientToZone( int client, int zoneType, int zoneTrack )
  1961. {
  1962.     g_bAllowReset[client] = true;
  1963.     Timer_TeleportClientToZone( client, zoneType, zoneTrack );
  1964. }
  1965.  
  1966. void PassToNext( int client, int next, any checkpoint[eCheckpoint], bool usecp = true )
  1967. {
  1968.     int length;
  1969.    
  1970.     length = Timer_GetTotalCheckpoints( client );
  1971.     for( int i = 0; i < length; i++ )
  1972.     {
  1973.         any cp[eCheckpoint];
  1974.         Timer_GetClientCheckpoint( client, i, cp );
  1975.        
  1976.         if( cp[CP_ReplayFrames] != checkpoint[CP_ReplayFrames] )
  1977.         {
  1978.             delete cp[CP_ReplayFrames];
  1979.         }
  1980.     }
  1981.    
  1982.     length = Timer_GetTotalCheckpoints( next );
  1983.     for( int i = 0; i < length; i++ )
  1984.     {
  1985.         any cp[eCheckpoint];
  1986.         Timer_GetClientCheckpoint( next, i, cp );
  1987.        
  1988.         if( cp[CP_ReplayFrames] != checkpoint[CP_ReplayFrames] )
  1989.         {
  1990.             delete cp[CP_ReplayFrames];
  1991.         }
  1992.     }
  1993.  
  1994.     Timer_ClearClientCheckpoints( client );
  1995.     Timer_ClearClientCheckpoints( next );
  1996.    
  1997.     if( usecp )
  1998.     {
  1999.         Timer_SetClientCheckpoint( next, -1, checkpoint );
  2000.     }
  2001.     ChangeClientTeam( next, CS_TEAM_SPECTATOR );
  2002.     ChangeClientTeam( next, CS_TEAM_T );
  2003.     CS_RespawnPlayer( next );
  2004.    
  2005.     for( int i = 1; i <= MaxClients; i++ )
  2006.     {
  2007.         if( IsClientInGame( i ) && !IsFakeClient( i ) && IsClientObserver( i ) && GetEntPropEnt( client, Prop_Send, "m_hObserverTarget" ) == client )
  2008.         {
  2009.             SetEntPropEnt( client, Prop_Send, "m_hObserverTarget", next );
  2010.             SetEntProp( client, Prop_Send, "m_iObserverMode", 4 );
  2011.         }
  2012.     }
  2013.    
  2014.     ChangeClientTeam( client, CS_TEAM_SPECTATOR );
  2015.     SetEntPropEnt( client, Prop_Send, "m_hObserverTarget", next );
  2016.     SetEntProp( client, Prop_Send, "m_iObserverMode", 4 );
  2017.    
  2018.     g_iCurrentPlayer[g_iTeamIndex[client]] = next;
  2019.    
  2020.     if( usecp )
  2021.     {
  2022.         Timer_TeleportClientToCheckpoint( next, 0 );
  2023.     }
  2024.     Timer_OpenCheckpointsMenu( next );
  2025.     Timer_OpenCheckpointsMenu( client );
  2026. }
  2027.  
  2028. void PrintToTeam( int teamidx, char[] message, any ... )
  2029. {
  2030.     char buffer[512];
  2031.     VFormat( buffer, sizeof(buffer), message, 3 );
  2032.    
  2033.     for( int i = 1; i <= MaxClients; i++ )
  2034.     {
  2035.         if( g_iTeamIndex[i] == teamidx )
  2036.         {
  2037.             PrintToChat( i, buffer );
  2038.         }
  2039.     }
  2040. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement