mirror of
https://github.com/shavitush/bhoptimer.git
synced 2025-12-06 18:08:26 +00:00
Added segmented run support with replay integration.
Use `segments` in the `specialstring` property to enable segmented checkpoints.
This commit is contained in:
parent
41c2136b42
commit
afe0a261a7
@ -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).
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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!"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user