/ctf/g_ctf.c
C | 4016 lines | 3164 code | 604 blank | 248 comment | 735 complexity | d17bceac2d78dadf19833410c4a5e5f8 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /*
- Copyright (C) 1997-2001 Id Software, Inc.
- This program 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.
- This program 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 this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- #include "g_local.h"
- #include "m_player.h"
- typedef enum match_s {
- MATCH_NONE,
- MATCH_SETUP,
- MATCH_PREGAME,
- MATCH_GAME,
- MATCH_POST
- } match_t;
- typedef enum {
- ELECT_NONE,
- ELECT_MATCH,
- ELECT_ADMIN,
- ELECT_MAP
- } elect_t;
- typedef struct ctfgame_s
- {
- int team1, team2;
- int total1, total2; // these are only set when going into intermission!
- float last_flag_capture;
- int last_capture_team;
- match_t match; // match state
- float matchtime; // time for match start/end (depends on state)
- int lasttime; // last time update
- elect_t election; // election type
- edict_t *etarget; // for admin election, who's being elected
- char elevel[32]; // for map election, target level
- int evotes; // votes so far
- int needvotes; // votes needed
- float electtime; // remaining time until election times out
- char emsg[256]; // election name
- ghost_t ghosts[MAX_CLIENTS]; // ghost codes
- } ctfgame_t;
- ctfgame_t ctfgame;
- cvar_t *ctf;
- cvar_t *ctf_forcejoin;
- cvar_t *competition;
- cvar_t *matchlock;
- cvar_t *electpercentage;
- cvar_t *matchtime;
- cvar_t *matchsetuptime;
- cvar_t *matchstarttime;
- cvar_t *admin_password;
- cvar_t *warp_list;
- char *ctf_statusbar =
- "yb -24 "
- // health
- "xv 0 "
- "hnum "
- "xv 50 "
- "pic 0 "
- // ammo
- "if 2 "
- " xv 100 "
- " anum "
- " xv 150 "
- " pic 2 "
- "endif "
- // armor
- "if 4 "
- " xv 200 "
- " rnum "
- " xv 250 "
- " pic 4 "
- "endif "
- // selected item
- "if 6 "
- " xv 296 "
- " pic 6 "
- "endif "
- "yb -50 "
- // picked up item
- "if 7 "
- " xv 0 "
- " pic 7 "
- " xv 26 "
- " yb -42 "
- " stat_string 8 "
- " yb -50 "
- "endif "
- // timer
- "if 9 "
- "xv 246 "
- "num 2 10 "
- "xv 296 "
- "pic 9 "
- "endif "
- // help / weapon icon
- "if 11 "
- "xv 148 "
- "pic 11 "
- "endif "
- // frags
- "xr -50 "
- "yt 2 "
- "num 3 14 "
- //tech
- "yb -129 "
- "if 26 "
- "xr -26 "
- "pic 26 "
- "endif "
- // red team
- "yb -102 "
- "if 17 "
- "xr -26 "
- "pic 17 "
- "endif "
- "xr -62 "
- "num 2 18 "
- //joined overlay
- "if 22 "
- "yb -104 "
- "xr -28 "
- "pic 22 "
- "endif "
- // blue team
- "yb -75 "
- "if 19 "
- "xr -26 "
- "pic 19 "
- "endif "
- "xr -62 "
- "num 2 20 "
- "if 23 "
- "yb -77 "
- "xr -28 "
- "pic 23 "
- "endif "
- // have flag graph
- "if 21 "
- "yt 26 "
- "xr -24 "
- "pic 21 "
- "endif "
- // id view state
- "if 27 "
- "xv 0 "
- "yb -58 "
- "string \"Viewing\" "
- "xv 64 "
- "stat_string 27 "
- "endif "
- "if 28 "
- "xl 0 "
- "yb -78 "
- "stat_string 28 "
- "endif "
- ;
- static char *tnames[] = {
- "item_tech1", "item_tech2", "item_tech3", "item_tech4",
- NULL
- };
- void stuffcmd(edict_t *ent, char *s)
- {
- gi.WriteByte (11);
- gi.WriteString (s);
- gi.unicast (ent, true);
- }
- /*--------------------------------------------------------------------------*/
- /*
- =================
- findradius
- Returns entities that have origins within a spherical area
- findradius (origin, radius)
- =================
- */
- static edict_t *loc_findradius (edict_t *from, vec3_t org, float rad)
- {
- vec3_t eorg;
- int j;
- if (!from)
- from = g_edicts;
- else
- from++;
- for ( ; from < &g_edicts[globals.num_edicts]; from++)
- {
- if (!from->inuse)
- continue;
- #if 0
- if (from->solid == SOLID_NOT)
- continue;
- #endif
- for (j=0 ; j<3 ; j++)
- eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
- if (VectorLength(eorg) > rad)
- continue;
- return from;
- }
- return NULL;
- }
- static void loc_buildboxpoints(vec3_t p[8], vec3_t org, vec3_t mins, vec3_t maxs)
- {
- VectorAdd(org, mins, p[0]);
- VectorCopy(p[0], p[1]);
- p[1][0] -= mins[0];
- VectorCopy(p[0], p[2]);
- p[2][1] -= mins[1];
- VectorCopy(p[0], p[3]);
- p[3][0] -= mins[0];
- p[3][1] -= mins[1];
- VectorAdd(org, maxs, p[4]);
- VectorCopy(p[4], p[5]);
- p[5][0] -= maxs[0];
- VectorCopy(p[0], p[6]);
- p[6][1] -= maxs[1];
- VectorCopy(p[0], p[7]);
- p[7][0] -= maxs[0];
- p[7][1] -= maxs[1];
- }
- static qboolean loc_CanSee (edict_t *targ, edict_t *inflictor)
- {
- trace_t trace;
- vec3_t targpoints[8];
- int i;
- vec3_t viewpoint;
- // bmodels need special checking because their origin is 0,0,0
- if (targ->movetype == MOVETYPE_PUSH)
- return false; // bmodels not supported
- loc_buildboxpoints(targpoints, targ->s.origin, targ->mins, targ->maxs);
-
- VectorCopy(inflictor->s.origin, viewpoint);
- viewpoint[2] += inflictor->viewheight;
- for (i = 0; i < 8; i++) {
- trace = gi.trace (viewpoint, vec3_origin, vec3_origin, targpoints[i], inflictor, MASK_SOLID);
- if (trace.fraction == 1.0)
- return true;
- }
- return false;
- }
- /*--------------------------------------------------------------------------*/
- static gitem_t *flag1_item;
- static gitem_t *flag2_item;
- void CTFSpawn(void)
- {
- if (!flag1_item)
- flag1_item = FindItemByClassname("item_flag_team1");
- if (!flag2_item)
- flag2_item = FindItemByClassname("item_flag_team2");
- memset(&ctfgame, 0, sizeof(ctfgame));
- CTFSetupTechSpawn();
- if (competition->value > 1) {
- ctfgame.match = MATCH_SETUP;
- ctfgame.matchtime = level.time + matchsetuptime->value * 60;
- }
- }
- void CTFInit(void)
- {
- ctf = gi.cvar("ctf", "1", CVAR_SERVERINFO);
- ctf_forcejoin = gi.cvar("ctf_forcejoin", "", 0);
- competition = gi.cvar("competition", "0", CVAR_SERVERINFO);
- matchlock = gi.cvar("matchlock", "1", CVAR_SERVERINFO);
- electpercentage = gi.cvar("electpercentage", "66", 0);
- matchtime = gi.cvar("matchtime", "20", CVAR_SERVERINFO);
- matchsetuptime = gi.cvar("matchsetuptime", "10", 0);
- matchstarttime = gi.cvar("matchstarttime", "20", 0);
- admin_password = gi.cvar("admin_password", "", 0);
- warp_list = gi.cvar("warp_list", "q2ctf1 q2ctf2 q2ctf3 q2ctf4 q2ctf5", 0);
- }
- /*--------------------------------------------------------------------------*/
- char *CTFTeamName(int team)
- {
- switch (team) {
- case CTF_TEAM1:
- return "RED";
- case CTF_TEAM2:
- return "BLUE";
- }
- return "UKNOWN";
- }
- char *CTFOtherTeamName(int team)
- {
- switch (team) {
- case CTF_TEAM1:
- return "BLUE";
- case CTF_TEAM2:
- return "RED";
- }
- return "UKNOWN";
- }
- int CTFOtherTeam(int team)
- {
- switch (team) {
- case CTF_TEAM1:
- return CTF_TEAM2;
- case CTF_TEAM2:
- return CTF_TEAM1;
- }
- return -1; // invalid value
- }
- /*--------------------------------------------------------------------------*/
- edict_t *SelectRandomDeathmatchSpawnPoint (void);
- edict_t *SelectFarthestDeathmatchSpawnPoint (void);
- float PlayersRangeFromSpot (edict_t *spot);
- void CTFAssignSkin(edict_t *ent, char *s)
- {
- int playernum = ent-g_edicts-1;
- char *p;
- char t[64];
- Com_sprintf(t, sizeof(t), "%s", s);
- if ((p = strrchr(t, '/')) != NULL)
- p[1] = 0;
- else
- strcpy(t, "male/");
- switch (ent->client->resp.ctf_team) {
- case CTF_TEAM1:
- gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s%s",
- ent->client->pers.netname, t, CTF_TEAM1_SKIN) );
- break;
- case CTF_TEAM2:
- gi.configstring (CS_PLAYERSKINS+playernum,
- va("%s\\%s%s", ent->client->pers.netname, t, CTF_TEAM2_SKIN) );
- break;
- default:
- gi.configstring (CS_PLAYERSKINS+playernum,
- va("%s\\%s", ent->client->pers.netname, s) );
- break;
- }
- // gi.cprintf(ent, PRINT_HIGH, "You have been assigned to %s team.\n", ent->client->pers.netname);
- }
- void CTFAssignTeam(gclient_t *who)
- {
- edict_t *player;
- int i;
- int team1count = 0, team2count = 0;
- who->resp.ctf_state = 0;
- if (!((int)dmflags->value & DF_CTF_FORCEJOIN)) {
- who->resp.ctf_team = CTF_NOTEAM;
- return;
- }
- for (i = 1; i <= maxclients->value; i++) {
- player = &g_edicts[i];
- if (!player->inuse || player->client == who)
- continue;
- switch (player->client->resp.ctf_team) {
- case CTF_TEAM1:
- team1count++;
- break;
- case CTF_TEAM2:
- team2count++;
- }
- }
- if (team1count < team2count)
- who->resp.ctf_team = CTF_TEAM1;
- else if (team2count < team1count)
- who->resp.ctf_team = CTF_TEAM2;
- else if (rand() & 1)
- who->resp.ctf_team = CTF_TEAM1;
- else
- who->resp.ctf_team = CTF_TEAM2;
- }
- /*
- ================
- SelectCTFSpawnPoint
- go to a ctf point, but NOT the two points closest
- to other players
- ================
- */
- edict_t *SelectCTFSpawnPoint (edict_t *ent)
- {
- edict_t *spot, *spot1, *spot2;
- int count = 0;
- int selection;
- float range, range1, range2;
- char *cname;
- if (ent->client->resp.ctf_state)
- if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST)
- return SelectFarthestDeathmatchSpawnPoint ();
- else
- return SelectRandomDeathmatchSpawnPoint ();
- ent->client->resp.ctf_state++;
- switch (ent->client->resp.ctf_team) {
- case CTF_TEAM1:
- cname = "info_player_team1";
- break;
- case CTF_TEAM2:
- cname = "info_player_team2";
- break;
- default:
- return SelectRandomDeathmatchSpawnPoint();
- }
- spot = NULL;
- range1 = range2 = 99999;
- spot1 = spot2 = NULL;
- while ((spot = G_Find (spot, FOFS(classname), cname)) != NULL)
- {
- count++;
- range = PlayersRangeFromSpot(spot);
- if (range < range1)
- {
- range1 = range;
- spot1 = spot;
- }
- else if (range < range2)
- {
- range2 = range;
- spot2 = spot;
- }
- }
- if (!count)
- return SelectRandomDeathmatchSpawnPoint();
- if (count <= 2)
- {
- spot1 = spot2 = NULL;
- }
- else
- count -= 2;
- selection = rand() % count;
- spot = NULL;
- do
- {
- spot = G_Find (spot, FOFS(classname), cname);
- if (spot == spot1 || spot == spot2)
- selection++;
- } while(selection--);
- return spot;
- }
- /*------------------------------------------------------------------------*/
- /*
- CTFFragBonuses
- Calculate the bonuses for flag defense, flag carrier defense, etc.
- Note that bonuses are not cumaltive. You get one, they are in importance
- order.
- */
- void CTFFragBonuses(edict_t *targ, edict_t *inflictor, edict_t *attacker)
- {
- int i;
- edict_t *ent;
- gitem_t *flag_item, *enemy_flag_item;
- int otherteam;
- edict_t *flag, *carrier;
- char *c;
- vec3_t v1, v2;
- if (targ->client && attacker->client) {
- if (attacker->client->resp.ghost)
- if (attacker != targ)
- attacker->client->resp.ghost->kills++;
- if (targ->client->resp.ghost)
- targ->client->resp.ghost->deaths++;
- }
- // no bonus for fragging yourself
- if (!targ->client || !attacker->client || targ == attacker)
- return;
- otherteam = CTFOtherTeam(targ->client->resp.ctf_team);
- 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 (targ->client->resp.ctf_team == CTF_TEAM1) {
- flag_item = flag1_item;
- enemy_flag_item = flag2_item;
- } else {
- flag_item = flag2_item;
- enemy_flag_item = flag1_item;
- }
- // did the attacker frag the flag carrier?
- if (targ->client->pers.inventory[ITEM_INDEX(enemy_flag_item)]) {
- attacker->client->resp.ctf_lastfraggedcarrier = level.time;
- attacker->client->resp.score += CTF_FRAG_CARRIER_BONUS;
- gi.cprintf(attacker, PRINT_MEDIUM, "BONUS: %d points for fragging enemy flag carrier.\n",
- CTF_FRAG_CARRIER_BONUS);
- // the target had the flag, clear the hurt carrier
- // field on the other team
- for (i = 1; i <= maxclients->value; i++) {
- ent = g_edicts + i;
- if (ent->inuse && ent->client->resp.ctf_team == otherteam)
- ent->client->resp.ctf_lasthurtcarrier = 0;
- }
- return;
- }
- if (targ->client->resp.ctf_lasthurtcarrier &&
- level.time - targ->client->resp.ctf_lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
- !attacker->client->pers.inventory[ITEM_INDEX(flag_item)]) {
- // attacker is on the same team as the flag carrier and
- // fragged a guy who hurt our flag carrier
- attacker->client->resp.score += CTF_CARRIER_DANGER_PROTECT_BONUS;
- gi.bprintf(PRINT_MEDIUM, "%s defends %s's flag carrier against an agressive enemy\n",
- attacker->client->pers.netname,
- CTFTeamName(attacker->client->resp.ctf_team));
- if (attacker->client->resp.ghost)
- attacker->client->resp.ghost->carrierdef++;
- return;
- }
- // flag and flag carrier area defense bonuses
- // we have to find the flag and carrier entities
- // find the flag
- switch (attacker->client->resp.ctf_team) {
- case CTF_TEAM1:
- c = "item_flag_team1";
- break;
- case CTF_TEAM2:
- c = "item_flag_team2";
- break;
- default:
- return;
- }
- flag = NULL;
- while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) {
- if (!(flag->spawnflags & DROPPED_ITEM))
- break;
- }
- if (!flag)
- return; // can't find attacker's flag
- // find attacker's team's flag carrier
- for (i = 1; i <= maxclients->value; i++) {
- carrier = g_edicts + i;
- if (carrier->inuse &&
- carrier->client->pers.inventory[ITEM_INDEX(flag_item)])
- break;
- carrier = NULL;
- }
- // 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->s.origin, flag->s.origin, v1);
- VectorSubtract(attacker->s.origin, flag->s.origin, v2);
- if ((VectorLength(v1) < CTF_TARGET_PROTECT_RADIUS ||
- VectorLength(v2) < CTF_TARGET_PROTECT_RADIUS ||
- loc_CanSee(flag, targ) || loc_CanSee(flag, attacker)) &&
- attacker->client->resp.ctf_team != targ->client->resp.ctf_team) {
- // we defended the base flag
- attacker->client->resp.score += CTF_FLAG_DEFENSE_BONUS;
- if (flag->solid == SOLID_NOT)
- gi.bprintf(PRINT_MEDIUM, "%s defends the %s base.\n",
- attacker->client->pers.netname,
- CTFTeamName(attacker->client->resp.ctf_team));
- else
- gi.bprintf(PRINT_MEDIUM, "%s defends the %s flag.\n",
- attacker->client->pers.netname,
- CTFTeamName(attacker->client->resp.ctf_team));
- if (attacker->client->resp.ghost)
- attacker->client->resp.ghost->basedef++;
- return;
- }
- if (carrier && carrier != attacker) {
- VectorSubtract(targ->s.origin, carrier->s.origin, v1);
- VectorSubtract(attacker->s.origin, carrier->s.origin, v1);
- if (VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS ||
- VectorLength(v2) < CTF_ATTACKER_PROTECT_RADIUS ||
- loc_CanSee(carrier, targ) || loc_CanSee(carrier, attacker)) {
- attacker->client->resp.score += CTF_CARRIER_PROTECT_BONUS;
- gi.bprintf(PRINT_MEDIUM, "%s defends the %s's flag carrier.\n",
- attacker->client->pers.netname,
- CTFTeamName(attacker->client->resp.ctf_team));
- if (attacker->client->resp.ghost)
- attacker->client->resp.ghost->carrierdef++;
- return;
- }
- }
- }
- void CTFCheckHurtCarrier(edict_t *targ, edict_t *attacker)
- {
- gitem_t *flag_item;
- if (!targ->client || !attacker->client)
- return;
- if (targ->client->resp.ctf_team == CTF_TEAM1)
- flag_item = flag2_item;
- else
- flag_item = flag1_item;
- if (targ->client->pers.inventory[ITEM_INDEX(flag_item)] &&
- targ->client->resp.ctf_team != attacker->client->resp.ctf_team)
- attacker->client->resp.ctf_lasthurtcarrier = level.time;
- }
- /*------------------------------------------------------------------------*/
- void CTFResetFlag(int ctf_team)
- {
- char *c;
- edict_t *ent;
- switch (ctf_team) {
- case CTF_TEAM1:
- c = "item_flag_team1";
- break;
- case CTF_TEAM2:
- c = "item_flag_team2";
- break;
- default:
- return;
- }
- ent = NULL;
- while ((ent = G_Find (ent, FOFS(classname), c)) != NULL) {
- if (ent->spawnflags & DROPPED_ITEM)
- G_FreeEdict(ent);
- else {
- ent->svflags &= ~SVF_NOCLIENT;
- ent->solid = SOLID_TRIGGER;
- gi.linkentity(ent);
- ent->s.event = EV_ITEM_RESPAWN;
- }
- }
- }
- void CTFResetFlags(void)
- {
- CTFResetFlag(CTF_TEAM1);
- CTFResetFlag(CTF_TEAM2);
- }
- qboolean CTFPickup_Flag(edict_t *ent, edict_t *other)
- {
- int ctf_team;
- int i;
- edict_t *player;
- gitem_t *flag_item, *enemy_flag_item;
- // figure out what team this flag is
- if (strcmp(ent->classname, "item_flag_team1") == 0)
- ctf_team = CTF_TEAM1;
- else if (strcmp(ent->classname, "item_flag_team2") == 0)
- ctf_team = CTF_TEAM2;
- else {
- gi.cprintf(ent, PRINT_HIGH, "Don't know what team the flag is on.\n");
- return false;
- }
- // same team, if the flag at base, check to he has the enemy flag
- if (ctf_team == CTF_TEAM1) {
- flag_item = flag1_item;
- enemy_flag_item = flag2_item;
- } else {
- flag_item = flag2_item;
- enemy_flag_item = flag1_item;
- }
- if (ctf_team == other->client->resp.ctf_team) {
- if (!(ent->spawnflags & DROPPED_ITEM)) {
- // the flag is at home base. if the player has the enemy
- // flag, he's just won!
-
- if (other->client->pers.inventory[ITEM_INDEX(enemy_flag_item)]) {
- gi.bprintf(PRINT_HIGH, "%s captured the %s flag!\n",
- other->client->pers.netname, CTFOtherTeamName(ctf_team));
- other->client->pers.inventory[ITEM_INDEX(enemy_flag_item)] = 0;
- ctfgame.last_flag_capture = level.time;
- ctfgame.last_capture_team = ctf_team;
- if (ctf_team == CTF_TEAM1)
- ctfgame.team1++;
- else
- ctfgame.team2++;
- gi.sound (ent, CHAN_RELIABLE+CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex("ctf/flagcap.wav"), 1, ATTN_NONE, 0);
- // other gets another 10 frag bonus
- other->client->resp.score += CTF_CAPTURE_BONUS;
- if (other->client->resp.ghost)
- other->client->resp.ghost->caps++;
- // Ok, let's do the player loop, hand out the bonuses
- for (i = 1; i <= maxclients->value; i++) {
- player = &g_edicts[i];
- if (!player->inuse)
- continue;
- if (player->client->resp.ctf_team != other->client->resp.ctf_team)
- player->client->resp.ctf_lasthurtcarrier = -5;
- else if (player->client->resp.ctf_team == other->client->resp.ctf_team) {
- if (player != other)
- player->client->resp.score += CTF_TEAM_BONUS;
- // award extra points for capture assists
- if (player->client->resp.ctf_lastreturnedflag + CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) {
- gi.bprintf(PRINT_HIGH, "%s gets an assist for returning the flag!\n", player->client->pers.netname);
- player->client->resp.score += CTF_RETURN_FLAG_ASSIST_BONUS;
- }
- if (player->client->resp.ctf_lastfraggedcarrier + CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) {
- gi.bprintf(PRINT_HIGH, "%s gets an assist for fragging the flag carrier!\n", player->client->pers.netname);
- player->client->resp.score += CTF_FRAG_CARRIER_ASSIST_BONUS;
- }
- }
- }
- CTFResetFlags();
- return false;
- }
- return false; // its at home base already
- }
- // hey, its not home. return it by teleporting it back
- gi.bprintf(PRINT_HIGH, "%s returned the %s flag!\n",
- other->client->pers.netname, CTFTeamName(ctf_team));
- other->client->resp.score += CTF_RECOVERY_BONUS;
- other->client->resp.ctf_lastreturnedflag = level.time;
- gi.sound (ent, CHAN_RELIABLE+CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex("ctf/flagret.wav"), 1, ATTN_NONE, 0);
- //CTFResetFlag will remove this entity! We must return false
- CTFResetFlag(ctf_team);
- return false;
- }
- // hey, its not our flag, pick it up
- gi.bprintf(PRINT_HIGH, "%s got the %s flag!\n",
- other->client->pers.netname, CTFTeamName(ctf_team));
- other->client->resp.score += CTF_FLAG_BONUS;
- other->client->pers.inventory[ITEM_INDEX(flag_item)] = 1;
- other->client->resp.ctf_flagsince = level.time;
- // pick up the flag
- // if it's not a dropped flag, we just make is disappear
- // if it's dropped, it will be removed by the pickup caller
- if (!(ent->spawnflags & DROPPED_ITEM)) {
- ent->flags |= FL_RESPAWN;
- ent->svflags |= SVF_NOCLIENT;
- ent->solid = SOLID_NOT;
- }
- return true;
- }
- static void CTFDropFlagTouch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
- {
- //owner (who dropped us) can't touch for two secs
- if (other == ent->owner &&
- ent->nextthink - level.time > CTF_AUTO_FLAG_RETURN_TIMEOUT-2)
- return;
- Touch_Item (ent, other, plane, surf);
- }
- static void CTFDropFlagThink(edict_t *ent)
- {
- // auto return the flag
- // reset flag will remove ourselves
- if (strcmp(ent->classname, "item_flag_team1") == 0) {
- CTFResetFlag(CTF_TEAM1);
- gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n",
- CTFTeamName(CTF_TEAM1));
- } else if (strcmp(ent->classname, "item_flag_team2") == 0) {
- CTFResetFlag(CTF_TEAM2);
- gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n",
- CTFTeamName(CTF_TEAM2));
- }
- }
- // Called from PlayerDie, to drop the flag from a dying player
- void CTFDeadDropFlag(edict_t *self)
- {
- edict_t *dropped = NULL;
- if (self->client->pers.inventory[ITEM_INDEX(flag1_item)]) {
- dropped = Drop_Item(self, flag1_item);
- self->client->pers.inventory[ITEM_INDEX(flag1_item)] = 0;
- gi.bprintf(PRINT_HIGH, "%s lost the %s flag!\n",
- self->client->pers.netname, CTFTeamName(CTF_TEAM1));
- } else if (self->client->pers.inventory[ITEM_INDEX(flag2_item)]) {
- dropped = Drop_Item(self, flag2_item);
- self->client->pers.inventory[ITEM_INDEX(flag2_item)] = 0;
- gi.bprintf(PRINT_HIGH, "%s lost the %s flag!\n",
- self->client->pers.netname, CTFTeamName(CTF_TEAM2));
- }
- if (dropped) {
- dropped->think = CTFDropFlagThink;
- dropped->nextthink = level.time + CTF_AUTO_FLAG_RETURN_TIMEOUT;
- dropped->touch = CTFDropFlagTouch;
- }
- }
- qboolean CTFDrop_Flag(edict_t *ent, gitem_t *item)
- {
- if (rand() & 1)
- gi.cprintf(ent, PRINT_HIGH, "Only lusers drop flags.\n");
- else
- gi.cprintf(ent, PRINT_HIGH, "Winners don't drop flags.\n");
- return false;
- }
- static void CTFFlagThink(edict_t *ent)
- {
- if (ent->solid != SOLID_NOT)
- ent->s.frame = 173 + (((ent->s.frame - 173) + 1) % 16);
- ent->nextthink = level.time + FRAMETIME;
- }
- void CTFFlagSetup (edict_t *ent)
- {
- trace_t tr;
- vec3_t dest;
- float *v;
- v = tv(-15,-15,-15);
- VectorCopy (v, ent->mins);
- v = tv(15,15,15);
- VectorCopy (v, ent->maxs);
- if (ent->model)
- gi.setmodel (ent, ent->model);
- else
- gi.setmodel (ent, ent->item->world_model);
- ent->solid = SOLID_TRIGGER;
- ent->movetype = MOVETYPE_TOSS;
- ent->touch = Touch_Item;
- v = tv(0,0,-128);
- VectorAdd (ent->s.origin, v, dest);
- tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID);
- if (tr.startsolid)
- {
- gi.dprintf ("CTFFlagSetup: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
- G_FreeEdict (ent);
- return;
- }
- VectorCopy (tr.endpos, ent->s.origin);
- gi.linkentity (ent);
- ent->nextthink = level.time + FRAMETIME;
- ent->think = CTFFlagThink;
- }
- void CTFEffects(edict_t *player)
- {
- player->s.effects &= ~(EF_FLAG1 | EF_FLAG2);
- if (player->health > 0) {
- if (player->client->pers.inventory[ITEM_INDEX(flag1_item)]) {
- player->s.effects |= EF_FLAG1;
- }
- if (player->client->pers.inventory[ITEM_INDEX(flag2_item)]) {
- player->s.effects |= EF_FLAG2;
- }
- }
- if (player->client->pers.inventory[ITEM_INDEX(flag1_item)])
- player->s.modelindex3 = gi.modelindex("players/male/flag1.md2");
- else if (player->client->pers.inventory[ITEM_INDEX(flag2_item)])
- player->s.modelindex3 = gi.modelindex("players/male/flag2.md2");
- else
- player->s.modelindex3 = 0;
- }
- // called when we enter the intermission
- void CTFCalcScores(void)
- {
- int i;
- ctfgame.total1 = ctfgame.total2 = 0;
- for (i = 0; i < maxclients->value; i++) {
- if (!g_edicts[i+1].inuse)
- continue;
- if (game.clients[i].resp.ctf_team == CTF_TEAM1)
- ctfgame.total1 += game.clients[i].resp.score;
- else if (game.clients[i].resp.ctf_team == CTF_TEAM2)
- ctfgame.total2 += game.clients[i].resp.score;
- }
- }
- void CTFID_f (edict_t *ent)
- {
- if (ent->client->resp.id_state) {
- gi.cprintf(ent, PRINT_HIGH, "Disabling player identication display.\n");
- ent->client->resp.id_state = false;
- } else {
- gi.cprintf(ent, PRINT_HIGH, "Activating player identication display.\n");
- ent->client->resp.id_state = true;
- }
- }
- static void CTFSetIDView(edict_t *ent)
- {
- vec3_t forward, dir;
- trace_t tr;
- edict_t *who, *best;
- float bd = 0, d;
- int i;
- ent->client->ps.stats[STAT_CTF_ID_VIEW] = 0;
- AngleVectors(ent->client->v_angle, forward, NULL, NULL);
- VectorScale(forward, 1024, forward);
- VectorAdd(ent->s.origin, forward, forward);
- tr = gi.trace(ent->s.origin, NULL, NULL, forward, ent, MASK_SOLID);
- if (tr.fraction < 1 && tr.ent && tr.ent->client) {
- ent->client->ps.stats[STAT_CTF_ID_VIEW] =
- CS_PLAYERSKINS + (ent - g_edicts - 1);
- return;
- }
- AngleVectors(ent->client->v_angle, forward, NULL, NULL);
- best = NULL;
- for (i = 1; i <= maxclients->value; i++) {
- who = g_edicts + i;
- if (!who->inuse || who->solid == SOLID_NOT)
- continue;
- VectorSubtract(who->s.origin, ent->s.origin, dir);
- VectorNormalize(dir);
- d = DotProduct(forward, dir);
- if (d > bd && loc_CanSee(ent, who)) {
- bd = d;
- best = who;
- }
- }
- if (bd > 0.90)
- ent->client->ps.stats[STAT_CTF_ID_VIEW] =
- CS_PLAYERSKINS + (best - g_edicts - 1);
- }
- void SetCTFStats(edict_t *ent)
- {
- gitem_t *tech;
- int i;
- int p1, p2;
- edict_t *e;
- if (ctfgame.match > MATCH_NONE)
- ent->client->ps.stats[STAT_CTF_MATCH] = CONFIG_CTF_MATCH;
- else
- ent->client->ps.stats[STAT_CTF_MATCH] = 0;
- //ghosting
- if (ent->client->resp.ghost) {
- ent->client->resp.ghost->score = ent->client->resp.score;
- strcpy(ent->client->resp.ghost->netname, ent->client->pers.netname);
- ent->client->resp.ghost->number = ent->s.number;
- }
- // logo headers for the frag display
- ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = gi.imageindex ("ctfsb1");
- ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = gi.imageindex ("ctfsb2");
- // if during intermission, we must blink the team header of the winning team
- if (level.intermissiontime && (level.framenum & 8)) { // blink 1/8th 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;
- else if (ctfgame.team2 > ctfgame.team1)
- ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
- else if (ctfgame.total1 > ctfgame.total2) // frag tie breaker
- ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
- else if (ctfgame.total2 > ctfgame.total1)
- ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
- else { // tie game!
- ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
- ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
- }
- }
- // tech icon
- i = 0;
- ent->client->ps.stats[STAT_CTF_TECH] = 0;
- while (tnames[i]) {
- if ((tech = FindItemByClassname(tnames[i])) != NULL &&
- ent->client->pers.inventory[ITEM_INDEX(tech)]) {
- ent->client->ps.stats[STAT_CTF_TECH] = gi.imageindex(tech->icon);
- break;
- }
- i++;
- }
- // figure out what icon to display for team logos
- // three states:
- // flag at base
- // flag taken
- // flag dropped
- p1 = gi.imageindex ("i_ctf1");
- e = G_Find(NULL, FOFS(classname), "item_flag_team1");
- if (e != NULL) {
- if (e->solid == SOLID_NOT) {
- int i;
- // not at base
- // check if on player
- p1 = gi.imageindex ("i_ctf1d"); // default to dropped
- for (i = 1; i <= maxclients->value; i++)
- if (g_edicts[i].inuse &&
- g_edicts[i].client->pers.inventory[ITEM_INDEX(flag1_item)]) {
- // enemy has it
- p1 = gi.imageindex ("i_ctf1t");
- break;
- }
- } else if (e->spawnflags & DROPPED_ITEM)
- p1 = gi.imageindex ("i_ctf1d"); // must be dropped
- }
- p2 = gi.imageindex ("i_ctf2");
- e = G_Find(NULL, FOFS(classname), "item_flag_team2");
- if (e != NULL) {
- if (e->solid == SOLID_NOT) {
- int i;
- // not at base
- // check if on player
- p2 = gi.imageindex ("i_ctf2d"); // default to dropped
- for (i = 1; i <= maxclients->value; i++)
- if (g_edicts[i].inuse &&
- g_edicts[i].client->pers.inventory[ITEM_INDEX(flag2_item)]) {
- // enemy has it
- p2 = gi.imageindex ("i_ctf2t");
- break;
- }
- } else if (e->spawnflags & DROPPED_ITEM)
- p2 = gi.imageindex ("i_ctf2d"); // must be dropped
- }
- ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1;
- ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2;
- if (ctfgame.last_flag_capture && level.time - ctfgame.last_flag_capture < 5) {
- if (ctfgame.last_capture_team == CTF_TEAM1)
- if (level.framenum & 8)
- ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1;
- else
- ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = 0;
- else
- if (level.framenum & 8)
- ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2;
- else
- ent->client->ps.stats[STAT_CTF_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;
- if (ent->client->resp.ctf_team == CTF_TEAM1 &&
- ent->client->pers.inventory[ITEM_INDEX(flag2_item)] &&
- (level.framenum & 8))
- ent->client->ps.stats[STAT_CTF_FLAG_PIC] = gi.imageindex ("i_ctf2");
- else if (ent->client->resp.ctf_team == CTF_TEAM2 &&
- ent->client->pers.inventory[ITEM_INDEX(flag1_item)] &&
- (level.framenum & 8))
- ent->client->ps.stats[STAT_CTF_FLAG_PIC] = gi.imageindex ("i_ctf1");
- ent->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = 0;
- ent->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = 0;
- if (ent->client->resp.ctf_team == CTF_TEAM1)
- ent->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = gi.imageindex ("i_ctfj");
- else if (ent->client->resp.ctf_team == CTF_TEAM2)
- ent->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = gi.imageindex ("i_ctfj");
- ent->client->ps.stats[STAT_CTF_ID_VIEW] = 0;
- if (ent->client->resp.id_state)
- CTFSetIDView(ent);
- }
- /*------------------------------------------------------------------------*/
- /*QUAKED info_player_team1 (1 0 0) (-16 -16 -24) (16 16 32)
- potential team1 spawning position for ctf games
- */
- void SP_info_player_team1(edict_t *self)
- {
- }
- /*QUAKED info_player_team2 (0 0 1) (-16 -16 -24) (16 16 32)
- potential team2 spawning position for ctf games
- */
- void SP_info_player_team2(edict_t *self)
- {
- }
- /*------------------------------------------------------------------------*/
- /* GRAPPLE */
- /*------------------------------------------------------------------------*/
- // ent is player
- void CTFPlayerResetGrapple(edict_t *ent)
- {
- if (ent->client && ent->client->ctf_grapple)
- CTFResetGrapple(ent->client->ctf_grapple);
- }
- // self is grapple, not player
- void CTFResetGrapple(edict_t *self)
- {
- if (self->owner->client->ctf_grapple) {
- float volume = 1.0;
- gclient_t *cl;
- if (self->owner->client->silencer_shots)
- volume = 0.2;
- gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grreset.wav"), volume, ATTN_NORM, 0);
- cl = self->owner->client;
- cl->ctf_grapple = NULL;
- cl->ctf_grapplereleasetime = level.time;
- cl->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
- cl->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
- G_FreeEdict(self);
- }
- }
- void CTFGrappleTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
- {
- float volume = 1.0;
- if (other == self->owner)
- return;
- if (self->owner->client->ctf_grapplestate != CTF_GRAPPLE_STATE_FLY)
- return;
- if (surf && (surf->flags & SURF_SKY))
- {
- CTFResetGrapple(self);
- return;
- }
- VectorCopy(vec3_origin, self->velocity);
- PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
- if (other->takedamage) {
- T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, 0, MOD_GRAPPLE);
- CTFResetGrapple(self);
- return;
- }
- self->owner->client->ctf_grapplestate = CTF_GRAPPLE_STATE_PULL; // we're on hook
- self->enemy = other;
- self->solid = SOLID_NOT;
- if (self->owner->client->silencer_shots)
- volume = 0.2;
- gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grpull.wav"), volume, ATTN_NORM, 0);
- gi.sound (self, CHAN_WEAPON, gi.soundindex("weapons/grapple/grhit.wav"), volume, ATTN_NORM, 0);
- gi.WriteByte (svc_temp_entity);
- gi.WriteByte (TE_SPARKS);
- gi.WritePosition (self->s.origin);
- if (!plane)
- gi.WriteDir (vec3_origin);
- else
- gi.WriteDir (plane->normal);
- gi.multicast (self->s.origin, MULTICAST_PVS);
- }
- // draw beam between grapple and self
- void CTFGrappleDrawCable(edict_t *self)
- {
- vec3_t offset, start, end, f, r;
- vec3_t dir;
- float distance;
- AngleVectors (self->owner->client->v_angle, f, r, NULL);
- VectorSet(offset, 16, 16, self->owner->viewheight-8);
- P_ProjectSource (self->owner->client, self->owner->s.origin, offset, f, r, start);
- VectorSubtract(start, self->owner->s.origin, offset);
- VectorSubtract (start, self->s.origin, dir);
- distance = VectorLength(dir);
- // don't draw cable if close
- if (distance < 64)
- return;
- #if 0
- if (distance > 256)
- return;
- // check for min/max pitch
- vectoangles (dir, angles);
- if (angles[0] < -180)
- angles[0] += 360;
- if (fabs(angles[0]) > 45)
- return;
- trace_t tr; //!!
- tr = gi.trace (start, NULL, NULL, self->s.origin, self, MASK_SHOT);
- if (tr.ent != self) {
- CTFResetGrapple(self);
- return;
- }
- #endif
- // adjust start for beam origin being in middle of a segment
- // VectorMA (start, 8, f, start);
- VectorCopy (self->s.origin, end);
- // adjust end z for end spot since the monster is currently dead
- // end[2] = self->absmin[2] + self->size[2] / 2;
- gi.WriteByte (svc_temp_entity);
- #if 1 //def USE_GRAPPLE_CABLE
- gi.WriteByte (TE_GRAPPLE_CABLE);
- gi.WriteShort (self->owner - g_edicts);
- gi.WritePosition (self->owner->s.origin);
- gi.WritePosition (end);
- gi.WritePosition (offset);
- #else
- gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
- gi.WriteShort (self - g_edicts);
- gi.WritePosition (end);
- gi.WritePosition (start);
- #endif
- gi.multicast (self->s.origin, MULTICAST_PVS);
- }
- void SV_AddGravity (edict_t *ent);
- // pull the player toward the grapple
- void CTFGrapplePull(edict_t *self)
- {
- vec3_t hookdir, v;
- float vlen;
- if (strcmp(self->owner->client->pers.weapon->classname, "weapon_grapple") == 0 &&
- !self->owner->client->newweapon &&
- self->owner->client->weaponstate != WEAPON_FIRING &&
- self->owner->client->weaponstate != WEAPON_ACTIVATING) {
- CTFResetGrapple(self);
- return;
- }
- if (self->enemy) {
- if (self->enemy->solid == SOLID_NOT) {
- CTFResetGrapple(self);
- return;
- }
- if (self->enemy->solid == SOLID_BBOX) {
- VectorScale(self->enemy->size, 0.5, v);
- VectorAdd(v, self->enemy->s.origin, v);
- VectorAdd(v, self->enemy->mins, self->s.origin);
- gi.linkentity (self);
- } else
- VectorCopy(self->enemy->velocity, self->velocity);
- if (self->enemy->takedamage &&
- !CheckTeamDamage (self->enemy, self->owner)) {
- float volume = 1.0;
- if (self->owner->client->silencer_shots)
- volume = 0.2;
- T_Damage (self->enemy, self, self->owner, self->velocity, self->s.origin, vec3_origin, 1, 1, 0, MOD_GRAPPLE);
- gi.sound (self, CHAN_WEAPON, gi.soundindex("weapons/grapple/grhurt.wav"), volume, ATTN_NORM, 0);
- }
- if (self->enemy->deadflag) { // he died
- CTFResetGrapple(self);
- return;
- }
- }
- CTFGrappleDrawCable(self);
- if (self->owner->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY) {
- // pull player toward grapple
- // this causes icky stuff with prediction, we need to extend
- // the prediction layer to include two new fields in the player
- // move stuff: a point and a velocity. The client should add
- // that velociy in the direction of the point
- vec3_t forward, up;
- AngleVectors (self->owner->client->v_angle, forward, NULL, up);
- VectorCopy(self->owner->s.origin, v);
- v[2] += self->owner->viewheight;
- VectorSubtract (self->s.origin, v, hookdir);
- vlen = VectorLength(hookdir);
- if (self->owner->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL &&
- vlen < 64) {
- float volume = 1.0;
- if (self->owner->client->silencer_shots)
- volume = 0.2;
- self->owner->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
- gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grhang.wav"), volume, ATTN_NORM, 0);
- self->owner->client->ctf_grapplestate = CTF_GRAPPLE_STATE_HANG;
- }
- VectorNormalize (hookdir);
- VectorScale(hookdir, CTF_GRAPPLE_PULL_SPEED, hookdir);
- VectorCopy(hookdir, self->owner->velocity);
- SV_AddGravity(self->owner);
- }
- }
- void CTFFireGrapple (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect)
- {
- edict_t *grapple;
- trace_t tr;
- VectorNormalize (dir);
- grapple = G_Spawn();
- VectorCopy (start, grapple->s.origin);
- VectorCopy (start, grapple->s.old_origin);
- vectoangles (dir, grapple->s.angles);
- VectorScale (dir, speed, grapple->velocity);
- grapple->movetype = MOVETYPE_FLYMISSILE;
- grapple->clipmask = MASK_SHOT;
- grapple->solid = SOLID_BBOX;
- grapple->s.effects |= effect;
- VectorClear (grapple->mins);
- VectorClear (grapple->maxs);
- grapple->s.modelindex = gi.modelindex ("models/weapons/grapple/hook/tris.md2");
- // grapple->s.sound = gi.soundindex ("misc/lasfly.wav");
- grapple->owner = self;
- grapple->touch = CTFGrappleTouch;
- // grapple->nextthink = level.time + FRAMETIME;
- // grapple->think = CTFGrappleThink;
- grapple->dmg = damage;
- self->client->ctf_grapple = grapple;
- self->client->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
- gi.linkentity (grapple);
- tr = gi.trace (self->s.origin, NULL, NULL, grapple->s.origin, grapple, MASK_SHOT);
- if (tr.fraction < 1.0)
- {
- VectorMA (grapple->s.origin, -10, dir, grapple->s.origin);
- grapple->touch (grapple, tr.ent, NULL, NULL);
- }
- }
- void CTFGrappleFire (edict_t *ent, vec3_t g_offset, int damage, int effect)
- {
- vec3_t forward, right;
- vec3_t start;
- vec3_t offset;
- float volume = 1.0;
- if (ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY)
- return; // it's already out
- AngleVectors (ent->client->v_angle, forward, right, NULL);
- // VectorSet(offset, 24, 16, ent->viewheight-8+2);
- VectorSet(offset, 24, 8, ent->viewheight-8+2);
- VectorAdd (offset, g_offset, offset);
- P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
- VectorScale (forward, -2, ent->client->kick_origin);
- ent->client->kick_angles[0] = -1;
- if (ent->client->silencer_shots)
- volume = 0.2;
- gi.sound (ent, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grfire.wav"), volume, ATTN_NORM, 0);
- CTFFireGrapple (ent, start, forward, damage, CTF_GRAPPLE_SPEED, effect);
- #if 0
- // send muzzle flash
- gi.WriteByte (svc_muzzleflash);
- gi.WriteShort (ent-g_edicts);
- gi.WriteByte (MZ_BLASTER);
- gi.multicast (ent->s.origin, MULTICAST_PVS);
- #endif
- PlayerNoise(ent, start, PNOISE_WEAPON);
- }
- void CTFWeapon_Grapple_Fire (edict_t *ent)
- {
- int damage;
- damage = 10;
- CTFGrappleFire (ent, vec3_origin, damage, 0);
- ent->client->ps.gunframe++;
- }
- void CTFWeapon_Grapple (edict_t *ent)
- {
- static int pause_frames[] = {10, 18, 27, 0};
- static int fire_frames[] = {6, 0};
- int prevstate;
- // if the the attack button is still down, stay in the firing frame
- if ((ent->client->buttons & BUTTON_ATTACK) &&
- ent->client->weaponstate == WEAPON_FIRING &&
- ent->client->ctf_grapple)
- ent->client->ps.gunframe = 9;
- if (!(ent->client->buttons & BUTTON_ATTACK) &&
- ent->client->ctf_grapple) {
- CTFResetGrapple(ent->client->ctf_grapple);
- if (ent->client->weaponstate == WEAPON_FIRING)
- ent->client->weaponstate = WEAPON_READY;
- }
- if (ent->client->newweapon &&
- ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY &&
- ent->client->weaponstate == WEAPON_FIRING) {
- // he wants to change weapons while grappled
- ent->client->weaponstate = WEAPON_DROPPING;
- ent->client->ps.gunframe = 32;
- }
- prevstate = ent->client->weaponstate;
- Weapon_Generic (ent, 5, 9, 31, 36, pause_frames, fire_frames,
- CTFWeapon_Grapple_Fire);
- // if we just switched back to grapple, immediately go to fire frame
- if (prevstate == WEAPON_ACTIVATING &&
- ent->client->weaponstate == WEAPON_READY &&
- ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY) {
- if (!(ent->client->buttons & BUTTON_ATTACK))
- ent->client->ps.gunframe = 9;
- else
- ent->client->ps.gunframe = 5;
- ent->client->weaponstate = WEAPON_FIRING;
- }
- }
- void CTFTeam_f (edict_t *ent)
- {
- char *t, *s;
- int desired_team;
- t = gi.args();
- if (!*t) {
- gi.cprintf(ent, PRINT_HIGH, "You are on the %s team.\n",
- CTFTeamName(ent->client->resp.ctf_team));
- return;
- }
- if (ctfgame.match > MATCH_SETUP) {
- gi.cprintf(ent, PRINT_HIGH, "Can't change teams in a match.\n");
- return;
- }
- if (Q_stricmp(t, "red") == 0)
- desired_team = CTF_TEAM1;
- else if (Q_stricmp(t, "blue") == 0)
- desired_team = CTF_TEAM2;
- else {
- gi.cprintf(ent, PRINT_HIGH, "Unknown team %s.\n", t);
- return;
- }
- if (ent->client->resp.ctf_team == desired_team) {
- gi.cprintf(ent, PRINT_HIGH, "You are already on the %s team.\n",
- CTFTeamName(ent->client->resp.ctf_team));
- return;
- }
- ////
- ent->svflags = 0;
- ent->flags &= ~FL_GODMODE;
- ent->client->resp.ctf_team = desired_team;
- ent->client->resp.ctf_state = 0;
- s = Info_ValueForKey (ent->client->pers.userinfo, "skin");
- CTFAssignSkin(ent, s);
- if (ent->solid == SOLID_NOT) { // spectator
- PutClientInServer (ent);
- // add a teleportation effect
- ent->s.event = EV_PLAYER_TELEPORT;
- // hold in place briefly
- ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
- ent->client->ps.pmove.pm_time = 14;
- gi.bprintf(PRINT_HIGH, "%s joined the %s team.\n",
- ent->client->pers.netname, CTFTeamName(desired_team));
- return;
- }
- ent->health = 0;
- player_die (ent, ent, ent, 100000, vec3_origin);
- // don't even bother waiting for death frames
- ent->deadflag = DEAD_DEAD;
- respawn (ent);
- ent->client->resp.score = 0;
- gi.bprintf(PRINT_HIGH, "%s changed to the %s team.\n",
- ent->client->pers.netname, CTFTeamName(desired_team));
- }
- /*
- ==================
- CTFScoreboardMessage
- ==================
- */
- void CTFScoreboardMessage (edict_t *ent, edict_t *killer)
- {
- char entry[1024];
- char string[1400];
- int len;
- int i, j, k, n;
- int sorted[2][MAX_CLIENTS];
- int sortedscores[2][MAX_CLIENTS];
- int score, total[2], totalscore[2];
- int last[2];
- gclient_t *cl;
- edict_t *cl_ent;
- int team;
- int maxsize = 1000;
- // sort the clients by team and score
- total[0] = total[1] = 0;
- last[0] = last[1] = 0;
- totalscore[0] = totalscore[1] = 0;
- for (i=0 ; i<game.maxclients ; i++)
- {
- cl_ent = g_edicts + 1 + i;
- if (!cl_ent->inuse)
- continue;
- if (game.clients[i].resp.ctf_team == CTF_TEAM1)
- team = 0;
- else if (game.clients[i].resp.ctf_team == CTF_TEAM2)
- team = 1;
- else
- continue; // unknown team?
- score = game.clients[i].resp.score;
- for (j=0 ; j<total[team] ; j++)
- {
- if (score > sortedscores[team][j])
- break;
- }
- for (k=total[team] ; k>j ; k--)
- {
- sorted[team][k] = sorted[team][k-1];
- sortedscores[team][k] = sortedscores[team][k-1];
- }
- sorted[team][j] = i;
- sortedscores[team][j] = score;
- totalscore[team] += score;
- total[team]++;
- }
- // print level name and exit rules
- // add the clients in sorted order
- *string = 0;
- len = 0;
- // team one
- sprintf(string, "if 24 xv 8 yv 8 pic 24 endif "
- "xv 40 yv 28 string \"%4d/%-3d\" "
- "xv 98 yv 12 num 2 18 "
- "if 25 xv 168 yv 8 pic 25 endif "
- "xv 200 yv 28 string \"%4d/%-3d\" "
- "xv 256 yv 12 num 2 20 ",
- totalscore[0], total[0],
- totalscore[1], total[1]);
- len = strlen(string);
- for (i=0 ; i<16 ; i++)
- {
- if (i >= total[0] && i >= total[1])
- break; // we're done
- #if 0 //ndef NEW_SCORE
- // set up y
- sprintf(entry, "yv %d ", 42 + i * 8);
- if (maxsize - len > strlen(entry)) {
- strcat(string, entry);
- len = strlen(string);
- }
- #else
- *entry = 0;
- #endif
- // left side
- if (i < total[0]) {
- cl = &game.clients[sorted[0][i]];
- cl_ent = g_edicts + 1 + sorted[0][i];
- #if 0 //ndef NEW_SCORE
- sprintf(entry+strlen(entry),
- "xv 0 %s \"%3d %3d %-12.12s\" ",
- (cl_ent == ent) ? "string2" : "string",
- cl->resp.score,
- (cl->ping > 999) ? 999 : cl->ping,
- cl->pers.netname);
- if (cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)])
- strcat(entry, "xv 56 picn sbfctf2 ");
- #else
- sprintf(entry+strlen(entry),
- "ctf 0 %d %d %d %d ",
- 42 + i * 8,
- sorted[0][i],
- cl->resp.score,
- cl->ping > 999 ? 999 : cl->ping);
- if (cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)])
- sprintf(entry + strlen(entry), "xv 56 yv %d picn sbfctf2 ",
- 42 + i * 8);
- #endif
- if (maxsize - len > strlen(entry)) {
- strcat(string, entry);
- len = strlen(string);
- last[0] = i;
- }
- }
- // right side
- if (i < total[1]) {
- cl = &game.clients[sorted[1][i]];
- cl_ent = g_edicts + 1 + sorted[1][i];
- #if 0 //ndef NEW_SCORE
- sprintf(entry+strlen(entry),
- "xv 160 %s \"%3d %3d %-12.12s\" ",
- (cl_ent == ent) ? "string2" : "string",
- cl->resp.score,
- (cl->ping > 999) ? 999 : cl->ping,
- cl->pers.netname);
- if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)])
- strcat(entry, "xv 216 picn sbfctf1 ");
- #else
- sprintf(entry+strlen(entry),
- "ctf 160 %d %d %d %d ",
- 42 + i * 8,
- sorted[1][i],
- cl->resp.score,
- cl->ping > 999 ? 999 : cl->ping);
- if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)])
- sprintf(entry + strlen(entry), "xv 216 yv %d picn sbfctf1 ",
- 42 + i * 8);
- #endif
- if (maxsize - len > strlen(entry)) {
- strcat(string, entry);
- len = strlen(string);
- last[1] = i;
- }
- }
- }
- // put in spectators if we have enough room
- if (last[0] > last[1])
- j = last[0];
- else
- j = last[1];
- j = (j + 2) * 8 + 42;
- k = n = 0;
- if (maxsize - len > 50) {
- for (i = 0; i < maxclients->value; i++) {
- cl_ent = g_edicts + 1 + i;
- cl = &game.clients[i];
- if (!cl_ent->inuse ||
- cl_ent->solid != SOLID_NOT ||
- cl_ent->client->resp.ctf_team != CTF_NOTEAM)
- continue;
- if (!k) {
- k = 1;
- sprintf(entry, "xv 0 yv %d string2 \"Spectators\" ", j);
- strcat(string, entry);
- len = strlen(string);
- j += 8;
- }
- sprintf(entry+strlen(entry),
- "ctf %d %d %d %d %d ",
- (n & 1) ? 160 : 0, // x
- j, // y
- i, // playernum
- cl->resp.score,
- cl->ping > 999 ? 999 : cl->ping);
- if (maxsize - len > strlen(entry)) {
- strcat(string, entry);
- len = strlen(string);
- }
-
- if (n & 1)
- j += 8;
- n++;
- }
- }
- if (total[0] - last[0] > 1) // couldn't fit everyone
- sprintf(string + strlen(string), "xv 8 yv %d string \"..and %d more\" ",
- 42 + (last[0]+1)*8, total[0] - last[0] - 1);
- if (total[1] - last[1] > 1) // couldn't fit everyone
- sprintf(string + strlen(string), "xv 168 yv %d string \"..and %d more\" ",
- 42 + (last[1]+1)*8, total[1] - last[1] - 1);
- gi.WriteByte (svc_layout);
- gi.WriteString (string);
- }
- /*------------------------------------------------------------------------*/
- /* TECH */
- /*------------------------------------------------------------------------*/
- void CTFHasTech(edict_t *who)
- {
- if (level.time - who->client->ctf_lasttechmsg > 2) {
- gi.centerprintf(who, "You already have a TECH powerup.");
- who->client->ctf_lasttechmsg = level.time;
- }
- }
- gitem_t *CTFWhat_Tech(edict_t *ent)
- {
- gitem_t *tech;
- int i;
- i = 0;
- while (tnames[i]) {
- if ((tech = FindItemByClassname(tnames[i])) != NULL &&
- ent->client->pers.inventory[ITEM_INDEX(tech)]) {
- return tech;
- }
- i++;
- }
- return NULL;
- }
- qboolean CTFPickup_Tech (edict_t *ent, edict_t *other)
- {
- gitem_t *tech;
- int i;
- i = 0;
- while (tnames[i]) {
- if ((tech = FindItemByClassname(tnames[i])) != NULL &&
- other->client->pers.inventory[ITEM_INDEX(tech)]) {
- CTFHasTech(other);
- return false; // has this one
- }
- i++;
- }
-
- // client only gets one tech
- other->client->pers.inventory[ITEM_INDEX(ent->item)]++;
- other->client->ctf_regentime = level.time;
- return true;
- }
- static void SpawnTech(gitem_t *item, edict_t *spot);
- static edict_t *FindTechSpawn(void)
- {
- edict_t *spot = NULL;
- int i = rand() % 16;
- while (i--)
- spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
- if (!spot)
- spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
- return spot;
- }
- static void TechThink(edict_t *tech)
- {
- edict_t *spot;
- if ((spot = FindTechSpawn()) != NULL) {
- SpawnTech(tech->item, spot);
- G_FreeEdict(tech);
- } else {
- tech->nextthink = level.time + CTF_TECH_TIMEOUT;
- tech->think = TechThink;
- }
- }
- void CTFDrop_Tech(edict_t *ent, gitem_t *item)
- {
- edict_t *tech;
- tech = Drop_Item(ent, item);
- tech->nextthink = level.time + CTF_TECH_TIMEOUT;
- tech->think = TechThink;
- ent->client->pers.inventory[ITEM_INDEX(item)] = 0;
- }
- void CTFDeadDropTech(edict_t *ent)
- {
- gitem_t *tech;
- edict_t *dropped;
- int i;
- i = 0;
- while (tnames[i]) {
- if ((tech = FindItemByClassname(tnames[i])) != NULL &&
- ent->client->pers.inventory[ITEM_INDEX(tech)]) {
- dropped = Drop_Item(ent, tech);
- // hack the velocity to make it bounce random
- dropped->velocity[0] = (rand() % 600) - 300;
- dropped->velocity[1] = (rand() % 600) - 300;
- dropped->nextthink = level.time + CTF_TECH_TIMEOUT;
- dropped->think = TechThink;
- dropped->owner = NULL;
- ent->client->pers.inventory[ITEM_INDEX(tech)] = 0;
- }
- i++;
- }
- }
- static void SpawnTech(gitem_t *item, edict_t *spot)
- {
- edict_t *ent;
- vec3_t forward, right;
- vec3_t angles;
- ent = G_Spawn();
- ent->classname = item->classname;
- ent->item = item;
- ent->spawnflags = DROPPED_ITEM;
- ent->s.effects = item->world_model_flags;
- ent->s.renderfx = RF_GLOW;
- VectorSet (ent->mins, -15, -15, -15);
- VectorSet (ent->maxs, 15, 15, 15);
- gi.setmodel (ent, ent->item->world_model);
- ent->solid = SOLID_TRIGGER;
- ent->movetype = MOVETYPE_TOSS;
- ent->touch = Touch_Item;
- ent->owner = ent;
- angles[0] = 0;
- angles[1] = rand() % 360;
- angles[2] = 0;
- AngleVectors (angles, forward, right, NULL);
- VectorCopy (spot->s.origin, ent->s.origin);
- ent->s.origin[2] += 16;
- VectorScale (forward, 100, ent->velocity);
- ent->velocity[2] = 300;
- ent->nextthink = level.time + CTF_TECH_TIMEOUT;
- ent->think = TechThink;
- gi.linkentity (ent);
- }
- static void SpawnTechs(edict_t *ent)
- {
- gitem_t *tech;
- edict_t *spot;
- int i;
- i = 0;
- while (tnames[i]) {
- if ((tech = FindItemByClassname(tnames[i])) != NULL &&
- (spot = FindTechSpawn()) != NULL)
- SpawnTech(tech, spot);
- i++;
- }
- if (ent)
- G_FreeEdict(ent);
- }
- // frees the passed edict!
- void CTFRespawnTech(edict_t *ent)
- {
- edict_t *spot;
- if ((spot = FindTechSpawn()) != NULL)
- SpawnTech(ent->item, spot);
- G_FreeEdict(ent);
- }
- void CTFSetupTechSpawn(void)
- {
- edict_t *ent;
- if (((int)dmflags->value & DF_CTF_NO_TECH))
- return;
- ent = G_Spawn();
- ent->nextthink = level.time + 2;
- ent->think = SpawnTechs;
- }
- void CTFResetTech(void)
- {
- edict_t *ent;
- int i;
- for (ent = g_edicts + 1, i = 1; i < globals.num_edicts; i++, ent++) {
- if (ent->inuse)
- if (ent->item && (ent->item->flags & IT_TECH))
- G_FreeEdict(ent);
- }
- SpawnTechs(NULL);
- }
- i…
Large files files are truncated, but you can click here to view the full file