Merge pull request #766 from shavitush/very_good_yes

2.5.0
This commit is contained in:
shavit 2019-03-28 18:24:19 +02:00 committed by GitHub
commit 95d9cad309
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1241 additions and 807 deletions

View File

@ -7,6 +7,10 @@
{
"0"
{
// Accessibility
"enabled" "1" // When disabled, style will be unusable. -1 to also make the style not show in menus.
"inaccessible" "0" // This setting makes it so you can't manually switch to this style but only by functionality from timer modules.
// Strings
"name" "Normal" // Style name.
"shortname" "NM" // Short style name.
@ -41,6 +45,7 @@
"block_pleft" "0" // Block +left. 2 to stop timer.
"block_pright" "0" // Block +right. 2 to stop timer.
"block_pstrafe" "0" // Prevent button inconsistencies (including +pstrafe). May have false positives when players lag. Will prevent some strafe hacks too. Set this to 2 to also stop the timer.
"kzcheckpoints" "0" // KZ styled checkpoints. They reset upon timer start and you don't get reverted to a save state, and you cannot save when airborne or someone else's checkpoints.
// Feature excluding
"unranked" "0" // Unranked style. No ranking points and no records.

View File

@ -23,7 +23,7 @@
#endif
#define _shavit_included
#define SHAVIT_VERSION "2.4.1"
#define SHAVIT_VERSION "2.5.0"
#define STYLE_LIMIT 256
#define MAX_ZONES 64
#define MAX_NAME_LENGTH_SQL 32
@ -157,6 +157,9 @@ enum struct stylesettings_t
float fRankingMultiplier;
int iSpecial;
int iOrdering;
bool bInaccessible;
int iEnabled;
bool bKZCheckpoints;
}
enum struct chatstrings_t
@ -495,6 +498,20 @@ forward void Shavit_OnDatabaseLoaded();
*/
forward void Shavit_OnChatConfigLoaded();
/**
* Called when a player teleports with checkpoints.
*
* @return Plugin_Continue to allow teleporting, anything else to prevent.
*/
forward Action Shavit_OnTeleport(int client);
/**
* Called when a player teleports with checkpoints.
*
* @return Plugin_Continue to allow teleporting, anything else to prevent.
*/
forward Action Shavit_OnSave(int client);
/**
* Called when a player enters a zone.
*
@ -701,7 +718,7 @@ native void Shavit_Rankings_DeleteMap(const char[] map);
*
* @param client Client index.
* @param style Style.
* @param force Ignore style permissions.
* @param force Ignore style permissions. This being true will bypass the `inaccessible` style setting as well.
* @param manual Is it a manual style change? (Was it caused by user interaction?)
* @param noforward Bypasses the call to `Shavit_OnStyleChanged`.
* @return False if failed due to lack of access, true otherwise.

View File

@ -382,7 +382,7 @@ public Action Hook_SayText2(UserMsg msg_id, any msg, const int[] players, int pl
char sName[MAXLENGTH_NAME];
char sCMessage[MAXLENGTH_CMESSAGE];
if((gCV_CustomChat.IntValue > 0 && (CheckCommandAccess(client, "shavit_chat", ADMFLAG_CHAT) || gCV_CustomChat.IntValue == 2)) && gI_ChatSelection[client] == -1)
if(HasCustomChat(client) && gI_ChatSelection[client] == -1)
{
if(gB_NameEnabled[client])
{
@ -614,11 +614,19 @@ public void OnClientCookiesCached(int client)
public void OnClientPutInServer(int client)
{
gB_NameEnabled[client] = false;
strcopy(gS_CustomName[client], 128, "");
gB_NameEnabled[client] = true;
strcopy(gS_CustomName[client], 128, "{team}{name}");
gB_MessageEnabled[client] = false;
strcopy(gS_CustomMessage[client], 128, "");
gB_MessageEnabled[client] = true;
strcopy(gS_CustomMessage[client], 128, "{default}");
}
public void OnClientDisconnect(int client)
{
if(HasCustomChat(client))
{
SaveToDatabase(client);
}
}
public void OnClientPostAdminCheck(int client)
@ -669,7 +677,7 @@ public Action Command_CCName(int client, int args)
return Plugin_Handled;
}
if(!(gCV_CustomChat.IntValue > 0 && (CheckCommandAccess(client, "shavit_chat", ADMFLAG_CHAT) || gCV_CustomChat.IntValue == 2)))
if(!HasCustomChat(client))
{
Shavit_PrintToChat(client, "%T", "NoCommandAccess", client);
@ -694,8 +702,6 @@ public Action Command_CCName(int client, int args)
gB_NameEnabled[client] = false;
SaveToDatabase(client);
return Plugin_Handled;
}
@ -704,8 +710,6 @@ public Action Command_CCName(int client, int args)
gB_NameEnabled[client] = true;
strcopy(gS_CustomName[client], 128, sArgs);
SaveToDatabase(client);
return Plugin_Handled;
}
@ -718,7 +722,7 @@ public Action Command_CCMessage(int client, int args)
return Plugin_Handled;
}
if(!(gCV_CustomChat.IntValue > 0 && (CheckCommandAccess(client, "shavit_chat", ADMFLAG_CHAT) || gCV_CustomChat.IntValue == 2)))
if(!HasCustomChat(client))
{
Shavit_PrintToChat(client, "%T", "NoCommandAccess", client);
@ -743,8 +747,6 @@ public Action Command_CCMessage(int client, int args)
gB_MessageEnabled[client] = false;
SaveToDatabase(client);
return Plugin_Handled;
}
@ -753,8 +755,6 @@ public Action Command_CCMessage(int client, int args)
gB_MessageEnabled[client] = true;
strcopy(gS_CustomMessage[client], 16, sArgs);
SaveToDatabase(client);
return Plugin_Handled;
}
@ -777,7 +777,7 @@ Action ShowChatRanksMenu(int client, int item)
FormatEx(sDisplay, MAXLENGTH_DISPLAY, "%T\n ", "AutoAssign", client);
menu.AddItem("-2", sDisplay, (gI_ChatSelection[client] == -2)? ITEMDRAW_DISABLED:ITEMDRAW_DEFAULT);
if(gCV_CustomChat.IntValue > 0 && (CheckCommandAccess(client, "shavit_chat", ADMFLAG_CHAT) || gCV_CustomChat.IntValue == 2))
if(HasCustomChat(client))
{
FormatEx(sDisplay, MAXLENGTH_DISPLAY, "%T\n ", "CustomChat", client);
menu.AddItem("-1", sDisplay, (gI_ChatSelection[client] == -1)? ITEMDRAW_DISABLED:ITEMDRAW_DEFAULT);
@ -1058,12 +1058,15 @@ void PreviewChat(int client, int rank)
EndMessage();
}
bool HasCustomChat(int client)
{
return (gCV_CustomChat.IntValue > 0 && (CheckCommandAccess(client, "shavit_chat", ADMFLAG_CHAT) || gCV_CustomChat.IntValue == 2));
}
bool HasRankAccess(int client, int rank)
{
bool bAllowCustom = gCV_CustomChat.IntValue > 0 && (CheckCommandAccess(client, "shavit_chat", ADMFLAG_CHAT) || gCV_CustomChat.IntValue == 2);
if(rank == -2 ||
(rank == -1 && bAllowCustom))
(rank == -1 && HasCustomChat(client)))
{
return true;
}
@ -1329,9 +1332,22 @@ void SQL_DBConnect()
bool bMySQL = StrEqual(sDriver, "mysql", false);
char sQuery[512];
FormatEx(sQuery, 512, "CREATE TABLE IF NOT EXISTS `%schat` (`auth` CHAR(32) NOT NULL, `name` INT NOT NULL DEFAULT 0, `ccname` CHAR(128) COLLATE 'utf8mb4_unicode_ci', `message` INT NOT NULL DEFAULT 0, `ccmessage` CHAR(16) COLLATE 'utf8mb4_unicode_ci', PRIMARY KEY (`auth`))%s;", gS_MySQLPrefix, (bMySQL)? " ENGINE=INNODB":"");
if(bMySQL)
{
FormatEx(sQuery, 512,
"CREATE TABLE IF NOT EXISTS `%schat` (`auth` VARCHAR(32) NOT NULL, `name` INT NOT NULL DEFAULT 0, `ccname` VARCHAR(128) COLLATE 'utf8mb4_unicode_ci', `message` INT NOT NULL DEFAULT 0, `ccmessage` VARCHAR(16) COLLATE 'utf8mb4_unicode_ci', PRIMARY KEY (`auth`), CONSTRAINT `ch_auth` FOREIGN KEY (`auth`) REFERENCES `users` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE) ENGINE=INNODB;",
gS_MySQLPrefix);
}
else
{
FormatEx(sQuery, 512,
"CREATE TABLE IF NOT EXISTS `%schat` (`auth` VARCHAR(32) NOT NULL, `name` INT NOT NULL DEFAULT 0, `ccname` VARCHAR(128), `message` INT NOT NULL DEFAULT 0, `ccmessage` VARCHAR(16), PRIMARY KEY (`auth`), CONSTRAINT `ch_auth` FOREIGN KEY (`auth`) REFERENCES `users` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE);",
gS_MySQLPrefix);
}
gH_SQL.Query(SQL_CreateTable_Callback, sQuery, 0, DBPrio_High);
gH_SQL.Query(SQL_CreateTable_Callback, sQuery);
}
}
@ -1376,9 +1392,11 @@ void SaveToDatabase(int client)
gH_SQL.Escape(gS_CustomMessage[client], sEscapedMessage, iLength);
char sQuery[512];
FormatEx(sQuery, 512, "REPLACE INTO %schat (auth, name, ccname, message, ccmessage) VALUES ('%s', %d, '%s', %d, '%s');", gS_MySQLPrefix, sAuthID3, gB_NameEnabled[client], sEscapedName, gB_MessageEnabled[client], sEscapedMessage);
FormatEx(sQuery, 512,
"REPLACE INTO %schat (auth, name, ccname, message, ccmessage) VALUES ('%s', %d, '%s', %d, '%s');",
gS_MySQLPrefix, sAuthID3, gB_NameEnabled[client], sEscapedName, gB_MessageEnabled[client], sEscapedMessage);
gH_SQL.Query(SQL_UpdateUser_Callback, sQuery, 0, DBPrio_High);
gH_SQL.Query(SQL_UpdateUser_Callback, sQuery, 0, DBPrio_Low);
}
public void SQL_UpdateUser_Callback(Database db, DBResultSet results, const char[] error, any data)
@ -1393,6 +1411,11 @@ public void SQL_UpdateUser_Callback(Database db, DBResultSet results, const char
void LoadFromDatabase(int client)
{
if(IsFakeClient(client))
{
return;
}
char sAuthID3[32];
if(!GetClientAuthId(client, AuthId_Steam3, sAuthID3, 32))

View File

@ -145,6 +145,7 @@ char gS_LogPath[PLATFORM_MAX_PATH];
char gS_DeleteMap[MAXPLAYERS+1][160];
char gS_WipePlayerID[MAXPLAYERS+1][32];
char gS_Verification[MAXPLAYERS+1][16];
bool gB_CookiesRetrieved[MAXPLAYERS+1];
// flags
int gI_StyleFlag[STYLE_LIMIT];
@ -698,14 +699,14 @@ void DeleteUserData(int client, const char[] sAuthID3)
{
char sQueryGetWorldRecords[256];
FormatEx(sQueryGetWorldRecords, 256,
"SELECT map, id, style, track FROM %splayertimes WHERE auth = '%s' GROUP BY map, style, track;",
"SELECT map, id, style, track FROM %splayertimes WHERE auth = '%s';",
gS_MySQLPrefix, sAuthID3);
DataPack pack = new DataPack();
pack.WriteCell(client);
pack.WriteString(sAuthID3);
DataPack hPack = new DataPack();
hPack.WriteCell(client);
hPack.WriteString(sAuthID3);
gH_SQL.Query(SQL_DeleteUserData_GetRecords_Callback, sQueryGetWorldRecords, pack, DBPrio_High);
gH_SQL.Query(SQL_DeleteUserData_GetRecords_Callback, sQueryGetWorldRecords, hPack, DBPrio_High);
}
else
@ -725,13 +726,13 @@ void DeleteUserData(int client, const char[] sAuthID3)
public void SQL_DeleteUserData_GetRecords_Callback(Database db, DBResultSet results, const char[] error, any data)
{
DataPack pack = view_as<DataPack>(data);
pack.Reset();
int client = pack.ReadCell();
DataPack hPack = view_as<DataPack>(data);
hPack.Reset();
int client = hPack.ReadCell();
char sAuthID3[32];
pack.ReadString(sAuthID3, 32);
delete pack;
hPack.ReadString(sAuthID3, 32);
delete hPack;
if(results == null)
{
@ -740,7 +741,7 @@ public void SQL_DeleteUserData_GetRecords_Callback(Database db, DBResultSet resu
return;
}
Transaction trans = new Transaction();
Transaction hTransaction = new Transaction();
while(results.FetchRow())
{
@ -756,20 +757,20 @@ public void SQL_DeleteUserData_GetRecords_Callback(Database db, DBResultSet resu
"SELECT id FROM %splayertimes WHERE map = '%s' AND style = %d AND track = %d ORDER BY time LIMIT 1;",
gS_MySQLPrefix, map, style, track);
DataPack transPack = new DataPack();
transPack.WriteString(map);
transPack.WriteCell(id);
transPack.WriteCell(style);
transPack.WriteCell(track);
DataPack hTransPack = new DataPack();
hTransPack.WriteString(map);
hTransPack.WriteCell(id);
hTransPack.WriteCell(style);
hTransPack.WriteCell(track);
trans.AddQuery(sQueryGetWorldRecordID, transPack);
hTransaction.AddQuery(sQueryGetWorldRecordID, hTransPack);
}
DataPack steamPack = new DataPack();
steamPack.WriteString(sAuthID3);
steamPack.WriteCell(client);
gH_SQL.Execute(trans, Trans_OnRecordCompare, INVALID_FUNCTION, steamPack, DBPrio_High);
gH_SQL.Execute(hTransaction, Trans_OnRecordCompare, INVALID_FUNCTION, steamPack, DBPrio_High);
}
public void Trans_OnRecordCompare(Database db, any data, int numQueries, DBResultSet[] results, any[] queryData)
@ -904,6 +905,14 @@ public Action Command_Style(int client, int args)
{
int iStyle = gI_OrderedStyles[i];
// this logic will prevent the style from showing in !style menu if it's specifically inaccessible
// or just completely disabled
if((gA_StyleSettings[iStyle].bInaccessible && gA_StyleSettings[iStyle].iEnabled == 1) ||
gA_StyleSettings[iStyle].iEnabled == -1)
{
continue;
}
char sInfo[8];
IntToString(iStyle, sInfo, 8);
@ -920,7 +929,7 @@ public Action Command_Style(int client, int args)
if(gB_WR)
{
time = Shavit_GetWorldRecord(iStyle, Track_Main);
time = Shavit_GetWorldRecord(iStyle, gA_Timers[client].iTrack);
}
if(time > 0.0)
@ -928,7 +937,15 @@ public Action Command_Style(int client, int args)
char sTime[32];
FormatSeconds(time, sTime, 32, false);
FormatEx(sDisplay, 64, "%s - WR: %s", gS_StyleStrings[iStyle].sStyleName, sTime);
char sWR[8];
strcopy(sWR, 8, "WR");
if(gA_Timers[client].iTrack == Track_Bonus)
{
strcopy(sWR, 8, "BWR");
}
FormatEx(sDisplay, 64, "%s - %s: %s", gS_StyleStrings[iStyle].sStyleName, sWR, sTime);
}
else
@ -1210,6 +1227,11 @@ public int Native_HasStyleAccess(Handle handler, int numParams)
{
int style = GetNativeCell(2);
if(gA_StyleSettings[style].bInaccessible || gA_StyleSettings[style].iEnabled <= 0)
{
return false;
}
return CheckCommandAccess(GetNativeCell(1), (strlen(gS_StyleOverride[style]) > 0)? gS_StyleOverride[style]:"<none>", gI_StyleFlag[style]);
}
@ -1601,7 +1623,7 @@ int GetTimerStatus(int client)
void StartTimer(int client, int track)
{
if(!IsValidClient(client, true) || GetClientTeam(client) < 2 || IsFakeClient(client))
if(!IsValidClient(client, true) || GetClientTeam(client) < 2 || IsFakeClient(client) || !gB_CookiesRetrieved[client])
{
return;
}
@ -1691,7 +1713,7 @@ void ResumeTimer(int client)
public void OnClientDisconnect(int client)
{
StopTimer(client);
RequestFrame(StopTimer, client);
}
public void OnClientCookiesCached(int client)
@ -1727,6 +1749,8 @@ public void OnClientCookiesCached(int client)
{
CallOnStyleChanged(client, gA_Timers[client].iStyle, style, false);
}
gB_CookiesRetrieved[client] = true;
}
public void OnClientPutInServer(int client)
@ -1747,6 +1771,8 @@ public void OnClientPutInServer(int client)
gA_Timers[client].iStyle = 0;
strcopy(gS_DeleteMap[client], 160, "");
gB_CookiesRetrieved[client] = false;
if(AreClientCookiesCached(client))
{
OnClientCookiesCached(client);
@ -1887,8 +1913,19 @@ bool LoadStyles()
gA_StyleSettings[i].fRankingMultiplier = kv.GetFloat("rankingmultiplier", 1.00);
gA_StyleSettings[i].iSpecial = kv.GetNum("special", 0);
gA_StyleSettings[i].iOrdering = kv.GetNum("ordering", i);
gA_StyleSettings[i].bInaccessible = view_as<bool>(kv.GetNum("inaccessible", false));
gA_StyleSettings[i].iEnabled = kv.GetNum("enabled", 1);
gA_StyleSettings[i].bKZCheckpoints = view_as<bool>(kv.GetNum("kzcheckpoints", 0));
if(!gB_Registered && strlen(gS_StyleStrings[i].sChangeCommand) > 0)
// if this style is disabled, we will force certain settings
if(gA_StyleSettings[i].iEnabled <= 0)
{
gA_StyleSettings[i].bNoReplay = true;
gA_StyleSettings[i].fRankingMultiplier = 0.0;
gA_StyleSettings[i].bInaccessible = true;
}
if(!gB_Registered && strlen(gS_StyleStrings[i].sChangeCommand) > 0 && !gA_StyleSettings[i].bInaccessible)
{
char sStyleCommands[32][32];
int iCommands = ExplodeString(gS_StyleStrings[i].sChangeCommand, ";", sStyleCommands, 32, 32, false);
@ -2099,16 +2136,15 @@ void SQL_DBConnect()
if(gB_MySQL)
{
FormatEx(sQuery, 512, "CREATE TABLE IF NOT EXISTS `%susers` (`auth` CHAR(32) NOT NULL, `name` VARCHAR(32) COLLATE 'utf8mb4_general_ci', `country` CHAR(32), `ip` CHAR(64), `lastlogin` INT NOT NULL DEFAULT -1, `points` FLOAT NOT NULL DEFAULT 0, PRIMARY KEY (`auth`), INDEX `points` (`points`)) ENGINE=INNODB;", gS_MySQLPrefix);
FormatEx(sQuery, 512, "CREATE TABLE IF NOT EXISTS `%susers` (`auth` VARCHAR(32) NOT NULL, `name` VARCHAR(32) COLLATE 'utf8mb4_general_ci', `country` VARCHAR(32), `ip` VARCHAR(64), `lastlogin` INT NOT NULL DEFAULT -1, `points` FLOAT NOT NULL DEFAULT 0, PRIMARY KEY (`auth`), INDEX `points` (`points`)) ENGINE=INNODB;", gS_MySQLPrefix);
}
else
{
FormatEx(sQuery, 512, "CREATE TABLE IF NOT EXISTS `%susers` (`auth` CHAR(32) NOT NULL PRIMARY KEY, `name` VARCHAR(32), `country` CHAR(32), `ip` CHAR(64), `lastlogin` INTEGER NOT NULL DEFAULT -1, `points` FLOAT NOT NULL DEFAULT 0);", gS_MySQLPrefix);
FormatEx(sQuery, 512, "CREATE TABLE IF NOT EXISTS `%susers` (`auth` VARCHAR(32) NOT NULL PRIMARY KEY, `name` VARCHAR(32), `country` VARCHAR(32), `ip` VARCHAR(64), `lastlogin` INTEGER NOT NULL DEFAULT -1, `points` FLOAT NOT NULL DEFAULT 0);", gS_MySQLPrefix);
}
// CREATE TABLE IF NOT EXISTS
gH_SQL.Query(SQL_CreateTable_Callback, sQuery);
gH_SQL.Query(SQL_CreateTable_Callback, sQuery, 0, DBPrio_High);
}
public void SQL_CreateTable_Callback(Database db, DBResultSet results, const char[] error, any data)
@ -2120,13 +2156,6 @@ public void SQL_CreateTable_Callback(Database db, DBResultSet results, const cha
return;
}
char sQuery[192];
FormatEx(sQuery, 192, "SELECT lastlogin FROM %susers LIMIT 1;", gS_MySQLPrefix);
gH_SQL.Query(SQL_TableMigration1_Callback, sQuery, 0, DBPrio_High);
FormatEx(sQuery, 192, "SELECT points FROM %susers LIMIT 1;", gS_MySQLPrefix);
gH_SQL.Query(SQL_TableMigration2_Callback, sQuery, 0, DBPrio_High);
char sTables[][] =
{
"maptiers",
@ -2139,58 +2168,18 @@ public void SQL_CreateTable_Callback(Database db, DBResultSet results, const cha
DataPack dp = new DataPack();
dp.WriteString(sTables[i]);
char sQuery[192];
FormatEx(sQuery, 192, "SELECT map FROM %s%s WHERE map LIKE 'workshop%%' GROUP BY map;", gS_MySQLPrefix, sTables[i]);
gH_SQL.Query(SQL_TableMigration3_Callback, sQuery, dp, DBPrio_Low);
gH_SQL.Query(SQL_TableMigration_Callback, sQuery, dp, DBPrio_High);
}
Call_StartForward(gH_Forwards_OnDatabaseLoaded);
Call_Finish();
}
public void SQL_TableMigration1_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
char sQuery[128];
FormatEx(sQuery, 128, "ALTER TABLE `%susers` ADD %s;", gS_MySQLPrefix, (gB_MySQL)? "(`lastlogin` INT NOT NULL DEFAULT -1)":"COLUMN `lastlogin` INTEGER NOT NULL DEFAULT -1");
gH_SQL.Query(SQL_AlterTable1_Callback, sQuery);
}
}
public void SQL_AlterTable1_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
LogError("Timer error! Table alteration 1 (core) failed. Reason: %s", error);
return;
}
}
public void SQL_TableMigration2_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
char sQuery[128];
FormatEx(sQuery, 128, "ALTER TABLE `%susers` ADD %s;", gS_MySQLPrefix, (gB_MySQL)? "(`points` FLOAT NOT NULL DEFAULT 0)":"COLUMN `points` FLOAT NOT NULL DEFAULT 0");
gH_SQL.Query(SQL_AlterTable2_Callback, sQuery);
}
}
public void SQL_AlterTable2_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
LogError("Timer error! Table alteration 2 (core) failed. Reason: %s", error);
return;
}
}
public void SQL_TableMigration3_Callback(Database db, DBResultSet results, const char[] error, DataPack data)
public void SQL_TableMigration_Callback(Database db, DBResultSet results, const char[] error, DataPack data)
{
char sTable[16];
data.Reset();
data.ReadString(sTable, 16);
delete data;
@ -2386,6 +2375,32 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
}
#endif
int iPButtons = buttons;
if(gA_StyleSettings[gA_Timers[client].iStyle].bStrafeCountW && !gA_StyleSettings[gA_Timers[client].iStyle].bBlockW &&
(gA_Timers[client].iLastButtons & IN_FORWARD) == 0 && (buttons & IN_FORWARD) > 0)
{
gA_Timers[client].iStrafes++;
}
if(gA_StyleSettings[gA_Timers[client].iStyle].bStrafeCountA && !gA_StyleSettings[gA_Timers[client].iStyle].bBlockA && (gA_Timers[client].iLastButtons & IN_MOVELEFT) == 0 &&
(buttons & IN_MOVELEFT) > 0 && (gA_StyleSettings[gA_Timers[client].iStyle].iForceHSW > 0 || ((buttons & IN_FORWARD) == 0 && (buttons & IN_BACK) == 0)))
{
gA_Timers[client].iStrafes++;
}
if(gA_StyleSettings[gA_Timers[client].iStyle].bStrafeCountS && !gA_StyleSettings[gA_Timers[client].iStyle].bBlockS &&
(gA_Timers[client].iLastButtons & IN_BACK) == 0 && (buttons & IN_BACK) > 0)
{
gA_Timers[client].iStrafes++;
}
if(gA_StyleSettings[gA_Timers[client].iStyle].bStrafeCountD && !gA_StyleSettings[gA_Timers[client].iStyle].bBlockD && (gA_Timers[client].iLastButtons & IN_MOVERIGHT) == 0 &&
(buttons & IN_MOVERIGHT) > 0 && (gA_StyleSettings[gA_Timers[client].iStyle].iForceHSW > 0 || ((buttons & IN_FORWARD) == 0 && (buttons & IN_BACK) == 0)))
{
gA_Timers[client].iStrafes++;
}
MoveType mtMoveType = GetEntityMoveType(client);
// key blocking
@ -2502,30 +2517,6 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
}
}
if(gA_StyleSettings[gA_Timers[client].iStyle].bStrafeCountW && !gA_StyleSettings[gA_Timers[client].iStyle].bBlockW &&
(gA_Timers[client].iLastButtons & IN_FORWARD) == 0 && (buttons & IN_FORWARD) > 0)
{
gA_Timers[client].iStrafes++;
}
if(gA_StyleSettings[gA_Timers[client].iStyle].bStrafeCountA && !gA_StyleSettings[gA_Timers[client].iStyle].bBlockA && (gA_Timers[client].iLastButtons & IN_MOVELEFT) == 0 &&
(buttons & IN_MOVELEFT) > 0 && (gA_StyleSettings[gA_Timers[client].iStyle].iForceHSW > 0 || ((buttons & IN_FORWARD) == 0 && (buttons & IN_BACK) == 0)))
{
gA_Timers[client].iStrafes++;
}
if(gA_StyleSettings[gA_Timers[client].iStyle].bStrafeCountS && !gA_StyleSettings[gA_Timers[client].iStyle].bBlockS &&
(gA_Timers[client].iLastButtons & IN_BACK) == 0 && (buttons & IN_BACK) > 0)
{
gA_Timers[client].iStrafes++;
}
if(gA_StyleSettings[gA_Timers[client].iStyle].bStrafeCountD && !gA_StyleSettings[gA_Timers[client].iStyle].bBlockD && (gA_Timers[client].iLastButtons & IN_MOVERIGHT) == 0 &&
(buttons & IN_MOVERIGHT) > 0 && (gA_StyleSettings[gA_Timers[client].iStyle].iForceHSW > 0 || ((buttons & IN_FORWARD) == 0 && (buttons & IN_BACK) == 0)))
{
gA_Timers[client].iStrafes++;
}
bool bInWater = (GetEntProp(client, Prop_Send, "m_nWaterLevel") >= 2);
// enable duck-jumping/bhop in tf2
@ -2538,8 +2529,6 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
SetEntPropVector(client, Prop_Data, "m_vecAbsVelocity", fSpeed);
}
int iPButtons = buttons;
if(gA_StyleSettings[gA_Timers[client].iStyle].bAutobhop && gA_Timers[client].bAuto && (buttons & IN_JUMP) > 0 && mtMoveType == MOVETYPE_WALK && !bInWater)
{
int iOldButtons = GetEntProp(client, Prop_Data, "m_nOldButtons");

View File

@ -1089,13 +1089,20 @@ int AddHUDToBuffer_Source2013(int client, huddata_t data, char[] buffer, int max
AddHUDLine(buffer, maxlen, sLine, iLines);
iLines++;
if(gA_StyleSettings[data.iStyle].fVelocityLimit > 0.0 && Shavit_InsideZone(data.iTarget, Zone_NoVelLimit, -1))
{
FormatEx(sLine, 128, "%T", "HudNoSpeedLimit", client);
AddHUDLine(buffer, maxlen, sLine, iLines);
iLines++;
}
}
if(data.iTimerStatus != Timer_Stopped && data.iTrack != Track_Main && (gI_HUD2Settings[client] & HUD2_TRACK) == 0)
{
char sTrack[32];
GetTrackName(client, data.iTrack, sTrack, 32);
Format(sTrack, 32, "%s", sTrack);
AddHUDLine(buffer, maxlen, sTrack, iLines);
iLines++;
@ -1216,7 +1223,6 @@ int AddHUDToBuffer_CSGO(int client, huddata_t data, char[] buffer, int maxlen)
{
char sTrack[32];
GetTrackName(client, data.iTrack, sTrack, 32);
Format(sTrack, 32, "%s", sTrack);
AddHUDLine(buffer, maxlen, sTrack, iLines);
iLines++;
@ -1314,6 +1320,7 @@ void UpdateMainHUD(int client)
float fSpeed[3];
GetEntPropVector(target, Prop_Data, "m_vecVelocity", fSpeed);
float fSpeedHUD = ((gI_HUDSettings[client] & HUD_2DVEL) == 0)? GetVectorLength(fSpeed):(SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0)));
bool bReplay = (gB_Replay && IsFakeClient(target));
ZoneHUD iZoneHUD = ZoneHUD_None;
int iReplayStyle = 0;
@ -1343,12 +1350,17 @@ void UpdateMainHUD(int client)
{
fReplayTime = Shavit_GetReplayTime(iReplayStyle, iReplayTrack);
fReplayLength = Shavit_GetReplayLength(iReplayStyle, iReplayTrack);
if(gA_StyleSettings[iReplayStyle].fSpeedMultiplier != 1.0)
{
fSpeedHUD /= gA_StyleSettings[iReplayStyle].fSpeedMultiplier;
}
}
}
huddata_t huddata;
huddata.iTarget = target;
huddata.iSpeed = RoundToNearest(((gI_HUDSettings[client] & HUD_2DVEL) == 0)? GetVectorLength(fSpeed):(SquareRoot(Pow(fSpeed[0], 2.0) + Pow(fSpeed[1], 2.0))));
huddata.iSpeed = RoundToNearest(fSpeedHUD);
huddata.iZoneHUD = iZoneHUD;
huddata.iStyle = (bReplay)? iReplayStyle:Shavit_GetBhopStyle(target);
huddata.iTrack = (bReplay)? iReplayTrack:Shavit_GetClientTrack(target);

View File

@ -73,6 +73,21 @@ enum struct player_cpcache_t
int iCurrentCheckpoint;
}
enum struct persistent_data_t
{
char sAuthID[32];
float fDisconnectTime;
float fPosition[3];
float fAngles[3];
MoveType iMoveType;
float fGravity;
float fSpeed;
timer_snapshot_t aSnapshot;
int iTargetname;
int iClassname;
ArrayList aFrames;
}
// game specific
EngineVersion gEV_Type = Engine_Unknown;
int gI_Ammo = -1;
@ -109,6 +124,7 @@ timer_snapshot_t gA_SaveStates[MAXPLAYERS+1];
bool gB_SaveStates[MAXPLAYERS+1];
char gS_SaveStateTargetname[MAXPLAYERS+1][32];
ArrayList gA_SaveFrames[MAXPLAYERS+1];
ArrayList gA_PersistentData = null;
// cookies
Handle gH_HideCookie = null;
@ -146,13 +162,14 @@ ConVar gCV_JointeamHook = null;
ConVar gCV_SpectatorList = null;
ConVar gCV_MaxCP = null;
ConVar gCV_MaxCP_Segmented = null;
ConVar gCV_HideChatCommands = null;
ConVar gCV_PersistData = null;
// forwards
Handle gH_Forwards_OnClanTagChangePre = null;
Handle gH_Forwards_OnClanTagChangePost = null;
// cached cvars
int gI_HumanTeam = 0;
Handle gH_Forwards_OnSave = null;
Handle gH_Forwards_OnTeleport = null;
// dhooks
Handle gH_GetPlayerMaxSpeed = null;
@ -180,10 +197,6 @@ public Plugin myinfo =
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
// forwards
gH_Forwards_OnClanTagChangePre = CreateGlobalForward("Shavit_OnClanTagChangePre", ET_Event, Param_Cell, Param_String, Param_Cell);
gH_Forwards_OnClanTagChangePost = CreateGlobalForward("Shavit_OnClanTagChangePost", ET_Event, Param_Cell, Param_String, Param_Cell);
gB_Late = late;
return APLRes_Success;
@ -191,6 +204,12 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max
public void OnPluginStart()
{
// forwards
gH_Forwards_OnClanTagChangePre = CreateGlobalForward("Shavit_OnClanTagChangePre", ET_Event, Param_Cell, Param_String, Param_Cell);
gH_Forwards_OnClanTagChangePost = CreateGlobalForward("Shavit_OnClanTagChangePost", ET_Event, Param_Cell, Param_String, Param_Cell);
gH_Forwards_OnSave = CreateGlobalForward("Shavit_OnSave", ET_Event, Param_Cell);
gH_Forwards_OnTeleport = CreateGlobalForward("Shavit_OnTeleport", ET_Event, Param_Cell);
// cache
gEV_Type = GetEngineVersion();
@ -229,6 +248,7 @@ public void OnPluginStart()
gSM_Checkpoints = new StringMap();
gA_Targetnames = new ArrayList(ByteCountToCells(64));
gA_Classnames = new ArrayList(ByteCountToCells(64));
gA_PersistentData = new ArrayList(sizeof(persistent_data_t));
gI_Ammo = FindSendPropInfo("CCSPlayer", "m_iAmmo");
@ -302,6 +322,8 @@ public void OnPluginStart()
gCV_SpectatorList = CreateConVar("shavit_misc_speclist", "1", "Who to show in !specs?\n0 - everyone\n1 - all admins (admin_speclisthide override to bypass)\n2 - players you can target", 0, true, 0.0, true, 2.0);
gCV_MaxCP = CreateConVar("shavit_misc_maxcp", "1000", "Maximum amount of checkpoints.\nNote: Very high values will result in high memory usage!", 0, true, 1.0, true, 10000.0);
gCV_MaxCP_Segmented = CreateConVar("shavit_misc_maxcp_seg", "10", "Maximum amount of segmented checkpoints. Make this less or equal to shavit_misc_maxcp.\nNote: Very high values will result in HUGE memory usage!", 0, true, 1.0, true, 50.0);
gCV_HideChatCommands = CreateConVar("shavit_misc_hidechatcmds", "1", "Hide commands from chat?\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
gCV_PersistData = CreateConVar("shavit_misc_persistdata", "300", "How long to persist timer data for disconnected users in seconds?\n-1 - Until map change\n0 - Disabled");
AutoExecConfig();
@ -312,9 +334,9 @@ public void OnPluginStart()
mp_humanteam = FindConVar("mp_humans_must_join_team");
}
mp_humanteam.AddChangeHook(OnConVarChanged);
// crons
CreateTimer(10.0, Timer_Cron, 0, TIMER_REPEAT);
if(gEV_Type != Engine_TF2)
{
CreateTimer(1.0, Timer_Scoreboard, 0, TIMER_REPEAT);
@ -440,32 +462,11 @@ public void Shavit_OnStyleChanged(int client, int oldstyle, int newstyle, int tr
if(StrContains(gS_StyleStrings[newstyle].sSpecialString, "segments") != -1)
{
OpenCheckpointsMenu(client, 0);
OpenCheckpointsMenu(client);
Shavit_PrintToChat(client, "%T", "MiscSegmentedCommand", client, gS_ChatStrings.sVariable, gS_ChatStrings.sText);
}
}
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
if(convar == mp_humanteam)
{
if(StrEqual(newValue, "t", false) || StrEqual(newValue, "red", false))
{
gI_HumanTeam = 2;
}
else if(StrEqual(newValue, "ct", false) || StrEqual(newValue, "blue", false))
{
gI_HumanTeam = 3;
}
else
{
gI_HumanTeam = 0;
}
}
}
public void OnConfigsExecuted()
{
if(sv_disable_immunity_alpha != null)
@ -481,9 +482,20 @@ public void OnMapStart()
ResetCheckpoints(i);
}
int iLength = gA_PersistentData.Length;
for(int i = iLength - 1; i >= 0; i--)
{
persistent_data_t aData;
gA_PersistentData.GetArray(i, aData);
delete aData.aFrames;
}
gSM_Checkpoints.Clear();
gA_Targetnames.Clear();
gA_Classnames.Clear();
gA_PersistentData.Clear();
GetCurrentMap(gS_CurrentMap, 192);
GetMapDisplayName(gS_CurrentMap, gS_CurrentMap, 192);
@ -608,6 +620,24 @@ public void OnLibraryRemoved(const char[] name)
}
}
int GetHumanTeam()
{
char sTeam[8];
mp_humanteam.GetString(sTeam, 8);
if(StrEqual(sTeam, "t", false) || StrEqual(sTeam, "red", false))
{
return 2;
}
else if(StrEqual(sTeam, "ct", false) || StrContains(sTeam, "blu", false) != -1)
{
return 3;
}
return 0;
}
public Action Command_Jointeam(int client, const char[] command, int args)
{
if(!IsValidClient(client) || !gCV_JointeamHook.BoolValue)
@ -624,10 +654,11 @@ public Action Command_Jointeam(int client, const char[] command, int args)
GetCmdArg(1, arg1, 8);
int iTeam = StringToInt(arg1);
int iHumanTeam = GetHumanTeam();
if(gI_HumanTeam == 0 && !(0 <= iTeam <= 1))
if(iHumanTeam != 0)
{
iTeam = gI_HumanTeam;
iTeam = iHumanTeam;
}
bool bRespawn = false;
@ -725,6 +756,25 @@ public MRESReturn CCSPlayer__GetPlayerMaxSpeed(int pThis, Handle hReturn)
return MRES_Override;
}
public Action Timer_Cron(Handle Timer)
{
int iLength = gA_PersistentData.Length;
float fTime = GetEngineTime();
for(int i = iLength - 1; i >= 0; i--)
{
persistent_data_t aData;
gA_PersistentData.GetArray(i, aData);
if(fTime - aData.fDisconnectTime >= gCV_PersistData.FloatValue)
{
DeletePersistentData(i, aData);
}
}
return Plugin_Continue;
}
public Action Timer_Scoreboard(Handle Timer)
{
for(int i = 1; i <= MaxClients; i++)
@ -1014,7 +1064,6 @@ public void OnClientPutInServer(int client)
ResetCheckpoints(client);
gB_SaveStates[client] = false;
delete gA_SaveFrames[client];
}
@ -1033,7 +1082,159 @@ public void OnClientDisconnect(int client)
}
}
if(IsFakeClient(client))
{
return;
}
ResetCheckpoints(client);
gB_SaveStates[client] = false;
delete gA_SaveFrames[client];
PersistData(client);
}
void PersistData(int client)
{
persistent_data_t aData;
if(!IsPlayerAlive(client) ||
!GetClientAuthId(client, AuthId_Steam3, aData.sAuthID, 32) ||
Shavit_GetTimerStatus(client) == Timer_Stopped ||
gCV_PersistData.IntValue == 0)
{
return;
}
if(gB_Replay)
{
aData.aFrames = Shavit_GetReplayData(client);
}
aData.fDisconnectTime = GetEngineTime();
aData.iMoveType = GetEntityMoveType(client);
aData.fGravity = GetEntityGravity(client);
aData.fSpeed = GetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue");
float fPosition[3];
GetClientAbsOrigin(client, fPosition);
CopyArray(fPosition, aData.fPosition, 3);
float fAngles[3];
GetClientEyeAngles(client, fAngles);
CopyArray(fAngles, aData.fAngles, 3);
timer_snapshot_t aSnapshot;
Shavit_SaveSnapshot(client, aSnapshot);
CopyArray(aSnapshot, aData.aSnapshot, sizeof(timer_snapshot_t));
char sTargetname[64];
GetEntPropString(client, Prop_Data, "m_iName", sTargetname, 64);
aData.iTargetname = gA_Targetnames.FindString(sTargetname);
if(aData.iTargetname == -1)
{
aData.iTargetname = gA_Targetnames.PushString(sTargetname);
}
char sClassname[64];
GetEntityClassname(client, sClassname, 64);
aData.iClassname = gA_Classnames.FindString(sClassname);
if(aData.iClassname == -1)
{
aData.iClassname = gA_Classnames.PushString(sClassname);
}
gA_PersistentData.PushArray(aData);
}
void DeletePersistentData(int index, persistent_data_t data)
{
delete data.aFrames;
gA_PersistentData.Erase(index);
}
public Action Timer_LoadPersistentData(Handle Timer, any data)
{
char sAuthID[32];
int client = GetClientFromSerial(data);
if(client == 0 ||
!GetClientAuthId(client, AuthId_Steam3, sAuthID, 32) ||
GetClientTeam(client) < 2 ||
!IsPlayerAlive(client))
{
return Plugin_Stop;
}
persistent_data_t aData;
int iIndex = -1;
int iLength = gA_PersistentData.Length;
for(int i = 0; i < iLength; i++)
{
gA_PersistentData.GetArray(i, aData);
if(StrEqual(sAuthID, aData.sAuthID))
{
iIndex = i;
break;
}
}
if(iIndex == -1)
{
return Plugin_Stop;
}
Shavit_StopTimer(client);
float fPosition[3];
CopyArray(aData.fPosition, fPosition, 3);
float fAngles[3];
CopyArray(aData.fAngles, fAngles, 3);
SetEntityMoveType(client, aData.iMoveType);
SetEntityGravity(client, aData.fGravity);
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", aData.fSpeed);
timer_snapshot_t aSnapshot;
CopyArray(aData.aSnapshot, aSnapshot, sizeof(timer_snapshot_t));
Shavit_LoadSnapshot(client, aSnapshot);
if(aData.iTargetname != -1)
{
char sTargetname[64];
gA_Targetnames.GetString(aData.iTargetname, sTargetname, 64);
SetEntPropString(client, Prop_Data, "m_iName", sTargetname);
}
if(aData.iClassname != -1)
{
char sClassname[64];
gA_Classnames.GetString(aData.iClassname, sClassname, 64);
SetEntPropString(client, Prop_Data, "m_iClassname", sClassname);
}
if(gB_Replay && aData.aFrames != null)
{
Shavit_SetReplayData(client, aData.aFrames);
}
TeleportEntity(client, fPosition, fAngles, view_as<float>({ 0.0, 0.0, 0.0 }));
delete aData.aFrames;
gA_PersistentData.Erase(iIndex);
return Plugin_Stop;
}
void RemoveWeapon(any data)
@ -1150,7 +1351,7 @@ public void OnPreThink(int client)
public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs)
{
if(IsChatTrigger())
if(IsChatTrigger() && gCV_HideChatCommands.BoolValue)
{
// hide commands
return Plugin_Handled;
@ -1442,7 +1643,7 @@ public Action Command_Checkpoints(int client, int args)
return Plugin_Handled;
}
return OpenCheckpointsMenu(client, 0);
return OpenCheckpointsMenu(client);
}
public Action Command_Save(int client, int args)
@ -1533,7 +1734,119 @@ public Action Command_Tele(int client, int args)
return Plugin_Handled;
}
public Action OpenCheckpointsMenu(int client, int item)
public Action OpenCheckpointsMenu(int client)
{
if(gA_StyleSettings[gI_Style[client]].bKZCheckpoints)
{
OpenKZCPMenu(client);
}
else
{
OpenNormalCPMenu(client);
}
return Plugin_Handled;
}
void OpenKZCPMenu(int client)
{
// if we're segmenting, resort to the normal checkpoints instead
if(CanSegment(client))
{
OpenNormalCPMenu(client);
return;
}
Menu menu = new Menu(MenuHandler_KZCheckpoints, MENU_ACTIONS_DEFAULT|MenuAction_DisplayItem);
menu.SetTitle("%T\n", "MiscCheckpointMenu", client);
char sDisplay[64];
FormatEx(sDisplay, 64, "%T", "MiscCheckpointSave", client, (gA_CheckpointsCache[client].iCheckpoints + 1));
menu.AddItem("save", sDisplay, (gA_CheckpointsCache[client].iCheckpoints < gCV_MaxCP.IntValue)? ITEMDRAW_DEFAULT:ITEMDRAW_DISABLED);
if(gA_CheckpointsCache[client].iCheckpoints > 0)
{
FormatEx(sDisplay, 64, "%T", "MiscCheckpointTeleport", client, gA_CheckpointsCache[client].iCurrentCheckpoint);
menu.AddItem("tele", sDisplay, ITEMDRAW_DEFAULT);
}
else
{
FormatEx(sDisplay, 64, "%T", "MiscCheckpointTeleport", client, 1);
menu.AddItem("tele", sDisplay, ITEMDRAW_DISABLED);
}
FormatEx(sDisplay, 64, "%T", "MiscCheckpointPrevious", client);
menu.AddItem("prev", sDisplay);
FormatEx(sDisplay, 64, "%T", "MiscCheckpointNext", client);
menu.AddItem("next", sDisplay);
menu.ExitButton = true;
menu.Display(client, MENU_TIME_FOREVER);
}
public int MenuHandler_KZCheckpoints(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
if(CanSegment(param1) || !gA_StyleSettings[gI_Style[param1]].bKZCheckpoints)
{
return 0;
}
int iCurrent = gA_CheckpointsCache[param1].iCurrentCheckpoint;
int iMaxCPs = GetMaxCPs(param1);
char sInfo[8];
menu.GetItem(param2, sInfo, 8);
if(StrEqual(sInfo, "save"))
{
if(gA_CheckpointsCache[param1].iCheckpoints < iMaxCPs &&
SaveCheckpoint(param1, gA_CheckpointsCache[param1].iCheckpoints + 1))
{
gA_CheckpointsCache[param1].iCurrentCheckpoint = ++gA_CheckpointsCache[param1].iCheckpoints;
}
}
else if(StrEqual(sInfo, "tele"))
{
TeleportToCheckpoint(param1, iCurrent, true);
}
else if(StrEqual(sInfo, "prev"))
{
if(iCurrent > 1)
{
gA_CheckpointsCache[param1].iCurrentCheckpoint--;
}
}
else if(StrEqual(sInfo, "next"))
{
cp_cache_t cpcache;
if(iCurrent++ < iMaxCPs && GetCheckpoint(param1, iCurrent, cpcache))
{
gA_CheckpointsCache[param1].iCurrentCheckpoint++;
}
}
OpenCheckpointsMenu(param1);
}
else if(action == MenuAction_End)
{
delete menu;
}
return 0;
}
void OpenNormalCPMenu(int client)
{
bool bSegmented = CanSegment(client);
@ -1541,7 +1854,7 @@ public Action OpenCheckpointsMenu(int client, int item)
{
Shavit_PrintToChat(client, "%T", "FeatureDisabled", client, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
return Plugin_Handled;
return;
}
Menu menu = new Menu(MenuHandler_Checkpoints, MENU_ACTIONS_DEFAULT|MenuAction_DisplayItem);
@ -1596,8 +1909,6 @@ public Action OpenCheckpointsMenu(int client, int item)
menu.Pagination = MENU_NO_PAGINATION;
menu.ExitButton = true;
menu.Display(client, MENU_TIME_FOREVER);
return Plugin_Handled;
}
public int MenuHandler_Checkpoints(Menu menu, MenuAction action, int param1, int param2)
@ -1676,7 +1987,7 @@ public int MenuHandler_Checkpoints(Menu menu, MenuAction action, int param1, int
}
}
OpenCheckpointsMenu(param1, GetMenuSelectionPosition());
OpenCheckpointsMenu(param1);
}
else if(action == MenuAction_DisplayItem && param2 >= 5)
@ -1712,6 +2023,7 @@ bool SaveCheckpoint(int client, int index, bool overflow = false)
int iObserverMode = GetEntProp(client, Prop_Send, "m_iObserverMode");
int iObserverTarget = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget");
int iFlags = GetEntityFlags(client);
if(IsClientObserver(client) && IsValidClient(iObserverTarget) && 3 <= iObserverMode <= 5)
{
@ -1732,6 +2044,24 @@ bool SaveCheckpoint(int client, int index, bool overflow = false)
return false;
}
if(gA_StyleSettings[gI_Style[client]].bKZCheckpoints &&
((iFlags & FL_ONGROUND) == 0 || client != target))
{
Shavit_PrintToChat(client, "%T", "CommandSaveCPKZInvalid", client);
return false;
}
Action result = Plugin_Continue;
Call_StartForward(gH_Forwards_OnSave);
Call_PushCell(client);
Call_Finish(result);
if(result != Plugin_Continue)
{
return false;
}
char sKey[32];
int iSerial = GetClientSerial(client);
FormatEx(sKey, 32, "%d_%d", iSerial, index);
@ -1783,8 +2113,6 @@ bool SaveCheckpoint(int client, int index, bool overflow = false)
cpcache.fGravity = GetEntityGravity(target);
cpcache.fSpeed = GetEntPropFloat(target, Prop_Send, "m_flLaggedMovementValue");
int iFlags = GetEntityFlags(target);
if(IsFakeClient(target))
{
iFlags |= FL_CLIENT;
@ -1926,7 +2254,7 @@ void TeleportToCheckpoint(int client, int index, bool suppressMessage)
cp_cache_t cpcache;
if(!GetCheckpoint(client, index, cpcache))
if(!GetCheckpoint(client, index, cpcache) || index > gA_CheckpointsCache[client].iCheckpoints)
{
Shavit_PrintToChat(client, "%T", "MiscCheckpointsEmpty", client, index, gS_ChatStrings.sWarning, gS_ChatStrings.sText);
@ -1940,6 +2268,16 @@ void TeleportToCheckpoint(int client, int index, bool suppressMessage)
return;
}
timer_snapshot_t snapshot;
CopyArray(cpcache.aSnapshot, snapshot, sizeof(timer_snapshot_t));
if(gA_StyleSettings[gI_Style[client]].bKZCheckpoints != gA_StyleSettings[snapshot.bsStyle].bKZCheckpoints)
{
Shavit_PrintToChat(client, "%T", "CommandTeleCPInvalid", client);
return;
}
float pos[3];
CopyArray(cpcache.fPosition, pos, 3);
@ -1955,19 +2293,64 @@ void TeleportToCheckpoint(int client, int index, bool suppressMessage)
return;
}
Action result = Plugin_Continue;
Call_StartForward(gH_Forwards_OnTeleport);
Call_PushCell(client);
Call_Finish(result);
if(result != Plugin_Continue)
{
return;
}
if(Shavit_InsideZone(client, Zone_Start, -1))
{
Shavit_StopTimer(client);
}
timer_snapshot_t snapshot;
CopyArray(cpcache.aSnapshot, snapshot, sizeof(timer_snapshot_t));
Shavit_LoadSnapshot(client, snapshot);
Shavit_ResumeTimer(client);
MoveType mt = cpcache.iMoveType;
if(mt == MOVETYPE_LADDER || mt == MOVETYPE_WALK)
{
SetEntityMoveType(client, mt);
}
SetEntityFlags(client, cpcache.iFlags);
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", cpcache.fSpeed);
SetEntPropEnt(client, Prop_Data, "m_hGroundEntity", cpcache.iGroundEntity);
if(gEV_Type != Engine_TF2)
{
SetEntPropFloat(client, Prop_Send, "m_flStamina", cpcache.fStamina);
SetEntProp(client, Prop_Send, "m_bDucked", cpcache.bDucked);
SetEntProp(client, Prop_Send, "m_bDucking", cpcache.bDucking);
}
if(gEV_Type == Engine_CSS)
{
SetEntPropFloat(client, Prop_Send, "m_flDucktime", cpcache.fDucktime);
}
else if(gEV_Type == Engine_CSGO)
{
SetEntPropFloat(client, Prop_Send, "m_flDuckAmount", cpcache.fDucktime);
SetEntPropFloat(client, Prop_Send, "m_flDuckSpeed", cpcache.fDuckSpeed);
}
float ang[3];
CopyArray(cpcache.fAngles, ang, 3);
// this is basically the same as normal checkpoints except much less data is used
if(gA_StyleSettings[gI_Style[client]].bKZCheckpoints)
{
TeleportEntity(client, pos, ang, view_as<float>({ 0.0, 0.0, 0.0 }));
return;
}
Shavit_LoadSnapshot(client, snapshot);
Shavit_ResumeTimer(client);
float vel[3];
if((gI_CheckpointsSettings[client] & CP_VELOCITY) > 0 || cpcache.bSegmented)
@ -2009,36 +2392,7 @@ void TeleportToCheckpoint(int client, int index, bool suppressMessage)
Shavit_SetPracticeMode(client, true, true);
}
MoveType mt = cpcache.iMoveType;
if(mt == MOVETYPE_LADDER || mt == MOVETYPE_WALK)
{
SetEntityMoveType(client, mt);
}
SetEntityGravity(client, cpcache.fGravity);
SetEntityFlags(client, cpcache.iFlags);
SetEntPropFloat(client, Prop_Send, "m_flLaggedMovementValue", cpcache.fSpeed);
SetEntPropEnt(client, Prop_Data, "m_hGroundEntity", cpcache.iGroundEntity);
if(gEV_Type != Engine_TF2)
{
SetEntPropFloat(client, Prop_Send, "m_flStamina", cpcache.fStamina);
SetEntProp(client, Prop_Send, "m_bDucked", cpcache.bDucked);
SetEntProp(client, Prop_Send, "m_bDucking", cpcache.bDucking);
}
if(gEV_Type == Engine_CSS)
{
SetEntPropFloat(client, Prop_Send, "m_flDucktime", cpcache.fDucktime);
}
else if(gEV_Type == Engine_CSGO)
{
SetEntPropFloat(client, Prop_Send, "m_flDuckAmount", cpcache.fDucktime);
SetEntPropFloat(client, Prop_Send, "m_flDuckSpeed", cpcache.fDuckSpeed);
}
if(cpcache.bSegmented && gB_Replay)
{
@ -2232,6 +2586,11 @@ public Action Shavit_OnStart(int client)
SetEntPropString(client, Prop_Data, "m_iClassname", "player");
}
if(gA_StyleSettings[gI_Style[client]].bKZCheckpoints)
{
ResetCheckpoints(client);
}
return Plugin_Continue;
}
@ -2289,6 +2648,11 @@ public void Shavit_OnWorldRecord(int client, int style, float time, int jumps, i
public void Shavit_OnRestart(int client, int track)
{
if(gA_StyleSettings[gI_Style[client]].bKZCheckpoints && GetClientMenu(client, null) == MenuSource_None)
{
OpenKZCPMenu(client);
}
if(!gCV_RespawnOnRestart.BoolValue)
{
return;
@ -2395,12 +2759,23 @@ public void Player_Spawn(Event event, const char[] name, bool dontBroadcast)
}
}
else
{
CreateTimer(0.10, Timer_LoadPersistentData, GetClientSerial(client), TIMER_FLAG_NO_MAPCHANGE);
}
if(gCV_Scoreboard.BoolValue)
{
UpdateScoreboard(client);
}
UpdateClanTag(client);
// refreshes kz cp menu if there is nothing open
if(gA_StyleSettings[gI_Style[client]].bKZCheckpoints && GetClientMenu(client, null) == MenuSource_None)
{
OpenKZCPMenu(client);
}
}
if(gCV_NoBlock.BoolValue)

View File

@ -49,6 +49,12 @@
// uncomment when done
// #define DEBUG
enum struct ranking_t
{
int iRank;
float fPoints;
}
char gS_MySQLPrefix[32];
Database gH_SQL = null;
@ -66,8 +72,7 @@ StringMap gA_MapTiers = null;
ConVar gCV_PointsPerTier = null;
ConVar gCV_WeightingMultiplier = null;
int gI_Rank[MAXPLAYERS+1];
float gF_Points[MAXPLAYERS+1];
ranking_t gA_Rankings[MAXPLAYERS+1];
int gI_RankedPlayers = 0;
Menu gH_Top100Menu = null;
@ -272,7 +277,7 @@ void SQL_DBConnect()
}
char sQuery[256];
FormatEx(sQuery, 256, "CREATE TABLE IF NOT EXISTS `%smaptiers` (`map` CHAR(128), `tier` INT NOT NULL DEFAULT 1, PRIMARY KEY (`map`)) ENGINE=INNODB;", gS_MySQLPrefix);
FormatEx(sQuery, 256, "CREATE TABLE IF NOT EXISTS `%smaptiers` (`map` VARCHAR(128), `tier` INT NOT NULL DEFAULT 1, PRIMARY KEY (`map`)) ENGINE=INNODB;", gS_MySQLPrefix);
gH_SQL.Query(SQL_CreateTable_Callback, sQuery, 0);
}
@ -305,7 +310,7 @@ public void SQL_CreateTable_Callback(Database db, DBResultSet results, const cha
bool bSuccess = true;
RunLongFastQuery(bSuccess, "CREATE GetWeightedPoints",
"CREATE FUNCTION GetWeightedPoints(authid CHAR(32)) " ...
"CREATE FUNCTION GetWeightedPoints(authid VARCHAR(32)) " ...
"RETURNS FLOAT " ...
"READS SQL DATA " ...
"BEGIN " ...
@ -329,7 +334,7 @@ public void SQL_CreateTable_Callback(Database db, DBResultSet results, const cha
"END;;", gS_MySQLPrefix, gCV_WeightingMultiplier.FloatValue);
RunLongFastQuery(bSuccess, "CREATE GetRecordPoints",
"CREATE FUNCTION GetRecordPoints(rstyle INT, rtrack INT, rtime FLOAT, rmap CHAR(128), pointspertier FLOAT, stylemultiplier FLOAT) " ...
"CREATE FUNCTION GetRecordPoints(rstyle INT, rtrack INT, rtime FLOAT, rmap VARCHAR(128), pointspertier FLOAT, stylemultiplier FLOAT) " ...
"RETURNS FLOAT " ...
"READS SQL DATA " ...
"BEGIN " ...
@ -381,8 +386,8 @@ void RunLongFastQuery(bool &success, const char[] func, const char[] query, any
public void OnClientConnected(int client)
{
gI_Rank[client] = 0;
gF_Points[client] = 0.0;
gA_Rankings[client].iRank = 0;
gA_Rankings[client].fPoints = 0.0;
}
public void OnClientPostAdminCheck(int client)
@ -568,7 +573,7 @@ public Action Command_Rank(int client, int args)
}
}
if(gF_Points[target] == 0.0)
if(gA_Rankings[target].fPoints == 0.0)
{
Shavit_PrintToChat(client, "%T", "Unranked", client, gS_ChatStrings.sVariable2, target, gS_ChatStrings.sText);
@ -576,9 +581,9 @@ public Action Command_Rank(int client, int args)
}
Shavit_PrintToChat(client, "%T", "Rank", client, gS_ChatStrings.sVariable2, target, gS_ChatStrings.sText,
gS_ChatStrings.sVariable, (gI_Rank[target] > gI_RankedPlayers)? gI_RankedPlayers:gI_Rank[target], gS_ChatStrings.sText,
gS_ChatStrings.sVariable, (gA_Rankings[target].iRank > gI_RankedPlayers)? gI_RankedPlayers:gA_Rankings[target].iRank, gS_ChatStrings.sText,
gI_RankedPlayers,
gS_ChatStrings.sVariable, gF_Points[target], gS_ChatStrings.sText);
gS_ChatStrings.sVariable, gA_Rankings[target].fPoints, gS_ChatStrings.sText);
return Plugin_Handled;
}
@ -782,8 +787,8 @@ void UpdateAllPoints()
#endif
char sQuery[128];
FormatEx(sQuery, 128, "UPDATE %susers SET points = GetWeightedPoints(auth);",
gS_MySQLPrefix);
FormatEx(sQuery, 128, "UPDATE %susers SET points = GetWeightedPoints(auth) WHERE auth IN (SELECT DISTINCT auth FROM %splayertimes);",
gS_MySQLPrefix, gS_MySQLPrefix);
gH_SQL.Query(SQL_UpdateAllPoints_Callback, sQuery);
}
@ -800,8 +805,8 @@ public void SQL_UpdateAllPoints_Callback(Database db, DBResultSet results, const
void UpdatePlayerRank(int client, bool first)
{
gI_Rank[client] = 0;
gF_Points[client] = 0.0;
gA_Rankings[client].iRank = 0;
gA_Rankings[client].fPoints = 0.0;
char sAuthID[32];
@ -810,7 +815,7 @@ void UpdatePlayerRank(int client, bool first)
// if there's any issue with this query,
// add "ORDER BY points DESC " before "LIMIT 1"
char sQuery[512];
FormatEx(sQuery, 512, "SELECT p.points, COUNT(*) rank FROM %susers u JOIN (SELECT points FROM %susers WHERE auth = '%s' LIMIT 1) p WHERE u.points >= p.points LIMIT 1;",
FormatEx(sQuery, 512, "SELECT u2.points, COUNT(*) FROM %susers u1 JOIN (SELECT points FROM %susers WHERE auth = '%s') u2 WHERE u1.points >= u2.points;",
gS_MySQLPrefix, gS_MySQLPrefix, sAuthID);
DataPack hPack = new DataPack();
@ -846,13 +851,13 @@ public void SQL_UpdatePlayerRank_Callback(Database db, DBResultSet results, cons
if(results.FetchRow())
{
gF_Points[client] = results.FetchFloat(0);
gI_Rank[client] = (gF_Points[client] > 0.0)? results.FetchInt(1):0;
gA_Rankings[client].fPoints = results.FetchFloat(0);
gA_Rankings[client].iRank = (gA_Rankings[client].fPoints > 0.0)? results.FetchInt(1):0;
Call_StartForward(gH_Forwards_OnRankAssigned);
Call_PushCell(client);
Call_PushCell(gI_Rank[client]);
Call_PushCell(gF_Points[client]);
Call_PushCell(gA_Rankings[client].iRank);
Call_PushCell(gA_Rankings[client].fPoints);
Call_PushCell(bFirst);
Call_Finish();
}
@ -976,12 +981,12 @@ public int Native_GetMapTiers(Handle handler, int numParams)
public int Native_GetPoints(Handle handler, int numParams)
{
return view_as<int>(gF_Points[GetNativeCell(1)]);
return view_as<int>(gA_Rankings[GetNativeCell(1)].fPoints);
}
public int Native_GetRank(Handle handler, int numParams)
{
return gI_Rank[GetNativeCell(1)];
return gA_Rankings[GetNativeCell(1)].iRank;
}
public int Native_GetRankedPlayers(Handle handler, int numParams)

View File

@ -77,6 +77,24 @@ enum
iBotShooting_Attack2 = (1 << 1)
}
// custom cvar settings
char gS_ForcedCvars[][][] =
{
{ "bot_quota", "{expected_bots}" },
{ "tf_bot_quota", "{expected_bots}" },
{ "bot_stop", "1" },
{ "bot_quota_mode", "normal" },
{ "tf_bot_quota_mode", "normal" },
{ "mp_limitteams", "0" },
{ "bot_join_after_player", "0" },
{ "tf_bot_join_after_player", "0" },
{ "bot_chatter", "off" },
{ "bot_flipout", "1" },
{ "bot_zombie", "1" },
{ "mp_autoteambalance", "0" },
{ "bot_controllable", "0" }
};
// game type
EngineVersion gEV_Type = Engine_Unknown;
@ -109,7 +127,6 @@ Handle gH_OnReplayEnd = null;
float gF_Tickrate = 0.0;
char gS_Map[160];
int gI_ExpectedBots = 0;
ConVar bot_quota = null;
centralbot_cache_t gA_CentralCache;
// how do i call this
@ -213,6 +230,29 @@ public void OnPluginStart()
gEV_Type = GetEngineVersion();
gF_Tickrate = (1.0 / GetTickInterval());
FindConVar((gEV_Type != Engine_TF2)? "bot_quota":"tf_bot_quota").Flags &= ~FCVAR_NOTIFY;
FindConVar("bot_stop").Flags &= ~FCVAR_CHEAT;
for(int i = 0; i < sizeof(gS_ForcedCvars); i++)
{
ConVar hCvar = FindConVar(gS_ForcedCvars[i][0]);
if(hCvar != null)
{
if(StrEqual(gS_ForcedCvars[i][1], "{expected_bots}"))
{
UpdateBotQuota(0);
}
else
{
hCvar.SetString(gS_ForcedCvars[i][1]);
}
hCvar.AddChangeHook(OnForcedConVarChanged);
}
}
// late load
for(int i = 1; i <= MaxClients; i++)
{
@ -267,6 +307,42 @@ public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] n
OnMapStart();
}
void UpdateBotQuota(int quota)
{
ConVar hCvar = FindConVar("bot_quota");
if(hCvar == null)
{
hCvar = FindConVar("tf_bot_quota");
}
hCvar.IntValue = gI_ExpectedBots = quota;
}
public void OnForcedConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
char sName[32];
convar.GetName(sName, 32);
for(int i = 0; i < sizeof(gS_ForcedCvars); i++)
{
if(StrEqual(sName, gS_ForcedCvars[i][0]))
{
if(StrEqual(gS_ForcedCvars[i][1], "{expected_bots}"))
{
convar.IntValue = gI_ExpectedBots;
}
else if(!StrEqual(newValue, gS_ForcedCvars[i][1]))
{
convar.SetString(gS_ForcedCvars[i][1]);
}
break;
}
}
}
public void OnAdminMenuCreated(Handle topmenu)
{
if(gH_AdminMenu == null || (topmenu == gH_AdminMenu && gH_TimerCommands != INVALID_TOPMENUOBJECT))
@ -447,7 +523,7 @@ public int Native_ReloadReplay(Handle handler, int numParams)
if(gI_ReplayBotClient[style] == 0)
{
ServerCommand((gEV_Type != Engine_TF2)? "bot_add":"tf_bot_add");
gI_ExpectedBots++;
UpdateBotQuota(gI_ExpectedBots + 1);
}
if(loaded && restart)
@ -670,17 +746,11 @@ public Action Cron(Handle Timer)
{
if(!gCV_Enabled.BoolValue)
{
bot_quota.IntValue = 0;
UpdateBotQuota(0);
return Plugin_Continue;
}
// make sure there are enough bots
else if(bot_quota != null && bot_quota.IntValue != gI_ExpectedBots)
{
bot_quota.IntValue = gI_ExpectedBots;
}
for(int i = 0; i < gI_Styles; i++)
{
for(int j = 0; j < ((gCV_CentralBot.BoolValue)? TRACKS_SIZE:1); j++)
@ -798,27 +868,8 @@ public void OnMapStart()
return;
}
ConVar bot_controllable = FindConVar("bot_controllable");
if(bot_controllable != null)
{
bot_controllable.BoolValue = false;
}
FindConVar((gEV_Type != Engine_TF2)? "bot_quota":"tf_bot_quota").Flags &= ~FCVAR_NOTIFY;
FindConVar("bot_stop").Flags &= ~FCVAR_CHEAT;
FindConVar("bot_stop").BoolValue = true;
FindConVar((gEV_Type != Engine_TF2)? "bot_quota_mode":"tf_bot_quota_mode").SetString("normal");
FindConVar("mp_limitteams").IntValue = 0;
FindConVar((gEV_Type != Engine_TF2)? "bot_join_after_player":"tf_bot_join_after_player").BoolValue = false;
FindConVar("bot_chatter").SetString("off");
FindConVar("bot_flipout").BoolValue = true;
FindConVar("bot_zombie").BoolValue = true;
FindConVar("mp_autoteambalance").BoolValue = false;
ServerCommand((gEV_Type != Engine_TF2)? "bot_kick":"tf_bot_kick all");
gI_ExpectedBots = 0;
UpdateBotQuota(0);
if(!DirExists(gS_ReplayFolder))
{
@ -861,7 +912,7 @@ public void OnMapStart()
if(!gCV_CentralBot.BoolValue)
{
ServerCommand((gEV_Type != Engine_TF2)? "bot_add":"tf_bot_add");
gI_ExpectedBots++;
UpdateBotQuota(gI_ExpectedBots + 1);
if(loaded)
{
@ -876,7 +927,7 @@ public void OnMapStart()
if(gCV_CentralBot.BoolValue)
{
gI_ExpectedBots = 1;
UpdateBotQuota(1);
ServerCommand((gEV_Type != Engine_TF2)? "bot_add":"tf_bot_add");
}
@ -1409,10 +1460,7 @@ public void OnClientDisconnect(int client)
if(!IsFakeClient(client))
{
if(gA_PlayerFrames[client] != null)
{
delete gA_PlayerFrames[client];
}
RequestFrame(DeleteFrames, client);
return;
}
@ -1435,6 +1483,11 @@ public void OnClientDisconnect(int client)
}
}
public void DeleteFrames(int client)
{
delete gA_PlayerFrames[client];
}
public Action Shavit_OnStart(int client)
{
ClearFrames(client);
@ -1627,7 +1680,7 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
return Plugin_Changed;
}
if(++gI_ReplayTick[style] >= gA_FrameCache[style][track].iFrameCount)
if(++gI_ReplayTick[style] >= gA_FrameCache[style][track].iFrameCount - 1)
{
gI_ReplayTick[style] = 0;
gRS_ReplayStatus[style] = gA_CentralCache.iReplayStatus = Replay_End;
@ -1668,6 +1721,7 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
buttons &= ~IN_USE;
}
bool bWalk = false;
MoveType mt = MOVETYPE_NOCLIP;
if(gA_FrameCache[style][track].iReplayVersion >= 0x02)
@ -1684,10 +1738,15 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
MoveType movetype = gA_Frames[style][track].Get(gI_ReplayTick[style], 7);
if(movetype == MOVETYPE_LADDER || (movetype == MOVETYPE_WALK && (iReplayFlags & FL_ONGROUND) > 0))
if(movetype == MOVETYPE_LADDER)
{
mt = movetype;
}
else if(movetype == MOVETYPE_WALK && (iReplayFlags & FL_ONGROUND) > 0)
{
bWalk = true;
}
}
SetEntityMoveType(client, mt);
@ -1701,7 +1760,7 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
(GetVectorLength(vecVelocity) > 50000.0 ||
// bot is on ground.. if the distance between the previous position is much bigger (1.5x) than the expected according
// to the bot's velocity, teleport to avoid sync issues
(mt == MOVETYPE_WALK && GetVectorDistance(vecCurrentPosition, vecPosition) > GetVectorLength(vecVelocity) / gF_Tickrate * 1.5)))
(bWalk && GetVectorDistance(vecCurrentPosition, vecPosition) > GetVectorLength(vecVelocity) / gF_Tickrate * 1.5)))
{
TeleportEntity(client, vecPosition, vecAngles, NULL_VECTOR);
@ -2196,7 +2255,7 @@ public int MenuHandler_Replay(Menu menu, MenuAction action, int param1, int para
return 0;
}
void OpenReplaySubMenu(int client, int track)
void OpenReplaySubMenu(int client, int track, int item = 0)
{
gI_Track[client] = track;
@ -2260,7 +2319,7 @@ void OpenReplaySubMenu(int client, int track)
}
menu.ExitBackButton = true;
menu.Display(client, 60);
menu.DisplayAt(client, item, 60);
}
public int MenuHandler_ReplaySubmenu(Menu menu, MenuAction action, int param1, int param2)
@ -2289,7 +2348,7 @@ public int MenuHandler_ReplaySubmenu(Menu menu, MenuAction action, int param1, i
{
Shavit_PrintToChat(param1, "%T", "CentralReplayPlaying", param1);
OpenReplaySubMenu(param1, gI_Track[param1]);
OpenReplaySubMenu(param1, gI_Track[param1], GetMenuSelectionPosition());
}
else

View File

@ -46,7 +46,7 @@ char gS_MySQLPrefix[32];
// cache
bool gB_AllowStats[MAXPLAYERS+1];
int gI_MapType[MAXPLAYERS+1];
int gBS_Style[MAXPLAYERS+1];
int gI_Style[MAXPLAYERS+1];
int gI_Track[MAXPLAYERS+1];
char gS_TargetAuth[MAXPLAYERS+1][32];
char gS_TargetName[MAXPLAYERS+1][MAX_NAME_LENGTH];
@ -109,6 +109,8 @@ public void OnPluginStart()
// player commands
RegConsoleCmd("sm_profile", Command_Profile, "Show the player's profile. Usage: sm_profile [target]");
RegConsoleCmd("sm_stats", Command_Profile, "Show the player's profile. Usage: sm_profile [target]");
RegConsoleCmd("sm_mapsdone", Command_MapsDoneLeft, "Show maps that the player has finished. Usage: sm_mapsdone [target]");
RegConsoleCmd("sm_mapsleft", Command_MapsDoneLeft, "Show maps that the player has not finished yet. Usage: sm_mapsleft [target]");
// translations
LoadTranslations("common.phrases");
@ -284,16 +286,22 @@ void UpdateWRs(int client)
if(GetClientAuthId(client, AuthId_Steam3, sAuthID, 32))
{
char sQuery[256];
char sQuery[512];
// default style only
if(gCV_MVPRankOnes.IntValue == 2)
{
FormatEx(sQuery, 256, "SELECT COUNT(*) FROM %splayertimes a JOIN (SELECT MIN(time) time FROM %splayertimes WHERE style = 0 %sGROUP by map) b ON a.time = b.time WHERE auth = '%s';", gS_MySQLPrefix, gS_MySQLPrefix, (gCV_MVPRankOnes_Main.BoolValue)? "AND track = 0 ":"", sAuthID);
FormatEx(sQuery, 512,
"SELECT COUNT(*) FROM %splayertimes a JOIN (SELECT MIN(time) time, map FROM %splayertimes WHERE style = 0 %sGROUP by map, track) b ON a.time = b.time AND a.map = b.map AND style = 0 %sWHERE auth = '%s';",
gS_MySQLPrefix, gS_MySQLPrefix, (gCV_MVPRankOnes_Main.BoolValue)? "AND track = 0 ":"", (gCV_MVPRankOnes_Main.BoolValue)? "AND track = 0 ":"", sAuthID);
}
// all styles
else
{
FormatEx(sQuery, 256, "SELECT COUNT(*) FROM %splayertimes a JOIN (SELECT MIN(time) time FROM %splayertimes %sGROUP by map) b ON a.time = b.time WHERE auth = '%s';", gS_MySQLPrefix, gS_MySQLPrefix, (gCV_MVPRankOnes_Main.BoolValue)? "WHERE track = 0 ":"", sAuthID);
FormatEx(sQuery, 512,
"SELECT COUNT(*) FROM %splayertimes a JOIN (SELECT MIN(time) time, map, style FROM %splayertimes %sGROUP by map, style, track) b ON a.time = b.time AND a.map = b.map AND a.style = b.style %sWHERE auth = '%s';",
gS_MySQLPrefix, gS_MySQLPrefix, (gCV_MVPRankOnes_Main.BoolValue)? "WHERE track = 0 ":"", (gCV_MVPRankOnes_Main.BoolValue)? "AND track = 0 ":"", sAuthID);
}
gH_SQL.Query(SQL_GetWRs_Callback, sQuery, GetClientSerial(client));
@ -326,9 +334,125 @@ public void SQL_GetWRs_Callback(Database db, DBResultSet results, const char[] e
gI_WRAmount[client] = iWRs;
}
public Action Command_MapsDoneLeft(int client, int args)
{
if(client == 0)
{
return Plugin_Handled;
}
int target = client;
if(args > 0)
{
char sArgs[64];
GetCmdArgString(sArgs, 64);
target = FindTarget(client, sArgs, true, false);
if(target == -1)
{
return Plugin_Handled;
}
}
GetClientAuthId(target, AuthId_Steam3, gS_TargetAuth[client], 32);
char sCommand[16];
GetCmdArg(0, sCommand, 16);
GetClientName(target, gS_TargetName[client], MAX_NAME_LENGTH);
ReplaceString(gS_TargetName[client], MAX_NAME_LENGTH, "#", "?");
Menu menu = new Menu(MenuHandler_MapsDoneLeft);
if(StrEqual(sCommand, "sm_mapsdone"))
{
gI_MapType[client] = MAPSDONE;
menu.SetTitle("%T\n ", "MapsDoneOnStyle", client, gS_TargetName[client]);
}
else
{
gI_MapType[client] = MAPSLEFT;
menu.SetTitle("%T\n ", "MapsLeftOnStyle", client, gS_TargetName[client]);
}
int[] styles = new int[gI_Styles];
Shavit_GetOrderedStyles(styles, gI_Styles);
for(int i = 0; i < gI_Styles; i++)
{
int iStyle = styles[i];
if(gA_StyleSettings[iStyle].bUnranked || gA_StyleSettings[iStyle].iEnabled == -1)
{
continue;
}
char sInfo[8];
IntToString(iStyle, sInfo, 8);
menu.AddItem(sInfo, gS_StyleStrings[iStyle].sStyleName);
}
menu.Display(client, 30);
return Plugin_Handled;
}
public int MenuHandler_MapsDoneLeft(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
char sInfo[8];
menu.GetItem(param2, sInfo, 8);
gI_Style[param1] = StringToInt(sInfo);
Menu submenu = new Menu(MenuHandler_MapsDoneLeft_Track);
submenu.SetTitle("%T\n ", "SelectTrack", param1);
for(int i = 0; i < TRACKS_SIZE; i++)
{
IntToString(i, sInfo, 8);
char sTrack[32];
GetTrackName(param1, i, sTrack, 32);
submenu.AddItem(sInfo, sTrack);
}
submenu.Display(param1, 30);
}
else if(action == MenuAction_End)
{
delete menu;
}
return 0;
}
public int MenuHandler_MapsDoneLeft_Track(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
char sInfo[8];
menu.GetItem(param2, sInfo, 8);
gI_Track[param1] = StringToInt(sInfo);
ShowMaps(param1);
}
else if(action == MenuAction_End)
{
delete menu;
}
return 0;
}
public Action Command_Profile(int client, int args)
{
if(!IsValidClient(client))
if(client == 0)
{
return Plugin_Handled;
}
@ -367,21 +491,21 @@ Action OpenStatsMenu(int client, const char[] authid)
if(gB_Rankings)
{
FormatEx(sQuery, 2048, "SELECT a.clears, b.maps, c.wrs, d.name, d.country, d.lastlogin, d.points, e.rank FROM " ...
"(SELECT COUNT(*) clears FROM (SELECT id FROM %splayertimes WHERE auth = '%s' AND track = 0 GROUP BY map) s) a " ...
"JOIN (SELECT COUNT(*) maps FROM (SELECT id FROM %smapzones WHERE track = 0 AND type = 0 GROUP BY map) s) b " ...
"JOIN (SELECT COUNT(*) wrs FROM %splayertimes a JOIN (SELECT MIN(time) time FROM %splayertimes WHERE style = 0 AND track = 0 GROUP by map) b ON a.time = b.time WHERE auth = '%s') c " ...
"JOIN (SELECT name, country, lastlogin, FORMAT(points, 2) points FROM %susers WHERE auth = '%s' LIMIT 1) d " ...
"JOIN (SELECT FORMAT(COUNT(*), 0) rank FROM %susers WHERE points >= (SELECT points FROM %susers WHERE auth = '%s' LIMIT 1) ORDER BY points DESC) e " ...
"(SELECT COUNT(*) clears FROM (SELECT map FROM %splayertimes WHERE auth = '%s' AND track = 0 GROUP BY map) s) a " ...
"JOIN (SELECT COUNT(*) maps FROM (SELECT map FROM %smapzones WHERE track = 0 AND type = 0 GROUP BY map) s) b " ...
"JOIN (SELECT COUNT(*) wrs FROM %splayertimes a JOIN (SELECT MIN(time) time, map FROM %splayertimes WHERE style = 0 AND track = 0 GROUP by map, style, track) b ON a.time = b.time AND a.map = b.map AND track = 0 AND style = 0 WHERE auth = '%s') c " ...
"JOIN (SELECT name, country, lastlogin, FORMAT(points, 2) points FROM %susers WHERE auth = '%s') d " ...
"JOIN (SELECT COUNT(*) rank FROM %susers u1 JOIN (SELECT points FROM %susers WHERE auth = '%s') u2 WHERE u1.points >= u2.points) e " ...
"LIMIT 1;", gS_MySQLPrefix, authid, gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix, authid, gS_MySQLPrefix, authid, gS_MySQLPrefix, gS_MySQLPrefix, authid);
}
else
{
FormatEx(sQuery, 2048, "SELECT a.clears, b.maps, c.wrs, d.name, d.country, d.lastlogin FROM " ...
"(SELECT COUNT(*) clears FROM (SELECT id FROM %splayertimes WHERE auth = '%s' AND track = 0 GROUP BY map) s) a " ...
"JOIN (SELECT COUNT(*) maps FROM (SELECT id FROM %smapzones WHERE track = 0 AND type = 0 GROUP BY map) s) b " ...
"JOIN (SELECT COUNT(*) wrs FROM %splayertimes a JOIN (SELECT MIN(time) time FROM %splayertimes WHERE style = 0 AND track = 0 GROUP by map) b ON a.time = b.time WHERE auth = '%s') c " ...
"JOIN (SELECT name, country, lastlogin FROM %susers WHERE auth = '%s' LIMIT 1) d " ...
"(SELECT COUNT(*) clears FROM (SELECT map FROM %splayertimes WHERE auth = '%s' AND track = 0 GROUP BY map) s) a " ...
"JOIN (SELECT COUNT(*) maps FROM (SELECT map FROM %smapzones WHERE track = 0 AND type = 0 GROUP BY map) s) b " ...
"JOIN (SELECT COUNT(*) wrs FROM %splayertimes a JOIN (SELECT MIN(time) time, map FROM %splayertimes WHERE style = 0 AND track = 0 GROUP by map, style, track) b ON a.time = b.time AND a.map = b.map AND track = 0 AND style = 0 WHERE auth = '%s') c " ...
"JOIN (SELECT name, country, lastlogin FROM %susers WHERE auth = '%s') d " ...
"LIMIT 1;", gS_MySQLPrefix, authid, gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix, authid, gS_MySQLPrefix, authid);
}
@ -469,7 +593,7 @@ public void OpenStatsMenuCallback(Database db, DBResultSet results, const char[]
{
int iStyle = styles[i];
if(gA_StyleSettings[iStyle].bUnranked)
if(gA_StyleSettings[iStyle].bUnranked || gA_StyleSettings[iStyle].iEnabled <= 0)
{
continue;
}
@ -505,10 +629,10 @@ public int MenuHandler_ProfileHandler(Menu menu, MenuAction action, int param1,
char sInfo[32];
menu.GetItem(param2, sInfo, 32);
gBS_Style[param1] = StringToInt(sInfo);
gI_Style[param1] = StringToInt(sInfo);
Menu submenu = new Menu(MenuHandler_TypeHandler);
submenu.SetTitle("%T", "MapsMenu", param1, gS_StyleStrings[gBS_Style[param1]].sShortName);
submenu.SetTitle("%T", "MapsMenu", param1, gS_StyleStrings[gI_Style[param1]].sShortName);
for(int j = 0; j < TRACKS_SIZE; j++)
{
@ -596,12 +720,26 @@ void ShowMaps(int client)
if(gI_MapType[client] == MAPSDONE)
{
FormatEx(sQuery, 512, "SELECT a.map, a.time, a.jumps, a.id, COUNT(b.map) + 1 rank, a.points FROM %splayertimes a LEFT JOIN %splayertimes b ON a.time > b.time AND a.map = b.map AND a.style = b.style AND a.track = b.track WHERE a.auth = '%s' AND a.style = %d AND a.track = %d GROUP BY a.map ORDER BY a.%s;", gS_MySQLPrefix, gS_MySQLPrefix, gS_TargetAuth[client], gBS_Style[client], gI_Track[client], (gB_Rankings)? "points DESC":"map");
FormatEx(sQuery, 512,
"SELECT a.map, a.time, a.jumps, a.id, COUNT(b.map) + 1 rank, a.points FROM %splayertimes a LEFT JOIN %splayertimes b ON a.time > b.time AND a.map = b.map AND a.style = b.style AND a.track = b.track WHERE a.auth = '%s' AND a.style = %d AND a.track = %d GROUP BY a.map, a.time, a.jumps, a.id, a.points ORDER BY a.%s;",
gS_MySQLPrefix, gS_MySQLPrefix, gS_TargetAuth[client], gI_Style[client], gI_Track[client], (gB_Rankings)? "points DESC":"map");
}
else
{
FormatEx(sQuery, 512, "SELECT DISTINCT m.map FROM %smapzones m LEFT JOIN %splayertimes r ON r.map = m.map AND r.auth = '%s' AND r.style = %d AND m.track = %d WHERE r.map IS NULL AND m.track = %d ORDER BY m.map;", gS_MySQLPrefix, gS_MySQLPrefix, gS_TargetAuth[client], gBS_Style[client], gI_Track[client], gI_Track[client]);
if(gB_Rankings)
{
FormatEx(sQuery, 512,
"SELECT DISTINCT m.map, t.tier FROM %smapzones m LEFT JOIN %smaptiers t ON m.map = t.map WHERE m.type = 0 AND m.track = %d AND m.map NOT IN (SELECT DISTINCT map FROM %splayertimes WHERE auth = '%s' AND style = %d AND track = %d) ORDER BY m.map;",
gS_MySQLPrefix, gS_MySQLPrefix, gI_Track[client], gS_MySQLPrefix, gS_TargetAuth[client], gI_Style[client], gI_Track[client]);
}
else
{
FormatEx(sQuery, 512,
"SELECT DISTINCT map FROM %smapzones WHERE type = 0 AND track = %d AND map NOT IN (SELECT DISTINCT map FROM %splayertimes WHERE auth = '%s' AND style = %d AND track = %d) ORDER BY map;",
gS_MySQLPrefix, gI_Track[client], gS_MySQLPrefix, gS_TargetAuth[client], gI_Style[client], gI_Track[client]);
}
}
gH_SQL.Query(ShowMapsCallback, sQuery, GetClientSerial(client), DBPrio_High);
@ -632,12 +770,12 @@ public void ShowMapsCallback(Database db, DBResultSet results, const char[] erro
if(gI_MapType[client] == MAPSDONE)
{
menu.SetTitle("%T (%s)", "MapsDoneFor", client, gS_StyleStrings[gBS_Style[client]].sShortName, gS_TargetName[client], rows, sTrack);
menu.SetTitle("%T (%s)", "MapsDoneFor", client, gS_StyleStrings[gI_Style[client]].sShortName, gS_TargetName[client], rows, sTrack);
}
else
{
menu.SetTitle("%T (%s)", "MapsLeftFor", client, gS_StyleStrings[gBS_Style[client]].sShortName, gS_TargetName[client], rows, sTrack);
menu.SetTitle("%T (%s)", "MapsLeftFor", client, gS_StyleStrings[gI_Style[client]].sShortName, gS_TargetName[client], rows, sTrack);
}
while(results.FetchRow())
@ -676,6 +814,19 @@ public void ShowMapsCallback(Database db, DBResultSet results, const char[] erro
else
{
strcopy(sDisplay, 192, sMap);
if(gB_Rankings)
{
int iTier = results.FetchInt(1);
if(results.IsFieldNull(1) || iTier == 0)
{
iTier = 1;
}
Format(sDisplay, 192, "%s (Tier %d)", sMap, iTier);
}
strcopy(sRecordID, 16, "nope");
}

View File

@ -159,7 +159,6 @@ public void OnPluginStart()
RegAdminCmd("sm_deleterecord", Command_Delete, ADMFLAG_RCON, "Opens a record deletion menu interface.");
RegAdminCmd("sm_deleterecords", Command_Delete, ADMFLAG_RCON, "Opens a record deletion menu interface.");
RegAdminCmd("sm_deleteall", Command_DeleteAll, ADMFLAG_RCON, "Deletes all the records for this map.");
RegAdminCmd("sm_deletestylerecords", Command_DeleteStyleRecords, ADMFLAG_RCON, "Deletes all the records for a style.");
// cvars
gCV_RecordsLimit = CreateConVar("shavit_wr_recordlimit", "50", "Limit of records shown in the WR menu.\nAdvised to not set above 1,000 because scrolling through so many pages is useless.\n(And can also cause the command to take long time to run)", 0, true, 1.0);
@ -222,7 +221,6 @@ public void OnAdminMenuReady(Handle topmenu)
gH_AdminMenu.AddItem("sm_deleteall", AdminMenu_DeleteAll, gH_TimerCommands, "sm_deleteall", ADMFLAG_RCON);
gH_AdminMenu.AddItem("sm_delete", AdminMenu_Delete, gH_TimerCommands, "sm_delete", ADMFLAG_RCON);
gH_AdminMenu.AddItem("sm_deletestylerecords", AdminMenu_DeleteStyleRecords, gH_TimerCommands, "sm_deletestylerecords", ADMFLAG_RCON);
}
}
@ -252,19 +250,6 @@ public void AdminMenu_DeleteAll(Handle topmenu, TopMenuAction action, TopMenuOb
}
}
public void AdminMenu_DeleteStyleRecords(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
{
if(action == TopMenuAction_DisplayOption)
{
FormatEx(buffer, maxlength, "%t", "DeleteStyleRecords");
}
else if(action == TopMenuAction_SelectOption)
{
Command_DeleteStyleRecords(param, 0);
}
}
public void OnLibraryAdded(const char[] name)
{
if(StrEqual(name, "shavit-rankings"))
@ -482,18 +467,22 @@ public void SQL_UpdateCache_Callback(Database db, DBResultSet results, const cha
void UpdateWRCache()
{
char sQuery[512];
// thanks Ollie Jones from stackoverflow! http://stackoverflow.com/a/36239523/5335680
// was a bit confused with this one :s
if(gB_MySQL)
{
FormatEx(sQuery, 512, "SELECT p.style, p.id, TRUNCATE(LEAST(s.time, p.time), 3), u.name, p.track FROM %splayertimes p JOIN(SELECT style, MIN(time) time, map, track FROM %splayertimes WHERE map = '%s' GROUP BY style, track ORDER BY date ASC) s ON p.style = s.style AND p.time = s.time AND p.map = s.map JOIN %susers u ON p.auth = u.auth GROUP BY p.style, p.track ORDER BY date ASC;", gS_MySQLPrefix, gS_MySQLPrefix, gS_Map, gS_MySQLPrefix);
FormatEx(sQuery, 512,
"SELECT p1.id, p1.style, p1.track, p1.time, u.name FROM %splayertimes p1 " ...
"JOIN (SELECT style, track, MIN(time) time FROM %splayertimes WHERE map = '%s' GROUP BY style, track) p2 " ...
"JOIN %susers u ON p1.style = p2.style AND p1.track = p2.track AND p1.time = p2.time AND u.auth = p1.auth " ...
"WHERE p1.map = '%s';",
gS_MySQLPrefix, gS_MySQLPrefix, gS_Map, gS_MySQLPrefix, gS_Map);
}
// sorry, LEAST() isn't available for SQLITE!
else
{
FormatEx(sQuery, 512, "SELECT p.style, p.id, s.time, u.name, p.track FROM %splayertimes p JOIN(SELECT style, MIN(time) time, map, track FROM %splayertimes WHERE map = '%s' GROUP BY style, track) s ON p.style = s.style AND p.time = s.time AND p.map = s.map AND s.track = p.track JOIN %susers u ON p.auth = u.auth GROUP BY p.style, p.track;", gS_MySQLPrefix, gS_MySQLPrefix, gS_Map, gS_MySQLPrefix);
FormatEx(sQuery, 512,
"SELECT p.id, p.style, p.track, s.time, u.name FROM %splayertimes p JOIN(SELECT style, MIN(time) time, map, track FROM %splayertimes WHERE map = '%s' GROUP BY style, track) s ON p.style = s.style AND p.time = s.time AND p.map = s.map AND s.track = p.track JOIN %susers u ON p.auth = u.auth GROUP BY p.style, p.track;",
gS_MySQLPrefix, gS_MySQLPrefix, gS_Map, gS_MySQLPrefix);
}
gH_SQL.Query(SQL_UpdateWRCache_Callback, sQuery, 0, DBPrio_Low);
@ -522,19 +511,18 @@ public void SQL_UpdateWRCache_Callback(Database db, DBResultSet results, const c
// setup cache again, dynamically and not hardcoded
while(results.FetchRow())
{
int style = results.FetchInt(0);
int iStyle = results.FetchInt(1);
int iTrack = results.FetchInt(2);
if(style >= gI_Styles || style < 0 || gA_StyleSettings[style].bUnranked)
if(iStyle >= gI_Styles || iStyle < 0 || gA_StyleSettings[iStyle].bUnranked)
{
continue;
}
int track = results.FetchInt(4);
gI_WRRecordID[style][track] = results.FetchInt(1);
gF_WRTime[style][track] = results.FetchFloat(2);
results.FetchString(3, gS_WRName[style][track], MAX_NAME_LENGTH);
ReplaceString(gS_WRName[style][track], MAX_NAME_LENGTH, "#", "?");
gI_WRRecordID[iStyle][iTrack] = results.FetchInt(0);
gF_WRTime[iStyle][iTrack] = results.FetchFloat(3);
results.FetchString(4, gS_WRName[iStyle][iTrack], MAX_NAME_LENGTH);
ReplaceString(gS_WRName[iStyle][iTrack], MAX_NAME_LENGTH, "#", "?");
}
UpdateLeaderboards();
@ -650,7 +638,7 @@ public Action Command_Junk(int client, int args)
char sAuth[32];
GetClientAuthId(client, AuthId_Steam3, sAuth, 32);
FormatEx(sQuery, 256, "INSERT INTO %splayertimes (auth, map, time, jumps, date, style, strafes, sync) VALUES ('%s', '%s', %.03f, %d, %d, 0, %d, %.02f);", gS_MySQLPrefix, sAuth, gS_Map, GetRandomFloat(10.0, 20.0), GetRandomInt(5, 15), GetTime(), GetRandomInt(5, 15), GetRandomFloat(50.0, 99.99));
FormatEx(sQuery, 256, "INSERT INTO %splayertimes (auth, map, time, jumps, date, style, strafes, sync) VALUES ('%s', '%s', %f, %d, %d, 0, %d, %.02f);", gS_MySQLPrefix, sAuth, gS_Map, GetRandomFloat(10.0, 20.0), GetRandomInt(5, 15), GetTime(), GetRandomInt(5, 15), GetRandomFloat(50.0, 99.99));
SQL_LockDatabase(gH_SQL);
SQL_FastQuery(gH_SQL, sQuery);
@ -752,6 +740,11 @@ void DeleteSubmenu(int client)
{
int iStyle = styles[i];
if(gA_StyleSettings[iStyle].iEnabled == -1)
{
continue;
}
char sInfo[8];
IntToString(iStyle, sInfo, 8);
@ -820,6 +813,11 @@ public int MenuHandler_DeleteAll_First(Menu menu, MenuAction action, int param1,
{
int iStyle = styles[i];
if(gA_StyleSettings[iStyle].iEnabled == -1)
{
continue;
}
char sStyle[64];
strcopy(sStyle, 64, gS_StyleStrings[iStyle].sStyleName);
@ -930,178 +928,6 @@ public int MenuHandler_DeleteAll(Menu menu, MenuAction action, int param1, int p
return 0;
}
public Action Command_DeleteStyleRecords(int client, int args)
{
if(!IsValidClient(client))
{
return Plugin_Handled;
}
Menu menu = new Menu(MenuHandler_DeleteStyleRecords);
menu.SetTitle("%T\n ", "DeleteStyleRecordsRecordsMenuTitle", client, gS_Map);
int[] styles = new int[gI_Styles];
Shavit_GetOrderedStyles(styles, gI_Styles);
for(int i = 0; i < gI_Styles; i++)
{
int iStyle = styles[i];
if(gA_StyleSettings[iStyle].bUnranked)
{
continue;
}
char sInfo[8];
IntToString(iStyle, sInfo, 8);
char sDisplay[64];
FormatEx(sDisplay, 64, "%s (%d %T)", gS_StyleStrings[iStyle].sStyleName, gI_RecordAmount[iStyle], "WRRecord", client);
int iTotalAmount = 0;
for(int j = 0; j < TRACKS_SIZE; j++)
{
iTotalAmount += gI_RecordAmount[iStyle][j];
}
menu.AddItem(sInfo, sDisplay, (iTotalAmount > 0)? ITEMDRAW_DEFAULT:ITEMDRAW_DISABLED);
}
if(menu.ItemCount == 0)
{
char sNoRecords[64];
FormatEx(sNoRecords, 64, "%T", "WRMapNoRecords", client);
menu.AddItem("-1", sNoRecords);
}
menu.ExitButton = true;
menu.Display(client, 20);
return Plugin_Handled;
}
public int MenuHandler_DeleteStyleRecords(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
char info[16];
menu.GetItem(param2, info, 16);
int style = StringToInt(info);
int iTotalAmount = 0;
for(int j = 0; j < TRACKS_SIZE; j++)
{
iTotalAmount += gI_RecordAmount[style][j];
}
if(iTotalAmount == 0)
{
return 0;
}
char sMenuItem[128];
Menu submenu = new Menu(MenuHandler_DeleteStyleRecords_Confirm);
submenu.SetTitle("%T\n ", "DeleteConfirmStyle", param1, gS_StyleStrings[style].sStyleName);
for(int i = 1; i <= GetRandomInt(1, 4); i++)
{
FormatEx(sMenuItem, 128, "%T", "MenuResponseNo", param1);
submenu.AddItem("-1", sMenuItem);
}
FormatEx(sMenuItem, 128, "%T", "MenuResponseYesStyle", param1, gS_StyleStrings[style].sStyleName);
IntToString(style, info, 16);
submenu.AddItem(info, sMenuItem);
for(int i = 1; i <= GetRandomInt(1, 3); i++)
{
FormatEx(sMenuItem, 128, "%T", "MenuResponseNo", param1);
submenu.AddItem("-1", sMenuItem);
}
submenu.ExitButton = true;
submenu.Display(param1, 20);
}
else if(action == MenuAction_End)
{
delete menu;
}
return 0;
}
public int MenuHandler_DeleteStyleRecords_Confirm(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
char info[16];
menu.GetItem(param2, info, 16);
int style = StringToInt(info);
if(style == -1)
{
Shavit_PrintToChat(param1, "%T", "DeletionAborted", param1);
return 0;
}
Shavit_LogMessage("%L - deleted all %s style records from map `%s`.", param1, gS_StyleStrings[style].sStyleName, gS_Map);
char sQuery[256];
FormatEx(sQuery, 256, "DELETE FROM %splayertimes WHERE map = '%s' AND style = %d;", gS_MySQLPrefix, gS_Map, style);
DataPack pack = new DataPack();
pack.WriteCell(GetClientSerial(param1));
pack.WriteCell(style);
gH_SQL.Query(DeleteStyleRecords_Callback, sQuery, pack, DBPrio_High);
}
else if(action == MenuAction_End)
{
delete menu;
}
return 0;
}
public void DeleteStyleRecords_Callback(Database db, DBResultSet results, const char[] error, DataPack data)
{
data.Reset();
int serial = data.ReadCell();
int style = data.ReadCell();
delete data;
if(results == null)
{
LogError("Timer (WR DeleteStyleRecords) SQL query failed. Reason: %s", error);
return;
}
UpdateWRCache();
for(int i = 1; i <= MaxClients; i++)
{
OnClientPutInServer(i);
}
int client = GetClientFromSerial(serial);
if(client == 0)
{
return;
}
Shavit_PrintToChat(client, "%T", "DeletedRecordsStyle", client, gS_ChatStrings.sStyle, gS_StyleStrings[style].sStyleName, gS_ChatStrings.sText);
}
public int MenuHandler_Delete(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
@ -1502,7 +1328,7 @@ Action ShowWRStyleMenu(int client, int track)
{
int iStyle = styles[i];
if(gA_StyleSettings[iStyle].bUnranked)
if(gA_StyleSettings[iStyle].bUnranked || gA_StyleSettings[iStyle].iEnabled == -1)
{
continue;
}
@ -1743,10 +1569,10 @@ public Action Command_RecentRecords(int client, int args)
char sQuery[512];
FormatEx(sQuery, 512,
"SELECT a.id, a.map, u.name, a.time, a.jumps, a.style, a.points, a.track FROM %splayertimes a " ...
"JOIN (SELECT MIN(time) time, map, style, track FROM %splayertimes GROUP by map, style, track) b " ...
"JOIN %susers u ON a.time = b.time AND a.auth = u.auth AND a.map = b.map AND a.style = b.style AND a.track = b.track " ...
"ORDER BY date DESC LIMIT 100;", gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix);
"SELECT a.id, a.map, u.name, a.time, a.style, a.track FROM %splayertimes a " ...
"JOIN (SELECT MIN(time) time, map, style, track FROM %splayertimes GROUP by map, style, track ORDER BY date DESC) b " ...
"JOIN %susers u ON a.time = b.time AND a.auth = u.auth AND a.map = b.map AND a.style = b.style AND a.track = b.track " ...
"LIMIT 100;", gS_MySQLPrefix, gS_MySQLPrefix, gS_MySQLPrefix);
gH_SQL.Query(SQL_RR_Callback, sQuery, GetClientSerial(client), DBPrio_Low);
@ -1784,33 +1610,27 @@ public void SQL_RR_Callback(Database db, DBResultSet results, const char[] error
char sName[MAX_NAME_LENGTH];
results.FetchString(2, sName, 10);
if(strlen(sName) == 9)
if(strlen(sName) >= 9)
{
Format(sName, MAX_NAME_LENGTH, "%s...", sName);
}
char sTime[16];
float time = results.FetchFloat(3);
FormatSeconds(time, sTime, 16);
float fTime = results.FetchFloat(3);
FormatSeconds(fTime, sTime, 16);
int jumps = results.FetchInt(4);
int style = results.FetchInt(5);
float fPoints = results.FetchFloat(6);
int iStyle = results.FetchInt(4);
if(iStyle >= gI_Styles || iStyle < 0 || gA_StyleSettings[iStyle].bUnranked)
{
continue;
}
char sTrack[32];
GetTrackName(client, results.FetchInt(7), sTrack, 32);
GetTrackName(client, results.FetchInt(5), sTrack, 32);
char sDisplay[192];
if(gB_Rankings && fPoints > 0.0)
{
FormatEx(sDisplay, 192, "[%s, %c] %s - %s @ %s (%.03f %T)", gS_StyleStrings[style].sShortName, sTrack[0], sMap, sName, sTime, fPoints, "WRPoints", client);
}
else
{
FormatEx(sDisplay, 192, "[%s, %c] %s - %s @ %s (%d %T)", gS_StyleStrings[style].sShortName, sTrack[0], sMap, sName, sTime, jumps, "WRJumps", client);
}
FormatEx(sDisplay, 192, "[%s/%c] %s - %s @ %s", gS_StyleStrings[iStyle].sShortName, sTrack[0], sMap, sName, sTime);
char sInfo[192];
FormatEx(sInfo, 192, "%d;%s", results.FetchInt(0), sMap);
@ -2125,19 +1945,23 @@ void SQL_DBConnect()
gH_SQL.Driver.GetIdentifier(sDriver, 8);
gB_MySQL = StrEqual(sDriver, "mysql", false);
char sQuery[512];
char sQuery[1024];
if(gB_MySQL)
{
FormatEx(sQuery, 512, "CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INT NOT NULL AUTO_INCREMENT, `auth` CHAR(32), `map` CHAR(128), `time` FLOAT, `jumps` INT, `style` TINYINT, `date` CHAR(16), `strafes` INT, `sync` FLOAT, `points` FLOAT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `perfs` FLOAT DEFAULT 0, PRIMARY KEY (`id`), INDEX `map` (`map`, `style`, `track`), INDEX `auth` (`auth`, `date`, `points`), INDEX `time` (`time`)) ENGINE=INNODB;", gS_MySQLPrefix);
FormatEx(sQuery, 1024,
"CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INT NOT NULL AUTO_INCREMENT, `auth` VARCHAR(32), `map` VARCHAR(128), `time` FLOAT, `jumps` INT, `style` TINYINT, `date` VARCHAR(16), `strafes` INT, `sync` FLOAT, `points` FLOAT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `perfs` FLOAT DEFAULT 0, PRIMARY KEY (`id`), INDEX `map` (`map`, `style`, `track`, `time`), INDEX `auth` (`auth`, `date`, `points`), INDEX `time` (`time`), CONSTRAINT `pt_auth` FOREIGN KEY (`auth`) REFERENCES `users` (`auth`) ON UPDATE CASCADE ON DELETE CASCADE) ENGINE=INNODB;",
gS_MySQLPrefix);
}
else
{
FormatEx(sQuery, 512, "CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INTEGER PRIMARY KEY, `auth` CHAR(32), `map` CHAR(128), `time` FLOAT, `jumps` INT, `style` TINYINT, `date` CHAR(16), `strafes` INT, `sync` FLOAT, `points` FLOAT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `perfs` FLOAT DEFAULT 0);", gS_MySQLPrefix);
FormatEx(sQuery, 1024,
"CREATE TABLE IF NOT EXISTS `%splayertimes` (`id` INTEGER PRIMARY KEY, `auth` VARCHAR(32), `map` VARCHAR(128), `time` FLOAT, `jumps` INT, `style` TINYINT, `date` VARCHAR(16), `strafes` INT, `sync` FLOAT, `points` FLOAT NOT NULL DEFAULT 0, `track` TINYINT NOT NULL DEFAULT 0, `perfs` FLOAT DEFAULT 0);",
gS_MySQLPrefix);
}
gH_SQL.Query(SQL_CreateTable_Callback, sQuery, 0, DBPrio_High);
gH_SQL.Query(SQL_CreateTable_Callback, sQuery);
}
public void SQL_CreateTable_Callback(Database db, DBResultSet results, const char[] error, any data)
@ -2159,136 +1983,8 @@ public void SQL_CreateTable_Callback(Database db, DBResultSet results, const cha
gB_Late = false;
}
char sQuery[64];
FormatEx(sQuery, 64, "SELECT strafes FROM %splayertimes LIMIT 1;", gS_MySQLPrefix);
gH_SQL.Query(SQL_TableMigration1_Callback, sQuery);
if(gB_MySQL) // this isn't possible in sqlite
{
FormatEx(sQuery, 64, "ALTER TABLE %splayertimes MODIFY date CHAR(16);", gS_MySQLPrefix);
gH_SQL.Query(SQL_AlterTable2_Callback, sQuery);
}
FormatEx(sQuery, 64, "SELECT points FROM %splayertimes LIMIT 1;", gS_MySQLPrefix);
gH_SQL.Query(SQL_TableMigration3_Callback, sQuery);
FormatEx(sQuery, 64, "SELECT track FROM %splayertimes LIMIT 1;", gS_MySQLPrefix);
gH_SQL.Query(SQL_TableMigration4_Callback, sQuery);
FormatEx(sQuery, 64, "SELECT perfs FROM %splayertimes LIMIT 1;", gS_MySQLPrefix);
gH_SQL.Query(SQL_TableMigration5_Callback, sQuery, 0, DBPrio_Low);
}
public void SQL_TableMigration1_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
char sQuery[256];
if(gB_MySQL)
{
FormatEx(sQuery, 256, "ALTER TABLE `%splayertimes` ADD (`strafes` INT NOT NULL DEFAULT 0, `sync` FLOAT NOT NULL DEFAULT 0);", gS_MySQLPrefix);
gH_SQL.Query(SQL_AlterTable1_Callback, sQuery);
}
else
{
FormatEx(sQuery, 256, "ALTER TABLE `%splayertimes` ADD COLUMN `strafes` INT NOT NULL DEFAULT 0;", gS_MySQLPrefix);
gH_SQL.Query(SQL_AlterTable1_Callback, sQuery);
FormatEx(sQuery, 256, "ALTER TABLE `%splayertimes` ADD COLUMN `sync` FLOAT NOT NULL DEFAULT 0;", gS_MySQLPrefix);
gH_SQL.Query(SQL_AlterTable1_Callback, sQuery);
}
}
}
public void SQL_AlterTable1_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
LogError("Timer (WR module) error! Times' table migration (1) failed. Reason: %s", error);
}
}
public void SQL_AlterTable2_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
LogError("Timer (WR module) error! Times' table migration (2) failed. Reason: %s", error);
}
}
public void SQL_TableMigration3_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
char sQuery[256];
FormatEx(sQuery, 256, "ALTER TABLE `%splayertimes` ADD %s;", gS_MySQLPrefix, (gB_MySQL)? "(`points` FLOAT NOT NULL DEFAULT 0)":"COLUMN `points` FLOAT NOT NULL DEFAULT 0");
gH_SQL.Query(SQL_AlterTable3_Callback, sQuery);
}
}
public void SQL_AlterTable3_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
LogError("Timer (WR module) error! Times' table migration (3) failed. Reason: %s", error);
}
}
public void SQL_TableMigration4_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
char sQuery[256];
FormatEx(sQuery, 256, "ALTER TABLE `%splayertimes` ADD %s;", gS_MySQLPrefix, (gB_MySQL)? "(`track` INT NOT NULL DEFAULT 0)":"COLUMN `track` INT NOT NULL DEFAULT 0");
gH_SQL.Query(SQL_AlterTable4_Callback, sQuery);
}
}
public void SQL_AlterTable4_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
LogError("Timer (WR module) error! Times' table migration (4) failed. Reason: %s", error);
}
}
public void SQL_TableMigration5_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
char sQuery[256];
if(gB_MySQL)
{
FormatEx(sQuery, 256, "ALTER TABLE `%splayertimes` ADD (`perfs` FLOAT DEFAULT 0);", gS_MySQLPrefix);
}
else
{
FormatEx(sQuery, 256, "ALTER TABLE `%splayertimes` ADD COLUMN `perfs` FLOAT DEFAULT 0;", gS_MySQLPrefix);
}
gH_SQL.Query(SQL_AlterTable5_Callback, sQuery);
return;
}
gB_Connected = true;
OnMapStart();
}
public void SQL_AlterTable5_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
LogError("Timer (WR module) error! Times' table migration (5) failed. Reason: %s", error);
return;
}
gB_Connected = true;
OnMapStart();
}
@ -2389,14 +2085,14 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st
return;
}
FormatEx(sQuery, 512, "INSERT INTO %splayertimes (auth, map, time, jumps, date, style, strafes, sync, points, track, perfs) VALUES ('%s', '%s', %.03f, %d, %d, %d, %d, %.2f, 0.0, %d, %.2f);", gS_MySQLPrefix, sAuthID, gS_Map, time, jumps, GetTime(), style, strafes, sync, track, perfs);
FormatEx(sQuery, 512, "INSERT INTO %splayertimes (auth, map, time, jumps, date, style, strafes, sync, points, track, perfs) VALUES ('%s', '%s', %f, %d, %d, %d, %d, %.2f, 0.0, %d, %.2f);", gS_MySQLPrefix, sAuthID, gS_Map, time, jumps, GetTime(), style, strafes, sync, track, perfs);
}
else // update
{
Shavit_PrintToChatAll("%s[%s]%s %T", gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText, "NotFirstCompletion", LANG_SERVER, gS_ChatStrings.sVariable2, client, gS_ChatStrings.sText, gS_ChatStrings.sStyle, gS_StyleStrings[style].sStyleName, gS_ChatStrings.sText, gS_ChatStrings.sVariable2, sTime, gS_ChatStrings.sText, gS_ChatStrings.sVariable, iRank, gS_ChatStrings.sText, jumps, strafes, sSync, gS_ChatStrings.sText, gS_ChatStrings.sWarning, sDifference);
FormatEx(sQuery, 512, "UPDATE %splayertimes SET time = %.03f, jumps = %d, date = %d, strafes = %d, sync = %.02f, points = 0.0, perfs = %.2f WHERE map = '%s' AND auth = '%s' AND style = %d AND track = %d;", gS_MySQLPrefix, time, jumps, GetTime(), strafes, sync, perfs, gS_Map, sAuthID, style, track);
FormatEx(sQuery, 512, "UPDATE %splayertimes SET time = %f, jumps = %d, date = %d, strafes = %d, sync = %.02f, points = 0.0, perfs = %.2f WHERE map = '%s' AND auth = '%s' AND style = %d AND track = %d;", gS_MySQLPrefix, time, jumps, GetTime(), strafes, sync, perfs, gS_Map, sAuthID, style, track);
}
gH_SQL.Query(SQL_OnFinish_Callback, sQuery, GetClientSerial(client), DBPrio_High);

View File

@ -810,7 +810,7 @@ void UnloadZones(int zone)
{
if(zone == Zone_CustomSpawn)
{
ClearCustomSpawn();
ClearCustomSpawn(-1);
}
else
@ -824,7 +824,7 @@ void UnloadZones(int zone)
}
}
ClearCustomSpawn();
ClearCustomSpawn(-1);
if(zone == 0)
{
@ -980,28 +980,71 @@ public Action Command_AddSpawn(int client, int args)
return Plugin_Handled;
}
if(!IsPlayerAlive(client))
{
Shavit_PrintToChat(client, "%T", "ZoneDead", client);
return DisplayCustomSpawnMenu(client);
}
return Plugin_Handled;
Action DisplayCustomSpawnMenu(int client)
{
Menu menu = new Menu(MenuHandler_AddCustomSpawn);
menu.SetTitle("%T\n ", "ZoneCustomSpawnMenuTitle", client);
for(int i = 0; i < TRACKS_SIZE; i++)
{
char sInfo[8];
IntToString(i, sInfo, 8);
char sTrack[32];
GetTrackName(client, i, sTrack, 32);
menu.AddItem(sInfo, sTrack);
}
if(!EmptyVector(gF_CustomSpawn[Track_Main]))
{
Shavit_PrintToChat(client, "%T", "ZoneCustomSpawnExists", client);
return Plugin_Handled;
}
gI_ZoneType[client] = Zone_CustomSpawn;
GetClientAbsOrigin(client, gV_Point1[client]);
InsertZone(client);
menu.ExitButton = true;
menu.Display(client, 60);
return Plugin_Handled;
}
public int MenuHandler_AddCustomSpawn(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
if(!IsPlayerAlive(param1))
{
Shavit_PrintToChat(param1, "%T", "ZoneDead", param1);
return 0;
}
char sInfo[8];
menu.GetItem(param2, sInfo, 8);
int iTrack = StringToInt(sInfo);
if(!EmptyVector(gF_CustomSpawn[iTrack]))
{
char sTrack[32];
GetTrackName(param1, iTrack, sTrack, 32);
Shavit_PrintToChat(param1, "%T", "ZoneCustomSpawnExists", param1, gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText);
return 0;
}
gI_ZoneType[param1] = Zone_CustomSpawn;
gI_ZoneTrack[param1] = iTrack;
GetClientAbsOrigin(param1, gV_Point1[param1]);
InsertZone(param1);
}
else if(action == MenuAction_End)
{
delete menu;
}
return 0;
}
public Action Command_DelSpawn(int client, int args)
{
if(!IsValidClient(client))
@ -1009,16 +1052,68 @@ public Action Command_DelSpawn(int client, int args)
return Plugin_Handled;
}
Shavit_LogMessage("%L - deleted custom spawn from map `%s`.", client, gS_Map);
return DisplayCustomSpawnDeleteMenu(client);
}
char sQuery[256];
FormatEx(sQuery, 256, "DELETE FROM %smapzones WHERE type = '%d' AND map = '%s';", gS_MySQLPrefix, Zone_CustomSpawn, gS_Map);
Action DisplayCustomSpawnDeleteMenu(int client)
{
Menu menu = new Menu(MenuHandler_DeleteCustomSpawn);
menu.SetTitle("%T\n ", "ZoneCustomSpawnMenuDeleteTitle", client);
gH_SQL.Query(SQL_DeleteCustom_Spawn_Callback, sQuery, GetClientSerial(client));
for(int i = 0; i < TRACKS_SIZE; i++)
{
char sInfo[8];
IntToString(i, sInfo, 8);
char sTrack[32];
GetTrackName(client, i, sTrack, 32);
menu.AddItem(sInfo, sTrack, (EmptyVector(gF_CustomSpawn[i]))? ITEMDRAW_DISABLED:ITEMDRAW_DEFAULT);
}
menu.ExitButton = true;
menu.Display(client, 60);
return Plugin_Handled;
}
public int MenuHandler_DeleteCustomSpawn(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
char sInfo[8];
menu.GetItem(param2, sInfo, 8);
int iTrack = StringToInt(sInfo);
if(EmptyVector(gF_CustomSpawn[iTrack]))
{
char sTrack[32];
GetTrackName(param1, iTrack, sTrack, 32);
Shavit_PrintToChat(param1, "%T", "ZoneCustomSpawnMissing", param1, gS_ChatStrings.sVariable, sTrack, gS_ChatStrings.sText);
return 0;
}
gI_ZoneTrack[param1] = iTrack;
Shavit_LogMessage("%L - deleted custom spawn from map `%s`.", param1, gS_Map);
char sQuery[256];
FormatEx(sQuery, 256,
"DELETE FROM %smapzones WHERE type = '%d' AND map = '%s' AND track = %d;",
gS_MySQLPrefix, Zone_CustomSpawn, gS_Map, iTrack);
gH_SQL.Query(SQL_DeleteCustom_Spawn_Callback, sQuery, GetClientSerial(param1));
}
else if(action == MenuAction_End)
{
delete menu;
}
return 0;
}
public void SQL_DeleteCustom_Spawn_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
@ -1035,14 +1130,23 @@ public void SQL_DeleteCustom_Spawn_Callback(Database db, DBResultSet results, co
return;
}
ClearCustomSpawn();
ClearCustomSpawn(gI_ZoneTrack[client]);
Shavit_PrintToChat(client, "%T", "ZoneCustomSpawnDelete", client);
}
void ClearCustomSpawn()
void ClearCustomSpawn(int track)
{
gF_CustomSpawn[Track_Main] = NULL_VECTOR;
if(track != -1)
{
gF_CustomSpawn[track] = NULL_VECTOR;
return;
}
for(int i = 0; i < TRACKS_SIZE; i++)
{
gF_CustomSpawn[i] = NULL_VECTOR;
}
}
void ClearPrebuiltZones()
@ -1091,20 +1195,18 @@ public Action Command_Zones(int client, int args)
Reset(client);
Menu menu = new Menu(Select_Type_MenuHandler);
menu.SetTitle("%T", "ZoneMenuTitle", client);
Menu menu = new Menu(MenuHandler_SelectZoneTrack);
menu.SetTitle("%T", "ZoneMenuTrack", client);
for(int i = 0; i < sizeof(gS_ZoneNames); i++)
for(int i = 0; i < TRACKS_SIZE; i++)
{
if(i == Zone_CustomSpawn)
{
continue;
}
char sInfo[8];
IntToString(i, sInfo, 8);
menu.AddItem(sInfo, gS_ZoneNames[i]);
char sDisplay[16];
GetTrackName(client, i, sDisplay, 16);
menu.AddItem(sInfo, sDisplay);
}
menu.ExitButton = true;
@ -1113,9 +1215,46 @@ public Action Command_Zones(int client, int args)
return Plugin_Handled;
}
public int MenuHandler_SelectZoneTrack(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
char sInfo[8];
menu.GetItem(param2, sInfo, 8);
gI_ZoneTrack[param1] = StringToInt(sInfo);
char sTrack[16];
GetTrackName(param1, gI_ZoneTrack[param1], sTrack, 16);
Menu submenu = new Menu(MenuHandler_SelectZoneType);
submenu.SetTitle("%T", "ZoneMenuTitle", param1, sTrack);
for(int i = 0; i < sizeof(gS_ZoneNames); i++)
{
if(i == Zone_CustomSpawn)
{
continue;
}
IntToString(i, sInfo, 8);
submenu.AddItem(sInfo, gS_ZoneNames[i]);
}
submenu.ExitButton = true;
submenu.Display(param1, 20);
}
else if(action == MenuAction_End)
{
delete menu;
}
return 0;
}
Action OpenEditMenu(int client)
{
Menu menu = new Menu(ZoneEdit_MenuHandler);
Menu menu = new Menu(MenuHandler_ZoneEdit);
menu.SetTitle("%T\n ", "ZoneEditTitle", client);
char sDisplay[64];
@ -1157,7 +1296,7 @@ Action OpenEditMenu(int client)
return Plugin_Handled;
}
public int ZoneEdit_MenuHandler(Menu menu, MenuAction action, int param1, int param2)
public int MenuHandler_ZoneEdit(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
@ -1220,7 +1359,7 @@ public Action Command_DeleteZone(int client, int args)
Action OpenDeleteMenu(int client)
{
Menu menu = new Menu(DeleteZone_MenuHandler);
Menu menu = new Menu(MenuHandler_DeleteZone);
menu.SetTitle("%T\n ", "ZoneMenuDeleteTitle", client);
char sDisplay[64];
@ -1261,7 +1400,7 @@ Action OpenDeleteMenu(int client)
return Plugin_Handled;
}
public int DeleteZone_MenuHandler(Menu menu, MenuAction action, int param1, int param2)
public int MenuHandler_DeleteZone(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
@ -1339,7 +1478,7 @@ public Action Command_DeleteAllZones(int client, int args)
return Plugin_Handled;
}
Menu menu = new Menu(DeleteAllZones_MenuHandler);
Menu menu = new Menu(MenuHandler_DeleteAllZones);
menu.SetTitle("%T", "ZoneMenuDeleteALLTitle", client);
char sMenuItem[64];
@ -1365,7 +1504,7 @@ public Action Command_DeleteAllZones(int client, int args)
return Plugin_Handled;
}
public int DeleteAllZones_MenuHandler(Menu menu, MenuAction action, int param1, int param2)
public int MenuHandler_DeleteAllZones(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
@ -1414,7 +1553,7 @@ public void SQL_DeleteAllZones_Callback(Database db, DBResultSet results, const
Shavit_PrintToChat(client, "%T", "ZoneDeleteAllSuccessful", client);
}
public int Select_Type_MenuHandler(Menu menu, MenuAction action, int param1, int param2)
public int MenuHandler_SelectZoneType(Menu menu, MenuAction action, int param1, int param2)
{
if(action == MenuAction_Select)
{
@ -1437,7 +1576,7 @@ public int Select_Type_MenuHandler(Menu menu, MenuAction action, int param1, int
void Reset(int client)
{
gI_ZoneTrack[client] = Track_Main;
gF_Modifier[client] = 10.0;
gF_Modifier[client] = 16.0;
gI_MapStep[client] = 0;
gI_GridSnap[client] = 16;
gB_SnapToWall[client] = false;
@ -1486,7 +1625,10 @@ void ShowPanel(int client, int step)
pPanel.DrawItem(sPanelItem);
char sDisplay[64];
FormatEx(sDisplay, 64, "%T", "GridSnap", client, gI_GridSnap[client]);
FormatEx(sDisplay, 64, "%T", "GridSnapPlus", client, gI_GridSnap[client]);
pPanel.DrawItem(sDisplay);
FormatEx(sDisplay, 64, "%T", "GridSnapMinus", client);
pPanel.DrawItem(sDisplay);
FormatEx(sDisplay, 64, "%T", "WallSnap", client, (gB_SnapToWall[client])? "ZoneSetYes":"ZoneSetNo", client);
@ -1524,6 +1666,16 @@ public int ZoneCreation_Handler(Menu menu, MenuAction action, int param1, int pa
}
case 3:
{
gI_GridSnap[param1] /= 2;
if(gI_GridSnap[param1] < 1)
{
gI_GridSnap[param1] = 64;
}
}
case 4:
{
gB_SnapToWall[param1] = !gB_SnapToWall[param1];
@ -1538,7 +1690,7 @@ public int ZoneCreation_Handler(Menu menu, MenuAction action, int param1, int pa
}
}
case 4:
case 5:
{
gB_CursorTracing[param1] = !gB_CursorTracing[param1];
@ -1759,12 +1911,6 @@ public int CreateZoneConfirm_Handler(Menu menu, MenuAction action, int param1, i
UpdateTeleportZone(param1);
CreateEditMenu(param1);
}
else if(StrEqual(info, "track"))
{
gI_ZoneTrack[param1] = CycleTracks(gI_ZoneTrack[param1]);
CreateEditMenu(param1);
}
}
else if(action == MenuAction_End)
@ -1775,16 +1921,6 @@ public int CreateZoneConfirm_Handler(Menu menu, MenuAction action, int param1, i
return 0;
}
int CycleTracks(int track)
{
if(++track >= TRACKS_SIZE)
{
return 0;
}
return track;
}
void GetTrackName(int client, int track, char[] output, int size)
{
if(track < 0 || track >= TRACKS_SIZE)
@ -1864,10 +2000,9 @@ void CreateEditMenu(int client)
FormatEx(sMenuItem, 64, "%T", "ZoneSetNo", client);
menu.AddItem("no", sMenuItem);
FormatEx(sMenuItem, 64, "%T", "ZoneSetAdjust", client);
menu.AddItem("adjust", sMenuItem);
FormatEx(sMenuItem, 64, "%T", "ZoneChangeTrack", client);
menu.AddItem("track", sMenuItem);
menu.ExitButton = true;
menu.Display(client, 600);
@ -1963,14 +2098,17 @@ void InsertZone(int client)
{
Shavit_LogMessage("%L - added custom spawn {%.2f, %.2f, %.2f} to map `%s`.", client, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gS_Map);
FormatEx(sQuery, 512, "INSERT INTO %smapzones (map, type, destination_x, destination_y, destination_z) VALUES ('%s', %d, '%.03f', '%.03f', '%.03f');", gS_MySQLPrefix, gS_Map, Zone_CustomSpawn, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2]);
FormatEx(sQuery, 512,
"INSERT INTO %smapzones (map, type, destination_x, destination_y, destination_z, track) VALUES ('%s', %d, '%.03f', '%.03f', '%.03f', %d);",
gS_MySQLPrefix, gS_Map, Zone_CustomSpawn, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gI_ZoneTrack[client]);
}
else if(insert) // insert
{
Shavit_LogMessage("%L - added %s to map `%s`.", client, gS_ZoneNames[type], gS_Map);
FormatEx(sQuery, 512, "INSERT INTO %smapzones (map, type, corner1_x, corner1_y, corner1_z, corner2_x, corner2_y, corner2_z, destination_x, destination_y, destination_z, track) VALUES ('%s', %d, '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', %d);",
FormatEx(sQuery, 512,
"INSERT INTO %smapzones (map, type, corner1_x, corner1_y, corner1_z, corner2_x, corner2_y, corner2_z, destination_x, destination_y, destination_z, track) VALUES ('%s', %d, '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', '%.03f', %d);",
gS_MySQLPrefix, gS_Map, type, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gV_Point2[client][0], gV_Point2[client][1], gV_Point2[client][2], gV_Teleport[client][0], gV_Teleport[client][1], gV_Teleport[client][2], gI_ZoneTrack[client]);
}
@ -1989,7 +2127,8 @@ void InsertZone(int client)
}
}
FormatEx(sQuery, 512, "UPDATE %smapzones SET corner1_x = '%.03f', corner1_y = '%.03f', corner1_z = '%.03f', corner2_x = '%.03f', corner2_y = '%.03f', corner2_z = '%.03f', destination_x = '%.03f', destination_y = '%.03f', destination_z = '%.03f', track = %d WHERE %s = %d;",
FormatEx(sQuery, 512,
"UPDATE %smapzones SET corner1_x = '%.03f', corner1_y = '%.03f', corner1_z = '%.03f', corner2_x = '%.03f', corner2_y = '%.03f', corner2_z = '%.03f', destination_x = '%.03f', destination_y = '%.03f', destination_z = '%.03f', track = %d WHERE %s = %d;",
gS_MySQLPrefix, gV_Point1[client][0], gV_Point1[client][1], gV_Point1[client][2], gV_Point2[client][0], gV_Point2[client][1], gV_Point2[client][2], gV_Teleport[client][0], gV_Teleport[client][1], gV_Teleport[client][2], gI_ZoneTrack[client], (gB_MySQL)? "id":"rowid", gI_ZoneDatabaseID[client]);
}
@ -2312,7 +2451,7 @@ void SQL_DBConnect()
gB_MySQL = StrEqual(sDriver, "mysql", false);
char sQuery[1024];
FormatEx(sQuery, 1024, "CREATE TABLE IF NOT EXISTS `%smapzones` (`id` INT AUTO_INCREMENT, `map` CHAR(128), `type` INT, `corner1_x` FLOAT, `corner1_y` FLOAT, `corner1_z` FLOAT, `corner2_x` FLOAT, `corner2_y` FLOAT, `corner2_z` FLOAT, `destination_x` FLOAT NOT NULL DEFAULT 0, `destination_y` FLOAT NOT NULL DEFAULT 0, `destination_z` FLOAT NOT NULL DEFAULT 0, `track` INT NOT NULL DEFAULT 0, PRIMARY KEY (`id`))%s;", gS_MySQLPrefix, (gB_MySQL)? " ENGINE=INNODB":"");
FormatEx(sQuery, 1024, "CREATE TABLE IF NOT EXISTS `%smapzones` (`id` INT AUTO_INCREMENT, `map` VARCHAR(128), `type` INT, `corner1_x` FLOAT, `corner1_y` FLOAT, `corner1_z` FLOAT, `corner2_x` FLOAT, `corner2_y` FLOAT, `corner2_z` FLOAT, `destination_x` FLOAT NOT NULL DEFAULT 0, `destination_y` FLOAT NOT NULL DEFAULT 0, `destination_z` FLOAT NOT NULL DEFAULT 0, `track` INT NOT NULL DEFAULT 0, PRIMARY KEY (`id`))%s;", gS_MySQLPrefix, (gB_MySQL)? " ENGINE=INNODB":"");
gH_SQL.Query(SQL_CreateTable_Callback, sQuery);
}
@ -2327,86 +2466,8 @@ public void SQL_CreateTable_Callback(Database db, DBResultSet results, const cha
return;
}
char sQuery[64];
FormatEx(sQuery, 64, "SELECT destination_x FROM %smapzones LIMIT 1;", gS_MySQLPrefix);
gH_SQL.Query(SQL_TableMigration1_Callback, sQuery);
FormatEx(sQuery, 64, "SELECT track FROM %smapzones LIMIT 1;", gS_MySQLPrefix);
gH_SQL.Query(SQL_TableMigration2_Callback, sQuery, 0, DBPrio_Low);
}
public void SQL_TableMigration1_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
char sQuery[256];
if(gB_MySQL)
{
FormatEx(sQuery, 256, "ALTER TABLE `%smapzones` ADD (`destination_x` FLOAT NOT NULL DEFAULT 0, `destination_y` FLOAT NOT NULL DEFAULT 0, `destination_z` FLOAT NOT NULL DEFAULT 0);", gS_MySQLPrefix);
gH_SQL.Query(SQL_AlterTable1_Callback, sQuery);
}
else
{
char sAxis[4];
strcopy(sAxis, 4, "xyz");
for(int i = 0; i < 3; i++)
{
FormatEx(sQuery, 256, "ALTER TABLE `%smapzones` ADD COLUMN `destination_%c` FLOAT NOT NULL DEFAULT 0;", gS_MySQLPrefix, sAxis[i]);
gH_SQL.Query(SQL_AlterTable1_Callback, sQuery);
}
}
}
}
public void SQL_AlterTable1_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
LogError("Timer (zones module) error! Map zones' table migration (1) failed. Reason: %s", error);
return;
}
}
public void SQL_TableMigration2_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
char sQuery[256];
if(gB_MySQL)
{
FormatEx(sQuery, 256, "ALTER TABLE `%smapzones` ADD (`track` INT NOT NULL DEFAULT 0);", gS_MySQLPrefix);
}
else
{
FormatEx(sQuery, 256, "ALTER TABLE `%smapzones` ADD COLUMN `track` INTEGER NOT NULL DEFAULT 0;", gS_MySQLPrefix);
}
gH_SQL.Query(SQL_AlterTable2_Callback, sQuery);
return;
}
gB_Connected = true;
OnMapStart();
}
public void SQL_AlterTable2_Callback(Database db, DBResultSet results, const char[] error, any data)
{
if(results == null)
{
LogError("Timer (zones module) error! Map zones' table migration (2) failed. Reason: %s", error);
return;
}
gB_Connected = true;
OnMapStart();
}

View File

@ -79,6 +79,10 @@
{
"en" "[PRACTICE]"
}
"HudNoSpeedLimit"
{
"en" "No Speed Limit"
}
// ---------- Menus ---------- //
"HUDMenuTitle"
{

View File

@ -11,6 +11,14 @@
"#format" "{1:s},{2:s},{3:s},{4:s}"
"en" "You have to be {1}alive{2} or {3}spectate a player{4} to use this command."
}
"CommandSaveCPKZInvalid"
{
"en" "You cannot perform this when airborne or while watching another player."
}
"CommandTeleCPInvalid"
{
"en" "You may not teleport to this checkpoint due to setting mismatch."
}
"CommandNoPause"
{
"#format" "{1:s},{2:s}"

View File

@ -7,6 +7,10 @@
"en" "{1}ERROR: {2}Could not open the stats menu."
}
// ---------- Map Completions ---------- //
"SelectTrack"
{
"en" "Select timer track:"
}
"MapsDone"
{
"en" "Maps done"
@ -16,6 +20,11 @@
"#format" "{1:s},{2:s},{3:d}"
"en" "[{1}] Maps done for {2}: ({3})"
}
"MapsDoneOnStyle"
{
"#format" "{1:s}"
"en" "Maps done for {1} on style:"
}
"MapsLeft"
{
"en" "Maps left"
@ -25,6 +34,11 @@
"#format" "{1:s},{2:s},{3:d}"
"en" "[{1}] Maps left for {2}: ({3})"
}
"MapsLeftOnStyle"
{
"#format" "{1:s}"
"en" "Maps left for {1} on style:"
}
"MapsMenu"
{
"#format" "{1:s}"

View File

@ -80,10 +80,6 @@
{
"en" "YES!!! DELETE ALL THE RECORDS!!! THIS ACTION IS IRREVERSIBLE!"
}
"DeleteStyleRecords"
{
"en" "Delete ALL map records for a style"
}
"DeleteStyleRecordsRecordsMenuTitle"
{
"#format" "{1:s}"

View File

@ -31,9 +31,23 @@
"#format" "{1:s},{2:s}"
"en" "You {1}cannot{2} setup mapzones when you're dead."
}
"ZoneCustomSpawnMenuTitle"
{
"en" "Add custom spawn for track:"
}
"ZoneCustomSpawnMenuDeleteTitle"
{
"en" "Delete custom spawn for track:"
}
"ZoneCustomSpawnExists"
{
"en" "Custom Spawn already exists. Please delete it before placing a new one."
"#format" "{1:s},{2:s},{3:s}"
"en" "Custom Spawn for track {1}{2}{3} already exists. Please delete it before placing a new one."
}
"ZoneCustomSpawnMissing"
{
"#format" "{1:s},{2:s},{3:s}"
"en" "Custom Spawn for track {1}{2}{3} is missing."
}
"ZoneCustomSpawnDelete"
{
@ -48,10 +62,14 @@
{
"en" "Abort zone creation"
}
"GridSnap"
"GridSnapPlus"
{
"#format" "{1:d}"
"en" "Grid snap: x{1}"
"en" "Grid snap + (x{1})"
}
"GridSnapMinus"
{
"en" "Grid snap -"
}
"WallSnap"
{
@ -125,9 +143,14 @@
{
"en" "No zones found."
}
"ZoneMenuTrack"
{
"en" "Select a track:"
}
"ZoneMenuTitle"
{
"en" "Select a zone type:"
"#format" "{1:s}"
"en" "Select a zone type: ({1})"
}
"ZoneMenuYes"
{
@ -195,10 +218,6 @@
"#format" "{1:s}"
"en" "Zone track: {1}"
}
"ZoneChangeTrack"
{
"en" "Change zone track"
}
"ZoneEdit"
{
"en" "Edit a map zone"