/* * CSB Rules Menu * Copyright (C) 2026 counter-strike-boost.com * * "say /rules" opens a menu of the server rules, loaded from an ini split into * [Section] blocks. Selecting a section shows its text in an MOTD window. Players * who just connected get a short one-line reminder to read the rules, and admins * can reload the ini without changing the map with amx_reloadrules. * * 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 new const PLUGIN[] = "CSB Rules Menu" new const VERSION[] = "1.0.0" new const AUTHOR[] = "counter-strike-boost.com" #define MAX_SECTIONS 16 new g_szTitle[MAX_SECTIONS][48] new g_szBody[MAX_SECTIONS][512] new g_iSections public plugin_init() { register_plugin(PLUGIN, VERSION, AUTHOR) register_clcmd("say /rules", "cmdRules") register_clcmd("say_team /rules", "cmdRules") register_concmd("amx_reloadrules", "cmdReload", ADMIN_CFG, "- reload the server rules ini") } public plugin_cfg() { loadRules() } loadRules() { g_iSections = 0 new cfgdir[128], path[160] get_localinfo("amxx_configsdir", cfgdir, charsmax(cfgdir)) formatex(path, charsmax(path), "%s/csb_rules.ini", cfgdir) if (!file_exists(path)) { /* fall back to a built-in rule set so the menu is never empty */ copy(g_szTitle[0], charsmax(g_szTitle[]), "General") copy(g_szBody[0], charsmax(g_szBody[]), "1. Respect all players and admins.^n2. No cheating of any kind.^n3. No advertising or spam.^n4. Admin decisions are final.") g_iSections = 1 return } new f = fopen(path, "rt") if (!f) return new line[256], cur = -1 while (!feof(f) && g_iSections < MAX_SECTIONS) { fgets(f, line, charsmax(line)) trim(line) if (!line[0] || line[0] == ';' || line[0] == '#') continue if (line[0] == '[') { new title[48] copy(title, charsmax(title), line[1]) new close = contain(title, "]") if (close != -1) title[close] = 0 trim(title) cur = g_iSections copy(g_szTitle[cur], charsmax(g_szTitle[]), title) g_szBody[cur][0] = 0 g_iSections++ } else if (cur >= 0) { if (g_szBody[cur][0]) add(g_szBody[cur], charsmax(g_szBody[]), "^n") add(g_szBody[cur], charsmax(g_szBody[]), line) } } fclose(f) if (!g_iSections) { copy(g_szTitle[0], charsmax(g_szTitle[]), "General") copy(g_szBody[0], charsmax(g_szBody[]), "Be excellent to each other.") g_iSections = 1 } } public client_putinserver(id) { set_task(12.0, "taskReminder", id) } public taskReminder(id) { if (is_user_connected(id)) client_print(id, print_chat, "[CSB] Type /rules to read the server rules.") } public cmdRules(id) { new menu = menu_create("\yServer Rules\w - pick a section:", "rulesHandler") new info[8] for (new i = 0; i < g_iSections; i++) { num_to_str(i, info, charsmax(info)) menu_additem(menu, g_szTitle[i], info, 0) } menu_setprop(menu, MPROP_EXIT, MEXIT_ALL) menu_display(id, menu, 0) return PLUGIN_HANDLED } public rulesHandler(id, menu, item) { if (item == MENU_EXIT) { menu_destroy(menu) return PLUGIN_HANDLED } new info[8], name[48], access, callback menu_item_getinfo(menu, item, access, info, charsmax(info), name, charsmax(name), callback) menu_destroy(menu) new sec = str_to_num(info) if (sec < 0 || sec >= g_iSections) return PLUGIN_HANDLED new head[64] formatex(head, charsmax(head), "Rules - %s", g_szTitle[sec]) show_motd(id, g_szBody[sec], head) return PLUGIN_HANDLED } public cmdReload(id, level, cid) { if (!cmd_access(id, level, cid, 1)) return PLUGIN_HANDLED loadRules() console_print(id, "[CSB] Reloaded %d rule section(s).", g_iSections) client_print(id, print_chat, "[CSB] Rules reloaded (%d sections).", g_iSections) return PLUGIN_HANDLED }