ensl-plugin/ENSL.sma
2012-01-06 21:29:05 +02:00

4571 lines
131 KiB
Text

/*
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 <amxmodx>
#include <amxmisc>
#include <sockets>
#include <fakemeta>
#include <fakemeta_stocks>
#include <engine>
#include <ns>
#include <fun>
#include <nsteam>
/**************************************************************************************
* Global constants
**************************************************************************************/
// Basic constants
#define PLUGIN "ENSL"
#define VERSION "1.3-b3"
#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 10 // 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_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 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", "125")
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")
// 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");
// 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/gather")
// 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])
{
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])
{
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)
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", 125)
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");
}
}
/**************************************************************************************
* 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 ( 125 < str_to_num(value) && pev(id, pev_team) == 1 && is_user_alive(id) )
{
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_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_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
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
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_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 )
{
client_print(id, print_center, "^n^n^n^n^n^n Only registered ENSL members can play. ^n^n Plese register at www.ensl.org. ^n^n You can spectate without registering!")
return 0
}
return 1
}
/**************************************************************************************
* Gather functions
**************************************************************************************/
// TBA
/**************************************************************************************
* 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
}