From 9269c0eddfa27d25006234984372acf8590bad23 Mon Sep 17 00:00:00 2001 From: Aaron Dean <8dino2@gmail.com> Date: Tue, 5 Sep 2023 12:47:49 -0400 Subject: [PATCH] Added match and radio --- actionlite/a_match.cpp | 608 +++++++++++++++++++++++++++++ actionlite/a_match.h | 17 + actionlite/a_radio.cpp | 817 +++++++++++++++++++++++++++++++++++++++ actionlite/a_radio.h | 55 +++ actionlite/a_team.cpp | 17 +- actionlite/bg_local.h | 40 +- actionlite/ctf/g_ctf.cpp | 46 +-- actionlite/g_local.h | 47 ++- actionlite/g_spawn.cpp | 10 +- actionlite/p_weapon.cpp | 4 +- 10 files changed, 1600 insertions(+), 61 deletions(-) create mode 100644 actionlite/a_match.cpp create mode 100644 actionlite/a_match.h create mode 100644 actionlite/a_radio.cpp create mode 100644 actionlite/a_radio.h diff --git a/actionlite/a_match.cpp b/actionlite/a_match.cpp new file mode 100644 index 0000000..3980e2e --- /dev/null +++ b/actionlite/a_match.cpp @@ -0,0 +1,608 @@ +#include "g_local.h" +#include "a_match.h" + +void SendScores(void) +{ + unsigned int mins, secs, gametime = level.matchTime; + + mins = gametime / 60; + secs = gametime % 60; + if(teamCount == 3) { + gi.bprintf(PRINT_HIGH, "\x9D\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9F\n"); + gi.bprintf(PRINT_HIGH, " Team 1 Score - Team 2 Score - Team 3 Score\n"); + gi.bprintf(PRINT_HIGH, " [%d] [%d] [%d]\n", teams[TEAM1].score, teams[TEAM2].score, teams[TEAM3].score); + gi.bprintf(PRINT_HIGH, " Total Played Time: %d:%02d\n", mins, secs); + gi.bprintf(PRINT_HIGH, "\x9D\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9F\n"); + } else { + int team1score = 0, team2score = 0; + + if(ctf->value) { + GetCTFScores(&team1score, &team2score); + } else { + team1score = teams[TEAM1].score; + team2score = teams[TEAM2].score; + } + gi.bprintf(PRINT_HIGH, "\x9D\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9F\n"); + gi.bprintf(PRINT_HIGH, " Team 1 Score - Team 2 Score\n"); + gi.bprintf(PRINT_HIGH, " [%d] [%d]\n", team1score, team2score); + gi.bprintf(PRINT_HIGH, " Total Played Time: %d:%02d\n", mins, secs); + gi.bprintf(PRINT_HIGH, "\x9D\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9E\x9F\n"); + } + gi.bprintf(PRINT_HIGH, "Match is over, waiting for next map, please vote a new one..\n"); + + #if USE_AQTION + // Needed to add this here because Matchmode does not call BeginIntermission, but other teamplay modes do call it + if (stat_logs->value) { + LogMatch(); // Generates end of game stats + LogEndMatchStats(); // Generates end of match stats + } + #endif + // Stats: Reset roundNum + game.roundNum = 0; + // Stats end + + #ifndef NO_BOTS + // Clear LTK bot names + LTKClearBotNames(); + #endif +} + +void Cmd_Sub_f(edict_t * ent) +{ + if (!matchmode->value) { + gi.cprintf(ent, PRINT_HIGH, "This command needs matchmode to be enabled\n"); + return; + } + + if (ent->client->resp.team == NOTEAM) { + gi.cprintf(ent, PRINT_HIGH, "You need to be on a team for that...\n"); + return; + } + if (!ent->client->resp.subteam) { + killPlayer(ent, true); // lets kill em. + gi.bprintf(PRINT_HIGH, "%s is now a substitute for %s\n", ent->client->pers.netname, teams[ent->client->resp.team].name); + ent->client->resp.subteam = ent->client->resp.team; + return; + } + + gi.bprintf(PRINT_HIGH, "%s is no longer a substitute for %s\n", ent->client->pers.netname, teams[ent->client->resp.team].name); + ent->client->resp.subteam = 0; + if (team_round_going && !(gameSettings & GS_ROUNDBASED)) + { + PutClientInServer (ent); + AddToTransparentList (ent); + } +} + + +/* +============== +MM_SetCaptain +============== +Set ent to be a captain of team, ent can be NULL to remove captain +*/ +void MM_SetCaptain( int teamNum, edict_t *ent ) +{ + int i; + edict_t *oldCaptain = teams[teamNum].captain; + + if (teamNum == NOTEAM) + ent = NULL; + + teams[teamNum].captain = ent; + if (esp->value) { + EspSetLeader(teamNum, ent); + } + if (!ent) { + if (!team_round_going || (gameSettings & GS_ROUNDBASED)) { + if (teams[teamNum].ready) { + char temp[128]; + Com_sprintf( temp, sizeof( temp ), "%s is no longer ready to play!", teams[teamNum].name ); + CenterPrintAll( temp ); + } + teams[teamNum].ready = 0; + } + if (oldCaptain) { + gi.bprintf( PRINT_HIGH, "%s is no longer %s's captain\n", oldCaptain->client->pers.netname, teams[teamNum].name ); + } + teams[teamNum].locked = 0; + return; + } + + if (ent != oldCaptain) { + gi.bprintf( PRINT_HIGH, "%s is now %s's captain\n", ent->client->pers.netname, teams[teamNum].name ); + gi.cprintf( ent, PRINT_CHAT, "You are the captain of '%s'\n", teams[teamNum].name ); + gi.sound( &g_edicts[0], CHAN_VOICE | CHAN_NO_PHS_ADD, gi.soundindex( "misc/comp_up.wav" ), 1.0, ATTN_NONE, 0.0 ); + + for (i = TEAM1; i <= teamCount; i++) { + if (i != teamNum && teams[i].wantReset) + gi.cprintf( ent, PRINT_HIGH, "Team %i wants to reset scores, type 'resetscores' to accept\n", i ); + } + } +} + +void MM_LeftTeam( edict_t *ent ) +{ + int teamNum = ent->client->resp.team; + + if (teams[teamNum].captain == ent) { + MM_SetCaptain( teamNum, NULL ); + } + ent->client->resp.subteam = 0; +} + +qboolean TeamsReady( void ) +{ + int i, ready = 0; + + for( i = TEAM1; i <= teamCount; i++ ) + { + if( teams[i].ready ) + ready ++; + else if( TeamHasPlayers(i) ) + return false; + } + return (ready >= 2); +} + +void Cmd_Captain_f(edict_t * ent) +{ + int teamNum; + edict_t *oldCaptain; + + // Aliases `captain` command to `volunteer` if Espionage is enabled + if (esp->value && !matchmode->value) { + Cmd_Volunteer_f(ent); + return; + } + + if (!matchmode->value) { + gi.cprintf(ent, PRINT_HIGH, "This command needs matchmode to be enabled\n"); + return; + } + + teamNum = ent->client->resp.team; + if (teamNum == NOTEAM) { + gi.cprintf(ent, PRINT_HIGH, "You need to be on a team for that...\n"); + return; + } + + oldCaptain = teams[teamNum].captain; + if (oldCaptain == ent) { + MM_SetCaptain( teamNum, NULL ); + if (esp->value) { + EspSetLeader(teamNum, NULL); + } + return; + } + + if (oldCaptain) { + gi.cprintf( ent, PRINT_HIGH, "Your team already has a captain\n" ); + return; + } + + MM_SetCaptain( teamNum, ent ); + if (esp->value) { + EspSetLeader( teamNum, ent ); + } +} + +//extern int started; // AQ2:M - Matchmode - Used for ready command +void Cmd_Ready_f(edict_t * ent) +{ + char temp[128]; + int teamNum; + team_t *team; + + if (!matchmode->value) { + gi.cprintf(ent, PRINT_HIGH, "This command needs matchmode to be enabled\n"); + return; + } + + teamNum = ent->client->resp.team; + if (teamNum == NOTEAM) { + gi.cprintf( ent, PRINT_HIGH, "You need to be on a team for that...\n" ); + return; + } + + team = &teams[teamNum]; + if (team->captain != ent) { + gi.cprintf( ent, PRINT_HIGH, "You need to be a captain for that\n" ); + return; + } + + if (!(gameSettings & GS_ROUNDBASED) && team_round_going) { + if(teamdm->value) + gi.cprintf(ent, PRINT_HIGH, "You can't unready in teamdm, use 'pausegame' instead\n"); + else + gi.cprintf(ent, PRINT_HIGH, "You can't unready in ctf, use 'pausegame' instead\n"); + return; + } + + team->ready = !team->ready; + Com_sprintf( temp, sizeof( temp ), "%s %s ready to play!", team->name, (team->ready) ? "is" : "is no longer" ); + CenterPrintAll( temp ); +} + +void Cmd_Teamname_f(edict_t * ent) +{ + int i, argc, teamNum; + char temp[32]; + team_t *team; + + if (!matchmode->value) { + gi.cprintf(ent, PRINT_HIGH, "This command needs matchmode to be enabled\n"); + return; + } + + if(ctf->value) { + gi.cprintf(ent, PRINT_HIGH, "You can't change teamnames in CTF mode\n"); + return; + } + + if(esp->value) { + gi.cprintf(ent, PRINT_HIGH, "You can't change teamnames in Espionage mode\n"); + return; + } + + teamNum = ent->client->resp.team; + if (teamNum == NOTEAM) { + gi.cprintf( ent, PRINT_HIGH, "You need to be on a team for that...\n" ); + return; + } + + team = &teams[teamNum]; + if (team->captain != ent) { + gi.cprintf( ent, PRINT_HIGH, "You need to be a captain for that\n" ); + return; + } + + if (team->ready) { + gi.cprintf( ent, PRINT_HIGH, "You can't use this while 'ready'\n" ); + return; + } + + if (team_round_going || team_game_going) { + gi.cprintf(ent, PRINT_HIGH, "You can't use this while playing\n"); + return; + } + + argc = gi.argc(); + if (argc < 2) { + gi.cprintf( ent, PRINT_HIGH, "Your team name is %s\n", team->name ); + return; + } + + Q_strncpyz(temp, gi.argv(1), sizeof(temp)); + for (i = 2; i < argc; i++) { + Q_strncatz(temp, " ", sizeof(temp)); + Q_strncatz(temp, gi.argv(i), sizeof(temp)); + } + temp[18] = 0; + + if (!temp[0]) + strcpy( temp, "noname" ); + + gi.dprintf("%s (team %i) is now known as %s\n", team->name, teamNum, temp); + IRC_printf(IRC_T_GAME, "%n (team %k) is now known as %n", team->name, teamNum, temp); + strcpy(team->name, temp); + gi.cprintf(ent, PRINT_HIGH, "New team name: %s\n", team->name); + +} + +void Cmd_Teamskin_f(edict_t * ent) +{ + char *s, newskin[32]; + int i, teamNum; + team_t *team; + edict_t *e; + + if (!esp->value) { + gi.cprintf(ent, PRINT_HIGH, "Espionage skins are set in the .esp file\n"); + return; + } + + if (!matchmode->value) { + gi.cprintf(ent, PRINT_HIGH, "This command needs matchmode to be enabled\n"); + return; + } + + teamNum = ent->client->resp.team; + if (teamNum == NOTEAM) { + gi.cprintf(ent, PRINT_HIGH, "You need to be on a team for that...\n"); + return; + } + + team = &teams[teamNum]; + if (team->captain != ent) { + gi.cprintf(ent, PRINT_HIGH, "You need to be a captain for that\n"); + return; + } + if (team->ready) { + gi.cprintf(ent, PRINT_HIGH, "You can't use this while 'Ready'\n"); + return; + } + if (team_round_going || team_game_going) { + gi.cprintf(ent, PRINT_HIGH, "You can't use this while playing\n"); + return; + } + if (gi.argc() < 2) { + gi.cprintf(ent, PRINT_HIGH, "Your team skin is %s\n", team->skin); + return; + } + + s = gi.argv(1); + Q_strncpyz(newskin, s, sizeof(newskin)); + if(ctf->value) { + s = strchr(newskin, '/'); + if(s) + s[1] = 0; + else + strcpy(newskin, "male/"); + Q_strncatz(newskin, teamNum == 1 ? CTF_TEAM1_SKIN : CTF_TEAM2_SKIN, sizeof(newskin)); + } + + if (!strcmp(newskin, team->skin)) { + gi.cprintf(ent, PRINT_HIGH, "Your team skin is already %s\n", newskin); + return; + } + + Q_strncpyz(team->skin, newskin, sizeof(team->skin)); + + Com_sprintf(team->skin_index, sizeof(team->skin_index), "../players/%s_i", team->skin ); + level.pic_teamskin[teamNum] = gi.imageindex(team->skin_index); + for (i = 0, e = &g_edicts[1]; i < game.maxclients; i++, e++) { //lets update players skin + if (!e->inuse || !e->client) + continue; + + if (e->client->resp.team == teamNum) + AssignSkin(e, team->skin, false); + } + gi.cprintf(ent, PRINT_HIGH, "New team skin: %s\n", team->skin); +} + +void Cmd_TeamLock_f(edict_t *ent, int a_switch) +{ + char msg[128], *s; + int teamNum, i; + team_t *team; + + if (!matchmode->value) { + gi.cprintf(ent, PRINT_HIGH, "This command needs matchmode to be enabled\n"); + return; + } + + if (!mm_allowlock->value) { + gi.cprintf(ent, PRINT_HIGH, "Team locking is disabled on this server\n"); + return; + } + + //Admin can lock teams + if (ent->client->pers.admin && gi.argc() > 1) + { + s = gi.argv(1); + teamNum = TP_GetTeamFromArg(s); + if (teamNum < 1) { + gi.cprintf(ent, PRINT_HIGH, "Unknown team '%s'.\n", s); + return; + } + team = &teams[teamNum]; + if (a_switch == team->locked) { + gi.cprintf(ent, PRINT_HIGH, "Team %s locked\n", (a_switch) ? "is already" : "isn't"); + return; + } + if (a_switch) { + gclient_t *client; + + for (i = 0, client = game.clients; i < game.maxclients; i++, client++) { + if (client->pers.connected && client->resp.team == teamNum) + break; + } + if (i == game.maxclients) { + gi.cprintf(ent, PRINT_HIGH, "You can't lock teams without players\n"); + return; + } + } + } + else + { + teamNum = ent->client->resp.team; + if (teamNum == NOTEAM) { + gi.cprintf(ent, PRINT_HIGH, "You are not on a team\n"); + return; + } + + team = &teams[teamNum]; + if (team->captain != ent) { + gi.cprintf(ent, PRINT_HIGH, "You are not the captain of your team\n"); + return; + } + + if (a_switch == team->locked) { + gi.cprintf(ent, PRINT_HIGH, "Your team %s locked\n", (a_switch) ? "is already" : "isn't"); + return; + } + } + + team->locked = a_switch; + Com_sprintf( msg, sizeof( msg ), "%s is now %s", team->name, (a_switch) ? "locked" : "unlocked" ); + CenterPrintAll(msg); +} + +void Cmd_SetAdmin_f (edict_t * ent) +{ + if (ent->client->pers.admin) { + gi.cprintf( ent, PRINT_HIGH, "You are no longer a match admin.\n" ); + gi.dprintf( "%s is no longer a match admin\n", ent->client->pers.netname ); + ent->client->pers.admin = 0; + } + + if(!matchmode->value) { + gi.cprintf(ent, PRINT_HIGH, "Matchmode is not enabled on this server.\n"); + return; + } + + if (strcmp( mm_adminpwd->string, "0" ) == 0) { + gi.cprintf( ent, PRINT_HIGH, "Match admin mode is not enabled on this server..\n" ); + return; + } + + if (gi.argc() < 2) { + gi.cprintf (ent, PRINT_HIGH, "Usage: matchadmin \n"); + return; + } + + if (strcmp( mm_adminpwd->string, gi.argv(1) )) { + gi.cprintf( ent, PRINT_HIGH, "Wrong password\n" ); + return; + } + + gi.cprintf (ent, PRINT_HIGH, "You are now a match admin.\n"); + gi.dprintf ("%s is now a match admin\n", ent->client->pers.netname); + IRC_printf (IRC_T_GAME, "%n is now a match admin", ent->client->pers.netname); + ent->client->pers.admin = 1; +} + +void Cmd_ResetScores_f(edict_t * ent) +{ + int i, teamNum, otherCaptain = 0; + + if (!matchmode->value) { + gi.cprintf(ent, PRINT_HIGH, "This command needs matchmode to be enabled\n"); + return; + } + + if (ent->client->pers.admin) //Admins can resetscores + { + ResetScores(true); + gi.bprintf(PRINT_HIGH, "Scores and time were reset by match admin %s\n", ent->client->pers.netname); + return; + } + + teamNum = ent->client->resp.team; + if (teamNum == NOTEAM) { + gi.cprintf(ent, PRINT_HIGH, "You need to be on a team for that...\n"); + return; + } + if (teams[teamNum].captain != ent) { + gi.cprintf(ent, PRINT_HIGH, "You need to be a captain for that\n"); + return; + } + + if (teams[teamNum].wantReset) + { + teams[teamNum].wantReset = 0; + for (i = TEAM1; ivalue) { + gi.cprintf(ent, PRINT_HIGH, "This command needs matchmode to be enabled\n"); + return; + } + + if ((int)mm_pausecount->value < 1) { + gi.cprintf(ent, PRINT_HIGH, "Pause is disabled, mm_pausecount is 0\n"); + return; + } + + if (mm_pausetime->value < FRAMETIME) { + gi.cprintf( ent, PRINT_HIGH, "Pause is disabled, mm_pausetime is 0\n" ); + return; + } + + teamNum = ent->client->resp.team; + if (teamNum == NOTEAM) { + gi.cprintf(ent, PRINT_HIGH, "You need to be on a team for that...\n"); + return; + } + + if (!team_round_going) { + gi.cprintf(ent, PRINT_HIGH, "No match running, so why pause?\n"); + //return; + } + + if (ent->client->resp.subteam) { + gi.cprintf(ent, PRINT_HIGH, "You can't pause when substitute\n"); + return; + } + + if(pause) + { + if(level.pauseFrames > 0) + { + gi.cprintf(ent, PRINT_HIGH, "Game is already paused you silly\n"); + return; + } + if (level.intermission_framenum) { + gi.cprintf(ent, PRINT_HIGH, "Can't pause in an intermission.\n"); + return; + } + if(teams[teamNum].pauses_used >= (int)mm_pausecount->value) + { + gi.cprintf(ent, PRINT_HIGH, "Your team doesn't have any pauses left.\n"); + return; + } + teams[teamNum].pauses_used++; + + CenterPrintAll (va("Game paused by %s\nTeam %i has %i pauses left", ent->client->pers.netname, ent->client->resp.team, (int)mm_pausecount->value - teams[ent->client->resp.team].pauses_used)); + level.pauseFrames = (int)(mm_pausetime->value * 60.0f * HZ); + lastPaused = teamNum; + } + else + { + if (!level.pauseFrames) + { + gi.cprintf(ent, PRINT_HIGH, "Game is not paused\n"); + return; + } + if(!lastPaused) + { + gi.cprintf(ent, PRINT_HIGH, "Already unpausing\n"); + return; + } + if(lastPaused != teamNum) + { + gi.cprintf(ent, PRINT_HIGH, "You can't unpause when paused by the other team\n"); + return; + } + level.pauseFrames = 10 * HZ; + lastPaused = 0; + } +} + diff --git a/actionlite/a_match.h b/actionlite/a_match.h new file mode 100644 index 0000000..d937e7e --- /dev/null +++ b/actionlite/a_match.h @@ -0,0 +1,17 @@ +#define IS_CAPTAIN(ent) (teams[(ent)->client->resp.team].captain == (ent)) +#define HAVE_CAPTAIN(teamNum) (teams[(teamNum)].captain) + +void SendScores (void); +bool TeamsReady( void ); +void MM_LeftTeam( edict_t * ent ); +void Cmd_Captain_f (edict_t * ent); +void Cmd_Ready_f (edict_t * ent); +void Cmd_Sub_f (edict_t * ent); +void Cmd_Teamname_f (edict_t * ent); +void Cmd_Teamskin_f (edict_t * ent); +void Cmd_TeamLock_f (edict_t * ent, int a_switch); +int CheckForCaptains (int cteam); + +void Cmd_SetAdmin_f (edict_t * ent); +void Cmd_TogglePause_f(edict_t * ent, bool pause); +void Cmd_ResetScores_f(edict_t * ent); diff --git a/actionlite/a_radio.cpp b/actionlite/a_radio.cpp new file mode 100644 index 0000000..ac6244a --- /dev/null +++ b/actionlite/a_radio.cpp @@ -0,0 +1,817 @@ +//----------------------------------------------------------------------------- +// Radio-related code for Action (formerly Axshun) +// +// -Fireblade +// +// $Id: a_radio.c,v 1.6 2004/04/08 23:19:51 slicerdw Exp $ +// +//----------------------------------------------------------------------------- +// $Log: a_radio.c,v $ +// Revision 1.6 2004/04/08 23:19:51 slicerdw +// Optimized some code, added a couple of features and fixed minor bugs +// +// Revision 1.5 2002/03/26 21:49:01 ra +// Bufferoverflow fixes +// +// Revision 1.4 2001/09/28 13:48:34 ra +// I ran indent over the sources. All .c and .h files reindented. +// +// Revision 1.3 2001/09/05 14:33:57 slicerdw +// Added Fix's from the 2.1 release +// +// Revision 1.2 2001/08/15 14:50:48 slicerdw +// Added Flood protections to Radio & Voice, Fixed the sniper bug AGAIN +// +// Revision 1.1.1.1 2001/05/06 17:24:29 igor_rock +// This is the PG Bund Edition V1.25 with all stuff laying around here... +// +//----------------------------------------------------------------------------- + +#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 +typedef struct radio_msg_s + +{ + + char *msg; // the msg name + + + + int length; // length in server frames (ie tenths of a second), rounded up + + int sndIndex; + + + +} radio_msg_t; + +static radio_msg_t male_radio_msgs[] = { + {"1", 6, 0}, + {"2", 6, 0}, + {"3", 8, 0}, + {"4", 7, 0}, + {"5", 8, 0}, + {"6", 9, 0}, + {"7", 8, 0}, + {"8", 7, 0}, + {"9", 7, 0}, + {"10", 6, 0}, + {"back", 6, 0}, + {"cover", 7, 0}, + {"down", 13, 0}, + {"enemyd", 10, 0}, + {"enemys", 9, 0}, + {"forward", 6, 0}, + {"go", 6, 0}, + {"im_hit", 7, 0}, + {"left", 7, 0}, + {"reportin", 9, 0}, + {"right", 6, 0}, + {"taking_f", 22, 0}, + {"teamdown", 13, 0}, + {"treport", 12, 0}, + {"up", 4, 0} + //{"END", 0, 0} // end of list delimiter +}; + +static radio_msg_t female_radio_msgs[] = { + {"1", 5, 0}, + {"2", 5, 0}, + {"3", 5, 0}, + {"4", 5, 0}, + {"5", 5, 0}, + {"6", 8, 0}, + {"7", 7, 0}, + {"8", 5, 0}, + {"9", 5, 0}, + {"10", 5, 0}, + {"back", 6, 0}, + {"cover", 5, 0}, + {"down", 6, 0}, + {"enemyd", 9, 0}, + {"enemys", 9, 0}, + {"forward", 8, 0}, + {"go", 6, 0}, + {"im_hit", 7, 0}, + {"left", 8, 0}, + {"reportin", 9, 0}, + {"right", 5, 0}, + {"taking_f", 22, 0}, + {"teamdown", 10, 0}, + {"treport", 12, 0}, + {"up", 6, 0} + //{"END", 0, 0}, // end of list delimiter +}; + +static const int numMaleSnds = ( sizeof( male_radio_msgs ) / sizeof( male_radio_msgs[0] ) ); +static const int numFemaleSnds = ( sizeof( female_radio_msgs ) / sizeof( female_radio_msgs[0] ) ); + +#define RADIO_MALE_DIR "radio/male/" +#define RADIO_FEMALE_DIR "radio/female/" +#define RADIO_CLICK 0 +#define RADIO_DEATH_MALE 1 +#define RADIO_DEATH_FEMALE 2 + +radio_msg_t globalRadio[] = { + {"radio/click.wav", 2, 0}, + {"radio/male/rdeath.wav", 27, 0}, + {"radio/female/rdeath.wav", 30, 0} +}; + +void PrecacheRadioSounds () +{ + int i; + char path[MAX_QPATH]; + + globalRadio[RADIO_CLICK].sndIndex = gi.soundindex(globalRadio[RADIO_CLICK].msg); + globalRadio[RADIO_DEATH_MALE].sndIndex = gi.soundindex(globalRadio[RADIO_DEATH_MALE].msg); + globalRadio[RADIO_DEATH_FEMALE].sndIndex = gi.soundindex(globalRadio[RADIO_DEATH_FEMALE].msg); + + //male + for(i = 0; i < numMaleSnds; i++) + { + Com_sprintf (path, sizeof(path), "%s%s.wav", RADIO_MALE_DIR, male_radio_msgs[i].msg); + male_radio_msgs[i].sndIndex = gi.soundindex(path); + } + + //female + for(i = 0; i < numFemaleSnds; i++) + { + Com_sprintf (path, sizeof(path), "%s%s.wav", RADIO_FEMALE_DIR, female_radio_msgs[i].msg); + female_radio_msgs[i].sndIndex = gi.soundindex(path); + } +} + +static void DeleteRadioQueueEntry( radio_t *radio, int entry_num ) +{ + int i; + + if (radio->queue_size <= entry_num) + { + gi.dprintf("DeleteRadioQueueEntry: attempt to delete out of range queue entry: %i\n", entry_num); + return; + } + + for (i = entry_num + 1; i < radio->queue_size; i++) + { + memcpy(&radio->queue[i - 1], &radio->queue[i], sizeof(radio_queue_entry_t)); + } + + radio->queue_size--; +} + +// RadioThink should be called once on each player per server frame. +void RadioThink (edict_t * ent) +{ + radio_t *radio = &ent->client->resp.radio; + + // Try to clean things up, a bit.... + if (radio->partner) + { + if (!radio->partner->inuse || + radio->partner->client->resp.radio.partner != ent) + { + radio->partner = NULL; + } + } + if (radio->partner_last_offered_to) + { + if (!radio->partner_last_offered_to->inuse || + radio->partner_last_offered_to->solid == SOLID_NOT) + { + radio->partner_last_offered_to = NULL; + } + } + if (radio->partner_last_denied_from) + { + if (!radio->partner_last_denied_from->inuse || + radio->partner_last_denied_from->solid == SOLID_NOT) + { + radio->partner_last_denied_from = NULL; + } + } + // ................................ + + if (radio->power_off) + { + radio->queue_size = 0; + return; + } + + if (radio->delay > 0) + { + radio->delay--; + if (radio->delay) + return; + } + + + if (radio->queue_size) + { + edict_t *from; + int check; + + from = radio->queue[0].from_player; + + if (!radio->queue[0].click && (!from->inuse || !IS_ALIVE(from))) + { + if (radio->queue[0].from_gender) + { + radio->queue[0].sndIndex = globalRadio[RADIO_DEATH_FEMALE].sndIndex; + radio->queue[0].length = globalRadio[RADIO_DEATH_FEMALE].length; + } + else + { + radio->queue[0].sndIndex = globalRadio[RADIO_DEATH_MALE].sndIndex; + radio->queue[0].length = globalRadio[RADIO_DEATH_MALE].length; + } + + for (check = 1; check < radio->queue_size; check++) + { + if (!radio->queue[check].click && radio->queue[check].from_player == from) + { + DeleteRadioQueueEntry( radio, check ); + check--; + } + } + } + + if( ! IsInIgnoreList( ent, from ) ) + { + unicastSound( ent, radio->queue[0].sndIndex, 1.0 ); + radio->delay = radio->queue[0].length; + } + DeleteRadioQueueEntry( radio, 0 ); //We can remove it here? + } +} + +static void AppendRadioMsgToQueue( radio_t *radio, int sndIndex, int len, int click, edict_t *from_player ) +{ + radio_queue_entry_t *newentry; + + if (radio->queue_size >= MAX_RADIO_QUEUE_SIZE) + { + gi.dprintf("AppendRadioMsgToQueue: Maximum radio queue size exceeded\n"); + return; + } + + newentry = &radio->queue[radio->queue_size]; + + newentry->sndIndex = sndIndex; + newentry->from_player = from_player; + newentry->from_gender = from_player->client->resp.radio.gender; + newentry->length = len; + newentry->click = click; + + radio->queue_size++; +} + +static void InsertRadioMsgInQueueBeforeClick( radio_t *radio, int sndIndex, int len, edict_t *from_player ) +{ + radio_queue_entry_t *newentry; + + if (radio->queue_size >= MAX_RADIO_QUEUE_SIZE) + { + gi.dprintf("InsertRadioMsgInQueueBeforeClick: Maximum radio queue size exceeded\n"); + return; + } + + newentry = &radio->queue[radio->queue_size - 1]; + + memcpy( &radio->queue[radio->queue_size], newentry, sizeof(radio_queue_entry_t)); + + newentry->sndIndex = sndIndex; + newentry->from_player = from_player; + newentry->from_gender = from_player->client->resp.radio.gender; + newentry->length = len; + newentry->click = 0; + + radio->queue_size++; +} + +static void AddRadioMsg( radio_t *radio, int sndIndex, int len, edict_t *from_player ) +{ + if (radio->queue_size == 0) + { + AppendRadioMsgToQueue( radio, globalRadio[RADIO_CLICK].sndIndex, globalRadio[RADIO_CLICK].length, 1, from_player ); + AppendRadioMsgToQueue( radio, sndIndex, len, 0, from_player ); + AppendRadioMsgToQueue( radio, globalRadio[RADIO_CLICK].sndIndex, globalRadio[RADIO_CLICK].length, 1, from_player ); + } + else // we have some msgs in it already... + { + if (radio->queue_size < MAX_RADIO_QUEUE_SIZE) + InsertRadioMsgInQueueBeforeClick( radio, sndIndex, len, from_player ); + // else ignore the message... + } +} + +void RadioBroadcast (edict_t * ent, int partner, char *msg) +{ + int j, i, msg_len, numSnds; + edict_t *other; + radio_msg_t *radio_msgs; + int msg_soundIndex = 0; + char msgname_num[8], filteredmsg[48]; + qboolean found = false; + radio_t *radio; + + if (!IS_ALIVE(ent)) + return; + + if (!teamplay->value) + { + if (!DMFLAGS( (DF_MODELTEAMS | DF_SKINTEAMS) )) + return; // don't allow in a non-team setup... + } + + radio = &ent->client->resp.radio; + if (radio->power_off) + { + gi.centerprintf (ent, "Your radio is off!"); + return; + } + + if (partner && radio->partner == NULL) + { + gi.cprintf (ent, PRINT_HIGH, "You don't have a partner.\n"); + return; + } + + if (radio->gender) + { + radio_msgs = female_radio_msgs; + numSnds = numFemaleSnds; + } + else + { + radio_msgs = male_radio_msgs; + numSnds = numMaleSnds; + } + + i = found = 0; + msg_len = 0; + + Q_strncpyz(filteredmsg, msg, sizeof(filteredmsg)); + + for(i = 0; i < numSnds; i++) + { + if (!Q_stricmp(radio_msgs[i].msg, filteredmsg)) + { + found = true; + msg_soundIndex = radio_msgs[i].sndIndex; + msg_len = radio_msgs[i].length; + break; + } + } + + if (!found) + { + gi.centerprintf (ent, "'%s' is not a valid radio message", filteredmsg); + return; + } + + if (radiolog->value) + { + gi.cprintf (NULL, PRINT_CHAT, "[%s RADIO] %s: %s\n", + partner ? "PARTNER" : "TEAM", ent->client->pers.netname, filteredmsg); + } + + //TempFile BEGIN + if (Q_stricmp (filteredmsg, "enemyd") == 0) + { + if (ent->client->radio_num_kills > 1 && ent->client->radio_num_kills <= 10) + { + // If we are reporting enemy down, add the number of kills. + sprintf( msgname_num, "%i", ent->client->radio_num_kills ); + ent->client->radio_num_kills = 0; // prevent from getting into an endless loop + + RadioBroadcast(ent, partner, msgname_num); // Now THAT'S recursion! =) + } + ent->client->radio_num_kills = 0; + } +//TempFile END + //AQ2:TNG Slicer + if (radio_repeat->value) + { //SLIC2 Optimization + if (CheckForRepeat (ent, i) == false) + return; + } + + if (radio_max->value) + { + if (CheckForFlood (ent) == false) + return; + } + + + //AQ2:TNG END + 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 != radio->partner) + continue; + AddRadioMsg( &other->client->resp.radio, msg_soundIndex, 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; + radio_t *radio; + + if (!teamplay->value) + { + if (!DMFLAGS( (DF_MODELTEAMS | DF_SKINTEAMS) )) + return; // don't allow in a non-team setup... + } + + radio = &ent->client->resp.radio; + arg = gi.args(); + if (arg == NULL || !*arg) + { + if (radio->gender) + gi.cprintf (ent, PRINT_HIGH, "Radio gender currently set to female\n"); + else + gi.cprintf (ent, PRINT_HIGH, "Radio gender currently set to male\n"); + return; + } + + if (!Q_stricmp(arg, "male")) + { + gi.cprintf (ent, PRINT_HIGH, "Radio gender set to male\n"); + radio->gender = 0; + } + else if (!Q_stricmp(arg, "female")) + { + gi.cprintf (ent, PRINT_HIGH, "Radio gender set to female\n"); + radio->gender = 1; + } + else + { + gi.cprintf (ent, PRINT_HIGH, "Invalid gender selection, try 'male' or 'female'\n"); + } +} + +void Cmd_Radio_power_f (edict_t * ent) +{ + radio_t *radio; + + if (!teamplay->value) + { + if (!DMFLAGS( (DF_MODELTEAMS | DF_SKINTEAMS) )) + return; // don't allow in a non-team setup... + } + + radio = &ent->client->resp.radio; + + radio->power_off = !radio->power_off; + + + + gi.centerprintf(ent, "Radio switched %s", (radio->power_off) ? "off" : "on"); + + unicastSound(ent, globalRadio[RADIO_CLICK].sndIndex, 1.0); +} + +void Cmd_Channel_f (edict_t * ent) +{ + radio_t *radio; + + if (!teamplay->value) + { + if (!DMFLAGS( (DF_MODELTEAMS | DF_SKINTEAMS) )) + return; // don't allow in a non-team setup... + } + + radio = &ent->client->resp.radio; + + radio->partner_mode = !radio->partner_mode; + if (radio->partner_mode) + { + gi.centerprintf (ent, "Channel set to 1, partner channel"); + } + else + { + gi.centerprintf (ent, "Channel set to 0, team channel"); + } +} + +edict_t *DetermineViewedPlayer(edict_t *ent, qboolean teammate); + +void Cmd_Partner_f (edict_t * ent) +{ + edict_t *target; + char *genderstr; + radio_t *radio, *tRadio; + + if (!teamplay->value) + { + if (!DMFLAGS( (DF_MODELTEAMS | DF_SKINTEAMS) )) + return; // don't allow in a non-team setup... + } + + if (!IS_ALIVE(ent)) + return; + + radio = &ent->client->resp.radio; + if (radio->partner) { + + if (radio->partner->inuse) { + + gi.centerprintf( ent, "You already have a partner, %s", radio->partner->client->pers.netname ); + + return; + + } + + // just in case RadioThink hasn't caught it yet... avoid any problems + + radio->partner = NULL; + + } + + target = DetermineViewedPlayer(ent, true); + if (target == NULL) { + gi.centerprintf (ent, "No potential partner selected"); + return; + } + + tRadio = &target->client->resp.radio; + if (tRadio->partner) { + gi.centerprintf (ent, "%s already has a partner", target->client->pers.netname); + return; + } + + if (tRadio->partner_last_offered_to == ent && + radio->partner_last_offered_from == target) + { + gi.centerprintf (ent, "%s is now your partner", target->client->pers.netname); + gi.centerprintf (target, "%s is now your partner", ent->client->pers.netname); + radio->partner = target; + tRadio->partner = ent; + radio->partner_last_offered_from = NULL; + tRadio->partner_last_offered_to = NULL; + return; + } + + if (tRadio->partner_last_denied_from == ent) + { + gi.centerprintf (ent, "%s has already denied you", target->client->pers.netname); + return; + } + + if (target == radio->partner_last_offered_to) + { + genderstr = GENDER_STR(target, "him", "her", "it"); + gi.centerprintf (ent, "Already awaiting confirmation from %s", genderstr); + return; + } + + genderstr = GENDER_STR(ent, "him", "her", "it"); + + gi.centerprintf (ent, "Awaiting confirmation from %s", target->client->pers.netname); + gi.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", + ent->client->pers.netname, genderstr); + + radio->partner_last_offered_to = target; + tRadio->partner_last_offered_from = ent; +} + +void Cmd_Unpartner_f (edict_t * ent) +{ + edict_t *target; + radio_t *radio; + + if (!teamplay->value) + { + if (!DMFLAGS( (DF_MODELTEAMS | DF_SKINTEAMS) )) + return; // don't allow in a non-team setup... + } + + radio = &ent->client->resp.radio; + if (radio->partner && !radio->partner->inuse) + { // just in case RadioThink hasn't caught it yet... avoid any problems + radio->partner = NULL; + } + + target = radio->partner; + if (target == NULL) { + gi.centerprintf (ent, "You don't have a partner"); + return; + } + + if (target->client->resp.radio.partner == ent) + { + gi.centerprintf (target, "%s broke your partnership", ent->client->pers.netname); + target->client->resp.radio.partner = NULL; + } + + gi.centerprintf (ent, "You broke your partnership with %s", target->client->pers.netname); + radio->partner = NULL; +} + +void Cmd_Deny_f (edict_t * ent) +{ + edict_t *target; + radio_t *radio; + + if (!teamplay->value) + { + if (!DMFLAGS( (DF_MODELTEAMS | DF_SKINTEAMS) )) + return; // don't allow in a non-team setup... + } + + if (!IS_ALIVE(ent)) + return; + + radio = &ent->client->resp.radio; + target = radio->partner_last_offered_from; + if (target && target->inuse) + { + gi.centerprintf (ent, "You denied %s", target->client->pers.netname); + gi.centerprintf (target, "%s has denied you", ent->client->pers.netname); + radio->partner_last_denied_from = target; + + radio->partner_last_offered_from = NULL; + if (target->client->resp.radio.partner_last_offered_to == ent) + + target->client->resp.radio.partner_last_offered_to = NULL; + } + else + { + gi.centerprintf (ent, "No one has offered to be your partner"); + return; + } +} + +void Cmd_Say_partner_f (edict_t * ent) +{ + if (!teamplay->value) + { + if (!DMFLAGS( (DF_MODELTEAMS | DF_SKINTEAMS) )) + return; // don't allow in a non-team setup... + } + + if (ent->client->resp.radio.partner == NULL) + { + gi.cprintf (ent, PRINT_HIGH, "You don't have a partner.\n"); + return; + } + + Cmd_Say_f (ent, false, false, true); +} + +//SLIC2 Redesigned and optimized these two functions + +qboolean CheckForFlood( edict_t * ent ) + +{ + + radio_t *radio = &ent->client->resp.radio; + + //If he's muted.. + + if (radio->rd_mute) { + + if (radio->rd_mute > level.framenum) // Still muted.. + + return false; + + + + radio->rd_mute = 0; // No longer muted.. + + } + + if (!radio->rd_Count) { + + radio->rd_time = level.framenum; + + radio->rd_Count++; + + } + + else { + + if (level.framenum - radio->rd_time < (int)(radio_time->value * HZ)) { + + if (++radio->rd_Count >= (int)radio_max->value) { + + gi.cprintf( ent, PRINT_HIGH, + + "[RADIO FLOOD PROTECTION]: Flood Detected, you are silenced for %d secs\n", (int)radio_ban->value ); + + radio->rd_mute = level.framenum + (int)(radio_ban->value * HZ); + + return false; + + } + + } + + else { + + radio->rd_Count = 0; + + } + + } + + + + return true; + +} + + + +qboolean CheckForRepeat( edict_t * ent, int radioCode ) + +{ + + radio_t *radio = &ent->client->resp.radio; + + + + //If he's muted.. + + if (radio->rd_mute) { + + if (radio->rd_mute > level.framenum) // Still muted.. + + return false; + + + + radio->rd_mute = 0; // No longer muted.. + + } + + + + if (radio->rd_lastRadio == radioCode) { //He's trying to repeat it.. + + if (level.framenum - radio->rd_repTime < (int)(radio_repeat_time->value * HZ)) { + + if (++radio->rd_repCount == (int)radio_repeat->value) { //Busted + + gi.cprintf( ent, PRINT_HIGH, "[RADIO FLOOD PROTECTION]: Repeat Flood Detected, you are silenced for %d secs\n", (int)radio_ban->value ); + + radio->rd_mute = level.framenum + (int)(radio_ban->value * HZ); + + return false; + + } + + } + + else { + + radio->rd_repCount = 0; + + } + + } + + else { + + radio->rd_lastRadio = radioCode; + + radio->rd_repCount = 0; + + } + + radio->rd_repTime = level.framenum; + + return true; + +} + diff --git a/actionlite/a_radio.h b/actionlite/a_radio.h new file mode 100644 index 0000000..f91036a --- /dev/null +++ b/actionlite/a_radio.h @@ -0,0 +1,55 @@ +#define MAX_SOUNDFILE_PATH_LEN 32 // max length of a sound file path +#define MAX_RADIO_MSG_QUEUE_SIZE 4 +#define MAX_RADIO_QUEUE_SIZE 6 // this must be at least 2 greater than the above + +typedef struct radio_queue_entry_s +{ + int sndIndex; + edict_t *from_player; + int from_gender; // true if female + + int length; + bool click; +} radio_queue_entry_t; + + +typedef struct radio_s +{ + int delay; + radio_queue_entry_t queue[MAX_RADIO_QUEUE_SIZE]; + int queue_size; + + bool gender; // radiogender + bool power_off; // radio_power + + // Partners stuff + bool partner_mode; // 'radio' command using team or partner + edict_t *partner; // current partner + edict_t *partner_last_offered_to; // last person I offered a partnership to + edict_t *partner_last_offered_from; // last person I received a partnership offer from + edict_t *partner_last_denied_from; // last person I denied a partnership offer from + + //Flood & Repeat + int rd_mute; //Time to be muted + int rd_Count; //Counter for the last msgs in "xx" secs allowed + int rd_time; //Frame for the first radio message of the ones to follow + + int rd_lastRadio; //Code of the last radio used + int rd_repCount; //Counter for the number of repeated radio msgs + int rd_repTime; //Frame for the last repeated radio msg +} radio_t; + +void RadioThink (edict_t *); +void Cmd_Radio_f (edict_t *); +void Cmd_Radiogender_f (edict_t *); +void Cmd_Radio_power_f (edict_t *); +void Cmd_Radiopartner_f (edict_t *); +void Cmd_Radioteam_f (edict_t *); +void Cmd_Channel_f (edict_t *); +void Cmd_Say_partner_f (edict_t *); +void Cmd_Partner_f (edict_t *); +void Cmd_Deny_f (edict_t *); +void Cmd_Unpartner_f (edict_t *); +void PrecacheRadioSounds (); +bool CheckForFlood (edict_t * ent); +bool CheckForRepeat (edict_t * ent, int radioCode); diff --git a/actionlite/a_team.cpp b/actionlite/a_team.cpp index 38369d6..d167bf4 100644 --- a/actionlite/a_team.cpp +++ b/actionlite/a_team.cpp @@ -1858,10 +1858,6 @@ int CheckTeamRules (void) if (!team_round_going) { RunWarmup(); - - if (!AllTeamsHaveLeaders()) - return 1; - if (CheckTimelimit()) return 1; @@ -2061,7 +2057,7 @@ int G_SortedClients( gclient_t **sortedList ) gclient_t *client; for (i = 0, client = game.clients; i < game.maxclients; i++, client++) { - if (!client->pers.connected || client->pers.mvdspec) + if (!client->pers.connected) continue; sortedList[total++] = client; @@ -2078,7 +2074,7 @@ int G_NotSortedClients( gclient_t **sortedList ) gclient_t *client; for (i = 0, client = game.clients; i < game.maxclients; i++, client++) { - if (!client->pers.connected || client->pers.mvdspec) + if (!client->pers.connected) continue; sortedList[total++] = client; @@ -2290,14 +2286,14 @@ void A_ScoreboardMessage (edict_t * ent, edict_t * killer) //Add team tag img if (!ctf->value) { - Q_strncatz(string, "if 22 ", sizeof(string)); + Q_strlcat(string, "if 22 ", sizeof(string)); len = strlen(string); for (i = TEAM1, line_x = base_x + headerOffset; i <= teamCount; i++, line_x += rowWidth) { sprintf(string + len, "xv %i pic 22 ", line_x + 32); len = strlen(string); } - Q_strncatz(string, "endif ", sizeof(string)); + Q_strlcat(string, "endif ", sizeof(string)); len = strlen(string); } @@ -2421,11 +2417,11 @@ void A_ScoreboardMessage (edict_t * ent, edict_t * killer) totalaliveprinted++; playername[0] = 0; - if (IS_CAPTAIN(cl_ent) || IS_LEADER(cl_ent)) { + if (IS_CAPTAIN(cl_ent)) { playername[0] = '@'; playername[1] = 0; } - Q_strncatz(playername, cl->pers.netname, sizeof(playername)); + Q_strlcat(playername, cl->pers.netname, sizeof(playername)); if (showExtra) { sprintf( string + len, "yv %d string%s \"%-15s %3d %3d %3d\" ", @@ -2764,7 +2760,6 @@ void GetSpawnPoints (void) potential_spawns[num_potential_spawns] = spot; num_potential_spawns++; } - if ((spot = G_FindByString<&edict_t::classname>(spot, "info_player_team2") != nullptr)) { potential_spawns[num_potential_spawns] = spot; diff --git a/actionlite/bg_local.h b/actionlite/bg_local.h index 5207cae..927e318 100644 --- a/actionlite/bg_local.h +++ b/actionlite/bg_local.h @@ -197,15 +197,15 @@ enum player_stat_t STAT_CHASE = 16, STAT_SPECTATOR = 17, - STAT_CTF_TEAM1_PIC = 18, + STAT_TEAM1_PIC = 18, STAT_CTF_TEAM1_CAPS = 19, - STAT_CTF_TEAM2_PIC = 20, + STAT_TEAM2_PIC = 20, STAT_CTF_TEAM2_CAPS = 21, - STAT_CTF_FLAG_PIC = 22, - STAT_CTF_JOINED_TEAM1_PIC = 23, - STAT_CTF_JOINED_TEAM2_PIC = 24, - STAT_CTF_TEAM1_HEADER = 25, - STAT_CTF_TEAM2_HEADER = 26, + STAT_FLAG_PIC = 22, + STAT_JOINED_TEAM1_PIC = 23, + STAT_JOINED_TEAM2_PIC = 24, + STAT_TEAM1_HEADER = 25, + STAT_TEAM2_HEADER = 26, STAT_CTF_TECH = 27, STAT_CTF_ID_VIEW = 28, STAT_CTF_MATCH = 29, @@ -221,9 +221,10 @@ enum player_stat_t STAT_POWERUP_INFO_END = STAT_POWERUP_INFO_START + NUM_POWERUP_STATS - 1, // [Paril-KEX] Key display - STAT_KEY_A, - STAT_KEY_B, - STAT_KEY_C, + // Not used in Action, freeing up stat numbers + // STAT_KEY_A, + // STAT_KEY_B, + // STAT_KEY_C, // [Paril-KEX] currently active wheel weapon (or one we're switching to) STAT_ACTIVE_WHEEL_WEAPON, @@ -239,6 +240,25 @@ enum player_stat_t STAT_HEALTH_BARS, // two health bar values; 7 bits for value, 1 bit for active // if active, + // Action + STAT_CLIP_ICON, + STAT_CLIP, + STAT_SNIPER_ICON, + STAT_ITEMS_ICON, + STAT_WEAPONS_ICON, + STAT_ID_VIEW, + + STAT_TEAM_HEADER, + + STAT_TEAM1_SCORE, + STAT_TEAM2_SCORE, + STAT_TEAM3_SCORE, + + STAT_GRENADE_ICON, + STAT_GRENADES, + + STAT_TEAM3_PIC, + // don't use; just for verification STAT_LAST }; diff --git a/actionlite/ctf/g_ctf.cpp b/actionlite/ctf/g_ctf.cpp index 7264b01..5456583 100644 --- a/actionlite/ctf/g_ctf.cpp +++ b/actionlite/ctf/g_ctf.cpp @@ -1027,8 +1027,8 @@ void SetCTFStats(edict_t *ent) } // logo headers for the frag display - ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = imageindex_ctfsb1; - ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = imageindex_ctfsb2; + ent->client->ps.stats[STAT_TEAM1_HEADER] = imageindex_ctfsb1; + ent->client->ps.stats[STAT_TEAM2_HEADER] = imageindex_ctfsb2; bool blink = (level.time.milliseconds() % 1000) < 500; @@ -1038,17 +1038,17 @@ void SetCTFStats(edict_t *ent) // blink half second // note that ctfgame.total[12] is set when we go to intermission if (ctfgame.team1 > ctfgame.team2) - ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0; + ent->client->ps.stats[STAT_TEAM1_HEADER] = 0; else if (ctfgame.team2 > ctfgame.team1) - ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0; + ent->client->ps.stats[STAT_TEAM2_HEADER] = 0; else if (ctfgame.total1 > ctfgame.total2) // frag tie breaker - ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0; + ent->client->ps.stats[STAT_TEAM1_HEADER] = 0; else if (ctfgame.total2 > ctfgame.total1) - ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0; + ent->client->ps.stats[STAT_TEAM2_HEADER] = 0; else { // tie game! - ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0; - ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0; + ent->client->ps.stats[STAT_TEAM1_HEADER] = 0; + ent->client->ps.stats[STAT_TEAM2_HEADER] = 0; } } @@ -1114,51 +1114,51 @@ void SetCTFStats(edict_t *ent) p2 = imageindex_i_ctf2d; // must be dropped } - ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1; - ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2; + ent->client->ps.stats[STAT_TEAM1_PIC] = p1; + ent->client->ps.stats[STAT_TEAM2_PIC] = p2; if (ctfgame.last_flag_capture && level.time - ctfgame.last_flag_capture < 5_sec) { if (ctfgame.last_capture_team == CTF_TEAM1) if (blink) - ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1; + ent->client->ps.stats[STAT_TEAM1_PIC] = p1; else - ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = 0; + ent->client->ps.stats[STAT_TEAM1_PIC] = 0; else if (blink) - ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2; + ent->client->ps.stats[STAT_TEAM2_PIC] = p2; else - ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = 0; + ent->client->ps.stats[STAT_TEAM2_PIC] = 0; } ent->client->ps.stats[STAT_CTF_TEAM1_CAPS] = ctfgame.team1; ent->client->ps.stats[STAT_CTF_TEAM2_CAPS] = ctfgame.team2; - ent->client->ps.stats[STAT_CTF_FLAG_PIC] = 0; + ent->client->ps.stats[STAT_FLAG_PIC] = 0; if (ent->client->resp.ctf_team == CTF_TEAM1 && ent->client->pers.inventory[IT_FLAG2] && (blink)) - ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf2; + ent->client->ps.stats[STAT_FLAG_PIC] = imageindex_i_ctf2; else if (ent->client->resp.ctf_team == CTF_TEAM2 && ent->client->pers.inventory[IT_FLAG1] && (blink)) - ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf1; + ent->client->ps.stats[STAT_FLAG_PIC] = imageindex_i_ctf1; } else { - ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = imageindex_i_ctf1; - ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = imageindex_i_ctf2; + ent->client->ps.stats[STAT_TEAM1_PIC] = imageindex_i_ctf1; + ent->client->ps.stats[STAT_TEAM2_PIC] = imageindex_i_ctf2; ent->client->ps.stats[STAT_CTF_TEAM1_CAPS] = ctfgame.total1; ent->client->ps.stats[STAT_CTF_TEAM2_CAPS] = ctfgame.total2; } - ent->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = 0; - ent->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = 0; + ent->client->ps.stats[STAT_JOINED_TEAM1_PIC] = 0; + ent->client->ps.stats[STAT_JOINED_TEAM2_PIC] = 0; if (ent->client->resp.ctf_team == CTF_TEAM1) - ent->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = imageindex_i_ctfj; + ent->client->ps.stats[STAT_JOINED_TEAM1_PIC] = imageindex_i_ctfj; else if (ent->client->resp.ctf_team == CTF_TEAM2) - ent->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = imageindex_i_ctfj; + ent->client->ps.stats[STAT_JOINED_TEAM2_PIC] = imageindex_i_ctfj; if (ent->client->resp.id_state) CTFSetIDView(ent); diff --git a/actionlite/g_local.h b/actionlite/g_local.h index aab041a..d79d346 100644 --- a/actionlite/g_local.h +++ b/actionlite/g_local.h @@ -1064,10 +1064,14 @@ struct game_locals_t std::array level_entries; int32_t max_lag_origins; vec3_t *lag_origins; // maxclients * max_lag_origins + + // Action + int32_t roundNum; }; constexpr size_t MAX_HEALTH_BARS = 2; +#define ITEM_MAX_NUM WEAPON_MAX+ITEM_MAX+AMMO_MAX_ANUM // // this structure is cleared as each map is entered // it is read/written to the level.sav file for savegames @@ -1171,6 +1175,18 @@ struct level_locals_t gtime_t next_match_report; // Action add + + int32_t pic_items[ITEM_MAX_NUM]; + int32_t pic_weapon_ammo[WEAPON_MAX]; + int32_t pic_sniper_mode[SNIPER_MODE_MAX]; + int32_t pic_teamskin[TEAM_TOP]; + int32_t pic_teamtag; + + int32_t pic_ctf_teamtag[TEAM_TOP]; + int32_t pic_ctf_flagbase[TEAM_TOP]; + int32_t pic_ctf_flagtaken[TEAM_TOP]; + int32_t pic_ctf_flagdropped[TEAM_TOP]; + int32_t snd_silencer; int32_t snd_headshot; int32_t snd_vesthit; @@ -1919,12 +1935,12 @@ extern gitem_t itemlist[IT_TOTAL]; #define GS_ROUNDBASED 8 #define GS_WEAPONCHOOSE 16 -// sniper modes -#define SNIPER_1X 0 -#define SNIPER_2X 1 -#define SNIPER_4X 2 -#define SNIPER_6X 3 -#define SNIPER_MODE_MAX 4 +// // sniper modes +// #define SNIPER_1X 0 +// #define SNIPER_2X 1 +// #define SNIPER_4X 2 +// #define SNIPER_6X 3 +// #define SNIPER_MODE_MAX 4 //TempFile sniper zoom moved to constants #define SNIPER_FOV1 90 @@ -1985,6 +2001,13 @@ void SP_LaserSight (edict_t * self, gitem_t * item); void Cmd_Reload_f (edict_t * ent); void Cmd_New_Reload_f (edict_t * ent); +enum layout_t{ + LAYOUT_NONE, + LAYOUT_SCORES, + LAYOUT_SCORES2, + LAYOUT_MENU +}; + enum action_sniper_modes_t { SNIPER_1X, @@ -2029,7 +2052,7 @@ enum action_ammo_num_t M4_ANUM, SHELL_ANUM, SNIPER_ANUM, - AMMO_MAX + AMMO_MAX_ANUM }; constexpr item_id_t weap_ids[] = { @@ -2948,8 +2971,11 @@ struct client_respawn_t // ZOID // Action Add + int32_t enterframe; // frame when player entered the game int32_t team_kills; int32_t team_wounds; + int32_t joined_team; // last frame # at which the player joined a team + int32_t lastWave; //last time used wave int32_t team; int32_t subteam; @@ -2977,6 +3003,7 @@ struct client_respawn_t int32_t streakHS; //Headshots in a Row int32_t streakKillsHighest; //Highest kills in a row int32_t streakHSHighest; //Highest headshots in a Row + int32_t damage_dealt; int32_t hitsLocations[LOC_MAX]; //Number of hits for different locations gunStats_t gunstats[MOD_TOTAL]; //Number of shots/hits for different guns, adjusted to MOD_TOTAL to allow grenade, kick and punch stats @@ -3141,7 +3168,6 @@ struct gclient_t gtime_t respawn_time; // can respawn when time > this - edict_t *chase_target; // player we are chasing bool update_chase; // need to update chase info? //======= @@ -3224,13 +3250,14 @@ struct gclient_t gtime_t last_attacker_time; // Action Add + layout_t layout; // set layout stat int32_t unique_item_total; int32_t unique_weapon_total; - int32_t inventory[MAX_ITEMS]; + int32_t inventory[MAX_ITEMS]; edict_t *chase_target; - int32_t chase_mode; + int32_t chase_mode; // ammo capacities int32_t ammo_index; int32_t max_pistolmags; diff --git a/actionlite/g_spawn.cpp b/actionlite/g_spawn.cpp index cd78590..b2f0d79 100644 --- a/actionlite/g_spawn.cpp +++ b/actionlite/g_spawn.cpp @@ -1344,19 +1344,19 @@ static void G_InitStatusbar() // ctf/tdm // red team - sb.yb(-110).ifstat(STAT_CTF_TEAM1_PIC).xr(-26).pic(STAT_CTF_TEAM1_PIC).endifstat().xr(-78).num(3, STAT_CTF_TEAM1_CAPS); + sb.yb(-110).ifstat(STAT_TEAM1_PIC).xr(-26).pic(STAT_TEAM1_PIC).endifstat().xr(-78).num(3, STAT_CTF_TEAM1_CAPS); // joined overlay - sb.ifstat(STAT_CTF_JOINED_TEAM1_PIC).yb(-112).xr(-28).pic(STAT_CTF_JOINED_TEAM1_PIC).endifstat(); + sb.ifstat(STAT_JOINED_TEAM1_PIC).yb(-112).xr(-28).pic(STAT_JOINED_TEAM1_PIC).endifstat(); // blue team - sb.yb(-83).ifstat(STAT_CTF_TEAM2_PIC).xr(-26).pic(STAT_CTF_TEAM2_PIC).endifstat().xr(-78).num(3, STAT_CTF_TEAM2_CAPS); + sb.yb(-83).ifstat(STAT_TEAM2_PIC).xr(-26).pic(STAT_TEAM2_PIC).endifstat().xr(-78).num(3, STAT_CTF_TEAM2_CAPS); // joined overlay - sb.ifstat(STAT_CTF_JOINED_TEAM2_PIC).yb(-85).xr(-28).pic(STAT_CTF_JOINED_TEAM2_PIC).endifstat(); + sb.ifstat(STAT_JOINED_TEAM2_PIC).yb(-85).xr(-28).pic(STAT_JOINED_TEAM2_PIC).endifstat(); if (ctf->integer) { // have flag graph - sb.ifstat(STAT_CTF_FLAG_PIC).yt(26).xr(-24).pic(STAT_CTF_FLAG_PIC).endifstat(); + sb.ifstat(STAT_FLAG_PIC).yt(26).xr(-24).pic(STAT_FLAG_PIC).endifstat(); } // id view state diff --git a/actionlite/p_weapon.cpp b/actionlite/p_weapon.cpp index 4be4f3a..67f3705 100644 --- a/actionlite/p_weapon.cpp +++ b/actionlite/p_weapon.cpp @@ -3977,13 +3977,13 @@ edict_t *FindSpecWeapSpawn(edict_t* ent) edict_t* spot = NULL; //gi.bprintf (PRINT_HIGH, "Calling the FindSpecWeapSpawn\n"); - spot = G_FindByString<&edict_t::classname>(spot, classname); + spot = G_FindByString<&edict_t::classname>(spot, ent->classname); //spot = G_Find(spot, FOFS(classname), ent->classname); //gi.bprintf (PRINT_HIGH, "spot = %p and spot->think = %p and playerholder = %p, spot, (spot ? spot->think : 0), PlaceHolder\n"); while (spot && spot->think != PlaceHolder) //(spot->spawnflags & DROPPED_ITEM ) && spot->think != PlaceHolder )//spot->solid == SOLID_NOT ) { // gi.bprintf (PRINT_HIGH, "Calling inside the loop FindSpecWeapSpawn\n"); - spot = G_FindByString<&edict_t::classname>(spot, classname); + spot = G_FindByString<&edict_t::classname>(spot, ent->classname); //spot = G_Find(spot, FOFS(classname), ent->classname); } return spot;