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
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

@ -50,19 +50,22 @@ enum CheckpointsCache
iCPFlags,
any:aCPSnapshot[TIMERSNAPSHOT_SIZE],
String:sCPTargetname[32],
ArrayList:aCPFrames,
bool:bCPSegmented,
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 25
// game specific
EngineVersion gEV_Type = Engine_Unknown;
@ -84,7 +87,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
{
@ -425,7 +428,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 +443,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 +462,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)
@ -503,6 +514,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 +563,14 @@ public void OnMapStart()
}
}
public void OnMapEnd()
{
for(int i = 1; i <= MaxClients; i++)
{
ResetCheckpoints(i);
}
}
bool LoadAdvertisementsConfig()
{
gA_Advertisements.Clear();
@ -727,7 +751,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 +912,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 +943,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 +964,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 +1009,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;
}
@ -1015,6 +1039,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 +1131,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]));
}
}
@ -1501,7 +1533,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 +1543,16 @@ public Action OpenCheckpointsMenu(int client, int item)
}
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];
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)
{
int current = gI_CheckpointsCache[param1][iCurrentCheckpoint];
bool bSegmenting = CanSegment(param1);
int iMaxCPs = GetMaxCPs(param1);
int iCurrent = gI_CheckpointsCache[param1][iCurrentCheckpoint];
switch(param2)
{
case 0:
{
// fight an exploit
if(gI_CheckpointsCache[param1][iCheckpoints] >= CP_MAX)
if(!bSegmenting)
{
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]);
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 +1644,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 +1691,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;
@ -1726,6 +1781,56 @@ bool SaveCheckpoint(int client, int index)
}
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);
return true;
@ -1733,7 +1838,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 +1874,10 @@ void TeleportToCheckpoint(int client, int index, bool suppressMessage)
Shavit_StopTimer(client);
}
Shavit_SetPracticeMode(client, true, !bInStart);
if(!cpcache[bCPSegmented])
{
Shavit_SetPracticeMode(client, true, !bInStart);
}
any snapshot[TIMERSNAPSHOT_SIZE];
CopyArray(cpcache[aCPSnapshot], snapshot, TIMERSNAPSHOT_SIZE);
@ -1815,6 +1923,16 @@ void TeleportToCheckpoint(int client, int index, bool suppressMessage)
SetEntPropFloat(client, Prop_Send, "m_flDuckAmount", cpcache[fCPDucktime]);
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)
{
@ -1977,7 +2095,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 +2617,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 +2669,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

@ -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!"