// rtv.sma - minimal "rock the vote" for cs16-dm-server. // Players type rtv (or /rtv, rockthevote). When amx_rtv_ratio of the players // agree, a 15s vote between up to 5 random mapcycle maps starts, then the map changes. // Blocked early in a map (amx_rtv_delay) and near map end, where mapchooser's own vote runs. #include #define VOTE_MAPS 5 #define VOTE_TIME 15 #define MENU_KEYS (MENU_KEY_1|MENU_KEY_2|MENU_KEY_3|MENU_KEY_4|MENU_KEY_5) new bool:g_rocked[MAX_PLAYERS + 1] new g_rocks new bool:g_voting, bool:g_finished new g_choices[VOTE_MAPS][32], g_numChoices, g_count[VOTE_MAPS] new Float:g_mapStart new g_pRatio, g_pDelay public plugin_init() { register_plugin("RTV", "1.0", "cs16-dm-server") register_clcmd("say rtv", "CmdRtv") register_clcmd("say /rtv", "CmdRtv") register_clcmd("say rockthevote", "CmdRtv") register_clcmd("say_team rtv", "CmdRtv") register_clcmd("say_team /rtv", "CmdRtv") g_pRatio = register_cvar("amx_rtv_ratio", "0.6") g_pDelay = register_cvar("amx_rtv_delay", "120") register_menucmd(register_menuid("RTV Vote"), MENU_KEYS, "VoteHandler") g_mapStart = get_gametime() } CountPlayers() { new players[MAX_PLAYERS], num get_players(players, num, "ch") return num } RocksNeeded() { new need = floatround(float(CountPlayers()) * get_pcvar_float(g_pRatio), floatround_ceil) return need < 1 ? 1 : need } public CmdRtv(id) { if (g_voting || g_finished) { client_print_color(id, print_team_default, "^4[RTV]^1 A map vote already ran on this map.") return PLUGIN_CONTINUE } new wait = get_pcvar_num(g_pDelay) - floatround(get_gametime() - g_mapStart) if (wait > 0) { client_print_color(id, print_team_default, "^4[RTV]^1 Too early, try again in ^3%d^1 seconds.", wait) return PLUGIN_CONTINUE } if (get_cvar_float("mp_timelimit") > 0.0 && get_timeleft() < 180) { client_print_color(id, print_team_default, "^4[RTV]^1 The end-of-map vote is about to start.") return PLUGIN_CONTINUE } if (g_rocked[id]) { client_print_color(id, print_team_default, "^4[RTV]^1 You already voted (^3%d^1/^3%d^1).", g_rocks, RocksNeeded()) return PLUGIN_CONTINUE } g_rocked[id] = true g_rocks++ new name[32] get_user_name(id, name, charsmax(name)) client_print_color(0, id, "^4[RTV]^3 %s^1 wants to change the map (^3%d^1/^3%d^1). Type ^4rtv^1 to agree.", name, g_rocks, RocksNeeded()) CheckRocks() return PLUGIN_CONTINUE } public client_disconnected(id) { if (g_rocked[id]) { g_rocked[id] = false g_rocks-- } // fewer players can mean the remaining votes are now enough if (g_rocks > 0) set_task(0.1, "CheckRocks") } public CheckRocks() { if (!g_voting && !g_finished && g_rocks > 0 && g_rocks >= RocksNeeded()) StartVote() } StartVote() { g_voting = true new current[32], cyclefile[64] get_mapname(current, charsmax(current)) get_cvar_string("mapcyclefile", cyclefile, charsmax(cyclefile)) new Array:maps = ArrayCreate(32) new fp = fopen(cyclefile, "rt") if (fp) { new line[64] while (!feof(fp)) { fgets(fp, line, charsmax(line)) trim(line) if (!line[0] || line[0] == ';' || (line[0] == '/' && line[1] == '/')) continue if (equali(line, current) || !is_map_valid(line)) continue ArrayPushString(maps, line) } fclose(fp) } g_numChoices = 0 while (g_numChoices < VOTE_MAPS && ArraySize(maps) > 0) { new pick = random(ArraySize(maps)) ArrayGetString(maps, pick, g_choices[g_numChoices], charsmax(g_choices[])) ArrayDeleteItem(maps, pick) g_count[g_numChoices] = 0 g_numChoices++ } ArrayDestroy(maps) if (!g_numChoices) { g_voting = false client_print_color(0, print_team_default, "^4[RTV]^1 No other maps in the mapcycle.") return } new menu[512], len, keys len = formatex(menu, charsmax(menu), "\yRock the vote - pick the next map:^n^n") for (new i = 0; i < g_numChoices; i++) { len += formatex(menu[len], charsmax(menu) - len, "\r%d.\w %s^n", i + 1, g_choices[i]) keys |= (1 << i) } show_menu(0, keys, menu, VOTE_TIME, "RTV Vote") client_print_color(0, print_team_default, "^4[RTV]^1 Vote started! You have ^3%d^1 seconds.", VOTE_TIME) set_task(float(VOTE_TIME) + 1.0, "FinishVote") } public VoteHandler(id, key) { if (!g_voting || key < 0 || key >= g_numChoices) return PLUGIN_HANDLED g_count[key]++ new name[32] get_user_name(id, name, charsmax(name)) client_print_color(0, id, "^4[RTV]^3 %s^1 chose ^4%s", name, g_choices[key]) return PLUGIN_HANDLED } public FinishVote() { g_voting = false g_finished = true new best = 0 for (new i = 1; i < g_numChoices; i++) { if (g_count[i] > g_count[best]) best = i } if (!g_count[best]) best = random(g_numChoices) set_cvar_string("amx_nextmap", g_choices[best]) client_print_color(0, print_team_default, "^4[RTV]^1 Next map: ^4%s^1 (^3%d^1 votes). Changing in 5 seconds...", g_choices[best], g_count[best]) set_task(5.0, "ChangeMap", best) } public ChangeMap(best) { server_cmd("changelevel %s", g_choices[best]) }