📄 g_team.c
字号:
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code 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 2 of the License,
or (at your option) any later version.
Quake III Arena source code 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 the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
//
#include "g_local.h"
typedef struct teamgame_s {
float last_flag_capture;
int last_capture_team;
flagStatus_t redStatus; // CTF
flagStatus_t blueStatus; // CTF
flagStatus_t flagStatus; // One Flag CTF
int redTakenTime;
int blueTakenTime;
int redObeliskAttackedTime;
int blueObeliskAttackedTime;
} teamgame_t;
teamgame_t teamgame;
gentity_t *neutralObelisk;
void Team_SetFlagStatus( int team, flagStatus_t status );
void Team_InitGame( void ) {
memset(&teamgame, 0, sizeof teamgame);
switch( g_gametype.integer ) {
case GT_CTF:
teamgame.redStatus = teamgame.blueStatus = -1; // Invalid to force update
Team_SetFlagStatus( TEAM_RED, FLAG_ATBASE );
Team_SetFlagStatus( TEAM_BLUE, FLAG_ATBASE );
break;
#ifdef MISSIONPACK
case GT_1FCTF:
teamgame.flagStatus = -1; // Invalid to force update
Team_SetFlagStatus( TEAM_FREE, FLAG_ATBASE );
break;
#endif
default:
break;
}
}
int OtherTeam(int team) {
if (team==TEAM_RED)
return TEAM_BLUE;
else if (team==TEAM_BLUE)
return TEAM_RED;
return team;
}
const char *TeamName(int team) {
if (team==TEAM_RED)
return "RED";
else if (team==TEAM_BLUE)
return "BLUE";
else if (team==TEAM_SPECTATOR)
return "SPECTATOR";
return "FREE";
}
const char *OtherTeamName(int team) {
if (team==TEAM_RED)
return "BLUE";
else if (team==TEAM_BLUE)
return "RED";
else if (team==TEAM_SPECTATOR)
return "SPECTATOR";
return "FREE";
}
const char *TeamColorString(int team) {
if (team==TEAM_RED)
return S_COLOR_RED;
else if (team==TEAM_BLUE)
return S_COLOR_BLUE;
else if (team==TEAM_SPECTATOR)
return S_COLOR_YELLOW;
return S_COLOR_WHITE;
}
// NULL for everyone
void QDECL PrintMsg( gentity_t *ent, const char *fmt, ... ) {
char msg[1024];
va_list argptr;
char *p;
va_start (argptr,fmt);
if (vsprintf (msg, fmt, argptr) > sizeof(msg)) {
G_Error ( "PrintMsg overrun" );
}
va_end (argptr);
// double quotes are bad
while ((p = strchr(msg, '"')) != NULL)
*p = '\'';
trap_SendServerCommand ( ( (ent == NULL) ? -1 : ent-g_entities ), va("print \"%s\"", msg ));
}
/*
==============
AddTeamScore
used for gametype > GT_TEAM
for gametype GT_TEAM the level.teamScores is updated in AddScore in g_combat.c
==============
*/
void AddTeamScore(vec3_t origin, int team, int score) {
gentity_t *te;
te = G_TempEntity(origin, EV_GLOBAL_TEAM_SOUND );
te->r.svFlags |= SVF_BROADCAST;
if ( team == TEAM_RED ) {
if ( level.teamScores[ TEAM_RED ] + score == level.teamScores[ TEAM_BLUE ] ) {
//teams are tied sound
te->s.eventParm = GTS_TEAMS_ARE_TIED;
}
else if ( level.teamScores[ TEAM_RED ] <= level.teamScores[ TEAM_BLUE ] &&
level.teamScores[ TEAM_RED ] + score > level.teamScores[ TEAM_BLUE ]) {
// red took the lead sound
te->s.eventParm = GTS_REDTEAM_TOOK_LEAD;
}
else {
// red scored sound
te->s.eventParm = GTS_REDTEAM_SCORED;
}
}
else {
if ( level.teamScores[ TEAM_BLUE ] + score == level.teamScores[ TEAM_RED ] ) {
//teams are tied sound
te->s.eventParm = GTS_TEAMS_ARE_TIED;
}
else if ( level.teamScores[ TEAM_BLUE ] <= level.teamScores[ TEAM_RED ] &&
level.teamScores[ TEAM_BLUE ] + score > level.teamScores[ TEAM_RED ]) {
// blue took the lead sound
te->s.eventParm = GTS_BLUETEAM_TOOK_LEAD;
}
else {
// blue scored sound
te->s.eventParm = GTS_BLUETEAM_SCORED;
}
}
level.teamScores[ team ] += score;
}
/*
==============
OnSameTeam
==============
*/
qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 ) {
if ( !ent1->client || !ent2->client ) {
return qfalse;
}
if ( g_gametype.integer < GT_TEAM ) {
return qfalse;
}
if ( ent1->client->sess.sessionTeam == ent2->client->sess.sessionTeam ) {
return qtrue;
}
return qfalse;
}
static char ctfFlagStatusRemap[] = { '0', '1', '*', '*', '2' };
static char oneFlagStatusRemap[] = { '0', '1', '2', '3', '4' };
void Team_SetFlagStatus( int team, flagStatus_t status ) {
qboolean modified = qfalse;
switch( team ) {
case TEAM_RED: // CTF
if( teamgame.redStatus != status ) {
teamgame.redStatus = status;
modified = qtrue;
}
break;
case TEAM_BLUE: // CTF
if( teamgame.blueStatus != status ) {
teamgame.blueStatus = status;
modified = qtrue;
}
break;
case TEAM_FREE: // One Flag CTF
if( teamgame.flagStatus != status ) {
teamgame.flagStatus = status;
modified = qtrue;
}
break;
}
if( modified ) {
char st[4];
if( g_gametype.integer == GT_CTF ) {
st[0] = ctfFlagStatusRemap[teamgame.redStatus];
st[1] = ctfFlagStatusRemap[teamgame.blueStatus];
st[2] = 0;
}
else { // GT_1FCTF
st[0] = oneFlagStatusRemap[teamgame.flagStatus];
st[1] = 0;
}
trap_SetConfigstring( CS_FLAGSTATUS, st );
}
}
void Team_CheckDroppedItem( gentity_t *dropped ) {
if( dropped->item->giTag == PW_REDFLAG ) {
Team_SetFlagStatus( TEAM_RED, FLAG_DROPPED );
}
else if( dropped->item->giTag == PW_BLUEFLAG ) {
Team_SetFlagStatus( TEAM_BLUE, FLAG_DROPPED );
}
else if( dropped->item->giTag == PW_NEUTRALFLAG ) {
Team_SetFlagStatus( TEAM_FREE, FLAG_DROPPED );
}
}
/*
================
Team_ForceGesture
================
*/
void Team_ForceGesture(int team) {
int i;
gentity_t *ent;
for (i = 0; i < MAX_CLIENTS; i++) {
ent = &g_entities[i];
if (!ent->inuse)
continue;
if (!ent->client)
continue;
if (ent->client->sess.sessionTeam != team)
continue;
//
ent->flags |= FL_FORCE_GESTURE;
}
}
/*
================
Team_FragBonuses
Calculate the bonuses for flag defense, flag carrier defense, etc.
Note that bonuses are not cumulative. You get one, they are in importance
order.
================
*/
void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker)
{
int i;
gentity_t *ent;
int flag_pw, enemy_flag_pw;
int otherteam;
int tokens;
gentity_t *flag, *carrier = NULL;
char *c;
vec3_t v1, v2;
int team;
// no bonus for fragging yourself or team mates
if (!targ->client || !attacker->client || targ == attacker || OnSameTeam(targ, attacker))
return;
team = targ->client->sess.sessionTeam;
otherteam = OtherTeam(targ->client->sess.sessionTeam);
if (otherteam < 0)
return; // whoever died isn't on a team
// same team, if the flag at base, check to he has the enemy flag
if (team == TEAM_RED) {
flag_pw = PW_REDFLAG;
enemy_flag_pw = PW_BLUEFLAG;
} else {
flag_pw = PW_BLUEFLAG;
enemy_flag_pw = PW_REDFLAG;
}
if (g_gametype.integer == GT_1FCTF) {
enemy_flag_pw = PW_NEUTRALFLAG;
}
// did the attacker frag the flag carrier?
tokens = 0;
#ifdef MISSIONPACK
if( g_gametype.integer == GT_HARVESTER ) {
tokens = targ->client->ps.generic1;
}
#endif
if (targ->client->ps.powerups[enemy_flag_pw]) {
attacker->client->pers.teamState.lastfraggedcarrier = level.time;
AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS);
attacker->client->pers.teamState.fragcarrier++;
PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's flag carrier!\n",
attacker->client->pers.netname, TeamName(team));
// the target had the flag, clear the hurt carrier
// field on the other team
for (i = 0; i < g_maxclients.integer; i++) {
ent = g_entities + i;
if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
ent->client->pers.teamState.lasthurtcarrier = 0;
}
return;
}
// did the attacker frag a head carrier? other->client->ps.generic1
if (tokens) {
attacker->client->pers.teamState.lastfraggedcarrier = level.time;
AddScore(attacker, targ->r.currentOrigin, CTF_FRAG_CARRIER_BONUS * tokens * tokens);
attacker->client->pers.teamState.fragcarrier++;
PrintMsg(NULL, "%s" S_COLOR_WHITE " fragged %s's skull carrier!\n",
attacker->client->pers.netname, TeamName(team));
// the target had the flag, clear the hurt carrier
// field on the other team
for (i = 0; i < g_maxclients.integer; i++) {
ent = g_entities + i;
if (ent->inuse && ent->client->sess.sessionTeam == otherteam)
ent->client->pers.teamState.lasthurtcarrier = 0;
}
return;
}
if (targ->client->pers.teamState.lasthurtcarrier &&
level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
!attacker->client->ps.powerups[flag_pw]) {
// attacker is on the same team as the flag carrier and
// fragged a guy who hurt our flag carrier
AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);
attacker->client->pers.teamState.carrierdefense++;
targ->client->pers.teamState.lasthurtcarrier = 0;
attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
team = attacker->client->sess.sessionTeam;
// add the sprite over the player's head
attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
return;
}
if (targ->client->pers.teamState.lasthurtcarrier &&
level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT) {
// attacker is on the same team as the skull carrier and
AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS);
attacker->client->pers.teamState.carrierdefense++;
targ->client->pers.teamState.lasthurtcarrier = 0;
attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
team = attacker->client->sess.sessionTeam;
// add the sprite over the player's head
attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
return;
}
// flag and flag carrier area defense bonuses
// we have to find the flag and carrier entities
#ifdef MISSIONPACK
if( g_gametype.integer == GT_OBELISK ) {
// find the team obelisk
switch (attacker->client->sess.sessionTeam) {
case TEAM_RED:
c = "team_redobelisk";
break;
case TEAM_BLUE:
c = "team_blueobelisk";
break;
default:
return;
}
} else if (g_gametype.integer == GT_HARVESTER ) {
// find the center obelisk
c = "team_neutralobelisk";
} else {
#endif
// find the flag
switch (attacker->client->sess.sessionTeam) {
case TEAM_RED:
c = "team_CTF_redflag";
break;
case TEAM_BLUE:
c = "team_CTF_blueflag";
break;
default:
return;
}
// find attacker's team's flag carrier
for (i = 0; i < g_maxclients.integer; i++) {
carrier = g_entities + i;
if (carrier->inuse && carrier->client->ps.powerups[flag_pw])
break;
carrier = NULL;
}
#ifdef MISSIONPACK
}
#endif
flag = NULL;
while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) {
if (!(flag->flags & FL_DROPPED_ITEM))
break;
}
if (!flag)
return; // can't find attacker's flag
// ok we have the attackers flag and a pointer to the carrier
// check to see if we are defending the base's flag
VectorSubtract(targ->r.currentOrigin, flag->r.currentOrigin, v1);
VectorSubtract(attacker->r.currentOrigin, flag->r.currentOrigin, v2);
if ( ( ( VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS &&
trap_InPVS(flag->r.currentOrigin, targ->r.currentOrigin ) ) ||
( VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS &&
trap_InPVS(flag->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
// we defended the base flag
AddScore(attacker, targ->r.currentOrigin, CTF_FLAG_DEFENSE_BONUS);
attacker->client->pers.teamState.basedefense++;
attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
// add the sprite over the player's head
attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
return;
}
if (carrier && carrier != attacker) {
VectorSubtract(targ->r.currentOrigin, carrier->r.currentOrigin, v1);
VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v1);
if ( ( ( VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS &&
trap_InPVS(carrier->r.currentOrigin, targ->r.currentOrigin ) ) ||
( VectorLength(v2) < CTF_ATTACKER_PROTECT_RADIUS &&
trap_InPVS(carrier->r.currentOrigin, attacker->r.currentOrigin ) ) ) &&
attacker->client->sess.sessionTeam != targ->client->sess.sessionTeam) {
AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_PROTECT_BONUS);
attacker->client->pers.teamState.carrierdefense++;
attacker->client->ps.persistant[PERS_DEFEND_COUNT]++;
// add the sprite over the player's head
attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
attacker->client->ps.eFlags |= EF_AWARD_DEFEND;
attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
return;
}
}
}
/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -