2011-02-18 14:31:32 +00:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
Copyright (C) 2002-2009 Q3Rally Team (Per Thormann - perle@q3rally.com)
|
|
|
|
|
|
|
|
This file is part of q3rally source code.
|
|
|
|
|
|
|
|
q3rally source code is free software; you can redistribute it
|
|
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
q3rally source code is distributed in the hope that it will be
|
|
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with q3rally; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "g_local.h"
|
|
|
|
|
|
|
|
// *********************** Race Entities ************************
|
|
|
|
// *********************** Race Entities ************************
|
|
|
|
// *********************** Race Entities ************************
|
|
|
|
// *********************** Race Entities ************************
|
|
|
|
// *********************** Race Entities ************************
|
|
|
|
|
|
|
|
#define CHECKPOINT_SOUNDS 1
|
|
|
|
#define CHECKPOINT_MESSAGES 2
|
|
|
|
|
|
|
|
void Touch_StartFinish (gentity_t *self, gentity_t *other, trace_t *trace ){
|
|
|
|
char *place;
|
|
|
|
|
|
|
|
if ( !other->client ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_developer.integer)
|
|
|
|
G_Printf( "Client %i touched the startfinish line. Checkpoint number %i\n", other->s.clientNum, self->number );
|
|
|
|
|
|
|
|
if ( other->currentLap > level.numberOfLaps && level.numberOfLaps ){
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->number == other->number){
|
|
|
|
other->currentLap++;
|
|
|
|
// increment lap
|
|
|
|
if ( other->currentLap > level.numberOfLaps && level.numberOfLaps ){
|
|
|
|
other->client->finishRaceTime = level.time;
|
|
|
|
other->s.weapon = WP_NONE;
|
|
|
|
other->takedamage = qfalse;
|
|
|
|
|
|
|
|
trap_SendServerCommand( -1, va("raceFinishTime %i %i", other->s.clientNum, other->client->finishRaceTime) );
|
|
|
|
|
|
|
|
if (!level.finishRaceTime){
|
|
|
|
other->client->ps.stats[STAT_POSITION] = 1; // make sure the player is first
|
|
|
|
|
|
|
|
level.winnerNumber = other->s.clientNum;
|
|
|
|
level.finishRaceTime = level.time;
|
|
|
|
trap_SendServerCommand( -1, va("print \"%s won the race!\n\"", other->client->pers.netname ));
|
|
|
|
trap_SendServerCommand( level.winnerNumber, "cp \"You won the race!\n\"");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
switch ( other->client->ps.stats[STAT_POSITION] ){
|
|
|
|
case 1:
|
|
|
|
place = "first";
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
place = "second";
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
place = "third";
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
place = "forth";
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
place = "fifth";
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
place = "sixth";
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
place = "seventh";
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
place = "eighth";
|
|
|
|
break;
|
|
|
|
default:
|
2011-03-08 09:07:05 +00:00
|
|
|
place = NULL;
|
2011-02-18 14:31:32 +00:00
|
|
|
Com_Printf( "Unknown placing: %i\n", other->client->ps.stats[STAT_POSITION] );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( other->client->ps.stats[STAT_POSITION] <= 8 ){
|
|
|
|
trap_SendServerCommand( -1, va("print \"%s finished the race in %s place!\n\"", other->client->pers.netname, place ));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
trap_SendServerCommand( -1, va("print \"%s finished the race!\n\"", other->client->pers.netname ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
other->number = 1;
|
|
|
|
other->client->ps.stats[STAT_NEXT_CHECKPOINT] = other->number;
|
|
|
|
other->client->ps.stats[STAT_FRAC_TO_NEXT_CHECKPOINT] = FLOAT2SHORT(0.1f);
|
|
|
|
// Com_Printf( "resetting frac, sf\n" );
|
|
|
|
trap_SendServerCommand( other->client->ps.clientNum, va("newLapTime %i %i\n", other->currentLap, level.time));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (other->currentLap == level.numberOfLaps ){
|
|
|
|
trap_SendServerCommand( other->s.number, "cp \"Final lap\n\"");
|
|
|
|
Rally_Sound( self, EV_GLOBAL_SOUND, CHAN_ANNOUNCER, G_SoundIndex("sound/rally/race/finallap.wav") );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Rally_Sound( self, EV_GLOBAL_SOUND, CHAN_ANNOUNCER, G_SoundIndex("sound/rally/race/checkpoint.wav") );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Think_StartFinish( gentity_t *self ){
|
|
|
|
gentity_t *ent;
|
|
|
|
int checkpoints;
|
|
|
|
|
|
|
|
// FIXME: only do this a couple times after a client joins
|
|
|
|
// send checkpoint to clients
|
|
|
|
/*
|
|
|
|
if ((level.time / 2000) % 2)
|
|
|
|
self->r.svFlags |= SVF_BROADCAST;
|
|
|
|
else
|
|
|
|
self->r.svFlags |= SVF_NOCLIENT;
|
|
|
|
|
|
|
|
self->nextthink = level.time + 2000;
|
|
|
|
*/
|
|
|
|
// if there is a target use its origin and angles instead
|
|
|
|
if ( self->target ){
|
|
|
|
ent = G_PickTarget( self->target );
|
|
|
|
if (ent){
|
|
|
|
VectorCopy(ent->s.origin, self->s.origin);
|
|
|
|
VectorCopy(ent->s.angles, self->s.angles);
|
|
|
|
self->s.frame = 1;
|
|
|
|
|
|
|
|
G_FreeEntity( ent );
|
|
|
|
}
|
|
|
|
self->target = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( self->s.origin2[0] == 0.0f &&
|
|
|
|
self->s.origin2[1] == 0.0f &&
|
|
|
|
self->s.origin2[2] == 0.0f &&
|
|
|
|
( self->s.origin[0] != 0.0f ||
|
|
|
|
self->s.origin[1] != 0.0f ||
|
|
|
|
self->s.origin[2] != 0.0f ) )
|
|
|
|
VectorCopy( self->s.origin, self->s.origin2 );
|
|
|
|
|
|
|
|
checkpoints = 0;
|
|
|
|
|
|
|
|
ent = NULL;
|
|
|
|
while ((ent = G_Find (ent, FOFS(classname), "rally_checkpoint")) != NULL) checkpoints++;
|
|
|
|
level.numCheckpoints = checkpoints;
|
|
|
|
if (g_trackReversed.integer && level.trackIsReversable){
|
|
|
|
ent = NULL;
|
|
|
|
while ((ent = G_Find (ent, FOFS(classname), "rally_checkpoint")) != NULL) {
|
|
|
|
ent->number = level.numCheckpoints - ent->number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self->number = level.numCheckpoints;
|
|
|
|
self->s.weapon = self->number;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SP_rally_startfinish( gentity_t *ent ) {
|
|
|
|
ent->classname = "rally_checkpoint";
|
|
|
|
|
|
|
|
trap_SetBrushModel( ent, ent->model );
|
|
|
|
|
|
|
|
if (!g_laplimit.integer){
|
|
|
|
level.numberOfLaps = ent->laps;
|
2011-03-08 09:07:05 +00:00
|
|
|
trap_Cvar_Set( "laplimit", va("%d", level.numberOfLaps) );
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
level.numberOfLaps = g_laplimit.integer;
|
|
|
|
|
|
|
|
// STONELANCE - April 23, 2002 temp for testing bezier curve stuff
|
|
|
|
ent->r.svFlags |= SVF_BROADCAST;
|
|
|
|
//
|
|
|
|
ent->s.eType = ET_CHECKPOINT;
|
|
|
|
|
|
|
|
ent->touch = Touch_StartFinish;
|
|
|
|
ent->think = Think_StartFinish;
|
|
|
|
ent->nextthink = level.time + 100;
|
|
|
|
ent->s.frame = 0;
|
|
|
|
|
|
|
|
trap_LinkEntity (ent);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// rally_checkpoint
|
|
|
|
//
|
|
|
|
|
|
|
|
void Touch_Checkpoint (gentity_t *self, gentity_t *other, trace_t *trace ){
|
|
|
|
if ( !other->client ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_developer.integer)
|
|
|
|
G_Printf( "Client %i touched checkpoint number %i\n", other->s.clientNum, self->number );
|
|
|
|
|
|
|
|
if (self->number == other->number){
|
|
|
|
other->number++; // FIXME: get rid of number? use s.weapon instead?
|
|
|
|
other->client->ps.stats[STAT_NEXT_CHECKPOINT] = other->number;
|
|
|
|
other->client->ps.stats[STAT_FRAC_TO_NEXT_CHECKPOINT] = FLOAT2SHORT(0.1f);
|
|
|
|
// Com_Printf( "resetting frac, cp\n" );
|
|
|
|
|
|
|
|
if (self->spawnflags & CHECKPOINT_SOUNDS)
|
|
|
|
Rally_Sound( self, EV_GLOBAL_SOUND, CHAN_ANNOUNCER, G_SoundIndex("sound/rally/race/checkpoint.wav") );
|
|
|
|
|
|
|
|
if ( self->spawnflags & CHECKPOINT_MESSAGES && self->s.otherEntityNum != -1 &&
|
|
|
|
self->s.otherEntityNum != other->s.number )
|
|
|
|
{
|
|
|
|
if ( g_entities[self->s.otherEntityNum].client->ps.stats[STAT_POSITION] < other->client->ps.stats[STAT_POSITION] )
|
|
|
|
{
|
|
|
|
trap_SendServerCommand( other->s.number,
|
|
|
|
va("print \"%s is ahead by %i seconds\n\"",
|
|
|
|
g_entities[self->s.otherEntityNum].client->pers.netname,
|
|
|
|
(level.time - self->updateTime) / 1000) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self->s.otherEntityNum = other->s.number;
|
|
|
|
self->updateTime = level.time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Think_Checkpoint( gentity_t *self ){
|
|
|
|
gentity_t *ent;
|
|
|
|
|
|
|
|
/*
|
|
|
|
// FIXME: only do this a couple times after a client joins
|
|
|
|
// send checkpoint to clients
|
|
|
|
if ((level.time / 2000) % 2){
|
|
|
|
Com_Printf("Broadcast %d\n", self->s.number);
|
|
|
|
self->r.svFlags |= SVF_BROADCAST;
|
|
|
|
trap_LinkEntity (ent);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
Com_Printf("Noclient\n");
|
|
|
|
self->r.svFlags |= SVF_NOCLIENT;
|
|
|
|
trap_LinkEntity (ent);
|
|
|
|
}
|
|
|
|
|
|
|
|
self->nextthink = level.time + 2000;
|
|
|
|
*/
|
|
|
|
// if there is a target use its origin and angles instead
|
|
|
|
if ( self->target ){
|
|
|
|
ent = G_PickTarget( self->target );
|
|
|
|
if (ent){
|
|
|
|
VectorCopy(ent->s.origin, self->s.origin);
|
|
|
|
VectorCopy(ent->s.angles, self->s.angles);
|
|
|
|
self->s.frame = 1;
|
|
|
|
|
|
|
|
G_FreeEntity( ent );
|
|
|
|
}
|
|
|
|
self->target = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( self->s.origin2[0] == 0.0f &&
|
|
|
|
self->s.origin2[1] == 0.0f &&
|
|
|
|
self->s.origin2[2] == 0.0f &&
|
|
|
|
( self->s.origin[0] != 0.0f ||
|
|
|
|
self->s.origin[1] != 0.0f ||
|
|
|
|
self->s.origin[2] != 0.0f ) )
|
|
|
|
VectorCopy( self->s.origin, self->s.origin2 );
|
|
|
|
|
|
|
|
self->s.weapon = self->number;
|
|
|
|
}
|
|
|
|
|
|
|
|
// spawnflag 1 enable messages, spawn flag 2 enable sound, 3 is enable both
|
|
|
|
void SP_rally_checkpoint( gentity_t *ent ) {
|
|
|
|
trap_SetBrushModel( ent, ent->model );
|
|
|
|
|
|
|
|
// STONELANCE - April 23, 2002 temp for testing bezier curve stuff
|
|
|
|
ent->r.svFlags |= SVF_BROADCAST;
|
|
|
|
//
|
|
|
|
ent->s.eType = ET_CHECKPOINT;
|
|
|
|
|
|
|
|
ent->think = Think_Checkpoint;
|
|
|
|
ent->nextthink = level.time + 200;
|
|
|
|
|
|
|
|
ent->touch = Touch_Checkpoint;
|
|
|
|
ent->s.frame = 0;
|
|
|
|
|
|
|
|
trap_LinkEntity (ent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SP_rally_sun( gentity_t *ent ){
|
|
|
|
// ent->s.eType = ET_LIGHT;
|
|
|
|
|
|
|
|
G_SetOrigin(ent, ent->s.origin);
|
|
|
|
|
|
|
|
trap_LinkEntity (ent);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// FIXME: improve these so they only need to be send to the client once?
|
|
|
|
void SP_rally_weather_rain( gentity_t *ent ){
|
|
|
|
trap_SetBrushModel( ent, ent->model );
|
|
|
|
ent->s.eType = ET_WEATHER;
|
|
|
|
|
|
|
|
ent->s.powerups = ent->number;
|
|
|
|
ent->s.weapon = 0;
|
|
|
|
ent->s.legsAnim = ent->spawnflags;
|
|
|
|
|
|
|
|
trap_LinkEntity (ent);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SP_rally_weather_snow( gentity_t *ent ){
|
|
|
|
trap_SetBrushModel( ent, ent->model );
|
|
|
|
ent->s.eType = ET_WEATHER;
|
|
|
|
|
|
|
|
ent->s.powerups = ent->number;
|
|
|
|
ent->s.weapon = 1;
|
|
|
|
ent->s.legsAnim = 0;
|
|
|
|
|
|
|
|
trap_LinkEntity (ent);
|
|
|
|
}
|