quake2-action/a_radio.c

704 lines
23 KiB
C
Raw Normal View History

2018-11-13 08:25:08 +00:00
/*
* Radio-related code for Action (formerly Axshun)
*
* -Fireblade
*/
#include "g_local.h"
void Cmd_Say_f (edict_t *ent, qboolean team, qboolean arg0, qboolean partner_msg);
// Each of the possible radio messages and their length
radio_msg_t male_radio_msgs[] = {
{ "1", 6 },
{ "2", 6 },
{ "3", 8 },
{ "4", 7 },
{ "5", 8 },
{ "6", 9 },
{ "7", 8 },
{ "8", 7 },
{ "9", 7 },
{ "10", 6 },
{ "back", 6 },
{ "cover", 7 },
{ "down", 13 },
{ "enemyd", 10 },
{ "enemys", 9 },
{ "forward", 6 },
{ "go", 6 },
{ "im_hit", 7 },
{ "left", 7 },
{ "reportin", 9 },
{ "right", 6 },
{ "taking_f", 22 },
{ "teamdown", 13 },
{ "treport", 12 },
{ "up", 4 },
{ "END", 0 }, // end of list delimiter
};
radio_msg_t female_radio_msgs[] = {
{ "1", 5 },
{ "2", 5 },
{ "3", 5 },
{ "4", 5 },
{ "5", 5 },
{ "6", 8 },
{ "7", 7 },
{ "8", 5 },
{ "9", 5 },
{ "10", 5 },
{ "back", 6 },
{ "cover", 5 },
{ "down", 6 },
{ "enemyd", 9 },
{ "enemys", 9 },
{ "forward", 8 },
{ "go", 6 },
{ "im_hit", 7 },
{ "left", 8 },
{ "reportin", 9 },
{ "right", 5 },
{ "taking_f", 22 },
{ "teamdown", 10 },
{ "treport", 12 },
{ "up", 6 },
{ "END", 0 }, // end of list delimiter
};
void PrecacheRadioMsgSet(radio_msg_t *msgs, char *base_path)
{
char msg_fullpath[2048];
while (strcmp(msgs->msg, "END"))
{
sprintf(msg_fullpath, "%s%s.wav", base_path, msgs->msg);
gi.soundindex(msg_fullpath);
msgs++;
}
}
void PrecacheRadioSounds()
{
gi.soundindex(RADIO_CLICK);
gi.soundindex(RADIO_DEATH_MALE);
gi.soundindex(RADIO_DEATH_FEMALE);
PrecacheRadioMsgSet(male_radio_msgs, "radio/male/");
PrecacheRadioMsgSet(female_radio_msgs, "radio/female/");
}
void DeleteFirstRadioQueueEntry(edict_t *ent)
{
int i;
if (ent->client->resp.radio_queue_size <= 0)
{
gi.dprintf("DeleteFirstRadioQueueEntry: attempt to delete without any entries\n");
return;
}
for (i = 1; i < ent->client->resp.radio_queue_size; i++)
{
memcpy(&(ent->client->resp.radio_queue[i - 1]),
&(ent->client->resp.radio_queue[i]),
sizeof(radio_queue_entry_t));
}
ent->client->resp.radio_queue_size--;
}
void DeleteRadioQueueEntry(edict_t *ent, int entry_num)
{
int i;
if (ent->client->resp.radio_queue_size <= entry_num)
{
gi.dprintf("DeleteRadioQueueEntry: attempt to delete out of range queue entry\n");
return;
}
for (i = entry_num + 1; i < ent->client->resp.radio_queue_size; i++)
{
memcpy(&(ent->client->resp.radio_queue[i - 1]),
&(ent->client->resp.radio_queue[i]),
sizeof(radio_queue_entry_t));
}
ent->client->resp.radio_queue_size--;
}
// RadioThink should be called once on each player per server frame.
void RadioThink(edict_t *ent)
{
// Try to clean things up, a bit....
if (ent->client->resp.radio_partner)
{
if (!ent->client->resp.radio_partner->inuse ||
ent->client->resp.radio_partner->client->resp.radio_partner != ent)
{
ent->client->resp.radio_partner = NULL;
}
}
if (ent->client->resp.partner_last_offered_to)
{
if (!ent->client->resp.partner_last_offered_to->inuse ||
ent->client->resp.partner_last_offered_to->solid == SOLID_NOT)
{
ent->client->resp.partner_last_offered_to = NULL;
}
}
if (ent->client->resp.partner_last_denied_from)
{
if (!ent->client->resp.partner_last_denied_from->inuse ||
ent->client->resp.partner_last_denied_from->solid == SOLID_NOT)
{
ent->client->resp.partner_last_denied_from = NULL;
}
}
// ................................
if (ent->client->resp.radio_power_off)
{
ent->client->resp.radio_queue_size = 0;
return;
}
if (ent->client->resp.radio_delay > 1)
{
ent->client->resp.radio_delay--;
return;
}
else if (ent->client->resp.radio_delay == 1)
{
DeleteFirstRadioQueueEntry(ent);
ent->client->resp.radio_delay = 0;
}
if (ent->client->resp.radio_queue_size)
{
char snd_play_cmd[512];
edict_t *from;
int check;
from = ent->client->resp.radio_queue[0].from_player;
if (!ent->client->resp.radio_queue[0].click &&
(!from->inuse || from->solid == SOLID_NOT || from->deadflag == DEAD_DEAD))
{
if (ent->client->resp.radio_queue[0].from_gender)
{
strcpy(ent->client->resp.radio_queue[0].soundfile, RADIO_DEATH_FEMALE);
ent->client->resp.radio_queue[0].length = RADIO_DEATH_FEMALE_LEN;
}
else
{
strcpy(ent->client->resp.radio_queue[0].soundfile, RADIO_DEATH_MALE);
ent->client->resp.radio_queue[0].length = RADIO_DEATH_MALE_LEN;
}
for (check = 1; check < ent->client->resp.radio_queue_size; check++)
{
if (ent->client->resp.radio_queue[check].from_player == from)
{
DeleteRadioQueueEntry(ent, check);
check--;
}
}
}
sprintf(snd_play_cmd, "play %s", ent->client->resp.radio_queue[0].soundfile);
stuffcmd(ent, snd_play_cmd);
ent->client->resp.radio_queue[0].now_playing = 1;
ent->client->resp.radio_delay = ent->client->resp.radio_queue[0].length;
}
}
int TotalNonClickMessagesInQueue(edict_t *ent)
{
int i, count = 0;
for (i = 0; i < ent->client->resp.radio_queue_size; i++)
{
if (!ent->client->resp.radio_queue[i].click)
count++;
}
return count;
}
void AppendRadioMsgToQueue(edict_t *ent, char *msg, int len, int click, edict_t *from_player)
{
radio_queue_entry_t *newentry;
if (ent->client->resp.radio_queue_size >= MAX_RADIO_QUEUE_SIZE)
{
gi.dprintf("AppendRadioMsgToQueue: Maximum radio queue size exceeded\n");
return;
}
newentry = &(ent->client->resp.radio_queue[ent->client->resp.radio_queue_size]);
if (strlen(msg) + 1 > MAX_SOUNDFILE_PATH_LEN)
{
gi.dprintf("Radio sound file path (%s) exceeded maximum length\n", msg);
*(msg + MAX_SOUNDFILE_PATH_LEN - 1) = 0;
}
strcpy(newentry->soundfile, msg);
newentry->from_player = from_player;
newentry->from_gender = from_player->client->resp.radio_gender;
newentry->now_playing = 0;
newentry->length = len;
newentry->click = click;
ent->client->resp.radio_queue_size++;
}
void InsertRadioMsgInQueueBeforeClick(edict_t *ent, char *msg, int len, edict_t *from_player)
{
radio_queue_entry_t *newentry;
if (ent->client->resp.radio_queue_size >= MAX_RADIO_QUEUE_SIZE)
{
gi.dprintf("InsertRadioMsgInQueueBeforeClick: Maximum radio queue size exceeded\n");
return;
}
memcpy(&(ent->client->resp.radio_queue[ent->client->resp.radio_queue_size]),
&(ent->client->resp.radio_queue[ent->client->resp.radio_queue_size - 1]),
sizeof(radio_queue_entry_t));
newentry = &(ent->client->resp.radio_queue[ent->client->resp.radio_queue_size - 1]);
if (strlen(msg) + 1 > MAX_SOUNDFILE_PATH_LEN)
{
gi.dprintf("Radio sound file path (%s) exceeded maximum length\n", msg);
*(msg + MAX_SOUNDFILE_PATH_LEN - 1) = 0;
}
strcpy(newentry->soundfile, msg);
newentry->from_player = from_player;
newentry->from_gender = from_player->client->resp.radio_gender;
newentry->now_playing = 0;
newentry->length = len;
newentry->click = 0;
ent->client->resp.radio_queue_size++;
}
void AddRadioMsg(edict_t *ent, char *msg, int len, edict_t *from_player)
{
if (ent->client->resp.radio_queue_size == 0 ||
(ent->client->resp.radio_queue[0].click &&
ent->client->resp.radio_queue_size == 1))
{
AppendRadioMsgToQueue(ent, RADIO_CLICK, RADIO_CLICK_LEN, 1, from_player);
AppendRadioMsgToQueue(ent, msg, len, 0, from_player);
AppendRadioMsgToQueue(ent, RADIO_CLICK, RADIO_CLICK_LEN, 1, from_player);
}
else // we have some msgs in it already...
{
if (TotalNonClickMessagesInQueue(ent) < MAX_RADIO_MSG_QUEUE_SIZE)
InsertRadioMsgInQueueBeforeClick(ent, msg, len, from_player);
// else ignore the message...
}
}
void RadioBroadcast(edict_t *ent, int partner, char *msg)
{
int j, i, msg_len, found;
edict_t *other;
radio_msg_t *radio_msgs;
char msg_fullpath[2048], *base_path;
if (ent->deadflag == DEAD_DEAD || ent->solid == SOLID_NOT)
return;
if (!teamplay->value)
{
if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
return; // don't allow in a non-team setup...
}
if (ent->client->resp.radio_power_off)
{
safe_centerprintf(ent, "Your radio is off!\n");
return;
}
if (partner && ent->client->resp.radio_partner == NULL)
{
safe_cprintf(ent, PRINT_HIGH, "You don't have a partner.\n");
return;
}
if (ent->client->resp.radio_gender)
{
radio_msgs = female_radio_msgs;
base_path = "radio/female/";
}
else
{
radio_msgs = male_radio_msgs;
base_path = "radio/male/";
}
i = found = 0;
while (strcmp(radio_msgs[i].msg, "END"))
{
if (!Q_stricmp(radio_msgs[i].msg, msg))
{
found = 1;
sprintf(msg_fullpath, "%s%s.wav", base_path, radio_msgs[i].msg);
msg_len = radio_msgs[i].length;
break;
}
i++;
}
if (!found)
{
safe_centerprintf(ent, "'%s' is not a valid radio message\n", msg);
return;
}
if (radiolog->value)
{
safe_cprintf(NULL, PRINT_CHAT, "[%s RADIO] %s: %s\n",
partner ? "PARTNER" : "TEAM",
ent->client->pers.netname,
msg);
}
for (j = 1; j <= game.maxclients; j++)
{
other = &g_edicts[j];
if (!other->inuse)
continue;
if (!other->client)
continue;
if (!OnSameTeam(ent, other))
continue;
if (partner && other != ent->client->resp.radio_partner)
continue;
AddRadioMsg(other, msg_fullpath, msg_len, ent);
}
}
void Cmd_Radio_f(edict_t *ent)
{
RadioBroadcast(ent, ent->client->resp.radio_partner_mode, gi.args());
}
void Cmd_Radiopartner_f(edict_t *ent)
{
RadioBroadcast(ent, 1, gi.args());
}
void Cmd_Radioteam_f(edict_t *ent)
{
RadioBroadcast(ent, 0, gi.args());
}
void Cmd_Radiogender_f(edict_t *ent)
{
char *arg;
if (!teamplay->value)
{
if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
return; // don't allow in a non-team setup...
}
arg = gi.args();
if (arg == NULL || !strlen(arg))
{
if (ent->client->resp.radio_gender)
safe_cprintf(ent, PRINT_HIGH, "Radio gender currently set to female\n");
else
safe_cprintf(ent, PRINT_HIGH, "Radio gender currently set to male\n");
return;
}
if (!Q_stricmp(arg, "male"))
{
safe_cprintf(ent, PRINT_HIGH, "Radio gender set to male\n");
ent->client->resp.radio_gender = 0;
}
else if (!Q_stricmp(arg, "female"))
{
safe_cprintf(ent, PRINT_HIGH, "Radio gender set to female\n");
ent->client->resp.radio_gender = 1;
}
else
{
safe_cprintf(ent, PRINT_HIGH, "Invalid gender selection, try 'male' or 'female'\n");
}
}
void Cmd_Radio_power_f(edict_t *ent)
{
if (!teamplay->value)
{
if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
return; // don't allow in a non-team setup...
}
ent->client->resp.radio_power_off = !ent->client->resp.radio_power_off;
if (ent->client->resp.radio_power_off)
{
safe_centerprintf(ent, "Radio switched off\n");
stuffcmd(ent, "play " RADIO_CLICK);
}
else
{
safe_centerprintf(ent, "Radio switched on\n");
stuffcmd(ent, "play " RADIO_CLICK);
}
}
void Cmd_Channel_f(edict_t *ent)
{
if (!teamplay->value)
{
if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
return; // don't allow in a non-team setup...
}
ent->client->resp.radio_partner_mode = !ent->client->resp.radio_partner_mode;
if (ent->client->resp.radio_partner_mode)
{
safe_centerprintf(ent, "Channel set to 1, partner channel\n");
}
else
{
safe_centerprintf(ent, "Channel set to 0, team channel\n");
}
}
// DetermineViewedTeammate: determine the current player you're viewing (only looks for live teammates)
// Modified from SetIDView (which was used from Zoid's CTF)
edict_t *DetermineViewedTeammate(edict_t *ent)
{
vec3_t forward, dir;
trace_t tr;
edict_t *who, *best;
//FIREBLADE, suggested by hal[9k] 3/11/1999
float bd = 0.9;
//FIREBLADE
float d;
int i;
AngleVectors(ent->client->v_angle, forward, NULL, NULL);
VectorScale(forward, 8192, forward);
VectorAdd(ent->s.origin, forward, forward);
PRETRACE();
tr = gi.trace(ent->s.origin, NULL, NULL, forward, ent, MASK_SOLID);
POSTTRACE();
if (tr.fraction < 1 && tr.ent && tr.ent->client) {
return NULL;
}
AngleVectors(ent->client->v_angle, forward, NULL, NULL);
best = NULL;
for (i = 1; i <= maxclients->value; i++) {
who = g_edicts + i;
if (!who->inuse)
continue;
VectorSubtract(who->s.origin, ent->s.origin, dir);
VectorNormalize(dir);
d = DotProduct(forward, dir);
if (d > bd && loc_CanSee(ent, who) &&
who->solid != SOLID_NOT &&
who->deadflag != DEAD_DEAD &&
OnSameTeam(who, ent)) {
bd = d;
best = who;
}
}
if (bd > 0.90)
{
return best;
}
return NULL;
}
void Cmd_Partner_f(edict_t *ent)
{
edict_t *target;
char *genderstr;
if (!teamplay->value)
{
if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
return; // don't allow in a non-team setup...
}
if (ent->deadflag == DEAD_DEAD || ent->solid == SOLID_NOT)
return;
if (ent->client->resp.radio_partner &&
!ent->client->resp.radio_partner->inuse)
{ // just in case RadioThink hasn't caught it yet... avoid any problems
ent->client->resp.radio_partner = NULL;
}
if (ent->client->resp.radio_partner)
{
safe_centerprintf(ent, "You already have a partner, %s\n",
ent->client->resp.radio_partner->client->pers.netname);
return;
}
target = DetermineViewedTeammate(ent);
if (target == NULL)
{
safe_centerprintf(ent, "No potential partner selected\n");
return;
}
if (target->client->resp.radio_partner)
{
safe_centerprintf(ent, "%s already has a partner\n", target->client->pers.netname);
return;
}
if (target->client->resp.partner_last_offered_to == ent &&
ent->client->resp.partner_last_offered_from == target)
{
safe_centerprintf(ent, "%s is now your partner\n", target->client->pers.netname);
safe_centerprintf(target, "%s is now your partner\n", ent->client->pers.netname);
ent->client->resp.radio_partner = target;
target->client->resp.radio_partner = ent;
ent->client->resp.partner_last_offered_from = NULL;
target->client->resp.partner_last_offered_to = NULL;
return;
}
if (target->client->resp.partner_last_denied_from == ent)
{
safe_centerprintf(ent, "%s has already denied you\n",
target->client->pers.netname);
return;
}
if (target == ent->client->resp.partner_last_offered_to)
{
if (IsFemale(target))
genderstr = "her";
else if (IsNeutral(target))
genderstr = "it";
else
genderstr = "him";
safe_centerprintf(ent, "Already awaiting confirmation from %s\n", genderstr);
return;
}
if (IsFemale(ent))
genderstr = "her";
else if (IsNeutral(ent))
genderstr = "it";
else
genderstr = "him";
safe_centerprintf(ent, "Awaiting confirmation from %s\n", target->client->pers.netname);
safe_centerprintf(target, "%s offers to be your partner\n"
"To accept:\nView %s and use the 'partner' command\n"
"To deny:\nUse the 'deny' command\n",
ent->client->pers.netname, genderstr);
ent->client->resp.partner_last_offered_to = target;
target->client->resp.partner_last_offered_from = ent;
}
void Cmd_Unpartner_f(edict_t *ent)
{
edict_t *target;
if (!teamplay->value)
{
if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
return; // don't allow in a non-team setup...
}
if (ent->client->resp.radio_partner &&
!ent->client->resp.radio_partner->inuse)
{ // just in case RadioThink hasn't caught it yet... avoid any problems
ent->client->resp.radio_partner = NULL;
}
target = ent->client->resp.radio_partner;
if (target == NULL)
{
safe_centerprintf(ent, "You don't have a partner\n");
return;
}
if (target->client->resp.radio_partner == ent)
{
safe_centerprintf(target, "%s broke your partnership\n",
ent->client->pers.netname);
target->client->resp.radio_partner = NULL;
}
safe_centerprintf(ent, "You broke your partnership with %s\n",
target->client->pers.netname);
ent->client->resp.radio_partner = NULL;
}
void Cmd_Deny_f(edict_t *ent)
{
edict_t *target;
if (!teamplay->value)
{
if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
return; // don't allow in a non-team setup...
}
if (ent->deadflag == DEAD_DEAD || ent->solid == SOLID_NOT)
return;
target = ent->client->resp.partner_last_offered_from;
if (target && target->inuse)
{
safe_centerprintf(ent, "You denied %s\n",
target->client->pers.netname);
safe_centerprintf(target, "%s has denied you\n",
ent->client->pers.netname);
ent->client->resp.partner_last_denied_from = target;
ent->client->resp.partner_last_offered_from = NULL;
if (target->client->resp.partner_last_offered_to == ent)
target->client->resp.partner_last_offered_to = NULL;
}
else
{
safe_centerprintf(ent, "No one has offered to be your partner\n");
return;
}
}
void Cmd_Say_partner_f(edict_t *ent)
{
if (!teamplay->value)
{
if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
return; // don't allow in a non-team setup...
}
if (ent->client->resp.radio_partner == NULL)
{
safe_cprintf(ent, PRINT_HIGH, "You don't have a partner.\n");
return;
}
Cmd_Say_f(ent, false, false, true);
}