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

3805 lines
No EOL
96 KiB
C
Raw Blame History

#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");
}