sin-sdk/ctf.cpp
1999-11-02 00:00:00 +00:00

1745 lines
42 KiB
C++

//-----------------------------------------------------------------------------
//
// $Logfile:: /Quake 2 Engine/Sin/code/game/ctf.cpp $
// $Revision:: 26 $
// $Author:: Markd $
// $Date:: 11/02/99 1:42p $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /Quake 2 Engine/Sin/code/game/ctf.cpp $
//
// 26 11/02/99 1:42p Markd
// Made forcejoin 0
//
// 25 11/02/99 12:30p Markd
// made forcejoin default to 1 with archive bit set
//
// 24 11/02/99 12:09p Markd
// Fixed CTF cheat bug
//
// 23 3/26/99 6:26p Aldie
// Fixed more CTF bugs, probably the last ones
//
// 22 3/22/99 2:09p Aldie
// various fixes
//
// 21 3/19/99 9:42p Aldie
// got rid of precache
//
// 20 3/19/99 8:58p Aldie
// Added precaches for grapple hook
//
// 19 3/19/99 4:32p Aldie
// Externed ctfgame and added return sounds
//
// 18 3/19/99 4:48p Aldie
//
// 17 3/18/99 6:47p Aldie
// techs are non-solid before removal
//
// 16 3/18/99 6:43p Aldie
// Fixed tech bug where tech was being removed twice in a row
//
// 15 3/18/99 3:37p Aldie
// Changed the layout of score and identify
//
// 14 3/17/99 3:55p Aldie
// Incremental CTF update
//
// 13 3/16/99 12:41p Aldie
// Changed scoreboard
//
// 12 3/16/99 11:50a Aldie
// Fixed assertion bug
//
// 11 3/14/99 1:31a Aldie
// fixed some DeatQuad damage code
//
// 10 3/12/99 10:18p Aldie
// Added "celebrate"
//
// 9 3/12/99 8:13p Aldie
// Added deathquad
//
// 8 3/12/99 4:54p Aldie
// Added CTF_CalcScores
//
// 7 3/11/99 8:57p Jimdose
// force deathmatch to 1 if ctf is set
//
// 6 3/11/99 3:45p Aldie
// Fixed some scoring bugs
//
// 5 3/03/99 12:37p Aldie
// Made the info_player stuff actual objects
//
// 4 3/02/99 9:06p Aldie
// Added CTF game code
//
// 2 2/16/99 4:08p Aldie
//
// 1 2/11/99 1:38p Aldie
//
// 3 10/10/98 3:37a Jimdose
// Began converting to Sin
//
// 2 10/10/98 3:03a Jimdose
// Created file
//
// 1 10/10/98 3:02a Jimdose
//
// DESCRIPTION:
// Game code for Capture the Flag.
//
// The original source for this code was graciously provided by Zoid and
// Id Software. Many thanks!
//
// Original credits:
//
// Programming - Dave 'Zoid' Kirsch
//
#include "g_local.h"
#include "ctf.h"
#include "player.h"
#include "PlayerStart.h"
cvar_t *ctf;
cvar_t *ctf_forcejoin;
cvar_t *ctf_hardcorps_skin;
cvar_t *ctf_sintek_skin;
cvar_t *capturelimit;
cvar_t *ctf_missingflags;
ctfgame_t ctfgame;
typedef SafePtr<CTF_Flag_Hardcorps> HardcorpsFlagPtr;
typedef SafePtr<CTF_Flag_Sintek> SintekFlagPtr;
static HardcorpsFlagPtr hardcorps_flag=NULL;
static SintekFlagPtr sintek_flag=NULL;
qboolean techspawn = false;
void CTF_Init
(
void
)
{
ctf = gi.cvar("ctf", "0", CVAR_SERVERINFO|CVAR_LATCH);
ctf_forcejoin = gi.cvar("ctf_forcejoin", "0", CVAR_ARCHIVE);
ctf_hardcorps_skin = gi.cvar("ctf_hardcorps_skin", "hc_skin", CVAR_SERVERINFO);
ctf_sintek_skin = gi.cvar("ctf_sintek_skin", "sintek_skin", CVAR_SERVERINFO);
ctf_missingflags = gi.cvar("ctf_missingflags", "0", 0 );
memset( &ctfgame, 0, sizeof( ctfgame ) );
techspawn = false;
if ( ctf->value )
{
// force deathmatch on
gi.cvar_set( "deathmatch", "1" );
}
}
void CTF_InitTech
(
void
)
{
Entity *tech;
tech = new CTF_Tech_Double;
tech = new CTF_Tech_Shield;
tech = new CTF_Tech_Regeneration;
tech = new CTF_Tech_Empathy;
tech = new CTF_Tech_DeathQuad;
}
void CTF_InitFlags
(
void
)
{
int num = 0;
// Find the flags
if ( num = G_FindClass( 0, "CTF_Flag_Hardcorps" ) )
hardcorps_flag = ( CTF_Flag_Hardcorps * )G_GetEntity( num );
if ( num = G_FindClass( 0, "CTF_Flag_Sintek" ) )
sintek_flag = ( CTF_Flag_Sintek * )G_GetEntity( num );
// Check for errors
if ( !hardcorps_flag )
gi.error( "CTF: Hardcorps flag not found!\n" );
if ( !sintek_flag )
gi.error( "CTF: Sintek flag not found!\n" );
// Init the tech
if ( !techspawn )
{
CTF_InitTech();
techspawn = true;
}
}
qboolean CTF_CheckRules
(
void
)
{
if ( capturelimit->value &&
( ctfgame.team_hardcorps >= capturelimit->value || ctfgame.team_sintek >= capturelimit->value ) )
{
return true;
}
return false;
}
void CTF_SetIDView
(
Player *player
)
{
Vector dir, pos, end;
trace_t tr;
player->client->ps.stats[STAT_CTF_ID_VIEW] = 0;
player->GetMuzzlePositionAndDirection(&pos, &dir);
end = pos + dir * 2048;
tr = gi.trace( pos.vec3(), vec_zero.vec3(), vec_zero.vec3(), end.vec3(), player->edict, MASK_SHOT);
if ( tr.fraction < 1 && tr.ent && tr.ent->client )
{
// Add the team number and status in the upper byte
byte status;
byte teamnum;
byte comp;
if ( tr.ent->client->resp.ctf_team != player->client->resp.ctf_team )
status = 0;
else if ( tr.ent->entity->health >= 100 )
status = 1; // excellent
else if ( tr.ent->entity->health >= 75 )
status = 2; // good
else if ( tr.ent->entity->health >= 50 )
status = 3; // fair
else if ( tr.ent->entity->health >= 25 )
status = 4; // poor
else if ( tr.ent->entity->health > 0 )
status = 5; // bad
else
status = 6; // dead
teamnum = tr.ent->client->resp.ctf_team;
// shift the status up 2 bits and or in the team number
comp = ( status << 2 ) | ( teamnum );
player->client->ps.stats[STAT_CTF_ID_VIEW] = ( comp << 8 );
// Or in the client number, but add 1 on the server and subtract one on the client
player->client->ps.stats[STAT_CTF_ID_VIEW] |= ( tr.ent-g_edicts );
return;
}
}
// called when we enter the intermission
void CTF_CalcScores
(
void
)
{
int i;
ctfgame.total_hardcorps = ctfgame.total_sintek = 0;
for ( i = 0; i < maxclients->value; i++ )
{
if (!g_edicts[i+1].inuse)
continue;
if ( game.clients[i].resp.ctf_team == CTF_TEAM_HARDCORPS )
ctfgame.total_hardcorps += game.clients[i].resp.score;
else if ( game.clients[i].resp.ctf_team == CTF_TEAM_SINTEK )
ctfgame.total_sintek += game.clients[i].resp.score;
}
}
void CTF_UpdateStats
(
Player *player
)
{
int i;
int p1, p2;
CTF_Tech *tech;
if ( !hardcorps_flag || !sintek_flag )
{
CTF_InitFlags();
}
// logo headers for the frag display
player->client->ps.stats[STAT_CTF_TEAM1_HEADER] = gi.imageindex ("i_hc_logo");
player->client->ps.stats[STAT_CTF_TEAM2_HEADER] = gi.imageindex ("i_st_logo");
// 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.team_hardcorps > ctfgame.team_sintek )
player->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
else if (ctfgame.team_sintek > ctfgame.team_hardcorps)
player->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
else if (ctfgame.total_hardcorps > ctfgame.total_sintek) // frag tie breaker
player->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
else if (ctfgame.total_sintek > ctfgame.total_hardcorps)
player->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
else
{ // tie game!
player->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
player->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
}
}
// tech icon
player->client->ps.stats[STAT_CTF_TECH] = 0;
if ( (tech = (CTF_Tech *)player->HasItemOfSuperclass( "CTF_Tech" ) ) )
{
player->client->ps.stats[STAT_CTF_TECH] = tech->GetIconIndex();
}
// figure out what icon to display for team logos
// three states:
// flag at base
// flag taken
// flag dropped
p1 = gi.imageindex( "i_ctf_hcflag" );
if ( hardcorps_flag )
{
if ( hardcorps_flag->edict->solid == SOLID_NOT )
{
// not at base
// check if on enemy player
p1 = gi.imageindex( "i_ctf_hcflagd" ); // default to dropped
for ( i = 1; i <= maxclients->value; i++ )
{
if ( g_edicts[i].inuse && g_edicts[i].client )
{
Player *player;
player = ( Player * )g_edicts[i].entity;
if ( player->HasItem( "CTF_Flag_Hardcorps" ) )
{
// enemy has it
p1 = gi.imageindex( "i_ctf_hcflagt" );
break;
}
}
}
// Nobody is holding the flag, make sure there is a dropped flag in the world
if ( i > maxclients->value )
{
int num = 0;
while( ( num = G_FindClass( num, "CTF_Flag_Hardcorps" ) ) != NULL )
{
CTF_Flag *flag;
flag = ( CTF_Flag *)G_GetEntity( num );
if ( flag == hardcorps_flag )
continue;
else
break;
}
// If no flag is found, it's become lost somehow.
if ( !num )
{
Com_Printf( "Lost Hardcorps Flag detected\n" );
gi.cvar_set( "ctf_missingflags", va("%i%", ++ctf_missingflags->value ) );
hardcorps_flag->ProcessEvent( EV_Item_Respawn );
}
}
}
else if ( hardcorps_flag->spawnflags & DROPPED_ITEM )
{
p1 = gi.imageindex( "i_ctf_hcflagd" ); // must be dropped
}
}
p2 = gi.imageindex( "i_ctf_stflag" );
if ( sintek_flag )
{
if ( sintek_flag->edict->solid == SOLID_NOT )
{
// not at base
// check if on enemy player
p2 = gi.imageindex( "i_ctf_stflagd" ); // default to dropped
for ( i = 1; i <= maxclients->value; i++ )
{
if ( g_edicts[i].inuse && g_edicts[i].client )
{
Player *player;
player = ( Player * )g_edicts[i].entity;
if ( player->HasItem( "CTF_Flag_Sintek" ) )
{
// enemy has it
p2 = gi.imageindex( "i_ctf_stflagt" );
break;
}
}
}
// Nobody is holding the flag, make sure there is a dropped flag in the world
if ( i > maxclients->value )
{
int num = 0;
while( ( num = G_FindClass( num, "CTF_Flag_Sintek" ) ) != NULL )
{
CTF_Flag *flag;
flag = ( CTF_Flag *)G_GetEntity( num );
if ( flag == sintek_flag )
continue;
else
break;
}
// If no flag is found, it's become lost somehow.
if ( !num )
{
Com_Printf( "Lost Sintek Flag detected\n" );
gi.cvar_set( "ctf_missingflags", va("%i%", ++ctf_missingflags->value ) );
sintek_flag->ProcessEvent( EV_Item_Respawn );
}
}
}
else if ( sintek_flag->spawnflags & DROPPED_ITEM )
{
p2 = gi.imageindex( "i_ctf_stflagd,tga" ); // must be dropped
}
}
player->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1;
player->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_TEAM_HARDCORPS )
if ( level.framenum & 8 )
player->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1;
else
player->client->ps.stats[STAT_CTF_TEAM1_PIC] = 0;
else
if ( level.framenum & 8 )
player->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2;
else
player->client->ps.stats[STAT_CTF_TEAM2_PIC] = 0;
}
// Update the number of captures for each team
player->client->ps.stats[STAT_CTF_TEAM1_CAPS] = ctfgame.team_hardcorps;
player->client->ps.stats[STAT_CTF_TEAM2_CAPS] = ctfgame.team_sintek;
player->client->ps.stats[STAT_CTF_FLAG_PIC] = 0;
// if hardcorps player has the sintek flag
if ( player->client->resp.ctf_team == CTF_TEAM_HARDCORPS &&
player->HasItem( "CTF_Flag_Sintek" ) &&
( level.framenum & 8 ) )
{
player->client->ps.stats[STAT_CTF_FLAG_PIC] = gi.imageindex( "i_ctf_stflag" );
}
// if sintek player has the hardcorps flag
if ( player->client->resp.ctf_team == CTF_TEAM_SINTEK &&
player->HasItem( "CTF_Flag_Hardcorps" ) &&
( level.framenum & 8 ) )
{
player->client->ps.stats[STAT_CTF_FLAG_PIC] = gi.imageindex( "i_ctf_hcflag" );
}
player->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = 0;
player->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = 0;
if ( player->client->resp.ctf_team == CTF_TEAM_HARDCORPS )
player->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = gi.imageindex( "i_ctfj" );
else if (player->client->resp.ctf_team == CTF_TEAM_SINTEK )
player->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = gi.imageindex( "i_ctfj" );
if (
( level.time > player->client->resp.ctf_idtime ) ||
( player->client->ps.stats[STAT_CTF_ID_VIEW] == 0 )
)
{
player->client->resp.ctf_idtime = level.time + 1.0f;
CTF_SetIDView( player );
}
}
/*
================
SelectCTFSpawnPoint
go to a CTF point, but NOT the two points closest
to other players
================
*/
extern Entity *SelectFarthestDeathmatchSpawnPoint( void );
extern Entity *SelectRandomDeathmatchSpawnPoint( void );
extern float PlayersRangeFromSpot( Entity *spot );
Entity *SelectCTFSpawnPoint
(
edict_t *ent
)
{
Entity *spot, *spot1, *spot2;
int count = 0;
int selection;
float range, range1, range2;
char *cname;
int num = 0;
if ( ent->client )
{
// Client is not in the START state, pick a random spot
if ( ent->client->resp.ctf_state != CTF_STATE_START )
{
// Search for regular deathmatch nodes, if there aren't any then
// spawn at a player's base
num = 0;
if ( ( num = G_FindClass( num, "info_player_deathmatch" ) ) != NULL )
{
count++;
}
// If there is a "info_player_deathmatch" go to that
// otherwise pick a ctf starting point
if ( count )
{
if ( DM_FLAG( DF_SPAWN_FARTHEST ) )
return SelectFarthestDeathmatchSpawnPoint ();
else
return SelectRandomDeathmatchSpawnPoint ();
}
}
// Set the player to PLAYING state
ent->client->resp.ctf_state = CTF_STATE_PLAYING;
switch (ent->client->resp.ctf_team)
{
case CTF_TEAM_HARDCORPS:
cname = "info_player_hardcorps";
break;
case CTF_TEAM_SINTEK:
cname = "info_player_sintek";
break;
default:
return SelectRandomDeathmatchSpawnPoint();
}
}
else
{
return SelectRandomDeathmatchSpawnPoint();
}
spot = NULL;
range1 = range2 = 99999;
spot1 = spot2 = NULL;
count = 0;
while( ( num = G_FindClass( num, cname ) ) != NULL )
{
spot = G_GetEntity( num );
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;
num = 0;
do {
num = G_FindClass( num, cname );
spot = G_GetEntity( num );
if (spot == spot1 || spot == spot2)
selection++;
}
while(selection--);
return spot;
}
qboolean CTF_CanSee
(
Entity *ent1,
Entity *ent2
)
{
Vector delta;
Vector start;
Vector end;
trace_t trace;
start = ent1->centroid;
end = ent2->centroid;
trace = G_Trace( start, vec_zero, vec_zero, end, ent1, MASK_OPAQUE, "CTF_CanSee" );
if ( trace.fraction == 1.0 || trace.ent == ent2->edict )
{
return true;
}
return false;
}
void CTF_FragBonuses
(
Sentient *targ,
Sentient *attacker
)
{
int i;
edict_t *ent;
int otherteam;
Vector v1, v2;
str attackers_flag, enemy_flag;
Player *carrier;
Entity *attackers_base_flag;
// no bonus for fragging yourself
if (!targ->client || !attacker->client || targ == attacker)
return;
otherteam = CTF_OtherTeam( targ->client->resp.ctf_team );
if (otherteam < 0)
return; // whoever died isn't on a team
if ( targ->client->resp.ctf_team == CTF_TEAM_HARDCORPS )
{
attackers_flag = "CTF_Flag_Sintek";
enemy_flag = "CTF_Flag_Hardcorps";
attackers_base_flag = sintek_flag;
}
else
{
attackers_flag = "CTF_Flag_Hardcorps";
enemy_flag = "CTF_Flag_Sintek";
attackers_base_flag = hardcorps_flag;
}
// did the attacker frag the flag carrier?
if ( targ->HasItem( attackers_flag.c_str() ) )
{
attacker->client->resp.ctf_lastfraggedcarrier = level.time;
attacker->client->resp.score += CTF_FRAG_CARRIER_BONUS;
gi.cprintf( attacker->edict, PRINT_MEDIUM, "BONUS: %d points for fragging enemy flag carrier.\n",
CTF_FRAG_CARRIER_BONUS);
// the target had the flag, clear the hurt carrier field on the other team
for (i = 1; i <= maxclients->value; i++)
{
ent = g_edicts + i;
if ( ent->inuse && ent->client->resp.ctf_team == otherteam )
ent->client->resp.ctf_lasthurtcarrier = 0;
}
return;
}
// attacker fragged a guy who hurt his flag carrier
if ( targ->client->resp.ctf_lasthurtcarrier &&
level.time - targ->client->resp.ctf_lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT &&
!attacker->HasItem( enemy_flag.c_str() )
)
{
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,
CTF_TeamName( attacker->client->resp.ctf_team ) );
return;
}
// flag and flag carrier area defense bonuses
if ( !attackers_base_flag )
return; // can't find attacker's flag
// find attacker's team's flag carrier
for (i = 1; i <= maxclients->value; i++)
{
ent = g_edicts + 1 + i;
if ( !ent->inuse)
continue;
carrier = (Player *)(Sentient *)ent->entity;
if ( carrier->HasItem( enemy_flag.c_str() ) )
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
v1 = targ->worldorigin - attackers_base_flag->worldorigin;
v2 = attacker->worldorigin - attackers_base_flag->worldorigin;
if ( v1.length() < CTF_TARGET_PROTECT_RADIUS ||
v2.length() < CTF_TARGET_PROTECT_RADIUS ||
CTF_CanSee( attackers_base_flag, targ ) ||
CTF_CanSee( attackers_base_flag, attacker )
)
{
// we defended the base flag
attacker->client->resp.score += CTF_FLAG_DEFENSE_BONUS;
if ( attackers_base_flag->edict->solid == SOLID_NOT )
{
gi.bprintf(PRINT_MEDIUM, "%s defends the %s base.\n",
attacker->client->pers.netname,
CTF_TeamName( attacker->client->resp.ctf_team ) );
}
else
{
gi.bprintf( PRINT_MEDIUM, "%s defends the %s flag.\n",
attacker->client->pers.netname,
CTF_TeamName( attacker->client->resp.ctf_team ) );
}
return;
}
if ( carrier && ( carrier != attacker ) )
{
v1 = targ->worldorigin - carrier->worldorigin;
v2 = attacker->worldorigin - carrier->worldorigin;
if ( v1.length() < CTF_ATTACKER_PROTECT_RADIUS ||
v2.length() < CTF_ATTACKER_PROTECT_RADIUS ||
CTF_CanSee( carrier, targ ) ||
CTF_CanSee( carrier, attacker )
)
{
// we defended the carrier
attacker->client->resp.score += CTF_CARRIER_PROTECT_BONUS;
gi.bprintf(PRINT_MEDIUM, "%s defends the %s's flag carrier.\n",
attacker->client->pers.netname,
CTF_TeamName(attacker->client->resp.ctf_team));
return;
}
}
}
void CTF_CheckHurtCarrier
(
Entity *targ,
Entity *attacker
)
{
str flag;
Sentient *target;
if ( !targ->client || !attacker->client )
return;
if ( targ->client->resp.ctf_team == CTF_TEAM_HARDCORPS )
flag = "ctf_flag_sintek";
else
flag = "ctf_flag_hardcorps";
target = ( Sentient * )targ;
if ( target->HasItem( flag.c_str() ) &&
target->client->resp.ctf_team != attacker->client->resp.ctf_team
)
attacker->client->resp.ctf_lasthurtcarrier = level.time;
}
void CTF_ScoreboardMessage
(
Entity *ent,
Entity *killer
)
{
char entry[1024];
char string[1400];
int len;
int i, j, k;
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;// sort the clients by team and score
int team;
int maxsize = 1000;
Player *client;
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_TEAM_HARDCORPS )
team = 0;
else if ( game.clients[i].resp.ctf_team == CTF_TEAM_SINTEK )
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]++;
}
*string = 0;
len = 0;
// Headers
sprintf( string,
"if 18 xv -146 yv 88 pic 18 endif " // team 1 header
"xv -136 yv 100 string \"%4d/%-3d\" " // team 1 total score
"xv -66 yv 92 num 12 " // team 1 num captures
"if 19 xv 26 yv 88 pic 19 endif " // team 2 header
"xv 36 yv 100 string \"%4d/%-3d\" " // team 2 total score
"xv 96 yv 92 num 14 ", // team 2 num captures
totalscore[0], total[0],
totalscore[1], total[1] );
len = strlen(string);
for (i=0 ; i<16 ; i++)
{
if (i >= total[0] && i >= total[1])
break; // we're done
sprintf( entry, "yv %d ", 80 - i * 8 );
if ( maxsize - len > strlen( entry ) )
{
strcat(string, entry);
len = strlen(string);
}
// left side
if ( i < total[0] )
{
cl = &game.clients[sorted[0][i]];
cl_ent = g_edicts + 1 + sorted[0][i];
#if 0
sprintf( entry + strlen( entry ),
"xv -130 %s \"%3d %3d %-12.12s\" ",
( cl_ent == ent->edict ) ? "string2" : "string",
cl->resp.score,
( cl->ping > 999 ) ? 999 : cl->ping,
cl->pers.netname );
#else
sprintf( entry + strlen( entry ),
"xv -162 ctfci %i %i %i %i %i ",
sorted[0][i],
cl->resp.score,
cl->ping,
( cl_ent == ent->edict ),
cl->resp.ctf_team
);
#endif
client = (Player *)(Sentient *)cl_ent->entity;
if ( client->HasItem( "CTF_Flag_Sintek" ) )
{
sprintf( entry + strlen( entry ), "xv -104 picn i_has_stflag " );
}
if ( maxsize - len > strlen( entry ) )
{
strcat( string, entry );
len = strlen( string );
last[0] = i;
}
}
if ( i < total[1] )
{
cl = &game.clients[sorted[1][i]];
cl_ent = g_edicts + 1 + sorted[1][i];
#if 0
sprintf( entry + strlen( entry ),
"xv 10 %s \"%3d %3d %-12.12s\" ",
( cl_ent == ent->edict ) ? "string2" : "string",
cl->resp.score,
( cl->ping > 999 ) ? 999 : cl->ping,
cl->pers.netname );
#else
sprintf( entry + strlen( entry ),
"xv 10 ctfci %i %i %i %i %i ",
sorted[1][i],
cl->resp.score,
cl->ping,
( cl_ent == ent->edict ),
cl->resp.ctf_team
);
#endif
client = (Player *)(Sentient *)cl_ent->entity;
if ( client->HasItem( "CTF_Flag_Hardcorps" ) )
{
sprintf( entry + strlen( entry ), "xv 66 picn i_has_hcflag " );
}
if ( maxsize - len > strlen( entry ) )
{
strcat( string, entry );
len = strlen( string );
last[1] = i;
}
}
}
gi.WriteByte (svc_layout);
gi.WriteString (string);
}
char *CTF_TeamName
(
int team
)
{
switch (team)
{
case CTF_TEAM_HARDCORPS:
return "HardCorps";
case CTF_TEAM_SINTEK:
return "Sintek";
}
return "Unknown";
}
char *CTF_OtherTeamName
(
int team
)
{
switch (team)
{
case CTF_TEAM_HARDCORPS:
return "Sintek";
case CTF_TEAM_SINTEK:
return "HardCorps";
}
return "UKNOWN";
}
int CTF_OtherTeam
(
int team
)
{
switch (team)
{
case CTF_TEAM_HARDCORPS:
return CTF_TEAM_SINTEK;
case CTF_TEAM_SINTEK:
return CTF_TEAM_HARDCORPS;
}
return -1; // invalid value
}
// FLAGS
CLASS_DECLARATION( InventoryItem, CTF_Flag, NULL );
Event EV_Flag_Reset( "resetflag" );
ResponseDef CTF_Flag::Responses[] =
{
{ &EV_Item_Pickup, (Response)CTF_Flag::PickupFlag },
{ &EV_Flag_Reset, (Response)CTF_Flag::ResetFlag },
{ NULL, NULL }
};
CTF_Flag::CTF_Flag
(
)
{
limp = false;
modelIndex( "ctf_flag_sintek_w.def" );
modelIndex( "ctf_flag_hardcorps_w.def" );
}
void CTF_Flag::ResetFlag
(
Event *ev
)
{
// The autoreturn time has timed out. This should be a dropped flag,
// remove it and respawn back at the home base.
assert( spawnflags & DROPPED_PLAYER_ITEM );
if ( ctf_team == CTF_TEAM_HARDCORPS )
hardcorps_flag->ProcessEvent( EV_Item_Respawn );
else if ( ctf_team == CTF_TEAM_SINTEK )
sintek_flag->ProcessEvent( EV_Item_Respawn );
if ( !( spawnflags & DROPPED_PLAYER_ITEM ) )
{
gi.dprintf( "CTF_Flag::ResetFlag: Tried to reset a non-dropped flag\n" );
return;
}
RandomGlobalSound( "snd_return", 1, CHAN_VOICE|CHAN_NO_PHS_ADD, ATTN_NONE );
gi.bprintf( PRINT_HIGH, "The %s flag has returned!\n", CTF_TeamName( ctf_team ) );
PostEvent( EV_Remove, 0 );
}
void CTF_Flag::PickupFlag
(
Event *ev
)
{
Player *player;
str enemy_flag, realname;
Item *item;
Entity *other;
edict_t *ent;
int i;
other = ev->GetEntity( 1 );
if ( !other->isClient() )
return;
player = (Player *)(Sentient *)other;
// Figure out the enemy flag
if ( ctf_team == CTF_TEAM_HARDCORPS )
{
enemy_flag = "CTF_Flag_Sintek";
}
else
{
enemy_flag = "CTF_Flag_Hardcorps";
}
if ( player->client->resp.ctf_team == ctf_team ) // Player touched his own flag
{
if ( !( spawnflags & ( DROPPED_ITEM|DROPPED_PLAYER_ITEM ) ) ) // This flag was not dropped, so it's at home base
{
if ( player->FindItem( enemy_flag.c_str() ) ) // Check to see if player has enemy's flag
{
// Player has the enemy flag and he just touched his team's flag, so he just scored
gi.bprintf( PRINT_HIGH, "%s captured the %s flag!\n", other->client->pers.netname, CTF_OtherTeamName( ctf_team ) );
RandomGlobalSound( "snd_flagscore", 1, CHAN_VOICE|CHAN_NO_PHS_ADD, ATTN_NONE );
// Take the enemy flag away from the player
player->takeItem( enemy_flag.c_str(), 1 );
player->edict->s.color_r = 0;
player->edict->s.color_g = 0;
player->edict->s.color_b = 0;
player->edict->s.radius = 0;
player->edict->s.renderfx &= ~RF_DLIGHT;
if ( !hardcorps_flag || !sintek_flag )
CTF_InitFlags();
// Respawn the enemy's flag at it's home base
if ( enemy_flag == "CTF_Flag_Hardcorps" )
hardcorps_flag->ProcessEvent( EV_Item_Respawn );
else if ( enemy_flag == "CTF_Flag_Sintek" )
sintek_flag->ProcessEvent( EV_Item_Respawn );
// Update capture time and team
ctfgame.last_flag_capture = level.time;
ctfgame.last_capture_team = ctf_team;
// Increment the team score
if ( ctf_team == CTF_TEAM_HARDCORPS )
ctfgame.team_hardcorps++;
else
ctfgame.team_sintek++;
// Player gets a bonus score for capture
other->client->resp.score += CTF_CAPTURE_BONUS;
// Team bonus awards
for( i = 0; i < maxclients->value; i++ )
{
ent = &g_edicts[ i + 1 ];
if ( !ent->inuse || !ent->client || !ent->entity || ( ent == other->edict ) )
{
continue;
}
// Team members get a bonus as well
if ( ent->client->resp.ctf_team == other->client->resp.ctf_team )
{
Sentient *sent;
sent = ( Sentient * )ent->entity;
if ( ent != other->edict )
ent->client->resp.score += CTF_TEAM_BONUS;
if ( !sent->deadflag )
{
sent->TempAnim( "celebrate", NULL );
}
}
if ( ent->client->resp.ctf_lastreturnedflag + CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time )
{
gi.bprintf(PRINT_HIGH, "%s gets an assist for returning the flag!\n", ent->client->pers.netname);
ent->client->resp.score += CTF_RETURN_FLAG_ASSIST_BONUS;
}
if ( ent->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", ent->client->pers.netname);
ent->client->resp.score += CTF_FRAG_CARRIER_ASSIST_BONUS;
}
}
}
}
else // Player touched his own flag that has been dropped, return it to his home base
{
if ( ctf_team == CTF_TEAM_HARDCORPS )
{
hardcorps_flag->ProcessEvent( EV_Item_Respawn );
}
else if ( ctf_team == CTF_TEAM_SINTEK )
{
sintek_flag->ProcessEvent( EV_Item_Respawn );
}
PostEvent( EV_Remove, 0 );
RandomGlobalSound( "snd_return", 1, CHAN_VOICE|CHAN_NO_PHS_ADD, ATTN_NONE );
gi.bprintf( PRINT_HIGH, "%s returned the %s flag!\n", other->client->pers.netname, CTF_TeamName( ctf_team ) );
// Add in the recovery bonus score
other->client->resp.score += CTF_RECOVERY_BONUS;
other->client->resp.ctf_lastreturnedflag = level.time;
}
}
else // Player touched enemy flag, pick it up
{
int groupindex;
int tri_num;
Vector orient;
// don't let the player pick it up if they just respawned
if ( player->respawn_time > ( level.time - 0.5f ) )
return;
gi.bprintf( PRINT_HIGH, "%s got the %s flag!\n",other->client->pers.netname, CTF_TeamName( ctf_team ) );
item = player->giveItem( getClassname(), 1, icon_index );
if ( !item )
{
return;
}
// Attach flag to player
if ( gi.GetBoneInfo( player->edict->s.modelindex, "pack", &groupindex, &tri_num, orient.vec3() ) )
{
Vector org;
org = Vector( "0 0 -32" );
item->showModel();
item->setOrigin( org );
item->attach( player->entnum, groupindex, tri_num, orient );
}
else
{
gi.dprintf( "attach failed\n" );
}
item->CancelEventsOfType( EV_Item_DropToFloor );
item->CancelEventsOfType( EV_Item_Respawn );
item->CancelEventsOfType( EV_FadeOut );
if ( ctf_team == CTF_TEAM_HARDCORPS )
{
player->edict->s.color_r = 0;
player->edict->s.color_g = 0;
player->edict->s.color_b = 1.0;
player->edict->s.radius = 150;
player->edict->s.renderfx |= RF_DLIGHT;
}
else if ( ctf_team == CTF_TEAM_SINTEK )
{
player->edict->s.color_r = 1.0;
player->edict->s.color_g = 0;
player->edict->s.color_b = 0;
player->edict->s.radius = 150;
player->edict->s.renderfx |= RF_DLIGHT;
}
if ( spawnflags & ( DROPPED_ITEM|DROPPED_PLAYER_ITEM ) )
{
// Remove this dropped flag because a player just picked it up
PostEvent( EV_Remove, 0 );
// Pickup sound
realname = GetRandomAlias( "snd_pickup" );
if ( realname.length() > 1 )
{
player->sound( realname, 1, CHAN_ITEM, ATTN_NORM );
}
}
else
{
// Broadcast an alert sound
RandomGlobalSound( "snd_flagcapture", 1, CHAN_VOICE|CHAN_NO_PHS_ADD, ATTN_NONE );
hideModel();
setSolidType( SOLID_NOT );
}
}
}
CLASS_DECLARATION( CTF_Flag, CTF_Flag_Hardcorps, "ctf_flag_hardcorps" )
ResponseDef CTF_Flag_Hardcorps::Responses[] =
{
{ NULL, NULL }
};
CTF_Flag_Hardcorps::CTF_Flag_Hardcorps
(
)
{
setModel( "ctf_flag_hardcorps_w.def" );
ctf_team = CTF_TEAM_HARDCORPS;
}
CLASS_DECLARATION( CTF_Flag, CTF_Flag_Sintek, "ctf_flag_sintek" )
ResponseDef CTF_Flag_Sintek::Responses[] =
{
{ NULL, NULL }
};
CTF_Flag_Sintek::CTF_Flag_Sintek
(
)
{
setModel( "ctf_flag_sintek_w.def" );
ctf_team = CTF_TEAM_SINTEK;
}
CLASS_DECLARATION( InventoryItem, CTF_Tech, NULL );
Event EV_Tech_Timeout( "tech_timeout" );
ResponseDef CTF_Tech::Responses[] =
{
{ &EV_Tech_Timeout, ( Response )CTF_Tech::Timeout },
{ NULL, NULL }
};
// TECH - 1 of each type is created at the beginning of the level, and
// it is respawned near the deathmatch starting locations.
CTF_Tech::CTF_Tech
(
)
{
Vector org, forward;
org = FindSpawnLocation();
org[2] += 16;
setOrigin( org );
worldorigin.copyTo(edict->s.old_origin);
setMoveType( MOVETYPE_TOSS );
angles[0] = 0;
angles[1] = rand() % 360;
angles[2] = 0;
setAngles( angles );
AngleVectors (angles.vec3(), forward.vec3(), NULL, NULL);
// Throw the tech up and out
velocity = forward * 100;
velocity[2] = 350;
// Techs don't respawn
setRespawn( false );
edict->s.effects |= EF_ROTATE;
PostEvent( EV_Tech_Timeout, CTF_TECH_TIMEOUT );
}
void CTF_Tech::HasTechMsg
(
edict_t *who
)
{
if ( level.time - who->client->resp.ctf_lasttechmsg > 2 )
{
gi.centerprintf( who, "jcx jcy string \"You already have a TECH powerup\"" );
who->client->resp.ctf_lasttechmsg = level.time;
}
}
qboolean CTF_Tech::Pickupable
(
Entity *other
)
{
if ( !other->isSubclassOf( Sentient ) )
{
return false;
}
else
{
Sentient *sent;
Item *item;
sent = ( Sentient * )other;
item = sent->HasItemOfSuperclass( getSuperclass() );
if ( item )
{
HasTechMsg( other->edict );
return false;
}
}
return true;
}
void CTF_Tech::Pickup
(
Event *ev
)
{
Entity *other;
Item *item;
other = ev->GetEntity( 1 );
if ( !other->isClient() )
return;
// Try to give the tech to the player
item = ItemPickup( other );
if ( item )
{
// Cancel the timeout so it doesn't get removed from the player
item->CancelEventsOfType( EV_Tech_Timeout );
// Cancel the timeout so it doesn't timeout before being removed
CancelEventsOfType( EV_Tech_Timeout );
// make this tech non-pickupable
setSolidType( SOLID_NOT );
// Don't need this one anymore, remove it
PostEvent( EV_Remove, 0 );
}
}
Vector CTF_Tech::FindSpawnLocation
(
void
)
{
Entity *spot = NULL;
int i = rand() % 16;
int num = 0;
// Find a location from the deathmatch spawn points
while( i-- )
{
num = G_FindClass( num, "info_player_deathmatch" );
}
if ( !num )
num = G_FindClass( num, "info_player_deathmatch" );
// No deathmatch locations, look for team starts
if ( !num )
{
i = rand() % 16;
if ( rand() & 1 )
{
while( i-- )
{
num = G_FindClass( num, "info_player_hardcorps" );
}
if ( !num )
num = G_FindClass( num, "info_player_hardcorps" );
}
else
{
while( i-- )
{
num = G_FindClass( num, "info_player_sintek" );
}
if ( !num )
num = G_FindClass( num, "info_player_sintek" );
}
}
spot = G_GetEntity( num );
return spot->worldorigin;
}
void CTF_Tech::Timeout
(
Event *ev
)
{
Entity *ent;
ClassDef *cls;
cls = getClassForID( getClassname() );
ent = ( Entity * )cls->newInstance();
// make this tech non-pickupable
setSolidType( SOLID_NOT );
// this tech has been idle and timed out, remove it and create another one.
PostEvent( EV_Remove, 0 );
}
CLASS_DECLARATION( CTF_Tech, CTF_Tech_Regeneration, "ctf_tech_regeneration" )
Event EV_Tech_Regenerate( "tech_regenerate" );
ResponseDef CTF_Tech_Regeneration::Responses[] =
{
{ &EV_Tech_Regenerate, (Response)CTF_Tech_Regeneration::Regenerate },
{ NULL, NULL }
};
void CTF_Tech_Regeneration::Regenerate
(
Event *ev
)
{
// Regenerate the owner
if ( owner )
{
if ( owner->health < CTF_TECH_REGENERATION_HEALTH )
{
owner->health += 2;
if ( owner->health > CTF_TECH_REGENERATION_HEALTH )
{
owner->health = CTF_TECH_REGENERATION_HEALTH;
}
}
if ( level.time > last_sound_event )
{
Event *ctfev;
ctfev = new Event( EV_Player_CTF_SoundEvent );
ctfev->AddInteger( DAMAGE );
owner->ProcessEvent( ctfev );
last_sound_event = level.time + 2;
}
}
PostEvent( EV_Tech_Regenerate, CTF_TECH_REGENERATION_TIME );
}
CTF_Tech_Regeneration::CTF_Tech_Regeneration
(
void
)
{
setModel( "ctf_regen.def" );
showModel();
CancelEventsOfType( EV_Tech_Regenerate );
PostEvent( EV_Tech_Regenerate, 1 );
last_sound_event = 0;
}
CLASS_DECLARATION( CTF_Tech, CTF_Tech_Double, "ctf_tech_double" )
ResponseDef CTF_Tech_Double::Responses[] =
{
{ NULL, NULL }
};
CTF_Tech_Double::CTF_Tech_Double
(
void
)
{
setModel( "ctf_double.def" );
showModel();
}
CLASS_DECLARATION( CTF_Tech, CTF_Tech_Shield, "ctf_tech_shield" )
ResponseDef CTF_Tech_Shield::Responses[] =
{
{ NULL, NULL }
};
CTF_Tech_Shield::CTF_Tech_Shield
(
void
)
{
setModel( "ctf_shield.def" );
showModel();
}
CLASS_DECLARATION( CTF_Tech, CTF_Tech_Aqua, "ctf_tech_aqua" )
ResponseDef CTF_Tech_Aqua::Responses[] =
{
{ NULL, NULL }
};
CTF_Tech_Aqua::CTF_Tech_Aqua
(
void
)
{
setModel( "ctf_aqua.def" );
showModel();
}
CLASS_DECLARATION( CTF_Tech, CTF_Tech_Jump, "ctf_tech_jump" )
ResponseDef CTF_Tech_Jump::Responses[] =
{
{ NULL, NULL }
};
CTF_Tech_Jump::CTF_Tech_Jump
(
void
)
{
setModel( "ctf_jump.def" );
showModel();
}
CLASS_DECLARATION( CTF_Tech, CTF_Tech_Empathy, "ctf_tech_empathy" )
ResponseDef CTF_Tech_Empathy::Responses[] =
{
{ NULL, NULL }
};
CTF_Tech_Empathy::CTF_Tech_Empathy
(
void
)
{
setModel( "ctf_empathy.def" );
showModel();
}
CLASS_DECLARATION( CTF_Tech, CTF_Tech_DeathQuad, "ctf_tech_deathquad" )
Event EV_Tech_Damage( "tech_damage" );
ResponseDef CTF_Tech_DeathQuad::Responses[] =
{
{ &EV_Tech_Damage, (Response)CTF_Tech_DeathQuad::Damage },
{ NULL, NULL }
};
CTF_Tech_DeathQuad::CTF_Tech_DeathQuad
(
void
)
{
setModel( "ctf_deathquad.def" );
PostEvent( EV_Tech_Damage, 1 );
showModel();
}
void CTF_Tech_DeathQuad::Damage
(
Event *ev
)
{
if ( owner )
{
owner->Damage( world, world, 2, worldorigin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_DEATHQUAD, -1, -1, 1.0f );
if ( owner && ( level.time > last_sound_event ) )
{
Event *ctfev;
ctfev = new Event( EV_Player_CTF_SoundEvent );
ctfev->AddInteger( DAMAGE );
if ( owner )
owner->ProcessEvent( ctfev );
last_sound_event = level.time + 2;
}
}
PostEvent( EV_Tech_Damage, 1 );
}
/*****************************************************************************/
/*SINED info_player_hardcorps (1 0 0) (-16 -16 0) (16 16 64)
The starting point for a member of team sintek when the first join
the game.
/*****************************************************************************/
class EXPORT_FROM_DLL PlayerHardcorpsStart : public PlayerStart
{
public:
CLASS_PROTOTYPE( PlayerHardcorpsStart );
};
CLASS_DECLARATION( Entity, PlayerHardcorpsStart, "info_player_hardcorps" );
ResponseDef PlayerHardcorpsStart::Responses[] =
{
{ NULL, NULL }
};
/*****************************************************************************/
/*SINED info_player_sintek (1 0 0) (-16 -16 0) (16 16 64)
The starting point for a member of team sintek when the first join
the game.
/*****************************************************************************/
class EXPORT_FROM_DLL PlayerSintekStart : public PlayerStart
{
public:
CLASS_PROTOTYPE( PlayerSintekStart );
};
CLASS_DECLARATION( Entity, PlayerSintekStart, "info_player_sintek" );
ResponseDef PlayerSintekStart::Responses[] =
{
{ NULL, NULL }
};