235 lines
7.3 KiB
SourcePawn
235 lines
7.3 KiB
SourcePawn
//----------------------------------------------------------------------------------------------------
|
|
// Radio commands: takes over the stock CS:S radio commands (the listener blocks the game's
|
|
// handling) and re-emits the voice line plus a custom-chatcolors styled chat line to the team.
|
|
//----------------------------------------------------------------------------------------------------
|
|
|
|
static const char g_sRadioCommands[][] = {
|
|
"coverme", "takepoint", "holdpos", "regroup", "followme", "takingfire", "go",
|
|
"fallback", "sticktog", "getinpos", "stormfront", "report", "roger", "enemyspot",
|
|
"needbackup", "sectorclear", "inposition", "reportingin", "getout", "negative", "enemydown",
|
|
"thanks", "cheer"
|
|
};
|
|
|
|
// Everything from this index on is a custom addition, not a stock radio command
|
|
// (gated behind sm_radio_custom).
|
|
#define RADIO_FIRST_CUSTOM (sizeof(g_sRadioCommands) - 2)
|
|
|
|
static const char g_sRadioSounds[][] = {
|
|
"radio/ct_coverme.wav", "radio/takepoint.wav", "radio/position.wav", "radio/regroup.wav",
|
|
"radio/followme.wav", "radio/fireassis.wav", "radio/com_go.wav", "radio/fallback.wav",
|
|
"radio/sticktog.wav", "radio/com_getinpos.wav", "radio/stormfront.wav", "radio/com_reportin.wav",
|
|
"radio/roger.wav", "radio/ct_enemys.wav", "radio/ct_backup.wav", "radio/clear.wav",
|
|
"radio/ct_inpos.wav", "radio/ct_reportingin.wav", "radio/blow.wav", "radio/negative.wav",
|
|
"radio/enemydown.wav",
|
|
"", "" // thanks/cheer have no stock radio sound; a random bot-chatter wav is picked below
|
|
};
|
|
|
|
#define SOUND_AFFIRMATIVE "radio/ct_affirm.wav"
|
|
#define SOUND_FIREINTHEHOLE "radio/ct_fireinhole.wav"
|
|
|
|
static const char g_sThanksSounds[][] = {
|
|
"bot/nice.wav", "bot/nice2.wav", "bot/good_one.wav", "bot/good_one2.wav",
|
|
"bot/very_nice.wav", "bot/well_done.wav"
|
|
};
|
|
|
|
static const char g_sCheerSounds[][] = {
|
|
"bot/whoo.wav", "bot/whoo2.wav", "bot/yesss.wav", "bot/yesss2.wav",
|
|
"bot/yea_baby.wav", "bot/oh_yea.wav", "bot/oh_yea2.wav"
|
|
};
|
|
|
|
// The bot chatter wavs are mastered 6-10 dB hotter than the radio wavs (radio averages
|
|
// -16.6 LUFS), so each one carries a gain that brings it down to the radio loudness.
|
|
static const float g_flThanksScales[] = {
|
|
0.61, 0.44, 0.45, 0.63,
|
|
0.43, 0.42
|
|
};
|
|
|
|
static const float g_flCheerScales[] = {
|
|
0.33, 0.27, 0.25, 0.32,
|
|
0.31, 0.32, 0.39
|
|
};
|
|
|
|
static const char g_sRadioPhrases[][] = {
|
|
"Cover Me!", "You take the point.", "Hold This position.", "Regroup Team.",
|
|
"Follow Me.", "Taking fire...need assistance!", "Go go go!", "Team, fall back!",
|
|
"Stick together, team.", "Get in position and wait for my go.", "Storm the Front!",
|
|
"Report in, team.", "Roger that.", "Enemy spotted.", "Need backup.", "Sector clear.",
|
|
"I'm in position.", "Reporting in.", "Get out of there, it's gonna blow!",
|
|
"Negative.", "Enemy down.",
|
|
"Thanks!", "Cheered!!!"
|
|
};
|
|
|
|
static const char g_sCheerColors[][] = {
|
|
"FF0000", "FF4500", "FF8C00", "FFD700", "ADFF2F",
|
|
"00FF00", "00BFFF", "1E90FF", "0000CD", "9400D3"
|
|
};
|
|
static const char g_sCheerText[] = "Cheered!!!";
|
|
static int g_iCheerOffset;
|
|
|
|
static void Commands_BuildCheerPhrase(char[] phrase, int maxlen)
|
|
{
|
|
phrase[0] = '\0';
|
|
for (int i = 0; i < sizeof(g_sCheerText) - 1; i++)
|
|
{
|
|
Format(phrase, maxlen, "%s\x07%s%c", phrase,
|
|
g_sCheerColors[(i + g_iCheerOffset) % sizeof(g_sCheerColors)], g_sCheerText[i]);
|
|
}
|
|
g_iCheerOffset = (g_iCheerOffset + 1) % sizeof(g_sCheerColors);
|
|
}
|
|
|
|
static StringMap g_RadioIndex;
|
|
|
|
void Commands_PrecacheSounds()
|
|
{
|
|
for (int i = 0; i < sizeof(g_sRadioSounds); i++)
|
|
{
|
|
if (g_sRadioSounds[i][0])
|
|
PrecacheSound(g_sRadioSounds[i]);
|
|
}
|
|
PrecacheSound(SOUND_AFFIRMATIVE);
|
|
PrecacheSound(SOUND_FIREINTHEHOLE); // grenade.sp
|
|
for (int i = 0; i < sizeof(g_sThanksSounds); i++)
|
|
PrecacheSound(g_sThanksSounds[i]);
|
|
for (int i = 0; i < sizeof(g_sCheerSounds); i++)
|
|
PrecacheSound(g_sCheerSounds[i]);
|
|
}
|
|
|
|
void Radio_EmitToPlayers(const int[] players, int playersNum, const char[] sound, float scale = 1.0)
|
|
{
|
|
for (int i = 0; i < playersNum; i++)
|
|
{
|
|
float volume = Settings_GetVolume(players[i]);
|
|
if (volume <= 0.0)
|
|
continue;
|
|
|
|
EmitSoundToClient(players[i], sound, SOUND_FROM_PLAYER, SNDCHAN_VOICE,
|
|
SNDLEVEL_NORMAL, SND_NOFLAGS, volume * scale);
|
|
}
|
|
}
|
|
|
|
void Commands_Init()
|
|
{
|
|
g_RadioIndex = new StringMap();
|
|
for (int i = 0; i < sizeof(g_sRadioCommands); i++)
|
|
{
|
|
g_RadioIndex.SetValue(g_sRadioCommands[i], i);
|
|
AddCommandListener(Command_Radio, g_sRadioCommands[i]);
|
|
}
|
|
}
|
|
|
|
public Action Command_Radio(int client, const char[] command, int argc)
|
|
{
|
|
if (!g_cvEnabled.BoolValue)
|
|
return Plugin_Continue;
|
|
|
|
if (client < 1 || client > MaxClients || !IsClientInGame(client))
|
|
return Plugin_Continue;
|
|
|
|
// Console commands are case-insensitive, StringMap keys are not.
|
|
char cmd[16];
|
|
strcopy(cmd, sizeof(cmd), command);
|
|
for (int i = 0; cmd[i]; i++)
|
|
cmd[i] = CharToLower(cmd[i]);
|
|
|
|
int index;
|
|
if (!g_RadioIndex.GetValue(cmd, index))
|
|
return Plugin_Continue;
|
|
|
|
// With the custom commands disabled they fall through as unknown commands.
|
|
if (index >= RADIO_FIRST_CUSTOM && !g_cvCustom.BoolValue)
|
|
return Plugin_Continue;
|
|
|
|
if (Bans_IsBanned(client))
|
|
return Plugin_Handled;
|
|
|
|
int team = GetClientTeam(client);
|
|
if (team != CS_TEAM_T && team != CS_TEAM_CT)
|
|
return Plugin_Handled;
|
|
|
|
if (!IsPlayerAlive(client) && !g_cvDead.BoolValue)
|
|
return Plugin_Handled;
|
|
|
|
bool bLeader = Radio_IsLeader(client);
|
|
|
|
float flNow = GetGameTime();
|
|
bool bPlaySound = g_cvSound.BoolValue;
|
|
|
|
if (!bLeader)
|
|
{
|
|
if (flNow < g_flNextRadio[client])
|
|
return Plugin_Handled;
|
|
|
|
int limit = g_cvRoundLimit.IntValue;
|
|
if (limit > 0 && g_iRadioCount[client] >= limit)
|
|
return Plugin_Handled;
|
|
|
|
g_flNextRadio[client] = flNow + g_cvCooldown.FloatValue;
|
|
}
|
|
else if (flNow < g_flNextRadio[client])
|
|
{
|
|
// Leaders may spam the chat line, but the voice line still honours the cooldown.
|
|
bPlaySound = false;
|
|
}
|
|
else
|
|
{
|
|
g_flNextRadio[client] = flNow + g_cvCooldown.FloatValue;
|
|
}
|
|
|
|
g_iRadioCount[client]++;
|
|
|
|
bool bCustom = (index >= RADIO_FIRST_CUSTOM);
|
|
|
|
int[] players = new int[MaxClients];
|
|
int playersNum = 0;
|
|
for (int i = 1; i <= MaxClients; i++)
|
|
{
|
|
if (!IsClientInGame(i) || IsFakeClient(i) || GetClientTeam(i) != team)
|
|
continue;
|
|
|
|
// Players may opt out of the custom commands in !settings.
|
|
if (bCustom && !Settings_GetCustom(i))
|
|
continue;
|
|
|
|
players[playersNum++] = i;
|
|
}
|
|
|
|
if (!playersNum)
|
|
return Plugin_Handled;
|
|
|
|
// phrase must hold the coloured cheer line (10 glyphs each prefixed by a 7-byte \x07RRGGBB code)
|
|
char sound[32], phrase[128];
|
|
float soundScale = 1.0;
|
|
strcopy(sound, sizeof(sound), g_sRadioSounds[index]);
|
|
strcopy(phrase, sizeof(phrase), g_sRadioPhrases[index]);
|
|
|
|
// Stock "roger" picks one of two acknowledgements at random.
|
|
if (StrEqual(cmd, "roger") && GetRandomInt(0, 1))
|
|
{
|
|
strcopy(sound, sizeof(sound), SOUND_AFFIRMATIVE);
|
|
strcopy(phrase, sizeof(phrase), "Affirmative.");
|
|
}
|
|
else if (StrEqual(cmd, "thanks"))
|
|
{
|
|
int pick = GetRandomInt(0, sizeof(g_sThanksSounds) - 1);
|
|
strcopy(sound, sizeof(sound), g_sThanksSounds[pick]);
|
|
soundScale = g_flThanksScales[pick];
|
|
}
|
|
else if (StrEqual(cmd, "cheer"))
|
|
{
|
|
int pick = GetRandomInt(0, sizeof(g_sCheerSounds) - 1);
|
|
strcopy(sound, sizeof(sound), g_sCheerSounds[pick]);
|
|
soundScale = g_flCheerScales[pick];
|
|
Commands_BuildCheerPhrase(phrase, sizeof(phrase));
|
|
}
|
|
|
|
if (bPlaySound && sound[0])
|
|
Radio_EmitToPlayers(players, playersNum, sound, soundScale);
|
|
|
|
char text[256];
|
|
Format_RadioLine(client, bLeader, phrase, text, sizeof(text));
|
|
|
|
Radio_SendChat(client, players, playersNum, text);
|
|
|
|
return Plugin_Handled;
|
|
}
|