First version.

This commit is contained in:
Paradox460 committed 2008-01-05 04:20:33 +00:00
commit ef1c2cc347
473 files changed
+305366

No files matched your search

+2031
View File
File diff suppressed because it is too large. Load diff
+104
View File
@@ -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
+88
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+3527
View File
File diff suppressed because it is too large. Load diff
+1334
View File
File diff suppressed because it is too large. Load diff
+416
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+5662
View File
File diff suppressed because it is too large. Load diff
+226
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+4416
View File
File diff suppressed because it is too large. Load diff
+1663
View File
File diff suppressed because it is too large. Load diff
+1376
View File
File diff suppressed because it is too large. Load diff
+2552
View File
File diff suppressed because it is too large. Load diff
+743
View File
@@ -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 );
}
}
}
+216
View File
@@ -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" );
}
+436
View File
@@ -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 );
}
+856
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+170
View File
@@ -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 );
}
+180
View File
@@ -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 );
}
+262
View File
@@ -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;
+167
View File
@@ -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 ] );
}
}
+698
View File
@@ -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
}
+768
View File
@@ -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;
}
+65
View File
@@ -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
+284
View File
@@ -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 );
}
+472
View File
@@ -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;
}
+276
View File
@@ -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;
}
}
}
+1141
View File
File diff suppressed because it is too large. Load diff
+848
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+625
View File
@@ -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