thirtyflightsofloving/game/g_ctf.c
Knightmare66 95295401a4 Added support for custom client railgun colors in 3ZB2, Awakening2, and Zaero DLLs.
Added weapon balancing and tech control cvars to 3ZB2 DLL.
Added Vampire and Ammogen techs to 3ZB2 game DLL.
Fixed func_door_secret movement bug (when built with origin brush) in Zaero DLL.
Fixed armor stacking bug in default Lazarus, missionpack, and Zaero DLLs.
Removed unused tech cvars in default Lazarus DLL.
Changed parsing of TE_BLASTER_COLORED tempent.
2021-10-31 03:53:09 -04:00

6045 lines
162 KiB
C

/*
===========================================================================
Copyright (C) 1997-2001 Id Software, Inc.
This file is part of Quake 2 source code.
Quake 2 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 2 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 Quake 2 source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "g_local.h"
#include "m_player.h"
/*
// moved to g_ctf.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, team3; // Knightmare added
int total1, total2, total3; // these are only set when going into intermission!
float last_flag_capture;
int last_capture_team;
int team1_doublecapture_time; //ScarFace- for double captures with two carriers
int team2_doublecapture_time;
int team3_doublecapture_time;
edict_t *team1_last_flag_capturer;
edict_t *team2_last_flag_capturer;
edict_t *team3_last_flag_capturer;
match_t match; // match state
float matchtime; // time for match start/end (depends on state)
int lasttime; // last time update
qboolean countdown; // has audio countdown started?
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
int warnactive; // true if stat string 30 is active
ghost_t ghosts[MAX_CLIENTS]; // ghost codes
} ctfgame_t;
ctfgame_t ctfgame;
cvar_t *ctf;
cvar_t *ttctf; // Knightmare added
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 *allow_admin;
cvar_t *warp_list;
cvar_t *warn_unbalanced;
// Index for various CTF pics, this saves us from calling gi.imageindex
// all the time and saves a few CPU cycles since we don't have to do
// a bunch of string compares all the time.
// These are set in CTFPrecache() called from worldspawn
int imageindex_i_ctf1;
int imageindex_i_ctf2;
int imageindex_i_ctf1d;
int imageindex_i_ctf2d;
int imageindex_i_ctf1t;
int imageindex_i_ctf2t;
int imageindex_i_ctfj;
int imageindex_sbfctf1;
int imageindex_sbfctf2;
int imageindex_ctfsb1;
int imageindex_ctfsb2;
int imageindex_i_ctf3; // Knightmare added
int imageindex_i_ctf3d; // Knightmare added
int imageindex_i_ctf3t; // Knightmare added
int imageindex_sbfctf3; // Knightmare added
int imageindex_ctfsb3; // Knightmare added
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 230 "
"num 4 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 "
// joined overlay
"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 112 "
"yb -58 "
"stat_string 27 "
"endif "
"if 29 "
"xv 96 "
"yb -58 "
"pic 29 "
"endif "
"if 28 "
"xl 0 "
"yb -78 "
"stat_string 28 "
"endif "
"if 30 "
"xl 0 "
"yb -88 "
"stat_string 30 "
"endif "
// vehicle speed
"if 31 "
" yb -90 "
" xv 128 "
" pic 31 "
"endif "
;
char *ttctf_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 230 "
"num 4 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 -104 "
"if 17 "
"xr -28 "
"pic 17 "
"endif "
"yb -102 " // added for alignment
"xr -62 "
"num 2 18 "
// joined overlay
"if 22 "
"yb -104 "
"xr -28 "
"pic 22 "
"endif "
// blue team
"yb -77 "
"if 19 "
"xr -28 "
"pic 19 "
"endif "
"yb -75 " // added for alignment
"xr -62 "
"num 2 20 "
// joined overlay
"if 23 "
"yb -77 "
"xr -28 "
"pic 23 "
"endif "
// green team
"yb -50 "
"if 32 "
"xr -28 "
"pic 32 "
"endif "
"yb -48 " // added for alignment
"xr -62 "
"num 2 33 "
// joined overlay
"if 34 "
"yb -50 "
"xr -28 "
"pic 34 "
"endif "
// have flag graph
"if 21 "
"yt 26 "
"xr -26 "
"pic 21 "
"endif "
// have flag graph2
"if 35 "
"yt 26 "
"xr -51 "
"pic 35 "
"endif "
// id view state
"if 27 "
"xv 112 "
"yb -58 "
"stat_string 27 "
"endif "
"if 29 "
"xv 96 "
"yb -58 "
"pic 29 "
"endif "
"if 28 "
"xl 0 "
"yb -78 "
"stat_string 28 "
"endif "
"if 30 "
"xl 0 "
"yb -88 "
"stat_string 30 "
"endif "
// vehicle speed
"if 31 "
" yb -90 "
" xv 128 "
" pic 31 "
"endif "
;
#define TECHTYPES 6
static char *tnames[] = {
"item_tech1", "item_tech2", "item_tech3", "item_tech4", "item_tech5", "item_tech6",
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;
static gitem_t *flag3_item; // Knightmare added
void CTFSpawn (void)
{
if (!ctf->value)
return;
if (!flag1_item)
flag1_item = FindItemByClassname("item_flag_team1");
if (!flag2_item)
flag2_item = FindItemByClassname("item_flag_team2");
if (!flag3_item)
flag3_item = FindItemByClassname("item_flag_team3");
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", "0", CVAR_SERVERINFO|CVAR_LATCH);
ttctf = gi.cvar("ttctf", "0", CVAR_SERVERINFO|CVAR_LATCH); // Knightmare added
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);
allow_admin = gi.cvar("allow_admin", "1", 0);
warp_list = gi.cvar("warp_list", "q2ctf1 q2ctf2 q2ctf3 q2ctf4 q2ctf5", 0);
warn_unbalanced = gi.cvar("warn_unbalanced", "1", 0);
}
/*
* Precache CTF items
*/
void CTFPrecache (void)
{
if (ttctf->value)
{
imageindex_i_ctf1 = gi.imageindex("3tctfr");
imageindex_i_ctf2 = gi.imageindex("3tctfb");
imageindex_i_ctf3 = gi.imageindex("3tctfg");
imageindex_i_ctf1d = gi.imageindex("3tctfrd");
imageindex_i_ctf2d = gi.imageindex("3tctfbd");
imageindex_i_ctf3d = gi.imageindex("3tctfgd");
imageindex_i_ctf1t = gi.imageindex("3tctfrt");
imageindex_i_ctf2t = gi.imageindex("3tctfbt");
imageindex_i_ctf3t = gi.imageindex("3tctfgt");
imageindex_i_ctfj = gi.imageindex("i_ctfj");
imageindex_sbfctf1 = gi.imageindex("sbfctf1");
imageindex_sbfctf2 = gi.imageindex("sbfctf2");
imageindex_sbfctf3 = gi.imageindex("sbfctf3");
imageindex_ctfsb1 = gi.imageindex("3tctfsb1");
imageindex_ctfsb2 = gi.imageindex("3tctfsb2");
imageindex_ctfsb3 = gi.imageindex("3tctfsb3");
}
else
{
imageindex_i_ctf1 = gi.imageindex("i_ctf1");
imageindex_i_ctf2 = gi.imageindex("i_ctf2");
imageindex_i_ctf1d = gi.imageindex("i_ctf1d");
imageindex_i_ctf2d = gi.imageindex("i_ctf2d");
imageindex_i_ctf1t = gi.imageindex("i_ctf1t");
imageindex_i_ctf2t = gi.imageindex("i_ctf2t");
imageindex_i_ctfj = gi.imageindex("i_ctfj");
imageindex_sbfctf1 = gi.imageindex("sbfctf1");
imageindex_sbfctf2 = gi.imageindex("sbfctf2");
imageindex_ctfsb1 = gi.imageindex("ctfsb1");
imageindex_ctfsb2 = gi.imageindex("ctfsb2");
}
}
/*--------------------------------------------------------------------------*/
char *CTFTeamName (int team)
{
switch (team) {
case CTF_TEAM1:
return "RED";
case CTF_TEAM2:
return "BLUE";
case CTF_TEAM3:
return "GREEN"; // Knightmare added
}
return "UNKNOWN"; // Hanzo pointed out this was spelled wrong as "UKNOWN"
}
// Knightmare added
int PlayersOnCTFTeam (int checkteam)
{
int i, total = 0;
for (i = 0; i < maxclients->value; i++)
{
if (!g_edicts[i+1].inuse)
continue;
if (game.clients[i].resp.ctf_team == checkteam)
total++;
}
return total;
}
// Knightmare added
int CTFFlagTeam (gitem_t *flag)
{
if (flag == flag1_item)
return CTF_TEAM1;
else if (flag == flag2_item)
return CTF_TEAM2;
else if (flag == flag3_item)
return CTF_TEAM3;
return CTF_NOTEAM; // invalid flag
}
char *CTFOtherTeamName (int team)
{
switch (team) {
case CTF_TEAM1:
return "BLUE";
case CTF_TEAM2:
return "RED";
case CTF_TEAM3: // Knightmare added
return "RED";
}
return "UNKNOWN"; // Hanzo pointed out this was spelled wrong as "UKNOWN"
}
// Knightmare added
char *CTFOtherTeamName2 (int team)
{
switch (team) {
case CTF_TEAM1:
return "GREEN";
case CTF_TEAM2:
return "GREEN";
case CTF_TEAM3: // Knightmare added
return "BLUE";
}
return "UNKNOWN"; // Hanzo pointed out this was spelled wrong as "UKNOWN"
}
int CTFOtherTeam (int team)
{
switch (team) {
case CTF_TEAM1:
return CTF_TEAM2;
case CTF_TEAM2:
return CTF_TEAM1;
case CTF_TEAM3: // Knightmare added
return CTF_TEAM1;
}
return -1; // invalid value
}
int CTFOtherTeam2 (int team)
{
switch (team) {
case CTF_TEAM1:
return CTF_TEAM3;
case CTF_TEAM2:
return CTF_TEAM3;
case CTF_TEAM3: // Knightmare added
return CTF_TEAM2;
}
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 = strchr(t, '/')) != NULL)
p[1] = 0;
else
// strncpy(t, "male/");
Q_strncpyz(t, sizeof(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;
case CTF_TEAM3: // Knightmare added
gi.configstring (CS_PLAYERSKINS+playernum,
va("%s\\%s%s", ent->client->pers.netname, t, CTF_TEAM3_SKIN) );
break;
default:
gi.configstring (CS_PLAYERSKINS+playernum,
va("%s\\%s", ent->client->pers.netname, s) );
break;
}
// safe_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, team3count = 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++;
break;
case CTF_TEAM3: // Knightmare added
team3count++;
break;
}
}
if (ttctf->value) // Knightmare added
{
float r = random();
// find team with the fewest players
if ( (team1count < team2count) && (team1count < team3count) )
who->resp.ctf_team = CTF_TEAM1;
else if ( (team2count < team1count) && (team2count < team3count) )
who->resp.ctf_team = CTF_TEAM2;
else if ( (team3count < team1count) && (team3count < team2count) )
who->resp.ctf_team = CTF_TEAM3;
// else select a random team
else if (r < 0.33)
who->resp.ctf_team = CTF_TEAM1;
else if (r < 0.67)
who->resp.ctf_team = CTF_TEAM2;
else
who->resp.ctf_team = CTF_TEAM3;
}
else
{
float r = random();
if (team1count < team2count)
who->resp.ctf_team = CTF_TEAM1;
else if (team2count < team1count)
who->resp.ctf_team = CTF_TEAM2;
else if (r < 0.5)
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;
case CTF_TEAM3: // Knightmare added
cname = "info_player_team3";
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, *enemy_flag_item2; // Knightmare added
int otherteam, otherteam2; // Knightmare added
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);
otherteam2 = CTFOtherTeam2(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;
enemy_flag_item2 = flag3_item;
}
// Knightmare added
else if (targ->client->resp.ctf_team == CTF_TEAM2) {
flag_item = flag2_item;
enemy_flag_item = flag1_item;
enemy_flag_item2 = flag3_item;
}
else {
flag_item = flag3_item;
enemy_flag_item = flag1_item;
enemy_flag_item2 = flag2_item;
}
// did the attacker frag the flag carrier?
if ( (targ->client->pers.inventory[ITEM_INDEX(enemy_flag_item)]
&& attacker->client->resp.ctf_team == CTFFlagTeam(enemy_flag_item))
|| (targ->client->pers.inventory[ITEM_INDEX(enemy_flag_item2)]
&& attacker->client->resp.ctf_team == CTFFlagTeam(enemy_flag_item2)) )
{
attacker->client->resp.ctf_lastfraggedcarrier = level.time;
attacker->client->resp.score += CTF_FRAG_CARRIER_BONUS;
safe_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_team == otherteam2) )
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;
safe_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;
case CTF_TEAM3: // Knightmare added
c = "item_flag_team3";
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)
safe_bprintf(PRINT_MEDIUM, "%s defends the %s base.\n",
attacker->client->pers.netname,
CTFTeamName(attacker->client->resp.ctf_team));
else
safe_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;
safe_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_item1, *flag_item2;
if (!ctf->value)
return;
if (!targ || !attacker)
return;
if (!targ->client || !attacker->client)
return;
if (targ->client->resp.ctf_team == CTF_TEAM1) {
flag_item1 = flag2_item;
flag_item2 = flag3_item;
}
// Knightmare added
else if (targ->client->resp.ctf_team == CTF_TEAM2) {
flag_item1 = flag1_item;
flag_item2 = flag3_item;
}
else if (targ->client->resp.ctf_team == CTF_TEAM3) {
flag_item1 = flag1_item;
flag_item2 = flag2_item;
}
if ( ( targ->client->pers.inventory[ITEM_INDEX(flag_item1)]
|| targ->client->pers.inventory[ITEM_INDEX(flag_item2)] ) // Knightmare added
&& 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;
case CTF_TEAM3: // Knightmare added
c = "item_flag_team3";
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);
CTFResetFlag(CTF_TEAM3); // Knightmare added
}
qboolean CTFPickup_Flag (edict_t *ent, edict_t *other)
{
int ctf_team;
int i, captures = 0;
edict_t *player;
gitem_t *flag_item, *enemy_flag_item1, *enemy_flag_item2, *captured_flag_item = NULL;
// 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;
// Knightmare added
else if (strcmp(ent->classname, "item_flag_team3") == 0)
ctf_team = CTF_TEAM3;
else {
safe_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_item1 = flag2_item;
enemy_flag_item2 = flag3_item;
// Knightmare added
} else if (ctf_team == CTF_TEAM2) {
flag_item = flag2_item;
enemy_flag_item1 = flag1_item;
enemy_flag_item2 = flag3_item;
} else {
flag_item = flag3_item;
enemy_flag_item1 = flag1_item;
enemy_flag_item2 = flag2_item;
}
if (ctf_team == other->client->resp.ctf_team)
{ // if returning to our flag
if (!(ent->spawnflags & DROPPED_ITEM))
{
// the flag is at home base. if the player has the enemy
// flag, he's just scored!
if (other->client->pers.inventory[ITEM_INDEX(enemy_flag_item1)]
|| other->client->pers.inventory[ITEM_INDEX(enemy_flag_item2)])
{
if (other->client->pers.inventory[ITEM_INDEX(enemy_flag_item1)])
{
// safe_bprintf(PRINT_HIGH, "%s captured the %s flag!\n",
// other->client->pers.netname, CTFTeamName(CTFFlagTeam(enemy_flag_item1)) );
other->client->pers.inventory[ITEM_INDEX(enemy_flag_item1)] = 0;
captured_flag_item = enemy_flag_item1;
ctfgame.last_flag_capture = level.time;
ctfgame.last_capture_team = ctf_team;
if (ctf_team == CTF_TEAM1)
ctfgame.team1++;
// Knightmare added
else if (ctf_team == CTF_TEAM2)
ctfgame.team2++;
else if (ctf_team == CTF_TEAM3)
ctfgame.team3++;
captures++;
if (other->client->resp.ghost)
other->client->resp.ghost->caps++;
CTFResetFlag(CTFFlagTeam(enemy_flag_item1));
}
if (ttctf->value && other->client->pers.inventory[ITEM_INDEX(enemy_flag_item2)])
{
// safe_bprintf(PRINT_HIGH, "%s captured the %s flag!\n",
// other->client->pers.netname, CTFTeamName(CTFFlagTeam(enemy_flag_item2)) );
other->client->pers.inventory[ITEM_INDEX(enemy_flag_item2)] = 0;
captured_flag_item = enemy_flag_item2;
ctfgame.last_flag_capture = level.time;
ctfgame.last_capture_team = ctf_team;
if (ctf_team == CTF_TEAM1)
ctfgame.team1++;
// Knightmare added
else if (ctf_team == CTF_TEAM2)
ctfgame.team2++;
else if (ctf_team == CTF_TEAM3)
ctfgame.team3++;
captures++;
if (other->client->resp.ghost)
other->client->resp.ghost->caps++;
CTFResetFlag(CTFFlagTeam(enemy_flag_item2));
}
gi.sound (ent, CHAN_RELIABLE+CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex("ctf/flagcap.wav"), 1, ATTN_NONE, 0);
// ScarFace- double capture detection
if (captures == 2) { // other gets 40 frag bonus
other->client->resp.score += CTF_DOUBLE_CAPTURE_BONUS;
safe_bprintf(PRINT_HIGH, "%s captured the %s and %s flags for a double capture!\n",
other->client->pers.netname, CTFTeamName(CTFFlagTeam(enemy_flag_item1)),
CTFTeamName(CTFFlagTeam(enemy_flag_item2)) );
}
else { // other gets 15 frag bonus
other->client->resp.score += CTF_CAPTURE_BONUS;
if (captured_flag_item != NULL)
safe_bprintf(PRINT_HIGH, "%s captured the %s flag!\n",
other->client->pers.netname, CTFTeamName(CTFFlagTeam(captured_flag_item)) );
}
// ScarFace- support for 2-carrier double capture in 3Team CTF mode
// 2 captures must be made within 20 seconds of each other
// Each carrier gets 30 points
// Carrier 1: CTF_CAPTURE_BONUS 15pts + CTF_TEAM_BONUS 10pts + 5pts = 30pts total
// Carrier 2: CTF_TEAM_BONUS 10pts + CTF_CAPTURE_BONUS 15pts + 5pts = 30pts total
// FIXME- how to check if the same flag has been captured twice?
if ( ttctf->value && (ctf_team == CTF_TEAM1) && (captures == 1) )
{
if (ctfgame.team1_doublecapture_time
&& ctfgame.team1_doublecapture_time > level.time
&& other != ctfgame.team1_last_flag_capturer)
{
if (captured_flag_item != NULL)
safe_bprintf(PRINT_HIGH, "%s captured the %s flag for a double capture!\n",
other->client->pers.netname, CTFTeamName(CTFFlagTeam(captured_flag_item)) );
other->client->resp.score += 5;
if (ctfgame.team1_last_flag_capturer && ctfgame.team1_last_flag_capturer->client)
ctfgame.team1_last_flag_capturer->client->resp.score += 5;
ctfgame.team1_doublecapture_time = 0;
ctfgame.team1_last_flag_capturer = other;
}
else
{
// safe_bprintf(PRINT_HIGH, "%s started the double capture timer!\n", other->client->pers.netname);
ctfgame.team1_doublecapture_time = level.time + CTF_DOUBLE_CAPTURE_TIMEOUT;
ctfgame.team1_last_flag_capturer = other;
}
}
else if ( ttctf->value && (ctf_team == CTF_TEAM2) && (captures == 1) )
{
if (ctfgame.team2_doublecapture_time
&& ctfgame.team2_doublecapture_time > level.time
&& other != ctfgame.team2_last_flag_capturer)
{
if (captured_flag_item != NULL)
safe_bprintf(PRINT_HIGH, "%s captured the %s flag for a double capture!\n",
other->client->pers.netname, CTFTeamName(CTFFlagTeam(captured_flag_item)) );
other->client->resp.score += 5;
if (ctfgame.team2_last_flag_capturer && ctfgame.team2_last_flag_capturer->client)
ctfgame.team2_last_flag_capturer->client->resp.score += 5;
ctfgame.team2_doublecapture_time = 0;
ctfgame.team2_last_flag_capturer = other;
}
else
{
// safe_bprintf(PRINT_HIGH, "%s started the double capture timer!\n", other->client->pers.netname);
ctfgame.team2_doublecapture_time = level.time + CTF_DOUBLE_CAPTURE_TIMEOUT;
ctfgame.team2_last_flag_capturer = other;
}
}
else if ( ttctf->value && (ctf_team == CTF_TEAM3) && (captures == 1) )
{
if (ctfgame.team3_doublecapture_time
&& ctfgame.team3_doublecapture_time > level.time
&& other != ctfgame.team3_last_flag_capturer)
{
if (captured_flag_item != NULL)
safe_bprintf(PRINT_HIGH, "%s captured the %s flag for a double capture!\n",
other->client->pers.netname, CTFTeamName(CTFFlagTeam(captured_flag_item)) );
other->client->resp.score += 5;
if (ctfgame.team3_last_flag_capturer && ctfgame.team3_last_flag_capturer->client)
ctfgame.team3_last_flag_capturer->client->resp.score += 5;
ctfgame.team3_doublecapture_time = 0;
ctfgame.team3_last_flag_capturer = other;
}
else
{
// safe_bprintf(PRINT_HIGH, "%s started the double capture timer!\n", other->client->pers.netname);
ctfgame.team3_doublecapture_time = level.time + CTF_DOUBLE_CAPTURE_TIMEOUT;
ctfgame.team3_last_flag_capturer = other;
}
}
// end ScarFace
// 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 += captures*CTF_TEAM_BONUS;
// award extra points for capture assists
if (player->client->resp.ctf_lastreturnedflag + CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) {
safe_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) {
safe_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;
}
}
}
}
return false; // its at home base already
}
// hey, its not home. return it by teleporting it back
safe_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
// Knightmare- added disable pickup option
if (!allow_flagpickup->value && PlayersOnCTFTeam(ctf_team) == 0) {
if (level.time - other->client->ctf_lasttechmsg > 2) {
safe_centerprintf(other, "Not allowed to take empty teams' flags!");
other->client->ctf_lasttechmsg = level.time;
}
return false;
}
safe_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;
}
gitem_t *CTFWhat_Flag (edict_t *ent)
{
gitem_t *flag;
if ((flag = FindItemByClassname("item_flag_team1")) != NULL &&
ent->client->pers.inventory[ITEM_INDEX(flag)])
return flag;
if ((flag = FindItemByClassname("item_flag_team2")) != NULL &&
ent->client->pers.inventory[ITEM_INDEX(flag)])
return flag;
// Knightmare added
if ((flag = FindItemByClassname("item_flag_team3")) != NULL &&
ent->client->pers.inventory[ITEM_INDEX(flag)])
return flag;
return NULL;
}
/*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)
|| (level.time < ent->touch_debounce_time) ) )
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);
safe_bprintf(PRINT_HIGH, "The %s flag has returned!\n",
CTFTeamName(CTF_TEAM1));
}
else if (strcmp(ent->classname, "item_flag_team2") == 0)
{
CTFResetFlag(CTF_TEAM2);
safe_bprintf(PRINT_HIGH, "The %s flag has returned!\n",
CTFTeamName(CTF_TEAM2));
}
// Knightmare added
else if (strcmp(ent->classname, "item_flag_team3") == 0)
{
CTFResetFlag(CTF_TEAM3);
safe_bprintf(PRINT_HIGH, "The %s flag has returned!\n",
CTFTeamName(CTF_TEAM3));
}
}
// Called from PlayerDie, to drop the flag from a dying player
void CTFDeadDropFlag (edict_t *self)
{
edict_t *dropped = NULL;
if (!ctf->value)
return;
if (self->client->pers.inventory[ITEM_INDEX(flag1_item)])
{
dropped = Drop_Item(self, flag1_item);
self->client->pers.inventory[ITEM_INDEX(flag1_item)] = 0;
safe_bprintf(PRINT_HIGH, "%s lost the %s flag!\n",
self->client->pers.netname, CTFTeamName(CTF_TEAM1));
if (dropped) {
dropped->think = CTFDropFlagThink;
dropped->nextthink = level.time + CTF_AUTO_FLAG_RETURN_TIMEOUT;
dropped->touch = CTFDropFlagTouch;
}
}
if (self->client->pers.inventory[ITEM_INDEX(flag2_item)])
{
dropped = Drop_Item(self, flag2_item);
self->client->pers.inventory[ITEM_INDEX(flag2_item)] = 0;
safe_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;
}
}
// Knightmare added
if (self->client->pers.inventory[ITEM_INDEX(flag3_item)])
{
dropped = Drop_Item(self, flag3_item);
self->client->pers.inventory[ITEM_INDEX(flag3_item)] = 0;
safe_bprintf(PRINT_HIGH, "%s lost the %s flag!\n",
self->client->pers.netname, CTFTeamName(CTF_TEAM3));
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)
{
edict_t *dropped = NULL;
// Knightmare changed this
if (!allow_flagdrop->value)
{
if (rand() & 1)
safe_cprintf(ent, PRINT_HIGH, "Only llamas drop flags.\n");
else
safe_cprintf(ent, PRINT_HIGH, "Winners don't drop flags.\n");
return false;
}
else
{
if (ent->client->pers.inventory[ITEM_INDEX(flag1_item)])
{
dropped = Drop_Item(ent, flag1_item);
ent->client->pers.inventory[ITEM_INDEX(flag1_item)] = 0;
safe_bprintf(PRINT_HIGH, "%s dropped the RED flag!\n", ent->client->pers.netname);
if (dropped) {
// hack the velocity to make it bounce random
dropped->velocity[0] = (rand() % 600) - 300;
dropped->velocity[1] = (rand() % 600) - 300;
dropped->think = CTFDropFlagThink;
dropped->nextthink = level.time + CTF_AUTO_FLAG_RETURN_TIMEOUT;
dropped->touch = CTFDropFlagTouch;
dropped->timestamp = level.time;
dropped->touch_debounce_time = level.time + 1.0;
dropped->owner = ent;
}
}
if (ent->client->pers.inventory[ITEM_INDEX(flag2_item)])
{
dropped = Drop_Item(ent, flag2_item);
ent->client->pers.inventory[ITEM_INDEX(flag2_item)] = 0;
safe_bprintf(PRINT_HIGH, "%s dropped the BLUE flag!\n", ent->client->pers.netname);
if (dropped) {
// hack the velocity to make it bounce random
dropped->velocity[0] = (rand() % 600) - 300;
dropped->velocity[1] = (rand() % 600) - 300;
dropped->think = CTFDropFlagThink;
dropped->nextthink = level.time + CTF_AUTO_FLAG_RETURN_TIMEOUT;
dropped->touch = CTFDropFlagTouch;
dropped->timestamp = level.time;
dropped->touch_debounce_time = level.time + 1.0;
dropped->owner = ent;
}
}
if (ent->client->pers.inventory[ITEM_INDEX(flag3_item)])
{
dropped = Drop_Item(ent, flag3_item);
ent->client->pers.inventory[ITEM_INDEX(flag3_item)] = 0;
safe_bprintf(PRINT_HIGH, "%s dropped the GREEN flag!\n", ent->client->pers.netname);
if (dropped) {
// hack the velocity to make it bounce random
dropped->velocity[0] = (rand() % 600) - 300;
dropped->velocity[1] = (rand() % 600) - 300;
dropped->think = CTFDropFlagThink;
dropped->nextthink = level.time + CTF_AUTO_FLAG_RETURN_TIMEOUT;
dropped->touch = CTFDropFlagTouch;
dropped->timestamp = level.time;
dropped->touch_debounce_time = level.time + 1.0;
dropped->owner = ent;
}
}
return true;
}
}
/*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);
// 3Team CTF uses different flag models
if (ttctf->value) {
if (strcmp(ent->classname, "item_flag_team1") == 0)
ent->item->world_model = "models/ctf/flags/flag1.md2";
if (strcmp(ent->classname, "item_flag_team2") == 0)
ent->item->world_model = "models/ctf/flags/flag2.md2";
if (strcmp(ent->classname, "item_flag_team3") == 0)
ent->item->world_model = "models/ctf/flags/flag3.md2";
}
else {
if (strcmp(ent->classname, "item_flag_team1") == 0)
ent->item->world_model = "players/male/flag1.md2";
if (strcmp(ent->classname, "item_flag_team2") == 0)
ent->item->world_model = "players/male/flag2.md2";
}
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 (!ctf->value)
{
player->s.modelindex3 = 0;
return;
}
if (!player || !player->client)
return;
if (player->health > 0)
{
if (player->client->pers.inventory[ITEM_INDEX(flag1_item)])
player->s.effects |= EF_FLAG1;
else if (player->client->pers.inventory[ITEM_INDEX(flag3_item)])
#ifdef KMQUAKE2_ENGINE_MOD
player->s.effects |= EF_FLAG1|EF_FLAG2;
#else
player->s.effects |= EF_FLAG2;
#endif
else if (player->client->pers.inventory[ITEM_INDEX(flag2_item)])
player->s.effects |= EF_FLAG2;
}
if (ttctf->value) // Knightmare added
{
if (player->client->pers.inventory[ITEM_INDEX(flag1_item)]
&& player->client->pers.inventory[ITEM_INDEX(flag2_item)])
player->s.modelindex3 = gi.modelindex("models/ctf/flags/flag4.md2");
else if (player->client->pers.inventory[ITEM_INDEX(flag1_item)]
&& player->client->pers.inventory[ITEM_INDEX(flag3_item)])
player->s.modelindex3 = gi.modelindex("models/ctf/flags/flag5.md2");
else if (player->client->pers.inventory[ITEM_INDEX(flag2_item)]
&& player->client->pers.inventory[ITEM_INDEX(flag3_item)])
player->s.modelindex3 = gi.modelindex("models/ctf/flags/flag6.md2");
else if (player->client->pers.inventory[ITEM_INDEX(flag1_item)])
player->s.modelindex3 = gi.modelindex("models/ctf/flags/flag1.md2");
else if (player->client->pers.inventory[ITEM_INDEX(flag2_item)])
player->s.modelindex3 = gi.modelindex("models/ctf/flags/flag2.md2");
else if (player->client->pers.inventory[ITEM_INDEX(flag3_item)])
player->s.modelindex3 = gi.modelindex("models/ctf/flags/flag3.md2");
else
player->s.modelindex3 = 0;
}
else
{
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;
else if (game.clients[i].resp.ctf_team == CTF_TEAM3)
ctfgame.total3 += game.clients[i].resp.score;
}
}
void CTFID_f (edict_t *ent)
{
if (ent->client->resp.id_state) {
safe_cprintf(ent, PRINT_HIGH, "Disabling player identication display.\n");
ent->client->resp.id_state = false;
} else {
safe_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;
// only check every few frames
if (level.time - ent->client->resp.lastidtime < 0.25)
return;
ent->client->resp.lastidtime = level.time;
ent->client->ps.stats[STAT_CTF_ID_VIEW] = 0;
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = 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_GENERAL + (tr.ent - g_edicts - 1);
if (tr.ent->client->resp.ctf_team == CTF_TEAM1)
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf1;
else if (tr.ent->client->resp.ctf_team == CTF_TEAM2)
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf2;
else if (tr.ent->client->resp.ctf_team == CTF_TEAM3)
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf3;
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_GENERAL + (best - g_edicts - 1);
if (best->client->resp.ctf_team == CTF_TEAM1)
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf1;
else if (best->client->resp.ctf_team == CTF_TEAM2)
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf2;
else if (best->client->resp.ctf_team == CTF_TEAM3)
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf3;
}
}
void SetCTFStats (edict_t *ent)
{
gitem_t *tech;
int i;
int p1, p2, p3;
edict_t *e;
if (!ctf->value)
return;
if (ctfgame.match > MATCH_NONE)
ent->client->ps.stats[STAT_CTF_MATCH] = CONFIG_CTF_MATCH;
else
ent->client->ps.stats[STAT_CTF_MATCH] = 0;
if (ctfgame.warnactive)
ent->client->ps.stats[STAT_CTF_TEAMINFO] = CONFIG_CTF_TEAMINFO;
else
ent->client->ps.stats[STAT_CTF_TEAMINFO] = 0;
//ghosting
if (ent->client->resp.ghost) {
ent->client->resp.ghost->score = ent->client->resp.score;
// strncpy(ent->client->resp.ghost->netname, ent->client->pers.netname);
Q_strncpyz(ent->client->resp.ghost->netname, sizeof(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] = imageindex_ctfsb1;
ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = imageindex_ctfsb2;
if (ttctf->value)
ent->client->ps.stats[STAT_CTF_TEAM3_HEADER] = imageindex_ctfsb3;
// if during intermission, we must blink the team header of the winning team
if (level.intermissiontime && (level.framenum & 8))
{ // blink 1/8th second
if (ttctf->value)
{
if (ctfgame.team1 > ctfgame.team2 && ctfgame.team1 > ctfgame.team3)
ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
else if (ctfgame.team2 > ctfgame.team1 && ctfgame.team2 > ctfgame.team3)
ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
else if (ctfgame.team3 > ctfgame.team1 && ctfgame.team3 > ctfgame.team2)
ent->client->ps.stats[STAT_CTF_TEAM3_HEADER] = 0;
// frag tie breaker
else if (ctfgame.total1 > ctfgame.total2 && ctfgame.total1 > ctfgame.total3)
ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
else if (ctfgame.total2 > ctfgame.total1 && ctfgame.total2 > ctfgame.total3)
ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
else if (ctfgame.total3 > ctfgame.total1 && ctfgame.total3 > ctfgame.total2)
ent->client->ps.stats[STAT_CTF_TEAM3_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_CTF_TEAM3_HEADER] = 0;
}
}
else
{
// 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 = 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 = 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 = imageindex_i_ctf1t;
break;
}
} else if (e->spawnflags & DROPPED_ITEM)
p1 = imageindex_i_ctf1d; // must be dropped
}
p2 = 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 = 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 = imageindex_i_ctf2t;
break;
}
} else if (e->spawnflags & DROPPED_ITEM)
p2 = imageindex_i_ctf2d; // must be dropped
}
// Knightmare added
p3 = imageindex_i_ctf3;
e = G_Find(NULL, FOFS(classname), "item_flag_team3");
if (e != NULL)
{
if (e->solid == SOLID_NOT)
{
int i;
// not at base
// check if on player
p3 = imageindex_i_ctf3d; // default to dropped
for (i = 1; i <= maxclients->value; i++)
if (g_edicts[i].inuse &&
g_edicts[i].client->pers.inventory[ITEM_INDEX(flag3_item)])
{
// enemy has it
p3 = imageindex_i_ctf3t;
break;
}
} else if (e->spawnflags & DROPPED_ITEM)
p3 = imageindex_i_ctf3d; // must be dropped
}
ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1;
ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2;
if (ttctf->value) // Knightmare added
ent->client->ps.stats[STAT_CTF_TEAM3_PIC] = p3;
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 (ctfgame.last_capture_team == CTF_TEAM2)
if (level.framenum & 8)
ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2;
else
ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = 0;
else // Knightmare added
if (level.framenum & 8)
ent->client->ps.stats[STAT_CTF_TEAM3_PIC] = p3;
else
ent->client->ps.stats[STAT_CTF_TEAM3_PIC] = 0;
}
ent->client->ps.stats[STAT_CTF_TEAM1_CAPS] = ctfgame.team1;
ent->client->ps.stats[STAT_CTF_TEAM2_CAPS] = ctfgame.team2;
if (ttctf->value) // Knightmare added
ent->client->ps.stats[STAT_CTF_TEAM3_CAPS] = ctfgame.team3;
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = 0;
ent->client->ps.stats[STAT_CTF_FLAG_PIC2] = 0;
// Knightmare changed
if (ent->client->resp.ctf_team == CTF_TEAM1)
{
if ( ttctf->value && ent->client->pers.inventory[ITEM_INDEX(flag2_item)]
&& ent->client->pers.inventory[ITEM_INDEX(flag3_item)]
&& (level.framenum & 8) )
{
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf2;
ent->client->ps.stats[STAT_CTF_FLAG_PIC2] = imageindex_i_ctf3;
}
else if (ttctf->value && ent->client->pers.inventory[ITEM_INDEX(flag3_item)]
&& (level.framenum & 8))
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf3;
else if (ent->client->pers.inventory[ITEM_INDEX(flag2_item)]
&& (level.framenum & 8))
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf2;
}
else if (ent->client->resp.ctf_team == CTF_TEAM2)
{
if ( ttctf->value && ent->client->pers.inventory[ITEM_INDEX(flag1_item)]
&& ent->client->pers.inventory[ITEM_INDEX(flag3_item)]
&& (level.framenum & 8) )
{
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf1;
ent->client->ps.stats[STAT_CTF_FLAG_PIC2] = imageindex_i_ctf3;
}
else if (ttctf->value && ent->client->pers.inventory[ITEM_INDEX(flag3_item)]
&& (level.framenum & 8))
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf3;
else if (ent->client->pers.inventory[ITEM_INDEX(flag1_item)]
&& (level.framenum & 8))
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf1;
}
else if (ttctf->value && ent->client->resp.ctf_team == CTF_TEAM3)
{
if (ent->client->pers.inventory[ITEM_INDEX(flag1_item)]
&& ent->client->pers.inventory[ITEM_INDEX(flag2_item)]
&& (level.framenum & 8))
{
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf1;
ent->client->ps.stats[STAT_CTF_FLAG_PIC2] = imageindex_i_ctf2;
}
else if (ent->client->pers.inventory[ITEM_INDEX(flag1_item)]
&& (level.framenum & 8))
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf1;
else if (ent->client->pers.inventory[ITEM_INDEX(flag2_item)]
&& (level.framenum & 8))
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf2;
}
ent->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = 0;
ent->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = 0;
if (ttctf->value) // Knightmare added
ent->client->ps.stats[STAT_CTF_JOINED_TEAM3_PIC] = 0;
if (ent->client->resp.ctf_team == CTF_TEAM1)
ent->client->ps.stats[STAT_CTF_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;
// Knightmare added
else if (ent->client->resp.ctf_team == CTF_TEAM3)
ent->client->ps.stats[STAT_CTF_JOINED_TEAM3_PIC] = imageindex_i_ctfj;
if (ent->client->resp.id_state)
CTFSetIDView(ent);
else {
ent->client->ps.stats[STAT_CTF_ID_VIEW] = 0;
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = 0;
}
}
/*------------------------------------------------------------------------*/
/*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)
{
}
// Knightmare added
/*QUAKED info_player_team3 (0 0 1) (-16 -16 -24) (16 16 32)
potential team3 spawning position for 3Team-CTF games
*/
void SP_info_player_team3 (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;
// ACEBOT_ADD
ACEND_GrapFired(self);
// ACEBOT_END
#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->classname = "grapple"; // Knightmare added
// 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, qboolean altfire)
{
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) {
safe_cprintf(ent, PRINT_HIGH, "You are on the %s team.\n",
CTFTeamName(ent->client->resp.ctf_team));
return;
}
if (ctfgame.match > MATCH_SETUP) {
safe_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 if (ttctf->value && Q_stricmp(t, "green") == 0)
desired_team = CTF_TEAM3;
else {
safe_cprintf(ent, PRINT_HIGH, "Unknown team %s.\n", t);
return;
}
if (ent->client->resp.ctf_team == desired_team) {
safe_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;
safe_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;
safe_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 tmp[128]; // Knightmare added
char string[1400];
int len;
int i, j, k, n;
int sorted[3][MAX_CLIENTS];
int sortedscores[3][MAX_CLIENTS];
int score, total[3], totalscore[3];
int last[3];
gclient_t *cl;
edict_t *cl_ent;
int team;
int maxsize = 1000;
// sort the clients by team and score
total[0] = total[1] = total[2] = 0;
last[0] = last[1] = last[2] = 0;
totalscore[0] = totalscore[1] = totalscore[2] = 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 if (game.clients[i].resp.ctf_team == CTF_TEAM3)
team = 2;
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 headers
if (ttctf->value)
Com_sprintf (string, sizeof(string), "if 24 xv -64 yv 8 pic 24 endif "
"xv -32 yv 28 string \"%4d/%-3d\" "
"xv 24 yv 12 num 2 18 "
"if 25 xv 96 yv 8 pic 25 endif "
"xv 128 yv 28 string \"%4d/%-3d\" "
"xv 184 yv 12 num 2 20 "
"if 36 xv 256 yv 8 pic 36 endif "
"xv 288 yv 28 string \"%4d/%-3d\" "
"xv 344 yv 12 num 2 33 ",
totalscore[0], total[0],
totalscore[1], total[1],
totalscore[2], total[2]);
else
Com_sprintf (string, sizeof(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 = (int)strlen(string);
for (i=0; i<16; i++)
{
if ( (i >= total[0]) && (i >= total[1]) && (i > total[2]) )
break; // we're done
*entry = 0;
// left side
if (i < total[0])
{
cl = &game.clients[sorted[0][i]];
cl_ent = g_edicts + 1 + sorted[0][i];
if (ttctf->value) {
// sprintf(entry+strlen(entry),
Com_sprintf (tmp, sizeof(tmp),
#ifdef KMQUAKE2_ENGINE_MOD
"3tctf -72 %d %d %d %d ",
#else
"ctf -72 %d %d %d %d ",
#endif
42 + i * 8,
sorted[0][i],
cl->resp.score,
cl->ping > 999 ? 999 : cl->ping);
Q_strncatz (entry, sizeof(entry), tmp);
}
else {
// sprintf(entry+strlen(entry),
Com_sprintf (tmp, sizeof(tmp),
"ctf 0 %d %d %d %d ",
42 + i * 8,
sorted[0][i],
cl->resp.score,
cl->ping > 999 ? 999 : cl->ping);
Q_strncatz (entry, sizeof(entry), tmp);
}
if (ttctf->value)
{
if (cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)]
&& cl_ent->client->pers.inventory[ITEM_INDEX(flag3_item)])
{
// sprintf(entry + strlen(entry), "xv -16 yv %d picn sbfctf2 "
Com_sprintf (tmp, sizeof(tmp), "xv -16 yv %d picn sbfctf2 "
"xv -8 yv %d picn sbfctf3 ", 42 + i * 8, 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
/*if (level.framenum & 1)
Com_sprintf (tmp, sizeof(tmp), "xv -16 yv %d picn sbfctf2 ", 42 + i * 8);
else
Com_sprintf (tmp, sizeof(tmp), "xv -16 yv %d picn sbfctf3 ", 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
*/
}
else if (cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)]) {
// sprintf(entry + strlen(entry), "xv -16 yv %d picn sbfctf2 ", 42 + i * 8);
Com_sprintf (tmp, sizeof(tmp), "xv -16 yv %d picn sbfctf2 ", 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
}
else if (cl_ent->client->pers.inventory[ITEM_INDEX(flag3_item)]) {
// sprintf(entry + strlen(entry), "xv -16 yv %d picn sbfctf3 ", 42 + i * 8);
Com_sprintf (tmp, sizeof(tmp), "xv -16 yv %d picn sbfctf3 ", 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
}
}
else
if (cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)]) {
// sprintf(entry + strlen(entry), "xv 56 yv %d picn sbfctf2 ", 42 + i * 8);
Com_sprintf (tmp, sizeof(tmp), "xv 56 yv %d picn sbfctf2 ", 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
}
if (maxsize - len > strlen(entry)) {
// strncat(string, entry);
Q_strncatz(string, sizeof(string), entry);
len = (int)strlen(string);
last[0] = i;
}
}
// right side /center
if (i < total[1])
{
cl = &game.clients[sorted[1][i]];
cl_ent = g_edicts + 1 + sorted[1][i];
if (ttctf->value) {
// sprintf(entry+strlen(entry),
Com_sprintf (tmp, sizeof(tmp),
#ifdef KMQUAKE2_ENGINE_MOD
"3tctf 88 %d %d %d %d ",
#else
"ctf 88 %d %d %d %d ",
#endif
42 + i * 8,
sorted[1][i],
cl->resp.score,
cl->ping > 999 ? 999 : cl->ping);
Q_strncatz (entry, sizeof(entry), tmp);
}
else {
// sprintf(entry+strlen(entry),
Com_sprintf (tmp, sizeof(tmp),
"ctf 160 %d %d %d %d ",
42 + i * 8,
sorted[1][i],
cl->resp.score,
cl->ping > 999 ? 999 : cl->ping);
Q_strncatz (entry, sizeof(entry), tmp);
}
if (ttctf->value)
{
if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)]
&& cl_ent->client->pers.inventory[ITEM_INDEX(flag3_item)])
{
// sprintf(entry + strlen(entry), "xv 144 yv %d picn sbfctf1 "
Com_sprintf (tmp, sizeof(tmp), "xv 144 yv %d picn sbfctf1 "
"xv 152 yv %d picn sbfctf3 ", 42 + i * 8, 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
/*if (level.framenum & 1)
Com_sprintf (tmp, sizeof(tmp), "xv 144 yv %d picn sbfctf1 ", 42 + i * 8);
else
Com_sprintf (tmp, sizeof(tmp), "xv 144 yv %d picn sbfctf3", 42 + i * 8);
Q_strncatz (entry, tmp, sizeof(entry));
*/
}
else if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)]) {
// sprintf(entry + strlen(entry), "xv 144 yv %d picn sbfctf1 ", 42 + i * 8);
Com_sprintf (tmp, sizeof(tmp), "xv 144 yv %d picn sbfctf1 ", 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
}
else if (cl_ent->client->pers.inventory[ITEM_INDEX(flag3_item)]) {
// sprintf(entry + strlen(entry), "xv 144 yv %d picn sbfctf3 ", 42 + i * 8);
Com_sprintf (tmp, sizeof(tmp), "xv 144 yv %d picn sbfctf3 ", 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
}
}
else
if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)]) {
// sprintf(entry + strlen(entry), "xv 216 yv %d picn sbfctf1 ", 42 + i * 8);
Com_sprintf (tmp, sizeof(tmp), "xv 216 yv %d picn sbfctf1 ", 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
}
if (maxsize - len > strlen(entry)) {
// strncat(string, entry);
Q_strncatz(string, sizeof(string), entry);
len = (int)strlen(string);
last[1] = i;
}
}
// 3Team CTF right side
if ( ttctf->value && (i < total[2]) )
{
cl = &game.clients[sorted[2][i]];
cl_ent = g_edicts + 1 + sorted[2][i];
// sprintf(entry+strlen(entry),
Com_sprintf (tmp, sizeof(tmp),
#ifdef KMQUAKE2_ENGINE_MOD
"3tctf 248 %d %d %d %d ",
#else
"ctf 248 %d %d %d %d ",
#endif
42 + i * 8,
sorted[2][i],
cl->resp.score,
cl->ping > 999 ? 999 : cl->ping);
Q_strncatz (entry, sizeof(entry), tmp);
if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)]
&& cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)])
{
// sprintf(entry + strlen(entry), "xv 304 yv %d picn sbfctf1 "
Com_sprintf (tmp, sizeof(tmp), "xv 304 yv %d picn sbfctf1 "
"xv 312 yv %d picn sbfctf2 ", 42 + i * 8, 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
/*if (level.framenum & 1)
Com_sprintf (tmp, sizeof(tmp), "xv 304 yv %d picn sbfctf1 ", 42 + i * 8);
else
Com_sprintf (tmp, sizeof(tmp), "xv 304 yv %d picn sbfctf2", 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
*/
}
else if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)]) {
// sprintf(entry + strlen(entry), "xv 304 yv %d picn sbfctf1 ", 42 + i * 8);
Com_sprintf (tmp, sizeof(tmp), "xv 304 yv %d picn sbfctf1 ", 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
}
else if (cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)]) {
// sprintf(entry + strlen(entry), "xv 304 yv %d picn sbfctf2 ", 42 + i * 8);
Com_sprintf (tmp, sizeof(tmp), "xv 304 yv %d picn sbfctf2 ", 42 + i * 8);
Q_strncatz (entry, sizeof(entry), tmp);
}
if (maxsize - len > strlen(entry)) {
// strncat(string, entry);
Q_strncatz(string, sizeof(string), entry);
len = (int)strlen(string);
last[2] = i;
}
}
}
// put in spectators if we have enough room
if (ttctf->value)
{
if (last[0] > last[1] && last[0] > last[2])
j = last[0];
else if (last[1] > last[0] && last[1] > last[2])
j = last[1];
else
j = last[2];
}
else
{
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);
Com_sprintf (entry, sizeof(entry), "xv 0 yv %d string2 \"Spectators\" ", j);
// strncat(string, entry);
Q_strncatz(string, sizeof(string), entry);
len = (int)strlen(string);
j += 8;
}
// sprintf(entry+strlen(entry),
Com_sprintf (tmp, sizeof(tmp),
"ctf %d %d %d %d %d ",
(n & 1) ? 160 : 0, // x
j, // y
i, // playernum
cl->resp.score,
cl->ping > 999 ? 999 : cl->ping);
Q_strncatz (entry, sizeof(entry), tmp);
if (maxsize - len > strlen(entry)) {
// strncat(string, entry);
Q_strncatz(string, sizeof(string), entry);
len = (int)strlen(string);
}
if (n & 1)
j += 8;
n++;
}
}
if (ttctf->value)
{
if (total[0] - last[0] > 1) { // couldn't fit everyone
// sprintf(string + strlen(string), "xv -64 yv %d string \"..and %d more\" ",
Com_sprintf (tmp, sizeof(tmp), "xv -64 yv %d string \"..and %d more\" ",
42 + (last[0]+1)*8, total[0] - last[0] - 1);
Q_strncatz (string, sizeof(string), tmp);
}
if (total[1] - last[1] > 1) { // couldn't fit everyone
// sprintf(string + strlen(string), "xv 96 yv %d string \"..and %d more\" ",
Com_sprintf (tmp, sizeof(tmp), "xv 96 yv %d string \"..and %d more\" ",
42 + (last[1]+1)*8, total[1] - last[1] - 1);
Q_strncatz (string, sizeof(string), tmp);
}
if (total[2] - last[2] > 1) { // couldn't fit everyone
// sprintf(string + strlen(string), "xv 256 yv %d string \"..and %d more\" ",
Com_sprintf (tmp, sizeof(tmp), "xv 256 yv %d string \"..and %d more\" ",
42 + (last[2]+1)*8, total[2] - last[2] - 1);
Q_strncatz (string, sizeof(string), tmp);
}
}
else
{
if (total[0] - last[0] > 1) { // couldn't fit everyone
// sprintf(string + strlen(string), "xv 8 yv %d string \"..and %d more\" ",
Com_sprintf (tmp, sizeof(tmp), "xv 8 yv %d string \"..and %d more\" ",
42 + (last[0]+1)*8, total[0] - last[0] - 1);
Q_strncatz (string, sizeof(string), tmp);
}
if (total[1] - last[1] > 1) { // couldn't fit everyone
// sprintf(string + strlen(string), "xv 168 yv %d string \"..and %d more\" ",
Com_sprintf (tmp, sizeof(tmp), "xv 168 yv %d string \"..and %d more\" ",
42 + (last[1]+1)*8, total[1] - last[1] - 1);
Q_strncatz (string, sizeof(string), tmp);
}
}
gi.WriteByte (svc_layout);
gi.WriteString (string);
}
/*------------------------------------------------------------------------*/
/* TECH */
/*------------------------------------------------------------------------*/
void Apply_Tech_Shell (gitem_t *item, edict_t *ent)
{
ent->s.renderfx = RF_GLOW;
if (use_lithiumtechs->value)
{
ent->s.modelindex = gi.modelindex ("models/items/keys/pyramid/tris.md2");
ent->item->world_model = "models/items/keys/pyramid/tris.md2";
ent->item->icon = "k_pyramid";
}
else
{
if (!strcmp(item->classname, "item_tech1")) {
ent->s.modelindex = gi.modelindex ("models/ctf/resistance/tris.md2");
ent->item->world_model = "models/ctf/resistance/tris.md2";
ent->item->icon = "tech1";
}
else if (!strcmp(item->classname, "item_tech2")) {
ent->s.modelindex = gi.modelindex ("models/ctf/strength/tris.md2");
ent->item->world_model = "models/ctf/strength/tris.md2";
ent->item->icon = "tech2";
}
else if (!strcmp(item->classname, "item_tech3")) {
ent->s.modelindex = gi.modelindex ("models/ctf/haste/tris.md2");
ent->item->world_model = "models/ctf/haste/tris.md2";
ent->item->icon = "tech3";
}
else if (!strcmp(item->classname, "item_tech4")) {
ent->s.modelindex = gi.modelindex ("models/ctf/regeneration/tris.md2");
ent->item->world_model = "models/ctf/regeneration/tris.md2";
ent->item->icon = "tech4";
}
else if (!strcmp(item->classname, "item_tech5")) {
ent->s.modelindex = gi.modelindex ("models/ctf/vampire/tris.md2");
ent->item->world_model = "models/ctf/vampire/tris.md2";
ent->item->icon = "tech5";
}
else if (!strcmp(item->classname, "item_tech6")) {
ent->s.modelindex = gi.modelindex ("models/ctf/ammogen/tris.md2");
ent->item->world_model = "models/ctf/ammogen/tris.md2";
ent->item->icon = "tech6";
}
}
if (!use_coloredtechs->value && !use_lithiumtechs->value)
return;
if (!strcmp(item->classname, "item_tech1")) // resist
ent->s.effects |= EF_QUAD;
else if (!strcmp(item->classname, "item_tech2")) // strength
ent->s.effects |= EF_PENT;
else if (!strcmp(item->classname, "item_tech3")) // haste
ent->s.effects |= EF_DOUBLE;
else if (!strcmp(item->classname, "item_tech4")) { // regen
ent->s.effects |= EF_COLOR_SHELL;
ent->s.renderfx = RF_SHELL_GREEN;
}
else if (!strcmp(item->classname, "item_tech5")) // vampire
ent->s.effects |= EF_QUAD | EF_PENT;
else if (!strcmp(item->classname, "item_tech6")) // ammogen
ent->s.effects |= EF_HALF_DAMAGE;
}
void CTFHasTech (edict_t *who)
{
if (level.time - who->client->ctf_lasttechmsg > 2) {
safe_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);
void CTFTechTouch (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) && (level.time < ent->touch_debounce_time))
return;
Touch_Item (ent, other, plane, surf);
}
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;
if (!allow_techdrop->value)
return;
tech = Drop_Item(ent, item);
tech->nextthink = level.time + tech_life->value; // was CTF_TECH_TIMEOUT
tech->think = TechThink;
if (allow_techpickup->value)
{
tech->touch = CTFTechTouch;
tech->touch_debounce_time = level.time + 1.0;
}
ent->client->pers.inventory[ITEM_INDEX(item)] = 0;
Apply_Tech_Shell (item, tech);
}
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 + tech_life->value; // was CTF_TECH_TIMEOUT
dropped->think = TechThink;
dropped->owner = NULL;
ent->client->pers.inventory[ITEM_INDEX(tech)] = 0;
Apply_Tech_Shell (tech, dropped);
}
i++;
}
}
// ScarFace- this function counts the number of runes in circulation
int TechCount (void)
{
gitem_t *tech;
edict_t *cl_ent;
edict_t *mapent = NULL;
int i, j;
int count = 0;
mapent = g_edicts+1; // skip the worldspawn
// cycle through all ents to find techs
for (i = 1; i < globals.num_edicts; i++, mapent++)
{
if (!mapent->classname)
continue;
if (!strncmp(mapent->classname, "item_tech", 9))
count++;
}
// cycle through all players to find techs
for (i = 0; i < game.maxclients; i++)
{
cl_ent = g_edicts + 1 + i;
if (cl_ent->inuse)
{
j = 0;
while (tnames[j])
{
if ((tech = FindItemByClassname(tnames[j])) != NULL &&
cl_ent->client->pers.inventory[ITEM_INDEX(tech)])
count++;
j++;
}
}
}
return count;
}
int NumOfTech (int index)
{
gitem_t *tech;
edict_t *cl_ent;
edict_t *mapent = NULL;
int i;
int count = 0;
char techname [80];
mapent = g_edicts+1; // skip the worldspawn
// cycle through all ents to find techs
for (i = 1; i < globals.num_edicts; i++, mapent++)
{
if (!mapent->classname)
continue;
Com_sprintf(techname, sizeof(techname), "item_tech%d", index+1);
if (!strcmp(mapent->classname, techname))
count++;
}
// cycle through all players to find techs
for (i = 0; i < game.maxclients; i++)
{
cl_ent = g_edicts + 1 + i;
if (cl_ent->inuse)
{
if ((tech = FindItemByClassname(tnames[index])) != NULL &&
cl_ent->client->pers.inventory[ITEM_INDEX(tech)])
count++;
}
}
// gi.dprintf ("Number of tech%d in map: %d\n", index+1, count);
return count;
}
// ScarFace- a diagnostic function to display the number of runes in circulation
void Cmd_TechCount_f (edict_t *ent)
{
int count;
count = TechCount();
// gi.dprintf ("Number of techs in game: %d\n", count);
safe_cprintf(ent, PRINT_HIGH, "Number of techs in game: %d\n", count);
}
// ScarFace- spawn the additional runes
void SpawnMoreTechs (int oldtechcount, int newtechcount, int numtechtypes)
{
gitem_t *tech;
edict_t *spot;
int i, j;
// gi.dprintf ("Number of techs in loop: %d\n", numtechtypes);
i = oldtechcount % numtechtypes; //Spawn next tech in succession of the last one spawned
j = oldtechcount; //Start with count at old tech
// gi.dprintf ("tech number to start on: %d\n", (i+1));
while ( (j < numtechtypes) || ((j < tech_max->value) && (j < newtechcount)) )
{
while ( (tnames[i]) &&
((j < numtechtypes) || ((j < tech_max->value) && (j < newtechcount))) )
{
if (((tech = FindItemByClassname(tnames[i])) != NULL &&
(spot = FindTechSpawn()) != NULL)
&& ((int)(tech_flags->value) & (0x1 << i)))
{
//gi.dprintf ("Spawning tech%d\n", (i+1));
SpawnTech(tech, spot);
j++;
}
i++;
}
i = 0;
}
// gi.dprintf ("Current number of techs in game: %d\n", j);
}
// ScarFace- remove some runes
void RemoveTechs (int oldtechcount, int newtechcount, int numtechtypes)
{
edict_t *mapent = NULL;
int i, j, k;
int removed;
// gi.dprintf ("Number of techs desired: %d\n", newtechcount);
// find the last tech added or the tech to be removed
if ((oldtechcount % numtechtypes) == 0)
i = TECHTYPES-1;
else {
for (i=TECHTYPES-1; i>=0; i--)
if ( NumOfTech(i) > (oldtechcount / numtechtypes)
|| !((int)(tech_flags->value) & (0x1 << i)) )
break;
}
j = oldtechcount;
// gi.dprintf ("tech number to start removing on: %d\n", (i+1));
while ((tnames[i]) && (j > newtechcount)) //leave at least 1 of each tech
{
removed = 0; //flag to remove only one tech per pass
mapent = g_edicts+1; //skip the worldspawn
for (k = 1; k < globals.num_edicts; k++, mapent++)
{
if (!mapent->classname)
continue;
if (!strcmp(mapent->classname, tnames[i]))
{
//gi.dprintf ("Removing tech%d\n", (i+1));
G_FreeEdict(mapent);
j--;
removed = 1;
}
if (removed == 1) //don't keep removing techs of this type
break;
}
// If we can't find this tech in map, wait until a player drops it instead of removing others
if (removed == 0)
return;
i--;
}
// gi.dprintf ("Current number of techs in game: %d\n", j);
}
// ScarFace- this function checks to see if we need to spawn or remove runes
void CheckNumTechs (void)
{
edict_t *cl_ent;
int i, numclients;
int newtechcount, numtechs, techbits;
int numtechtypes = 0;
if ( !deathmatch->value || (level.time <= 0) ) // don't run while no in map
return;
if ( !use_techs->value || (ctf->value && ((int)dmflags->value & DF_CTF_NO_TECH)) )
return;
// clamp tech_flags to valid range
techbits = (int)(tech_flags->value);
if (techbits < 1 || techbits > 63) {
gi.dprintf ("Invalid tech_flags, setting default of 15.\n");
gi.cvar_forceset("tech_flags", "15");
}
// count number of tech types enabled
i = 0;
while (tnames[i]) {
if ((int)(tech_flags->value) & (0x1 << i))
numtechtypes++;
i++;
}
// count num of clients
numclients = 0;
for (i = 0; i < game.maxclients; i++)
{
cl_ent = g_edicts + 1 + i;
if (cl_ent->inuse)
numclients++;
}
newtechcount = tech_perplayer->value * numclients;
if (newtechcount > tech_max->value) // cap at tech_max
newtechcount = tech_max->value;
if (newtechcount < numtechtypes) // leave at least 1 of each enabled tech
newtechcount = numtechtypes;
numtechs = TechCount();
if (newtechcount > numtechs)
{
// gi.dprintf ("Number of techs to spawn: %d\n", newtechcount);
SpawnMoreTechs (numtechs, newtechcount, numtechtypes);
}
if (newtechcount < numtechs)
{
// gi.dprintf ("Number of techs to spawn: %d\n", newtechcount);
RemoveTechs (numtechs, newtechcount, numtechtypes);
}
}
/*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;
Apply_Tech_Shell (item, ent);
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 + tech_life->value; // was CTF_TECH_TIMEOUT
ent->think = TechThink;
gi.linkentity (ent);
}
/*static*/ void SpawnTechs (edict_t *ent)
{
gitem_t *tech;
edict_t *spot;
int i, techbits;
// clamp tech_flags to valid range
techbits = (int)(tech_flags->value);
if (techbits < 1 || techbits > 63) {
gi.dprintf ("Invalid tech_flags, setting default of 15.\n");
gi.cvar_forceset("tech_flags", "15");
}
i = 0;
while (tnames[i])
{
if ( (tech = FindItemByClassname(tnames[i])) != NULL &&
(spot = FindTechSpawn()) != NULL
&& ((int)tech_flags->value & (0x1 << i)) )
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 (!use_techs->value || (ctf->value && ((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);
}
int CTFApplyResistance (edict_t *ent, int dmg)
{
static gitem_t *tech = NULL;
float volume = 1.0;
//if (!deathmatch->value)
// return dmg;
if (ent->client && ent->client->silencer_shots)
volume = 0.2;
if (!tech)
tech = FindItemByClassname("item_tech1");
if (dmg && tech && ent->client && ent->client->pers.inventory[ITEM_INDEX(tech)]) {
// make noise
gi.sound (ent, CHAN_VOICE, gi.soundindex("ctf/tech1.wav"), volume, ATTN_NORM, 0);
return dmg / tech_resist->value;
}
return dmg;
}
int CTFApplyStrength (edict_t *ent, int dmg)
{
static gitem_t *tech = NULL;
//if (!deathmatch->value)
// return dmg;
if (!tech)
tech = FindItemByClassname("item_tech2");
if (dmg && tech && ent->client && ent->client->pers.inventory[ITEM_INDEX(tech)]) {
return dmg * tech_strength->value;
}
return dmg;
}
qboolean CTFApplyStrengthSound (edict_t *ent)
{
static gitem_t *tech = NULL;
float volume = 1.0;
//if (!deathmatch->value)
// return false;
if (ent->client && ent->client->silencer_shots)
volume = 0.2;
if (!tech)
tech = FindItemByClassname("item_tech2");
if (tech && ent->client &&
ent->client->pers.inventory[ITEM_INDEX(tech)]) {
if (ent->client->ctf_techsndtime < level.time) {
ent->client->ctf_techsndtime = level.time + 1;
if (ent->client->quad_framenum > level.framenum)
gi.sound(ent, CHAN_VOICE, gi.soundindex("ctf/tech2x.wav"), volume, ATTN_NORM, 0);
else
gi.sound(ent, CHAN_VOICE, gi.soundindex("ctf/tech2.wav"), volume, ATTN_NORM, 0);
}
return true;
}
return false;
}
qboolean CTFApplyHaste (edict_t *ent)
{
static gitem_t *tech = NULL;
//if (!deathmatch->value)
// return false;
if (!tech)
tech = FindItemByClassname("item_tech3");
if (tech && ent->client &&
ent->client->pers.inventory[ITEM_INDEX(tech)])
return true;
return false;
}
void CTFApplyHasteSound (edict_t *ent)
{
static gitem_t *tech = NULL;
float volume = 1.0;
//if (!deathmatch->value)
// return;
if (ent->client && ent->client->silencer_shots)
volume = 0.2;
if (!tech)
tech = FindItemByClassname("item_tech3");
if (tech && ent->client &&
ent->client->pers.inventory[ITEM_INDEX(tech)] &&
ent->client->ctf_techsndtime < level.time) {
ent->client->ctf_techsndtime = level.time + 1;
gi.sound(ent, CHAN_VOICE, gi.soundindex("ctf/tech3.wav"), volume, ATTN_NORM, 0);
}
}
void CTFApplyRegeneration (edict_t *ent)
{
static gitem_t *tech = NULL;
qboolean noise = false;
gclient_t *client;
int index;
float volume = 1.0;
//if (!deathmatch->value)
// return;
client = ent->client;
if (!client)
return;
if (ent->client->silencer_shots)
volume = 0.2;
if (!tech)
tech = FindItemByClassname("item_tech4");
if (tech && client->pers.inventory[ITEM_INDEX(tech)])
{
if (client->ctf_regentime < level.time) {
client->ctf_regentime = level.time;
if (ent->health < tech_regen_health_max->value)
{
ent->health += 5;
if (ent->health > tech_regen_health_max->value)
ent->health = tech_regen_health_max->value;
client->ctf_regentime += 0.5;
noise = true;
}
if (tech_regen_armor->value)
{
index = ArmorIndex (ent);
if (index)
{
index = ArmorIndex (ent);
if (index && client->pers.inventory[index] < tech_regen_armor_max->value)
{
client->pers.inventory[index] += 5;
if (client->pers.inventory[index] > tech_regen_armor_max->value)
client->pers.inventory[index] = tech_regen_armor_max->value;
client->ctf_regentime += 0.5;
noise = true;
}
}
else if (tech_regen_armor_always->value)
{
index = combat_armor_index;
if (index && client->pers.inventory[index] < tech_regen_armor_max->value)
{
client->pers.inventory[index] += 5;
if (client->pers.inventory[index] > tech_regen_armor_max->value)
client->pers.inventory[index] = tech_regen_armor_max->value;
client->ctf_regentime += 0.5;
noise = true;
}
}
}
}
if (noise && ent->client->ctf_techsndtime < level.time) {
ent->client->ctf_techsndtime = level.time + 1;
gi.sound(ent, CHAN_VOICE, gi.soundindex("ctf/tech4.wav"), volume, ATTN_NORM, 0);
}
}
}
qboolean CTFHasRegeneration (edict_t *ent)
{
static gitem_t *tech = NULL;
//if (!deathmatch->value)
// return false;
if (!tech)
tech = FindItemByClassname("item_tech4");
if (tech && ent->client &&
ent->client->pers.inventory[ITEM_INDEX(tech)])
return true;
return false;
}
// Knightmare added
void CTFApplyVampire (edict_t *ent, int dmg)
{
static gitem_t *tech = NULL;
//if (!deathmatch->value)
// return;
if (!tech)
tech = FindItemByClassname("item_tech5");
if (dmg && tech && ent->client && ent->client->pers.inventory[ITEM_INDEX(tech)])
{
if (ent->health < tech_vampiremax->value)
{
ent->health += dmg * tech_vampire->value;
ent->health = min(ent->health, tech_vampiremax->value);
}
CTFApplyVampireSound(ent);
}
}
// Knightmare added
void CTFApplyVampireSound (edict_t *ent)
{
static gitem_t *tech = NULL;
float volume = 1.0;
if (ent->client && ent->client->silencer_shots)
volume = 0.2;
if (!tech)
tech = FindItemByClassname("item_tech5");
if (tech && ent->client &&
ent->client->pers.inventory[ITEM_INDEX(tech)] &&
ent->client->ctf_techsndtime < level.time) {
ent->client->ctf_techsndtime = level.time + 1;
gi.sound(ent, CHAN_VOICE, gi.soundindex("ctf/tech5.wav"), volume, ATTN_NORM, 0);
}
}
// Knightmare added
void CTFApplyAmmogen (edict_t *attacker, edict_t *targ)
{
static gitem_t *tech = NULL, *pack = NULL;
if (!deathmatch->value)
return;
if (!attacker || !targ)
return;
if (!tech)
tech = FindItemByClassname("item_tech6");
if (!pack)
pack = FindItemByClassname("item_ammogen_pack");
if (tech && pack && attacker->client && targ->client && attacker->client->pers.inventory[ITEM_INDEX(tech)])
{
Drop_Item(targ, pack);
CTFApplyAmmogenSound(attacker);
}
}
// Knightmare added
void CTFApplyAmmogenSound (edict_t *ent)
{
static gitem_t *tech = NULL;
float volume = 1.0;
if (ent->client && ent->client->silencer_shots)
volume = 0.2;
if (!tech)
tech = FindItemByClassname("item_tech6");
if (tech && ent->client &&
ent->client->pers.inventory[ITEM_INDEX(tech)] &&
ent->client->ctf_techsndtime < level.time)
{
ent->client->ctf_techsndtime = level.time + 1;
gi.sound(ent, CHAN_VOICE, gi.soundindex("ctf/tech6.wav"), volume, ATTN_NORM, 0);
}
}
/*
======================================================================
SAY_TEAM
======================================================================
*/
// This array is in 'importance order', it indicates what items are
// more important when reporting their names.
struct {
char *classname;
int priority;
} loc_names[] =
{
{ "item_flag_team1", 1 },
{ "item_flag_team2", 1 },
{ "item_flag_team3", 1 }, // Knightmare added
{ "item_quad", 2 },
{ "item_invulnerability", 2 },
{ "item_jetpack", 3 }, // Knightmare added
{ "item_tech1", 3 },
{ "item_tech2", 3 },
{ "item_tech3", 3 },
{ "item_tech4", 3 },
{ "item_tech5", 3 },
{ "item_tech6", 3 },
{ "weapon_railgun", 4 },
{ "weapon_rocketlauncher", 4 },
{ "weapon_hyperblaster", 4 },
{ "weapon_chaingun", 4 },
{ "weapon_grenadelauncher", 4 },
{ "weapon_machinegun", 4 },
{ "weapon_supershotgun", 4 },
{ "weapon_shotgun", 4 },
{ "item_power_screen", 5 },
{ "item_power_shield", 5 },
{ "item_armor_body", 6 },
{ "item_armor_combat", 6 },
{ "item_armor_jacket", 6 },
{ "item_silencer", 7 },
{ "item_breather", 7 },
{ "item_enviro", 7 },
{ "item_adrenaline", 7 },
{ "item_bandolier", 8 },
{ "item_pack", 8 },
{ "item_flashlight", 8 }, // Knightmare added
{ NULL, 0 }
};
/*static*/ void CTFSay_Team_Location (edict_t *who, char *buf, size_t bufSize)
{
edict_t *what = NULL;
edict_t *hot = NULL;
float hotdist = 999999, newdist, newdist2;
vec3_t v;
int hotindex = 999;
int i;
gitem_t *item;
int nearteam = -1;
edict_t *flag1, *flag2, *flag3;
qboolean hotsee = false;
qboolean cansee;
while ((what = loc_findradius(what, who->s.origin, 1024)) != NULL) {
// find what in loc_classnames
for (i = 0; loc_names[i].classname; i++)
if (strcmp(what->classname, loc_names[i].classname) == 0)
break;
if (!loc_names[i].classname)
continue;
// something we can see get priority over something we can't
cansee = loc_CanSee(what, who);
if (cansee && !hotsee) {
hotsee = true;
hotindex = loc_names[i].priority;
hot = what;
VectorSubtract(what->s.origin, who->s.origin, v);
hotdist = VectorLength(v);
continue;
}
// if we can't see this, but we have something we can see, skip it
if (hotsee && !cansee)
continue;
if (hotsee && hotindex < loc_names[i].priority)
continue;
VectorSubtract(what->s.origin, who->s.origin, v);
newdist = VectorLength(v);
if (newdist < hotdist ||
(cansee && loc_names[i].priority < hotindex)) {
hot = what;
hotdist = newdist;
hotindex = i;
hotsee = loc_CanSee(hot, who);
}
}
if (!hot) {
// strncpy(buf, "nowhere");
Q_strncpyz(buf, bufSize, "nowhere");
return;
}
// we now have the closest item
// see if there's more than one in the map, if so
// we need to determine what team is closest
what = NULL;
while ((what = G_Find(what, FOFS(classname), hot->classname)) != NULL) {
if (what == hot)
continue;
// if we are here, there is more than one, find out if hot
// is closer to red flag or blue flag
if (ttctf->value)
{
if ((flag1 = G_Find(NULL, FOFS(classname), "item_flag_team1")) != NULL &&
(flag2 = G_Find(NULL, FOFS(classname), "item_flag_team2")) != NULL &&
(flag3 = G_Find(NULL, FOFS(classname), "item_flag_team3")) != NULL)
{
VectorSubtract(hot->s.origin, flag1->s.origin, v);
hotdist = VectorLength(v);
VectorSubtract(hot->s.origin, flag2->s.origin, v);
newdist = VectorLength(v);
VectorSubtract(hot->s.origin, flag3->s.origin, v);
newdist2 = VectorLength(v);
if ( (hotdist < newdist) && (hotdist < newdist2) )
nearteam = CTF_TEAM1;
else if ( (hotdist > newdist) && (newdist2 > newdist) )
nearteam = CTF_TEAM2;
else if ( (newdist2 < hotdist) && (newdist2 < newdist) )
nearteam = CTF_TEAM3;
}
}
else
{
if ((flag1 = G_Find(NULL, FOFS(classname), "item_flag_team1")) != NULL &&
(flag2 = G_Find(NULL, FOFS(classname), "item_flag_team2")) != NULL)
{
VectorSubtract(hot->s.origin, flag1->s.origin, v);
hotdist = VectorLength(v);
VectorSubtract(hot->s.origin, flag2->s.origin, v);
newdist = VectorLength(v);
if (hotdist < newdist)
nearteam = CTF_TEAM1;
else if (hotdist > newdist)
nearteam = CTF_TEAM2;
}
}
break;
}
if ((item = FindItemByClassname(hot->classname)) == NULL) {
// strncpy(buf, "nowhere");
Q_strncpyz(buf, bufSize, "nowhere");
return;
}
// in water?
if (who->waterlevel)
// strncpy(buf, "in the water ");
Q_strncpyz(buf, bufSize, "in the water ");
else
*buf = 0;
// near or above
VectorSubtract(who->s.origin, hot->s.origin, v);
if (fabs(v[2]) > fabs(v[0]) && fabs(v[2]) > fabs(v[1]))
if (v[2] > 0)
// strncat(buf, "above ");
Q_strncatz(buf, bufSize, "above ");
else
// strncat(buf, "below ");
Q_strncatz(buf, bufSize, "below ");
else
// strncat(buf, "near ");
Q_strncatz(buf, bufSize, "near ");
if (nearteam == CTF_TEAM1)
// strncat(buf, "the red ");
Q_strncatz(buf, bufSize, "the red ");
else if (nearteam == CTF_TEAM2)
// strncat(buf, "the blue ");
Q_strncatz(buf, bufSize, "the blue ");
else if (nearteam == CTF_TEAM3)
// strncat(buf, "the green ");
Q_strncatz(buf, bufSize, "the green ");
else
// strncat(buf, "the ");
Q_strncatz(buf, bufSize, "the ");
// strncat(buf, item->pickup_name);
Q_strncatz(buf, bufSize, item->pickup_name);
}
/*static*/ void CTFSay_Team_Armor (edict_t *who, char *buf, size_t bufSize)
{
gitem_t *item;
int index, cells;
int power_armor_type;
char tmp[128]; // Knightmare added
*buf = 0;
power_armor_type = PowerArmorType (who);
if (power_armor_type)
{
cells = who->client->pers.inventory[ITEM_INDEX(FindItem ("cells"))];
if (cells) {
// sprintf(buf+strlen(buf), "%s with %i cells ",
Com_sprintf (tmp, sizeof(tmp), "%s with %i cells ",
(power_armor_type == POWER_ARMOR_SCREEN) ?
"Power Screen" : "Power Shield", cells);
Q_strncatz (buf, bufSize, tmp);
}
}
index = ArmorIndex (who);
if (index)
{
item = GetItemByIndex (index);
if (item) {
if (*buf)
// strncat(buf, "and ");
Q_strncatz(buf, bufSize, "and ");
// sprintf(buf+strlen(buf), "%i units of %s",
Com_sprintf (tmp, sizeof(tmp), "%i units of %s",
who->client->pers.inventory[index], item->pickup_name);
Q_strncatz (buf, bufSize, tmp);
}
}
if (!*buf)
// strncpy(buf, "no armor", bufSize);
Q_strncpyz(buf, bufSize, "no armor");
}
/*static*/ void CTFSay_Team_Health (edict_t *who, char *buf, size_t bufSize)
{
if (who->health <= 0)
// strncpy(buf, "dead", bufSize);
Q_strncpyz (buf, bufSize, "dead");
else
Com_sprintf (buf, bufSize, "%i health", who->health);
}
/*static*/ void CTFSay_Team_Tech (edict_t *who, char *buf, size_t bufSize)
{
gitem_t *tech;
int i;
// see if the player has a tech powerup
i = 0;
while (tnames[i]) {
if ((tech = FindItemByClassname(tnames[i])) != NULL &&
who->client->pers.inventory[ITEM_INDEX(tech)]) {
Com_sprintf (buf, bufSize, "the %s", tech->pickup_name);
return;
}
i++;
}
// strncpy(buf, "no powerup", bufSize);
Q_strncpyz(buf, bufSize, "no powerup");
}
/*static*/ void CTFSay_Team_Weapon (edict_t *who, char *buf, size_t bufSize)
{
if (who->client->pers.weapon)
// strncpy(buf, who->client->pers.weapon->pickup_name, bufSize);
Q_strncpyz(buf, bufSize, who->client->pers.weapon->pickup_name);
else
// strncpy(buf, "none", bufSize);
Q_strncpyz(buf, bufSize, "none");
}
/*static*/ void CTFSay_Team_Sight (edict_t *who, char *buf, size_t bufSize)
{
int i;
edict_t *targ;
int n = 0;
char s[1024];
char s2[1024];
*s = *s2 = 0;
for (i = 1; i <= maxclients->value; i++)
{
targ = g_edicts + i;
if (!targ->inuse ||
targ == who ||
!loc_CanSee(targ, who))
continue;
if (*s2) {
if (strlen(s) + strlen(s2) + 3 < sizeof(s)) {
if (n)
// strncat(s, ", ");
Q_strncatz(s, sizeof(s), ", ");
// strncat(s, s2);
Q_strncatz(s, sizeof(s), s2);
*s2 = 0;
}
n++;
}
// strncpy(s2, targ->client->pers.netname);
Q_strncpyz(s2, sizeof(s2), targ->client->pers.netname);
}
if (*s2) {
if (strlen(s) + strlen(s2) + 6 < sizeof(s)) {
if (n)
// strncat(s, " and ");
Q_strncatz(s, sizeof(s), " and ");
// strncat(s, s2);
Q_strncatz(s, sizeof(s), s2);
}
// strncpy(buf, s, bufSize);
Q_strncpyz(buf, bufSize, s);
} else
// strncpy(buf, "no one", bufSize);
Q_strncpyz(buf, bufSize, "no one");
}
void CTFSay_Team (edict_t *who, char *msg)
{
char outmsg[256];
char buf[256];
int i;
char *p;
edict_t *cl_ent;
// size_t outmsgSize = sizeof(outmsg);
if (CheckFlood(who))
return;
outmsg[0] = 0;
if (*msg == '\"') {
msg[strlen(msg) - 1] = 0;
msg++;
}
for (p = outmsg; *msg && (p - outmsg) < sizeof(outmsg) - 1; msg++) // was -2
{
if (*msg == '%')
{
switch (*++msg)
{
case 'l' :
case 'L' :
CTFSay_Team_Location(who, buf, sizeof(buf));
if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 1) { // was -2
// strncpy(p, buf);
Q_strncpyz(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
p += strlen(buf);
// outmsgSize -= strlen(buf);
}
break;
case 'a' :
case 'A' :
CTFSay_Team_Armor(who, buf, sizeof(buf));
if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 1) { // was -2
// strncpy(p, buf);
Q_strncpyz(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
p += strlen(buf);
// outmsgSize -= strlen(buf);
}
break;
case 'h' :
case 'H' :
CTFSay_Team_Health(who, buf, sizeof(buf));
if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 1) { // was -2
// strncpy(p, buf);
Q_strncpyz(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
p += strlen(buf);
// outmsgSize -= strlen(buf);
}
break;
case 't' :
case 'T' :
CTFSay_Team_Tech(who, buf, sizeof(buf));
if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 1) { // was -2
// strncpy(p, buf);
Q_strncpyz(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
p += strlen(buf);
// outmsgSize -= strlen(buf);
}
break;
case 'w' :
case 'W' :
CTFSay_Team_Weapon(who, buf, sizeof(buf));
if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 1) { // was -2
// strncpy(p, buf);
Q_strncpyz(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
p += strlen(buf);
// outmsgSize -= strlen(buf);
}
break;
case 'n' :
case 'N' :
CTFSay_Team_Sight(who, buf, sizeof(buf));
if (strlen(buf) + (p - outmsg) < sizeof(outmsg) - 1) { // was -2
// strncpy(p, buf);
Q_strncpyz(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
p += strlen(buf);
// outmsgSize -= strlen(buf);
}
break;
default :
*p++ = *msg;
}
}
else
*p++ = *msg;
}
*p = 0;
for (i = 0; i < maxclients->value; i++)
{
cl_ent = g_edicts + 1 + i;
if (!cl_ent->inuse)
continue;
if (cl_ent->client->resp.ctf_team == who->client->resp.ctf_team)
safe_cprintf(cl_ent, PRINT_CHAT, "(%s): %s\n",
who->client->pers.netname, outmsg);
}
}
/*-----------------------------------------------------------------------*/
/*QUAKED misc_ctf_banner (1 .5 0) (-4 -64 0) (4 64 248) TEAM2 TEAM3
The origin is the bottom of the banner.
The banner is 248 tall.
*/
/*static*/ void misc_ctf_banner_think (edict_t *ent)
{
ent->s.frame = (ent->s.frame + 1) % 16;
ent->nextthink = level.time + FRAMETIME;
}
void SP_misc_ctf_banner (edict_t *ent)
{
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_NOT;
ent->s.modelindex = gi.modelindex ("models/ctf/banner/tris.md2");
ent->s.renderfx |= RF_NOSHADOW;
// Knightmare- added team 3 skin
if (ent->spawnflags & 2) // team3
ent->s.skinnum = 2;
else if (ent->spawnflags & 1) // team2
ent->s.skinnum = 1;
ent->s.frame = rand() % 16;
gi.linkentity (ent);
ent->think = misc_ctf_banner_think;
ent->nextthink = level.time + FRAMETIME;
}
/*QUAKED misc_ctf_small_banner (1 .5 0) (-4 -32 0) (4 32 124) TEAM2 TEAM3
The origin is the bottom of the banner.
The banner is 124 tall.
*/
void SP_misc_ctf_small_banner (edict_t *ent)
{
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_NOT;
ent->s.modelindex = gi.modelindex ("models/ctf/banner/small.md2");
ent->s.renderfx |= RF_NOSHADOW;
// Knightmare- added team 3 skin
if (ent->spawnflags & 2) // team3
ent->s.skinnum = 2;
else if (ent->spawnflags & 1) // team2
ent->s.skinnum = 1;
ent->s.frame = rand() % 16;
gi.linkentity (ent);
ent->think = misc_ctf_banner_think;
ent->nextthink = level.time + FRAMETIME;
}
/*-----------------------------------------------------------------------*/
/*static*/ void SetLevelName (pmenu_t *p)
{
static char levelname[33];
levelname[0] = '*';
if (g_edicts[0].message)
// strncpy(levelname+1, g_edicts[0].message, sizeof(levelname) - 2);
Q_strncpyz(levelname+1, sizeof(levelname) - 1, g_edicts[0].message);
else
// strncpy(levelname+1, level.mapname, sizeof(levelname) - 2);
Q_strncpyz(levelname+1, sizeof(levelname) - 1, level.mapname);
// levelname[sizeof(levelname) - 1] = 0;
p->text = levelname;
}
/*-----------------------------------------------------------------------*/
/* ELECTIONS */
qboolean CTFBeginElection (edict_t *ent, elect_t type, char *msg)
{
int i;
int count;
edict_t *e;
if (electpercentage->value == 0) {
safe_cprintf(ent, PRINT_HIGH, "Elections are disabled, only an admin can process this action.\n");
return false;
}
if (ctfgame.election != ELECT_NONE) {
safe_cprintf(ent, PRINT_HIGH, "Election already in progress.\n");
return false;
}
// clear votes
count = 0;
for (i = 1; i <= maxclients->value; i++) {
e = g_edicts + i;
e->client->resp.voted = false;
if (e->inuse)
count++;
}
if (count < 2) {
safe_cprintf(ent, PRINT_HIGH, "Not enough players for election.\n");
return false;
}
ctfgame.etarget = ent;
ctfgame.election = type;
ctfgame.evotes = 0;
ctfgame.needvotes = (count * electpercentage->value) / 100;
ctfgame.electtime = level.time + 20; // twenty seconds for election
// strncpy(ctfgame.emsg, msg, sizeof(ctfgame.emsg) - 1);
Q_strncpyz(ctfgame.emsg, sizeof(ctfgame.emsg), msg);
// tell everyone
safe_bprintf(PRINT_CHAT, "%s\n", ctfgame.emsg);
safe_bprintf(PRINT_HIGH, "Type YES or NO to vote on this request.\n");
safe_bprintf(PRINT_HIGH, "Votes: %d Needed: %d Time left: %ds\n", ctfgame.evotes, ctfgame.needvotes,
(int)(ctfgame.electtime - level.time));
return true;
}
void DoRespawn (edict_t *ent);
void CTFResetAllPlayers (void)
{
int i;
edict_t *ent;
for (i = 1; i <= maxclients->value; i++) {
ent = g_edicts + i;
if (!ent->inuse)
continue;
if (ent->client->menu)
PMenu_Close(ent);
CTFPlayerResetGrapple(ent);
CTFDeadDropFlag(ent);
CTFDeadDropTech(ent);
ent->client->resp.ctf_team = CTF_NOTEAM;
ent->client->resp.ready = false;
ent->svflags = 0;
ent->flags &= ~FL_GODMODE;
PutClientInServer(ent);
}
// reset the level
CTFResetTech();
CTFResetFlags();
for (ent = g_edicts + 1, i = 1; i < globals.num_edicts; i++, ent++) {
if (ent->inuse && !ent->client) {
if (ent->solid == SOLID_NOT && ent->think == DoRespawn &&
ent->nextthink >= level.time) {
ent->nextthink = 0;
DoRespawn(ent);
}
}
}
if (ctfgame.match == MATCH_SETUP)
ctfgame.matchtime = level.time + matchsetuptime->value * 60;
}
void CTFAssignGhost (edict_t *ent)
{
int ghost, i;
for (ghost = 0; ghost < MAX_CLIENTS; ghost++)
if (!ctfgame.ghosts[ghost].code)
break;
if (ghost == MAX_CLIENTS)
return;
ctfgame.ghosts[ghost].team = ent->client->resp.ctf_team;
ctfgame.ghosts[ghost].score = 0;
for (;;) {
ctfgame.ghosts[ghost].code = 10000 + (rand() % 90000);
for (i = 0; i < MAX_CLIENTS; i++)
if (i != ghost && ctfgame.ghosts[i].code == ctfgame.ghosts[ghost].code)
break;
if (i == MAX_CLIENTS)
break;
}
ctfgame.ghosts[ghost].ent = ent;
// strncpy(ctfgame.ghosts[ghost].netname, ent->client->pers.netname);
Q_strncpyz(ctfgame.ghosts[ghost].netname, sizeof(ctfgame.ghosts[ghost].netname), ent->client->pers.netname);
ent->client->resp.ghost = ctfgame.ghosts + ghost;
safe_cprintf(ent, PRINT_CHAT, "Your ghost code is **** %d ****\n", ctfgame.ghosts[ghost].code);
safe_cprintf(ent, PRINT_HIGH, "If you lose connection, you can rejoin with your score "
"intact by typing \"ghost %d\".\n", ctfgame.ghosts[ghost].code);
}
// start a match
void CTFStartMatch (void)
{
int i;
edict_t *ent;
int ghost = 0;
ctfgame.match = MATCH_GAME;
ctfgame.matchtime = level.time + matchtime->value * 60;
ctfgame.countdown = false;
ctfgame.team1 = ctfgame.team2 = 0;
memset(ctfgame.ghosts, 0, sizeof(ctfgame.ghosts));
for (i = 1; i <= maxclients->value; i++) {
ent = g_edicts + i;
if (!ent->inuse)
continue;
ent->client->resp.score = 0;
ent->client->resp.ctf_state = 0;
ent->client->resp.ghost = NULL;
safe_centerprintf(ent, "******************\n\nMATCH HAS STARTED!\n\n******************");
if (ent->client->resp.ctf_team != CTF_NOTEAM)
{
// make up a ghost code
CTFAssignGhost(ent);
CTFPlayerResetGrapple(ent);
ent->svflags = SVF_NOCLIENT;
ent->flags &= ~FL_GODMODE;
ent->client->respawn_time = level.time + 1.0 + ((rand()%30)/10.0);
ent->client->ps.pmove.pm_type = PM_DEAD;
ent->client->anim_priority = ANIM_DEATH;
ent->s.frame = FRAME_death308-1;
ent->client->anim_end = FRAME_death308;
ent->deadflag = DEAD_DEAD;
ent->movetype = MOVETYPE_NOCLIP;
ent->client->ps.gunindex = 0;
gi.linkentity (ent);
}
}
}
void CTFEndMatch (void)
{
ctfgame.match = MATCH_POST;
safe_bprintf(PRINT_CHAT, "MATCH COMPLETED!\n");
CTFCalcScores();
safe_bprintf(PRINT_HIGH, "RED TEAM: %d captures, %d points\n",
ctfgame.team1, ctfgame.total1);
safe_bprintf(PRINT_HIGH, "BLUE TEAM: %d captures, %d points\n",
ctfgame.team2, ctfgame.total2);
if (ttctf->value) // Knightmare added
{
safe_bprintf(PRINT_HIGH, "GREEN TEAM: %d captures, %d points\n",
ctfgame.team3, ctfgame.total3);
if (ctfgame.team1 > ctfgame.team2 && ctfgame.team1 > ctfgame.team3)
safe_bprintf(PRINT_CHAT, "RED team won over the BLUE and GREEN teams by %d CAPTURES!\n",
ctfgame.team1 - (ctfgame.team2 > ctfgame.team3)? ctfgame.team2:ctfgame.team3);
else if (ctfgame.team2 > ctfgame.team1 && ctfgame.team2 > ctfgame.team3)
safe_bprintf(PRINT_CHAT, "BLUE team won over the RED and GREEN teams by %d CAPTURES!\n",
ctfgame.team2 - (ctfgame.team1 > ctfgame.team3)? ctfgame.team1:ctfgame.team3);
else if (ctfgame.team3 > ctfgame.team1 && ctfgame.team3 > ctfgame.team2)
safe_bprintf(PRINT_CHAT, "GREEN team won over the RED and BLUE teams by %d CAPTURES!\n",
ctfgame.team3 - (ctfgame.team1 > ctfgame.team2)? ctfgame.team1:ctfgame.team2);
// frag tie breaker
else if (ctfgame.total1 > ctfgame.total2 && ctfgame.total1 > ctfgame.total3)
safe_bprintf(PRINT_CHAT, "RED team won over the BLUE and GREEN teams by %d POINTS!\n",
ctfgame.total1 - (ctfgame.total2 > ctfgame.total3)?ctfgame.total2:ctfgame.total3);
else if (ctfgame.total2 > ctfgame.total1 && ctfgame.total2 > ctfgame.total3)
safe_bprintf(PRINT_CHAT, "BLUE team won over the RED and GREEN teams by %d POINTS!\n",
ctfgame.total2 - (ctfgame.total1 > ctfgame.total3)?ctfgame.total1:ctfgame.total3);
else if (ctfgame.total3 > ctfgame.total1 && ctfgame.total3 > ctfgame.total2)
safe_bprintf(PRINT_CHAT, "GREEN team won over the RED and BLUE teams by %d POINTS!\n",
ctfgame.total3 - (ctfgame.total1 > ctfgame.total2)?ctfgame.total1:ctfgame.total2);
else
safe_bprintf(PRINT_CHAT, "TIE GAME!\n");
}
else
{
if (ctfgame.team1 > ctfgame.team2)
safe_bprintf(PRINT_CHAT, "RED team won over the BLUE team by %d CAPTURES!\n",
ctfgame.team1 - ctfgame.team2);
else if (ctfgame.team2 > ctfgame.team1)
safe_bprintf(PRINT_CHAT, "BLUE team won over the RED team by %d CAPTURES!\n",
ctfgame.team2 - ctfgame.team1);
else if (ctfgame.total1 > ctfgame.total2) // frag tie breaker
safe_bprintf(PRINT_CHAT, "RED team won over the BLUE team by %d POINTS!\n",
ctfgame.total1 - ctfgame.total2);
else if (ctfgame.total2 > ctfgame.total1)
safe_bprintf(PRINT_CHAT, "BLUE team won over the RED team by %d POINTS!\n",
ctfgame.total2 - ctfgame.total1);
else
safe_bprintf(PRINT_CHAT, "TIE GAME!\n");
}
EndDMLevel();
}
qboolean CTFNextMap (void)
{
if (ctfgame.match == MATCH_POST) {
ctfgame.match = MATCH_SETUP;
CTFResetAllPlayers();
return true;
}
return false;
}
void CTFWinElection (void)
{
switch (ctfgame.election) {
case ELECT_MATCH :
// reset into match mode
if (competition->value < 3)
gi.cvar_set("competition", "2");
ctfgame.match = MATCH_SETUP;
CTFResetAllPlayers();
break;
case ELECT_ADMIN :
ctfgame.etarget->client->resp.admin = true;
safe_bprintf(PRINT_HIGH, "%s has become an admin.\n", ctfgame.etarget->client->pers.netname);
safe_cprintf(ctfgame.etarget, PRINT_HIGH, "Type 'admin' to access the adminstration menu.\n");
break;
case ELECT_MAP :
safe_bprintf(PRINT_HIGH, "%s is warping to level %s.\n",
ctfgame.etarget->client->pers.netname, ctfgame.elevel);
// strncpy(level.forcemap, ctfgame.elevel, sizeof(level.forcemap) - 1);
Q_strncpyz(level.forcemap, sizeof(level.forcemap), ctfgame.elevel);
EndDMLevel();
break;
}
ctfgame.election = ELECT_NONE;
}
void CTFVoteYes (edict_t *ent)
{
if (ctfgame.election == ELECT_NONE) {
safe_cprintf(ent, PRINT_HIGH, "No election is in progress.\n");
return;
}
if (ent->client->resp.voted) {
safe_cprintf(ent, PRINT_HIGH, "You already voted.\n");
return;
}
if (ctfgame.etarget == ent) {
safe_cprintf(ent, PRINT_HIGH, "You can't vote for yourself.\n");
return;
}
ent->client->resp.voted = true;
ctfgame.evotes++;
if (ctfgame.evotes == ctfgame.needvotes) {
// the election has been won
CTFWinElection();
return;
}
safe_bprintf(PRINT_HIGH, "%s\n", ctfgame.emsg);
safe_bprintf(PRINT_CHAT, "Votes: %d Needed: %d Time left: %ds\n", ctfgame.evotes, ctfgame.needvotes,
(int)(ctfgame.electtime - level.time));
}
void CTFVoteNo (edict_t *ent)
{
if (ctfgame.election == ELECT_NONE) {
safe_cprintf(ent, PRINT_HIGH, "No election is in progress.\n");
return;
}
if (ent->client->resp.voted) {
safe_cprintf(ent, PRINT_HIGH, "You already voted.\n");
return;
}
if (ctfgame.etarget == ent) {
safe_cprintf(ent, PRINT_HIGH, "You can't vote for yourself.\n");
return;
}
ent->client->resp.voted = true;
safe_bprintf(PRINT_HIGH, "%s\n", ctfgame.emsg);
safe_bprintf(PRINT_CHAT, "Votes: %d Needed: %d Time left: %ds\n", ctfgame.evotes, ctfgame.needvotes,
(int)(ctfgame.electtime - level.time));
}
void CTFReady (edict_t *ent)
{
int i, j;
edict_t *e;
int t1, t2;
if (ent->client->resp.ctf_team == CTF_NOTEAM) {
safe_cprintf(ent, PRINT_HIGH, "Pick a team first (hit <TAB> for menu)\n");
return;
}
if (ctfgame.match != MATCH_SETUP) {
safe_cprintf(ent, PRINT_HIGH, "A match is not being setup.\n");
return;
}
if (ent->client->resp.ready) {
safe_cprintf(ent, PRINT_HIGH, "You have already commited.\n");
return;
}
ent->client->resp.ready = true;
safe_bprintf(PRINT_HIGH, "%s is ready.\n", ent->client->pers.netname);
t1 = t2 = 0;
for (j = 0, i = 1; i <= maxclients->value; i++) {
e = g_edicts + i;
if (!e->inuse)
continue;
if (e->client->resp.ctf_team != CTF_NOTEAM && !e->client->resp.ready)
j++;
if (e->client->resp.ctf_team == CTF_TEAM1)
t1++;
else if (e->client->resp.ctf_team == CTF_TEAM2)
t2++;
}
if (!j && t1 && t2) {
// everyone has commited
safe_bprintf(PRINT_CHAT, "All players have commited. Match starting\n");
ctfgame.match = MATCH_PREGAME;
ctfgame.matchtime = level.time + matchstarttime->value;
ctfgame.countdown = false;
gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("misc/talk1.wav"), 1, ATTN_NONE, 0);
}
}
void CTFNotReady (edict_t *ent)
{
if (ent->client->resp.ctf_team == CTF_NOTEAM) {
safe_cprintf(ent, PRINT_HIGH, "Pick a team first (hit <TAB> for menu)\n");
return;
}
if (ctfgame.match != MATCH_SETUP && ctfgame.match != MATCH_PREGAME) {
safe_cprintf(ent, PRINT_HIGH, "A match is not being setup.\n");
return;
}
if (!ent->client->resp.ready) {
safe_cprintf(ent, PRINT_HIGH, "You haven't commited.\n");
return;
}
ent->client->resp.ready = false;
safe_bprintf(PRINT_HIGH, "%s is no longer ready.\n", ent->client->pers.netname);
if (ctfgame.match == MATCH_PREGAME) {
safe_bprintf(PRINT_CHAT, "Match halted.\n");
ctfgame.match = MATCH_SETUP;
ctfgame.matchtime = level.time + matchsetuptime->value * 60;
}
}
void CTFGhost (edict_t *ent)
{
int i;
int n;
if (gi.argc() < 2) {
safe_cprintf(ent, PRINT_HIGH, "Usage: ghost <code>\n");
return;
}
if (ent->client->resp.ctf_team != CTF_NOTEAM) {
safe_cprintf(ent, PRINT_HIGH, "You are already in the game.\n");
return;
}
if (ctfgame.match != MATCH_GAME) {
safe_cprintf(ent, PRINT_HIGH, "No match is in progress.\n");
return;
}
n = atoi(gi.argv(1));
for (i = 0; i < MAX_CLIENTS; i++) {
if (ctfgame.ghosts[i].code && ctfgame.ghosts[i].code == n) {
safe_cprintf(ent, PRINT_HIGH, "Ghost code accepted, your position has been reinstated.\n");
ctfgame.ghosts[i].ent->client->resp.ghost = NULL;
ent->client->resp.ctf_team = ctfgame.ghosts[i].team;
ent->client->resp.ghost = ctfgame.ghosts + i;
ent->client->resp.score = ctfgame.ghosts[i].score;
ent->client->resp.ctf_state = 0;
ctfgame.ghosts[i].ent = ent;
ent->svflags = 0;
ent->flags &= ~FL_GODMODE;
PutClientInServer(ent);
safe_bprintf(PRINT_HIGH, "%s has been reinstated to %s team.\n",
ent->client->pers.netname, CTFTeamName(ent->client->resp.ctf_team));
return;
}
}
safe_cprintf(ent, PRINT_HIGH, "Invalid ghost code.\n");
}
qboolean CTFMatchSetup (void)
{
if (ctfgame.match == MATCH_SETUP || ctfgame.match == MATCH_PREGAME)
return true;
return false;
}
qboolean CTFMatchOn (void)
{
if (ctfgame.match == MATCH_GAME)
return true;
return false;
}
/*-----------------------------------------------------------------------*/
void CTFJoinTeam1(edict_t *ent, pmenuhnd_t *p);
void CTFJoinTeam2(edict_t *ent, pmenuhnd_t *p);
void CTFJoinTeam3(edict_t *ent, pmenuhnd_t *p); // Knightmare added
void CTFCredits(edict_t *ent, pmenuhnd_t *p);
void CTFReturnToMain(edict_t *ent, pmenuhnd_t *p);
void CTFChaseCam(edict_t *ent, pmenuhnd_t *p);
pmenu_t creditsmenu[] = {
{ "*Quake II", PMENU_ALIGN_CENTER, NULL },
{ "*ThreeWave Capture the Flag", PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ "*Programming", PMENU_ALIGN_CENTER, NULL },
{ "Dave 'Zoid' Kirsch", PMENU_ALIGN_CENTER, NULL },
{ "*Level Design", PMENU_ALIGN_CENTER, NULL },
{ "Christian Antkow", PMENU_ALIGN_CENTER, NULL },
{ "Tim Willits", PMENU_ALIGN_CENTER, NULL },
{ "Dave 'Zoid' Kirsch", PMENU_ALIGN_CENTER, NULL },
{ "*Art", PMENU_ALIGN_CENTER, NULL },
{ "Adrian Carmack Paul Steed", PMENU_ALIGN_CENTER, NULL },
{ "Kevin Cloud", PMENU_ALIGN_CENTER, NULL },
{ "*Sound", PMENU_ALIGN_CENTER, NULL },
{ "Tom 'Bjorn' Klok", PMENU_ALIGN_CENTER, NULL },
{ "*Original CTF Art Design", PMENU_ALIGN_CENTER, NULL },
{ "Brian 'Whaleboy' Cozzens", PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ "Return to Main Menu", PMENU_ALIGN_LEFT, CTFReturnToMain }
};
static const int jmenu_level = 2;
static const int jmenu_match = 3;
static const int jmenu_red = 5;
static const int jmenu_blue = 7;
static const int jmenu_chase = 9;
static const int jmenu_reqmatch = 11;
pmenu_t joinmenu[] = {
{ "*Quake II", PMENU_ALIGN_CENTER, NULL },
{ "*ThreeWave Capture the Flag", PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ "Join Red Team", PMENU_ALIGN_LEFT, CTFJoinTeam1 },
{ NULL, PMENU_ALIGN_LEFT, NULL },
{ "Join Blue Team", PMENU_ALIGN_LEFT, CTFJoinTeam2 },
{ NULL, PMENU_ALIGN_LEFT, NULL },
{ "Chase Camera", PMENU_ALIGN_LEFT, CTFChaseCam },
{ "Credits", PMENU_ALIGN_LEFT, CTFCredits },
{ NULL, PMENU_ALIGN_LEFT, NULL },
{ NULL, PMENU_ALIGN_LEFT, NULL },
{ "Use [ and ] to move cursor", PMENU_ALIGN_LEFT, NULL },
{ "ENTER to select", PMENU_ALIGN_LEFT, NULL },
{ "ESC to Exit Menu", PMENU_ALIGN_LEFT, NULL },
{ "(TAB to Return)", PMENU_ALIGN_LEFT, NULL },
{ "v" CTF_STRING_VERSION, PMENU_ALIGN_RIGHT, NULL },
};
pmenu_t nochasemenu[] = {
{ "*Quake II", PMENU_ALIGN_CENTER, NULL },
{ "*ThreeWave Capture the Flag", PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ "No one to chase", PMENU_ALIGN_LEFT, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ "Return to Main Menu", PMENU_ALIGN_LEFT, CTFReturnToMain }
};
static const int ttctf_jmenu_level = 2;
static const int ttctf_jmenu_match = 3;
static const int ttctf_jmenu_red = 4;
static const int ttctf_jmenu_blue = 6;
static const int ttctf_jmenu_green = 8;
static const int ttctf_jmenu_chase = 10;
static const int ttctf_jmenu_reqmatch = 12;
pmenu_t ttctf_joinmenu[] = {
{ "*Quake II", PMENU_ALIGN_CENTER, NULL },
{ "*3Team CTF", PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ "Join Red Team", PMENU_ALIGN_LEFT, CTFJoinTeam1 },
{ NULL, PMENU_ALIGN_LEFT, NULL },
{ "Join Blue Team", PMENU_ALIGN_LEFT, CTFJoinTeam2 },
{ NULL, PMENU_ALIGN_LEFT, NULL },
{ "Join Green Team", PMENU_ALIGN_LEFT, CTFJoinTeam3 },
{ NULL, PMENU_ALIGN_LEFT, NULL },
{ "Chase Camera", PMENU_ALIGN_LEFT, CTFChaseCam },
{ "Credits", PMENU_ALIGN_LEFT, CTFCredits },
{ NULL, PMENU_ALIGN_LEFT, NULL },
{ "Use [ and ] to move cursor", PMENU_ALIGN_LEFT, NULL },
{ "ENTER to select", PMENU_ALIGN_LEFT, NULL },
{ "ESC to Exit Menu", PMENU_ALIGN_LEFT, NULL },
{ "(TAB to Return)", PMENU_ALIGN_LEFT, NULL },
{ "v" CTF_STRING_VERSION, PMENU_ALIGN_RIGHT, NULL },
};
pmenu_t ttctf_nochasemenu[] = {
{ "*Quake II", PMENU_ALIGN_CENTER, NULL },
{ "*3Team CTF", PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ "No one to chase", PMENU_ALIGN_LEFT, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ "Return to Main Menu", PMENU_ALIGN_LEFT, CTFReturnToMain }
};
void CTFJoinTeam (edict_t *ent, int desired_team)
{
char *s;
// Knightmare added
if (ent->solid == SOLID_NOT && ctfgame.match > MATCH_SETUP) {
safe_cprintf(ent, PRINT_HIGH, "Can't change teams in a match.\n");
return;
}
PMenu_Close(ent);
gi.dprintf ("joining team\n");
ent->svflags &= ~SVF_NOCLIENT;
ent->client->resp.ctf_team = desired_team;
ent->client->resp.ctf_state = 0;
s = Info_ValueForKey (ent->client->pers.userinfo, "skin");
CTFAssignSkin(ent, s);
// assign a ghost if we are in match mode
if (ctfgame.match == MATCH_GAME) {
if (ent->client->resp.ghost)
ent->client->resp.ghost->code = 0;
ent->client->resp.ghost = NULL;
CTFAssignGhost(ent);
}
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;
safe_bprintf(PRINT_HIGH, "%s joined the %s team.\n",
ent->client->pers.netname, CTFTeamName(desired_team));
if (ctfgame.match == MATCH_SETUP) {
safe_centerprintf(ent, "***********************\n"
"Type \"ready\" in console\n"
"to ready up.\n"
"***********************");
}
}
void CTFJoinTeam1 (edict_t *ent, pmenuhnd_t *p)
{
CTFJoinTeam(ent, CTF_TEAM1);
}
void CTFJoinTeam2 (edict_t *ent, pmenuhnd_t *p)
{
CTFJoinTeam(ent, CTF_TEAM2);
}
// Knightmare added
void CTFJoinTeam3 (edict_t *ent, pmenuhnd_t *p)
{
CTFJoinTeam(ent, CTF_TEAM3);
}
pmenuhnd_t *PMenu_Open (edict_t *ent, pmenu_t *entries, int cur, int num, void *arg);
void CTFChaseCam (edict_t *ent, pmenuhnd_t *p)
{
int i;
edict_t *e;
if (ent->client->chase_target)
{
ent->client->chase_target = NULL;
ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
PMenu_Close(ent);
return;
}
for (i = 1; i <= maxclients->value; i++)
{
e = g_edicts + i;
if (e->inuse && e->solid != SOLID_NOT && e != ent)
{
ent->client->chase_target = e;
#ifdef KMQUAKE2_ENGINE_MOD
// Knightmare- turn off client-side chasecam
stuffcmd (ent, CLIENT_THIRDPERSON_CVAR" 0");
#endif
PMenu_Close(ent);
ent->client->update_chase = true;
return;
}
}
// Knightmare added
if (ttctf->value) {
SetLevelName(ttctf_nochasemenu + jmenu_level);
PMenu_Close(ent);
PMenu_Open(ent, ttctf_nochasemenu, -1, sizeof(ttctf_nochasemenu) / sizeof(pmenu_t), NULL);
}
else {
SetLevelName(nochasemenu + jmenu_level);
PMenu_Close(ent);
PMenu_Open(ent, nochasemenu, -1, sizeof(nochasemenu) / sizeof(pmenu_t), NULL);
}
}
void CTFReturnToMain (edict_t *ent, pmenuhnd_t *p)
{
PMenu_Close(ent);
if (ttctf->value)
TTCTFOpenJoinMenu(ent);
else
CTFOpenJoinMenu(ent);
}
void CTFRequestMatch (edict_t *ent, pmenuhnd_t *p)
{
char text[1024];
PMenu_Close(ent);
Com_sprintf (text, sizeof(text), "%s has requested to switch to competition mode.",
ent->client->pers.netname);
CTFBeginElection(ent, ELECT_MATCH, text);
}
void DeathmatchScoreboard (edict_t *ent);
void CTFShowScores (edict_t *ent, pmenu_t *p)
{
PMenu_Close(ent);
ent->client->showscores = true;
ent->client->showinventory = false;
DeathmatchScoreboard (ent);
}
int CTFUpdateJoinMenu (edict_t *ent)
{
static char team1players[32];
static char team2players[32];
int num1, num2, num3, i;
float r;
if (ctfgame.match >= MATCH_PREGAME && matchlock->value)
{
joinmenu[jmenu_red].text = "MATCH IS LOCKED";
joinmenu[jmenu_red].SelectFunc = NULL;
joinmenu[jmenu_blue].text = " (entry is not permitted)";
joinmenu[jmenu_blue].SelectFunc = NULL;
}
else
{
if (ctfgame.match >= MATCH_PREGAME)
{
joinmenu[jmenu_red].text = "Join Red MATCH Team";
joinmenu[jmenu_blue].text = "Join Blue MATCH Team";
}
else
{
joinmenu[jmenu_red].text = "Join Red Team";
joinmenu[jmenu_blue].text = "Join Blue Team";
}
joinmenu[jmenu_red].SelectFunc = CTFJoinTeam1;
joinmenu[jmenu_blue].SelectFunc = CTFJoinTeam2;
}
if (ctf_forcejoin->string && *ctf_forcejoin->string)
{
if (Q_stricmp(ctf_forcejoin->string, "red") == 0) {
joinmenu[jmenu_blue].text = NULL;
joinmenu[jmenu_blue].SelectFunc = NULL;
} else if (Q_stricmp(ctf_forcejoin->string, "blue") == 0) {
joinmenu[jmenu_red].text = NULL;
joinmenu[jmenu_red].SelectFunc = NULL;
}
}
if (ent->client->chase_target)
joinmenu[jmenu_chase].text = "Leave Chase Camera";
else
joinmenu[jmenu_chase].text = "Chase Camera";
SetLevelName(joinmenu + jmenu_level);
num1 = num2 = num3 = 0;
for (i = 0; i < maxclients->value; i++)
{
if (!g_edicts[i+1].inuse)
continue;
if (game.clients[i].resp.ctf_team == CTF_TEAM1)
num1++;
else if (game.clients[i].resp.ctf_team == CTF_TEAM2)
num2++;
else if (game.clients[i].resp.ctf_team == CTF_TEAM3)
num3++;
}
Com_sprintf (team1players, sizeof(team1players), " (%d players)", num1);
Com_sprintf (team2players, sizeof(team2players), " (%d players)", num2);
switch (ctfgame.match)
{
case MATCH_NONE :
joinmenu[jmenu_match].text = NULL;
break;
case MATCH_SETUP :
joinmenu[jmenu_match].text = "*MATCH SETUP IN PROGRESS";
break;
case MATCH_PREGAME :
joinmenu[jmenu_match].text = "*MATCH STARTING";
break;
case MATCH_GAME :
joinmenu[jmenu_match].text = "*MATCH IN PROGRESS";
break;
}
if (joinmenu[jmenu_red].text)
joinmenu[jmenu_red+1].text = team1players;
else
joinmenu[jmenu_red+1].text = NULL;
if (joinmenu[jmenu_blue].text)
joinmenu[jmenu_blue+1].text = team2players;
else
joinmenu[jmenu_blue+1].text = NULL;
joinmenu[jmenu_reqmatch].text = NULL;
joinmenu[jmenu_reqmatch].SelectFunc = NULL;
if (competition->value && ctfgame.match < MATCH_SETUP) {
joinmenu[jmenu_reqmatch].text = "Request Match";
joinmenu[jmenu_reqmatch].SelectFunc = CTFRequestMatch;
}
r = random();
if (num1 > num2)
return CTF_TEAM1;
else if (num2 > num1)
return CTF_TEAM2;
else if (r < 0.5)
return CTF_TEAM1;
else
return CTF_TEAM2;
}
// Knightmare added
int TTCTFUpdateJoinMenu (edict_t *ent)
{
static char team1players[32];
static char team2players[32];
static char team3players[32];
int num1, num2, num3, i;
float r;
if (ctfgame.match >= MATCH_PREGAME && matchlock->value)
{
ttctf_joinmenu[ttctf_jmenu_red].text = "MATCH IS LOCKED";
ttctf_joinmenu[ttctf_jmenu_red].SelectFunc = NULL;
ttctf_joinmenu[ttctf_jmenu_blue].text = " (entry is not permitted)";
ttctf_joinmenu[ttctf_jmenu_blue].SelectFunc = NULL;
ttctf_joinmenu[ttctf_jmenu_green].text = NULL;
ttctf_joinmenu[ttctf_jmenu_green].SelectFunc = NULL;
}
else
{
if (ctfgame.match >= MATCH_PREGAME)
{
ttctf_joinmenu[ttctf_jmenu_red].text = "Join Red MATCH Team";
ttctf_joinmenu[ttctf_jmenu_blue].text = "Join Blue MATCH Team";
ttctf_joinmenu[ttctf_jmenu_green].text = "Join Green MATCH Team";
}
else
{
ttctf_joinmenu[ttctf_jmenu_red].text = "Join Red Team";
ttctf_joinmenu[ttctf_jmenu_blue].text = "Join Blue Team";
ttctf_joinmenu[ttctf_jmenu_green].text = "Join Green Team";
}
ttctf_joinmenu[ttctf_jmenu_red].SelectFunc = CTFJoinTeam1;
ttctf_joinmenu[ttctf_jmenu_blue].SelectFunc = CTFJoinTeam2;
ttctf_joinmenu[ttctf_jmenu_green].SelectFunc = CTFJoinTeam3;
}
if (ctf_forcejoin->string && *ctf_forcejoin->string)
{
if (Q_stricmp(ctf_forcejoin->string, "red") == 0) {
ttctf_joinmenu[ttctf_jmenu_blue].text = NULL;
ttctf_joinmenu[ttctf_jmenu_blue].SelectFunc = NULL;
ttctf_joinmenu[ttctf_jmenu_green].text = NULL;
ttctf_joinmenu[ttctf_jmenu_green].SelectFunc = NULL;
}
else if (Q_stricmp(ctf_forcejoin->string, "blue") == 0) {
ttctf_joinmenu[ttctf_jmenu_red].text = NULL;
ttctf_joinmenu[ttctf_jmenu_red].SelectFunc = NULL;
ttctf_joinmenu[ttctf_jmenu_green].text = NULL;
ttctf_joinmenu[ttctf_jmenu_green].SelectFunc = NULL;
}
else if (Q_stricmp(ctf_forcejoin->string, "green") == 0) {
ttctf_joinmenu[ttctf_jmenu_red].text = NULL;
ttctf_joinmenu[ttctf_jmenu_red].SelectFunc = NULL;
ttctf_joinmenu[ttctf_jmenu_blue].text = NULL;
ttctf_joinmenu[ttctf_jmenu_blue].SelectFunc = NULL;
}
}
if (ent->client->chase_target)
ttctf_joinmenu[ttctf_jmenu_chase].text = "Leave Chase Camera";
else
ttctf_joinmenu[ttctf_jmenu_chase].text = "Chase Camera";
SetLevelName(ttctf_joinmenu + jmenu_level);
num1 = num2 = num3 = 0;
for (i = 0; i < maxclients->value; i++)
{
if (!g_edicts[i+1].inuse)
continue;
if (game.clients[i].resp.ctf_team == CTF_TEAM1)
num1++;
else if (game.clients[i].resp.ctf_team == CTF_TEAM2)
num2++;
else if (game.clients[i].resp.ctf_team == CTF_TEAM3)
num3++;
}
Com_sprintf (team1players, sizeof(team1players), " (%d players)", num1);
Com_sprintf (team2players, sizeof(team2players), " (%d players)", num2);
Com_sprintf (team3players, sizeof(team3players), " (%d players)", num3);
switch (ctfgame.match)
{
case MATCH_NONE :
ttctf_joinmenu[ttctf_jmenu_match].text = NULL;
break;
case MATCH_SETUP :
ttctf_joinmenu[ttctf_jmenu_match].text = "*MATCH SETUP IN PROGRESS";
break;
case MATCH_PREGAME :
ttctf_joinmenu[ttctf_jmenu_match].text = "*MATCH STARTING";
break;
case MATCH_GAME :
ttctf_joinmenu[ttctf_jmenu_match].text = "*MATCH IN PROGRESS";
break;
}
if (ttctf_joinmenu[ttctf_jmenu_red].text)
ttctf_joinmenu[ttctf_jmenu_red+1].text = team1players;
else
ttctf_joinmenu[ttctf_jmenu_red+1].text = NULL;
if (ttctf_joinmenu[ttctf_jmenu_blue].text)
ttctf_joinmenu[ttctf_jmenu_blue+1].text = team2players;
else
ttctf_joinmenu[ttctf_jmenu_blue+1].text = NULL;
if (ttctf_joinmenu[ttctf_jmenu_green].text)
ttctf_joinmenu[ttctf_jmenu_green+1].text = team3players;
else
ttctf_joinmenu[ttctf_jmenu_green+1].text = NULL;
ttctf_joinmenu[ttctf_jmenu_reqmatch].text = NULL;
ttctf_joinmenu[ttctf_jmenu_reqmatch].SelectFunc = NULL;
if (competition->value && ctfgame.match < MATCH_SETUP) {
ttctf_joinmenu[ttctf_jmenu_reqmatch].text = "Request Match";
ttctf_joinmenu[ttctf_jmenu_reqmatch].SelectFunc = CTFRequestMatch;
}
r = random();
if (num1 > num2 && num1 > num3)
return CTF_TEAM1;
else if (num2 > num1 && num2 > num3)
return CTF_TEAM2;
else if (num3 > num1 && num3 > num2)
return CTF_TEAM3;
// random
else if (r < 0.33)
return CTF_TEAM1;
else if (r < 0.67)
return CTF_TEAM2;
else
return CTF_TEAM3;
}
void CTFOpenJoinMenu (edict_t *ent)
{
int team;
if (ent->client->textdisplay)
Text_Close(ent);
if (ent->client->showinventory)
ent->client->showinventory = false;
team = CTFUpdateJoinMenu(ent);
if (ent->client->chase_target)
team = 9;
else if (team == CTF_TEAM1)
team = 5;
else if (team == CTF_TEAM2)
team = 7;
PMenu_Open(ent, joinmenu, team, sizeof(joinmenu) / sizeof(pmenu_t), NULL);
}
// Knightmare added
void TTCTFOpenJoinMenu (edict_t *ent)
{
int team;
if (ent->client->textdisplay)
Text_Close(ent);
if (ent->client->showinventory)
ent->client->showinventory = false;
team = TTCTFUpdateJoinMenu(ent);
if (ent->client->chase_target)
team = 10;
else if (team == CTF_TEAM1)
team = 4;
else if (team == CTF_TEAM2)
team = 6;
else if (team == CTF_TEAM3)
team = 8;
PMenu_Open(ent, ttctf_joinmenu, team, sizeof(ttctf_joinmenu) / sizeof(pmenu_t), NULL);
}
void CTFCredits (edict_t *ent, pmenuhnd_t *p)
{
PMenu_Close(ent);
PMenu_Open(ent, creditsmenu, -1, sizeof(creditsmenu) / sizeof(pmenu_t), NULL);
}
qboolean CTFStartClient (edict_t *ent)
{
if (ent->client->resp.ctf_team != CTF_NOTEAM)
return false;
if (!((int)dmflags->value & DF_CTF_FORCEJOIN) || ctfgame.match >= MATCH_SETUP) {
// start as 'observer'
ent->movetype = MOVETYPE_NOCLIP;
ent->solid = SOLID_NOT;
ent->svflags |= SVF_NOCLIENT;
ent->client->resp.ctf_team = CTF_NOTEAM;
ent->client->ps.gunindex = 0;
gi.linkentity (ent);
if (ttctf->value)
TTCTFOpenJoinMenu(ent);
else
CTFOpenJoinMenu(ent);
return true;
}
return false;
}
void CTFObserver (edict_t *ent)
{
char userinfo[MAX_INFO_STRING];
// start as 'observer'
if (ent->movetype == MOVETYPE_NOCLIP)
CTFPlayerResetGrapple(ent);
CTFDeadDropFlag(ent);
CTFDeadDropTech(ent);
ent->deadflag = DEAD_NO;
ent->movetype = MOVETYPE_NOCLIP;
ent->solid = SOLID_NOT;
ent->svflags |= SVF_NOCLIENT;
ent->client->resp.ctf_team = CTF_NOTEAM;
ent->client->ps.gunindex = 0;
ent->client->resp.score = 0;
memcpy (userinfo, ent->client->pers.userinfo, sizeof(userinfo));
InitClientPersistant (ent->client, 0);
ClientUserinfoChanged (ent, userinfo);
gi.linkentity (ent);
if (ttctf->value)
TTCTFOpenJoinMenu(ent);
else
CTFOpenJoinMenu(ent);
}
qboolean CTFInMatch (void)
{
if (ctfgame.match > MATCH_NONE)
return true;
return false;
}
qboolean CTFCheckRules (void)
{
int t;
int i, j;
char text[64];
edict_t *ent;
if (ctfgame.election != ELECT_NONE && ctfgame.electtime <= level.time) {
safe_bprintf(PRINT_CHAT, "Election timed out and has been cancelled.\n");
ctfgame.election = ELECT_NONE;
}
if (ctfgame.match != MATCH_NONE) {
t = ctfgame.matchtime - level.time;
// no team warnings in match mode
ctfgame.warnactive = 0;
if (t <= 0) { // time ended on something
switch (ctfgame.match) {
case MATCH_SETUP :
// go back to normal mode
if (competition->value < 3) {
ctfgame.match = MATCH_NONE;
gi.cvar_set("competition", "1");
CTFResetAllPlayers();
} else {
// reset the time
ctfgame.matchtime = level.time + matchsetuptime->value * 60;
}
return false;
case MATCH_PREGAME :
// match started!
CTFStartMatch();
gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("misc/tele_up.wav"), 1, ATTN_NONE, 0);
return false;
case MATCH_GAME :
// match ended!
CTFEndMatch();
gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("misc/bigtele.wav"), 1, ATTN_NONE, 0);
return false;
}
}
if (t == ctfgame.lasttime)
return false;
ctfgame.lasttime = t;
switch (ctfgame.match) {
case MATCH_SETUP :
for (j = 0, i = 1; i <= maxclients->value; i++) {
ent = g_edicts + i;
if (!ent->inuse)
continue;
if (ent->client->resp.ctf_team != CTF_NOTEAM &&
!ent->client->resp.ready)
j++;
}
if (competition->value < 3)
Com_sprintf (text, sizeof(text), "%02d:%02d SETUP: %d not ready",
t / 60, t % 60, j);
else
Com_sprintf(text, sizeof(text), "SETUP: %d not ready", j);
gi.configstring (CONFIG_CTF_MATCH, text);
break;
case MATCH_PREGAME :
Com_sprintf (text, sizeof(text), "%02d:%02d UNTIL START",
t / 60, t % 60);
gi.configstring (CONFIG_CTF_MATCH, text);
if (t <= 10 && !ctfgame.countdown) {
ctfgame.countdown = true;
gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("world/10_0.wav"), 1, ATTN_NONE, 0);
}
break;
case MATCH_GAME:
Com_sprintf (text, sizeof(text), "%02d:%02d MATCH",
t / 60, t % 60);
gi.configstring (CONFIG_CTF_MATCH, text);
if (t <= 10 && !ctfgame.countdown) {
ctfgame.countdown = true;
gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("world/10_0.wav"), 1, ATTN_NONE, 0);
}
break;
}
return false;
} else {
int team1 = 0, team2 = 0;
if (level.time == ctfgame.lasttime)
return false;
ctfgame.lasttime = level.time;
// this is only done in non-match (public) mode
if (warn_unbalanced->value) {
// count up the team totals
for (i = 1; i <= maxclients->value; i++) {
ent = g_edicts + i;
if (!ent->inuse)
continue;
if (ent->client->resp.ctf_team == CTF_TEAM1)
team1++;
else if (ent->client->resp.ctf_team == CTF_TEAM2)
team2++;
}
if (team1 - team2 >= 2 && team2 >= 2) {
if (ctfgame.warnactive != CTF_TEAM1) {
ctfgame.warnactive = CTF_TEAM1;
gi.configstring (CONFIG_CTF_TEAMINFO, "WARNING: Red has too many players");
}
} else if (team2 - team1 >= 2 && team1 >= 2) {
if (ctfgame.warnactive != CTF_TEAM2) {
ctfgame.warnactive = CTF_TEAM2;
gi.configstring (CONFIG_CTF_TEAMINFO, "WARNING: Blue has too many players");
}
} else
ctfgame.warnactive = 0;
} else
ctfgame.warnactive = 0;
}
if (capturelimit->value &&
(ctfgame.team1 >= capturelimit->value ||
ctfgame.team2 >= capturelimit->value)) {
safe_bprintf (PRINT_HIGH, "Capturelimit hit.\n");
return true;
}
return false;
}
/*--------------------------------------------------------------------------
* just here to help old map conversions
*--------------------------------------------------------------------------*/
/*static*/ void old_teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
edict_t *dest;
int i;
vec3_t forward;
if (!other->client)
return;
dest = G_Find (NULL, FOFS(targetname), self->target);
if (!dest)
{
gi.dprintf ("Couldn't find destination\n");
return;
}
//ZOID
CTFPlayerResetGrapple(other);
//ZOID
// unlink to make sure it can't possibly interfere with KillBox
gi.unlinkentity (other);
VectorCopy (dest->s.origin, other->s.origin);
VectorCopy (dest->s.origin, other->s.old_origin);
// other->s.origin[2] += 10;
// clear the velocity and hold them in place briefly
VectorClear (other->velocity);
other->client->ps.pmove.pm_time = 160>>3; // hold time
other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
// draw the teleport splash at source and on the player
self->enemy->s.event = EV_PLAYER_TELEPORT;
other->s.event = EV_PLAYER_TELEPORT;
// set angles
for (i=0 ; i<3 ; i++)
other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);
other->s.angles[PITCH] = 0;
other->s.angles[YAW] = dest->s.angles[YAW];
other->s.angles[ROLL] = 0;
VectorCopy (dest->s.angles, other->client->ps.viewangles);
VectorCopy (dest->s.angles, other->client->v_angle);
// give a little forward velocity
AngleVectors (other->client->v_angle, forward, NULL, NULL);
VectorScale(forward, 200, other->velocity);
// kill anything at the destination
if (!KillBox (other))
{
}
gi.linkentity (other);
}
/*QUAKED trigger_teleport (0.5 0.5 0.5) ?
Players touching this will be teleported
*/
void SP_trigger_teleport (edict_t *ent)
{
edict_t *s;
int i;
if (!ent->target)
{
gi.dprintf ("teleporter without a target.\n");
G_FreeEdict (ent);
return;
}
ent->svflags |= SVF_NOCLIENT;
ent->solid = SOLID_TRIGGER;
ent->touch = old_teleporter_touch;
gi.setmodel (ent, ent->model);
gi.linkentity (ent);
// noise maker and splash effect dude
s = G_Spawn();
ent->enemy = s;
for (i = 0; i < 3; i++)
s->s.origin[i] = ent->mins[i] + (ent->maxs[i] - ent->mins[i])/2;
s->s.sound = gi.soundindex ("world/hum1.wav");
gi.linkentity(s);
}
/*QUAKED info_teleport_destination (0.5 0.5 0.5) (-16 -16 -24) (16 16 32)
Point trigger_teleports at these.
*/
void SP_info_teleport_destination (edict_t *ent)
{
ent->s.origin[2] += 16;
}
/*----------------------------------------------------------------------------------*/
/* ADMIN */
typedef struct admin_settings_s {
int matchlen;
int matchsetuplen;
int matchstartlen;
qboolean weaponsstay;
qboolean instantitems;
qboolean quaddrop;
qboolean instantweap;
qboolean matchlock;
} admin_settings_t;
#define SETMENU_SIZE (7 + 5)
void CTFAdmin_UpdateSettings(edict_t *ent, pmenuhnd_t *setmenu);
void CTFOpenAdminMenu(edict_t *ent);
void CTFAdmin_SettingsApply(edict_t *ent, pmenuhnd_t *p)
{
admin_settings_t *settings = p->arg;
char st[80];
int i;
if (settings->matchlen != matchtime->value) {
safe_bprintf(PRINT_HIGH, "%s changed the match length to %d minutes.\n",
ent->client->pers.netname, settings->matchlen);
if (ctfgame.match == MATCH_GAME) {
// in the middle of a match, change it on the fly
ctfgame.matchtime = (ctfgame.matchtime - matchtime->value*60) + settings->matchlen*60;
}
Com_sprintf (st, sizeof(st), "%d", settings->matchlen);
gi.cvar_set("matchtime", st);
}
if (settings->matchsetuplen != matchsetuptime->value) {
safe_bprintf(PRINT_HIGH, "%s changed the match setup time to %d minutes.\n",
ent->client->pers.netname, settings->matchsetuplen);
if (ctfgame.match == MATCH_SETUP) {
// in the middle of a match, change it on the fly
ctfgame.matchtime = (ctfgame.matchtime - matchsetuptime->value*60) + settings->matchsetuplen*60;
}
Com_sprintf (st, sizeof(st), "%d", settings->matchsetuplen);
gi.cvar_set("matchsetuptime", st);
}
if (settings->matchstartlen != matchstarttime->value) {
safe_bprintf(PRINT_HIGH, "%s changed the match start time to %d seconds.\n",
ent->client->pers.netname, settings->matchstartlen);
if (ctfgame.match == MATCH_PREGAME) {
// in the middle of a match, change it on the fly
ctfgame.matchtime = (ctfgame.matchtime - matchstarttime->value) + settings->matchstartlen;
}
Com_sprintf (st, sizeof(st), "%d", settings->matchstartlen);
gi.cvar_set("matchstarttime", st);
}
if (settings->weaponsstay != !!((int)dmflags->value & DF_WEAPONS_STAY)) {
safe_bprintf(PRINT_HIGH, "%s turned %s weapons stay.\n",
ent->client->pers.netname, settings->weaponsstay ? "on" : "off");
i = (int)dmflags->value;
if (settings->weaponsstay)
i |= DF_WEAPONS_STAY;
else
i &= ~DF_WEAPONS_STAY;
Com_sprintf (st, sizeof(st), "%d", i);
gi.cvar_set("dmflags", st);
}
if (settings->instantitems != !!((int)dmflags->value & DF_INSTANT_ITEMS)) {
safe_bprintf(PRINT_HIGH, "%s turned %s instant items.\n",
ent->client->pers.netname, settings->instantitems ? "on" : "off");
i = (int)dmflags->value;
if (settings->instantitems)
i |= DF_INSTANT_ITEMS;
else
i &= ~DF_INSTANT_ITEMS;
Com_sprintf (st, sizeof(st), "%d", i);
gi.cvar_set("dmflags", st);
}
if (settings->quaddrop != !!((int)dmflags->value & DF_QUAD_DROP)) {
safe_bprintf(PRINT_HIGH, "%s turned %s quad drop.\n",
ent->client->pers.netname, settings->quaddrop ? "on" : "off");
i = (int)dmflags->value;
if (settings->quaddrop)
i |= DF_QUAD_DROP;
else
i &= ~DF_QUAD_DROP;
Com_sprintf (st, sizeof(st), "%d", i);
gi.cvar_set("dmflags", st);
}
if (settings->instantweap != !!((int)instantweap->value)) {
safe_bprintf(PRINT_HIGH, "%s turned %s instant weapons.\n",
ent->client->pers.netname, settings->instantweap ? "on" : "off");
Com_sprintf (st, sizeof(st), "%d", (int)settings->instantweap);
gi.cvar_set("instantweap", st);
}
if (settings->matchlock != !!((int)matchlock->value)) {
safe_bprintf(PRINT_HIGH, "%s turned %s match lock.\n",
ent->client->pers.netname, settings->matchlock ? "on" : "off");
Com_sprintf (st, sizeof(st), "%d", (int)settings->matchlock);
gi.cvar_set("matchlock", st);
}
PMenu_Close(ent);
CTFOpenAdminMenu(ent);
}
void CTFAdmin_SettingsCancel(edict_t *ent, pmenuhnd_t *p)
{
admin_settings_t *settings = p->arg;
PMenu_Close(ent);
CTFOpenAdminMenu(ent);
}
void CTFAdmin_ChangeMatchLen(edict_t *ent, pmenuhnd_t *p)
{
admin_settings_t *settings = p->arg;
settings->matchlen = (settings->matchlen % 60) + 5;
if (settings->matchlen < 5)
settings->matchlen = 5;
CTFAdmin_UpdateSettings(ent, p);
}
void CTFAdmin_ChangeMatchSetupLen(edict_t *ent, pmenuhnd_t *p)
{
admin_settings_t *settings = p->arg;
settings->matchsetuplen = (settings->matchsetuplen % 60) + 5;
if (settings->matchsetuplen < 5)
settings->matchsetuplen = 5;
CTFAdmin_UpdateSettings(ent, p);
}
void CTFAdmin_ChangeMatchStartLen(edict_t *ent, pmenuhnd_t *p)
{
admin_settings_t *settings = p->arg;
settings->matchstartlen = (settings->matchstartlen % 600) + 10;
if (settings->matchstartlen < 20)
settings->matchstartlen = 20;
CTFAdmin_UpdateSettings(ent, p);
}
void CTFAdmin_ChangeWeapStay(edict_t *ent, pmenuhnd_t *p)
{
admin_settings_t *settings = p->arg;
settings->weaponsstay = !settings->weaponsstay;
CTFAdmin_UpdateSettings(ent, p);
}
void CTFAdmin_ChangeInstantItems(edict_t *ent, pmenuhnd_t *p)
{
admin_settings_t *settings = p->arg;
settings->instantitems = !settings->instantitems;
CTFAdmin_UpdateSettings(ent, p);
}
void CTFAdmin_ChangeQuadDrop(edict_t *ent, pmenuhnd_t *p)
{
admin_settings_t *settings = p->arg;
settings->quaddrop = !settings->quaddrop;
CTFAdmin_UpdateSettings(ent, p);
}
void CTFAdmin_ChangeInstantWeap(edict_t *ent, pmenuhnd_t *p)
{
admin_settings_t *settings = p->arg;
settings->instantweap = !settings->instantweap;
CTFAdmin_UpdateSettings(ent, p);
}
void CTFAdmin_ChangeMatchLock(edict_t *ent, pmenuhnd_t *p)
{
admin_settings_t *settings = p->arg;
settings->matchlock = !settings->matchlock;
CTFAdmin_UpdateSettings(ent, p);
}
void CTFAdmin_UpdateSettings(edict_t *ent, pmenuhnd_t *setmenu)
{
int i = 2;
char text[64];
admin_settings_t *settings = setmenu->arg;
Com_sprintf (text, sizeof(text), "Match Len: %2d mins", settings->matchlen);
PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeMatchLen);
i++;
Com_sprintf (text, sizeof(text), "Match Setup Len: %2d mins", settings->matchsetuplen);
PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeMatchSetupLen);
i++;
Com_sprintf (text, sizeof(text), "Match Start Len: %2d secs", settings->matchstartlen);
PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeMatchStartLen);
i++;
Com_sprintf (text, sizeof(text), "Weapons Stay: %s", settings->weaponsstay ? "Yes" : "No");
PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeWeapStay);
i++;
Com_sprintf (text, sizeof(text), "Instant Items: %s", settings->instantitems ? "Yes" : "No");
PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeInstantItems);
i++;
Com_sprintf (text, sizeof(text), "Quad Drop: %s", settings->quaddrop ? "Yes" : "No");
PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeQuadDrop);
i++;
Com_sprintf (text, sizeof(text), "Instant Weapons: %s", settings->instantweap ? "Yes" : "No");
PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeInstantWeap);
i++;
Com_sprintf (text, sizeof(text), "Match Lock: %s", settings->matchlock ? "Yes" : "No");
PMenu_UpdateEntry(setmenu->entries + i, text, PMENU_ALIGN_LEFT, CTFAdmin_ChangeMatchLock);
i++;
PMenu_Update(ent);
}
pmenu_t def_setmenu[] = {
{ "*Settings Menu", PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_LEFT, NULL }, //int matchlen;
{ NULL, PMENU_ALIGN_LEFT, NULL }, //int matchsetuplen;
{ NULL, PMENU_ALIGN_LEFT, NULL }, //int matchstartlen;
{ NULL, PMENU_ALIGN_LEFT, NULL }, //qboolean weaponsstay;
{ NULL, PMENU_ALIGN_LEFT, NULL }, //qboolean instantitems;
{ NULL, PMENU_ALIGN_LEFT, NULL }, //qboolean quaddrop;
{ NULL, PMENU_ALIGN_LEFT, NULL }, //qboolean instantweap;
{ NULL, PMENU_ALIGN_LEFT, NULL }, //qboolean matchlock;
{ NULL, PMENU_ALIGN_LEFT, NULL },
{ "Apply", PMENU_ALIGN_LEFT, CTFAdmin_SettingsApply },
{ "Cancel", PMENU_ALIGN_LEFT, CTFAdmin_SettingsCancel }
};
void CTFAdmin_Settings(edict_t *ent, pmenuhnd_t *p)
{
admin_settings_t *settings;
pmenuhnd_t *menu;
PMenu_Close(ent);
// settings = malloc(sizeof(*settings));
settings = gi.TagMalloc(sizeof(*settings), TAG_LEVEL);
settings->matchlen = matchtime->value;
settings->matchsetuplen = matchsetuptime->value;
settings->matchstartlen = matchstarttime->value;
settings->weaponsstay = !!((int)dmflags->value & DF_WEAPONS_STAY);
settings->instantitems = !!((int)dmflags->value & DF_INSTANT_ITEMS);
settings->quaddrop = !!((int)dmflags->value & DF_QUAD_DROP);
settings->instantweap = instantweap->value != 0;
settings->matchlock = matchlock->value != 0;
menu = PMenu_Open(ent, def_setmenu, -1, sizeof(def_setmenu) / sizeof(pmenu_t), settings);
CTFAdmin_UpdateSettings(ent, menu);
}
void CTFAdmin_MatchSet(edict_t *ent, pmenuhnd_t *p)
{
PMenu_Close(ent);
if (ctfgame.match == MATCH_SETUP) {
safe_bprintf(PRINT_CHAT, "Match has been forced to start.\n");
ctfgame.match = MATCH_PREGAME;
ctfgame.matchtime = level.time + matchstarttime->value;
gi.positioned_sound (world->s.origin, world, CHAN_AUTO | CHAN_RELIABLE, gi.soundindex("misc/talk1.wav"), 1, ATTN_NONE, 0);
ctfgame.countdown = false;
}
else if (ctfgame.match == MATCH_GAME) {
safe_bprintf(PRINT_CHAT, "Match has been forced to terminate.\n");
ctfgame.match = MATCH_SETUP;
ctfgame.matchtime = level.time + matchsetuptime->value * 60;
CTFResetAllPlayers();
}
}
void CTFAdmin_MatchMode(edict_t *ent, pmenuhnd_t *p)
{
PMenu_Close(ent);
if (ctfgame.match != MATCH_SETUP) {
if (competition->value < 3)
gi.cvar_set("competition", "2");
ctfgame.match = MATCH_SETUP;
CTFResetAllPlayers();
}
}
void CTFAdmin_Reset(edict_t *ent, pmenuhnd_t *p)
{
PMenu_Close(ent);
// go back to normal mode
safe_bprintf(PRINT_CHAT, "Match mode has been terminated, reseting to normal game.\n");
ctfgame.match = MATCH_NONE;
gi.cvar_set("competition", "1");
CTFResetAllPlayers();
}
void CTFAdmin_Cancel(edict_t *ent, pmenuhnd_t *p)
{
PMenu_Close(ent);
}
pmenu_t adminmenu[] = {
{ "*Administration Menu", PMENU_ALIGN_CENTER, NULL },
{ NULL, PMENU_ALIGN_CENTER, NULL }, // blank
{ "Settings", PMENU_ALIGN_LEFT, CTFAdmin_Settings },
{ NULL, PMENU_ALIGN_LEFT, NULL },
{ NULL, PMENU_ALIGN_LEFT, NULL },
{ "Cancel", PMENU_ALIGN_LEFT, CTFAdmin_Cancel },
{ NULL, PMENU_ALIGN_CENTER, NULL },
};
void CTFOpenAdminMenu(edict_t *ent)
{
adminmenu[3].text = NULL;
adminmenu[3].SelectFunc = NULL;
adminmenu[4].text = NULL;
adminmenu[4].SelectFunc = NULL;
if (ctfgame.match == MATCH_SETUP) {
adminmenu[3].text = "Force start match";
adminmenu[3].SelectFunc = CTFAdmin_MatchSet;
adminmenu[4].text = "Reset to pickup mode";
adminmenu[4].SelectFunc = CTFAdmin_Reset;
} else if (ctfgame.match == MATCH_GAME || ctfgame.match == MATCH_PREGAME) {
adminmenu[3].text = "Cancel match";
adminmenu[3].SelectFunc = CTFAdmin_MatchSet;
} else if (ctfgame.match == MATCH_NONE && competition->value) {
adminmenu[3].text = "Switch to match mode";
adminmenu[3].SelectFunc = CTFAdmin_MatchMode;
}
// if (ent->client->menu)
// PMenu_Close(ent->client->menu);
PMenu_Open(ent, adminmenu, -1, sizeof(adminmenu) / sizeof(pmenu_t), NULL);
}
void CTFAdmin(edict_t *ent)
{
char text[1024];
if (!allow_admin->value) {
safe_cprintf(ent, PRINT_HIGH, "Administration is disabled\n");
return;
}
if (gi.argc() > 1 && admin_password->string && *admin_password->string &&
!ent->client->resp.admin && strcmp(admin_password->string, gi.argv(1)) == 0) {
ent->client->resp.admin = true;
safe_bprintf(PRINT_HIGH, "%s has become an admin.\n", ent->client->pers.netname);
safe_cprintf(ent, PRINT_HIGH, "Type 'admin' to access the adminstration menu.\n");
}
if (!ent->client->resp.admin) {
Com_sprintf (text, sizeof(text), "%s has requested admin rights.",
ent->client->pers.netname);
CTFBeginElection(ent, ELECT_ADMIN, text);
return;
}
if (ent->client->menu)
PMenu_Close(ent);
CTFOpenAdminMenu(ent);
}
/*----------------------------------------------------------------*/
void CTFStats(edict_t *ent)
{
int i, e;
ghost_t *g;
char st[80];
char text[1024];
char tmp[128]; // Knightmare added
edict_t *e2;
*text = 0;
if (ctfgame.match == MATCH_SETUP)
{
for (i = 1; i <= maxclients->value; i++)
{
e2 = g_edicts + i;
if (!e2->inuse)
continue;
if (!e2->client->resp.ready && e2->client->resp.ctf_team != CTF_NOTEAM)
{
Com_sprintf (st, sizeof(st), "%s is not ready.\n", e2->client->pers.netname);
if (strlen(text) + strlen(st) < sizeof(text) - 50)
// strncat(text, st);
Q_strncatz(text, sizeof(text), st);
}
}
}
for (i = 0, g = ctfgame.ghosts; i < MAX_CLIENTS; i++, g++)
if (g->ent)
break;
if (i == MAX_CLIENTS)
{
if (*text)
safe_cprintf(ent, PRINT_HIGH, "%s", text);
safe_cprintf(ent, PRINT_HIGH, "No statistics available.\n");
return;
}
// strncat(text, " #|Name |Score|Kills|Death|BasDf|CarDf|Effcy|\n");
Q_strncatz(text, sizeof(text), " #|Name |Score|Kills|Death|BasDf|CarDf|Effcy|\n");
for (i = 0, g = ctfgame.ghosts; i < MAX_CLIENTS; i++, g++)
{
if (!*g->netname)
continue;
if (g->deaths + g->kills == 0)
e = 50;
else
e = g->kills * 100 / (g->kills + g->deaths);
Com_sprintf (st, sizeof(st), "%3d|%-16.16s|%5d|%5d|%5d|%5d|%5d|%4d%%|\n",
g->number,
g->netname,
g->score,
g->kills,
g->deaths,
g->basedef,
g->carrierdef,
e);
if (strlen(text) + strlen(st) > sizeof(text) - 50)
{
// sprintf(text+strlen(text), "And more...\n");
Com_sprintf (tmp, sizeof(tmp), "And more...\n");
Q_strncatz (text, sizeof(text), tmp);
safe_cprintf(ent, PRINT_HIGH, "%s", text);
return;
}
// strncat(text, st);
Q_strncatz(text, sizeof(text), st);
}
safe_cprintf(ent, PRINT_HIGH, "%s", text);
}
void CTFPlayerList(edict_t *ent)
{
int i;
char st[80];
char text[1400];
char tmp[128]; // Knightmare added
edict_t *e2;
#if 0
*text = 0;
if (ctfgame.match == MATCH_SETUP)
{
for (i = 1; i <= maxclients->value; i++)
{
e2 = g_edicts + i;
if (!e2->inuse)
continue;
if (!e2->client->resp.ready && e2->client->resp.ctf_team != CTF_NOTEAM) {
Com_sprintf (st, sizeof(st), "%s is not ready.\n", e2->client->pers.netname);
if (strlen(text) + strlen(st) < sizeof(text) - 50)
// strncat(text, st);
Q_strncatz (text, sizeof(text), st);
}
}
}
#endif
// number, name, connect time, ping, score, admin
*text = 0;
for (i = 1; i <= maxclients->value; i++) {
e2 = g_edicts + i;
if (!e2->inuse)
continue;
Com_sprintf (st, sizeof(st), "%3d %-16.16s %02d:%02d %4d %3d%s%s\n",
i,
e2->client->pers.netname,
(level.framenum - e2->client->resp.enterframe) / 600,
((level.framenum - e2->client->resp.enterframe) % 600)/10,
e2->client->ping,
e2->client->resp.score,
(ctfgame.match == MATCH_SETUP || ctfgame.match == MATCH_PREGAME) ?
(e2->client->resp.ready ? " (ready)" : " (notready)") : "",
e2->client->resp.admin ? " (admin)" : "");
if (strlen(text) + strlen(st) > sizeof(text) - 50) {
// sprintf(text+strlen(text), "And more...\n");
Com_sprintf (tmp, sizeof(tmp), "And more...\n");
Q_strncatz (text, sizeof(text), tmp);
safe_cprintf(ent, PRINT_HIGH, "%s", text);
return;
}
// strncat(text, st);
Q_strncatz(text, sizeof(text), st);
}
safe_cprintf(ent, PRINT_HIGH, "%s", text);
}
void CTFWarp(edict_t *ent)
{
char text[1024];
char *mlist, *token;
static const char *seps = " \t\n\r";
if (gi.argc() < 2) {
safe_cprintf(ent, PRINT_HIGH, "Where do you want to warp to?\n");
safe_cprintf(ent, PRINT_HIGH, "Available levels are: %s\n", warp_list->string);
return;
}
mlist = strdup(warp_list->string);
token = strtok(mlist, seps);
while (token != NULL) {
if (Q_stricmp(token, gi.argv(1)) == 0)
break;
token = strtok(NULL, seps);
}
if (token == NULL) {
safe_cprintf(ent, PRINT_HIGH, "Unknown CTF level.\n");
safe_cprintf(ent, PRINT_HIGH, "Available levels are: %s\n", warp_list->string);
free(mlist);
return;
}
free(mlist);
if (ent->client->resp.admin) {
safe_bprintf(PRINT_HIGH, "%s is warping to level %s.\n",
ent->client->pers.netname, gi.argv(1));
// strncpy(level.forcemap, gi.argv(1), sizeof(level.forcemap) - 1);
Q_strncpyz(level.forcemap, sizeof(level.forcemap), gi.argv(1));
EndDMLevel();
return;
}
Com_sprintf (text, sizeof(text), "%s has requested warping to level %s.",
ent->client->pers.netname, gi.argv(1));
if (CTFBeginElection(ent, ELECT_MAP, text))
// strncpy(ctfgame.elevel, gi.argv(1), sizeof(ctfgame.elevel) - 1);
Q_strncpyz(ctfgame.elevel, sizeof(ctfgame.elevel), gi.argv(1));
}
void CTFBoot(edict_t *ent)
{
int i;
edict_t *targ;
char text[80];
if (!ent->client->resp.admin) {
safe_cprintf(ent, PRINT_HIGH, "You are not an admin.\n");
return;
}
if (gi.argc() < 2) {
safe_cprintf(ent, PRINT_HIGH, "Who do you want to kick?\n");
return;
}
if (*gi.argv(1) < '0' && *gi.argv(1) > '9') {
safe_cprintf(ent, PRINT_HIGH, "Specify the player number to kick.\n");
return;
}
i = atoi(gi.argv(1));
if (i < 1 || i > maxclients->value) {
safe_cprintf(ent, PRINT_HIGH, "Invalid player number.\n");
return;
}
targ = g_edicts + i;
if (!targ->inuse) {
safe_cprintf(ent, PRINT_HIGH, "That player number is not connected.\n");
return;
}
Com_sprintf (text, sizeof(text), "kick %d\n", i - 1);
gi.AddCommandString(text);
}
void CTFSetPowerUpEffect(edict_t *ent, int def)
{
if (ent->client->resp.ctf_team == CTF_TEAM1)
ent->s.effects |= EF_PENT; // red
else if (ent->client->resp.ctf_team == CTF_TEAM2)
ent->s.effects |= EF_QUAD; // red
else
ent->s.effects |= def;
}