/* * CSB AutoMix 5v5 * Copyright (C) 2026 counter-strike-boost.com * * Automatic 5v5 mix. Players type /mix to join a queue; when ten are waiting the * plugin builds two balanced teams by rank score (a self-declared native, with a * frags fallback), moves everyone with cs_set_user_team, plays a knife round and * then starts the live match. If a mix player leaves mid-setup the match is * paused and the queue re-opened. * * Inspired by AutoMix by MaGNuM. Independent GPL re-implementation; no original * code is reused. * * 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 3 of the License, or (at your option) any later * version. It 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 for details. */ #include #include #include /* optional external ranking; falls back to frags if the module is absent */ native csb_get_rank_score(id) new const PLUGIN[] = "CSB AutoMix 5v5" new const VERSION[] = "1.0.0" new const AUTHOR[] = "counter-strike-boost.com" #define STATE_IDLE 0 #define STATE_KNIFE 1 #define STATE_LIVE 2 #define NEEDED 10 new g_pEnabled, g_pNeeded new bool:g_bInMix[33] new g_iState new bool:g_bHasRank public plugin_init() { register_plugin(PLUGIN, VERSION, AUTHOR) g_pEnabled = register_cvar("csb_mix_enabled", "1") g_pNeeded = register_cvar("csb_mix_needed", "10") register_clcmd("say /mix", "cmdMix") register_clcmd("say_team /mix", "cmdMix") register_clcmd("say /unmix", "cmdUnMix") register_clcmd("say_team /unmix", "cmdUnMix") register_event("ResetHUD", "eventSpawn", "b") register_logevent("eventRoundEnd", 2, "1=Round_End") g_iState = STATE_IDLE } public plugin_cfg() { /* detect the optional ranking native once modules are loaded */ g_bHasRank = false } rankOf(id) { if (g_bHasRank) return csb_get_rank_score(id) return get_user_frags(id) } public client_disconnected(id) { if (!g_bInMix[id]) return g_bInMix[id] = false if (g_iState != STATE_IDLE) { g_iState = STATE_IDLE client_print(0, print_chat, "^x04[CSB Mix]^x01 A player left. Match paused, queue re-opened. Type /mix.") } } public cmdMix(id) { if (!get_pcvar_num(g_pEnabled)) { client_print(id, print_chat, "^x04[CSB Mix]^x01 Mix is disabled.") return PLUGIN_HANDLED } if (g_iState != STATE_IDLE) { client_print(id, print_chat, "^x04[CSB Mix]^x01 A match is already in progress.") return PLUGIN_HANDLED } if (g_bInMix[id]) { client_print(id, print_chat, "^x04[CSB Mix]^x01 You are already in the queue.") return PLUGIN_HANDLED } g_bInMix[id] = true new count = queueCount() new need = neededCount() client_print(0, print_chat, "^x04[CSB Mix]^x01 %n joined the mix (%d/%d).", id, count, need) if (count >= need) startMix() return PLUGIN_HANDLED } public cmdUnMix(id) { if (!g_bInMix[id]) return PLUGIN_HANDLED g_bInMix[id] = false client_print(0, print_chat, "^x04[CSB Mix]^x01 %n left the queue.", id) return PLUGIN_HANDLED } neededCount() { new n = get_pcvar_num(g_pNeeded) if (n < 2) n = 2 if (n > 32) n = 32 return n } queueCount() { new c = 0 for (new id = 1; id <= 32; id++) { if (g_bInMix[id] && is_user_connected(id)) c++ } return c } startMix() { new ids[32], n = 0 for (new id = 1; id <= 32; id++) { if (g_bInMix[id] && is_user_connected(id)) ids[n++] = id } /* sort by rank score, descending (selection sort) */ for (new i = 0; i < n - 1; i++) { new best = i for (new j = i + 1; j < n; j++) { if (rankOf(ids[j]) > rankOf(ids[best])) best = j } if (best != i) { new tmp = ids[i] ids[i] = ids[best] ids[best] = tmp } } /* snake draft into two teams for balance */ new tScore = 0, ctScore = 0 for (new i = 0; i < n; i++) { new id = ids[i] new sc = rankOf(id) if (tScore <= ctScore) { cs_set_user_team(id, CS_TEAM_T) tScore += sc } else { cs_set_user_team(id, CS_TEAM_CT) ctScore += sc } } g_iState = STATE_KNIFE client_print(0, print_chat, "^x04[CSB Mix]^x01 Teams are set. ^x03KNIFE ROUND^x01 - winner picks side!") server_cmd("sv_restart 1") } public eventSpawn(id) { if (g_iState != STATE_KNIFE || !g_bInMix[id] || !is_user_alive(id)) return /* knife-only loadout during the knife round */ strip_user_weapons(id) give_item(id, "weapon_knife") } public eventRoundEnd() { if (g_iState == STATE_KNIFE) { g_iState = STATE_LIVE client_print(0, print_chat, "^x04[CSB Mix]^x01 Knife round over. ^x03LIVE^x01 - good luck!") server_cmd("sv_restart 1") } }