First version.
This commit is contained in:
commit
ef1c2cc347
473 files changed
+305366
No files matched your search
+2031
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// bg_lib.h -- standard C library replacement routines used by code
|
||||
// compiled for the virtual machine
|
||||
|
||||
// This file is NOT included on native builds
|
||||
#ifndef BG_LIB_H
|
||||
#define BG_LIB_H
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
typedef int size_t;
|
||||
|
||||
typedef char * va_list;
|
||||
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
|
||||
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
|
||||
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
|
||||
#define va_end(ap) ( ap = (va_list)0 )
|
||||
|
||||
#define CHAR_BIT 8 /* number of bits in a char */
|
||||
#define SCHAR_MIN (-128) /* minimum signed char value */
|
||||
#define SCHAR_MAX 127 /* maximum signed char value */
|
||||
#define UCHAR_MAX 0xff /* maximum unsigned char value */
|
||||
|
||||
#define SHRT_MIN (-32768) /* minimum (signed) short value */
|
||||
#define SHRT_MAX 32767 /* maximum (signed) short value */
|
||||
#define USHRT_MAX 0xffff /* maximum unsigned short value */
|
||||
#define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */
|
||||
#define INT_MAX 2147483647 /* maximum (signed) int value */
|
||||
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
|
||||
#define LONG_MIN (-2147483647L - 1) /* minimum (signed) long value */
|
||||
#define LONG_MAX 2147483647L /* maximum (signed) long value */
|
||||
#define ULONG_MAX 0xffffffffUL /* maximum unsigned long value */
|
||||
|
||||
// Misc functions
|
||||
typedef int cmp_t( const void *, const void * );
|
||||
void qsort( void *a, size_t n, size_t es, cmp_t *cmp );
|
||||
void srand( unsigned seed );
|
||||
int rand( void );
|
||||
|
||||
// String functions
|
||||
size_t strlen( const char *string );
|
||||
char *strcat( char *strDestination, const char *strSource );
|
||||
char *strcpy( char *strDestination, const char *strSource );
|
||||
int strcmp( const char *string1, const char *string2 );
|
||||
char *strchr( const char *string, int c );
|
||||
char *strrchr( const char *string, int c );
|
||||
char *strstr( const char *string, const char *strCharSet );
|
||||
char *strncpy( char *strDest, const char *strSource, size_t count );
|
||||
int tolower( int c );
|
||||
int toupper( int c );
|
||||
|
||||
double atof( const char *string );
|
||||
double _atof( const char **stringPtr );
|
||||
int atoi( const char *string );
|
||||
int _atoi( const char **stringPtr );
|
||||
|
||||
|
||||
int vsprintf( char *buffer, const char *fmt, va_list argptr );
|
||||
int sscanf( const char *buffer, const char *fmt, ... );
|
||||
|
||||
// Memory functions
|
||||
void *memmove( void *dest, const void *src, size_t count );
|
||||
void *memset( void *dest, int c, size_t count );
|
||||
void *memcpy( void *dest, const void *src, size_t count );
|
||||
|
||||
// Math functions
|
||||
double ceil( double x );
|
||||
double floor( double x );
|
||||
double sqrt( double x );
|
||||
double sin( double x );
|
||||
double cos( double x );
|
||||
double atan2( double y, double x );
|
||||
double tan( double x );
|
||||
int abs( int n );
|
||||
double fabs( double x );
|
||||
double acos( double x );
|
||||
float pow( float x, float y );
|
||||
double rint( double v );
|
||||
|
||||
#endif // BG_LIB_H
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// bg_local.h -- local definitions for the bg (both games) files
|
||||
|
||||
#define MIN_WALK_NORMAL 0.7f // can't walk on very steep slopes
|
||||
|
||||
#define STEPSIZE 18
|
||||
|
||||
#define TIMER_LAND 130
|
||||
#define TIMER_GESTURE (34*66+50)
|
||||
#define TIMER_ATTACK 500 //nonsegmented models
|
||||
|
||||
#define OVERCLIP 1.001f
|
||||
|
||||
#define FALLING_THRESHOLD -900.0f //what vertical speed to start falling sound at
|
||||
|
||||
|
||||
// all of the locals will be zeroed before each
|
||||
// pmove, just to make damn sure we don't have
|
||||
// any differences when running on client or server
|
||||
typedef struct
|
||||
{
|
||||
vec3_t forward, right, up;
|
||||
float frametime;
|
||||
|
||||
int msec;
|
||||
|
||||
qboolean walking;
|
||||
qboolean groundPlane;
|
||||
qboolean ladder;
|
||||
trace_t groundTrace;
|
||||
|
||||
float impactSpeed;
|
||||
|
||||
vec3_t previous_origin;
|
||||
vec3_t previous_velocity;
|
||||
int previous_waterlevel;
|
||||
} pml_t;
|
||||
|
||||
extern pmove_t *pm;
|
||||
extern pml_t pml;
|
||||
|
||||
// movement parameters
|
||||
extern float pm_stopspeed;
|
||||
extern float pm_duckScale;
|
||||
extern float pm_swimScale;
|
||||
extern float pm_wadeScale;
|
||||
|
||||
extern float pm_accelerate;
|
||||
extern float pm_airaccelerate;
|
||||
extern float pm_wateraccelerate;
|
||||
extern float pm_flyaccelerate;
|
||||
|
||||
extern float pm_friction;
|
||||
extern float pm_waterfriction;
|
||||
extern float pm_flightfriction;
|
||||
|
||||
extern int c_pmove;
|
||||
|
||||
void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce );
|
||||
void PM_AddTouchEnt( int entityNum );
|
||||
void PM_AddEvent( int newEvent );
|
||||
|
||||
qboolean PM_SlideMove( qboolean gravity );
|
||||
void PM_StepEvent( vec3_t from, vec3_t to, vec3_t normal );
|
||||
qboolean PM_StepSlideMove( qboolean gravity, qboolean predictive );
|
||||
qboolean PM_PredictStepMove( void );
|
||||
+5729
File diff suppressed because it is too large.
Load diff
+3527
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// bg_slidemove.c -- part of bg_pmove functionality
|
||||
|
||||
#include "../qcommon/q_shared.h"
|
||||
#include "bg_public.h"
|
||||
#include "bg_local.h"
|
||||
|
||||
/*
|
||||
|
||||
input: origin, velocity, bounds, groundPlane, trace function
|
||||
|
||||
output: origin, velocity, impacts, stairup boolean
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
==================
|
||||
PM_SlideMove
|
||||
|
||||
Returns qtrue if the velocity was clipped in some way
|
||||
==================
|
||||
*/
|
||||
#define MAX_CLIP_PLANES 5
|
||||
qboolean PM_SlideMove( qboolean gravity )
|
||||
{
|
||||
int bumpcount, numbumps;
|
||||
vec3_t dir;
|
||||
float d;
|
||||
int numplanes;
|
||||
vec3_t planes[MAX_CLIP_PLANES];
|
||||
vec3_t primal_velocity;
|
||||
vec3_t clipVelocity;
|
||||
int i, j, k;
|
||||
trace_t trace;
|
||||
vec3_t end;
|
||||
float time_left;
|
||||
float into;
|
||||
vec3_t endVelocity;
|
||||
vec3_t endClipVelocity;
|
||||
|
||||
numbumps = 4;
|
||||
|
||||
VectorCopy( pm->ps->velocity, primal_velocity );
|
||||
|
||||
if( gravity )
|
||||
{
|
||||
VectorCopy( pm->ps->velocity, endVelocity );
|
||||
endVelocity[ 2 ] -= pm->ps->gravity * pml.frametime;
|
||||
pm->ps->velocity[ 2 ] = ( pm->ps->velocity[ 2 ] + endVelocity[ 2 ] ) * 0.5;
|
||||
primal_velocity[ 2 ] = endVelocity[ 2 ];
|
||||
|
||||
if( pml.groundPlane )
|
||||
{
|
||||
// slide along the ground plane
|
||||
PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal,
|
||||
pm->ps->velocity, OVERCLIP );
|
||||
}
|
||||
}
|
||||
|
||||
time_left = pml.frametime;
|
||||
|
||||
// never turn against the ground plane
|
||||
if( pml.groundPlane )
|
||||
{
|
||||
numplanes = 1;
|
||||
VectorCopy( pml.groundTrace.plane.normal, planes[ 0 ] );
|
||||
}
|
||||
else
|
||||
numplanes = 0;
|
||||
|
||||
// never turn against original velocity
|
||||
VectorNormalize2( pm->ps->velocity, planes[ numplanes ] );
|
||||
numplanes++;
|
||||
|
||||
for( bumpcount = 0; bumpcount < numbumps; bumpcount++ )
|
||||
{
|
||||
// calculate position we are trying to move to
|
||||
VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end );
|
||||
|
||||
// see if we can make it there
|
||||
pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask );
|
||||
|
||||
if( trace.allsolid )
|
||||
{
|
||||
// entity is completely trapped in another solid
|
||||
pm->ps->velocity[ 2 ] = 0; // don't build up falling damage, but allow sideways acceleration
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( trace.fraction > 0 )
|
||||
{
|
||||
// actually covered some distance
|
||||
VectorCopy( trace.endpos, pm->ps->origin );
|
||||
}
|
||||
|
||||
if( trace.fraction == 1 )
|
||||
break; // moved the entire distance
|
||||
|
||||
// save entity for contact
|
||||
PM_AddTouchEnt( trace.entityNum );
|
||||
|
||||
time_left -= time_left * trace.fraction;
|
||||
|
||||
if( numplanes >= MAX_CLIP_PLANES )
|
||||
{
|
||||
// this shouldn't really happen
|
||||
VectorClear( pm->ps->velocity );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
//
|
||||
// if this is the same plane we hit before, nudge velocity
|
||||
// out along it, which fixes some epsilon issues with
|
||||
// non-axial planes
|
||||
//
|
||||
for( i = 0 ; i < numplanes ; i++ )
|
||||
{
|
||||
if( DotProduct( trace.plane.normal, planes[i] ) > 0.99 )
|
||||
{
|
||||
VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( i < numplanes )
|
||||
continue;
|
||||
|
||||
VectorCopy( trace.plane.normal, planes[ numplanes ] );
|
||||
numplanes++;
|
||||
|
||||
//
|
||||
// modify velocity so it parallels all of the clip planes
|
||||
//
|
||||
|
||||
// find a plane that it enters
|
||||
for( i = 0; i < numplanes; i++ )
|
||||
{
|
||||
into = DotProduct( pm->ps->velocity, planes[ i ] );
|
||||
if( into >= 0.1 )
|
||||
continue; // move doesn't interact with the plane
|
||||
|
||||
// see how hard we are hitting things
|
||||
if( -into > pml.impactSpeed )
|
||||
pml.impactSpeed = -into;
|
||||
|
||||
// slide along the plane
|
||||
PM_ClipVelocity( pm->ps->velocity, planes[ i ], clipVelocity, OVERCLIP );
|
||||
|
||||
// slide along the plane
|
||||
PM_ClipVelocity( endVelocity, planes[ i ], endClipVelocity, OVERCLIP );
|
||||
|
||||
// see if there is a second plane that the new move enters
|
||||
for( j = 0; j < numplanes; j++ )
|
||||
{
|
||||
if( j == i )
|
||||
continue;
|
||||
|
||||
if( DotProduct( clipVelocity, planes[ j ] ) >= 0.1 )
|
||||
continue; // move doesn't interact with the plane
|
||||
|
||||
// try clipping the move to the plane
|
||||
PM_ClipVelocity( clipVelocity, planes[ j ], clipVelocity, OVERCLIP );
|
||||
PM_ClipVelocity( endClipVelocity, planes[ j ], endClipVelocity, OVERCLIP );
|
||||
|
||||
// see if it goes back into the first clip plane
|
||||
if( DotProduct( clipVelocity, planes[ i ] ) >= 0 )
|
||||
continue;
|
||||
|
||||
// slide the original velocity along the crease
|
||||
CrossProduct( planes[ i ], planes[ j ], dir );
|
||||
VectorNormalize( dir );
|
||||
d = DotProduct( dir, pm->ps->velocity );
|
||||
VectorScale( dir, d, clipVelocity );
|
||||
|
||||
CrossProduct( planes[ i ], planes[ j ], dir);
|
||||
VectorNormalize( dir );
|
||||
d = DotProduct( dir, endVelocity );
|
||||
VectorScale( dir, d, endClipVelocity );
|
||||
|
||||
// see if there is a third plane the the new move enters
|
||||
for( k = 0; k < numplanes; k++ )
|
||||
{
|
||||
if( k == i || k == j )
|
||||
continue;
|
||||
|
||||
if( DotProduct( clipVelocity, planes[ k ] ) >= 0.1 )
|
||||
continue; // move doesn't interact with the plane
|
||||
|
||||
// stop dead at a tripple plane interaction
|
||||
VectorClear( pm->ps->velocity );
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have fixed all interactions, try another move
|
||||
VectorCopy( clipVelocity, pm->ps->velocity );
|
||||
VectorCopy( endClipVelocity, endVelocity );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( gravity )
|
||||
VectorCopy( endVelocity, pm->ps->velocity );
|
||||
|
||||
// don't change velocity if in a timer (FIXME: is this correct?)
|
||||
if( pm->ps->pm_time )
|
||||
VectorCopy( primal_velocity, pm->ps->velocity );
|
||||
|
||||
return ( bumpcount != 0 );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PM_StepEvent
|
||||
==================
|
||||
*/
|
||||
void PM_StepEvent( vec3_t from, vec3_t to, vec3_t normal )
|
||||
{
|
||||
float size;
|
||||
vec3_t delta, dNormal;
|
||||
|
||||
VectorSubtract( from, to, delta );
|
||||
VectorCopy( delta, dNormal );
|
||||
VectorNormalize( dNormal );
|
||||
|
||||
size = DotProduct( normal, dNormal ) * VectorLength( delta );
|
||||
|
||||
if( size > 0.0f )
|
||||
{
|
||||
if( size > 2.0f )
|
||||
{
|
||||
if( size < 7.0f )
|
||||
PM_AddEvent( EV_STEPDN_4 );
|
||||
else if( size < 11.0f )
|
||||
PM_AddEvent( EV_STEPDN_8 );
|
||||
else if( size < 15.0f )
|
||||
PM_AddEvent( EV_STEPDN_12 );
|
||||
else
|
||||
PM_AddEvent( EV_STEPDN_16 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size = fabs( size );
|
||||
|
||||
if( size > 2.0f )
|
||||
{
|
||||
if( size < 7.0f )
|
||||
PM_AddEvent( EV_STEP_4 );
|
||||
else if( size < 11.0f )
|
||||
PM_AddEvent( EV_STEP_8 );
|
||||
else if( size < 15.0f )
|
||||
PM_AddEvent( EV_STEP_12 );
|
||||
else
|
||||
PM_AddEvent( EV_STEP_16 );
|
||||
}
|
||||
}
|
||||
|
||||
if( pm->debugLevel )
|
||||
Com_Printf( "%i:stepped\n", c_pmove );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PM_StepSlideMove
|
||||
==================
|
||||
*/
|
||||
qboolean PM_StepSlideMove( qboolean gravity, qboolean predictive )
|
||||
{
|
||||
vec3_t start_o, start_v;
|
||||
vec3_t down_o, down_v;
|
||||
trace_t trace;
|
||||
vec3_t normal;
|
||||
vec3_t step_v, step_vNormal;
|
||||
vec3_t up, down;
|
||||
float stepSize;
|
||||
qboolean stepped = qfalse;
|
||||
|
||||
if( pm->ps->stats[ STAT_STATE ] & SS_WALLCLIMBING )
|
||||
{
|
||||
if( pm->ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING )
|
||||
VectorSet( normal, 0.0f, 0.0f, -1.0f );
|
||||
else
|
||||
VectorCopy( pm->ps->grapplePoint, normal );
|
||||
}
|
||||
else
|
||||
VectorSet( normal, 0.0f, 0.0f, 1.0f );
|
||||
|
||||
VectorCopy( pm->ps->origin, start_o );
|
||||
VectorCopy( pm->ps->velocity, start_v );
|
||||
|
||||
if( PM_SlideMove( gravity ) == 0 )
|
||||
{
|
||||
VectorCopy( start_o, down );
|
||||
VectorMA( down, -STEPSIZE, normal, down );
|
||||
pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask );
|
||||
|
||||
//we can step down
|
||||
if( trace.fraction > 0.01f && trace.fraction < 1.0f &&
|
||||
!trace.allsolid && pml.groundPlane != qfalse )
|
||||
{
|
||||
if( pm->debugLevel )
|
||||
Com_Printf( "%d: step down\n", c_pmove );
|
||||
|
||||
stepped = qtrue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy( start_o, down );
|
||||
VectorMA( down, -STEPSIZE, normal, down );
|
||||
pm->trace( &trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask );
|
||||
// never step up when you still have up velocity
|
||||
if( DotProduct( trace.plane.normal, pm->ps->velocity ) > 0.0f &&
|
||||
( trace.fraction == 1.0f || DotProduct( trace.plane.normal, normal ) < 0.7f ) )
|
||||
{
|
||||
return stepped;
|
||||
}
|
||||
|
||||
VectorCopy( pm->ps->origin, down_o );
|
||||
VectorCopy( pm->ps->velocity, down_v );
|
||||
|
||||
VectorCopy( start_o, up );
|
||||
VectorMA( up, STEPSIZE, normal, up );
|
||||
|
||||
// test the player position if they were a stepheight higher
|
||||
pm->trace( &trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask );
|
||||
if( trace.allsolid )
|
||||
{
|
||||
if( pm->debugLevel )
|
||||
Com_Printf( "%i:bend can't step\n", c_pmove );
|
||||
|
||||
return stepped; // can't step up
|
||||
}
|
||||
|
||||
VectorSubtract( trace.endpos, start_o, step_v );
|
||||
VectorCopy( step_v, step_vNormal );
|
||||
VectorNormalize( step_vNormal );
|
||||
|
||||
stepSize = DotProduct( normal, step_vNormal ) * VectorLength( step_v );
|
||||
// try slidemove from this position
|
||||
VectorCopy( trace.endpos, pm->ps->origin );
|
||||
VectorCopy( start_v, pm->ps->velocity );
|
||||
|
||||
if( PM_SlideMove( gravity ) == 0 )
|
||||
{
|
||||
if( pm->debugLevel )
|
||||
Com_Printf( "%d: step up\n", c_pmove );
|
||||
|
||||
stepped = qtrue;
|
||||
}
|
||||
|
||||
// push down the final amount
|
||||
VectorCopy( pm->ps->origin, down );
|
||||
VectorMA( down, -stepSize, normal, down );
|
||||
pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask );
|
||||
|
||||
if( !trace.allsolid )
|
||||
VectorCopy( trace.endpos, pm->ps->origin );
|
||||
|
||||
if( trace.fraction < 1.0f )
|
||||
PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP );
|
||||
}
|
||||
|
||||
if( !predictive && stepped )
|
||||
PM_StepEvent( start_o, pm->ps->origin, normal );
|
||||
|
||||
return stepped;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
PM_PredictStepMove
|
||||
==================
|
||||
*/
|
||||
qboolean PM_PredictStepMove( void )
|
||||
{
|
||||
vec3_t velocity, origin;
|
||||
float impactSpeed;
|
||||
qboolean stepped = qfalse;
|
||||
|
||||
VectorCopy( pm->ps->velocity, velocity );
|
||||
VectorCopy( pm->ps->origin, origin );
|
||||
impactSpeed = pml.impactSpeed;
|
||||
|
||||
if( PM_StepSlideMove( qfalse, qtrue ) )
|
||||
stepped = qtrue;
|
||||
|
||||
VectorCopy( velocity, pm->ps->velocity );
|
||||
VectorCopy( origin, pm->ps->origin );
|
||||
pml.impactSpeed = impactSpeed;
|
||||
|
||||
return stepped;
|
||||
}
|
||||
+1970
File diff suppressed because it is too large.
Load diff
+5662
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 2004-2006 Tony J. White
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#ifndef _G_ADMIN_H
|
||||
#define _G_ADMIN_H
|
||||
|
||||
#define AP(x) trap_SendServerCommand(-1, x)
|
||||
#define CP(x) trap_SendServerCommand(ent-g_entities, x)
|
||||
#define CPx(x, y) trap_SendServerCommand(x, y)
|
||||
#define ADMP(x) G_admin_print(ent, x)
|
||||
#define ADMBP(x) G_admin_buffer_print(ent, x)
|
||||
#define ADMBP_begin() G_admin_buffer_begin()
|
||||
#define ADMBP_end() G_admin_buffer_end(ent)
|
||||
|
||||
#define MAX_ADMIN_LEVELS 32
|
||||
#define MAX_ADMIN_ADMINS 1024
|
||||
#define MAX_ADMIN_BANS 1024
|
||||
#define MAX_ADMIN_NAMELOGS 128
|
||||
#define MAX_ADMIN_NAMELOG_NAMES 5
|
||||
#define MAX_ADMIN_FLAGS 64
|
||||
#define MAX_ADMIN_COMMANDS 64
|
||||
#define MAX_ADMIN_CMD_LEN 20
|
||||
#define MAX_ADMIN_BAN_REASON 50
|
||||
|
||||
#define MAX_ADMIN_BANSUSPEND_DAYS 14
|
||||
|
||||
/*
|
||||
* 1 - cannot be vote kicked, vote muted
|
||||
* 2 - cannot be censored or flood protected TODO
|
||||
* 3 - never loses credits for changing teams
|
||||
* 4 - can see team chat as a spectator
|
||||
* 5 - can switch teams any time, regardless of balance
|
||||
* 6 - does not need to specify a reason for a kick/ban
|
||||
* 7 - can call a vote at any time (regardless of a vote being disabled or
|
||||
* voting limitations)
|
||||
* 8 - does not need to specify a duration for a ban
|
||||
* 9 - can run commands from team chat
|
||||
* 0 - inactivity rules do not apply to them
|
||||
* ! - admin commands cannot be used on them
|
||||
* @ - does not show up as an admin in !listplayers
|
||||
* $ - sees all information in !listplayers
|
||||
* # - permanent designated builder
|
||||
* ? - sees and can use adminchat
|
||||
*/
|
||||
#define ADMF_IMMUNITY '1'
|
||||
#define ADMF_NOCENSORFLOOD '2' /* TODO */
|
||||
#define ADMF_TEAMCHANGEFREE '3'
|
||||
#define ADMF_SPEC_ALLCHAT '4'
|
||||
#define ADMF_FORCETEAMCHANGE '5'
|
||||
#define ADMF_UNACCOUNTABLE '6'
|
||||
#define ADMF_NO_VOTE_LIMIT '7'
|
||||
#define ADMF_CAN_PERM_BAN '8'
|
||||
#define ADMF_TEAMCHAT_CMD '9'
|
||||
#define ADMF_ACTIVITY '0'
|
||||
|
||||
#define ADMF_IMMUTABLE '!'
|
||||
#define ADMF_INCOGNITO '@'
|
||||
#define ADMF_SEESFULLLISTPLAYERS '$'
|
||||
#define ADMF_DBUILDER '#'
|
||||
#define ADMF_ADMINCHAT '?'
|
||||
|
||||
#define ADMF_BANIMMUNITY '&'
|
||||
|
||||
#define MAX_ADMIN_LISTITEMS 20
|
||||
#define MAX_ADMIN_SHOWBANS 10
|
||||
|
||||
#define MAX_ADMIN_MAPLOG_LENGTH 5
|
||||
|
||||
// important note: QVM does not seem to allow a single char to be a
|
||||
// member of a struct at init time. flag has been converted to char*
|
||||
typedef struct
|
||||
{
|
||||
char *keyword;
|
||||
qboolean ( * handler ) ( gentity_t *ent, int skiparg );
|
||||
char *flag;
|
||||
char *function; // used for !help
|
||||
char *syntax; // used for !help
|
||||
}
|
||||
g_admin_cmd_t;
|
||||
|
||||
typedef struct g_admin_level
|
||||
{
|
||||
int level;
|
||||
char name[ MAX_NAME_LENGTH ];
|
||||
char flags[ MAX_ADMIN_FLAGS ];
|
||||
}
|
||||
g_admin_level_t;
|
||||
|
||||
typedef struct g_admin_admin
|
||||
{
|
||||
char guid[ 33 ];
|
||||
char name[ MAX_NAME_LENGTH ];
|
||||
int level;
|
||||
char flags[ MAX_ADMIN_FLAGS ];
|
||||
}
|
||||
g_admin_admin_t;
|
||||
|
||||
typedef struct g_admin_ban
|
||||
{
|
||||
char name[ MAX_NAME_LENGTH ];
|
||||
char guid[ 33 ];
|
||||
char ip[ 18 ];
|
||||
char reason[ MAX_ADMIN_BAN_REASON ];
|
||||
char made[ 18 ]; // big enough for strftime() %c
|
||||
int expires;
|
||||
int suspend;
|
||||
char banner[ MAX_NAME_LENGTH ];
|
||||
}
|
||||
g_admin_ban_t;
|
||||
|
||||
typedef struct g_admin_command
|
||||
{
|
||||
char command[ MAX_ADMIN_CMD_LEN ];
|
||||
char exec[ MAX_QPATH ];
|
||||
char desc[ 50 ];
|
||||
int levels[ MAX_ADMIN_LEVELS + 1 ];
|
||||
}
|
||||
g_admin_command_t;
|
||||
|
||||
typedef struct g_admin_namelog
|
||||
{
|
||||
char name[ MAX_ADMIN_NAMELOG_NAMES ][MAX_NAME_LENGTH ];
|
||||
char ip[ 16 ];
|
||||
char guid[ 33 ];
|
||||
int slot;
|
||||
qboolean banned;
|
||||
}
|
||||
g_admin_namelog_t;
|
||||
|
||||
qboolean G_admin_ban_check( char *userinfo, char *reason, int rlen );
|
||||
qboolean G_admin_cmd_check( gentity_t *ent, qboolean say );
|
||||
qboolean G_admin_readconfig( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_permission( gentity_t *ent, char flag );
|
||||
qboolean G_admin_name_check( gentity_t *ent, char *name, char *err, int len );
|
||||
void G_admin_namelog_update( gclient_t *ent, qboolean disconnect );
|
||||
int G_admin_level( gentity_t *ent );
|
||||
|
||||
// ! command functions
|
||||
qboolean G_admin_time( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_setlevel( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_kick( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_adjustban( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_ban( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_unban( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_putteam( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_listadmins( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_listlayouts( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_listplayers( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_map( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_devmap( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_layoutsave( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_mute( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_denybuild( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_showbans( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_help( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_admintest( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_allready( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_cancelvote( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_passvote( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_spec999( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_register( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_rename( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_restart( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_nextmap( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_namelog( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_lock( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_unlock( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_L1( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_info( gentity_t *ent, int skiparg );
|
||||
void G_admin_info_parse( char *unparsed, char *dest, int maxlength );
|
||||
qboolean G_admin_putmespec( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_warn( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_designate( gentity_t *ent, int skiparg );
|
||||
//added
|
||||
qboolean G_admin_forcespec( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_unforcespec( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_pause( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_buildlog( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_revert( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_slap( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_L0( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_listmaps( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_immunity( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_suspendban( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_maplog( gentity_t *ent, int skiparg );
|
||||
void G_admin_maplog_update( void );
|
||||
//all of my commands WEEEEEEEEEE
|
||||
qboolean G_admin_switch( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_mix( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_steal( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_trade( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_drug( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_drop( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_cp( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_customgrav( gentity_t *ent, int skiparg );
|
||||
qboolean G_admin_explode( gentity_t *ent, int skiparg );
|
||||
|
||||
void G_admin_print( gentity_t *ent, char *m );
|
||||
void G_admin_buffer_print( gentity_t *ent, char *m );
|
||||
void G_admin_buffer_begin( void );
|
||||
void G_admin_buffer_end( gentity_t *ent );
|
||||
|
||||
void G_admin_duration( int secs, char *duration, int dursize );
|
||||
void G_admin_cleanup( void );
|
||||
void G_admin_namelog_cleanup( void );
|
||||
|
||||
#endif /* ifndef _G_ADMIN_H */
|
||||
File diff suppressed because it is too large.
Load diff
+1776
File diff suppressed because it is too large.
Load diff
+4416
File diff suppressed because it is too large.
Load diff
+1663
File diff suppressed because it is too large.
Load diff
+1376
File diff suppressed because it is too large.
Load diff
+2552
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,743 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// g_maprotation.c -- the map rotation system
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
static mapRotations_t mapRotations;
|
||||
|
||||
/*
|
||||
===============
|
||||
G_ParseCommandSection
|
||||
|
||||
Parse a map rotation command section
|
||||
===============
|
||||
*/
|
||||
static qboolean G_ParseMapCommandSection( mapRotationEntry_t *mre, char **text_p )
|
||||
{
|
||||
char *token;
|
||||
|
||||
// read optional parameters
|
||||
while( 1 )
|
||||
{
|
||||
token = COM_Parse( text_p );
|
||||
|
||||
if( !token )
|
||||
break;
|
||||
|
||||
if( !Q_stricmp( token, "" ) )
|
||||
return qfalse;
|
||||
|
||||
if( !Q_stricmp( token, "}" ) )
|
||||
return qtrue; //reached the end of this command section
|
||||
|
||||
if( !Q_stricmp( token, "layouts" ) )
|
||||
{
|
||||
token = COM_ParseExt( text_p, qfalse );
|
||||
mre->layouts[ 0 ] = '\0';
|
||||
while( token && token[ 0 ] != 0 )
|
||||
{
|
||||
Q_strcat( mre->layouts, sizeof( mre->layouts ), token );
|
||||
Q_strcat( mre->layouts, sizeof( mre->layouts ), " " );
|
||||
token = COM_ParseExt( text_p, qfalse );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Q_strncpyz( mre->postCmds[ mre->numCmds ], token, sizeof( mre->postCmds[ 0 ] ) );
|
||||
Q_strcat( mre->postCmds[ mre->numCmds ], sizeof( mre->postCmds[ 0 ] ), " " );
|
||||
|
||||
token = COM_ParseExt( text_p, qfalse );
|
||||
|
||||
while( token && token[ 0 ] != 0 )
|
||||
{
|
||||
Q_strcat( mre->postCmds[ mre->numCmds ], sizeof( mre->postCmds[ 0 ] ), token );
|
||||
Q_strcat( mre->postCmds[ mre->numCmds ], sizeof( mre->postCmds[ 0 ] ), " " );
|
||||
token = COM_ParseExt( text_p, qfalse );
|
||||
}
|
||||
|
||||
if( mre->numCmds == MAX_MAP_COMMANDS )
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: maximum number of map commands (%d) reached\n",
|
||||
MAX_MAP_COMMANDS );
|
||||
return qfalse;
|
||||
}
|
||||
else
|
||||
mre->numCmds++;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_ParseMapRotation
|
||||
|
||||
Parse a map rotation section
|
||||
===============
|
||||
*/
|
||||
static qboolean G_ParseMapRotation( mapRotation_t *mr, char **text_p )
|
||||
{
|
||||
char *token;
|
||||
qboolean mnSet = qfalse;
|
||||
mapRotationEntry_t *mre = NULL;
|
||||
mapRotationCondition_t *mrc;
|
||||
|
||||
// read optional parameters
|
||||
while( 1 )
|
||||
{
|
||||
token = COM_Parse( text_p );
|
||||
|
||||
if( !token )
|
||||
break;
|
||||
|
||||
if( !Q_stricmp( token, "" ) )
|
||||
return qfalse;
|
||||
|
||||
if( !Q_stricmp( token, "{" ) )
|
||||
{
|
||||
if( !mnSet )
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: map settings section with no name\n" );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if( !G_ParseMapCommandSection( mre, text_p ) )
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: failed to parse map command section\n" );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
mnSet = qfalse;
|
||||
continue;
|
||||
}
|
||||
else if( !Q_stricmp( token, "goto" ) )
|
||||
{
|
||||
token = COM_Parse( text_p );
|
||||
|
||||
if( !token )
|
||||
break;
|
||||
|
||||
mrc = &mre->conditions[ mre->numConditions ];
|
||||
mrc->unconditional = qtrue;
|
||||
Q_strncpyz( mrc->dest, token, sizeof( mrc->dest ) );
|
||||
|
||||
if( mre->numConditions == MAX_MAP_ROTATION_CONDS )
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: maximum number of conditions for one map (%d) reached\n",
|
||||
MAX_MAP_ROTATION_CONDS );
|
||||
return qfalse;
|
||||
}
|
||||
else
|
||||
mre->numConditions++;
|
||||
|
||||
continue;
|
||||
}
|
||||
else if( !Q_stricmp( token, "if" ) )
|
||||
{
|
||||
token = COM_Parse( text_p );
|
||||
|
||||
if( !token )
|
||||
break;
|
||||
|
||||
mrc = &mre->conditions[ mre->numConditions ];
|
||||
|
||||
if( !Q_stricmp( token, "numClients" ) )
|
||||
{
|
||||
mrc->lhs = MCV_NUMCLIENTS;
|
||||
|
||||
token = COM_Parse( text_p );
|
||||
|
||||
if( !token )
|
||||
break;
|
||||
|
||||
if( !Q_stricmp( token, "<" ) )
|
||||
mrc->op = MCO_LT;
|
||||
else if( !Q_stricmp( token, ">" ) )
|
||||
mrc->op = MCO_GT;
|
||||
else if( !Q_stricmp( token, "=" ) )
|
||||
mrc->op = MCO_EQ;
|
||||
else
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: invalid operator in expression: %s\n", token );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
token = COM_Parse( text_p );
|
||||
|
||||
if( !token )
|
||||
break;
|
||||
|
||||
mrc->numClients = atoi( token );
|
||||
}
|
||||
else if( !Q_stricmp( token, "lastWin" ) )
|
||||
{
|
||||
mrc->lhs = MCV_LASTWIN;
|
||||
|
||||
token = COM_Parse( text_p );
|
||||
|
||||
if( !token )
|
||||
break;
|
||||
|
||||
if( !Q_stricmp( token, "aliens" ) )
|
||||
mrc->lastWin = PTE_ALIENS;
|
||||
else if( !Q_stricmp( token, "humans" ) )
|
||||
mrc->lastWin = PTE_HUMANS;
|
||||
else
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: invalid right hand side in expression: %s\n", token );
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
else if( !Q_stricmp( token, "random" ) )
|
||||
mrc->lhs = MCV_RANDOM;
|
||||
else
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: invalid left hand side in expression: %s\n", token );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
token = COM_Parse( text_p );
|
||||
|
||||
if( !token )
|
||||
break;
|
||||
|
||||
mrc->unconditional = qfalse;
|
||||
Q_strncpyz( mrc->dest, token, sizeof( mrc->dest ) );
|
||||
|
||||
if( mre->numConditions == MAX_MAP_ROTATION_CONDS )
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: maximum number of conditions for one map (%d) reached\n",
|
||||
MAX_MAP_ROTATION_CONDS );
|
||||
return qfalse;
|
||||
}
|
||||
else
|
||||
mre->numConditions++;
|
||||
|
||||
continue;
|
||||
}
|
||||
else if( !Q_stricmp( token, "}" ) )
|
||||
return qtrue; //reached the end of this map rotation
|
||||
|
||||
mre = &mr->maps[ mr->numMaps ];
|
||||
|
||||
if( mr->numMaps == MAX_MAP_ROTATION_MAPS )
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: maximum number of maps in one rotation (%d) reached\n",
|
||||
MAX_MAP_ROTATION_MAPS );
|
||||
return qfalse;
|
||||
}
|
||||
else
|
||||
mr->numMaps++;
|
||||
|
||||
Q_strncpyz( mre->name, token, sizeof( mre->name ) );
|
||||
mnSet = qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_ParseMapRotationFile
|
||||
|
||||
Load the map rotations from a map rotation file
|
||||
===============
|
||||
*/
|
||||
static qboolean G_ParseMapRotationFile( const char *fileName )
|
||||
{
|
||||
char *text_p;
|
||||
int i;
|
||||
int len;
|
||||
char *token;
|
||||
char text[ 20000 ];
|
||||
char mrName[ MAX_QPATH ];
|
||||
qboolean mrNameSet = qfalse;
|
||||
fileHandle_t f;
|
||||
|
||||
// load the file
|
||||
len = trap_FS_FOpenFile( fileName, &f, FS_READ );
|
||||
if( len <= 0 )
|
||||
return qfalse;
|
||||
|
||||
if( len >= sizeof( text ) - 1 )
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: map rotation file %s too long\n", fileName );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
trap_FS_Read( text, len, f );
|
||||
text[ len ] = 0;
|
||||
trap_FS_FCloseFile( f );
|
||||
|
||||
// parse the text
|
||||
text_p = text;
|
||||
|
||||
// read optional parameters
|
||||
while( 1 )
|
||||
{
|
||||
token = COM_Parse( &text_p );
|
||||
|
||||
if( !token )
|
||||
break;
|
||||
|
||||
if( !Q_stricmp( token, "" ) )
|
||||
break;
|
||||
|
||||
if( !Q_stricmp( token, "{" ) )
|
||||
{
|
||||
if( mrNameSet )
|
||||
{
|
||||
//check for name space clashes
|
||||
for( i = 0; i < mapRotations.numRotations; i++ )
|
||||
{
|
||||
if( !Q_stricmp( mapRotations.rotations[ i ].name, mrName ) )
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: a map rotation is already named %s\n", mrName );
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
Q_strncpyz( mapRotations.rotations[ mapRotations.numRotations ].name, mrName, MAX_QPATH );
|
||||
|
||||
if( !G_ParseMapRotation( &mapRotations.rotations[ mapRotations.numRotations ], &text_p ) )
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: %s: failed to parse map rotation %s\n", fileName, mrName );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
//start parsing particle systems again
|
||||
mrNameSet = qfalse;
|
||||
|
||||
if( mapRotations.numRotations == MAX_MAP_ROTATIONS )
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: maximum number of map rotations (%d) reached\n",
|
||||
MAX_MAP_ROTATIONS );
|
||||
return qfalse;
|
||||
}
|
||||
else
|
||||
mapRotations.numRotations++;
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: unamed map rotation\n" );
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
if( !mrNameSet )
|
||||
{
|
||||
Q_strncpyz( mrName, token, sizeof( mrName ) );
|
||||
mrNameSet = qtrue;
|
||||
}
|
||||
else
|
||||
{
|
||||
G_Printf( S_COLOR_RED "ERROR: map rotation already named\n" );
|
||||
return qfalse;
|
||||
}
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_PrintRotations
|
||||
|
||||
Print the parsed map rotations
|
||||
===============
|
||||
*/
|
||||
void G_PrintRotations( void )
|
||||
{
|
||||
int i, j, k;
|
||||
|
||||
G_Printf( "Map rotations as parsed:\n\n" );
|
||||
|
||||
for( i = 0; i < mapRotations.numRotations; i++ )
|
||||
{
|
||||
G_Printf( "rotation: %s\n{\n", mapRotations.rotations[ i ].name );
|
||||
|
||||
for( j = 0; j < mapRotations.rotations[ i ].numMaps; j++ )
|
||||
{
|
||||
G_Printf( " map: %s\n {\n", mapRotations.rotations[ i ].maps[ j ].name );
|
||||
|
||||
for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numCmds; k++ )
|
||||
{
|
||||
G_Printf( " command: %s\n",
|
||||
mapRotations.rotations[ i ].maps[ j ].postCmds[ k ] );
|
||||
}
|
||||
|
||||
G_Printf( " }\n" );
|
||||
|
||||
for( k = 0; k < mapRotations.rotations[ i ].maps[ j ].numConditions; k++ )
|
||||
{
|
||||
G_Printf( " conditional: %s\n",
|
||||
mapRotations.rotations[ i ].maps[ j ].conditions[ k ].dest );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
G_Printf( "}\n" );
|
||||
}
|
||||
|
||||
G_Printf( "Total memory used: %d bytes\n", sizeof( mapRotations ) );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_GetCurrentMapArray
|
||||
|
||||
Fill a static array with the current map of each rotation
|
||||
===============
|
||||
*/
|
||||
static int *G_GetCurrentMapArray( void )
|
||||
{
|
||||
static int currentMap[ MAX_MAP_ROTATIONS ];
|
||||
int i = 0;
|
||||
char text[ MAX_MAP_ROTATIONS * 2 ];
|
||||
char *text_p, *token;
|
||||
|
||||
Q_strncpyz( text, g_currentMap.string, sizeof( text ) );
|
||||
|
||||
text_p = text;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
token = COM_Parse( &text_p );
|
||||
|
||||
if( !token )
|
||||
break;
|
||||
|
||||
if( !Q_stricmp( token, "" ) )
|
||||
break;
|
||||
|
||||
currentMap[ i++ ] = atoi( token );
|
||||
}
|
||||
|
||||
return currentMap;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_SetCurrentMap
|
||||
|
||||
Set the current map in some rotation
|
||||
===============
|
||||
*/
|
||||
static void G_SetCurrentMap( int currentMap, int rotation )
|
||||
{
|
||||
char text[ MAX_MAP_ROTATIONS * 2 ] = { 0 };
|
||||
int *p = G_GetCurrentMapArray( );
|
||||
int i;
|
||||
|
||||
p[ rotation ] = currentMap;
|
||||
|
||||
for( i = 0; i < mapRotations.numRotations; i++ )
|
||||
Q_strcat( text, sizeof( text ), va( "%d ", p[ i ] ) );
|
||||
|
||||
trap_Cvar_Set( "g_currentMap", text );
|
||||
trap_Cvar_Update( &g_currentMap );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_GetCurrentMap
|
||||
|
||||
Return the current map in some rotation
|
||||
===============
|
||||
*/
|
||||
static int G_GetCurrentMap( int rotation )
|
||||
{
|
||||
int *p = G_GetCurrentMapArray( );
|
||||
|
||||
return p[ rotation ];
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_IssueMapChange
|
||||
|
||||
Send commands to the server to actually change the map
|
||||
===============
|
||||
*/
|
||||
static void G_IssueMapChange( int rotation )
|
||||
{
|
||||
int i;
|
||||
int map = G_GetCurrentMap( rotation );
|
||||
char cmd[ MAX_TOKEN_CHARS ];
|
||||
|
||||
// allow a manually defined g_layouts setting to override the maprotation
|
||||
if( !g_layouts.string[ 0 ] &&
|
||||
mapRotations.rotations[ rotation ].maps[ map ].layouts[ 0 ] )
|
||||
{
|
||||
trap_Cvar_Set( "g_layouts",
|
||||
mapRotations.rotations[ rotation ].maps[ map ].layouts );
|
||||
}
|
||||
|
||||
trap_SendConsoleCommand( EXEC_APPEND, va( "map %s\n",
|
||||
mapRotations.rotations[ rotation ].maps[ map ].name ) );
|
||||
|
||||
// load up map defaults if g_mapConfigs is set
|
||||
G_MapConfigs( mapRotations.rotations[ rotation ].maps[ map ].name );
|
||||
|
||||
for( i = 0; i < mapRotations.rotations[ rotation ].maps[ map ].numCmds; i++ )
|
||||
{
|
||||
Q_strncpyz( cmd, mapRotations.rotations[ rotation ].maps[ map ].postCmds[ i ],
|
||||
sizeof( cmd ) );
|
||||
Q_strcat( cmd, sizeof( cmd ), "\n" );
|
||||
trap_SendConsoleCommand( EXEC_APPEND, cmd );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_ResolveConditionDestination
|
||||
|
||||
Resolve the destination of some condition
|
||||
===============
|
||||
*/
|
||||
static mapConditionType_t G_ResolveConditionDestination( int *n, char *name )
|
||||
{
|
||||
int i;
|
||||
|
||||
//search the current rotation first...
|
||||
for( i = 0; i < mapRotations.rotations[ g_currentMapRotation.integer ].numMaps; i++ )
|
||||
{
|
||||
if( !Q_stricmp( mapRotations.rotations[ g_currentMapRotation.integer ].maps[ i ].name, name ) )
|
||||
{
|
||||
*n = i;
|
||||
return MCT_MAP;
|
||||
}
|
||||
}
|
||||
|
||||
//...then search the rotation names
|
||||
for( i = 0; i < mapRotations.numRotations; i++ )
|
||||
{
|
||||
if( !Q_stricmp( mapRotations.rotations[ i ].name, name ) )
|
||||
{
|
||||
*n = i;
|
||||
return MCT_ROTATION;
|
||||
}
|
||||
}
|
||||
|
||||
//this should probably be prevented by a 2nd pass at compile time
|
||||
//but i'm lazy (FIXME)
|
||||
return MCT_ERR;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_EvaluateMapCondition
|
||||
|
||||
Evaluate a map condition
|
||||
===============
|
||||
*/
|
||||
static qboolean G_EvaluateMapCondition( mapRotationCondition_t *mrc )
|
||||
{
|
||||
switch( mrc->lhs )
|
||||
{
|
||||
case MCV_RANDOM:
|
||||
return rand( ) & 1;
|
||||
break;
|
||||
|
||||
case MCV_NUMCLIENTS:
|
||||
switch( mrc->op )
|
||||
{
|
||||
case MCO_LT:
|
||||
return level.numConnectedClients < mrc->numClients;
|
||||
break;
|
||||
|
||||
case MCO_GT:
|
||||
return level.numConnectedClients > mrc->numClients;
|
||||
break;
|
||||
|
||||
case MCO_EQ:
|
||||
return level.numConnectedClients == mrc->numClients;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case MCV_LASTWIN:
|
||||
return level.lastWin == mrc->lastWin;
|
||||
break;
|
||||
|
||||
default:
|
||||
case MCV_ERR:
|
||||
G_Printf( S_COLOR_RED "ERROR: malformed map switch condition\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_AdvanceMapRotation
|
||||
|
||||
Increment the current map rotation
|
||||
===============
|
||||
*/
|
||||
qboolean G_AdvanceMapRotation( void )
|
||||
{
|
||||
mapRotation_t *mr;
|
||||
mapRotationEntry_t *mre;
|
||||
mapRotationCondition_t *mrc;
|
||||
int currentRotation, currentMap, nextMap;
|
||||
int i, n;
|
||||
mapConditionType_t mct;
|
||||
|
||||
if( ( currentRotation = g_currentMapRotation.integer ) == NOT_ROTATING )
|
||||
return qfalse;
|
||||
|
||||
currentMap = G_GetCurrentMap( currentRotation );
|
||||
|
||||
mr = &mapRotations.rotations[ currentRotation ];
|
||||
mre = &mr->maps[ currentMap ];
|
||||
nextMap = ( currentMap + 1 ) % mr->numMaps;
|
||||
|
||||
for( i = 0; i < mre->numConditions; i++ )
|
||||
{
|
||||
mrc = &mre->conditions[ i ];
|
||||
|
||||
if( mrc->unconditional || G_EvaluateMapCondition( mrc ) )
|
||||
{
|
||||
mct = G_ResolveConditionDestination( &n, mrc->dest );
|
||||
|
||||
switch( mct )
|
||||
{
|
||||
case MCT_MAP:
|
||||
nextMap = n;
|
||||
break;
|
||||
|
||||
case MCT_ROTATION:
|
||||
G_StartMapRotation( mrc->dest, qtrue );
|
||||
return qtrue;
|
||||
break;
|
||||
|
||||
default:
|
||||
case MCT_ERR:
|
||||
G_Printf( S_COLOR_YELLOW "WARNING: map switch destination could not be resolved: %s\n",
|
||||
mrc->dest );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
G_SetCurrentMap( nextMap, currentRotation );
|
||||
G_IssueMapChange( currentRotation );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_StartMapRotation
|
||||
|
||||
Switch to a new map rotation
|
||||
===============
|
||||
*/
|
||||
qboolean G_StartMapRotation( char *name, qboolean changeMap )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < mapRotations.numRotations; i++ )
|
||||
{
|
||||
if( !Q_stricmp( mapRotations.rotations[ i ].name, name ) )
|
||||
{
|
||||
trap_Cvar_Set( "g_currentMapRotation", va( "%d", i ) );
|
||||
trap_Cvar_Update( &g_currentMapRotation );
|
||||
|
||||
if( changeMap )
|
||||
G_IssueMapChange( i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( i == mapRotations.numRotations )
|
||||
return qfalse;
|
||||
else
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_StopMapRotation
|
||||
|
||||
Stop the current map rotation
|
||||
===============
|
||||
*/
|
||||
void G_StopMapRotation( void )
|
||||
{
|
||||
trap_Cvar_Set( "g_currentMapRotation", va( "%d", NOT_ROTATING ) );
|
||||
trap_Cvar_Update( &g_currentMapRotation );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_MapRotationActive
|
||||
|
||||
Test if any map rotation is currently active
|
||||
===============
|
||||
*/
|
||||
qboolean G_MapRotationActive( void )
|
||||
{
|
||||
return ( g_currentMapRotation.integer != NOT_ROTATING );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_InitMapRotations
|
||||
|
||||
Load and intialise the map rotations
|
||||
===============
|
||||
*/
|
||||
void G_InitMapRotations( void )
|
||||
{
|
||||
const char *fileName = "maprotation.cfg";
|
||||
fileHandle_t f;
|
||||
|
||||
//load the file if it exists
|
||||
if( trap_FS_FOpenFile( fileName, &f, FS_READ ) > 0 )
|
||||
{
|
||||
trap_FS_FCloseFile( f );
|
||||
|
||||
if( !G_ParseMapRotationFile( fileName ) )
|
||||
G_Printf( S_COLOR_RED "ERROR: failed to parse %s file\n", fileName );
|
||||
}
|
||||
else
|
||||
G_Printf( "%s file not found.\n", fileName );
|
||||
|
||||
if( g_currentMapRotation.integer == NOT_ROTATING )
|
||||
{
|
||||
if( g_initialMapRotation.string[ 0 ] != 0 )
|
||||
{
|
||||
G_StartMapRotation( g_initialMapRotation.string, qfalse );
|
||||
|
||||
trap_Cvar_Set( "g_initialMapRotation", "" );
|
||||
trap_Cvar_Update( &g_initialMapRotation );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
#define POOLSIZE ( 1024 * 1024 )
|
||||
#define FREEMEMCOOKIE ((int)0xDEADBE3F) // Any unlikely to be used value
|
||||
#define ROUNDBITS 31 // Round to 32 bytes
|
||||
|
||||
struct freememnode
|
||||
{
|
||||
// Size of ROUNDBITS
|
||||
int cookie, size; // Size includes node (obviously)
|
||||
struct freememnode *prev, *next;
|
||||
};
|
||||
|
||||
static char memoryPool[POOLSIZE];
|
||||
static struct freememnode *freehead;
|
||||
static int freemem;
|
||||
|
||||
void *G_Alloc( int size )
|
||||
{
|
||||
// Find a free block and allocate.
|
||||
// Does two passes, attempts to fill same-sized free slot first.
|
||||
|
||||
struct freememnode *fmn, *prev, *next, *smallest;
|
||||
int allocsize, smallestsize;
|
||||
char *endptr;
|
||||
int *ptr;
|
||||
|
||||
allocsize = ( size + sizeof(int) + ROUNDBITS ) & ~ROUNDBITS; // Round to 32-byte boundary
|
||||
ptr = NULL;
|
||||
|
||||
smallest = NULL;
|
||||
smallestsize = POOLSIZE + 1; // Guaranteed not to miss any slots :)
|
||||
for( fmn = freehead; fmn; fmn = fmn->next )
|
||||
{
|
||||
if( fmn->cookie != FREEMEMCOOKIE )
|
||||
G_Error( "G_Alloc: Memory corruption detected!\n" );
|
||||
|
||||
if( fmn->size >= allocsize )
|
||||
{
|
||||
// We've got a block
|
||||
if( fmn->size == allocsize )
|
||||
{
|
||||
// Same size, just remove
|
||||
|
||||
prev = fmn->prev;
|
||||
next = fmn->next;
|
||||
if( prev )
|
||||
prev->next = next; // Point previous node to next
|
||||
if( next )
|
||||
next->prev = prev; // Point next node to previous
|
||||
if( fmn == freehead )
|
||||
freehead = next; // Set head pointer to next
|
||||
ptr = (int *) fmn;
|
||||
break; // Stop the loop, this is fine
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep track of the smallest free slot
|
||||
if( fmn->size < smallestsize )
|
||||
{
|
||||
smallest = fmn;
|
||||
smallestsize = fmn->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !ptr && smallest )
|
||||
{
|
||||
// We found a slot big enough
|
||||
smallest->size -= allocsize;
|
||||
endptr = (char *) smallest + smallest->size;
|
||||
ptr = (int *) endptr;
|
||||
}
|
||||
|
||||
if( ptr )
|
||||
{
|
||||
freemem -= allocsize;
|
||||
if( g_debugAlloc.integer )
|
||||
G_Printf( "G_Alloc of %i bytes (%i left)\n", allocsize, freemem );
|
||||
memset( ptr, 0, allocsize );
|
||||
*ptr++ = allocsize; // Store a copy of size for deallocation
|
||||
return( (void *) ptr );
|
||||
}
|
||||
|
||||
G_Error( "G_Alloc: failed on allocation of %i bytes\n", size );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
void G_Free( void *ptr )
|
||||
{
|
||||
// Release allocated memory, add it to the free list.
|
||||
|
||||
struct freememnode *fmn;
|
||||
char *freeend;
|
||||
int *freeptr;
|
||||
|
||||
freeptr = ptr;
|
||||
freeptr--;
|
||||
|
||||
freemem += *freeptr;
|
||||
if( g_debugAlloc.integer )
|
||||
G_Printf( "G_Free of %i bytes (%i left)\n", *freeptr, freemem );
|
||||
|
||||
for( fmn = freehead; fmn; fmn = fmn->next )
|
||||
{
|
||||
freeend = ((char *) fmn) + fmn->size;
|
||||
if( freeend == (char *) freeptr )
|
||||
{
|
||||
// Released block can be merged to an existing node
|
||||
|
||||
fmn->size += *freeptr; // Add size of node.
|
||||
return;
|
||||
}
|
||||
}
|
||||
// No merging, add to head of list
|
||||
|
||||
fmn = (struct freememnode *) freeptr;
|
||||
fmn->size = *freeptr; // Set this first to avoid corrupting *freeptr
|
||||
fmn->cookie = FREEMEMCOOKIE;
|
||||
fmn->prev = NULL;
|
||||
fmn->next = freehead;
|
||||
freehead->prev = fmn;
|
||||
freehead = fmn;
|
||||
}
|
||||
|
||||
void G_InitMemory( void )
|
||||
{
|
||||
// Set up the initial node
|
||||
|
||||
freehead = (struct freememnode *)memoryPool;
|
||||
freehead->cookie = FREEMEMCOOKIE;
|
||||
freehead->size = POOLSIZE;
|
||||
freehead->next = NULL;
|
||||
freehead->prev = NULL;
|
||||
freemem = sizeof( memoryPool );
|
||||
}
|
||||
|
||||
void G_DefragmentMemory( void )
|
||||
{
|
||||
// If there's a frenzy of deallocation and we want to
|
||||
// allocate something big, this is useful. Otherwise...
|
||||
// not much use.
|
||||
|
||||
struct freememnode *startfmn, *endfmn, *fmn;
|
||||
|
||||
for( startfmn = freehead; startfmn; )
|
||||
{
|
||||
endfmn = (struct freememnode *)(((char *) startfmn) + startfmn->size);
|
||||
for( fmn = freehead; fmn; )
|
||||
{
|
||||
if( fmn->cookie != FREEMEMCOOKIE )
|
||||
G_Error( "G_DefragmentMemory: Memory corruption detected!\n" );
|
||||
|
||||
if( fmn == endfmn )
|
||||
{
|
||||
// We can add fmn onto startfmn.
|
||||
|
||||
if( fmn->prev )
|
||||
fmn->prev->next = fmn->next;
|
||||
if( fmn->next )
|
||||
{
|
||||
if( !(fmn->next->prev = fmn->prev) )
|
||||
freehead = fmn->next; // We're removing the head node
|
||||
}
|
||||
startfmn->size += fmn->size;
|
||||
memset( fmn, 0, sizeof(struct freememnode) ); // A redundant call, really.
|
||||
|
||||
startfmn = freehead;
|
||||
endfmn = fmn = NULL; // Break out of current loop
|
||||
}
|
||||
else
|
||||
fmn = fmn->next;
|
||||
}
|
||||
|
||||
if( endfmn )
|
||||
startfmn = startfmn->next; // endfmn acts as a 'restart' flag here
|
||||
}
|
||||
}
|
||||
|
||||
void Svcmd_GameMem_f( void )
|
||||
{
|
||||
// Give a breakdown of memory
|
||||
|
||||
struct freememnode *fmn;
|
||||
|
||||
G_Printf( "Game memory status: %i out of %i bytes allocated\n", POOLSIZE - freemem, POOLSIZE );
|
||||
|
||||
for( fmn = freehead; fmn; fmn = fmn->next )
|
||||
G_Printf( " %dd: %d bytes free.\n", fmn, fmn->size );
|
||||
G_Printf( "Status complete.\n" );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
|
||||
/*QUAKED func_group (0 0 0) ?
|
||||
Used to group brushes together just for editor convenience. They are turned into normal brushes by the utilities.
|
||||
*/
|
||||
|
||||
|
||||
/*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
|
||||
Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
|
||||
*/
|
||||
void SP_info_null( gentity_t *self )
|
||||
{
|
||||
G_FreeEntity( self );
|
||||
}
|
||||
|
||||
|
||||
/*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
|
||||
Used as a positional target for in-game calculation, like jumppad targets.
|
||||
target_position does the same thing
|
||||
*/
|
||||
void SP_info_notnull( gentity_t *self )
|
||||
{
|
||||
G_SetOrigin( self, self->s.origin );
|
||||
}
|
||||
|
||||
|
||||
/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear
|
||||
Non-displayed light.
|
||||
"light" overrides the default 300 intensity.
|
||||
Linear checbox gives linear falloff instead of inverse square
|
||||
Lights pointed at a target will be spotlights.
|
||||
"radius" overrides the default 64 unit radius of a spotlight at the target point.
|
||||
*/
|
||||
void SP_light( gentity_t *self )
|
||||
{
|
||||
G_FreeEntity( self );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=================================================================================
|
||||
|
||||
TELEPORTERS
|
||||
|
||||
=================================================================================
|
||||
*/
|
||||
|
||||
void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles )
|
||||
{
|
||||
// unlink to make sure it can't possibly interfere with G_KillBox
|
||||
trap_UnlinkEntity( player );
|
||||
|
||||
VectorCopy( origin, player->client->ps.origin );
|
||||
player->client->ps.origin[ 2 ] += 1;
|
||||
|
||||
// spit the player out
|
||||
AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
|
||||
VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
|
||||
player->client->ps.pm_time = 160; // hold time
|
||||
player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
|
||||
|
||||
// toggle the teleport bit so the client knows to not lerp
|
||||
player->client->ps.eFlags ^= EF_TELEPORT_BIT;
|
||||
G_UnlaggedClear( player );
|
||||
|
||||
// set angles
|
||||
SetClientViewAngle( player, angles );
|
||||
|
||||
// kill anything at the destination
|
||||
if( player->client->sess.sessionTeam != TEAM_SPECTATOR )
|
||||
G_KillBox( player );
|
||||
|
||||
// save results of pmove
|
||||
BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
|
||||
|
||||
// use the precise origin for linking
|
||||
VectorCopy( player->client->ps.origin, player->r.currentOrigin );
|
||||
|
||||
if( player->client->sess.sessionTeam != TEAM_SPECTATOR )
|
||||
trap_LinkEntity (player);
|
||||
}
|
||||
|
||||
|
||||
/*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
|
||||
Point teleporters at these.
|
||||
Now that we don't have teleport destination pads, this is just
|
||||
an info_notnull
|
||||
*/
|
||||
void SP_misc_teleporter_dest( gentity_t *ent )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//===========================================================
|
||||
|
||||
/*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16)
|
||||
"model" arbitrary .md3 file to display
|
||||
*/
|
||||
void SP_misc_model( gentity_t *ent )
|
||||
{
|
||||
#if 0
|
||||
ent->s.modelindex = G_ModelIndex( ent->model );
|
||||
VectorSet (ent->mins, -16, -16, -16);
|
||||
VectorSet (ent->maxs, 16, 16, 16);
|
||||
trap_LinkEntity (ent);
|
||||
|
||||
G_SetOrigin( ent, ent->s.origin );
|
||||
VectorCopy( ent->s.angles, ent->s.apos.trBase );
|
||||
#else
|
||||
G_FreeEntity( ent );
|
||||
#endif
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
|
||||
void locateCamera( gentity_t *ent )
|
||||
{
|
||||
vec3_t dir;
|
||||
gentity_t *target;
|
||||
gentity_t *owner;
|
||||
|
||||
owner = G_PickTarget( ent->target );
|
||||
if( !owner )
|
||||
{
|
||||
G_Printf( "Couldn't find target for misc_portal_surface\n" );
|
||||
G_FreeEntity( ent );
|
||||
return;
|
||||
}
|
||||
ent->r.ownerNum = owner->s.number;
|
||||
|
||||
// frame holds the rotate speed
|
||||
if( owner->spawnflags & 1 )
|
||||
ent->s.frame = 25;
|
||||
else if( owner->spawnflags & 2 )
|
||||
ent->s.frame = 75;
|
||||
|
||||
// swing camera ?
|
||||
if( owner->spawnflags & 4 )
|
||||
{
|
||||
// set to 0 for no rotation at all
|
||||
ent->s.powerups = 0;
|
||||
}
|
||||
else
|
||||
ent->s.powerups = 1;
|
||||
|
||||
// clientNum holds the rotate offset
|
||||
ent->s.clientNum = owner->s.clientNum;
|
||||
|
||||
VectorCopy( owner->s.origin, ent->s.origin2 );
|
||||
|
||||
// see if the portal_camera has a target
|
||||
target = G_PickTarget( owner->target );
|
||||
if( target )
|
||||
{
|
||||
VectorSubtract( target->s.origin, owner->s.origin, dir );
|
||||
VectorNormalize( dir );
|
||||
}
|
||||
else
|
||||
G_SetMovedir( owner->s.angles, dir );
|
||||
|
||||
ent->s.eventParm = DirToByte( dir );
|
||||
}
|
||||
|
||||
/*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8)
|
||||
The portal surface nearest this entity will show a view from the targeted misc_portal_camera, or a mirror view if untargeted.
|
||||
This must be within 64 world units of the surface!
|
||||
*/
|
||||
void SP_misc_portal_surface( gentity_t *ent )
|
||||
{
|
||||
VectorClear( ent->r.mins );
|
||||
VectorClear( ent->r.maxs );
|
||||
trap_LinkEntity( ent );
|
||||
|
||||
ent->r.svFlags = SVF_PORTAL;
|
||||
ent->s.eType = ET_PORTAL;
|
||||
|
||||
if( !ent->target )
|
||||
{
|
||||
VectorCopy( ent->s.origin, ent->s.origin2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->think = locateCamera;
|
||||
ent->nextthink = level.time + 100;
|
||||
}
|
||||
}
|
||||
|
||||
/*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate noswing
|
||||
|
||||
The target for a misc_portal_director. You can set either angles or target another entity to determine the direction of view.
|
||||
"roll" an angle modifier to orient the camera around the target vector;
|
||||
*/
|
||||
void SP_misc_portal_camera( gentity_t *ent )
|
||||
{
|
||||
float roll;
|
||||
|
||||
VectorClear( ent->r.mins );
|
||||
VectorClear( ent->r.maxs );
|
||||
trap_LinkEntity( ent );
|
||||
|
||||
G_SpawnFloat( "roll", "0", &roll );
|
||||
|
||||
ent->s.clientNum = roll / 360.0f * 256;
|
||||
}
|
||||
|
||||
/*
|
||||
======================================================================
|
||||
|
||||
NEAT EFFECTS AND STUFF FOR TREMULOUS
|
||||
|
||||
======================================================================
|
||||
*/
|
||||
|
||||
void SP_toggle_particle_system( gentity_t *self )
|
||||
{
|
||||
//toggle EF_NODRAW
|
||||
self->s.eFlags ^= EF_NODRAW;
|
||||
|
||||
self->nextthink = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SP_use_particle_system
|
||||
|
||||
Use function for particle_system
|
||||
===============
|
||||
*/
|
||||
void SP_use_particle_system( gentity_t *self, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
SP_toggle_particle_system( self );
|
||||
|
||||
if( self->wait > 0.0f )
|
||||
{
|
||||
self->think = SP_toggle_particle_system;
|
||||
self->nextthink = level.time + (int)( self->wait * 1000 );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SP_spawn_particle_system
|
||||
|
||||
Spawn function for particle system
|
||||
===============
|
||||
*/
|
||||
void SP_misc_particle_system( gentity_t *self )
|
||||
{
|
||||
char *s;
|
||||
|
||||
G_SetOrigin( self, self->s.origin );
|
||||
|
||||
G_SpawnString( "psName", "", &s );
|
||||
G_SpawnFloat( "wait", "0", &self->wait );
|
||||
|
||||
//add the particle system to the client precache list
|
||||
self->s.modelindex = G_ParticleSystemIndex( s );
|
||||
|
||||
if( self->spawnflags & 1 )
|
||||
self->s.eFlags |= EF_NODRAW;
|
||||
|
||||
self->use = SP_use_particle_system;
|
||||
self->s.eType = ET_PARTICLE_SYSTEM;
|
||||
trap_LinkEntity( self );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SP_use_anim_model
|
||||
|
||||
Use function for anim model
|
||||
===============
|
||||
*/
|
||||
void SP_use_anim_model( gentity_t *self, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
if( self->spawnflags & 1 )
|
||||
{
|
||||
//if spawnflag 1 is set
|
||||
//toggle EF_NODRAW
|
||||
if( self->s.eFlags & EF_NODRAW )
|
||||
self->s.eFlags &= ~EF_NODRAW;
|
||||
else
|
||||
self->s.eFlags |= EF_NODRAW;
|
||||
}
|
||||
else
|
||||
{
|
||||
//if the animation loops then toggle the animation
|
||||
//toggle EF_MOVER_STOP
|
||||
if( self->s.eFlags & EF_MOVER_STOP )
|
||||
self->s.eFlags &= ~EF_MOVER_STOP;
|
||||
else
|
||||
self->s.eFlags |= EF_MOVER_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SP_misc_anim_model
|
||||
|
||||
Spawn function for anim model
|
||||
===============
|
||||
*/
|
||||
void SP_misc_anim_model( gentity_t *self )
|
||||
{
|
||||
self->s.powerups = (int)self->animation[ 0 ];
|
||||
self->s.weapon = (int)self->animation[ 1 ];
|
||||
self->s.torsoAnim = (int)self->animation[ 2 ];
|
||||
self->s.legsAnim = (int)self->animation[ 3 ];
|
||||
|
||||
self->s.angles2[ 0 ] = self->pos2[ 0 ];
|
||||
|
||||
//add the model to the client precache list
|
||||
self->s.modelindex = G_ModelIndex( self->model );
|
||||
|
||||
self->use = SP_use_anim_model;
|
||||
|
||||
self->s.eType = ET_ANIMMAPOBJ;
|
||||
|
||||
// spawn with animation stopped
|
||||
if( self->spawnflags & 2 )
|
||||
self->s.eFlags |= EF_MOVER_STOP;
|
||||
|
||||
trap_LinkEntity( self );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SP_use_light_flare
|
||||
|
||||
Use function for light flare
|
||||
===============
|
||||
*/
|
||||
void SP_use_light_flare( gentity_t *self, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
self->s.eFlags ^= EF_NODRAW;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
findEmptySpot
|
||||
|
||||
Finds an empty spot radius units from origin
|
||||
==============
|
||||
*/
|
||||
static void findEmptySpot( vec3_t origin, float radius, vec3_t spot )
|
||||
{
|
||||
int i, j, k;
|
||||
vec3_t delta, test, total;
|
||||
trace_t tr;
|
||||
|
||||
VectorClear( total );
|
||||
|
||||
//54(!) traces to test for empty spots
|
||||
for( i = -1; i <= 1; i++ )
|
||||
{
|
||||
for( j = -1; j <= 1; j++ )
|
||||
{
|
||||
for( k = -1; k <= 1; k++ )
|
||||
{
|
||||
VectorSet( delta, ( i * radius ),
|
||||
( j * radius ),
|
||||
( k * radius ) );
|
||||
|
||||
VectorAdd( origin, delta, test );
|
||||
|
||||
trap_Trace( &tr, test, NULL, NULL, test, -1, MASK_SOLID );
|
||||
|
||||
if( !tr.allsolid )
|
||||
{
|
||||
trap_Trace( &tr, test, NULL, NULL, origin, -1, MASK_SOLID );
|
||||
VectorScale( delta, tr.fraction, delta );
|
||||
VectorAdd( total, delta, total );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VectorNormalize( total );
|
||||
VectorScale( total, radius, total );
|
||||
VectorAdd( origin, total, spot );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SP_misc_light_flare
|
||||
|
||||
Spawn function for light flare
|
||||
===============
|
||||
*/
|
||||
void SP_misc_light_flare( gentity_t *self )
|
||||
{
|
||||
self->s.eType = ET_LIGHTFLARE;
|
||||
self->s.modelindex = G_ShaderIndex( self->targetShaderName );
|
||||
VectorCopy( self->pos2, self->s.origin2 );
|
||||
|
||||
//try to find a spot near to the flare which is empty. This
|
||||
//is used to facilitate visibility testing
|
||||
findEmptySpot( self->s.origin, 8.0f, self->s.angles2 );
|
||||
|
||||
self->use = SP_use_light_flare;
|
||||
|
||||
G_SpawnFloat( "speed", "200", &self->speed );
|
||||
self->s.time = self->speed;
|
||||
|
||||
G_SpawnInt( "mindist", "0", &self->s.generic1 );
|
||||
|
||||
if( self->spawnflags & 1 )
|
||||
self->s.eFlags |= EF_NODRAW;
|
||||
|
||||
trap_LinkEntity( self );
|
||||
}
|
||||
@@ -0,0 +1,856 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
#define MISSILE_PRESTEP_TIME 50
|
||||
|
||||
/*
|
||||
================
|
||||
G_BounceMissile
|
||||
|
||||
================
|
||||
*/
|
||||
void G_BounceMissile( gentity_t *ent, trace_t *trace )
|
||||
{
|
||||
vec3_t velocity;
|
||||
float dot;
|
||||
int hitTime;
|
||||
|
||||
// reflect the velocity on the trace plane
|
||||
hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
|
||||
BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
|
||||
dot = DotProduct( velocity, trace->plane.normal );
|
||||
VectorMA( velocity, -2 * dot, trace->plane.normal, ent->s.pos.trDelta );
|
||||
|
||||
if( ent->s.eFlags & EF_BOUNCE_HALF )
|
||||
{
|
||||
VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta );
|
||||
// check for stop
|
||||
if( trace->plane.normal[ 2 ] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 )
|
||||
{
|
||||
G_SetOrigin( ent, trace->endpos );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin );
|
||||
VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
|
||||
ent->s.pos.trTime = level.time;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
G_ExplodeMissile
|
||||
|
||||
Explode a missile without an impact
|
||||
================
|
||||
*/
|
||||
void G_ExplodeMissile( gentity_t *ent )
|
||||
{
|
||||
vec3_t dir;
|
||||
vec3_t origin;
|
||||
|
||||
BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
|
||||
SnapVector( origin );
|
||||
G_SetOrigin( ent, origin );
|
||||
|
||||
// we don't have a valid direction, so just point straight up
|
||||
dir[ 0 ] = dir[ 1 ] = 0;
|
||||
dir[ 2 ] = 1;
|
||||
|
||||
ent->s.eType = ET_GENERAL;
|
||||
|
||||
//TA: tired... can't be fucked... hack
|
||||
if( ent->s.weapon != WP_LOCKBLOB_LAUNCHER &&
|
||||
ent->s.weapon != WP_FLAMER )
|
||||
G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) );
|
||||
|
||||
ent->freeAfterEvent = qtrue;
|
||||
|
||||
// splash damage
|
||||
if( ent->splashDamage )
|
||||
G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage,
|
||||
ent->splashRadius, ent, ent->splashMethodOfDeath );
|
||||
|
||||
trap_LinkEntity( ent );
|
||||
}
|
||||
|
||||
void AHive_ReturnToHive( gentity_t *self );
|
||||
|
||||
/*
|
||||
================
|
||||
G_MissileImpact
|
||||
|
||||
================
|
||||
*/
|
||||
void G_MissileImpact( gentity_t *ent, trace_t *trace )
|
||||
{
|
||||
gentity_t *other, *attacker;
|
||||
qboolean returnAfterDamage = qfalse;
|
||||
vec3_t dir;
|
||||
|
||||
other = &g_entities[ trace->entityNum ];
|
||||
attacker = &g_entities[ ent->r.ownerNum ];
|
||||
|
||||
// check for bounce
|
||||
if( !other->takedamage &&
|
||||
( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) )
|
||||
{
|
||||
G_BounceMissile( ent, trace );
|
||||
|
||||
//only play a sound if requested
|
||||
if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) )
|
||||
G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( !strcmp( ent->classname, "grenade" ) )
|
||||
{
|
||||
//grenade doesn't explode on impact
|
||||
G_BounceMissile( ent, trace );
|
||||
|
||||
//only play a sound if requested
|
||||
if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) )
|
||||
G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
|
||||
|
||||
return;
|
||||
}
|
||||
else if( !strcmp( ent->classname, "lockblob" ) )
|
||||
{
|
||||
if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
|
||||
{
|
||||
other->client->ps.stats[ STAT_STATE ] |= SS_BLOBLOCKED;
|
||||
other->client->lastLockTime = level.time;
|
||||
AngleVectors( other->client->ps.viewangles, dir, NULL, NULL );
|
||||
other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir );
|
||||
}
|
||||
}
|
||||
else if( !strcmp( ent->classname, "slowblob" ) )
|
||||
{
|
||||
if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
|
||||
{
|
||||
other->client->ps.stats[ STAT_STATE ] |= SS_SLOWLOCKED;
|
||||
other->client->lastSlowTime = level.time;
|
||||
AngleVectors( other->client->ps.viewangles, dir, NULL, NULL );
|
||||
other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir );
|
||||
}
|
||||
}
|
||||
else if( !strcmp( ent->classname, "hive" ) )
|
||||
{
|
||||
if( other->s.eType == ET_BUILDABLE && other->s.modelindex == BA_A_HIVE )
|
||||
{
|
||||
if( !ent->parent )
|
||||
G_Printf( S_COLOR_YELLOW "WARNING: hive entity has no parent in G_MissileImpact\n" );
|
||||
else
|
||||
ent->parent->active = qfalse;
|
||||
|
||||
G_FreeEntity( ent );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//prevent collision with the client when returning
|
||||
ent->r.ownerNum = other->s.number;
|
||||
|
||||
ent->think = AHive_ReturnToHive;
|
||||
ent->nextthink = level.time + FRAMETIME;
|
||||
|
||||
//only damage humans
|
||||
if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
|
||||
returnAfterDamage = qtrue;
|
||||
else
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// impact damage
|
||||
if( other->takedamage )
|
||||
{
|
||||
// FIXME: wrong damage direction?
|
||||
if( ent->damage )
|
||||
{
|
||||
vec3_t velocity;
|
||||
|
||||
BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
|
||||
if( VectorLength( velocity ) == 0 )
|
||||
velocity[ 2 ] = 1; // stepped on a grenade
|
||||
|
||||
G_Damage( other, ent, attacker, velocity, ent->s.origin, ent->damage,
|
||||
0, ent->methodOfDeath );
|
||||
}
|
||||
}
|
||||
|
||||
if( returnAfterDamage )
|
||||
return;
|
||||
|
||||
// is it cheaper in bandwidth to just remove this ent and create a new
|
||||
// one, rather than changing the missile into the explosion?
|
||||
|
||||
if( other->takedamage && other->client )
|
||||
{
|
||||
G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
|
||||
ent->s.otherEntityNum = other->s.number;
|
||||
}
|
||||
else if( trace->surfaceFlags & SURF_METALSTEPS )
|
||||
G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) );
|
||||
else
|
||||
G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
|
||||
|
||||
ent->freeAfterEvent = qtrue;
|
||||
|
||||
// change over to a normal entity right at the point of impact
|
||||
ent->s.eType = ET_GENERAL;
|
||||
|
||||
SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth
|
||||
|
||||
G_SetOrigin( ent, trace->endpos );
|
||||
|
||||
// splash damage (doesn't apply to person directly hit)
|
||||
if( ent->splashDamage )
|
||||
G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
|
||||
other, ent->splashMethodOfDeath );
|
||||
|
||||
trap_LinkEntity( ent );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
G_RunMissile
|
||||
|
||||
================
|
||||
*/
|
||||
void G_RunMissile( gentity_t *ent )
|
||||
{
|
||||
vec3_t origin;
|
||||
trace_t tr;
|
||||
int passent;
|
||||
|
||||
// get current position
|
||||
BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
|
||||
|
||||
// ignore interactions with the missile owner
|
||||
passent = ent->r.ownerNum;
|
||||
|
||||
// trace a line from the previous position to the current position
|
||||
trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask );
|
||||
|
||||
if( tr.startsolid || tr.allsolid )
|
||||
{
|
||||
// make sure the tr.entityNum is set to the entity we're stuck in
|
||||
trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask );
|
||||
tr.fraction = 0;
|
||||
}
|
||||
else
|
||||
VectorCopy( tr.endpos, ent->r.currentOrigin );
|
||||
|
||||
ent->r.contents = CONTENTS_SOLID; //trick trap_LinkEntity into...
|
||||
trap_LinkEntity( ent );
|
||||
ent->r.contents = 0; //...encoding bbox information
|
||||
|
||||
if( tr.fraction != 1 )
|
||||
{
|
||||
// never explode or bounce on sky
|
||||
if( tr.surfaceFlags & SURF_NOIMPACT )
|
||||
{
|
||||
// If grapple, reset owner
|
||||
if( ent->parent && ent->parent->client && ent->parent->client->hook == ent )
|
||||
ent->parent->client->hook = NULL;
|
||||
|
||||
G_FreeEntity( ent );
|
||||
return;
|
||||
}
|
||||
|
||||
G_MissileImpact( ent, &tr );
|
||||
if( ent->s.eType != ET_MISSILE )
|
||||
return; // exploded
|
||||
}
|
||||
|
||||
// check think function after bouncing
|
||||
G_RunThink( ent );
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_flamer
|
||||
|
||||
=================
|
||||
*/
|
||||
gentity_t *fire_flamer( gentity_t *self, vec3_t start, vec3_t dir )
|
||||
{
|
||||
gentity_t *bolt;
|
||||
vec3_t pvel;
|
||||
|
||||
VectorNormalize (dir);
|
||||
|
||||
bolt = G_Spawn();
|
||||
bolt->classname = "flame";
|
||||
bolt->nextthink = level.time + FLAMER_LIFETIME;
|
||||
bolt->think = G_ExplodeMissile;
|
||||
bolt->s.eType = ET_MISSILE;
|
||||
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
||||
bolt->s.weapon = WP_FLAMER;
|
||||
bolt->s.generic1 = self->s.generic1; //weaponMode
|
||||
bolt->r.ownerNum = self->s.number;
|
||||
bolt->parent = self;
|
||||
bolt->damage = FLAMER_DMG;
|
||||
bolt->splashDamage = FLAMER_DMG;
|
||||
bolt->splashRadius = FLAMER_RADIUS;
|
||||
bolt->methodOfDeath = MOD_FLAMER;
|
||||
bolt->splashMethodOfDeath = MOD_FLAMER_SPLASH;
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->target_ent = NULL;
|
||||
bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -15.0f;
|
||||
bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = 15.0f;
|
||||
|
||||
bolt->s.pos.trType = TR_LINEAR;
|
||||
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
||||
VectorCopy( start, bolt->s.pos.trBase );
|
||||
VectorScale( self->client->ps.velocity, FLAMER_LAG, pvel );
|
||||
VectorMA( pvel, FLAMER_SPEED, dir, bolt->s.pos.trDelta );
|
||||
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
|
||||
|
||||
VectorCopy( start, bolt->r.currentOrigin );
|
||||
|
||||
return bolt;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_blaster
|
||||
|
||||
=================
|
||||
*/
|
||||
gentity_t *fire_blaster( gentity_t *self, vec3_t start, vec3_t dir )
|
||||
{
|
||||
gentity_t *bolt;
|
||||
|
||||
VectorNormalize (dir);
|
||||
|
||||
bolt = G_Spawn();
|
||||
bolt->classname = "blaster";
|
||||
bolt->nextthink = level.time + 10000;
|
||||
bolt->think = G_ExplodeMissile;
|
||||
bolt->s.eType = ET_MISSILE;
|
||||
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
||||
bolt->s.weapon = WP_BLASTER;
|
||||
bolt->s.generic1 = self->s.generic1; //weaponMode
|
||||
bolt->r.ownerNum = self->s.number;
|
||||
bolt->parent = self;
|
||||
bolt->damage = BLASTER_DMG;
|
||||
bolt->splashDamage = 0;
|
||||
bolt->splashRadius = 0;
|
||||
bolt->methodOfDeath = MOD_BLASTER;
|
||||
bolt->splashMethodOfDeath = MOD_BLASTER;
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->target_ent = NULL;
|
||||
|
||||
bolt->s.pos.trType = TR_LINEAR;
|
||||
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
||||
VectorCopy( start, bolt->s.pos.trBase );
|
||||
VectorScale( dir, BLASTER_SPEED, bolt->s.pos.trDelta );
|
||||
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
|
||||
|
||||
VectorCopy( start, bolt->r.currentOrigin );
|
||||
|
||||
return bolt;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_pulseRifle
|
||||
|
||||
=================
|
||||
*/
|
||||
gentity_t *fire_pulseRifle( gentity_t *self, vec3_t start, vec3_t dir )
|
||||
{
|
||||
gentity_t *bolt;
|
||||
|
||||
VectorNormalize (dir);
|
||||
|
||||
bolt = G_Spawn();
|
||||
bolt->classname = "pulse";
|
||||
bolt->nextthink = level.time + 10000;
|
||||
bolt->think = G_ExplodeMissile;
|
||||
bolt->s.eType = ET_MISSILE;
|
||||
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
||||
bolt->s.weapon = WP_PULSE_RIFLE;
|
||||
bolt->s.generic1 = self->s.generic1; //weaponMode
|
||||
bolt->r.ownerNum = self->s.number;
|
||||
bolt->parent = self;
|
||||
bolt->damage = PRIFLE_DMG;
|
||||
bolt->splashDamage = 0;
|
||||
bolt->splashRadius = 0;
|
||||
bolt->methodOfDeath = MOD_PRIFLE;
|
||||
bolt->splashMethodOfDeath = MOD_PRIFLE;
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->target_ent = NULL;
|
||||
|
||||
bolt->s.pos.trType = TR_LINEAR;
|
||||
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
||||
VectorCopy( start, bolt->s.pos.trBase );
|
||||
VectorScale( dir, PRIFLE_SPEED, bolt->s.pos.trDelta );
|
||||
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
|
||||
|
||||
VectorCopy( start, bolt->r.currentOrigin );
|
||||
|
||||
return bolt;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_luciferCannon
|
||||
|
||||
=================
|
||||
*/
|
||||
gentity_t *fire_luciferCannon( gentity_t *self, vec3_t start, vec3_t dir, int damage, int radius )
|
||||
{
|
||||
gentity_t *bolt;
|
||||
int localDamage = (int)( ceil( ( (float)damage /
|
||||
(float)LCANNON_TOTAL_CHARGE ) * (float)LCANNON_DAMAGE ) );
|
||||
|
||||
VectorNormalize( dir );
|
||||
|
||||
bolt = G_Spawn( );
|
||||
bolt->classname = "lcannon";
|
||||
|
||||
if( damage == LCANNON_TOTAL_CHARGE )
|
||||
bolt->nextthink = level.time;
|
||||
else
|
||||
bolt->nextthink = level.time + 10000;
|
||||
|
||||
bolt->think = G_ExplodeMissile;
|
||||
bolt->s.eType = ET_MISSILE;
|
||||
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
||||
bolt->s.weapon = WP_LUCIFER_CANNON;
|
||||
bolt->s.generic1 = self->s.generic1; //weaponMode
|
||||
bolt->r.ownerNum = self->s.number;
|
||||
bolt->parent = self;
|
||||
bolt->damage = localDamage;
|
||||
bolt->splashDamage = localDamage / 2;
|
||||
bolt->splashRadius = radius;
|
||||
bolt->methodOfDeath = MOD_LCANNON;
|
||||
bolt->splashMethodOfDeath = MOD_LCANNON_SPLASH;
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->target_ent = NULL;
|
||||
|
||||
bolt->s.pos.trType = TR_LINEAR;
|
||||
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
||||
VectorCopy( start, bolt->s.pos.trBase );
|
||||
VectorScale( dir, LCANNON_SPEED, bolt->s.pos.trDelta );
|
||||
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
|
||||
|
||||
VectorCopy( start, bolt->r.currentOrigin );
|
||||
|
||||
return bolt;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
launch_grenade
|
||||
|
||||
=================
|
||||
*/
|
||||
gentity_t *launch_grenade( gentity_t *self, vec3_t start, vec3_t dir )
|
||||
{
|
||||
gentity_t *bolt;
|
||||
|
||||
VectorNormalize( dir );
|
||||
|
||||
bolt = G_Spawn( );
|
||||
bolt->classname = "grenade";
|
||||
bolt->nextthink = level.time + 5000;
|
||||
bolt->think = G_ExplodeMissile;
|
||||
bolt->s.eType = ET_MISSILE;
|
||||
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
||||
bolt->s.weapon = WP_GRENADE;
|
||||
bolt->s.eFlags = EF_BOUNCE_HALF;
|
||||
bolt->s.generic1 = WPM_PRIMARY; //weaponMode
|
||||
bolt->r.ownerNum = self->s.number;
|
||||
bolt->parent = self;
|
||||
bolt->damage = GRENADE_DAMAGE;
|
||||
bolt->splashDamage = GRENADE_DAMAGE;
|
||||
bolt->splashRadius = GRENADE_RANGE;
|
||||
bolt->methodOfDeath = MOD_GRENADE;
|
||||
bolt->splashMethodOfDeath = MOD_GRENADE;
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->target_ent = NULL;
|
||||
bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -3.0f;
|
||||
bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = 3.0f;
|
||||
bolt->s.time = level.time;
|
||||
|
||||
bolt->s.pos.trType = TR_GRAVITY;
|
||||
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
||||
VectorCopy( start, bolt->s.pos.trBase );
|
||||
VectorScale( dir, GRENADE_SPEED, bolt->s.pos.trDelta );
|
||||
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
|
||||
|
||||
VectorCopy( start, bolt->r.currentOrigin );
|
||||
|
||||
return bolt;
|
||||
}
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
================
|
||||
AHive_ReturnToHive
|
||||
|
||||
Adjust the trajectory to point towards the hive
|
||||
================
|
||||
*/
|
||||
void AHive_ReturnToHive( gentity_t *self )
|
||||
{
|
||||
vec3_t dir;
|
||||
trace_t tr;
|
||||
|
||||
if( !self->parent )
|
||||
{
|
||||
G_Printf( S_COLOR_YELLOW "WARNING: AHive_ReturnToHive called with no self->parent\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
trap_UnlinkEntity( self->parent );
|
||||
trap_Trace( &tr, self->r.currentOrigin, self->r.mins, self->r.maxs,
|
||||
self->parent->r.currentOrigin, self->r.ownerNum, self->clipmask );
|
||||
trap_LinkEntity( self->parent );
|
||||
|
||||
if( tr.fraction < 1.0f )
|
||||
{
|
||||
//if can't see hive then disperse
|
||||
VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
|
||||
self->s.pos.trType = TR_STATIONARY;
|
||||
self->s.pos.trTime = level.time;
|
||||
|
||||
self->think = G_ExplodeMissile;
|
||||
self->nextthink = level.time + 2000;
|
||||
self->parent->active = qfalse; //allow the parent to start again
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorSubtract( self->parent->r.currentOrigin, self->r.currentOrigin, dir );
|
||||
VectorNormalize( dir );
|
||||
|
||||
//change direction towards the hive
|
||||
VectorScale( dir, HIVE_SPEED, self->s.pos.trDelta );
|
||||
SnapVector( self->s.pos.trDelta ); // save net bandwidth
|
||||
VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
|
||||
self->s.pos.trTime = level.time;
|
||||
|
||||
self->think = G_ExplodeMissile;
|
||||
self->nextthink = level.time + 15000;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
AHive_SearchAndDestroy
|
||||
|
||||
Adjust the trajectory to point towards the target
|
||||
================
|
||||
*/
|
||||
void AHive_SearchAndDestroy( gentity_t *self )
|
||||
{
|
||||
vec3_t dir;
|
||||
trace_t tr;
|
||||
|
||||
trap_Trace( &tr, self->r.currentOrigin, self->r.mins, self->r.maxs,
|
||||
self->target_ent->r.currentOrigin, self->r.ownerNum, self->clipmask );
|
||||
|
||||
//if there is no LOS or the parent hive is too far away or the target is dead or notargeting, return
|
||||
if( tr.entityNum == ENTITYNUM_WORLD ||
|
||||
Distance( self->r.currentOrigin, self->parent->r.currentOrigin ) > ( HIVE_RANGE * 5 ) ||
|
||||
self->target_ent->health <= 0 || self->target_ent->flags & FL_NOTARGET )
|
||||
{
|
||||
self->r.ownerNum = ENTITYNUM_WORLD;
|
||||
|
||||
self->think = AHive_ReturnToHive;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorSubtract( self->target_ent->r.currentOrigin, self->r.currentOrigin, dir );
|
||||
VectorNormalize( dir );
|
||||
|
||||
//change direction towards the player
|
||||
VectorScale( dir, HIVE_SPEED, self->s.pos.trDelta );
|
||||
SnapVector( self->s.pos.trDelta ); // save net bandwidth
|
||||
VectorCopy( self->r.currentOrigin, self->s.pos.trBase );
|
||||
self->s.pos.trTime = level.time;
|
||||
|
||||
self->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_hive
|
||||
=================
|
||||
*/
|
||||
gentity_t *fire_hive( gentity_t *self, vec3_t start, vec3_t dir )
|
||||
{
|
||||
gentity_t *bolt;
|
||||
|
||||
VectorNormalize ( dir );
|
||||
|
||||
bolt = G_Spawn( );
|
||||
bolt->classname = "hive";
|
||||
bolt->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD;
|
||||
bolt->think = AHive_SearchAndDestroy;
|
||||
bolt->s.eType = ET_MISSILE;
|
||||
bolt->s.eFlags |= EF_BOUNCE|EF_NO_BOUNCE_SOUND;
|
||||
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
||||
bolt->s.weapon = WP_HIVE;
|
||||
bolt->s.generic1 = WPM_PRIMARY; //weaponMode
|
||||
bolt->r.ownerNum = self->s.number;
|
||||
bolt->parent = self;
|
||||
bolt->damage = HIVE_DMG;
|
||||
bolt->splashDamage = 0;
|
||||
bolt->splashRadius = 0;
|
||||
bolt->methodOfDeath = MOD_SWARM;
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->target_ent = self->target_ent;
|
||||
|
||||
bolt->s.pos.trType = TR_LINEAR;
|
||||
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
||||
VectorCopy( start, bolt->s.pos.trBase );
|
||||
VectorScale( dir, HIVE_SPEED, bolt->s.pos.trDelta );
|
||||
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
|
||||
VectorCopy( start, bolt->r.currentOrigin );
|
||||
|
||||
return bolt;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_lockblob
|
||||
=================
|
||||
*/
|
||||
gentity_t *fire_lockblob( gentity_t *self, vec3_t start, vec3_t dir )
|
||||
{
|
||||
gentity_t *bolt;
|
||||
|
||||
VectorNormalize ( dir );
|
||||
|
||||
bolt = G_Spawn( );
|
||||
bolt->classname = "lockblob";
|
||||
bolt->nextthink = level.time + 15000;
|
||||
bolt->think = G_ExplodeMissile;
|
||||
bolt->s.eType = ET_MISSILE;
|
||||
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
||||
bolt->s.weapon = WP_LOCKBLOB_LAUNCHER;
|
||||
bolt->s.generic1 = WPM_PRIMARY; //weaponMode
|
||||
bolt->r.ownerNum = self->s.number;
|
||||
bolt->parent = self;
|
||||
bolt->damage = 0;
|
||||
bolt->splashDamage = 0;
|
||||
bolt->splashRadius = 0;
|
||||
bolt->methodOfDeath = MOD_UNKNOWN; //doesn't do damage so will never kill
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->target_ent = NULL;
|
||||
|
||||
bolt->s.pos.trType = TR_LINEAR;
|
||||
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
||||
VectorCopy( start, bolt->s.pos.trBase );
|
||||
VectorScale( dir, 500, bolt->s.pos.trDelta );
|
||||
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
|
||||
VectorCopy( start, bolt->r.currentOrigin );
|
||||
|
||||
return bolt;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_slowBlob
|
||||
=================
|
||||
*/
|
||||
gentity_t *fire_slowBlob( gentity_t *self, vec3_t start, vec3_t dir )
|
||||
{
|
||||
gentity_t *bolt;
|
||||
|
||||
VectorNormalize ( dir );
|
||||
|
||||
bolt = G_Spawn( );
|
||||
bolt->classname = "slowblob";
|
||||
bolt->nextthink = level.time + 15000;
|
||||
bolt->think = G_ExplodeMissile;
|
||||
bolt->s.eType = ET_MISSILE;
|
||||
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
||||
bolt->s.weapon = WP_ABUILD2;
|
||||
bolt->s.generic1 = self->s.generic1; //weaponMode
|
||||
bolt->r.ownerNum = self->s.number;
|
||||
bolt->parent = self;
|
||||
bolt->damage = ABUILDER_BLOB_DMG;
|
||||
bolt->splashDamage = 0;
|
||||
bolt->splashRadius = 0;
|
||||
bolt->methodOfDeath = MOD_SLOWBLOB;
|
||||
bolt->splashMethodOfDeath = MOD_SLOWBLOB;
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->target_ent = NULL;
|
||||
|
||||
bolt->s.pos.trType = TR_GRAVITY;
|
||||
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
||||
VectorCopy( start, bolt->s.pos.trBase );
|
||||
VectorScale( dir, ABUILDER_BLOB_SPEED, bolt->s.pos.trDelta );
|
||||
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
|
||||
VectorCopy( start, bolt->r.currentOrigin );
|
||||
|
||||
return bolt;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_paraLockBlob
|
||||
=================
|
||||
*/
|
||||
gentity_t *fire_paraLockBlob( gentity_t *self, vec3_t start, vec3_t dir )
|
||||
{
|
||||
gentity_t *bolt;
|
||||
|
||||
VectorNormalize ( dir );
|
||||
|
||||
bolt = G_Spawn( );
|
||||
bolt->classname = "lockblob";
|
||||
bolt->nextthink = level.time + 15000;
|
||||
bolt->think = G_ExplodeMissile;
|
||||
bolt->s.eType = ET_MISSILE;
|
||||
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
||||
bolt->s.weapon = WP_LOCKBLOB_LAUNCHER;
|
||||
bolt->s.generic1 = self->s.generic1; //weaponMode
|
||||
bolt->r.ownerNum = self->s.number;
|
||||
bolt->parent = self;
|
||||
bolt->damage = 0;
|
||||
bolt->splashDamage = 0;
|
||||
bolt->splashRadius = 0;
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->target_ent = NULL;
|
||||
|
||||
bolt->s.pos.trType = TR_GRAVITY;
|
||||
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
||||
VectorCopy( start, bolt->s.pos.trBase );
|
||||
VectorScale( dir, LOCKBLOB_SPEED, bolt->s.pos.trDelta );
|
||||
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
|
||||
VectorCopy( start, bolt->r.currentOrigin );
|
||||
|
||||
return bolt;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
fire_bounceBall
|
||||
=================
|
||||
*/
|
||||
gentity_t *fire_bounceBall( gentity_t *self, vec3_t start, vec3_t dir )
|
||||
{
|
||||
gentity_t *bolt;
|
||||
|
||||
VectorNormalize ( dir );
|
||||
|
||||
bolt = G_Spawn( );
|
||||
bolt->classname = "bounceball";
|
||||
bolt->nextthink = level.time + 3000;
|
||||
bolt->think = G_ExplodeMissile;
|
||||
bolt->s.eType = ET_MISSILE;
|
||||
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
||||
bolt->s.weapon = WP_ALEVEL3_UPG;
|
||||
bolt->s.generic1 = self->s.generic1; //weaponMode
|
||||
bolt->r.ownerNum = self->s.number;
|
||||
bolt->parent = self;
|
||||
bolt->damage = LEVEL3_BOUNCEBALL_DMG;
|
||||
bolt->splashDamage = 0;
|
||||
bolt->splashRadius = 0;
|
||||
bolt->methodOfDeath = MOD_LEVEL3_BOUNCEBALL;
|
||||
bolt->splashMethodOfDeath = MOD_LEVEL3_BOUNCEBALL;
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->target_ent = NULL;
|
||||
|
||||
bolt->s.pos.trType = TR_GRAVITY;
|
||||
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
||||
VectorCopy( start, bolt->s.pos.trBase );
|
||||
VectorScale( dir, LEVEL3_BOUNCEBALL_SPEED, bolt->s.pos.trDelta );
|
||||
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
|
||||
VectorCopy( start, bolt->r.currentOrigin );
|
||||
/*bolt->s.eFlags |= EF_BOUNCE;*/
|
||||
|
||||
return bolt;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
launch_grenade2
|
||||
|
||||
This is called in the explode command in g_admin.c
|
||||
=================
|
||||
*/
|
||||
gentity_t *launch_grenade2( gentity_t *self, vec3_t start, vec3_t dir )
|
||||
{
|
||||
gentity_t *bolt;
|
||||
|
||||
VectorNormalize( dir );
|
||||
|
||||
bolt = G_Spawn( );
|
||||
bolt->classname = "grenade";
|
||||
bolt->nextthink = level.time;
|
||||
bolt->think = G_ExplodeMissile;
|
||||
bolt->s.eType = ET_MISSILE;
|
||||
bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
||||
bolt->s.weapon = WP_GRENADE;
|
||||
bolt->s.eFlags = EF_BOUNCE_HALF;
|
||||
bolt->s.generic1 = WPM_PRIMARY; //weaponMode
|
||||
bolt->r.ownerNum = self->s.number;
|
||||
bolt->parent = self;
|
||||
bolt->damage = GRENADE_DAMAGE;
|
||||
bolt->splashDamage = GRENADE_DAMAGE;
|
||||
bolt->splashRadius = GRENADE_RANGE;
|
||||
bolt->methodOfDeath = MOD_SUICIDE;
|
||||
bolt->splashMethodOfDeath = MOD_EXPLODE;
|
||||
bolt->clipmask = MASK_SHOT;
|
||||
bolt->target_ent = NULL;
|
||||
bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -3.0f;
|
||||
bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = 3.0f;
|
||||
bolt->s.time = level.time;
|
||||
|
||||
bolt->s.pos.trType = TR_GRAVITY;
|
||||
bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame
|
||||
VectorCopy( start, bolt->s.pos.trBase );
|
||||
VectorScale( dir, GRENADE_SPEED, bolt->s.pos.trDelta );
|
||||
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
|
||||
|
||||
VectorCopy( start, bolt->r.currentOrigin );
|
||||
|
||||
return bolt;
|
||||
}
|
||||
//=============================================================================
|
||||
+2479
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
/*
|
||||
================
|
||||
G_Bounce
|
||||
|
||||
================
|
||||
*/
|
||||
static void G_Bounce( gentity_t *ent, trace_t *trace )
|
||||
{
|
||||
vec3_t velocity;
|
||||
float dot;
|
||||
int hitTime;
|
||||
float minNormal;
|
||||
qboolean invert = qfalse;
|
||||
|
||||
// reflect the velocity on the trace plane
|
||||
hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
|
||||
BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
|
||||
dot = DotProduct( velocity, trace->plane.normal );
|
||||
VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
|
||||
|
||||
if( ent->s.eType == ET_BUILDABLE )
|
||||
{
|
||||
minNormal = BG_FindMinNormalForBuildable( ent->s.modelindex );
|
||||
invert = BG_FindInvertNormalForBuildable( ent->s.modelindex );
|
||||
}
|
||||
else
|
||||
minNormal = 0.707f;
|
||||
|
||||
// cut the velocity to keep from bouncing forever
|
||||
if( ( trace->plane.normal[ 2 ] >= minNormal ||
|
||||
( invert && trace->plane.normal[ 2 ] <= -minNormal ) ) &&
|
||||
trace->entityNum == ENTITYNUM_WORLD )
|
||||
VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );
|
||||
else
|
||||
VectorScale( ent->s.pos.trDelta, 0.3f, ent->s.pos.trDelta );
|
||||
|
||||
if( VectorLength( ent->s.pos.trDelta ) < 10 )
|
||||
{
|
||||
VectorMA( trace->endpos, 0.5f, trace->plane.normal, trace->endpos ); // make sure it is off ground
|
||||
// Never allow origin to be snapped lower (buildables fall through map bug)
|
||||
trace->endpos[2] = (float)ceil(trace->endpos[2]);
|
||||
SnapVector( trace->endpos );
|
||||
G_SetOrigin( ent, trace->endpos );
|
||||
ent->s.groundEntityNum = trace->entityNum;
|
||||
VectorCopy( trace->plane.normal, ent->s.origin2 );
|
||||
VectorSet( ent->s.pos.trDelta, 0.0f, 0.0f, 0.0f );
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
|
||||
VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
|
||||
ent->s.pos.trTime = level.time;
|
||||
}
|
||||
|
||||
#define PHYSICS_TIME 200
|
||||
|
||||
/*
|
||||
================
|
||||
G_Physics
|
||||
|
||||
================
|
||||
*/
|
||||
void G_Physics( gentity_t *ent, int msec )
|
||||
{
|
||||
vec3_t origin;
|
||||
trace_t tr;
|
||||
int contents;
|
||||
int mask;
|
||||
|
||||
// if groundentity has been set to -1, it may have been pushed off an edge
|
||||
if( ent->s.groundEntityNum == -1 )
|
||||
{
|
||||
if( ent->s.eType == ET_BUILDABLE )
|
||||
{
|
||||
if( ent->s.pos.trType != BG_FindTrajectoryForBuildable( ent->s.modelindex ) )
|
||||
{
|
||||
ent->s.pos.trType = BG_FindTrajectoryForBuildable( ent->s.modelindex );
|
||||
ent->s.pos.trTime = level.time;
|
||||
}
|
||||
}
|
||||
else if( ent->s.pos.trType != TR_GRAVITY )
|
||||
{
|
||||
ent->s.pos.trType = TR_GRAVITY;
|
||||
ent->s.pos.trTime = level.time;
|
||||
}
|
||||
}
|
||||
|
||||
// trace a line from the previous position to the current position
|
||||
if( ent->clipmask )
|
||||
mask = ent->clipmask;
|
||||
else
|
||||
mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;//MASK_SOLID;
|
||||
|
||||
if( ent->s.pos.trType == TR_STATIONARY )
|
||||
{
|
||||
// check think function
|
||||
G_RunThink( ent );
|
||||
|
||||
//check floor infrequently
|
||||
if( ent->nextPhysicsTime < level.time )
|
||||
{
|
||||
VectorCopy( ent->r.currentOrigin, origin );
|
||||
|
||||
VectorMA( origin, -2.0f, ent->s.origin2, origin );
|
||||
|
||||
trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, mask );
|
||||
|
||||
if( tr.fraction == 1.0f )
|
||||
ent->s.groundEntityNum = -1;
|
||||
|
||||
ent->nextPhysicsTime = level.time + PHYSICS_TIME;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// get current position
|
||||
BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
|
||||
|
||||
trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, mask );
|
||||
|
||||
VectorCopy( tr.endpos, ent->r.currentOrigin );
|
||||
|
||||
if( tr.startsolid )
|
||||
tr.fraction = 0;
|
||||
|
||||
trap_LinkEntity( ent ); // FIXME: avoid this for stationary?
|
||||
|
||||
// check think function
|
||||
G_RunThink( ent );
|
||||
|
||||
if( tr.fraction == 1.0f )
|
||||
return;
|
||||
|
||||
// if it is in a nodrop volume, remove it
|
||||
contents = trap_PointContents( ent->r.currentOrigin, -1 );
|
||||
if( contents & CONTENTS_NODROP )
|
||||
{
|
||||
G_FreeEntity( ent );
|
||||
return;
|
||||
}
|
||||
|
||||
G_Bounce( ent, &tr );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// g_ptr.c -- post timeout restoration handling
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
static connectionRecord_t connections[ MAX_CLIENTS ];
|
||||
|
||||
/*
|
||||
===============
|
||||
G_CheckForUniquePTRC
|
||||
|
||||
Callback to detect ptrc clashes
|
||||
===============
|
||||
*/
|
||||
static qboolean G_CheckForUniquePTRC( int code )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( code == 0 )
|
||||
return qfalse;
|
||||
|
||||
for( i = 0; i < MAX_CLIENTS; i++ )
|
||||
{
|
||||
if( connections[ i ].ptrCode == code )
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_UpdatePTRConnection
|
||||
|
||||
Update the data in a connection record
|
||||
===============
|
||||
*/
|
||||
void G_UpdatePTRConnection( gclient_t *client )
|
||||
{
|
||||
if( client && client->pers.connection )
|
||||
{
|
||||
client->pers.connection->clientTeam = client->pers.teamSelection;
|
||||
if( client->pers.teamSelection == PTE_NONE )
|
||||
client->pers.connection->clientCredit = client->pers.savedCredit;
|
||||
else
|
||||
client->pers.connection->clientCredit = client->ps.persistant[ PERS_CREDIT ];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_GenerateNewConnection
|
||||
|
||||
Generates a new connection
|
||||
===============
|
||||
*/
|
||||
connectionRecord_t *G_GenerateNewConnection( gclient_t *client )
|
||||
{
|
||||
int code = 0;
|
||||
int i;
|
||||
|
||||
// this should be really random
|
||||
srand( trap_Milliseconds( ) );
|
||||
|
||||
// there is a very very small possibility that this
|
||||
// will loop infinitely
|
||||
do
|
||||
{
|
||||
code = rand( );
|
||||
} while( !G_CheckForUniquePTRC( code ) );
|
||||
|
||||
for( i = 0; i < MAX_CLIENTS; i++ )
|
||||
{
|
||||
//found an unused slot
|
||||
if( !connections[ i ].ptrCode )
|
||||
{
|
||||
connections[ i ].ptrCode = code;
|
||||
connections[ i ].clientNum = client->ps.clientNum;
|
||||
client->pers.connection = &connections[ i ];
|
||||
G_UpdatePTRConnection( client );
|
||||
|
||||
return &connections[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_VerifyPTRC
|
||||
|
||||
Check a PTR code for validity
|
||||
===============
|
||||
*/
|
||||
qboolean G_VerifyPTRC( int code )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( code == 0 )
|
||||
return qfalse;
|
||||
|
||||
for( i = 0; i < MAX_CLIENTS; i++ )
|
||||
{
|
||||
if( connections[ i ].ptrCode == code )
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_FindConnectionForCode
|
||||
|
||||
Finds a connection for a given code
|
||||
===============
|
||||
*/
|
||||
connectionRecord_t *G_FindConnectionForCode( int code )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( code == 0 )
|
||||
return NULL;
|
||||
|
||||
for( i = 0; i < MAX_CLIENTS; i++ )
|
||||
{
|
||||
if( connections[ i ].ptrCode == code )
|
||||
return &connections[ i ];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_DeletePTRConnection
|
||||
|
||||
Finds a connection and deletes it
|
||||
===============
|
||||
*/
|
||||
void G_DeletePTRConnection( connectionRecord_t *connection )
|
||||
{
|
||||
if( connection )
|
||||
memset( connection, 0, sizeof( connectionRecord_t ) );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_ResetPTRConnections
|
||||
|
||||
Invalidate any existing codes
|
||||
===============
|
||||
*/
|
||||
void G_ResetPTRConnections( void )
|
||||
{
|
||||
memset( connections, 0, sizeof( connectionRecord_t ) * MAX_CLIENTS );
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// g_public.h -- game module information visible to server
|
||||
|
||||
#define GAME_API_VERSION 8
|
||||
|
||||
// entity->svFlags
|
||||
// the server does not know how to interpret most of the values
|
||||
// in entityStates (level eType), so the game must explicitly flag
|
||||
// special server behaviors
|
||||
#define SVF_NOCLIENT 0x00000001 // don't send entity to clients, even if it has effects
|
||||
|
||||
// TTimo
|
||||
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=551
|
||||
#define SVF_CLIENTMASK 0x00000002
|
||||
|
||||
#define SVF_BROADCAST 0x00000020 // send to all connected clients
|
||||
#define SVF_PORTAL 0x00000040 // merge a second pvs at origin2 into snapshots
|
||||
#define SVF_USE_CURRENT_ORIGIN 0x00000080 // entity->r.currentOrigin instead of entity->s.origin
|
||||
// for link position (missiles and movers)
|
||||
#define SVF_SINGLECLIENT 0x00000100 // only send to a single client (entityShared_t->singleClient)
|
||||
#define SVF_NOSERVERINFO 0x00000200 // don't send CS_SERVERINFO updates to this client
|
||||
// so that it can be updated for ping tools without
|
||||
// lagging clients
|
||||
#define SVF_CAPSULE 0x00000400 // use capsule for collision detection instead of bbox
|
||||
#define SVF_NOTSINGLECLIENT 0x00000800 // send entity to everyone but one client
|
||||
// (entityShared_t->singleClient)
|
||||
|
||||
//===============================================================
|
||||
|
||||
|
||||
typedef struct {
|
||||
entityState_t s; // communicated by server to clients
|
||||
|
||||
qboolean linked; // qfalse if not in any good cluster
|
||||
int linkcount;
|
||||
|
||||
int svFlags; // SVF_NOCLIENT, SVF_BROADCAST, etc
|
||||
int singleClient; // only send to this client when SVF_SINGLECLIENT is set
|
||||
|
||||
qboolean bmodel; // if false, assume an explicit mins / maxs bounding box
|
||||
// only set by trap_SetBrushModel
|
||||
vec3_t mins, maxs;
|
||||
int contents; // CONTENTS_TRIGGER, CONTENTS_SOLID, CONTENTS_BODY, etc
|
||||
// a non-solid entity should set to 0
|
||||
|
||||
vec3_t absmin, absmax; // derived from mins/maxs and origin + rotation
|
||||
|
||||
// currentOrigin will be used for all collision detection and world linking.
|
||||
// it will not necessarily be the same as the trajectory evaluation for the current
|
||||
// time, because each entity must be moved one at a time after time is advanced
|
||||
// to avoid simultanious collision issues
|
||||
vec3_t currentOrigin;
|
||||
vec3_t currentAngles;
|
||||
|
||||
// when a trace call is made and passEntityNum != ENTITYNUM_NONE,
|
||||
// an ent will be excluded from testing if:
|
||||
// ent->s.number == passEntityNum (don't interact with self)
|
||||
// ent->s.ownerNum = passEntityNum (don't interact with your own missiles)
|
||||
// entity[ent->s.ownerNum].ownerNum = passEntityNum (don't interact with other missiles from owner)
|
||||
int ownerNum;
|
||||
} entityShared_t;
|
||||
|
||||
|
||||
|
||||
// the server looks at a sharedEntity, which is the start of the game's gentity_t structure
|
||||
typedef struct {
|
||||
entityState_t s; // communicated by server to clients
|
||||
entityShared_t r; // shared by both the server system and game
|
||||
} sharedEntity_t;
|
||||
|
||||
|
||||
|
||||
//===============================================================
|
||||
|
||||
//
|
||||
// system traps provided by the main engine
|
||||
//
|
||||
typedef enum {
|
||||
//============== general Quake services ==================
|
||||
|
||||
G_PRINT, // ( const char *string );
|
||||
// print message on the local console
|
||||
|
||||
G_ERROR, // ( const char *string );
|
||||
// abort the game
|
||||
|
||||
G_MILLISECONDS, // ( void );
|
||||
// get current time for profiling reasons
|
||||
// this should NOT be used for any game related tasks,
|
||||
// because it is not journaled
|
||||
|
||||
// console variable interaction
|
||||
G_CVAR_REGISTER, // ( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags );
|
||||
G_CVAR_UPDATE, // ( vmCvar_t *vmCvar );
|
||||
G_CVAR_SET, // ( const char *var_name, const char *value );
|
||||
G_CVAR_VARIABLE_INTEGER_VALUE, // ( const char *var_name );
|
||||
|
||||
G_CVAR_VARIABLE_STRING_BUFFER, // ( const char *var_name, char *buffer, int bufsize );
|
||||
|
||||
G_ARGC, // ( void );
|
||||
// ClientCommand and ServerCommand parameter access
|
||||
|
||||
G_ARGV, // ( int n, char *buffer, int bufferLength );
|
||||
|
||||
G_FS_FOPEN_FILE, // ( const char *qpath, fileHandle_t *file, fsMode_t mode );
|
||||
G_FS_READ, // ( void *buffer, int len, fileHandle_t f );
|
||||
G_FS_WRITE, // ( const void *buffer, int len, fileHandle_t f );
|
||||
G_FS_FCLOSE_FILE, // ( fileHandle_t f );
|
||||
|
||||
G_SEND_CONSOLE_COMMAND, // ( const char *text );
|
||||
// add commands to the console as if they were typed in
|
||||
// for map changing, etc
|
||||
|
||||
|
||||
//=========== server specific functionality =============
|
||||
|
||||
G_LOCATE_GAME_DATA, // ( gentity_t *gEnts, int numGEntities, int sizeofGEntity_t,
|
||||
// playerState_t *clients, int sizeofGameClient );
|
||||
// the game needs to let the server system know where and how big the gentities
|
||||
// are, so it can look at them directly without going through an interface
|
||||
|
||||
G_DROP_CLIENT, // ( int clientNum, const char *reason );
|
||||
// kick a client off the server with a message
|
||||
|
||||
G_SEND_SERVER_COMMAND, // ( int clientNum, const char *fmt, ... );
|
||||
// reliably sends a command string to be interpreted by the given
|
||||
// client. If clientNum is -1, it will be sent to all clients
|
||||
|
||||
G_SET_CONFIGSTRING, // ( int num, const char *string );
|
||||
// config strings hold all the index strings, and various other information
|
||||
// that is reliably communicated to all clients
|
||||
// All of the current configstrings are sent to clients when
|
||||
// they connect, and changes are sent to all connected clients.
|
||||
// All confgstrings are cleared at each level start.
|
||||
|
||||
G_GET_CONFIGSTRING, // ( int num, char *buffer, int bufferSize );
|
||||
|
||||
G_GET_USERINFO, // ( int num, char *buffer, int bufferSize );
|
||||
// userinfo strings are maintained by the server system, so they
|
||||
// are persistant across level loads, while all other game visible
|
||||
// data is completely reset
|
||||
|
||||
G_SET_USERINFO, // ( int num, const char *buffer );
|
||||
|
||||
G_GET_SERVERINFO, // ( char *buffer, int bufferSize );
|
||||
// the serverinfo info string has all the cvars visible to server browsers
|
||||
|
||||
G_SET_BRUSH_MODEL, // ( gentity_t *ent, const char *name );
|
||||
// sets mins and maxs based on the brushmodel name
|
||||
|
||||
G_TRACE, // ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask );
|
||||
// collision detection against all linked entities
|
||||
|
||||
G_POINT_CONTENTS, // ( const vec3_t point, int passEntityNum );
|
||||
// point contents against all linked entities
|
||||
|
||||
G_IN_PVS, // ( const vec3_t p1, const vec3_t p2 );
|
||||
|
||||
G_IN_PVS_IGNORE_PORTALS, // ( const vec3_t p1, const vec3_t p2 );
|
||||
|
||||
G_ADJUST_AREA_PORTAL_STATE, // ( gentity_t *ent, qboolean open );
|
||||
|
||||
G_AREAS_CONNECTED, // ( int area1, int area2 );
|
||||
|
||||
G_LINKENTITY, // ( gentity_t *ent );
|
||||
// an entity will never be sent to a client or used for collision
|
||||
// if it is not passed to linkentity. If the size, position, or
|
||||
// solidity changes, it must be relinked.
|
||||
|
||||
G_UNLINKENTITY, // ( gentity_t *ent );
|
||||
// call before removing an interactive entity
|
||||
|
||||
G_ENTITIES_IN_BOX, // ( const vec3_t mins, const vec3_t maxs, gentity_t **list, int maxcount );
|
||||
// EntitiesInBox will return brush models based on their bounding box,
|
||||
// so exact determination must still be done with EntityContact
|
||||
|
||||
G_ENTITY_CONTACT, // ( const vec3_t mins, const vec3_t maxs, const gentity_t *ent );
|
||||
// perform an exact check against inline brush models of non-square shape
|
||||
|
||||
G_GET_USERCMD, // ( int clientNum, usercmd_t *cmd )
|
||||
|
||||
G_GET_ENTITY_TOKEN, // qboolean ( char *buffer, int bufferSize )
|
||||
// Retrieves the next string token from the entity spawn text, returning
|
||||
// false when all tokens have been parsed.
|
||||
// This should only be done at GAME_INIT time.
|
||||
|
||||
G_FS_GETFILELIST,
|
||||
G_REAL_TIME,
|
||||
G_SNAPVECTOR,
|
||||
|
||||
G_TRACECAPSULE, // ( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask );
|
||||
G_ENTITY_CONTACTCAPSULE, // ( const vec3_t mins, const vec3_t maxs, const gentity_t *ent );
|
||||
|
||||
// 1.32
|
||||
G_FS_SEEK,
|
||||
|
||||
G_PARSE_ADD_GLOBAL_DEFINE,
|
||||
G_PARSE_LOAD_SOURCE,
|
||||
G_PARSE_FREE_SOURCE,
|
||||
G_PARSE_READ_TOKEN,
|
||||
G_PARSE_SOURCE_FILE_AND_LINE,
|
||||
|
||||
G_SEND_GAMESTAT
|
||||
} gameImport_t;
|
||||
|
||||
|
||||
//
|
||||
// functions exported by the game subsystem
|
||||
//
|
||||
typedef enum {
|
||||
GAME_INIT, // ( int levelTime, int randomSeed, int restart );
|
||||
// init and shutdown will be called every single level
|
||||
// The game should call G_GET_ENTITY_TOKEN to parse through all the
|
||||
// entity configuration text and spawn gentities.
|
||||
|
||||
GAME_SHUTDOWN, // (void);
|
||||
|
||||
GAME_CLIENT_CONNECT, // ( int clientNum, qboolean firstTime );
|
||||
// return NULL if the client is allowed to connect, otherwise return
|
||||
// a text string with the reason for denial
|
||||
|
||||
GAME_CLIENT_BEGIN, // ( int clientNum );
|
||||
|
||||
GAME_CLIENT_USERINFO_CHANGED, // ( int clientNum );
|
||||
|
||||
GAME_CLIENT_DISCONNECT, // ( int clientNum );
|
||||
|
||||
GAME_CLIENT_COMMAND, // ( int clientNum );
|
||||
|
||||
GAME_CLIENT_THINK, // ( int clientNum );
|
||||
|
||||
GAME_RUN_FRAME, // ( int levelTime );
|
||||
|
||||
GAME_CONSOLE_COMMAND // ( void );
|
||||
// ConsoleCommand will be called when a command has been issued
|
||||
// that is not recognized as a builtin function.
|
||||
// The game can issue trap_argc() / trap_argv() commands to get the command
|
||||
// and parameters. Return qfalse if the game doesn't recognize it as a command.
|
||||
} gameExport_t;
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
|
||||
/*
|
||||
=======================================================================
|
||||
|
||||
SESSION DATA
|
||||
|
||||
Session data is the only data that stays persistant across level loads
|
||||
and tournament restarts.
|
||||
=======================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
================
|
||||
G_WriteClientSessionData
|
||||
|
||||
Called on game shutdown
|
||||
================
|
||||
*/
|
||||
void G_WriteClientSessionData( gclient_t *client )
|
||||
{
|
||||
const char *s;
|
||||
const char *var;
|
||||
|
||||
s = va( "%i %i %i %i %i %i %i %i %s",
|
||||
client->sess.sessionTeam,
|
||||
client->sess.restartTeam,
|
||||
client->sess.spectatorTime,
|
||||
client->sess.spectatorState,
|
||||
client->sess.spectatorClient,
|
||||
client->sess.wins,
|
||||
client->sess.losses,
|
||||
client->sess.teamLeader,
|
||||
BG_ClientListString( &client->sess.ignoreList )
|
||||
);
|
||||
|
||||
var = va( "session%i", client - level.clients );
|
||||
|
||||
trap_Cvar_Set( var, s );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
G_ReadSessionData
|
||||
|
||||
Called on a reconnect
|
||||
================
|
||||
*/
|
||||
void G_ReadSessionData( gclient_t *client )
|
||||
{
|
||||
char s[ MAX_STRING_CHARS ];
|
||||
const char *var;
|
||||
|
||||
// bk001205 - format
|
||||
int teamLeader;
|
||||
int spectatorState;
|
||||
int sessionTeam;
|
||||
int restartTeam;
|
||||
|
||||
var = va( "session%i", client - level.clients );
|
||||
trap_Cvar_VariableStringBuffer( var, s, sizeof(s) );
|
||||
|
||||
// FIXME: should be using BG_ClientListParse() for ignoreList, but
|
||||
// bg_lib.c's sscanf() currently lacks %s
|
||||
sscanf( s, "%i %i %i %i %i %i %i %i %x%x",
|
||||
&sessionTeam,
|
||||
&restartTeam,
|
||||
&client->sess.spectatorTime,
|
||||
&spectatorState,
|
||||
&client->sess.spectatorClient,
|
||||
&client->sess.wins,
|
||||
&client->sess.losses,
|
||||
&teamLeader,
|
||||
&client->sess.ignoreList.hi,
|
||||
&client->sess.ignoreList.lo
|
||||
);
|
||||
// bk001205 - format issues
|
||||
client->sess.sessionTeam = (team_t)sessionTeam;
|
||||
client->sess.restartTeam = (pTeam_t)restartTeam;
|
||||
client->sess.spectatorState = (spectatorState_t)spectatorState;
|
||||
client->sess.teamLeader = (qboolean)teamLeader;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
G_InitSessionData
|
||||
|
||||
Called on a first-time connect
|
||||
================
|
||||
*/
|
||||
void G_InitSessionData( gclient_t *client, char *userinfo )
|
||||
{
|
||||
clientSession_t *sess;
|
||||
const char *value;
|
||||
|
||||
sess = &client->sess;
|
||||
|
||||
// initial team determination
|
||||
value = Info_ValueForKey( userinfo, "team" );
|
||||
if( value[ 0 ] == 's' )
|
||||
{
|
||||
// a willing spectator, not a waiting-in-line
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( g_maxGameClients.integer > 0 &&
|
||||
level.numNonSpectatorClients >= g_maxGameClients.integer )
|
||||
sess->sessionTeam = TEAM_SPECTATOR;
|
||||
else
|
||||
sess->sessionTeam = TEAM_FREE;
|
||||
}
|
||||
|
||||
sess->restartTeam = PTE_NONE;
|
||||
sess->spectatorState = SPECTATOR_FREE;
|
||||
sess->spectatorTime = level.time;
|
||||
sess->spectatorClient = -1;
|
||||
memset( &sess->ignoreList, 0, sizeof( sess->ignoreList ) );
|
||||
|
||||
G_WriteClientSessionData( client );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
G_WriteSessionData
|
||||
|
||||
==================
|
||||
*/
|
||||
void G_WriteSessionData( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
//TA: ?
|
||||
trap_Cvar_Set( "session", va( "%i", 0 ) );
|
||||
|
||||
for( i = 0 ; i < level.maxclients ; i++ )
|
||||
{
|
||||
if( level.clients[ i ].pers.connected == CON_CONNECTED )
|
||||
G_WriteClientSessionData( &level.clients[ i ] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,698 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
qboolean G_SpawnString( const char *key, const char *defaultString, char **out )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( !level.spawning )
|
||||
{
|
||||
*out = (char *)defaultString;
|
||||
// G_Error( "G_SpawnString() called while not spawning" );
|
||||
}
|
||||
|
||||
for( i = 0; i < level.numSpawnVars; i++ )
|
||||
{
|
||||
if( !Q_stricmp( key, level.spawnVars[ i ][ 0 ] ) )
|
||||
{
|
||||
*out = level.spawnVars[ i ][ 1 ];
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
*out = (char *)defaultString;
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
qboolean G_SpawnFloat( const char *key, const char *defaultString, float *out )
|
||||
{
|
||||
char *s;
|
||||
qboolean present;
|
||||
|
||||
present = G_SpawnString( key, defaultString, &s );
|
||||
*out = atof( s );
|
||||
return present;
|
||||
}
|
||||
|
||||
qboolean G_SpawnInt( const char *key, const char *defaultString, int *out )
|
||||
{
|
||||
char *s;
|
||||
qboolean present;
|
||||
|
||||
present = G_SpawnString( key, defaultString, &s );
|
||||
*out = atoi( s );
|
||||
return present;
|
||||
}
|
||||
|
||||
qboolean G_SpawnVector( const char *key, const char *defaultString, float *out )
|
||||
{
|
||||
char *s;
|
||||
qboolean present;
|
||||
|
||||
present = G_SpawnString( key, defaultString, &s );
|
||||
sscanf( s, "%f %f %f", &out[ 0 ], &out[ 1 ], &out[ 2 ] );
|
||||
return present;
|
||||
}
|
||||
|
||||
qboolean G_SpawnVector4( const char *key, const char *defaultString, float *out )
|
||||
{
|
||||
char *s;
|
||||
qboolean present;
|
||||
|
||||
present = G_SpawnString( key, defaultString, &s );
|
||||
sscanf( s, "%f %f %f %f", &out[ 0 ], &out[ 1 ], &out[ 2 ], &out[ 3 ] );
|
||||
return present;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// fields are needed for spawning from the entity string
|
||||
//
|
||||
typedef enum
|
||||
{
|
||||
F_INT,
|
||||
F_FLOAT,
|
||||
F_LSTRING, // string on disk, pointer in memory, TAG_LEVEL
|
||||
F_GSTRING, // string on disk, pointer in memory, TAG_GAME
|
||||
F_VECTOR,
|
||||
F_VECTOR4, //TA
|
||||
F_ANGLEHACK,
|
||||
F_ENTITY, // index on disk, pointer in memory
|
||||
F_ITEM, // index on disk, pointer in memory
|
||||
F_CLIENT, // index on disk, pointer in memory
|
||||
F_IGNORE
|
||||
} fieldtype_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
int ofs;
|
||||
fieldtype_t type;
|
||||
int flags;
|
||||
} field_t;
|
||||
|
||||
field_t fields[ ] =
|
||||
{
|
||||
{"classname", FOFS(classname), F_LSTRING},
|
||||
{"origin", FOFS(s.origin), F_VECTOR},
|
||||
{"model", FOFS(model), F_LSTRING},
|
||||
{"model2", FOFS(model2), F_LSTRING},
|
||||
{"spawnflags", FOFS(spawnflags), F_INT},
|
||||
{"speed", FOFS(speed), F_FLOAT},
|
||||
{"target", FOFS(target), F_LSTRING},
|
||||
{"targetname", FOFS(targetname), F_LSTRING},
|
||||
{"message", FOFS(message), F_LSTRING},
|
||||
{"team", FOFS(team), F_LSTRING},
|
||||
{"wait", FOFS(wait), F_FLOAT},
|
||||
{"random", FOFS(random), F_FLOAT},
|
||||
{"count", FOFS(count), F_INT},
|
||||
{"health", FOFS(health), F_INT},
|
||||
{"light", 0, F_IGNORE},
|
||||
{"dmg", FOFS(damage), F_INT},
|
||||
{"angles", FOFS(s.angles), F_VECTOR},
|
||||
{"angle", FOFS(s.angles), F_ANGLEHACK},
|
||||
{"bounce", FOFS(physicsBounce), F_FLOAT},
|
||||
{"alpha", FOFS(pos1), F_VECTOR},
|
||||
{"radius", FOFS(pos2), F_VECTOR},
|
||||
{"acceleration", FOFS(acceleration), F_VECTOR},
|
||||
{"animation", FOFS(animation), F_VECTOR4},
|
||||
{"rotatorAngle", FOFS(rotatorAngle), F_FLOAT},
|
||||
{"targetShaderName", FOFS(targetShaderName), F_LSTRING},
|
||||
{"targetShaderNewName", FOFS(targetShaderNewName), F_LSTRING},
|
||||
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
void (*spawn)(gentity_t *ent);
|
||||
} spawn_t;
|
||||
|
||||
void SP_info_player_start( gentity_t *ent );
|
||||
void SP_info_player_deathmatch( gentity_t *ent );
|
||||
void SP_info_player_intermission( gentity_t *ent );
|
||||
|
||||
void SP_info_alien_intermission( gentity_t *ent );
|
||||
void SP_info_human_intermission( gentity_t *ent );
|
||||
|
||||
void SP_info_firstplace( gentity_t *ent );
|
||||
void SP_info_secondplace( gentity_t *ent );
|
||||
void SP_info_thirdplace( gentity_t *ent );
|
||||
void SP_info_podium( gentity_t *ent );
|
||||
|
||||
void SP_func_plat( gentity_t *ent );
|
||||
void SP_func_static( gentity_t *ent );
|
||||
void SP_func_rotating( gentity_t *ent );
|
||||
void SP_func_bobbing( gentity_t *ent );
|
||||
void SP_func_pendulum( gentity_t *ent );
|
||||
void SP_func_button( gentity_t *ent );
|
||||
void SP_func_door( gentity_t *ent );
|
||||
void SP_func_door_rotating( gentity_t *ent );
|
||||
void SP_func_door_model( gentity_t *ent );
|
||||
void SP_func_train( gentity_t *ent );
|
||||
void SP_func_timer( gentity_t *self);
|
||||
|
||||
void SP_trigger_always( gentity_t *ent );
|
||||
void SP_trigger_multiple( gentity_t *ent );
|
||||
void SP_trigger_push( gentity_t *ent );
|
||||
void SP_trigger_teleport( gentity_t *ent );
|
||||
void SP_trigger_hurt( gentity_t *ent );
|
||||
void SP_trigger_stage( gentity_t *ent );
|
||||
void SP_trigger_win( gentity_t *ent );
|
||||
void SP_trigger_buildable( gentity_t *ent );
|
||||
void SP_trigger_class( gentity_t *ent );
|
||||
void SP_trigger_equipment( gentity_t *ent );
|
||||
void SP_trigger_gravity( gentity_t *ent );
|
||||
void SP_trigger_heal( gentity_t *ent );
|
||||
void SP_trigger_ammo( gentity_t *ent );
|
||||
|
||||
void SP_target_delay( gentity_t *ent );
|
||||
void SP_target_speaker( gentity_t *ent );
|
||||
void SP_target_print( gentity_t *ent );
|
||||
void SP_target_character( gentity_t *ent );
|
||||
void SP_target_score( gentity_t *ent );
|
||||
void SP_target_teleporter( gentity_t *ent );
|
||||
void SP_target_relay( gentity_t *ent );
|
||||
void SP_target_kill( gentity_t *ent );
|
||||
void SP_target_position( gentity_t *ent );
|
||||
void SP_target_location( gentity_t *ent );
|
||||
void SP_target_push( gentity_t *ent );
|
||||
void SP_target_rumble( gentity_t *ent );
|
||||
void SP_target_alien_win( gentity_t *ent );
|
||||
void SP_target_human_win( gentity_t *ent );
|
||||
void SP_target_hurt( gentity_t *ent );
|
||||
|
||||
void SP_light( gentity_t *self );
|
||||
void SP_info_null( gentity_t *self );
|
||||
void SP_info_notnull( gentity_t *self );
|
||||
void SP_info_camp( gentity_t *self );
|
||||
void SP_path_corner( gentity_t *self );
|
||||
|
||||
void SP_misc_teleporter_dest( gentity_t *self );
|
||||
void SP_misc_model( gentity_t *ent );
|
||||
void SP_misc_portal_camera( gentity_t *ent );
|
||||
void SP_misc_portal_surface( gentity_t *ent );
|
||||
|
||||
void SP_shooter_rocket( gentity_t *ent );
|
||||
void SP_shooter_plasma( gentity_t *ent );
|
||||
void SP_shooter_grenade( gentity_t *ent );
|
||||
|
||||
void SP_misc_particle_system( gentity_t *ent );
|
||||
void SP_misc_anim_model( gentity_t *ent );
|
||||
void SP_misc_light_flare( gentity_t *ent );
|
||||
|
||||
spawn_t spawns[ ] =
|
||||
{
|
||||
// info entities don't do anything at all, but provide positional
|
||||
// information for things controlled by other processes
|
||||
{ "info_player_start", SP_info_player_start },
|
||||
{ "info_player_deathmatch", SP_info_player_deathmatch },
|
||||
{ "info_player_intermission", SP_info_player_intermission },
|
||||
|
||||
//TA: extra bits
|
||||
{ "info_alien_intermission", SP_info_alien_intermission },
|
||||
{ "info_human_intermission", SP_info_human_intermission },
|
||||
|
||||
{ "info_null", SP_info_null },
|
||||
{ "info_notnull", SP_info_notnull }, // use target_position instead
|
||||
|
||||
{ "func_plat", SP_func_plat },
|
||||
{ "func_button", SP_func_button },
|
||||
{ "func_door", SP_func_door },
|
||||
{ "func_door_rotating", SP_func_door_rotating }, //TA
|
||||
{ "func_door_model", SP_func_door_model }, //TA
|
||||
{ "func_static", SP_func_static },
|
||||
{ "func_rotating", SP_func_rotating },
|
||||
{ "func_bobbing", SP_func_bobbing },
|
||||
{ "func_pendulum", SP_func_pendulum },
|
||||
{ "func_train", SP_func_train },
|
||||
{ "func_group", SP_info_null },
|
||||
{ "func_timer", SP_func_timer }, // rename trigger_timer?
|
||||
|
||||
// Triggers are brush objects that cause an effect when contacted
|
||||
// by a living player, usually involving firing targets.
|
||||
// While almost everything could be done with
|
||||
// a single trigger class and different targets, triggered effects
|
||||
// could not be client side predicted (push and teleport).
|
||||
{ "trigger_always", SP_trigger_always },
|
||||
{ "trigger_multiple", SP_trigger_multiple },
|
||||
{ "trigger_push", SP_trigger_push },
|
||||
{ "trigger_teleport", SP_trigger_teleport },
|
||||
{ "trigger_hurt", SP_trigger_hurt },
|
||||
{ "trigger_stage", SP_trigger_stage },
|
||||
{ "trigger_win", SP_trigger_win },
|
||||
{ "trigger_buildable", SP_trigger_buildable },
|
||||
{ "trigger_class", SP_trigger_class },
|
||||
{ "trigger_equipment", SP_trigger_equipment },
|
||||
{ "trigger_gravity", SP_trigger_gravity },
|
||||
{ "trigger_heal", SP_trigger_heal },
|
||||
{ "trigger_ammo", SP_trigger_ammo },
|
||||
|
||||
// targets perform no action by themselves, but must be triggered
|
||||
// by another entity
|
||||
{ "target_delay", SP_target_delay },
|
||||
{ "target_speaker", SP_target_speaker },
|
||||
{ "target_print", SP_target_print },
|
||||
{ "target_score", SP_target_score },
|
||||
{ "target_teleporter", SP_target_teleporter },
|
||||
{ "target_relay", SP_target_relay },
|
||||
{ "target_kill", SP_target_kill },
|
||||
{ "target_position", SP_target_position },
|
||||
{ "target_location", SP_target_location },
|
||||
{ "target_push", SP_target_push },
|
||||
{ "target_rumble", SP_target_rumble },
|
||||
{ "target_alien_win", SP_target_alien_win },
|
||||
{ "target_human_win", SP_target_human_win },
|
||||
{ "target_hurt", SP_target_hurt },
|
||||
|
||||
{ "light", SP_light },
|
||||
{ "path_corner", SP_path_corner },
|
||||
|
||||
{ "misc_teleporter_dest", SP_misc_teleporter_dest },
|
||||
{ "misc_model", SP_misc_model },
|
||||
{ "misc_portal_surface", SP_misc_portal_surface },
|
||||
{ "misc_portal_camera", SP_misc_portal_camera },
|
||||
|
||||
{ "misc_particle_system", SP_misc_particle_system },
|
||||
{ "misc_anim_model", SP_misc_anim_model },
|
||||
{ "misc_light_flare", SP_misc_light_flare },
|
||||
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
===============
|
||||
G_CallSpawn
|
||||
|
||||
Finds the spawn function for the entity and calls it,
|
||||
returning qfalse if not found
|
||||
===============
|
||||
*/
|
||||
qboolean G_CallSpawn( gentity_t *ent )
|
||||
{
|
||||
spawn_t *s;
|
||||
buildable_t buildable;
|
||||
|
||||
if( !ent->classname )
|
||||
{
|
||||
G_Printf( "G_CallSpawn: NULL classname\n" );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
//check buildable spawn functions
|
||||
if( ( buildable = BG_FindBuildNumForEntityName( ent->classname ) ) != BA_NONE )
|
||||
{
|
||||
// don't spawn built-in buildings if we are using a custom layout
|
||||
if( level.layout[ 0 ] && Q_stricmp( level.layout, "*BUILTIN*" ) )
|
||||
return qtrue;
|
||||
|
||||
if( buildable == BA_A_SPAWN || buildable == BA_H_SPAWN )
|
||||
{
|
||||
ent->s.angles[ YAW ] += 180.0f;
|
||||
AngleNormalize360( ent->s.angles[ YAW ] );
|
||||
}
|
||||
|
||||
G_SpawnBuildable( ent, buildable );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
// check normal spawn functions
|
||||
for( s = spawns; s->name; s++ )
|
||||
{
|
||||
if( !strcmp( s->name, ent->classname ) )
|
||||
{
|
||||
// found it
|
||||
s->spawn( ent );
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
G_Printf( "%s doesn't have a spawn function\n", ent->classname );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
G_NewString
|
||||
|
||||
Builds a copy of the string, translating \n to real linefeeds
|
||||
so message texts can be multi-line
|
||||
=============
|
||||
*/
|
||||
char *G_NewString( const char *string )
|
||||
{
|
||||
char *newb, *new_p;
|
||||
int i,l;
|
||||
|
||||
l = strlen( string ) + 1;
|
||||
|
||||
newb = G_Alloc( l );
|
||||
|
||||
new_p = newb;
|
||||
|
||||
// turn \n into a real linefeed
|
||||
for( i = 0 ; i < l ; i++ )
|
||||
{
|
||||
if( string[ i ] == '\\' && i < l - 1 )
|
||||
{
|
||||
i++;
|
||||
if( string[ i ] == 'n' )
|
||||
*new_p++ = '\n';
|
||||
else
|
||||
*new_p++ = '\\';
|
||||
}
|
||||
else
|
||||
*new_p++ = string[ i ];
|
||||
}
|
||||
|
||||
return newb;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
G_ParseField
|
||||
|
||||
Takes a key/value pair and sets the binary values
|
||||
in a gentity
|
||||
===============
|
||||
*/
|
||||
void G_ParseField( const char *key, const char *value, gentity_t *ent )
|
||||
{
|
||||
field_t *f;
|
||||
byte *b;
|
||||
float v;
|
||||
vec3_t vec;
|
||||
vec4_t vec4;
|
||||
|
||||
for( f = fields; f->name; f++ )
|
||||
{
|
||||
if( !Q_stricmp( f->name, key ) )
|
||||
{
|
||||
// found it
|
||||
b = (byte *)ent;
|
||||
|
||||
switch( f->type )
|
||||
{
|
||||
case F_LSTRING:
|
||||
*(char **)( b + f->ofs ) = G_NewString( value );
|
||||
break;
|
||||
|
||||
case F_VECTOR:
|
||||
sscanf( value, "%f %f %f", &vec[ 0 ], &vec[ 1 ], &vec[ 2 ] );
|
||||
|
||||
( (float *)( b + f->ofs ) )[ 0 ] = vec[ 0 ];
|
||||
( (float *)( b + f->ofs ) )[ 1 ] = vec[ 1 ];
|
||||
( (float *)( b + f->ofs ) )[ 2 ] = vec[ 2 ];
|
||||
break;
|
||||
|
||||
case F_VECTOR4:
|
||||
sscanf( value, "%f %f %f %f", &vec4[ 0 ], &vec4[ 1 ], &vec4[ 2 ], &vec4[ 3 ] );
|
||||
|
||||
( (float *)( b + f->ofs ) )[ 0 ] = vec4[ 0 ];
|
||||
( (float *)( b + f->ofs ) )[ 1 ] = vec4[ 1 ];
|
||||
( (float *)( b + f->ofs ) )[ 2 ] = vec4[ 2 ];
|
||||
( (float *)( b + f->ofs ) )[ 3 ] = vec4[ 3 ];
|
||||
break;
|
||||
|
||||
case F_INT:
|
||||
*(int *)( b + f->ofs ) = atoi( value );
|
||||
break;
|
||||
|
||||
case F_FLOAT:
|
||||
*(float *)( b + f->ofs ) = atof( value );
|
||||
break;
|
||||
|
||||
case F_ANGLEHACK:
|
||||
v = atof( value );
|
||||
( (float *)( b + f->ofs ) )[ 0 ] = 0;
|
||||
( (float *)( b + f->ofs ) )[ 1 ] = v;
|
||||
( (float *)( b + f->ofs ) )[ 2 ] = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
case F_IGNORE:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
===================
|
||||
G_SpawnGEntityFromSpawnVars
|
||||
|
||||
Spawn an entity and fill in all of the level fields from
|
||||
level.spawnVars[], then call the class specfic spawn function
|
||||
===================
|
||||
*/
|
||||
void G_SpawnGEntityFromSpawnVars( void )
|
||||
{
|
||||
int i;
|
||||
gentity_t *ent;
|
||||
|
||||
// get the next free entity
|
||||
ent = G_Spawn( );
|
||||
|
||||
for( i = 0 ; i < level.numSpawnVars ; i++ )
|
||||
G_ParseField( level.spawnVars[ i ][ 0 ], level.spawnVars[ i ][ 1 ], ent );
|
||||
|
||||
G_SpawnInt( "notq3a", "0", &i );
|
||||
|
||||
if( i )
|
||||
{
|
||||
G_FreeEntity( ent );
|
||||
return;
|
||||
}
|
||||
|
||||
// move editor origin to pos
|
||||
VectorCopy( ent->s.origin, ent->s.pos.trBase );
|
||||
VectorCopy( ent->s.origin, ent->r.currentOrigin );
|
||||
|
||||
// if we didn't get a classname, don't bother spawning anything
|
||||
if( !G_CallSpawn( ent ) )
|
||||
G_FreeEntity( ent );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
G_AddSpawnVarToken
|
||||
====================
|
||||
*/
|
||||
char *G_AddSpawnVarToken( const char *string )
|
||||
{
|
||||
int l;
|
||||
char *dest;
|
||||
|
||||
l = strlen( string );
|
||||
if( level.numSpawnVarChars + l + 1 > MAX_SPAWN_VARS_CHARS )
|
||||
G_Error( "G_AddSpawnVarToken: MAX_SPAWN_CHARS" );
|
||||
|
||||
dest = level.spawnVarChars + level.numSpawnVarChars;
|
||||
memcpy( dest, string, l + 1 );
|
||||
|
||||
level.numSpawnVarChars += l + 1;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
G_ParseSpawnVars
|
||||
|
||||
Parses a brace bounded set of key / value pairs out of the
|
||||
level's entity strings into level.spawnVars[]
|
||||
|
||||
This does not actually spawn an entity.
|
||||
====================
|
||||
*/
|
||||
qboolean G_ParseSpawnVars( void )
|
||||
{
|
||||
char keyname[ MAX_TOKEN_CHARS ];
|
||||
char com_token[ MAX_TOKEN_CHARS ];
|
||||
|
||||
level.numSpawnVars = 0;
|
||||
level.numSpawnVarChars = 0;
|
||||
|
||||
// parse the opening brace
|
||||
if( !trap_GetEntityToken( com_token, sizeof( com_token ) ) )
|
||||
{
|
||||
// end of spawn string
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
if( com_token[ 0 ] != '{' )
|
||||
G_Error( "G_ParseSpawnVars: found %s when expecting {", com_token );
|
||||
|
||||
// go through all the key / value pairs
|
||||
while( 1 )
|
||||
{
|
||||
// parse key
|
||||
if( !trap_GetEntityToken( keyname, sizeof( keyname ) ) )
|
||||
G_Error( "G_ParseSpawnVars: EOF without closing brace" );
|
||||
|
||||
if( keyname[0] == '}' )
|
||||
break;
|
||||
|
||||
// parse value
|
||||
if( !trap_GetEntityToken( com_token, sizeof( com_token ) ) )
|
||||
G_Error( "G_ParseSpawnVars: EOF without closing brace" );
|
||||
|
||||
if( com_token[0] == '}' )
|
||||
G_Error( "G_ParseSpawnVars: closing brace without data" );
|
||||
|
||||
if( level.numSpawnVars == MAX_SPAWN_VARS )
|
||||
G_Error( "G_ParseSpawnVars: MAX_SPAWN_VARS" );
|
||||
|
||||
level.spawnVars[ level.numSpawnVars ][ 0 ] = G_AddSpawnVarToken( keyname );
|
||||
level.spawnVars[ level.numSpawnVars ][ 1 ] = G_AddSpawnVarToken( com_token );
|
||||
level.numSpawnVars++;
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*QUAKED worldspawn (0 0 0) ?
|
||||
|
||||
Every map should have exactly one worldspawn.
|
||||
"music" music wav file
|
||||
"gravity" 800 is default gravity
|
||||
"message" Text to print during connection process
|
||||
*/
|
||||
void SP_worldspawn( void )
|
||||
{
|
||||
char *s;
|
||||
|
||||
G_SpawnString( "classname", "", &s );
|
||||
|
||||
if( Q_stricmp( s, "worldspawn" ) )
|
||||
G_Error( "SP_worldspawn: The first entity isn't 'worldspawn'" );
|
||||
|
||||
// make some data visible to connecting client
|
||||
trap_SetConfigstring( CS_GAME_VERSION, GAME_VERSION );
|
||||
|
||||
trap_SetConfigstring( CS_LEVEL_START_TIME, va( "%i", level.startTime ) );
|
||||
|
||||
G_SpawnString( "music", "", &s );
|
||||
trap_SetConfigstring( CS_MUSIC, s );
|
||||
|
||||
G_SpawnString( "message", "", &s );
|
||||
trap_SetConfigstring( CS_MESSAGE, s ); // map specific message
|
||||
|
||||
trap_SetConfigstring( CS_MOTD, g_motd.string ); // message of the day
|
||||
|
||||
G_SpawnString( "gravity", "800", &s );
|
||||
trap_Cvar_Set( "g_gravity", s );
|
||||
|
||||
G_SpawnString( "humanBuildPoints", DEFAULT_HUMAN_BUILDPOINTS, &s );
|
||||
trap_Cvar_Set( "g_humanBuildPoints", s );
|
||||
|
||||
G_SpawnString( "humanMaxStage", DEFAULT_HUMAN_MAX_STAGE, &s );
|
||||
trap_Cvar_Set( "g_humanMaxStage", s );
|
||||
|
||||
G_SpawnString( "humanStage2Threshold", DEFAULT_HUMAN_STAGE2_THRESH, &s );
|
||||
trap_Cvar_Set( "g_humanStage2Threshold", s );
|
||||
|
||||
G_SpawnString( "humanStage3Threshold", DEFAULT_HUMAN_STAGE3_THRESH, &s );
|
||||
trap_Cvar_Set( "g_humanStage3Threshold", s );
|
||||
|
||||
G_SpawnString( "alienBuildPoints", DEFAULT_ALIEN_BUILDPOINTS, &s );
|
||||
trap_Cvar_Set( "g_alienBuildPoints", s );
|
||||
|
||||
G_SpawnString( "alienMaxStage", DEFAULT_ALIEN_MAX_STAGE, &s );
|
||||
trap_Cvar_Set( "g_alienMaxStage", s );
|
||||
|
||||
G_SpawnString( "alienStage2Threshold", DEFAULT_ALIEN_STAGE2_THRESH, &s );
|
||||
trap_Cvar_Set( "g_alienStage2Threshold", s );
|
||||
|
||||
G_SpawnString( "alienStage3Threshold", DEFAULT_ALIEN_STAGE3_THRESH, &s );
|
||||
trap_Cvar_Set( "g_alienStage3Threshold", s );
|
||||
|
||||
G_SpawnString( "enableDust", "0", &s );
|
||||
trap_Cvar_Set( "g_enableDust", s );
|
||||
|
||||
G_SpawnString( "enableBreath", "0", &s );
|
||||
trap_Cvar_Set( "g_enableBreath", s );
|
||||
|
||||
G_SpawnString( "disabledEquipment", "", &s );
|
||||
trap_Cvar_Set( "g_disabledEquipment", s );
|
||||
|
||||
G_SpawnString( "disabledClasses", "", &s );
|
||||
trap_Cvar_Set( "g_disabledClasses", s );
|
||||
|
||||
G_SpawnString( "disabledBuildables", "", &s );
|
||||
trap_Cvar_Set( "g_disabledBuildables", s );
|
||||
|
||||
g_entities[ ENTITYNUM_WORLD ].s.number = ENTITYNUM_WORLD;
|
||||
g_entities[ ENTITYNUM_WORLD ].classname = "worldspawn";
|
||||
|
||||
if( g_restarted.integer )
|
||||
trap_Cvar_Set( "g_restarted", "0" );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
G_SpawnEntitiesFromString
|
||||
|
||||
Parses textual entity definitions out of an entstring and spawns gentities.
|
||||
==============
|
||||
*/
|
||||
void G_SpawnEntitiesFromString( void )
|
||||
{
|
||||
// allow calls to G_Spawn*()
|
||||
level.spawning = qtrue;
|
||||
level.numSpawnVars = 0;
|
||||
|
||||
// the worldspawn is not an actual entity, but it still
|
||||
// has a "spawn" function to perform any global setup
|
||||
// needed by a level (setting configstrings or cvars, etc)
|
||||
if( !G_ParseSpawnVars( ) )
|
||||
G_Error( "SpawnEntities: no entities" );
|
||||
|
||||
SP_worldspawn( );
|
||||
|
||||
// parse ents
|
||||
while( G_ParseSpawnVars( ) )
|
||||
G_SpawnGEntityFromSpawnVars( );
|
||||
|
||||
level.spawning = qfalse; // any future calls to G_Spawn*() will be errors
|
||||
}
|
||||
|
||||
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// this file holds commands that can be executed by the server console, but not remote clients
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
PACKET FILTERING
|
||||
|
||||
|
||||
You can add or remove addresses from the filter list with:
|
||||
|
||||
addip <ip>
|
||||
removeip <ip>
|
||||
|
||||
The ip address is specified in dot format, and you can use '*' to match any value
|
||||
so you can specify an entire class C network with "addip 192.246.40.*"
|
||||
|
||||
Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host.
|
||||
|
||||
listip
|
||||
Prints the current list of filters.
|
||||
|
||||
g_filterban <0 or 1>
|
||||
|
||||
If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting.
|
||||
|
||||
If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network.
|
||||
|
||||
TTimo NOTE: for persistence, bans are stored in g_banIPs cvar MAX_CVAR_VALUE_STRING
|
||||
The size of the cvar string buffer is limiting the banning to around 20 masks
|
||||
this could be improved by putting some g_banIPs2 g_banIps3 etc. maybe
|
||||
still, you should rely on PB for banning instead
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
// extern vmCvar_t g_banIPs;
|
||||
// extern vmCvar_t g_filterBan;
|
||||
|
||||
|
||||
typedef struct ipFilter_s
|
||||
{
|
||||
unsigned mask;
|
||||
unsigned compare;
|
||||
} ipFilter_t;
|
||||
|
||||
#define MAX_IPFILTERS 1024
|
||||
|
||||
static ipFilter_t ipFilters[ MAX_IPFILTERS ];
|
||||
static int numIPFilters;
|
||||
|
||||
/*
|
||||
=================
|
||||
StringToFilter
|
||||
=================
|
||||
*/
|
||||
static qboolean StringToFilter( char *s, ipFilter_t *f )
|
||||
{
|
||||
char num[ 128 ];
|
||||
int i, j;
|
||||
byte b[ 4 ];
|
||||
byte m[ 4 ];
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
b[ i ] = 0;
|
||||
m[ i ] = 0;
|
||||
}
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
if( *s < '0' || *s > '9' )
|
||||
{
|
||||
if( *s == '*' ) // 'match any'
|
||||
{
|
||||
//b[ i ] and m[ i ] to 0
|
||||
s++;
|
||||
if ( !*s )
|
||||
break;
|
||||
|
||||
s++;
|
||||
continue;
|
||||
}
|
||||
|
||||
G_Printf( "Bad filter address: %s\n", s );
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
j = 0;
|
||||
while( *s >= '0' && *s <= '9' )
|
||||
num[ j++ ] = *s++;
|
||||
|
||||
num[ j ] = 0;
|
||||
b[ i ] = atoi( num );
|
||||
|
||||
m[ i ] = 255;
|
||||
|
||||
if( !*s )
|
||||
break;
|
||||
|
||||
s++;
|
||||
}
|
||||
|
||||
f->mask = *(unsigned *)m;
|
||||
f->compare = *(unsigned *)b;
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
UpdateIPBans
|
||||
=================
|
||||
*/
|
||||
static void UpdateIPBans( void )
|
||||
{
|
||||
byte b[ 4 ];
|
||||
byte m[ 4 ];
|
||||
int i, j;
|
||||
char iplist_final[ MAX_CVAR_VALUE_STRING ];
|
||||
char ip[ 64 ];
|
||||
|
||||
*iplist_final = 0;
|
||||
|
||||
for( i = 0 ; i < numIPFilters ; i++ )
|
||||
{
|
||||
if( ipFilters[ i ].compare == 0xffffffff )
|
||||
continue;
|
||||
|
||||
*(unsigned *)b = ipFilters[ i ].compare;
|
||||
*(unsigned *)m = ipFilters[ i ].mask;
|
||||
*ip = 0;
|
||||
|
||||
for( j = 0 ; j < 4 ; j++ )
|
||||
{
|
||||
if( m[ j ] != 255 )
|
||||
Q_strcat( ip, sizeof( ip ), "*" );
|
||||
else
|
||||
Q_strcat( ip, sizeof( ip ), va( "%i", b[ j ] ) );
|
||||
|
||||
Q_strcat( ip, sizeof( ip ), ( j < 3 ) ? "." : " " );
|
||||
}
|
||||
|
||||
if( strlen( iplist_final ) + strlen( ip ) < MAX_CVAR_VALUE_STRING )
|
||||
Q_strcat( iplist_final, sizeof( iplist_final ), ip );
|
||||
else
|
||||
{
|
||||
Com_Printf( "g_banIPs overflowed at MAX_CVAR_VALUE_STRING\n" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
trap_Cvar_Set( "g_banIPs", iplist_final );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
G_FilterPacket
|
||||
=================
|
||||
*/
|
||||
qboolean G_FilterPacket( char *from )
|
||||
{
|
||||
int i;
|
||||
unsigned in;
|
||||
byte m[ 4 ];
|
||||
char *p;
|
||||
|
||||
i = 0;
|
||||
p = from;
|
||||
while( *p && i < 4 )
|
||||
{
|
||||
m[ i ] = 0;
|
||||
while( *p >= '0' && *p <= '9' )
|
||||
{
|
||||
m[ i ] = m[ i ] * 10 + ( *p - '0' );
|
||||
p++;
|
||||
}
|
||||
|
||||
if( !*p || *p == ':' )
|
||||
break;
|
||||
|
||||
i++, p++;
|
||||
}
|
||||
|
||||
in = *(unsigned *)m;
|
||||
|
||||
for( i = 0; i < numIPFilters; i++ )
|
||||
if( ( in & ipFilters[ i ].mask ) == ipFilters[ i ].compare )
|
||||
return g_filterBan.integer != 0;
|
||||
|
||||
return g_filterBan.integer == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
AddIP
|
||||
=================
|
||||
*/
|
||||
static void AddIP( char *str )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0 ; i < numIPFilters ; i++ )
|
||||
if( ipFilters[ i ].compare == 0xffffffff )
|
||||
break; // free spot
|
||||
|
||||
if( i == numIPFilters )
|
||||
{
|
||||
if( numIPFilters == MAX_IPFILTERS )
|
||||
{
|
||||
G_Printf( "IP filter list is full\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
numIPFilters++;
|
||||
}
|
||||
|
||||
if( !StringToFilter( str, &ipFilters[ i ] ) )
|
||||
ipFilters[ i ].compare = 0xffffffffu;
|
||||
|
||||
UpdateIPBans( );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
G_ProcessIPBans
|
||||
=================
|
||||
*/
|
||||
void G_ProcessIPBans( void )
|
||||
{
|
||||
char *s, *t;
|
||||
char str[ MAX_CVAR_VALUE_STRING ];
|
||||
|
||||
Q_strncpyz( str, g_banIPs.string, sizeof( str ) );
|
||||
|
||||
for( t = s = g_banIPs.string; *t; /* */ )
|
||||
{
|
||||
s = strchr( s, ' ' );
|
||||
|
||||
if( !s )
|
||||
break;
|
||||
|
||||
while( *s == ' ' )
|
||||
*s++ = 0;
|
||||
|
||||
if( *t )
|
||||
AddIP( t );
|
||||
|
||||
t = s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
Svcmd_AddIP_f
|
||||
=================
|
||||
*/
|
||||
void Svcmd_AddIP_f( void )
|
||||
{
|
||||
char str[ MAX_TOKEN_CHARS ];
|
||||
|
||||
if( trap_Argc( ) < 2 )
|
||||
{
|
||||
G_Printf( "Usage: addip <ip-mask>\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
trap_Argv( 1, str, sizeof( str ) );
|
||||
|
||||
AddIP( str );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Svcmd_RemoveIP_f
|
||||
=================
|
||||
*/
|
||||
void Svcmd_RemoveIP_f( void )
|
||||
{
|
||||
ipFilter_t f;
|
||||
int i;
|
||||
char str[ MAX_TOKEN_CHARS ];
|
||||
|
||||
if( trap_Argc( ) < 2 )
|
||||
{
|
||||
G_Printf( "Usage: sv removeip <ip-mask>\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
trap_Argv( 1, str, sizeof( str ) );
|
||||
|
||||
if( !StringToFilter( str, &f ) )
|
||||
return;
|
||||
|
||||
for( i = 0; i < numIPFilters; i++ )
|
||||
{
|
||||
if( ipFilters[ i ].mask == f.mask &&
|
||||
ipFilters[ i ].compare == f.compare)
|
||||
{
|
||||
ipFilters[ i ].compare = 0xffffffffu;
|
||||
G_Printf ( "Removed.\n" );
|
||||
|
||||
UpdateIPBans( );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
G_Printf ( "Didn't find %s.\n", str );
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
Svcmd_EntityList_f
|
||||
===================
|
||||
*/
|
||||
void Svcmd_EntityList_f( void )
|
||||
{
|
||||
int e;
|
||||
gentity_t *check;
|
||||
|
||||
check = g_entities + 1;
|
||||
|
||||
for( e = 1; e < level.num_entities; e++, check++ )
|
||||
{
|
||||
if( !check->inuse )
|
||||
continue;
|
||||
|
||||
G_Printf( "%3i:", e );
|
||||
|
||||
switch( check->s.eType )
|
||||
{
|
||||
case ET_GENERAL:
|
||||
G_Printf( "ET_GENERAL " );
|
||||
break;
|
||||
case ET_PLAYER:
|
||||
G_Printf( "ET_PLAYER " );
|
||||
break;
|
||||
case ET_ITEM:
|
||||
G_Printf( "ET_ITEM " );
|
||||
break;
|
||||
case ET_BUILDABLE:
|
||||
G_Printf( "ET_BUILDABLE " );
|
||||
break;
|
||||
case ET_MISSILE:
|
||||
G_Printf( "ET_MISSILE " );
|
||||
break;
|
||||
case ET_MOVER:
|
||||
G_Printf( "ET_MOVER " );
|
||||
break;
|
||||
case ET_BEAM:
|
||||
G_Printf( "ET_BEAM " );
|
||||
break;
|
||||
case ET_PORTAL:
|
||||
G_Printf( "ET_PORTAL " );
|
||||
break;
|
||||
case ET_SPEAKER:
|
||||
G_Printf( "ET_SPEAKER " );
|
||||
break;
|
||||
case ET_PUSH_TRIGGER:
|
||||
G_Printf( "ET_PUSH_TRIGGER " );
|
||||
break;
|
||||
case ET_TELEPORT_TRIGGER:
|
||||
G_Printf( "ET_TELEPORT_TRIGGER " );
|
||||
break;
|
||||
case ET_INVISIBLE:
|
||||
G_Printf( "ET_INVISIBLE " );
|
||||
break;
|
||||
case ET_GRAPPLE:
|
||||
G_Printf( "ET_GRAPPLE " );
|
||||
break;
|
||||
default:
|
||||
G_Printf( "%3i ", check->s.eType );
|
||||
break;
|
||||
}
|
||||
|
||||
if( check->classname )
|
||||
G_Printf( "%s", check->classname );
|
||||
|
||||
G_Printf( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
gclient_t *ClientForString( const char *s )
|
||||
{
|
||||
gclient_t *cl;
|
||||
int i;
|
||||
int idnum;
|
||||
|
||||
// numeric values are just slot numbers
|
||||
if( s[ 0 ] >= '0' && s[ 0 ] <= '9' )
|
||||
{
|
||||
idnum = atoi( s );
|
||||
|
||||
if( idnum < 0 || idnum >= level.maxclients )
|
||||
{
|
||||
Com_Printf( "Bad client slot: %i\n", idnum );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cl = &level.clients[ idnum ];
|
||||
|
||||
if( cl->pers.connected == CON_DISCONNECTED )
|
||||
{
|
||||
G_Printf( "Client %i is not connected\n", idnum );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
// check for a name match
|
||||
for( i = 0; i < level.maxclients; i++ )
|
||||
{
|
||||
cl = &level.clients[ i ];
|
||||
if( cl->pers.connected == CON_DISCONNECTED )
|
||||
continue;
|
||||
|
||||
if( !Q_stricmp( cl->pers.netname, s ) )
|
||||
return cl;
|
||||
}
|
||||
|
||||
G_Printf( "User %s is not on the server\n", s );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
Svcmd_ForceTeam_f
|
||||
|
||||
forceteam <player> <team>
|
||||
===================
|
||||
*/
|
||||
void Svcmd_ForceTeam_f( void )
|
||||
{
|
||||
gclient_t *cl;
|
||||
char str[ MAX_TOKEN_CHARS ];
|
||||
|
||||
// find the player
|
||||
trap_Argv( 1, str, sizeof( str ) );
|
||||
cl = ClientForString( str );
|
||||
|
||||
if( !cl )
|
||||
return;
|
||||
|
||||
// set the team
|
||||
trap_Argv( 2, str, sizeof( str ) );
|
||||
/*SetTeam( &g_entities[cl - level.clients], str );*/
|
||||
//FIXME: tremulise this
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
Svcmd_LayoutSave_f
|
||||
|
||||
layoutsave <name>
|
||||
===================
|
||||
*/
|
||||
void Svcmd_LayoutSave_f( void )
|
||||
{
|
||||
char str[ MAX_QPATH ];
|
||||
char str2[ MAX_QPATH - 4 ];
|
||||
char *s;
|
||||
int i = 0;
|
||||
|
||||
if( trap_Argc( ) != 2 )
|
||||
{
|
||||
G_Printf( "usage: layoutsave LAYOUTNAME\n" );
|
||||
return;
|
||||
}
|
||||
trap_Argv( 1, str, sizeof( str ) );
|
||||
|
||||
// sanitize name
|
||||
s = &str[ 0 ];
|
||||
while( *s && i < sizeof( str2 ) - 1 )
|
||||
{
|
||||
if( ( *s >= '0' && *s <= '9' ) ||
|
||||
( *s >= 'a' && *s <= 'z' ) ||
|
||||
( *s >= 'A' && *s <= 'Z' ) || *s == '-' || *s == '_' )
|
||||
{
|
||||
str2[ i++ ] = *s;
|
||||
str2[ i ] = '\0';
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
if( !str2[ 0 ] )
|
||||
{
|
||||
G_Printf("layoutsave: invalid name \"%s\"\n", str );
|
||||
return;
|
||||
}
|
||||
|
||||
G_LayoutSave( str2 );
|
||||
}
|
||||
|
||||
char *ConcatArgs( int start );
|
||||
|
||||
/*
|
||||
===================
|
||||
Svcmd_LayoutLoad_f
|
||||
|
||||
layoutload [<name> [<name2> [<name3 [...]]]]
|
||||
|
||||
This is just a silly alias for doing:
|
||||
set g_layouts "name name2 name3"
|
||||
map_restart
|
||||
===================
|
||||
*/
|
||||
void Svcmd_LayoutLoad_f( void )
|
||||
{
|
||||
char layouts[ MAX_CVAR_VALUE_STRING ];
|
||||
char *s;
|
||||
|
||||
s = ConcatArgs( 1 );
|
||||
Q_strncpyz( layouts, s, sizeof( layouts ) );
|
||||
trap_Cvar_Set( "g_layouts", layouts );
|
||||
trap_SendConsoleCommand( EXEC_APPEND, "map_restart\n" );
|
||||
level.restarted = qtrue;
|
||||
}
|
||||
|
||||
static void Svcmd_AdmitDefeat_f( void )
|
||||
{
|
||||
int team;
|
||||
char teamNum[ 2 ];
|
||||
|
||||
if( trap_Argc( ) != 2 )
|
||||
{
|
||||
G_Printf("admitdefeat: must provide a team\n");
|
||||
return;
|
||||
}
|
||||
trap_Argv( 1, teamNum, sizeof( teamNum ) );
|
||||
team = atoi( teamNum );
|
||||
if( team == PTE_ALIENS || teamNum[ 0 ] == 'a' )
|
||||
{
|
||||
level.surrenderTeam = PTE_ALIENS;
|
||||
G_BaseSelfDestruct( PTE_ALIENS );
|
||||
G_TeamCommand( PTE_ALIENS, "cp \"Hivemind Link Broken\" 1");
|
||||
trap_SendServerCommand( -1, "print \"Alien team has admitted defeat\n\"" );
|
||||
}
|
||||
else if( team == PTE_HUMANS || teamNum[ 0 ] == 'h' )
|
||||
{
|
||||
level.surrenderTeam = PTE_HUMANS;
|
||||
G_BaseSelfDestruct( PTE_HUMANS );
|
||||
G_TeamCommand( PTE_HUMANS, "cp \"Life Support Terminated\" 1");
|
||||
trap_SendServerCommand( -1, "print \"Human team has admitted defeat\n\"" );
|
||||
}
|
||||
else
|
||||
{
|
||||
G_Printf("admitdefeat: invalid team\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ConsoleCommand
|
||||
|
||||
=================
|
||||
*/
|
||||
qboolean ConsoleCommand( void )
|
||||
{
|
||||
char cmd[ MAX_TOKEN_CHARS ];
|
||||
|
||||
trap_Argv( 0, cmd, sizeof( cmd ) );
|
||||
|
||||
if( Q_stricmp( cmd, "entitylist" ) == 0 )
|
||||
{
|
||||
Svcmd_EntityList_f( );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( Q_stricmp( cmd, "forceteam" ) == 0 )
|
||||
{
|
||||
Svcmd_ForceTeam_f( );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( Q_stricmp( cmd, "game_memory" ) == 0 )
|
||||
{
|
||||
Svcmd_GameMem_f( );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( Q_stricmp( cmd, "addip" ) == 0 )
|
||||
{
|
||||
Svcmd_AddIP_f( );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( Q_stricmp( cmd, "removeip" ) == 0 )
|
||||
{
|
||||
Svcmd_RemoveIP_f( );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( Q_stricmp( cmd, "listip" ) == 0 )
|
||||
{
|
||||
trap_SendConsoleCommand( EXEC_NOW, "g_banIPs\n" );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( Q_stricmp( cmd, "mapRotation" ) == 0 )
|
||||
{
|
||||
char *rotationName = ConcatArgs( 1 );
|
||||
|
||||
if( !G_StartMapRotation( rotationName, qfalse ) )
|
||||
G_Printf( "Can't find map rotation %s\n", rotationName );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( Q_stricmp( cmd, "stopMapRotation" ) == 0 )
|
||||
{
|
||||
G_StopMapRotation( );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( Q_stricmp( cmd, "advanceMapRotation" ) == 0 )
|
||||
{
|
||||
G_AdvanceMapRotation( );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( Q_stricmp( cmd, "alienWin" ) == 0 )
|
||||
{
|
||||
int i;
|
||||
gentity_t *e;
|
||||
|
||||
for( i = 1, e = g_entities + i; i < level.num_entities; i++, e++ )
|
||||
{
|
||||
if( e->s.modelindex == BA_H_SPAWN )
|
||||
G_Damage( e, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE );
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( Q_stricmp( cmd, "humanWin" ) == 0 )
|
||||
{
|
||||
int i;
|
||||
gentity_t *e;
|
||||
|
||||
for( i = 1, e = g_entities + i; i < level.num_entities; i++, e++ )
|
||||
{
|
||||
if( e->s.modelindex == BA_A_SPAWN )
|
||||
G_Damage( e, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE );
|
||||
}
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( !Q_stricmp( cmd, "layoutsave" ) )
|
||||
{
|
||||
Svcmd_LayoutSave_f( );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( !Q_stricmp( cmd, "layoutload" ) )
|
||||
{
|
||||
Svcmd_LayoutLoad_f( );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( !Q_stricmp( cmd, "admitdefeat" ) )
|
||||
{
|
||||
Svcmd_AdmitDefeat_f( );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if( !Q_stricmp( cmd, "evacuation" ) )
|
||||
{
|
||||
trap_SendServerCommand( -1, "print \"Evacuation ordered\n\"" );
|
||||
level.lastWin = PTE_NONE;
|
||||
trap_SetConfigstring( CS_WINNER, "Evacuation" );
|
||||
LogExit( "Evacuation." );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
// see if this is a a admin command
|
||||
if( G_admin_cmd_check( NULL, qfalse ) )
|
||||
return qtrue;
|
||||
|
||||
if( g_dedicated.integer )
|
||||
{
|
||||
if( Q_stricmp( cmd, "say" ) == 0 )
|
||||
{
|
||||
trap_SendServerCommand( -1, va( "print \"server: %s\n\"", ConcatArgs( 1 ) ) );
|
||||
return qtrue;
|
||||
}
|
||||
else if( !Q_stricmp( cmd, "chat" ) )
|
||||
{
|
||||
trap_SendServerCommand( -1, va( "chat \"%s\" -1 0", ConcatArgs( 1 ) ) );
|
||||
G_Printf( "chat: %s\n", ConcatArgs( 1 ) );
|
||||
return qtrue;
|
||||
}
|
||||
else if( !Q_stricmp( cmd, "cp" ) )
|
||||
{
|
||||
int i, j;
|
||||
char buffer[MAX_STRING_CHARS];
|
||||
Q_strncpyz( buffer, ConcatArgs( 1 ), sizeof( buffer ) );
|
||||
for( i = j = 0; i < strlen( buffer ); i++, j++ )
|
||||
{
|
||||
if(!buffer[i]) break;
|
||||
if(buffer[i] == '\\')
|
||||
{
|
||||
if(buffer[i + 1] == '\\')
|
||||
buffer[j] = buffer[++i];
|
||||
else if(buffer[i + 1] == 'n')
|
||||
{
|
||||
buffer[j] = '\n';
|
||||
i++;
|
||||
}
|
||||
else
|
||||
buffer[j] = buffer[i];
|
||||
}
|
||||
else
|
||||
buffer[j] = buffer[i];
|
||||
}
|
||||
buffer[j] = 0;
|
||||
trap_SendServerCommand( -1, va( "cp \"%s\"", buffer ) );
|
||||
G_Printf( "cp: %s\n", ConcatArgs( 1 ) );
|
||||
return qtrue;
|
||||
}
|
||||
else if( !Q_stricmp( cmd, "m" ) )
|
||||
{
|
||||
G_PrivateMessage( NULL );
|
||||
return qtrue;
|
||||
}
|
||||
else if( !Q_stricmp( cmd, "a" ) || !Q_stricmp( cmd, "say_admins" ))
|
||||
{
|
||||
G_Say( NULL, NULL, SAY_ADMINS, ConcatArgs( 1 ) );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
G_Printf( "unknown command: %s\n", cmd );
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
code
|
||||
|
||||
equ trap_Printf -1
|
||||
equ trap_Error -2
|
||||
equ trap_Milliseconds -3
|
||||
equ trap_Cvar_Register -4
|
||||
equ trap_Cvar_Update -5
|
||||
equ trap_Cvar_Set -6
|
||||
equ trap_Cvar_VariableIntegerValue -7
|
||||
equ trap_Cvar_VariableStringBuffer -8
|
||||
equ trap_Argc -9
|
||||
equ trap_Argv -10
|
||||
equ trap_FS_FOpenFile -11
|
||||
equ trap_FS_Read -12
|
||||
equ trap_FS_Write -13
|
||||
equ trap_FS_FCloseFile -14
|
||||
equ trap_SendConsoleCommand -15
|
||||
equ trap_LocateGameData -16
|
||||
equ trap_DropClient -17
|
||||
equ trap_SendServerCommand -18
|
||||
equ trap_SetConfigstring -19
|
||||
equ trap_GetConfigstring -20
|
||||
equ trap_GetUserinfo -21
|
||||
equ trap_SetUserinfo -22
|
||||
equ trap_GetServerinfo -23
|
||||
equ trap_SetBrushModel -24
|
||||
equ trap_Trace -25
|
||||
equ trap_PointContents -26
|
||||
equ trap_InPVS -27
|
||||
equ trap_InPVSIgnorePortals -28
|
||||
equ trap_AdjustAreaPortalState -29
|
||||
equ trap_AreasConnected -30
|
||||
equ trap_LinkEntity -31
|
||||
equ trap_UnlinkEntity -32
|
||||
equ trap_EntitiesInBox -33
|
||||
equ trap_EntityContact -34
|
||||
equ trap_GetUsercmd -35
|
||||
equ trap_GetEntityToken -36
|
||||
equ trap_FS_GetFileList -37
|
||||
equ trap_RealTime -38
|
||||
equ trap_SnapVector -39
|
||||
equ trap_TraceCapsule -40
|
||||
equ trap_EntityContactCapsule -41
|
||||
equ trap_FS_Seek -42
|
||||
|
||||
equ trap_Parse_AddGlobalDefine -43
|
||||
equ trap_Parse_LoadSource -44
|
||||
equ trap_Parse_FreeSource -45
|
||||
equ trap_Parse_ReadToken -46
|
||||
equ trap_Parse_SourceFileAndLine -47
|
||||
|
||||
equ trap_SendGameStat -48
|
||||
|
||||
|
||||
equ memset -101
|
||||
equ memcpy -102
|
||||
equ strncpy -103
|
||||
equ sin -104
|
||||
equ cos -105
|
||||
equ atan2 -106
|
||||
equ sqrt -107
|
||||
equ floor -111
|
||||
equ ceil -112
|
||||
equ testPrintInt -113
|
||||
equ testPrintFloat -114
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
// this file is only included when building a dll
|
||||
// g_syscalls.asm is included instead when building a qvm
|
||||
|
||||
static intptr_t (QDECL *syscall)( intptr_t arg, ... ) = (intptr_t (QDECL *)( intptr_t, ...))-1;
|
||||
|
||||
|
||||
void dllEntry( intptr_t (QDECL *syscallptr)( intptr_t arg,... ) )
|
||||
{
|
||||
syscall = syscallptr;
|
||||
}
|
||||
|
||||
int PASSFLOAT( float x )
|
||||
{
|
||||
float floatTemp;
|
||||
floatTemp = x;
|
||||
return *(int *)&floatTemp;
|
||||
}
|
||||
|
||||
void trap_Printf( const char *fmt )
|
||||
{
|
||||
syscall( G_PRINT, fmt );
|
||||
}
|
||||
|
||||
void trap_Error( const char *fmt )
|
||||
{
|
||||
syscall( G_ERROR, fmt );
|
||||
}
|
||||
|
||||
int trap_Milliseconds( void )
|
||||
{
|
||||
return syscall( G_MILLISECONDS );
|
||||
}
|
||||
int trap_Argc( void )
|
||||
{
|
||||
return syscall( G_ARGC );
|
||||
}
|
||||
|
||||
void trap_Argv( int n, char *buffer, int bufferLength )
|
||||
{
|
||||
syscall( G_ARGV, n, buffer, bufferLength );
|
||||
}
|
||||
|
||||
int trap_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode )
|
||||
{
|
||||
return syscall( G_FS_FOPEN_FILE, qpath, f, mode );
|
||||
}
|
||||
|
||||
void trap_FS_Read( void *buffer, int len, fileHandle_t f )
|
||||
{
|
||||
syscall( G_FS_READ, buffer, len, f );
|
||||
}
|
||||
|
||||
void trap_FS_Write( const void *buffer, int len, fileHandle_t f )
|
||||
{
|
||||
syscall( G_FS_WRITE, buffer, len, f );
|
||||
}
|
||||
|
||||
void trap_FS_FCloseFile( fileHandle_t f )
|
||||
{
|
||||
syscall( G_FS_FCLOSE_FILE, f );
|
||||
}
|
||||
|
||||
int trap_FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize )
|
||||
{
|
||||
return syscall( G_FS_GETFILELIST, path, extension, listbuf, bufsize );
|
||||
}
|
||||
|
||||
void trap_SendConsoleCommand( int exec_when, const char *text )
|
||||
{
|
||||
syscall( G_SEND_CONSOLE_COMMAND, exec_when, text );
|
||||
}
|
||||
|
||||
void trap_Cvar_Register( vmCvar_t *cvar, const char *var_name, const char *value, int flags )
|
||||
{
|
||||
syscall( G_CVAR_REGISTER, cvar, var_name, value, flags );
|
||||
}
|
||||
|
||||
void trap_Cvar_Update( vmCvar_t *cvar )
|
||||
{
|
||||
syscall( G_CVAR_UPDATE, cvar );
|
||||
}
|
||||
|
||||
void trap_Cvar_Set( const char *var_name, const char *value )
|
||||
{
|
||||
syscall( G_CVAR_SET, var_name, value );
|
||||
}
|
||||
|
||||
int trap_Cvar_VariableIntegerValue( const char *var_name )
|
||||
{
|
||||
return syscall( G_CVAR_VARIABLE_INTEGER_VALUE, var_name );
|
||||
}
|
||||
|
||||
void trap_Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize )
|
||||
{
|
||||
syscall( G_CVAR_VARIABLE_STRING_BUFFER, var_name, buffer, bufsize );
|
||||
}
|
||||
|
||||
|
||||
void trap_LocateGameData( gentity_t *gEnts, int numGEntities, int sizeofGEntity_t,
|
||||
playerState_t *clients, int sizeofGClient )
|
||||
{
|
||||
syscall( G_LOCATE_GAME_DATA, gEnts, numGEntities, sizeofGEntity_t, clients, sizeofGClient );
|
||||
}
|
||||
|
||||
void trap_DropClient( int clientNum, const char *reason )
|
||||
{
|
||||
syscall( G_DROP_CLIENT, clientNum, reason );
|
||||
}
|
||||
|
||||
void trap_SendServerCommand( int clientNum, const char *text )
|
||||
{
|
||||
syscall( G_SEND_SERVER_COMMAND, clientNum, text );
|
||||
}
|
||||
|
||||
void trap_SetConfigstring( int num, const char *string )
|
||||
{
|
||||
syscall( G_SET_CONFIGSTRING, num, string );
|
||||
}
|
||||
|
||||
void trap_GetConfigstring( int num, char *buffer, int bufferSize )
|
||||
{
|
||||
syscall( G_GET_CONFIGSTRING, num, buffer, bufferSize );
|
||||
}
|
||||
|
||||
void trap_GetUserinfo( int num, char *buffer, int bufferSize )
|
||||
{
|
||||
syscall( G_GET_USERINFO, num, buffer, bufferSize );
|
||||
}
|
||||
|
||||
void trap_SetUserinfo( int num, const char *buffer )
|
||||
{
|
||||
syscall( G_SET_USERINFO, num, buffer );
|
||||
}
|
||||
|
||||
void trap_GetServerinfo( char *buffer, int bufferSize )
|
||||
{
|
||||
syscall( G_GET_SERVERINFO, buffer, bufferSize );
|
||||
}
|
||||
|
||||
void trap_SetBrushModel( gentity_t *ent, const char *name )
|
||||
{
|
||||
syscall( G_SET_BRUSH_MODEL, ent, name );
|
||||
}
|
||||
|
||||
void trap_Trace( trace_t *results, const vec3_t start, const vec3_t mins,
|
||||
const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask )
|
||||
{
|
||||
syscall( G_TRACE, results, start, mins, maxs, end, passEntityNum, contentmask );
|
||||
}
|
||||
|
||||
void trap_TraceCapsule( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask )
|
||||
{
|
||||
syscall( G_TRACECAPSULE, results, start, mins, maxs, end, passEntityNum, contentmask );
|
||||
}
|
||||
|
||||
int trap_PointContents( const vec3_t point, int passEntityNum )
|
||||
{
|
||||
return syscall( G_POINT_CONTENTS, point, passEntityNum );
|
||||
}
|
||||
|
||||
|
||||
qboolean trap_InPVS( const vec3_t p1, const vec3_t p2 )
|
||||
{
|
||||
return syscall( G_IN_PVS, p1, p2 );
|
||||
}
|
||||
|
||||
qboolean trap_InPVSIgnorePortals( const vec3_t p1, const vec3_t p2 )
|
||||
{
|
||||
return syscall( G_IN_PVS_IGNORE_PORTALS, p1, p2 );
|
||||
}
|
||||
|
||||
void trap_AdjustAreaPortalState( gentity_t *ent, qboolean open )
|
||||
{
|
||||
syscall( G_ADJUST_AREA_PORTAL_STATE, ent, open );
|
||||
}
|
||||
|
||||
qboolean trap_AreasConnected( int area1, int area2 )
|
||||
{
|
||||
return syscall( G_AREAS_CONNECTED, area1, area2 );
|
||||
}
|
||||
|
||||
void trap_LinkEntity( gentity_t *ent )
|
||||
{
|
||||
syscall( G_LINKENTITY, ent );
|
||||
}
|
||||
|
||||
void trap_UnlinkEntity( gentity_t *ent )
|
||||
{
|
||||
syscall( G_UNLINKENTITY, ent );
|
||||
}
|
||||
|
||||
|
||||
int trap_EntitiesInBox( const vec3_t mins, const vec3_t maxs, int *list, int maxcount )
|
||||
{
|
||||
return syscall( G_ENTITIES_IN_BOX, mins, maxs, list, maxcount );
|
||||
}
|
||||
|
||||
qboolean trap_EntityContact( const vec3_t mins, const vec3_t maxs, const gentity_t *ent )
|
||||
{
|
||||
return syscall( G_ENTITY_CONTACT, mins, maxs, ent );
|
||||
}
|
||||
|
||||
qboolean trap_EntityContactCapsule( const vec3_t mins, const vec3_t maxs, const gentity_t *ent )
|
||||
{
|
||||
return syscall( G_ENTITY_CONTACTCAPSULE, mins, maxs, ent );
|
||||
}
|
||||
|
||||
void trap_GetUsercmd( int clientNum, usercmd_t *cmd )
|
||||
{
|
||||
syscall( G_GET_USERCMD, clientNum, cmd );
|
||||
}
|
||||
|
||||
qboolean trap_GetEntityToken( char *buffer, int bufferSize )
|
||||
{
|
||||
return syscall( G_GET_ENTITY_TOKEN, buffer, bufferSize );
|
||||
}
|
||||
|
||||
int trap_RealTime( qtime_t *qtime )
|
||||
{
|
||||
return syscall( G_REAL_TIME, qtime );
|
||||
}
|
||||
|
||||
void trap_SnapVector( float *v )
|
||||
{
|
||||
syscall( G_SNAPVECTOR, v );
|
||||
return;
|
||||
}
|
||||
|
||||
void trap_SendGameStat( const char *data )
|
||||
{
|
||||
syscall( G_SEND_GAMESTAT, data );
|
||||
return;
|
||||
}
|
||||
|
||||
int trap_Parse_AddGlobalDefine( char *define )
|
||||
{
|
||||
return syscall( G_PARSE_ADD_GLOBAL_DEFINE, define );
|
||||
}
|
||||
|
||||
int trap_Parse_LoadSource( const char *filename )
|
||||
{
|
||||
return syscall( G_PARSE_LOAD_SOURCE, filename );
|
||||
}
|
||||
|
||||
int trap_Parse_FreeSource( int handle )
|
||||
{
|
||||
return syscall( G_PARSE_FREE_SOURCE, handle );
|
||||
}
|
||||
|
||||
int trap_Parse_ReadToken( int handle, pc_token_t *pc_token )
|
||||
{
|
||||
return syscall( G_PARSE_READ_TOKEN, handle, pc_token );
|
||||
}
|
||||
|
||||
int trap_Parse_SourceFileAndLine( int handle, char *filename, int *line )
|
||||
{
|
||||
return syscall( G_PARSE_SOURCE_FILE_AND_LINE, handle, filename, line );
|
||||
}
|
||||
|
||||
@@ -0,0 +1,472 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_delay (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
"wait" seconds to pause before firing targets.
|
||||
"random" delay variance, total delay = delay +/- random seconds
|
||||
*/
|
||||
void Think_Target_Delay( gentity_t *ent )
|
||||
{
|
||||
G_UseTargets( ent, ent->activator );
|
||||
}
|
||||
|
||||
void Use_Target_Delay( gentity_t *ent, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
ent->nextthink = level.time + ( ent->wait + ent->random * crandom( ) ) * 1000;
|
||||
ent->think = Think_Target_Delay;
|
||||
ent->activator = activator;
|
||||
}
|
||||
|
||||
void SP_target_delay( gentity_t *ent )
|
||||
{
|
||||
// check delay for backwards compatability
|
||||
if( !G_SpawnFloat( "delay", "0", &ent->wait ) )
|
||||
G_SpawnFloat( "wait", "1", &ent->wait );
|
||||
|
||||
if( !ent->wait )
|
||||
ent->wait = 1;
|
||||
|
||||
ent->use = Use_Target_Delay;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_score (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
"count" number of points to add, default 1
|
||||
|
||||
The activator is given this many points.
|
||||
*/
|
||||
void Use_Target_Score( gentity_t *ent, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
AddScore( activator, ent->count );
|
||||
}
|
||||
|
||||
void SP_target_score( gentity_t *ent )
|
||||
{
|
||||
if( !ent->count )
|
||||
ent->count = 1;
|
||||
|
||||
ent->use = Use_Target_Score;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_print (1 0 0) (-8 -8 -8) (8 8 8) humanteam alienteam private
|
||||
"message" text to print
|
||||
If "private", only the activator gets the message. If no checks, all clients get the message.
|
||||
*/
|
||||
void Use_Target_Print( gentity_t *ent, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
if( activator->client && ( ent->spawnflags & 4 ) )
|
||||
{
|
||||
trap_SendServerCommand( activator-g_entities, va( "cp \"%s\"", ent->message ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if( ent->spawnflags & 3 )
|
||||
{
|
||||
if( ent->spawnflags & 1 )
|
||||
G_TeamCommand( PTE_HUMANS, va( "cp \"%s\"", ent->message ) );
|
||||
if( ent->spawnflags & 2 )
|
||||
G_TeamCommand( PTE_ALIENS, va( "cp \"%s\"", ent->message ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
trap_SendServerCommand( -1, va("cp \"%s\"", ent->message ) );
|
||||
}
|
||||
|
||||
void SP_target_print( gentity_t *ent )
|
||||
{
|
||||
ent->use = Use_Target_Print;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
|
||||
|
||||
/*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off global activator
|
||||
"noise" wav file to play
|
||||
|
||||
A global sound will play full volume throughout the level.
|
||||
Activator sounds will play on the player that activated the target.
|
||||
Global and activator sounds can't be combined with looping.
|
||||
Normal sounds play each time the target is used.
|
||||
Looped sounds will be toggled by use functions.
|
||||
Multiple identical looping sounds will just increase volume without any speed cost.
|
||||
"wait" : Seconds between auto triggerings, 0 = don't auto trigger
|
||||
"random" wait variance, default is 0
|
||||
*/
|
||||
void Use_Target_Speaker( gentity_t *ent, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
if( ent->spawnflags & 3 )
|
||||
{ // looping sound toggles
|
||||
if( ent->s.loopSound )
|
||||
ent->s.loopSound = 0; // turn it off
|
||||
else
|
||||
ent->s.loopSound = ent->noise_index; // start it
|
||||
}
|
||||
else
|
||||
{
|
||||
// normal sound
|
||||
if( ent->spawnflags & 8 )
|
||||
G_AddEvent( activator, EV_GENERAL_SOUND, ent->noise_index );
|
||||
else if( ent->spawnflags & 4 )
|
||||
G_AddEvent( ent, EV_GLOBAL_SOUND, ent->noise_index );
|
||||
else
|
||||
G_AddEvent( ent, EV_GENERAL_SOUND, ent->noise_index );
|
||||
}
|
||||
}
|
||||
|
||||
void SP_target_speaker( gentity_t *ent )
|
||||
{
|
||||
char buffer[ MAX_QPATH ];
|
||||
char *s;
|
||||
|
||||
G_SpawnFloat( "wait", "0", &ent->wait );
|
||||
G_SpawnFloat( "random", "0", &ent->random );
|
||||
|
||||
if( !G_SpawnString( "noise", "NOSOUND", &s ) )
|
||||
G_Error( "target_speaker without a noise key at %s", vtos( ent->s.origin ) );
|
||||
|
||||
// force all client reletive sounds to be "activator" speakers that
|
||||
// play on the entity that activates it
|
||||
if( s[ 0 ] == '*' )
|
||||
ent->spawnflags |= 8;
|
||||
|
||||
if( !strstr( s, ".wav" ) )
|
||||
Com_sprintf( buffer, sizeof( buffer ), "%s.wav", s );
|
||||
else
|
||||
Q_strncpyz( buffer, s, sizeof( buffer ) );
|
||||
|
||||
ent->noise_index = G_SoundIndex( buffer );
|
||||
|
||||
// a repeating speaker can be done completely client side
|
||||
ent->s.eType = ET_SPEAKER;
|
||||
ent->s.eventParm = ent->noise_index;
|
||||
ent->s.frame = ent->wait * 10;
|
||||
ent->s.clientNum = ent->random * 10;
|
||||
|
||||
|
||||
// check for prestarted looping sound
|
||||
if( ent->spawnflags & 1 )
|
||||
ent->s.loopSound = ent->noise_index;
|
||||
|
||||
ent->use = Use_Target_Speaker;
|
||||
|
||||
if( ent->spawnflags & 4 )
|
||||
ent->r.svFlags |= SVF_BROADCAST;
|
||||
|
||||
VectorCopy( ent->s.origin, ent->s.pos.trBase );
|
||||
|
||||
// must link the entity so we get areas and clusters so
|
||||
// the server can determine who to send updates to
|
||||
trap_LinkEntity( ent );
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
void target_teleporter_use( gentity_t *self, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
gentity_t *dest;
|
||||
|
||||
if( !activator->client )
|
||||
return;
|
||||
|
||||
dest = G_PickTarget( self->target );
|
||||
|
||||
if( !dest )
|
||||
{
|
||||
G_Printf( "Couldn't find teleporter destination\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
TeleportPlayer( activator, dest->s.origin, dest->s.angles );
|
||||
}
|
||||
|
||||
/*QUAKED target_teleporter (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
The activator will be teleported away.
|
||||
*/
|
||||
void SP_target_teleporter( gentity_t *self )
|
||||
{
|
||||
if( !self->targetname )
|
||||
G_Printf( "untargeted %s at %s\n", self->classname, vtos( self->s.origin ) );
|
||||
|
||||
self->use = target_teleporter_use;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
|
||||
/*QUAKED target_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) RED_ONLY BLUE_ONLY RANDOM
|
||||
This doesn't perform any actions except fire its targets.
|
||||
The activator can be forced to be from a certain team.
|
||||
if RANDOM is checked, only one of the targets will be fired, not all of them
|
||||
*/
|
||||
void target_relay_use( gentity_t *self, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
if( ( self->spawnflags & 1 ) && activator->client &&
|
||||
activator->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS )
|
||||
return;
|
||||
|
||||
if( ( self->spawnflags & 2 ) && activator->client &&
|
||||
activator->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS )
|
||||
return;
|
||||
|
||||
if( self->spawnflags & 4 )
|
||||
{
|
||||
gentity_t *ent;
|
||||
|
||||
ent = G_PickTarget( self->target );
|
||||
if( ent && ent->use )
|
||||
ent->use( ent, self, activator );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
G_UseTargets( self, activator );
|
||||
}
|
||||
|
||||
void SP_target_relay( gentity_t *self )
|
||||
{
|
||||
self->use = target_relay_use;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_kill (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
||||
Kills the activator.
|
||||
*/
|
||||
void target_kill_use( gentity_t *self, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
G_Damage( activator, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
|
||||
}
|
||||
|
||||
void SP_target_kill( gentity_t *self )
|
||||
{
|
||||
self->use = target_kill_use;
|
||||
}
|
||||
|
||||
/*QUAKED target_position (0 0.5 0) (-4 -4 -4) (4 4 4)
|
||||
Used as a positional target for in-game calculation, like jumppad targets.
|
||||
*/
|
||||
void SP_target_position( gentity_t *self )
|
||||
{
|
||||
G_SetOrigin( self, self->s.origin );
|
||||
}
|
||||
|
||||
static void target_location_linkup( gentity_t *ent )
|
||||
{
|
||||
int i;
|
||||
int n;
|
||||
|
||||
if( level.locationLinked )
|
||||
return;
|
||||
|
||||
level.locationLinked = qtrue;
|
||||
|
||||
level.locationHead = NULL;
|
||||
|
||||
trap_SetConfigstring( CS_LOCATIONS, "unknown" );
|
||||
|
||||
for( i = 0, ent = g_entities, n = 1; i < level.num_entities; i++, ent++)
|
||||
{
|
||||
if( ent->classname && !Q_stricmp( ent->classname, "target_location" ) )
|
||||
{
|
||||
// lets overload some variables!
|
||||
ent->health = n; // use for location marking
|
||||
trap_SetConfigstring( CS_LOCATIONS + n, ent->message );
|
||||
n++;
|
||||
ent->nextTrain = level.locationHead;
|
||||
level.locationHead = ent;
|
||||
}
|
||||
}
|
||||
// All linked together now
|
||||
}
|
||||
|
||||
/*QUAKED target_location (0 0.5 0) (-8 -8 -8) (8 8 8)
|
||||
Set "message" to the name of this location.
|
||||
Set "count" to 0-7 for color.
|
||||
0:white 1:red 2:green 3:yellow 4:blue 5:cyan 6:magenta 7:white
|
||||
|
||||
Closest target_location in sight used for the location, if none
|
||||
in site, closest in distance
|
||||
*/
|
||||
void SP_target_location( gentity_t *self )
|
||||
{
|
||||
self->think = target_location_linkup;
|
||||
self->nextthink = level.time + 200; // Let them all spawn first
|
||||
|
||||
G_SetOrigin( self, self->s.origin );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
target_rumble_think
|
||||
===============
|
||||
*/
|
||||
void target_rumble_think( gentity_t *self )
|
||||
{
|
||||
int i;
|
||||
gentity_t *ent;
|
||||
|
||||
if( self->last_move_time < level.time )
|
||||
self->last_move_time = level.time + 0.5;
|
||||
|
||||
for( i = 0, ent = g_entities + i; i < level.num_entities; i++, ent++ )
|
||||
{
|
||||
if( !ent->inuse )
|
||||
continue;
|
||||
|
||||
if( !ent->client )
|
||||
continue;
|
||||
|
||||
if( ent->client->ps.groundEntityNum == ENTITYNUM_NONE )
|
||||
continue;
|
||||
|
||||
ent->client->ps.groundEntityNum = ENTITYNUM_NONE;
|
||||
ent->client->ps.velocity[ 0 ] += crandom( ) * 150;
|
||||
ent->client->ps.velocity[ 1 ] += crandom( ) * 150;
|
||||
ent->client->ps.velocity[ 2 ] = self->speed;
|
||||
}
|
||||
|
||||
if( level.time < self->timestamp )
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
target_rumble_use
|
||||
===============
|
||||
*/
|
||||
void target_rumble_use( gentity_t *self, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
self->timestamp = level.time + ( self->count * FRAMETIME );
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
self->activator = activator;
|
||||
self->last_move_time = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SP_target_rumble
|
||||
===============
|
||||
*/
|
||||
void SP_target_rumble( gentity_t *self )
|
||||
{
|
||||
if( !self->targetname )
|
||||
{
|
||||
G_Printf( S_COLOR_YELLOW "WARNING: untargeted %s at %s\n", self->classname,
|
||||
vtos( self->s.origin ) );
|
||||
}
|
||||
|
||||
if( !self->count )
|
||||
self->count = 10;
|
||||
|
||||
if( !self->speed )
|
||||
self->speed = 100;
|
||||
|
||||
self->think = target_rumble_think;
|
||||
self->use = target_rumble_use;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
target_alien_win_use
|
||||
===============
|
||||
*/
|
||||
void target_alien_win_use( gentity_t *self, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
level.uncondAlienWin = qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SP_target_alien_win
|
||||
===============
|
||||
*/
|
||||
void SP_target_alien_win( gentity_t *self )
|
||||
{
|
||||
self->use = target_alien_win_use;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
target_human_win_use
|
||||
===============
|
||||
*/
|
||||
void target_human_win_use( gentity_t *self, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
level.uncondHumanWin = qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SP_target_human_win
|
||||
===============
|
||||
*/
|
||||
void SP_target_human_win( gentity_t *self )
|
||||
{
|
||||
self->use = target_human_win_use;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
target_hurt_use
|
||||
===============
|
||||
*/
|
||||
void target_hurt_use( gentity_t *self, gentity_t *other, gentity_t *activator )
|
||||
{
|
||||
// hurt the activator
|
||||
if( !activator->takedamage )
|
||||
return;
|
||||
|
||||
G_Damage( activator, self, self, NULL, NULL, self->damage, 0, MOD_TRIGGER_HURT );
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
SP_target_hurt
|
||||
===============
|
||||
*/
|
||||
void SP_target_hurt( gentity_t *self )
|
||||
{
|
||||
if( !self->targetname )
|
||||
{
|
||||
G_Printf( S_COLOR_YELLOW "WARNING: untargeted %s at %s\n", self->classname,
|
||||
vtos( self->s.origin ) );
|
||||
}
|
||||
|
||||
if( !self->damage )
|
||||
self->damage = 5;
|
||||
|
||||
self->use = target_hurt_use;
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
// NULL for everyone
|
||||
void QDECL PrintMsg( gentity_t *ent, const char *fmt, ... )
|
||||
{
|
||||
char msg[ 1024 ];
|
||||
va_list argptr;
|
||||
char *p;
|
||||
|
||||
va_start( argptr,fmt );
|
||||
|
||||
if( vsprintf( msg, fmt, argptr ) > sizeof( msg ) )
|
||||
G_Error ( "PrintMsg overrun" );
|
||||
|
||||
va_end( argptr );
|
||||
|
||||
// double quotes are bad
|
||||
while( ( p = strchr( msg, '"' ) ) != NULL )
|
||||
*p = '\'';
|
||||
|
||||
trap_SendServerCommand( ( ( ent == NULL ) ? -1 : ent-g_entities ), va( "print \"%s\"", msg ) );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
OnSameTeam
|
||||
==============
|
||||
*/
|
||||
qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 )
|
||||
{
|
||||
if( !ent1->client || !ent2->client )
|
||||
return qfalse;
|
||||
|
||||
if( ent1->client->pers.teamSelection == ent2->client->pers.teamSelection )
|
||||
return qtrue;
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
Team_GetLocation
|
||||
|
||||
Report a location for the player. Uses placed nearby target_location entities
|
||||
============
|
||||
*/
|
||||
gentity_t *Team_GetLocation( gentity_t *ent )
|
||||
{
|
||||
gentity_t *eloc, *best;
|
||||
float bestlen, len;
|
||||
vec3_t origin;
|
||||
|
||||
best = NULL;
|
||||
bestlen = 3.0f * 8192.0f * 8192.0f;
|
||||
|
||||
VectorCopy( ent->r.currentOrigin, origin );
|
||||
|
||||
for( eloc = level.locationHead; eloc; eloc = eloc->nextTrain )
|
||||
{
|
||||
len = ( origin[ 0 ] - eloc->r.currentOrigin[ 0 ] ) * ( origin[ 0 ] - eloc->r.currentOrigin[ 0 ] )
|
||||
+ ( origin[ 1 ] - eloc->r.currentOrigin[ 1 ] ) * ( origin[ 1 ] - eloc->r.currentOrigin[ 1 ] )
|
||||
+ ( origin[ 2 ] - eloc->r.currentOrigin[ 2 ] ) * ( origin[ 2 ] - eloc->r.currentOrigin[ 2 ] );
|
||||
|
||||
if( len > bestlen )
|
||||
continue;
|
||||
|
||||
if( !trap_InPVS( origin, eloc->r.currentOrigin ) )
|
||||
continue;
|
||||
|
||||
bestlen = len;
|
||||
best = eloc;
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===========
|
||||
Team_GetLocationMsg
|
||||
|
||||
Report a location message for the player. Uses placed nearby target_location entities
|
||||
============
|
||||
*/
|
||||
qboolean Team_GetLocationMsg( gentity_t *ent, char *loc, int loclen )
|
||||
{
|
||||
gentity_t *best;
|
||||
|
||||
best = Team_GetLocation( ent );
|
||||
|
||||
if( !best )
|
||||
return qfalse;
|
||||
|
||||
if( best->count )
|
||||
{
|
||||
if( best->count < 0 )
|
||||
best->count = 0;
|
||||
|
||||
if( best->count > 7 )
|
||||
best->count = 7;
|
||||
|
||||
Com_sprintf( loc, loclen, "%c%c%s" S_COLOR_WHITE, Q_COLOR_ESCAPE, best->count + '0', best->message );
|
||||
}
|
||||
else
|
||||
Com_sprintf( loc, loclen, "%s", best->message );
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
static int QDECL SortClients( const void *a, const void *b )
|
||||
{
|
||||
return *(int *)a - *(int *)b;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
TeamplayLocationsMessage
|
||||
|
||||
Format:
|
||||
clientNum location health armor weapon powerups
|
||||
|
||||
==================
|
||||
*/
|
||||
void TeamplayInfoMessage( gentity_t *ent )
|
||||
{
|
||||
char entry[ 1024 ];
|
||||
char string[ 8192 ];
|
||||
int stringlength;
|
||||
int i, j;
|
||||
gentity_t *player;
|
||||
int cnt;
|
||||
int h, a = 0;
|
||||
int clients[ TEAM_MAXOVERLAY ];
|
||||
|
||||
if( ! ent->client->pers.teamInfo )
|
||||
return;
|
||||
|
||||
// figure out what client should be on the display
|
||||
// we are limited to 8, but we want to use the top eight players
|
||||
// but in client order (so they don't keep changing position on the overlay)
|
||||
for( i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++ )
|
||||
{
|
||||
player = g_entities + level.sortedClients[ i ];
|
||||
|
||||
if( player->inuse && player->client->sess.sessionTeam ==
|
||||
ent->client->sess.sessionTeam )
|
||||
clients[ cnt++ ] = level.sortedClients[ i ];
|
||||
}
|
||||
|
||||
// We have the top eight players, sort them by clientNum
|
||||
qsort( clients, cnt, sizeof( clients[ 0 ] ), SortClients );
|
||||
|
||||
// send the latest information on all clients
|
||||
string[ 0 ] = 0;
|
||||
stringlength = 0;
|
||||
|
||||
for( i = 0, cnt = 0; i < g_maxclients.integer && cnt < TEAM_MAXOVERLAY; i++)
|
||||
{
|
||||
player = g_entities + i;
|
||||
|
||||
if( player->inuse && player->client->sess.sessionTeam ==
|
||||
ent->client->sess.sessionTeam )
|
||||
{
|
||||
h = player->client->ps.stats[ STAT_HEALTH ];
|
||||
|
||||
if( h < 0 )
|
||||
h = 0;
|
||||
|
||||
Com_sprintf( entry, sizeof( entry ),
|
||||
" %i %i %i %i %i %i",
|
||||
// level.sortedClients[i], player->client->pers.teamState.location, h, a,
|
||||
i, player->client->pers.teamState.location, h, a,
|
||||
player->client->ps.weapon, player->s.powerups );
|
||||
|
||||
j = strlen( entry );
|
||||
|
||||
if( stringlength + j > sizeof( string ) )
|
||||
break;
|
||||
|
||||
strcpy( string + stringlength, entry );
|
||||
stringlength += j;
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
trap_SendServerCommand( ent - g_entities, va( "tinfo %i %s", cnt, string ) );
|
||||
}
|
||||
|
||||
void CheckTeamStatus( void )
|
||||
{
|
||||
int i;
|
||||
gentity_t *loc, *ent;
|
||||
|
||||
if( level.time - level.lastTeamLocationTime > TEAM_LOCATION_UPDATE_TIME )
|
||||
{
|
||||
level.lastTeamLocationTime = level.time;
|
||||
|
||||
for( i = 0; i < g_maxclients.integer; i++ )
|
||||
{
|
||||
ent = g_entities + i;
|
||||
if( ent->client->pers.connected != CON_CONNECTED )
|
||||
continue;
|
||||
|
||||
if( ent->inuse && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ||
|
||||
ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) )
|
||||
{
|
||||
|
||||
loc = Team_GetLocation( ent );
|
||||
|
||||
if( loc )
|
||||
ent->client->pers.teamState.location = loc->health;
|
||||
else
|
||||
ent->client->pers.teamState.location = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for( i = 0; i < g_maxclients.integer; i++ )
|
||||
{
|
||||
ent = g_entities + i;
|
||||
if( ent->client->pers.connected != CON_CONNECTED )
|
||||
continue;
|
||||
|
||||
if( ent->inuse && ( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ||
|
||||
ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) )
|
||||
TeamplayInfoMessage( ent );
|
||||
}
|
||||
}
|
||||
|
||||
//Warn on unbalanced teams
|
||||
if ( g_teamImbalanceWarnings.integer && !level.intermissiontime && level.time - level.lastTeamUnbalancedTime > ( g_teamImbalanceWarnings.integer * 1000 ) && level.numTeamWarnings<3 )
|
||||
{
|
||||
level.lastTeamUnbalancedTime = level.time;
|
||||
if (level.numAlienSpawns > 0 && level.numHumanClients - level.numAlienClients > 2)
|
||||
{
|
||||
trap_SendServerCommand (-1, "print \"Teams are unbalanced. Humans have more players.\n Humans will keep their points when switching teams.\n\"");
|
||||
level.numTeamWarnings++;
|
||||
}
|
||||
else if (level.numHumanSpawns > 0 && level.numAlienClients - level.numHumanClients > 2)
|
||||
{
|
||||
trap_SendServerCommand (-1, "print \"Teams are unbalanced. Aliens have more players.\n Aliens will keep their points when switching teams.\n\"");
|
||||
level.numTeamWarnings++;
|
||||
}
|
||||
else
|
||||
{
|
||||
level.numTeamWarnings = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,848 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
// g_utils.c -- misc utility functions for game module
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char oldShader[ MAX_QPATH ];
|
||||
char newShader[ MAX_QPATH ];
|
||||
float timeOffset;
|
||||
} shaderRemap_t;
|
||||
|
||||
#define MAX_SHADER_REMAPS 128
|
||||
|
||||
int remapCount = 0;
|
||||
shaderRemap_t remappedShaders[ MAX_SHADER_REMAPS ];
|
||||
|
||||
void AddRemap( const char *oldShader, const char *newShader, float timeOffset )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < remapCount; i++ )
|
||||
{
|
||||
if( Q_stricmp( oldShader, remappedShaders[ i ].oldShader ) == 0 )
|
||||
{
|
||||
// found it, just update this one
|
||||
strcpy( remappedShaders[ i ].newShader,newShader );
|
||||
remappedShaders[ i ].timeOffset = timeOffset;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( remapCount < MAX_SHADER_REMAPS )
|
||||
{
|
||||
strcpy( remappedShaders[ remapCount ].newShader,newShader );
|
||||
strcpy( remappedShaders[ remapCount ].oldShader,oldShader );
|
||||
remappedShaders[ remapCount ].timeOffset = timeOffset;
|
||||
remapCount++;
|
||||
}
|
||||
}
|
||||
|
||||
const char *BuildShaderStateConfig( void )
|
||||
{
|
||||
static char buff[ MAX_STRING_CHARS * 4 ];
|
||||
char out[ ( MAX_QPATH * 2 ) + 5 ];
|
||||
int i;
|
||||
|
||||
memset( buff, 0, MAX_STRING_CHARS );
|
||||
|
||||
for( i = 0; i < remapCount; i++ )
|
||||
{
|
||||
Com_sprintf( out, ( MAX_QPATH * 2 ) + 5, "%s=%s:%5.2f@", remappedShaders[ i ].oldShader,
|
||||
remappedShaders[ i ].newShader, remappedShaders[ i ].timeOffset );
|
||||
Q_strcat( buff, sizeof( buff ), out );
|
||||
}
|
||||
return buff;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=========================================================================
|
||||
|
||||
model / sound configstring indexes
|
||||
|
||||
=========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
================
|
||||
G_FindConfigstringIndex
|
||||
|
||||
================
|
||||
*/
|
||||
int G_FindConfigstringIndex( char *name, int start, int max, qboolean create )
|
||||
{
|
||||
int i;
|
||||
char s[ MAX_STRING_CHARS ];
|
||||
|
||||
if( !name || !name[ 0 ] )
|
||||
return 0;
|
||||
|
||||
for( i = 1; i < max; i++ )
|
||||
{
|
||||
trap_GetConfigstring( start + i, s, sizeof( s ) );
|
||||
if( !s[ 0 ] )
|
||||
break;
|
||||
|
||||
if( !strcmp( s, name ) )
|
||||
return i;
|
||||
}
|
||||
|
||||
if( !create )
|
||||
return 0;
|
||||
|
||||
if( i == max )
|
||||
G_Error( "G_FindConfigstringIndex: overflow" );
|
||||
|
||||
trap_SetConfigstring( start + i, name );
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
//TA: added ParticleSystemIndex
|
||||
int G_ParticleSystemIndex( char *name )
|
||||
{
|
||||
return G_FindConfigstringIndex( name, CS_PARTICLE_SYSTEMS, MAX_GAME_PARTICLE_SYSTEMS, qtrue );
|
||||
}
|
||||
|
||||
//TA: added ShaderIndex
|
||||
int G_ShaderIndex( char *name )
|
||||
{
|
||||
return G_FindConfigstringIndex( name, CS_SHADERS, MAX_GAME_SHADERS, qtrue );
|
||||
}
|
||||
|
||||
int G_ModelIndex( char *name )
|
||||
{
|
||||
return G_FindConfigstringIndex( name, CS_MODELS, MAX_MODELS, qtrue );
|
||||
}
|
||||
|
||||
int G_SoundIndex( char *name )
|
||||
{
|
||||
return G_FindConfigstringIndex( name, CS_SOUNDS, MAX_SOUNDS, qtrue );
|
||||
}
|
||||
|
||||
//=====================================================================
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
G_TeamCommand
|
||||
|
||||
Broadcasts a command to only a specific team
|
||||
================
|
||||
*/
|
||||
void G_TeamCommand( pTeam_t team, char *cmd )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0 ; i < level.maxclients ; i++ )
|
||||
{
|
||||
if( level.clients[ i ].pers.connected == CON_CONNECTED )
|
||||
{
|
||||
if( level.clients[ i ].pers.teamSelection == team ||
|
||||
( level.clients[ i ].pers.teamSelection == PTE_NONE &&
|
||||
G_admin_permission( &g_entities[ i ], ADMF_SPEC_ALLCHAT ) ) )
|
||||
trap_SendServerCommand( i, cmd );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
G_Find
|
||||
|
||||
Searches all active entities for the next one that holds
|
||||
the matching string at fieldofs (use the FOFS() macro) in the structure.
|
||||
|
||||
Searches beginning at the entity after from, or the beginning if NULL
|
||||
NULL will be returned if the end of the list is reached.
|
||||
|
||||
=============
|
||||
*/
|
||||
gentity_t *G_Find( gentity_t *from, int fieldofs, const char *match )
|
||||
{
|
||||
char *s;
|
||||
|
||||
if( !from )
|
||||
from = g_entities;
|
||||
else
|
||||
from++;
|
||||
|
||||
for( ; from < &g_entities[ level.num_entities ]; from++ )
|
||||
{
|
||||
if( !from->inuse )
|
||||
continue;
|
||||
s = *(char **)( (byte *)from + fieldofs );
|
||||
|
||||
if( !s )
|
||||
continue;
|
||||
|
||||
if( !Q_stricmp( s, match ) )
|
||||
return from;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
G_PickTarget
|
||||
|
||||
Selects a random entity from among the targets
|
||||
=============
|
||||
*/
|
||||
#define MAXCHOICES 32
|
||||
|
||||
gentity_t *G_PickTarget( char *targetname )
|
||||
{
|
||||
gentity_t *ent = NULL;
|
||||
int num_choices = 0;
|
||||
gentity_t *choice[ MAXCHOICES ];
|
||||
|
||||
if( !targetname )
|
||||
{
|
||||
G_Printf("G_PickTarget called with NULL targetname\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
ent = G_Find( ent, FOFS( targetname ), targetname );
|
||||
|
||||
if( !ent )
|
||||
break;
|
||||
|
||||
choice[ num_choices++ ] = ent;
|
||||
|
||||
if( num_choices == MAXCHOICES )
|
||||
break;
|
||||
}
|
||||
|
||||
if( !num_choices )
|
||||
{
|
||||
G_Printf( "G_PickTarget: target %s not found\n", targetname );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return choice[ rand( ) % num_choices ];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============================
|
||||
G_UseTargets
|
||||
|
||||
"activator" should be set to the entity that initiated the firing.
|
||||
|
||||
Search for (string)targetname in all entities that
|
||||
match (string)self.target and call their .use function
|
||||
|
||||
==============================
|
||||
*/
|
||||
void G_UseTargets( gentity_t *ent, gentity_t *activator )
|
||||
{
|
||||
gentity_t *t;
|
||||
|
||||
if( !ent )
|
||||
return;
|
||||
|
||||
if( ent->targetShaderName && ent->targetShaderNewName )
|
||||
{
|
||||
float f = level.time * 0.001;
|
||||
AddRemap( ent->targetShaderName, ent->targetShaderNewName, f );
|
||||
trap_SetConfigstring( CS_SHADERSTATE, BuildShaderStateConfig( ) );
|
||||
}
|
||||
|
||||
if( !ent->target )
|
||||
return;
|
||||
|
||||
t = NULL;
|
||||
while( ( t = G_Find( t, FOFS( targetname ), ent->target ) ) != NULL )
|
||||
{
|
||||
if( t == ent )
|
||||
G_Printf( "WARNING: Entity used itself.\n" );
|
||||
else
|
||||
{
|
||||
if( t->use )
|
||||
t->use( t, ent, activator );
|
||||
}
|
||||
|
||||
if( !ent->inuse )
|
||||
{
|
||||
G_Printf( "entity was removed while using targets\n" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
TempVector
|
||||
|
||||
This is just a convenience function
|
||||
for making temporary vectors for function calls
|
||||
=============
|
||||
*/
|
||||
float *tv( float x, float y, float z )
|
||||
{
|
||||
static int index;
|
||||
static vec3_t vecs[ 8 ];
|
||||
float *v;
|
||||
|
||||
// use an array so that multiple tempvectors won't collide
|
||||
// for a while
|
||||
v = vecs[ index ];
|
||||
index = ( index + 1 ) & 7;
|
||||
|
||||
v[ 0 ] = x;
|
||||
v[ 1 ] = y;
|
||||
v[ 2 ] = z;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
VectorToString
|
||||
|
||||
This is just a convenience function
|
||||
for printing vectors
|
||||
=============
|
||||
*/
|
||||
char *vtos( const vec3_t v )
|
||||
{
|
||||
static int index;
|
||||
static char str[ 8 ][ 32 ];
|
||||
char *s;
|
||||
|
||||
// use an array so that multiple vtos won't collide
|
||||
s = str[ index ];
|
||||
index = ( index + 1 ) & 7;
|
||||
|
||||
Com_sprintf( s, 32, "(%i %i %i)", (int)v[ 0 ], (int)v[ 1 ], (int)v[ 2 ] );
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
G_SetMovedir
|
||||
|
||||
The editor only specifies a single value for angles (yaw),
|
||||
but we have special constants to generate an up or down direction.
|
||||
Angles will be cleared, because it is being used to represent a direction
|
||||
instead of an orientation.
|
||||
===============
|
||||
*/
|
||||
void G_SetMovedir( vec3_t angles, vec3_t movedir )
|
||||
{
|
||||
static vec3_t VEC_UP = { 0, -1, 0 };
|
||||
static vec3_t MOVEDIR_UP = { 0, 0, 1 };
|
||||
static vec3_t VEC_DOWN = { 0, -2, 0 };
|
||||
static vec3_t MOVEDIR_DOWN = { 0, 0, -1 };
|
||||
|
||||
if( VectorCompare( angles, VEC_UP ) )
|
||||
VectorCopy( MOVEDIR_UP, movedir );
|
||||
else if( VectorCompare( angles, VEC_DOWN ) )
|
||||
VectorCopy( MOVEDIR_DOWN, movedir );
|
||||
else
|
||||
AngleVectors( angles, movedir, NULL, NULL );
|
||||
|
||||
VectorClear( angles );
|
||||
}
|
||||
|
||||
|
||||
float vectoyaw( const vec3_t vec )
|
||||
{
|
||||
float yaw;
|
||||
|
||||
if( vec[ YAW ] == 0 && vec[ PITCH ] == 0 )
|
||||
{
|
||||
yaw = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( vec[ PITCH ] )
|
||||
yaw = ( atan2( vec[ YAW ], vec[ PITCH ] ) * 180 / M_PI );
|
||||
else if( vec[ YAW ] > 0 )
|
||||
yaw = 90;
|
||||
else
|
||||
yaw = 270;
|
||||
|
||||
if( yaw < 0 )
|
||||
yaw += 360;
|
||||
}
|
||||
|
||||
return yaw;
|
||||
}
|
||||
|
||||
|
||||
void G_InitGentity( gentity_t *e )
|
||||
{
|
||||
e->inuse = qtrue;
|
||||
e->classname = "noclass";
|
||||
e->s.number = e - g_entities;
|
||||
e->r.ownerNum = ENTITYNUM_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
G_Spawn
|
||||
|
||||
Either finds a free entity, or allocates a new one.
|
||||
|
||||
The slots from 0 to MAX_CLIENTS-1 are always reserved for clients, and will
|
||||
never be used by anything else.
|
||||
|
||||
Try to avoid reusing an entity that was recently freed, because it
|
||||
can cause the client to think the entity morphed into something else
|
||||
instead of being removed and recreated, which can cause interpolated
|
||||
angles and bad trails.
|
||||
=================
|
||||
*/
|
||||
gentity_t *G_Spawn( void )
|
||||
{
|
||||
int i, force;
|
||||
gentity_t *e;
|
||||
|
||||
e = NULL; // shut up warning
|
||||
i = 0; // shut up warning
|
||||
|
||||
for( force = 0; force < 2; force++ )
|
||||
{
|
||||
// if we go through all entities and can't find one to free,
|
||||
// override the normal minimum times before use
|
||||
e = &g_entities[ MAX_CLIENTS ];
|
||||
|
||||
for( i = MAX_CLIENTS; i < level.num_entities; i++, e++ )
|
||||
{
|
||||
if( e->inuse )
|
||||
continue;
|
||||
|
||||
// the first couple seconds of server time can involve a lot of
|
||||
// freeing and allocating, so relax the replacement policy
|
||||
if( !force && e->freetime > level.startTime + 2000 && level.time - e->freetime < 1000 )
|
||||
continue;
|
||||
|
||||
// reuse this slot
|
||||
G_InitGentity( e );
|
||||
return e;
|
||||
}
|
||||
|
||||
if( i != MAX_GENTITIES )
|
||||
break;
|
||||
}
|
||||
|
||||
if( i == ENTITYNUM_MAX_NORMAL )
|
||||
{
|
||||
for( i = 0; i < MAX_GENTITIES; i++ )
|
||||
G_Printf( "%4i: %s\n", i, g_entities[ i ].classname );
|
||||
|
||||
G_Error( "G_Spawn: no free entities" );
|
||||
}
|
||||
|
||||
// open up a new slot
|
||||
level.num_entities++;
|
||||
|
||||
// let the server system know that there are more entities
|
||||
trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ),
|
||||
&level.clients[ 0 ].ps, sizeof( level.clients[ 0 ] ) );
|
||||
|
||||
G_InitGentity( e );
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
G_EntitiesFree
|
||||
=================
|
||||
*/
|
||||
qboolean G_EntitiesFree( void )
|
||||
{
|
||||
int i;
|
||||
gentity_t *e;
|
||||
|
||||
e = &g_entities[ MAX_CLIENTS ];
|
||||
|
||||
for( i = MAX_CLIENTS; i < level.num_entities; i++, e++ )
|
||||
{
|
||||
if( e->inuse )
|
||||
continue;
|
||||
|
||||
// slot available
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
G_FreeEntity
|
||||
|
||||
Marks the entity as free
|
||||
=================
|
||||
*/
|
||||
void G_FreeEntity( gentity_t *ent )
|
||||
{
|
||||
trap_UnlinkEntity( ent ); // unlink from world
|
||||
|
||||
if( ent->neverFree )
|
||||
return;
|
||||
|
||||
memset( ent, 0, sizeof( *ent ) );
|
||||
ent->classname = "freent";
|
||||
ent->freetime = level.time;
|
||||
ent->inuse = qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
G_TempEntity
|
||||
|
||||
Spawns an event entity that will be auto-removed
|
||||
The origin will be snapped to save net bandwidth, so care
|
||||
must be taken if the origin is right on a surface (snap towards start vector first)
|
||||
=================
|
||||
*/
|
||||
gentity_t *G_TempEntity( vec3_t origin, int event )
|
||||
{
|
||||
gentity_t *e;
|
||||
vec3_t snapped;
|
||||
|
||||
e = G_Spawn( );
|
||||
e->s.eType = ET_EVENTS + event;
|
||||
|
||||
e->classname = "tempEntity";
|
||||
e->eventTime = level.time;
|
||||
e->freeAfterEvent = qtrue;
|
||||
|
||||
VectorCopy( origin, snapped );
|
||||
SnapVector( snapped ); // save network bandwidth
|
||||
G_SetOrigin( e, snapped );
|
||||
|
||||
// find cluster for PVS
|
||||
trap_LinkEntity( e );
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
Kill box
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
=================
|
||||
G_KillBox
|
||||
|
||||
Kills all entities that would touch the proposed new positioning
|
||||
of ent. Ent should be unlinked before calling this!
|
||||
=================
|
||||
*/
|
||||
void G_KillBox( gentity_t *ent )
|
||||
{
|
||||
int i, num;
|
||||
int touch[ MAX_GENTITIES ];
|
||||
gentity_t *hit;
|
||||
vec3_t mins, maxs;
|
||||
|
||||
VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
|
||||
VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
|
||||
num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
|
||||
|
||||
for( i = 0; i < num; i++ )
|
||||
{
|
||||
hit = &g_entities[ touch[ i ] ];
|
||||
|
||||
if( !hit->client )
|
||||
continue;
|
||||
|
||||
//TA: impossible to telefrag self
|
||||
if( ent == hit )
|
||||
continue;
|
||||
|
||||
// nail it
|
||||
G_Damage( hit, ent, ent, NULL, NULL,
|
||||
100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
/*
|
||||
===============
|
||||
G_AddPredictableEvent
|
||||
|
||||
Use for non-pmove events that would also be predicted on the
|
||||
client side: jumppads and item pickups
|
||||
Adds an event+parm and twiddles the event counter
|
||||
===============
|
||||
*/
|
||||
void G_AddPredictableEvent( gentity_t *ent, int event, int eventParm )
|
||||
{
|
||||
if( !ent->client )
|
||||
return;
|
||||
|
||||
BG_AddPredictableEventToPlayerstate( event, eventParm, &ent->client->ps );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
G_AddEvent
|
||||
|
||||
Adds an event+parm and twiddles the event counter
|
||||
===============
|
||||
*/
|
||||
void G_AddEvent( gentity_t *ent, int event, int eventParm )
|
||||
{
|
||||
int bits;
|
||||
|
||||
if( !event )
|
||||
{
|
||||
G_Printf( "G_AddEvent: zero event added for entity %i\n", ent->s.number );
|
||||
return;
|
||||
}
|
||||
|
||||
// eventParm is converted to uint8_t (0 - 255) in msg.c
|
||||
if( eventParm & ~0xFF )
|
||||
{
|
||||
G_Printf( S_COLOR_YELLOW "WARNING: G_AddEvent: event %d "
|
||||
" eventParm uint8_t overflow (given %d)\n", event, eventParm );
|
||||
}
|
||||
|
||||
// clients need to add the event in playerState_t instead of entityState_t
|
||||
if( ent->client )
|
||||
{
|
||||
bits = ent->client->ps.externalEvent & EV_EVENT_BITS;
|
||||
bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
|
||||
ent->client->ps.externalEvent = event | bits;
|
||||
ent->client->ps.externalEventParm = eventParm;
|
||||
ent->client->ps.externalEventTime = level.time;
|
||||
}
|
||||
else
|
||||
{
|
||||
bits = ent->s.event & EV_EVENT_BITS;
|
||||
bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
|
||||
ent->s.event = event | bits;
|
||||
ent->s.eventParm = eventParm;
|
||||
}
|
||||
|
||||
ent->eventTime = level.time;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
G_BroadcastEvent
|
||||
|
||||
Sends an event to every client
|
||||
===============
|
||||
*/
|
||||
void G_BroadcastEvent( int event, int eventParm )
|
||||
{
|
||||
gentity_t *ent;
|
||||
|
||||
ent = G_TempEntity( vec3_origin, event );
|
||||
ent->s.eventParm = eventParm;
|
||||
ent->r.svFlags = SVF_BROADCAST; // send to everyone
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
G_Sound
|
||||
=============
|
||||
*/
|
||||
void G_Sound( gentity_t *ent, int channel, int soundIndex )
|
||||
{
|
||||
gentity_t *te;
|
||||
|
||||
te = G_TempEntity( ent->r.currentOrigin, EV_GENERAL_SOUND );
|
||||
te->s.eventParm = soundIndex;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
G_ClientIsLagging
|
||||
=============
|
||||
*/
|
||||
qboolean G_ClientIsLagging( gclient_t *client )
|
||||
{
|
||||
if( client )
|
||||
{
|
||||
if( client->ps.ping >= 999 )
|
||||
return qtrue;
|
||||
else
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
return qfalse; //is a non-existant client lagging? woooo zen
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
G_SetOrigin
|
||||
|
||||
Sets the pos trajectory for a fixed position
|
||||
================
|
||||
*/
|
||||
void G_SetOrigin( gentity_t *ent, vec3_t origin )
|
||||
{
|
||||
VectorCopy( origin, ent->s.pos.trBase );
|
||||
ent->s.pos.trType = TR_STATIONARY;
|
||||
ent->s.pos.trTime = 0;
|
||||
ent->s.pos.trDuration = 0;
|
||||
VectorClear( ent->s.pos.trDelta );
|
||||
|
||||
VectorCopy( origin, ent->r.currentOrigin );
|
||||
VectorCopy( origin, ent->s.origin ); //TA: if shit breaks - blame this line
|
||||
}
|
||||
|
||||
//TA: from quakestyle.telefragged.com
|
||||
// (NOBODY): Code helper function
|
||||
//
|
||||
gentity_t *G_FindRadius( gentity_t *from, vec3_t org, float rad )
|
||||
{
|
||||
vec3_t eorg;
|
||||
int j;
|
||||
|
||||
if( !from )
|
||||
from = g_entities;
|
||||
else
|
||||
from++;
|
||||
|
||||
for( ; from < &g_entities[ level.num_entities ]; from++ )
|
||||
{
|
||||
if( !from->inuse )
|
||||
continue;
|
||||
|
||||
for( j = 0; j < 3; j++ )
|
||||
eorg[ j ] = org[ j ] - ( from->r.currentOrigin[ j ] + ( from->r.mins[ j ] + from->r.maxs[ j ] ) * 0.5 );
|
||||
|
||||
if( VectorLength( eorg ) > rad )
|
||||
continue;
|
||||
|
||||
return from;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_Visible
|
||||
|
||||
Test for a LOS between two entities
|
||||
===============
|
||||
*/
|
||||
qboolean G_Visible( gentity_t *ent1, gentity_t *ent2 )
|
||||
{
|
||||
trace_t trace;
|
||||
|
||||
trap_Trace( &trace, ent1->s.pos.trBase, NULL, NULL, ent2->s.pos.trBase, ent1->s.number, MASK_SHOT );
|
||||
|
||||
if( trace.contents & CONTENTS_SOLID )
|
||||
return qfalse;
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_ClosestEnt
|
||||
|
||||
Test a list of entities for the closest to a particular point
|
||||
===============
|
||||
*/
|
||||
gentity_t *G_ClosestEnt( vec3_t origin, gentity_t **entities, int numEntities )
|
||||
{
|
||||
int i;
|
||||
float nd, d = 1000000.0f;
|
||||
gentity_t *closestEnt = NULL;
|
||||
|
||||
for( i = 0; i < numEntities; i++ )
|
||||
{
|
||||
gentity_t *ent = entities[ i ];
|
||||
|
||||
if( ( nd = Distance( origin, ent->s.origin ) ) < d )
|
||||
{
|
||||
d = nd;
|
||||
closestEnt = ent;
|
||||
}
|
||||
}
|
||||
|
||||
return closestEnt;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
G_TriggerMenu
|
||||
|
||||
Trigger a menu on some client
|
||||
===============
|
||||
*/
|
||||
void G_TriggerMenu( int clientNum, dynMenu_t menu )
|
||||
{
|
||||
char buffer[ 32 ];
|
||||
|
||||
Com_sprintf( buffer, 32, "servermenu %d", menu );
|
||||
trap_SendServerCommand( clientNum, buffer );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
G_CloseMenus
|
||||
|
||||
Close all open menus on some client
|
||||
===============
|
||||
*/
|
||||
void G_CloseMenus( int clientNum )
|
||||
{
|
||||
char buffer[ 32 ];
|
||||
|
||||
Com_sprintf( buffer, 32, "serverclosemenus" );
|
||||
trap_SendServerCommand( clientNum, buffer );
|
||||
}
|
||||
+1633
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,625 @@
|
||||
/*
|
||||
===========================================================================
|
||||
Copyright (C) 1999-2005 Id Software, Inc.
|
||||
Copyright (C) 2000-2006 Tim Angus
|
||||
|
||||
This file is part of Tremulous.
|
||||
|
||||
Tremulous 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.
|
||||
|
||||
Tremulous 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 Tremulous; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
===========================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ALIEN weapons
|
||||
*
|
||||
* _REPEAT - time in msec until the weapon can be used again
|
||||
* _DMG - amount of damage the weapon does
|
||||
*
|
||||
* ALIEN_WDMG_MODIFIER - overall damage modifier for coarse tuning
|
||||
*
|
||||
*/
|
||||
|
||||
#define ALIEN_WDMG_MODIFIER 1.0f
|
||||
#define ADM(d) ((int)((float)d*ALIEN_WDMG_MODIFIER))
|
||||
|
||||
#define ABUILDER_BUILD_REPEAT 500
|
||||
#define ABUILDER_CLAW_DMG ADM(20)
|
||||
#define ABUILDER_CLAW_RANGE 64.0f
|
||||
#define ABUILDER_CLAW_WIDTH 4.0f
|
||||
#define ABUILDER_CLAW_REPEAT 1000
|
||||
#define ABUILDER_CLAW_K_SCALE 1.0f
|
||||
#define ABUILDER_BASE_DELAY 17000
|
||||
#define ABUILDER_ADV_DELAY 12000
|
||||
#define ABUILDER_BLOB_DMG ADM(9)
|
||||
#define ABUILDER_BLOB_REPEAT 600
|
||||
#define ABUILDER_BLOB_SPEED 800.0f
|
||||
#define ABUILDER_BLOB_SPEED_MOD 0.5f
|
||||
#define ABUILDER_BLOB_TIME 5000
|
||||
|
||||
#define LEVEL0_BITE_DMG ADM(48)
|
||||
#define LEVEL0_BITE_RANGE 64.0f
|
||||
#define LEVEL0_BITE_WIDTH 6.0f
|
||||
#define LEVEL0_BITE_REPEAT 500
|
||||
#define LEVEL0_BITE_K_SCALE 1.0f
|
||||
|
||||
#define LEVEL1_CLAW_DMG ADM(32)
|
||||
#define LEVEL1_CLAW_RANGE 96.0f
|
||||
#define LEVEL1_CLAW_WIDTH 10.0f
|
||||
#define LEVEL1_CLAW_REPEAT 600
|
||||
#define LEVEL1_CLAW_U_REPEAT 500
|
||||
#define LEVEL1_CLAW_K_SCALE 1.0f
|
||||
#define LEVEL1_CLAW_U_K_SCALE 1.0f
|
||||
#define LEVEL1_GRAB_RANGE 64.0f
|
||||
#define LEVEL1_GRAB_TIME 300
|
||||
#define LEVEL1_GRAB_U_TIME 450
|
||||
#define LEVEL1_PCLOUD_DMG ADM(4)
|
||||
#define LEVEL1_PCLOUD_RANGE 200.0f
|
||||
#define LEVEL1_PCLOUD_REPEAT 2000
|
||||
#define LEVEL1_PCLOUD_TIME 10000
|
||||
|
||||
#define LEVEL2_CLAW_DMG ADM(40)
|
||||
#define LEVEL2_CLAW_RANGE 96.0f
|
||||
#define LEVEL2_CLAW_WIDTH 12.0f
|
||||
#define LEVEL2_CLAW_REPEAT 500
|
||||
#define LEVEL2_CLAW_K_SCALE 1.0f
|
||||
#define LEVEL2_CLAW_U_REPEAT 400
|
||||
#define LEVEL2_CLAW_U_K_SCALE 1.0f
|
||||
#define LEVEL2_AREAZAP_DMG ADM(80)
|
||||
#define LEVEL2_AREAZAP_RANGE 200.0f
|
||||
#define LEVEL2_AREAZAP_WIDTH 15.0f
|
||||
#define LEVEL2_AREAZAP_REPEAT 1500
|
||||
#define LEVEL2_AREAZAP_TIME 1000
|
||||
#define LEVEL2_AREAZAP_MAX_TARGETS 3
|
||||
#define LEVEL2_WALLJUMP_MAXSPEED 1000.0f
|
||||
|
||||
#define LEVEL3_CLAW_DMG ADM(80)
|
||||
#define LEVEL3_CLAW_RANGE 96.0f
|
||||
#define LEVEL3_CLAW_WIDTH 16.0f
|
||||
#define LEVEL3_CLAW_REPEAT 700
|
||||
#define LEVEL3_CLAW_K_SCALE 1.0f
|
||||
#define LEVEL3_CLAW_U_REPEAT 600
|
||||
#define LEVEL3_CLAW_U_K_SCALE 1.0f
|
||||
#define LEVEL3_POUNCE_DMG ADM(100)
|
||||
#define LEVEL3_POUNCE_RANGE 72.0f
|
||||
#define LEVEL3_POUNCE_WIDTH 16.0f
|
||||
#define LEVEL3_POUNCE_SPEED 700
|
||||
#define LEVEL3_POUNCE_UPG_SPEED 800
|
||||
#define LEVEL3_POUNCE_SPEED_MOD 0.75f
|
||||
#define LEVEL3_POUNCE_CHARGE_TIME 700
|
||||
#define LEVEL3_POUNCE_TIME 400
|
||||
#define LEVEL3_BOUNCEBALL_DMG ADM(110)
|
||||
#define LEVEL3_BOUNCEBALL_REPEAT 1000
|
||||
#define LEVEL3_BOUNCEBALL_SPEED 1000.0f
|
||||
|
||||
#define LEVEL4_CLAW_DMG ADM(100)
|
||||
#define LEVEL4_CLAW_RANGE 128.0f
|
||||
#define LEVEL4_CLAW_WIDTH 20.0f
|
||||
#define LEVEL4_CLAW_REPEAT 750
|
||||
#define LEVEL4_CLAW_K_SCALE 1.0f
|
||||
#define LEVEL4_REGEN_RANGE 200.0f
|
||||
#define LEVEL4_REGEN_MOD 2.0f
|
||||
#define LEVEL4_CHARGE_SPEED 2.0f
|
||||
#define LEVEL4_CHARGE_TIME 3000
|
||||
#define LEVEL4_CHARGE_CHARGE_TIME 1500
|
||||
#define LEVEL4_MIN_CHARGE_TIME 750
|
||||
#define LEVEL4_CHARGE_CHARGE_RATIO (LEVEL4_CHARGE_TIME/LEVEL4_CHARGE_CHARGE_TIME)
|
||||
#define LEVEL4_CHARGE_REPEAT 1000
|
||||
#define LEVEL4_CHARGE_DMG ADM(110)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ALIEN classes
|
||||
*
|
||||
* _SPEED - fraction of Q3A run speed the class can move
|
||||
* _REGEN - health per second regained
|
||||
*
|
||||
* ALIEN_HLTH_MODIFIER - overall health modifier for coarse tuning
|
||||
*
|
||||
*/
|
||||
|
||||
#define ALIEN_HLTH_MODIFIER 1.0f
|
||||
#define AHM(h) ((int)((float)h*ALIEN_HLTH_MODIFIER))
|
||||
|
||||
#define ALIEN_VALUE_MODIFIER 1.0f
|
||||
#define AVM(h) ((int)((float)h*ALIEN_VALUE_MODIFIER))
|
||||
|
||||
#define ABUILDER_SPEED 0.8f
|
||||
#define ABUILDER_VALUE AVM(200)
|
||||
#define ABUILDER_HEALTH AHM(50)
|
||||
#define ABUILDER_REGEN 2
|
||||
#define ABUILDER_COST 0
|
||||
|
||||
#define ABUILDER_UPG_SPEED 1.0f
|
||||
#define ABUILDER_UPG_VALUE AVM(250)
|
||||
#define ABUILDER_UPG_HEALTH AHM(75)
|
||||
#define ABUILDER_UPG_REGEN 3
|
||||
#define ABUILDER_UPG_COST 0
|
||||
|
||||
#define LEVEL0_SPEED 1.3f
|
||||
#define LEVEL0_VALUE AVM(175)
|
||||
#define LEVEL0_HEALTH AHM(25)
|
||||
#define LEVEL0_REGEN 1
|
||||
#define LEVEL0_COST 0
|
||||
|
||||
#define LEVEL1_SPEED 1.25f
|
||||
#define LEVEL1_VALUE AVM(225)
|
||||
#define LEVEL1_HEALTH AHM(75)
|
||||
#define LEVEL1_REGEN 2
|
||||
#define LEVEL1_COST 1
|
||||
|
||||
#define LEVEL1_UPG_SPEED 1.25f
|
||||
#define LEVEL1_UPG_VALUE AVM(275)
|
||||
#define LEVEL1_UPG_HEALTH AHM(100)
|
||||
#define LEVEL1_UPG_REGEN 3
|
||||
#define LEVEL1_UPG_COST 1
|
||||
|
||||
#define LEVEL2_SPEED 1.2f
|
||||
#define LEVEL2_VALUE AVM(350)
|
||||
#define LEVEL2_HEALTH AHM(150)
|
||||
#define LEVEL2_REGEN 4
|
||||
#define LEVEL2_COST 1
|
||||
|
||||
#define LEVEL2_UPG_SPEED 1.2f
|
||||
#define LEVEL2_UPG_VALUE AVM(450)
|
||||
#define LEVEL2_UPG_HEALTH AHM(175)
|
||||
#define LEVEL2_UPG_REGEN 5
|
||||
#define LEVEL2_UPG_COST 1
|
||||
|
||||
#define LEVEL3_SPEED 1.1f
|
||||
#define LEVEL3_VALUE AVM(500)
|
||||
#define LEVEL3_HEALTH AHM(200)
|
||||
#define LEVEL3_REGEN 6
|
||||
#define LEVEL3_COST 1
|
||||
|
||||
#define LEVEL3_UPG_SPEED 1.1f
|
||||
#define LEVEL3_UPG_VALUE AVM(600)
|
||||
#define LEVEL3_UPG_HEALTH AHM(250)
|
||||
#define LEVEL3_UPG_REGEN 7
|
||||
#define LEVEL3_UPG_COST 1
|
||||
|
||||
#define LEVEL4_SPEED 1.2f
|
||||
#define LEVEL4_VALUE AVM(800)
|
||||
#define LEVEL4_HEALTH AHM(400)
|
||||
#define LEVEL4_REGEN 7
|
||||
#define LEVEL4_COST 1
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ALIEN buildables
|
||||
*
|
||||
* _BP - build points required for this buildable
|
||||
* _BT - build time required for this buildable
|
||||
* _REGEN - the amount of health per second regained
|
||||
* _SPLASHDAMGE - the amount of damage caused by this buildable when melting
|
||||
* _SPLASHRADIUS - the radius around which it does this damage
|
||||
*
|
||||
* CREEP_BASESIZE - the maximum distance a buildable can be from an egg/overmind
|
||||
* ALIEN_BHLTH_MODIFIER - overall health modifier for coarse tuning
|
||||
*
|
||||
*/
|
||||
|
||||
#define ALIEN_BHLTH_MODIFIER 1.0f
|
||||
#define ABHM(h) ((int)((float)h*ALIEN_BHLTH_MODIFIER))
|
||||
|
||||
#define CREEP_BASESIZE 700
|
||||
#define CREEP_TIMEOUT 1000
|
||||
#define CREEP_MODIFIER 0.5f
|
||||
#define CREEP_ARMOUR_MODIFIER 0.75f
|
||||
#define CREEP_SCALEDOWN_TIME 3000
|
||||
|
||||
#define ASPAWN_BP 10
|
||||
#define ASPAWN_BT 15000
|
||||
#define ASPAWN_HEALTH ABHM(250)
|
||||
#define ASPAWN_REGEN 8
|
||||
#define ASPAWN_SPLASHDAMAGE 50
|
||||
#define ASPAWN_SPLASHRADIUS 50
|
||||
#define ASPAWN_CREEPSIZE 120
|
||||
#define ASPAWN_VALUE 150
|
||||
|
||||
#define BARRICADE_BP 10
|
||||
#define BARRICADE_BT 20000
|
||||
#define BARRICADE_HEALTH ABHM(200)
|
||||
#define BARRICADE_REGEN 14
|
||||
#define BARRICADE_SPLASHDAMAGE 50
|
||||
#define BARRICADE_SPLASHRADIUS 50
|
||||
#define BARRICADE_CREEPSIZE 120
|
||||
|
||||
#define BOOSTER_BP 12
|
||||
#define BOOSTER_BT 15000
|
||||
#define BOOSTER_HEALTH ABHM(150)
|
||||
#define BOOSTER_REGEN 8
|
||||
#define BOOSTER_SPLASHDAMAGE 50
|
||||
#define BOOSTER_SPLASHRADIUS 50
|
||||
#define BOOSTER_CREEPSIZE 120
|
||||
#define BOOSTER_INTERVAL 30000 //time in msec between uses (per player)
|
||||
#define BOOSTER_REGEN_MOD 2.0f
|
||||
#define BOOST_TIME 30000
|
||||
|
||||
#define ACIDTUBE_BP 8
|
||||
#define ACIDTUBE_BT 15000
|
||||
#define ACIDTUBE_HEALTH ABHM(125)
|
||||
#define ACIDTUBE_REGEN 10
|
||||
#define ACIDTUBE_SPLASHDAMAGE 6
|
||||
#define ACIDTUBE_SPLASHRADIUS 300
|
||||
#define ACIDTUBE_CREEPSIZE 120
|
||||
#define ACIDTUBE_RANGE 300.0f
|
||||
#define ACIDTUBE_REPEAT 3000
|
||||
#define ACIDTUBE_K_SCALE 1.0f
|
||||
|
||||
#define HIVE_BP 12
|
||||
#define HIVE_BT 20000
|
||||
#define HIVE_HEALTH ABHM(125)
|
||||
#define HIVE_REGEN 10
|
||||
#define HIVE_SPLASHDAMAGE 30
|
||||
#define HIVE_SPLASHRADIUS 200
|
||||
#define HIVE_CREEPSIZE 120
|
||||
#define HIVE_RANGE 400.0f
|
||||
#define HIVE_REPEAT 5000
|
||||
#define HIVE_K_SCALE 1.0f
|
||||
#define HIVE_DMG 50
|
||||
#define HIVE_SPEED 240.0f
|
||||
#define HIVE_DIR_CHANGE_PERIOD 500
|
||||
|
||||
#define TRAPPER_BP 8
|
||||
#define TRAPPER_BT 12000
|
||||
#define TRAPPER_HEALTH ABHM(50)
|
||||
#define TRAPPER_REGEN 6
|
||||
#define TRAPPER_SPLASHDAMAGE 15
|
||||
#define TRAPPER_SPLASHRADIUS 100
|
||||
#define TRAPPER_CREEPSIZE 30
|
||||
#define TRAPPER_RANGE 400
|
||||
#define TRAPPER_REPEAT 1000
|
||||
#define TRAPPER_K_SCALE 1.0f
|
||||
#define LOCKBLOB_SPEED 650.0f
|
||||
#define LOCKBLOB_LOCKTIME 5000
|
||||
#define LOCKBLOB_DOT 0.85f // max angle = acos( LOCKBLOB_DOT )
|
||||
#define LOCKBLOB_K_SCALE 1.0f
|
||||
|
||||
#define OVERMIND_BP 0
|
||||
#define OVERMIND_BT 30000
|
||||
#define OVERMIND_HEALTH ABHM(750)
|
||||
#define OVERMIND_REGEN 6
|
||||
#define OVERMIND_SPLASHDAMAGE 15
|
||||
#define OVERMIND_SPLASHRADIUS 300
|
||||
#define OVERMIND_CREEPSIZE 120
|
||||
#define OVERMIND_ATTACK_RANGE 150.0f
|
||||
#define OVERMIND_ATTACK_REPEAT 1000
|
||||
#define OVERMIND_VALUE 300
|
||||
|
||||
#define HOVEL_BP 0
|
||||
#define HOVEL_BT 15000
|
||||
#define HOVEL_HEALTH ABHM(375)
|
||||
#define HOVEL_REGEN 20
|
||||
#define HOVEL_SPLASHDAMAGE 20
|
||||
#define HOVEL_SPLASHRADIUS 200
|
||||
#define HOVEL_CREEPSIZE 120
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* ALIEN misc
|
||||
*
|
||||
* ALIENSENSE_RANGE - the distance alien sense is useful for
|
||||
*
|
||||
*/
|
||||
|
||||
#define ALIENSENSE_RANGE 1000.0f
|
||||
|
||||
#define ALIEN_POISON_TIME 5000
|
||||
#define ALIEN_POISON_DMG 5
|
||||
#define ALIEN_POISON_DIVIDER (1.0f/1.32f) //about 1.0/(time`th root of damage)
|
||||
|
||||
#define ALIEN_SPAWN_REPEAT_TIME 10000
|
||||
|
||||
#define ALIEN_REGEN_DAMAGE_TIME 2000 //msec since damage that regen starts again
|
||||
|
||||
/*
|
||||
* HUMAN weapons
|
||||
*
|
||||
* _REPEAT - time between firings
|
||||
* _RELOAD - time needed to reload
|
||||
* _PRICE - amount in credits weapon costs
|
||||
*
|
||||
* HUMAN_WDMG_MODIFIER - overall damage modifier for coarse tuning
|
||||
*
|
||||
*/
|
||||
|
||||
#define HUMAN_WDMG_MODIFIER 1.0f
|
||||
#define HDM(d) ((int)((float)d*HUMAN_WDMG_MODIFIER))
|
||||
|
||||
#define BLASTER_REPEAT 600
|
||||
#define BLASTER_K_SCALE 1.0f
|
||||
#define BLASTER_SPREAD 200
|
||||
#define BLASTER_SPEED 1400
|
||||
#define BLASTER_DMG HDM(9)
|
||||
|
||||
#define RIFLE_CLIPSIZE 30
|
||||
#define RIFLE_MAXCLIPS 6
|
||||
#define RIFLE_REPEAT 90
|
||||
#define RIFLE_K_SCALE 1.0f
|
||||
#define RIFLE_RELOAD 2000
|
||||
#define RIFLE_PRICE 0
|
||||
#define RIFLE_SPREAD 200
|
||||
#define RIFLE_DMG HDM(5)
|
||||
|
||||
#define PAINSAW_PRICE 100
|
||||
#define PAINSAW_REPEAT 75
|
||||
#define PAINSAW_K_SCALE 1.0f
|
||||
#define PAINSAW_DAMAGE HDM(15)
|
||||
#define PAINSAW_RANGE 40.0f
|
||||
|
||||
#define GRENADE_PRICE 200
|
||||
#define GRENADE_REPEAT 0
|
||||
#define GRENADE_K_SCALE 1.0f
|
||||
#define GRENADE_DAMAGE HDM(310)
|
||||
#define GRENADE_RANGE 192.0f
|
||||
#define GRENADE_SPEED 400.0f
|
||||
|
||||
#define SHOTGUN_PRICE 150
|
||||
#define SHOTGUN_SHELLS 8
|
||||
#define SHOTGUN_PELLETS 8 //used to sync server and client side
|
||||
#define SHOTGUN_MAXCLIPS 3
|
||||
#define SHOTGUN_REPEAT 1000
|
||||
#define SHOTGUN_K_SCALE 1.0f
|
||||
#define SHOTGUN_RELOAD 2000
|
||||
#define SHOTGUN_SPREAD 900
|
||||
#define SHOTGUN_DMG HDM(7)
|
||||
|
||||
#define LASGUN_PRICE 250
|
||||
#define LASGUN_AMMO 200
|
||||
#define LASGUN_REPEAT 200
|
||||
#define LASGUN_K_SCALE 1.0f
|
||||
#define LASGUN_RELOAD 2000
|
||||
#define LASGUN_DAMAGE HDM(9)
|
||||
|
||||
#define MDRIVER_PRICE 350
|
||||
#define MDRIVER_CLIPSIZE 5
|
||||
#define MDRIVER_MAXCLIPS 4
|
||||
#define MDRIVER_DMG HDM(38)
|
||||
#define MDRIVER_REPEAT 1000
|
||||
#define MDRIVER_K_SCALE 1.0f
|
||||
#define MDRIVER_RELOAD 2000
|
||||
|
||||
#define CHAINGUN_PRICE 400
|
||||
#define CHAINGUN_BULLETS 300
|
||||
#define CHAINGUN_REPEAT 80
|
||||
#define CHAINGUN_K_SCALE 1.0f
|
||||
#define CHAINGUN_SPREAD 1000
|
||||
#define CHAINGUN_DMG HDM(6)
|
||||
|
||||
#define PRIFLE_PRICE 400
|
||||
#define PRIFLE_CLIPS 50
|
||||
#define PRIFLE_MAXCLIPS 4
|
||||
#define PRIFLE_REPEAT 100
|
||||
#define PRIFLE_K_SCALE 1.0f
|
||||
#define PRIFLE_RELOAD 2000
|
||||
#define PRIFLE_DMG HDM(9)
|
||||
#define PRIFLE_SPEED 1000
|
||||
|
||||
#define FLAMER_PRICE 450
|
||||
#define FLAMER_GAS 150
|
||||
#define FLAMER_REPEAT 200
|
||||
#define FLAMER_K_SCALE 1.0f
|
||||
#define FLAMER_DMG HDM(20)
|
||||
#define FLAMER_RADIUS 50
|
||||
#define FLAMER_LIFETIME 800.0f
|
||||
#define FLAMER_SPEED 200.0f
|
||||
#define FLAMER_LAG 0.65f //the amount of player velocity that is added to the fireball
|
||||
|
||||
#define LCANNON_PRICE 600
|
||||
#define LCANNON_AMMO 90
|
||||
#define LCANNON_REPEAT 500
|
||||
#define LCANNON_K_SCALE 1.0f
|
||||
#define LCANNON_CHARGEREPEAT 1000
|
||||
#define LCANNON_RELOAD 2000
|
||||
#define LCANNON_DAMAGE HDM(265)
|
||||
#define LCANNON_RADIUS 150
|
||||
#define LCANNON_SECONDARY_DAMAGE HDM(27)
|
||||
#define LCANNON_SECONDARY_RADIUS 75
|
||||
#define LCANNON_SPEED 350
|
||||
#define LCANNON_CHARGE_TIME 2000
|
||||
#define LCANNON_TOTAL_CHARGE 255
|
||||
#define LCANNON_MIN_CHARGE 50
|
||||
|
||||
#define HBUILD_PRICE 0
|
||||
#define HBUILD_REPEAT 1000
|
||||
#define HBUILD_DELAY 17500
|
||||
#define HBUILD_HEALRATE 18
|
||||
|
||||
#define HBUILD2_PRICE 0
|
||||
#define HBUILD2_REPEAT 1000
|
||||
#define HBUILD2_DELAY 15000
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* HUMAN upgrades
|
||||
*/
|
||||
|
||||
#define LIGHTARMOUR_PRICE 70
|
||||
#define LIGHTARMOUR_POISON_PROTECTION 1
|
||||
|
||||
#define HELMET_PRICE 90
|
||||
#define HELMET_RANGE 1000.0f
|
||||
#define HELMET_POISON_PROTECTION 2
|
||||
|
||||
#define MEDKIT_PRICE 0
|
||||
|
||||
#define BATTPACK_PRICE 100
|
||||
#define BATTPACK_MODIFIER 1.5f //modifier for extra energy storage available
|
||||
|
||||
#define JETPACK_PRICE 120
|
||||
#define JETPACK_FLOAT_SPEED 128.0f //up movement speed
|
||||
#define JETPACK_SINK_SPEED 192.0f //down movement speed
|
||||
#define JETPACK_DISABLE_TIME 1000 //time to disable the jetpack when player damaged
|
||||
#define JETPACK_DISABLE_CHANCE 0.3f
|
||||
|
||||
#define BSUIT_PRICE 400
|
||||
#define BSUIT_POISON_PROTECTION 4
|
||||
|
||||
#define MGCLIP_PRICE 0
|
||||
|
||||
#define CGAMMO_PRICE 0
|
||||
|
||||
#define GAS_PRICE 0
|
||||
|
||||
#define MEDKIT_POISON_IMMUNITY_TIME 0
|
||||
#define MEDKIT_STARTUP_TIME 4000
|
||||
#define MEDKIT_STARTUP_SPEED 5
|
||||
|
||||
|
||||
/*
|
||||
* HUMAN buildables
|
||||
*
|
||||
* _BP - build points required for this buildable
|
||||
* _BT - build time required for this buildable
|
||||
* _SPLASHDAMGE - the amount of damage caused by this buildable when it blows up
|
||||
* _SPLASHRADIUS - the radius around which it does this damage
|
||||
*
|
||||
* REACTOR_BASESIZE - the maximum distance a buildable can be from an reactor
|
||||
* REPEATER_BASESIZE - the maximum distance a buildable can be from a repeater
|
||||
* HUMAN_BHLTH_MODIFIER - overall health modifier for coarse tuning
|
||||
*
|
||||
*/
|
||||
|
||||
#define HUMAN_BHLTH_MODIFIER 1.0f
|
||||
#define HBHM(h) ((int)((float)h*HUMAN_BHLTH_MODIFIER))
|
||||
|
||||
#define REACTOR_BASESIZE 1000
|
||||
#define REPEATER_BASESIZE 500
|
||||
#define HUMAN_DETONATION_DELAY 5000
|
||||
|
||||
#define HSPAWN_BP 10
|
||||
#define HSPAWN_BT 10000
|
||||
#define HSPAWN_HEALTH HBHM(310)
|
||||
#define HSPAWN_SPLASHDAMAGE 50
|
||||
#define HSPAWN_SPLASHRADIUS 100
|
||||
#define HSPAWN_VALUE 1
|
||||
|
||||
#define MEDISTAT_BP 8
|
||||
#define MEDISTAT_BT 10000
|
||||
#define MEDISTAT_HEALTH HBHM(190)
|
||||
#define MEDISTAT_SPLASHDAMAGE 50
|
||||
#define MEDISTAT_SPLASHRADIUS 100
|
||||
|
||||
#define MGTURRET_BP 8
|
||||
#define MGTURRET_BT 10000
|
||||
#define MGTURRET_HEALTH HBHM(190)
|
||||
#define MGTURRET_SPLASHDAMAGE 100
|
||||
#define MGTURRET_SPLASHRADIUS 100
|
||||
#define MGTURRET_ANGULARSPEED 8 //degrees/think ~= 200deg/sec
|
||||
#define MGTURRET_ACCURACYTOLERANCE MGTURRET_ANGULARSPEED / 1.5f //angular difference for turret to fire
|
||||
#define MGTURRET_VERTICALCAP 30 // +/- maximum pitch
|
||||
#define MGTURRET_REPEAT 100
|
||||
#define MGTURRET_K_SCALE 1.0f
|
||||
#define MGTURRET_RANGE 300.0f
|
||||
#define MGTURRET_SPREAD 200
|
||||
#define MGTURRET_DMG HDM(4)
|
||||
#define MGTURRET_DCC_ANGULARSPEED 10
|
||||
#define MGTURRET_DCC_ACCURACYTOLERANCE MGTURRET_DCC_ANGULARSPEED / 1.5f
|
||||
#define MGTURRET_GRAB_ANGULARSPEED 3
|
||||
#define MGTURRET_GRAB_ACCURACYTOLERANCE MGTURRET_GRAB_ANGULARSPEED / 1.5f
|
||||
|
||||
#define TESLAGEN_BP 10
|
||||
#define TESLAGEN_BT 15000
|
||||
#define TESLAGEN_HEALTH HBHM(220)
|
||||
#define TESLAGEN_SPLASHDAMAGE 50
|
||||
#define TESLAGEN_SPLASHRADIUS 100
|
||||
#define TESLAGEN_REPEAT 250
|
||||
#define TESLAGEN_K_SCALE 4.0f
|
||||
#define TESLAGEN_RANGE 250
|
||||
#define TESLAGEN_DMG HDM(9)
|
||||
|
||||
#define DC_BP 8
|
||||
#define DC_BT 10000
|
||||
#define DC_HEALTH HBHM(190)
|
||||
#define DC_SPLASHDAMAGE 50
|
||||
#define DC_SPLASHRADIUS 100
|
||||
|
||||
#define ARMOURY_BP 10
|
||||
#define ARMOURY_BT 10000
|
||||
#define ARMOURY_HEALTH HBHM(280)
|
||||
#define ARMOURY_SPLASHDAMAGE 50
|
||||
#define ARMOURY_SPLASHRADIUS 100
|
||||
|
||||
#define REACTOR_BP 0
|
||||
#define REACTOR_BT 20000
|
||||
#define REACTOR_HEALTH HBHM(930)
|
||||
#define REACTOR_SPLASHDAMAGE 200
|
||||
#define REACTOR_SPLASHRADIUS 300
|
||||
#define REACTOR_ATTACK_RANGE 100.0f
|
||||
#define REACTOR_ATTACK_REPEAT 1000
|
||||
#define REACTOR_ATTACK_DAMAGE 40
|
||||
#define REACTOR_VALUE 2
|
||||
|
||||
#define REPEATER_BP 0
|
||||
#define REPEATER_BT 10000
|
||||
#define REPEATER_HEALTH HBHM(250)
|
||||
#define REPEATER_SPLASHDAMAGE 50
|
||||
#define REPEATER_SPLASHRADIUS 100
|
||||
#define REPEATER_INACTIVE_TIME 90000
|
||||
|
||||
/*
|
||||
* HUMAN misc
|
||||
*/
|
||||
|
||||
#define HUMAN_SPRINT_MODIFIER 1.2f
|
||||
#define HUMAN_JOG_MODIFIER 1.0f
|
||||
#define HUMAN_BACK_MODIFIER 0.8f
|
||||
#define HUMAN_SIDE_MODIFIER 0.9f
|
||||
|
||||
#define STAMINA_STOP_RESTORE 25
|
||||
#define STAMINA_WALK_RESTORE 15
|
||||
#define STAMINA_SPRINT_TAKE 8
|
||||
#define STAMINA_LARMOUR_TAKE 4
|
||||
|
||||
#define HUMAN_SPAWN_REPEAT_TIME 10000
|
||||
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
|
||||
#define MIN_FALL_DISTANCE 30.0f //the fall distance at which fall damage kicks in
|
||||
#define MAX_FALL_DISTANCE 120.0f //the fall distance at which maximum damage is dealt
|
||||
#define AVG_FALL_DISTANCE ((MIN_FALL_DISTANCE+MAX_FALL_DISTANCE)/2.0f)
|
||||
|
||||
#define HUMAN_MAXED 900 //a human with a strong selection of weapons/upgrades
|
||||
#define HUMAN_MAX_CREDITS 2000
|
||||
#define ALIEN_MAX_KILLS 9
|
||||
#define ALIEN_MAX_SINGLE_KILLS 3
|
||||
|
||||
#define FREEKILL_PERIOD 120000 //msec
|
||||
#define FREEKILL_ALIEN 1
|
||||
#define FREEKILL_HUMAN LEVEL0_VALUE
|
||||
|
||||
#define DEFAULT_ALIEN_BUILDPOINTS "100"
|
||||
#define DEFAULT_ALIEN_STAGE2_THRESH "20"
|
||||
#define DEFAULT_ALIEN_STAGE3_THRESH "40"
|
||||
#define DEFAULT_ALIEN_MAX_STAGE "2"
|
||||
#define DEFAULT_HUMAN_BUILDPOINTS "100"
|
||||
#define DEFAULT_HUMAN_STAGE2_THRESH "20"
|
||||
#define DEFAULT_HUMAN_STAGE3_THRESH "40"
|
||||
#define DEFAULT_HUMAN_MAX_STAGE "2"
|
||||
|
||||
#define DAMAGE_FRACTION_FOR_KILL 0.5f //how much damage players (versus structures) need to
|
||||
//do to increment the stage kill counters
|
||||
|
||||
// g_suddenDeathMode settings
|
||||
#define SDMODE_BP 0
|
||||
#define SDMODE_NO_BUILD 1
|
||||
#define SDMODE_SELECTIVE 2
|
||||
Reference in new issue
Block a user