├── 1v1_skeetstats.sp
├── 1v1pm.sp
├── ACS_v1.2.4.sp
├── README.md
├── SMDJ.sp
├── SpecListener2.0.sp
├── cannounce.sp
├── cannounce
├── countryshow.sp
├── geolist.sp
├── joinmsg.sp
├── joinmsg
│ ├── allow.sp
│ ├── disallow.sp
│ ├── set.sp
│ └── sound.sp
└── suppress.sp
├── compiled
└── readyup.smx
├── config
├── HUD.zip
├── nade_training.cfg
└── tgmaster.cfg
├── current.sp
├── customvotes.sp
├── customvotes_new.sp
├── dmg_during_tank.sp
├── fix_fastmelee.sp
├── hardcoop
├── ai_aggressivespecials.sp
├── ai_chargefromclose.sp
├── ai_hunterpouncing.sp
├── ai_jockeybehaviour.sp
├── ai_smokersettings.sp
├── ai_tankbehaviour.sp
├── ai_targeting.sp
├── autowipe.sp
├── coopbosses.sp
├── l4d2_playstats_fixed.sp
├── l4d_tank_damage_announce_fixed.sp
├── mapskipper.sp
├── pillsonly.sp
├── sb_godmode.sp
├── special_infected_wave_spawner.sp
├── survivor_reset.sp
└── survivormanagement.sp
├── l4d2_ai_damagefix.sp
├── l4d2_bossvote.sp
├── l4d2_botchat.sp
├── l4d2_playstats.sp
├── l4d2_skill_detect.sp
├── l4d2_stats.sp
├── l4d2_tank_skin.sp
├── l4d2_tankswap.sp
├── l4d2_vote_manager3.sp
├── l4d_boss_percent.sp
├── l4d_ff_limit.sp
├── l4d_multitanks_v17.sp
├── l4d_playerjoin.sp
├── l4d_pluginsvote.sp
├── l4d_sm_respawn.sp
├── l4d_stats.sp
├── lerpmonitor.sp
├── lightweight_specs.sp
├── match_vote.sp
├── pause.sp
├── pouncedamageplus.sp
├── pugsetup
├── include
│ ├── logdebug.inc
│ ├── practicemode.inc
│ ├── priorityqueue.inc
│ ├── pugsetup.inc
│ └── restorecvars.inc
├── practicemode.sp
├── practicemode
│ ├── bots.sp
│ ├── commands.sp
│ ├── generic.sp
│ ├── grenadeiterators.sp
│ ├── grenademenus.sp
│ ├── grenadeutils.sp
│ ├── natives.sp
│ ├── pugsetup_integration.sp
│ └── spawns.sp
├── pugsetup.sp
├── pugsetup
│ ├── captainpickmenus.sp
│ ├── configs.sp
│ ├── consolecommands.sp
│ ├── generic.sp
│ ├── instantrunoffvote.sp
│ ├── kniferounds.sp
│ ├── leadermenus.sp
│ ├── liveon3.sp
│ ├── maps.sp
│ ├── mapveto.sp
│ ├── mapvote.sp
│ ├── natives.sp
│ ├── setupmenus.sp
│ └── steamapi.sp
├── pugsetup_autokicker.sp
├── pugsetup_chatmoney.sp
├── pugsetup_damageprint.sp
├── pugsetup_hostname.sp
├── pugsetup_rwsbalancer.sp
├── pugsetup_teamlocker.sp
└── pugsetup_teamnames.sp
├── ratemonitor.sp
├── readyup(pm).sp
├── roundstartbotstop.sp
├── si_class_announce.sp
├── spechud.sp
└── warmod.sp
/1v1pm.sp:
--------------------------------------------------------------------------------
1 | /*
2 | SourcePawn is Copyright (C) 2006-2008 AlliedModders LLC. All rights reserved.
3 | SourceMod is Copyright (C) 2006-2008 AlliedModders LLC. All rights reserved.
4 | Pawn and SMALL are Copyright (C) 1997-2008 ITB CompuPhase.
5 | Source is Copyright (C) Valve Corporation.
6 | All trademarks are property of their respective owners.
7 |
8 | This program is free software: you can redistribute it and/or modify it
9 | under the terms of the GNU General Public License as published by the
10 | Free Software Foundation, either version 3 of the License, or (at your
11 | option) any later version.
12 |
13 | This program is distributed in the hope that it will be useful, but
14 | WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 | General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License along
19 | with this program. If not, see .
20 | */
21 | #pragma semicolon 1
22 |
23 | #include
24 | #include
25 | #include "left4downtown"
26 | #include
27 |
28 | #define TEAM_INFECTED 3
29 | #define TAUNT_HIGH_THRESHOLD 0.4
30 | #define TAUNT_MID_THRESHOLD 0.2
31 | #define TAUNT_LOW_THRESHOLD 0.04
32 |
33 | enum SIClasses
34 | {
35 | SMOKER_CLASS=1,
36 | BOOMER_CLASS,
37 | HUNTER_CLASS,
38 | SPITTER_CLASS,
39 | JOCKEY_CLASS,
40 | CHARGER_CLASS,
41 | WITCH_CLASS,
42 | TANK_CLASS,
43 | NOTINFECTED_CLASS
44 | }
45 |
46 | static String:SINames[_:SIClasses][] =
47 | {
48 | "",
49 | "gas", // smoker
50 | "exploding", // boomer
51 | "hunter",
52 | "spitter",
53 | "jockey",
54 | "charger",
55 | "witch",
56 | "tank",
57 | ""
58 | };
59 |
60 | new Handle: hCvarDmgThreshold = INVALID_HANDLE;
61 | new Handle: hSpecialInfectedHP[_:SIClasses] = INVALID_HANDLE;
62 |
63 |
64 | public Plugin:myinfo =
65 | {
66 | name = "1v1 Pro Mod",
67 | author = "Blade + Confogl Team, Tabun",
68 | description = "A plugin designed to support 1v1.",
69 | version = "6.0c",
70 | url = "https://github.com/malathion/promod/"
71 | }
72 |
73 |
74 | public OnPluginStart()
75 | {
76 | decl String:buffer[17];
77 | for (new i = 1; i < _:SIClasses; i++)
78 | {
79 | Format(buffer, sizeof(buffer), "z_%s_health", SINames[i]);
80 | hSpecialInfectedHP[i] = FindConVar(buffer);
81 | }
82 |
83 | hCvarDmgThreshold = CreateConVar("sm_1v1_dmgthreshold", "33", "Amount of damage done (at once) before SI suicides.", FCVAR_PLUGIN, true, 1.0);
84 |
85 | HookEvent("player_hurt", Event_PlayerHurt, EventHookMode_Post);
86 | }
87 |
88 | public Action:Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)
89 | {
90 | new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
91 | new victim = GetClientOfUserId(GetEventInt(event, "userid"));
92 |
93 | if (!IsClientAndInGame(attacker))
94 | return;
95 |
96 | new damage = GetEventInt(event, "dmg_health");
97 | new zombie_class = GetZombieClass(attacker);
98 |
99 | if (GetClientTeam(attacker) == TEAM_INFECTED && zombie_class != _:TANK_CLASS && damage >= GetConVarInt(hCvarDmgThreshold))
100 | {
101 | new remaining_health = GetClientHealth(attacker);
102 | CPrintToChatAll("{blue}[{default}1v1 Mode{blue}]{default} Infected ({olive}%N{default}) health remaining: {blue}%d{default}", attacker, remaining_health);
103 |
104 | ForcePlayerSuicide(attacker);
105 |
106 | new maxHealth = GetSpecialInfectedHP(zombie_class);
107 | if (!maxHealth)
108 | return;
109 |
110 | if (remaining_health == 1)
111 | {
112 | CPrintToChat(victim, "{red}Get owned.");
113 | }
114 | else if (remaining_health <= RoundToCeil(maxHealth * TAUNT_LOW_THRESHOLD))
115 | {
116 | CPrintToChat(victim, "{red}You seem upset.");
117 | }
118 | else if (remaining_health <= RoundToCeil(maxHealth * TAUNT_MID_THRESHOLD))
119 | {
120 | CPrintToChat(victim, "{red}So close!");
121 | }
122 | else if (remaining_health <= RoundToCeil(maxHealth * TAUNT_HIGH_THRESHOLD))
123 | {
124 | CPrintToChat(victim, "{red}Not bad.");
125 | }
126 | }
127 | }
128 |
129 |
130 | stock GetZombieClass(client) return GetEntProp(client, Prop_Send, "m_zombieClass");
131 |
132 | stock GetSpecialInfectedHP(class)
133 | {
134 | if (hSpecialInfectedHP[class] != INVALID_HANDLE)
135 | return GetConVarInt(hSpecialInfectedHP[class]);
136 |
137 | return 0;
138 | }
139 |
140 | stock bool:IsClientAndInGame(index)
141 | {
142 | if (index > 0 && index < MaxClients)
143 | {
144 | return IsClientInGame(index);
145 | }
146 | return false;
147 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Scripting
2 | SourcePawn
3 |
--------------------------------------------------------------------------------
/SpecListener2.0.sp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #define VOICE_NORMAL 0 /**< Allow the client to listen and speak normally. */
6 | #define VOICE_MUTED 1 /**< Mutes the client from speaking to everyone. */
7 | #define VOICE_SPEAKALL 2 /**< Allow the client to speak to everyone. */
8 | #define VOICE_LISTENALL 4 /**< Allow the client to listen to everyone. */
9 | #define VOICE_TEAM 8 /**< Allow the client to always speak to team, even when dead. */
10 | #define VOICE_LISTENTEAM 16 /**< Allow the client to always hear teammates, including dead ones. */
11 |
12 | #define TEAM_SPEC 1
13 | #define TEAM_SURVIVOR 2
14 | #define TEAM_INFECTED 3
15 |
16 | new Handle:hAllTalk;
17 |
18 |
19 | #define PLUGIN_VERSION "2.2"
20 | public Plugin:myinfo =
21 | {
22 | name = "SpecLister",
23 | author = "waertf & bear modded by bman & Blazers Team",
24 | description = "Allows spectator listen others team voice for l4d",
25 | version = PLUGIN_VERSION,
26 | url = "http://forums.alliedmods.net/showthread.php?t=95474"
27 | }
28 |
29 |
30 | public OnPluginStart()
31 | {
32 | HookEvent("player_team",Event_PlayerChangeTeam);
33 | RegConsoleCmd("hear", Panel_hear);
34 |
35 | //Fix for End of round all-talk.
36 | hAllTalk = FindConVar("sv_alltalk");
37 | HookConVarChange(hAllTalk, OnAlltalkChange);
38 |
39 | //Spectators hear Team_Chat
40 | AddCommandListener(Command_SayTeam, "say_team");
41 |
42 | }
43 | public PanelHandler1(Handle:menu, MenuAction:action, param1, param2)
44 | {
45 | if (action == MenuAction_Select)
46 | {
47 | PrintToConsole(param1, "You selected item: %d", param2)
48 | if(param2==1)
49 | {
50 | SetClientListeningFlags(param1, VOICE_LISTENALL);
51 | PrintToChat(param1,"\x03[Voice] \x01You have \x04Enabled \x01voice listen." );
52 | }
53 | else
54 | {
55 | SetClientListeningFlags(param1, VOICE_NORMAL);
56 | PrintToChat(param1,"\x03[Voice] \x01You have \x04Disabled \x01voice listen." );
57 | }
58 |
59 | }
60 | else if (action == MenuAction_Cancel) {
61 | PrintToServer("Client %d's menu was cancelled. Reason: %d", param1, param2);
62 | }
63 | }
64 |
65 |
66 | public Action:Panel_hear(client,args)
67 | {
68 | if(GetClientTeam(client)!=TEAM_SPEC)
69 | return Plugin_Handled;
70 | new Handle:panel = CreatePanel();
71 | SetPanelTitle(panel, "Enable listen mode ?");
72 | DrawPanelItem(panel, "Yes");
73 | DrawPanelItem(panel, "No");
74 |
75 | SendPanelToClient(panel, client, PanelHandler1, 20);
76 |
77 | CloseHandle(panel);
78 |
79 | return Plugin_Handled;
80 |
81 | }
82 |
83 | public Action:Command_SayTeam(client, const char[] command, args)
84 | {
85 | char text[4096];
86 | GetCmdArgString(text, sizeof(text));
87 | new senderteam = GetClientTeam(client);
88 |
89 | /*
90 | if(FindCharInString(text, '@') == 0) //Check for admin messages
91 | return Plugin_Continue;
92 | if(text[1] == '!' || text[1] == '/') // Hidden command or chat trigger
93 | return Plugin_Continue;
94 | new startidx = trim_quotes(text); //Not sure why this function is needed.(bman)
95 | */
96 |
97 | StripQuotes(text);
98 | if (IsChatTrigger() && text[0] == '/' || text[0] == '!' || text[0] == '@') // Hidden command or chat trigger
99 | {
100 | return Plugin_Continue;
101 | }
102 |
103 | char senderTeamName[10];
104 | switch (senderteam)
105 | {
106 | case 3:
107 | senderTeamName = "Infected"
108 | case 2:
109 | senderTeamName = "Survivor"
110 | case 1:
111 | senderTeamName = "SPEC"
112 | }
113 |
114 | //Is not console, Sender is not on Spectators, and there are players on the spectator team
115 | if (client == 0)
116 | PrintToChatAll("Console : %s", text);
117 | else if (senderteam != TEAM_SPEC && GetTeamClientCount(TEAM_SPEC) > 0)
118 | {
119 | for (new i = 1; i <= GetMaxClients(); i++)
120 | {
121 | if (IsClientInGame(i) && GetClientTeam(i) == TEAM_SPEC)
122 | {
123 | //Format(buffermsg, 256, "{default}(%s) {teamcolor}%s{olive} : %s", senderTeamName, name, text[startidx]);
124 | //Format(buffermsg, 256, "\x01(TEAM-%s) \x03%s\x05: %s", senderTeamName, name, text[startidx]);
125 | //CPrintToChatEx(i, client, buffermsg); //Send the message to spectators
126 |
127 | CPrintToChatEx(i, client, "{default}(%s) {teamcolor}%N{default} : {olive} %s", senderTeamName, client, text);
128 | }
129 | }
130 | }
131 | return Plugin_Continue;
132 | }
133 | /*
134 | public trim_quotes(String:text[])
135 | {
136 | new startidx = 0
137 | if (text[0] == '"')
138 | {
139 | startidx = 1
140 | // Strip the ending quote, if there is one
141 | new len = strlen(text);
142 | if (text[len-1] == '"')
143 | {
144 | text[len-1] = '\0'
145 | }
146 | }
147 |
148 | return startidx
149 | }
150 | */
151 | public Event_PlayerChangeTeam(Handle:event, const String:name[], bool:dontBroadcast)
152 | {
153 | new client = GetClientOfUserId(GetEventInt(event, "userid"));
154 | new userTeam = GetEventInt(event, "team");
155 | if(client==0)
156 | return ;
157 |
158 | //PrintToChat(userID,"\x02X02 \x03X03 \x04X04 \x05X05 ");\\ \x02:color:default \x03:lightgreen \x04:orange \x05:darkgreen
159 |
160 | if(userTeam==TEAM_SPEC && IsValidClient(client))
161 | {
162 | SetClientListeningFlags(client, VOICE_NORMAL);
163 | }
164 | else
165 | {
166 | SetClientListeningFlags(client, VOICE_NORMAL);
167 | }
168 | }
169 |
170 | public OnAlltalkChange(Handle:convar, const String:oldValue[], const String:newValue[])
171 | {
172 | if (StringToInt(newValue) == 0)
173 | {
174 | for (new i = 1; i <= MaxClients; i++)
175 | {
176 | if (IsValidClient(i) && GetClientTeam(i) == TEAM_SPEC)
177 | {
178 | SetClientListeningFlags(i, VOICE_LISTENALL);
179 | //PrintToChat(i,"Re-Enable Listen Because of All-Talk");
180 | }
181 | }
182 | }
183 | }
184 |
185 | public OnClientDisconnect(client)
186 | {
187 | if(IsClientInGame(client)) {
188 | if (!IsFakeClient(client) && GetClientTeam(client) != 1) //Make the choose team menu display when someone quits
189 | {
190 | for (new i = 1; i <= MaxClients; i++)
191 | {
192 | if (IsClientInGame(i)) {
193 | if (IsValidClient(i) && GetClientTeam(i) == 1)
194 | {
195 | ClientCommand(i, "chooseteam");
196 | }
197 | }
198 | }
199 | }
200 | }
201 | }
202 |
203 | public IsValidClient (client)
204 | {
205 | if (client == 0)
206 | return false;
207 |
208 | if (!IsClientConnected(client))
209 | return false;
210 |
211 | if (IsFakeClient(client))
212 | return false;
213 |
214 | if (!IsClientInGame(client))
215 | return false;
216 |
217 | return true;
218 | }
219 |
--------------------------------------------------------------------------------
/cannounce/countryshow.sp:
--------------------------------------------------------------------------------
1 | /*****************************************************************
2 |
3 |
4 | G L O B A L V A R S
5 |
6 |
7 | *****************************************************************/
8 | new Handle:g_CvarShowConnect = INVALID_HANDLE;
9 | new Handle:g_CvarShowDisconnect = INVALID_HANDLE;
10 | new Handle:g_CvarShowEnhancedToAdmins = INVALID_HANDLE;
11 |
12 | new Handle:hKVCountryShow = INVALID_HANDLE;
13 |
14 |
15 | /*****************************************************************
16 |
17 |
18 | F O R W A R D P U B L I C S
19 |
20 |
21 | *****************************************************************/
22 | SetupCountryShow()
23 | {
24 | g_CvarShowConnect = CreateConVar("sm_ca_showenhanced", "1", "displays enhanced message when player connects");
25 | g_CvarShowDisconnect = CreateConVar("sm_ca_showenhanceddisc", "1", "displays enhanced message when player disconnects");
26 | g_CvarShowEnhancedToAdmins = CreateConVar("sm_ca_showenhancedadmins", "0", "displays a different enhanced message to admin players (ADMFLAG_GENERIC)");
27 |
28 | //prepare kv for countryshow
29 | hKVCountryShow = CreateKeyValues("CountryShow");
30 |
31 | if(!FileToKeyValues(hKVCountryShow, g_filesettings))
32 | {
33 | KeyValuesToFile(hKVCountryShow, g_filesettings);
34 | }
35 |
36 | SetupDefaultMessages();
37 | }
38 |
39 | OnPostAdminCheck_CountryShow(client)
40 | {
41 | decl String:rawmsg[364];
42 | decl String:rawadmmsg[364];
43 |
44 | //if enabled, show message
45 | if( GetConVarInt(g_CvarShowConnect) )
46 | {
47 | KvRewind(hKVCountryShow);
48 |
49 | //get message admins will see (if sm_ca_showenhancedadmins)
50 | if( KvJumpToKey(hKVCountryShow, "messages_admin", false) )
51 | {
52 | KvGetString(hKVCountryShow, "playerjoin", rawadmmsg, sizeof(rawadmmsg), "");
53 | Format(rawadmmsg, sizeof(rawadmmsg), "%c%s", 1, rawadmmsg);
54 | KvRewind(hKVCountryShow);
55 | }
56 |
57 | //get message all players will see
58 | if( KvJumpToKey(hKVCountryShow, "messages", false) )
59 | {
60 | KvGetString(hKVCountryShow, "playerjoin", rawmsg, sizeof(rawmsg), "");
61 | Format(rawmsg, sizeof(rawmsg), "%c%s", 1, rawmsg);
62 | KvRewind(hKVCountryShow);
63 | }
64 |
65 | //if sm_ca_showenhancedadmins - show diff messages to admins
66 | if( GetConVarInt(g_CvarShowEnhancedToAdmins) )
67 | {
68 | PrintFormattedMessageToAdmins( rawadmmsg, client );
69 | PrintFormattedMsgToNonAdmins( rawmsg, client );
70 | }
71 | else
72 | {
73 | PrintFormattedMessageToAll( rawmsg, client );
74 | }
75 | }
76 | }
77 |
78 | OnPluginEnd_CountryShow()
79 | {
80 | CloseHandle(hKVCountryShow);
81 | }
82 |
83 |
84 | /****************************************************************
85 |
86 |
87 | C A L L B A C K F U N C T I O N S
88 |
89 |
90 | ****************************************************************/
91 | public Action:event_PlayerDisc_CountryShow(Handle:event, const String:name[], bool:dontBroadcast)
92 | {
93 | decl String:rawmsg[364];
94 | decl String:rawadmmsg[364];
95 | decl String:reason[128];
96 | decl String:timedOut[256];
97 |
98 | new client = GetClientOfUserId(GetEventInt(event, "userid"));
99 |
100 | //if enabled, show message
101 | if( GetConVarInt(g_CvarShowDisconnect) )
102 | {
103 | GetEventString(event, "reason", reason, sizeof(reason));
104 | Format(timedOut, sizeof(timedOut), "%s timed out", client);
105 |
106 | if (strcmp(reason, timedOut) == 0 || strcmp(reason, "No Steam logon") == 0)
107 | {
108 | Format(reason, sizeof(reason), "Crashed game.");
109 | }
110 | KvRewind(hKVCountryShow);
111 |
112 | //get message admins will see (if sm_ca_showenhancedadmins)
113 | if( KvJumpToKey(hKVCountryShow, "messages_admin", false) )
114 | {
115 | KvGetString(hKVCountryShow, "playerdisc", rawadmmsg, sizeof(rawadmmsg), "");
116 | Format(rawadmmsg, sizeof(rawadmmsg), "%c%s", 1, rawadmmsg);
117 | KvRewind(hKVCountryShow);
118 |
119 | //first replace disconnect reason if applicable
120 | if (StrContains(rawadmmsg, "{DISC_REASON}") != -1 )
121 | {
122 | ReplaceString(rawadmmsg, sizeof(rawadmmsg), "{DISC_REASON}", reason);
123 |
124 | //strip carriage returns, replace with space
125 | ReplaceString(rawadmmsg, sizeof(rawadmmsg), "\n", " ");
126 |
127 | }
128 | }
129 |
130 | //get message all players will see
131 | if( KvJumpToKey(hKVCountryShow, "messages", false) )
132 | {
133 | KvGetString(hKVCountryShow, "playerdisc", rawmsg, sizeof(rawmsg), "");
134 | Format(rawmsg, sizeof(rawmsg), "%c%s", 1, rawmsg);
135 | KvRewind(hKVCountryShow);
136 |
137 | //first replace disconnect reason if applicable
138 | if (StrContains(rawmsg, "{DISC_REASON}") != -1 )
139 | {
140 | ReplaceString(rawmsg, sizeof(rawmsg), "{DISC_REASON}", reason);
141 |
142 | //strip carriage returns, replace with space
143 | ReplaceString(rawmsg, sizeof(rawmsg), "\n", " ");
144 | }
145 | }
146 |
147 | //if sm_ca_showenhancedadmins - show diff messages to admins
148 | if( GetConVarInt(g_CvarShowEnhancedToAdmins) )
149 | {
150 | PrintFormattedMessageToAdmins( rawadmmsg, client );
151 | PrintFormattedMsgToNonAdmins( rawmsg, client );
152 | }
153 | else
154 | {
155 | PrintFormattedMessageToAll( rawmsg, client );
156 | }
157 |
158 | KvRewind(hKVCountryShow);
159 | }
160 | }
161 |
162 | /*****************************************************************
163 |
164 |
165 | P L U G I N F U N C T I O N S
166 |
167 |
168 | *****************************************************************/
169 | SetupDefaultMessages()
170 | {
171 | if(!KvJumpToKey(hKVCountryShow, "messages"))
172 | {
173 | KvJumpToKey(hKVCountryShow, "messages", true);
174 | KvSetString(hKVCountryShow, "playerjoin", "{PLAYERTYPE} {GREEN}{PLAYERNAME} {DEFAULT}<{LIGHTGREEN}{STEAMID}{DEFAULT}> connected from country {GREEN}{PLAYERCOUNTRY} {DEFAULT}({LIGHTGREEN}{PLAYERCOUNTRYSHORT}{DEFAULT}), IP {GREEN}{PLAYERIP}");
175 | KvSetString(hKVCountryShow, "playerdisc", "{PLAYERTYPE} {GREEN}{PLAYERNAME} {DEFAULT}<{LIGHTGREEN}{STEAMID}{DEFAULT}> from country {GREEN}{PLAYERCOUNTRY} {DEFAULT}({LIGHTGREEN}{PLAYERCOUNTRYSHORT}{DEFAULT}) disconnected from IP {GREEN}{PLAYERIP}{GREEN}reason: {DEFAULT}{DISC_REASON}");
176 |
177 | KvRewind(hKVCountryShow);
178 | KeyValuesToFile(hKVCountryShow, g_filesettings);
179 | }
180 |
181 | KvRewind(hKVCountryShow);
182 |
183 | if(!KvJumpToKey(hKVCountryShow, "messages_admin"))
184 | {
185 | KvJumpToKey(hKVCountryShow, "messages_admin", true);
186 | KvSetString(hKVCountryShow, "playerjoin", "{PLAYERTYPE} {GREEN}{PLAYERNAME} {DEFAULT}<{LIGHTGREEN}{STEAMID}{DEFAULT}> connected from country {GREEN}{PLAYERCOUNTRY} {DEFAULT}({LIGHTGREEN}{PLAYERCOUNTRYSHORT}{DEFAULT}), IP {GREEN}{PLAYERIP}");
187 | KvSetString(hKVCountryShow, "playerdisc", "{PLAYERTYPE} {GREEN}{PLAYERNAME} {DEFAULT}<{LIGHTGREEN}{STEAMID}{DEFAULT}> from country {GREEN}{PLAYERCOUNTRY} {DEFAULT}({LIGHTGREEN}{PLAYERCOUNTRYSHORT}{DEFAULT}) disconnected from IP {GREEN}{PLAYERIP}{GREEN}reason: {DEFAULT}{DISC_REASON}");
188 |
189 | KvRewind(hKVCountryShow);
190 | KeyValuesToFile(hKVCountryShow, g_filesettings);
191 | }
192 |
193 | KvRewind(hKVCountryShow);
194 | }
--------------------------------------------------------------------------------
/cannounce/geolist.sp:
--------------------------------------------------------------------------------
1 |
2 |
3 | SetupGeoList()
4 | {
5 | RegAdminCmd("sm_geolist", Command_GeoList, ADMFLAG_GENERIC, "sm_geolist - prints geopraphical information about target(s)");
6 | }
7 |
8 |
9 | public Action:Command_GeoList(client, args)
10 | {
11 | decl String:target[65];
12 |
13 | decl String:target_name[MAX_TARGET_LENGTH];
14 | decl target_list[MAXPLAYERS];
15 | decl target_count;
16 | decl bool:tn_is_ml;
17 | decl String:name[32];
18 |
19 | decl String:ip[16];
20 | decl String:city[46];
21 | decl String:region[46];
22 | decl String:country[46];
23 | decl String:ccode[3];
24 | decl String:ccode3[4];
25 | new bool:bIsLanIp;
26 |
27 | //not enough arguments, display usage
28 | if (args < 1)
29 | {
30 | ReplyToCommand(client, "[SM] Usage: sm_geolist ");
31 | return Plugin_Handled;
32 | }
33 |
34 | //get command arguments
35 | GetCmdArg(1, target, sizeof(target));
36 |
37 |
38 | //get the target of this command, return error if invalid
39 | if ((target_count = ProcessTargetString(
40 | target,
41 | client,
42 | target_list,
43 | MAXPLAYERS,
44 | 0,
45 | target_name,
46 | sizeof(target_name),
47 | tn_is_ml)) <= 0)
48 | {
49 | ReplyToTargetError(client, target_count);
50 | return Plugin_Handled;
51 | }
52 |
53 |
54 | for (new i = 0; i < target_count; i++)
55 | {
56 | GetClientIP(target_list[i], ip, sizeof(ip));
57 | GetClientName(target_list[i], name, 32);
58 |
59 | //detect LAN ip
60 | bIsLanIp = IsLanIP( ip );
61 |
62 | // Using GeoIPCity extension...
63 | if ( g_UseGeoIPCity )
64 | {
65 | if( !GeoipGetRecord( ip, city, region, country, ccode, ccode3 ) )
66 | {
67 | if( bIsLanIp )
68 | {
69 | Format( city, sizeof(city), "%T", "LAN City Desc", LANG_SERVER );
70 | Format( region, sizeof(region), "%T", "LAN Region Desc", LANG_SERVER );
71 | Format( country, sizeof(country), "%T", "LAN Country Desc", LANG_SERVER );
72 | Format( ccode, sizeof(ccode), "%T", "LAN Country Short", LANG_SERVER );
73 | Format( ccode3, sizeof(ccode3), "%T", "LAN Country Short 3", LANG_SERVER );
74 | }
75 | else
76 | {
77 | Format( city, sizeof(city), "%T", "Unknown City Desc", LANG_SERVER );
78 | Format( region, sizeof(region), "%T", "Unknown Region Desc", LANG_SERVER );
79 | Format( country, sizeof(country), "%T", "Unknown Country Desc", LANG_SERVER );
80 | Format( ccode, sizeof(ccode), "%T", "Unknown Country Short", LANG_SERVER );
81 | Format( ccode3, sizeof(ccode3), "%T", "Unknown Country Short 3", LANG_SERVER );
82 | }
83 | }
84 | }
85 | else // Using GeoIP default extension...
86 | {
87 | if( !GeoipCode2(ip, ccode) )
88 | {
89 | if( bIsLanIp )
90 | {
91 | Format( ccode, sizeof(ccode), "%T", "LAN Country Short", LANG_SERVER );
92 | }
93 | else
94 | {
95 | Format( ccode, sizeof(ccode), "%T", "Unknown Country Short", LANG_SERVER );
96 | }
97 | }
98 |
99 | if( !GeoipCountry(ip, country, sizeof(country)) )
100 | {
101 | if( bIsLanIp )
102 | {
103 | Format( country, sizeof(country), "%T", "LAN Country Desc", LANG_SERVER );
104 | }
105 | else
106 | {
107 | Format( country, sizeof(country), "%T", "Unknown Country Desc", LANG_SERVER );
108 | }
109 | }
110 |
111 | // Since the GeoIPCity extension isn't loaded, we don't know the city or region.
112 | if( bIsLanIp )
113 | {
114 | Format( city, sizeof(city), "%T", "LAN City Desc", LANG_SERVER );
115 | Format( region, sizeof(region), "%T", "LAN Region Desc", LANG_SERVER );
116 | Format( ccode3, sizeof(ccode3), "%T", "LAN Country Short 3", LANG_SERVER );
117 | }
118 | else
119 | {
120 | Format( city, sizeof(city), "%T", "Unknown City Desc", LANG_SERVER );
121 | Format( region, sizeof(region), "%T", "Unknown Region Desc", LANG_SERVER );
122 | Format( ccode3, sizeof(ccode3), "%T", "Unknown Country Short 3", LANG_SERVER );
123 | }
124 | }
125 |
126 | // Fallback for unknown/empty location strings
127 | if( StrEqual( city, "" ) )
128 | {
129 | Format( city, sizeof(city), "%T", "Unknown City Desc", LANG_SERVER );
130 | }
131 |
132 | if( StrEqual( region, "" ) )
133 | {
134 | Format( region, sizeof(region), "%T", "Unknown Region Desc", LANG_SERVER );
135 | }
136 |
137 | if( StrEqual( country, "" ) )
138 | {
139 | Format( country, sizeof(country), "%T", "Unknown Country Desc", LANG_SERVER );
140 | }
141 |
142 | if( StrEqual( ccode, "" ) )
143 | {
144 | Format( ccode, sizeof(ccode), "%T", "Unknown Country Short", LANG_SERVER );
145 | }
146 |
147 | if( StrEqual( ccode3, "" ) )
148 | {
149 | Format( ccode3, sizeof(ccode3), "%T", "Unknown Country Short 3", LANG_SERVER );
150 | }
151 |
152 | // Add "The" in front of certain countries
153 | if( StrContains( country, "United", false ) != -1 ||
154 | StrContains( country, "Republic", false ) != -1 ||
155 | StrContains( country, "Federation", false ) != -1 ||
156 | StrContains( country, "Island", false ) != -1 ||
157 | StrContains( country, "Netherlands", false ) != -1 ||
158 | StrContains( country, "Isle", false ) != -1 ||
159 | StrContains( country, "Bahamas", false ) != -1 ||
160 | StrContains( country, "Maldives", false ) != -1 ||
161 | StrContains( country, "Philippines", false ) != -1 ||
162 | StrContains( country, "Vatican", false ) != -1 )
163 | {
164 | Format( country, sizeof(country), "The %s", country );
165 | }
166 |
167 | ReplyToCommand( client, "%s from %s in %s/%s", name, city, region, country );
168 | }
169 |
170 | return Plugin_Handled;
171 | }
--------------------------------------------------------------------------------
/cannounce/joinmsg.sp:
--------------------------------------------------------------------------------
1 |
2 | #define MSGLENGTH 151
3 | #define SOUNDFILE_PATH_LEN 256
4 | #define CHECKFLAG ADMFLAG_ROOT
5 |
6 |
7 | /*****************************************************************
8 |
9 |
10 | G L O B A L V A R S
11 |
12 |
13 | *****************************************************************/
14 | new Handle:hKVCustomJoinMessages = INVALID_HANDLE;
15 |
16 | new Handle:g_CvarPlaySound = INVALID_HANDLE;
17 | new Handle:g_CvarPlaySoundFile = INVALID_HANDLE;
18 |
19 | new Handle:g_CvarPlayDiscSound = INVALID_HANDLE;
20 | new Handle:g_CvarPlayDiscSoundFile = INVALID_HANDLE;
21 |
22 | new Handle:g_CvarMapStartNoSound = INVALID_HANDLE;
23 |
24 | new bool:noSoundPeriod = false;
25 |
26 | /*****************************************************************
27 |
28 |
29 | L I B R A R Y I N C L U D E S
30 |
31 |
32 | *****************************************************************/
33 | #include "cannounce/joinmsg/allow.sp"
34 | #include "cannounce/joinmsg/disallow.sp"
35 | #include "cannounce/joinmsg/set.sp"
36 | #include "cannounce/joinmsg/sound.sp"
37 |
38 |
39 | /*****************************************************************
40 |
41 |
42 | F O R W A R D P U B L I C S
43 |
44 |
45 | *****************************************************************/
46 |
47 | SetupJoinMsg()
48 | {
49 | noSoundPeriod = false;
50 |
51 | //cvars
52 | g_CvarPlaySound = CreateConVar("sm_ca_playsound", "0", "Plays a specified (sm_ca_playsoundfile) sound on player connect");
53 | g_CvarPlaySoundFile = CreateConVar("sm_ca_playsoundfile", "ambient\\alarms\\klaxon1.wav", "Sound to play on player connect if sm_ca_playsound = 1");
54 |
55 | g_CvarPlayDiscSound = CreateConVar("sm_ca_playdiscsound", "0", "Plays a specified (sm_ca_playdiscsoundfile) sound on player discconnect");
56 | g_CvarPlayDiscSoundFile = CreateConVar("sm_ca_playdiscsoundfile", "weapons\\cguard\\charging.wav", "Sound to play on player discconnect if sm_ca_playdiscsound = 1");
57 |
58 | g_CvarMapStartNoSound = CreateConVar("sm_ca_mapstartnosound", "30.0", "Time to ignore all player join sounds on a map load");
59 |
60 |
61 | //prepare kv custom messages file
62 | hKVCustomJoinMessages = CreateKeyValues("CustomJoinMessages");
63 |
64 | if(!FileToKeyValues(hKVCustomJoinMessages, g_fileset))
65 | {
66 | KeyValuesToFile(hKVCustomJoinMessages, g_fileset);
67 | }
68 |
69 | SetupJoinMsg_Allow();
70 |
71 | SetupJoinMsg_DisAllow();
72 |
73 | SetupJoinMsg_Set();
74 |
75 | SetupJoinSound_Set();
76 | }
77 |
78 |
79 | OnAdminMenuReady_JoinMsg()
80 | {
81 | //Build the "Player Commands" category
82 | new TopMenuObject:player_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_PLAYERCOMMANDS);
83 |
84 | if (player_commands != INVALID_TOPMENUOBJECT)
85 | {
86 | OnAdminMenuReady_JoinMsg_Allow(player_commands);
87 |
88 | OnAdminMenuReady_JoinMsg_DAllow(player_commands);
89 | }
90 | }
91 |
92 |
93 | OnMapStart_JoinMsg()
94 | {
95 | decl Float:waitPeriod;
96 |
97 | noSoundPeriod = false;
98 |
99 | waitPeriod = GetConVarFloat(g_CvarMapStartNoSound);
100 |
101 | if( waitPeriod > 0 )
102 | {
103 | noSoundPeriod = true;
104 | CreateTimer(waitPeriod, Timer_MapStartNoSound);
105 | }
106 | }
107 |
108 | OnPostAdminCheck_JoinMsg(const String:steamId[])
109 | {
110 | decl String:soundfile[SOUNDFILE_PATH_LEN];
111 |
112 | new String:message[MSGLENGTH + 1];
113 | new String:output[364];
114 | new String:soundFilePath[SOUNDFILE_PATH_LEN];
115 |
116 | new bool:customSoundPlayed = false;
117 |
118 | //get from kv file
119 | KvRewind(hKVCustomJoinMessages);
120 | if(KvJumpToKey(hKVCustomJoinMessages, steamId))
121 | {
122 | //Custom join MESSAGE
123 | KvGetString(hKVCustomJoinMessages, "message", message, sizeof(message), "");
124 |
125 | if( strlen(message) > 0)
126 | {
127 | //print output
128 | Format(output, sizeof(output), "%c\"%c%s%c\"", 4, 1, message, 4);
129 |
130 | PrintFormattedMessageToAll(output, -1);
131 | }
132 |
133 | //Custom join SOUND
134 | KvGetString(hKVCustomJoinMessages, "soundfile", soundFilePath, sizeof(soundFilePath), "");
135 |
136 | if( strlen(soundFilePath) > 0 && !noSoundPeriod )
137 | {
138 | EmitSoundToAll( soundFilePath );
139 | customSoundPlayed = true;
140 | }
141 | }
142 |
143 | KvRewind(hKVCustomJoinMessages);
144 |
145 | //if enabled and custom sound not already played, play all player sound
146 | if( GetConVarInt(g_CvarPlaySound) && !customSoundPlayed)
147 | {
148 | GetConVarString(g_CvarPlaySoundFile, soundfile, sizeof(soundfile));
149 |
150 | if( strlen(soundfile) > 0 && !noSoundPeriod)
151 | {
152 | EmitSoundToAll( soundfile );
153 | }
154 | }
155 | }
156 |
157 | OnClientDisconnect_JoinMsg()
158 | {
159 | decl String:soundfile[SOUNDFILE_PATH_LEN];
160 |
161 | if( GetConVarInt(g_CvarPlayDiscSound))
162 | {
163 | GetConVarString(g_CvarPlayDiscSoundFile, soundfile, sizeof(soundfile));
164 |
165 | if( strlen(soundfile) > 0)
166 | {
167 | EmitSoundToAll( soundfile );
168 | }
169 | }
170 | }
171 |
172 |
173 | OnPluginEnd_JoinMsg()
174 | {
175 | CloseHandle(hKVCustomJoinMessages);
176 | }
177 |
178 |
179 | public Action:Timer_MapStartNoSound(Handle:timer)
180 | {
181 | noSoundPeriod = false;
182 |
183 | return Plugin_Handled;
184 | }
185 |
186 |
187 | /*****************************************************************
188 |
189 |
190 | P L U G I N F U N C T I O N S
191 |
192 |
193 | *****************************************************************/
194 | LoadSoundFilesAll()
195 | {
196 | new String:c_soundFile[SOUNDFILE_PATH_LEN];
197 | new String:c_soundFileFullPath[SOUNDFILE_PATH_LEN + 6];
198 |
199 | new String:dc_soundFile[SOUNDFILE_PATH_LEN];
200 | new String:dc_soundFileFullPath[SOUNDFILE_PATH_LEN + 6];
201 |
202 | //download and cache connect sound
203 | if( GetConVarInt(g_CvarPlaySound))
204 | {
205 | GetConVarString(g_CvarPlaySoundFile, c_soundFile, sizeof(c_soundFile));
206 | Format(c_soundFileFullPath, sizeof(c_soundFileFullPath), "sound/%s", c_soundFile);
207 |
208 | if( FileExists( c_soundFileFullPath ) )
209 | {
210 | AddFileToDownloadsTable(c_soundFileFullPath);
211 |
212 | PrecacheSound( c_soundFile );
213 | }
214 | }
215 |
216 | //cache disconnect sound
217 | if( GetConVarInt(g_CvarPlayDiscSound))
218 | {
219 | GetConVarString(g_CvarPlayDiscSoundFile, dc_soundFile, sizeof(dc_soundFile));
220 | Format(dc_soundFileFullPath, sizeof(dc_soundFileFullPath), "sound/%s", dc_soundFile);
221 |
222 | if( FileExists( dc_soundFileFullPath ) )
223 | {
224 | AddFileToDownloadsTable(dc_soundFileFullPath);
225 |
226 | PrecacheSound( dc_soundFile );
227 | }
228 | }
229 | }
--------------------------------------------------------------------------------
/cannounce/joinmsg/allow.sp:
--------------------------------------------------------------------------------
1 | /*****************************************************************
2 |
3 |
4 | F O R W A R D P U B L I C S
5 |
6 |
7 | *****************************************************************/
8 |
9 | SetupJoinMsg_Allow()
10 | {
11 | RegAdminCmd("sm_joinmsgon", Command_AllowJoinMsg, CHECKFLAG, "sm_joinmsgon - allows a client to set a custom join message");
12 | RegAdminCmd("sm_joinmsgonid", Command_AllowJoinMsgID, CHECKFLAG, "sm_joinmsgonid \"\" \"\" - allows specified steamid to set a custom join message");
13 | }
14 |
15 |
16 | OnAdminMenuReady_JoinMsg_Allow(TopMenuObject:player_commands)
17 | {
18 | AddToTopMenu(hTopMenu,
19 | "sm_joinmsgon",
20 | TopMenuObject_Item,
21 | AdminMenu_AllowJoinMsg,
22 | player_commands,
23 | "sm_joinmsgon",
24 | CHECKFLAG);
25 | }
26 |
27 | /****************************************************************
28 |
29 |
30 | C A L L B A C K F U N C T I O N S
31 |
32 |
33 | ****************************************************************/
34 |
35 | public Action:Command_AllowJoinMsg(client, args)
36 | {
37 | decl String:target[65];
38 |
39 | decl String:target_name[MAX_TARGET_LENGTH];
40 | decl target_list[MAXPLAYERS];
41 | decl target_count;
42 | decl bool:tn_is_ml;
43 | new String:steamId[24];
44 |
45 | //not enough arguments, display usage
46 | if (args < 1)
47 | {
48 | ReplyToCommand(client, "[SM] Usage: sm_joinmsgon ");
49 | return Plugin_Handled;
50 | }
51 |
52 | //get command arguments
53 | GetCmdArg(1, target, sizeof(target));
54 |
55 | //get the target of this command, return error if invalid
56 | if ((target_count = ProcessTargetString(
57 | target,
58 | client,
59 | target_list,
60 | MAXPLAYERS,
61 | COMMAND_FILTER_NO_MULTI,
62 | target_name,
63 | sizeof(target_name),
64 | tn_is_ml)) <= 0)
65 | {
66 | ReplyToTargetError(client, target_count);
67 | return Plugin_Handled;
68 | }
69 |
70 | //set allowed custom join in kv file
71 | if( target_count > 0 && GetClientAuthId(target_list[0], AuthId_Steam2, steamId, sizeof(steamId)) )
72 | {
73 | DoAllowJoinMsg(steamId,target_name,client);
74 |
75 | //inform player of their enabled custom join msg
76 | PrintToChat(target_list[0], "[SM] type sm_joinmsg in console to set your custom join message!");
77 | }
78 | else
79 | {
80 | ReplyToCommand(client, "[SM] Unable to find player's steam id");
81 | }
82 |
83 | return Plugin_Handled;
84 | }
85 |
86 |
87 |
88 | public Action:Command_AllowJoinMsgID(client, args)
89 | {
90 | decl String:player_name[MAX_TARGET_LENGTH];
91 | new String:steamId[24];
92 |
93 | //not enough arguments, display usage
94 | if (args != 2)
95 | {
96 | ReplyToCommand(client, "[SM] Usage: sm_joinmsgonid \"\" \"\"");
97 | return Plugin_Handled;
98 | }
99 |
100 | //get command arguments
101 | GetCmdArg(1, steamId, sizeof(steamId));
102 | GetCmdArg(2, player_name, sizeof(player_name));
103 |
104 | //allow steam id
105 | DoAllowJoinMsg(steamId,player_name,client);
106 |
107 | return Plugin_Handled;
108 | }
109 |
110 |
111 | /*****************************************************************
112 |
113 |
114 | P L U G I N F U N C T I O N S
115 |
116 |
117 | *****************************************************************/
118 |
119 | DoAllowJoinMsg( String:steamId[], String:target_name[], client )
120 | {
121 | if( AllowJoinMsg(steamId,target_name) )
122 | {
123 | LogMessage( "\"%L\" allowed custom join message for player \"%s\" (Steam ID: %s)", client, target_name, steamId );
124 | ReplyToCommand(client, "[SM] Allowed custom join message for player %s (Steam ID: %s)", target_name, steamId);
125 | }
126 | else
127 | {
128 | ReplyToCommand(client, "[SM] Player %s (Steam ID: %s) is already allowed custom join message", target_name, steamId);
129 | }
130 | }
131 |
132 |
133 | bool:AllowJoinMsg( String:steamId[], String:player_name[] )
134 | {
135 | if(!KvJumpToKey(hKVCustomJoinMessages, steamId))
136 | {
137 | KvJumpToKey(hKVCustomJoinMessages, steamId, true);
138 | KvSetString(hKVCustomJoinMessages, "playerwasnamed", player_name );
139 |
140 | KvRewind(hKVCustomJoinMessages);
141 | KeyValuesToFile(hKVCustomJoinMessages, g_fileset);
142 |
143 | return true;
144 | }
145 | else
146 | {
147 | KvRewind(hKVCustomJoinMessages);
148 |
149 | return false;
150 | }
151 | }
152 |
153 |
154 | /*****************************************************************
155 |
156 |
157 | A D M I N M E N U F U N C T I O N S
158 |
159 |
160 | *****************************************************************/
161 |
162 | public AdminMenu_AllowJoinMsg(Handle:topmenu,
163 | TopMenuAction:action,
164 | TopMenuObject:object_id,
165 | param,
166 | String:buffer[],
167 | maxlength)
168 | {
169 | if (action == TopMenuAction_DisplayOption)
170 | {
171 | Format(buffer, maxlength, "%s", "Allow custom join message");
172 | }
173 | else if (action == TopMenuAction_SelectOption)
174 | {
175 | DisplayAllowJoinMsgMenu(param);
176 | }
177 | }
178 |
179 | DisplayAllowJoinMsgMenu(client)
180 | {
181 | new Handle:menu = CreateMenu(MenuHandler_AllowJoinMsg);
182 |
183 | decl String:title[100];
184 | Format(title, sizeof(title), "%s:", "Allow custom join message");
185 | SetMenuTitle(menu, title);
186 | SetMenuExitBackButton(menu, true);
187 |
188 | AddTargetsToMenu(menu, client, true, false);
189 |
190 | DisplayMenu(menu, client, MENU_TIME_FOREVER);
191 | }
192 |
193 |
194 | public MenuHandler_AllowJoinMsg(Handle:menu, MenuAction:action, param1, param2)
195 | {
196 | if (action == MenuAction_End)
197 | {
198 | CloseHandle(menu);
199 | }
200 | else if (action == MenuAction_Cancel)
201 | {
202 | if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
203 | {
204 | DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
205 | }
206 | }
207 | else if (action == MenuAction_Select)
208 | {
209 | decl String:info[32];
210 | new userid, target;
211 | new String:steamId[24];
212 | new String:target_name[MAX_TARGET_LENGTH];
213 |
214 |
215 | GetMenuItem(menu, param2, info, sizeof(info));
216 | userid = StringToInt(info);
217 |
218 | if ((target = GetClientOfUserId(userid)) == 0)
219 | {
220 | PrintToChat(param1, "[SM] %t", "Player no longer available");
221 | }
222 | else if (!CanUserTarget(param1, target))
223 | {
224 | PrintToChat(param1, "[SM] %t", "Unable to target");
225 | }
226 | else
227 | {
228 | GetClientName(target, target_name, sizeof(target_name));
229 |
230 | //set allowed custom join in kv file
231 | if( GetClientAuthId(target, AuthId_Steam2, steamId, sizeof(steamId)) )
232 | {
233 | DoAllowJoinMsg(steamId,target_name,param1);
234 |
235 | //inform player of their enabled custom join msg
236 | PrintToChat(target, "[SM] type sm_joinmsg in console to set your custom join message!");
237 | }
238 | else
239 | {
240 | PrintToChat(param1, "[SM] Unable to find player's steam id");
241 | }
242 | }
243 |
244 | /* Re-draw the menu if they're still valid */
245 | if (IsClientInGame(param1) && !IsClientInKickQueue(param1))
246 | {
247 | DisplayAllowJoinMsgMenu(param1);
248 | }
249 | }
250 | }
--------------------------------------------------------------------------------
/cannounce/joinmsg/disallow.sp:
--------------------------------------------------------------------------------
1 | /*****************************************************************
2 |
3 |
4 | F O R W A R D P U B L I C S
5 |
6 |
7 | *****************************************************************/
8 |
9 | SetupJoinMsg_DisAllow()
10 | {
11 | RegAdminCmd("sm_joinmsgoff", Command_DisAllowJoinMsg, CHECKFLAG, "sm_joinmsgoff - disallows a client from setting a custom join message");
12 | RegAdminCmd("sm_joinmsgoffid", Command_DisAllowJoinMsgID, CHECKFLAG, "sm_joinmsgoffid \"\" - allows specified steamid from setting a custom join message");
13 | }
14 |
15 | OnAdminMenuReady_JoinMsg_DAllow(TopMenuObject:player_commands)
16 | {
17 | AddToTopMenu(hTopMenu,
18 | "sm_joinmsgoff",
19 | TopMenuObject_Item,
20 | AdminMenu_DisAllowJoinMsg,
21 | player_commands,
22 | "sm_joinmsgoff",
23 | CHECKFLAG);
24 | }
25 |
26 | /****************************************************************
27 |
28 |
29 | C A L L B A C K F U N C T I O N S
30 |
31 |
32 | ****************************************************************/
33 |
34 | public Action:Command_DisAllowJoinMsg(client, args)
35 | {
36 | decl String:target[65];
37 |
38 | decl String:target_name[MAX_TARGET_LENGTH];
39 | decl target_list[MAXPLAYERS];
40 | decl target_count;
41 | decl bool:tn_is_ml;
42 | new String:steamId[24];
43 |
44 | //not enough arguments, display usage
45 | if (args < 1)
46 | {
47 | ReplyToCommand(client, "[SM] Usage: sm_joinmsgoff ");
48 | return Plugin_Handled;
49 | }
50 |
51 | //get command arguments
52 | GetCmdArg(1, target, sizeof(target));
53 |
54 |
55 | //get the target of this command, return error if invalid
56 | if ((target_count = ProcessTargetString(
57 | target,
58 | client,
59 | target_list,
60 | MAXPLAYERS,
61 | COMMAND_FILTER_NO_MULTI,
62 | target_name,
63 | sizeof(target_name),
64 | tn_is_ml)) <= 0)
65 | {
66 | ReplyToTargetError(client, target_count);
67 | return Plugin_Handled;
68 | }
69 |
70 |
71 | //remove allowed custom join in kv file
72 | if( target_count > 0 && GetClientAuthId(target_list[0], AuthId_Steam2, steamId, sizeof(steamId)) )
73 | {
74 | DoDisAllowJoinMsg( steamId, target_name, client );
75 | }
76 | else
77 | {
78 | ReplyToCommand(client, "[SM] Unable to find player's steam id");
79 | }
80 |
81 | return Plugin_Handled;
82 | }
83 |
84 |
85 |
86 | public Action:Command_DisAllowJoinMsgID(client, args)
87 | {
88 | new String:steamId[24];
89 |
90 | //not enough arguments, display usage
91 | if (args < 1)
92 | {
93 | ReplyToCommand(client, "[SM] Usage: sm_joinmsgoffid \"\"");
94 | return Plugin_Handled;
95 | }
96 |
97 | //get command arguments
98 | GetCmdArg(1, steamId, sizeof(steamId));
99 |
100 | //disallow steam id
101 | DoDisAllowJoinMsg(steamId, "", client);
102 |
103 | return Plugin_Handled;
104 | }
105 |
106 |
107 | /*****************************************************************
108 |
109 |
110 | P L U G I N F U N C T I O N S
111 |
112 |
113 | *****************************************************************/
114 |
115 | DoDisAllowJoinMsg( String:steamId[], String:target_name[], client )
116 | {
117 | if( DisAllowJoinMsg( steamId ) )
118 | {
119 | LogMessage( "\"%L\" disallowed custom join message for player \"%s\" (Steam ID: %s)", client, target_name, steamId );
120 | ReplyToCommand(client, "[SM] Disallowed custom join message for player %s (Steam ID: %s)", target_name, steamId);
121 | }
122 | else
123 | {
124 | ReplyToCommand(client, "[SM] Player %s (Steam ID: %s) is not currently allowed a custom join message", target_name, steamId);
125 | }
126 | }
127 |
128 |
129 | bool:DisAllowJoinMsg( String:steamId[] )
130 | {
131 | if(KvJumpToKey(hKVCustomJoinMessages, steamId))
132 | {
133 | KvDeleteThis(hKVCustomJoinMessages);
134 |
135 | KvRewind(hKVCustomJoinMessages);
136 | KeyValuesToFile(hKVCustomJoinMessages, g_fileset);
137 |
138 | return true;
139 | }
140 | else
141 | {
142 | KvRewind(hKVCustomJoinMessages);
143 |
144 | return false;
145 | }
146 | }
147 |
148 | /*****************************************************************
149 |
150 |
151 | A D M I N M E N U F U N C T I O N S
152 |
153 |
154 | *****************************************************************/
155 |
156 | public AdminMenu_DisAllowJoinMsg(Handle:topmenu,
157 | TopMenuAction:action,
158 | TopMenuObject:object_id,
159 | param,
160 | String:buffer[],
161 | maxlength)
162 | {
163 | if (action == TopMenuAction_DisplayOption)
164 | {
165 | Format(buffer, maxlength, "%s", "Disallow custom join message");
166 | }
167 | else if (action == TopMenuAction_SelectOption)
168 | {
169 | DisplayDisAllowJoinMsgMenu(param);
170 | }
171 | }
172 |
173 | DisplayDisAllowJoinMsgMenu(client)
174 | {
175 | new Handle:menu = CreateMenu(MenuHandler_DisAllowJoinMsg);
176 |
177 | decl String:title[100];
178 | Format(title, sizeof(title), "%s:", "Disallow custom join message");
179 | SetMenuTitle(menu, title);
180 | SetMenuExitBackButton(menu, true);
181 |
182 | AddTargetsToMenu(menu, client, true, false);
183 |
184 | DisplayMenu(menu, client, MENU_TIME_FOREVER);
185 | }
186 |
187 |
188 | public MenuHandler_DisAllowJoinMsg(Handle:menu, MenuAction:action, param1, param2)
189 | {
190 | if (action == MenuAction_End)
191 | {
192 | CloseHandle(menu);
193 | }
194 | else if (action == MenuAction_Cancel)
195 | {
196 | if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
197 | {
198 | DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
199 | }
200 | }
201 | else if (action == MenuAction_Select)
202 | {
203 | decl String:info[32];
204 | new userid, target;
205 | new String:steamId[24];
206 | new String:target_name[MAX_TARGET_LENGTH];
207 |
208 |
209 | GetMenuItem(menu, param2, info, sizeof(info));
210 | userid = StringToInt(info);
211 |
212 | if ((target = GetClientOfUserId(userid)) == 0)
213 | {
214 | PrintToChat(param1, "[SM] %t", "Player no longer available");
215 | }
216 | else if (!CanUserTarget(param1, target))
217 | {
218 | PrintToChat(param1, "[SM] %t", "Unable to target");
219 | }
220 | else
221 | {
222 | GetClientName(target, target_name, sizeof(target_name));
223 |
224 | //remove allowed custom join in kv file
225 | if( GetClientAuthId(target, AuthId_Steam2, steamId, sizeof(steamId)) )
226 | {
227 | DoDisAllowJoinMsg( steamId, target_name, param1 );
228 | }
229 | else
230 | {
231 | PrintToChat(param1, "[SM] Unable to find player's steam id");
232 | }
233 | }
234 |
235 | /* Re-draw the menu if they're still valid */
236 | if (IsClientInGame(param1) && !IsClientInKickQueue(param1))
237 | {
238 | DisplayDisAllowJoinMsgMenu(param1);
239 | }
240 | }
241 | }
--------------------------------------------------------------------------------
/cannounce/joinmsg/sound.sp:
--------------------------------------------------------------------------------
1 | /*****************************************************************
2 |
3 |
4 | F O R W A R D P U B L I C S
5 |
6 |
7 | *****************************************************************/
8 |
9 | SetupJoinSound_Set()
10 | {
11 | RegAdminCmd("sm_setjoinsnd", Command_SetJoinSnd, CHECKFLAG, "sm_setjoinsnd \"\" - sets a custom join sound for specified player");
12 | RegAdminCmd("sm_setjoinsndid", Command_SetJoinSndID, CHECKFLAG, "sm_setjoinsndid \"\" \"\" - sets a custom join sound for specified steam ID");
13 | RegAdminCmd("sm_playsnd", Command_PlaySnd, CHECKFLAG, "sm_playsnd \"\" [entity] - Plays sound file on all clients, entity is optional - default 'from player'");
14 | }
15 |
16 | /****************************************************************
17 |
18 |
19 | C A L L B A C K F U N C T I O N S
20 |
21 |
22 | ****************************************************************/
23 |
24 | public Action:Command_SetJoinSnd(client, args)
25 | {
26 | decl String:target[65];
27 |
28 | decl String:target_name[MAX_TARGET_LENGTH];
29 | decl target_list[MAXPLAYERS];
30 | decl target_count;
31 | decl bool:tn_is_ml;
32 | new String:steamId[24];
33 | new String:sndFile[SOUNDFILE_PATH_LEN];
34 | decl charsSet;
35 |
36 | //not enough arguments, display usage
37 | if (args != 2)
38 | {
39 | ReplyToCommand(client, "[SM] Usage: sm_setjoinsnd \"\"");
40 | return Plugin_Handled;
41 | }
42 |
43 | //get command arguments
44 | GetCmdArg(1, target, sizeof(target));
45 |
46 | //check message length
47 | charsSet = GetCmdArg( 2, sndFile, sizeof(sndFile) );
48 |
49 | if( charsSet > SOUNDFILE_PATH_LEN)
50 | {
51 | ReplyToCommand(client, "[SM] Maximum sound file path length is %d characters", MSGLENGTH );
52 | return Plugin_Handled;
53 | }
54 |
55 | //get the target of this command, return error if invalid
56 | if ((target_count = ProcessTargetString(
57 | target,
58 | client,
59 | target_list,
60 | MAXPLAYERS,
61 | COMMAND_FILTER_NO_MULTI,
62 | target_name,
63 | sizeof(target_name),
64 | tn_is_ml)) <= 0)
65 | {
66 | ReplyToTargetError(client, target_count);
67 | return Plugin_Handled;
68 | }
69 |
70 | //set custom join msg in kv file
71 | if( target_count > 0 && GetClientAuthId(target_list[0], AuthId_Steam2, steamId, sizeof(steamId)) )
72 | {
73 | if( SetJoinSnd( steamId, sndFile ) )
74 | {
75 | LogMessage( "\"%L\" set custom join sound for player \"%s\" (Steam ID: %s)", client, target_name, steamId );
76 | ReplyToCommand(client, "[SM] Auto join sound set for player %s", target_name);
77 | }
78 | else
79 | {
80 | ReplyToCommand(client, "[SM] Player %s is not allowed to have a custom join sound", target_name);
81 | }
82 | }
83 | else
84 | {
85 | ReplyToCommand(client, "[SM] Unable to find player's steam id");
86 | }
87 |
88 | return Plugin_Handled;
89 | }
90 |
91 |
92 | public Action:Command_SetJoinSndID(client, args)
93 | {
94 | decl String:steamId[24];
95 | new String:sndFile[SOUNDFILE_PATH_LEN];
96 | decl charsSet;
97 |
98 | //not enough arguments, display usage
99 | if (args != 2)
100 | {
101 | ReplyToCommand(client, "[SM] Usage: sm_setjoinsndid \"\" \"\"");
102 | return Plugin_Handled;
103 | }
104 |
105 | //get command arguments
106 | GetCmdArg(1, steamId, sizeof(steamId));
107 |
108 | //check message length
109 | charsSet = GetCmdArg( 2, sndFile, sizeof(sndFile) );
110 |
111 | if( charsSet > SOUNDFILE_PATH_LEN)
112 | {
113 | ReplyToCommand(client, "[SM] Maximum sound file path length is %d characters", MSGLENGTH );
114 | return Plugin_Handled;
115 | }
116 |
117 | //set custom join msg in kv file
118 | if( SetJoinSnd( steamId, sndFile ) )
119 | {
120 | LogMessage( "\"%L\" set custom join sound for steam id: \"%s\"", client, steamId );
121 | ReplyToCommand(client, "[SM] Auto join sound set for steam ID \"%s\"", steamId);
122 | }
123 | else
124 | {
125 | ReplyToCommand(client, "[SM] Steam ID \"%s\" is not allowed to have a custom join sound", steamId);
126 | }
127 |
128 | return Plugin_Handled;
129 | }
130 |
131 |
132 | public Action:Command_PlaySnd(client, args)
133 | {
134 | decl String:sFile[256];
135 | new entity = SOUND_FROM_PLAYER;
136 | decl String:arg2[20];
137 |
138 | //not enough arguments, display usage
139 | if (args < 1)
140 | {
141 | ReplyToCommand(client, "[SM] Usage: sm_playsnd \"\" [entity]");
142 | return Plugin_Handled;
143 | }
144 |
145 | //get entity param, if supplied
146 | if (args > 1)
147 | {
148 | GetCmdArg(2, arg2, sizeof(arg2));
149 |
150 | if (StringToIntEx(arg2, entity) == 0)
151 | {
152 | ReplyToCommand(client, "[SM] Invalid entity");
153 | return Plugin_Handled;
154 | }
155 | }
156 |
157 | //get command arguments
158 | GetCmdArg(1, sFile, sizeof(sFile));
159 |
160 | PrecacheSound(sFile);
161 |
162 | //play sound
163 | EmitSoundToAll( sFile, entity);
164 |
165 | ReplyToCommand(client, "[SM] Played Sound \"%s\"", sFile);
166 |
167 | return Plugin_Handled;
168 | }
169 |
170 |
171 | /*****************************************************************
172 |
173 |
174 | P L U G I N F U N C T I O N S
175 |
176 |
177 | *****************************************************************/
178 | bool:SetJoinSnd( String:steamId[], String:sndFile[] )
179 | {
180 | if(KvJumpToKey(hKVCustomJoinMessages, steamId))
181 | {
182 | KvSetString(hKVCustomJoinMessages, "soundfile", sndFile );
183 |
184 | KvRewind(hKVCustomJoinMessages);
185 | KeyValuesToFile(hKVCustomJoinMessages, g_fileset);
186 |
187 | return true;
188 | }
189 | else
190 | {
191 | KvRewind(hKVCustomJoinMessages);
192 |
193 | return false;
194 | }
195 | }
196 |
197 | LoadSoundFilesCustomPlayer()
198 | {
199 | new String:sndFile[SOUNDFILE_PATH_LEN];
200 | new String:sndFileFullPath[SOUNDFILE_PATH_LEN + 6];
201 |
202 | KvGotoFirstSubKey(hKVCustomJoinMessages);
203 |
204 | //cycle thru soundfile values, if they exist, add to download table and precache
205 | do
206 | {
207 | KvGetString(hKVCustomJoinMessages,"soundfile", sndFile, sizeof(sndFile) );
208 |
209 | if( strlen( sndFile ) > 0 )
210 | {
211 | Format(sndFileFullPath, sizeof(sndFileFullPath), "sound/%s", sndFile);
212 |
213 | if( FileExists( sndFileFullPath ) )
214 | {
215 | AddFileToDownloadsTable(sndFileFullPath);
216 |
217 | PrecacheSound(sndFile);
218 | }
219 | else
220 | {
221 | LogError( "[CANNOUNCE] Sound file '%s' does not exist on server", sndFileFullPath );
222 | }
223 | }
224 | }
225 | while (KvGotoNextKey(hKVCustomJoinMessages));
226 |
227 | KvRewind(hKVCustomJoinMessages);
228 | }
229 |
--------------------------------------------------------------------------------
/cannounce/suppress.sp:
--------------------------------------------------------------------------------
1 | /*****************************************************************
2 |
3 |
4 | G L O B A L V A R S
5 |
6 |
7 | *****************************************************************/
8 | new Handle:g_CvarShowConnectionMsg = INVALID_HANDLE;
9 | new Handle:g_CvarShowDisonnectionMsg = INVALID_HANDLE;
10 |
11 |
12 | /*****************************************************************
13 |
14 |
15 | F O R W A R D P U B L I C S
16 |
17 |
18 | *****************************************************************/
19 | SetupSuppress()
20 | {
21 | g_CvarShowConnectionMsg = CreateConVar("sm_ca_showstandard", "0", "shows standard player connected message");
22 | g_CvarShowDisonnectionMsg = CreateConVar("sm_ca_showstandarddisc", "0", "shows standard player discconnected message");
23 |
24 | HookEvent("player_connect", event_PlayerConnect, EventHookMode_Pre);
25 | }
26 |
27 |
28 | /****************************************************************
29 |
30 |
31 | C A L L B A C K F U N C T I O N S
32 |
33 |
34 | ****************************************************************/
35 | public Action:event_PlayerConnect(Handle:event, const String:name[], bool:dontBroadcast)
36 | {
37 | if (!dontBroadcast && !GetConVarInt(g_CvarShowConnectionMsg))
38 | {
39 | decl String:clientName[33], String:networkID[22], String:address[32];
40 | GetEventString(event, "name", clientName, sizeof(clientName));
41 | GetEventString(event, "networkid", networkID, sizeof(networkID));
42 | GetEventString(event, "address", address, sizeof(address));
43 |
44 | new Handle:newEvent = CreateEvent("player_connect", true);
45 | SetEventString(newEvent, "name", clientName);
46 | SetEventInt(newEvent, "index", GetEventInt(event, "index"));
47 | SetEventInt(newEvent, "userid", GetEventInt(event, "userid"));
48 | SetEventString(newEvent, "networkid", networkID);
49 | SetEventString(newEvent, "address", address);
50 |
51 | FireEvent(newEvent, true);
52 |
53 | return Plugin_Handled;
54 | }
55 |
56 | return Plugin_Continue;
57 | }
58 |
59 |
60 | public Action:event_PlayerDisconnect_Suppress(Handle:event, const String:name[], bool:dontBroadcast)
61 | {
62 | if (!dontBroadcast && !GetConVarInt(g_CvarShowDisonnectionMsg))
63 | {
64 | decl String:clientName[33], String:networkID[22], String:reason[65];
65 | GetEventString(event, "name", clientName, sizeof(clientName));
66 | GetEventString(event, "networkid", networkID, sizeof(networkID));
67 | GetEventString(event, "reason", reason, sizeof(reason));
68 |
69 | new Handle:newEvent = CreateEvent("player_disconnect", true);
70 | SetEventInt(newEvent, "userid", GetEventInt(event, "userid"));
71 | SetEventString(newEvent, "reason", reason);
72 | SetEventString(newEvent, "name", clientName);
73 | SetEventString(newEvent, "networkid", networkID);
74 |
75 | FireEvent(newEvent, true);
76 |
77 | return Plugin_Handled;
78 | }
79 |
80 | return Plugin_Continue;
81 | }
--------------------------------------------------------------------------------
/compiled/readyup.smx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TGMaster/Sourcepawn/7a2e63796dfaa1376ae8eb683b8805ac5fdb77b5/compiled/readyup.smx
--------------------------------------------------------------------------------
/config/HUD.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TGMaster/Sourcepawn/7a2e63796dfaa1376ae8eb683b8805ac5fdb77b5/config/HUD.zip
--------------------------------------------------------------------------------
/config/nade_training.cfg:
--------------------------------------------------------------------------------
1 | // Config for server
2 | sv_cheats 1
3 | sv_infinite_ammo 1
4 | ammo_grenade_limit_total 5
5 | mp_warmup_end
6 | mp_freezetime 0
7 | mp_roundtime 60
8 | mp_roundtime_defuse 60
9 | sv_grenade_trajectory 1
10 | sv_grenade_trajectory_time 10
11 | sv_showimpacts 1
12 | sv_showimpacts_time 10
13 | mp_limitteams 0
14 | mp_autoteambalance 0
15 | mp_maxmoney 60000
16 | mp_startmoney 60000
17 | mp_buytime 9999
18 | mp_buy_anywhere 1
19 | mp_restartgame 1
20 |
21 | // Bot commands
22 | bot_add_t
23 | bot_add_ct
24 | bot_kick
25 | bot_stop
26 |
--------------------------------------------------------------------------------
/config/tgmaster.cfg:
--------------------------------------------------------------------------------
1 | // Crosshair
2 | cl_crosshair_drawoutline 0
3 | cl_crosshairalpha "9999"
4 | cl_crosshaircolor "4"
5 | cl_crosshaircolor_b "50"
6 | cl_crosshaircolor_g "250"
7 | cl_crosshaircolor_r "50"
8 | cl_crosshairdot "0"
9 | cl_crosshairgap "-2"
10 | cl_crosshairscale "1000"
11 | cl_crosshairsize "4"
12 | cl_crosshairstyle "4"
13 | cl_crosshairthickness "1"
14 | cl_crosshairusealpha "1"
15 | cl_crosshair_t "1"
16 |
17 | // Useful commands
18 | cl_use_opens_buy_menu 0
19 | snd_headphone_pan_exponent 2
20 | cl_disablefreezecam 1
21 | cl_autowepswitch 0
22 | cl_downloadfilter all
23 | cl_forcepreload 1
24 | cl_autobuy 0
25 | cl_dm_buyrandomweapons 0
26 | muzzleflash_light 1
27 | func_break_max_pieces 15
28 |
29 | // Net Graph
30 | net_graphheight "64"
31 | net_graphmsecs "400"
32 | net_graphpos "2"
33 | net_graphproportionalfont "1" // large netgraph font
34 | net_graphshowinterp "1"
35 | net_graphshowlatency "1"
36 | net_graphshowsvframerate "0"
37 | net_graphsolid "1"
38 | net_graphtext "1"
39 |
40 | // Mouse
41 | m_customaccel "0"
42 | m_customaccel_exponent "1.05"
43 | m_customaccel_max "0"
44 | m_customaccel_scale "0.04"
45 | m_forward "1"
46 | m_mouseaccel1 "0"
47 | m_mouseaccel2 "0"
48 | m_mousespeed "1"
49 | m_pitch "0.022"
50 | m_rawinput "1"
51 | m_side "0.8"
52 | m_yaw "0.022"
53 | zoom_sensitivity_ratio_mouse "1.0"
54 | m_mousespeed "0"
55 | sensitivity 1.76
56 |
57 | // Video
58 | mat_monitorgamma "1.6"
59 | mat_queue_mode "-1" // auto detect multi-core rendering
60 | fps_max "999"
61 | fps_max_menu "145"
62 | r_dynamic "0"
63 | r_drawtracers_firstperson "0"
64 |
65 | // Reposition gun model to mimic source
66 | viewmodel_presetpos "3"
67 | viewmodel_fov "70"
68 | viewmodel_offset_x "2.500000"
69 | viewmodel_offset_y "0"
70 | viewmodel_offset_z "-1.500000"
71 |
72 | // Reduce gun shifting when crouching
73 | cl_viewmodel_shift_left_amt "0.5"
74 | cl_viewmodel_shift_right_amt "0.25"
75 |
76 | // Reduce gun and scope shifting/bobbing when moving
77 | cl_bobcycle "0.98"
78 | cl_bob_lower_amt "5"
79 | cl_bobamt_lat "0.1"
80 | cl_bobamt_vert "0.1"
81 |
82 | // HUD
83 | hud_scaling "0.80"
84 | hud_showtargetid "1"
85 | cl_hud_background_alpha "0.100000"
86 | cl_hud_bomb_under_radar "0"
87 | cl_hud_color "1"
88 | cl_hud_healthammo_style "1"
89 | cl_hud_playercount_pos "1"
90 | cl_hud_playercount_showcount "1"
91 | cl_hud_radar_scale "0.950000"
92 | cl_teamid_overhead_name_alpha "240"
93 | cl_righthand "1"
94 | cl_showloadout "1"
95 |
96 | // Alias commands
97 | alias +jumpthrow "+jump; -attack"; alias -jumpthrow -jump
98 | alias voice_on "voice_scale 0.4; bind k voice_off"
99 | alias voice_off "voice_scale 0; bind k voice_on"
100 | alias +shows "+showscores; net_graph 1"
101 | alias -shows "-showscores; net_graph 0"
102 |
103 | //speaker setting
104 | windows_speaker_config "1"
105 | snd_mixahead "0.05"
106 | snd_headphone_pan_exponent "2"
107 | snd_headphone_pan_radial_weight "2
108 | snd_legacy_surround "0"
109 | snd_pitchquality "1"
110 | dsp_enhance_stereo "0"
111 | voice_mixer_volume "0.8"
112 | voice_scale "0.4"
113 |
114 | // Radar
115 | cl_radar_always_centered "0"
116 | cl_radar_rotate "1"
117 | cl_radar_scale "0.4"
118 | cl_radar_icon_scale_min "0.4"
119 | cl_radar_square_with_scoreboard "0"
120 |
121 |
122 | // Bind keys
123 | bind f2 "rebuy"
124 | bind f3 "disconnect"
125 | bind j +jumpthrow
126 | bind shift "+speed; r_cleardecals"
127 | bind ctrl "+duck; r_cleardecals"
128 | bindtoggle v gameinstructor_enable
129 | bind k "voice_off"
130 | bindtoggle MOUSE5 cl_righthand
131 | bind TAB +shows
132 |
133 | // Rate Settings
134 | rate 128000
135 | cl_updaterate 128
136 | cl_cmdrate 128
137 | cl_interp 0
138 | cl_interpolate 1
139 | cl_interp_ratio 1
140 | cl_predict 1
141 | cl_lagcompensation 1
142 |
143 |
144 | host_writeconfig
145 | echo "TGMaster Config Loaded Successfully!!!"
146 |
--------------------------------------------------------------------------------
/current.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 |
3 | #include
4 | #include
5 | #define L4D2UTIL_STOCKS_ONLY
6 | #include
7 |
8 | #include
9 |
10 | #define MAX(%0,%1) (((%0) > (%1)) ? (%0) : (%1))
11 |
12 | new Handle:hCvarPrintToEveryone;
13 | new Handle:survivor_limit;
14 | new Handle:z_max_player_zombies;
15 |
16 | public OnPluginStart()
17 | {
18 | hCvarPrintToEveryone =
19 | CreateConVar("l4d_global_percent", "1",
20 | "Display boss percentages to entire team when using commands",
21 | FCVAR_PLUGIN);
22 |
23 | RegConsoleCmd("sm_cur", CurrentCmd);
24 | RegConsoleCmd("sm_current", CurrentCmd);
25 |
26 | survivor_limit = FindConVar("survivor_limit");
27 | z_max_player_zombies = FindConVar("z_max_player_zombies");
28 | }
29 |
30 | public Action:CurrentCmd(client, args)
31 | {
32 | new L4D2_Team:team = L4D2_Team:GetClientTeam(client);
33 | if (team == L4D2Team_Spectator)
34 | {
35 | PrintCurrentToClient(client);
36 | }
37 | else
38 | {
39 | if (GetConVarBool(hCvarPrintToEveryone))
40 | {
41 | PrintCurrentToTeam(team);
42 | }
43 | else
44 | {
45 | PrintCurrentToClient(client);
46 | }
47 | }
48 | }
49 |
50 | stock PrintCurrentToClient(client)
51 | {
52 | CPrintToChat(client, "{blue}Current: {olive}%d%%", GetMaxSurvivorCompletion());
53 | }
54 |
55 | stock PrintCurrentToTeam(L4D2_Team:team)
56 | {
57 | new members_found;
58 | new team_max = GetTeamMaxHumans(team);
59 | new max_completion = GetMaxSurvivorCompletion();
60 | for (new client = 1;
61 | client <= MaxClients && members_found < team_max;
62 | client++)
63 | {
64 | if(IsClientInGame(client) && !IsFakeClient(client) &&
65 | L4D2_Team:GetClientTeam(client) == team)
66 | {
67 | members_found++;
68 | CPrintToChat(client, "{blue}Current: {olive}%d%%", max_completion);
69 | }
70 | }
71 | }
72 |
73 | stock GetMaxSurvivorCompletion()
74 | {
75 | new Float:flow = 0.0;
76 | decl Float:tmp_flow;
77 | decl Float:origin[3];
78 | decl Address:pNavArea;
79 | for (new client = 1; client <= MaxClients; client++)
80 | {
81 | if(IsClientInGame(client) &&
82 | L4D2_Team:GetClientTeam(client) == L4D2Team_Survivor)
83 | {
84 | GetClientAbsOrigin(client, origin);
85 | pNavArea = L4D2Direct_GetTerrorNavArea(origin);
86 | if (pNavArea != Address_Null)
87 | {
88 | tmp_flow = L4D2Direct_GetTerrorNavAreaFlow(pNavArea);
89 | flow = MAX(flow, tmp_flow);
90 | }
91 | }
92 | }
93 | return RoundToNearest(flow * 100 / L4D2Direct_GetMapMaxFlowDistance());
94 | }
95 |
96 | stock GetTeamMaxHumans(L4D2_Team:team)
97 | {
98 | if (team == L4D2Team_Survivor)
99 | {
100 | return GetConVarInt(survivor_limit);
101 | }
102 | else if (team == L4D2Team_Infected)
103 | {
104 | return GetConVarInt(z_max_player_zombies);
105 | }
106 | return MaxClients;
107 | }
108 |
--------------------------------------------------------------------------------
/fix_fastmelee.sp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #define PL_VERSION "2.1"
6 |
7 | new Float:fLastMeleeSwing[MAXPLAYERS + 1];
8 | new bool:bLate;
9 |
10 | public Plugin:myinfo =
11 | {
12 | name = "Fast melee fix",
13 | author = "sheo",
14 | description = "Fixes the bug with too fast melee attacks",
15 | version = PL_VERSION,
16 | url = "http://steamcommunity.com/groups/b1com"
17 | }
18 |
19 | public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
20 | {
21 | bLate = late;
22 | return APLRes_Success;
23 | }
24 |
25 | public OnPluginStart()
26 | {
27 | decl String:gfstring[128];
28 | GetGameFolderName(gfstring, sizeof(gfstring));
29 | if (!StrEqual(gfstring, "left4dead2", false))
30 | {
31 | SetFailState("Plugin supports Left 4 dead 2 only!");
32 | }
33 | HookEvent("weapon_fire", Event_WeaponFire);
34 | CreateConVar("l4d2_fast_melee_fix_version", PL_VERSION, "Fast melee fix version", FCVAR_PLUGIN | FCVAR_NOTIFY);
35 | if (bLate)
36 | {
37 | for (new i = 1; i <= MaxClients; i++)
38 | {
39 | if (IsClientInGame(i) && !IsFakeClient(i))
40 | {
41 | SDKHook(i, SDKHook_WeaponSwitchPost, OnWeaponSwitched);
42 | }
43 | }
44 | }
45 | }
46 |
47 | public OnClientPutInServer(client)
48 | {
49 | if (!IsFakeClient(client))
50 | {
51 | SDKHook(client, SDKHook_WeaponSwitchPost, OnWeaponSwitched);
52 | }
53 | fLastMeleeSwing[client] = 0.0;
54 | }
55 |
56 | public Action:Event_WeaponFire(Handle:event, const String:name[], bool:dontBroadcast)
57 | {
58 | new client = GetClientOfUserId(GetEventInt(event, "userid"));
59 | if (client > 0 && !IsFakeClient(client))
60 | {
61 | decl String:sBuffer[64];
62 | GetEventString(event, "weapon", sBuffer, sizeof(sBuffer));
63 | if (StrEqual(sBuffer, "melee"))
64 | {
65 | fLastMeleeSwing[client] = GetGameTime();
66 | }
67 | }
68 | }
69 |
70 | public OnWeaponSwitched(client, weapon)
71 | {
72 | if (!IsFakeClient(client))
73 | {
74 | decl String:sBuffer[32];
75 | GetEntityClassname(weapon, sBuffer, sizeof(sBuffer));
76 | if (StrEqual(sBuffer, "weapon_melee"))
77 | {
78 | new Float:fShouldbeNextAttack = fLastMeleeSwing[client] + 0.92;
79 | new Float:fByServerNextAttack = GetGameTime() + 0.5;
80 | SetEntPropFloat(weapon, Prop_Send, "m_flNextPrimaryAttack", (fShouldbeNextAttack > fByServerNextAttack) ? fShouldbeNextAttack : fByServerNextAttack);
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/hardcoop/ai_aggressivespecials.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 |
3 | #define DEBUG
4 | #define KICKDELAY 0.1
5 | #define INFECTED_TEAM 3
6 | #define ASSAULT_DELAY 0.3 // using 0.3 to be safe (command does not register in the first 0.2 seconds after spawn)
7 |
8 | #define PLUGIN_AUTHOR "Breezy"
9 | #define PLUGIN_VERSION "1.0"
10 |
11 | #include
12 | #include
13 | #define L4D2UTIL_STOCKS_ONLY
14 | #include
15 | #include
16 |
17 | public Plugin:myinfo =
18 | {
19 | name = "AI: Aggressive Specials",
20 | author = PLUGIN_AUTHOR,
21 | description = "Force SI to be aggressive",
22 | version = PLUGIN_VERSION,
23 | url = ""
24 | };
25 |
26 |
27 | new Handle:hCvarBoomerExposedTimeTolerance;
28 | new Handle:hCvarBoomerVomitDelay;
29 |
30 | public OnPluginStart() {
31 | hCvarBoomerExposedTimeTolerance = FindConVar("boomer_exposed_time_tolerance");
32 | hCvarBoomerVomitDelay = FindConVar("boomer_vomit_delay");
33 | HookEvent("player_spawn", OnPlayerSpawn, EventHookMode_Pre);
34 | HookEvent("ability_use", OnAbilityUse, EventHookMode_Pre);
35 | }
36 |
37 | public Action:L4D_OnFirstSurvivorLeftSafeArea(client) {
38 | SetConVarFloat(hCvarBoomerExposedTimeTolerance, 10000.0);
39 | SetConVarFloat(hCvarBoomerVomitDelay, 0.1);
40 | }
41 |
42 | public OnPluginEnd() {
43 | ResetConVar(hCvarBoomerExposedTimeTolerance);
44 | ResetConVar(hCvarBoomerVomitDelay);
45 | }
46 |
47 | /***********************************************************************************************************************************************************************************
48 |
49 | AGGRESSIVE UPON SPAWN
50 |
51 | ***********************************************************************************************************************************************************************************/
52 |
53 | // Command SI to be aggressive so they do not run away
54 | public Action:OnPlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast) {
55 | new client = GetClientOfUserId(GetEventInt(event, "userid"));
56 | if (IsBotInfected(client)) {
57 | CreateTimer(ASSAULT_DELAY, Timer_PostSpawnAssault, _, TIMER_FLAG_NO_MAPCHANGE);
58 | }
59 | }
60 |
61 | public Action:Timer_PostSpawnAssault(Handle:timer) {
62 | CheatCommand("nb_assault");
63 | return Plugin_Stop;
64 | }
65 |
66 | /***********************************************************************************************************************************************************************************
67 |
68 | STOP SMOKERS & SPITTERS FLEEING
69 |
70 | ***********************************************************************************************************************************************************************************/
71 |
72 | // Stop smokers and spitters running away
73 | public Action:OnAbilityUse(Handle:event, const String:name[], bool:dontBroadcast) {
74 | new String:abilityName[MAX_NAME_LENGTH];
75 | GetEventString(event, "ability", abilityName, sizeof(abilityName));
76 | if (StrEqual(abilityName, "ability_tongue") || StrEqual(abilityName, "ability_spit")) {
77 | new client = GetClientOfUserId(GetEventInt(event, "userid"));
78 | SetEntityMoveType(client, MOVETYPE_NONE);
79 | }
80 | }
81 |
82 | /***********************************************************************************************************************************************************************************
83 |
84 | UTILITY
85 |
86 | ***********************************************************************************************************************************************************************************/
87 |
88 | // Executes through a dummy client, without setting sv_cheats to 1, a console command marked as a cheat
89 | CheatCommand(String:command[], String:argument1[] = "", String:argument2[] = "") {
90 | new anyclient = GetAnyClient();
91 | if (anyclient == 0)
92 | {
93 | anyclient = CreateFakeClient("Bot");
94 | if (anyclient == 0)
95 | {
96 | return;
97 | }
98 | }
99 |
100 | new flags = GetCommandFlags(command);
101 | SetCommandFlags(command, flags & ~FCVAR_CHEAT);
102 |
103 | FakeClientCommand(anyclient, "%s %s %s", command, argument1, argument2);
104 |
105 | SetCommandFlags(command, flags);
106 | }
107 |
108 | bool:IsBotInfected(client) {
109 | return (IsValidClient(client) && GetClientTeam(client) == INFECTED_TEAM && IsFakeClient(client) && IsPlayerAlive(client));
110 | }
111 |
112 | bool:IsValidClient(client) {
113 | if ( !( 1 <= client <= MaxClients ) || !IsClientInGame(client) ) return false;
114 | return true;
115 | }
116 |
117 | public GetAnyClient()
118 | {
119 | new i;
120 | for (i=1;i<=GetMaxClients();i++)
121 | {
122 | if (IsClientConnected(i) && IsClientInGame(i) && (!IsFakeClient(i)))
123 | {
124 | return i;
125 | }
126 | }
127 | return 0;
128 | }
129 |
--------------------------------------------------------------------------------
/hardcoop/ai_chargefromclose.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 | #include
3 | #define ZC_CHARGER 6 //zombie class
4 | #define INFECTED_TEAM 3
5 | #define MAX_CHARGE_PROXIMITY 400
6 |
7 | public Plugin:myinfo = {
8 | name = "AI: Charge From Close",
9 | author = "Breezy",
10 | description = "Force AI chargers to get close to survivors before charging",
11 | version = "1.0"
12 | };
13 |
14 | // custom convar
15 | new Handle:hCvarChargeProximity;
16 | new g_iChargeProximity;
17 |
18 | new bShouldCharge[MAXPLAYERS];
19 |
20 | public OnPluginStart() {
21 | // "ai_charge_proximity"
22 | hCvarChargeProximity = CreateConVar("ai_charge_proximity", "300", "How close a charger will approach before charging");
23 | g_iChargeProximity = GetConVarInt(hCvarChargeProximity);
24 | HookConVarChange(hCvarChargeProximity, OnCvarChange);
25 |
26 | HookEvent("player_spawn", OnPlayerSpawn, EventHookMode_Pre);
27 | }
28 |
29 | //Update convars if they have been changed midgame
30 | public OnCvarChange(Handle:convar, const String:oldValue[], const String:newValue[]) {
31 | if (!StrEqual(oldValue, newValue)) g_iChargeProximity = GetConVarInt(hCvarChargeProximity);
32 | }
33 |
34 | /***********************************************************************************************************************************************************************************
35 |
36 | KEEP CHARGE ON COOLDOWN UNTIL WITHIN PROXIMITY
37 |
38 | ***********************************************************************************************************************************************************************************/
39 |
40 | // Initiate spawned chargers
41 | public Action:OnPlayerSpawn(Handle:event, String:name[], bool:dontBroadcast) {
42 | new client = GetClientOfUserId(GetEventInt(event, "userid"));
43 | if (IsBotCharger(client)) {
44 | bShouldCharge[client] = false;
45 | }
46 | }
47 |
48 | public Action:OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:angles[3], &weapon) {
49 | // Proceed for charger bots
50 | if(IsBotCharger(client)) {
51 | new charger = client;
52 | new iProximity = GetSurvivorProximity(charger);
53 | new chargeDistance = GetRandomInt(g_iChargeProximity, MAX_CHARGE_PROXIMITY);
54 | if (iProximity > chargeDistance) { // if charger has not yet approached within range
55 | if (!bShouldCharge[charger]) { // prevent charge until survivors are within the defined proximity
56 | new chargeEntity = GetEntPropEnt(charger, Prop_Send, "m_customAbility");
57 | if (chargeEntity > 0) { // charger entity persists for a short while after death; check ability entity is valid
58 | SetEntPropFloat(chargeEntity, Prop_Send, "m_timestamp", GetGameTime() + 0.1); // keep extending cooldown period
59 | }
60 | }
61 | } else {
62 | bShouldCharge[charger] = true; // charger has been within proximity
63 | }
64 | }
65 | return Plugin_Changed;
66 | }
67 |
68 | /***********************************************************************************************************************************************************************************
69 |
70 | UTILITY
71 |
72 | ***********************************************************************************************************************************************************************************/
73 |
74 | bool:IsBotCharger(client) {
75 | // Check the input is valid
76 | if (!IsValidClient(client)) return false;
77 | // Check if player is on the infected team, a jockey, and a bot
78 | new zombieClass = GetEntProp(client, Prop_Send, "m_zombieClass");
79 | if (GetClientTeam(client) == INFECTED_TEAM) {
80 | if (zombieClass == ZC_CHARGER) {
81 | if(IsFakeClient(client)) {
82 | return true;
83 | }
84 | }
85 | }
86 | return false;
87 | }
88 |
89 | GetSurvivorProximity(referenceClient) {
90 | // Get the reference's position
91 | new Float:referencePosition[3];
92 | GetEntPropVector(referenceClient, Prop_Send, "m_vecOrigin", referencePosition);
93 | // Find the proximity of the closest survivor
94 | new iClosestAbsDisplacement = -1; // closest absolute displacement
95 | for (new client = 1; client < MaxClients; client++) {
96 | if (IsValidClient(client) && IsSurvivor(client)) {
97 | // Get displacement between this survivor and the reference
98 | new Float:survivorPosition[3];
99 | GetEntPropVector(client, Prop_Send, "m_vecOrigin", survivorPosition);
100 | new iAbsDisplacement = RoundToNearest(GetVectorDistance(referencePosition, survivorPosition));
101 | // Start with the absolute displacement to the first survivor found:
102 | if (iClosestAbsDisplacement == -1) {
103 | iClosestAbsDisplacement = iAbsDisplacement;
104 | } else if (iAbsDisplacement < iClosestAbsDisplacement) { // closest survivor so far
105 | iClosestAbsDisplacement = iAbsDisplacement;
106 | }
107 | }
108 | }
109 | // return the closest survivor's proximity
110 | return iClosestAbsDisplacement;
111 | }
112 |
113 | bool:IsValidClient(client) {
114 | if ( !( 1 <= client <= MaxClients ) || !IsClientInGame(client) ) return false;
115 | return true;
116 | }
117 |
118 | bool:IsSurvivor(client) {
119 | return (IsValidClient(client) && GetClientTeam(client) == 2);
120 | }
--------------------------------------------------------------------------------
/hardcoop/ai_smokersettings.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 |
3 | #define DEBUG 1
4 |
5 | #define PLUGIN_AUTHOR "Breezy"
6 | #define PLUGIN_VERSION "1.0"
7 |
8 | #include
9 | #include
10 |
11 | new Handle:hCvarTongueDelay;
12 | new Handle:hCvarTongueRange;
13 | new Handle:hCvarSmokerHealth;
14 | new Handle:hCvarChokeDamageInterrupt;
15 | new Handle:hCvarChokeDamage;
16 |
17 | public Plugin:myinfo =
18 | {
19 | name = "AI: Smoker Settings",
20 | author = PLUGIN_AUTHOR,
21 | description = "Adjusts AI smokers to inflict and receive damage like players",
22 | version = PLUGIN_VERSION,
23 | url = ""
24 | };
25 |
26 | public OnPluginStart() {
27 | // Smoker health
28 | hCvarSmokerHealth = FindConVar("z_gas_health");
29 | HookConVarChange(hCvarSmokerHealth, ConVarChanged:OnSmokerHealthChanged);
30 | // Damage required to kill a smoker using its tongue
31 | hCvarChokeDamageInterrupt = FindConVar("tongue_break_from_damage_amount"); // default 50
32 | // Delay before smoker shoots its tongue
33 | hCvarTongueDelay = FindConVar("smoker_tongue_delay"); // default 1.5
34 | // Range of smoker tongue
35 | hCvarTongueRange = FindConVar("tongue_range"); // default 750
36 | // Damage done by choke
37 | hCvarChokeDamage = FindConVar("tongue_choke_damage_amount"); // default 10
38 | }
39 |
40 | public Action:L4D_OnFirstSurvivorLeftSafeArea(client) {
41 | SetCheatConVarInt(hCvarChokeDamageInterrupt, GetConVarInt(hCvarSmokerHealth));
42 | SetCheatConVarFloat(hCvarTongueDelay, 0.5);
43 | SetCheatConVarInt(hCvarTongueRange, 500);
44 | SetCheatConVarInt(hCvarChokeDamage, 4);
45 | }
46 |
47 | // Update choke damage interrupt to match smoker max health
48 | public Action:OnSmokerHealthChanged() {
49 | SetConVarInt(hCvarChokeDamageInterrupt, GetConVarInt(hCvarSmokerHealth));
50 | }
51 |
52 | public OnPluginEnd() {
53 | ResetConVar(hCvarChokeDamageInterrupt);
54 | ResetConVar(hCvarTongueDelay);
55 | ResetConVar(hCvarTongueRange);
56 | ResetConVar(hCvarChokeDamage);
57 | }
58 |
59 | SetCheatConVarInt(Handle:hCvarHandle, value) {
60 | // unset cheat flag
61 | new cvarFlags = GetConVarFlags(hCvarHandle);
62 | SetConVarFlags(hCvarHandle, cvarFlags ^ FCVAR_CHEAT);
63 | // set new value
64 | SetConVarInt(hCvarHandle, value);
65 | // reset cheat flag
66 | SetConVarFlags(hCvarHandle, cvarFlags);
67 | }
68 |
69 | SetCheatConVarFloat(Handle:hCvarHandle, Float:value) {
70 | // unset cheat flag
71 | new cvarFlags = GetConVarFlags(hCvarHandle);
72 | SetConVarFlags(hCvarHandle, cvarFlags ^ FCVAR_CHEAT);
73 | // set new value
74 | SetConVarFloat(hCvarHandle, value);
75 | // reset cheat flag
76 | SetConVarFlags(hCvarHandle, cvarFlags);
77 | }
78 |
--------------------------------------------------------------------------------
/hardcoop/ai_tankbehaviour.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | // Define
10 | #define DEBUG
11 | #define INFECTED_TEAM 3
12 | #define ZC_TANK 8
13 | #define PLUGIN_AUTHOR "Breezy"
14 | #define PLUGIN_VERSION "1.0"
15 |
16 | // Bhop
17 | #define BoostForward 60.0
18 |
19 | // Velocity
20 | enum VelocityOverride {
21 | VelocityOvr_None = 0,
22 | VelocityOvr_Velocity,
23 | VelocityOvr_OnlyWhenNegative,
24 | VelocityOvr_InvertReuseVelocity
25 | };
26 |
27 | public Plugin:myinfo =
28 | {
29 | name = "AI: Tank Behaviour",
30 | author = PLUGIN_AUTHOR,
31 | description = "Blocks AI tanks from throwing rocks",
32 | version = PLUGIN_VERSION,
33 | url = ""
34 | };
35 |
36 | /*
37 | ===================================================================================
38 | = TANK BHOP =
39 | ===================================================================================
40 | */
41 | public Action:OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:angles[3], &weapon, &subtype, &cmdnum, &tickcount, &seed, mouse[2]) {
42 | //Proceed if this player is a tank
43 | if(IsBotTank(client)) {
44 | new tank = client;
45 | new flags = GetEntityFlags(tank);
46 |
47 | // Get the player velocity:
48 | new float:fVelocity[3];
49 | GetEntPropVector(client, Prop_Data, "m_vecVelocity", fVelocity);
50 | new float:currentspeed = SquareRoot(Pow(fVelocity[0],2.0)+Pow(fVelocity[1],2.0));
51 | //PrintCenterTextAll("Tank Speed: %.1f", currentspeed);
52 |
53 | // Get Angle of Tank
54 | decl Float:clientEyeAngles[3];
55 | GetClientEyeAngles(tank,clientEyeAngles);
56 |
57 | // Start fast pouncing if close enough to survivors
58 | new iSurvivorsProximity = GetSurvivorProximity(tank);
59 | new bool:bHasSight = bool:GetEntProp(tank, Prop_Send, "m_hasVisibleThreats"); //Line of sight to survivors
60 |
61 | // Near survivors
62 | if (bHasSight && (450 > iSurvivorsProximity > 100) && currentspeed > 190.0) // Random number to make bhop?
63 | {
64 | buttons &= ~IN_ATTACK2; // Block throwing rock
65 | if (flags & FL_ONGROUND) {
66 | buttons |= IN_DUCK;
67 | buttons |= IN_JUMP;
68 | if(buttons & IN_FORWARD)
69 | Client_Push(client,clientEyeAngles,BoostForward,VelocityOverride:{VelocityOvr_None,VelocityOvr_None,VelocityOvr_None});
70 |
71 | if(buttons & IN_BACK){
72 | clientEyeAngles[1] += 180.0;
73 | Client_Push(client,clientEyeAngles,BoostForward,VelocityOverride:{VelocityOvr_None,VelocityOvr_None,VelocityOvr_None});
74 | }
75 |
76 | if(buttons & IN_MOVELEFT){
77 | clientEyeAngles[1] += 90.0;
78 | Client_Push(client,clientEyeAngles,BoostForward,VelocityOverride:{VelocityOvr_None,VelocityOvr_None,VelocityOvr_None});
79 | }
80 |
81 | if(buttons & IN_MOVERIGHT){
82 | clientEyeAngles[1] += -90.0;
83 | Client_Push(client,clientEyeAngles,BoostForward,VelocityOverride:{VelocityOvr_None,VelocityOvr_None,VelocityOvr_None});
84 | }
85 | }
86 | //Block Jumping and Crouching when on ladder
87 | if (GetEntityMoveType(tank) & MOVETYPE_LADDER) {
88 | buttons &= ~IN_JUMP;
89 | buttons &= ~IN_DUCK;
90 | }
91 | }
92 | }
93 | return Plugin_Changed;
94 | }
95 |
96 | public Action:L4D2_OnSelectTankAttack(client, &sequence)
97 | {
98 | if (IsFakeClient(client) && sequence == 50)
99 | {
100 | sequence = GetRandomInt(0, 1) ? 49 : 51;
101 | return Plugin_Handled;
102 | }
103 | return Plugin_Continue;
104 | }
105 |
106 | bool:IsBotTank(client) {
107 | // Check the input is valid
108 | if (!IsValidClient(client)) return false;
109 | // Check if player is on the infected team, a hunter, and a bot
110 | if (GetClientTeam(client) == INFECTED_TEAM) {
111 | new zombieClass = GetEntProp(client, Prop_Send, "m_zombieClass");
112 | if (zombieClass == ZC_TANK) {
113 | if(IsFakeClient(client)) { // is a bot
114 | return true;
115 | }
116 | }
117 | }
118 | return false; // otherwise
119 | }
120 |
121 | bool:IsValidClient(client) {
122 | if ( !( 1 <= client <= MaxClients ) || !IsClientInGame(client) ) return false;
123 | return true;
124 | }
125 |
126 | bool:IsSurvivor(client)
127 | {
128 | return (client > 0 && client <= MaxClients && IsClientInGame(client) && GetClientTeam(client) == 2);
129 | }
130 |
131 | GetSurvivorProximity(referenceClient) {
132 | // Get the reference's position
133 | new Float:referencePosition[3];
134 | GetEntPropVector(referenceClient, Prop_Send, "m_vecOrigin", referencePosition);
135 | // Find the proximity of the closest survivor
136 | new iClosestAbsDisplacement = -1; // closest absolute displacement
137 | for (new client = 1; client < MaxClients; client++) {
138 | if (IsValidClient(client) && IsSurvivor(client)) {
139 | // Get displacement between this survivor and the reference
140 | new Float:survivorPosition[3];
141 | GetEntPropVector(client, Prop_Send, "m_vecOrigin", survivorPosition);
142 | new iAbsDisplacement = RoundToNearest(GetVectorDistance(referencePosition, survivorPosition));
143 | // Start with the absolute displacement to the first survivor found:
144 | if (iClosestAbsDisplacement == -1) {
145 | iClosestAbsDisplacement = iAbsDisplacement;
146 | } else if (iAbsDisplacement < iClosestAbsDisplacement) { // closest survivor so far
147 | iClosestAbsDisplacement = iAbsDisplacement;
148 | }
149 | }
150 | }
151 | // return the closest survivor's proximity
152 | return iClosestAbsDisplacement;
153 | }
154 |
155 |
156 | // Thanks Chanz (Infinite Jumping plugin)
157 | stock Client_Push(client, Float:clientEyeAngle[3], Float:power, VelocityOverride:override[3]=VelocityOvr_None)
158 | {
159 | decl Float:forwardVector[3],
160 | Float:newVel[3];
161 |
162 | GetAngleVectors(clientEyeAngle, forwardVector, NULL_VECTOR, NULL_VECTOR);
163 | NormalizeVector(forwardVector, forwardVector);
164 | ScaleVector(forwardVector, power);
165 | //PrintToChatAll("Tank velocity: %.2f", forwardVector[1]);
166 |
167 | Entity_GetAbsVelocity(client,newVel);
168 |
169 | for(new i=0;i<3;i++){
170 | switch(override[i]){
171 | case VelocityOvr_Velocity:{
172 | newVel[i] = 0.0;
173 | }
174 | case VelocityOvr_OnlyWhenNegative:{
175 | if(newVel[i] < 0.0){
176 | newVel[i] = 0.0;
177 | }
178 | }
179 | case VelocityOvr_InvertReuseVelocity:{
180 | if(newVel[i] < 0.0){
181 | newVel[i] *= -1.0;
182 | }
183 | }
184 | }
185 |
186 | newVel[i] += forwardVector[i];
187 | }
188 |
189 | Entity_SetAbsVelocity(client,newVel);
190 | }
--------------------------------------------------------------------------------
/hardcoop/autowipe.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 | #define AS_DEBUG 0
3 | #define GRACETIME 7.0
4 | #define TEAM_SURVIVOR 2
5 | #include
6 | #include
7 |
8 | // This plugin was created because of a Hard12 bug where one ore more survivors were not taking damage while pinned
9 | // by special infected. If the whole team is immobilised, they get a grace period before they are AutoWiped.
10 | public Plugin:myinfo = {
11 | name = "AutoWipe",
12 | author = "Breezy",
13 | description = "Wipes the team if they are simultaneously incapped/pinned for a period of time",
14 | version = "1.0"
15 | };
16 |
17 | new bool:g_bCanAllowNewAutowipe = false; // start true to prevent autowipe being activated at round start
18 |
19 | public OnPluginStart() {
20 | // Disabling autowipe
21 | HookEvent("map_transition", EventHook:DisableAutoWipe, EventHookMode_PostNoCopy);
22 | HookEvent("mission_lost", EventHook:DisableAutoWipe, EventHookMode_PostNoCopy);
23 | }
24 |
25 | public DisableAutoWipe() {
26 | g_bCanAllowNewAutowipe = false; // prevents autowipe from being called until next map
27 | }
28 |
29 | public Action:L4D_OnFirstSurvivorLeftSafeArea(client) {
30 | g_bCanAllowNewAutowipe = true;
31 | }
32 |
33 | public OnGameFrame() {
34 | // activate AutoWipe if necessary
35 | if (g_bCanAllowNewAutowipe) {
36 | if (IsTeamImmobilised()) {
37 | //PrintToChatAll("[AW] Initiating an AutoWipe...");
38 | CreateTimer(GRACETIME, Timer_AutoWipe, _, TIMER_FLAG_NO_MAPCHANGE);
39 | g_bCanAllowNewAutowipe = false;
40 | }
41 | }
42 | }
43 |
44 | public Action:Timer_AutoWipe(Handle:timer) {
45 | if (IsTeamImmobilised()) {
46 | WipeSurvivors();
47 | //PrintToChatAll("[AW] AutoWiped survivors!");
48 | return Plugin_Stop;
49 | } else {
50 | //PrintToChatAll("[AW] ...AutoWipe cancelled!");
51 | g_bCanAllowNewAutowipe = true;
52 | return Plugin_Stop;
53 | }
54 | }
55 |
56 | WipeSurvivors() { //incap everyone
57 | for (new client = 1; client < MaxClients; client++) {
58 | if (IsSurvivor(client) && IsPlayerAlive(client)) {
59 | SetEntProp(client, Prop_Send, "m_isIncapacitated", true);
60 | }
61 | }
62 | }
63 |
64 | /***********************************************************************************************************************************************************************************
65 |
66 | UTILITY
67 |
68 | ***********************************************************************************************************************************************************************************/
69 |
70 | bool:IsTeamImmobilised() {
71 | //Check if there is still an upright survivor
72 | new bool:bIsTeamImmobilised = true;
73 | for (new client = 1; client < MaxClients; client++) {
74 | // If a survivor is found to be alive and neither pinned nor incapacitated
75 | // team is not immobilised.
76 | if (IsSurvivor(client) && IsPlayerAlive(client)) {
77 | if ( !IsPinned(client) && !IsIncapacitated(client) ) {
78 | bIsTeamImmobilised = false;
79 | #if AS_DEBUG
80 | decl String:ClientName[32];
81 | GetClientName(client, ClientName, sizeof(ClientName));
82 | LogMessage("IsTeamImmobilised() -> %s is mobile, team not immobilised: \x05", ClientName);
83 | #endif
84 | break;
85 | }
86 | }
87 | }
88 | return bIsTeamImmobilised;
89 | }
90 |
91 | bool:IsPinned(client) {
92 | new bool:bIsPinned = false;
93 | if (IsSurvivor(client)) {
94 | // check if held by:
95 | if (GetEntPropEnt(client, Prop_Send, "m_tongueOwner") > 0) bIsPinned = true; // smoker
96 | if (GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") > 0) bIsPinned = true; // hunter
97 | if (GetEntPropEnt(client, Prop_Send, "m_pummelAttacker") > 0) bIsPinned = true; // charger
98 | if (GetEntPropEnt(client, Prop_Send, "m_jockeyAttacker") > 0) bIsPinned = true; // jockey
99 | }
100 | return bIsPinned;
101 | }
102 |
103 | bool:IsIncapacitated(client) {
104 | new bool:bIsIncapped = false;
105 | if ( IsSurvivor(client) ) {
106 | if (GetEntProp(client, Prop_Send, "m_isIncapacitated") > 0) bIsIncapped = true;
107 | if (!IsPlayerAlive(client)) bIsIncapped = true;
108 | }
109 | return bIsIncapped;
110 | }
111 |
112 | bool:IsSurvivor(client) {
113 | return IsValidClient(client) && GetClientTeam(client) == TEAM_SURVIVOR;
114 | }
115 |
116 | bool:IsValidClient(client) {
117 | if (client <= 0 || client > MaxClients || !IsClientConnected(client)) return false;
118 | return IsClientInGame(client);
119 | }
120 |
--------------------------------------------------------------------------------
/hardcoop/mapskipper.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 | #include
3 | #include
4 | #include // ForceChangeLevel()
5 | #include
6 | #include
7 |
8 | #define MISSIONS_PATH "missions"
9 | #define DELAY_FORCEMAP 6.5
10 | #define MS_DEBUG 0
11 |
12 | /*
13 | * Bibliography
14 | * "[L4D/2] Campaign Manager" by Bigbuck
15 | */
16 |
17 | new String:NextMap[256];
18 |
19 | new bool:g_bIsFinale;
20 |
21 | new bool:g_bCanRetry;
22 | new Handle:hCvarEnableRetry;
23 |
24 | public Plugin: myinfo = {
25 | name = "Map Skipper",
26 | author = "Breezy",
27 | description = "Skip to next map in coop when wiping",
28 | version = "1.0",
29 | url = ""
30 | };
31 |
32 | public OnPluginStart() {
33 | // Make sure the 'missions' folder exists
34 | if (!DirExists(MISSIONS_PATH)) {
35 | SetFailState("Missions directory does not exist on this server. Map Skipper cannot continue operation");
36 | }
37 | hCvarEnableRetry = CreateConVar("enable_retry", "1", "Enable retry of a map if team wipes");
38 | g_bCanRetry = GetConVarBool(hCvarEnableRetry);
39 | RegConsoleCmd("sm_toggleretry", Cmd_ToggleRetry);
40 | HookEvent("mission_lost", EventHook:OnMissionLost, EventHookMode_PostNoCopy);
41 | }
42 |
43 | public Action:L4D_OnFirstSurvivorLeftSafeArea(client) {
44 | PrintRetryOption();
45 | }
46 |
47 | public Action:Cmd_ToggleRetry(client, args) {
48 | g_bCanRetry = !g_bCanRetry;
49 | PrintRetryOption();
50 | }
51 |
52 | PrintRetryOption() {
53 | if (g_bCanRetry) {
54 | Client_PrintToChatAll(true, "Retry is {G}enabled!");
55 | } else {
56 | Client_PrintToChatAll(true, "Retry is {O}disabled!");
57 | }
58 | }
59 |
60 | public OnMissionLost() {
61 | if (GetNextMapName()) {
62 | CreateTimer(DELAY_FORCEMAP, Timer_ForceNextMap, _, TIMER_FLAG_NO_MAPCHANGE);
63 | }
64 | }
65 |
66 | public Action:Timer_ForceNextMap(Handle:timer) {
67 | if (!g_bCanRetry){
68 | //Fire a map_transition event for static scoremod
69 | new Handle:event = CreateEvent("map_transition");
70 | if (g_bIsFinale) {
71 | SetEventBool(event, "finale", true);
72 | }
73 | FireEvent(event);
74 | // Change level
75 | ForceChangeLevel(NextMap, "Map Skipper");
76 | LogMessage("Force changing map to %s", NextMap);
77 | }
78 | }
79 |
80 | bool:GetNextMapName() { // returns true if the next map was found
81 | // Open the missions directory
82 | new Handle: missions_dir = INVALID_HANDLE;
83 | missions_dir = OpenDirectory(MISSIONS_PATH);
84 | if (missions_dir == INVALID_HANDLE) {
85 | SetFailState("Cannot open missions directory");
86 | }
87 |
88 | // Setup strings
89 | new String: current_map[256]; //current map being played
90 | GetCurrentMap(current_map, sizeof(current_map));
91 | LogMessage("Current map: %s", current_map);
92 | decl String: buffer[256];
93 | decl String: full_path[256];
94 |
95 | // Loop through all the mission text files
96 | while (ReadDirEntry(missions_dir, buffer, sizeof(buffer))) {
97 | // Skip folders and credits file
98 | if (DirExists(buffer) || StrEqual(buffer, "credits.txt", false)) {continue;}
99 |
100 | // Create a keyvalues structure from the current iteration's mission .txt
101 | Format(full_path, sizeof(full_path), "%s/%s", MISSIONS_PATH, buffer);
102 | new Handle: missions_kv = CreateKeyValues("mission"); // find "mission" to use as the structure's root node
103 | FileToKeyValues(missions_kv, full_path);
104 |
105 | #if MS_DEBUG
106 | LogMessage("Searching for current map in file: %s", full_path);
107 | #endif
108 |
109 | // Get to "coop" section to start looping
110 | KvJumpToKey(missions_kv, "modes", false);
111 |
112 | // Check if a "coop" section exists
113 | if(KvJumpToKey(missions_kv, "coop", false)) {
114 |
115 | KvGotoFirstSubKey(missions_kv); // first map
116 |
117 | // Check the current maps against all the maps in this missions file
118 | do {
119 | new String:map_name[256];
120 | KvGetString(missions_kv, "map", map_name, sizeof(map_name));
121 |
122 | // If we have found the map name in this missions file, read in the next map
123 | if (StrEqual(map_name, current_map, false)) { // third parameter indicates case sensitivity
124 |
125 | // If there is a map listed next, a finale is not being played
126 | if (KvGotoNextKey(missions_kv)) {
127 | LogMessage("Found next map: %s", NextMap);
128 | g_bIsFinale = false;
129 | // Get the next map's name
130 | KvGetString(missions_kv, "map", NextMap, sizeof(NextMap));
131 | // Close handles
132 | CloseHandle(missions_kv);
133 | CloseHandle(missions_dir);
134 | return true;
135 |
136 | }
137 |
138 | // else a finale is being played
139 | else {
140 | LogMessage("Finale being played, map skip will restart campaign");
141 | g_bIsFinale = true;
142 | // Loop back to the first map
143 | KvGoBack(missions_kv);
144 | KvGotoFirstSubKey(missions_kv);
145 | KvGetString(missions_kv, "map", NextMap, sizeof(NextMap));
146 | // Close handles
147 | CloseHandle(missions_kv);
148 | CloseHandle(missions_dir);
149 | return true;
150 | }
151 |
152 | }
153 | } while (KvGotoNextKey(missions_kv));
154 |
155 | } else {
156 | #if MS_DEBUG
157 | LogMessage("Could not find a coop section in missions file: %s", full_path);
158 | #endif
159 | }
160 |
161 | CloseHandle(missions_kv); // Close the KV handle for this missions file
162 | }
163 |
164 | LogMessage("The next map could not be found. No valid missions file?");
165 | CloseHandle(missions_dir); // Close the handle for this folder/directory
166 | return false;
167 | }
--------------------------------------------------------------------------------
/hardcoop/pillsonly.sp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #define EMPTY_SLOT -1
7 | #define TEAM_SURVIVOR 2
8 |
9 | //Reference: 'Hard 12 manager' by Standalone and High Cookie
10 |
11 | public Plugin:myinfo =
12 | {
13 | name = "Pills Only",
14 | author = "Breezy",
15 | description = "Maps only contain scavenged pills and an initial set granted when leaving saferoom",
16 | version = "1.0",
17 | url = ""
18 | };
19 |
20 | /***********************************************************************************************************************************************************************************
21 |
22 | ITEM FILTERING
23 |
24 | ***********************************************************************************************************************************************************************************/
25 |
26 | new const NUM_ITEMS = 4
27 | new String:undesiredItems[][] = {"adrenaline", "molotov", "pipe_bomb", "vomitjar"};
28 |
29 | // Detect when an entity is about to be spawned, then pass onto SpawnPost SDKHOOK
30 | public OnEntityCreated(entity, const String:classname[]) {
31 | if(IsValidEntity(entity)) {
32 | for (new i = 0; i < NUM_ITEMS; i++) {
33 | if(StrContains(classname, undesiredItems[i], false) != -1) {
34 | SDKHook(entity, SDKHook_SpawnPost, DestroyEntitySpawn);
35 | }
36 | }
37 | }
38 | }
39 |
40 | public DestroyEntitySpawn(entity) {
41 | AcceptEntityInput(entity, "kill");
42 | }
43 |
44 | /***********************************************************************************************************************************************************************************
45 |
46 | STARTING PILLS
47 |
48 | ***********************************************************************************************************************************************************************************/
49 |
50 | public Action:L4D_OnFirstSurvivorLeftSafeArea() {
51 | DistributePills();
52 | }
53 |
54 | public DistributePills() {
55 | // iterate though all clients
56 | for (new client = 1; client <= MaxClients; client++) {
57 | //check player is a survivor
58 | if (IsSurvivor(client)) {
59 | // check pills slot is empty
60 | if (GetPlayerWeaponSlot(client, 5) == EMPTY_SLOT) {
61 | GiveItem(client, "pain_pills");
62 | }
63 | }
64 | }
65 | }
66 |
67 | /***********************************************************************************************************************************************************************************
68 |
69 | UTILITY
70 |
71 | ***********************************************************************************************************************************************************************************/
72 |
73 | GiveItem(client, String:Item[22]) {
74 | new flags = GetCommandFlags("give");
75 | SetCommandFlags("give", flags & ~FCVAR_CHEAT);
76 | FakeClientCommand(client, "give %s", Item);
77 | SetCommandFlags("give", flags|FCVAR_CHEAT);
78 | }
79 |
80 | bool:IsSurvivor(client) {
81 | if (client <= 0 || client > MaxClients || !IsClientConnected(client)) return false; // not a valid client
82 | else return IsClientInGame(client) && GetClientTeam(client) == TEAM_SURVIVOR;
83 | }
--------------------------------------------------------------------------------
/hardcoop/sb_godmode.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 |
3 | #define DEBUG
4 | #define TEAM_SURVIVORS 2
5 | #define TEAM_INFECTED 3
6 | #define ZC_TANK 8
7 |
8 | #define PLUGIN_AUTHOR "Breezy"
9 | #define PLUGIN_VERSION "1.0"
10 |
11 | #include
12 | #include
13 | #include
14 |
15 | public Plugin:myinfo =
16 | {
17 | name = "Survivor Bot Godmode",
18 | author = PLUGIN_AUTHOR,
19 | description = "Bot survivors can only take damage from tanks",
20 | version = PLUGIN_VERSION,
21 | url = ""
22 | };
23 |
24 | public OnClientPutInServer(client) {
25 | SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);
26 | }
27 |
28 | public OnClientDisconnect(client) {
29 | SDKUnhook(client, SDKHook_OnTakeDamage, OnTakeDamage);
30 | }
31 |
32 | public Action:OnTakeDamage(victim, &attacker, &inflictor, &Float:damage, &damagetype) {
33 | if (IsBotSurvivor(victim) && !IsTank(attacker)) {
34 | damage = 0.0;
35 | return Plugin_Changed;
36 | }
37 | return Plugin_Continue;
38 | }
39 |
40 | bool:IsTank(client) {
41 | if (IsValidClient(client)) {
42 | new playerClass = GetEntProp(client, Prop_Send, "m_zombieClass");
43 | if (GetClientTeam(client) == TEAM_INFECTED && playerClass == ZC_TANK) {
44 | return true;
45 | }
46 | }
47 | return false;
48 | }
49 |
50 | bool:IsBotSurvivor(client) {
51 | if (IsValidClient(client)) {
52 | if (GetClientTeam(client) == TEAM_SURVIVORS && IsFakeClient(client)) {
53 | return true;
54 | }
55 | }
56 | return false;
57 | }
58 |
59 | bool:IsValidClient(client) {
60 | if ( !( 1 <= client <= MaxClients ) || !IsClientInGame(client) ) return false;
61 | return true;
62 | }
--------------------------------------------------------------------------------
/hardcoop/survivor_reset.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #define VLC_DEBUG 0
8 | #define NO_TEMP_HEALTH 0.0
9 | #define SECONDARY_SLOT 1
10 |
11 | public Plugin:myinfo =
12 | {
13 | name = "Survivor Reset",
14 | author = "Breezy",
15 | description = "Start each map in a campaign with full health and a single pistol",
16 | version = "1.0",
17 | url = ""
18 | };
19 |
20 | new Handle:hCvarSurvivorRespawnHealth;
21 |
22 | public OnPluginStart() {
23 | hCvarSurvivorRespawnHealth = FindConVar("z_survivor_respawn_health");
24 | SetCheatConVarInt(hCvarSurvivorRespawnHealth, 100);
25 | HookEvent("map_transition", EventHook:ResetSurvivors, EventHookMode_PostNoCopy); // finishing a map
26 | HookEvent("round_freeze_end", EventHook:ResetSurvivors, EventHookMode_PostNoCopy); // restarting map after a wipe
27 | }
28 |
29 | public OnPluginEnd() {
30 | ResetConVar(hCvarSurvivorRespawnHealth);
31 | }
32 |
33 | public ResetSurvivors() {
34 | RestoreHealth();
35 | ResetInventory();
36 | }
37 |
38 | //restoring health of survivors respawning with 50 health from a death in the previous map
39 | public Action:L4D_OnFirstSurvivorLeftSafeArea(client) {
40 | #if VLC_DEBUG
41 | PrintToChatAll("L4D_OnFirstSurvivorLeftSafeArea (Left4Downtown2)");
42 | #endif
43 | RestoreHealth();
44 | }
45 |
46 | public RestoreHealth() {
47 | for (new client = 1; client <= MaxClients; client++) {
48 | if ( IsSurvivor(client) ) {
49 | GiveItem(client, "health");
50 | SetEntPropFloat(client, Prop_Send, "m_healthBuffer", NO_TEMP_HEALTH);
51 | SetEntProp(client, Prop_Send, "m_currentReviveCount", 0); //reset incaps
52 | SetEntProp(client, Prop_Send, "m_bIsOnThirdStrike", false);
53 | #if VLC_DEBUG
54 | new String:ClientName[32];
55 | GetClientName(client, ClientName, sizeof(ClientName));
56 | PrintToChatAll("Restored health and reset revive count on %s (entity index %i):", ClientName, client);
57 | #endif
58 | }
59 | }
60 | }
61 |
62 | public ResetInventory() {
63 | for (new client = 0; client <= MaxClients; client++) {
64 | if ( IsSurvivor(client) ) {
65 | #if VLC_DEBUG
66 | new String:ClientName[32];
67 | GetClientName(client, ClientName, sizeof(ClientName));
68 | PrintToChatAll("Resetting inventory of %s (entity index %i):", ClientName, client);
69 | #endif
70 | // Reset survivor inventories so they only hold dual pistols
71 | for (new i = 0; i < 5; i++) {
72 | DeleteInventoryItem(client, i);
73 | }
74 | GiveItem(client, "pistol");
75 | }
76 | }
77 | }
78 |
79 | GiveItem(client, String:itemName[]) {
80 | new flags = GetCommandFlags("give");
81 | SetCommandFlags("give", flags ^ FCVAR_CHEAT);
82 | FakeClientCommand(client, "give %s", itemName);
83 | SetCommandFlags("give", flags);
84 | }
85 |
86 | DeleteInventoryItem(client, slot) {
87 | new item = GetPlayerWeaponSlot(client, slot);
88 | if (item > 0) {
89 | RemovePlayerItem(client, item);
90 | }
91 | }
92 |
93 | bool:IsSurvivor(client) {
94 | return client > 0 && client <= MaxClients && IsClientInGame(client) && GetClientTeam(client) == 2;
95 | }
96 |
97 | SetCheatConVarInt(Handle:hCvarHandle, value) {
98 | // unset cheat flag
99 | new cvarFlags = GetConVarFlags(hCvarHandle);
100 | SetConVarFlags(hCvarHandle, cvarFlags ^ FCVAR_CHEAT);
101 | // set new value
102 | SetConVarInt(hCvarHandle, value);
103 | // reset cheat flag
104 | SetConVarFlags(hCvarHandle, cvarFlags);
105 | }
--------------------------------------------------------------------------------
/hardcoop/survivormanagement.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 |
3 | #define DEBUG 0
4 | #define TEAM_SPECTATORS 1
5 | #define TEAM_SURVIVORS 2
6 | #define PLUGIN_AUTHOR "Breezy"
7 | #define PLUGIN_VERSION "1.0"
8 |
9 | #include
10 | #include
11 | #include
12 |
13 | // Bibliography: "sb_takecontrol" by "pan xiaohai"
14 |
15 | public Plugin:myinfo =
16 | {
17 | name = "Join Survivors",
18 | author = PLUGIN_AUTHOR,
19 | description = "Join a coop game from spectator mode",
20 | version = PLUGIN_VERSION,
21 | url = ""
22 | };
23 |
24 | new g_bHasLeftStartArea = true;
25 | new g_bIsJoined = false;
26 |
27 | public OnPluginStart()
28 | {
29 | HookEvent("round_freeze_end", EventHook:OnRoundFreezeEnd, EventHookMode_PostNoCopy);
30 | HookEvent("player_team", OnTeamChange);
31 | RegConsoleCmd("sm_join", Cmd_Join, "join survivor team in coop from spectator");
32 | RegConsoleCmd("sm_respawn", Cmd_Respawn, "Respawn if user spawned dead in saferoom");
33 | RegConsoleCmd("sm_return", Cmd_Return, "if respawned out of map and team has not left safe area yet");
34 | }
35 |
36 | public OnClientAuthorized(client, const String:auth[]) {
37 | if (IsValidClient(client))
38 | CreateTimer(5.0, CheckClientTeam, client);
39 | }
40 |
41 | public Action:CheckClientTeam(Handle:timer, any:client) {
42 | if (GetClientTeam(client) == 1)
43 | CreateTimer(15.0, WarningPlayer, client, TIMER_FLAG_NO_MAPCHANGE);
44 | }
45 |
46 | public OnTeamChange(Handle:event, const String:name[], bool:dontBroadcast)
47 | {
48 | new client = GetClientOfUserId(GetEventInt(event, "userid"));
49 | new userTeam = GetEventInt(event, "team");
50 | if(!IsValidClient(client))
51 | return;
52 | if (userTeam == 1)
53 | CreateTimer(15.0, WarningPlayer, client, TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT);
54 | else
55 | g_bIsJoined = true;
56 | }
57 |
58 | public Action:WarningPlayer(Handle:timer, any:client)
59 | {
60 | PrintToChat(client, "\x01Type \x04!join\x01 to play as survivor.");
61 | if (g_bIsJoined)
62 | return Plugin_Stop;
63 | return Plugin_Continue;
64 | }
65 |
66 | public Action:Cmd_Join(client, args) {
67 | ClientCommand(client, "jointeam 2");
68 | return Plugin_Handled;
69 | }
70 |
71 | /***********************************************************************************************************************************************************************************
72 |
73 | SAFEROOM ENTERING/LEAVING FLAG
74 |
75 | ***********************************************************************************************************************************************************************************/
76 |
77 | public Action:L4D_OnFirstSurvivorLeftSafeArea(client) {
78 | g_bHasLeftStartArea = true;
79 | }
80 |
81 | public OnRoundFreezeEnd() {
82 | g_bHasLeftStartArea = false;
83 | }
84 |
85 | /***********************************************************************************************************************************************************************************
86 |
87 | RETURN TO SAFEROOM (IF GLITCHED OUT OF MAP ON LOAD FOLLOWING WIPE)
88 |
89 | ***********************************************************************************************************************************************************************************/
90 |
91 | public Action:Cmd_Return(client, args) {
92 | if (IsSurvivor(client) && !g_bHasLeftStartArea) {
93 | ReturnPlayerToSaferoom(client);
94 | }
95 | }
96 |
97 | ReturnPlayerToSaferoom(client) {
98 | CheatCommand(client, "warp_to_start_area");
99 | }
100 |
101 | /***********************************************************************************************************************************************************************************
102 |
103 | SPAWN A SURVIVOR (WORK AROUND FOR 'RESPAWNING DEAD' BUG)
104 |
105 | ***********************************************************************************************************************************************************************************/
106 |
107 | public Action:Cmd_Respawn(client, args) {
108 | if (IsSurvivor(client) && !IsPlayerAlive(client) && !g_bHasLeftStartArea) {
109 | // Move player to spectators
110 | ChangeClientTeam(client, TEAM_SPECTATORS);
111 |
112 | // Create a fake client
113 | new bot = CreateFakeClient("Dummy");
114 | if(bot != 0) {
115 | ChangeClientTeam(bot, TEAM_SURVIVORS);
116 | // Error checking
117 | if(DispatchKeyValue(bot, "classname", "SurvivorBot") == false) {
118 | PrintToChatAll("\x01Create bot failed");
119 | return Plugin_Handled;
120 | }
121 | if(DispatchSpawn(bot) == false) {
122 | PrintToChatAll("\x01Create bot failed");
123 | return Plugin_Handled;
124 | }
125 |
126 | // Kick bot
127 | SetEntityRenderColor(bot, 128, 0, 0, 255);
128 | CreateTimer(1.0, Timer_Kick, bot, TIMER_FLAG_NO_MAPCHANGE);
129 | }
130 |
131 | // Take control of new survivor
132 | ClientCommand(client, "jointeam 2");
133 | }
134 | return Plugin_Handled;
135 | }
136 |
137 | public Action:Timer_Kick(Handle:timer, any:bot) {
138 | KickClient(bot, "Fake Player");
139 | return Plugin_Stop;
140 | }
141 | /***********************************************************************************************************************************************************************************
142 |
143 | UTILITY
144 |
145 | ***********************************************************************************************************************************************************************************/
146 |
147 | CheatCommand(client, const String:command[]) {
148 | new flags = GetCommandFlags(command);
149 | SetCommandFlags(command, flags ^ FCVAR_CHEAT);
150 | FakeClientCommand(client, command);
151 | SetCommandFlags(command, flags);
152 | }
153 |
154 | public bool:IsSurvivorBotAvailable() {
155 | // Count the number of survivors controlled by players
156 | new playerSurvivorCount = 0;
157 | for (new i = 1; i <= MaxClients; i++) {
158 | if (IsClientInGame(i)) {
159 | if ( GetClientTeam(i) == TEAM_SURVIVORS && !IsFakeClient(i) ) {
160 | playerSurvivorCount++;
161 | }
162 | }
163 | }
164 | // Find the size of the survivor team
165 | new maxSurvivors = GetConVarInt(FindConVar("survivor_limit"));
166 | // Determine whether the team is full
167 | if (playerSurvivorCount < maxSurvivors) {
168 | return true;
169 | } else {
170 | return false; // all survivors are controlled by players
171 | }
172 | }
173 |
174 | bool:IsSurvivor(client) {
175 | return (IsValidClient(client) && GetClientTeam(client) == 2);
176 | }
177 |
178 | bool:IsValidClient(client) {
179 | if ( !( 1 <= client <= MaxClients ) || !IsClientInGame(client) ) return false;
180 | return true;
181 | }
--------------------------------------------------------------------------------
/l4d2_bossvote.sp:
--------------------------------------------------------------------------------
1 | /*
2 | SourcePawn is Copyright (C) 2006-2008 AlliedModders LLC. All rights reserved.
3 | SourceMod is Copyright (C) 2006-2008 AlliedModders LLC. All rights reserved.
4 | Pawn and SMALL are Copyright (C) 1997-2008 ITB CompuPhase.
5 | Source is Copyright (C) Valve Corporation.
6 | All trademarks are property of their respective owners.
7 |
8 | This program is free software: you can redistribute it and/or modify it
9 | under the terms of the GNU General Public License as published by the
10 | Free Software Foundation, either version 3 of the License, or (at your
11 | option) any later version.
12 |
13 | This program is distributed in the hope that it will be useful, but
14 | WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 | General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License along
19 | with this program. If not, see .
20 | */
21 |
22 | #include
23 | #include
24 | #include
25 | #define REQUIRE_PLUGIN
26 | #include
27 | #include
28 | #include
29 |
30 | new Handle:hVote;
31 |
32 | new Float:fTankFlow;
33 | new Float:fWitchFlow;
34 |
35 | new String:tank[8];
36 | new String:witch[8];
37 |
38 | public Plugin:myinfo =
39 | {
40 | name = "L4D2 Boss Percents Vote",
41 | author = "Visor",
42 | version = "1.0",
43 | description = "Vote for percentage",
44 | url = "https://github.com/Attano/Equilibrium"
45 | };
46 |
47 | public OnPluginStart()
48 | {
49 | //HookEvent("round_start", EventHook:OnRoundStart, EventHookMode_PostNoCopy);
50 | RegConsoleCmd("sm_voteboss", Vote);
51 | }
52 |
53 | public Action:Vote(client, args)
54 | {
55 | if (args < 1)
56 | {
57 | CPrintToChat(client, "{blue}[{default}BossVote{blue}]{default} Usage: {green}!voteboss{olive} ");
58 | CPrintToChat(client, "{blue}[{default}BossVote{blue}]{default} Example: {green}!voteboss{default} 70 50");
59 | CPrintToChat(client, "{blue}[{default}BossVote{blue}]{default} Your setting will be {olive}Tank 70%% {default}and {olive}Witch 50%");
60 | return Plugin_Handled;
61 | }
62 | GetCmdArg(1, tank, sizeof(tank));
63 | fTankFlow = StringToFloat(tank) / 100.0;
64 | GetCmdArg(2, witch, sizeof(witch));
65 | fWitchFlow = StringToFloat(witch) / 100.0;
66 |
67 | if (IsSpectator(client) || !IsInReady() || InSecondHalfOfRound())
68 | {
69 | CPrintToChat(client, "{blue}[{default}BossVote{blue}]{default} Vote can only be started by a player during ready-up @ first round!");
70 | return Plugin_Handled;
71 | }
72 |
73 | new String:printmsg[128];
74 | Format(printmsg, sizeof(printmsg), "Change Tank: %s%%, Witch: %s%% ?", tank, witch);
75 | if (StartVote(client, printmsg)) {
76 | FakeClientCommand(client, "Vote Yes");
77 | if (IsInReady())
78 | PrintToChatAll("\x05%N\x01 called a vote to change Tank: \x04%s%%\x01, Witch: \x04%s%%", client, tank, witch);
79 | }
80 |
81 | return Plugin_Handled;
82 | }
83 |
84 | bool:StartVote(client, const String:sVoteHeader[])
85 | {
86 | if (IsNewBuiltinVoteAllowed())
87 | {
88 | new iNumPlayers;
89 | decl players[MaxClients];
90 | for (new i = 1; i <= MaxClients; i++)
91 | {
92 | if (!IsClientConnected(i) || !IsClientInGame(i)) continue;
93 | if (IsSpectator(i) || IsFakeClient(i)) continue;
94 |
95 | players[iNumPlayers++] = i;
96 | }
97 |
98 | hVote = CreateBuiltinVote(VoteActionHandler, BuiltinVoteType_Custom_YesNo, BuiltinVoteAction_Cancel | BuiltinVoteAction_VoteEnd | BuiltinVoteAction_End);
99 | SetBuiltinVoteArgument(hVote, sVoteHeader);
100 | SetBuiltinVoteInitiator(hVote, client);
101 | SetBuiltinVoteResultCallback(hVote, VoteResultHandler);
102 | DisplayBuiltinVote(hVote, players, iNumPlayers, 20);
103 | return true;
104 | }
105 |
106 | CPrintToChat(client, "{blue}[{default}BossVote{blue}]{default} Vote cannot be started now.");
107 | return false;
108 | }
109 |
110 | public VoteActionHandler(Handle:vote, BuiltinVoteAction:action, param1, param2)
111 | {
112 | switch (action)
113 | {
114 | case BuiltinVoteAction_End:
115 | {
116 | hVote = INVALID_HANDLE;
117 | CloseHandle(vote);
118 | }
119 | case BuiltinVoteAction_Cancel:
120 | {
121 | DisplayBuiltinVoteFail(vote, BuiltinVoteFailReason:param1);
122 | }
123 | }
124 | }
125 |
126 | public VoteResultHandler(Handle:vote, num_votes, num_clients, const client_info[][2], num_items, const item_info[][2])
127 | {
128 | for (new i = 0; i < num_items; i++)
129 | {
130 | if (item_info[i][BUILTINVOTEINFO_ITEM_INDEX] == BUILTINVOTES_VOTE_YES)
131 | {
132 | if (item_info[i][BUILTINVOTEINFO_ITEM_VOTES] > (num_clients / 2))
133 | {
134 | DisplayBuiltinVotePass(vote, "Applying custom boss spawns...");
135 | CreateTimer(3.0, RewriteBossFlows);
136 | CreateTimer(5.0, PrintMessage);
137 | return;
138 | }
139 | }
140 | }
141 | DisplayBuiltinVoteFail(vote, BuiltinVoteFail_Loses);
142 | }
143 |
144 | /*public OnRoundStart()
145 | {
146 | CreateTimer(5.5, RewriteBossFlows);
147 | }*/
148 |
149 | public Action:RewriteBossFlows(Handle:timer)
150 | {
151 | if (!InSecondHalfOfRound())
152 | {
153 | SetTankSpawn(fTankFlow);
154 | SetWitchSpawn(fWitchFlow);
155 | UpdateBossPercents();
156 | }
157 | }
158 |
159 | public Action:PrintMessage(Handle:timer)
160 | {
161 | for (new i = 1; i <= MaxClients; i++)
162 | {
163 | if(IsClientInGame(i))
164 | if(!IsFakeClient(i))
165 | FakeClientCommand(i, "sm_boss");
166 | }
167 | }
168 |
169 | SetTankSpawn(Float:flow)
170 | {
171 | for (new i = 0; i <= 1; i++)
172 | {
173 | if (flow != 0)
174 | {
175 | L4D2Direct_SetVSTankToSpawnThisRound(i, true);
176 | L4D2Direct_SetVSTankFlowPercent(i, flow);
177 | }
178 | else
179 | {
180 | L4D2Direct_SetVSTankToSpawnThisRound(i, false);
181 | }
182 | }
183 | }
184 |
185 | SetWitchSpawn(Float:flow)
186 | {
187 | for (new i = 0; i <= 1; i++)
188 | {
189 | if (flow != 0)
190 | {
191 | L4D2Direct_SetVSWitchToSpawnThisRound(i, true);
192 | L4D2Direct_SetVSWitchFlowPercent(i, flow);
193 | }
194 | else
195 | {
196 | L4D2Direct_SetVSWitchToSpawnThisRound(i, false);
197 | }
198 | }
199 | }
200 |
201 | stock bool:IsSpectator(client)
202 | {
203 | return client > 0 && client <= MaxClients && IsClientInGame(client) && GetClientTeam(client) == 1;
204 | }
205 |
206 | stock InSecondHalfOfRound()
207 | {
208 | return GameRules_GetProp("m_bInSecondHalfOfRound");
209 | }
210 |
--------------------------------------------------------------------------------
/l4d2_tank_skin.sp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #pragma semicolon 1
5 |
6 | #define PLUGIN_VERSION "1.0"
7 |
8 | #define g_sTankNorm "models/infected/hulk.mdl"
9 | #define g_sTankSac "models/infected/hulk_dlc3.mdl"
10 |
11 | new bool:g_bIsTankAlive;
12 |
13 | public Plugin:myinfo =
14 | {
15 | name = "RandomTank",
16 | author = "Ludastar (Armonic)",
17 | description = "Just Adds the 50% Chance between each tank model",
18 | version = PLUGIN_VERSION,
19 | url = "http://steamcommunity.com/id/ArmonicJourney"
20 | }
21 |
22 | public OnPluginStart()
23 | {
24 | HookEvent("tank_spawn", eTankSpawn);
25 | HookEvent("round_start", EventHook:OnRoundStart, EventHookMode_PostNoCopy);
26 | HookEvent("tank_killed", EventHook:OnTankDead, EventHookMode_PostNoCopy);
27 | }
28 |
29 | public OnMapStart()
30 | {
31 | PrecacheModel(g_sTankNorm, true);
32 | PrecacheModel(g_sTankSac, true);
33 | PrecacheSound("ui/pickup_secret01.wav");
34 | }
35 |
36 | public OnRoundStart()
37 | {
38 | g_bIsTankAlive = false;
39 | }
40 |
41 | public eTankSpawn(Handle:hEvent, const String:sname[], bool:bDontBroadcast)
42 | {
43 | new iTank = GetEventInt(hEvent, "tankid");
44 | if(iTank > 0 && iTank <= MaxClients && IsClientInGame(iTank) && GetClientTeam(iTank) == 3 && IsPlayerAlive(iTank))
45 | {
46 | switch(GetRandomInt(1, 2))
47 | {
48 | case 1:
49 | {
50 | SetEntityModel(iTank, g_sTankSac);
51 | }
52 | case 2:
53 | {
54 | SetEntityModel(iTank, g_sTankNorm);
55 | }
56 | }
57 | }
58 | if (!g_bIsTankAlive)
59 | {
60 | g_bIsTankAlive = true;
61 | CPrintToChatAll("{red}[{default}!{red}] {olive}Buckle up!");
62 | EmitSoundToAll("ui/pickup_secret01.wav");
63 | }
64 | }
65 |
66 | public OnTankDead()
67 | {
68 | if (g_bIsTankAlive)
69 | g_bIsTankAlive = false;
70 | }
71 |
--------------------------------------------------------------------------------
/l4d_boss_percent.sp:
--------------------------------------------------------------------------------
1 | /*
2 | SourcePawn is Copyright (C) 2006-2008 AlliedModders LLC. All rights reserved.
3 | SourceMod is Copyright (C) 2006-2008 AlliedModders LLC. All rights reserved.
4 | Pawn and SMALL are Copyright (C) 1997-2008 ITB CompuPhase.
5 | Source is Copyright (C) Valve Corporation.
6 | All trademarks are property of their respective owners.
7 |
8 | This program is free software: you can redistribute it and/or modify it
9 | under the terms of the GNU General Public License as published by the
10 | Free Software Foundation, either version 3 of the License, or (at your
11 | option) any later version.
12 |
13 | This program is distributed in the hope that it will be useful, but
14 | WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 | General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License along
19 | with this program. If not, see .
20 | */
21 | #pragma semicolon 1
22 |
23 | #include
24 | #include
25 | #define L4D2UTIL_STOCKS_ONLY
26 | #include
27 | #undef REQUIRE_PLUGIN
28 | #include
29 | #include
30 |
31 | public Plugin:myinfo =
32 | {
33 | name = "L4D2 Boss Flow Announce (Back to roots edition)",
34 | author = "ProdigySim, Jahze, Stabby, CircleSquared, CanadaRox, Visor",
35 | version = "1.6.1",
36 | description = "Announce boss flow percents!",
37 | url = "https://github.com/Attano/Equilibrium"
38 | };
39 |
40 | new iWitchPercent = 0;
41 | new iTankPercent = 0;
42 |
43 | new Handle:g_hVsBossBuffer;
44 | new Handle:hCvarPrintToEveryone;
45 | new Handle:hCvarTankPercent;
46 | new Handle:hCvarWitchPercent;
47 | new bool:readyUpIsAvailable;
48 | new bool:readyFooterAdded;
49 |
50 | public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
51 | {
52 | CreateNative("UpdateBossPercents", Native_UpdateBossPercents);
53 | MarkNativeAsOptional("AddStringToReadyFooter");
54 | RegPluginLibrary("l4d_boss_percent");
55 | return APLRes_Success;
56 | }
57 |
58 | public OnPluginStart()
59 | {
60 | g_hVsBossBuffer = FindConVar("versus_boss_buffer");
61 |
62 | hCvarPrintToEveryone = CreateConVar("l4d_global_percent", "1", "Display boss percentages to entire team when using commands", FCVAR_PLUGIN);
63 | hCvarTankPercent = CreateConVar("l4d_tank_percent", "1", "Display Tank flow percentage in chat", FCVAR_PLUGIN);
64 | hCvarWitchPercent = CreateConVar("l4d_witch_percent", "1", "Display Witch flow percentage in chat", FCVAR_PLUGIN);
65 |
66 | RegConsoleCmd("sm_boss", BossCmd);
67 | RegConsoleCmd("sm_tank", BossCmd);
68 | RegConsoleCmd("sm_witch", BossCmd);
69 | RegConsoleCmd("sm_t", BossCmd);
70 | RegConsoleCmd("sm_w", BossCmd);
71 |
72 | HookEvent("player_left_start_area", LeftStartAreaEvent, EventHookMode_PostNoCopy);
73 | HookEvent("round_start", RoundStartEvent, EventHookMode_PostNoCopy);
74 | }
75 |
76 | public OnAllPluginsLoaded()
77 | {
78 | readyUpIsAvailable = LibraryExists("readyup");
79 | }
80 |
81 | public OnLibraryRemoved(const String:name[])
82 | {
83 | if (StrEqual(name, "readyup")) readyUpIsAvailable = false;
84 | }
85 |
86 | public OnLibraryAdded(const String:name[])
87 | {
88 | if (StrEqual(name, "readyup")) readyUpIsAvailable = true;
89 | }
90 |
91 | public LeftStartAreaEvent(Handle:event, const String:name[], bool:dontBroadcast)
92 | {
93 | if (!readyUpIsAvailable)
94 | for (new client = 1; client <= MaxClients; client++)
95 | if (IsClientConnected(client) && IsClientInGame(client))
96 | PrintBossPercents(client);
97 | }
98 |
99 | public OnRoundIsLive()
100 | {
101 | for (new client = 1; client <= MaxClients; client++)
102 | if (IsClientConnected(client) && IsClientInGame(client))
103 | PrintBossPercents(client);
104 | }
105 |
106 | public RoundStartEvent(Handle:event, const String:name[], bool:dontBroadcast)
107 | {
108 | readyFooterAdded = false;
109 |
110 | CreateTimer(5.0, SaveBossFlows);
111 | CreateTimer(6.0, AddReadyFooter); // workaround for boss equalizer
112 | }
113 |
114 | public Native_UpdateBossPercents(Handle:plugin, numParams)
115 | {
116 | CreateTimer(0.1, SaveBossFlows);
117 | CreateTimer(0.2, AddReadyFooter);
118 | return true;
119 | }
120 |
121 | public Action:SaveBossFlows(Handle:timer)
122 | {
123 | if (!InSecondHalfOfRound())
124 | {
125 | iWitchPercent = 0;
126 | iTankPercent = 0;
127 |
128 | if (L4D2Direct_GetVSWitchToSpawnThisRound(0))
129 | {
130 | iWitchPercent = RoundToNearest(GetWitchFlow(0)*100.0);
131 | }
132 | if (L4D2Direct_GetVSTankToSpawnThisRound(0))
133 | {
134 | iTankPercent = RoundToNearest(GetTankFlow(0)*100.0);
135 | }
136 | }
137 | else
138 | {
139 | if (iWitchPercent != 0)
140 | {
141 | iWitchPercent = RoundToNearest(GetWitchFlow(1)*100.0);
142 | }
143 | if (iTankPercent != 0)
144 | {
145 | iTankPercent = RoundToNearest(GetTankFlow(1)*100.0);
146 | }
147 | }
148 | }
149 |
150 | public Action:AddReadyFooter(Handle:timer)
151 | {
152 | if (readyFooterAdded) return;
153 | if (readyUpIsAvailable)
154 | {
155 | decl String:readyString[65];
156 | if (iWitchPercent && iTankPercent)
157 | Format(readyString, sizeof(readyString), "Tank: %d%%, Witch: %d%%", iTankPercent, iWitchPercent);
158 | else if (iTankPercent)
159 | Format(readyString, sizeof(readyString), "Tank: %d%%, Witch: None", iTankPercent);
160 | else if (iWitchPercent)
161 | Format(readyString, sizeof(readyString), "Tank: None, Witch: %d%%", iWitchPercent);
162 | else
163 | Format(readyString, sizeof(readyString), "Tank: None, Witch: None");
164 | AddStringToReadyFooter(readyString);
165 | readyFooterAdded = true;
166 | }
167 | }
168 |
169 | stock PrintBossPercents(client)
170 | {
171 | if(GetConVarBool(hCvarTankPercent))
172 | {
173 | if (iTankPercent)
174 | CPrintToChat(client, "{default}[{red}!{default}] Tank: [{olive}%d%%{default}]", iTankPercent);
175 | else
176 | PrintToChat(client, "\x01Tank: \x05None");
177 | }
178 |
179 | if(GetConVarBool(hCvarWitchPercent))
180 | {
181 | if (iWitchPercent)
182 | CPrintToChat(client, "{default}[{red}!{default}] Witch: [{olive}%d%%{default}]", iWitchPercent);
183 | else
184 | PrintToChat(client, "\x01Witch: \x05None");
185 | }
186 | }
187 |
188 | public Action:BossCmd(client, args)
189 | {
190 | new L4D2_Team:iTeam = L4D2_Team:GetClientTeam(client);
191 | if (iTeam == L4D2Team_Spectator)
192 | {
193 | PrintBossPercents(client);
194 | return Plugin_Handled;
195 | }
196 |
197 | if (GetConVarBool(hCvarPrintToEveryone))
198 | {
199 | for (new i = 1; i <= MaxClients; i++)
200 | {
201 | if (IsClientConnected(i) && IsClientInGame(i) && L4D2_Team:GetClientTeam(i) == iTeam)
202 | {
203 | PrintBossPercents(i);
204 | }
205 | }
206 | }
207 | else
208 | {
209 | PrintBossPercents(client);
210 | }
211 |
212 | return Plugin_Handled;
213 | }
214 |
215 | stock Float:GetTankFlow(round)
216 | {
217 | return L4D2Direct_GetVSTankFlowPercent(round) -
218 | ( Float:GetConVarInt(g_hVsBossBuffer) / L4D2Direct_GetMapMaxFlowDistance() );
219 | }
220 |
221 | stock Float:GetWitchFlow(round)
222 | {
223 | return L4D2Direct_GetVSWitchFlowPercent(round) -
224 | ( Float:GetConVarInt(g_hVsBossBuffer) / L4D2Direct_GetMapMaxFlowDistance() );
225 | }
226 |
--------------------------------------------------------------------------------
/lightweight_specs.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 |
3 | #include
4 | #include
5 |
6 | enum L4D2Team
7 | {
8 | L4D2Team_None = 0,
9 | L4D2Team_Spectator,
10 | L4D2Team_Survivor,
11 | L4D2Team_Infected
12 | };
13 |
14 | new Handle:sv_mincmdrate;
15 | new Handle:sv_maxcmdrate;
16 | new Handle:sv_minupdaterate;
17 | new Handle:sv_maxupdaterate;
18 | new Handle:sv_minrate;
19 | new Handle:sv_maxrate;
20 | new Handle:sv_client_min_interp_ratio;
21 | new Handle:sv_client_max_interp_ratio;
22 |
23 | new String:netvars[8][8];
24 |
25 | new Float:fLastAdjusted[MAXPLAYERS + 1];
26 |
27 | public Plugin:myinfo =
28 | {
29 | name = "Lightweight Spectating",
30 | author = "Visor",
31 | description = "Forces low rates on spectators",
32 | version = "1.2",
33 | url = "https://github.com/Attano/smplugins"
34 | };
35 |
36 | public OnPluginStart()
37 | {
38 | sv_mincmdrate = FindConVar("sv_mincmdrate");
39 | sv_maxcmdrate = FindConVar("sv_maxcmdrate");
40 | sv_minupdaterate = FindConVar("sv_minupdaterate");
41 | sv_maxupdaterate = FindConVar("sv_maxupdaterate");
42 | sv_minrate = FindConVar("sv_minrate");
43 | sv_maxrate = FindConVar("sv_maxrate");
44 | sv_client_min_interp_ratio = FindConVar("sv_client_min_interp_ratio");
45 | sv_client_max_interp_ratio = FindConVar("sv_client_max_interp_ratio");
46 |
47 | HookEvent("player_team", OnTeamChange);
48 | }
49 |
50 | public OnPluginEnd()
51 | {
52 | SetConVarString(sv_minupdaterate, netvars[2]);
53 | }
54 |
55 | public OnConfigsExecuted()
56 | {
57 | GetConVarString(sv_mincmdrate, netvars[0], 8);
58 | GetConVarString(sv_maxcmdrate, netvars[1], 8);
59 | GetConVarString(sv_minupdaterate, netvars[2], 8);
60 | GetConVarString(sv_maxupdaterate, netvars[3], 8);
61 | GetConVarString(sv_minrate, netvars[4], 8);
62 | GetConVarString(sv_maxrate, netvars[5], 8);
63 | GetConVarString(sv_client_min_interp_ratio, netvars[6], 8);
64 | GetConVarString(sv_client_max_interp_ratio, netvars[7], 8);
65 |
66 | SetConVarInt(sv_minupdaterate, 20);
67 | }
68 |
69 | public OnClientPutInServer(client)
70 | {
71 | fLastAdjusted[client] = 0.0;
72 | }
73 |
74 | public OnTeamChange(Handle:event, String:name[], bool:dontBroadcast)
75 | {
76 | new client = GetClientOfUserId(GetEventInt(event, "userid"));
77 | CreateTimer(10.0, TimerAdjustRates, client, TIMER_FLAG_NO_MAPCHANGE);
78 | }
79 |
80 | public Action:TimerAdjustRates(Handle:timer, any:client)
81 | {
82 | AdjustRates(client);
83 | return Plugin_Handled;
84 | }
85 |
86 | public OnClientSettingsChanged(client)
87 | {
88 | AdjustRates(client);
89 | }
90 |
91 | AdjustRates(client)
92 | {
93 | if (!IsValidClient(client))
94 | return;
95 |
96 | if (fLastAdjusted[client] < GetEngineTime() - 1.0)
97 | {
98 | fLastAdjusted[client] = GetEngineTime();
99 |
100 | new L4D2Team:team = L4D2Team:GetClientTeam(client);
101 | if (team == L4D2Team_Survivor || team == L4D2Team_Infected)
102 | {
103 | ResetRates(client);
104 | }
105 | else if (team == L4D2Team_Spectator)
106 | {
107 | SetSpectatorRates(client);
108 | }
109 | }
110 | }
111 |
112 | SetSpectatorRates(client)
113 | {
114 | SendConVarValue(client, sv_mincmdrate, "30");
115 | SendConVarValue(client, sv_maxcmdrate, "30");
116 | SendConVarValue(client, sv_minupdaterate, "20");
117 | SendConVarValue(client, sv_maxupdaterate, "20");
118 | SendConVarValue(client, sv_minrate, "10000");
119 | SendConVarValue(client, sv_maxrate, "10000");
120 | SendConVarValue(client, sv_client_min_interp_ratio, "2.0");
121 | SendConVarValue(client, sv_client_max_interp_ratio, "2.0");
122 |
123 | SetClientInfo(client, "cl_updaterate", "20");
124 | }
125 |
126 | ResetRates(client)
127 | {
128 | SendConVarValue(client, sv_mincmdrate, netvars[0]);
129 | SendConVarValue(client, sv_maxcmdrate, netvars[1]);
130 | SendConVarValue(client, sv_minupdaterate, netvars[2]);
131 | SendConVarValue(client, sv_maxupdaterate, netvars[3]);
132 | SendConVarValue(client, sv_minrate, netvars[4]);
133 | SendConVarValue(client, sv_maxrate, netvars[5]);
134 | SendConVarValue(client, sv_client_min_interp_ratio, netvars[6]);
135 | SendConVarValue(client, sv_client_max_interp_ratio, netvars[7]);
136 |
137 | SetClientInfo(client, "cl_updaterate", netvars[2]);
138 | }
139 |
140 | bool:IsValidClient(client)
141 | {
142 | return client > 0 && client <= MaxClients && IsClientInGame(client) && !IsFakeClient(client);
143 | }
--------------------------------------------------------------------------------
/pugsetup/include/logdebug.inc:
--------------------------------------------------------------------------------
1 | /*
2 | * logdebug.inc by Dr. McKay
3 | *
4 | * A simple to use library for debug logging which can be redirected or disabled on-the-fly.
5 | * Employs transitional syntax - SourceMod 1.7 or newer is required to compile and run.
6 | */
7 |
8 | #if defined _logdebug_included
9 | #endinput
10 | #endif
11 | #define _logdebug_included
12 |
13 | // define NO_DEBUG before including this file to completely disable all debugging
14 | #if defined NO_DEBUG
15 | stock void InitDebugLog(const char[] convarName, const char[] debugTag,
16 | int adminFlags = ADMFLAG_GENERIC) {}
17 | stock bool LogDebug(const char[] format, any...) {
18 | return false;
19 | }
20 | #endinput
21 | #endif
22 |
23 | #define DEBUG_SERVER_CONSOLE 1 /**< Message will be routed to server console */
24 | #define DEBUG_CLIENT_CONSOLE 2 /**< Message will be routed to all clients' consoles */
25 | #define DEBUG_ADMIN_CONSOLE \
26 | 4 /**< Message will be routed to consoles of admins with a flag specified by plugin */
27 | #define DEBUG_CLIENT_CHAT \
28 | 8 /**< Message will be routed to all clients' chat boxes (and consequently consoles) */
29 | #define DEBUG_ADMIN_CHAT \
30 | 16 /**< Message will be routed to chat boxes of admins with a flag specified by plugin */
31 | #define DEBUG_LOG_FILE 32 /**< Message will be routed to plugin's debug log */
32 |
33 | ConVar g_cvarDebugMode;
34 | char g_DebugLogFileName[PLATFORM_MAX_PATH];
35 | char g_DebugTag[11];
36 | char g_DebugCvarName[64];
37 | int g_DebugAdminFlags;
38 |
39 | /**
40 | * Inits debug logging. You must call this in OnPluginStart().
41 | *
42 | * @param convarName A name to use for the cvar which controls debug log output. Also
43 | * used
44 | * as filename for logfile.
45 | * @param debugTag Tag to prepend to messages, without []. Max 10 characters.
46 | * @param adminFlag One or more admin flagbits which define whether a user is an
47 | * "admin". If you pass multiple flags, users will need ALL flags.
48 | * @noreturn
49 | */
50 | stock void InitDebugLog(const char[] convarName, const char[] debugTag,
51 | int adminFlags = ADMFLAG_GENERIC) {
52 | BuildPath(Path_SM, g_DebugLogFileName, sizeof(g_DebugLogFileName), "logs/%s.log", convarName);
53 |
54 | char flagChars[32];
55 | AdminFlag flags[22];
56 | int numFlags = FlagBitsToArray(adminFlags, flags, sizeof(flags));
57 | for (int i = 0; i < numFlags; i++) {
58 | int len = strlen(flagChars);
59 |
60 | if (len > 0) {
61 | len += StrCat(flagChars, sizeof(flagChars), ", ");
62 | }
63 |
64 | int flag = 0;
65 | FindFlagChar(flags[i], flag);
66 | flagChars[len] = view_as(flag);
67 |
68 | flagChars[len + 1] = '\0';
69 | }
70 |
71 | g_DebugAdminFlags = adminFlags;
72 |
73 | char convarDescription[512];
74 | Format(
75 | convarDescription, sizeof(convarDescription),
76 | "Add up values to enable debug logging to those locations\n 1 = server console\n 2 = all clients' consoles\n 4 = consoles of admins with '%s' flag%s or access to '%s' override\n 8 = all clients' chat\n 16 = chat of admins with '%s' flag%s or access to '%s' override\n 32 = debug log file %s",
77 | flagChars, flagChars[1] == '\0' ? "" : "s", convarName, flagChars,
78 | flagChars[1] == '\0' ? "" : "s", convarName, g_DebugLogFileName);
79 | g_cvarDebugMode =
80 | CreateConVar(convarName, "0", convarDescription, FCVAR_DONTRECORD, true, 0.0, true, 63.0);
81 |
82 | strcopy(g_DebugTag, sizeof(g_DebugTag), debugTag);
83 | strcopy(g_DebugCvarName, sizeof(g_DebugCvarName), convarName);
84 | }
85 |
86 | /**
87 | * Logs a message to all enabled debug output points
88 | *
89 | * @param format Message text with formatting tokens
90 | * @param ... Variable number of format parameters
91 | * @return true if message was output to at least one place
92 | */
93 | stock bool LogDebug(const char[] format, any...) {
94 | if (g_cvarDebugMode == null) {
95 | ThrowError("InitDebugLog must be called before LogDebug");
96 | }
97 |
98 | int output = g_cvarDebugMode.IntValue;
99 | if (output == 0) {
100 | return false;
101 | }
102 |
103 | char buffer[512];
104 | VFormat(buffer, sizeof(buffer), format, 2);
105 |
106 | if (output & DEBUG_SERVER_CONSOLE) {
107 | PrintToServer("[%s] %s", g_DebugTag, buffer);
108 | }
109 |
110 | if (output & DEBUG_CLIENT_CONSOLE) {
111 | for (int i = 1; i <= MaxClients; i++) {
112 | if (IsClientInGame(i) && !IsFakeClient(i)) {
113 | PrintToConsole(i, "[%s] %s", g_DebugTag, buffer);
114 | }
115 | }
116 | }
117 |
118 | if (output & DEBUG_ADMIN_CONSOLE) {
119 | for (int i = 1; i <= MaxClients; i++) {
120 | if (IsClientInGame(i) && !IsFakeClient(i) &&
121 | CheckCommandAccess(i, g_DebugCvarName, g_DebugAdminFlags, true)) {
122 | PrintToConsole(i, "[%s] %s", g_DebugTag, buffer);
123 | }
124 | }
125 | }
126 |
127 | if (output & DEBUG_CLIENT_CHAT) {
128 | PrintToChatAll("[%s] %s", g_DebugTag, buffer);
129 | }
130 |
131 | if (output & DEBUG_ADMIN_CHAT) {
132 | for (int i = 1; i <= MaxClients; i++) {
133 | if (IsClientInGame(i) && !IsFakeClient(i) &&
134 | CheckCommandAccess(i, g_DebugCvarName, g_DebugAdminFlags, true)) {
135 | PrintToChat(i, "[%s] %s", g_DebugTag, buffer);
136 | }
137 | }
138 | }
139 |
140 | if (output & DEBUG_LOG_FILE) {
141 | LogToFileEx(g_DebugLogFileName, "[%s] %s", g_DebugTag, buffer);
142 | }
143 |
144 | return true;
145 | }
146 |
147 | /**
148 | * Returns a bitstring containing bits enabled for each output location (see DEBUG_ constants)
149 | *
150 | * @return bitstring for enabled outputs
151 | */
152 | stock int GetDebugOutputs() {
153 | return g_cvarDebugMode.IntValue;
154 | }
155 |
--------------------------------------------------------------------------------
/pugsetup/include/practicemode.inc:
--------------------------------------------------------------------------------
1 | #if defined _practicemode_included
2 | #endinput
3 | #endif
4 | #define _practicemode_included
5 |
6 | // #include "pugsetup.inc"
7 |
8 | #define OPTION_NAME_LENGTH 128 // max length of a setting name/id
9 | #define CVAR_NAME_LENGTH 64 // max length of a cvar
10 | #define CVAR_VALUE_LENGTH 128 // max length of a cvar value
11 |
12 | /**
13 | * Called when practice mode is enabled.
14 | */
15 | forward void PM_OnPracticeModeEnabled();
16 |
17 | /**
18 | * Called when practice mode is disabled.
19 | */
20 | forward void PM_OnPracticeModeDisabled();
21 |
22 | /**
23 | * Called after practice mode reads its setting config file.
24 | */
25 | forward void PM_OnSettingsRead();
26 |
27 | /**
28 | * Called when a client saves a grenade position.
29 | *
30 | * @return Plugin_Handled or a higher action to block the save.
31 | */
32 | forward Action PM_OnGrenadeSaved(int client, const float origin[3], const float angle[3],
33 | const char[] name);
34 |
35 | /**
36 | * Called when a practice mode setting is changed.
37 | */
38 | forward void PM_OnSettingChanged(int settingNumber, const char[] settingId,
39 | const char[] displayName, bool enabled);
40 |
41 | /**
42 | * Starts practice mode, returning if it was started.
43 | */
44 | native bool PM_StartPracticeMode();
45 |
46 | /**
47 | * Exits practice mode, returning if it was exited.
48 | */
49 | native bool PM_ExitPracticeMode();
50 |
51 | /**
52 | * Adds a new setting to practice mode.
53 | * @note The practicemode plugin will take over ownership of the enabledCvars and enabledValues
54 | * arrays.
55 | * @return the settingNumber value for the new setting.
56 | */
57 | native int PM_AddSetting(const char[] settingId, const char[] name, ArrayList enabledCvars,
58 | ArrayList enabledValues, bool defaultEnabled = false,
59 | bool changeable = true, ArrayList disabledCvars = null,
60 | ArrayList disabeldValues = null);
61 |
62 | /**
63 | * Returns if practice mode is enabled.
64 | */
65 | native bool PM_IsPracticeModeEnabled();
66 |
67 | /**
68 | * Returns if a practice mode setting is currently enabled.
69 | */
70 | native bool PM_IsSettingEnabled(int settingNumber);
71 |
72 | native void PM_Message(int client, const char[] format, any:...);
73 |
74 | native void PM_MessageToAll(const char[] format, any:...);
75 |
76 | native void PM_AddChatAlias(const char[] alias, const char[] command);
77 |
78 | public SharedPlugin __pl_practicemode = {
79 | name = "practicemode", file = "practicemode.smx",
80 | #if defined REQUIRE_PLUGIN
81 | required = 1,
82 | #else
83 | required = 0,
84 | #endif
85 | };
86 |
87 | #if !defined REQUIRE_PLUGIN
88 | public __pl_practicemode_SetNTVOptional() {
89 | MarkNativeAsOptional("PM_StartPracticeMode");
90 | MarkNativeAsOptional("PM_ExitPracticeMode");
91 | MarkNativeAsOptional("PM_AddSetting");
92 | MarkNativeAsOptional("PM_IsPracticeModeEnabled");
93 | MarkNativeAsOptional("PM_IsSettingEnabled");
94 | MarkNativeAsOptional("PM_Message");
95 | MarkNativeAsOptional("PM_MessageToAll");
96 | MarkNativeAsOptional("PM_AddChatAlias");
97 | }
98 | #endif
99 |
--------------------------------------------------------------------------------
/pugsetup/include/priorityqueue.inc:
--------------------------------------------------------------------------------
1 | #if defined _priorityqueue_included
2 | #endinput
3 | #endif
4 | #define _priorityqueue_included
5 |
6 | /**
7 | * Initializes the queue and returns a handle to it that must be closed.
8 | */
9 | stock Handle PQ_Init() {
10 | Handle queueHandle = CreateArray(2);
11 | ClearArray(queueHandle);
12 | return queueHandle;
13 | }
14 |
15 | /**
16 | * Adds a player and a value to the queue.
17 | * If the player is already in the queue, their score is updated in place.
18 | */
19 | stock void PQ_Enqueue(Handle queueHandle, int client, float value) {
20 | int index = PQ_FindClient(queueHandle, client);
21 |
22 | // if they're not already in, put them at the end
23 | if (index == -1) {
24 | index = GetArraySize(queueHandle);
25 | PushArrayCell(queueHandle, client);
26 | SetArrayCell(queueHandle, index, client, 0);
27 | }
28 |
29 | // set their value
30 | SetArrayCell(queueHandle, index, value, 1);
31 | }
32 |
33 | /**
34 | * Selects the player with the max value in the queue and removes them, returning their client
35 | * index.
36 | */
37 | stock int PQ_Dequeue(Handle queueHandle) {
38 | int maxIndex = -1;
39 | int maxClient = -1;
40 | float maxScore;
41 |
42 | for (int i = 0; i < GetArraySize(queueHandle); i++) {
43 | int client = GetArrayCell(queueHandle, i, 0);
44 | float score = GetArrayCell(queueHandle, i, 1);
45 | if (maxIndex == -1 || score > maxScore) {
46 | maxIndex = i;
47 | maxClient = client;
48 | maxScore = score;
49 | }
50 | }
51 | if (maxIndex != -1) {
52 | RemoveFromArray(queueHandle, maxIndex);
53 | }
54 | return maxClient;
55 | }
56 |
57 | /**
58 | * Finds an index of the client in the queue. Returns -1 if the client isn't in it.
59 | */
60 | stock int PQ_FindClient(Handle queueHandle, int client) {
61 | for (int i = 0; i < GetArraySize(queueHandle); i++) {
62 | int c = GetArrayCell(queueHandle, i, 0);
63 | if (client == c)
64 | return i;
65 | }
66 | return -1;
67 | }
68 |
69 | /**
70 | * Drops a client from the queue completely.
71 | */
72 | stock void PQ_DropFromQueue(Handle queueHandle, int client) {
73 | int index = PQ_FindClient(queueHandle, client);
74 | if (index != -1)
75 | RemoveFromArray(queueHandle, index);
76 | }
77 |
78 | /**
79 | * Returns the current size of the queue.
80 | */
81 | stock int PQ_GetSize(Handle queueHandle) {
82 | return GetArraySize(queueHandle);
83 | }
84 |
85 | /**
86 | * Returns is the queu is empty.
87 | */
88 | stock bool PQ_IsEmpty(Handle queueHandle) {
89 | return PQ_GetSize(queueHandle) == 0;
90 | }
91 |
92 | /**
93 | * Clears the Handle for a queue.
94 | */
95 | stock void PQ_Destroy(Handle queueHandle) {
96 | CloseHandle(queueHandle);
97 | }
98 |
--------------------------------------------------------------------------------
/pugsetup/include/restorecvars.inc:
--------------------------------------------------------------------------------
1 | #define CVAR_NAME_MAX_LENGTH 255
2 | #define CVAR_VALUE_MAX_LENGTH 255
3 |
4 | // Returns a cvar Handle that can be used to restore cvars.
5 | stock Handle SaveCvars(ArrayList cvarNames) {
6 | ArrayList storageList = CreateArray();
7 | ArrayList cvarNameList = new ArrayList(CVAR_NAME_MAX_LENGTH);
8 | ArrayList cvarValueList = new ArrayList(CVAR_VALUE_MAX_LENGTH);
9 |
10 | char nameBuffer[CVAR_NAME_MAX_LENGTH];
11 | char valueBuffer[CVAR_VALUE_MAX_LENGTH];
12 | for (int i = 0; cvarNames != null && i < cvarNames.Length; i++) {
13 | cvarNames.GetString(i, nameBuffer, sizeof(nameBuffer));
14 |
15 | Handle cvar = FindConVar(nameBuffer);
16 | if (cvar != INVALID_HANDLE) {
17 | GetConVarString(cvar, valueBuffer, sizeof(valueBuffer));
18 | cvarNameList.PushString(nameBuffer);
19 | cvarValueList.PushString(valueBuffer);
20 | }
21 | }
22 |
23 | storageList.Push(cvarNameList);
24 | storageList.Push(cvarValueList);
25 | return storageList;
26 | }
27 |
28 | // Restores cvars to their previous value using a return value of SaveCvars.
29 | stock void RestoreCvars(Handle& cvarStorage, bool close = false) {
30 | ArrayList cvarNameList = view_as(GetArrayCell(cvarStorage, 0));
31 | ArrayList cvarValueList = view_as(GetArrayCell(cvarStorage, 1));
32 |
33 | char name[CVAR_NAME_MAX_LENGTH];
34 | char value[CVAR_VALUE_MAX_LENGTH];
35 | for (int i = 0; i < cvarNameList.Length; i++) {
36 | cvarNameList.GetString(i, name, sizeof(name));
37 | cvarValueList.GetString(i, value, sizeof(value));
38 |
39 | Handle cvar = FindConVar(name);
40 | if (cvar != INVALID_HANDLE) {
41 | SetConVarString(cvar, value);
42 | }
43 | }
44 |
45 | if (close) {
46 | CloseCvarStorage(cvarStorage);
47 | }
48 | }
49 |
50 | // Closes a cvar storage object returned by SaveCvars.
51 | stock void CloseCvarStorage(Handle& cvarStorage) {
52 | CloseHandle(GetArrayCell(cvarStorage, 0));
53 | CloseHandle(GetArrayCell(cvarStorage, 1));
54 | CloseHandle(cvarStorage);
55 | cvarStorage = INVALID_HANDLE;
56 | }
57 |
58 | // Returns the first "word" in a line, as seperated by whitespace.
59 | stock bool __firstWord(const char[] line, char[] buffer, int len) {
60 | char[] lineBuffer = new char[strlen(line)];
61 | strcopy(lineBuffer, strlen(line), line);
62 | TrimString(lineBuffer);
63 | int splitIndex = StrContains(line, " ");
64 | if (splitIndex == -1)
65 | splitIndex = StrContains(line, "\t");
66 |
67 | if (splitIndex == -1) {
68 | Format(buffer, len, "");
69 | return false;
70 | }
71 |
72 | int destLen = splitIndex + 1;
73 | if (destLen > len)
74 | destLen = len;
75 |
76 | strcopy(buffer, destLen, lineBuffer);
77 | return true;
78 | }
79 |
80 | // Wrapper for SetCvars from an arbitrary file.
81 | // Returns if successful.
82 | stock Handle ExecuteAndSaveCvars(const char[] cfgFile) {
83 | char lineBuffer[CVAR_NAME_MAX_LENGTH + CVAR_VALUE_MAX_LENGTH];
84 | char nameBuffer[CVAR_NAME_MAX_LENGTH];
85 |
86 | char filePath[PLATFORM_MAX_PATH];
87 | Format(filePath, sizeof(filePath), "cfg/%s", cfgFile);
88 |
89 | File file = OpenFile(filePath, "r");
90 | if (file != null) {
91 | ArrayList nameList = new ArrayList(CVAR_NAME_MAX_LENGTH);
92 |
93 | while (!file.EndOfFile() && file.ReadLine(lineBuffer, sizeof(lineBuffer))) {
94 | if (__firstWord(lineBuffer, nameBuffer, sizeof(nameBuffer))) {
95 | TrimString(nameBuffer);
96 | nameList.PushString(nameBuffer);
97 | }
98 | }
99 |
100 | Handle ret = SaveCvars(nameList);
101 | ServerCommand("exec %s", cfgFile);
102 | delete nameList;
103 | delete file;
104 | return ret;
105 | } else {
106 | LogError("Failed to open file for reading: %s", filePath);
107 | return INVALID_HANDLE;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/pugsetup/practicemode/bots.sp:
--------------------------------------------------------------------------------
1 | stock int CreateBot(int client, bool forceCrouch=false) {
2 | int bot = GetClientBot(client);
3 | if (bot <= 0) {
4 | char name[64];
5 | GetClientName(client, name, sizeof(name));
6 | StrCat(name, sizeof(name), " ");
7 | bot = CreateFakeClient(name);
8 | g_BotOwned[client] = bot;
9 | g_IsPMBot[bot] = true;
10 | PM_Message(client, "Created bot, use .nobot to remove it.");
11 | }
12 | if (bot <= 0) {
13 | PM_Message(client, "Failed to create bot :(");
14 | return -1;
15 | }
16 |
17 | int botTeam = GetClientTeam(client) == CS_TEAM_CT ? CS_TEAM_T : CS_TEAM_CT;
18 | ChangeClientTeam(bot, botTeam);
19 |
20 | bool clientCrouching = (GetEntityFlags(client) & FL_DUCKING != 0);
21 | g_BotCrouching[bot] = forceCrouch || clientCrouching;
22 |
23 | CS_RespawnPlayer(bot);
24 | return bot;
25 | }
26 |
27 | bool IsPMBot(int client) {
28 | return client > 0 && g_IsPMBot[client] && IsClientInGame(client) && IsFakeClient(client);
29 | }
30 |
31 | public int GetClientBot(int client) {
32 | int bot = g_BotOwned[client];
33 | if (IsPMBot(bot)) {
34 | return bot;
35 | }
36 | return -1;
37 | }
38 |
39 | void KickClientBot(int client) {
40 | int bot = GetClientBot(client);
41 | if (bot > 0) {
42 | KickClient(bot);
43 | g_BotOwned[client] = -1;
44 | g_IsPMBot[bot] = false;
45 | }
46 | }
47 |
48 | void GiveBotParams(int bot) {
49 | Client_RemoveAllWeapons(bot);
50 | GivePlayerItem(bot, g_BotSpawnWeapon[bot]);
51 | TeleportEntity(bot, g_BotSpawnOrigin[bot], g_BotSpawnAngles[bot], NULL_VECTOR);
52 | }
53 |
54 | // Commands.
55 |
56 | public Action Command_Bot(int client, int args) {
57 | if (!g_InPracticeMode) {
58 | return Plugin_Handled;
59 | }
60 |
61 | int bot = CreateBot(client);
62 | if (bot <= 0) {
63 | return Plugin_Handled;
64 | }
65 |
66 | GetClientAbsOrigin(client, g_BotSpawnOrigin[bot]);
67 | GetClientEyeAngles(client, g_BotSpawnAngles[bot]);
68 | GetClientWeapon(client, g_BotSpawnWeapon[bot], CLASS_LENGTH);
69 | GiveBotParams(bot);
70 |
71 | SetEntityMoveType(client, MOVETYPE_NOCLIP);
72 | return Plugin_Handled;
73 | }
74 |
75 | public Action Command_CrouchBot(int client, int args) {
76 | if (!g_InPracticeMode) {
77 | return Plugin_Handled;
78 | }
79 |
80 | int bot = CreateBot(client, true);
81 | if (bot <= 0) {
82 | return Plugin_Handled;
83 | }
84 |
85 | GetClientAbsOrigin(client, g_BotSpawnOrigin[bot]);
86 | GetClientEyeAngles(client, g_BotSpawnAngles[bot]);
87 | GetClientWeapon(client, g_BotSpawnWeapon[bot], CLASS_LENGTH);
88 | GiveBotParams(bot);
89 |
90 | SetEntityMoveType(client, MOVETYPE_NOCLIP);
91 | return Plugin_Handled;
92 | }
93 |
94 | public Action Command_BotPlace(int client, int args) {
95 | // Based on Franc1sco's bot_spawner plugin:
96 | // https://github.com/Franc1sco/BotSpawner/blob/master/bot_spawner.sp
97 | int bot = CreateBot(client);
98 | if (bot <= 0) {
99 | return Plugin_Handled;
100 | }
101 |
102 | float start[3], angle[3], end[3], normal[3];
103 | GetClientEyePosition(client, start);
104 | GetClientEyeAngles(client, angle);
105 |
106 | TR_TraceRayFilter(start, angle, MASK_SOLID, RayType_Infinite, RayDontHitSelf, client);
107 | if (TR_DidHit(INVALID_HANDLE)) {
108 | TR_GetEndPosition(end, INVALID_HANDLE);
109 | TR_GetPlaneNormal(INVALID_HANDLE, normal);
110 | GetVectorAngles(normal, normal);
111 | normal[0] += 90.0;
112 |
113 | g_BotSpawnOrigin[bot] = end;
114 | g_BotSpawnAngles[bot] = normal;
115 | GetClientWeapon(client, g_BotSpawnWeapon[bot], CLASS_LENGTH);
116 | GiveBotParams(bot);
117 | }
118 |
119 | return Plugin_Handled;
120 | }
121 |
122 | public Action Command_Boost(int client, int args) {
123 | if (!g_InPracticeMode) {
124 | return Plugin_Handled;
125 | }
126 |
127 | int bot = CreateBot(client);
128 | if (bot <= 0) {
129 | return Plugin_Handled;
130 | }
131 |
132 | float origin[3];
133 | GetClientAbsOrigin(client, origin);
134 | g_BotSpawnOrigin[bot] = origin;
135 |
136 | GetClientEyeAngles(client, g_BotSpawnAngles[bot]);
137 | GetClientWeapon(client, g_BotSpawnWeapon[bot], CLASS_LENGTH);
138 | GiveBotParams(bot);
139 |
140 | origin[2] += PLAYER_HEIGHT;
141 | TeleportEntity(client, origin, NULL_VECTOR, NULL_VECTOR);
142 | return Plugin_Handled;
143 | }
144 |
145 | public Action Command_CrouchBoost(int client, int args) {
146 | if (!g_InPracticeMode) {
147 | return Plugin_Handled;
148 | }
149 |
150 | int bot = CreateBot(client, true);
151 | if (bot <= 0) {
152 | return Plugin_Handled;
153 | }
154 |
155 | float origin[3];
156 | GetClientAbsOrigin(client, origin);
157 | g_BotSpawnOrigin[bot] = origin;
158 |
159 | GetClientEyeAngles(client, g_BotSpawnAngles[bot]);
160 | GetClientWeapon(client, g_BotSpawnWeapon[bot], CLASS_LENGTH);
161 | GiveBotParams(bot);
162 |
163 | origin[2] += PLAYER_HEIGHT;
164 | TeleportEntity(client, origin, NULL_VECTOR, NULL_VECTOR);
165 | return Plugin_Handled;
166 | }
167 |
168 | public bool RayDontHitSelf(int entity, int contentsMask, any data) {
169 | return entity != data;
170 | }
171 |
172 | public Action Command_RemoveBot(int client, int args) {
173 | if (!g_InPracticeMode) {
174 | return Plugin_Handled;
175 | }
176 |
177 | KickClientBot(client);
178 | return Plugin_Handled;
179 | }
180 |
--------------------------------------------------------------------------------
/pugsetup/practicemode/grenadeiterators.sp:
--------------------------------------------------------------------------------
1 | typedef GrenadeIteratorFunction = function Action(
2 | const char[] ownerName, const char[] ownerAuth, const char[] name, const char[] description,
3 | ArrayList categories, const char[] grenadeId, float origin[3], float angles[3], any data);
4 |
5 | // Helper that calls a GrenadeIteratorFunction over all grenades
6 | // for the current map.
7 | // TODO: this should be able to modify the grenade strings as well,
8 | // so name, description, and grenadeId shouldn't be const.
9 | // For now only 'origin' and 'angles' can be updated.
10 | stock void IterateGrenades(GrenadeIteratorFunction f, any data = 0) {
11 | char ownerName[MAX_NAME_LENGTH];
12 | char ownerAuth[AUTH_LENGTH];
13 | char name[GRENADE_NAME_LENGTH];
14 | char description[GRENADE_DESCRIPTION_LENGTH];
15 | char categoryString[GRENADE_CATEGORY_LENGTH];
16 | char grenadeId[GRENADE_ID_LENGTH];
17 | float origin[3];
18 | float angles[3];
19 |
20 | // Outer iteration by users.
21 | if (g_GrenadeLocationsKv.GotoFirstSubKey()) {
22 | do {
23 | g_GrenadeLocationsKv.GetSectionName(ownerAuth, sizeof(ownerAuth));
24 | g_GrenadeLocationsKv.GetString("name", ownerName, sizeof(ownerName));
25 |
26 | // Inner iteration by grenades for a user.
27 | if (g_GrenadeLocationsKv.GotoFirstSubKey()) {
28 | do {
29 | g_GrenadeLocationsKv.GetSectionName(grenadeId, sizeof(grenadeId));
30 | g_GrenadeLocationsKv.GetString("name", name, sizeof(name));
31 | g_GrenadeLocationsKv.GetString("description", description, sizeof(description));
32 | g_GrenadeLocationsKv.GetString("categories", categoryString, sizeof(categoryString));
33 | g_GrenadeLocationsKv.GetVector("origin", origin);
34 | g_GrenadeLocationsKv.GetVector("angles", angles);
35 |
36 | ArrayList cats = new ArrayList(64);
37 | AddCategoriesToList(categoryString, cats);
38 |
39 | Action ret = Plugin_Continue;
40 | Call_StartFunction(INVALID_HANDLE, f);
41 | Call_PushString(ownerName);
42 | Call_PushString(ownerAuth);
43 | Call_PushString(name);
44 | Call_PushString(description);
45 | Call_PushCell(cats);
46 | Call_PushString(grenadeId);
47 | Call_PushArrayEx(origin, sizeof(origin), SM_PARAM_COPYBACK);
48 | Call_PushArrayEx(angles, sizeof(angles), SM_PARAM_COPYBACK);
49 | Call_PushCell(data);
50 | Call_Finish(ret);
51 |
52 | g_GrenadeLocationsKv.SetVector("origin", origin);
53 | g_GrenadeLocationsKv.SetVector("angles", angles);
54 |
55 | delete cats;
56 |
57 | if (ret >= Plugin_Handled) {
58 | g_GrenadeLocationsKv.GoBack();
59 | g_GrenadeLocationsKv.GoBack();
60 | return;
61 | }
62 |
63 | } while (g_GrenadeLocationsKv.GotoNextKey());
64 | g_GrenadeLocationsKv.GoBack();
65 | }
66 |
67 | } while (g_GrenadeLocationsKv.GotoNextKey());
68 | g_GrenadeLocationsKv.GoBack();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/pugsetup/practicemode/natives.sp:
--------------------------------------------------------------------------------
1 | #define MESSAGE_PREFIX "[\x05PracticeMode\x01]"
2 |
3 | /**
4 | * Natives.
5 | */
6 | public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) {
7 | g_ChatAliases = new ArrayList(ALIAS_LENGTH);
8 | g_ChatAliasesCommands = new ArrayList(COMMAND_LENGTH);
9 | CreateNative("PM_StartPracticeMode", Native_StartPracticeMode);
10 | CreateNative("PM_ExitPracticeMode", Native_ExitPracticeMode);
11 | CreateNative("PM_AddSetting", Native_AddSetting);
12 | CreateNative("PM_IsPracticeModeEnabled", Native_IsPracticeModeEnabled);
13 | CreateNative("PM_IsSettingEnabled", Native_IsSettingEnabled);
14 | CreateNative("PM_Message", Native_Message);
15 | CreateNative("PM_MessageToAll", Native_MessageToAll);
16 | CreateNative("PM_AddChatAlias", Native_AddChatAlias);
17 | RegPluginLibrary("practicemode");
18 | }
19 |
20 | public int Native_StartPracticeMode(Handle plugin, int numParams) {
21 | if (g_InPracticeMode) {
22 | return false;
23 | } else {
24 | LaunchPracticeMode();
25 | return true;
26 | }
27 | }
28 |
29 | public int Native_ExitPracticeMode(Handle plugin, int numParams) {
30 | if (g_InPracticeMode) {
31 | ExitPracticeMode();
32 | return true;
33 | } else {
34 | return false;
35 | }
36 | }
37 |
38 | public int Native_AddSetting(Handle plugin, int numParams) {
39 | char settingId[OPTION_NAME_LENGTH];
40 | char name[OPTION_NAME_LENGTH];
41 |
42 | GetNativeString(1, settingId, sizeof(settingId));
43 | GetNativeString(2, name, sizeof(name));
44 | ArrayList enabledCvars = view_as(GetNativeCell(3));
45 | ArrayList enabledValues = view_as(GetNativeCell(4));
46 | bool enabled = GetNativeCell(5);
47 | bool changeable = GetNativeCell(6);
48 | ArrayList disabledCvars = null;
49 | ArrayList disabledValues = null;
50 | if (numParams >= 8) {
51 | disabledCvars = view_as(GetNativeCell(7));
52 | disabledValues = view_as(GetNativeCell(8));
53 | }
54 |
55 | if (enabledCvars.Length != enabledValues.Length) {
56 | ThrowNativeError(SP_ERROR_PARAM, "Cvar name list size (%d) mismatch with cvar value list (%d)",
57 | enabledCvars.Length, enabledValues.Length);
58 | }
59 |
60 | g_BinaryOptionIds.PushString(settingId);
61 | g_BinaryOptionNames.PushString(name);
62 | g_BinaryOptionEnabled.Push(enabled);
63 | g_BinaryOptionChangeable.Push(changeable);
64 | g_BinaryOptionEnabledCvars.Push(enabledCvars);
65 | g_BinaryOptionEnabledValues.Push(enabledValues);
66 | g_BinaryOptionCvarRestore.Push(INVALID_HANDLE);
67 | g_BinaryOptionDisabledCvars.Push(disabledCvars);
68 | g_BinaryOptionDisabledValues.Push(disabledValues);
69 |
70 | return g_BinaryOptionIds.Length - 1;
71 | }
72 |
73 | public int Native_IsPracticeModeEnabled(Handle plugin, int numParams) {
74 | return g_InPracticeMode;
75 | }
76 |
77 | public int Native_IsSettingEnabled(Handle plugin, int numParams) {
78 | int index = GetNativeCell(1);
79 | if (index < 0 || index >= g_BinaryOptionIds.Length) {
80 | ThrowNativeError(SP_ERROR_PARAM, "Setting %d is not valid", index);
81 | }
82 | return g_BinaryOptionEnabled.Get(index);
83 | }
84 |
85 | public int Native_Message(Handle plugin, int numParams) {
86 | int client = GetNativeCell(1);
87 | if (client != 0 && (!IsClientConnected(client) || !IsClientInGame(client)))
88 | return;
89 |
90 | char buffer[1024];
91 | int bytesWritten = 0;
92 | SetGlobalTransTarget(client);
93 | FormatNativeString(0, 2, 3, sizeof(buffer), bytesWritten, buffer);
94 |
95 | char prefix[64] = MESSAGE_PREFIX;
96 |
97 | char finalMsg[1024];
98 | if (StrEqual(prefix, ""))
99 | Format(finalMsg, sizeof(finalMsg), " %s", buffer);
100 | else
101 | Format(finalMsg, sizeof(finalMsg), "%s %s", prefix, buffer);
102 |
103 | if (client == 0) {
104 | Colorize(finalMsg, sizeof(finalMsg), false);
105 | PrintToConsole(client, finalMsg);
106 | } else if (IsClientInGame(client)) {
107 | Colorize(finalMsg, sizeof(finalMsg));
108 | PrintToChat(client, finalMsg);
109 | }
110 | }
111 |
112 | public int Native_MessageToAll(Handle plugin, int numParams) {
113 | char prefix[64] = MESSAGE_PREFIX;
114 | char buffer[1024];
115 | int bytesWritten = 0;
116 |
117 | for (int i = 0; i <= MaxClients; i++) {
118 | if (i != 0 && (!IsClientConnected(i) || !IsClientInGame(i)))
119 | continue;
120 |
121 | SetGlobalTransTarget(i);
122 | FormatNativeString(0, 1, 2, sizeof(buffer), bytesWritten, buffer);
123 |
124 | char finalMsg[1024];
125 | if (StrEqual(prefix, ""))
126 | Format(finalMsg, sizeof(finalMsg), " %s", buffer);
127 | else
128 | Format(finalMsg, sizeof(finalMsg), "%s %s", prefix, buffer);
129 |
130 | if (i != 0) {
131 | Colorize(finalMsg, sizeof(finalMsg));
132 | PrintToChat(i, finalMsg);
133 | } else {
134 | Colorize(finalMsg, sizeof(finalMsg), false);
135 | PrintToConsole(i, finalMsg);
136 | }
137 | }
138 | }
139 |
140 | public int Native_AddChatAlias(Handle plugin, int numParams) {
141 | char alias[ALIAS_LENGTH];
142 | char command[COMMAND_LENGTH];
143 | GetNativeString(1, alias, sizeof(alias));
144 | GetNativeString(2, command, sizeof(command));
145 |
146 | // don't allow duplicate aliases to be added
147 | if (g_ChatAliases.FindString(alias) == -1) {
148 | g_ChatAliases.PushString(alias);
149 | g_ChatAliasesCommands.PushString(command);
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/pugsetup/practicemode/pugsetup_integration.sp:
--------------------------------------------------------------------------------
1 | /**
2 | * Pugsetup forwards.
3 | */
4 | public Action PugSetup_OnSetupMenuOpen(int client, Menu menu, bool displayOnly) {
5 | if (!g_PugsetupLoaded) {
6 | return Plugin_Continue;
7 | }
8 |
9 | int leader = PugSetup_GetLeader(false);
10 | if (!IsPlayer(leader)) {
11 | PugSetup_SetLeader(client);
12 | }
13 |
14 | int style = ITEMDRAW_DEFAULT;
15 | if (!PugSetup_HasPermissions(client, Permission_Leader) || displayOnly) {
16 | style = ITEMDRAW_DISABLED;
17 | }
18 |
19 | if (g_InPracticeMode) {
20 | GivePracticeMenu(client, style);
21 | return Plugin_Stop;
22 | } else if (CanStartPracticeMode(client)) {
23 | AddMenuItem(menu, "launch_practice", "Launch practice mode", style);
24 | return Plugin_Continue;
25 | }
26 |
27 | return Plugin_Continue;
28 | }
29 |
30 | public void PugSetup_OnReadyToStart() {
31 | if (!g_PugsetupLoaded) {
32 | return;
33 | }
34 |
35 | if (g_InPracticeMode) {
36 | ExitPracticeMode();
37 | }
38 | }
39 |
40 | public void PugSetup_OnSetupMenuSelect(Menu menu, int client, const char[] selected_info,
41 | int selected_position) {
42 | if (!g_PugsetupLoaded) {
43 | return;
44 | }
45 |
46 | if (StrEqual(selected_info, "launch_practice")) {
47 | LaunchPracticeMode();
48 | GivePracticeMenu(client);
49 | }
50 | }
51 |
52 | public void PugSetup_OnHelpCommand(int client, ArrayList replyMessages, int maxMessageSize, bool& block) {
53 | if (!g_PugsetupLoaded) {
54 | return;
55 | }
56 |
57 | if (g_InPracticeMode) {
58 | block = true;
59 | ShowHelpInfo(client);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/pugsetup/practicemode/spawns.sp:
--------------------------------------------------------------------------------
1 | public void Spawns_MapStart() {
2 | FindMapSpawnsForTeam(g_CTSpawns, "info_player_counterterrorist");
3 | FindMapSpawnsForTeam(g_TSpawns, "info_player_terrorist");
4 |
5 | // Initialize the saved spawn names.
6 | char mapName[PLATFORM_MAX_PATH];
7 | GetCleanMapName(mapName, sizeof(mapName));
8 | char path[PLATFORM_MAX_PATH];
9 | BuildPath(Path_SM, path, sizeof(path), "data/practicemode/spawns/%s.cfg", mapName);
10 |
11 | g_NamedSpawnsKv = new KeyValues("NamedSpawns");
12 | g_NamedSpawnsKv.ImportFromFile(path);
13 | }
14 |
15 | public void Spawns_MapEnd() {
16 | char dir[PLATFORM_MAX_PATH];
17 | BuildPath(Path_SM, dir, sizeof(dir), "data/practicemode/spawns");
18 | if (!DirExists(dir)) {
19 | if (!CreateDirectory(dir, 511))
20 | LogError("Failed to create directory %s", dir);
21 | }
22 |
23 | char mapName[PLATFORM_MAX_PATH];
24 | GetCleanMapName(mapName, sizeof(mapName));
25 | char path[PLATFORM_MAX_PATH];
26 | Format(path, sizeof(path), "%s/%s.cfg", dir, mapName);
27 | g_NamedSpawnsKv.ExportToFile(path);
28 | delete g_NamedSpawnsKv;
29 | }
30 |
31 | static void FindMapSpawnsForTeam(ArrayList list, const char[] spawnClassName) {
32 | list.Clear();
33 | int minPriority = -1;
34 |
35 | // First pass over spawns to find minPriority.
36 | int ent = -1;
37 | while ((ent = FindEntityByClassname(ent, spawnClassName)) != -1) {
38 | int priority = GetEntProp(ent, Prop_Data, "m_iPriority");
39 | if (priority < minPriority || minPriority == -1) {
40 | minPriority = priority;
41 | }
42 | }
43 |
44 | // Second pass only adds spawns with the lowest priority to the list.
45 | ent = -1;
46 | while ((ent = FindEntityByClassname(ent, spawnClassName)) != -1) {
47 | int priority = GetEntProp(ent, Prop_Data, "m_iPriority");
48 | if (priority == minPriority) {
49 | list.Push(ent);
50 | }
51 | }
52 | }
53 |
54 | public void TeleportToSpawnEnt(int client, int ent) {
55 | float origin[3];
56 | float angles[3];
57 | float velocity[3];
58 | GetEntPropVector(ent, Prop_Data, "m_vecOrigin", origin);
59 | GetEntPropVector(ent, Prop_Data, "m_angRotation", angles);
60 | TeleportEntity(client, origin, angles, velocity);
61 | }
62 |
63 | public int FindNearestSpawnIndex(int client, ArrayList list) {
64 | float clientOrigin[3];
65 | GetClientAbsOrigin(client, clientOrigin);
66 |
67 | float origin[3];
68 | int closestIndex = -1;
69 | float minDist = 0.0;
70 |
71 | for (int i = 0; i < list.Length; i++) {
72 | int ent = list.Get(i);
73 | GetEntPropVector(ent, Prop_Data, "m_vecOrigin", origin);
74 | float dist = GetVectorDistance(clientOrigin, origin);
75 | if (closestIndex < 0 || dist < minDist) {
76 | minDist = dist;
77 | closestIndex = i;
78 | }
79 | }
80 |
81 | return closestIndex;
82 | }
83 |
84 | public int FindFurthestSpawnIndex(int client, ArrayList list) {
85 | float clientOrigin[3];
86 | GetClientAbsOrigin(client, clientOrigin);
87 |
88 | float origin[3];
89 | int farthestIndex = -1;
90 | float maxDist = 0.0;
91 |
92 | for (int i = 0; i < list.Length; i++) {
93 | int ent = list.Get(i);
94 | GetEntPropVector(ent, Prop_Data, "m_vecOrigin", origin);
95 | float dist = GetVectorDistance(clientOrigin, origin);
96 | if (farthestIndex < 0 || dist > maxDist) {
97 | maxDist = dist;
98 | farthestIndex = i;
99 | }
100 | }
101 |
102 | return farthestIndex;
103 | }
104 |
105 | #define TEAM_STRING(%1) (%1 == CS_TEAM_CT ? "ct" : "t")
106 |
107 | public bool DoesNamedSpawnExist(int team, const char[] name) {
108 | bool found = false;
109 | if (g_NamedSpawnsKv.JumpToKey(TEAM_STRING(team))) {
110 | if (g_NamedSpawnsKv.JumpToKey(name)) {
111 | found = true;
112 | g_NamedSpawnsKv.GoBack();
113 | }
114 | g_NamedSpawnsKv.GoBack();
115 | }
116 |
117 | return found;
118 | }
119 |
120 | public void SaveNamedSpawn(int client, int team, const char[] name) {
121 | ArrayList list = (team == CS_TEAM_CT) ? g_CTSpawns : g_TSpawns;
122 | int index = FindNearestSpawnIndex(client, list);
123 | if (g_NamedSpawnsKv.JumpToKey(TEAM_STRING(team), true)) {
124 | g_NamedSpawnsKv.SetNum(name, index);
125 | g_NamedSpawnsKv.GoBack();
126 | }
127 | }
128 |
129 | public void TeleportToNamedSpawn(int client, int team, const char[] name) {
130 | ArrayList list = (team == CS_TEAM_CT) ? g_CTSpawns : g_TSpawns;
131 | int index = -1;
132 | if (g_NamedSpawnsKv.JumpToKey(TEAM_STRING(team))) {
133 | index = g_NamedSpawnsKv.GetNum(name, -1);
134 | g_NamedSpawnsKv.GoBack();
135 | }
136 |
137 | if (index >= 0) {
138 | TeleportToSpawnEnt(client, list.Get(index));
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/pugsetup/pugsetup/consolecommands.sp:
--------------------------------------------------------------------------------
1 | public Action Command_Pugstatus(int client, int args) {
2 | ReplyToCommand(client, "Pugsetup version: %s", PLUGIN_VERSION);
3 | #if defined COMMIT_STRING
4 | ReplyToCommand(client, "Compiled from commit %s", COMMIT_STRING);
5 | #endif
6 |
7 | char stateString[64];
8 | switch (g_GameState) {
9 | case GameState_None:
10 | Format(stateString, sizeof(stateString), "None");
11 | case GameState_Warmup:
12 | Format(stateString, sizeof(stateString), "In warmup phase");
13 | case GameState_PickingPlayers:
14 | Format(stateString, sizeof(stateString), "Captains are picking players");
15 | case GameState_WaitingForStart:
16 | Format(stateString, sizeof(stateString), "Waiting for .start command from the leader");
17 | case GameState_Countdown:
18 | Format(stateString, sizeof(stateString), "Countdown timer active");
19 | case GameState_KnifeRound:
20 | Format(stateString, sizeof(stateString), "In knife round");
21 | case GameState_WaitingForKnifeRoundDecision:
22 | Format(stateString, sizeof(stateString), "Waiting for knife winner to pick sides");
23 | case GameState_GoingLive:
24 | Format(stateString, sizeof(stateString), "Going live");
25 | case GameState_Live:
26 | Format(stateString, sizeof(stateString), "Live");
27 | default:
28 | Format(stateString, sizeof(stateString), "Unknown");
29 | }
30 |
31 | char buffer[256];
32 | ReplyToCommand(client, "Current pug game state: %s", stateString);
33 |
34 | if (g_GameState != GameState_None) {
35 | int leader = PugSetup_GetLeader();
36 | if (IsPlayer(leader))
37 | ReplyToCommand(client, "Pug leader: %L", leader);
38 | else
39 | ReplyToCommand(client, "Pug leader: none");
40 |
41 | if (UsingCaptains()) {
42 | if (IsPlayer(g_capt1))
43 | ReplyToCommand(client, "Captain 1: %L", g_capt1);
44 | else
45 | ReplyToCommand(client, "Captain 1: not selected");
46 |
47 | if (IsPlayer(g_capt2))
48 | ReplyToCommand(client, "Captain 2: %L", g_capt2);
49 | else
50 | ReplyToCommand(client, "Captain 2: not selected");
51 | }
52 | }
53 |
54 | if (g_GameState == GameState_Warmup) {
55 | GetTeamString(buffer, sizeof(buffer), g_TeamType);
56 | ReplyToCommand(client, "Team Type (%d vs %d): %s", g_PlayersPerTeam, g_PlayersPerTeam, buffer);
57 |
58 | GetMapString(buffer, sizeof(buffer), g_MapType);
59 | ReplyToCommand(client, "Map Type: %s", buffer);
60 |
61 | GetEnabledString(buffer, sizeof(buffer), g_RecordGameOption);
62 | ReplyToCommand(client, "Recording: %s", buffer);
63 |
64 | GetEnabledString(buffer, sizeof(buffer), g_AutoLive);
65 | ReplyToCommand(client, "Autolive: %s", buffer);
66 |
67 | GetEnabledString(buffer, sizeof(buffer), g_DoKnifeRound);
68 | ReplyToCommand(client, "Knife round: %s", buffer);
69 |
70 | if (g_MapType == MapType_Vote || g_MapType == MapType_Veto) {
71 | GetTrueString(buffer, sizeof(buffer), g_OnDecidedMap);
72 | ReplyToCommand(client, "Map decided: %s", buffer);
73 | }
74 |
75 | if (g_OnDecidedMap) {
76 | GetCurrentMap(buffer, sizeof(buffer));
77 | ReplyToCommand(client, "On map %s", buffer);
78 | }
79 |
80 | for (int i = 1; i <= MaxClients; i++) {
81 | if (IsPlayer(i)) {
82 | if (PugSetup_IsReady(i))
83 | ReplyToCommand(client, " Player %L is READY", i);
84 | else
85 | ReplyToCommand(client, " Player %L is NOT READY", i);
86 | }
87 | }
88 | }
89 |
90 | if (g_GameState >= GameState_WaitingForStart) {
91 | ReplyToCommand(client, "CT Team (score = %d):", CS_GetTeamScore(CS_TEAM_CT));
92 | for (int i = 1; i <= MaxClients; i++) {
93 | if (IsPlayer(i) && GetClientTeam(i) == CS_TEAM_CT)
94 | ReplyToCommand(client, " %L", i);
95 | }
96 |
97 | ReplyToCommand(client, "T Team (score = %d):", CS_GetTeamScore(CS_TEAM_T));
98 | for (int i = 1; i <= MaxClients; i++) {
99 | if (IsPlayer(i) && GetClientTeam(i) == CS_TEAM_T)
100 | ReplyToCommand(client, " %L", i);
101 | }
102 | }
103 |
104 | return Plugin_Handled;
105 | }
106 |
107 | public Action Command_ShowPermissions(int client, int args) {
108 | char command[COMMAND_LENGTH];
109 | char permission[64];
110 |
111 | for (int i = 0; i < g_Commands.Length; i++) {
112 | g_Commands.GetString(i, command, sizeof(command));
113 | Permission p = Permission_All;
114 | g_PermissionsMap.GetValue(command, p);
115 | switch (p) {
116 | case Permission_All:
117 | Format(permission, sizeof(permission), "all");
118 | case Permission_Captains:
119 | Format(permission, sizeof(permission), "captains");
120 | case Permission_Leader:
121 | Format(permission, sizeof(permission), "leader");
122 | case Permission_Admin:
123 | Format(permission, sizeof(permission), "admin");
124 | }
125 | ReplyToCommand(client, "%s: %s", command, permission);
126 | }
127 |
128 | return Plugin_Handled;
129 | }
130 |
131 | public Action Command_ShowChatAliases(int client, int args) {
132 | char command[COMMAND_LENGTH];
133 | char alias[ALIAS_LENGTH];
134 |
135 | for (int i = 0; i < g_ChatAliases.Length; i++) {
136 | g_ChatAliases.GetString(i, alias, sizeof(alias));
137 | g_ChatAliasesCommands.GetString(i, command, sizeof(command));
138 | ReplyToCommand(client, "%s -> %s", alias, command);
139 | }
140 |
141 | return Plugin_Handled;
142 | }
143 |
--------------------------------------------------------------------------------
/pugsetup/pugsetup/kniferounds.sp:
--------------------------------------------------------------------------------
1 | #define KNIFE_CONFIG "sourcemod/pugsetup/knife.cfg"
2 | Handle g_KnifeCvarRestore = INVALID_HANDLE;
3 |
4 | public Action StartKnifeRound(Handle timer) {
5 | if (g_GameState != GameState_KnifeRound)
6 | return Plugin_Handled;
7 |
8 | // reset player tags
9 | for (int i = 1; i <= MaxClients; i++) {
10 | if (IsPlayer(i)) {
11 | UpdateClanTag(i, true); // force strip them
12 | }
13 | }
14 |
15 | g_KnifeCvarRestore = ExecuteAndSaveCvars(KNIFE_CONFIG);
16 | if (g_KnifeCvarRestore == INVALID_HANDLE) {
17 | LogError("Failed to save cvar values when executing %s", KNIFE_CONFIG);
18 | }
19 |
20 | RestartGame(1);
21 | g_KnifeRoundVotesCast = 0;
22 | for (int i = 1; i <= MaxClients; i++) {
23 | g_KnifeRoundVotes[i] = KnifeDecision_None;
24 | }
25 |
26 | // This is done on a delay since the cvar changes from
27 | // the knife cfg execute have their own delay of when they are printed
28 | // into global chat.
29 | CreateTimer(1.0, Timer_AnnounceKnife);
30 | return Plugin_Handled;
31 | }
32 |
33 | public Action Timer_AnnounceKnife(Handle timer) {
34 | if (g_GameState != GameState_KnifeRound)
35 | return Plugin_Handled;
36 |
37 | for (int i = 0; i < 3; i++)
38 | PugSetup_MessageToAll("%t", "KnifeRound");
39 | return Plugin_Handled;
40 | }
41 |
42 | public Action Timer_HandleKnifeDecisionVote(Handle timer) {
43 | HandleKnifeDecisionVote();
44 | }
45 |
46 | public void HandleKnifeDecisionVote() {
47 | if (g_GameState != GameState_WaitingForKnifeRoundDecision) {
48 | return;
49 | }
50 |
51 | int stayCount = 0;
52 | int swapCount = 0;
53 | for (int i = 1; i <= MaxClients; i++) {
54 | if (IsValidClient(i) && GetClientTeam(i) == g_KnifeWinner) {
55 | if (g_KnifeRoundVotes[i] == KnifeDecision_Stay)
56 | stayCount++;
57 | else if (g_KnifeRoundVotes[i] == KnifeDecision_Swap)
58 | swapCount++;
59 | }
60 | }
61 | bool doSwap = (swapCount > stayCount);
62 | EndKnifeRound(doSwap);
63 | }
64 |
65 | public void EndKnifeRound(bool swap) {
66 | Call_StartForward(g_hOnKnifeRoundDecision);
67 | Call_PushCell(swap);
68 | Call_Finish();
69 |
70 | if (swap) {
71 | for (int i = 1; i <= MaxClients; i++) {
72 | if (IsValidClient(i)) {
73 | int team = GetClientTeam(i);
74 | if (team == CS_TEAM_T)
75 | SwitchPlayerTeam(i, CS_TEAM_CT);
76 | else if (team == CS_TEAM_CT)
77 | SwitchPlayerTeam(i, CS_TEAM_T);
78 | }
79 | }
80 | }
81 |
82 | ChangeState(GameState_GoingLive);
83 | if (g_KnifeCvarRestore != INVALID_HANDLE) {
84 | RestoreCvars(g_KnifeCvarRestore);
85 | CloseCvarStorage(g_KnifeCvarRestore);
86 | g_KnifeCvarRestore = INVALID_HANDLE;
87 | }
88 | CreateTimer(3.0, BeginLO3, _, TIMER_FLAG_NO_MAPCHANGE);
89 | }
90 |
91 | static bool AwaitingDecision(int client, const char[] command) {
92 | if (g_DoVoteForKnifeRoundDecisionCvar.IntValue != 0) {
93 | return (g_GameState == GameState_WaitingForKnifeRoundDecision) && IsPlayer(client) &&
94 | GetClientTeam(client) == g_KnifeWinner;
95 | } else {
96 | // Always lets console make the decision
97 | if (client == 0)
98 | return true;
99 |
100 | // Check if they're on the winning team
101 | bool canMakeDecision = (g_GameState == GameState_WaitingForKnifeRoundDecision) &&
102 | IsPlayer(client) && GetClientTeam(client) == g_KnifeWinner;
103 | bool hasPermissions = DoPermissionCheck(client, command);
104 | return canMakeDecision && hasPermissions;
105 | }
106 | }
107 |
108 | public Action Command_Stay(int client, int args) {
109 | if (AwaitingDecision(client, "sm_stay")) {
110 | if (g_DoVoteForKnifeRoundDecisionCvar.IntValue == 0) {
111 | EndKnifeRound(false);
112 | } else {
113 | g_KnifeRoundVotes[client] = KnifeDecision_Stay;
114 | PugSetup_Message(client, "%t", "KnifeRoundVoteStay");
115 | g_KnifeRoundVotesCast++;
116 | if (g_KnifeRoundVotesCast == g_PlayersPerTeam) {
117 | HandleKnifeDecisionVote();
118 | }
119 | }
120 | }
121 | return Plugin_Handled;
122 | }
123 |
124 | public Action Command_Swap(int client, int args) {
125 | if (AwaitingDecision(client, "sm_swap")) {
126 | if (g_DoVoteForKnifeRoundDecisionCvar.IntValue == 0) {
127 | EndKnifeRound(true);
128 | } else {
129 | g_KnifeRoundVotes[client] = KnifeDecision_Swap;
130 | PugSetup_Message(client, "%t", "KnifeRoundVoteSwap");
131 | g_KnifeRoundVotesCast++;
132 | if (g_KnifeRoundVotesCast == g_PlayersPerTeam) {
133 | HandleKnifeDecisionVote();
134 | }
135 | }
136 | }
137 | return Plugin_Handled;
138 | }
139 |
140 | public Action Command_Ct(int client, int args) {
141 | if (IsPlayer(client)) {
142 | if (GetClientTeam(client) == CS_TEAM_CT)
143 | FakeClientCommand(client, "sm_stay");
144 | else if (GetClientTeam(client) == CS_TEAM_T)
145 | FakeClientCommand(client, "sm_swap");
146 | }
147 | return Plugin_Handled;
148 | }
149 |
150 | public Action Command_T(int client, int args) {
151 | if (IsPlayer(client)) {
152 | if (GetClientTeam(client) == CS_TEAM_T)
153 | FakeClientCommand(client, "sm_stay");
154 | else if (GetClientTeam(client) == CS_TEAM_CT)
155 | FakeClientCommand(client, "sm_swap");
156 | }
157 | return Plugin_Handled;
158 | }
159 |
160 | public int GetKnifeRoundWinner() {
161 | int ctAlive = CountAlivePlayersOnTeam(CS_TEAM_CT);
162 | int tAlive = CountAlivePlayersOnTeam(CS_TEAM_T);
163 | int winningCSTeam = CS_TEAM_NONE;
164 | if (ctAlive > tAlive) {
165 | winningCSTeam = CS_TEAM_CT;
166 | } else if (tAlive > ctAlive) {
167 | winningCSTeam = CS_TEAM_T;
168 | } else {
169 | int ctHealth = SumHealthOfTeam(CS_TEAM_CT);
170 | int tHealth = SumHealthOfTeam(CS_TEAM_T);
171 | if (ctHealth > tHealth) {
172 | winningCSTeam = CS_TEAM_CT;
173 | } else if (tHealth > ctHealth) {
174 | winningCSTeam = CS_TEAM_T;
175 | } else {
176 | if (GetRandomFloat(0.0, 1.0) < 0.5) {
177 | winningCSTeam = CS_TEAM_CT;
178 | } else {
179 | winningCSTeam = CS_TEAM_T;
180 | }
181 | }
182 | }
183 |
184 | return winningCSTeam;
185 | }
186 |
--------------------------------------------------------------------------------
/pugsetup/pugsetup/leadermenus.sp:
--------------------------------------------------------------------------------
1 | /**
2 | * Displays a menu to select the captains for the game.
3 | */
4 | public void Captain1Menu(int client) {
5 | Menu menu = new Menu(Captain1MenuHandler);
6 | char title[128];
7 | Format(title, sizeof(title), "%T", "ChooseCaptainTitle", client, 1);
8 | SetMenuTitle(menu, title);
9 |
10 | if (CountPotentialCaptains(g_capt2) >= 2)
11 | AddMenuInt(menu, -1, "%T", "Random", client);
12 |
13 | if (AddPotentialCaptains(menu, g_capt2) >= 1)
14 | DisplayMenu(menu, client, MENU_TIME_FOREVER);
15 | else
16 | CloseHandle(menu);
17 | }
18 |
19 | public int Captain1MenuHandler(Menu menu, MenuAction action, int param1, int param2) {
20 | if (action == MenuAction_Select) {
21 | int client = param1;
22 | int choice = GetMenuInt(menu, param2);
23 | if (choice == -1) {
24 | int randomClient = RandomPlayer();
25 | if (IsPlayer(randomClient))
26 | PugSetup_SetCaptain(1, randomClient, true);
27 | } else if (IsPlayer(choice)) {
28 | PugSetup_SetCaptain(1, choice, true);
29 | }
30 |
31 | Captain2Menu(client);
32 | } else if (action == MenuAction_End) {
33 | CloseHandle(menu);
34 | }
35 | }
36 |
37 | public void Captain2Menu(int client) {
38 | Menu menu = new Menu(Captain2MenuHandler);
39 | char title[128];
40 | Format(title, sizeof(title), "%T", "ChooseCaptainTitle", client, 2);
41 | SetMenuTitle(menu, title);
42 |
43 | if (CountPotentialCaptains(g_capt1) >= 2)
44 | AddMenuInt(menu, -1, "%T", "Random", client);
45 |
46 | if (AddPotentialCaptains(menu, g_capt1) >= 1)
47 | DisplayMenu(menu, client, MENU_TIME_FOREVER);
48 | else
49 | CloseHandle(menu);
50 | }
51 |
52 | public int Captain2MenuHandler(Menu menu, MenuAction action, int param1, int param2) {
53 | if (action == MenuAction_Select) {
54 | int choice = GetMenuInt(menu, param2);
55 | if (choice == -1) {
56 | int randomClient = RandomPlayer(g_capt1);
57 | if (IsPlayer(randomClient))
58 | PugSetup_SetCaptain(2, randomClient, true);
59 | } else if (IsPlayer(choice)) {
60 | PugSetup_SetCaptain(2, choice, true);
61 | }
62 | } else if (action == MenuAction_End) {
63 | CloseHandle(menu);
64 | }
65 | }
66 |
67 | static int CountPotentialCaptains(int otherCaptain) {
68 | int count = 0;
69 | for (int client = 1; client <= MaxClients; client++) {
70 | if (IsPotentialCaptain(client, otherCaptain)) {
71 | count++;
72 | }
73 | }
74 | return count;
75 | }
76 |
77 | static bool IsPotentialCaptain(int client, int otherCaptain) {
78 | return IsPlayer(client) && otherCaptain != client;
79 | }
80 |
81 | static int AddPotentialCaptains(Menu menu, int otherCaptain) {
82 | int count = 0;
83 | for (int client = 1; client <= MaxClients; client++) {
84 | if (IsPotentialCaptain(client, otherCaptain)) {
85 | char name[MAX_NAME_LENGTH];
86 | GetClientName(client, name, sizeof(name));
87 | AddMenuInt(menu, client, name);
88 | count++;
89 | }
90 | }
91 | return count;
92 | }
93 |
94 | /**
95 | * Extra menu for selecting the leader of the game.
96 | */
97 | public void LeaderMenu(int client) {
98 | Menu menu = new Menu(LeaderMenuHandler);
99 | SetMenuTitle(menu, "%T", "ChooseLeaderTitle", client);
100 | if (AddAllPlayers(menu) >= 1)
101 | DisplayMenu(menu, client, MENU_TIME_FOREVER);
102 | else
103 | CloseHandle(menu);
104 | }
105 |
106 | public int LeaderMenuHandler(Menu menu, MenuAction action, int param1, int param2) {
107 | if (action == MenuAction_Select) {
108 | int choice = GetMenuInt(menu, param2);
109 | PugSetup_SetLeader(choice);
110 | } else if (action == MenuAction_End) {
111 | CloseHandle(menu);
112 | }
113 | }
114 |
115 | static int AddAllPlayers(Menu menu) {
116 | int count = 0;
117 | for (int client = 1; client <= MaxClients; client++) {
118 | if (IsPlayer(client)) {
119 | char name[MAX_NAME_LENGTH];
120 | GetClientName(client, name, sizeof(name));
121 | AddMenuInt(menu, client, name);
122 | count++;
123 | }
124 | }
125 | return count;
126 | }
127 |
--------------------------------------------------------------------------------
/pugsetup/pugsetup/liveon3.sp:
--------------------------------------------------------------------------------
1 | /** Begins the LO3 process. **/
2 | public Action BeginLO3(Handle timer) {
3 | if (g_GameState == GameState_None)
4 | return Plugin_Handled;
5 |
6 | ChangeState(GameState_GoingLive);
7 |
8 | // force kill the warmup if we need to
9 | if (InWarmup()) {
10 | EndWarmup();
11 | }
12 |
13 | // reset player tags
14 | for (int i = 1; i <= MaxClients; i++) {
15 | if (IsPlayer(i)) {
16 | UpdateClanTag(i, true); // force strip them
17 | }
18 | }
19 |
20 | SetConVarInt(FindConVar("sv_cheats"), 0);
21 | Call_StartForward(g_hOnGoingLive);
22 | Call_Finish();
23 |
24 | if (GetConVarInt(g_QuickRestartsCvar) == 0) {
25 | // start lo3
26 | PugSetup_MessageToAll("%t", "RestartCounter", 1);
27 | RestartGame(1);
28 | CreateTimer(3.0, Restart2);
29 | } else {
30 | // single restart
31 | RestartGame(5);
32 | CreateTimer(5.1, MatchLive);
33 | }
34 |
35 | return Plugin_Handled;
36 | }
37 |
38 | public Action Restart2(Handle timer) {
39 | if (g_GameState == GameState_None)
40 | return Plugin_Handled;
41 |
42 | PugSetup_MessageToAll("%t", "RestartCounter", 2);
43 | RestartGame(1);
44 | CreateTimer(4.0, Restart3);
45 |
46 | return Plugin_Handled;
47 | }
48 |
49 | public Action Restart3(Handle timer) {
50 | if (g_GameState == GameState_None)
51 | return Plugin_Handled;
52 |
53 | PugSetup_MessageToAll("%t", "RestartCounter", 3);
54 | RestartGame(5);
55 | CreateTimer(5.1, MatchLive);
56 |
57 | return Plugin_Handled;
58 | }
59 |
60 | public Action MatchLive(Handle timer) {
61 | if (g_GameState == GameState_None)
62 | return Plugin_Handled;
63 |
64 | ChangeState(GameState_Live);
65 | Call_StartForward(g_hOnLive);
66 | Call_Finish();
67 |
68 | // Restore client clan tags since we're live.
69 | for (int i = 1; i <= MaxClients; i++) {
70 | if (IsPlayer(i)) {
71 | RestoreClanTag(i);
72 | }
73 | }
74 |
75 | for (int i = 0; i < 3; i++) {
76 | PugSetup_MessageToAll("%t", "Live");
77 | }
78 |
79 | return Plugin_Handled;
80 | }
81 |
--------------------------------------------------------------------------------
/pugsetup/pugsetup/mapveto.sp:
--------------------------------------------------------------------------------
1 | /**
2 | * Map vetoing functions
3 | */
4 | public void CreateMapVeto() {
5 | if (GetConVarInt(g_RandomizeMapOrderCvar) != 0) {
6 | RandomizeArray(g_MapList);
7 | }
8 |
9 | ClearArray(g_MapVetoed);
10 | for (int i = 0; i < g_MapList.Length; i++) {
11 | g_MapVetoed.Push(false);
12 | }
13 |
14 | GiveVetoMenu(g_capt1);
15 | }
16 |
17 | public void GiveVetoMenu(int client) {
18 | Menu menu = new Menu(VetoHandler);
19 | menu.ExitButton = false;
20 | menu.SetTitle("%T", "VetoMenuTitle", client);
21 |
22 | for (int i = 0; i < g_MapList.Length; i++) {
23 | if (!g_MapVetoed.Get(i)) {
24 | AddMapIndexToMenu(menu, g_MapList, i);
25 | }
26 | }
27 | DisplayMenu(menu, client, MENU_TIME_FOREVER);
28 | }
29 |
30 | static int GetNumMapsLeft() {
31 | int count = 0;
32 | for (int i = 0; i < g_MapList.Length; i++) {
33 | if (!g_MapVetoed.Get(i))
34 | count++;
35 | }
36 | return count;
37 | }
38 |
39 | static int GetFirstMapLeft() {
40 | for (int i = 0; i < g_MapList.Length; i++) {
41 | if (!g_MapVetoed.Get(i))
42 | return i;
43 | }
44 | return -1;
45 | }
46 |
47 | public int VetoHandler(Menu menu, MenuAction action, int param1, int param2) {
48 | if (action == MenuAction_Select) {
49 | int client = param1;
50 | int index = GetMenuInt(menu, param2);
51 | char map[PLATFORM_MAX_PATH];
52 | FormatMapName(g_MapList, index, map, sizeof(map));
53 |
54 | char captString[64];
55 | FormatPlayerName(client, client, captString);
56 | PugSetup_MessageToAll("%t", "PlayerVetoed", captString, map);
57 |
58 | g_MapVetoed.Set(index, true);
59 | if (GetNumMapsLeft() == 1) {
60 | ChangeMap(g_MapList, GetFirstMapLeft());
61 | } else {
62 | int other = OtherCaptain(client);
63 | GiveVetoMenu(other);
64 | for (int i = 1; i <= MaxClients; i++) {
65 | if (IsPlayer(i) && i != other) {
66 | VetoStatusDisplay(i);
67 | }
68 | }
69 | }
70 |
71 | } else if (action == MenuAction_End) {
72 | CloseHandle(menu);
73 | }
74 | }
75 |
76 | static void VetoStatusDisplay(int client) {
77 | Menu menu = new Menu(VetoStatusHandler);
78 | SetMenuExitButton(menu, true);
79 | SetMenuTitle(menu, "%T", "MapsLeft", client);
80 | for (int i = 0; i < g_MapList.Length; i++) {
81 | if (!g_MapVetoed.Get(i)) {
82 | AddMapIndexToMenu(menu, g_MapList, i, true);
83 | }
84 | }
85 | DisplayMenu(menu, client, 30);
86 | }
87 |
88 | public int VetoStatusHandler(Menu menu, MenuAction action, int param1, int param2) {
89 | if (action == MenuAction_End) {
90 | CloseHandle(menu);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/pugsetup/pugsetup/mapvote.sp:
--------------------------------------------------------------------------------
1 | #define RANDOM_MAP_VOTE "-1" // must be in invalid index for array indexing
2 |
3 | /**
4 | * Map voting functions
5 | */
6 | public void CreateMapVote() {
7 | if (g_ExcludedMaps.IntValue > 0 && g_MapList.Length > g_PastMaps.Length) {
8 | SetupMapVotePool(true);
9 | } else {
10 | SetupMapVotePool(false);
11 | }
12 |
13 | if (g_RandomizeMapOrderCvar.IntValue != 0) {
14 | RandomizeArray(g_MapVotePool);
15 | }
16 |
17 | if (g_InstantRunoffVotingCvar.IntValue == 0 || g_MapList.Length < 3) {
18 | StartMapVote();
19 | } else {
20 | StartInstantRunoffMapVote();
21 | }
22 | }
23 |
24 | static void StartMapVote() {
25 | Menu menu = new Menu(MapVoteHandler);
26 | menu.SetTitle("%T", "VoteMenuTitle", LANG_SERVER);
27 | menu.ExitButton = false;
28 |
29 | if (g_RandomOptionInMapVoteCvar.IntValue != 0) {
30 | char buffer[255];
31 | Format(buffer, sizeof(buffer), "%T", "Random", LANG_SERVER);
32 | AddMenuItem(menu, RANDOM_MAP_VOTE, buffer);
33 | }
34 |
35 | // Don't paginate the menu if we have 7 maps or less, as they will fit
36 | // on one page when we don't add the pagination options
37 | if (g_MapVotePool.Length <= 7) {
38 | menu.Pagination = MENU_NO_PAGINATION;
39 | }
40 |
41 | for (int i = 0; i < g_MapVotePool.Length; i++) {
42 | AddMapIndexToMenu(menu, g_MapVotePool, i);
43 | }
44 |
45 | VoteMenuToAll(menu, g_MapVoteTimeCvar.IntValue);
46 | }
47 |
48 | public int MapVoteHandler(Menu menu, MenuAction action, int param1, int param2) {
49 | if (action == MenuAction_Select && GetCvarIntSafe("sm_vote_progress_chat") == 0 &&
50 | g_DisplayMapVotesCvar.IntValue != 0) {
51 | // Only prints votes to chat if sourcemod isn't automatically printing votes in chat
52 | int client = param1;
53 | char clientName[MAX_NAME_LENGTH];
54 | GetClientName(client, clientName, sizeof(clientName));
55 |
56 | int mapIndex = GetMenuInt(menu, param2);
57 | char mapName[255];
58 |
59 | if (mapIndex >= 0) {
60 | FormatMapName(g_MapVotePool, mapIndex, mapName, sizeof(mapName));
61 | } else {
62 | Format(mapName, sizeof(mapName), "%T", "RandomMapVote", LANG_SERVER);
63 | }
64 |
65 | PugSetup_MessageToAll("%t", "Voted For", clientName, mapName);
66 |
67 | } else if (action == MenuAction_Display) {
68 | char buffer[255];
69 | Format(buffer, sizeof(buffer), "%T", "VoteMenuTitle", param1);
70 | SetPanelTitle(view_as(param2), buffer);
71 |
72 | } else if (action == MenuAction_DisplayItem) {
73 | char info[32];
74 | GetMenuItem(menu, param2, info, sizeof(info));
75 | char display[64];
76 | if (StrEqual(info, RANDOM_MAP_VOTE)) {
77 | Format(display, sizeof(display), "%T", "Random", param1);
78 | return RedrawMenuItem(display);
79 | }
80 |
81 | } else if (action == MenuAction_VoteEnd) {
82 | int winner = GetMenuInt(menu, param1);
83 | if (winner == StringToInt(RANDOM_MAP_VOTE)) {
84 | ChangeMap(g_MapVotePool, GetArrayRandomIndex(g_MapVotePool));
85 | } else {
86 | ChangeMap(g_MapVotePool, GetMenuInt(menu, param1));
87 | }
88 |
89 | } else if (action == MenuAction_End) {
90 | CloseHandle(menu);
91 | }
92 |
93 | return 0;
94 | }
95 |
--------------------------------------------------------------------------------
/pugsetup/pugsetup_autokicker.sp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "include/logdebug.inc"
5 | #include "include/pugsetup.inc"
6 | #include "pugsetup/generic.sp"
7 |
8 | #pragma semicolon 1
9 | #pragma newdecls required
10 |
11 | ConVar g_AutoKickerEnabledCvar;
12 | ConVar g_KickMessageCvar;
13 | ConVar g_KickNotPickedCvar;
14 | ConVar g_KickWhenLiveCvar;
15 | ConVar g_TimeToReadyCvar;
16 | ConVar g_TimeToReadyKickMessageCvar;
17 | ConVar g_UseAdminImmunityCvar;
18 |
19 | bool g_CompletedAdminCheck[MAXPLAYERS + 1];
20 | int g_ClientReadyTime[MAXPLAYERS +
21 | 1]; // first time (seconds) when the client was capable of readying up
22 |
23 | // clang-format off
24 | public Plugin myinfo = {
25 | name = "CS:GO PugSetup: autokicker",
26 | author = "splewis",
27 | description = "Adds cvars to automatically kick players when they aren't part of the current pug",
28 | version = PLUGIN_VERSION,
29 | url = "https://github.com/splewis/csgo-pug-setup"
30 | };
31 | // clang-format on
32 |
33 | public void OnPluginStart() {
34 | LoadTranslations("pugsetup.phrases");
35 | g_AutoKickerEnabledCvar = CreateConVar("sm_pugsetup_autokicker_enabled", "1",
36 | "Whether the autokicker is enabled or not");
37 | g_KickMessageCvar = CreateConVar("sm_pugsetup_autokicker_message", "Sorry, this pug is full",
38 | "Message to show to clients when they are kicked");
39 | g_KickNotPickedCvar =
40 | CreateConVar("sm_pugsetup_autokicker_kick_not_picked", "1",
41 | "Whether to kick players not selected by captains in a captain-style game");
42 | g_KickWhenLiveCvar = CreateConVar(
43 | "sm_pugsetup_autokicker_kick_when_live", "1",
44 | "Whether the autokicker kicks newly connecting clients during live matches when there are already full teams");
45 | g_TimeToReadyCvar = CreateConVar(
46 | "sm_pugsetup_autokicker_ready_time", "0",
47 | "The time (in seconds) clients have to ready up before being kicked, set to 0 to disable.");
48 | g_TimeToReadyKickMessageCvar = CreateConVar(
49 | "sm_pugsetup_autokicker_ready_time_kick_message", "You failed to ready up in time",
50 | "Message clients recieve when kicked for not readying up in time.");
51 | g_UseAdminImmunityCvar =
52 | CreateConVar("sm_pugsetup_autokicker_admin_immunity", "1",
53 | "Whether admins (defined by pugsetup's admin flag cvar) are immune to kicks");
54 | AutoExecConfig(true, "pugsetup_autokicker", "sourcemod/pugsetup");
55 | InitDebugLog(DEBUG_CVAR, "autokicker");
56 | }
57 |
58 | public void OnClientConnected(int client) {
59 | g_CompletedAdminCheck[client] = false;
60 | }
61 |
62 | public void OnClientPostAdminCheck(int client) {
63 | g_CompletedAdminCheck[client] = true;
64 | bool enabled = g_AutoKickerEnabledCvar.IntValue != 0 && g_KickWhenLiveCvar.IntValue != 0;
65 | bool live = PugSetup_IsMatchLive() || PugSetup_IsPendingStart();
66 |
67 | if (enabled && live && !PugSetup_PlayerAtStart(client)) {
68 | int count = 0;
69 | for (int i = 1; i <= MaxClients; i++) {
70 | if (IsPlayer(i)) {
71 | int team = GetClientTeam(i);
72 | if (team != CS_TEAM_NONE && team != CS_TEAM_SPECTATOR) {
73 | count++;
74 | }
75 | }
76 | }
77 |
78 | LogDebug("%L connected, count of players = %d", client, count);
79 | if (count >= PugSetup_GetPugMaxPlayers()) {
80 | Kick(client, g_KickMessageCvar);
81 | }
82 | }
83 |
84 | if (g_AutoKickerEnabledCvar.IntValue != 0) {
85 | g_ClientReadyTime[client] = GetTime();
86 | }
87 | }
88 |
89 | public void PugSetup_OnSetup() {
90 | for (int i = 1; i <= MaxClients; i++) {
91 | g_ClientReadyTime[i] = GetTime();
92 | }
93 | }
94 |
95 | public void PugSetup_OnNotPicked(int client) {
96 | if (g_AutoKickerEnabledCvar.IntValue != 0 && g_KickNotPickedCvar.IntValue != 0) {
97 | Kick(client, g_KickMessageCvar);
98 | }
99 | }
100 |
101 | public void PugSetup_OnReadyToStartCheck(int readyPlayers, int totalPlayers) {
102 | if (g_AutoKickerEnabledCvar.IntValue != 0 && g_TimeToReadyCvar.IntValue != 0) {
103 | for (int i = 1; i <= MaxClients; i++) {
104 | int dt = GetTime() - g_ClientReadyTime[i];
105 | if (g_CompletedAdminCheck[i] && IsPlayer(i) && !PugSetup_IsReady(i) &&
106 | dt > g_TimeToReadyCvar.IntValue) {
107 | Kick(i, g_TimeToReadyKickMessageCvar);
108 | }
109 | }
110 | }
111 | }
112 |
113 | static void Kick(int client, ConVar msgCvar) {
114 | if (g_UseAdminImmunityCvar.IntValue != 0 && PugSetup_IsPugAdmin(client)) {
115 | LogDebug("Blocking kick of %L since he is an admin", client);
116 | return;
117 | }
118 |
119 | char msg[1024];
120 | GetConVarString(msgCvar, msg, sizeof(msg));
121 | KickClient(client, msg);
122 | LogDebug("Kicking %L with message %s", client, msg);
123 | }
124 |
--------------------------------------------------------------------------------
/pugsetup/pugsetup_chatmoney.sp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "include/pugsetup.inc"
5 | #include "pugsetup/generic.sp"
6 |
7 | #pragma semicolon 1
8 | #pragma newdecls required
9 |
10 | ConVar g_hEnabled;
11 |
12 | // clang-format off
13 | public Plugin myinfo = {
14 | name = "CS:GO PugSetup: write team money to chat",
15 | author = "Versatile_BFG/jkroepke",
16 | description = "Write the team members' money to the chat (like WarMod)",
17 | version = PLUGIN_VERSION,
18 | url = "https://github.com/splewis/csgo-pug-setup"
19 | };
20 | // clang-format on
21 |
22 | public void OnPluginStart() {
23 | LoadTranslations("pugsetup.phrases");
24 | g_hEnabled = CreateConVar("sm_pugsetup_chatmoney_enabled", "1", "Whether the plugin is enabled");
25 | AutoExecConfig(true, "pugsetup_chatmoney", "sourcemod/pugsetup");
26 | HookEvent("round_start", Event_Round_Start);
27 | }
28 |
29 | public Action Event_Round_Start(Event event, const char[] name, bool dontBroadcast) {
30 | if (!PugSetup_IsMatchLive() || g_hEnabled.IntValue == 0)
31 | return;
32 |
33 | ArrayList players = new ArrayList();
34 |
35 | // sort by money
36 | for (int i = 1; i <= MaxClients; i++) {
37 | if (IsPlayer(i) && OnActiveTeam(i)) {
38 | players.Push(i);
39 | }
40 | }
41 |
42 | SortADTArrayCustom(players, SortMoneyFunction);
43 |
44 | char player_money[16];
45 | char has_weapon[4];
46 | int pri_weapon;
47 |
48 | int numPlayers = players.Length;
49 |
50 | // display team players money
51 | for (int i = 0; i < numPlayers; i++) {
52 | for (int j = 0; j < numPlayers; j++) {
53 | int displayClient = players.Get(i);
54 | int moneyClient = players.Get(j);
55 |
56 | if (GetClientTeam(displayClient) == GetClientTeam(moneyClient)) {
57 | pri_weapon = GetPlayerWeaponSlot(moneyClient, 0);
58 | if (pri_weapon == -1) {
59 | has_weapon = ">";
60 | } else {
61 | has_weapon = "\0";
62 | }
63 | IntToMoney(GetClientMoney(moneyClient), player_money, sizeof(player_money));
64 | PugSetup_Message(displayClient, "\x01$%s \x04%s> \x03%N", player_money, has_weapon,
65 | moneyClient);
66 | }
67 | }
68 | }
69 |
70 | delete players;
71 | }
72 |
73 | public int SortMoneyFunction(int index1, int index2, Handle array, Handle hnd) {
74 | int client1 = GetArrayCell(array, index1);
75 | int client2 = GetArrayCell(array, index2);
76 | int money1 = GetClientMoney(client1);
77 | int money2 = GetClientMoney(client2);
78 |
79 | if (money1 > money2) {
80 | return -1;
81 | } else if (money1 == money2) {
82 | return 0;
83 | } else {
84 | return 1;
85 | }
86 | }
87 |
88 | public int GetClientMoney(int client) {
89 | int offset = FindSendPropInfo("CCSPlayer", "m_iAccount");
90 | return GetEntData(client, offset);
91 | }
92 |
93 | /**
94 | * Get the comma'd string version of an integer
95 | *
96 | * @param OldMoney the integer to convert
97 | * @param String:NewMoney the buffer to save the string in
98 | * @param size the size of the buffer
99 | * @noreturn
100 | */
101 | public void IntToMoney(int OldMoney, char[] NewMoney, int size) {
102 | char Temp[32];
103 | char OldMoneyStr[32];
104 | char tempChar;
105 | int RealLen = 0;
106 |
107 | IntToString(OldMoney, OldMoneyStr, sizeof(OldMoneyStr));
108 |
109 | for (int i = strlen(OldMoneyStr) - 1; i >= 0; i--) {
110 | if (RealLen % 3 == 0 && RealLen != strlen(OldMoneyStr) && i != strlen(OldMoneyStr) - 1) {
111 | tempChar = OldMoneyStr[i];
112 | Format(Temp, sizeof(Temp), "%s,%s", tempChar, Temp);
113 | } else {
114 | tempChar = OldMoneyStr[i];
115 | Format(Temp, sizeof(Temp), "%s%s", tempChar, Temp);
116 | }
117 | RealLen++;
118 | }
119 | Format(NewMoney, size, "%s", Temp);
120 | }
121 |
--------------------------------------------------------------------------------
/pugsetup/pugsetup_hostname.sp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "include/pugsetup.inc"
5 | #include "pugsetup/generic.sp"
6 |
7 | #pragma semicolon 1
8 | #pragma newdecls required
9 |
10 | #define MAX_HOST_LENGTH 256
11 |
12 | ConVar g_hEnabled;
13 | ConVar g_HostnameCvar;
14 |
15 | bool g_GotHostName = false; // keep track of it, so we only fetch it once
16 | char g_HostName[MAX_HOST_LENGTH]; // stores the original hostname
17 |
18 | // clang-format off
19 | public Plugin myinfo = {
20 | name = "CS:GO PugSetup: hostname setter",
21 | author = "splewis",
22 | description = "Tweaks the server hostname according to the pug status",
23 | version = PLUGIN_VERSION,
24 | url = "https://github.com/splewis/csgo-pug-setup"
25 | };
26 | // clang-format on
27 |
28 | public void OnPluginStart() {
29 | LoadTranslations("pugsetup.phrases");
30 | g_hEnabled = CreateConVar("sm_pugsetup_hostname_enabled", "1", "Whether the plugin is enabled");
31 | AutoExecConfig(true, "pugsetup_hostname", "sourcemod/pugsetup");
32 | g_HostnameCvar = FindConVar("hostname");
33 | g_GotHostName = false;
34 |
35 | if (g_HostnameCvar == INVALID_HANDLE)
36 | SetFailState("Failed to find cvar \"hostname\"");
37 |
38 | HookEvent("round_start", Event_RoundStart);
39 | }
40 |
41 | public void OnConfigsExecuted() {
42 | if (!g_GotHostName) {
43 | g_HostnameCvar.GetString(g_HostName, sizeof(g_HostName));
44 | g_GotHostName = true;
45 | }
46 | }
47 |
48 | public void PugSetup_OnReadyToStartCheck(int readyPlayers, int totalPlayers) {
49 | if (g_hEnabled.IntValue == 0)
50 | return;
51 |
52 | char hostname[MAX_HOST_LENGTH];
53 | int need = PugSetup_GetPugMaxPlayers() - totalPlayers;
54 |
55 | if (need >= 1) {
56 | Format(hostname, sizeof(hostname), "%s [%i / %i]", g_HostName, totalPlayers, PugSetup_GetPugMaxPlayers());
57 | } else {
58 | Format(hostname, sizeof(hostname), "%s [WARM UP]", g_HostName);
59 | }
60 |
61 | g_HostnameCvar.SetString(hostname);
62 | }
63 |
64 | public void PugSetup_OnGoingLive() {
65 | if (g_hEnabled.IntValue == 0)
66 | return;
67 |
68 | char hostname[MAX_HOST_LENGTH];
69 | Format(hostname, sizeof(hostname), "%s [LIVE]", g_HostName);
70 | g_HostnameCvar.SetString(hostname);
71 | }
72 |
73 | public Action Event_RoundStart(Event event, const char[] name, bool dontBroadcast) {
74 | if (g_hEnabled.IntValue == 0 || !PugSetup_IsMatchLive())
75 | return Plugin_Continue;
76 |
77 | char hostname[MAX_HOST_LENGTH];
78 | Format(hostname, sizeof(hostname), "%s [%i / %i]", g_HostName, CS_GetTeamScore(CS_TEAM_CT),
79 | CS_GetTeamScore(CS_TEAM_T));
80 | g_HostnameCvar.SetString(hostname);
81 |
82 | return Plugin_Continue;
83 | }
84 |
85 | public void PugSetup_OnMatchOver() {
86 | if (GetConVarInt(g_hEnabled) == 0)
87 | return;
88 |
89 | g_HostnameCvar.SetString(g_HostName);
90 | }
91 |
--------------------------------------------------------------------------------
/pugsetup/pugsetup_teamlocker.sp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "include/logdebug.inc"
5 | #include "include/pugsetup.inc"
6 | #include "pugsetup/generic.sp"
7 |
8 | #pragma semicolon 1
9 | #pragma newdecls required
10 |
11 | ConVar g_hBlockSpecJoins;
12 | ConVar g_hKickTime;
13 | ConVar g_hLockTeamsEnabled;
14 |
15 | // clang-format off
16 | public Plugin myinfo = {
17 | name = "CS:GO PugSetup: team locker",
18 | author = "splewis",
19 | description = "Blocks team join events to full teams",
20 | version = PLUGIN_VERSION,
21 | url = "https://github.com/splewis/csgo-pug-setup"
22 | };
23 | // clang-format on
24 |
25 | public void OnPluginStart() {
26 | InitDebugLog(DEBUG_CVAR, "teamlock");
27 |
28 | g_hBlockSpecJoins = CreateConVar(
29 | "sm_pugsetup_block_spectate_joins", "0",
30 | "Whether players are blocked from joining spectator (admins excluded) during a live match.");
31 | g_hKickTime = CreateConVar(
32 | "sm_pugsetup_kick_time", "0",
33 | "If players don't join a team after this many seconds, they will be kicked. Use 0 to disable.");
34 | g_hLockTeamsEnabled = CreateConVar("sm_pugsetup_teamlocker_enabled", "1",
35 | "Whether teams are locked when matches are live.");
36 |
37 | AutoExecConfig(true, "pugsetup_teamlocker", "sourcemod/pugsetup");
38 | AddCommandListener(Command_JoinTeam, "jointeam");
39 | HookEvent("player_team", Event_OnPlayerTeam, EventHookMode_Pre);
40 | }
41 |
42 | public void OnClientPutInServer(int client) {
43 | if (PugSetup_GetGameState() == GameState_None) {
44 | return;
45 | }
46 |
47 | int kickTime = g_hKickTime.IntValue;
48 | if (kickTime != 0) {
49 | CreateTimer(float(kickTime), Timer_CheckIfSpectator, GetClientSerial(client));
50 | }
51 | }
52 |
53 | public Action Timer_CheckIfSpectator(Handle timer, int serial) {
54 | if (PugSetup_GetGameState() == GameState_None) {
55 | return Plugin_Handled;
56 | }
57 |
58 | int client = GetClientFromSerial(serial);
59 | if (IsPlayer(client) && !PugSetup_IsPugAdmin(client)) {
60 | int team = GetClientTeam(client);
61 | if (team == CS_TEAM_SPECTATOR || team == CS_TEAM_NONE) {
62 | KickClient(client, "You did not join a team in time");
63 | }
64 | }
65 |
66 | return Plugin_Handled;
67 | }
68 |
69 | public Action Event_OnPlayerTeam(Event event, const char[] name, bool dontBroadcast) {
70 | return Plugin_Continue;
71 | }
72 |
73 | public Action Command_JoinTeam(int client, const char[] command, int argc) {
74 | if (!IsValidClient(client))
75 | return Plugin_Stop;
76 |
77 | if (g_hLockTeamsEnabled.IntValue == 0)
78 | return Plugin_Continue;
79 |
80 | // blocks changes during team-selection/lo3-process
81 | if (PugSetup_IsPendingStart())
82 | return Plugin_Stop;
83 |
84 | // don't do anything if not live/not in startup phase
85 | if (!PugSetup_IsMatchLive())
86 | return Plugin_Continue;
87 |
88 | char arg[4];
89 | GetCmdArg(1, arg, sizeof(arg));
90 | int team_to = StringToInt(arg);
91 |
92 | LogDebug("%L jointeam command, from %d to %d", client, GetClientTeam(client), team_to);
93 |
94 | // don't let someone change to a "none" team (e.g. using auto-select)
95 | if (team_to == CS_TEAM_NONE)
96 | return Plugin_Stop;
97 |
98 | if (team_to == CS_TEAM_SPECTATOR && !PugSetup_IsPugAdmin(client) &&
99 | g_hBlockSpecJoins.IntValue != 0)
100 | return Plugin_Stop;
101 |
102 | int playerCount = 0;
103 | for (int i = 1; i <= MaxClients; i++) {
104 | if (IsPlayer(i) && GetClientTeam(i) == team_to) {
105 | playerCount++;
106 | }
107 | }
108 |
109 | LogDebug("playerCount on team %d = %d", team_to, playerCount);
110 |
111 | if (playerCount >= PugSetup_GetPugMaxPlayers() / 2) {
112 | LogDebug("blocking jointeam");
113 | return Plugin_Stop;
114 | } else {
115 | LogDebug("allowing jointeam");
116 | return Plugin_Continue;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/pugsetup/pugsetup_teamnames.sp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include "include/logdebug.inc"
7 | #include "include/pugsetup.inc"
8 | #include "pugsetup/generic.sp"
9 |
10 | #pragma semicolon 1
11 | #pragma newdecls required
12 |
13 | /** Client cookie handles **/
14 | Handle g_teamNameCookie = INVALID_HANDLE;
15 | Handle g_teamFlagCookie = INVALID_HANDLE;
16 | #define TEAM_NAME_LENGTH 128
17 | #define TEAM_FLAG_LENGTH 4
18 |
19 | // clang-format off
20 | public Plugin myinfo = {
21 | name = "CS:GO PugSetup: team names setter",
22 | author = "splewis",
23 | description = "Sets team names/flags on game going live",
24 | version = PLUGIN_VERSION,
25 | url = "https://github.com/splewis/csgo-pug-setup"
26 | };
27 | // clang-format on
28 |
29 | public void OnPluginStart() {
30 | InitDebugLog(DEBUG_CVAR, "teamnames");
31 | LoadTranslations("common.phrases");
32 | LoadTranslations("pugsetup.phrases");
33 | RegAdminCmd(
34 | "sm_name", Command_Name, ADMFLAG_CHANGEMAP,
35 | "Sets a team name/flag to go with a player: sm_name , use quotes for the team name if it includes a space!");
36 | RegAdminCmd("sm_listnames", Command_ListNames, ADMFLAG_CHANGEMAP,
37 | "Lists all players' and their team names/flag, if they have one set.");
38 | g_teamNameCookie =
39 | RegClientCookie("pugsetup_teamname", "Pugsetup team name", CookieAccess_Protected);
40 | g_teamFlagCookie = RegClientCookie(
41 | "pugsetup_teamflag", "Pugsetup team flag (2-letter country code)", CookieAccess_Protected);
42 | }
43 |
44 | public void PugSetup_OnGoingLive() {
45 | ArrayList ctNames = new ArrayList(TEAM_NAME_LENGTH);
46 | ArrayList ctFlags = new ArrayList(TEAM_FLAG_LENGTH);
47 | ArrayList tNames = new ArrayList(TEAM_NAME_LENGTH);
48 | ArrayList tFlags = new ArrayList(TEAM_FLAG_LENGTH);
49 |
50 | FillPotentialNames(CS_TEAM_CT, ctNames, ctFlags);
51 | FillPotentialNames(CS_TEAM_T, tNames, tFlags);
52 |
53 | int choice = -1;
54 | char name[TEAM_NAME_LENGTH];
55 | char flag[TEAM_FLAG_LENGTH];
56 |
57 | if (GetArraySize(ctNames) > 0) {
58 | choice = GetArrayRandomIndex(ctNames);
59 | GetArrayString(ctNames, choice, name, sizeof(name));
60 | GetArrayString(ctFlags, choice, flag, sizeof(flag));
61 | LogDebug("Setting ct name, flag = %s, %s", name, flag);
62 | SetTeamInfo(CS_TEAM_CT, name, flag);
63 | }
64 |
65 | if (GetArraySize(tNames) > 0) {
66 | choice = GetArrayRandomIndex(tNames);
67 | GetArrayString(tNames, choice, name, sizeof(name));
68 | GetArrayString(tFlags, choice, flag, sizeof(flag));
69 | LogDebug("Setting t name, flag = %s, %s", name, flag);
70 | SetTeamInfo(CS_TEAM_T, name, flag);
71 | }
72 |
73 | delete ctNames;
74 | delete ctFlags;
75 | delete tNames;
76 | delete tFlags;
77 | }
78 |
79 | public Action Command_ListNames(int client, int args) {
80 | int count = 0;
81 | for (int i = 1; i <= MaxClients; i++) {
82 | if (IsPlayer(i) && AreClientCookiesCached(i)) {
83 | char name[TEAM_NAME_LENGTH];
84 | char flag[TEAM_FLAG_LENGTH];
85 | GetClientCookie(i, g_teamNameCookie, name, sizeof(name));
86 | GetClientCookie(i, g_teamFlagCookie, flag, sizeof(flag));
87 | if (!StrEqual(name, "")) {
88 | ReplyToCommand(client, "%N: %s (%s)", i, name, flag);
89 | count++;
90 | }
91 | }
92 | }
93 | if (count == 0)
94 | ReplyToCommand(client, "Nobody has a team name/flag set.");
95 |
96 | return Plugin_Handled;
97 | }
98 |
99 | public Action Command_Name(int client, int args) {
100 | char arg1[MAX_NAME_LENGTH];
101 | char arg2[TEAM_NAME_LENGTH];
102 |
103 | if (args >= 2 && GetCmdArg(1, arg1, sizeof(arg1)) && GetCmdArg(2, arg2, sizeof(arg2))) {
104 | int target = FindTarget(client, arg1, true, false);
105 | char flag[3];
106 |
107 | if (IsPlayer(target)) {
108 | SetClientCookie(target, g_teamNameCookie, arg2);
109 |
110 | // by default, use arg3 from the command, otherwise try to use the ip address
111 | if (args <= 2 || !GetCmdArg(3, flag, sizeof(flag))) {
112 | if (GetPlayerFlagFromIP(target, flag))
113 | SetClientCookie(target, g_teamFlagCookie, flag);
114 | }
115 | SetClientCookie(target, g_teamFlagCookie, flag);
116 | ReplyToCommand(client, "Set team data for %L: name = %s, flag = %s", target, arg2, flag);
117 | }
118 |
119 | } else {
120 | ReplyToCommand(client, "Usage: sm_name [team flag code]");
121 | }
122 |
123 | return Plugin_Handled;
124 | }
125 |
126 | static bool GetPlayerFlagFromIP(int client, char flag[3]) {
127 | char ip[32];
128 | if (!GetClientIP(client, ip, sizeof(ip)) || !GeoipCode2(ip, flag)) {
129 | Format(flag, sizeof(flag), "");
130 | return true;
131 | }
132 | return false;
133 | }
134 |
135 | public void FillPotentialNames(int team, ArrayList names, ArrayList flags) {
136 | for (int i = 1; i <= MaxClients; i++) {
137 | if (IsPlayer(i) && GetClientTeam(i) == team && AreClientCookiesCached(i)) {
138 | char name[TEAM_NAME_LENGTH];
139 | char flag[TEAM_FLAG_LENGTH];
140 | GetClientCookie(i, g_teamNameCookie, name, sizeof(name));
141 | GetClientCookie(i, g_teamFlagCookie, flag, sizeof(flag));
142 |
143 | if (StrEqual(name, ""))
144 | continue;
145 |
146 | names.PushString(name);
147 | flags.PushString(flag);
148 | }
149 | }
150 |
151 | // if we have no results, might as well throw in some geo-ip flags with empty names
152 | if (names.Length == 0) {
153 | for (int i = 1; i <= MaxClients; i++) {
154 | char flag[3];
155 | if (IsPlayer(i) && GetClientTeam(i) == team && GetPlayerFlagFromIP(i, flag)) {
156 | names.PushString("");
157 | flags.PushString(flag);
158 | }
159 | }
160 | }
161 | }
162 |
163 | /** Clear the names/flags when the game is over **/
164 | public void PugSetup_OnMatchOver(bool hasDemo, const char[] demoFileName) {
165 | LogDebug("Match over - resetting team names");
166 | SetTeamInfo(CS_TEAM_T, "", "");
167 | SetTeamInfo(CS_TEAM_CT, "", "");
168 | }
169 |
--------------------------------------------------------------------------------
/roundstartbotstop.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #define SOUND "/level/gnomeftw.wav"
9 |
10 | public Plugin:myinfo =
11 | {
12 | name = "L4D2 Ready-Up",
13 | author = "CanadaRox & Blazers Team",
14 | description = "New and improved ready-up plugin.",
15 | version = "6",
16 | url = ""
17 | };
18 |
19 | enum L4D2Team
20 | {
21 | L4D2Team_None = 0,
22 | L4D2Team_Spectator,
23 | L4D2Team_Survivor,
24 | L4D2Team_Infected
25 | }
26 |
27 | // Game Cvars
28 | new Handle:god;
29 | new Handle:sb_move;
30 | new Handle:liveForward;
31 | new bool:inReadyUp;
32 | new bool:blockSecretSpam[MAXPLAYERS + 1];
33 |
34 | public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
35 | {
36 | CreateNative("IsInReady", Native_IsInReady);
37 | liveForward = CreateGlobalForward("OnRoundIsLive", ET_Event);
38 | return APLRes_Success;
39 | }
40 | public OnPluginStart()
41 | {
42 | HookEvent("round_start", RoundStart_Event, EventHookMode_Post);
43 |
44 | god = FindConVar("god");
45 | sb_move = FindConVar("sb_move");
46 |
47 | RegConsoleCmd("sm_return", Return_Cmd, "Return to a valid saferoom spawn if you get stuck");
48 | RegConsoleCmd("sm_secret", Secret_Cmd, "Every player has a different secret number between 0-1023");
49 |
50 | }
51 |
52 | public OnMapStart()
53 | {
54 | PrecacheSound(SOUND);
55 | for (new client = 1; client <= MAXPLAYERS; client++)
56 | {
57 | blockSecretSpam[client] = false;
58 | }
59 | }
60 |
61 | public OnPluginEnd()
62 | {
63 | if (inReadyUp)
64 | InitiateLive(false);
65 | }
66 |
67 | /* This ensures all cvars are reset if the map is changed during ready-up */
68 | public OnMapEnd()
69 | {
70 | if (inReadyUp)
71 | InitiateLive(false);
72 | }
73 |
74 | public Native_IsInReady(Handle:plugin, numParams)
75 | {
76 | return _:inReadyUp;
77 | }
78 |
79 |
80 | /* No need to do any other checks since it seems like this is required no matter what since the intros unfreezes players after the animation completes */
81 | public Action:OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:angles[3], &weapon)
82 | {
83 | if (inReadyUp)
84 | {
85 | if (IsClientInGame(client) && L4D2Team:GetClientTeam(client) == L4D2Team_Survivor)
86 | {
87 | if (GetEntityFlags(client) & FL_INWATER)
88 | ReturnPlayerToSaferoom(client, false);
89 | }
90 | }
91 | }
92 |
93 | public Action:L4D_OnFirstSurvivorLeftSafeArea(client)
94 | {
95 | if (inReadyUp)
96 | {
97 | InitiateLive();
98 | return Plugin_Stop;
99 | }
100 | return Plugin_Continue;
101 | }
102 |
103 | public Action:Return_Cmd(client, args)
104 | {
105 | if (inReadyUp)
106 | ReturnPlayerToSaferoom(client, false);
107 | else ReplyToCommand(client, "[SM] This command can only be used when nobody leaves safe area.");
108 | return Plugin_Handled;
109 | }
110 |
111 | public RoundStart_Event(Handle:event, const String:name[], bool:dontBroadcast)
112 | {
113 | InitiateReadyUp();
114 | }
115 |
116 |
117 | InitiateReadyUp()
118 | {
119 |
120 | inReadyUp = true;
121 |
122 | SetConVarFlags(god, GetConVarFlags(god) & ~FCVAR_NOTIFY);
123 | SetConVarBool(god, true);
124 | SetConVarFlags(god, GetConVarFlags(god) | FCVAR_NOTIFY);
125 | SetConVarBool(sb_move, false);
126 |
127 | L4D2_CTimerStart(L4D2CT_VersusStartTimer, 99999.9);
128 |
129 | }
130 |
131 | InitiateLive(bool:real = true)
132 | {
133 | inReadyUp = false;
134 |
135 | SetConVarFlags(god, GetConVarFlags(god) & ~FCVAR_NOTIFY);
136 | SetConVarBool(god, false);
137 | SetConVarFlags(god, GetConVarFlags(god) | FCVAR_NOTIFY);
138 | SetConVarBool(sb_move, true);
139 |
140 | L4D2_CTimerStart(L4D2CT_VersusStartTimer, 60.0);
141 | if (real)
142 | {
143 | Call_StartForward(liveForward);
144 | Call_Finish();
145 | }
146 |
147 | }
148 |
149 | ReturnPlayerToSaferoom(client, bool:flagsSet = true)
150 | {
151 | new warp_flags;
152 | new give_flags;
153 | if (!flagsSet)
154 | {
155 | warp_flags = GetCommandFlags("warp_to_start_area");
156 | SetCommandFlags("warp_to_start_area", warp_flags & ~FCVAR_CHEAT);
157 | give_flags = GetCommandFlags("give");
158 | SetCommandFlags("give", give_flags & ~FCVAR_CHEAT);
159 | }
160 |
161 | if (GetEntProp(client, Prop_Send, "m_isHangingFromLedge"))
162 | {
163 | FakeClientCommand(client, "give health");
164 | }
165 |
166 | FakeClientCommand(client, "warp_to_start_area");
167 |
168 | if (!flagsSet)
169 | {
170 | SetCommandFlags("warp_to_start_area", warp_flags);
171 | SetCommandFlags("give", give_flags);
172 | }
173 | }
174 |
175 | public Action:Secret_Cmd(client, args)
176 | {
177 | if (inReadyUp)
178 | {
179 | new AdminId:id;
180 | id = GetUserAdmin(client);
181 | new bool:hasFlag = false;
182 | if (id != INVALID_ADMIN_ID)
183 | {
184 | hasFlag = GetAdminFlag(id, Admin_Reservation); // Check for specific admin flag
185 | }
186 |
187 | if (!hasFlag)
188 | {
189 | ReplyToCommand(client, "[SM] Only admins can do this.");
190 | return Plugin_Handled;
191 | }
192 | DoSecrets(client);
193 |
194 | return Plugin_Handled;
195 |
196 | }
197 | ReplyToCommand(client, "[SM] This command can only be used when nobody leaves safe area.");
198 | return Plugin_Continue;
199 | }
200 |
201 | stock DoSecrets(client)
202 | {
203 | if (L4D2Team:GetClientTeam(client) == L4D2Team_Survivor && !blockSecretSpam[client])
204 | {
205 | new particle = CreateEntityByName("info_particle_system");
206 | decl Float:pos[3];
207 | GetClientAbsOrigin(client, pos);
208 | pos[2] += 50;
209 | TeleportEntity(particle, pos, NULL_VECTOR, NULL_VECTOR);
210 | DispatchKeyValue(particle, "effect_name", "achieved");
211 | DispatchKeyValue(particle, "targetname", "particle");
212 | DispatchSpawn(particle);
213 | ActivateEntity(particle);
214 | AcceptEntityInput(particle, "start");
215 | CreateTimer(10.0, killParticle, particle, TIMER_FLAG_NO_MAPCHANGE);
216 | EmitSoundToAll(SOUND, client, SNDCHAN_AUTO, SNDLEVEL_NORMAL, SND_NOFLAGS, 0.5);
217 | CreateTimer(2.0, SecretSpamDelay, client);
218 | blockSecretSpam[client] = true;
219 | }
220 | }
221 |
222 | public Action:SecretSpamDelay(Handle:timer, any:client)
223 | {
224 | blockSecretSpam[client] = false;
225 | }
226 |
227 | public Action:killParticle(Handle:timer, any:entity)
228 | {
229 | if (entity > 0 && IsValidEntity(entity) && IsValidEdict(entity))
230 | {
231 | AcceptEntityInput(entity, "Kill");
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/si_class_announce.sp:
--------------------------------------------------------------------------------
1 | #pragma semicolon 1
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #undef REQUIRE_PLUGIN
8 | #include
9 | #define REQUIRE_PLUGIN
10 |
11 | #define IS_VALID_CLIENT(%1) (%1 > 0 && %1 <= MaxClients)
12 | #define IS_SURVIVOR(%1) (GetClientTeam(%1) == 2)
13 | #define IS_INFECTED(%1) (GetClientTeam(%1) == 3)
14 | #define IS_VALID_INGAME(%1) (IS_VALID_CLIENT(%1) && IsClientInGame(%1))
15 | #define IS_VALID_SURVIVOR(%1) (IS_VALID_INGAME(%1) && IS_SURVIVOR(%1))
16 | #define IS_VALID_INFECTED(%1) (IS_VALID_INGAME(%1) && IS_INFECTED(%1))
17 | #define IS_SURVIVOR_ALIVE(%1) (IS_VALID_SURVIVOR(%1) && IsPlayerAlive(%1))
18 | #define IS_INFECTED_ALIVE(%1) (IS_VALID_INFECTED(%1) && IsPlayerAlive(%1))
19 |
20 | #define ZC_SMOKER 1
21 | #define ZC_BOOMER 2
22 | #define ZC_HUNTER 3
23 | #define ZC_SPITTER 4
24 | #define ZC_JOCKEY 5
25 | #define ZC_CHARGER 6
26 | #define ZC_WITCH 7
27 | #define ZC_TANK 8
28 |
29 | #define MAXSPAWNS 8
30 |
31 | new bool: g_bReadyUpAvailable = false;
32 | new bool: g_bIsRoundLive = false;
33 | new Handle: g_hOnOff;
34 |
35 |
36 | new const String: g_csSIClassName[][] =
37 | {
38 | "",
39 | "Smoker",
40 | "Boomer",
41 | "Hunter",
42 | "Spitter",
43 | "Jockey",
44 | "Charger",
45 | "Witch",
46 | "Tank"
47 | };
48 |
49 |
50 | public Plugin:myinfo =
51 | {
52 | name = "Special Infected Class Announce",
53 | author = "Tabun",
54 | description = "Report what SI classes are up when the round starts.",
55 | version = "0.9.2",
56 | url = "none"
57 | }
58 |
59 | public OnAllPluginsLoaded()
60 | {
61 | g_bIsRoundLive = false;
62 | g_bReadyUpAvailable = LibraryExists("readyup");
63 | g_hOnOff = CreateConVar("l4d_announceSI_on", "1", "Display SI class for player when using commands", FCVAR_PLUGIN);
64 | RegConsoleCmd("sm_spawns", PrintSpawns);
65 | HookEvent("round_end", Event_RoundEnd);
66 | }
67 | public OnLibraryRemoved(const String:name[])
68 | {
69 | if ( StrEqual(name, "readyup") ) { g_bReadyUpAvailable = false; }
70 | }
71 | public OnLibraryAdded(const String:name[])
72 | {
73 | if ( StrEqual(name, "readyup") ) { g_bReadyUpAvailable = true; }
74 | }
75 |
76 | public OnRoundIsLive()
77 | {
78 | if (GetConVarBool(g_hOnOff)) {
79 | // announce SI classes up now
80 | for (new i = 1; i <= MaxClients; i++)
81 | if (IS_SURVIVOR_ALIVE(i))
82 | AnnounceSIClasses(i);
83 | g_bIsRoundLive = true;
84 | }
85 | }
86 |
87 | public Action:Event_RoundEnd(Handle:event, String:name[], bool:dontBroadcast) {
88 | g_bIsRoundLive = false;
89 | }
90 |
91 | public Action:PrintSpawns(client, args) {
92 | if (!IsClientInGame(client) || !(1 <= client <= MaxClients)) {return;}
93 | if (g_bIsRoundLive) {
94 | PrintToChat(client, "\x01Special infected: \x05Your mom\x01, \x05Your dad\x01.");
95 | return;
96 | }
97 | if (!GetConVarBool(g_hOnOff)) return;
98 | AnnounceSIClasses(client);
99 | }
100 |
101 | public Action: L4D_OnFirstSurvivorLeftSafeArea( client )
102 | {
103 | // if no readyup, use this as the starting event
104 | if (!g_bReadyUpAvailable) {
105 | for (new i = 1; i <= MaxClients; i++)
106 | if(IS_SURVIVOR_ALIVE(i))
107 | AnnounceSIClasses(i);
108 | g_bIsRoundLive = true;
109 | }
110 | }
111 |
112 | stock AnnounceSIClasses(any:client)
113 | {
114 | // get currently active SI classes
115 | new iSpawns;
116 | new iSpawnClass[MAXSPAWNS+1];
117 |
118 | for (new i = 1; i <= MaxClients && iSpawns < MAXSPAWNS; i++) {
119 | if (!IS_INFECTED_ALIVE(i)) { continue; }
120 |
121 | iSpawnClass[iSpawns] = GetEntProp(i, Prop_Send, "m_zombieClass");
122 | iSpawns++;
123 | }
124 |
125 | // print classes, according to amount of spawns found
126 | switch (iSpawns) {
127 | case 4: {
128 | CPrintToChat(client,
129 | "Special Infected: {olive}%s{default}, {olive}%s{default}, {olive}%s{default}, {olive}%s{default}.",
130 | g_csSIClassName[iSpawnClass[0]],
131 | g_csSIClassName[iSpawnClass[1]],
132 | g_csSIClassName[iSpawnClass[2]],
133 | g_csSIClassName[iSpawnClass[3]]
134 | );
135 | }
136 | case 3: {
137 | CPrintToChat(client,
138 | "Special Infected: {olive}%s{default}, {olive}%s{default}, {olive}%s{default}.",
139 | g_csSIClassName[iSpawnClass[0]],
140 | g_csSIClassName[iSpawnClass[1]],
141 | g_csSIClassName[iSpawnClass[2]]
142 | );
143 | }
144 | case 2: {
145 | CPrintToChat(client,
146 | "Special Infected: {olive}%s{default}, {olive}%s{default}.",
147 | g_csSIClassName[iSpawnClass[0]],
148 | g_csSIClassName[iSpawnClass[1]]
149 | );
150 | }
151 | case 1: {
152 | CPrintToChat(client,
153 | "Special Infected: {olive}%s{default}.",
154 | g_csSIClassName[iSpawnClass[0]]
155 | );
156 | }
157 | default: {
158 | CPrintToChat(client, "There are no special infected.");
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------