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