diff --git a/plugins/ns/adminslots.sma b/plugins/ns/adminslots.sma new file mode 100755 index 00000000..11b68a37 --- /dev/null +++ b/plugins/ns/adminslots.sma @@ -0,0 +1,108 @@ +/* AMX Mod X +* Slots Reservation Plugin +* +* by the AMX Mod X Development Team +* originally developed by OLO +* +* This file is part of AMX Mod X. +* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +*/ + +#include +#include + +// Comment if you don't want to hide not used reserved slots +#define HIDE_RESERVED_SLOTS + +#if !defined NO_STEAM +new g_cmdLoopback[16] +#endif + +public plugin_init() +{ + register_plugin("Slots Reservation","0.20","AMXX Dev Team") + register_cvar("amx_reservation","1") + +#if !defined NO_STEAM + format( g_cmdLoopback, 15, "amxres%c%c%c%c" , + random_num('A','Z') , random_num('A','Z') ,random_num('A','Z'),random_num('A','Z') ) + + register_clcmd( g_cmdLoopback, "ackSignal" ) +#endif +} + +#if !defined NO_STEAM +public ackSignal(id) + server_cmd("kick #%d ^"Dropped due to slot reservation^"", get_user_userid(id) ) + +public client_authorized(id) +#else +public client_connect(id) +#endif +{ + new maxplayers = get_maxplayers() + new players = get_playersnum( 1 ) + new limit = maxplayers - get_cvar_num( "amx_reservation" ) + + if ( (get_user_flags(id) & ADMIN_RESERVATION) || (players <= limit) ) + { +#if defined HIDE_RESERVED_SLOTS + setVisibleSlots( players , maxplayers, limit ) +#endif + return PLUGIN_CONTINUE + } + +#if !defined NO_STEAM + client_cmd(id,g_cmdLoopback) +#else + server_cmd("kick #%d ^"Dropped due to slot reservation^"", get_user_userid(id) ) +#endif + + return PLUGIN_HANDLED +} + +#if defined HIDE_RESERVED_SLOTS +public client_disconnect(id) +{ + new maxplayers = get_maxplayers( ) + setVisibleSlots( get_playersnum(1) - 1 , maxplayers , + maxplayers - get_cvar_num( "amx_reservation" ) ) + return PLUGIN_CONTINUE +} + +setVisibleSlots( players , maxplayers , limit ) +{ + new num = players + 1 + + if ( players == maxplayers ) + num = maxplayers + else if ( players < limit ) + num = limit + + set_cvar_num( "sv_visiblemaxplayers" , num ) +} +#endif \ No newline at end of file diff --git a/plugins/ns/mapchooser.sma b/plugins/ns/mapchooser.sma new file mode 100755 index 00000000..45643007 --- /dev/null +++ b/plugins/ns/mapchooser.sma @@ -0,0 +1,178 @@ +/* AMX Mod X +* Nextmap Chooser Plugin +* +* by the AMX Mod X Development Team +* originally developed by OLO +* +* This file is part of AMX Mod X. +* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +*/ + +#include +#include + +#define MAX_MAPS 128 +#define SELECTMAPS 5 + +new g_mapName[MAX_MAPS][32] +new g_mapNums + +new g_nextName[SELECTMAPS] +new g_voteCount[SELECTMAPS+2] +new g_mapVoteNum +new g_lastMap[32] + +new bool:g_selected = false + +public plugin_init() +{ + register_plugin("Nextmap Chooser","0.20","AMXX Dev Team") + register_menucmd(register_menuid("AMX Choose nextmap:"),(-1^(-1<<(SELECTMAPS+2))),"countVote") + register_cvar("amx_extendmap_max","90") + register_cvar("amx_extendmap_step","15") + + get_localinfo("lastMap",g_lastMap,31) + set_localinfo("lastMap","") + + new maps_ini_file[64]; + get_configsdir(maps_ini_file, 63); + format(maps_ini_file, 63, "%s/maps.ini", maps_ini_file); + if ( loadSettings(maps_ini_file) ) + set_task(15.0,"voteNextmap",987456,"",0,"b") +} + +public checkVotes(){ + new b = 0 + for(new a = 0; a < g_mapVoteNum; ++a) + if (g_voteCount[b] < g_voteCount[a]) + b = a + if ( g_voteCount[SELECTMAPS] > g_voteCount[b] ) { + new mapname[32] + get_mapname(mapname,31) + new Float:steptime = get_cvar_float("amx_extendmap_step") + set_cvar_float("mp_timelimit", get_cvar_float("mp_timelimit") + steptime ) + client_print(0,print_chat,"Choosing finished. Current map will be extended to next %.0f minutes", steptime ) + log_amx("Vote: Voting for the nextmap finished. Map %s will be extended to next %.0f minutes", + mapname , steptime ) + return + } + if ( g_voteCount[b] && g_voteCount[SELECTMAPS+1] <= g_voteCount[b] ) + set_cvar_string("amx_nextmap", g_mapName[g_nextName[b]] ) + new smap[32] + get_cvar_string("amx_nextmap",smap,31) + client_print(0,print_chat,"Choosing finished. The nextmap will be %s", smap ) + log_amx("Vote: Voting for the nextmap finished. The nextmap will be %s", smap) +} + +public countVote(id,key){ + if ( get_cvar_float("amx_vote_answers") ) { + new name[32] + get_user_name(id,name,31) + if ( key == SELECTMAPS ) + client_print(0,print_chat,"%s chose map extending", name ) + else if ( key < SELECTMAPS ) + client_print(0,print_chat,"%s chose %s", name, g_mapName[g_nextName[key]] ) + } + ++g_voteCount[key] + return PLUGIN_HANDLED +} + +bool:isInMenu(id){ + for(new a=0; a129){ + g_selected = false + return + } + + if (g_selected) + return + g_selected = true + new menu[512], a, mkeys = (1< SELECTMAPS) ? SELECTMAPS : g_mapNums + for(g_mapVoteNum = 0;g_mapVoteNum= g_mapNums) a = 0 + g_nextName[g_mapVoteNum] = a + pos += format(menu[pos],511,"%d. %s^n",g_mapVoteNum+1,g_mapName[a]) + mkeys |= (1< +#include + +#define MAX_MAPS 128 +#define INFO_READ_TIME 5.0 + +new g_mapCycle[MAX_MAPS][mapdata] +new g_numMaps, g_numPlayers, g_nextPos +new bool:g_mapChanging, bool:g_changeMapDelay + +public plugin_init() { + register_plugin("NextMap","0.20","AMXX Dev Team") + register_cvar("amx_nextmap","",FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_SPONLY) + register_cvar("amx_mapnum_ignore", "0") + register_event("TextMsg", "voteMap", "a", "1=3", "2&executed votemap") + register_event("PlayHUDNot", "roundEnded", "bc", "1=0", "2=57") + register_clcmd("say nextmap", "sayNextMap", 0, "- displays nextmap") + + readMapCycle() + findNextMap() +} + +public server_changelevel() { + if (g_mapChanging) + return BLOCK_ONCE + + // Check if the cvar "amx_nextmap" has changed since the map loaded as it overrides the min/max settings. + new szCvarNextMap[32] + get_cvar_string("amx_nextmap", szCvarNextMap, 31) + if ( !equal(szCvarNextMap, g_mapCycle[g_nextPos][NAME]) ) { + if (is_map_valid(szCvarNextMap)) { + if (g_changeMapDelay) + set_task(INFO_READ_TIME, "changeMap", 0, szCvarNextMap, 32) + else + changeMap(szCvarNextMap) + return BLOCK_ONCE + } + } + + new szNextMap[32] + getNextValidMap(szNextMap) + if (is_map_valid(szNextMap)) { + if (g_changeMapDelay) + set_task(INFO_READ_TIME, "changeMap", 0, szNextMap, 32) + else + changeMap(szNextMap) + } else + return BLOCK_NOT // When NS trys to change map it disables gameplay, so we MUST change map. + // This is a backup incase something unexpected happens to make sure we change map. + + g_mapChanging = true + return BLOCK_ONCE +} + +public getNextValidMap(szNextMap[]) { + +/* Test min/max player settings. + First test with number of players when the round ended, then with the current number of players, + as this is how NS does it. +*/ + new startPos = g_nextPos + + if (!get_cvar_num("amx_mapnum_ignore")) { + new curNumPlayers = get_playersnum() + if (g_numPlayers == 0) g_numPlayers = curNumPlayers + new looped + while(looped < 2) { + new minPlayers = g_mapCycle[g_nextPos][MIN] + new maxPlayers = g_mapCycle[g_nextPos][MAX] + if (maxPlayers == 0) maxPlayers = 32 + if (minPlayers <= g_numPlayers <= maxPlayers) break + if (minPlayers <= curNumPlayers <= maxPlayers) { + g_numPlayers = curNumPlayers + break + } + if (++g_nextPos == g_numMaps) { + g_nextPos = 0 + looped++ + } + } + } + copy(szNextMap, 31, g_mapCycle[g_nextPos][NAME]) + + if (startPos != g_nextPos) { // Next Map wasn't valid due to min/max player settings + client_print(0, print_chat, "Too %s players for %s. Next valid map: %s", + (g_mapCycle[startPos][MIN] <= g_numPlayers) ? "many" : "few", g_mapCycle[startPos][NAME], g_mapCycle[g_nextPos][NAME]) + + new szPos[8] + num_to_str(g_nextPos, szPos, 7) + set_localinfo("amx_nextmap_pos", szPos) + set_cvar_string("amx_nextmap", g_mapCycle[g_nextPos][NAME]) + g_changeMapDelay = true + } +} + +public voteMap() { + new szVoteMap[128] + read_data(2, szVoteMap, 127) // "YO | Cheesy Peteza executed votemap 2 (co_angst 1/5)" + + new start, end, szData[64] + for (new i; i + +new g_TimeSet[32][2] +new g_LastTime +new g_CountDown +new g_Switch +new Float:g_roundStartTime +new bool:is_combat + +public plugin_init() { + register_plugin("TimeLeft","0.20","AMXX Dev Team") + register_cvar("amx_time_voice","1") + register_srvcmd("amx_time_display","setDisplaying") + register_cvar("amx_timeleft","00:00",FCVAR_SERVER|FCVAR_EXTDLL|FCVAR_UNLOGGED|FCVAR_SPONLY) + register_clcmd("say timeleft","sayTimeLeft",0,"- displays timeleft") + register_clcmd("say thetime","sayTheTime",0,"- displays current time") + set_task(0.8,"timeRemain",8648458,"",0,"b") + + new szMapName[4] + get_mapname(szMapName, 3) + if (equal(szMapName, "co_")) { + register_event("PlayHUDNot", "roundStart", "bc", "1=0", "2=56") + is_combat = true + } +} + +public sayTheTime(id){ + if ( get_cvar_num("amx_time_voice") ){ + new mhours[6], mmins[6], whours[32], wmins[32], wpm[6] + get_time("%H",mhours,5) + get_time("%M",mmins,5) + new mins = str_to_num(mmins) + new hrs = str_to_num(mhours) + if (mins) + num_to_word(mins,wmins,31) + else + wmins[0] = 0 + if (hrs < 12) + wpm = "am " + else { + if (hrs > 12) hrs -= 12 + wpm = "pm " + } + if (hrs) + num_to_word(hrs,whours,31) + else + whours = "twelve " + client_cmd(id, "spk ^"fvox/time_is_now %s_period %s%s^"",whours,wmins,wpm ) + } + new ctime[64] + get_time("%m/%d/%Y - %H:%M:%S",ctime,63) + client_print(0,print_chat, "The time: %s",ctime ) + return PLUGIN_CONTINUE +} + +public sayTimeLeft(id){ + if (get_cvar_float("mp_timelimit")) { + new a = get_timeleft() + if ( get_cvar_num("amx_time_voice") ) { + new svoice[128] + setTimeVoice( svoice , 127 , 0 , a ) + client_cmd( id , svoice ) + } + client_print(0,print_chat, "Time Left: %d:%02d", (a / 60) , (a % 60) ) + } + else + client_print(0,print_chat, "No Time Limit" ) + return PLUGIN_CONTINUE +} + +setTimeText(text[],len,tmlf){ + new secs = tmlf % 60 + new mins = tmlf / 60 + if (secs == 0) + format(text,len,"%d minute%s", mins , (mins > 1) ? "s" : "" ) + else if (mins == 0) + format(text,len,"%d second%s", secs,(secs > 1) ? "s" : "" ) + else + format(text,len,"%d minute%s %d second%s", mins , (mins > 1) ? "s" : "" ,secs ,(secs > 1) ? "s" : "" ) +} + +/* +temp[0] = number of hours +temp[1] = "hours " +temp[2] = number of minutes +temp[3] = "minutes " +temp[4] = +temp[5] = +temp[6] = "remaining " +Flags: +4 (c) - Don't add "remaining " +8 (d)- Don't add "hours " or "minutes" +*/ +setTimeVoice(text[],len,flags,tmlf){ + new temp[7][32] + new secs = tmlf % 60 + new mins = tmlf / 60 + for(new a = 0;a < 7;++a) + temp[a][0] = 0 + if (secs > 0){ + num_to_word(secs,temp[4],31) + if (!(flags & 8)) temp[5] = "seconds " /* there is no "second" in default hl */ + } + if (mins > 59){ + new hours = mins / 60 + num_to_word(hours,temp[0],31) + if (!(flags & 8)) temp[1] = "hours " + mins = mins % 60 + } + if (mins > 0) { + num_to_word(mins ,temp[2],31) + if (!(flags & 8)) temp[3] = "minutes " + } + if (!(flags & 4)) temp[6] = "remaining " + return format(text,len,"spk ^"vox/%s%s%s%s%s%s%s^"", temp[0],temp[1],temp[2],temp[3],temp[4],temp[5],temp[6] ) +} + +findDispFormat(timeleft){ + for(new i = 0;g_TimeSet[i][0];++i){ + if (g_TimeSet[i][1] & 16){ // show/speak if current time is less than this set in parameter + if (g_TimeSet[i][0] > timeleft){ + if (!g_Switch) { + g_CountDown = g_Switch = timeleft + remove_task(8648458) + set_task(0.98,"timeRemain",34543,"",0,"b") // 0.98 because of cumulative time lost during each server frame + } + return i + } + } + else if (g_TimeSet[i][0] == timeleft){ + return i + } + } + return -1 +} + +/* +Displaying of round time remaining on combat maps +a (1) - display white text on bottom +b (2) - use voice +c (4) - don't add "remaining" (only in voice) +d (8) - don't add "hours/minutes/seconds" (only in voice) +e (16) - show/speak if current time is less than this set in parameter +amx_time_display "ab 1200" "ab 600" "ab 300" "ab 180" "ab 60" "bcde 11" + +g_TimeSet[i][0] = timeleft at which to display its value +g_timeSet[i][1] = bit mask of the flags abcde +*/ +public setDisplaying(){ + new arg[32], flags[32], num[32] + new argc = read_argc() - 1 + for(new i; (argc > i < 32); ++i) { + read_argv(i+1,arg,31) + parse(arg,flags,31,num,31) + g_TimeSet[i][0] = str_to_num(num) + g_TimeSet[i][1] = read_flags(flags) + g_TimeSet[i+1][0] = 0 + } + return PLUGIN_HANDLED +} + +public timeRemain(param[]){ + new gmtm = get_timeleft() + new stimel[12] + format(stimel,11,"%02d:%02d",gmtm / 60, gmtm % 60) + set_cvar_string("amx_timeleft",stimel) + + if (!is_combat) + return + + new tmlf = g_Switch ? --g_CountDown : getCombatTimeLeft() + + if ( g_Switch && tmlf > g_Switch ) { + g_Switch = 0 + set_task(0.8,"timeRemain",8648458,"",0,"b") + return + } + + if (tmlf > 0 && g_LastTime != tmlf){ + g_LastTime = tmlf + new tm_set = findDispFormat(tmlf) + if ( tm_set != -1){ + new flags = g_TimeSet[tm_set][1] + new arg[128] + if (flags & 1){ // display white text on bottom + setTimeText(arg,127,tmlf) + if (flags & 16) // show/speak if current time is less than this set in parameter + set_hudmessage(255, 255, 255, -1.0, 0.85, 0, 0.0, 1.1, 0.1, 0.5, 1) + else + set_hudmessage(255, 255, 255, -1.0, 0.85, 0, 0.0, 3.0, 0.0, 0.5, 1) + show_hudmessage(0,arg) + } + if (flags & 2){ // use voice + setTimeVoice(arg,127,flags,tmlf) + client_cmd(0,arg) + } + } + } +} + +public roundStart() { + g_roundStartTime = get_gametime() +} + +getCombatTimeLeft() { + new combatTime = floatround( (get_cvar_float("mp_combattime") * 60) - (get_gametime() - g_roundStartTime) ) + return (combatTime < 0) ? 0 : combatTime +} \ No newline at end of file