/* Plugin generated by AMXX-Studio European Natural Selection League Plugin Author: Ari "jiriki" Timonen Credits: - DarkSnow's socket example - CAL Features by CAL-ns|Austin (xHomicide) and Jim "JazzX" Olson - Asmodee for waypoint fixes & weaponfix & other fixes - Multivac's Jetpack FIX - sawce's icons plugin - www.nsmod.org - www.amxmodx.org TODO: SQLx threaded -> gather early joining block /mercsok not working? amx_enslmove not working AFK kicker */ #include #include #include #include #include #include #include #include #include /************************************************************************************** * Global constants **************************************************************************************/ // Basic constants #define PLUGIN "ENSL" #define VERSION "1.4" #define AUTHOR "jiriki" #define WEB "www.ensl.org" #define CHANNEL "#ENSL" #define PASSWORD "europe" // AMX constants #define A_LEN_CMD 32 // Maximum length of command name #define A_LEN_CMDINFO 100 // Maximum length of command info // Global settings #define G_FREQ 5.0 // Global command delay // User tracking #define U_NUM_IDS 32 // Maximum number of users to track // Task ID's #define T_ADVERT 26 #define T_MAPBOOT 27 // Map boot #define T_JPCHECK 28 // Jetpack check #define T_STARTUP 29 // Startup #define T_AUTH 30 // Authentication #define T_OPTIONS 31 // Optionchecking #define T_RATECHECK 32 // Ratechecking #define T_FFCLOCK 33 // Forfeit clock #define T_HLTV 36 // Query: HLTV #define T_ESI 37 // Query: ESI #define T_AREQ 38 // Query: Admin Request #define T_BAN 39 // Query: Global Ban #define T_USER 100 // Query: User // HL/NS-related #define HL_NUM_PLAYERS 32 // Maximum number of players #define HL_NUM_COLUMN 10 // Maximum number of columns #define HL_LEN_NICK 31 // Maximum length of player nick #define HL_LEN_STEAMID 31 // Maximum length of SteamID #define HL_LEN_IP 15 // Maximum length of IP address #define HL_LEN_PORT 5 // Maximum length of server port #define HL_LEN_ADDR 21 // Maximum length of server address #define HL_LEN_DNS 50 // Maximum length of DNS #define HL_LEN_TEAM 15 // Maxmium length of teamname #define HL_LEN_COLUMN 20 // Maxmium length of column #define HL_LEN_SAY 191 // Maximum length of say msg #define HL_LEN_MSG 140 // Maximum length of console msg #define HL_LEN_POPUP 179 // Maxmium length of popup message #define HL_LEN_HUDMESSAGE 478 // Maxmium length of HUD message #define HL_LEN_MAP 25 // Maximum length of mapname #define HL_LEN_CMD 31 // Maximum length of cmdname #define HL_LEN_INFO 127 // Maximum length of cmdinfo #define HL_LEN_PWD 20 // Maximum length of password #define HL_ENT_MARINES 78 // Marine team joining entity #define HL_ENT_ALIENS 77 // Alien -- // Help #define H_NUM_CVARS 12 // Number of CVARs // ENSL query #define W_IP "78.46.36.107" // Script IP #define W_HOST "www.ensl.org" // Script hostname #define W_PORT 80 // Script port #define W_TIMEOUT 1 // HTTP Timeout #define W_READS 10 // Read attempts #define W_NEWLINE 13 // Newline character code #define W_NUM_LINES 30 // Number of script's output lines #define W_LEN_URL 230 // Maximum length of query's URL #define W_LEN_QUERY 250 // Maximum length of query #define W_LEN_INPUT 2048 // Maximum length of input buffer #define W_LEN_MARK 10 // Maximum length of start mark #define W_LEN_LINE 160 // Maximum length of output line #define W_LEN_CH 10 // Maximum length of challenge code #define W_LEN_FUNC 30 // Maximum length of handl. func. #define W_MARK "#RESPONSE#" // General response mark // ENSL Server Information #define S_URL "http://www.ensl.org/plugin/esi" // Script URL (ensl server info.) #define S_MARK "#ESI#" // Mark for found http line #define S_NUM_LINES 4 // Number of data lines (ex. chal.) #define S_LEN_TS 10 // Maximum length of timestamp // ENSL Admin request #define AR_URL "http://www.ensl.org/plugin/admin" // Script URL (adminrequest) #define AR_LEN_MSG 100 // Maximum length of timestamp // ENSL Global Bans #define B_URL "http://www.ensl.org/plugin/ban/" // Script URL (global ban) #define B_LEN_MSG 50 // Maximum length of ban reason // ENSL HLTV #define HT_URL "http://www.ensl.org/plugin/hltv_" // Script URL (HLTV) #define HT_MARK "#HLTV#" // Mark for found http line #define HT_NUM_LINES 1 // Number of data lines (ex. chal.) #define HT_LEN_RESPONSE 160 // Maximum length of ENSL Servers #define HT_LEN_DEMO 40 // Maximum length of demo name // ENSL playerinfo #define E_URL "http://www.ensl.org/plugin/user/" // Script URL (user query) #define E_MARK "#USER#" // Mark for found HTTP line #define E_NUM_LINES 11 // Number of data lines (ex. chal.) #define E_LEN_NICK 50 // Maximum length of username #define E_LEN_IP 15 // Maximum length of IP #define E_LEN_TEAM 50 // Maximum length of teamname #define E_LEN_LEVEL 10 // Maximum length of level #define E_LEN_RANK 10 // Maximum length of rank #define E_LEN_IDS 10 // Maximum length of ID's #define E_LEVEL_USER "User" // User level: user #define E_LEVEL_DONOR "Donor" // User level: donor #define E_LEVEL_REFEREE "Referee" // User level: referee #define E_LEVEL_ADMIN "Admin" // User level: admin #define E_LEVEL_NOTFOUND "Not Found" // User level: not found #define E_RANK_MEMBER "Member" // User level: member #define E_RANK_DEPUTEE "Deputee" // User level: deputee #define E_RANK_LEADER "Leader" // User level: leader #define E_RANK_NA "N/A" // User level: not applicable // Confirmation code #define CC_PASSCODE1 "9WvcZ9hX" // First part #define CC_PASSCODE2 "KF7L4luQ" // Second part #define CC_LEN_CODE 10 // Length of result #define CC_LEN_INPUT 250 // Length of input #define CC_LEN_MD5 33 // Length of MD5 checksum // Variable/rate-checking #define RC_DISABLED 0 // Ratechecking: disabled #define RC_ENABLED_KICK 1 // Ratechecking: enabled, kicks ON #define RC_ENABLED_WARN 2 // Ratechecking: enabled, kicks OFF #define RC_ENABLED_FETCH 3 // Ratechecking: enabled, fetch only #define RC_WARN_NEGATIVE 0 // Rate warning: yes #define RC_WARN_POSITIVE 1 // Rate warning: on #define RC_WARN_REQUEST 2 // Rate warning: requested #define RC_LEN_RANGE 20 // Maximum length of range string // Server CVARs #define C_LEN_NAME 30 // Maximum length of CVAR name #define C_NUM_INTS 41 // Number of integer CVAR's #define C_NUM_FLOATS 2 // Number of float CVAR's #define C_LEVEL_MANDATORY 0 // Importance: mandatory #define C_LEVEL_VOLUNTARY 1 // Importance: voluntary // Referee commands #define R_LEN_KICKREASON 30 // Maximum length of kick reason #define R_LEN_MINS 3 // Maximum length of ban time (mins) #define R_LEN_NUM 6 // Maximum length of a number value #define R_MAX_MINS 180 // Maximum ban time (mins) // Icons #define I_AUTH_ICON 512 // Msg Type: custom icon #define I_AUTH_NORMAL 0 // Msg Type: normal icon #define I_LEN_ICON 35 // Maximum length of icon name #define I_LEN_PATH 255 // Maximum length of icon name // Flashlight block #define F_FREQ 0.3 // Flashlight delay #define F_IMLUPSE 100 // Flashlight impulse number // Timetext #define TT_LEN_OUTPUT 40 // Maximum length of time text // Join delay #define DD_DELAY_DIGESTED 50 // Delay when disc. from belly // Combat #define CO_NORMAL 0 // Normal combat #define CO_CELERESUPPLY 1 // Celeresupply combat #define CO_ENSL 2 // ENSL Combat #define CO_SGSFADES 3 // SGs vs Fades #define CO_SGSKULKS 4 // SGs vs Skulks #define CO_LMGLERKS 5 // LMGs vs Lerks #define CO_NUM_MODES 6 // Number of combat modes #define CO_NUM_UPGRADES 40 // Maximum number of upgrades // Forfeit counter #define FF_TIME 600.0 // Forfeit counter time (secs) // Unstuck #define S_B MASK_PLAYER_STUNNED|MASK_ENSNARED|MASK_ALIEN_EMBRYO // Blocked masks #define S_START_DISTANCE 32 // The first search distance #define S_NUM_ATTEMPTS 128 // Max attempts to search free spot #define S_NUM_DISTANCE 1000 // Max unstuck spot distance // Waypoints #define WP_GUARD 5 // Waypoint guard /************************************************************************************** * Enums **************************************************************************************/ enum { SI_PLAYER = 1, SI_SCORE, SI_KILLS, SI_DEATHS, SI_LEVEL, SI_CLASS, SI_AUTH, SI_TEAM, SI_HEALTH, SI_ICON, SI_NUM_SCOREVARS } enum { HL_RRSPECS = 0, HL_MARINES, HL_ALIENS, HL_NUM_TEAMS } enum { CT_GENERAL = 0, CT_TEAMJOIN, CT_NUM_CMDS } enum { W_I_ESI = 0, W_I_HLTV, W_I_AREQ, W_I_BAN, W_NUM_FUNCS } enum { I_I_DONOR = 0, I_I_CHAMP, I_I_REF, I_I_ADMIN, I_NUM_ICONS } enum { RC_I_RATE = 0, RC_I_CMDRATE, RC_I_UPDATERATE, RC_I_FPS, RC_NUM_VARS } /************************************************************************************** * Global variables **************************************************************************************/ new g_gameStarted = 0 // Game started? new g_gameEnding = 0 // Game ending? new g_gameModerated = 0 // Game chat moderated? new g_gameHltvs = 0 // Number of HLTV on server new g_pauseStatus = 0 // Game paused? new g_pausePending = 0 // Pending pause cmd from user? new g_pauseReq = 0 // Pause requestor? new g_timeFetchedRemote = 0 // Remote time when time was fetched new Float:g_timeFetchedLocal = 0.0 // Local time when time was fetched new g_userIndex = 0 // PID table index new g_userCount = 0 // PID table cells used new Float:g_flashTime[HL_NUM_PLAYERS + 1] // List: User ID <-> last FL-time new g_userAuthed[HL_NUM_PLAYERS + 1] // List: User ID <-> authenticated? new g_userIconIndex[HL_NUM_PLAYERS + 1] // List: User ID <-> icon index new g_userIconFlags[HL_NUM_PLAYERS + 1] // List: User ID <-> icon flags new Float:g_userCmdTime[HL_NUM_PLAYERS + 1][CT_NUM_CMDS] // List: User ID <-> cmdId <-> time new g_userCmdWarn[HL_NUM_PLAYERS + 1][CT_NUM_CMDS] // List: User ID <-> cmdId <-> warned? new g_userDefendWP[HL_NUM_PLAYERS + 1] // List: User ID <-> guard waypoint new g_userOrderType[HL_NUM_PLAYERS + 1]; // List: User ID <-> waypoint type new g_userPidIndex[HL_NUM_PLAYERS + 1] // List: User ID <-> PID new g_iconName[I_NUM_ICONS][I_LEN_ICON + 1] // Icon id <-> Icon name + color new g_iconFlags[I_NUM_ICONS] // Icon id <-> Icon flag new g_userIdIndex[U_NUM_IDS] // List: PID <-> User ID new g_userStatus[U_NUM_IDS] // List: PID <-> On server? new g_userName[U_NUM_IDS][HL_LEN_NICK + 1] // List: PID <-> Name new g_userSteamID[U_NUM_IDS][HL_LEN_STEAMID + 1] // List: PID <-> Steam ID new g_userIP[U_NUM_IDS][HL_LEN_IP + 1] // List: PID <-> IP Address new g_userTeam[U_NUM_IDS] // List. PID <-> Team new g_userRCVars[U_NUM_IDS][RC_NUM_VARS] // List: PID <-> var <-> value new g_userRCWarn[U_NUM_IDS][RC_NUM_VARS] // List: PID <-> var <-> warned? new g_userWebSocket[U_NUM_IDS] // List: PID <-> web socket new g_userWebHex[U_NUM_IDS][CC_LEN_CODE + 1] // List: PID <-> web hex challenge new g_userWebReads[U_NUM_IDS] // List: PID <-> web reads new g_userWebData[U_NUM_IDS][E_NUM_LINES][W_LEN_LINE + 1] // List: PID <-> web data new g_userWebDataLine[U_NUM_IDS] // List: PID <-> web data line idx new g_userEnslInfo[U_NUM_IDS] // List: PID <-> Exists in ENSL DB? new g_userEnslNick[U_NUM_IDS][E_LEN_NICK + 1] // List: PID <-> ENSL Nick new g_userEnslIP[U_NUM_IDS][E_LEN_IP + 1] // List: PID <-> ENSL IP new g_userEnslTeam[U_NUM_IDS][E_LEN_TEAM + 1] // List: PID <-> ENSL Team new g_userEnslId[U_NUM_IDS] // List: PID <-> ENSL User ID new g_userEnslTid[U_NUM_IDS] // List: PID <-> ENSL Team ID new g_userEnslLevel[U_NUM_IDS][E_LEN_LEVEL + 1] // List: PID <-> ENSL Level new g_userEnslRank[U_NUM_IDS][E_LEN_RANK + 1] // List: PID <-> ENSL Rank new g_userEnslGather[U_NUM_IDS] // List: PID <-> ENSL Gather status new g_userCode[U_NUM_IDS][CC_LEN_CODE + 1] // List: PID <-> Verification code new g_userSSPassed[U_NUM_IDS] // List: PID <-> Deaths /wo snapshot new g_userSSCount[U_NUM_IDS] // List: PID <-> Deaths /w snapshot new g_userSSRand[U_NUM_IDS] // List: PID <-> Random SS count new g_userDDTime[U_NUM_IDS] // List: PID <-> Rejoin time new g_userMerc[U_NUM_IDS] // List: PID <-> Mercable team new g_userGagged[U_NUM_IDS] // List: PID <-> Gagged? new g_userVoice[U_NUM_IDS] // List: PID <-> Allowed to talk? new g_teamPlayers[HL_NUM_TEAMS] // List: Team <-> player counts new g_teamReady[HL_NUM_TEAMS] // List: Team <-> ready? new g_teamNames[HL_NUM_TEAMS][HL_LEN_TEAM + 1] // List: Team <-> name new g_teamENSLId[HL_NUM_TEAMS] // List: Team <-> ENSL ID new g_teamENSLName[HL_NUM_TEAMS][E_LEN_TEAM + 1] // List: Team <-> ENSL Name new g_teamMReq[HL_NUM_TEAMS] // List: Team <-> requestor merc PID new Float:g_FFCStarted = 0.0 // Forfeit clock: time started new g_FFCUsed = 0 // Forfeit clock: time used new g_cvarIntIndex = 0 // Server integer CVAR index new g_cvarIntName[C_NUM_INTS][C_LEN_NAME + 1] // Server integer CVAR names new g_cvarIntLevel[C_NUM_INTS] // Server integer CVAR importance new g_cvarIntValue[C_NUM_INTS] // Server integer CVAR values new g_cvarIntPCW[C_NUM_INTS] // Server integer CVAR PCW value new g_cvarFltIndex = 0 // Server float CVAR index new g_cvarFltName[C_NUM_FLOATS][C_LEN_NAME + 1] // Server float CVAR names new g_cvarFltLevel[C_NUM_FLOATS] // Server float CVAR importance new Float:g_cvarFltValue[C_NUM_FLOATS] // Server float CVAR values new Float:g_cvarFltPCW[C_NUM_FLOATS] // Server float CVAR PCW value new g_coNum[CO_NUM_MODES] // Combat: mode <-> numbr. of blocks new g_coBlocked[CO_NUM_MODES][CO_NUM_UPGRADES] // Combat: mode <-> blocked impulses new g_refSSUser = -1 // PID of the next ref SS target new g_webSocket[W_NUM_FUNCS] // Function ID <-> websocket new g_webHex[W_NUM_FUNCS][CC_LEN_CODE + 1] // Function ID <-> hex challenge new g_webReads[W_NUM_FUNCS] // Function ID <-> reads new g_webData[W_NUM_FUNCS][W_NUM_LINES][W_LEN_LINE + 1] // Function ID <-> line ID <-> data new g_webDataLine[W_NUM_FUNCS] // Function ID <-> data line pointer new g_msgId_SayText // MsgID: SayText new g_msgId_Scoreboard // MsgID: Scoreboard new g_msgId_Waypoint // MsgID: Waypoint new g_msgId_DeathMsg = 0; // MsgID: Death new g_maxPlayers = 0; // Maxplayers new g_mapBootMins = 0 // Map boot cycles /************************************************************************************** * CVARs **************************************************************************************/ new g_cvar_minrate new g_cvar_maxrate new g_cvar_mincmdrate new g_cvar_maxcmdrate new g_cvar_minupdaterate new g_cvar_maxupdaterate new g_cvar_minfps new g_cvar_maxfps new g_cvar_checkids new g_cvar_checkrates new g_cvar_snapshots new g_cvar_ffclock new g_cvar_merclimit new g_cvar_teamlimit new g_cvar_speclimit new g_cvar_refaccess new g_cvar_combatmode new g_cvar_membersonly new g_cvar_webquery new const g_cvarHelp[H_NUM_CVARS][HL_LEN_MSG + 1] = { "ensl_minrate & ensl_maxrate: Minimum and maximum rate allowed for players", "ensl_mincmdrate & ensl_maxcmdrate: Minimum and maximum cl_cmdrate allowed for players.", "ensl_minupdaterate & ensl_maxupdaterate: Minimum and maximum cl_updaterate allowed for players.", "ensl_checkids [0 = off, 1 = on]: Player ID fetching from ENSL database.", "ensl_snapshots [0 = off, 1 = on]: Player snapshots on death.", "ensl_checkrates [0 = off, 1 = on+kick, 2 = on+warning, 3 = fetch only]: Ratechecking", "ensl_ffclock [0 = off, 1 = on]: Forfeit clock triggered by ready.", "ensl_merclimit [0 = off, 1 = on]: Merclimiting prevents players in dfferent ENSL team to join a team.", "ensl_teamlimit [num]: Allows teams to play with only given number of players.", "ensl_speclimit [0 = off, 1 = on]: Speclimiting prevents non-referees to join spectators mid-game.", "ensl_refaccess [0 = off, 1 = on]: Toggles usage of admin commands by referees.", "ensl_combatmode [0 = off, 1 = celeresupply, 2: ENSL-combat, 3: lifeforms]: Limits the usable combat upgrades." } /************************************************************************************** * Plugin loader functions **************************************************************************************/ // Initalize the plugin public plugin_init() { // Register plugin register_plugin(PLUGIN, VERSION, AUTHOR) // Get Message ID's g_msgId_SayText = get_user_msgid("SayText") g_msgId_Scoreboard = get_user_msgid("ScoreInfo") g_msgId_Waypoint = get_user_msgid("SetOrder") g_msgId_DeathMsg = get_user_msgid("DeathMsg") // Globals g_maxPlayers = get_maxplayers(); // Register CVARs g_cvar_minrate = register_cvar("ensl_minrate", "10000") g_cvar_maxrate = register_cvar("ensl_maxrate", "25000") g_cvar_mincmdrate = register_cvar("ensl_mincmdrate", "50") g_cvar_maxcmdrate = register_cvar("ensl_maxcmdrate", "150") g_cvar_minupdaterate = register_cvar("ensl_minupdaterate", "40") g_cvar_maxupdaterate = register_cvar("ensl_maxupdaterate", "150") g_cvar_minfps = register_cvar("ensl_minfps", "50") g_cvar_maxfps = register_cvar("ensl_maxfps", "999") g_cvar_checkids = register_cvar("ensl_checkids", "1") g_cvar_checkrates = register_cvar("ensl_checkrates", "3") g_cvar_snapshots = register_cvar("ensl_snapshots", "0") g_cvar_ffclock = register_cvar("ensl_ffclock", "0") g_cvar_merclimit = register_cvar("ensl_merclimit", "0") g_cvar_teamlimit = register_cvar("ensl_teamlimit", "0") g_cvar_speclimit = register_cvar("ensl_speclimit", "0") g_cvar_refaccess = register_cvar("ensl_refaccess", "1") g_cvar_combatmode = register_cvar("ensl_combatmode", "1") g_cvar_membersonly = register_cvar("ensl_membersonly", "0") g_cvar_webquery = register_cvar("ensl_webquery", "0") // Register basic events register_event("DeathMsg", "event_death", "a") register_event("GameStatus","event_roundend","abc","1=2") // Register scoreboard event for icons register_message(g_msgId_Scoreboard, "event_scoreboard") register_message(g_msgId_Waypoint, "func_msg_waypoint"); register_message(g_msgId_DeathMsg, "func_msg_death"); // Register team joins register_clcmd("jointeamone", "cmd_jointeamone") register_clcmd("jointeamtwo", "cmd_jointeamtwo") register_clcmd("readyroom", "cmd_readyroom") register_clcmd("impulse 5", "cmd_readyroom") register_clcmd("spectate", "cmd_spectate") register_clcmd("autoassign", "cmd_autoassign") register_touch("info_spectate", "player", "touch_spectate") register_touch("info_join_team","player","touch_teamjoin") register_touch("info_join_autoassign","player", "touch_autoassign") // Say commands register_clcmd("say", "cmd_say") register_clcmd("say /ensl", "cmd_ensl") register_clcmd("say_team /ensl", "cmd_ensl") register_clcmd("say /exp", "cmd_exp") register_clcmd("say_team /exp", "cmd_exp") register_clcmd("say /stuck", "cmd_unstuck") register_clcmd("say_team /stuck", "cmd_unstuck") register_clcmd("say /ok", "cmd_ok") register_clcmd("say_team /ok", "cmd_ok") register_clcmd("say /mercs", "cmd_mercs") register_clcmd("say_team /mercs", "cmd_mercs") register_clcmd("say /mercsok", "cmd_mercsok") register_clcmd("say_team /mercsok", "cmd_mercsok") register_clcmd("say /check", "cmd_check") register_clcmd("say_team /check", "cmd_check") register_clcmd("say /makeroom", "cmd_makeroom") register_clcmd("say_team /makeroom", "cmd_makeroom") register_clcmd("say ready", "cmd_ready") register_clcmd("say_team ready", "cmd_ready") register_clcmd("say notready", "cmd_notready") register_clcmd("say_team notready", "cmd_notready") register_clcmd("say /clearwp", "cmd_clearwp"); register_clcmd("say_team /clearwp", "cmd_clearwp"); register_clcmd("say /noweapons", "func_cmd_stripweapons"); // Console commands register_concmd("amx_enslinfo", "cmd_enslinfo", ADMIN_USER, "[server/marines/aliens/others] (Print information)") register_concmd("amx_enslcfg", "cmd_enslcfg", ADMIN_LEVEL_E, "[pcw] (Load ENSL official or pcw settings)") register_concmd("amx_enslcvar", "cmd_enslcvar", ADMIN_LEVEL_E, "[name] [value] (Set server cvar)") register_concmd("amx_enslteam", "cmd_enslteam", ADMIN_LEVEL_E, "[name/#ID] [team] (Force player to team)") register_concmd("amx_enslkick", "cmd_enslkick", ADMIN_LEVEL_E, "[name/#ID] [bantime(mins)] [^"reason^"] (Kick/ban player)") register_concmd("amx_enslnicks", "cmd_enslnicks", ADMIN_LEVEL_E, "(Enforce ENSL nicks on all players)") register_concmd("amx_enslvoice", "cmd_enslvoice", ADMIN_LEVEL_E, "[empty=freetalk or name/#ID] (Limit talking)") register_concmd("amx_enslpause", "cmd_enslpause", ADMIN_LEVEL_E, "(Pause / unpause game)") register_concmd("amx_enslmap", "cmd_enslmap", ADMIN_LEVEL_E, "[mapname] (Change map)") register_concmd("amx_enslclock", "cmd_enslclock", ADMIN_LEVEL_E, "[mins] [secs] (Add time to the forfeit clock") register_concmd("amx_enslshot", "cmd_enslshot", ADMIN_LEVEL_E, "[name/#ID] (Force snapshot)") register_concmd("amx_enslsay", "cmd_enslsay", ADMIN_LEVEL_E, "[message] (Global say)") register_concmd("amx_enslpwd", "cmd_enslpwd", ADMIN_LEVEL_E, "[passord] (Set password)") register_concmd("amx_enslmerc", "cmd_enslmerc", ADMIN_LEVEL_E, "[name/#ID] (Toggle mercing)") register_concmd("amx_enslmove", "cmd_enslmove", ADMIN_LEVEL_E, "[#ID/ip:port] [password] (Connect everyone to another server)") register_concmd("amx_enslhltv", "cmd_enslhltv", ADMIN_LEVEL_E, "[matchID/demoname/stop] (Request a HLTV.)") register_concmd("amx_enslgag", "cmd_enslgag", ADMIN_LEVEL_E, "[name/#ID] (Prvents user from talking)") register_concmd("amx_enslhelp", "cmd_enslhelp", ADMIN_USER, "(Show help about ENSL Plugin commands)") register_concmd("amx_enslcvars", "cmd_enslcvars", ADMIN_USER, "(Show help about ENSL Plugin CVARs)") // Impulset register_impulse(81, "cmd_clearwp"); // Special register_clcmd("pauseMark", "cmd_pause") // Set team names g_teamNames[HL_RRSPECS] = "RR/Specs" g_teamNames[HL_MARINES] = "Marines" g_teamNames[HL_ALIENS] = "Aliens" // Set server CVARs, init combat stuff, server PID and waypoints func_init_cvars() func_init_co() func_init_pid() func_init_waypoints() // Rate / FPS checks set_task(10.0, "task_check_rates", T_RATECHECK, "", 0, "b", 0) // Set a task 10s after start set_task(10.0, "task_startup", T_STARTUP, "", 0, "a", 1) // Set a task 10s after start set_task(60.0, "task_mapboot", T_MAPBOOT, "", 0, "b", 0) // Set a task 10s after start set_task(300.0, "task_advert", T_ADVERT, "", 0, "b", 0) } // Force default WAD's public plugin_precache() { // Force certain unmodified files force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/co_kestrel.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/hallwall_1.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns2.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns_ayumi.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns_bast.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns_eclipse.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns_eon.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns_hera.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns_lost.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns_metal.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns_nancy.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns_nothing.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns_shiva.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/ns_tanith.wad") force_unmodified(force_exactfile,{0,0,0},{0,0,0},"/v_wad.wad") // Initialize icons func_init_icons() // Precache custom icons new iconName[I_LEN_ICON + 1], shortIcon[I_LEN_ICON - 2], iconPath[I_LEN_PATH + 1] for ( new i = 0; i < I_NUM_ICONS; i++ ) { iconName = g_iconName[i] copy(shortIcon, strlen(iconName) - 3, iconName) formatex(iconPath, I_LEN_PATH, "gfx/vgui/640_%s.tga", shortIcon) precache_generic(iconPath) } } /************************************************************************************** * Events concerning individual player **************************************************************************************/ // Event: player joins public client_authorized(id) { // Authenticate the player func_authenticate(id) return PLUGIN_CONTINUE } // Event: player is connected public client_putinserver(id) { // If its a hltv, announce it if ( is_user_hltv(id) ) { new ip[HL_LEN_IP + HL_LEN_PORT + 2]; get_user_ip(id, ip, HL_LEN_IP + HL_LEN_PORT + 1) client_print(0, print_chat, "[ENSL] HLTV (%s) has joined the server.", ip) g_gameHltvs++ } // Increase RR+Spec counter g_teamPlayers[HL_RRSPECS]++ return PLUGIN_CONTINUE } // Event: info changes public client_infochanged(id) { // Apply icon func_apply_icon(id) return PLUGIN_CONTINUE } // Event: player leaves public client_disconnect(id) { // Authenticate if not already + get PID new pid = func_authenticate(id) // Reset variables g_userIconFlags[id] = 0 g_userIconIndex[id] = -1 g_userAuthed[id] = 0 g_userStatus[pid] = 0 // Reset rates so retrying with different rates wont get kicked for rate changing for ( new i = 0; i < RC_NUM_VARS; i++ ) { g_userRCVars[pid][i] = 0 g_userRCWarn[pid][i] = RC_WARN_NEGATIVE } // If user was being digested, user has to be out alteast 50 seconds if ( ns_get_mask(id, MASK_DIGESTING) ) { g_userDDTime[pid] = get_systime() + DD_DELAY_DIGESTED } // Process team leave func_teamleave(id, g_userTeam[pid]) g_userTeam[pid] = HL_RRSPECS // Decrease the HLTV counter if necessary if ( is_user_hltv(id) ) { g_gameHltvs-- } // Remove any authentication tasks if ( task_exists(T_USER + pid) ) { remove_task(T_USER + pid) socket_close(g_userWebSocket[pid]) } // Set last username get_user_name(id, g_userName[pid], HL_LEN_NICK) return PLUGIN_CONTINUE } // Event: impulse command public client_impulse(id, impulse) { // Process flashlight if ( impulse == F_IMLUPSE ) { // If the user is trying to use the flashlight too often, abort if ( get_gametime() - g_flashTime[id] < F_FREQ ) { return PLUGIN_HANDLED } // Set the last time flashlight was used g_flashTime[id] = get_gametime() return PLUGIN_CONTINUE } // Block upgrades if celeresupply is on new mode = get_pcvar_num(g_cvar_combatmode) if ( ns_is_combat() && mode != CO_NORMAL && mode < CO_NUM_MODES ) { new coNum = g_coNum[mode] for ( new i = 0; i < coNum; i++ ) { if ( impulse == g_coBlocked[mode][i] ) { return PLUGIN_HANDLED } } } return PLUGIN_CONTINUE } // Event: player death public event_death() { // Authenticate new pid = func_authenticate(read_data(2)) // Take a random snapshot if necessary if ( get_pcvar_num(g_cvar_snapshots) && func_random_snap(pid) ) { func_snapshot(pid, 0) } return PLUGIN_CONTINUE } /************************************************************************************** * Global events **************************************************************************************/ // Event: round start public round_start() { new ratecheck; ratecheck = get_pcvar_num(g_cvar_checkrates) // Echo the warning if rate checking is enabled w/ kick if ( ratecheck == RC_ENABLED_KICK ) { client_print(0, print_chat, "[ENSL] Rate changing / breaching punishable by kick. Do not change rates.") } // Echo the warning if rate checking is enabled w/o kick else if ( ratecheck == RC_ENABLED_WARN ) { client_print(0, print_chat, "[ENSL] Rate changing / breaching not allowed.") } // Reset some player settings for ( new i = 0; i < g_userCount; i++ ) { g_userDDTime[i] = 0 g_userSSPassed[i] = 0 g_userSSCount[i] = 0 } // Mark game started and reset variables g_gameStarted = 1 g_teamReady[HL_MARINES] = 0 g_teamReady[HL_ALIENS] = 0 // Stop the forfeit clock. func_stop_ffclock(1) // Init waypoints func_init_waypoints() return PLUGIN_CONTINUE } // Event: round end public event_roundend() { new playerCount, player new players[HL_NUM_PLAYERS]; get_players(players, playerCount, "h") // If screenshots are enabled, take the final shot if ( get_pcvar_num(g_cvar_snapshots) ) { for ( new i = 0; i < playerCount; i++ ) { func_snapshot(g_userPidIndex[players[i]], 1) } } // Print player info to everyone for ( new i = 0; i < playerCount; i++ ) { player = players[i] func_print_players(player, HL_MARINES) func_print_players(player, HL_ALIENS) func_print_players(player, HL_RRSPECS) } // Reset times after snapshots g_gameStarted = 0 g_gameEnding = 1 // Reset some values for ( new i = 0; i < U_NUM_IDS; i++ ) { g_userDDTime[i] = 0 g_userMerc[i] = 0 g_userSSCount[i] = 0 g_userSSPassed[i] = 0 g_userSSRand[i] = 0 } // Reset team count, about six seconds after game-end, players are in RR set_task(6.0, "task_roundend", 0, "", 0, "a", 1) return PLUGIN_CONTINUE } /************************************************************************************** * Global tasks **************************************************************************************/ // Task: startup public task_startup() { func_get_esi() // Fix comm bug set_cvar_num("sv_maxspectatorspeed", 1000) set_cvar_num("sv_maxspeed", 4000) } // Task: round ends public task_roundend() { func_reset_teams() g_gameEnding = 0 } // Task: map boot, fixes pgs public task_mapboot() { if ( g_mapBootMins == 5 ) { new mapname[HL_LEN_MAP + 1]; get_mapname(mapname, HL_LEN_MAP) server_cmd("wait; wait; wait; wait; wait; wait; wait; wait; changelevel %s", mapname) } else if ( get_playersnum() <= 2 ) { g_mapBootMins++ } else { g_mapBootMins = 0 } } // Task: advert public task_advert() { new playerCount = 0, target = 0 new players[HL_NUM_PLAYERS]; get_players(players, playerCount, "h") new msg[HL_LEN_HUDMESSAGE + 1] // Print the hud message including serverside info (nick, ip etc.) + gametimes set_hudmessage(20, 120, 120, -1.0, 0.6, 0, 0.5, 10.0, 2.0, 2.0, 4) formatex(msg, HL_LEN_HUDMESSAGE, "Welcome to ENSL Server^n^n^nYou can join pick-up games^nby registering at www.ensl.org^nand joining at www.ensl.org/gathers/latest/ns1") // Check readyroom first for ( new i = 0; i < playerCount; i++ ) { if ( nst_is_readyroom(players[i]) || nst_is_spectate(players[i]) ) { target = players[i] show_hudmessage(target, msg) } } } /************************************************************************************** * Icons **************************************************************************************/ // Function: Initialize icons public func_init_icons() { g_iconName[I_I_DONOR] = "ensl_12_donor999" g_iconFlags[I_I_DONOR] = 1 g_iconName[I_I_CHAMP] = "ensl_12_champ999" g_iconFlags[I_I_CHAMP] = 2 g_iconName[I_I_REF] = "ensl_12_ref999" g_iconFlags[I_I_REF] = 4 g_iconName[I_I_ADMIN] = "ensl_12_admin999" g_iconFlags[I_I_ADMIN] = 8 } // Function: handle scoreboard call public event_scoreboard() { new id = get_msg_arg_int(SI_PLAYER) // Set custom icon if necessary if ( g_userIconIndex[id] != -1 ) { set_msg_arg_int(SI_AUTH, ARG_SHORT, I_AUTH_ICON) set_msg_arg_string(SI_ICON, g_iconName[g_userIconIndex[id]]) } return PLUGIN_CONTINUE } // Function: enable next icon public func_next_icon(id) { // Increase it as long as the icon is not allowed do { g_userIconIndex[id]-- g_userIconIndex[id] = g_userIconIndex[id] == -2 ? I_NUM_ICONS - 1 : g_userIconIndex[id] } while ( g_userIconIndex[id] != -1 && !(g_iconFlags[g_userIconIndex[id]] & g_userIconFlags[id]) ) } // Function: apply user's favourite icon public func_apply_icon(id) { new strIcon[E_LEN_IDS + 1]; get_user_info(id, "enslicon", strIcon, E_LEN_IDS) new intIcon = str_to_num(strIcon) // If icon is wanted to be default, set it if ( intIcon == -1 ) { g_userIconIndex[id] = -1 } // If the icon is set and within range else if ( intIcon > 0 && intIcon <= I_NUM_ICONS && (g_iconFlags[intIcon - 1] & g_userIconFlags[id]) ) { g_userIconIndex[id] = intIcon - 1 } } /************************************************************************************** * Unstucking functions **************************************************************************************/ // Command: /unstuck public cmd_unstuck(id) { // Get PID new pid = func_authenticate(id) // Check for too rapid action if ( func_check_cmdtime(pid, CT_GENERAL, print_chat) == PLUGIN_HANDLED ) { return PLUGIN_HANDLED } // Execute the unstuck after a random interval set_task(random_float(0.5,5.0), "task_unstuck", id) return PLUGIN_HANDLED } // Task: Unstuck a player public task_unstuck(id) { new Float:origin[3], Float:newOrigin[3], hullsize, distance // Get username new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new pid = func_authenticate(id) // Do no not unstuck when gestating etc. if ( ns_get_mask(id, S_B) ) { client_print(id, print_chat, "[ENSL] You cannot free yourself while evolving, stunned or webbed") log_message("[ENSL] [unstuck] [%s:%s] [FAILED] [Webbed/Stunned/Evolving]", g_userSteamID[pid], name) return PLUGIN_CONTINUE } // Get hull size of the player and abort if invalid switch ( ns_get_class(id) ) { // Small lifeforms: return the smaller hull case 1,2,3: hullsize = HULL_HEAD // Others: If the user is ducking, return the smaller hull, otherwise midsize case 4,6,7,8: hullsize = (entity_get_int(id, EV_INT_flags) & FL_DUCKING) ? HULL_HEAD : HULL_HUMAN // Onos: If the user is ducking, return the smaller hull, otherwise midsize case 5: hullsize = (entity_get_int(id, EV_INT_flags) & FL_DUCKING) ? HULL_HUMAN : HULL_LARGE // Return, print and log error if not applicable default: { client_print(id, print_chat, "[ENSL] You cannot free yourself at this time.") log_message("[ENSL] [unstuck] [%s:%s] [FAILED] [Bad Hullsize]", g_userSteamID[pid], name) return PLUGIN_CONTINUE } } // Get user coordinates and init default stuck distance entity_get_vector(id, EV_VEC_origin, origin) distance = S_START_DISTANCE // NOTE: Distance should never be further than 1000 units while ( distance < S_NUM_DISTANCE ) { for ( new i = 0; i < S_NUM_ATTEMPTS; ++i ) { // Generate new random coordinates newOrigin[0] = random_float(origin[0]-distance, origin[0]+distance) newOrigin[1] = random_float(origin[1]-distance ,origin[1]+distance) newOrigin[2] = random_float(origin[2]-distance, origin[2]+distance) // Unstuck player if ( trace_hull(newOrigin, hullsize, id) == 0 ) { entity_set_origin(id, newOrigin) client_print(0, print_chat, "[ENSL] Player (%s) unstucked.", name) log_message("[ENSL] [unstuck] [%s:%s] [OK]", g_userSteamID[pid], name) return PLUGIN_CONTINUE } } distance += S_START_DISTANCE } // Echo error in case no free spot found client_print(id, print_chat, "[ENSL] Couldn't find a free spot to move you too.") log_message("[ENSL] [unstuck] [%s:%s] [FAILED] [No Free Spot]", g_userSteamID[pid], name) return PLUGIN_CONTINUE } /************************************************************************************** * Plugin informational commands **************************************************************************************/ // Command: say /ensl (Print plugin info) public cmd_ensl(id) { // Authenticate new pid = func_authenticate(id) // Not too rapid execution if ( func_check_cmdtime(pid, CT_GENERAL, print_chat) == PLUGIN_HANDLED ) { return PLUGIN_HANDLED } // Print verison and settings client_print(id, print_chat, "[ENSL] Plugin Version %s - %s - %s on IRC.Quakenet.Org", VERSION, WEB, CHANNEL) return PLUGIN_HANDLED } // Command /say whois (Find user information public cmd_whois(id, msg[HL_LEN_SAY + 1]) { // Authenticate new pid = func_authenticate(id) // Fetch arguments new nick[HL_LEN_NICK + 1]; copy(nick, HL_LEN_NICK, msg[7]) new targetID = func_get_player(id, nick) // Not too rapid execution if ( func_check_cmdtime(pid, CT_GENERAL, print_chat) == PLUGIN_HANDLED ) { return PLUGIN_HANDLED } // If user cannot be found, abort if ( targetID == -1 || equal(msg[7], "") ) { client_print(id, print_chat, "[ENSL] User not found.") return PLUGIN_HANDLED } // Get pid and nick new tPid = g_userPidIndex[targetID] new name[HL_LEN_NICK + 1]; get_user_name(targetID, name, HL_LEN_NICK) // Print nick and steamid client_print(id, print_chat, "[ENSL] Nick: %s SteamID: %s IP: %s", name, g_userSteamID[tPid], g_userIP[tPid]) // Print possible ENSL information if ( g_userEnslInfo[tPid] ) { new args[2] = {0, 0} args[0] = id args[1] = tPid set_task(0.1, "task_whois_ensl", 0, args, 2, "a", 1) set_task(0.3, "task_whois_rates", 0, args, 2, "a", 1) } return PLUGIN_HANDLED } // Task: print ensl data public task_whois_ensl(const args[]) { client_print(args[0], print_chat, "[ENSL] [ENSL Name: %s] [ENSL Team: %s] [ENSL IP: %s]", g_userEnslNick[args[1]], g_userEnslTeam[args[1]], g_userEnslIP[args[1]]) } // Task: print ensl data public task_whois_rates(const args[]) { client_print(args[0], print_chat, "[ENSL] Rates [Upd: %d Cmd: %d Rate: %dk]", g_userRCVars[args[1]][RC_I_UPDATERATE], g_userRCVars[args[1]][RC_I_CMDRATE], g_userRCVars[args[1]][RC_I_RATE] / 1000) } // Command: amx_enslinfo (Print miscellaenous information) public cmd_enslinfo(id) { // Authenticate new pid = func_authenticate(id) // Not too rapid execution if ( func_check_cmdtime(pid, CT_GENERAL, print_console) == PLUGIN_HANDLED ) { return PLUGIN_HANDLED } // In-game only specs can use it if ( g_gameStarted && !func_is_referee(pid) ) { console_print(id, "This command can be used only by referees when round is in progress.") return PLUGIN_HANDLED } // Check arguments if ( read_argc() < 2 ) { console_print(id, "Please use amx_enslinfo [server/marines/aliens/others]") return PLUGIN_HANDLED } // Fetch arguments new function[HL_LEN_TEAM + 1]; read_argv(1, function, HL_LEN_TEAM) // Print server information when necessary if ( equal(function, "server") ) { func_print_server(id) } // Print server information when necessary else if ( equal(function, "marines") ) { func_print_players(id, HL_MARINES) } // Print marine information when necessary else if ( equal(function, "aliens") ) { func_print_players(id, HL_ALIENS) } // Print alien information when necessary else if ( equal(function, "others") ) { func_print_players(id, HL_RRSPECS) } // Or print error for wrong argument else { console_print(id, "Valid values: server, marines, aliens or others") } return PLUGIN_HANDLED } // Function: print server info public func_print_server(id) { // Init variables new iInt = 0, iFlt = 0, cMandatory = 0, cVoluntary = 0, idx = 0 new wrongInts[C_NUM_INTS], wrongFloats[C_NUM_FLOATS] // Check server integers for ( new i = 0; i < C_NUM_INTS; i++ ) { // If an incorrect settings is found.. if ( get_cvar_num(g_cvarIntName[i]) != g_cvarIntValue[i] ) { // Advance the mandatory cvar violation counter if ( g_cvarIntLevel[i] == C_LEVEL_MANDATORY ) { cMandatory++ } // Or increase the voluntary cvar violation counter else { cVoluntary++ } // Record it: increase index + counter wrongInts[iInt] = i iInt++ } } // Check server floats for ( new i = 0; i < C_NUM_FLOATS; i++ ) { // If an incorrect settings is found, record it and increase index + counter if ( get_cvar_float(g_cvarFltName[i]) != g_cvarFltValue[i] ) { // Advance the mandatory cvar violation counter if ( g_cvarFltLevel[i] == C_LEVEL_MANDATORY ) { cMandatory++ } // Or increase the voluntary cvar violation counter else { cVoluntary++ } // Record it: increase index + counter wrongFloats[iFlt] = i iFlt++ } } // Check server settings, mandatory ones if ( cMandatory > 0 ) { console_print(id, "Server has %d settings in unapproved position:", cMandatory) // Print integers cvars for ( new i = 0; i < iInt; i++ ) { idx = wrongInts[i] if ( g_cvarIntLevel[idx] == C_LEVEL_MANDATORY ) { console_print(id, "%s = %d (should be %d)", g_cvarIntName[idx], get_cvar_num(g_cvarIntName[idx]), g_cvarIntValue[idx]) } } // Print float cvars for ( new i = 0; i < iFlt; i++ ) { idx = wrongFloats[i] if ( g_cvarFltLevel[idx] == C_LEVEL_MANDATORY ) { console_print(id, "%s = %f (should be %f)", g_cvarFltName[idx], get_cvar_float(g_cvarFltName[idx]), g_cvarFltValue[idx]) } } } // Check server settings, voluntary ones if ( cVoluntary > 0 ) { console_print(id, "^nServer has %d settings in non-recommended position:", cVoluntary) // Print integers cvars for ( new i = 0; i < iInt; i++ ) { idx = wrongInts[i] if ( g_cvarIntLevel[idx] == C_LEVEL_VOLUNTARY ) { console_print(id, "%s = %d (should be %d)", g_cvarIntName[idx], get_cvar_num(g_cvarIntName[idx]), g_cvarIntValue[idx]) } } // Print float cvars for ( new i = 0; i < iFlt; i++ ) { idx = wrongFloats[i] if ( g_cvarFltLevel[idx] == C_LEVEL_VOLUNTARY ) { console_print(id, "%s = %f (should be %f)", g_cvarFltName[idx], get_cvar_float(g_cvarFltName[idx]), g_cvarFltValue[idx]) } } } // Print rules if ( cMandatory > 0 ) { console_print(id, "^nSince there are unapproved settings, match can only be played if both teams agree (say ready).") console_print(id, "If either of the teams disagree, contact server admin or use another server.") } else if ( cVoluntary == 0 ) { console_print(id, "All settings OK.") } else { console_print(id, "Mandatory official settings OK.") } } // Print team's player information public func_print_players(id, team) { // Ignore if the team does't have players if ( g_teamPlayers[team] == 0 ) { console_print(id, "No players found of the given team.") return } // Generate header new columns[HL_NUM_COLUMN][HL_LEN_COLUMN + 1], header[HL_LEN_MSG + 1] copy(columns[0], 15, "Name") copy(columns[1], 15, "SteamID") copy(columns[2], 15, "IP Address") copy(columns[3], 15, "ENSL Name") copy(columns[4], 15, "ENSL IP") copy(columns[5], 15, "ENSL Team") copy(columns[6], 15, "ENSL Level") copy(columns[7], 15, "Upd/Cmd/Rat/FPS") func_gentable(8, 15, columns, header) console_print(id, "-- %s --", g_teamNames[team]) console_print(id, "%s", header) // Print players for ( new p = 1; p < g_userCount; p++ ) { if ( g_userStatus[p] && g_userTeam[p] == team ) { func_print_player(id, p) } } // If requestor has referee access, print past players if ( team == HL_RRSPECS && func_is_referee(g_userPidIndex[id]) ) { // Print headers console_print(id, "^n -- Past Players --") console_print(id, "%s", header) // Print players, exclude server itself for ( new p = 1; p < g_userCount && p < 10; p++ ) { if ( !g_userStatus[p] ) { func_print_player(id, p) } } } } // Function: print players' playerinfo on 1 line public func_print_player(id, pid) { new values[HL_NUM_COLUMN][HL_LEN_COLUMN + 1], output[HL_LEN_MSG + 1] // Get/generate username + rates + shots new name[HL_LEN_NICK + 1] new rates[16]; formatex(rates, 15, "%d/%d/%dk/%d", g_userRCVars[pid][RC_I_UPDATERATE], g_userRCVars[pid][RC_I_CMDRATE], g_userRCVars[pid][RC_I_RATE] / 1000, g_userRCVars[pid][RC_I_FPS]) // Get username if ( g_userStatus[pid] ) { get_user_name(g_userIdIndex[pid], name, HL_LEN_NICK) } else { copy(name, HL_LEN_NICK, g_userName[pid]) } // Insert values add(values[0], 15, name, 15) add(values[1], 15, g_userSteamID[pid], 15) add(values[2], 15, g_userIP[pid], HL_LEN_IP) add(values[3], 15, g_userEnslNick[pid], 15) add(values[4], 15, g_userEnslIP[pid], E_LEN_IP) add(values[5], 15, g_userEnslTeam[pid], 15) add(values[6], 15, g_userEnslLevel[pid], E_LEN_LEVEL) add(values[7], 15, rates, 15) // Add info if user is CL if ( equal(g_userEnslRank[pid], E_RANK_LEADER, 1) ) { add(values[5], E_LEN_TEAM, " (CL)", 15) } // Or deputee else if ( equal(g_userEnslRank[pid], E_RANK_DEPUTEE, 1) ) { add(values[5], E_LEN_TEAM, " (D)", 15) } // Print output func_gentable(8, 15, values, output) console_print(id, output) } /************************************************************************************** * Combat stuff **************************************************************************************/ // Command: say /exp (giv exp) public cmd_exp(id, level, cid) { // Authenticate the player new pid = func_authenticate(id) // Not too rapid execution if ( func_check_cmdtime(pid, CT_GENERAL, print_chat) == PLUGIN_HANDLED ) { return PLUGIN_HANDLED } new mode = get_pcvar_num(g_cvar_combatmode) if ( ns_is_combat() && mode > CO_ENSL ) { ns_set_exp(id, 9999.0) } return PLUGIN_HANDLED } /************************************************************************************** * Admin request **************************************************************************************/ // Function: say /ensl [msg] (Send ENSL admin request) public cmd_adminrequest(id, msg[HL_LEN_SAY + 1]) { // Authenticate user new pid = func_authenticate(id) // Prevent multirun if ( task_exists(T_AREQ) ) { client_print(id, print_chat, "[ENSL] Admin request already running, please wait and try again.") return PLUGIN_HANDLED } // Fetch arguments new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new nameParsed[HL_LEN_NICK + 1]; func_urlencode(name, nameParsed, HL_LEN_NICK) new addr[HL_LEN_ADDR + 1]; get_user_ip(0, addr, HL_LEN_ADDR) new pwd[HL_LEN_PWD + 1]; get_cvar_string("sv_password", pwd, HL_LEN_PWD) new msgParsed[HL_LEN_SAY + 1]; func_urlencode(msg[7], msgParsed, HL_LEN_SAY) new url[W_LEN_URL + 1]; formatex(url, W_LEN_URL, "%s?addr=%s&pwd=%s&msg=%s&player=%s&user=%d", AR_URL, addr, pwd, msgParsed, nameParsed, g_userEnslId[pid]) // Schedule the query function set_task(0.5, "task_web_areq", T_AREQ, url, W_LEN_URL) client_print(id, print_chat, "[ENSL] Admin request sent.") return PLUGIN_HANDLED } // Task: Send admin request public task_web_areq(url[W_LEN_URL + 1]) { new webquery = get_pcvar_num(g_cvar_webquery) if ( webquery != 0 ) { func_write_web(g_webSocket[W_I_AREQ], url) socket_close(g_webSocket[W_I_AREQ]) } } /************************************************************************************** * Miscellaeonus referee commands **************************************************************************************/ // Command: amx_enslmap [mapname] (change map) public cmd_enslmap(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 1) ) { return PLUGIN_HANDLED } // Fetch arguments new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new mapname[HL_LEN_MAP + 1]; read_argv(1, mapname, HL_LEN_MAP) // Check if the map is valid if ( !is_map_valid(mapname) ) { console_print(id, "Map not found: %s", mapname) return PLUGIN_HANDLED } // Change map client_print(0, print_chat, "[ENSL] Referee (%s) changed map to %s", name, mapname) log_message("[ENSL] [enslmap] [%s:%s] [%s]", g_userSteamID[pid], name, mapname) server_cmd("wait; wait; wait; wait; wait; wait; wait; wait; changelevel %s", mapname) return PLUGIN_HANDLED } // Command: amx_enslteam [name/#ID] [team] (Force player to a team) public cmd_enslteam(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 2) ) { return PLUGIN_HANDLED } // Fetch arguments new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new target[HL_LEN_NICK + 1]; read_argv(1, target, HL_LEN_NICK); remove_quotes(target) new team[HL_LEN_TEAM +1]; read_argv(2, team, HL_LEN_TEAM); remove_quotes(team) new targetID = func_get_player(id, target) // If user cannot be found, abort if ( targetID == -1 || equal(target, "") ) { return PLUGIN_HANDLED } // Get target user's name and PdI new targetName[HL_LEN_NICK + 1]; get_user_name(targetID, targetName, HL_LEN_NICK) new targetPid = func_authenticate(targetID) // If team is marines.. if ( equal(team, g_teamNames[HL_MARINES]) ) { engclient_cmd(targetID, "jointeamone") client_print(0, print_chat, "[ENSL] Referee (%s) enforced player %s to join marines.", name, targetName) } // If team is aliens.. else if ( equal(team, g_teamNames[HL_ALIENS]) ) { engclient_cmd(targetID, "jointeamtwo") client_print(0, print_chat, "[ENSL] Referee (%s) enforced player %s to join aliens.", name, targetName) } // If team is random.. else if ( equal(team, "random") ) { // Get random team. Don't trust autoassign. if ( random(2) == 0 ) { engclient_cmd(targetID, "jointeamone") } else { engclient_cmd(targetID, "jointeamtwo") } // Output client_print(0, print_chat, "[ENSL] Referee (%s) enforced player %s to join random team.", name, targetName) } // Instructions for lowsy arguments else { console_print(id, "Please give either Marines, Aliens or random.") } // Log event log_message("[ENSL] [enslteam] [%s:%s] [%d:%s] [%s]", g_userSteamID[pid], name, g_userSteamID[targetPid], targetName, team) return PLUGIN_HANDLED } // Command: amx_enslkick [name/#ID] [bantime(min)] [reason] (kick user) public cmd_enslkick(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 1) ) { return PLUGIN_HANDLED } // Prevent multirun if ( task_exists(T_BAN) ) { client_print(id, print_chat, "[ENSL] Ban being processed, please wait.") return PLUGIN_HANDLED } // Fetch arguments new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new target[HL_LEN_NICK + 1]; read_argv(1, target, HL_LEN_NICK); remove_quotes(target) new sMins[R_LEN_MINS +1]; read_argv(2, sMins, R_LEN_MINS); remove_quotes(sMins) new reason[R_LEN_KICKREASON + 1]; read_argv(3, reason, R_LEN_KICKREASON); remove_quotes(reason) new mins = str_to_num(sMins) new targetID = func_get_player(id, target) // Parse reason for security purposes func_parse_string(reason, R_LEN_KICKREASON) // If user cannot be found, abort if ( targetID == -1 || equal(target, "") ) { return PLUGIN_HANDLED } // Get user ID new targetName[HL_LEN_NICK + 1]; get_user_name(targetID, targetName, HL_LEN_NICK) new targetUID = get_user_userid(targetID) new targetPid = func_authenticate(targetID) // Ban player if minutes specified if ( mins > 0 ) { // Ban only for maximum minutes mins = mins > R_MAX_MINS ? R_MAX_MINS : mins // Generate variables new addr[HL_LEN_ADDR + 1]; get_user_ip(0, addr, HL_LEN_ADDR) new eReason[HL_LEN_SAY + 1]; func_urlencode(reason, eReason, HL_LEN_SAY) new ts = func_get_time() new chal[CC_LEN_INPUT + 1]; formatex(chal, CC_LEN_INPUT, "%s%d", g_userSteamID[pid], ts) new sign[CC_LEN_CODE + 1]; func_crypt(chal, sign) new url[W_LEN_URL + 1]; formatex(url, W_LEN_URL, "%s%s?len=%d&user=%d&addr=%s&reason=%s&ts=%d&sign=%s", B_URL, g_userSteamID[targetPid], mins, g_userEnslId[pid], addr, eReason, ts, sign) // Schedule the sending and reading function set_task(0.5, "task_web_ban", T_BAN, url, W_LEN_URL) // Ban player server_cmd("banid %d #%d; wait; kick #%d ^"BANNED: %s^"", mins, targetUID, targetUID, reason) client_print(0, print_chat, "[ENSL] Referee (%s) banned player %s for %d minutes", name, targetName, mins) log_message("[ENSL] [enslban] [%s:%s] [%d:%s] [%sm] [%s]", g_userSteamID[pid], name, g_userSteamID[targetPid], targetName, mins, reason) } // Otherwise just kick the player else { server_cmd("kick #%d ^"%s^"", targetUID, reason) client_print(0, print_chat, "[ENSL] Referee (%s) kicked player %s", name, targetName) log_message("[ENSL] [enslkick] [%s:%s] [%d:%s] [%s]", g_userSteamID[pid], name, g_userSteamID[targetPid], targetName, reason) } return PLUGIN_HANDLED } // Task: Send ban query public task_web_ban(url[W_LEN_URL + 1]) { new webquery = get_pcvar_num(g_cvar_webquery) if ( webquery != 0 ) { func_write_web(g_webSocket[W_I_BAN], url) socket_close(g_webSocket[W_I_BAN]) } } // Function: enforce real nicks public cmd_enslnicks(id, level, cid) { // Authenticate user func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 0) ) { return PLUGIN_HANDLED } // Go through all players new name[HL_LEN_NICK + 1] for ( new i = 1; i < g_userCount; i++ ) { // If player is on server if ( g_userStatus[i] ) { // Get player name name = "^0"; get_user_name(g_userIdIndex[i], name, HL_LEN_NICK) // If the name is wrong, fix it if ( g_userEnslInfo[i] && containi(name, g_userEnslNick[i]) == -1 ) { client_cmd(g_userIdIndex[i], "name ^"%s^"", g_userEnslNick[i]) } } } // Announce name = "^0"; get_user_name(id, name, HL_LEN_NICK) client_print(0, print_chat, "[ENSL] Referee (%s) enforced ENSL nicks.", name) return PLUGIN_HANDLED } // Function: amx_enslsay [message] (Global say) public cmd_enslsay(id, level, cid) { // Authenticate user func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 1) ) { return PLUGIN_HANDLED } // Say colored message new msg[HL_LEN_SAY + 1]; read_args(msg, HL_LEN_SAY); remove_quotes(msg) new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) // Print the message client_print(0, print_chat, "%s : %s", name, msg[1]) return PLUGIN_HANDLED } // Function: amx_enslpwd [password] (Set password) public cmd_enslpwd(id, level, cid) { // Authenticate user func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 1) ) { return PLUGIN_HANDLED } // Init variables new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new pwd[HL_LEN_PWD + 1]; read_args(pwd, HL_LEN_PWD); remove_quotes(pwd) // Parse the password func_parse_string(pwd, HL_LEN_PWD) // Say the message set_cvar_string("sv_password", pwd) return PLUGIN_HANDLED } // Function: amx_enslmove [#ID/ip:port] [password] (Connect everyone to another server) public cmd_enslmove(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 1) ) { return PLUGIN_HANDLED } // Init variables new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new addr[HL_LEN_ADDR + 1]; get_user_ip(0, addr, HL_LEN_ADDR) new newaddr[HL_LEN_DNS + 1]; read_argv(1, newaddr, HL_LEN_DNS + 1); remove_quotes(newaddr) new pwd[HL_LEN_PWD + 1], port[HL_LEN_PORT + 1] // Set port and password correctly if port is given (amxx splits args. by :) new delim[2]; read_argv(2, delim, 1); remove_quotes(delim) if ( equal(delim, ":") ) { read_argv(3, port, HL_LEN_PORT); remove_quotes(port) add(newaddr, HL_LEN_DNS, delim, 1) add(newaddr, HL_LEN_DNS, port, HL_LEN_PORT) read_argv(4, pwd, HL_LEN_PWD); remove_quotes(pwd) } // Normal procedure without port else { add(newaddr, HL_LEN_DNS, ":27015") read_argv(2, pwd, HL_LEN_PWD); remove_quotes(pwd) } // Inform people and log client_print(0, print_chat, "[ENSL] Referee (%s) is moving everyone to server: %s", name, newaddr) log_message("[ENSL] [enslmove] [%s:%s] [%s]", g_userSteamID[pid], name, addr) // Move everyone new cmd[HL_LEN_MSG + 1]; formatex(cmd, HL_LEN_MSG, " connect %s", newaddr) log_message("[DEBUG] enslmove: %s", cmd) set_task(5.0, "task_enslmove", 0, cmd, HL_LEN_MSG, "b", 1) // Move the HLTV if necessary if ( g_gameHltvs > 0 ) { new url[W_LEN_URL + 1], readargs[1]; readargs[0] = id formatex(url, W_LEN_URL, "%smove?addr=%s&newaddr=%s&newpwd=%s", HT_URL, addr, newaddr, pwd) new webquery = get_pcvar_num(g_cvar_webquery) if ( webquery != 0 ) { func_write_web(g_webSocket[W_I_HLTV], url) set_task(0.5, "task_web_hltv_send", 0, url, W_LEN_URL) set_task(1.0, "task_web_hltv", T_HLTV, readargs, 1) } } return PLUGIN_HANDLED } // Task: move everyone to another server public task_enslmove(args[]) { client_cmd(0, args) } // Function: amx_enslhelp (Show help about ENSL Plugin commands) public cmd_enslhelp(id, level, cid) { // Authenticate user func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 0) ) { return PLUGIN_HANDLED } // Show information about all commands new cmd[A_LEN_CMD + 1], info[A_LEN_CMDINFO + 1], flags for ( new i = 0; get_concmd(i, cmd, A_LEN_CMD, flags, info, A_LEN_CMDINFO, -1, 1); i++) { console_print(id, "%s %s", cmd, info) } return PLUGIN_HANDLED } // Function: amx_enslcvars (Show help about ENSL Plugin CVARs) public cmd_enslcvars(id, level, cid) { // Authenticate user func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 0) ) { return PLUGIN_HANDLED } // Show information about all cvars for ( new i = 0; i < H_NUM_CVARS; i++) { console_print(id, "%s", g_cvarHelp[i]) } return PLUGIN_HANDLED } // Function: amx_enslgag [name/#ID] (Prvents user from talking) public cmd_enslgag(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 1) ) { return PLUGIN_HANDLED } // Fetch arguments new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new target[HL_LEN_NICK + 1]; read_argv(1, target, HL_LEN_NICK) new targetID = func_get_player(id, target) // If user cannot be found, abort if ( targetID == -1 || equal(target, "") ) { return PLUGIN_HANDLED } // Get user ID new targetName[HL_LEN_NICK + 1]; get_user_name(targetID, targetName, HL_LEN_NICK) new targetPid = func_authenticate(targetID) // If user is gagged... if ( g_userGagged[targetPid] ) { // Ungag g_userGagged[targetPid] = 0 // Log and inform people client_print(0, print_chat, "[ENSL] Referee (%s) ungagged player: %s", name, targetName) log_message("[ENSL] [enslgag] [%s:%s] [%d:%s]", g_userSteamID[pid], name, g_userSteamID[targetPid], targetName) } // If user is not gaggged.. else { // Gag g_userGagged[targetPid] = 1 // Log and inform people client_print(0, print_chat, "[ENSL] Referee (%s) gagged player: %s", name, targetName) log_message("[ENSL] [enslgag] [ungag] [%s:%s] [%d:%s]", g_userSteamID[pid], name, g_userSteamID[targetPid], targetName) } return PLUGIN_HANDLED } /************************************************************************************** * HLTV handling **************************************************************************************/ // Function: amx_enslhltv [matchID/demoname/stop] (Request a HLTV.) public cmd_enslhltv(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 1) ) { return PLUGIN_HANDLED } // Prevent multirun if ( task_exists(T_HLTV) ) { console_print(id, "HLTV query already running, please wait and try again.") return PLUGIN_HANDLED } // Fetch arguments new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new arg[HT_LEN_DEMO + 1]; read_argv(1, arg, HT_LEN_DEMO); remove_quotes(arg) new addr[HL_LEN_IP + HL_LEN_PORT + 2]; get_user_ip(0, addr, HL_LEN_IP + HL_LEN_PORT + 1) new url[W_LEN_URL + 1], Float:delay = 0.0 // If stop-command was requested, generate stop query if ( equal(arg, "shoutcast") ) { new pwd[HL_LEN_PWD + 1]; get_cvar_string("sv_password", pwd, HL_LEN_PWD) console_print(id, "Requesting a shoutcast HLTV to this server (%s pwd: %s)", addr, pwd) formatex(url, W_LEN_URL, "%sshoutcast?addr=%s&pwd=%s", HT_URL, addr, pwd) } else if ( equal(arg, "stop") ) { console_print(id, "HLTV will be stopped in 90 seconds.") formatex(url, W_LEN_URL, "%sstop?addr=%s", HT_URL, addr) delay = 90.0 } // If immediate stop was requested, generate stop query else if ( equal(arg, "stopnow") ) { console_print(id, "HLTV will be stopped now.") formatex(url, W_LEN_URL, "%sstop?addr=%s", HT_URL, addr) } // Otherwise send normal request else { new pwd[HL_LEN_PWD + 1]; get_cvar_string("sv_password", pwd, HL_LEN_PWD) new challenge[CC_LEN_INPUT + 1]; copy(challenge, CC_LEN_INPUT, arg) new sign[CC_LEN_CODE + 1]; func_crypt(challenge, sign) console_print(id, "Requesting a HLTV to this server (%s pwd: %s) for game: %s", addr, pwd, arg) formatex(url, W_LEN_URL, "%sreq?game=%s&addr=%s&pwd=%s&user=%d&sign=%s", HT_URL, arg, addr, pwd, g_userEnslId[pid], sign) } // Schedule the sending and reading functions new readargs[1]; readargs[0] = id set_task(delay, "task_web_hltv_send", 0, url, W_LEN_URL) set_task(delay + 0.5, "task_web_hltv", T_HLTV, readargs, 1) return PLUGIN_HANDLED } // Task: Send HLTV command public task_web_hltv_send(url[W_LEN_URL + 1]) { func_write_web(g_webSocket[W_I_HLTV], url) } // Function: Reads HLTV response from the web public task_web_hltv(args[]) { // Increase reads g_webReads[W_I_HLTV]++ // If there's data in the socket, read it. if ( socket_change(g_webSocket[W_I_HLTV], 200) ) { func_read_web(g_webSocket[W_I_HLTV], HT_MARK, HT_NUM_LINES, g_webData[W_I_HLTV], g_webDataLine[W_I_HLTV]) } // If socket hasn't finished, retry if ( g_webDataLine[W_I_HLTV] != HT_NUM_LINES && g_webReads[W_I_HLTV] < W_READS ) { set_task(0.5, "task_web_hltv", T_HLTV, args, 1) return } // Print output to user if there's data if ( g_webDataLine[W_I_HLTV] > 0 ) { console_print(args[0], "[HLTV] %s", g_webData[W_I_HLTV][0]) } // Finally close the socket and reset vars socket_close(g_webSocket[W_I_HLTV]) g_webReads[W_I_HLTV] = 0 g_webDataLine[W_I_HLTV] = 0 // Reset data for ( new i = 0; i < W_NUM_LINES; i++ ) { g_webData[W_I_HLTV][i] = "^0" } } /************************************************************************************** * Game pausing **************************************************************************************/ // Command: pause public cmd_pause(id) { // If pausing or unpausing is not awaited, ignore if ( !g_pausePending ) { return PLUGIN_HANDLED } // Get ref's name and say the message new name[HL_LEN_NICK + 1]; get_user_name(g_pauseReq, name, HL_LEN_NICK) // Disable pause wait g_pauseReq = 0 g_pausePending = 0 server_cmd("pausable 0") // If already paused, this will be unpause if ( g_pauseStatus ) { g_pauseStatus = 0 client_print(0, print_chat, "[ENSL] Referee (%s) has unpaused the game", name) } // Otherwise this will be pause else { g_pauseStatus = 1 client_print(0, print_chat, "[ENSL] Referee (%s) has pause the game", name) } return PLUGIN_CONTINUE } // Function: amx_enslpause (Pause / unpause) public cmd_enslpause(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 0) ) { return PLUGIN_HANDLED } // Init variables new pauser = find_player("h") new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) // If pausable player cannot be found, abort if ( !pauser ) { console_print(id, "Unable to (un)pause") return PLUGIN_HANDLED } // Set the plugin to wait for pause from client g_pauseReq = id g_pausePending = 1 server_cmd("pausable 1") client_cmd(pauser, "pause; pauseMark") log_message("[ENSL] [enslpause] [%s:%s] [status=%d]", g_userSteamID[pid], name, g_pauseStatus) return PLUGIN_HANDLED } /************************************************************************************** * Voice blocking **************************************************************************************/ // Command: public say public cmd_say(id) { // Authenticate new pid = func_authenticate(id) // Get the message new msg[HL_LEN_SAY + 1]; read_args(msg, HL_LEN_SAY); remove_quotes(msg) // Check if its a whois query if ( contain(msg, "/whois") == 0 ) { cmd_whois(id, msg) return PLUGIN_HANDLED } // Check if user is disallowed to talk if ( g_gameModerated && !g_userVoice[pid] && !func_is_referee(pid) ) { client_print(id, print_chat, "[ENSL] Public chat is moderated and you are not allowed to talk.") return PLUGIN_HANDLED } // Check if user has been disallowed to talk if ( g_userGagged[pid] ) { client_print(id, print_chat, "[ENSL] You have been gagged by a referee.") return PLUGIN_HANDLED } // Check if user has global talk features if ( g_userTeam[pid] == HL_RRSPECS ) { new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) client_print(0, print_chat, "%s : %s", name, msg) return PLUGIN_HANDLED } return PLUGIN_CONTINUE } // Command: empty=freetalk or [name/#ID] (Limit talking) public cmd_enslvoice(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 0) ) { return PLUGIN_HANDLED } // Initialize variables new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new userStr[HL_LEN_MSG + 1], userNum = 0, argC = read_argc() new userSearch[HL_LEN_NICK + 1], userName[HL_LEN_NICK + 1], userId, userPid // Take action if arguments are specified if ( argC > 1 ) { // Loop through the arguments for ( new i = 1; i < argC; i++ ) { // Fetch the given users userSearch = "^0"; read_argv(i, userSearch, HL_LEN_NICK) userId = func_get_player(id, userSearch) // If user can be found.. if ( userId != -1 ) { // Authenticate + fetch username & pid userName = "^0"; get_user_name(userId, userName, HL_LEN_NICK) userPid = func_authenticate(userId) // If user is voiced, unvoice user if ( g_userVoice[userPid] ) { g_userVoice[userPid] = 0 console_print(id, "Talk privilege revoked from: %s", userName) log_message("[ENSL] [enslvoice] [%s:%s] [revoke] [%s:%s]", g_userSteamID[pid], name, userName, g_userSteamID[userPid]) } // If user is not voiced, voice user else { g_userVoice[userPid] = 1 console_print(id, "Talk privilege granted to: %s", userName) log_message("[ENSL] [enslvoice] [%s:%s] [grant] [%s:%s]", g_userSteamID[pid], name, userName, g_userSteamID[userPid]) // If moderation is enabled, add user to the list of privileged people if ( g_gameModerated ) { add(userStr, HL_LEN_MSG, " [", 2) add(userStr, HL_LEN_MSG, userName, HL_LEN_NICK) add(userStr, HL_LEN_MSG, "]", 1) userNum++ } } } } } // Set moderation if arguments specified or it already disabled if ( !g_gameModerated || argC > 1 ) { // Find users who may talk. if we are enabling the chat moderation if ( !g_gameModerated ) { for ( new i = 1; i < g_userCount; i++ ) { // If user is on server and is able talk, add user if ( g_userStatus[i] && g_userVoice[i] ) { userName = "^0"; get_user_name(g_userIdIndex[i], userName, HL_LEN_NICK) add(userStr, HL_LEN_MSG, " [", 2) add(userStr, HL_LEN_MSG, userName, HL_LEN_NICK) add(userStr, HL_LEN_MSG, "]", 1) userNum++ } } } // Set moderation g_gameModerated = 1 client_print(0, print_chat, "[ENSL] Referee (%s) has enabled chat moderation.", name) log_message("[ENSL] [enslvoice] [%s:%s] [ENABLE]", g_userSteamID[pid], name) // Echo who may talk if ( userNum > 0 ) { client_print(0, print_chat, "[ENSL] Chat allowed for:%s", userStr) } } // Otherwise ... else { // Remove voice privilege for ( new i = 1; i < g_userCount; i++ ) { g_userVoice[i] = 0 } // Unset moderation g_gameModerated = 0 client_print(0, print_chat, "[ENSL] Referee (%s) has disabled chat moderation.", name) log_message("[ENSL] [enslvoice] [%s:%s] [DISABLED]", g_userSteamID[pid], name) } return PLUGIN_HANDLED } /************************************************************************************** * ENSL Official Match setting CVAR enforcement **************************************************************************************/ // Function: initialize server CVARs public func_init_cvars() { // Integers func_add_int_cvar("mp_blockscripts", 0) func_add_int_cvar("mp_consistency", 1) func_add_int_cvar("mp_combattime", 15) func_add_int_cvar("mp_drawdamage", 0) func_add_int_cvar("mp_falldamage", 1) func_add_int_cvar("mp_flashlight", 1) func_add_int_cvar("mp_footsteps", 1) func_add_int_cvar("mp_friendlyfire", 1) func_add_int_cvar("mp_killdelay", 3) func_add_int_cvar("mp_team1damagepercent", 100) func_add_int_cvar("mp_team2damagepercent", 100) func_add_int_cvar("mp_timelimit", 0) func_add_int_cvar("mp_tournamentmode", 1) func_add_int_cvar("pausable", 0) func_add_int_cvar("sv_aim", 0) func_add_int_cvar("sv_cheats", 0) func_add_int_cvar("sv_maxspeed", 4000) func_add_int_cvar("sv_minrate", 10000) func_add_int_cvar("sv_maxrate", 20000) func_add_int_cvar("sv_minupdaterate", 40) func_add_int_cvar("sv_maxupdaterate", 100) func_add_int_cvar("sv_proxies", 5) func_add_int_cvar("ensl_minrate", 10000) func_add_int_cvar("ensl_maxrate", 25000) func_add_int_cvar("ensl_mincmdrate", 50) func_add_int_cvar("ensl_maxcmdrate", 150) func_add_int_cvar("ensl_minupdaterate", 40) func_add_int_cvar("ensl_maxupdaterate", 150) func_add_int_cvar("ensl_minfps", 50) func_add_int_cvar("ensl_maxfps", 999) func_add_int_cvar("ensl_snapshots", 1, 0) func_add_int_cvar("ensl_checkids", 1, 1) func_add_int_cvar("ensl_checkrates", 1, RC_ENABLED_FETCH) func_add_int_cvar("ensl_ffclock", 1, 0) func_add_int_cvar("ensl_merclimit", 1, 0) func_add_int_cvar("ensl_teamlimit", 6, 0) func_add_int_cvar("ensl_speclimit", 1, 0) func_add_int_cvar("ensl_refaccess", 1, 1) func_add_int_cvar("ensl_combatmode", 2, 1) // Floats func_add_flt_cvar("mp_countdowntime", 0.2) func_add_flt_cvar("sv_clienttrace", 3.5) } // Command: amx_enslcfg (Execute ENSL Match Settings) public cmd_enslcfg(id, level, cid) { // Authenticate user if necessary + get username new pid = func_authenticate(id) new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) // Check if user can access the command if ( !func_check_access(id, level, cid, 0) ) { return PLUGIN_HANDLED } // If it is pcw mode, run pcw mode settings new mode[4]; read_argv(1, mode, 3); new pcw = equal(mode, "pcw") // Set Server ENSL Cvars, integers for ( new i = 0; i < C_NUM_INTS; i++ ) { set_cvar_num(g_cvarIntName[i], pcw ? g_cvarIntPCW[i] : g_cvarIntValue[i]) } // Set Server ENSL Cvars, floats for ( new i = 0; i < C_NUM_FLOATS; i++ ) { set_cvar_float(g_cvarFltName[i], pcw ? g_cvarFltPCW[i] : g_cvarFltValue[i]) } // Log and announce the event if ( pcw ) { client_print(0, print_chat, "[ENSL] Version %s PCW Settings Loaded", VERSION) } else { client_print(0, print_chat, "[ENSL] Version %s OFFICIAL Settings Loaded", VERSION) } // Log it log_message("[ENSL] [enslcfg] [%s:%s] [%d]", g_userSteamID[pid], name, pcw) return PLUGIN_HANDLED } // Command: amx_enslcvar (Change CVAR value) public cmd_enslcvar(id, level, cid) { // Authenticate user if necessary new pid = func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 2) ) { return PLUGIN_HANDLED } // Get referee name and argument new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new cvarName[C_LEN_NAME + 1]; read_argv(1, cvarName, C_LEN_NAME) new cvarValue[R_LEN_NUM + 1]; read_argv(2, cvarValue, R_LEN_NUM) // Find the CVAR, integers for ( new i = 0; i < C_NUM_INTS; i++ ) { // If found, set it if ( equal(g_cvarIntName[i], cvarName) ) { // Format values for an integer cvar new intValue = str_to_num(cvarValue), oldValue = get_cvar_num(g_cvarIntName[i]) set_cvar_num(g_cvarIntName[i], intValue) // Announce it, log it and exit client_print(0, print_chat, "[ENSL] Referee (%s) changed %s from %d to %d", name, cvarName, oldValue, intValue) log_message("[ENSL] [cvar] [%s:%s] [%s] %d -> %d", g_userSteamID[pid], name, cvarName, oldValue, intValue) return PLUGIN_HANDLED } } // Find the CVAR, floats for ( new i = 0; i < C_NUM_FLOATS; i++ ) { // If found, set it if ( equal(g_cvarFltName[i], cvarName) ) { // Format values for a float cvar new Float:fltValue = str_to_float(cvarValue), Float:oldValue = get_cvar_float(g_cvarFltName[i]) set_cvar_float(g_cvarFltName[i], fltValue) // Announce it, log it and exit client_print(0, print_chat, "[ENSL] Referee (%s) changed %s from %.1f to %.1f", name, cvarName, oldValue, fltValue) log_message("[ENSL] [cvar] [%s:%s] [%s] %f -> %f", g_userSteamID[pid], name, cvarName, oldValue, fltValue) return PLUGIN_HANDLED } } // Otherwise abort since cvar was not found console_print(id, "CVAR not found.") return PLUGIN_HANDLED } // Function: add integer cvar stock func_add_int_cvar(name[C_LEN_NAME + 1], value = 0, pcw = -1, level = C_LEVEL_MANDATORY) { // Don't add over borders if ( g_cvarIntIndex == sizeof(g_cvarIntName) ) { return g_cvarIntIndex } g_cvarIntName[g_cvarIntIndex] = name g_cvarIntValue[g_cvarIntIndex] = value g_cvarIntPCW[g_cvarIntIndex] = pcw != -1 ? pcw : value g_cvarIntLevel[g_cvarIntIndex] = level g_cvarIntIndex++ return g_cvarIntIndex - 1 } // Function: add float cvar stock func_add_flt_cvar(name[C_LEN_NAME + 1], Float:value = 0.0, Float:pcw = -1.0, level = C_LEVEL_MANDATORY) { // Don't add over borders if ( g_cvarFltIndex == sizeof(g_cvarFltName) ) { return g_cvarFltIndex } g_cvarFltName[g_cvarFltIndex] = name g_cvarFltValue[g_cvarFltIndex] = value g_cvarFltPCW[g_cvarFltIndex] = pcw != -1.0 ? pcw : value g_cvarFltLevel[g_cvarFltIndex] = level g_cvarFltIndex++ return g_cvarFltIndex - 1 } /************************************************************************************** * Spectation block **************************************************************************************/ // Command: spectate public cmd_spectate(id) { // Authenticate new pid = func_authenticate(id) // Check for too rapid action if ( func_check_cmdtime(pid, CT_TEAMJOIN, print_chat) == PLUGIN_HANDLED ) { return PLUGIN_HANDLED } // Check if user can join if ( get_pcvar_num(g_cvar_speclimit) && !func_is_referee(pid) ) { client_print(id, print_chat, "[ENSL] You cannot join spectators. Only referees and admins allowed.") return PLUGIN_HANDLED } return PLUGIN_CONTINUE } // Touch: spectate public touch_spectate(ent, id) { return cmd_spectate(id) } /************************************************************************************** * Team management functions **************************************************************************************/ // Event: player changes team public client_changeteam(id, newTeam, oldTeam) { // Authenticate new pid = func_authenticate(id) // Set the new team and increase the playercount g_userTeam[pid] = newTeam g_teamPlayers[newTeam]++ // Waypoint target g_userDefendWP[id] = 0; // Process team leave func_teamleave(id, oldTeam) return PLUGIN_CONTINUE } // Entity catches public touch_teamjoin(ent, id) { if ( ent == HL_ENT_MARINES ) { return cmd_jointeamone(id) } else if ( ent == HL_ENT_ALIENS ) { return cmd_jointeamtwo(id) } return PLUGIN_HANDLED } // Entity catch for autoassign public touch_autoassign(ent, id) { client_print(id, print_chat, "[ENSL] Autoassign disabled.") return PLUGIN_HANDLED } // Command: autoassign public cmd_autoassign(id) { client_print(id, print_chat, "[ENSL] Autoassign disabled.") return PLUGIN_HANDLED } // Command: jointeamone (marine join) // NOTE: This doesn't cover all teamchanges -> counters not updated here public cmd_jointeamone(id) { // Authenticate player new pid = func_authenticate(id) // Check if user was digested if ( g_userDDTime[pid] != 0 && g_userDDTime[pid] > get_systime() ) { client_print(id, print_chat, "[ENSL] Please wait %d seconds before joining.", g_userDDTime[pid] - get_systime()) return PLUGIN_HANDLED } // Check if user can join (merclimit) return func_teamjoin(id, HL_MARINES) } // Command: jointeamtwo (alien join) // NOTE: This doesn't cover all teamchanges -> counters not updated here public cmd_jointeamtwo(id) { // Authenticate player func_authenticate(id) return func_teamjoin(id, HL_ALIENS) } // Command: enter readyroom public cmd_readyroom(id) { // Authenticate new pid = func_authenticate(id) // Allow f4f1 g_userCmdTime[pid][CT_TEAMJOIN] = get_gametime() - G_FREQ // Reset rates so user can change them for ( new i = 0; i < RC_NUM_VARS; i++ ) { g_userRCVars[pid][i] = 0 g_userRCWarn[pid][i] = RC_WARN_NEGATIVE } return PLUGIN_CONTINUE } // Function: player tries to join a team (marines/aliens) public func_teamjoin(id, newTeam) { new maxPlayers = get_pcvar_num(g_cvar_teamlimit), pid = g_userPidIndex[id], otherTeam = newTeam == HL_ALIENS ? HL_MARINES : HL_ALIENS // Limit spamming if ( func_check_cmdtime(pid, CT_TEAMJOIN, print_chat) == PLUGIN_HANDLED ) { return PLUGIN_HANDLED } // Check if member limit is one if ( func_check_membersonly(id, pid) == 0 ) { return PLUGIN_HANDLED } // If player is already on a proper team, ignore it if ( g_userTeam[pid] != HL_RRSPECS ) { client_print(id, print_chat, "[ENSL] You are already on a team") return PLUGIN_HANDLED } // If game has started, also limit player count if enabled if ( g_gameStarted && maxPlayers > 0 && g_teamPlayers[newTeam] >= maxPlayers ) { client_print(id, print_chat, "[ENSL] Player limiting is enabled. You cannot join. Maximum is %d players.", maxPlayers) return PLUGIN_HANDLED } // Check if user is allowed to merc if ( (g_gameStarted || g_teamReady[newTeam]) && get_pcvar_num(g_cvar_merclimit) && g_userMerc[pid] != newTeam && g_userEnslTid[pid] != g_teamENSLId[newTeam] ) { new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) client_print(0, print_chat, "[ENSL] %s is trying to merc for %s. %s can approve it by typing /ok", name, g_teamNames[newTeam], g_teamNames[otherTeam]) g_teamMReq[newTeam] = pid return PLUGIN_HANDLED } return PLUGIN_CONTINUE } // Function:process player leave from team public func_teamleave(id, oldTeam) { // Decrease the player count g_teamPlayers[oldTeam]-- // For marines & aliens if ( oldTeam == HL_MARINES || oldTeam == HL_ALIENS ) { // If game has not started... if ( !g_gameStarted && !g_gameEnding ) { // If neither of teams have players, stop the clock if ( g_teamPlayers[HL_MARINES] == 0 && g_teamPlayers[HL_ALIENS] == 0 ) { func_stop_ffclock(0) func_reset_teams() } // If only the team which was left, is empty, reset its vars else if ( g_teamPlayers[oldTeam] == 0 ) { func_reset_team(oldTeam) } // Otherwise force unready else if ( g_teamReady[oldTeam] ) { // Loop through all players for ( new pid = 1; pid < g_userCount; pid++ ) { // Unready the first player, who is on server, of the team given if ( g_userStatus[pid] && g_userTeam[pid] == oldTeam ) { client_print(g_userIdIndex[pid], print_chat, "[ENSL] A player left your team and auto-unready is forced.") client_cmd(g_userIdIndex[pid], "say notready") break } } } } } return PLUGIN_CONTINUE } // Command: allow mercs of opponent team to play public cmd_mercsok(id) { // Authenticate and get team ids new pid = func_authenticate(id) new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new team = g_userTeam[pid], other = team == HL_ALIENS ? HL_MARINES : HL_ALIENS new userNum = 0, userStr[HL_LEN_MSG + 1] // Check for too rapid action if ( func_check_cmdtime(pid, CT_GENERAL, print_chat) == PLUGIN_HANDLED ) { return PLUGIN_HANDLED } // Do not accept it from other than aliens / marines before game start if ( (team != HL_MARINES && team!= HL_ALIENS) || g_gameStarted ) { client_print(id, print_chat, "[ENSL] Only marines/aliens can approve mercs before game start.") return PLUGIN_HANDLED } // If merclimiting is off, ignore it if ( get_pcvar_num(g_cvar_merclimit) == 0 ) { client_print(id, print_chat, "[ENSL] Merclimiting is not enabled.") return PLUGIN_HANDLED } // If opponent hasn't registered yet, don't bother if ( g_teamENSLId[other] == 0 ) { client_print(id, print_chat, "[ENSL] Other team has not registered yet.") return PLUGIN_HANDLED } // Go through all players for ( new i = 1; i < g_userCount; i++ ) { // Check if the player on the other team is a merc if ( g_userStatus[i] && g_userTeam[i] == other && g_userEnslTid[i] != g_teamENSLId[other] && g_userMerc[i] != other ) { // Allow mercing g_userMerc[i] = other // Add seperating comma for 2nd+ mercs if ( userNum > 0 ) { add(userStr, HL_LEN_MSG, ", ", 2) } // Add player name to the list new userName[HL_LEN_NICK]; get_user_name(g_userIdIndex[i], userName, HL_LEN_NICK) add(userStr, HL_LEN_MSG, userName, 15) userNum++ } } // If no mercs were found, ignore it if ( userNum == 0 ) { client_print(id, print_chat, "[ENSL] No mercs were found from %s.", g_teamNames[other]) } // Otherwise announce it else { client_print(0, print_chat, "[ENSL] %s allowed mercs: %s.", g_teamNames[team], userStr) log_message("[ENSL] [mercsok] [%s:%s] [%s]", g_userSteamID[pid], userStr) } return PLUGIN_HANDLED } // Command: list mercs of the opposite team public cmd_mercs(id) { // Authenticate and get team ids new pid = func_authenticate(id) new team = g_userTeam[pid], other = team == HL_ALIENS ? HL_MARINES : HL_ALIENS new userNum = 0, userStr[HL_LEN_MSG + 1] // Check for too rapid action if ( func_check_cmdtime(pid, CT_GENERAL, print_chat) == PLUGIN_HANDLED ) { return PLUGIN_HANDLED } // Do not accept it from other than aliens / marines before game start if ( (team != HL_MARINES && team!= HL_ALIENS) || g_gameStarted ) { client_print(id, print_chat, "[ENSL] Only marines/aliens can list mercs before game start.") return PLUGIN_HANDLED } // If opponent hasn't registered yet, don't bother if ( g_teamENSLId[other] == 0 ) { client_print(id, print_chat, "[ENSL] Other team has not registered yet.") return PLUGIN_HANDLED } // Go through all players for ( new i = 1; i < g_userCount; i++ ) { // Check if the player on the other team is a merc if ( g_userStatus[i] && g_userTeam[i] == other && g_userEnslTid[i] != g_teamENSLId[other] ) { // Add seperating comma for 2nd+ mercs if ( userNum > 0 ) { add(userStr, HL_LEN_MSG, ", ", 2) } // Add player name to the list new userName[HL_LEN_NICK]; get_user_name(g_userIdIndex[i], userName, HL_LEN_NICK) add(userStr, HL_LEN_MSG, userName, 15) userNum++ } } // If no mercs were found, say it if ( userNum == 0 ) { client_print(id, print_chat, "[ENSL] No mercs were found from %s.", g_teamNames[other]) } // Otherwise announce it else { client_print(id, print_chat, "[ENSL] %s' mercs: %s.", g_teamNames[other], userStr) } return PLUGIN_HANDLED } // Command: approve single merc public cmd_ok(id) { // Authenticate and get team ids new pid = func_authenticate(id) new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new team = g_userTeam[pid], other = team == HL_ALIENS ? HL_MARINES : HL_ALIENS new merc = g_teamMReq[other] // Do not accept it from othe than aliens / marines if ( team != HL_MARINES && team!= HL_ALIENS ) { return PLUGIN_HANDLED } // If a merc has requested to join if ( merc != -1 && g_userMerc[merc] != other && g_userStatus[merc] ) { // Allowd mercing, reset requestor spot g_userMerc[merc] = other g_teamMReq[other] = -1 // Announce it new mercName[HL_LEN_NICK + 1]; get_user_name(g_userIdIndex[merc], mercName, HL_LEN_NICK) client_print(0, print_chat, "[ENSL] %s has been allowed to merc for %s.", mercName, g_teamNames[other]) log_message("[ENSL] [mercok] [%s:%s] [%s:%s] [%d]", g_userSteamID[pid], name, g_userSteamID[merc], mercName, team) } // Otherwise print error else { client_print(id, print_chat, "[ENSL] No merc requests found.") } return PLUGIN_HANDLED } // Command: [user/#ID] [team] (toggle mercing) public cmd_enslmerc(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 1) ) { return PLUGIN_HANDLED } // Initialize variables new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new strMerc[HL_LEN_NICK + 1]; read_argv(1, strMerc, HL_LEN_NICK) new mercId = func_get_player(id, strMerc) // Check that player is found, not already mercing and on server if ( mercId == -1 ) { return PLUGIN_HANDLED } // Get PID new mercPid = func_authenticate(mercId) // If merc has been granted access, revoke it if ( g_userMerc[mercPid] ) { // Revoke mercing g_userMerc[mercPid] = 0 // Announec and log it new mercName[HL_LEN_NICK + 1]; get_user_name(mercId, mercName, HL_LEN_NICK) client_print(0, print_chat, "[ENSL] Referee (%s) has disallowed %s's mercing.", name, mercName) log_message("[ENSL] [enslmerc] [%s:%s] [%s:%s] [REVOKE]", g_userSteamID[pid], name, g_userSteamID[mercPid], mercName) return PLUGIN_HANDLED } // Fetch team new strTeam[HL_LEN_TEAM + 1]; read_argv(2, strTeam, HL_LEN_TEAM) new team = func_get_team(id, strTeam) // Abort it team cannot be found if ( team == -1 ) { return PLUGIN_HANDLED } // Allow mercing, reset requestor spot g_userMerc[mercPid] = team // Announec and log it new mercName[HL_LEN_NICK + 1]; get_user_name(mercId, mercName, HL_LEN_NICK) client_print(0, print_chat, "[ENSL] Referee (%s) has allowed %s to merc for %s.", name, mercName, g_teamNames[team]) log_message("[ENSL] [enslmerc] [%s:%s] [%s:%s] [%d] [PERMIT]", g_userSteamID[pid], name, g_userSteamID[mercPid], mercName, team) return PLUGIN_HANDLED } // Function: reset team variables at round end public func_reset_team(team) { // Reset teams (marines & aliens) g_teamReady[team] = 0 g_teamENSLId[team] = 0 g_teamENSLName[team] = "^0" g_teamMReq[team] = -1 } // Task: reset both teams public func_reset_teams() { // Reset teams func_reset_team(HL_MARINES) func_reset_team(HL_ALIENS) // Remove merc status for ( new i = 1; i < U_NUM_IDS; i++ ) { g_userMerc[i] = 0 } } /************************************************************************************** * Ready related functions & forfeitclock **************************************************************************************/ // Command: say ready public cmd_ready(id) { // Authenticate user new pid = func_authenticate(id) // Initialize variables new team = g_userTeam[pid], maxPlayers = get_pcvar_num(g_cvar_teamlimit) new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) // If game has started, requester is spec or team is already ready, skip process if ( g_gameStarted || team == HL_RRSPECS || g_teamReady[team] ) { return PLUGIN_CONTINUE } // If pause is on, dont allow ready if ( g_pauseStatus ) { client_print(id, print_chat, "[ENSL] You cannot ready while game is paused.") return PLUGIN_HANDLED } // Regenerate the player count g_teamPlayers[team] = 0 for ( new i = 0; i < U_NUM_IDS; i++ ) { if ( g_userTeam[i] == team ) { g_teamPlayers[team]++ } } // If teams are limited, check players if ( !ns_is_combat() && maxPlayers != 0 && g_teamPlayers[team] != maxPlayers ) { client_print(id, print_chat, "[ENSL] You can only ready with %d players on your team.", maxPlayers) return PLUGIN_HANDLED } // If mercliming is on... if ( get_pcvar_num(g_cvar_merclimit) ) { // If user is unteamed, abort if ( !g_userEnslTid[pid] ) { client_print(id, print_chat, "[ENSL] Unteamed players are not allowed to ready when merclimiting is enabled.") return PLUGIN_HANDLED } // Register requestor's team as team's choice and announce team registration g_teamENSLId[team] = g_userEnslTid[pid] g_teamENSLName[team] = g_userEnslTeam[pid] client_print(id, print_chat, "[ENSL] You are registered as ^"%s^".", g_teamENSLName[team]) // Loop through all players for ( new iPid = 1; iPid < g_userCount; iPid++ ) { // If the marine / alien has different ENSL team.. if ( g_userStatus[iPid] && g_userTeam[iPid] == team && g_userEnslTid[iPid] != g_teamENSLId[team] && !g_userMerc[iPid] ) { // Fetch the name new iName[HL_LEN_NICK + 1]; get_user_name(g_userIdIndex[iPid], iName, HL_LEN_NICK) // Echo warning and abort client_print(id, print_chat, "[ENSL] Player ^"%s^" isn't on your ENSL roster.", iName) return PLUGIN_HANDLED } } } // Mark team ready g_teamReady[team] = 1 log_message("[ENSL] [ready] [%s:%s] [%s] [%d:%s]", g_userSteamID[pid], name, g_teamNames[team], g_teamENSLId[team], g_teamENSLName[team]) // If both teams are ready... if ( g_teamReady[HL_MARINES] && g_teamReady[HL_ALIENS] ) { // Stop the clock func_stop_ffclock(0) // Set game started g_gameStarted = 1 } // Otherwise start it else { func_start_ffclock() } return PLUGIN_CONTINUE } // Command: not ready public cmd_notready(id) { // Authenticate user and get team new pid = func_authenticate(id) new team = g_userTeam[pid] // Mark team unready if its aliens or marines if ( !g_gameStarted && team > HL_RRSPECS ) { g_teamReady[team] = 0 // Pause the forfeit clock if both teams are unready if ( !g_teamReady[HL_MARINES] && !g_teamReady[HL_ALIENS] ) { func_pause_ffclock() } } return PLUGIN_CONTINUE } // Command: amx_enslclock [mins] [secs] (add extra time to the forfeit clock) public cmd_enslclock(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check if user can access the command and game is not running if ( !func_check_access(id, level, cid, 1) || g_gameStarted || !get_pcvar_num(g_cvar_ffclock) ) { return PLUGIN_HANDLED } // Init variables new secs = 0 new strMins[R_LEN_MINS + 1]; read_argv(1, strMins, R_LEN_MINS); secs += str_to_num(strMins) * 60 new strSecs[R_LEN_MINS + 1]; read_argv(2, strSecs, R_LEN_MINS); secs += str_to_num(strSecs) new timeText[TT_LEN_OUTPUT + 1]; func_format_time(secs+0.0, timeText) new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) // Or add extra time g_FFCUsed -= secs client_print(0, print_chat, "[ENSL] Referee (%s) has %s the forfeit clock by %s", name, secs < 0 ? "decreased" : "increased", timeText) log_message("[ENSL] [enslclock] [%s:%s] [%s]", g_userSteamID[pid], name, timeText) return PLUGIN_HANDLED } // Function: start the forfeit clock // Requirements: 1 team is ready public func_start_ffclock() { // Do not start if the task exists if ( task_exists(T_FFCLOCK) || !get_pcvar_num(g_cvar_ffclock) || g_gameStarted ) { return PLUGIN_HANDLED } // Start the clock g_FFCStarted = get_gametime() set_task(1.0, "task_ffclock", T_FFCLOCK, "", 0, "b") // Print the start message new other = g_teamReady[HL_ALIENS] ? HL_MARINES : HL_ALIENS new timeText[TT_LEN_OUTPUT + 1]; func_format_time(FF_TIME - g_FFCUsed, timeText) client_print(0, print_chat, "[ENSL] Forfeit clock started. %s have %s to ready.", g_teamNames[other], timeText) log_message("[ENSL] [enslclock] [START] [%s] [%s]", timeText, g_teamNames[other]) return PLUGIN_CONTINUE } // Function: pause the forfeit clock public func_pause_ffclock() { // Exit if the task doesn't exist if ( !task_exists(T_FFCLOCK) ) { return PLUGIN_HANDLED } // Save the used forfeit time. If it is zero or negative, give 1min extra g_FFCUsed = floatround(get_gametime() - g_FFCStarted + g_FFCUsed, floatround_floor) g_FFCUsed = g_FFCUsed <= 0 ? floatround(FF_TIME) - 60 : g_FFCUsed remove_task(T_FFCLOCK) // Announce it new timeText[TT_LEN_OUTPUT + 1]; func_format_time(FF_TIME - g_FFCUsed, timeText) client_print(0, print_chat, "[ENSL] Forfeit clock has paused with %s left. Type ready to start it again.", timeText) log_message("[ENSL] [enslclock] [PAUSE] [%s]", timeText) return PLUGIN_CONTINUE } // Function: stop the forfeit clock public func_stop_ffclock(silent) { // If the task exists, stop it if ( task_exists(T_FFCLOCK) ) { remove_task(T_FFCLOCK) } // Reset timers g_FFCStarted = 0.0 g_FFCUsed = 0 // Announce it if not silent stop if ( !silent ) { client_print(0, print_console, "[ENSL] Forfeit clock has been stopped and timers have been reset.") } // Log the event log_message("[ENSL] [enslclock] [STOP]") return PLUGIN_CONTINUE } // Task: forfeit clock public task_ffclock() { new timeText[TT_LEN_OUTPUT + 1] // Calculate the remaining time new Float:remaining = FF_TIME - get_gametime() + g_FFCStarted - g_FFCUsed // If the time is out, announce the forfeit if ( remaining <= 0 ) { // Give the forfeit if ( !g_teamReady[HL_MARINES] ) { client_print(0, print_chat, "Marines (%s) have received the forfeit loss.", g_teamENSLName[1]) } else if ( !g_teamReady[HL_ALIENS] ) { client_print(0, print_chat, "Aliens (%s) have received the forfeit loss.", g_teamENSLName[2]) } // Remove the task, reset variables and quit remove_task(T_FFCLOCK) g_FFCStarted = 0.0 g_FFCUsed = 0 // Run through and give everyone a final snapshot for ( new pid = 1; pid < g_userCount; pid++ ) { // Ignore RR+Specs if ( g_userStatus[pid] && g_userTeam[pid] > 0 ) { func_snapshot(pid, 1) } } return PLUGIN_HANDLED } // Format unready team and timetext new team = !g_teamReady[HL_MARINES] ? HL_MARINES : HL_ALIENS func_format_time(remaining, timeText) // Announce the team lacking players for ( new pid = 1; pid < g_userCount; pid++ ) { // Print it to everyone on the server if ( g_userStatus[pid] ) { set_hudmessage(255, 0, 0, -1.0, 0.9, 0, 0.0, 0.9, 0.0, 0.0) show_hudmessage(g_userIdIndex[pid], "%s have %s to ready.", g_teamNames[team], timeText) } } // Print casual message every 30 seconds if ( floatround(remaining) % 30 == 0 ) { client_print(0, print_chat, "%s have %s to ready.", g_teamNames[team], timeText) } return PLUGIN_CONTINUE } /************************************************************************************** * Combat modes **************************************************************************************/ public func_init_co() { // If its not combat, don't bother if ( !ns_is_combat() ) { return PLUGIN_HANDLED } register_event("WeapPickup", "evolve_lerk", "b", "1=6") // Normal combat g_coNum[CO_NORMAL] = 0 g_coBlocked[CO_NORMAL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // Celeresupply g_coNum[CO_CELERESUPPLY] = 31 g_coBlocked[CO_CELERESUPPLY] = {20, 21, 22, 23, 24, 25, 27, 33, 37, 38, 39, 53, 61, 62, 64, 65, 66, 101, 102, 103, 108, 109, 110, 111, 112, 114, 115, 116, 117, 118, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0} // ENSL combat g_coNum[CO_ENSL] = 5 g_coBlocked[CO_ENSL] = {38, 39, 117, 126, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // SGs vs Fades g_coNum[CO_SGSFADES] = 21 g_coBlocked[CO_SGSFADES] = {65, 66, 38, 39, 27, 53, 33, 126, 114, 115, 117, 101, 102, 103, 108, 109, 110, 111, 112, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // SGs vs Leap Skulks g_coNum[CO_SGSKULKS] = 27 g_coBlocked[CO_SGSKULKS] = {20, 21, 22, 24, 25, 27, 33, 37, 38, 39, 53, 61, 62, 65, 66, 102, 103, 108, 109, 110, 111, 112, 114, 115, 116, 117, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // LMGs vs Cele Lerks g_coNum[CO_LMGLERKS] = 26 g_coBlocked[CO_LMGLERKS] = {21, 22, 27, 33, 37, 38, 39, 53, 61, 62, 64, 65, 66, 101, 102, 103, 108, 109, 110, 111, 112, 114, 116, 117, 118, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} return PLUGIN_CONTINUE } public evolve_lerk(id) { if ( get_pcvar_num(g_cvar_combatmode) == CO_LMGLERKS ) { strip_user_weapons(id); ns_give_item(id, "weapon_bite2gun"); } } public func_cmd_stripweapons(id) { strip_user_weapons(id); ns_give_item(id, "weapon_knife"); return PLUGIN_HANDLED; } /************************************************************************************** * Extra user kicker, slot maker, widow maker, roommaker **************************************************************************************/ // Command: /say /makeroom (kick specs) public cmd_makeroom(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check for too rapid action if ( func_check_cmdtime(pid, CT_GENERAL, print_chat) == PLUGIN_HANDLED ) { return PLUGIN_HANDLED } if ( get_maxplayers() > get_playersnum(1) ) { client_print(id, print_chat, "[ENSL] There is still room. No-one kicked.") return PLUGIN_HANDLED } new playerCount = 0, target = 0, maxTime = 0 new players[HL_NUM_PLAYERS]; get_players(players, playerCount, "h") // Check readyroom first for ( new i = 0; i < playerCount; i++ ) { if ( nst_is_readyroom(players[i]) && (target == 0 || get_user_time(players[i]) < maxTime) ) { target = players[i] maxTime = get_user_time(players[i]) } } // Then go to specs if ( target == 0 ) { for ( new i = 0; i < playerCount; i++ ) { if ( nst_is_spectate(players[i]) && (target == 0 || get_user_time(players[i]) < maxTime) ) { target = players[i] maxTime = get_user_time(players[i]) } } } if ( target == 0 ) { client_print(id, print_chat, "[ENSL] No suitable player found, contact admin.") return PLUGIN_HANDLED } new targetUID = get_user_userid(target) new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new targetName[HL_LEN_NICK + 1]; get_user_name(target, targetName, HL_LEN_NICK) server_cmd("kick #%d ^"Room needed for players.^"", targetUID) client_print(0, print_chat, "[ENSL] Player (%s) kicked spec: %s", name, targetName) log_message("[ENSL] [makeroom] [%s:%s] [%d:%s]", g_userSteamID[g_userPidIndex[id]], name, g_userSteamID[g_userPidIndex[target]], targetName) return PLUGIN_CONTINUE } /************************************************************************************** * Client rate and variable checking functions **************************************************************************************/ // Task: check all players' rates public task_check_rates() { // Fetch players (non-dead, non-hltv, non-bots) new playerCount, players[HL_NUM_PLAYERS]; get_players(players, playerCount, "ch") // Loop through all players for ( new i = 0; i < playerCount; i++ ) { func_check_rates(players[i]) } return PLUGIN_CONTINUE } // Command: check own rates public cmd_check(id) { // Check that user is authenticated new pid = func_authenticate(id) // Check for too rapid action if ( func_check_cmdtime(pid, CT_GENERAL, print_chat) == PLUGIN_HANDLED ) { return PLUGIN_HANDLED } // Set requested ratecheck for ( new i = 0; i < RC_NUM_VARS; i++ ) { g_userRCWarn[pid][i] = RC_WARN_REQUEST } // Inform user client_print(id, print_chat, "[ENSL] Checking your rates...") // Check rates manually func_check_rates(id) return PLUGIN_HANDLED } // Function: check player's rates and variables public func_check_rates(id) { // Check that user is authenticated new pid = func_authenticate(id) // Don't bother with players on HLTV, dead, RR or Spec if ( g_userTeam[pid] == HL_RRSPECS || ns_get_class(id) >= 11 ) { return } // Check for illegal values query_client_cvar(id, "rate", "func_check_rate", 1, {RC_I_RATE}) query_client_cvar(id, "cl_updaterate", "func_check_rate", 1, {RC_I_UPDATERATE}) query_client_cvar(id, "cl_cmdrate", "func_check_rate", 1, {RC_I_CMDRATE}) query_client_cvar(id, "fps_max", "func_check_rate", 1, {RC_I_FPS}) } // Function: check player's rate CVAR public func_check_rate(id, const cvar[], const value[], const args[]) { new tMin = 0, tMax = 500, pid = g_userPidIndex[id], nValue = str_to_num(value), pValue = g_userRCVars[pid][args[0]] // Get min and max values switch ( args[0] ) { case RC_I_RATE: { tMin = get_pcvar_num(g_cvar_minrate) tMax = get_pcvar_num(g_cvar_maxrate) } case RC_I_UPDATERATE: { tMin = get_pcvar_num(g_cvar_minupdaterate) tMax = get_pcvar_num(g_cvar_maxupdaterate) } case RC_I_CMDRATE: { tMin = get_pcvar_num(g_cvar_mincmdrate) tMax = get_pcvar_num(g_cvar_maxcmdrate) } case RC_I_FPS: { tMin = get_pcvar_num(g_cvar_minfps) tMax = get_pcvar_num(g_cvar_maxfps) // TODO: check marine team // Remove jetpack if ( tMax < str_to_num(value) && pev(id, pev_team) == 1 && is_user_alive(id) && tMax != 0 ) { ns_set_mask(id, MASK_JETPACK, 0) } } } // If the value is out of range... if ( nValue < tMin || nValue > tMax ) { new range[RC_LEN_RANGE + 1]; formatex(range, RC_LEN_RANGE, "%d - %d", tMin, tMax) func_illegal_value(pid, cvar, nValue, range, args[0]) } // If the value has been changed... else if ( nValue != pValue && pValue != 0 && g_gameStarted && get_pcvar_num(g_cvar_checkrates) == RC_ENABLED_KICK ) { func_illegal_change(pid, cvar, pValue, nValue) } // Save the value g_userRCVars[pid][args[0]] = nValue } // Function: Action when illegal rate/var value is found public func_illegal_value(pid, const cvar[], const value, const range[RC_LEN_RANGE + 1], index) { // Get playername and userID new name[HL_LEN_NICK + 1]; get_user_name(g_userIdIndex[pid], name, HL_LEN_NICK) new rc = get_pcvar_num(g_cvar_checkrates), userId = get_user_userid(g_userIdIndex[pid]) // Kick if necessary if ( rc == RC_ENABLED_KICK && g_gameStarted ) { client_print(0, print_chat, "[ENSL] Player (%s) was kicked for illegal %s value %d. Correct: %s.", name, cvar, value, range) server_cmd("kick #%d ^"[ENSL] CVAR violation (%s set to %d). Correct: %s^"", userId, cvar, value, range) log_message("[ENSL] [ratecheck] [%s:%s] [ILLEGAL VALUE] [%s=%d] [KICKED]", g_userSteamID[pid], name, cvar, value) } // Global warning if necessary else if ( (rc == RC_ENABLED_WARN || rc == RC_ENABLED_KICK) && (g_userRCWarn[pid][index] == RC_WARN_NEGATIVE || g_userRCWarn[pid][index] == RC_WARN_REQUEST || g_userRCVars[pid][index] != value) ) { client_print(0, print_chat, "[ENSL] Player (%s) has illegally %s set to %d. Correct: %s.", name, cvar, value, range) log_message("[ENSL] [ratecheck] [%s:%s] [ILLEGAL VALUE] [%s=%d]", g_userSteamID[pid], name, cvar, value) g_userRCWarn[pid][index] = RC_WARN_POSITIVE } // Personal warning if necessary else if ( g_userRCWarn[pid][index] == RC_WARN_REQUEST ) { client_print(g_userIdIndex[pid], print_chat, "[ENSL] Illegal CVAR value (%s set to %d) / Correct: %s", cvar, value, range) g_userRCWarn[pid][index] = RC_WARN_POSITIVE } } // Function: Action when rate changed public func_illegal_change(pid, const cvar[], const oldValue, const newValue) { // Get playername and userID new name[HL_LEN_NICK + 1]; get_user_name(g_userIdIndex[pid], name, HL_LEN_NICK) new userId = get_user_userid(g_userIdIndex[pid]) // Perform the kick client_print(0, print_chat, "[ENSL] Player (%s) kicked for changing %s from %d to %d.", name, cvar, oldValue, newValue) server_cmd("kick #%d ^"[ENSL] CVAR (%s) changed in-game.^"", userId, cvar) log_message("[ENSL] [ratecheck] [%s:%s] [ILLEGAL CHANGE] [%s] [%d->%d] [KICKED]", g_userSteamID[pid], name, cvar, oldValue, newValue) } /************************************************************************************** * Snapshot functions **************************************************************************************/ // Command: amx_enslshot [name/ID] (force snapshot) public cmd_enslshot(id, level, cid) { // Authenticate user new pid = func_authenticate(id) // Check if user can access the command if ( !func_check_access(id, level, cid, 0) ) { return PLUGIN_HANDLED } // Get referee name + fetch arguments new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) new target[HL_LEN_NICK + 1]; read_argv(1, target, HL_LEN_NICK) new targetID = read_argc() > 1 ? func_get_player(id, target) : g_refSSUser // If user was searched and the ID is not found, abort if ( read_argc() > 1 && targetID == -1 ) { return PLUGIN_HANDLED } // If random was requested but not yet set it else if ( read_argc() == 1 && g_refSSUser == -1 ) { // Find the first user on marines/aliens to get the snapshot for ( new i = 1; i < U_NUM_IDS; i++ ) { if ( g_userStatus[i] && g_userTeam[i] > HL_RRSPECS ) { g_refSSUser = i g_userSSRand[i]++ break } } // Say if a a proper player cannot be found if ( g_refSSUser == -1 ) { client_print(pid, print_chat, "[ENSL] Cannot find any aliens/marines.") client_print(pid, print_console, "Cannot find any aliens/marines.") } // Otherwise print it else { new targetName[HL_LEN_NICK + 1]; get_user_name(g_userIdIndex[g_refSSUser], targetName, HL_LEN_NICK) client_print(pid, print_chat, "[ENSL] Next random snapshot goes to: %s", targetName) client_print(pid, print_console, "Next random snapshot goes to: %s", targetName) } return PLUGIN_HANDLED } // Fetch target's pid & name new targetPID = func_authenticate(targetID) new targetName[HL_LEN_NICK + 1]; get_user_name(targetID, targetName, HL_LEN_NICK) // Take snaphsot func_snapshot(targetPID, 0) client_print(targetID, print_chat, "[ENSL] Referee enforced snapshot was taken.") // If random snapshot was taken if ( read_argc() == 1 ) { new lowestUser = -1, lowestLevel = -1 // Find the user with lowest number of random ref shots for ( new i = 0; i < U_NUM_IDS; i++ ) { // If player is in game and on marines/aliens if ( g_userStatus[i] && g_userTeam[i] > HL_RRSPECS ) { // Check if user's count is lower or equal to the lowest count if ( g_userSSRand[i] < lowestLevel || lowestLevel == -1 ) { lowestUser = i lowestLevel = g_userSSRand[i] } } } // If the lowest user was found, set the player as the next victim if ( lowestUser != -1 ) { g_refSSUser = lowestUser g_userSSRand[lowestUser]++ // Print it new targetName[HL_LEN_NICK + 1]; get_user_name(g_userIdIndex[g_refSSUser], targetName, HL_LEN_NICK) client_print(pid, print_chat, "[ENSL] Next random snapshot goes to: %s", targetName) client_print(pid, print_console, "Next random snapshot goes to: %s", targetName) } // Otherwise print it else { g_refSSUser = -1 client_print(pid, print_chat, "[ENSL] Cannot find any aliens/marines.") client_print(pid, print_console, "Cannot find any aliens/marines.") } } return PLUGIN_HANDLED } // Function: check if user is next on line for random snapshot public func_random_snap(pid) { // Check if user won the snapshot prize if ( random_num(1, 3 - g_userSSPassed[pid] + g_userSSCount[pid] * 4) == 1 ) { return true } // Otherwise just increase the passed death counter g_userSSPassed[pid]++ return false } // Take a snapshot public func_snapshot(pid, extra) { // Initialize variables new msg[2][HL_LEN_HUDMESSAGE + 1], num = 0 new watermark[] = "`xxxxxxxxx``xx````xx```xxxxxxx``xx```````^n`xx`````````xxx```xx``xx````````xx```````^n`xx`````````xx`x``xx``xx````````xx```````^n`xxxxxx`````xx`x``xx``xxxxxxx```xx```````^n`xx`````````xx``x`xx````````xx``xx```````^n`xx`````````xx``x`xx````````xx``xx```````^n`xxxxxxxxx``xx````xx``xxxxxxx```xxxxxxxx`" new name[HL_LEN_NICK + 1]; get_user_name(g_userIdIndex[pid], name, HL_LEN_NICK) new strTime[18]; format_time(strTime, 17, "%d.%m.%y %H:%M", func_get_time()) // Increase the shot counter g_userSSCount[pid]++ // If its endsnapshot, print count if ( extra ) { // Generate teammate info for ( new i = 0; i < U_NUM_IDS && num < 6; i++ ) { // If user is on server and in the same aetm if ( g_userStatus[i] && g_userTeam[i] == g_userTeam[pid] ) { // Generate name and cast integers to string new iName[HL_LEN_NICK + 1]; get_user_name(g_userIdIndex[i], iName, HL_LEN_NICK) new sId[E_LEN_IDS + 1]; num_to_str(g_userEnslId[i], sId, E_LEN_IDS) new sTid[E_LEN_IDS + 1]; num_to_str(g_userEnslTid[i], sTid, E_LEN_IDS) // Insert values add(msg[0], HL_LEN_HUDMESSAGE, iName, 15) add(msg[0], HL_LEN_HUDMESSAGE, " ", 2) add(msg[0], HL_LEN_HUDMESSAGE, g_userSteamID[i], 15) add(msg[0], HL_LEN_HUDMESSAGE, " ", 2) add(msg[0], HL_LEN_HUDMESSAGE, g_userIP[i], HL_LEN_IP) add(msg[0], HL_LEN_HUDMESSAGE, "^n", 1) add(msg[0], HL_LEN_HUDMESSAGE, g_userEnslNick[i], 15) add(msg[0], HL_LEN_HUDMESSAGE, " (", 3) add(msg[0], HL_LEN_HUDMESSAGE, sId, E_LEN_IDS) add(msg[0], HL_LEN_HUDMESSAGE, "/", 1) add(msg[0], HL_LEN_HUDMESSAGE, sTid, E_LEN_IDS) add(msg[0], HL_LEN_HUDMESSAGE, ") ", 3) add(msg[0], HL_LEN_HUDMESSAGE, g_userCode[i], CC_LEN_CODE) add(msg[0], HL_LEN_HUDMESSAGE, "^n^n") num++ } } // Print snapshot count in the last shot client_print(g_userIdIndex[pid], print_chat, "[ENSL] %d snapshots were taken.", g_userSSCount[pid]) } // Otherwise just input the specific player else { // Cast integers ot string new sId[E_LEN_IDS + 1]; num_to_str(g_userEnslId[pid], sId, E_LEN_IDS) new sTid[E_LEN_IDS + 1]; num_to_str(g_userEnslTid[pid], sTid, E_LEN_IDS) // Insert values add(msg[0], HL_LEN_HUDMESSAGE, name, 15) add(msg[0], HL_LEN_HUDMESSAGE, " ", 2) add(msg[0], HL_LEN_HUDMESSAGE, g_userSteamID[pid], 15) add(msg[0], HL_LEN_HUDMESSAGE, " ", 2) add(msg[0], HL_LEN_HUDMESSAGE, g_userIP[pid], HL_LEN_IP) add(msg[0], HL_LEN_HUDMESSAGE, "^n", 1) add(msg[0], HL_LEN_HUDMESSAGE, g_userEnslNick[pid], 15) add(msg[0], HL_LEN_HUDMESSAGE, " (", 3) add(msg[0], HL_LEN_HUDMESSAGE, sId, E_LEN_IDS) add(msg[0], HL_LEN_HUDMESSAGE, "/", 1) add(msg[0], HL_LEN_HUDMESSAGE, sTid, E_LEN_IDS) add(msg[0], HL_LEN_HUDMESSAGE, ") ", 3) add(msg[0], HL_LEN_HUDMESSAGE, g_userCode[pid], CC_LEN_CODE) add(msg[0], HL_LEN_HUDMESSAGE, "^n^n") } // Print teammate info set_hudmessage(50, 50, 50, -1.0, 0.05, 0, 0.0, 2.0, 0.0, 0.2, 1) show_hudmessage(g_userIdIndex[pid], msg[0]) // Print the hud message including serverside info (nick, ip etc.) + gametimes set_hudmessage(120, 0, 0, -1.0, 0.6, 0, 0.0, 2.0, 0.0, 0.2, 4) formatex(msg[1], HL_LEN_HUDMESSAGE, "^n%s^n%s^n%s^nSnapshot #%d^n^n%s", name, g_userSteamID[pid], strTime, g_userSSCount[pid], watermark) show_hudmessage(g_userIdIndex[pid], msg[1]) // Take the actual snapshot client_cmd(g_userIdIndex[pid], "wait; wait; snapshot") // Log the event log_message("[ENSL] [snapshot] [%s:%s]", g_userSteamID[pid], name) } /************************************************************************************** * Authentication / Player tracking functions **************************************************************************************/ // Function: init server pid public func_init_pid() { // Set appropiate values g_userIndex = 1 g_userCount = 1 g_userAuthed[0] = 1 g_userIdIndex[0] = 0 g_userPidIndex[0] = 0 g_userStatus[0] = 0 g_userSteamID[0] = "SERVER" g_userIP[0] = "127.0.0.1" g_userTeam[0] = HL_RRSPECS } // Function: authenticate a player public func_authenticate(id) { // Do not dbl authenticate if ( g_userAuthed[id] ) { new tempsteamid[HL_LEN_STEAMID]; get_user_authid(id, tempsteamid, HL_LEN_STEAMID); replace(tempsteamid, HL_LEN_STEAMID, "STEAM_", ""); new temppid = g_userPidIndex[id]; if (!equal(tempsteamid, g_userSteamID[temppid])) { log_message("[ENSL ERROR CHECK] [func_authenticate fail] [%s:%s]", tempsteamid, g_userSteamID[temppid]); } return g_userPidIndex[id] } // Initialize variables new pid = -1, steamid[HL_LEN_STEAMID + 1]; get_user_authid(id, steamid, HL_LEN_STEAMID) // Remove prefix from steam ID replace(steamid, HL_LEN_STEAMID, "STEAM_", "") // Search for an existing history entry for ( new i = 0; i < U_NUM_IDS; i++ ) { // Find a match if ( equal(steamid, g_userSteamID[i]) ) { pid = i; } } // If the user is not found, create a new entry if ( pid == -1 ) { // If history is full.. if ( g_userCount == U_NUM_IDS ) { // Search for the first entry of a player not on server.. for ( new i = 1; i < U_NUM_IDS; i++ ) { // Exclude players who are playing if ( !g_userStatus[i] ) { // Use it and reset variables so old data won't be used // NOTE: No need to format variables twice g_userIndex = i g_userName[i] = "^0" g_userSteamID[i] = "^0" g_userIP[id] = "^0" g_userName[i] = "^0" g_userEnslInfo[i] = 0 g_userEnslNick[i] = "^0" g_userEnslTeam[i] = "^0" g_userEnslIP[i] = "^0" g_userEnslId[i] = 0 g_userEnslTid[i] = 0 g_userEnslLevel[i] = "^0" g_userEnslGather[i] = 0 g_userEnslRank[i] = "^0" g_userCode[i] = "^0" g_userSSPassed[i] = 0 g_userSSCount[i] = 0 g_userSSRand[i] = 0 g_userVoice[i] = 0 g_userGagged[i] = 0 g_userDDTime[i] = 0 g_userMerc[i] = 0 g_userWebHex[i] = "^0" g_userWebReads[i] = 0 g_userWebSocket[i] = 0 // Reset rates for ( new n = 0; n < RC_NUM_VARS; n++ ) { g_userRCVars[i][n] = 0 g_userRCWarn[i][n] = 0 } break } } } // Use the marked entry, save it pid = g_userIndex if (pid >= U_NUM_IDS) { log_message("[ENSL ERROR CHECK] [pid overflow] [%d]", pid); } copy(g_userSteamID[pid], HL_LEN_STEAMID, steamid) // Advance the index and the counter g_userCount = ( g_userCount < U_NUM_IDS ) ? g_userCount + 1 : U_NUM_IDS g_userIndex++ } // Save user's information g_userPidIndex[id] = pid g_userAuthed[id] = 1 g_userIdIndex[pid] = id g_userStatus[pid] = 1 // Save IP get_user_ip(id, g_userIP[pid], HL_LEN_IP, 1) // Reset icon data g_userIconFlags[id] = 0 g_userIconIndex[id] = -1 // Fetch player info when id checking is enabled if ( get_pcvar_num(g_cvar_checkids) ) { // Empty existing data if necessary if ( g_userEnslInfo[pid] || equal(g_userEnslNick[pid], "") ) { g_userEnslNick[pid] = "^0" g_userEnslIP[pid] = "^0" g_userEnslTeam[pid] = "^0" g_userEnslId[pid] = 0 g_userEnslTid[pid] = 0 g_userEnslLevel[pid] = "^0" g_userEnslGather[pid] = 0 g_userEnslRank[pid] = "^0" } // Fetch ensl info func_get_enslinfo(pid) } // Log the event new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) log_message("[ENSL] [authenticate] [%s:%s]", g_userSteamID[pid], name) return pid } // Function: check if user has access to this command public func_check_access(id, level, cid, params) { // Get user's pid new pid = func_authenticate(id) // Check param count if ( read_argc() - 1 < params ) { new cmd[HL_LEN_CMD + 1], info[HL_LEN_INFO + 1], cflags get_clcmd(cid, cmd, HL_LEN_CMD, cflags, info, HL_LEN_INFO, level) console_print(id, "Wrong parameter count. Usage: %s %s", cmd, info) return false } // Check user's access if ( (level == ADMIN_LEVEL_E && !func_is_referee(pid)) && !(get_user_flags(id) & level) ) { console_print(id, "You don't have access to that command") return false } // Check if ref commands are allowed if ( level == ADMIN_LEVEL_E && !get_pcvar_num(g_cvar_refaccess) ) { console_print(id, "Referee admin access is disabled by server") return false } return true } // Function: check if player is referee public func_is_referee(pid) { // Ref or admin is okay if ( equal(g_userEnslLevel[pid], E_LEVEL_REFEREE, 1) || equal(g_userEnslLevel[pid], E_LEVEL_ADMIN, 1) ) { return true } return false } /************************************************************************************** * ENSL web query handling functions **************************************************************************************/ // Function: Fetches ENSL Server information public func_get_esi() { // Initialize variables func_get_randhex(W_LEN_CH, g_webHex[W_I_ESI]) new url[W_LEN_URL + 1]; formatex(url, W_LEN_URL, "%s?ch=%s", S_URL, g_webHex[W_I_ESI]) // Write web and start the reading function new webquery = get_pcvar_num(g_cvar_webquery) if ( webquery != 0 ) { func_write_web(g_webSocket[W_I_ESI], url) set_task(0.5, "task_web_esi", T_ESI) } return PLUGIN_CONTINUE } // Function: Reads ENSL Server information from web public task_web_esi() { // Increase reads g_webReads[W_I_ESI]++ // If there's data in the socket, read it. if ( socket_change(g_webSocket[W_I_ESI], W_TIMEOUT) ) { func_read_web(g_webSocket[W_I_ESI], S_MARK, S_NUM_LINES, g_webData[W_I_ESI], g_webDataLine[W_I_ESI]) } // If socket hasn't finished, retry if ( g_webDataLine[W_I_ESI] != S_NUM_LINES && g_webReads[W_I_ESI] < W_READS ) { set_task(0.5, "task_web_esi", T_ESI) return } // Finally close the socket and reset vars socket_close(g_webSocket[W_I_ESI]) g_webReads[W_I_ESI] = 0 // Exit if there is no data if ( g_webDataLine[W_I_ESI] == 0 ) { return } // Set time g_timeFetchedRemote = str_to_num(g_webData[W_I_ESI][1]) g_timeFetchedLocal = get_gametime() // Set ENSL Plugin version if ( strcmp(VERSION, g_webData[W_I_ESI][2]) != 0 ) { client_print(0, print_chat, "[ENSL] Plugin is outdated, please update.") } // Reset data g_webDataLine[W_I_ESI] = 0 for ( new i = 0; i < W_NUM_LINES; i++ ) { g_webData[W_I_ESI][i] = "^0" } } // Function: Fetches playerinfo from ENSL DB public func_get_enslinfo(pid) { // If task exists, abort if ( task_exists(T_USER + pid) ) { return } // Set task for data send new args[1]; args[0] = pid new webquery = get_pcvar_num(g_cvar_webquery) if ( webquery != 0 ) { set_task(0.1, "task_send_enslinfo", T_USER + pid, args, 1) } } // Task: Open and send socket data to ENSL DB public task_send_enslinfo(args[]) { new pid = args[0], url[W_LEN_URL + 1] // Set URL and webhex func_get_randhex(W_LEN_CH, g_userWebHex[pid]) formatex(url, W_LEN_URL, "%s%s?ch=%s", E_URL, g_userSteamID[pid], g_userWebHex[pid]) // log_message("[DEBUG] url: %s", url) // Open socket and write to the web func_write_web(g_userWebSocket[pid], url) // Start reading function set_task(0.5, "task_web_enslinfo", T_USER + pid, args, 1) } // Task: Attempt to read playerdata from ENSL DB public task_web_enslinfo(args[]) { new pid = args[0] // Increase reads g_userWebReads[pid]++ // If there's data in the socket, read it. if ( socket_change(g_userWebSocket[pid], W_TIMEOUT) ) { // log_message("[DEBUG] reading data...") func_read_web(g_userWebSocket[pid], E_MARK, E_NUM_LINES, g_userWebData[pid], g_userWebDataLine[pid]) } // If socket hasn't finished, retry if ( g_userWebDataLine[pid] != E_NUM_LINES && g_userWebReads[pid] < W_READS ) { // log_message("[DEBUG] waiting for data...") set_task(0.5, "task_web_enslinfo", T_USER + pid, args, 1) return } // Finally close the socket and reset vars socket_close(g_userWebSocket[pid]) g_userWebReads[pid] = 0 // Exit if there is no data if ( g_userWebDataLine[pid] == 0 ) { return } // Check if user is banned if ( contain(g_userWebData[pid][0], "BANNED") != -1 ) { // Ban user + call disconnect manually since its not called for some reason new uid = get_user_userid(g_userIdIndex[pid]) new mins = (str_to_num(g_userWebData[pid][1]) - func_get_time()) / 60 log_message("user %s banned for %d mins (time: %d, %s)", g_userSteamID[pid], mins, func_get_time(), g_userWebData[pid][1]) server_cmd("banid %d STEAM_%s; wait; kick #%d ^"Banned: %.20s^"", mins, g_userSteamID[pid], uid, g_userWebData[pid][2]) client_disconnect(g_userIdIndex[pid]) // Reset data g_userWebDataLine[pid] = 0 for ( new i = 0; i < E_NUM_LINES; i++ ) { g_userWebData[pid][i] = "^0" } return } // Set data.. copy(g_userEnslNick[pid], E_LEN_NICK, g_userWebData[pid][2]) copy(g_userEnslIP[pid], E_LEN_IP, g_userWebData[pid][3]) copy(g_userEnslTeam[pid], E_LEN_TEAM, g_userWebData[pid][4]) g_userEnslId[pid] = str_to_num(g_userWebData[pid][5]) g_userEnslTid[pid] = str_to_num(g_userWebData[pid][6]) copy(g_userEnslLevel[pid], E_LEN_LEVEL, g_userWebData[pid][7]) copy(g_userEnslRank[pid], E_LEN_RANK, g_userWebData[pid][8]) g_userIconFlags[g_userIdIndex[pid]] = str_to_num(g_userWebData[pid][9]) g_userEnslGather[g_userIdIndex[pid]] = str_to_num(g_userWebData[pid][10]) g_userEnslInfo[pid] = 1 // Enable icon g_userIconIndex[g_userIdIndex[pid]] = -1 func_next_icon(g_userIdIndex[pid]) func_apply_icon(g_userIdIndex[pid]) // Calculate verification code new challenge[CC_LEN_INPUT + 1] new sId[E_LEN_IDS + 1]; num_to_str(g_userEnslId[pid], sId, E_LEN_IDS) new sTid[E_LEN_IDS + 1]; num_to_str(g_userEnslTid[pid], sTid, E_LEN_IDS) add(challenge, CC_LEN_INPUT, g_userSteamID[pid], HL_LEN_STEAMID) add(challenge, CC_LEN_INPUT, g_userIP[pid], HL_LEN_IP) add(challenge, CC_LEN_INPUT, sId, E_LEN_IDS) add(challenge, CC_LEN_INPUT, sTid, E_LEN_IDS) func_crypt(challenge, g_userCode[pid]) // Reset data g_userWebDataLine[pid] = 0 for ( new i = 0; i < E_NUM_LINES; i++ ) { g_userWebData[pid][i] = "^0" } } /************************************************************************************** * Web queyring functions **************************************************************************************/ // Function: write to web public func_write_web(&websocket, url[W_LEN_URL + 1]) { // Open HTTP socket connection new error; websocket = socket_open(W_IP, W_PORT, SOCKET_TCP, error) // Process any errors if ( websocket <= 0 ) { // Log it switch ( error ) { case 1: { log_message("[ENSL] Error creating socket"); } case 2: { log_message("[ENSL] Error resolving remote hostname"); } case 3: { log_message("[ENSL] Error connecting socket"); } } // Print to players client_print(0, print_chat, "[ENSL] Unable to connect to ENSL database") } // Write data new output[W_LEN_QUERY + 1]; formatex(output, W_LEN_QUERY, "GET %s HTTP/1.1^r^n", url) socket_send(websocket, output, W_LEN_INPUT) output = "^0"; formatex(output, W_LEN_QUERY, "Host: %s^r^n^r^n", W_HOST) socket_send(websocket, output, W_LEN_INPUT) } // Function: read from web public func_read_web(&websocket, mark[W_LEN_MARK + 1], lines, data[][W_LEN_LINE + 1], &pointer) { // Receive the HTTP data new input[W_LEN_INPUT + 1], numChars = socket_recv(websocket, input, W_LEN_INPUT) if ( numChars == 0 ) { return 0 } // Go through all lines new line[W_LEN_LINE + 1], i = 0, numCopied = 0, found = 0; // If no data has been fetched before, find the start mark if ( pointer == 0 ) { // Search until end of data while ( numCopied < numChars ) { // Copy the line to a variable.Increase copy char ocunter + 1 (newline) line = "^0"; numCopied += copyc(line, W_LEN_LINE, input[numCopied], W_NEWLINE) + 1 // If the matching line is found, stop here if ( strfind(line, mark) != -1 ) { found = 1 break } } // Exit if the marker was not found if ( found == 0 ) { return 0 } } // Get data line by line for ( i = pointer; i < W_NUM_LINES && i < lines && numCopied < numChars; i++ ) { numCopied += copyc(data[i], W_LEN_LINE, input[numCopied], W_NEWLINE) + 1 } // Save the data line pointer and return number of copied lines new numLines = i - pointer; pointer = i return numLines } /************************************************************************************** * Waypoint fix by Asmodee **************************************************************************************/ public func_init_waypoints() { for ( new i = 0; i <= g_maxPlayers; i++ ) { g_userOrderType[i] = 0; } } public func_msg_waypoint() { new id = get_msg_arg_int(1); g_userOrderType[id] = get_msg_arg_int(2); return PLUGIN_CONTINUE; } public cmd_clearwp(id) { if ( pev(id, pev_team) == 1 ) { new type = g_userOrderType[id]; if ( type ) { message_begin(MSG_ONE, g_msgId_Waypoint, _, id); write_byte(id); write_byte(type); if ( (type == 5) || (type == 3) ) { write_short(0); } else { write_coord(0); write_coord(0); write_coord(0); } write_byte(0); write_byte(0); write_byte(1); message_end(); g_userOrderType[id] = 0; } } return PLUGIN_HANDLED; } /************************************************************************************** * Teamjoining limit for ENSL members **************************************************************************************/ public func_check_membersonly(id, pid) { if ( get_pcvar_num(g_cvar_membersonly) == 1 && g_userEnslId[pid] == 0 && g_userEnslGather[pid] > 0 ) { client_print(id, print_center, "^n^n^n^n^n^n Only registered ENSL members who are in THIS gather can play. ^n^n Plese register at www.ensl.org. ^n^n ..and join at www.ensl.org/gather") return 0 } return 1 } /************************************************************************************** * Floating gun fix **************************************************************************************/ public func_msg_death() { new victim = get_msg_arg_int(2); if ( pev(victim, pev_team) == 1 ) { func_fix_guns(victim); } } public func_fix_guns(id) { new Float:oPlayer[3]; pev(id, pev_origin, oPlayer); new classname[32]; new entid = -1; while ((entid = EF_FindEntityInSphere(entid, oPlayer, 128.0)) > 0) { pev(entid, pev_classname, classname, 31); if (equal(classname, "weapon_shotgun")|| equal(classname, "weapon_welder") || equal(classname, "weapon_heavymachinegun") || equal(classname, "weapon_mine") || equal(classname, "weapon_grenadegun")) { EF_DropToFloor(entid); } } } /************************************************************************************** * Helper functions **************************************************************************************/ // Function: color message stock func_color_msg(id, msg[]) { new name[HL_LEN_NICK + 1]; get_user_name(id, name, HL_LEN_NICK) // If user is referee, make comments red if ( func_is_referee(g_userPidIndex[id]) ) { format(msg, HL_LEN_SAY, "^x04%s: ^x01%s", name, msg) } // Send it to all new playerCount, player, players[HL_NUM_PLAYERS]; get_players(players, playerCount) for ( new i = 0; i < playerCount; i++ ) { player = players[i] message_begin(MSG_ALL, g_msgId_SayText, {0,0,0}, player) write_byte(player) write_string(msg) message_end() } } // Function: check that command isn't used too quickly public func_check_cmdtime(pid, cmdId, type) { // Get elapsed time since last slow ENSL cmd new Float:elapsed = get_gametime() - g_userCmdTime[pid][cmdId] // If the user is trying to use the command too often, abort if ( elapsed < G_FREQ ) { // Print message when appropiate if ( type != 0 && !g_userCmdWarn[pid][cmdId] ) { g_userCmdWarn[pid][cmdId] = 1 client_print(g_userIdIndex[pid], type, "[ENSL] Please wait %.1fs before using this command.", G_FREQ - elapsed) } return PLUGIN_HANDLED } // Set the last time command used g_userCmdTime[pid][cmdId] = get_gametime() g_userCmdWarn[pid][cmdId] = 0 return PLUGIN_CONTINUE } // Print a table of data stock func_gentable(numColumn, lenColumn, data[HL_NUM_COLUMN][HL_LEN_COLUMN + 1], output[HL_LEN_MSG + 1]) { // Go through all columns for ( new i = 0; i < numColumn; i++ ) { // Calculate number of space needed new space[HL_LEN_COLUMN + 1], numSpace = lenColumn - strlen(data[i]) // Add the space for ( new n = 0; n < numSpace; n++ ) { add(space, lenColumn, " ") } // Join the data and space add(output, HL_LEN_MSG, data[i]) add(output, HL_LEN_MSG, space) } } // Function: format time: eg. 2 minutes 10 seconds stock func_format_time(Float:inSecs, timeText[TT_LEN_OUTPUT + 1]) { // Calculate result. Minutes have to be floored obviously new Float:fMins = floatdiv(floatabs(inSecs), 60.0) new outMins = floatround(fMins, floatround_floor) new outSecs = floatround(floatfract(fMins) * 60, floatround_round) // Change 1min 60 secs -> 2mins if ( outSecs == 60 ) { outMins++ outSecs = 0 } // Print different text if seconds is zero if ( outSecs == 0 ) { formatex(timeText, TT_LEN_OUTPUT, "%d mins", outMins) } else if ( outMins == 0 ) { formatex(timeText, TT_LEN_OUTPUT, "%d secs", outSecs) } else { formatex(timeText, TT_LEN_OUTPUT, "%d mins and %d secs", outMins, outSecs) } return PLUGIN_CONTINUE } // Function: print confirmation code stock func_crypt(input[CC_LEN_INPUT + 1], output[CC_LEN_CODE + 1]) { // Init variables new chars[6] = { 'A', 'B', 'C', 'D', 'E', 'F'}, nums[10], curChar[2], curPos = 0, curNum = 0 new md5input[HL_LEN_MSG + 1]; formatex(md5input, HL_LEN_MSG, "%s%s%s", CC_PASSCODE1, input, CC_PASSCODE2) new sum[CC_LEN_MD5 + 1]; md5(md5input, sum); strtoupper(sum) new lastChar[2]; copy(lastChar, 1, sum[31]) new lastPos = str_to_num(lastChar) // Puzl it for ( new i = 0; i < CC_LEN_CODE; i++ ) { // Get the current character and position out of it copy(curChar, 1, sum[i]) curPos = str_to_num(curChar) // Edit the position depending on the value if ( curPos == 0 ) { curPos = power(lastPos, i % 4) } else if ( (curPos % 4) == 0 ) { curPos = curPos * lastPos + i } else if ( (curPos % 3) == 0 ) { curPos = power(curPos, i % 4) } else if ( (curPos % 2) == 0 ) { curPos = curPos * i + curPos } // Decrease the position if it is over the top if ( curPos > 31 ) { curPos = curPos % 32 } // Save the character by position copy(curChar, 1, sum[CC_LEN_MD5 - 2 - curPos]) curNum = str_to_num(curChar) // If the code has been used before, mix it more if ( nums[curNum] > 0 ) { // If it is letter or zero if ( curNum == 0 ) { copy(curChar, 1, chars[curPos % 6]) curNum = str_to_num(curChar) } // Or if it is a number else { num_to_str(curPos % 10, curChar, 1) curNum = str_to_num(curChar) } } // Save the final result nums[curNum]++ add(output, CC_LEN_CODE, curChar, 1) copy(lastChar, 1, curChar) lastPos = curPos } return PLUGIN_CONTINUE } // Function: generate a random hex value stock func_get_randhex(length, output[]) { static hex[6][2] = { "A", "B", "C", "D", "E", "F" } for ( new i = 0; i < length; i++ ) { new num = random(15), str[2] // Add character value if ( num > 9 ) { add(output, length, hex[num - 10], 1) } // Or number value else { num_to_str(num, str, 1) add(output, length, str, 1) } } } // Function: search for a player by username / ID stock func_get_player(id, target[HL_LEN_NICK + 1]) { new targetID = -1 // Check if hash can be found -> ID search if ( strfind(target, "#", 0, 0) != -1 ) { // Get numerical value targetID = str_to_num(target[1]) // Check player ID if ( !is_user_connected(targetID) ) { console_print(id, "Unable to find username by user ID: %d", targetID) return -1 } } // Otherwise its a string search for playernames else { // Fetch players (non-dead, non-hltv, non-bots) new playerCount, players[HL_NUM_PLAYERS], targetName[HL_LEN_NICK + 1] get_players(players, playerCount) // Loop through all players. // NOTE: ID list also includes non-authed players for ( new i = 0; i < playerCount; i++ ) { // Fetch playername targetName = "^0"; get_user_name(players[i], targetName, HL_LEN_NICK) // If it matches... if ( strfind(targetName, target) != -1 && players[i] != 0 ) { // If the string is in 2+ players' name if ( targetID != -1 ) { console_print(id, "Two users match, aborting.") return -1 } // Otherwise just save it else { targetID = players[i] } } } // If not found, announce it if ( targetID == -1 ) { console_print(id, "Unable to find user by username: %s", target) } } return targetID } // Function: check team stock func_get_team(id, input[HL_LEN_TEAM + 1]) { new intTarget = str_to_num(input) // Check if it is a valid team if ( intTarget != 1 && intTarget != 2 ) { console_print(id, "Invalid team. Please enter: 1=marines or 2=aliens") return -1 } return intTarget } // Function: parse arguments stock func_parse_string(string[], len) { replace_all(string, len, ";", "") replace_all(string, len, "^2", "") replace_all(string, len, "'", "") return true } // Function: URL encode a string stock func_urlencode(input[], output[], outLen) { new c, inLen = strlen(input), letter1, letter2, hex[3] for ( new i = 0; i < inLen; i++ ) { c = input[i] // Find non-alphanumeric characters, and replace them with %ASCIIHEX if ( c < 48 || (c > 57 && c < 65) || (c > 90 && c < 97) || c > 122 ) { letter1 = c/16; letter2 = c % 16 hex[0] = (letter1 < 10) ? letter1 + 48 : letter1 + 55 hex[1] = (letter2 < 10) ? letter2 + 48 : letter2 + 55 add(output, outLen, "%", 1) add(output, outLen, hex, 2) } // Add other characters normally else { add(output, outLen, input[i], 1) } } } // Function: get most precise time stock func_get_time() { // If ENSL time has been fetched, use it if ( g_timeFetchedRemote > 0 ) { return floatround(g_timeFetchedRemote + (get_gametime() - g_timeFetchedLocal), floatround_ceil) } // Otherwise use unreliable system time return get_systime() } // Convert decimal to hex stock func_dec_to_hex(dec, hex[3]) { static l hex[0] = (letter1 < 10) ? letter1+48 : letter1+55 hex[1] = (letter2 < 10) ? letter2+48 : letter2+55 }