forked from TFC-Developers/sw_base_plugins
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SW_SKILLS_MYSQL.sma
796 lines (724 loc) · 40.6 KB
/
SW_SKILLS_MYSQL.sma
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
#include "include/api_skills_mysql" // Include file for this plugin source
#include "include/global" // Include file for general functions
#include "include/sql_tquery" // Include file for threaded queries
#include <engine>
#include <fakemeta>
#include "include/utils" // Include file for utility functions
#include "include/sql_dbqueries" // Include file for database queries (provides the strings)
#include "include/effects" // Include file for effects
#include "include/api_skills_mapentities" // Include file for map entities
//global variables
new g_pCvarOldRanks; new g_pCvarOldRuns; // Cvars for the old ranks and runs table
new bool:g_bInRequest[32]; // Array to check if a player is already requesting data
new g_iRequestFor[32]; // Array to check for which player the request is
new g_iRequest[32]; // Array to check which request is being made
new bool:g_bLegacyFound; // Boolean to check if a legacy course was found in the database
new bool:g_bReloaded = false; // Boolean to check if the courses were reloaded
//array of requests number => description
new const g_szRequest[3][32] = { "Retrieve legacy stats", "","" };
//end global variables
//strings
new const g_szPleaseWait[] = "* Requesting data from database, please wait...";
new const g_szNoData[] = "* No data found in the database.";
new const g_szError[] = "* Failed to load the statistics from the database. Please try again later.";
new const g_szWait[] = "* Please wait for the previous request to finish.";
//end strings
enum eSpeedTop_t
{
m_iTopPlayerIdent, // The player's ident (sql player id)
m_sTopPlayerAuthid[MAX_AUTHID_LEN], // The player's authid
m_sTopPlayerName[MAX_NAME_LEN], // The player's name
Float:m_fTime, // The player's time
m_iCourseID, // The course id (Mysql ID not local id)
m_CreatedAt[64], // The date the run was created
m_iPlayerClass, // The player's class
m_bAmxmodx_used // If the player has used amxmodx
}
new Array:g_TopList = Invalid_Array; // Array for the top 100 players
new Array:g_GroupedTopList = Invalid_Array; // Array for the grouped top 100 players
new g_iGroupedTopCount = -1;
new g_iTopCount = -1;
#define MAX_MOTD_RANKS 20
#define RECORD_SOUND "Trumpet1.wav" // The sound played when the all time speedrun record is broken
#define define_inrequest { if(g_bInRequest[id]) { client_print(id, print_chat, g_szWait); return; } }
#define define_sql_error { if(SQLCheckThreadedError(iFailState, hQuery, sError, iError)) { client_print(id, print_chat, g_szError); g_bInRequest[id] = false; return PLUGIN_HANDLED; } }
public plugin_natives() {
register_native("api_sql_insertcourse", "SQLNative_InsertCourse");
register_native("api_sql_insertlegacycps", "SQLNative_InsertLegacyCPs");
register_native("api_sql_insertrun", "SQLNative_InsertRun");
register_native("api_sql_reloadcourses", "SQLNative_ReloadCourses");
register_native("api_sql_updatemapflags", "SQLNative_UpdateMapFlags");
register_library("sw_sql_skills");
}
public plugin_init()
{
RegisterPlugin();
register_clcmd("say /oldtop5", "cmd_oldtop5"); // Shows the old top5
register_clcmd("say /oldstatsme", "cmd_oldstatsme"); // Shows the old stats of the player
register_clcmd("say /oldstats", "menu_deploy"); // Shows the old stats menu
register_clcmd("say", "Handle_Say"); // Handle the say command
register_clcmd("say_team", "Handle_Say"); // Handle the say_team command
g_pCvarOldRanks = register_cvar("sw_sqloldranks", "climb_oldranks") // Cvar for the old ranks table
g_pCvarOldRuns = register_cvar("sw_sqloldruns", "climb_oldrunstable") // Cvar for the old runs table
g_bLegacyFound = false; // Set the legacy found boolean to false
g_TopList = ArrayCreate(eSpeedTop_t); // Create the array for the top 100 players
g_GroupedTopList = ArrayCreate(eSpeedTop_t); // Create the array for the grouped top 100 players
}
public plugin_end()
{
ArrayDestroy(g_TopList); // Destroy the array for the top 100 players
ArrayDestroy(g_GroupedTopList); // Destroy the array for the grouped top 100 players
}
public SQLNative_UpdateMapFlags(iPlugin, iParams) {
new iFlags[1];
iFlags[0] = get_param(1);
new szMapname[64]; get_mapname(szMapname, charsmax(szMapname));
new szQuery[256]; formatex(szQuery, charsmax(szQuery), sql_insertmap, szMapname);
api_SQLAddThreadedQuery(szQuery, "Handle_UpdateMapFlags", QUERY_NOT_DISPOSABLE, PRIORITY_HIGHEST, iFlags, 1);
}
public Handle_UpdateMapFlags(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent) {
if(SQLCheckThreadedError(iFailState, hQuery, sError, iError)) {
DebugPrintLevel(0, "Failed to update map flags: %s", sError);
}
new iFlags = 0;
if (iLen > 0) {
iFlags = Data[0];
}
new szMapname[64]; get_mapname(szMapname, charsmax(szMapname));
new szQuery[256]; formatex(szQuery, charsmax(szQuery), sql_updatemapflags, iFlags, szMapname);
api_SQLAddThreadedQuery(szQuery, "Handle_UpdateMapFlagsTWO", QUERY_NOT_DISPOSABLE, PRIORITY_HIGHEST);
}
public Handle_UpdateMapFlagsTWO(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent) {
if(SQLCheckThreadedError(iFailState, hQuery, sError, iError)) {
DebugPrintLevel(0, "Failed to update map flags: %s", sError);
}
}
public SQLNative_ReloadCourses() {
// set a task of 5 seconds to reload the courses
// native set_task(Float:time, const function[], id = 0, const any:parameter[] = "", len = 0, const flags[] = "", repeat = 0);
if (g_bReloaded == true) { return; }
g_bReloaded = true;
client_print(0, print_chat, "* Reloading the map in 30 seconds due to the import of the legacy courses");
set_task(15.0, "reload_map", 1407, "");
}
public reload_map() {
client_print(0, print_chat, "* Reloading the map in 15 seconds due to the import of the legacy courses");
set_task(15.0, "reload_map2", 1408, "");
}
public reload_map2()
{
new szMap[64]; get_mapname(szMap, charsmax(szMap));
server_cmd("changelevel %s\n", szMap)
}
// logic; sql_loadcourses -> api_registercourse
// |-done-> load all cps for map (done in Handle_QueryLoadCourses) and check there if a legacy course was if not call the api function to load the cps from file
public plugin_precache() {
precache_sound(RECORD_SOUND);
sql_loadcourses();
sql_loadruns();
sql_loadgroupedruns();
}
//loads all courses from the database
public sql_loadcourses() {
new szMapname[64]; get_mapname(szMapname, charsmax(szMapname));
new szQuery[512]; formatex(szQuery, charsmax(szQuery), sql_selectcourses, szMapname);
api_SQLAddThreadedQuery(szQuery, "Handle_QueryLoadCourses", QUERY_NOT_DISPOSABLE, PRIORITY_HIGHEST);
new szQuery2[512]; formatex(szQuery2, charsmax(szQuery2), sql_retrievemapflags, szMapname);
api_SQLAddThreadedQuery(szQuery2, "Handle_QueryLoadMapFlags", QUERY_NOT_DISPOSABLE, PRIORITY_HIGHEST);
}
public Handle_QueryLoadMapFlags(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent) {
if(SQLCheckThreadedError(iFailState, hQuery, sError, iError)) {
DebugPrintLevel(0, "Failed to load map flags: %s", sError);
}
new iFlags = 0;
if (SQL_NumResults(hQuery) > 0) {
if (SQL_FieldNameToNum(hQuery,"flags") >= 0) { iFlags = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"flags")); }
} else {
DebugPrintLevel(0, "No map flags found for current map");
}
api_process_mapflags(iFlags);
}
public Handle_QueryLoadCourses(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent) {
if(SQLCheckThreadedError(iFailState, hQuery, sError, iError)) {
DebugPrintLevel(0, "Failed to insert course into database: %s", sError);
}
new Buffer[eCourseData_t];
if (SQL_NumResults(hQuery) == 0) {
//no courses... call the api function to load the cps from file
api_legacycheck();
return;
}
while (SQL_MoreResults(hQuery)) {
if (SQL_FieldNameToNum(hQuery,"id") >= 0) { Buffer[mC_sqlCourseID] = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"id")); }
if (SQL_FieldNameToNum(hQuery,"creator_id") >= 0) { Buffer[mC_iCreatorID] = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"creator_id")); }
if (SQL_FieldNameToNum(hQuery,"name") >= 0) { SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"name"), Buffer[mC_szCourseName], charsmax(Buffer[mC_szCourseName])); }
if (SQL_FieldNameToNum(hQuery,"description") >= 0) { SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"description"), Buffer[mC_szCourseDescription], charsmax(Buffer[mC_szCourseDescription])); }
if (SQL_FieldNameToNum(hQuery,"legacy") >= 0) { Buffer[mC_bLegacy] = bool:SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"legacy")); }
if (Buffer[mC_bLegacy]) { g_bLegacyFound = true; }
if (SQL_FieldNameToNum(hQuery,"difficulty") >= 0) { Buffer[mC_iDifficulty] = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"difficulty")); }
if (SQL_FieldNameToNum(hQuery,"active") >= 0) { Buffer[mC_bSQLActive] = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"active")); }
if (SQL_FieldNameToNum(hQuery,"flags") >= 0) { Buffer[mC_iFlags] = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"flags")); }
if (SQL_FieldNameToNum(hQuery,"created_at") >= 0) { SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"created_at"), Buffer[mC_szCreated_at], charsmax(Buffer[mC_szCreated_at])); }
if (SQL_FieldNameToNum(hQuery,"creator_name") >= 0) { SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"creator_name"), Buffer[mC_szCreatorName], charsmax(Buffer[mC_szCreatorName])); }
DebugPrintLevel(0, "Loaded course: CourseID #%d Name:%s Description:%s Legacy:%d Difficulty:%d Active:%d Flags:%d Created_at:%s CreatorID:%d CreatorName:%s",
Buffer[mC_sqlCourseID], Buffer[mC_szCourseName], Buffer[mC_szCourseDescription],
Buffer[mC_bLegacy], Buffer[mC_iDifficulty], Buffer[mC_bSQLActive], Buffer[mC_szGoalTeams],
Buffer[mC_szCreated_at], Buffer[mC_iCreatorID], Buffer[mC_szCreatorName]
);
api_registercourse(Buffer);
SQL_NextRow(hQuery);
}
new szMapname[64]; get_mapname(szMapname, charsmax(szMapname));
new szQuery[512]; formatex(szQuery, charsmax(szQuery), sql_loadcps, szMapname);
api_SQLAddThreadedQuery(szQuery, "Handle_QueryLoadCheckpoints", QUERY_NOT_DISPOSABLE, PRIORITY_HIGHEST);
}
public Handle_QueryLoadCheckpoints(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent) {
if(SQLCheckThreadedError(iFailState, hQuery, sError, iError)) {
DebugPrintLevel(0, "Failed to insert course into database: %s", sError);
}
new Buffer[eCheckPoints_t];
while (SQL_MoreResults(hQuery)) {
if (SQL_FieldNameToNum(hQuery,"course_id") >= 0) { Buffer[mCP_sqlCourseID] = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"course_id")); }
if (SQL_FieldNameToNum(hQuery,"x") >= 0) { SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"x"),Buffer[mCP_fOrigin][0]); }
if (SQL_FieldNameToNum(hQuery,"y") >= 0) { SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"y"),Buffer[mCP_fOrigin][1]); }
if (SQL_FieldNameToNum(hQuery,"z") >= 0) { SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"z"),Buffer[mCP_fOrigin][2]); }
if (SQL_FieldNameToNum(hQuery,"checkpoint_type") >= 0) { Buffer[mCP_iType] = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"checkpoint_type")); }
api_registercheckpoint(Buffer);
SQL_NextRow(hQuery);
}
api_spawnallcourses(); // only called ONCE because it iterated through all cps already for the
if (!g_bLegacyFound) {
DebugPrintLevel(0, "No legacy course found in the database, loading from file...");
api_legacycheck();
}
}
public player_connect(id)
{
g_bInRequest[id] = false;
g_iRequestFor[id] = -1;
g_iRequest[id] = 0;
}
public player_disconnected(id)
{
g_bInRequest[id] = false;
g_iRequestFor[id] = -1;
g_iRequest[id] = 0;
}
public cmd_oldstatsme(id) {
define_inrequest
sql_requestoldstats(id, id);
}
public menu_deploy(id) {
new iCurrentRequest = g_iRequest[id];
new szMenu[128]; formatex(szMenu, charsmax(szMenu), "\\y%s\\d\n\nChoose a player:", g_szRequest[iCurrentRequest]);
new menu = menu_create( szMenu, "skillsmenu_clicked" );
new szName[ 32 ], szTempid[ 10 ];
new szMenuStr[32];
for (new i = 1; i <= get_maxplayers(); i++)
{
if (is_connected_user(i)) {
get_user_name( i, szName, 31 );
num_to_str( i, szTempid, 9 );
//formatex(szMenuStr, charsmax(szMenuStr), "\\y%s\\d.\\w %s\n", szTempid, szName);
formatex(szMenuStr, charsmax(szMenuStr), "\\w %s\n", szName);
menu_additem( menu, szMenuStr, szTempid, 0 );
}
}
menu_display( id, menu ); //time out after 60 seconds
return PLUGIN_HANDLED;
}
public skillsmenu_clicked( const id, const menu, const item )
{
if( item == MENU_EXIT )
{
menu_destroy( menu );
return PLUGIN_HANDLED;
}
new data[ 6 ], iName[ 64 ];
new access, callback;
menu_item_getinfo( menu, item, access, data,5, iName, 63, callback );
new tempid = str_to_num( data );
if( !is_user_bot( tempid ) ) {
new szName[32]; get_user_name( tempid, szName, 31 );
//case according to the actions in this plugin
switch(g_iRequest[id]) {
case 0: //old stats
{
sql_requestoldstats(tempid,id);
}
}
}
menu_destroy( menu );
return PLUGIN_HANDLED;
}
public sql_requestoldstats(id, id2) {
new szRunsTable[128]; get_pcvar_string(g_pCvarOldRuns, szRunsTable, charsmax(szRunsTable));
new szRanksTable[128]; get_pcvar_string(g_pCvarOldRanks, szRanksTable, charsmax(szRanksTable));
new szSteamID[32]; get_user_authid(id, szSteamID, charsmax(szSteamID));
new szQuery[1024]; formatex(szQuery, charsmax(szQuery), sql_oldStatsme,szRanksTable,szRanksTable,szRunsTable,szRunsTable,szRunsTable,szRanksTable, szSteamID);
//write_file("debug.txt", szQuery);
g_bInRequest[id] = true;
new sData[2]; sData[0] = id; sData[1] = id2;
api_SQLAddThreadedQuery(szQuery, "Handle_QueryOldStatsme", QUERY_NOT_DISPOSABLE, PRIORITY_NORMAL, sData, 2);
client_print(id, print_console, g_szPleaseWait);
}
public Handle_QueryOldStatsme(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent) {
new id; if (iLen > 0) { id = Data[0]; } // Get the player id (whose stats we are requesting)
new id2; if (iLen > 1) { id2 = Data[1]; } // Get the player id (for the display_motd)
if (!is_connected_user(id2)) { g_bInRequest[id] = false; return PLUGIN_HANDLED; } // If the player is not connected anymore, return
define_sql_error // Check for SQL errors
if (SQL_NumResults(hQuery) == 0) { client_print(id, print_console, g_szNoData); g_bInRequest[id] = false; return PLUGIN_HANDLED; } // If no data is found, return
send_motd(id2, "<<<================= [ LEGACY STATS ] =================>>>\n\n");
new szSteamID[32];
while(SQL_MoreResults(hQuery))
{
//dump for debugging purposes
dump_sqldata(hQuery);
new iFieldNumSteamID = SQL_FieldNameToNum(hQuery,"steamId");
new iFieldNumPrimaryRank = SQL_FieldNameToNum(hQuery,"primaryRank");
new iFieldNumNFinnished = SQL_FieldNameToNum(hQuery,"nFinnished");
new iFieldNumPosition = SQL_FieldNameToNum(hQuery,"position");
new iFieldNumPosMax = SQL_FieldNameToNum(hQuery,"posmax");
new iFieldNumMapFinishes = SQL_FieldNameToNum(hQuery,"mapFinishes");
if (iFieldNumSteamID >= 0 && iFieldNumPrimaryRank >= 0 && iFieldNumNFinnished >= 0 && iFieldNumPosition >= 0 && iFieldNumPosMax >= 0 && iFieldNumMapFinishes >= 0) {
SQL_ReadResult(hQuery, iFieldNumSteamID, szSteamID, charsmax(szSteamID));
new szPrimaryRank[32]; SQL_ReadResult(hQuery, iFieldNumPrimaryRank, szPrimaryRank, charsmax(szPrimaryRank));
new szNFinnished[32]; SQL_ReadResult(hQuery, iFieldNumNFinnished, szNFinnished, charsmax(szNFinnished));
new szPosition[32]; SQL_ReadResult(hQuery, iFieldNumPosition, szPosition, charsmax(szPosition));
new szPosMax[32]; SQL_ReadResult(hQuery, iFieldNumPosMax, szPosMax, charsmax(szPosMax));
new szMapFinishes[32]; SQL_ReadResult(hQuery, iFieldNumMapFinishes, szMapFinishes, charsmax(szMapFinishes));
send_motd(id2, "Their average difficulty of finished maps = %s\n",szPrimaryRank);
send_motd(id2, "They were on place [%s] of [%s] ubers.\nHarder maps = more uber :)\n",szPosition,szPosMax);
send_motd(id2, "They finished [%s] maps, the most finished map is [%s] times finished.\n",szNFinnished,szMapFinishes);
}
SQL_NextRow(hQuery);
}
send_motd(id2, "\n\nStats up to july 2023\nsee all legacy stats at http://stats.skillzworld.eu")
display_motd(id2,"Legcy stats for %s",szSteamID);
g_bInRequest[id] = false;
return PLUGIN_HANDLED;
}
public cmd_oldtop5(id) {
define_inrequest
new szRunsTable[64]; get_pcvar_string(g_pCvarOldRanks, szRunsTable, charsmax(szRunsTable));
new szQuery[256]; formatex(szQuery, charsmax(szQuery), "SELECT nickNames, primaryRank,nFinnished FROM %s WHERE nFinnished>=100 ORDER BY primaryRank DESC LIMIT 10", szRunsTable);
g_bInRequest[id] = true;
new sData[2]; sData[0] = id; sData[1] = 1;
api_SQLAddThreadedQuery(szQuery, "Handle_QueryOldTop5", QUERY_NOT_DISPOSABLE, PRIORITY_NORMAL, sData, 2);
client_print(id, print_console, g_szPleaseWait);
}
public sql_getoldtop_highrank(id) {
new szRunsTable[64]; get_pcvar_string(g_pCvarOldRanks, szRunsTable, charsmax(szRunsTable));
new szQuery[256]; formatex(szQuery, charsmax(szQuery), "SELECT nickNames, primaryRank FROM %s ORDER BY `primaryRank` DESC LIMIT 10", szRunsTable);
new sData[2]; sData[0] = id; sData[1] = 2;
api_SQLAddThreadedQuery(szQuery, "Handle_QueryOldTop5", QUERY_NOT_DISPOSABLE, PRIORITY_NORMAL, sData, 2);
}
public sql_getoldtop_mapcount(id) {
new szRunsTable[64]; get_pcvar_string(g_pCvarOldRanks, szRunsTable, charsmax(szRunsTable));
new szQuery[256]; formatex(szQuery, charsmax(szQuery), "SELECT * FROM %s ORDER BY `nFinnished` DESC LIMIT 10", szRunsTable);
new sData[2]; sData[0] = id; sData[1] = 3;
api_SQLAddThreadedQuery(szQuery, "Handle_QueryOldTop5", QUERY_NOT_DISPOSABLE, PRIORITY_NORMAL, sData, 2);
}
public Handle_QueryOldTop5(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent)
{
new id; if (iLen > 0) { id = Data[0]; } // Get the player id
new iState; if (iLen > 1) { iState = Data[1]; } // Get the state (1 = UBER, 2 = Highrank)
define_sql_error
switch (iState) {
case 3: {
send_motd(id, "\n<<<================= [ MOST FINISHED MAPS ] =================>>>\n");
if (SQL_NumResults(hQuery) == 0) { send_motd(id, "No map data available.\n"); }
new iCount = 1;
while(SQL_MoreResults(hQuery))
{
new szNickNames[256]; new nFinnished;
SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"nickNames"), szNickNames, charsmax(szNickNames));
nFinnished = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"nFinnished"));
send_motd(id, "%i. %s = %d maps\n", iCount, szNickNames, nFinnished);
iCount++;
}
display_motd(id, "Old statistics up to july 2023")
g_bInRequest[id] = false;
} case 2: {
send_motd(id, "\n<<<================= [ HIGHRANKS ] =================>>>\n");
if (SQL_NumResults(hQuery) == 0) { send_motd(id, "No Highranks data available.\n"); }
new iCount = 1;
while(SQL_MoreResults(hQuery)) {
new szNickNames[256]; new iPrimaryRank;
SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"nickNames"), szNickNames, charsmax(szNickNames));
iPrimaryRank = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"primaryRank"));
send_motd(id, "%i. %s = %d avg. points\n", iCount, szNickNames, iPrimaryRank);
iCount++;
SQL_NextRow(hQuery);
}
sql_getoldtop_mapcount(id);
} case 1: {
send_motd(id, "<<<================= [ UBERS ] =================>>>\n");
if (SQL_NumResults(hQuery) == 0) { send_motd(id, "No UBER data available.\n"); }
new iCount = 1;
while(SQL_MoreResults(hQuery))
{
new szNickNames[256]; new iPrimaryRank; new iFinnished;
SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"nickNames"), szNickNames, charsmax(szNickNames));
iPrimaryRank = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"primaryRank"));
iFinnished = SQL_ReadResult(hQuery, SQL_FieldNameToNum(hQuery,"nFinnished"));
send_motd(id, "%i. %s = %d avg. points (%d maps finished)\n", iCount, szNickNames, iPrimaryRank,iFinnished);
iCount++;
SQL_NextRow(hQuery);
}
sql_getoldtop_highrank(id);
}}
return PLUGIN_HANDLED
}
stock dump_sqldata(Handle:hQuery) {
new iColumns = SQL_NumColumns(hQuery);
DebugPrintLevel(0, "---------------------------------\nSQL_NumResults: %d", SQL_NumResults(hQuery));
if (iColumns == 0) { DebugPrintLevel(0, "No columns found."); return; }
new szColumns[256];
for(new i = 0; i < iColumns; i++)
{
SQL_FieldNumToName(hQuery, i, szColumns, charsmax(szColumns));
new szValue[256];
SQL_ReadResult(hQuery, i, szValue, charsmax(szValue));
DebugPrintLevel(0, "Column %d (%s) = %s", i, szColumns, szValue);
}
DebugPrintLevel(0, "---------------------------------");
}
public SQLNative_InsertCourse(Data[]) {
new Buffer[eCourseData_t];
get_array(1, Buffer, sizeof(Buffer));
new szCourseName[MAX_COURSE_NAME]; new szCourseDescription[MAX_COURSE_DESCRIPTION]; new iNumCheckpoints; new iDifficulty; new bool:bLegacy; new iCreatorID;
if (strlen(Buffer[mC_szCourseName]) == 0) { formatex(szCourseName, charsmax(szCourseName), "Unknown"); } else { formatex(szCourseName, charsmax(szCourseName), Buffer[mC_szCourseName]); }
if (strlen(Buffer[mC_szCourseDescription]) == 0) { formatex(szCourseDescription, charsmax(szCourseDescription), "No description provided."); } else { formatex(szCourseDescription, charsmax(szCourseDescription), Buffer[mC_szCourseDescription]); }
iDifficulty = Buffer[mC_iDifficulty]; if (iDifficulty < 0) { iDifficulty = 0; } else if (iDifficulty > 100) { iDifficulty = 100; }
iCreatorID = Buffer[mC_iCreatorID]; if (iCreatorID < -1) { iCreatorID = -1; }
bLegacy = Buffer[mC_bLegacy];
new iFlags = 0;
if (containi(Buffer[mC_szGoalTeams],"B") >= 0) { iFlags |= SRFLAG_TEAMBLUE; }
if (containi(Buffer[mC_szGoalTeams],"R") >= 0) { iFlags |= SRFLAG_TEAMRED; }
if (containi(Buffer[mC_szGoalTeams],"G") >= 0) { iFlags |= SRFLAG_TEAMGREEN; }
if (containi(Buffer[mC_szGoalTeams],"Y") >= 0) { iFlags |= SRFLAG_TEAMYELLOW; }
new szMapname[64]; get_mapname(szMapname, charsmax(szMapname));
new szPreQuery[1024]; formatex(szPreQuery, charsmax(szPreQuery), sql_insertmap, szMapname); // insert map if it doesn't exist
new szQuery[1024]; formatex(szQuery, charsmax(szQuery), sql_insertcourse, szMapname, iCreatorID, szCourseName, szCourseDescription, bLegacy, iDifficulty, 1, iFlags);
new szFinalQuery[1024]; formatex(szFinalQuery, charsmax(szFinalQuery), "%s %s", szPreQuery, szQuery);
write_file("debug.txt", szFinalQuery);
api_SQLAddThreadedQuery(szFinalQuery, "Handle_QueryInsertCourse", QUERY_NOT_DISPOSABLE, PRIORITY_NORMAL);
} //http://database.gruk.io:9000/index.php
public Handle_QueryInsertCourse(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent)
{
if(SQLCheckThreadedError(iFailState, hQuery, sError, iError)) {
DebugPrintLevel(0, "Failed to insert course into database: %s", sError);
}
return PLUGIN_HANDLED;
}
public SQLNative_InsertLegacyCPs( Data[] ) {
new Buffer[eCheckPoints_t];
get_array(1, Buffer, sizeof(Buffer));
//prepare the query
//new const sql_insertlegacycps[] = "INSERT INTO checkpoints(course_id, x, y, z, checkpoint_type) VALUES ( (SELECT c.id FROM courses c JOIN maps m ON m.id = c.map_id WHERE m.name = '%s' AND c.legacy = TRUE), %f, %f, %f, %f);"
//get mapname
new szMapname[64]; get_mapname(szMapname, charsmax(szMapname));
new Float:x, Float:y, Float:z; x = Buffer[mCP_fOrigin][0]; y = Buffer[mCP_fOrigin][1]; z = Buffer[mCP_fOrigin][2];
new szQuery[1024]; formatex(szQuery, charsmax(szQuery), sql_insertlegacycps, szMapname, x, y, z, Buffer[mCP_iType]);
api_SQLAddThreadedQuery(szQuery, "Handle_QueryInsertLegacyCPs", QUERY_NOT_DISPOSABLE, PRIORITY_NORMAL);
}
public Handle_QueryInsertLegacyCPs(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent) {
if(SQLCheckThreadedError(iFailState, hQuery, sError, iError)) {
DebugPrintLevel(0, "Failed to insert course into database: %s", sError);
}
return PLUGIN_HANDLED;
}
public SQLNative_InsertRun(iPluign, iParams) {
new id = get_param(1); new Float:fTime = get_param_f(2); new course = get_param(3); new iCpsUsed = get_param(4); new bAltmodxUsed = get_param(5);
if (course <= 0) { DebugPrintLevel(0,"SQLNative_InsertRun <= 0 exception (course was %d)", course); return; }
new szSteamID[32]; get_user_authid(id, szSteamID, charsmax(szSteamID));
new iClass = pev(id,pev_playerclass);
//requires %d/courseid %f/runtime %d/class %d/cps used %d/altmodx used %s/steamid
//new const sql_insertrunquery[] = "INSERT INTO runs (course_id, player_id, time, player_class) SELECT %d, players.id, %f, %d FROM players WHERE players.steamid = %s;"
new szQuery[1024]; formatex(szQuery, charsmax(szQuery), sql_insertrunquery, course, fTime, iClass, iCpsUsed, bAltmodxUsed, szSteamID);
api_SQLAddThreadedQuery(szQuery, "Handle_QueryInsertRun", QUERY_NOT_DISPOSABLE, PRIORITY_NORMAL);
// check if run is the best run for this map
new bool:bIsBestRun = false;
if ((ArraySize(g_TopList) == 0) && (fTime > 0.0)) { bIsBestRun = true; }
else if (fTime > 0.0) {
new Buffer[eSpeedTop_t]; ArrayGetArray(g_TopList,0,Buffer);
new Float:fBestTime = Buffer[m_fTime];
if (fTime < fBestTime) { bIsBestRun = true; }
}
if (bIsBestRun) { // announce to server
new szPlayerName[32]; get_user_name(id, szPlayerName, charsmax(szPlayerName));
new sTime[32]; formatex(sTime, charsmax(sTime), "%02d:%02d.%02d", floatround(fTime /60.0, floatround_floor), floatround(fTime, floatround_floor) % 60, floatround(fTime*100.0, floatround_floor) % 100);
new szMessage[128]; formatex(szMessage, charsmax(szMessage), "New record by %s with a time of %s!", szPlayerName, sTime);
client_print(0, print_chat, szMessage);
client_cmd(0,"play %s",RECORD_SOUND);
api_firework(id, 3);
}
}
public Handle_QueryInsertRun(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent) {
if(SQLCheckThreadedError(iFailState, hQuery, sError, iError)) {
DebugPrintLevel(0, "Failed to insert run into database: %s", sError);
}
//reload the top list
ArrayDestroy(g_TopList);
g_TopList = ArrayCreate(eSpeedTop_t);
g_iTopCount = 0;
sql_loadruns();
ArrayDestroy(g_GroupedTopList);
g_GroupedTopList = ArrayCreate(eSpeedTop_t);
g_iGroupedTopCount = 0;
sql_loadgroupedruns();
return PLUGIN_HANDLED;
}
public sql_loadruns() {
new szMapName[64]; get_mapname(szMapName, charsmax(szMapName));
new szQuery[1024]; formatex(szQuery, charsmax(szQuery), sql_getrunsformap, szMapName);
api_SQLAddThreadedQuery(szQuery, "Handle_QueryLoadRuns", QUERY_NOT_DISPOSABLE, PRIORITY_NORMAL);
}
public Handle_QueryLoadRuns(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent) {
if(SQLCheckThreadedError(iFailState, hQuery, sError, iError)) {
DebugPrintLevel(0, "Failed to load runs: %s", sError);
}
new Buffer[eSpeedTop_t];
g_iTopCount = 0; // plugin is now ready to process requests
/*eSpeedTop_t*/
while(SQL_MoreResults(hQuery)) {
new iFieldNumSteamID = SQL_FieldNameToNum(hQuery,"steamid");
new iFieldNumTime = SQL_FieldNameToNum(hQuery,"time");
new iFieldNumClass = SQL_FieldNameToNum(hQuery,"player_class");
new iFieldNumNickname = SQL_FieldNameToNum(hQuery,"most_used_nickname");
new iFieldNumCourseID = SQL_FieldNameToNum(hQuery,"course_id");
new iFieldNumID = SQL_FieldNameToNum(hQuery,"id");
new iFieldNumCreatedAt = SQL_FieldNameToNum(hQuery,"created_at");
new iFieldNumPlayerID = SQL_FieldNameToNum(hQuery,"player_id");
new iFieldNumAmxmUsed = SQL_FieldNameToNum(hQuery,"altmodx_used"); //m_bAmxmodx_used
if (iFieldNumSteamID == -1 || iFieldNumTime == -1 || iFieldNumClass == -1 || iFieldNumNickname == -1 || iFieldNumCourseID == -1 || iFieldNumID == -1 || iFieldNumCreatedAt == -1 || iFieldNumPlayerID == -1) {
DebugPrintLevel(0, "Failed to load runs: %s", "Missing field in query");
return PLUGIN_HANDLED;
}
//now load the data into the struct
Buffer[m_iTopPlayerIdent] = SQL_ReadResult(hQuery, iFieldNumPlayerID);
SQL_ReadResult(hQuery, iFieldNumSteamID, Buffer[m_sTopPlayerAuthid], charsmax(Buffer[m_sTopPlayerAuthid]));
SQL_ReadResult(hQuery, iFieldNumNickname, Buffer[m_sTopPlayerName], charsmax(Buffer[m_sTopPlayerName]));
SQL_ReadResult(hQuery, iFieldNumTime,Buffer[m_fTime]);
Buffer[m_iCourseID] = SQL_ReadResult(hQuery, iFieldNumCourseID);
SQL_ReadResult(hQuery, iFieldNumCreatedAt, Buffer[m_CreatedAt], charsmax(Buffer[m_CreatedAt]));
Buffer[m_iPlayerClass] = SQL_ReadResult(hQuery, iFieldNumClass);
Buffer[m_bAmxmodx_used] = SQL_ReadResult(hQuery, iFieldNumAmxmUsed);
//now add the run to the list
ArrayPushArray(g_TopList, Buffer);
g_iTopCount++;
SQL_NextRow(hQuery);
}
return PLUGIN_HANDLED;
}
public sql_loadgroupedruns() {
new szMapName[64]; get_mapname(szMapName, charsmax(szMapName));
new szQuery[1024]; formatex(szQuery, charsmax(szQuery), sql_getgroupedrunsformap, szMapName);
api_SQLAddThreadedQuery(szQuery, "Handle_QueryLoadGroupedRuns", QUERY_NOT_DISPOSABLE, PRIORITY_NORMAL);
}
public Handle_QueryLoadGroupedRuns(iFailState, Handle:hQuery, sError[], iError, Data[], iLen, Float:fQueueTime, iQueryIdent) {
if(SQLCheckThreadedError(iFailState, hQuery, sError, iError)) {
DebugPrintLevel(0, "Failed to load runs: %s", sError);
}
new Buffer[eSpeedTop_t];
g_iGroupedTopCount = 0; // plugin is now ready to process requests
/*eSpeedTop_t*/
while(SQL_MoreResults(hQuery)) {
new iFieldNumSteamID = SQL_FieldNameToNum(hQuery,"steamid");
new iFieldNumTime = SQL_FieldNameToNum(hQuery,"time");
new iFieldNumClass = SQL_FieldNameToNum(hQuery,"player_class");
new iFieldNumNickname = SQL_FieldNameToNum(hQuery,"most_used_nickname");
new iFieldNumCourseID = SQL_FieldNameToNum(hQuery,"course_id");
new iFieldNumID = SQL_FieldNameToNum(hQuery,"id");
new iFieldNumCreatedAt = SQL_FieldNameToNum(hQuery,"created_at");
new iFieldNumPlayerID = SQL_FieldNameToNum(hQuery,"player_id");
new iFieldNumAmxmUsed = SQL_FieldNameToNum(hQuery,"altmodx_used"); //m_bAmxmodx_used
if (iFieldNumSteamID == -1 || iFieldNumTime == -1 || iFieldNumClass == -1 || iFieldNumNickname == -1 || iFieldNumCourseID == -1 || iFieldNumID == -1 || iFieldNumCreatedAt == -1 || iFieldNumPlayerID == -1) {
DebugPrintLevel(0, "Failed to load runs: %s", "Missing field in query");
return PLUGIN_HANDLED;
}
//now load the data into the struct
Buffer[m_iTopPlayerIdent] = SQL_ReadResult(hQuery, iFieldNumPlayerID);
SQL_ReadResult(hQuery, iFieldNumSteamID, Buffer[m_sTopPlayerAuthid], charsmax(Buffer[m_sTopPlayerAuthid]));
SQL_ReadResult(hQuery, iFieldNumNickname, Buffer[m_sTopPlayerName], charsmax(Buffer[m_sTopPlayerName]));
SQL_ReadResult(hQuery, iFieldNumTime,Buffer[m_fTime]);
Buffer[m_iCourseID] = SQL_ReadResult(hQuery, iFieldNumCourseID);
SQL_ReadResult(hQuery, iFieldNumCreatedAt, Buffer[m_CreatedAt], charsmax(Buffer[m_CreatedAt]));
Buffer[m_iPlayerClass] = SQL_ReadResult(hQuery, iFieldNumClass);
Buffer[m_bAmxmodx_used] = SQL_ReadResult(hQuery, iFieldNumAmxmUsed);
//now add the run to the list
ArrayPushArray(g_GroupedTopList, Buffer);
g_iGroupedTopCount++;
SQL_NextRow(hQuery);
}
return PLUGIN_HANDLED;
}
//modified from fm / benwatch
public ShowTop(id, iStart, iEnd, bool:bOnlyPBs) {
new top_count, Array:top_list
if (bOnlyPBs) {
top_list = g_GroupedTopList
top_count = g_iGroupedTopCount
} else {
top_list = g_TopList
top_count = g_iTopCount
}
if (!top_count) {
client_print(id, print_chat, "* No players have completed a speedrun on the current map")
return
}
/// Ensure that iStart is in range [0, g_iTopCount), iEnd is in [iStart+1, top_count]
/// iEnd is exclusive, so iEnd-iStart is the amount of (potential) items and is in range [1, top_count]
/// This mirrors the way Python slicing works, except that some runs could be filtered out.
iStart = clamp(iStart, 0, top_count - 1)
iEnd = clamp(iEnd, iStart+1, top_count)
new iRequestedRuns = iEnd - iStart
//console_print id, "DEBUG: ShowTop bounds: %d - %d", iStart, iEnd
static sBuffer[1024]
new sCurrentMap[MAX_MAP_LEN]; get_mapname(sCurrentMap, charsmax(sCurrentMap))
if (bOnlyPBs) {
send_motd(id,"Showing the fastest players on %s\n", sCurrentMap)
} else {
send_motd(id,"Showing the fastest runs on %s\n", sCurrentMap)
}
new iClass = pev(id, pev_playerclass);
send_motd(id, "\nCurrently only showing the runs for your player class: %s\n\n", g_szClassNames[iClass]);
new iCourseInternal = api_get_player_course(id)
new iCourseSQL = api_get_course_mysqlid(iCourseInternal)
new szCourseName[MAX_COURSE_NAME]; api_get_coursename(iCourseInternal, szCourseName, charsmax(szCourseName));
send_motd(id, "[ %s ]", szCourseName);
// only show runs for the current course by comparing the course ids (the mysql ids!)
#define RUN_IS_SHOWABLE (iCourseSQL == TopInfo[m_iCourseID] && iClass == TopInfo[m_iPlayerClass])
// Bug: Viewing the leaderboard as spectator doesn't work because of the class filter
// console_print id, "DEBUG: Course's internal ID: %d, player class: %d", iCourseInternal, iClass
new TopInfo[eSpeedTop_t], run_i
new iSkipped
while (iSkipped < iStart && run_i < top_count) { // Skip leading runs for pagination
// console_print id, "DEBUG: Skipping potential run"
ArrayGetArray(top_list, run_i, TopInfo)
if (RUN_IS_SHOWABLE) {console_print id, "Skipped a run"; iSkipped++;}
run_i++
}
new sTime[0x20], iAcquired
while (iRequestedRuns > iAcquired < MAX_MOTD_RANKS && run_i < top_count) {
// console_print id, "DEBUG: Before: %d > %d < %d && %d < %d", iRequestedRuns, iAcquired, MAX_MOTD_RANKS, run_i, top_count
ArrayGetArray(top_list, run_i, TopInfo)
run_i++
// console_print id, "DEBUG: Course's SQLid: %d, checked run's course id: %d, checked run's class: %d", iCourseSQL, TopInfo[m_iCourseID], TopInfo[m_iPlayerClass]
if (!RUN_IS_SHOWABLE) continue
// console_print id, "DEBUG: Passed!"
new Float:fTime = TopInfo[m_fTime];
new iTotalSeconds = floatround(fTime, floatround_floor), iCentis = floatround(fTime*100.0, floatround_floor) % 100
new iHours = iTotalSeconds / (60*60), iMinutes = iTotalSeconds / 60 % 60, iSeconds = iTotalSeconds % 60
if (iHours) formatex(sTime, charsmax(sTime), "%02d:%02d:%02d.%02d", iHours, iMinutes, iSeconds, iCentis)
else formatex(sTime, charsmax(sTime), "%02d:%02d.%02d", iMinutes, iSeconds, iCentis)
send_motd(id, "\n%3d. [%s] %s <%s> \ton %s", 1 + iStart + iAcquired, sTime, TopInfo[m_sTopPlayerName], TopInfo[m_sTopPlayerAuthid], TopInfo[m_CreatedAt]);
iAcquired++
// console_print id, "DEBUG: After: %d > %d < %d && %d < %d", iRequestedRuns, iAcquired, MAX_MOTD_RANKS, run_i, top_count
}
new iTrailingRuns
while (run_i < top_count && iTrailingRuns < MAX_MOTD_RANKS) { // Scan for trailing runs for pagination help
ArrayGetArray(top_list, run_i++, TopInfo)
if (RUN_IS_SHOWABLE) iTrailingRuns++
}
if (iTrailingRuns) {
new iNextStart = iStart + iAcquired
new iNextEnd = iNextStart + iTrailingRuns
if (bOnlyPBs) send_motd id, "\n\nSay \"/top %d-%d\" to view the next %d.", iNextStart + 1, iNextEnd, iNextEnd - iNextStart
else send_motd id, "\n\nSay \"/runs %d-%d\" to view the next %d.", iNextStart + 1, iNextEnd, iNextEnd - iNextStart
}
if (bOnlyPBs) send_motd(id,"\n\nTo get a list of all runs, say \"/runs\"\n");
display_motd(id, "Speedrun Ranks")
return
}
public ShowMapstats(id) {
new szMapname[64]; get_mapname(szMapname, charsmax(szMapname));
send_motd(id, "<<<=========== [ Mapstats - %s ] ===========>>>\n\n", szMapname);
new iNumCourses = api_get_number_courses();
if (iNumCourses == 0) {
send_motd(id, "No courses found for this map.\n");
} else {
send_motd(id, "Found %d courses for this map:\n", iNumCourses);
//iterate through all courses
for (new i = 0; i < iNumCourses; i++) {
new szCourseName[64]; api_get_coursename(i+1, szCourseName, charsmax(szCourseName));
new szCourseDesc[128]; api_get_coursedescription(i+1, szCourseDesc, charsmax(szCourseDesc));
new iDiff = api_get_mapdifficulty(i+1);
send_motd(id, "%d. %s\n Description: %s\n Difficulty: %d\n", i, szCourseName, szCourseDesc, iDiff);
}
}
new szSteamID[32]; get_user_authid(id, szSteamID, charsmax(szSteamID));
//check if player has completed any courses by iterating through g_TopList
new iNumCompletedCourses = 0;
new Buffer[eSpeedTop_t];
for (new i = 0; i < g_iTopCount; i++) {
ArrayGetArray(g_TopList, i, Buffer);
//compare steamids
if (i == 0) { //record the top player
new szRunTime[64]; format_seconds(szRunTime, Buffer[m_fTime], charsmax(szRunTime));
formatex(szRunTime, charsmax(szRunTime), "%s, %d milliseconds", szRunTime, floatround(Buffer[m_fTime]*1000.0, floatround_floor) % 1000);
send_motd(id, "\nSpeedrun record set by %s <%s>\n >>> %s\n\n", Buffer[m_sTopPlayerName], Buffer[m_sTopPlayerAuthid], szRunTime);
}
if (equal(Buffer[m_sTopPlayerAuthid], szSteamID)) {
iNumCompletedCourses++;
if (iNumCompletedCourses == 1) {
new Float:fTime = Buffer[m_fTime];
new sTime[32]; formatex(sTime, charsmax(sTime), "%02d:%02d.%02d", floatround(fTime /60.0, floatround_floor), floatround(fTime, floatround_floor) % 60, floatround(fTime*100.0, floatround_floor) % 100);
send_motd(id, "\nYour best time on this map is: %s (%s) currently we only have legacy courses enabled.\n", sTime, Buffer[m_CreatedAt]);
}
}
}
if (iNumCompletedCourses == 0) {
send_motd(id, "No one has completed any courses on this map.\n");
} else {
send_motd(id, "You have %d runs on this map :)\n", iNumCompletedCourses);
}
display_motd(id, "Mapstats");
}
public Handle_Say(id) {
new sArgs[192]; read_args(sArgs, charsmax(sArgs))
remove_quotes(sArgs)
if (!sArgs[0]) return PLUGIN_HANDLED
new args_i, cmd_length
new bool:bWantsTop = false
new bool:bWantsGTop = false
switch (sArgs[0]) {case '!', '/', '\\': args_i++;}
/// Support these commands: /top 10, /top10, /top10-20
if (equali(sArgs[args_i], "top" , (cmd_length = 3))) bWantsTop = bWantsGTop = true
else if (equali(sArgs[args_i], "sr" , (cmd_length = 2))) bWantsTop = bWantsGTop = true
else if (equali(sArgs[args_i], "runs", (cmd_length = 4))) bWantsTop = true
if (bWantsTop) {
args_i += cmd_length
new iOffset
new iStart = strtol(sArgs[args_i], .endPos = iOffset, .base = 10), iEnd
if (!iStart) {iStart = 1; iEnd = MAX_MOTD_RANKS;} // top<nothing or nonsense>
else { // top10-20
for (; sArgs[iOffset]; iOffset++) { // Skip whitespace and the hyphen
if (sArgs[iOffset] == '-') {
iOffset++
break
}
}
iEnd = str_to_num(sArgs[iOffset])
}
// iStart is an index, iEnd is an exclusive endpoint
if (iEnd) ShowTop id, clamp(iStart - 1, 0) , clamp(iEnd, 1), bWantsGTop // Of the format /top 10-20
else ShowTop id, 0, clamp(iStart, 1), bWantsGTop // Of the format /top 10 (which means 1-10)
return PLUGIN_HANDLED
}
///
if (equali(sArgs[args_i], "mapstats", 8) || equali(sArgs[args_i], "mapinfo", 7)) {
ShowMapstats(id);
return PLUGIN_HANDLED
}
if (equali(sArgs[args_i], "diff", 4) || equali(sArgs[args_i], "difficulty", 10)) {
new iInCourse = api_get_player_course(id);
new iDiff = api_get_mapdifficulty(iInCourse);
new szCourseName[64]; api_get_coursename(iInCourse, szCourseName, charsmax(szCourseName));
new szString[128]; formatex(szString, charsmax(szString), "* The difficulty for the course [%s] is %d", szCourseName, iDiff);
client_print(id, print_chat, szString);
return PLUGIN_HANDLED
}
return PLUGIN_CONTINUE
}