1745 lines
42 KiB
C++
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 }
|
|
};
|
|
|