Merge pull request #629 from shavitush/very_good_yes

v2.1.0
This commit is contained in:
shavit 2018-05-02 12:38:14 +03:00 committed by GitHub
commit 3e558558b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 649 additions and 171 deletions

View File

@ -36,7 +36,7 @@ Some features are: Per-player settings (!hud), truevel and gradient-like display
#### shavit-misc
This plugin handles miscellaneous things used in bunnyhop servers.
Such as: Team handling (respawning/spectating too), spectators list (!specs), smart player hiding that works for spectating too, teleportation to other players, weapon commands (!knife/!usp/!glock) and ammo management, segmented checkpoints, noclipping (can be set to work for VIPs/admins only), drop-all, godmode, prespeed blocking, prespeed limitation, chat tidying, radar hiding, weapon drop cleaning, player collision removal, auto-respawning, spawn points generator, radio removal, scoreboard manipulation, model opacity changes, fixed runspeed, automatic and configurable chat advertisements, player ragdoll removal and WR messages.
Such as: Segmented runs, team handling (respawning/spectating too), spectators list (!specs), smart player hiding that works for spectating too, teleportation to other players, weapon commands (!knife/!usp/!glock) and ammo management, segmented checkpoints, noclipping (can be set to work for VIPs/admins only), drop-all, godmode, prespeed blocking, prespeed limitation, chat tidying, radar hiding, weapon drop cleaning, player collision removal, auto-respawning, spawn points generator, radio removal, scoreboard manipulation, model opacity changes, fixed runspeed, automatic and configurable chat advertisements, player ragdoll removal and WR messages.
#### shavit-rankings
Enables !rank, !top and introduces map tiers (!settier).

View File

@ -11,7 +11,7 @@
This is (nearly) an all-in-one server plugin for Counter-Strike: Source, Counter-Strike: Global Offensive and Team Fortress 2 that adds a timer system and many other utilities, so you can install it and have a proper bunnyhop server running.
Including a records system, map zones (start/end marks etc), bonuses, HUD with useful information, chat processor, miscellaneous such as weapon commands/spawn point generator, bots that replay the best records of the map, sounds, statistics, a fair & competitive rankings system and more!
Including a records system, map zones (start/end marks etc), bonuses, HUD with useful information, chat processor, miscellaneous such as weapon commands/spawn point generator, bots that replay the best records of the map, sounds, statistics, segmented running, a fair & competitive rankings system and more!
[Mapzones' setup demonstration](https://youtu.be/OXFMGm40F6c)

View File

@ -58,7 +58,7 @@
// Special flags
"special" "0" // For third-party modules. The core plugins will not need this setting.
"specialstring" "" // For third-party modules. The core plugins will not need this setting.
"specialstring" "" // For modularity. Separated with semicolon. Built-in flags: "segments".
"permission" "" // Permission required. Syntax: "flag;override". For example "p;style_tas" to require the 'p' flag or the "style_tas" override.
}
@ -187,6 +187,18 @@
}
"7"
{
"name" "Segmented"
"shortname" "SEG"
"htmlcolor" "FFFFFF"
"command" "sr; seg; segment; segmented"
"clantag" "SEG"
"rankingmultiplier" "0.0"
"specialstring" "segments"
}
"8"
{
"name" "Low Gravity"
"shortname" "LG"
@ -199,7 +211,7 @@
"unranked" "1"
}
"8"
"9"
{
"name" "Slow Motion"
"shortname" "SLOW"

View File

@ -23,7 +23,7 @@
#endif
#define _shavit_included
#define SHAVIT_VERSION "2.0.3"
#define SHAVIT_VERSION "2.1.0"
#define STYLE_LIMIT 256
#define MAX_ZONES 64
@ -145,6 +145,8 @@ enum
fServerTime,
iSHSWCombination,
iTimerTrack,
iMeasuredJumps,
iPerfectJumps,
TIMERSNAPSHOT_SIZE
};
@ -386,9 +388,10 @@ forward Action Shavit_OnFinishPre(int client, any snapshot[TIMERSNAPSHOT_SIZE]);
* @param sync Sync percentage (0.0 to 100.0) or -1.0 when not measured.
* @param track Timer track.
* @param oldtime The player's best time on the map before this finish.
* @param perfs Perfect jump percentage (0.0 to 100.0) or 100.0 when not measured.
* @noreturn
*/
forward void Shavit_OnFinish(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime);
forward void Shavit_OnFinish(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs);
/**
* Like Shavit_OnFinish, but after the insertion query was called.
@ -403,9 +406,11 @@ forward void Shavit_OnFinish(int client, int style, float time, int jumps, int s
* @param rank Rank on map.
* @param overwrite 1 - brand new record. 2 - update.
* @param track Timer track.
* @param oldtime The player's best time on the map before this finish.
* @param perfs Perfect jump percentage (0.0 to 100.0) or 100.0 when not measured.
* @noreturn
*/
forward void Shavit_OnFinish_Post(int client, int style, float time, int jumps, int strafes, float sync, int rank, int overwrite, int track);
forward void Shavit_OnFinish_Post(int client, int style, float time, int jumps, int strafes, float sync, int rank, int overwrite, int track, float oldtime, float perfs);
/**
* Called when there's a new WR on the map.
@ -418,9 +423,11 @@ forward void Shavit_OnFinish_Post(int client, int style, float time, int jumps,
* @param sync Sync percentage (0.0 to 100.0) or -1.0 when not measured.
* @param track Timer track.
* @param oldwr Time of the old WR. 0.0 if there's none.
* @param oldtime The player's best time on the map before this finish.
* @param perfs Perfect jump percentage (0.0 to 100.0) or 100.0 when not measured.
* @noreturn
*/
forward void Shavit_OnWorldRecord(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldwr);
forward void Shavit_OnWorldRecord(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldwr, float oldtime, float perfs);
/**
* Called when an admin deletes a WR.
@ -520,9 +527,11 @@ forward void Shavit_OnLeaveZone(int client, int type, int track, int id, int ent
* @param strafes Amount of strafes.
* @param sync Sync percentage (0.0 to 100.0) or -1.0 when not measured.
* @param track Timer track.
* @param oldtime The player's best time on the map before this finish.
* @param perfs Perfect jump percentage (0.0 to 100.0) or 100.0 when not measured.
* @noreturn
*/
forward void Shavit_OnWorstRecord(int client, int style, float time, int jumps, int strafes, float sync, int track);
forward void Shavit_OnWorstRecord(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs);
/**
* Gets called when a map's tier is assigned.
@ -661,6 +670,15 @@ native TimerStatus Shavit_GetTimerStatus(int client);
*/
native int Shavit_GetStrafeCount(int client);
/**
* Retrieve the perfect jumps percentage for the player.
* Will return 100.0 if no jumps were measured.
*
* @param client Client index.
* @return Perfect jump percentage.
*/
native float Shavit_GetPerfectJumps(int client);
/**
* Retrieve strafe sync since timer start.
* Will return 0.0 if timer isn't running or -1.0 when not measured.
@ -871,6 +889,17 @@ native float Shavit_GetReplayTime(int style, int track);
*/
native void Shavit_GetReplayName(int style, int track, char[] buffer, int length);
/**
* Hijack the replay data so that this view angle will be used for the next tick.
* Use case is to make segmented runs look smoother.
*
* @param client Client index.
* @param pitch Vertical view angle.
* @param yaw Horizontal view angle.
* @noreturn
*/
native void Shavit_HijackAngles(int client, float pitch, float yaw);
/**
* Checks if there's loaded replay data for a bhop style or not.
*
@ -1172,6 +1201,7 @@ public void __pl_shavit_SetNTVOptional()
MarkNativeAsOptional("Shavit_GetHUDSettings");
MarkNativeAsOptional("Shavit_GetMapTier");
MarkNativeAsOptional("Shavit_GetMapTiers");
MarkNativeAsOptional("Shavit_GetPerfectJumps");
MarkNativeAsOptional("Shavit_GetPlayerPB");
MarkNativeAsOptional("Shavit_GetPoints");
MarkNativeAsOptional("Shavit_GetRank");
@ -1202,6 +1232,7 @@ public void __pl_shavit_SetNTVOptional()
MarkNativeAsOptional("Shavit_GetWRRecordID");
MarkNativeAsOptional("Shavit_GetWRTime");
MarkNativeAsOptional("Shavit_HasStyleAccess");
MarkNativeAsOptional("Shavit_HijackAngles");
MarkNativeAsOptional("Shavit_InsideZone");
MarkNativeAsOptional("Shavit_IsClientCreatingZone");
MarkNativeAsOptional("Shavit_IsKZMap");

View File

@ -25,7 +25,6 @@
#include <clientprefs>
#undef REQUIRE_PLUGIN
#include <adminmenu>
#include <bhopstats>
#define USES_CHAT_COLORS
@ -79,6 +78,8 @@ float gF_StrafeWarning[MAXPLAYERS+1];
bool gB_PracticeMode[MAXPLAYERS+1];
int gI_SHSW_FirstCombination[MAXPLAYERS+1];
int gI_Track[MAXPLAYERS+1];
int gI_MeasuredJumps[MAXPLAYERS+1];
int gI_PerfectJumps[MAXPLAYERS+1];
StringMap gSM_StyleCommands = null;
@ -133,6 +134,7 @@ char gS_ChatStrings[CHATSETTINGS_SIZE][128];
bool gB_StopChatSound = false;
bool gB_HookedJump = false;
char gS_LogPath[PLATFORM_MAX_PATH];
int gI_GroundTicks[MAXPLAYERS+1];
// flags
int gI_StyleFlag[STYLE_LIMIT];
@ -161,6 +163,7 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max
CreateNative("Shavit_GetDatabase", Native_GetDatabase);
CreateNative("Shavit_GetDB", Native_GetDB);
CreateNative("Shavit_GetGameType", Native_GetGameType);
CreateNative("Shavit_GetPerfectJumps", Native_GetPerfectJumps);
CreateNative("Shavit_GetStrafeCount", Native_GetStrafeCount);
CreateNative("Shavit_GetStyleCount", Native_GetStyleCount);
CreateNative("Shavit_GetStyleSettings", Native_GetStyleSettings);
@ -198,7 +201,7 @@ public void OnPluginStart()
gH_Forwards_Start = CreateGlobalForward("Shavit_OnStart", ET_Event, Param_Cell, Param_Cell);
gH_Forwards_Stop = CreateGlobalForward("Shavit_OnStop", ET_Event, Param_Cell, Param_Cell);
gH_Forwards_FinishPre = CreateGlobalForward("Shavit_OnFinishPre", ET_Event, Param_Cell, Param_Array);
gH_Forwards_Finish = CreateGlobalForward("Shavit_OnFinish", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
gH_Forwards_Finish = CreateGlobalForward("Shavit_OnFinish", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
gH_Forwards_OnRestart = CreateGlobalForward("Shavit_OnRestart", ET_Event, Param_Cell, Param_Cell);
gH_Forwards_OnEnd = CreateGlobalForward("Shavit_OnEnd", ET_Event, Param_Cell, Param_Cell);
gH_Forwards_OnPause = CreateGlobalForward("Shavit_OnPause", ET_Event, Param_Cell, Param_Cell);
@ -328,8 +331,6 @@ public void OnPluginStart()
// late
if(gB_Late)
{
OnAdminMenuReady(null);
for(int i = 1; i <= MaxClients; i++)
{
if(IsValidClient(i))
@ -385,29 +386,6 @@ public void OnLibraryRemoved(const char[] name)
}
}
public void OnAdminMenuReady(Handle topmenu)
{
Handle hTopMenu = INVALID_HANDLE;
if(LibraryExists("adminmenu") && ((hTopMenu = GetAdminTopMenu()) != INVALID_HANDLE))
{
AddToTopMenu(hTopMenu, "Timer Commands", TopMenuObject_Category, CategoryHandler, INVALID_TOPMENUOBJECT);
}
}
public void CategoryHandler(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
{
if(action == TopMenuAction_DisplayTitle)
{
strcopy(buffer, maxlength, "Timer Commands:");
}
else if(action == TopMenuAction_DisplayOption)
{
strcopy(buffer, maxlength, "Timer Commands");
}
}
public void OnMapStart()
{
// styles
@ -981,6 +959,8 @@ public int Native_FinishMap(Handle handler, int numParams)
snapshot[fCurrentTime] = gF_PlayerTimer[client];
snapshot[iSHSWCombination] = gI_SHSW_FirstCombination[client];
snapshot[iTimerTrack] = gI_Track[client];
snapshot[iMeasuredJumps] = gI_MeasuredJumps[client];
snapshot[iPerfectJumps] = gI_PerfectJumps[client];
Action result = Plugin_Continue;
Call_StartForward(gH_Forwards_FinishPre);
@ -998,6 +978,7 @@ public int Native_FinishMap(Handle handler, int numParams)
int style = 0;
int track = Track_Main;
float perfs = 100.0;
if(result == Plugin_Continue)
{
@ -1007,6 +988,7 @@ public int Native_FinishMap(Handle handler, int numParams)
Call_PushCell(gI_Strafes[client]);
Call_PushCell((gA_StyleSettings[gI_Style[client]][bSync])? (gI_GoodGains[client] == 0)? 0.0:(gI_GoodGains[client] / float(gI_TotalMeasures[client]) * 100.0):-1.0);
Call_PushCell(track = gI_Track[client]);
perfs = (gI_MeasuredJumps[client] == 0)? 100.0:(gI_PerfectJumps[client] / float(gI_MeasuredJumps[client]) * 100.0);
}
else
@ -1017,6 +999,7 @@ public int Native_FinishMap(Handle handler, int numParams)
Call_PushCell(snapshot[iStrafes]);
Call_PushCell((gA_StyleSettings[snapshot[bsStyle]][bSync])? (snapshot[iGoodGains] == 0)? 0.0:(snapshot[iGoodGains] / float(snapshot[iTotalMeasures]) * 100.0):-1.0);
Call_PushCell(track = snapshot[iTimerTrack]);
perfs = (snapshot[iMeasuredJumps] == 0)? 100.0:(snapshot[iPerfectJumps] / float(snapshot[iMeasuredJumps]) * 100.0);
}
float oldtime = 0.0;
@ -1027,6 +1010,7 @@ public int Native_FinishMap(Handle handler, int numParams)
}
Call_PushCell(oldtime);
Call_PushCell(perfs);
Call_Finish();
StopTimer(client);
@ -1091,6 +1075,13 @@ public int Native_RestartTimer(Handle handler, int numParams)
StartTimer(client, track);
}
public int Native_GetPerfectJumps(Handle handler, int numParams)
{
int client = GetNativeCell(1);
return view_as<int>((gI_MeasuredJumps[client] == 0)? 100.0:(gI_PerfectJumps[client] / float(gI_MeasuredJumps[client]) * 100.0));
}
public int Native_GetStrafeCount(Handle handler, int numParams)
{
return gI_Strafes[GetNativeCell(1)];
@ -1158,6 +1149,8 @@ public int Native_SaveSnapshot(Handle handler, int numParams)
snapshot[fCurrentTime] = gF_PlayerTimer[client];
snapshot[iSHSWCombination] = gI_SHSW_FirstCombination[client];
snapshot[iTimerTrack] = gI_Track[client];
snapshot[iMeasuredJumps] = gI_MeasuredJumps[client];
snapshot[iPerfectJumps] = gI_PerfectJumps[client];
return SetNativeArray(2, snapshot, TIMERSNAPSHOT_SIZE);
}
@ -1185,6 +1178,8 @@ public int Native_LoadSnapshot(Handle handler, int numParams)
gI_GoodGains[client] = view_as<int>(snapshot[iGoodGains]);
gF_PlayerTimer[client] = snapshot[fCurrentTime];
gI_SHSW_FirstCombination[client] = view_as<int>(snapshot[iSHSWCombination]);
gI_MeasuredJumps[client] = view_as<int>(snapshot[iMeasuredJumps]);
gI_PerfectJumps[client] = view_as<int>(snapshot[iPerfectJumps]);
}
public int Native_LogMessage(Handle plugin, int numParams)
@ -1255,6 +1250,8 @@ void StartTimer(int client, int track)
gI_SHSW_FirstCombination[client] = -1;
gF_PlayerTimer[client] = 0.0;
gB_PracticeMode[client] = false;
gI_MeasuredJumps[client] = 0;
gI_PerfectJumps[client] = 0;
SetEntityGravity(client, view_as<float>(gA_StyleSettings[gI_Style[client]][fGravityMultiplier]));
SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", view_as<float>(gA_StyleSettings[gI_Style[client]][fSpeedMultiplier]));
@ -2112,15 +2109,40 @@ 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[gI_Style[client]][bAutobhop] && gB_Auto[client] && (buttons & IN_JUMP) > 0 && mtMoveType == MOVETYPE_WALK && !bInWater)
{
int iOldButtons = GetEntProp(client, Prop_Data, "m_nOldButtons");
SetEntProp(client, Prop_Data, "m_nOldButtons", iOldButtons & ~IN_JUMP);
SetEntProp(client, Prop_Data, "m_nOldButtons", (iOldButtons & ~IN_JUMP));
iPButtons &= ~IN_JUMP;
}
else if(gB_DoubleSteps[client] && (buttons & IN_JUMP) == 0)
{
buttons |= IN_JUMP;
iPButtons = buttons |= IN_JUMP;
}
// perf jump measuring
if(!bInWater && mtMoveType == MOVETYPE_WALK && iGroundEntity != -1)
{
gI_GroundTicks[client]++;
if((((gI_ButtonCache[client] & IN_JUMP) == 0 && (iPButtons & IN_JUMP) > 0) || iPButtons != buttons) &&
1 <= gI_GroundTicks[client] <= 8)
{
gI_MeasuredJumps[client]++;
if(gI_GroundTicks[client] == 1)
{
gI_PerfectJumps[client]++;
}
}
}
else
{
gI_GroundTicks[client] = 0;
}
if(bInStart && gB_BlockPreJump && !gA_StyleSettings[gI_Style[client]][bPrespeed] && (vel[2] > 0 || (buttons & IN_JUMP) > 0))
@ -2208,7 +2230,7 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
}
}
gI_ButtonCache[client] = buttons;
gI_ButtonCache[client] = iPButtons;
gF_AngleCache[client] = angles[1];
return Plugin_Continue;

View File

@ -761,7 +761,7 @@ void UpdateHUD(int client)
{
if(gA_StyleSettings[style][bSync])
{
Format(sHintText, 512, "%s%s\t%T: %d (%.02f%%)", sHintText, (iSpeed < 1000)? "\t":"", "HudStrafeText", client, strafes, Shavit_GetSync(target));
Format(sHintText, 512, "%s%s\t%T: %d (%.01f%%)", sHintText, (iSpeed < 1000)? "\t":"", "HudStrafeText", client, strafes, Shavit_GetSync(target));
}
else
@ -1075,9 +1075,16 @@ void UpdateKeyHint(int client)
if(IsValidClient(target) && (target == client || (gI_HUDSettings[client] & HUD_OBSERVE) > 0))
{
if((gI_HUDSettings[client] & HUD_SYNC) > 0 && Shavit_GetTimerStatus(target) == Timer_Running && gA_StyleSettings[Shavit_GetBhopStyle(target)][bSync] && !IsFakeClient(target) && (!gB_Zones || !Shavit_InsideZone(target, Zone_Start, -1)))
int style = Shavit_GetBhopStyle(target);
if((gI_HUDSettings[client] & HUD_SYNC) > 0 && Shavit_GetTimerStatus(target) == Timer_Running && gA_StyleSettings[style][bSync] && !IsFakeClient(target) && (!gB_Zones || !Shavit_InsideZone(target, Zone_Start, -1)))
{
Format(sMessage, 256, "%s%s%T: %.02f", sMessage, (strlen(sMessage) > 0)? "\n\n":"", "HudSync", client, Shavit_GetSync(target));
Format(sMessage, 256, "%s%s%T: %.01f", sMessage, (strlen(sMessage) > 0)? "\n\n":"", "HudSync", client, Shavit_GetSync(target));
if(!gA_StyleSettings[style][bAutobhop])
{
Format(sMessage, 256, "%s\n%T: %.1f", sMessage, "HudPerfs", client, Shavit_GetPerfectJumps(target));
}
}
if((gI_HUDSettings[client] & HUD_SPECTATORS) > 0)

View File

@ -50,19 +50,23 @@ enum CheckpointsCache
iCPFlags,
any:aCPSnapshot[TIMERSNAPSHOT_SIZE],
String:sCPTargetname[32],
ArrayList:aCPFrames,
bool:bCPSegmented,
bool:bCPSpectated,
PCPCACHE_SIZE
}
#pragma newdecls required
#pragma semicolon 1
#pragma dynamic 131072
#pragma dynamic 524288
#define CP_ANGLES (1 << 0)
#define CP_VELOCITY (1 << 1)
#define CP_DEFAULT (CP_ANGLES|CP_VELOCITY)
#define CP_MAX 1000 // segmented runs shouldn't even reach 1k jumps on any map anyways
#define CP_MAX 1000 // shouldn't even reach 1k jumps on any map when routing anyways
#define CP_MAX_SEGMENTED 10
// game specific
EngineVersion gEV_Type = Engine_Unknown;
@ -74,6 +78,7 @@ char gS_RadioCommands[][] = {"coverme", "takepoint", "holdpos", "regroup", "foll
// cache
ConVar sv_disable_immunity_alpha = null;
ConVar mp_humanteam = null;
ConVar hostname = null;
ConVar hostport = null;
@ -84,7 +89,7 @@ int gI_LastShot[MAXPLAYERS+1];
ArrayList gA_Advertisements = null;
int gI_AdvertisementsCycle = 0;
char gS_CurrentMap[192];
int gBS_Style[MAXPLAYERS+1];
int gI_Style[MAXPLAYERS+1];
enum
{
@ -167,6 +172,7 @@ bool gB_DropAll = true;
bool gB_ResetTargetname = false;
bool gB_RestoreStates = false;
bool gB_JointeamHook = true;
int gI_HumanTeam = 0;
// dhooks
Handle gH_GetPlayerMaxSpeed = null;
@ -277,6 +283,12 @@ public void OnPluginStart()
gA_Advertisements = new ArrayList(300);
hostname = FindConVar("hostname");
hostport = FindConVar("hostport");
mp_humanteam = FindConVar("mp_humanteam");
if(mp_humanteam == null)
{
mp_humanteam = FindConVar("mp_humans_must_join_team");
}
// cvars and stuff
gCV_GodMode = CreateConVar("shavit_misc_godmode", "3", "Enable godmode for players?\n0 - Disabled\n1 - Only prevent fall/world damage.\n2 - Only prevent damage from other players.\n3 - Full godmode.", 0, true, 0.0, true, 3.0);
@ -336,6 +348,7 @@ public void OnPluginStart()
gCV_ResetTargetname.AddChangeHook(OnConVarChanged);
gCV_RestoreStates.AddChangeHook(OnConVarChanged);
gCV_JointeamHook.AddChangeHook(OnConVarChanged);
mp_humanteam.AddChangeHook(OnConVarChanged);
AutoExecConfig();
@ -425,7 +438,7 @@ public void OnClientCookiesCached(int client)
gI_CheckpointsSettings[client] = StringToInt(sSetting);
}
gBS_Style[client] = Shavit_GetBhopStyle(client);
gI_Style[client] = Shavit_GetBhopStyle(client);
}
public void Shavit_OnStyleConfigLoaded(int styles)
@ -440,6 +453,7 @@ public void Shavit_OnStyleConfigLoaded(int styles)
Shavit_GetStyleSettings(i, gA_StyleSettings[i]);
Shavit_GetStyleStrings(i, sStyleName, gS_StyleStrings[i][sStyleName], 128);
Shavit_GetStyleStrings(i, sClanTag, gS_StyleStrings[i][sClanTag], 128);
Shavit_GetStyleStrings(i, sSpecialString, gS_StyleStrings[i][sSpecialString], 128);
}
}
@ -458,7 +472,14 @@ public void Shavit_OnChatConfigLoaded()
public void Shavit_OnStyleChanged(int client, int oldstyle, int newstyle, int track, bool manual)
{
gBS_Style[client] = newstyle;
gI_Style[client] = newstyle;
if(StrContains(gS_StyleStrings[oldstyle][sSpecialString], "segments") == -1 &&
StrContains(gS_StyleStrings[newstyle][sSpecialString], "segments") != -1)
{
OpenCheckpointsMenu(client, 0);
Shavit_PrintToChat(client, "%T", "MiscSegmentedCommand", client, gS_ChatStrings[sMessageVariable], gS_ChatStrings[sMessageText]);
}
}
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
@ -491,6 +512,24 @@ public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] n
gB_ResetTargetname = gCV_ResetTargetname.BoolValue;
gB_RestoreStates = gCV_RestoreStates.BoolValue;
gB_JointeamHook = gCV_JointeamHook.BoolValue;
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()
@ -503,6 +542,11 @@ public void OnConfigsExecuted()
public void OnMapStart()
{
for(int i = 1; i <= MaxClients; i++)
{
ResetCheckpoints(i);
}
gSM_Checkpoints.Clear();
GetCurrentMap(gS_CurrentMap, 192);
@ -547,6 +591,14 @@ public void OnMapStart()
}
}
public void OnMapEnd()
{
for(int i = 1; i <= MaxClients; i++)
{
ResetCheckpoints(i);
}
}
bool LoadAdvertisementsConfig()
{
gA_Advertisements.Clear();
@ -637,6 +689,11 @@ public Action Command_Jointeam(int client, const char[] command, int args)
int iTeam = StringToInt(arg1);
if(gI_HumanTeam == 0 && !(0 <= iTeam <= 1))
{
iTeam = gI_HumanTeam;
}
bool bRespawn = false;
switch(iTeam)
@ -727,7 +784,7 @@ public MRESReturn DHook_GetPlayerMaxSpeed(int pThis, Handle hReturn)
return MRES_Ignored;
}
DHookSetReturn(hReturn, view_as<float>(gA_StyleSettings[gBS_Style[pThis]][fRunspeed]));
DHookSetReturn(hReturn, view_as<float>(gA_StyleSettings[gI_Style[pThis]][fRunspeed]));
return MRES_Override;
}
@ -888,8 +945,8 @@ void UpdateClanTag(int client)
char[] sCustomTag = new char[32];
strcopy(sCustomTag, 32, gS_ClanTag);
ReplaceString(sCustomTag, 32, "{style}", gS_StyleStrings[gBS_Style[client]][sStyleName]);
ReplaceString(sCustomTag, 32, "{styletag}", gS_StyleStrings[gBS_Style[client]][sClanTag]);
ReplaceString(sCustomTag, 32, "{style}", gS_StyleStrings[gI_Style[client]][sStyleName]);
ReplaceString(sCustomTag, 32, "{styletag}", gS_StyleStrings[gI_Style[client]][sClanTag]);
ReplaceString(sCustomTag, 32, "{time}", sTime);
ReplaceString(sCustomTag, 32, "{tr}", sTrack);
@ -919,7 +976,7 @@ public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float
int iGroundEntity = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity");
// prespeed
if(!bNoclip && !gA_StyleSettings[gBS_Style[client]][bPrespeed] && Shavit_InsideZone(client, Zone_Start, track))
if(!bNoclip && !gA_StyleSettings[gI_Style[client]][bPrespeed] && Shavit_InsideZone(client, Zone_Start, track))
{
if((gI_PreSpeed == 2 || gI_PreSpeed == 3) && gI_GroundEntity[client] == -1 && iGroundEntity != -1 && (buttons & IN_JUMP) > 0)
{
@ -940,7 +997,7 @@ public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float
if(gI_PreSpeed < 4)
{
fLimit = view_as<float>(gA_StyleSettings[gBS_Style[client]][fRunspeed]) + gF_PrestrafeLimit;
fLimit = view_as<float>(gA_StyleSettings[gI_Style[client]][fRunspeed]) + gF_PrestrafeLimit;
// if trying to jump, add a very low limit to stop prespeeding in an elegant way
// otherwise, make sure nothing weird is happening (such as sliding at ridiculous speeds, at zone enter)
@ -985,7 +1042,7 @@ public void OnClientPutInServer(int client)
if(!AreClientCookiesCached(client))
{
gBS_Style[client] = Shavit_GetBhopStyle(client);
gI_Style[client] = Shavit_GetBhopStyle(client);
gB_Hide[client] = false;
gI_CheckpointsSettings[client] = CP_DEFAULT;
}
@ -1004,9 +1061,30 @@ public void OnClientPutInServer(int client)
public void OnClientDisconnect(int client)
{
if(gB_NoWeaponDrops)
{
int entity = -1;
while((entity = FindEntityByClassname(entity, "weapon_*")) != -1)
{
if(GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity") == client)
{
RequestFrame(RemoveWeapon, EntIndexToEntRef(entity));
}
}
}
ResetCheckpoints(client);
}
void RemoveWeapon(any data)
{
if(IsValidEntity(data))
{
AcceptEntityInput(data, "Kill");
}
}
void ResetCheckpoints(int client)
{
int serial = GetClientSerial(client);
@ -1015,6 +1093,14 @@ void ResetCheckpoints(int client)
for(int i = 0; i < gI_CheckpointsCache[client][iCheckpoints]; i++)
{
FormatEx(key, 32, "%d_%d", serial, i);
CheckpointsCache cpcache[PCPCACHE_SIZE];
if(gSM_Checkpoints.GetArray(key, cpcache[0], view_as<int>(PCPCACHE_SIZE)))
{
delete cpcache[aCPFrames]; // free up replay frames if there are any
}
gSM_Checkpoints.Remove(key);
}
@ -1099,7 +1185,7 @@ public void OnPreThink(int client)
if(IsPlayerAlive(client))
{
// not the best method, but only one i found for tf2
SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", view_as<float>(gA_StyleSettings[gBS_Style[client]][fRunspeed]));
SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", view_as<float>(gA_StyleSettings[gI_Style[client]][fRunspeed]));
}
}
@ -1409,13 +1495,17 @@ public Action Command_Save(int client, int args)
return Plugin_Handled;
}
if(!gB_Checkpoints)
bool bSegmented = CanSegment(client);
if(!gB_Checkpoints && !bSegmented)
{
Shavit_PrintToChat(client, "%T", "FeatureDisabled", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
return Plugin_Handled;
}
int iMaxCPs = GetMaxCPs(client);
if(args > 0)
{
int index = 0;
@ -1425,7 +1515,7 @@ public Action Command_Save(int client, int args)
int parsed = StringToInt(arg);
if(parsed > 0 && parsed <= CP_MAX)
if(parsed > 0 && parsed <= iMaxCPs)
{
index = (parsed - 1);
}
@ -1440,7 +1530,7 @@ public Action Command_Save(int client, int args)
{
bool bSaved = false;
if(gI_CheckpointsCache[client][iCheckpoints] < CP_MAX)
if(gI_CheckpointsCache[client][iCheckpoints] < iMaxCPs)
{
if((bSaved = SaveCheckpoint(client, gI_CheckpointsCache[client][iCheckpoints])))
{
@ -1448,9 +1538,9 @@ public Action Command_Save(int client, int args)
}
}
else if((bSaved = SaveCheckpoint(client, (CP_MAX - 1))))
else if((bSaved = SaveCheckpoint(client, (iMaxCPs - 1), true)))
{
gI_CheckpointsCache[client][iCurrentCheckpoint] = CP_MAX;
gI_CheckpointsCache[client][iCurrentCheckpoint] = iMaxCPs;
}
if(bSaved)
@ -1501,7 +1591,9 @@ public Action Command_Tele(int client, int args)
public Action OpenCheckpointsMenu(int client, int item)
{
if(!gB_Checkpoints)
bool bSegmented = CanSegment(client);
if(!gB_Checkpoints && !bSegmented)
{
Shavit_PrintToChat(client, "%T", "FeatureDisabled", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
@ -1509,7 +1601,16 @@ public Action OpenCheckpointsMenu(int client, int item)
}
Menu menu = new Menu(MenuHandler_Checkpoints, MENU_ACTIONS_DEFAULT|MenuAction_DisplayItem);
if(!bSegmented)
{
menu.SetTitle("%T\n%T\n ", "MiscCheckpointMenu", client, "MiscCheckpointWarning", client);
}
else
{
menu.SetTitle("%T\n ", "MiscCheckpointMenuSegmented", client);
}
char[] sDisplay = new char[64];
FormatEx(sDisplay, 64, "%T", "MiscCheckpointSave", client, (gI_CheckpointsCache[client][iCheckpoints] + 1));
@ -1536,6 +1637,8 @@ public Action OpenCheckpointsMenu(int client, int item)
FormatEx(sDisplay, 64, "%T", "MiscCheckpointReset", client);
menu.AddItem("reset", sDisplay);
if(!bSegmented)
{
char[] sInfo = new char[16];
IntToString(CP_ANGLES, sInfo, 16);
FormatEx(sDisplay, 64, "%T", "MiscCheckpointUseAngles", client);
@ -1544,6 +1647,7 @@ public Action OpenCheckpointsMenu(int client, int item)
IntToString(CP_VELOCITY, sInfo, 16);
FormatEx(sDisplay, 64, "%T", "MiscCheckpointUseVelocity", client);
menu.AddItem(sInfo, sDisplay);
}
menu.Pagination = MENU_NO_PAGINATION;
menu.ExitButton = true;
@ -1556,14 +1660,18 @@ public int MenuHandler_Checkpoints(Menu menu, MenuAction action, int param1, int
{
if(action == MenuAction_Select)
{
int current = gI_CheckpointsCache[param1][iCurrentCheckpoint];
bool bSegmenting = CanSegment(param1);
int iMaxCPs = GetMaxCPs(param1);
int iCurrent = gI_CheckpointsCache[param1][iCurrentCheckpoint];
switch(param2)
{
case 0:
{
if(!bSegmenting)
{
// fight an exploit
if(gI_CheckpointsCache[param1][iCheckpoints] >= CP_MAX)
if(gI_CheckpointsCache[param1][iCheckpoints] >= iMaxCPs)
{
return 0;
}
@ -1572,14 +1680,22 @@ public int MenuHandler_Checkpoints(Menu menu, MenuAction action, int param1, int
gI_CheckpointsCache[param1][iCurrentCheckpoint] = ++gI_CheckpointsCache[param1][iCheckpoints];
}
else
{
bool bOverflow = gI_CheckpointsCache[param1][iCheckpoints] >= iMaxCPs;
SaveCheckpoint(param1, gI_CheckpointsCache[param1][iCheckpoints], bOverflow);
gI_CheckpointsCache[param1][iCurrentCheckpoint] = (bOverflow)? iMaxCPs:++gI_CheckpointsCache[param1][iCheckpoints];
}
}
case 1:
{
TeleportToCheckpoint(param1, current - 1, true);
TeleportToCheckpoint(param1, iCurrent - 1, true);
}
case 2:
{
if(current > 1)
if(iCurrent > 1)
{
gI_CheckpointsCache[param1][iCurrentCheckpoint]--;
}
@ -1589,7 +1705,7 @@ public int MenuHandler_Checkpoints(Menu menu, MenuAction action, int param1, int
{
CheckpointsCache cpcache[PCPCACHE_SIZE];
if(current < CP_MAX && GetCheckpoint(param1, current, cpcache))
if(iCurrent < iMaxCPs && GetCheckpoint(param1, iCurrent, cpcache))
{
gI_CheckpointsCache[param1][iCurrentCheckpoint]++;
}
@ -1636,7 +1752,7 @@ public int MenuHandler_Checkpoints(Menu menu, MenuAction action, int param1, int
return 0;
}
bool SaveCheckpoint(int client, int index)
bool SaveCheckpoint(int client, int index, bool overflow = false)
{
int target = client;
@ -1655,6 +1771,18 @@ bool SaveCheckpoint(int client, int index)
return false;
}
char[] sKey = new char[32];
int iSerial = GetClientSerial(client);
FormatEx(sKey, 32, "%d_%d", iSerial, index);
CheckpointsCache cpcacheprev[PCPCACHE_SIZE];
if(gSM_Checkpoints.GetArray(sKey, cpcacheprev[0], view_as<int>(PCPCACHE_SIZE)))
{
delete cpcacheprev[aCPFrames];
gSM_Checkpoints.SetArray(sKey, cpcacheprev[0], view_as<int>(PCPCACHE_SIZE));
}
CheckpointsCache cpcache[PCPCACHE_SIZE];
float temp[3];
@ -1726,6 +1854,55 @@ bool SaveCheckpoint(int client, int index)
}
CopyArray(snapshot, cpcache[aCPSnapshot], TIMERSNAPSHOT_SIZE);
if(CanSegment(target))
{
if(gB_Replay)
{
cpcache[aCPFrames] = Shavit_GetReplayData(target);
}
cpcache[bCPSegmented] = true;
}
else
{
cpcache[aCPFrames] = null;
cpcache[bCPSegmented] = false;
}
cpcache[bCPSpectated] = (client != target);
if(overflow)
{
int iMaxCPs = GetMaxCPs(client);
for(int i = 0; i < iMaxCPs; i++)
{
CheckpointsCache cpcacheold[PCPCACHE_SIZE];
FormatEx(sKey, 32, "%d_%d", iSerial, i);
if(!gSM_Checkpoints.GetArray(sKey, cpcacheold[0], view_as<int>(PCPCACHE_SIZE)))
{
continue; // ???
}
if(i == 0)
{
delete cpcacheold[aCPFrames];
gSM_Checkpoints.Remove(sKey);
continue;
}
gSM_Checkpoints.Remove(sKey);
FormatEx(sKey, 32, "%d_%d", iSerial, (i - 1)); // set cp index to one less
gSM_Checkpoints.SetArray(sKey, cpcacheold[0], view_as<int>(PCPCACHE_SIZE));
}
index = (iMaxCPs - 1);
}
SetCheckpoint(client, index, cpcache);
return true;
@ -1733,7 +1910,7 @@ bool SaveCheckpoint(int client, int index)
void TeleportToCheckpoint(int client, int index, bool suppressMessage)
{
if(index < 0 || index >= CP_MAX)
if(index < 0 || index >= CP_MAX || (!gB_Checkpoints && !CanSegment(client)))
{
return;
}
@ -1769,7 +1946,10 @@ void TeleportToCheckpoint(int client, int index, bool suppressMessage)
Shavit_StopTimer(client);
}
if(!cpcache[bCPSegmented] || cpcache[bCPSpectated])
{
Shavit_SetPracticeMode(client, true, !bInStart);
}
any snapshot[TIMERSNAPSHOT_SIZE];
CopyArray(cpcache[aCPSnapshot], snapshot, TIMERSNAPSHOT_SIZE);
@ -1782,8 +1962,8 @@ void TeleportToCheckpoint(int client, int index, bool suppressMessage)
CopyArray(cpcache[fCPVelocity], vel, 3);
TeleportEntity(client, pos,
((gI_CheckpointsSettings[client] & CP_ANGLES) > 0)? ang:NULL_VECTOR,
((gI_CheckpointsSettings[client] & CP_VELOCITY) > 0)? vel:NULL_VECTOR);
((gI_CheckpointsSettings[client] & CP_ANGLES) > 0 || cpcache[bCPSegmented])? ang:NULL_VECTOR,
((gI_CheckpointsSettings[client] & CP_VELOCITY) > 0 || cpcache[bCPSegmented])? vel:NULL_VECTOR);
MoveType mt = cpcache[mtCPMoveType];
@ -1816,6 +1996,24 @@ void TeleportToCheckpoint(int client, int index, bool suppressMessage)
SetEntPropFloat(client, Prop_Send, "m_flDuckSpeed", cpcache[fCPDuckSpeed]);
}
if(cpcache[bCPSegmented] && gB_Replay)
{
if(cpcache[aCPFrames] == null)
{
LogError("SetReplayData for %L failed, recorded frames are null.", client);
}
else
{
Shavit_SetReplayData(client, cpcache[aCPFrames]);
if((gI_CheckpointsSettings[client] & CP_ANGLES) > 0)
{
Shavit_HijackAngles(client, ang[0], ang[1]);
}
}
}
if(!suppressMessage)
{
Shavit_PrintToChat(client, "%T", "MiscCheckpointsTeleported", client, (index + 1), gS_ChatStrings[sMessageVariable], gS_ChatStrings[sMessageText]);
@ -1977,7 +2175,7 @@ public Action Command_Specs(int client, int args)
public Action Shavit_OnStart(int client)
{
if(!gA_StyleSettings[gBS_Style[client]][bPrespeed] && GetEntityMoveType(client) == MOVETYPE_NOCLIP)
if(!gA_StyleSettings[gI_Style[client]][bPrespeed] && GetEntityMoveType(client) == MOVETYPE_NOCLIP)
{
return Plugin_Stop;
}
@ -2499,6 +2697,7 @@ void LoadState(int client)
if(gB_Replay && gA_SaveFrames[client] != null)
{
Shavit_SetReplayData(client, gA_SaveFrames[client]);
Shavit_HijackAngles(client, gF_SaveStateData[client][1][0], gF_SaveStateData[client][1][1]);
}
delete gA_SaveFrames[client];
@ -2550,3 +2749,13 @@ void CopyArray(const any[] from, any[] to, int size)
to[i] = from[i];
}
}
bool CanSegment(int client)
{
return StrContains(gS_StyleStrings[gI_Style[client]][sSpecialString], "segments") != -1;
}
int GetMaxCPs(int client)
{
return CanSegment(client)? CP_MAX_SEGMENTED:CP_MAX;
}

View File

@ -24,6 +24,7 @@
#undef REQUIRE_PLUGIN
#include <shavit>
#include <adminmenu>
#undef REQUIRE_EXTENSIONS
#include <cstrike>
@ -60,6 +61,12 @@ enum
REPLAYSTRINGS_SIZE
};
enum
{
iBotShooting_Attack1 = (1 << 0),
iBotShooting_Attack2 = (1 << 1)
}
// game type
EngineVersion gEV_Type = Engine_Unknown;
@ -82,7 +89,6 @@ bool gB_Record[MAXPLAYERS+1];
int gI_Track[MAXPLAYERS+1];
bool gB_Late = false;
int gI_DefaultTeamSlots = 0;
// server specific
float gF_Tickrate = 0.0;
@ -94,6 +100,8 @@ any gA_CentralCache[CENTRALBOTCACHE_SIZE];
// how do i call this
bool gB_HideNameChange = false;
bool gB_DontCallTimer = false;
bool gB_HijackFrame[MAXPLAYERS+1];
float gF_HijackedAngles[MAXPLAYERS+1][2];
// plugin cvars
ConVar gCV_Enabled = null;
@ -101,6 +109,7 @@ ConVar gCV_ReplayDelay = null;
ConVar gCV_TimeLimit = null;
ConVar gCV_DefaultTeam = null;
ConVar gCV_CentralBot = null;
ConVar gCV_BotShooting = null;
// cached cvars
bool gB_Enabled = true;
@ -108,6 +117,7 @@ float gF_ReplayDelay = 5.0;
float gF_TimeLimit = 5400.0;
int gI_DefaultTeam = 3;
bool gB_CentralBot = true;
int gI_BotShooting = 3;
// timer settings
int gI_Styles = 0;
@ -120,6 +130,10 @@ char gS_ChatStrings[CHATSETTINGS_SIZE][128];
// replay settings
char gS_ReplayStrings[REPLAYSTRINGS_SIZE][MAX_NAME_LENGTH];
// admin menu
TopMenu gH_AdminMenu = null;
TopMenuObject gH_TimerCommands = INVALID_TOPMENUOBJECT;
// database related things
Database gH_SQL = null;
char gS_MySQLPrefix[32];
@ -146,6 +160,7 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max
CreateNative("Shavit_GetReplayLength", Native_GetReplayLength);
CreateNative("Shavit_GetReplayName", Native_GetReplayName);
CreateNative("Shavit_GetReplayTime", Native_GetReplayTime);
CreateNative("Shavit_HijackAngles", Native_HijackAngles);
CreateNative("Shavit_IsReplayDataLoaded", Native_IsReplayDataLoaded);
CreateNative("Shavit_ReloadReplay", Native_ReloadReplay);
CreateNative("Shavit_ReloadReplays", Native_ReloadReplays);
@ -196,22 +211,29 @@ public void OnPluginStart()
gCV_TimeLimit = CreateConVar("shavit_replay_timelimit", "5400.0", "Maximum amount of time (in seconds) to allow saving to disk.\nDefault is 5400.0 (1:30 hours)\n0 - Disabled");
gCV_DefaultTeam = CreateConVar("shavit_replay_defaultteam", "3", "Default team to make the bots join, if possible.\n2 - Terrorists/RED\n3 - Counter Terrorists/BLU", 0, true, 2.0, true, 3.0);
gCV_CentralBot = CreateConVar("shavit_replay_centralbot", "1", "Have one central bot instead of one bot per replay.\nTriggered with !replay.\nRestart the map for changes to take effect.\nThe disabled setting is not supported - use at your own risk.\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
gCV_BotShooting = CreateConVar("shavit_replay_botshooting", "3", "Attacking buttons to allow for bots.\n0 - none\1 - +attack\n2 - +attack2\n3 - both", 0, true, 0.0, true, 3.0);
gCV_Enabled.AddChangeHook(OnConVarChanged);
gCV_ReplayDelay.AddChangeHook(OnConVarChanged);
gCV_TimeLimit.AddChangeHook(OnConVarChanged);
gCV_DefaultTeam.AddChangeHook(OnConVarChanged);
gCV_CentralBot.AddChangeHook(OnConVarChanged);
gCV_BotShooting.AddChangeHook(OnConVarChanged);
AutoExecConfig();
// admin menu
if(LibraryExists("adminmenu") && ((gH_AdminMenu = GetAdminTopMenu()) != null))
{
OnAdminMenuReady(gH_AdminMenu);
}
// hooks
HookEvent("player_spawn", Player_Event, EventHookMode_Pre);
HookEvent("player_death", Player_Event, EventHookMode_Pre);
HookEvent("player_connect", BotEvents, EventHookMode_Pre);
HookEvent("player_disconnect", BotEvents, EventHookMode_Pre);
HookEventEx("player_connect_client", BotEvents, EventHookMode_Pre);
HookEvent("round_start", Round_Start);
// name change suppression
HookUserMessage(GetUserMessageId("SayText2"), Hook_SayText2, true);
@ -231,6 +253,7 @@ public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] n
gF_TimeLimit = gCV_TimeLimit.FloatValue;
gI_DefaultTeam = gCV_DefaultTeam.IntValue;
gB_CentralBot = gCV_CentralBot.BoolValue;
gI_BotShooting = gCV_BotShooting.IntValue;
if(convar == gCV_CentralBot)
{
@ -238,6 +261,60 @@ public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] n
}
}
public void OnAdminMenuCreated(Handle topmenu)
{
if(gH_AdminMenu == null || (topmenu == gH_AdminMenu && gH_TimerCommands != INVALID_TOPMENUOBJECT))
{
return;
}
gH_TimerCommands = gH_AdminMenu.AddCategory("Timer Commands", CategoryHandler, "shavit_admin", ADMFLAG_RCON);
}
public void CategoryHandler(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
{
if(action == TopMenuAction_DisplayTitle)
{
FormatEx(buffer, maxlength, "%T:", "TimerCommands", param);
}
else if(action == TopMenuAction_DisplayOption)
{
FormatEx(buffer, maxlength, "%T", "TimerCommands", param);
}
}
public void OnAdminMenuReady(Handle topmenu)
{
if((gH_AdminMenu = GetAdminTopMenu()) != null)
{
if(gH_TimerCommands == INVALID_TOPMENUOBJECT)
{
gH_TimerCommands = gH_AdminMenu.FindCategory("Timer Commands");
if(gH_TimerCommands == INVALID_TOPMENUOBJECT)
{
OnAdminMenuCreated(topmenu);
}
}
gH_AdminMenu.AddItem("sm_deletereplay", AdminMenu_DeleteReplay, gH_TimerCommands, "sm_deletereplay", ADMFLAG_RCON);
}
}
public void AdminMenu_DeleteReplay(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
{
if(action == TopMenuAction_DisplayOption)
{
FormatEx(buffer, maxlength, "%t", "DeleteReplayAdminMenu");
}
else if(action == TopMenuAction_SelectOption)
{
Command_DeleteReplay(param, 0);
}
}
public int Native_GetReplayBotFirstFrame(Handle handler, int numParams)
{
SetNativeCellRef(2, gF_StartTick[GetNativeCell(1)]);
@ -360,10 +437,16 @@ public int Native_SetReplayData(Handle handler, int numParams)
int client = GetNativeCell(1);
ArrayList frames = view_as<ArrayList>(CloneHandle(GetNativeCell(2), handler));
delete gA_PlayerFrames[client];
gA_PlayerFrames[client] = frames.Clone();
delete frames;
gI_PlayerFrames[client] = gA_PlayerFrames[client].Length;
if(gI_PlayerFrames[client] > 0)
{
gB_Record[client] = true;
}
}
public int Native_GetReplayData(Handle handler, int numParams)
@ -422,6 +505,15 @@ public int Native_GetReplayTime(Handle handler, int numParams)
return view_as<int>(float(gI_ReplayTick[style]) / gF_Tickrate);
}
public int Native_HijackAngles(Handle handler, int numParams)
{
int client = GetNativeCell(1);
gB_HijackFrame[client] = true;
gF_HijackedAngles[client][0] = view_as<float>(GetNativeCell(2));
gF_HijackedAngles[client][1] = view_as<float>(GetNativeCell(3));
}
public int Native_GetReplayBotStyle(Handle handler, int numParams)
{
return (gB_CentralBot && gA_CentralCache[iCentralReplayStatus] == Replay_Idle)? -1:GetReplayStyle(GetNativeCell(1));
@ -1185,7 +1277,7 @@ void UpdateReplayInfo(int client, int style, float time, int track)
}
}
if(gI_DefaultTeamSlots >= gI_Styles && GetClientTeam(client) != gI_DefaultTeam)
if(GetClientTeam(client) != gI_DefaultTeam)
{
if(gEV_Type == Engine_TF2)
{
@ -1461,6 +1553,16 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
buttons = gA_Frames[style][track].Get(gI_ReplayTick[style], 5);
if((gI_BotShooting & iBotShooting_Attack1) == 0)
{
buttons &= ~IN_ATTACK;
}
if((gI_BotShooting & iBotShooting_Attack2) == 0)
{
buttons &= ~IN_ATTACK2;
}
MoveType mt = MOVETYPE_NOCLIP;
if(gA_FrameCache[style][track][3] >= 0x02)
@ -1524,8 +1626,19 @@ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3
gA_PlayerFrames[client].Set(gI_PlayerFrames[client], vecCurrentPosition[1], 1);
gA_PlayerFrames[client].Set(gI_PlayerFrames[client], vecCurrentPosition[2], 2);
if(!gB_HijackFrame[client])
{
gA_PlayerFrames[client].Set(gI_PlayerFrames[client], angles[0], 3);
gA_PlayerFrames[client].Set(gI_PlayerFrames[client], angles[1], 4);
}
else
{
gA_PlayerFrames[client].Set(gI_PlayerFrames[client], gF_HijackedAngles[client][0], 3);
gA_PlayerFrames[client].Set(gI_PlayerFrames[client], gF_HijackedAngles[client][1], 4);
gB_HijackFrame[client] = false;
}
gA_PlayerFrames[client].Set(gI_PlayerFrames[client], buttons, 5);
gA_PlayerFrames[client].Set(gI_PlayerFrames[client], GetEntityFlags(client), 6);
@ -1642,30 +1755,6 @@ public void BotEvents(Event event, const char[] name, bool dontBroadcast)
}
}
public void Round_Start(Event event, const char[] name, bool dontBroadcast)
{
gI_DefaultTeamSlots = 0;
char[] sEntity = new char[32];
if(gEV_Type == Engine_TF2)
{
strcopy(sEntity, 32, "info_player_teamspawn");
}
else
{
strcopy(sEntity, 32, (gI_DefaultTeam == 2)? "info_player_terrorist":"info_player_counterterrorist");
}
int iEntity = -1;
while((iEntity = FindEntityByClassname(iEntity, sEntity)) != INVALID_ENT_REFERENCE)
{
gI_DefaultTeamSlots++;
}
}
public Action Hook_SayText2(UserMsg msg_id, any msg, const int[] players, int playersNum, bool reliable, bool init)
{
if(!gB_HideNameChange || !gB_Enabled)

View File

@ -62,7 +62,8 @@ ArrayList gA_Leaderboard[STYLE_LIMIT][TRACKS_SIZE];
float gF_PlayerRecord[MAXPLAYERS+1][STYLE_LIMIT][TRACKS_SIZE];
// admin menu
Handle gH_AdminMenu = null;
TopMenu gH_AdminMenu = null;
TopMenuObject gH_TimerCommands = INVALID_TOPMENUOBJECT;
// table prefix
char gS_MySQLPrefix[32];
@ -130,10 +131,10 @@ public void OnPluginStart()
#endif
// forwards
gH_OnWorldRecord = CreateGlobalForward("Shavit_OnWorldRecord", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
gH_OnFinish_Post = CreateGlobalForward("Shavit_OnFinish_Post", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
gH_OnWorldRecord = CreateGlobalForward("Shavit_OnWorldRecord", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
gH_OnFinish_Post = CreateGlobalForward("Shavit_OnFinish_Post", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
gH_OnWRDeleted = CreateGlobalForward("Shavit_OnWRDeleted", ET_Event, Param_Cell, Param_Cell, Param_Cell);
gH_OnWorstRecord = CreateGlobalForward("Shavit_OnWorstRecord", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
gH_OnWorstRecord = CreateGlobalForward("Shavit_OnWorstRecord", ET_Event, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell);
// player commands
RegConsoleCmd("sm_wr", Command_WorldRecord, "View the leaderboard of a map. Usage: sm_wr [map]");
@ -164,7 +165,10 @@ public void OnPluginStart()
AutoExecConfig();
// admin menu
OnAdminMenuReady(null);
if(LibraryExists("adminmenu") && ((gH_AdminMenu = GetAdminTopMenu()) != null))
{
OnAdminMenuReady(gH_AdminMenu);
}
// modules
gB_Rankings = LibraryExists("shavit-rankings");
@ -182,19 +186,47 @@ public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] n
gI_RecentLimit = gCV_RecentLimit.IntValue;
}
public void OnAdminMenuCreated(Handle topmenu)
{
if(gH_AdminMenu == null || (topmenu == gH_AdminMenu && gH_TimerCommands != INVALID_TOPMENUOBJECT))
{
return;
}
gH_TimerCommands = gH_AdminMenu.AddCategory("Timer Commands", CategoryHandler, "shavit_admin", ADMFLAG_RCON);
}
public void CategoryHandler(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
{
if(action == TopMenuAction_DisplayTitle)
{
FormatEx(buffer, maxlength, "%T:", "TimerCommands", param);
}
else if(action == TopMenuAction_DisplayOption)
{
FormatEx(buffer, maxlength, "%T", "TimerCommands", param);
}
}
public void OnAdminMenuReady(Handle topmenu)
{
if(LibraryExists("adminmenu") && ((gH_AdminMenu = GetAdminTopMenu()) != null))
if((gH_AdminMenu = GetAdminTopMenu()) != null)
{
TopMenuObject tmoTimer = FindTopMenuCategory(gH_AdminMenu, "Timer Commands");
if(gH_TimerCommands == INVALID_TOPMENUOBJECT)
{
gH_TimerCommands = gH_AdminMenu.FindCategory("Timer Commands");
if(tmoTimer != INVALID_TOPMENUOBJECT)
if(gH_TimerCommands == INVALID_TOPMENUOBJECT)
{
AddToTopMenu(gH_AdminMenu, "sm_deleteall", TopMenuObject_Item, AdminMenu_DeleteAll, tmoTimer, "sm_deleteall", ADMFLAG_RCON);
AddToTopMenu(gH_AdminMenu, "sm_delete", TopMenuObject_Item, AdminMenu_Delete, tmoTimer, "sm_delete", ADMFLAG_RCON);
AddToTopMenu(gH_AdminMenu, "sm_deletestylerecords", TopMenuObject_Item, AdminMenu_DeleteStyleRecords, tmoTimer, "sm_deletestylerecords", ADMFLAG_RCON);
OnAdminMenuCreated(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);
}
}
public void AdminMenu_Delete(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
@ -263,7 +295,7 @@ public void OnLibraryRemoved(const char[] name)
else if(StrEqual(name, "adminmenu"))
{
gH_AdminMenu = null;
delete gH_AdminMenu;
}
}
@ -668,7 +700,7 @@ void DeleteSubmenu(int client)
char[] sDisplay = new char[64];
FormatEx(sDisplay, 64, "%s (%T: %d)", gS_StyleStrings[i][sStyleName], "WRRecord", client, gI_RecordAmount[i][gI_LastTrack[client]]);
menu.AddItem(sInfo, gS_StyleStrings[i][sStyleName]);
menu.AddItem(sInfo, sDisplay, (gI_RecordAmount[i][gI_LastTrack[client]] > 0)? ITEMDRAW_DEFAULT:ITEMDRAW_DISABLED);
}
menu.ExitButton = true;
@ -1639,7 +1671,7 @@ public int RRMenu_Handler(Menu m, MenuAction action, int param1, int param2)
void OpenSubMenu(int client, int id)
{
char[] sQuery = new char[512];
FormatEx(sQuery, 512, "SELECT u.name, p.time, p.jumps, p.style, u.auth, p.date, p.map, p.strafes, p.sync, p.points, p.track FROM %splayertimes p JOIN %susers u ON p.auth = u.auth WHERE p.id = %d LIMIT 1;", gS_MySQLPrefix, gS_MySQLPrefix, id);
FormatEx(sQuery, 512, "SELECT u.name, p.time, p.jumps, p.style, u.auth, p.date, p.map, p.strafes, p.sync, p.perfs, p.points, p.track FROM %splayertimes p JOIN %susers u ON p.auth = u.auth WHERE p.id = %d LIMIT 1;", gS_MySQLPrefix, gS_MySQLPrefix, id);
DataPack datapack = new DataPack();
datapack.WriteCell(GetClientSerial(client));
@ -1690,19 +1722,30 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[]
menu.AddItem("-1", sDisplay);
// 2 - jumps
int style = results.FetchInt(3);
int jumps = results.FetchInt(2);
if(gA_StyleSettings[style][bAutobhop])
{
FormatEx(sDisplay, 128, "%T: %d", "WRJumps", client, jumps);
}
else
{
float perfs = results.FetchFloat(9);
FormatEx(sDisplay, 128, "%T: %d (%.2f%%)", "WRJumps", client, jumps, perfs);
}
menu.AddItem("-1", sDisplay);
// 3 - style
int style = results.FetchInt(3);
FormatEx(sDisplay, 128, "%T: %s", "WRStyle", client, gS_StyleStrings[style][sStyleName]);
menu.AddItem("-1", sDisplay);
// 6 - map
results.FetchString(6, sMap, 192);
float fPoints = results.FetchFloat(9);
float fPoints = results.FetchFloat(10);
if(gB_Rankings && fPoints > 0.0)
{
@ -1752,7 +1795,7 @@ public void SQL_SubMenu_Callback(Database db, DBResultSet results, const char[]
menu.AddItem(sInfo, sMenuItem);
}
GetTrackName(client, results.FetchInt(10), sTrack, 32);
GetTrackName(client, results.FetchInt(11), sTrack, 32);
}
else
@ -1889,12 +1932,12 @@ void SQL_DBConnect()
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, PRIMARY KEY (`id`), INDEX `map` (`map`, `style`, `track`), INDEX `auth` (`auth`, `date`, `points`), INDEX `time` (`time`)) ENGINE=INNODB;", gS_MySQLPrefix);
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);
}
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);", gS_MySQLPrefix);
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);
}
gH_SQL.Query(SQL_CreateTable_Callback, sQuery, 0, DBPrio_High);
@ -1934,6 +1977,9 @@ public void SQL_CreateTable_Callback(Database db, DBResultSet results, const cha
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);
}
public void SQL_TableMigration1_Callback(Database db, DBResultSet results, const char[] error, any data)
@ -1964,8 +2010,6 @@ public void SQL_AlterTable1_Callback(Database db, DBResultSet results, const cha
if(results == null)
{
LogError("Timer (WR module) error! Times' table migration (1) failed. Reason: %s", error);
return;
}
}
@ -1974,8 +2018,6 @@ public void SQL_AlterTable2_Callback(Database db, DBResultSet results, const cha
if(results == null)
{
LogError("Timer (WR module) error! Times' table migration (2) failed. Reason: %s", error);
return;
}
}
@ -1994,8 +2036,6 @@ public void SQL_AlterTable3_Callback(Database db, DBResultSet results, const cha
if(results == null)
{
LogError("Timer (WR module) error! Times' table migration (3) failed. Reason: %s", error);
return;
}
}
@ -2006,11 +2046,7 @@ public void SQL_TableMigration4_Callback(Database db, DBResultSet results, const
char[] sQuery = new char[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);
return;
}
OnMapStart();
}
public void SQL_AlterTable4_Callback(Database db, DBResultSet results, const char[] error, any data)
@ -2018,6 +2054,26 @@ public void SQL_AlterTable4_Callback(Database db, DBResultSet results, const cha
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 = new char[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;
}
@ -2025,7 +2081,19 @@ public void SQL_AlterTable4_Callback(Database db, DBResultSet results, const cha
OnMapStart();
}
public void Shavit_OnFinish(int client, int style, float time, int jumps, int strafes, float sync, int track)
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;
}
OnMapStart();
}
public void Shavit_OnFinish(int client, int style, float time, int jumps, int strafes, float sync, int track, float oldtime, float perfs)
{
char[] sTime = new char[32];
FormatSeconds(time, sTime, 32);
@ -2067,6 +2135,8 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st
Call_PushCell(sync);
Call_PushCell(track);
Call_PushCell(oldwr);
Call_PushCell(oldtime);
Call_PushCell(perfs);
Call_Finish();
#if defined DEBUG
@ -2086,6 +2156,8 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st
Call_PushCell(strafes);
Call_PushCell(sync);
Call_PushCell(track);
Call_PushCell(oldtime);
Call_PushCell(perfs);
Call_Finish();
}
@ -2118,14 +2190,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) VALUES ('%s', '%s', %.03f, %d, %d, %d, %d, %.2f, 0.0, %d);", gS_MySQLPrefix, sAuthID, gS_Map, time, jumps, GetTime(), style, strafes, sync, track);
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);
}
else // update
{
Shavit_PrintToChatAll("%s[%s]%s %T", gS_ChatStrings[sMessageVariable], sTrack, gS_ChatStrings[sMessageText], "NotFirstCompletion", LANG_SERVER, gS_ChatStrings[sMessageVariable2], client, gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageStyle], gS_StyleStrings[style][sStyleName], gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageVariable2], sTime, gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageVariable], iRank, gS_ChatStrings[sMessageText], jumps, strafes, sSync, gS_ChatStrings[sMessageText], gS_ChatStrings[sMessageWarning], sDifference);
FormatEx(sQuery, 512, "UPDATE %splayertimes SET time = %.03f, jumps = %d, date = %d, strafes = %d, sync = %.02f, points = 0.0 WHERE map = '%s' AND auth = '%s' AND style = %d AND track = %d;", gS_MySQLPrefix, time, jumps, GetTime(), strafes, sync, gS_Map, sAuthID, style, track);
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);
}
gH_SQL.Query(SQL_OnFinish_Callback, sQuery, GetClientSerial(client), DBPrio_High);
@ -2140,6 +2212,8 @@ public void Shavit_OnFinish(int client, int style, float time, int jumps, int st
Call_PushCell(iRank);
Call_PushCell(overwrite);
Call_PushCell(track);
Call_PushCell(oldtime);
Call_PushCell(perfs);
Call_Finish();
gF_PlayerRecord[client][style][track] = time;

View File

@ -108,7 +108,8 @@ int gI_BeamSprite = -1;
int gI_HaloSprite = -1;
// admin menu
Handle gH_AdminMenu = INVALID_HANDLE;
TopMenu gH_AdminMenu = null;
TopMenuObject gH_TimerCommands = INVALID_TOPMENUOBJECT;
// misc cache
bool gB_Late = false;
@ -177,11 +178,6 @@ public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max
public void OnAllPluginsLoaded()
{
if(gB_Late)
{
OnAdminMenuReady(null);
}
if(gH_SQL == null)
{
Shavit_OnDatabaseLoaded();
@ -252,6 +248,12 @@ public void OnPluginStart()
AutoExecConfig();
// admin menu
if(LibraryExists("adminmenu") && ((gH_AdminMenu = GetAdminTopMenu()) != null))
{
OnAdminMenuReady(gH_AdminMenu);
}
// misc cvars
sv_gravity = FindConVar("sv_gravity");
@ -322,20 +324,14 @@ public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] n
}
}
public void OnAdminMenuReady(Handle topmenu)
public void OnAdminMenuCreated(Handle topmenu)
{
if(LibraryExists("adminmenu") && ((gH_AdminMenu = GetAdminTopMenu()) != null))
if(gH_AdminMenu == null || (topmenu == gH_AdminMenu && gH_TimerCommands != INVALID_TOPMENUOBJECT))
{
TopMenuObject tmoTimer = FindTopMenuCategory(gH_AdminMenu, "Timer Commands");
return;
}
if(tmoTimer != INVALID_TOPMENUOBJECT)
{
AddToTopMenu(gH_AdminMenu, "sm_zones", TopMenuObject_Item, AdminMenu_Zones, tmoTimer, "sm_zones", ADMFLAG_RCON);
AddToTopMenu(gH_AdminMenu, "sm_deletezone", TopMenuObject_Item, AdminMenu_DeleteZone, tmoTimer, "sm_deletezone", ADMFLAG_RCON);
AddToTopMenu(gH_AdminMenu, "sm_deleteallzones", TopMenuObject_Item, AdminMenu_DeleteAllZones, tmoTimer, "sm_deleteallzones", ADMFLAG_RCON);
AddToTopMenu(gH_AdminMenu, "sm_zoneedit", TopMenuObject_Item, AdminMenu_ZoneEdit, tmoTimer, "sm_zoneedit", ADMFLAG_RCON);
}
}
gH_TimerCommands = gH_AdminMenu.AddCategory("Timer Commands", CategoryHandler, "shavit_admin", ADMFLAG_RCON);
}
public void CategoryHandler(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
@ -351,6 +347,27 @@ public void CategoryHandler(Handle topmenu, TopMenuAction action, TopMenuObject
}
}
public void OnAdminMenuReady(Handle topmenu)
{
if((gH_AdminMenu = GetAdminTopMenu()) != null)
{
if(gH_TimerCommands == INVALID_TOPMENUOBJECT)
{
gH_TimerCommands = gH_AdminMenu.FindCategory("Timer Commands");
if(gH_TimerCommands == INVALID_TOPMENUOBJECT)
{
OnAdminMenuCreated(topmenu);
}
}
gH_AdminMenu.AddItem("sm_zones", AdminMenu_Zones, gH_TimerCommands, "sm_zones", ADMFLAG_RCON);
gH_AdminMenu.AddItem("sm_deletezone", AdminMenu_DeleteZone, gH_TimerCommands, "sm_deletezone", ADMFLAG_RCON);
gH_AdminMenu.AddItem("sm_deleteallzones", AdminMenu_DeleteAllZones, gH_TimerCommands, "sm_deleteallzones", ADMFLAG_RCON);
gH_AdminMenu.AddItem("sm_zoneedit", AdminMenu_ZoneEdit, gH_TimerCommands, "sm_zoneedit", ADMFLAG_RCON);
}
}
public void AdminMenu_Zones(Handle topmenu, TopMenuAction action, TopMenuObject object_id, int param, char[] buffer, int maxlength)
{
if(action == TopMenuAction_DisplayOption)

View File

@ -31,6 +31,10 @@
"#format" "{1:s}"
"en" "Usage: {1}"
}
"TimerCommands"
{
"en" "Timer Commands"
}
// ---------- Timelimit ---------- //
"Minutes"
{

View File

@ -56,6 +56,10 @@
{
"en" "Sync"
}
"HudPerfs"
{
"en" "Perfect jumps"
}
"HudPracticeMode"
{
"en" "[PRACTICE]"

View File

@ -83,6 +83,11 @@
"#format" "{1:d},{2:s},{3:s}"
"en" "Checkpoint {1} is {2}empty{3}."
}
"MiscSegmentedCommand"
{
"#format" "{1:s},{2:s}"
"en" "Use {1}!cp{2} to re-open the segmented checkpoints menu."
}
// ---------- Menus ---------- //
"TeleportMenuTitle"
{
@ -97,6 +102,10 @@
{
"en" "Checkpoints"
}
"MiscCheckpointMenuSegmented"
{
"en" "Segmented Checkpoints"
}
"MiscCheckpointWarning"
{
"en" "WARNING: Teleporting will stop your timer!"

View File

@ -1,6 +1,10 @@
"Phrases"
{
// ---------- Menus ---------- //
"DeleteReplayAdminMenu"
{
"en" "Delete replay"
}
"DeleteReplayMenuTitle"
{
"en" "Delete a replay:"

View File

@ -13,10 +13,6 @@
{
"en" "Delete map zone"
}
"TimerCommands"
{
"en" "Timer Commands"
}
// ---------- Commands ---------- //
"ModifierCommandNoArgs"
{