Files
sm-plugins/EventNotifier/scripting/EventNotifierAnnouncements.sp
T
2026-09-28 21:40:26 +01:00

182 lines
5.0 KiB
SourcePawn

#include <sourcemod>
#include <multicolors>
#include <EventNotifier>
#pragma semicolon 1
#pragma newdecls required
#define PLUGIN_VERSION "1.0"
// Shortest automatic announcement interval, in seconds. Values below this are
// raised to it; 0 or below disables the timer entirely.
#define MIN_ANNOUNCE_INTERVAL 60.0
// Visible characters kept from the event name in chat.
#define ANNOUNCE_NAME_MAX 64
// Visible characters allowed for the map list before " +N more" is appended.
#define ANNOUNCE_MAPS_BUDGET 120
ConVar g_cvInterval;
ConVar g_cvCount;
Handle g_hAnnounceTimer;
int g_iRotation;
public Plugin myinfo =
{
name = "EventNotifier Announcements",
author = "marisakirisame.net",
description = "Periodically announces upcoming Unloze events in chat",
version = PLUGIN_VERSION,
url = "https://marisakirisame.net"
};
public void OnPluginStart()
{
g_cvInterval = CreateConVar("sm_event_announce_interval", "120",
"Seconds between event announcements in chat, -1 to disable automatic announcements (sm_event_announce still works)", _, true, -1.0);
g_cvCount = CreateConVar("sm_event_announce_count", "3",
"Rotate announcements through the next N upcoming events, one per announcement", _, true, 1.0, true, 20.0);
// CreateConVar keeps the old bounds when the cvar already exists (plugin reload),
// so apply the lower bound explicitly.
g_cvInterval.SetBounds(ConVarBound_Lower, true, -1.0);
g_cvInterval.AddChangeHook(OnIntervalChanged);
AutoExecConfig(true, "EventNotifierAnnouncements");
RegAdminCmd("sm_event_announce", Command_Announce, ADMFLAG_GENERIC, "Posts the next event announcement in chat now");
}
public void OnConfigsExecuted()
{
RestartAnnounceTimer();
}
public void OnIntervalChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
RestartAnnounceTimer();
}
void RestartAnnounceTimer()
{
delete g_hAnnounceTimer;
float interval = g_cvInterval.FloatValue;
if (interval <= 0.0)
return;
if (interval < MIN_ANNOUNCE_INTERVAL)
interval = MIN_ANNOUNCE_INTERVAL;
g_hAnnounceTimer = CreateTimer(interval, Timer_Announce, _, TIMER_REPEAT);
}
public Action Timer_Announce(Handle timer)
{
PostAnnouncement();
return Plugin_Continue;
}
public Action Command_Announce(int client, int args)
{
if (PostAnnouncement())
ReplyToCommand(client, "[Events] Announcement posted.");
else
ReplyToCommand(client, "[Events] No upcoming events to announce.");
return Plugin_Handled;
}
// ---------------------------------------------------------------------------
// Announcement
// ---------------------------------------------------------------------------
// Posts one event from the rotation window. Returns false when there is nothing to post.
bool PostAnnouncement()
{
int count = EventNotifier_GetEventCount();
if (count <= 0)
return false;
int window = g_cvCount.IntValue;
if (window > count)
window = count;
int index = g_iRotation % window;
g_iRotation = (index + 1) % window;
EventInfo ev;
if (!EventNotifier_GetEvent(index, ev))
return false;
char name[ANNOUNCE_NAME_MAX + 1];
strcopy(name, sizeof(name), ev.name);
if (!name[0])
strcopy(name, sizeof(name), "Unnamed event");
int now = GetTime();
char span[32];
if (ev.start <= 0)
{
CPrintToChatAll("{darkorange}[Events] {yellow}#%d: {lightgreen}%s {grey}date TBA", ev.number, name);
}
else if (now < ev.start)
{
EventNotifier_FormatDuration(ev.start - now, span, sizeof(span));
CPrintToChatAll("{darkorange}[Events] {yellow}#%d: {lightgreen}%s {lime}starts in %s", ev.number, name, span);
}
else
{
EventNotifier_FormatDuration(now - ev.start, span, sizeof(span));
CPrintToChatAll("{darkorange}[Events] {yellow}#%d: {lightgreen}%s {orchid}started %s ago", ev.number, name, span);
}
if (ev.maps[0])
{
char mapsLine[ANNOUNCE_MAPS_BUDGET + 132];
int more = BuildMapsLine(ev.maps, mapsLine, sizeof(mapsLine), ANNOUNCE_MAPS_BUDGET);
if (more > 0)
CPrintToChatAll("{darkorange}[Events] {white}Maps: {lightgreen}%s {grey}+%d more", mapsLine, more);
else
CPrintToChatAll("{darkorange}[Events] {white}Maps: {lightgreen}%s", mapsLine);
}
CPrintToChatAll("{darkorange}[Events] {white}Type {lime}!event %d {white}for details and the full map list.", ev.number);
return true;
}
// Copies ", "-separated items from maps into out while the visible length stays
// within budget. Always emits at least the first item. Returns how many items
// were left out (0 when everything fit).
int BuildMapsLine(const char[] maps, char[] out, int maxlen, int budget)
{
// Count from the raw string so "+N more" is right even past the explode limit.
int total = 1;
for (int i = 0; maps[i]; i++)
{
if (maps[i] == ',' && maps[i + 1] == ' ')
total++;
}
char parts[32][128];
int count = ExplodeString(maps, ", ", parts, sizeof(parts), sizeof(parts[]));
out[0] = '\0';
int used = 0;
int shown = 0;
for (int i = 0; i < count; i++)
{
int add = strlen(parts[i]) + ((shown > 0) ? 2 : 0);
if (shown > 0 && used + add > budget)
break;
if (shown > 0)
StrCat(out, maxlen, ", ");
StrCat(out, maxlen, parts[i]);
used += add;
shown++;
}
return total - shown;
}