📄 g_ctf.c
字号:
/*
Copyright (C) 1997-2001 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "g_local.h"
#include "m_player.h"
typedef enum match_s {
MATCH_NONE,
MATCH_SETUP,
MATCH_PREGAME,
MATCH_GAME,
MATCH_POST
} match_t;
typedef enum {
ELECT_NONE,
ELECT_MATCH,
ELECT_ADMIN,
ELECT_MAP
} elect_t;
typedef struct ctfgame_s
{
int team1, team2;
int total1, total2; // these are only set when going into intermission!
float last_flag_capture;
int last_capture_team;
match_t match; // match state
float matchtime; // time for match start/end (depends on state)
int lasttime; // last time update
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 *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;
char *ctf_statusbar =
"yb -24 "
// health
"xv 0 "
"hnum "
"xv 50 "
"pic 0 "
// ammo
"if 2 "
" xv 100 "
" anum "
" xv 150 "
" pic 2 "
"endif "
// armor
"if 4 "
" xv 200 "
" rnum "
" xv 250 "
" pic 4 "
"endif "
// selected item
"if 6 "
" xv 296 "
" pic 6 "
"endif "
"yb -50 "
// picked up item
"if 7 "
" xv 0 "
" pic 7 "
" xv 26 "
" yb -42 "
" stat_string 8 "
" yb -50 "
"endif "
// timer
"if 9 "
"xv 246 "
"num 2 10 "
"xv 296 "
"pic 9 "
"endif "
// help / weapon icon
"if 11 "
"xv 148 "
"pic 11 "
"endif "
// frags
"xr -50 "
"yt 2 "
"num 3 14 "
//tech
"yb -129 "
"if 26 "
"xr -26 "
"pic 26 "
"endif "
// red team
"yb -102 "
"if 17 "
"xr -26 "
"pic 17 "
"endif "
"xr -62 "
"num 2 18 "
//joined overlay
"if 22 "
"yb -104 "
"xr -28 "
"pic 22 "
"endif "
// blue team
"yb -75 "
"if 19 "
"xr -26 "
"pic 19 "
"endif "
"xr -62 "
"num 2 20 "
"if 23 "
"yb -77 "
"xr -28 "
"pic 23 "
"endif "
// have flag graph
"if 21 "
"yt 26 "
"xr -24 "
"pic 21 "
"endif "
// id view state
"if 27 "
"xv 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 "
;
static char *tnames[] = {
"item_tech1", "item_tech2", "item_tech3", "item_tech4",
NULL
};
void stuffcmd(edict_t *ent, char *s)
{
gi.WriteByte (11);
gi.WriteString (s);
gi.unicast (ent, true);
}
/*--------------------------------------------------------------------------*/
/*
=================
findradius
Returns entities that have origins within a spherical area
findradius (origin, radius)
=================
*/
static edict_t *loc_findradius (edict_t *from, vec3_t org, float rad)
{
vec3_t eorg;
int j;
if (!from)
from = g_edicts;
else
from++;
for ( ; from < &g_edicts[globals.num_edicts]; from++)
{
if (!from->inuse)
continue;
#if 0
if (from->solid == SOLID_NOT)
continue;
#endif
for (j=0 ; j<3 ; j++)
eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
if (VectorLength(eorg) > rad)
continue;
return from;
}
return NULL;
}
static void loc_buildboxpoints(vec3_t p[8], vec3_t org, vec3_t mins, vec3_t maxs)
{
VectorAdd(org, mins, p[0]);
VectorCopy(p[0], p[1]);
p[1][0] -= mins[0];
VectorCopy(p[0], p[2]);
p[2][1] -= mins[1];
VectorCopy(p[0], p[3]);
p[3][0] -= mins[0];
p[3][1] -= mins[1];
VectorAdd(org, maxs, p[4]);
VectorCopy(p[4], p[5]);
p[5][0] -= maxs[0];
VectorCopy(p[0], p[6]);
p[6][1] -= maxs[1];
VectorCopy(p[0], p[7]);
p[7][0] -= maxs[0];
p[7][1] -= maxs[1];
}
static qboolean loc_CanSee (edict_t *targ, edict_t *inflictor)
{
trace_t trace;
vec3_t targpoints[8];
int i;
vec3_t viewpoint;
// bmodels need special checking because their origin is 0,0,0
if (targ->movetype == MOVETYPE_PUSH)
return false; // bmodels not supported
loc_buildboxpoints(targpoints, targ->s.origin, targ->mins, targ->maxs);
VectorCopy(inflictor->s.origin, viewpoint);
viewpoint[2] += inflictor->viewheight;
for (i = 0; i < 8; i++) {
trace = gi.trace (viewpoint, vec3_origin, vec3_origin, targpoints[i], inflictor, MASK_SOLID);
if (trace.fraction == 1.0)
return true;
}
return false;
}
/*--------------------------------------------------------------------------*/
static gitem_t *flag1_item;
static gitem_t *flag2_item;
void CTFSpawn(void)
{
if (!flag1_item)
flag1_item = FindItemByClassname("item_flag_team1");
if (!flag2_item)
flag2_item = FindItemByClassname("item_flag_team2");
memset(&ctfgame, 0, sizeof(ctfgame));
CTFSetupTechSpawn();
if (competition->value > 1) {
ctfgame.match = MATCH_SETUP;
ctfgame.matchtime = level.time + matchsetuptime->value * 60;
}
}
void CTFInit(void)
{
ctf = gi.cvar("ctf", "1", CVAR_SERVERINFO);
ctf_forcejoin = gi.cvar("ctf_forcejoin", "", 0);
competition = gi.cvar("competition", "0", CVAR_SERVERINFO);
matchlock = gi.cvar("matchlock", "1", CVAR_SERVERINFO);
electpercentage = gi.cvar("electpercentage", "66", 0);
matchtime = gi.cvar("matchtime", "20", CVAR_SERVERINFO);
matchsetuptime = gi.cvar("matchsetuptime", "10", 0);
matchstarttime = gi.cvar("matchstarttime", "20", 0);
admin_password = gi.cvar("admin_password", "", 0);
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)
{
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";
}
return "UNKNOWN"; // Hanzo pointed out this was spelled wrong as "UKNOWN"
}
char *CTFOtherTeamName(int team)
{
switch (team) {
case CTF_TEAM1:
return "BLUE";
case CTF_TEAM2:
return "RED";
}
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;
}
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
strcpy(t, "male/");
switch (ent->client->resp.ctf_team) {
case CTF_TEAM1:
gi.configstring (CS_PLAYERSKINS+playernum, va("%s\\%s%s",
ent->client->pers.netname, t, CTF_TEAM1_SKIN) );
break;
case CTF_TEAM2:
gi.configstring (CS_PLAYERSKINS+playernum,
va("%s\\%s%s", ent->client->pers.netname, t, CTF_TEAM2_SKIN) );
break;
default:
gi.configstring (CS_PLAYERSKINS+playernum,
va("%s\\%s", ent->client->pers.netname, s) );
break;
}
// gi.cprintf(ent, PRINT_HIGH, "You have been assigned to %s team.\n", ent->client->pers.netname);
}
void CTFAssignTeam(gclient_t *who)
{
edict_t *player;
int i;
int team1count = 0, team2count = 0;
who->resp.ctf_state = 0;
if (!((int)dmflags->value & DF_CTF_FORCEJOIN)) {
who->resp.ctf_team = CTF_NOTEAM;
return;
}
for (i = 1; i <= maxclients->value; i++) {
player = &g_edicts[i];
if (!player->inuse || player->client == who)
continue;
switch (player->client->resp.ctf_team) {
case CTF_TEAM1:
team1count++;
break;
case CTF_TEAM2:
team2count++;
}
}
if (team1count < team2count)
who->resp.ctf_team = CTF_TEAM1;
else if (team2count < team1count)
who->resp.ctf_team = CTF_TEAM2;
else if (rand() & 1)
who->resp.ctf_team = CTF_TEAM1;
else
who->resp.ctf_team = CTF_TEAM2;
}
/*
================
SelectCTFSpawnPoint
go to a ctf point, but NOT the two points closest
to other players
================
*/
edict_t *SelectCTFSpawnPoint (edict_t *ent)
{
edict_t *spot, *spot1, *spot2;
int count = 0;
int selection;
float range, range1, range2;
char *cname;
if (ent->client->resp.ctf_state)
if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST)
return SelectFarthestDeathmatchSpawnPoint ();
else
return SelectRandomDeathmatchSpawnPoint ();
ent->client->resp.ctf_state++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -