mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
95295401a4
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.
3805 lines
No EOL
96 KiB
C
3805 lines
No EOL
96 KiB
C
#include "g_local.h"
|
||
#include "bot.h"
|
||
|
||
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;
|
||
} ctfgame_t;
|
||
//PON
|
||
qboolean bots_moveok ( edict_t *ent,float ryaw,vec3_t pos,float dist,float *bottom);
|
||
//PON
|
||
ctfgame_t ctfgame;
|
||
qboolean techs_spawned = false;
|
||
|
||
cvar_t *ctf;
|
||
cvar_t *ctf_forcejoin;
|
||
|
||
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 "
|
||
// 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 0 "
|
||
"yb -58 "
|
||
"string \"Viewing\" "
|
||
"xv 64 "
|
||
"stat_string 27 "
|
||
"endif "
|
||
|
||
//sight
|
||
"if 31 "
|
||
" xv 96 "
|
||
" yv 56 "
|
||
" 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)
|
||
{
|
||
if (ent->svflags & SVF_MONSTER) return;
|
||
|
||
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 CTFInit (void)
|
||
{
|
||
ctf = gi.cvar("ctf", "0", CVAR_SERVERINFO);
|
||
ctf_forcejoin = gi.cvar("ctf_forcejoin", "", 0);
|
||
|
||
if (!flag1_item)
|
||
flag1_item = FindItemByClassname("item_flag_team1");
|
||
if (!flag2_item)
|
||
flag2_item = FindItemByClassname("item_flag_team2");
|
||
memset(&ctfgame, 0, sizeof(ctfgame));
|
||
techs_spawned = false;
|
||
}
|
||
|
||
/*--------------------------------------------------------------------------*/
|
||
|
||
char *CTFTeamName (int team)
|
||
{
|
||
switch (team) {
|
||
case CTF_TEAM1:
|
||
return "RED";
|
||
case CTF_TEAM2:
|
||
return "BLUE";
|
||
// case CTF_TEAM3:
|
||
// return "GREEN"; // Knightmare added
|
||
}
|
||
return "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 "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 = strrchr(t, '/')) != NULL)
|
||
p[1] = 0;
|
||
else
|
||
Com_strcpy (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;
|
||
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 = CTF_STATE_START;
|
||
|
||
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 < team1count)
|
||
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 != CTF_STATE_START)
|
||
if ( (int)(dmflags->value) & DF_SPAWN_FARTHEST)
|
||
return SelectFarthestDeathmatchSpawnPoint ();
|
||
else
|
||
return SelectRandomDeathmatchSpawnPoint ();
|
||
|
||
ent->client->resp.ctf_state = CTF_STATE_PLAYING;
|
||
|
||
switch (ent->client->resp.ctf_team) {
|
||
case CTF_TEAM1:
|
||
cname = "info_player_team1";
|
||
break;
|
||
case CTF_TEAM2:
|
||
cname = "info_player_team2";
|
||
break;
|
||
default:
|
||
return SelectRandomDeathmatchSpawnPoint();
|
||
}
|
||
|
||
spot = NULL;
|
||
range1 = range2 = 99999;
|
||
spot1 = spot2 = NULL;
|
||
|
||
while ((spot = G_Find (spot, FOFS(classname), cname)) != NULL)
|
||
{
|
||
count++;
|
||
range = PlayersRangeFromSpot(spot);
|
||
if (range < range1)
|
||
{
|
||
range1 = range;
|
||
spot1 = spot;
|
||
}
|
||
else if (range < range2)
|
||
{
|
||
range2 = range;
|
||
spot2 = spot;
|
||
}
|
||
}
|
||
|
||
if (!count)
|
||
return SelectRandomDeathmatchSpawnPoint();
|
||
|
||
if (count <= 2)
|
||
{
|
||
spot1 = spot2 = NULL;
|
||
}
|
||
else
|
||
count -= 2;
|
||
|
||
selection = rand() % count;
|
||
|
||
spot = NULL;
|
||
do
|
||
{
|
||
spot = G_Find (spot, FOFS(classname), cname);
|
||
if (spot == spot1 || spot == spot2)
|
||
selection++;
|
||
} while (selection--);
|
||
|
||
return spot;
|
||
}
|
||
|
||
/*------------------------------------------------------------------------*/
|
||
/*
|
||
CTFFragBonuses
|
||
|
||
Calculate the bonuses for flag defense, flag carrier defense, etc.
|
||
Note that bonuses are not cumaltive. You get one, they are in importance
|
||
order.
|
||
*/
|
||
void CTFFragBonuses(edict_t *targ, edict_t *inflictor, edict_t *attacker)
|
||
{
|
||
int i;
|
||
edict_t *ent;
|
||
gitem_t *flag_item, *enemy_flag_item;
|
||
int otherteam;
|
||
edict_t *flag, *carrier;
|
||
char *c;
|
||
vec3_t v1, v2;
|
||
|
||
// no bonus for fragging yourself
|
||
if (!targ->client || !attacker->client || targ == attacker)
|
||
return;
|
||
|
||
otherteam = CTFOtherTeam(targ->client->resp.ctf_team);
|
||
if (otherteam < 0)
|
||
return; // whoever died isn't on a team
|
||
|
||
// same team, if the flag at base, check to he has the enemy flag
|
||
if (targ->client->resp.ctf_team == CTF_TEAM1) {
|
||
flag_item = flag1_item;
|
||
enemy_flag_item = flag2_item;
|
||
} else {
|
||
flag_item = flag2_item;
|
||
enemy_flag_item = flag1_item;
|
||
}
|
||
|
||
// did the attacker frag the flag carrier?
|
||
if (targ->client->pers.inventory[ITEM_INDEX(enemy_flag_item)]) {
|
||
attacker->client->resp.ctf_lastfraggedcarrier = level.time;
|
||
attacker->client->resp.score += CTF_FRAG_CARRIER_BONUS;
|
||
if (!(attacker->svflags & SVF_MONSTER))
|
||
gi.cprintf(attacker, PRINT_MEDIUM, "BONUS: %d points for fragging enemy flag carrier.\n",
|
||
CTF_FRAG_CARRIER_BONUS);
|
||
|
||
// the the target had the flag, clear the hurt carrier
|
||
// field on the other team
|
||
for (i = 1; i <= maxclients->value; i++) {
|
||
ent = g_edicts + i;
|
||
if (ent->inuse && ent->client->resp.ctf_team == otherteam)
|
||
ent->client->resp.ctf_lasthurtcarrier = 0;
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (targ->client->resp.ctf_lasthurtcarrier &&
|
||
level.time - targ->client->resp.ctf_lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
|
||
!attacker->client->pers.inventory[ITEM_INDEX(flag_item)]) {
|
||
// attacker is on the same team as the flag carrier and
|
||
// fragged a guy who hurt our flag carrier
|
||
attacker->client->resp.score += CTF_CARRIER_DANGER_PROTECT_BONUS;
|
||
gi.bprintf(PRINT_MEDIUM, "%s defends %s's flag carrier against an agressive enemy\n",
|
||
attacker->client->pers.netname,
|
||
CTFTeamName(attacker->client->resp.ctf_team));
|
||
return;
|
||
}
|
||
|
||
// flag and flag carrier area defense bonuses
|
||
|
||
// we have to find the flag and carrier entities
|
||
|
||
// find the flag
|
||
switch (attacker->client->resp.ctf_team) {
|
||
case CTF_TEAM1:
|
||
c = "item_flag_team1";
|
||
break;
|
||
case CTF_TEAM2:
|
||
c = "item_flag_team2";
|
||
break;
|
||
default:
|
||
return;
|
||
}
|
||
|
||
flag = NULL;
|
||
while ((flag = G_Find (flag, FOFS(classname), c)) != NULL) {
|
||
if (!(flag->spawnflags & DROPPED_ITEM))
|
||
break;
|
||
}
|
||
|
||
if (!flag)
|
||
return; // can't find attacker's flag
|
||
|
||
//PONKO
|
||
if (attacker)
|
||
{
|
||
VectorSubtract(targ->s.origin,attacker->s.origin,v1);
|
||
if (VectorLength(v1) < 300
|
||
&& attacker->client && !(attacker->deadflag) && (attacker->svflags & SVF_MONSTER))
|
||
{
|
||
attacker->client->zc.second_target = flag;
|
||
}
|
||
}
|
||
//PONKO
|
||
// 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)) {
|
||
// we defended the base flag
|
||
attacker->client->resp.score += CTF_FLAG_DEFENSE_BONUS;
|
||
if (flag->solid == SOLID_NOT)
|
||
gi.bprintf(PRINT_MEDIUM, "%s defends the %s base.\n",
|
||
attacker->client->pers.netname,
|
||
CTFTeamName(attacker->client->resp.ctf_team));
|
||
else
|
||
gi.bprintf(PRINT_MEDIUM, "%s defends the %s flag.\n",
|
||
attacker->client->pers.netname,
|
||
CTFTeamName(attacker->client->resp.ctf_team));
|
||
return;
|
||
}
|
||
|
||
if (carrier && carrier != attacker) {
|
||
VectorSubtract(targ->s.origin, carrier->s.origin, v1);
|
||
VectorSubtract(attacker->s.origin, carrier->s.origin, v1);
|
||
|
||
if (VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS ||
|
||
VectorLength(v2) < CTF_ATTACKER_PROTECT_RADIUS ||
|
||
loc_CanSee(carrier, targ) || loc_CanSee(carrier, attacker)) {
|
||
attacker->client->resp.score += CTF_CARRIER_PROTECT_BONUS;
|
||
gi.bprintf(PRINT_MEDIUM, "%s defends the %s's flag carrier.\n",
|
||
attacker->client->pers.netname,
|
||
CTFTeamName(attacker->client->resp.ctf_team));
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
void CTFCheckHurtCarrier(edict_t *targ, edict_t *attacker)
|
||
{
|
||
gitem_t *flag_item;
|
||
|
||
if (!targ->client || !attacker->client)
|
||
return;
|
||
|
||
if (targ->client->resp.ctf_team == CTF_TEAM1)
|
||
flag_item = flag2_item;
|
||
else
|
||
flag_item = flag1_item;
|
||
|
||
if (targ->client->pers.inventory[ITEM_INDEX(flag_item)] &&
|
||
targ->client->resp.ctf_team != attacker->client->resp.ctf_team)
|
||
attacker->client->resp.ctf_lasthurtcarrier = level.time;
|
||
}
|
||
|
||
|
||
/*------------------------------------------------------------------------*/
|
||
|
||
void CTFResetFlag(int ctf_team)
|
||
{
|
||
char *c;
|
||
edict_t *ent;
|
||
|
||
switch (ctf_team) {
|
||
case CTF_TEAM1:
|
||
c = "item_flag_team1";
|
||
break;
|
||
case CTF_TEAM2:
|
||
c = "item_flag_team2";
|
||
break;
|
||
default:
|
||
return;
|
||
}
|
||
|
||
ent = NULL;
|
||
while ((ent = G_Find (ent, FOFS(classname), c)) != NULL) {
|
||
if (ent->spawnflags & DROPPED_ITEM)
|
||
G_FreeEdict(ent);
|
||
else {
|
||
ent->svflags &= ~SVF_NOCLIENT;
|
||
ent->solid = SOLID_TRIGGER;
|
||
gi.linkentity(ent);
|
||
ent->s.event = EV_ITEM_RESPAWN;
|
||
}
|
||
}
|
||
}
|
||
|
||
void CTFResetFlags(void)
|
||
{
|
||
CTFResetFlag(CTF_TEAM1);
|
||
CTFResetFlag(CTF_TEAM2);
|
||
}
|
||
|
||
qboolean CTFPickup_Flag(edict_t *ent, edict_t *other)
|
||
{
|
||
int ctf_team;
|
||
int i;
|
||
edict_t *player;
|
||
gitem_t *flag_item, *enemy_flag_item;
|
||
|
||
|
||
if (chedit->value) {SetRespawn (ent, 30); return true;};
|
||
|
||
|
||
// figure out what team this flag is
|
||
if (strcmp(ent->classname, "item_flag_team1") == 0)
|
||
ctf_team = CTF_TEAM1;
|
||
else if (strcmp(ent->classname, "item_flag_team2") == 0)
|
||
ctf_team = CTF_TEAM2;
|
||
else {
|
||
if (!(ent->svflags & SVF_MONSTER))
|
||
gi.cprintf(ent, PRINT_HIGH, "Don't know what team the flag is on.\n");
|
||
return false;
|
||
}
|
||
|
||
// same team, if the flag at base, check to he has the enemy flag
|
||
if (ctf_team == CTF_TEAM1) {
|
||
flag_item = flag1_item;
|
||
enemy_flag_item = flag2_item;
|
||
} else {
|
||
flag_item = flag2_item;
|
||
enemy_flag_item = flag1_item;
|
||
}
|
||
|
||
if (ctf_team == other->client->resp.ctf_team) {
|
||
|
||
if (!(ent->spawnflags & DROPPED_ITEM)) {
|
||
// the flag is at home base. if the player has the enemy
|
||
// flag, he's just won!
|
||
|
||
if (other->client->pers.inventory[ITEM_INDEX(enemy_flag_item)]) {
|
||
gi.bprintf(PRINT_HIGH, "%s captured the %s flag!\n",
|
||
other->client->pers.netname, CTFOtherTeamName(ctf_team));
|
||
other->client->pers.inventory[ITEM_INDEX(enemy_flag_item)] = 0;
|
||
|
||
ctfgame.last_flag_capture = level.time;
|
||
ctfgame.last_capture_team = ctf_team;
|
||
if (ctf_team == CTF_TEAM1)
|
||
ctfgame.team1++;
|
||
else
|
||
ctfgame.team2++;
|
||
|
||
gi.sound (ent, CHAN_RELIABLE+CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex("ctf/flagcap.wav"), 1, ATTN_NONE, 0);
|
||
|
||
// other gets another 10 frag bonus
|
||
other->client->resp.score += CTF_CAPTURE_BONUS;
|
||
|
||
// Ok, let's do the player loop, hand out the bonuses
|
||
for (i = 1; i <= maxclients->value; i++) {
|
||
player = &g_edicts[i];
|
||
if (!player->inuse)
|
||
continue;
|
||
|
||
if (player->client->resp.ctf_team != other->client->resp.ctf_team)
|
||
player->client->resp.ctf_lasthurtcarrier = -5;
|
||
else if (player->client->resp.ctf_team == other->client->resp.ctf_team) {
|
||
if (player != other)
|
||
player->client->resp.score += CTF_TEAM_BONUS;
|
||
// award extra points for capture assists
|
||
if (player->client->resp.ctf_lastreturnedflag + CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) {
|
||
gi.bprintf(PRINT_HIGH, "%s gets an assist for returning the flag!\n", player->client->pers.netname);
|
||
player->client->resp.score += CTF_RETURN_FLAG_ASSIST_BONUS;
|
||
}
|
||
if (player->client->resp.ctf_lastfraggedcarrier + CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) {
|
||
gi.bprintf(PRINT_HIGH, "%s gets an assist for fragging the flag carrier!\n", player->client->pers.netname);
|
||
player->client->resp.score += CTF_FRAG_CARRIER_ASSIST_BONUS;
|
||
}
|
||
}
|
||
}
|
||
|
||
CTFResetFlags();
|
||
return false;
|
||
}
|
||
return false; // its at home base already
|
||
}
|
||
// hey, its not home. return it by teleporting it back
|
||
gi.bprintf(PRINT_HIGH, "%s returned the %s flag!\n",
|
||
other->client->pers.netname, CTFTeamName(ctf_team));
|
||
other->client->resp.score += CTF_RECOVERY_BONUS;
|
||
other->client->resp.ctf_lastreturnedflag = level.time;
|
||
gi.sound (ent, CHAN_RELIABLE+CHAN_NO_PHS_ADD+CHAN_VOICE, gi.soundindex("ctf/flagret.wav"), 1, ATTN_NONE, 0);
|
||
//CTFResetFlag will remove this entity! We must return false
|
||
CTFResetFlag(ctf_team);
|
||
return false;
|
||
}
|
||
|
||
// hey, its not our flag, pick it up
|
||
|
||
// Knightmare- added disable pickup option
|
||
if (!allow_flagpickup->value && PlayersOnCTFTeam (ctf_team) == 0) {
|
||
if (level.time - other->client->ctf_lasttechmsg > 2) {
|
||
gi.centerprintf (other, "Not allowed to take empty teams' flags!");
|
||
other->client->ctf_lasttechmsg = level.time;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
gi.bprintf(PRINT_HIGH, "%s got the %s flag!\n",
|
||
other->client->pers.netname, CTFTeamName(ctf_team));
|
||
other->client->resp.score += CTF_FLAG_BONUS;
|
||
|
||
other->client->pers.inventory[ITEM_INDEX(flag_item)] = 1;
|
||
other->client->resp.ctf_flagsince = level.time;
|
||
|
||
// pick up the flag
|
||
// if it's not a dropped flag, we just make is disappear
|
||
// if it's dropped, it will be removed by the pickup caller
|
||
if (!(ent->spawnflags & DROPPED_ITEM)) {
|
||
ent->flags |= FL_RESPAWN;
|
||
ent->svflags |= SVF_NOCLIENT;
|
||
ent->solid = SOLID_NOT;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
static void CTFDropFlagTouch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||
{
|
||
//owner (who dropped us) can't touch for two secs
|
||
if (other == ent->owner &&
|
||
ent->nextthink - level.time > CTF_AUTO_FLAG_RETURN_TIMEOUT-2)
|
||
return;
|
||
|
||
Touch_Item (ent, other, plane, surf);
|
||
}
|
||
|
||
static void CTFDropFlagThink(edict_t *ent)
|
||
{
|
||
// auto return the flag
|
||
// reset flag will remove ourselves
|
||
if (strcmp(ent->classname, "item_flag_team1") == 0) {
|
||
CTFResetFlag(CTF_TEAM1);
|
||
gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n",
|
||
CTFTeamName(CTF_TEAM1));
|
||
} else if (strcmp(ent->classname, "item_flag_team2") == 0) {
|
||
CTFResetFlag(CTF_TEAM2);
|
||
gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n",
|
||
CTFTeamName(CTF_TEAM2));
|
||
}
|
||
}
|
||
|
||
// Called from PlayerDie, to drop the flag from a dying player
|
||
void CTFDeadDropFlag(edict_t *self)
|
||
{
|
||
edict_t *dropped = NULL;
|
||
|
||
if (!flag1_item || !flag2_item)
|
||
CTFInit();
|
||
|
||
if (self->client->pers.inventory[ITEM_INDEX(flag1_item)]) {
|
||
dropped = Drop_Item(self, flag1_item);
|
||
self->client->pers.inventory[ITEM_INDEX(flag1_item)] = 0;
|
||
gi.bprintf(PRINT_HIGH, "%s lost the %s flag!\n",
|
||
self->client->pers.netname, CTFTeamName(CTF_TEAM1));
|
||
} else if (self->client->pers.inventory[ITEM_INDEX(flag2_item)]) {
|
||
dropped = Drop_Item(self, flag2_item);
|
||
self->client->pers.inventory[ITEM_INDEX(flag2_item)] = 0;
|
||
gi.bprintf(PRINT_HIGH, "%s lost the %s flag!\n",
|
||
self->client->pers.netname, CTFTeamName(CTF_TEAM2));
|
||
}
|
||
|
||
if (dropped) {
|
||
dropped->think = CTFDropFlagThink;
|
||
dropped->nextthink = level.time + CTF_AUTO_FLAG_RETURN_TIMEOUT;
|
||
dropped->touch = CTFDropFlagTouch;
|
||
}
|
||
}
|
||
|
||
qboolean CTFDrop_Flag(edict_t *ent, gitem_t *item)
|
||
{
|
||
edict_t *dropped = NULL;
|
||
|
||
// Knightmare changed this
|
||
if (!allow_flagdrop->value)
|
||
{
|
||
if (rand() & 1)
|
||
{
|
||
if (!(ent->svflags & SVF_MONSTER))
|
||
gi.cprintf(ent, PRINT_HIGH, "Only losers drop flags.\n");
|
||
}
|
||
else
|
||
{
|
||
if (!(ent->svflags & SVF_MONSTER))
|
||
gi.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;
|
||
gi.cprintf(ent, 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;
|
||
gi.cprintf(ent, 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;
|
||
gi.cprintf(ent, 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 droptofloor (edict_t *ent);
|
||
void SpawnItem3 (edict_t *ent, gitem_t *item);
|
||
//edict_t *GetBotFlag1(); //<2F>`<60>[<5B><>1<EFBFBD>̊<EFBFBD>
|
||
//edict_t *GetBotFlag2(); //<2F>`<60>[<5B><>2<EFBFBD>̊<EFBFBD>
|
||
void ChainPodThink (edict_t *ent);
|
||
qboolean ChkTFlg( void );//<2F><><EFBFBD>Z<EFBFBD>b<EFBFBD>g<EFBFBD>A<EFBFBD>b<EFBFBD>v<EFBFBD>ς݁H
|
||
void SetBotFlag1(edict_t *ent); //<2F>`<60>[<5B><>1<EFBFBD>̊<EFBFBD>
|
||
void SetBotFlag2(edict_t *ent); //<2F>`<60>[<5B><>2<EFBFBD>̊<EFBFBD>
|
||
|
||
void CTFFlagSetup (edict_t *ent)
|
||
{
|
||
trace_t tr;
|
||
vec3_t dest;
|
||
float *v;
|
||
|
||
v = tv(-15,-15,-15);
|
||
VectorCopy (v, ent->mins);
|
||
v = tv(15,15,15);
|
||
VectorCopy (v, ent->maxs);
|
||
|
||
if (ent->model)
|
||
gi.setmodel (ent, ent->model);
|
||
else if (ent->item->world_model) //PONKO
|
||
gi.setmodel (ent, ent->item->world_model);
|
||
else ent->s.modelindex = 0; //PONKO
|
||
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;
|
||
|
||
////PON
|
||
// if (Q_stricmp(ent->classname, "item_flag_team1") == 0) SetBotFlag1(ent);
|
||
// else if (Q_stricmp(ent->classname, "item_flag_team2") == 0) SetBotFlag2(ent);
|
||
////PON
|
||
return;
|
||
}
|
||
|
||
void CTFEffects(edict_t *player)
|
||
{
|
||
player->s.effects &= ~(EF_FLAG1 | EF_FLAG2);
|
||
if (player->health > 0) {
|
||
if (player->client->pers.inventory[ITEM_INDEX(flag1_item)]) {
|
||
player->s.effects |= EF_FLAG1;
|
||
}
|
||
if (player->client->pers.inventory[ITEM_INDEX(flag2_item)]
|
||
|| player->client->pers.inventory[ITEM_INDEX(zflag_item)]) {
|
||
player->s.effects |= EF_FLAG2;
|
||
}
|
||
}
|
||
|
||
if (player->client->pers.inventory[ITEM_INDEX(flag1_item)])
|
||
player->s.modelindex3 = gi.modelindex("players/male/flag1.md2");
|
||
else if (player->client->pers.inventory[ITEM_INDEX(flag2_item)])
|
||
player->s.modelindex3 = gi.modelindex("players/male/flag2.md2");
|
||
else if (player->client->pers.inventory[ITEM_INDEX(zflag_item)])
|
||
{
|
||
player->s.modelindex3 = gi.modelindex("models/zflag.md2");
|
||
}
|
||
else
|
||
player->s.modelindex3 = 0;
|
||
}
|
||
|
||
// called when we enter the intermission
|
||
void CTFCalcScores(void)
|
||
{
|
||
int i;
|
||
|
||
ctfgame.total1 = ctfgame.total2 = 0;
|
||
for (i = 0; i < maxclients->value; i++) {
|
||
if (!g_edicts[i+1].inuse)
|
||
continue;
|
||
if (game.clients[i].resp.ctf_team == CTF_TEAM1)
|
||
ctfgame.total1 += game.clients[i].resp.score;
|
||
else if (game.clients[i].resp.ctf_team == CTF_TEAM2)
|
||
ctfgame.total2 += game.clients[i].resp.score;
|
||
}
|
||
}
|
||
|
||
void CTFID_f (edict_t *ent)
|
||
{
|
||
if (ent->client->resp.id_state) {
|
||
if (!(ent->svflags & SVF_MONSTER))
|
||
gi.cprintf(ent, PRINT_HIGH, "Disabling player identication display.\n");
|
||
ent->client->resp.id_state = false;
|
||
} else {
|
||
if (!(ent->svflags & SVF_MONSTER))
|
||
gi.cprintf(ent, PRINT_HIGH, "Activating player identication display.\n");
|
||
ent->client->resp.id_state = true;
|
||
}
|
||
}
|
||
|
||
static void CTFSetIDView(edict_t *ent)
|
||
{
|
||
vec3_t forward, dir;
|
||
trace_t tr;
|
||
edict_t *who, *best;
|
||
float bd = 0, d;
|
||
int i;
|
||
|
||
ent->client->ps.stats[STAT_CTF_ID_VIEW] = 0;
|
||
|
||
AngleVectors(ent->client->v_angle, forward, NULL, NULL);
|
||
VectorScale(forward, 1024, forward);
|
||
VectorAdd(ent->s.origin, forward, forward);
|
||
tr = gi.trace(ent->s.origin, NULL, NULL, forward, ent, MASK_SOLID);
|
||
if (tr.fraction < 1 && tr.ent && tr.ent->client) {
|
||
ent->client->ps.stats[STAT_CTF_ID_VIEW] =
|
||
CS_PLAYERSKINS + (ent - g_edicts - 1);
|
||
return;
|
||
}
|
||
|
||
AngleVectors(ent->client->v_angle, forward, NULL, NULL);
|
||
best = NULL;
|
||
for (i = 1; i <= maxclients->value; i++) {
|
||
who = g_edicts + i;
|
||
if (!who->inuse)
|
||
continue;
|
||
VectorSubtract(who->s.origin, ent->s.origin, dir);
|
||
VectorNormalize(dir);
|
||
d = DotProduct(forward, dir);
|
||
if (d > bd && loc_CanSee(ent, who)) {
|
||
bd = d;
|
||
best = who;
|
||
}
|
||
}
|
||
if (bd > 0.90)
|
||
ent->client->ps.stats[STAT_CTF_ID_VIEW] =
|
||
CS_PLAYERSKINS + (best - g_edicts - 1);
|
||
}
|
||
|
||
void SetCTFStats (edict_t *ent)
|
||
{
|
||
gitem_t *tech;
|
||
int i;
|
||
int p1, p2;
|
||
edict_t *e;
|
||
|
||
// logo headers for the frag display
|
||
ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = gi.imageindex ("ctfsb1");
|
||
ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = gi.imageindex ("ctfsb2");
|
||
|
||
// if during intermission, we must blink the team header of the winning team
|
||
if (level.intermissiontime && (level.framenum & 8)) { // blink 1/8th second
|
||
// note that ctfgame.total[12] is set when we go to intermission
|
||
if (ctfgame.team1 > ctfgame.team2)
|
||
ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
|
||
else if (ctfgame.team2 > ctfgame.team1)
|
||
ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
|
||
else if (ctfgame.total1 > ctfgame.total2) // frag tie breaker
|
||
ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
|
||
else if (ctfgame.total2 > ctfgame.total1)
|
||
ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
|
||
else { // tie game!
|
||
ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
|
||
ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
|
||
}
|
||
}
|
||
|
||
// tech icon
|
||
i = 0;
|
||
ent->client->ps.stats[STAT_CTF_TECH] = 0;
|
||
while (tnames[i]) {
|
||
if ((tech = FindItemByClassname(tnames[i])) != NULL &&
|
||
ent->client->pers.inventory[ITEM_INDEX(tech)]) {
|
||
ent->client->ps.stats[STAT_CTF_TECH] = gi.imageindex(tech->icon);
|
||
break;
|
||
}
|
||
i++;
|
||
}
|
||
|
||
// figure out what icon to display for team logos
|
||
// three states:
|
||
// flag at base
|
||
// flag taken
|
||
// flag dropped
|
||
p1 = gi.imageindex ("i_ctf1");
|
||
e = G_Find(NULL, FOFS(classname), "item_flag_team1");
|
||
if (e != NULL) {
|
||
if (e->solid == SOLID_NOT) {
|
||
int i;
|
||
|
||
// not at base
|
||
// check if on player
|
||
p1 = gi.imageindex ("i_ctf1d"); // default to dropped
|
||
for (i = 1; i <= maxclients->value; i++)
|
||
if (g_edicts[i].inuse &&
|
||
g_edicts[i].client->pers.inventory[ITEM_INDEX(flag1_item)]) {
|
||
// enemy has it
|
||
p1 = gi.imageindex ("i_ctf1t");
|
||
break;
|
||
}
|
||
} else if (e->spawnflags & DROPPED_ITEM)
|
||
p1 = gi.imageindex ("i_ctf1d"); // must be dropped
|
||
}
|
||
p2 = gi.imageindex ("i_ctf2");
|
||
e = G_Find(NULL, FOFS(classname), "item_flag_team2");
|
||
if (e != NULL) {
|
||
if (e->solid == SOLID_NOT) {
|
||
int i;
|
||
|
||
// not at base
|
||
// check if on player
|
||
p2 = gi.imageindex ("i_ctf2d"); // default to dropped
|
||
for (i = 1; i <= maxclients->value; i++)
|
||
if (g_edicts[i].inuse &&
|
||
g_edicts[i].client->pers.inventory[ITEM_INDEX(flag2_item)]) {
|
||
// enemy has it
|
||
p2 = gi.imageindex ("i_ctf2t");
|
||
break;
|
||
}
|
||
} else if (e->spawnflags & DROPPED_ITEM)
|
||
p2 = gi.imageindex ("i_ctf2d"); // must be dropped
|
||
}
|
||
|
||
|
||
ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1;
|
||
ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2;
|
||
|
||
if (ctfgame.last_flag_capture && level.time - ctfgame.last_flag_capture < 5) {
|
||
if (ctfgame.last_capture_team == CTF_TEAM1)
|
||
if (level.framenum & 8)
|
||
ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1;
|
||
else
|
||
ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = 0;
|
||
else
|
||
if (level.framenum & 8)
|
||
ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2;
|
||
else
|
||
ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = 0;
|
||
}
|
||
|
||
ent->client->ps.stats[STAT_CTF_TEAM1_CAPS] = ctfgame.team1;
|
||
ent->client->ps.stats[STAT_CTF_TEAM2_CAPS] = ctfgame.team2;
|
||
|
||
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = 0;
|
||
if (ent->client->resp.ctf_team == CTF_TEAM1 &&
|
||
ent->client->pers.inventory[ITEM_INDEX(flag2_item)] &&
|
||
(level.framenum & 8))
|
||
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = gi.imageindex ("i_ctf2");
|
||
|
||
else if (ent->client->resp.ctf_team == CTF_TEAM2 &&
|
||
ent->client->pers.inventory[ITEM_INDEX(flag1_item)] &&
|
||
(level.framenum & 8))
|
||
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = gi.imageindex ("i_ctf1");
|
||
|
||
ent->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = 0;
|
||
ent->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = 0;
|
||
if (ent->client->resp.ctf_team == CTF_TEAM1)
|
||
ent->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = gi.imageindex ("i_ctfj");
|
||
else if (ent->client->resp.ctf_team == CTF_TEAM2)
|
||
ent->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = gi.imageindex ("i_ctfj");
|
||
|
||
ent->client->ps.stats[STAT_CTF_ID_VIEW] = 0;
|
||
if (ent->client->resp.id_state)
|
||
CTFSetIDView(ent);
|
||
}
|
||
|
||
/*------------------------------------------------------------------------*/
|
||
|
||
/*QUAKED info_player_team1 (1 0 0) (-16 -16 -24) (16 16 32)
|
||
potential team1 spawning position for ctf games
|
||
*/
|
||
void SP_info_player_team1(edict_t *self)
|
||
{
|
||
}
|
||
|
||
/*QUAKED info_player_team2 (0 0 1) (-16 -16 -24) (16 16 32)
|
||
potential team2 spawning position for ctf games
|
||
*/
|
||
void SP_info_player_team2(edict_t *self)
|
||
{
|
||
}
|
||
|
||
|
||
/*------------------------------------------------------------------------*/
|
||
/* GRAPPLE */
|
||
/*------------------------------------------------------------------------*/
|
||
|
||
// ent is player
|
||
void CTFPlayerResetGrapple(edict_t *ent)
|
||
{
|
||
int i;
|
||
edict_t *e;
|
||
vec3_t v,vv;
|
||
|
||
//PON-CTF
|
||
if (chedit->value && ent == &g_edicts[1] && ent->client->ctf_grapple)
|
||
{
|
||
e = (edict_t*)ent->client->ctf_grapple;
|
||
VectorCopy(e->s.origin,vv);
|
||
|
||
for (i = 1;(CurrentIndex - i) > 0;i++)
|
||
{
|
||
if (Route[CurrentIndex - i].state == GRS_GRAPHOOK) break;
|
||
else if (Route[CurrentIndex - i].state == GRS_GRAPSHOT) break;
|
||
}
|
||
if (Route[CurrentIndex - i].state == GRS_GRAPHOOK)
|
||
{
|
||
Route[CurrentIndex].state = GRS_GRAPRELEASE;
|
||
VectorCopy(ent->s.origin,Route[CurrentIndex].Pt);
|
||
VectorSubtract(ent->s.origin,vv,v);
|
||
Route[CurrentIndex].Tcourner[0] = VectorLength(v);
|
||
//gi.bprintf(PRINT_HIGH,"length %f\n",VectorLength(v));
|
||
}
|
||
else if (Route[CurrentIndex - i].state == GRS_GRAPSHOT)
|
||
{
|
||
CurrentIndex = CurrentIndex - i -1;
|
||
}
|
||
//gi.bprintf(PRINT_HIGH,"length %f\n",VectorLength(v));
|
||
|
||
if ((CurrentIndex - i) > 0)
|
||
{
|
||
if (++CurrentIndex < MAXNODES)
|
||
{
|
||
gi.bprintf(PRINT_HIGH,"Grapple has been released.Last %i pod(s).\n",MAXNODES - CurrentIndex);
|
||
memset(&Route[CurrentIndex],0,sizeof(route_t)); //initialize
|
||
Route[CurrentIndex].index = Route[CurrentIndex - 1].index +1;
|
||
}
|
||
}
|
||
}
|
||
//PON-CTF
|
||
|
||
if (ent->client && ent->client->ctf_grapple)
|
||
CTFResetGrapple(ent->client->ctf_grapple);
|
||
ent->s.sound = 0;
|
||
}
|
||
|
||
// self is grapple, not player
|
||
void CTFResetGrapple(edict_t *self)
|
||
{
|
||
self->s.sound = 0;
|
||
|
||
if (self->owner == NULL)
|
||
{
|
||
G_FreeEdict(self);
|
||
return;
|
||
}
|
||
|
||
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)
|
||
{
|
||
short i;
|
||
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;
|
||
|
||
//PON-CTF
|
||
if (chedit->value && self->owner == &g_edicts[1])
|
||
{
|
||
i = CurrentIndex;
|
||
while (--i > 0)
|
||
{
|
||
if (Route[i].state == GRS_GRAPSHOT)
|
||
{
|
||
VectorCopy(self->s.origin,Route[i].Tcourner);
|
||
break;
|
||
}
|
||
}
|
||
Route[CurrentIndex].state = GRS_GRAPHOOK;
|
||
VectorCopy(self->owner->s.origin,Route[CurrentIndex].Pt);
|
||
|
||
if (++CurrentIndex < MAXNODES)
|
||
{
|
||
gi.bprintf(PRINT_HIGH,"Grapple has been hook.Last %i pod(s).\n",MAXNODES - CurrentIndex);
|
||
memset(&Route[CurrentIndex],0,sizeof(route_t)); //initialize
|
||
Route[CurrentIndex].index = Route[CurrentIndex - 1].index +1;
|
||
}
|
||
}
|
||
//PON-CTF
|
||
|
||
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;
|
||
float x;
|
||
|
||
if (1/*!(self->owner->svflags & SVF_MONSTER)*/)
|
||
{
|
||
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);
|
||
}
|
||
else
|
||
{
|
||
x = self->owner->s.angles[YAW] ;
|
||
x = x * M_PI * 2 / 360;
|
||
start[0] = self->owner->s.origin[0] + cos(x) * 16;
|
||
start[1] = self->owner->s.origin[1] + sin(x) * 16;
|
||
if (self->owner->maxs[2] >=32) start[2] = self->owner->s.origin[2]+16;
|
||
else start[2] = self->owner->s.origin[2]-12;
|
||
}
|
||
|
||
VectorSubtract(start, self->owner->s.origin, offset);
|
||
|
||
VectorSubtract (start, self->s.origin, dir);
|
||
distance = VectorLength(dir);
|
||
// don't draw cable if close
|
||
if (distance < 64)
|
||
return;
|
||
|
||
#if 0
|
||
if (distance > 256)
|
||
return;
|
||
|
||
// check for min/max pitch
|
||
vectoangles (dir, angles);
|
||
if (angles[0] < -180)
|
||
angles[0] += 360;
|
||
if (fabs(angles[0]) > 45)
|
||
return;
|
||
|
||
trace_t tr; //!!
|
||
|
||
tr = gi.trace (start, NULL, NULL, self->s.origin, self, MASK_SHOT);
|
||
if (tr.ent != self) {
|
||
CTFResetGrapple(self);
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
// adjust start for beam origin being in middle of a segment
|
||
// VectorMA (start, 8, f, start);
|
||
|
||
VectorCopy (self->s.origin, end);
|
||
// adjust end z for end spot since the monster is currently dead
|
||
// end[2] = self->absmin[2] + self->size[2] / 2;
|
||
|
||
gi.WriteByte (svc_temp_entity);
|
||
#if 1 //def USE_GRAPPLE_CABLE
|
||
gi.WriteByte (TE_GRAPPLE_CABLE);
|
||
gi.WriteShort (self->owner - g_edicts);
|
||
gi.WritePosition (self->owner->s.origin);
|
||
gi.WritePosition (end);
|
||
gi.WritePosition (offset);
|
||
#else
|
||
gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
|
||
gi.WriteShort (self - g_edicts);
|
||
gi.WritePosition (end);
|
||
gi.WritePosition (start);
|
||
#endif
|
||
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||
}
|
||
|
||
void SV_AddGravity (edict_t *ent);
|
||
|
||
// pull the player toward the grapple
|
||
void CTFGrapplePull(edict_t *self)
|
||
{
|
||
vec3_t hookdir, v;
|
||
float vlen;
|
||
|
||
if (self->owner == NULL)
|
||
{
|
||
CTFResetGrapple(self);
|
||
return;
|
||
}
|
||
|
||
/* if (!(self->owner->svflags & SVF_MONSTER))
|
||
{
|
||
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;
|
||
|
||
if (1/*!(self->owner->svflags & SVF_MONSTER)*/)
|
||
{
|
||
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);
|
||
}
|
||
else
|
||
{
|
||
VectorCopy(self->owner->s.origin, v);
|
||
if (self->owner->maxs[2] >=32) v[2] += 16;
|
||
else v[2] -= 12;
|
||
VectorSubtract (self->s.origin, v, hookdir);
|
||
}
|
||
|
||
vlen = VectorLength(hookdir);
|
||
|
||
if (self->owner->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL &&
|
||
vlen < 64) {
|
||
float volume = 1.0;
|
||
|
||
if (self->owner->client->silencer_shots)
|
||
volume = 0.2;
|
||
|
||
self->owner->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
|
||
gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grhang.wav"), volume, ATTN_NORM, 0);
|
||
self->owner->client->ctf_grapplestate = CTF_GRAPPLE_STATE_HANG;
|
||
}
|
||
|
||
VectorNormalize (hookdir);
|
||
VectorScale(hookdir, CTF_GRAPPLE_PULL_SPEED, hookdir);
|
||
VectorCopy(hookdir, self->owner->velocity);
|
||
SV_AddGravity(self->owner);
|
||
}
|
||
}
|
||
|
||
void CTFFireGrapple (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect)
|
||
{
|
||
edict_t *grapple;
|
||
trace_t tr;
|
||
|
||
VectorNormalize (dir);
|
||
|
||
grapple = G_Spawn();
|
||
VectorCopy (start, grapple->s.origin);
|
||
VectorCopy (start, grapple->s.old_origin);
|
||
vectoangles (dir, grapple->s.angles);
|
||
VectorScale (dir, speed, grapple->velocity);
|
||
grapple->movetype = MOVETYPE_FLYMISSILE;
|
||
grapple->clipmask = MASK_SHOT;
|
||
grapple->solid = SOLID_BBOX;
|
||
grapple->s.effects |= effect;
|
||
VectorClear (grapple->mins);
|
||
VectorClear (grapple->maxs);
|
||
grapple->s.modelindex = gi.modelindex ("models/weapons/grapple/hook/tris.md2");
|
||
// grapple->s.sound = gi.soundindex ("misc/lasfly.wav");
|
||
grapple->owner = self;
|
||
grapple->touch = CTFGrappleTouch;
|
||
// grapple->nextthink = level.time + FRAMETIME;
|
||
// grapple->think = CTFGrappleThink;
|
||
grapple->dmg = damage;
|
||
self->client->ctf_grapple = grapple;
|
||
self->client->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
|
||
gi.linkentity (grapple);
|
||
|
||
tr = gi.trace (self->s.origin, NULL, NULL, grapple->s.origin, grapple, MASK_SHOT);
|
||
if (tr.fraction < 1.0)
|
||
{
|
||
VectorMA (grapple->s.origin, -10, dir, grapple->s.origin);
|
||
grapple->touch (grapple, tr.ent, NULL, NULL);
|
||
}
|
||
//PON-CTF
|
||
if (chedit->value && self == &g_edicts[1])
|
||
{
|
||
Route[CurrentIndex].state = GRS_GRAPSHOT;
|
||
VectorCopy(self->s.origin,Route[CurrentIndex].Pt);
|
||
|
||
if (++CurrentIndex < MAXNODES)
|
||
{
|
||
gi.bprintf(PRINT_HIGH,"Grapple has been shoot.Last %i pod(s).\n",MAXNODES - CurrentIndex);
|
||
memset(&Route[CurrentIndex],0,sizeof(route_t)); //initialize
|
||
Route[CurrentIndex].index = Route[CurrentIndex - 1].index +1;
|
||
}
|
||
}
|
||
//PON-CTF
|
||
|
||
}
|
||
|
||
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,i;
|
||
vec3_t v,vv;
|
||
edict_t *e;
|
||
|
||
// 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)
|
||
{
|
||
i = ent->client->ctf_grapplestate;
|
||
e = (edict_t*)ent->client->ctf_grapple;
|
||
VectorCopy(e->s.origin,vv);
|
||
CTFResetGrapple(ent->client->ctf_grapple);
|
||
if (ent->client->weaponstate == WEAPON_FIRING)
|
||
ent->client->weaponstate = WEAPON_READY;
|
||
|
||
if (ent->svflags & SVF_MONSTER)
|
||
{
|
||
if (ent->waterlevel
|
||
|| i == CTF_GRAPPLE_STATE_HANG ) VectorClear(ent->velocity);
|
||
//fix the moving speed
|
||
if (i == CTF_GRAPPLE_STATE_PULL)
|
||
{
|
||
v[0] = ent->velocity[0];
|
||
v[1] = ent->velocity[1];
|
||
v[2] = 0;
|
||
|
||
ent->client->zc.moveyaw = Get_yaw(v);
|
||
ent->moveinfo.speed = (VectorLength(v) * FRAMETIME) / MOVE_SPD_RUN;
|
||
ent->velocity[0] = 0;
|
||
ent->velocity[1] = 0;
|
||
}
|
||
else if (i == CTF_GRAPPLE_STATE_HANG )
|
||
{
|
||
ent->moveinfo.speed = 0;
|
||
ent->velocity[0] = 0;
|
||
ent->velocity[1] = 0;
|
||
}
|
||
}
|
||
//PON-CTF
|
||
if (chedit->value && ent == &g_edicts[1])
|
||
{
|
||
for (i = 0;(CurrentIndex - i) > 0;i++)
|
||
{
|
||
if (Route[CurrentIndex - i].state == GRS_GRAPHOOK) break;
|
||
else if (Route[CurrentIndex - i].state == GRS_GRAPSHOT) break;
|
||
}
|
||
if (Route[CurrentIndex - i].state == GRS_GRAPHOOK)
|
||
{
|
||
Route[CurrentIndex].state = GRS_GRAPRELEASE;
|
||
VectorCopy(ent->s.origin,Route[CurrentIndex].Pt);
|
||
VectorSubtract(ent->s.origin,vv,v);
|
||
Route[CurrentIndex].Tcourner[0] = VectorLength(v);
|
||
//gi.bprintf(PRINT_HIGH,"length %f\n",VectorLength(v));
|
||
}
|
||
else if (Route[CurrentIndex - i].state == GRS_GRAPSHOT)
|
||
{
|
||
CurrentIndex = CurrentIndex - i -1;
|
||
}
|
||
//gi.bprintf(PRINT_HIGH,"length %f\n",VectorLength(v));
|
||
|
||
if ((CurrentIndex - i) > 0)
|
||
{
|
||
if (++CurrentIndex < MAXNODES)
|
||
{
|
||
gi.bprintf(PRINT_HIGH,"Grapple has been released.Last %i pod(s).\n",MAXNODES - CurrentIndex);
|
||
memset(&Route[CurrentIndex],0,sizeof(route_t)); //initialize
|
||
Route[CurrentIndex].index = Route[CurrentIndex - 1].index +1;
|
||
}
|
||
}
|
||
}
|
||
//PON-CTF
|
||
|
||
}
|
||
|
||
|
||
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 (ent->svflags & SVF_MONSTER)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 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) {
|
||
if (!(ent->svflags & SVF_MONSTER))
|
||
gi.cprintf(ent, PRINT_HIGH, "You are on the %s team.\n",
|
||
CTFTeamName(ent->client->resp.ctf_team));
|
||
return;
|
||
}
|
||
if (Q_stricmp(t, "red") == 0)
|
||
desired_team = CTF_TEAM1;
|
||
else if (Q_stricmp(t, "blue") == 0)
|
||
desired_team = CTF_TEAM2;
|
||
else {
|
||
if (!(ent->svflags & SVF_MONSTER))
|
||
gi.cprintf(ent, PRINT_HIGH, "Unknown team %s.\n", t);
|
||
return;
|
||
}
|
||
|
||
if (ent->client->resp.ctf_team == desired_team) {
|
||
if (!(ent->svflags & SVF_MONSTER))
|
||
gi.cprintf(ent, PRINT_HIGH, "You are already on the %s team.\n",
|
||
CTFTeamName(ent->client->resp.ctf_team));
|
||
return;
|
||
}
|
||
|
||
////
|
||
ent->svflags = 0;
|
||
ent->flags &= ~FL_GODMODE;
|
||
ent->client->resp.ctf_team = desired_team;
|
||
ent->client->resp.ctf_state = CTF_STATE_START;
|
||
s = Info_ValueForKey (ent->client->pers.userinfo, "skin");
|
||
CTFAssignSkin(ent, s);
|
||
|
||
if (ent->solid == SOLID_NOT) { // spectator
|
||
PutClientInServer (ent);
|
||
// add a teleportation effect
|
||
ent->s.event = EV_PLAYER_TELEPORT;
|
||
// hold in place briefly
|
||
ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
|
||
ent->client->ps.pmove.pm_time = 14;
|
||
gi.bprintf(PRINT_HIGH, "%s joined the %s team.\n",
|
||
ent->client->pers.netname, CTFTeamName(desired_team));
|
||
return;
|
||
}
|
||
|
||
ent->health = 0;
|
||
player_die (ent, ent, ent, 100000, vec3_origin);
|
||
// don't even bother waiting for death frames
|
||
ent->deadflag = DEAD_DEAD;
|
||
respawn (ent);
|
||
|
||
ent->client->resp.score = 0;
|
||
|
||
gi.bprintf(PRINT_HIGH, "%s changed to the %s team.\n",
|
||
ent->client->pers.netname, CTFTeamName(desired_team));
|
||
}
|
||
|
||
/*
|
||
==================
|
||
CTFScoreboardMessage
|
||
==================
|
||
*/
|
||
void CTFScoreboardMessage (edict_t *ent, edict_t *killer)
|
||
{
|
||
char entry[1024];
|
||
char string[1400];
|
||
int len;
|
||
int i, j, k, n;
|
||
int sorted[2][MAX_CLIENTS];
|
||
int sortedscores[2][MAX_CLIENTS];
|
||
int score, total[2], totalscore[2];
|
||
int last[2];
|
||
gclient_t *cl;
|
||
edict_t *cl_ent;
|
||
int team;
|
||
int maxsize = 1000;
|
||
|
||
// sort the clients by team and score
|
||
total[0] = total[1] = 0;
|
||
last[0] = last[1] = 0;
|
||
totalscore[0] = totalscore[1] = 0;
|
||
for (i=0 ; i<game.maxclients ; i++)
|
||
{
|
||
cl_ent = g_edicts + 1 + i;
|
||
if (!cl_ent->inuse)
|
||
continue;
|
||
if (game.clients[i].resp.ctf_team == CTF_TEAM1)
|
||
team = 0;
|
||
else if (game.clients[i].resp.ctf_team == CTF_TEAM2)
|
||
team = 1;
|
||
else
|
||
continue; // unknown team?
|
||
|
||
score = game.clients[i].resp.score;
|
||
for (j=0 ; j<total[team] ; j++)
|
||
{
|
||
if (score > sortedscores[team][j])
|
||
break;
|
||
}
|
||
for (k=total[team] ; k>j ; k--)
|
||
{
|
||
sorted[team][k] = sorted[team][k-1];
|
||
sortedscores[team][k] = sortedscores[team][k-1];
|
||
}
|
||
sorted[team][j] = i;
|
||
sortedscores[team][j] = score;
|
||
totalscore[team] += score;
|
||
total[team]++;
|
||
}
|
||
|
||
// print level name and exit rules
|
||
// add the clients in sorted order
|
||
*string = 0;
|
||
len = 0;
|
||
|
||
// team one
|
||
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])
|
||
break; // we're done
|
||
|
||
#if 0 //ndef NEW_SCORE
|
||
// set up y
|
||
Com_sprintf (entry, sizeof(entry), "yv %d ", 42 + i * 8);
|
||
if (maxsize - len > strlen(entry)) {
|
||
Com_strcat (string, sizeof(string), entry);
|
||
len = strlen(string);
|
||
}
|
||
#else
|
||
*entry = 0;
|
||
#endif
|
||
|
||
// left side
|
||
if (i < total[0]) {
|
||
cl = &game.clients[sorted[0][i]];
|
||
cl_ent = g_edicts + 1 + sorted[0][i];
|
||
|
||
#if 0 //ndef NEW_SCORE
|
||
Com_sprintf (entry+strlen(entry), sizeof(entry)-strlen(entry),
|
||
"xv 0 %s \"%3d %3d %-12.12s\" ",
|
||
(cl_ent == ent) ? "string2" : "string",
|
||
cl->resp.score,
|
||
(cl->ping > 999) ? 999 : cl->ping,
|
||
cl->pers.netname);
|
||
|
||
if (cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)])
|
||
Com_strcat (entry, sizeof(entry), "xv 56 picn sbfctf2 ");
|
||
#else
|
||
Com_sprintf (entry+strlen(entry), sizeof(entry)-strlen(entry),
|
||
"ctf 0 %d %d %d %d ",
|
||
42 + i * 8,
|
||
sorted[0][i],
|
||
cl->resp.score,
|
||
cl->ping > 999 ? 999 : cl->ping);
|
||
|
||
if (cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)])
|
||
Com_sprintf(entry + strlen(entry), sizeof(entry)-strlen(entry), "xv 56 yv %d picn sbfctf2 ", 42 + i * 8);
|
||
#endif
|
||
|
||
if (maxsize - len > strlen(entry)) {
|
||
Com_strcat (string, sizeof(string), entry);
|
||
len = (int)strlen(string);
|
||
last[0] = i;
|
||
}
|
||
}
|
||
|
||
// right side
|
||
if (i < total[1]) {
|
||
cl = &game.clients[sorted[1][i]];
|
||
cl_ent = g_edicts + 1 + sorted[1][i];
|
||
|
||
#if 0 //ndef NEW_SCORE
|
||
Com_sprintf (entry+strlen(entry), sizeof(entry)-strlen(entry),
|
||
"xv 160 %s \"%3d %3d %-12.12s\" ",
|
||
(cl_ent == ent) ? "string2" : "string",
|
||
cl->resp.score,
|
||
(cl->ping > 999) ? 999 : cl->ping,
|
||
cl->pers.netname);
|
||
|
||
if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)])
|
||
Com_strcat (entry, sizeof(entry), "xv 216 picn sbfctf1 ");
|
||
#else
|
||
|
||
Com_sprintf (entry+strlen(entry), sizeof(entry)-strlen(entry),
|
||
"ctf 160 %d %d %d %d ",
|
||
42 + i * 8,
|
||
sorted[1][i],
|
||
cl->resp.score,
|
||
cl->ping > 999 ? 999 : cl->ping);
|
||
|
||
if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)])
|
||
Com_sprintf(entry + strlen(entry), sizeof(entry)-strlen(entry), "xv 216 yv %d picn sbfctf1 ", 42 + i * 8);
|
||
#endif
|
||
if (maxsize - len > strlen(entry)) {
|
||
Com_strcat (string, sizeof(string), entry);
|
||
len = (int)strlen(string);
|
||
last[1] = i;
|
||
}
|
||
}
|
||
}
|
||
|
||
// put in spectators if we have enough room
|
||
if (last[0] > last[1])
|
||
j = last[0];
|
||
else
|
||
j = last[1];
|
||
j = (j + 2) * 8 + 42;
|
||
|
||
k = n = 0;
|
||
if (maxsize - len > 50)
|
||
{
|
||
for (i = 0; i < maxclients->value; i++)
|
||
{
|
||
cl_ent = g_edicts + 1 + i;
|
||
cl = &game.clients[i];
|
||
if (!cl_ent->inuse ||
|
||
cl_ent->solid != SOLID_NOT ||
|
||
cl_ent->client->resp.ctf_team != CTF_NOTEAM)
|
||
continue;
|
||
|
||
if (!k)
|
||
{
|
||
k = 1;
|
||
Com_sprintf (entry, sizeof(entry), "xv 0 yv %d string2 \"Spectators\" ", j);
|
||
Com_strcat (string, sizeof(string), entry);
|
||
len = (int)strlen(string);
|
||
j += 8;
|
||
}
|
||
|
||
Com_sprintf (entry+strlen(entry), sizeof(entry)-strlen(entry),
|
||
"ctf %d %d %d %d %d ",
|
||
(n & 1) ? 160 : 0, // x
|
||
j, // y
|
||
i, // playernum
|
||
cl->resp.score,
|
||
cl->ping > 999 ? 999 : cl->ping);
|
||
if (maxsize - len > strlen(entry)) {
|
||
Com_strcat (string, sizeof(string), entry);
|
||
len = (int)strlen(string);
|
||
}
|
||
|
||
if (n & 1)
|
||
j += 8;
|
||
n++;
|
||
}
|
||
}
|
||
|
||
if (total[0] - last[0] > 1) // couldn't fit everyone
|
||
Com_sprintf (string + strlen(string), sizeof(string)-strlen(string), "xv 8 yv %d string \"..and %d more\" ",
|
||
42 + (last[0]+1)*8, total[0] - last[0] - 1);
|
||
if (total[1] - last[1] > 1) // couldn't fit everyone
|
||
Com_sprintf (string + strlen(string), sizeof(string)-strlen(string), "xv 168 yv %d string \"..and %d more\" ",
|
||
42 + (last[1]+1)*8, total[1] - last[1] - 1);
|
||
|
||
gi.WriteByte (svc_layout);
|
||
gi.WriteString (string);
|
||
}
|
||
|
||
/*------------------------------------------------------------------------*/
|
||
/* TECH */
|
||
/*------------------------------------------------------------------------*/
|
||
|
||
void 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 (who->svflags & SVF_MONSTER) return ;
|
||
if (level.time - who->client->ctf_lasttechmsg > 2) {
|
||
gi.centerprintf(who, "You already have a TECH powerup.");
|
||
who->client->ctf_lasttechmsg = level.time;
|
||
}
|
||
}
|
||
|
||
gitem_t *CTFWhat_Tech (edict_t *ent)
|
||
{
|
||
gitem_t *tech;
|
||
int i;
|
||
|
||
i = 0;
|
||
while (tnames[i]) {
|
||
if ((tech = FindItemByClassname(tnames[i])) != NULL &&
|
||
ent->client->pers.inventory[ITEM_INDEX(tech)]) {
|
||
return tech;
|
||
}
|
||
i++;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
qboolean CTFPickup_Tech (edict_t *ent, edict_t *other)
|
||
{
|
||
gitem_t *tech;
|
||
int i;
|
||
|
||
i = 0;
|
||
while (tnames[i])
|
||
{
|
||
if ((tech = FindItemByClassname(tnames[i])) != NULL &&
|
||
other->client->pers.inventory[ITEM_INDEX(tech)]) {
|
||
CTFHasTech(other);
|
||
return false; // has this one
|
||
}
|
||
i++;
|
||
}
|
||
|
||
// client only gets one tech
|
||
other->client->pers.inventory[ITEM_INDEX(ent->item)]++;
|
||
other->client->ctf_regentime = level.time;
|
||
return true;
|
||
}
|
||
|
||
static void SpawnTech(gitem_t *item, edict_t *spot);
|
||
|
||
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 + CTF_TECH_TIMEOUT;
|
||
dropped->think = TechThink;
|
||
dropped->owner = NULL;
|
||
ent->client->pers.inventory[ITEM_INDEX(tech)] = 0;
|
||
Apply_Tech_Shell (tech, dropped);
|
||
}
|
||
i++;
|
||
}
|
||
}
|
||
|
||
#if 0
|
||
// 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);
|
||
gi.dprintf ("CheckNumTech: spawning more techs (numtechs = %d, newtechcount=%d, numtechtypes=%d).\n", numtechs, newtechcount, numtechtypes);
|
||
SpawnMoreTechs (numtechs, newtechcount, numtechtypes);
|
||
}
|
||
if (newtechcount < numtechs)
|
||
{
|
||
// gi.dprintf ("Number of techs to spawn: %d\n", newtechcount);
|
||
gi.dprintf ("CheckNumTech: removing techs (numtechs = %d, newtechcount=%d, numtechtypes=%d).\n", numtechs, newtechcount, numtechtypes);
|
||
RemoveTechs (numtechs, newtechcount, numtechtypes);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
static void SpawnTech (gitem_t *item, edict_t *spot)
|
||
{
|
||
edict_t *ent;
|
||
vec3_t forward, right;
|
||
vec3_t angles;
|
||
|
||
if (!ctf->value)
|
||
return;
|
||
|
||
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);
|
||
|
||
// ent->s.renderfx = RF_GLOW;
|
||
VectorSet (ent->mins, -15, -15, -15);
|
||
VectorSet (ent->maxs, 15, 15, 15);
|
||
if (ent->item->world_model) //PONKO
|
||
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++;
|
||
}
|
||
}
|
||
|
||
// 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 (techs_spawned || ((int)dmflags->value & DF_CTF_NO_TECH))
|
||
// 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;
|
||
techs_spawned = true;
|
||
}
|
||
|
||
int CTFApplyResistance(edict_t *ent, int dmg)
|
||
{
|
||
static gitem_t *tech = NULL;
|
||
float volume = 1.0;
|
||
|
||
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; // was 2
|
||
}
|
||
return dmg;
|
||
}
|
||
|
||
int CTFApplyStrength(edict_t *ent, int dmg)
|
||
{
|
||
static gitem_t *tech = NULL;
|
||
|
||
if (!tech)
|
||
tech = FindItemByClassname("item_tech2");
|
||
if (dmg && tech && ent->client && ent->client->pers.inventory[ITEM_INDEX(tech)]) {
|
||
return dmg * tech_strength->value; // was 2
|
||
}
|
||
return dmg;
|
||
}
|
||
|
||
qboolean CTFApplyStrengthSound(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_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 (!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 (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;
|
||
|
||
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) // was 150
|
||
{
|
||
ent->health += 5;
|
||
if (ent->health > tech_regen_health_max->value) // was 150
|
||
ent->health = tech_regen_health_max->value; // was 150
|
||
client->ctf_regentime += 0.5;
|
||
noise = true;
|
||
}
|
||
if (tech_regen_armor->value)
|
||
{
|
||
index = ArmorIndex (ent);
|
||
if (index && client->pers.inventory[index] < tech_regen_armor_max->value) // was 150
|
||
{
|
||
client->pers.inventory[index] += 5;
|
||
if (client->pers.inventory[index] > tech_regen_armor_max->value) // was 150
|
||
client->pers.inventory[index] = tech_regen_armor_max->value; // was 150
|
||
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 (!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_quad", 2 },
|
||
{ "item_invulnerability", 2 },
|
||
{ "weapon_bfg", 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 },
|
||
{ 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;
|
||
vec3_t v;
|
||
int hotindex = 999;
|
||
int i;
|
||
gitem_t *item;
|
||
int nearteam = -1;
|
||
edict_t *flag1, *flag2;
|
||
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) {
|
||
Com_strcpy (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 ((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) {
|
||
Com_strcpy (buf, bufSize, "nowhere");
|
||
return;
|
||
}
|
||
|
||
// in water?
|
||
if (who->waterlevel)
|
||
Com_strcpy (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)
|
||
Com_strcat (buf, bufSize, "above ");
|
||
else
|
||
Com_strcat (buf, bufSize, "below ");
|
||
else
|
||
Com_strcat (buf, bufSize, "near ");
|
||
|
||
if (nearteam == CTF_TEAM1)
|
||
Com_strcat (buf, bufSize, "the red ");
|
||
else if (nearteam == CTF_TEAM2)
|
||
Com_strcat (buf, bufSize, "the blue ");
|
||
else
|
||
Com_strcat (buf, bufSize, "the ");
|
||
|
||
Com_strcat (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;
|
||
|
||
*buf = 0;
|
||
|
||
power_armor_type = PowerArmorType (who);
|
||
if (power_armor_type)
|
||
{
|
||
cells = who->client->pers.inventory[ITEM_INDEX(Fdi_CELLS/*FindItem ("cells")*/)];
|
||
if (cells)
|
||
// sprintf (buf+strlen(buf), "%s with %i cells ",
|
||
Com_sprintf (buf+strlen(buf), bufSize - strlen(buf), "%s with %i cells ",
|
||
(power_armor_type == POWER_ARMOR_SCREEN) ?
|
||
"Power Screen" : "Power Shield", cells);
|
||
}
|
||
|
||
index = ArmorIndex (who);
|
||
if (index)
|
||
{
|
||
item = GetItemByIndex (index);
|
||
if (item)
|
||
{
|
||
if (*buf) {
|
||
Com_strcat (buf, bufSize, "and ");
|
||
}
|
||
// sprintf(buf+strlen(buf), "%i units of %s",
|
||
Com_sprintf (buf+strlen(buf), bufSize - strlen(buf), "%i units of %s",
|
||
who->client->pers.inventory[index], item->pickup_name);
|
||
}
|
||
}
|
||
|
||
if (!*buf)
|
||
Com_strcpy (buf, bufSize, "no armor");
|
||
}
|
||
|
||
static void CTFSay_Team_Health (edict_t *who, char *buf, size_t bufSize)
|
||
{
|
||
if (who->health <= 0)
|
||
Com_strcpy (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++;
|
||
}
|
||
Com_strcpy (buf, bufSize, "no powerup");
|
||
}
|
||
|
||
static void CTFSay_Team_Weapon(edict_t *who, char *buf, size_t bufSize)
|
||
{
|
||
if (who->client->pers.weapon)
|
||
Com_strcpy (buf, bufSize, who->client->pers.weapon->pickup_name);
|
||
else
|
||
Com_strcpy (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)
|
||
Com_strcat (s, sizeof(s), ", ");
|
||
Com_strcat(s, sizeof(s), s2);
|
||
*s2 = 0;
|
||
}
|
||
n++;
|
||
}
|
||
Com_strcpy (s2, sizeof(s2), targ->client->pers.netname);
|
||
}
|
||
if (*s2)
|
||
{
|
||
if (strlen(s) + strlen(s2) + 6 < sizeof(s)) {
|
||
if (n)
|
||
Com_strcat (s, sizeof(s), " and ");
|
||
Com_strcat (s, sizeof(s), s2);
|
||
}
|
||
Com_strcpy (buf, bufSize, s);
|
||
}
|
||
else
|
||
Com_strcpy (buf, bufSize, "no one");
|
||
}
|
||
|
||
void CTFSay_Team (edict_t *who, char *msg)
|
||
{
|
||
char outmsg[1024];
|
||
char buf[1024];
|
||
int i;
|
||
char *p;
|
||
edict_t *cl_ent;
|
||
|
||
outmsg[0] = 0;
|
||
|
||
if (*msg == '\"') {
|
||
msg[strlen(msg) - 1] = 0;
|
||
msg++;
|
||
}
|
||
|
||
for (p = outmsg; *msg && (p - outmsg) < sizeof(outmsg) - 1; msg++) {
|
||
if (*msg == '%') {
|
||
switch (*++msg) {
|
||
case 'l' :
|
||
case 'L' :
|
||
CTFSay_Team_Location(who, buf, sizeof(buf));
|
||
// strcpy(p, buf);
|
||
Com_strcpy(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
|
||
p += strlen(buf);
|
||
break;
|
||
case 'a' :
|
||
case 'A' :
|
||
CTFSay_Team_Armor(who, buf, sizeof(buf));
|
||
// strcpy(p, buf);
|
||
Com_strcpy(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
|
||
p += strlen(buf);
|
||
break;
|
||
case 'h' :
|
||
case 'H' :
|
||
CTFSay_Team_Health(who, buf, sizeof(buf));
|
||
// strcpy(p, buf);
|
||
Com_strcpy(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
|
||
p += strlen(buf);
|
||
break;
|
||
case 't' :
|
||
case 'T' :
|
||
CTFSay_Team_Tech(who, buf, sizeof(buf));
|
||
// strcpy(p, buf);
|
||
Com_strcpy(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
|
||
p += strlen(buf);
|
||
break;
|
||
case 'w' :
|
||
case 'W' :
|
||
CTFSay_Team_Weapon(who, buf, sizeof(buf));
|
||
// strcpy(p, buf);
|
||
Com_strcpy(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
|
||
p += strlen(buf);
|
||
break;
|
||
|
||
case 'n' :
|
||
case 'N' :
|
||
CTFSay_Team_Sight(who, buf, sizeof(buf));
|
||
// strcpy(p, buf);
|
||
Com_strcpy(p, sizeof(outmsg) - (p - outmsg) - 1, buf);
|
||
p += 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)
|
||
{
|
||
if (!(cl_ent->svflags & SVF_MONSTER))
|
||
gi.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
|
||
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");
|
||
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
|
||
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");
|
||
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;
|
||
}
|
||
|
||
|
||
/*-----------------------------------------------------------------------*/
|
||
|
||
void CTFJoinTeam(edict_t *ent, int desired_team)
|
||
{
|
||
char *s;
|
||
|
||
PMenu_Close(ent);
|
||
|
||
ent->svflags &= ~SVF_NOCLIENT;
|
||
ent->client->resp.ctf_team = desired_team;
|
||
ent->client->resp.ctf_state = CTF_STATE_START;
|
||
s = Info_ValueForKey (ent->client->pers.userinfo, "skin");
|
||
CTFAssignSkin(ent, s);
|
||
|
||
/* if (!hokuto->value)
|
||
{*/
|
||
PutClientInServer (ent);
|
||
// add a teleportation effect
|
||
ent->s.event = EV_PLAYER_TELEPORT;
|
||
// hold in place briefly
|
||
ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT;
|
||
ent->client->ps.pmove.pm_time = 14;
|
||
gi.bprintf(PRINT_HIGH, "%s joined the %s team.\n",
|
||
ent->client->pers.netname, CTFTeamName(ent->client->resp.ctf_team/*desired_team*/));
|
||
/* }
|
||
else ZigockJoinMenu(ent);*/
|
||
}
|
||
|
||
void CTFJoinTeam1(edict_t *ent, pmenu_t *p)
|
||
{
|
||
CTFJoinTeam(ent, CTF_TEAM1);
|
||
}
|
||
|
||
void CTFJoinTeam2(edict_t *ent, pmenu_t *p)
|
||
{
|
||
CTFJoinTeam(ent, CTF_TEAM2);
|
||
}
|
||
|
||
void CTFChaseCam(edict_t *ent, pmenu_t *p)
|
||
{
|
||
int i;
|
||
edict_t *e;
|
||
|
||
if (ent->client->chase_target) {
|
||
ent->client->chase_target = NULL;
|
||
PMenu_Close(ent);
|
||
return;
|
||
}
|
||
|
||
for (i = 1; i <= maxclients->value; i++) {
|
||
e = g_edicts + i;
|
||
if (e->inuse && e->solid != SOLID_NOT) {
|
||
ent->client->chase_target = e;
|
||
PMenu_Close(ent);
|
||
ent->client->update_chase = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
void CTFReturnToMain(edict_t *ent, pmenu_t *p)
|
||
{
|
||
PMenu_Close(ent);
|
||
CTFOpenJoinMenu(ent);
|
||
}
|
||
|
||
void CTFCredits(edict_t *ent, pmenu_t *p);
|
||
|
||
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);
|
||
}
|
||
|
||
pmenu_t creditsmenu[] = {
|
||
{ "*Quake II", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "*ThreeWave Capture the Flag", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ NULL, PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "*Programming", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "Dave 'Zoid' Kirsch", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "*Level Design", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "Christian Antkow", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "Tim Willits", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "Dave 'Zoid' Kirsch", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "*Art", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "Adrian Carmack Paul Steed", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "Kevin Cloud", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "*Sound", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "Tom 'Bjorn' Klok", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "*Original CTF Art Design", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "Brian 'Whaleboy' Cozzens", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ NULL, PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "Return to Main Menu", PMENU_ALIGN_LEFT, NULL, CTFReturnToMain }
|
||
};
|
||
|
||
|
||
pmenu_t joinmenu[] = {
|
||
{ "*Quake II", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "*ThreeWave Capture the Flag", PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ NULL, PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ NULL, PMENU_ALIGN_CENTER, NULL, NULL },
|
||
{ "Join Red Team", PMENU_ALIGN_LEFT, NULL, CTFJoinTeam1 },
|
||
{ NULL, PMENU_ALIGN_LEFT, NULL, NULL },
|
||
{ "Join Blue Team", PMENU_ALIGN_LEFT, NULL, CTFJoinTeam2 },
|
||
{ NULL, PMENU_ALIGN_LEFT, NULL, NULL },
|
||
{ "Chase Camera", PMENU_ALIGN_LEFT, NULL, CTFChaseCam },
|
||
{ "Credits", PMENU_ALIGN_LEFT, NULL, CTFCredits },
|
||
{ NULL, PMENU_ALIGN_LEFT, NULL, NULL },
|
||
{ "Use [ and ] to move cursor", PMENU_ALIGN_LEFT, NULL, NULL },
|
||
{ "ENTER to select", PMENU_ALIGN_LEFT, NULL, NULL },
|
||
{ "ESC to Exit Menu", PMENU_ALIGN_LEFT, NULL, NULL },
|
||
{ "(TAB to Return)", PMENU_ALIGN_LEFT, NULL, NULL },
|
||
{ NULL, PMENU_ALIGN_LEFT, NULL, NULL },
|
||
{ "v" CTF_STRING_VERSION, PMENU_ALIGN_RIGHT, NULL, NULL },
|
||
};
|
||
|
||
int CTFUpdateJoinMenu(edict_t *ent)
|
||
{
|
||
static char levelname[32];
|
||
static char team1players[32];
|
||
static char team2players[32];
|
||
int num1, num2, i;
|
||
|
||
joinmenu[4].text = "Join Red Team";
|
||
joinmenu[4].SelectFunc = CTFJoinTeam1;
|
||
joinmenu[6].text = "Join Blue Team";
|
||
joinmenu[6].SelectFunc = CTFJoinTeam2;
|
||
|
||
if (ctf_forcejoin->string && *ctf_forcejoin->string) {
|
||
if (Q_stricmp(ctf_forcejoin->string, "red") == 0) {
|
||
joinmenu[6].text = NULL;
|
||
joinmenu[6].SelectFunc = NULL;
|
||
} else if (Q_stricmp(ctf_forcejoin->string, "blue") == 0) {
|
||
joinmenu[4].text = NULL;
|
||
joinmenu[4].SelectFunc = NULL;
|
||
}
|
||
}
|
||
|
||
if (ent->client->chase_target)
|
||
joinmenu[8].text = "Leave Chase Camera";
|
||
else
|
||
joinmenu[8].text = "Chase Camera";
|
||
|
||
levelname[0] = '*';
|
||
if (g_edicts[0].message)
|
||
strncpy(levelname+1, g_edicts[0].message, sizeof(levelname) - 2);
|
||
else
|
||
strncpy(levelname+1, level.mapname, sizeof(levelname) - 2);
|
||
levelname[sizeof(levelname) - 1] = 0;
|
||
|
||
num1 = num2 = 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++;
|
||
}
|
||
|
||
Com_sprintf (team1players, sizeof(team1players), " (%d players)", num1);
|
||
Com_sprintf (team2players, sizeof(team2players), " (%d players)", num2);
|
||
|
||
joinmenu[2].text = levelname;
|
||
if (joinmenu[4].text)
|
||
joinmenu[5].text = team1players;
|
||
else
|
||
joinmenu[5].text = NULL;
|
||
if (joinmenu[6].text)
|
||
joinmenu[7].text = team2players;
|
||
else
|
||
joinmenu[7].text = NULL;
|
||
|
||
if (num1 > num2)
|
||
return CTF_TEAM1;
|
||
else if (num2 > num1)
|
||
return CTF_TEAM1;
|
||
return (rand() & 1) ? CTF_TEAM1 : CTF_TEAM2;
|
||
}
|
||
|
||
void CTFOpenJoinMenu(edict_t *ent)
|
||
{
|
||
int team;
|
||
|
||
team = CTFUpdateJoinMenu(ent);
|
||
if (ent->client->chase_target)
|
||
team = 8;
|
||
else if (team == CTF_TEAM1)
|
||
team = 4;
|
||
else
|
||
team = 6;
|
||
PMenu_Open(ent, joinmenu, team, sizeof(joinmenu) / sizeof(pmenu_t));
|
||
}
|
||
|
||
void CTFCredits(edict_t *ent, pmenu_t *p)
|
||
{
|
||
PMenu_Close(ent);
|
||
PMenu_Open(ent, creditsmenu, -1, sizeof(creditsmenu) / sizeof(pmenu_t));
|
||
}
|
||
|
||
qboolean CTFStartClient(edict_t *ent)
|
||
{
|
||
if (ent->client->resp.ctf_team != CTF_NOTEAM)
|
||
return false;
|
||
|
||
if (!((int)dmflags->value & DF_CTF_FORCEJOIN)) {
|
||
// 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);
|
||
|
||
CTFOpenJoinMenu(ent);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
qboolean CTFCheckRules(void)
|
||
{
|
||
if (capturelimit->value &&
|
||
(ctfgame.team1 >= capturelimit->value ||
|
||
ctfgame.team2 >= capturelimit->value)) {
|
||
gi.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;
|
||
if (ent->model)
|
||
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;
|
||
}
|
||
|
||
//PON
|
||
void SpawnExtra(vec3_t position,char *classname);
|
||
void ED_CallSpawn (edict_t *ent);
|
||
|
||
//------------------------
|
||
//
|
||
// Route setup
|
||
//
|
||
// Not only for CTF but also DM
|
||
//
|
||
//------------------------
|
||
void CTFSetupNavSpawn()
|
||
{
|
||
FILE *fpout;
|
||
char name[256];
|
||
char code[8];
|
||
char SRCcode[8];
|
||
int i,j;
|
||
vec3_t v;
|
||
edict_t *other;
|
||
|
||
unsigned int size;
|
||
|
||
//PONKO
|
||
spawncycle = level.time + FRAMETIME * 100;
|
||
//PONKO
|
||
//<2F><><EFBFBD>[<5B>g<EFBFBD><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
CurrentIndex = 0;
|
||
memset(Route,0,sizeof(Route));
|
||
memset(code,0,8);
|
||
|
||
if (!ctf->value)
|
||
Com_sprintf (name, sizeof(name), ".\\%s\\chdtm\\%s.chn",gamepath->string,level.mapname);
|
||
else
|
||
Com_sprintf (name, sizeof(name), ".\\%s\\chctf\\%s.chf",gamepath->string,level.mapname);
|
||
|
||
fpout = fopen(name,"rb");
|
||
if (fpout == NULL)
|
||
{
|
||
if (!ctf->value) gi.dprintf("Chaining: file %s.chn not found.\n",level.mapname);
|
||
else gi.dprintf("Chaining: file %s.chf not found.\n",level.mapname);
|
||
}
|
||
else
|
||
{
|
||
fread(code,sizeof(char),8,fpout);
|
||
|
||
if (!ctf->value) strncpy(SRCcode,"3ZBRGDTM",8);
|
||
else strncpy(SRCcode,"3ZBRGCTF",8);
|
||
|
||
if (strncmp(code,SRCcode,8))
|
||
{
|
||
CurrentIndex = 0;
|
||
gi.dprintf("Chaining: %s.chn is not a chaining file.\n",level.mapname);
|
||
fclose(fpout);
|
||
return;
|
||
}
|
||
gi.dprintf("Chaining: %s.chn founded.\n",level.mapname);
|
||
fread(&CurrentIndex,sizeof(int),1,fpout);
|
||
|
||
size = (unsigned int)CurrentIndex * sizeof(route_t);
|
||
fread(Route,size,1,fpout);
|
||
|
||
for (i = 0;i < CurrentIndex;i++)
|
||
{
|
||
if (Route[i].state == GRS_TELEPORT)
|
||
gi.dprintf("GRS_TELEPORT\n");
|
||
if ((Route[i].state > GRS_TELEPORT/*GRS_ONROTATE*/ && Route[i].state <= GRS_PUSHBUTTON)
|
||
|| Route[i].state == GRS_REDFLAG || Route[i].state == GRS_BLUEFLAG)
|
||
{
|
||
other = &g_edicts[(int)maxclients->value+1];
|
||
for ( j=maxclients->value+1 ; j<globals.num_edicts ; j++, other++)
|
||
{
|
||
if (other->inuse)
|
||
{
|
||
if (Route[i].state == GRS_ONPLAT
|
||
|| Route[i].state == GRS_ONTRAIN
|
||
|| Route[i].state == GRS_PUSHBUTTON
|
||
|| Route[i].state == GRS_ONDOOR)
|
||
{
|
||
VectorAdd(other->s.origin,other->mins,v);
|
||
if (VectorCompare (Route[i].Pt,v/*other->monsterinfo.last_sighting*/))
|
||
{
|
||
//onplat
|
||
if (Route[i].state == GRS_ONPLAT
|
||
&& (!Q_stricmp(other->classname, "func_plat") || !Q_stricmp(other->classname, "func_plat2")) )
|
||
{
|
||
//gi.dprintf("assingned %s\n",other->classname);
|
||
Route[i].ent = other;
|
||
break;
|
||
}
|
||
//train
|
||
else if (Route[i].state == GRS_ONTRAIN
|
||
&& !Q_stricmp(other->classname, "func_train"))
|
||
{
|
||
//gi.dprintf("assingned %s\n",other->classname);
|
||
Route[i].ent = other;
|
||
break;
|
||
}
|
||
//button
|
||
else if (Route[i].state == GRS_PUSHBUTTON
|
||
&& !Q_stricmp(other->classname, "func_button"))
|
||
{
|
||
//gi.dprintf("assingned %s\n",other->classname);
|
||
Route[i].ent = other;
|
||
break;
|
||
}
|
||
//door
|
||
else if (Route[i].state == GRS_ONDOOR
|
||
&& !Q_stricmp(other->classname, "func_door"))
|
||
{
|
||
//gi.dprintf("assingned %s\n",other->classname);
|
||
Route[i].ent = other;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else if (Route[i].state == GRS_ITEMS
|
||
|| Route[i].state == GRS_REDFLAG || Route[i].state == GRS_BLUEFLAG)
|
||
{
|
||
//else gi.dprintf("CYAU!!!!!!!\n");
|
||
if (VectorCompare (Route[i].Pt,other->monsterinfo.last_sighting/*->s.origin*/))
|
||
{
|
||
//onplat
|
||
if (1/*other->classname[0] == 'w' || other->classname[0] == 'i'*/)
|
||
{
|
||
//if (Route[i].state == GRS_REDFLAG || Route[i].state == GRS_BLUEFLAG)
|
||
//gi.dprintf("HATA ATTA!!!!!!!\n");
|
||
//gi.dprintf("assingned %s\n",other->classname);
|
||
Route[i].ent = other;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//if (Route[i].state == GRS_REDFLAG || Route[i].state == GRS_BLUEFLAG)
|
||
//gi.dprintf("HATA Dame!!!!!!!\n");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (j >= globals.num_edicts && (Route[i].state == GRS_ITEMS || Route[i].state == GRS_REDFLAG || Route[i].state == GRS_BLUEFLAG)) gi.dprintf("kicked item\n");
|
||
if (j >= globals.num_edicts) Route[i].state = GRS_NORMAL;
|
||
}
|
||
}
|
||
gi.dprintf("Chaining: Total %i chaining pod assigned.\n",CurrentIndex);
|
||
fclose(fpout);
|
||
}
|
||
return;
|
||
}
|
||
|
||
void SpawnExtra(vec3_t position,char *classname)
|
||
{
|
||
edict_t *it_ent;
|
||
|
||
it_ent = G_Spawn();
|
||
|
||
it_ent->classname = classname;
|
||
VectorCopy(position,it_ent->s.origin);
|
||
ED_CallSpawn(it_ent);
|
||
|
||
if (ctf->value && chedit->value)
|
||
{
|
||
it_ent->moveinfo.speed = -1;
|
||
it_ent->s.effects |= EF_QUAD;
|
||
}
|
||
}
|
||
|
||
void CTFJobAssign (void)
|
||
{
|
||
int i;
|
||
int defend1,defend2; //<2F>f<EFBFBD>B<EFBFBD>t<EFBFBD>F<EFBFBD><46><EFBFBD>_<EFBFBD>[<5B><><EFBFBD><EFBFBD>
|
||
int mate1,mate2; //<2F>`<60>[<5B><><EFBFBD><EFBFBD><EFBFBD>C<EFBFBD>g<EFBFBD><67><EFBFBD><EFBFBD>
|
||
gclient_t *client;
|
||
edict_t *e;
|
||
edict_t *defei1,*defei2; //<2F><><EFBFBD><EFBFBD>
|
||
edict_t *geti1,*geti2; //<2F><><EFBFBD><EFBFBD>
|
||
|
||
defend1 = 0;
|
||
defend2 = 0;
|
||
mate1 = 0;
|
||
mate2 = 0;
|
||
defei1 = NULL;
|
||
defei2 = NULL;
|
||
geti1 = NULL;
|
||
geti2 = NULL;
|
||
|
||
e = &g_edicts[(int)maxclients->value];
|
||
for ( i = maxclients->value ; i >= 1 ; i--, e--)
|
||
{
|
||
if (e->inuse)
|
||
{
|
||
client = e->client;
|
||
if (client->zc.ctfstate == CTFS_NONE) client->zc.ctfstate = CTFS_DEFENDER;
|
||
//if (client->zc.ctfstate == CTFS_CARRIER)
|
||
//gi.bprintf(PRINT_HIGH,"I am carrierY!!\n");
|
||
if (e->client->resp.ctf_team == CTF_TEAM1)
|
||
{
|
||
mate1++;
|
||
if ( e->client->pers.inventory[ITEM_INDEX(FindItem("Blue Flag"))])
|
||
{
|
||
client->zc.ctfstate = CTFS_CARRIER;
|
||
}
|
||
if (1/*e->svflags & SVF_MONSTER*/)
|
||
{
|
||
|
||
if ( client->zc.ctfstate == CTFS_OFFENCER && random()>0.7) defei1 = e;
|
||
else if ( client->zc.ctfstate == CTFS_DEFENDER)
|
||
{
|
||
if (random()>0.7) geti1 = e;
|
||
defend1++;
|
||
}
|
||
else if ( client->zc.ctfstate == CTFS_CARRIER ) defend1++;
|
||
}
|
||
}
|
||
else if (e->client->resp.ctf_team == CTF_TEAM2)
|
||
{
|
||
mate2++;
|
||
if ( e->client->pers.inventory[ITEM_INDEX(FindItem("Red Flag"))])
|
||
{
|
||
client->zc.ctfstate = CTFS_CARRIER;
|
||
}
|
||
if (1/*e->svflags & SVF_MONSTER*/)
|
||
{
|
||
if ( client->zc.ctfstate == CTFS_OFFENCER && random()>0.8) defei2 = e;
|
||
else if ( client->zc.ctfstate == CTFS_DEFENDER)
|
||
{
|
||
if (random()>0.7) geti2 = e;
|
||
defend2++;
|
||
}
|
||
else if ( client->zc.ctfstate == CTFS_CARRIER ) defend2++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (defend1 < mate1 / 3 && mate1 >= 2)
|
||
{
|
||
if (defei1 != NULL) defei1->client->zc.ctfstate = CTFS_DEFENDER;
|
||
}
|
||
else if (defend1 > mate1 / 3 )
|
||
{
|
||
if (geti1 != NULL) geti1->client->zc.ctfstate = CTFS_OFFENCER;
|
||
}
|
||
if (defend2 < mate2 / 3 && mate2 >= 2)
|
||
{
|
||
if (defei2 != NULL) defei2->client->zc.ctfstate = CTFS_DEFENDER;
|
||
}
|
||
else if (defend2 > mate2 / 3 )
|
||
{
|
||
if (geti2 != NULL) geti2->client->zc.ctfstate = CTFS_OFFENCER;
|
||
}
|
||
/// gi.bprintf(PRINT_HIGH,"Called!!!!\n");
|
||
} |