Promuj serwer
csb-adverts / addons / amxmodx / scripting / csb_adverts.sma
239 lines 6.6 KiB Pawn Raw Permalink History
/*
 * CSB Adverts
 * Copyright (C) 2026 counter-strike-boost.com
 *
 * Rotating server messages read from a plain-text file and shown every N
 * seconds as coloured chat, a HUD message or a DHUD message. Each line carries
 * its own display type and may contain placeholders that are expanded at show
 * time: {HOSTNAME} {TIMELEFT} {NEXTMAP} {PLAYERS} {MAXPLAYERS} {MAP} {NAME}.
 * A per-map file is loaded automatically when it exists, and every player can
 * mute the adverts for himself with "say /noads".
 *
 * Inspired by the classic "Advertisements" plugins for AMX Mod X. This is an
 * 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 <https://www.gnu.org/licenses/> for details.
 */

#include <amxmodx>

new const PLUGIN[] = "CSB Adverts"
new const VERSION[] = "1.0.0"
new const AUTHOR[]  = "counter-strike-boost.com"

#define MAX_ADS      48
#define MAX_AD_LEN   192

new g_szAds[MAX_ADS][MAX_AD_LEN]
new g_iAdType[MAX_ADS]           /* 0 = chat, 1 = hud, 2 = dhud */
new g_iNumAds
new g_iCurrent

new bool:g_bMuted[33]

new g_pEnabled, g_pInterval, g_pRandom, g_pPrefix

public plugin_init()
{
    register_plugin(PLUGIN, VERSION, AUTHOR)

    g_pEnabled  = register_cvar("csb_adverts", "1")
    g_pInterval = register_cvar("csb_adverts_interval", "45")
    g_pRandom   = register_cvar("csb_adverts_random", "0")
    g_pPrefix   = register_cvar("csb_adverts_prefix", "[Server]")

    register_clcmd("say /noads",      "cmdToggle")
    register_clcmd("say_team /noads", "cmdToggle")
}

public plugin_cfg()
{
    loadAds()

    new Float:interval = get_pcvar_float(g_pInterval)
    if (interval < 5.0)
        interval = 5.0

    set_task(interval, "showNextAd", 0, _, _, "b")
}

public client_putinserver(id)
{
    g_bMuted[id] = false
}

public cmdToggle(id)
{
    g_bMuted[id] = !g_bMuted[id]
    client_print(id, print_chat, "[CSB] Adverts are now %s for you.", g_bMuted[id] ? "OFF" : "ON")
    return PLUGIN_HANDLED
}

loadAds()
{
    g_iNumAds = 0
    g_iCurrent = 0

    new dir[128]
    get_localinfo("amxx_configsdir", dir, charsmax(dir))

    new map[32]
    get_mapname(map, charsmax(map))

    /* prefer a per-map file, fall back to the global one */
    new path[160]
    formatex(path, charsmax(path), "%s/csb_adverts_%s.ini", dir, map)

    if (!file_exists(path))
        formatex(path, charsmax(path), "%s/csb_adverts.ini", dir)

    if (!file_exists(path))
    {
        writeDefaults(path)
    }

    new fp = fopen(path, "rt")
    if (!fp)
    {
        log_amx("[CSB Adverts] could not open %s", path)
        return
    }

    new line[MAX_AD_LEN + 16]

    while (!feof(fp) && g_iNumAds < MAX_ADS)
    {
        fgets(fp, line, charsmax(line))
        trim(line)

        if (!line[0] || line[0] == ';' || (line[0] == '/' && line[1] == '/'))
            continue

        new type = 0
        new start = 0

        if (equali(line, "hud:", 4))       { type = 1; start = 4; }
        else if (equali(line, "dhud:", 5)) { type = 2; start = 5; }
        else if (equali(line, "chat:", 5)) { type = 0; start = 5; }

        new msg[MAX_AD_LEN]
        copy(msg, charsmax(msg), line[start])
        trim(msg)

        if (!msg[0])
            continue

        copy(g_szAds[g_iNumAds], charsmax(g_szAds[]), msg)
        g_iAdType[g_iNumAds] = type
        g_iNumAds++
    }

    fclose(fp)

    log_amx("[CSB Adverts] loaded %d advert(s) from %s", g_iNumAds, path)
}

writeDefaults(const path[])
{
    new fp = fopen(path, "wt")
    if (!fp)
        return

    fputs(fp, "; CSB Adverts - one message per line.^n")
    fputs(fp, "; Prefix a line with chat:, hud: or dhud: to pick how it is shown.^n")
    fputs(fp, "; Placeholders: {HOSTNAME} {TIMELEFT} {NEXTMAP} {PLAYERS} {MAXPLAYERS} {MAP} {NAME}^n")
    fputs(fp, "chat: Welcome to {HOSTNAME}, {NAME}! There are {PLAYERS}/{MAXPLAYERS} players online.^n")
    fputs(fp, "chat: Current map is {MAP} - {TIMELEFT} left. Next map: {NEXTMAP}.^n")
    fputs(fp, "dhud: Type /noads to hide these messages.^n")
    fclose(fp)
}

public showNextAd()
{
    if (!get_pcvar_num(g_pEnabled) || g_iNumAds < 1)
        return

    if (!get_playersnum())
        return

    new idx
    if (get_pcvar_num(g_pRandom))
        idx = random_num(0, g_iNumAds - 1)
    else
    {
        idx = g_iCurrent
        g_iCurrent = (g_iCurrent + 1) % g_iNumAds
    }

    new players[32], num
    get_players(players, num, "ch")

    for (new i = 0; i < num; i++)
    {
        new id = players[i]

        if (g_bMuted[id])
            continue

        new msg[MAX_AD_LEN]
        buildMessage(id, g_szAds[idx], msg, charsmax(msg))

        switch (g_iAdType[idx])
        {
            case 1:
            {
                set_hudmessage(0, 160, 255, -1.0, 0.18, 0, 0.0, 6.0, 0.2, 0.2, -1)
                show_hudmessage(id, "%s", msg)
            }
            case 2:
            {
                show_dhudmessage(id, "%s", msg)
            }
            default:
            {
                new prefix[32], out[MAX_AD_LEN + 40]
                get_pcvar_string(g_pPrefix, prefix, charsmax(prefix))
                formatex(out, charsmax(out), "^x04%s^x01 %s", prefix, msg)
                client_print_color(id, print_team_default, "%s", out)
            }
        }
    }
}

buildMessage(id, const src[], out[], len)
{
    copy(out, len, src)

    new host[64], nextmap[32], map[32], name[32]
    new tl[16], pc[8], mx[8]

    get_cvar_string("hostname", host, charsmax(host))
    get_cvar_string("amx_nextmap", nextmap, charsmax(nextmap))
    if (!nextmap[0])
        copy(nextmap, charsmax(nextmap), "unknown")

    get_mapname(map, charsmax(map))
    get_user_name(id, name, charsmax(name))

    new left = get_timeleft()
    if (left > 0)
        formatex(tl, charsmax(tl), "%d:%02d", left / 60, left % 60)
    else
        copy(tl, charsmax(tl), "no limit")

    num_to_str(get_playersnum(), pc, charsmax(pc))
    num_to_str(get_maxplayers(), mx, charsmax(mx))

    replace_string(out, len, "{HOSTNAME}", host)
    replace_string(out, len, "{NEXTMAP}", nextmap)
    replace_string(out, len, "{TIMELEFT}", tl)
    replace_string(out, len, "{PLAYERS}", pc)
    replace_string(out, len, "{MAXPLAYERS}", mx)
    replace_string(out, len, "{MAP}", map)
    replace_string(out, len, "{NAME}", name)
}