-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathl4d2_restartmap.sp
More file actions
238 lines (201 loc) · 8.8 KB
/
l4d2_restartmap.sp
File metadata and controls
238 lines (201 loc) · 8.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#pragma semicolon 1
#include <sourcemod>
#include <sdktools>
#include <left4dhooks>
#include <builtinvotes>
#undef REQUIRE_PLUGIN
#include <team_consistency>
#include <l4d2_changelevel>
#define REQUIRE_PLUGIN
#include "includes/rl4d2l_util"
#pragma newdecls required
#define TEAM_SPECTATOR 1
#define MAXMAP 32
// Used to set the scores
Handle gConf = INVALID_HANDLE;
Handle fSetCampaignScores = INVALID_HANDLE;
int g_iMapRestarts; // current number of restart attempts
bool g_bIsMapRestarted; // whether map has been restarted by this plugin
Handle g_hCvarDebug = INVALID_HANDLE;
Handle g_hCvarAutofix = INVALID_HANDLE;
Handle g_hCvarAutofixMaxTries = INVALID_HANDLE; // max number of restart attempts convar
Handle hVote; // restart vote handle
int g_iSurvivorScore;
int g_iInfectedScore;
char g_sMapName[MAXMAP] = "";
bool g_L4D2ChangeLevelAvailable = false;
bool g_bTeamConsistencyAvailable = false;
public Plugin myinfo = {
name = "L4D2 Restart Map",
author = "devilesk",
description = "Adds sm_restartmap to restart the current map and keep current scores. Automatically restarts map when broken flow detected.",
version = "0.8.0",
url = "https://github.com/devilesk/rl4d2l-plugins"
};
public void OnPluginStart() {
g_hCvarDebug = CreateConVar("sm_restartmap_debug", "0", "Restart Map debug mode", 0, true, 0.0, true, 1.0);
g_hCvarAutofix = CreateConVar("sm_restartmap_autofix", "1", "Check for broken flow on map load and automatically restart.", 0, true, 0.0, true, 1.0);
g_hCvarAutofixMaxTries = CreateConVar("sm_restartmap_autofix_max_tries", "1", "Max number of automatic restart attempts to fix broken flow.", 0, true, 1.0);
RegConsoleCmd("sm_restartmap", Command_RestartMap);
g_iMapRestarts = 0;
g_bIsMapRestarted = false;
gConf = LoadGameConfigFile("left4dhooks.l4d2");
if(gConf == INVALID_HANDLE) {
LogError("Could not load gamedata/left4dhooks.l4d2.txt");
}
StartPrepSDKCall(SDKCall_GameRules);
if (PrepSDKCall_SetFromConf(gConf, SDKConf_Signature, "CTerrorGameRules::SetCampaignScores")) {
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
fSetCampaignScores = EndPrepSDKCall();
if (fSetCampaignScores == INVALID_HANDLE) {
LogError("Function 'SetCampaignScores' found, but something went wrong.");
}
}
else {
LogError("Function 'SetCampaignScores' not found.");
}
}
public void OnAllPluginsLoaded() {
g_L4D2ChangeLevelAvailable = LibraryExists("l4d2_changelevel");
g_bTeamConsistencyAvailable = LibraryExists("team_consistency");
}
public void OnLibraryRemoved(const char[] name) {
if ( StrEqual(name, "l4d2_changelevel") ) { g_L4D2ChangeLevelAvailable = false; }
else if ( StrEqual(name, "team_consistency") ) { g_bTeamConsistencyAvailable = false; }
}
public void OnLibraryAdded(const char[] name) {
if ( StrEqual(name, "l4d2_changelevel") ) { g_L4D2ChangeLevelAvailable = true; }
else if ( StrEqual(name, "team_consistency") ) { g_bTeamConsistencyAvailable = true; }
}
public void OnMapStart() {
// Compare current map to previous map and reset if different.
char sBuffer[MAXMAP];
GetCurrentMapLower(sBuffer, sizeof(sBuffer));
if (!StrEqual(g_sMapName, sBuffer, false)) {
g_bIsMapRestarted = false;
g_iMapRestarts = 0;
}
// Start broken flow check timer if autofix enabled and max tries not reached
if (GetConVarBool(g_hCvarAutofix) && g_iMapRestarts < GetConVarInt(g_hCvarAutofixMaxTries)) {
CreateTimer(2.0, CheckFlowBroken, _, TIMER_FLAG_NO_MAPCHANGE);
}
// Set scores if map restarted
if (g_bIsMapRestarted) {
PrintDebug("[OnMapStart] Restarted. Setting scores... survivor score %i, infected score %i", g_iSurvivorScore, g_iInfectedScore);
//Set the scores
SDKCall(fSetCampaignScores, g_iSurvivorScore, g_iInfectedScore); //visible scores
L4D2Direct_SetVSCampaignScore(0, g_iSurvivorScore); //real scores
L4D2Direct_SetVSCampaignScore(1, g_iInfectedScore);
g_bIsMapRestarted = false;
}
}
public Action CheckFlowBroken(Handle timer) {
bool bIsFlowBroken = IsFlowBroken();
PrintDebug("[CheckFlowBroken] Flow broken: %i", bIsFlowBroken);
if (bIsFlowBroken) {
PrintToChatAll("Broken flow detected.");
PrintToConsoleAll("Broken flow detected.");
PrintDebug("Broken flow detected.");
RestartMap();
}
else {
g_iMapRestarts = 0;
}
return Plugin_Continue;
}
public void RestartMap() {
int iSurvivorTeamIndex = GameRules_GetProp("m_bAreTeamsFlipped") ? 1 : 0;
int iInfectedTeamIndex = GameRules_GetProp("m_bAreTeamsFlipped") ? 0 : 1;
g_iSurvivorScore = L4D2Direct_GetVSCampaignScore(iSurvivorTeamIndex);
g_iInfectedScore = L4D2Direct_GetVSCampaignScore(iInfectedTeamIndex);
g_bIsMapRestarted = true;
g_iMapRestarts++;
PrintToConsoleAll("[RestartMap] Restarting map. Attempt: %i of %i... survivor: %i, score %i, infected: %i, score %i", g_iMapRestarts, GetConVarInt(g_hCvarAutofixMaxTries), iSurvivorTeamIndex, g_iSurvivorScore, iInfectedTeamIndex, g_iInfectedScore);
PrintDebug("[RestartMap] Restarting map. Attempt: %i of %i... survivor: %i, score %i, infected: %i, score %i", g_iMapRestarts, GetConVarInt(g_hCvarAutofixMaxTries), iSurvivorTeamIndex, g_iSurvivorScore, iInfectedTeamIndex, g_iInfectedScore);
GetCurrentMapLower(g_sMapName, sizeof(g_sMapName));
if (g_bTeamConsistencyAvailable)
ClearTeamConsistency();
if (g_L4D2ChangeLevelAvailable)
L4D2_ChangeLevel(g_sMapName);
else
ServerCommand("changelevel %s", g_sMapName);
}
bool IsSpectator(int client) {
return client > 0 && client <= MaxClients && IsClientInGame(client) && GetClientTeam(client) == TEAM_SPECTATOR;
}
bool CanStartVote(int client) {
if (IsSpectator(client)) {
PrintToChat(client, "\x01[\x04Restart Map\x01] Vote can only be started by a player!");
return false;
}
return true;
}
bool IsFlowBroken() {
return L4D2Direct_GetMapMaxFlowDistance() == 0;
}
public Action Command_RestartMap(int client, int args)
{
if (CheckCommandAccess(client, "sm_restartmap", ADMFLAG_KICK, true)) {
RestartMap();
}
else if (CanStartVote(client)) {
char prompt[100];
Format(prompt, sizeof(prompt), "Restart map? Scores will be preserved.");
if (StartVote(client, prompt)) {
FakeClientCommand(client, "Vote Yes");
}
}
return Plugin_Handled;
}
bool StartVote(int client, const char[] sVoteHeader) {
if (IsNewBuiltinVoteAllowed()) {
int iNumPlayers;
int[] players = new int[MaxClients];
for (int i = 1; i <= MaxClients; i++)
{
if (!IsClientConnected(i) || !IsClientInGame(i)) continue;
if (IsSpectator(i) || IsFakeClient(i)) continue;
players[iNumPlayers++] = i;
}
hVote = CreateBuiltinVote(VoteActionHandler, BuiltinVoteType_Custom_YesNo, BuiltinVoteAction_Cancel | BuiltinVoteAction_VoteEnd | BuiltinVoteAction_End);
SetBuiltinVoteArgument(hVote, sVoteHeader);
SetBuiltinVoteInitiator(hVote, client);
SetBuiltinVoteResultCallback(hVote, VoteResultHandler);
DisplayBuiltinVote(hVote, players, iNumPlayers, 20);
return true;
}
PrintToChat(client, "\x01[\x04Restart Map\x01] Vote cannot be started now.");
return false;
}
public void VoteActionHandler(Handle vote, BuiltinVoteAction action, int param1, int param2) {
switch (action) {
case BuiltinVoteAction_End: {
hVote = INVALID_HANDLE;
CloseHandle(vote);
}
case BuiltinVoteAction_Cancel: {
DisplayBuiltinVoteFail(vote, view_as<BuiltinVoteFailReason>(param1));
}
}
}
public void VoteResultHandler(Handle vote, int num_votes, int num_clients, const int[][] client_info, int num_items, const int[][] item_info) {
for (int i = 0; i < num_items; i++) {
if (item_info[i][BUILTINVOTEINFO_ITEM_INDEX] == BUILTINVOTES_VOTE_YES) {
if (item_info[i][BUILTINVOTEINFO_ITEM_VOTES] > (num_clients / 2)) {
DisplayBuiltinVotePass(vote, "Restarting map...");
PrintToChatAll("\x01[\x04Restart Map\x01] Vote passed! Restarting map...");
RestartMap();
return;
}
}
}
DisplayBuiltinVoteFail(vote, BuiltinVoteFail_Loses);
}
stock void PrintDebug(const char[] Message, any ...) {
if (GetConVarBool(g_hCvarDebug)) {
char DebugBuff[256];
VFormat(DebugBuff, sizeof(DebugBuff), Message, 2);
LogMessage(DebugBuff);
}
}