Added segmented run support with replay integration.

Use `segments` in the `specialstring` property to enable segmented checkpoints.
This commit is contained in:
shavit 2018-04-30 12:04:48 +03:00
parent 41c2136b42
commit afe0a261a7
4 changed files with 168 additions and 30 deletions

View File

@ -36,7 +36,7 @@ Some features are: Per-player settings (!hud), truevel and gradient-like display
#### shavit-misc #### shavit-misc
This plugin handles miscellaneous things used in bunnyhop servers. 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 #### shavit-rankings
Enables !rank, !top and introduces map tiers (!settier). 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. 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) [Mapzones' setup demonstration](https://youtu.be/OXFMGm40F6c)

View File

@ -50,19 +50,22 @@ enum CheckpointsCache
iCPFlags, iCPFlags,
any:aCPSnapshot[TIMERSNAPSHOT_SIZE], any:aCPSnapshot[TIMERSNAPSHOT_SIZE],
String:sCPTargetname[32], String:sCPTargetname[32],
ArrayList:aCPFrames,
bool:bCPSegmented,
PCPCACHE_SIZE PCPCACHE_SIZE
} }
#pragma newdecls required #pragma newdecls required
#pragma semicolon 1 #pragma semicolon 1
#pragma dynamic 131072 #pragma dynamic 524288
#define CP_ANGLES (1 << 0) #define CP_ANGLES (1 << 0)
#define CP_VELOCITY (1 << 1) #define CP_VELOCITY (1 << 1)
#define CP_DEFAULT (CP_ANGLES|CP_VELOCITY) #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 25
// game specific // game specific
EngineVersion gEV_Type = Engine_Unknown; EngineVersion gEV_Type = Engine_Unknown;
@ -84,7 +87,7 @@ int gI_LastShot[MAXPLAYERS+1];
ArrayList gA_Advertisements = null; ArrayList gA_Advertisements = null;
int gI_AdvertisementsCycle = 0; int gI_AdvertisementsCycle = 0;
char gS_CurrentMap[192]; char gS_CurrentMap[192];
int gBS_Style[MAXPLAYERS+1]; int gI_Style[MAXPLAYERS+1];
enum enum
{ {
@ -425,7 +428,7 @@ public void OnClientCookiesCached(int client)
gI_CheckpointsSettings[client] = StringToInt(sSetting); gI_CheckpointsSettings[client] = StringToInt(sSetting);
} }
gBS_Style[client] = Shavit_GetBhopStyle(client); gI_Style[client] = Shavit_GetBhopStyle(client);
} }
public void Shavit_OnStyleConfigLoaded(int styles) public void Shavit_OnStyleConfigLoaded(int styles)
@ -440,6 +443,7 @@ public void Shavit_OnStyleConfigLoaded(int styles)
Shavit_GetStyleSettings(i, gA_StyleSettings[i]); Shavit_GetStyleSettings(i, gA_StyleSettings[i]);
Shavit_GetStyleStrings(i, sStyleName, gS_StyleStrings[i][sStyleName], 128); Shavit_GetStyleStrings(i, sStyleName, gS_StyleStrings[i][sStyleName], 128);
Shavit_GetStyleStrings(i, sClanTag, gS_StyleStrings[i][sClanTag], 128); Shavit_GetStyleStrings(i, sClanTag, gS_StyleStrings[i][sClanTag], 128);
Shavit_GetStyleStrings(i, sSpecialString, gS_StyleStrings[i][sSpecialString], 128);
} }
} }
@ -458,7 +462,14 @@ public void Shavit_OnChatConfigLoaded()
public void Shavit_OnStyleChanged(int client, int oldstyle, int newstyle, int track, bool manual) 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) public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
@ -503,6 +514,11 @@ public void OnConfigsExecuted()
public void OnMapStart() public void OnMapStart()
{ {
for(int i = 1; i <= MaxClients; i++)
{
ResetCheckpoints(i);
}
gSM_Checkpoints.Clear(); gSM_Checkpoints.Clear();
GetCurrentMap(gS_CurrentMap, 192); GetCurrentMap(gS_CurrentMap, 192);
@ -547,6 +563,14 @@ public void OnMapStart()
} }
} }
public void OnMapEnd()
{
for(int i = 1; i <= MaxClients; i++)
{
ResetCheckpoints(i);
}
}
bool LoadAdvertisementsConfig() bool LoadAdvertisementsConfig()
{ {
gA_Advertisements.Clear(); gA_Advertisements.Clear();
@ -727,7 +751,7 @@ public MRESReturn DHook_GetPlayerMaxSpeed(int pThis, Handle hReturn)
return MRES_Ignored; 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; return MRES_Override;
} }
@ -888,8 +912,8 @@ void UpdateClanTag(int client)
char[] sCustomTag = new char[32]; char[] sCustomTag = new char[32];
strcopy(sCustomTag, 32, gS_ClanTag); strcopy(sCustomTag, 32, gS_ClanTag);
ReplaceString(sCustomTag, 32, "{style}", gS_StyleStrings[gBS_Style[client]][sStyleName]); ReplaceString(sCustomTag, 32, "{style}", gS_StyleStrings[gI_Style[client]][sStyleName]);
ReplaceString(sCustomTag, 32, "{styletag}", gS_StyleStrings[gBS_Style[client]][sClanTag]); ReplaceString(sCustomTag, 32, "{styletag}", gS_StyleStrings[gI_Style[client]][sClanTag]);
ReplaceString(sCustomTag, 32, "{time}", sTime); ReplaceString(sCustomTag, 32, "{time}", sTime);
ReplaceString(sCustomTag, 32, "{tr}", sTrack); ReplaceString(sCustomTag, 32, "{tr}", sTrack);
@ -919,7 +943,7 @@ public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float
int iGroundEntity = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity"); int iGroundEntity = GetEntPropEnt(client, Prop_Send, "m_hGroundEntity");
// prespeed // 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) if((gI_PreSpeed == 2 || gI_PreSpeed == 3) && gI_GroundEntity[client] == -1 && iGroundEntity != -1 && (buttons & IN_JUMP) > 0)
{ {
@ -940,7 +964,7 @@ public Action Shavit_OnUserCmdPre(int client, int &buttons, int &impulse, float
if(gI_PreSpeed < 4) 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 // 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) // otherwise, make sure nothing weird is happening (such as sliding at ridiculous speeds, at zone enter)
@ -985,7 +1009,7 @@ public void OnClientPutInServer(int client)
if(!AreClientCookiesCached(client)) if(!AreClientCookiesCached(client))
{ {
gBS_Style[client] = Shavit_GetBhopStyle(client); gI_Style[client] = Shavit_GetBhopStyle(client);
gB_Hide[client] = false; gB_Hide[client] = false;
gI_CheckpointsSettings[client] = CP_DEFAULT; gI_CheckpointsSettings[client] = CP_DEFAULT;
} }
@ -1015,6 +1039,14 @@ void ResetCheckpoints(int client)
for(int i = 0; i < gI_CheckpointsCache[client][iCheckpoints]; i++) for(int i = 0; i < gI_CheckpointsCache[client][iCheckpoints]; i++)
{ {
FormatEx(key, 32, "%d_%d", serial, 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); gSM_Checkpoints.Remove(key);
} }
@ -1099,7 +1131,7 @@ public void OnPreThink(int client)
if(IsPlayerAlive(client)) if(IsPlayerAlive(client))
{ {
// not the best method, but only one i found for tf2 // 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]));
} }
} }
@ -1501,7 +1533,9 @@ public Action Command_Tele(int client, int args)
public Action OpenCheckpointsMenu(int client, int item) 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]); Shavit_PrintToChat(client, "%T", "FeatureDisabled", client, gS_ChatStrings[sMessageWarning], gS_ChatStrings[sMessageText]);
@ -1509,7 +1543,16 @@ public Action OpenCheckpointsMenu(int client, int item)
} }
Menu menu = new Menu(MenuHandler_Checkpoints, MENU_ACTIONS_DEFAULT|MenuAction_DisplayItem); Menu menu = new Menu(MenuHandler_Checkpoints, MENU_ACTIONS_DEFAULT|MenuAction_DisplayItem);
menu.SetTitle("%T\n%T\n ", "MiscCheckpointMenu", client, "MiscCheckpointWarning", client);
if(!bSegmented)
{
menu.SetTitle("%T\n%T\n ", "MiscCheckpointMenu", client, "MiscCheckpointWarning", client);
}
else
{
menu.SetTitle("%T\n ", "MiscCheckpointMenuSegmented", client);
}
char[] sDisplay = new char[64]; char[] sDisplay = new char[64];
FormatEx(sDisplay, 64, "%T", "MiscCheckpointSave", client, (gI_CheckpointsCache[client][iCheckpoints] + 1)); FormatEx(sDisplay, 64, "%T", "MiscCheckpointSave", client, (gI_CheckpointsCache[client][iCheckpoints] + 1));
@ -1556,30 +1599,42 @@ public int MenuHandler_Checkpoints(Menu menu, MenuAction action, int param1, int
{ {
if(action == MenuAction_Select) 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) switch(param2)
{ {
case 0: case 0:
{ {
// fight an exploit if(!bSegmenting)
if(gI_CheckpointsCache[param1][iCheckpoints] >= CP_MAX)
{ {
return 0; // fight an exploit
if(gI_CheckpointsCache[param1][iCheckpoints] >= iMaxCPs)
{
return 0;
}
SaveCheckpoint(param1, gI_CheckpointsCache[param1][iCheckpoints]);
gI_CheckpointsCache[param1][iCurrentCheckpoint] = ++gI_CheckpointsCache[param1][iCheckpoints];
} }
SaveCheckpoint(param1, gI_CheckpointsCache[param1][iCheckpoints]); else
gI_CheckpointsCache[param1][iCurrentCheckpoint] = ++gI_CheckpointsCache[param1][iCheckpoints]; {
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: case 1:
{ {
TeleportToCheckpoint(param1, current - 1, true); TeleportToCheckpoint(param1, iCurrent - 1, true);
} }
case 2: case 2:
{ {
if(current > 1) if(iCurrent > 1)
{ {
gI_CheckpointsCache[param1][iCurrentCheckpoint]--; gI_CheckpointsCache[param1][iCurrentCheckpoint]--;
} }
@ -1589,7 +1644,7 @@ public int MenuHandler_Checkpoints(Menu menu, MenuAction action, int param1, int
{ {
CheckpointsCache cpcache[PCPCACHE_SIZE]; CheckpointsCache cpcache[PCPCACHE_SIZE];
if(current < CP_MAX && GetCheckpoint(param1, current, cpcache)) if(iCurrent < iMaxCPs && GetCheckpoint(param1, iCurrent, cpcache))
{ {
gI_CheckpointsCache[param1][iCurrentCheckpoint]++; gI_CheckpointsCache[param1][iCurrentCheckpoint]++;
} }
@ -1636,7 +1691,7 @@ public int MenuHandler_Checkpoints(Menu menu, MenuAction action, int param1, int
return 0; return 0;
} }
bool SaveCheckpoint(int client, int index) bool SaveCheckpoint(int client, int index, bool overflow = false)
{ {
int target = client; int target = client;
@ -1726,6 +1781,56 @@ bool SaveCheckpoint(int client, int index)
} }
CopyArray(snapshot, cpcache[aCPSnapshot], TIMERSNAPSHOT_SIZE); CopyArray(snapshot, cpcache[aCPSnapshot], TIMERSNAPSHOT_SIZE);
if(CanSegment(client))
{
if(gB_Replay)
{
cpcache[aCPFrames] = Shavit_GetReplayData(client);
}
cpcache[bCPSegmented] = true;
}
else
{
cpcache[aCPFrames] = null;
cpcache[bCPSegmented] = false;
}
if(overflow)
{
int iSerial = GetClientSerial(client);
int iMaxCPs = GetMaxCPs(client);
char[] sKey = new char[32];
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); SetCheckpoint(client, index, cpcache);
return true; return true;
@ -1733,7 +1838,7 @@ bool SaveCheckpoint(int client, int index)
void TeleportToCheckpoint(int client, int index, bool suppressMessage) 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; return;
} }
@ -1769,7 +1874,10 @@ void TeleportToCheckpoint(int client, int index, bool suppressMessage)
Shavit_StopTimer(client); Shavit_StopTimer(client);
} }
Shavit_SetPracticeMode(client, true, !bInStart); if(!cpcache[bCPSegmented])
{
Shavit_SetPracticeMode(client, true, !bInStart);
}
any snapshot[TIMERSNAPSHOT_SIZE]; any snapshot[TIMERSNAPSHOT_SIZE];
CopyArray(cpcache[aCPSnapshot], snapshot, TIMERSNAPSHOT_SIZE); CopyArray(cpcache[aCPSnapshot], snapshot, TIMERSNAPSHOT_SIZE);
@ -1816,6 +1924,16 @@ void TeleportToCheckpoint(int client, int index, bool suppressMessage)
SetEntPropFloat(client, Prop_Send, "m_flDuckSpeed", cpcache[fCPDuckSpeed]); SetEntPropFloat(client, Prop_Send, "m_flDuckSpeed", cpcache[fCPDuckSpeed]);
} }
if(cpcache[bCPSegmented] && gB_Replay)
{
Shavit_SetReplayData(client, cpcache[aCPFrames]);
if((gI_CheckpointsSettings[client] & CP_ANGLES) > 0)
{
Shavit_HijackAngles(client, ang[0], ang[1]);
}
}
if(!suppressMessage) if(!suppressMessage)
{ {
Shavit_PrintToChat(client, "%T", "MiscCheckpointsTeleported", client, (index + 1), gS_ChatStrings[sMessageVariable], gS_ChatStrings[sMessageText]); Shavit_PrintToChat(client, "%T", "MiscCheckpointsTeleported", client, (index + 1), gS_ChatStrings[sMessageVariable], gS_ChatStrings[sMessageText]);
@ -1977,7 +2095,7 @@ public Action Command_Specs(int client, int args)
public Action Shavit_OnStart(int client) 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; return Plugin_Stop;
} }
@ -2499,6 +2617,7 @@ void LoadState(int client)
if(gB_Replay && gA_SaveFrames[client] != null) if(gB_Replay && gA_SaveFrames[client] != null)
{ {
Shavit_SetReplayData(client, gA_SaveFrames[client]); Shavit_SetReplayData(client, gA_SaveFrames[client]);
Shavit_HijackAngles(client, gF_SaveStateData[client][1][0], gF_SaveStateData[client][1][1]);
} }
delete gA_SaveFrames[client]; delete gA_SaveFrames[client];
@ -2550,3 +2669,13 @@ void CopyArray(const any[] from, any[] to, int size)
to[i] = from[i]; 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

@ -83,6 +83,11 @@
"#format" "{1:d},{2:s},{3:s}" "#format" "{1:d},{2:s},{3:s}"
"en" "Checkpoint {1} is {2}empty{3}." "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 ---------- // // ---------- Menus ---------- //
"TeleportMenuTitle" "TeleportMenuTitle"
{ {
@ -97,6 +102,10 @@
{ {
"en" "Checkpoints" "en" "Checkpoints"
} }
"MiscCheckpointMenuSegmented"
{
"en" "Segmented Checkpoints"
}
"MiscCheckpointWarning" "MiscCheckpointWarning"
{ {
"en" "WARNING: Teleporting will stop your timer!" "en" "WARNING: Teleporting will stop your timer!"