summaryrefslogtreecommitdiff
path: root/src/game/g_utils.c
diff options
context:
space:
mode:
authorIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
committerIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
commit425decdf7e9284d15aa726e3ae96b9942fb0e3ea (patch)
tree6c0dd7edfefff1be7b9e75fe0b3a0a85fe1595f3 /src/game/g_utils.c
parentccb0b2e4d6674a7a00c9bf491f08fc73b6898c54 (diff)
create tremded branch
Diffstat (limited to 'src/game/g_utils.c')
-rw-r--r--src/game/g_utils.c464
1 files changed, 401 insertions, 63 deletions
diff --git a/src/game/g_utils.c b/src/game/g_utils.c
index a74df3f..5c74188 100644
--- a/src/game/g_utils.c
+++ b/src/game/g_utils.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
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,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,8 +17,8 @@ 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
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
@@ -93,7 +94,7 @@ G_FindConfigstringIndex
================
*/
-int G_FindConfigstringIndex( char *name, int start, int max, qboolean create )
+int G_FindConfigstringIndex( const char *name, int start, int max, qboolean create )
{
int i;
char s[ MAX_STRING_CHARS ];
@@ -122,55 +123,28 @@ int G_FindConfigstringIndex( char *name, int start, int max, qboolean create )
return i;
}
-//TA: added ParticleSystemIndex
-int G_ParticleSystemIndex( char *name )
+int G_ParticleSystemIndex( const char *name )
{
return G_FindConfigstringIndex( name, CS_PARTICLE_SYSTEMS, MAX_GAME_PARTICLE_SYSTEMS, qtrue );
}
-//TA: added ShaderIndex
-int G_ShaderIndex( char *name )
+int G_ShaderIndex( const char *name )
{
return G_FindConfigstringIndex( name, CS_SHADERS, MAX_GAME_SHADERS, qtrue );
}
-int G_ModelIndex( char *name )
+int G_ModelIndex( const char *name )
{
return G_FindConfigstringIndex( name, CS_MODELS, MAX_MODELS, qtrue );
}
-int G_SoundIndex( char *name )
+int G_SoundIndex( const 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
@@ -249,7 +223,7 @@ gentity_t *G_PickTarget( char *targetname )
return NULL;
}
- return choice[ rand( ) % num_choices ];
+ return choice[ rand( ) / ( RAND_MAX / num_choices + 1 ) ];
}
@@ -268,9 +242,6 @@ 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;
@@ -505,7 +476,6 @@ qboolean G_EntitiesFree( void )
return qfalse;
}
-
/*
=================
G_FreeEntity
@@ -528,6 +498,181 @@ void G_FreeEntity( gentity_t *ent )
/*
=================
+G_RemoveEntity
+
+Safely remove an entity, perform reasonable cleanup logic
+=================
+*/
+void G_RemoveEntity( gentity_t *ent )
+{
+ gentity_t *e;
+
+ if( ent->client )
+ {
+ // removing a player causes the player to "unspawn"
+ class_t class = ent->client->pers.classSelection; // back up the spawn queue choice
+ weapon_t weapon = ent->client->pers.humanItemSelection; // back up
+ ent->client->pers.classSelection = PCL_NONE;
+ ent->client->pers.humanItemSelection = WP_NONE;
+ ent->suicideTime = 0; // cancel any timed suicides
+ ClientSpawn( ent, NULL, NULL, NULL );
+ ent->client->pers.classSelection = class; // restore the spawn queue choice
+ ent->client->pers.humanItemSelection = weapon; // restore
+ return;
+ }
+ else if( ent->s.eType == ET_RANGE_MARKER )
+ {
+ for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e )
+ {
+ if( e->rangeMarker == ent )
+ {
+ // clear the buildable's reference to this range marker
+ e->rangeMarker = NULL;
+ break;
+ }
+ }
+ }
+ else if( ent->s.eType == ET_BUILDABLE )
+ {
+ // the range marker (if any) goes away with the buildable
+ G_RemoveRangeMarkerFrom( ent );
+ }
+ else if( !strcmp( ent->classname, "lev2zapchain" ) )
+ {
+ zap_t *z;
+ for( z = &zaps[ 0 ]; z < &zaps[ MAX_ZAPS ]; ++z )
+ {
+ if( z->used && z->effectChannel == ent )
+ {
+ // free the zap slot occupied by this zap effect
+ z->used = qfalse;
+ break;
+ }
+ }
+ }
+ else if( ent->s.eType == ET_MOVER )
+ {
+ if( !strcmp( ent->classname, "func_door" ) ||
+ !strcmp( ent->classname, "func_door_rotating" ) ||
+ !strcmp( ent->classname, "func_door_model" ) ||
+ !strcmp( ent->classname, "func_door_model_clip_brush" ) ||
+ !strcmp( ent->classname, "func_plat" ) )
+ {
+ // each func_door_model entity is paired with a clip brush, remove the other
+ if( ent->clipBrush != NULL )
+ G_FreeEntity( ent->clipBrush );
+ for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e )
+ {
+ if( e->parent == ent )
+ {
+ // this mover has a trigger area brush
+ if( ent->teammaster != NULL && ent->teammaster->teamchain != NULL )
+ {
+ // the mover is part of a team of at least 2
+ e->parent = ent->teammaster->teamchain; // hand the brush over to the next mover in command
+ }
+ else
+ G_FreeEntity( e ); // remove the teamless or to-be-orphaned brush
+ break;
+ }
+ }
+ }
+ // removing a mover opens the relevant portal
+ trap_AdjustAreaPortalState( ent, qtrue );
+ }
+ else if( !strcmp( ent->classname, "path_corner" ) )
+ {
+ for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e )
+ {
+ if( e->nextTrain == ent )
+ e->nextTrain = ent->nextTrain; // redirect func_train and path_corner entities
+ }
+ }
+ else if( !strcmp( ent->classname, "info_player_intermission" ) ||
+ !strcmp( ent->classname, "info_player_deathmatch" ) )
+ {
+ for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e )
+ {
+ if( e->inuse && e != ent &&
+ ( !strcmp( e->classname, "info_player_intermission" ) ||
+ !strcmp( e->classname, "info_player_deathmatch" ) ) )
+ {
+ break;
+ }
+ }
+ // refuse to remove the last info_player_intermission/info_player_deathmatch entity
+ // (because it is required for initial camera placement)
+ if( e >= &g_entities[ level.num_entities ] )
+ return;
+ }
+ else if( !strcmp( ent->classname, "target_location" ) )
+ {
+ if( ent == level.locationHead )
+ level.locationHead = ent->nextTrain;
+ else
+ {
+ for( e = level.locationHead; e != NULL; e = e->nextTrain )
+ {
+ if( e->nextTrain == ent )
+ {
+ e->nextTrain = ent->nextTrain;
+ break;
+ }
+ }
+ }
+ }
+ else if( !Q_stricmp( ent->classname, "misc_portal_camera" ) )
+ {
+ for( e = &g_entities[ MAX_CLIENTS ]; e < &g_entities[ level.num_entities ]; ++e )
+ {
+ if( e->r.ownerNum == ent - g_entities )
+ {
+ // disown the surface
+ e->r.ownerNum = ENTITYNUM_NONE;
+ }
+ }
+ }
+
+ if( ent->teammaster != NULL )
+ {
+ // this entity is part of a mover team
+ if( ent == ent->teammaster )
+ {
+ // the entity is the master
+ gentity_t *snd = ent->teamchain;
+ for( e = snd; e != NULL; e = e->teamchain )
+ e->teammaster = snd; // put the 2nd entity (if any) in command
+ if( snd )
+ {
+ if( !strcmp( ent->classname, snd->classname ) )
+ {
+ // transfer certain activity properties
+ snd->think = ent->think;
+ snd->nextthink = ent->nextthink;
+ }
+ snd->flags &= ~FL_TEAMSLAVE; // put the 2nd entity (if any) in command
+ }
+ }
+ else
+ {
+ // the entity is a slave
+ for( e = ent->teammaster; e != NULL; e = e->teamchain )
+ {
+ if( e->teamchain == ent )
+ {
+ // unlink it from the chain
+ e->teamchain = ent->teamchain;
+ break;
+ }
+ }
+ }
+ }
+
+ G_FreeEntity( ent );
+}
+
+/*
+=================
G_TempEntity
Spawns an event entity that will be auto-removed
@@ -535,7 +680,7 @@ 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 *G_TempEntity( const vec3_t origin, int event )
{
gentity_t *e;
vec3_t snapped;
@@ -582,18 +727,18 @@ void G_KillBox( gentity_t *ent )
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 );
+ VectorAdd( ent->r.currentOrigin, ent->r.mins, mins );
+ VectorAdd( ent->r.currentOrigin, 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 )
+ if( ent->client && !hit->client ) // players can telefrag only other players
continue;
- //TA: impossible to telefrag self
+ // impossible to telefrag self
if( ent == hit )
continue;
@@ -644,8 +789,8 @@ void G_AddEvent( gentity_t *ent, int event, int eventParm )
// 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 );
+ G_Printf( S_COLOR_YELLOW "WARNING: G_AddEvent( %s ) has eventParm %d, "
+ "which will overflow\n", BG_EventName( event ), eventParm );
}
// clients need to add the event in playerState_t instead of entityState_t
@@ -728,7 +873,7 @@ G_SetOrigin
Sets the pos trajectory for a fixed position
================
*/
-void G_SetOrigin( gentity_t *ent, vec3_t origin )
+void G_SetOrigin( gentity_t *ent, const vec3_t origin )
{
VectorCopy( origin, ent->s.pos.trBase );
ent->s.pos.trType = TR_STATIONARY;
@@ -737,10 +882,9 @@ void G_SetOrigin( gentity_t *ent, vec3_t origin )
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
+// from quakestyle.telefragged.com
// (NOBODY): Code helper function
//
gentity_t *G_FindRadius( gentity_t *from, vec3_t org, float rad )
@@ -777,16 +921,14 @@ G_Visible
Test for a LOS between two entities
===============
*/
-qboolean G_Visible( gentity_t *ent1, gentity_t *ent2 )
+qboolean G_Visible( gentity_t *ent1, gentity_t *ent2, int contents )
{
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;
+ trap_Trace( &trace, ent1->s.pos.trBase, NULL, NULL, ent2->s.pos.trBase,
+ ent1->s.number, contents );
- return qtrue;
+ return trace.fraction >= 1.0f || trace.entityNum == ent2 - g_entities;
}
/*
@@ -799,15 +941,21 @@ 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;
+ float nd, d;
+ gentity_t *closestEnt;
+
+ if( numEntities <= 0 )
+ return NULL;
+
+ closestEnt = entities[ 0 ];
+ d = DistanceSquared( origin, closestEnt->r.currentOrigin );
- for( i = 0; i < numEntities; i++ )
+ for( i = 1; i < numEntities; i++ )
{
gentity_t *ent = entities[ i ];
- nd = DistanceSquared( origin, ent->s.origin );
- if( i == 0 || nd < d )
+ nd = DistanceSquared( origin, ent->r.currentOrigin );
+ if( nd < d )
{
d = nd;
closestEnt = ent;
@@ -828,10 +976,24 @@ void G_TriggerMenu( int clientNum, dynMenu_t menu )
{
char buffer[ 32 ];
- Com_sprintf( buffer, 32, "servermenu %d", menu );
+ Com_sprintf( buffer, sizeof( buffer ), "servermenu %d", menu );
trap_SendServerCommand( clientNum, buffer );
}
+/*
+===============
+G_TriggerMenuArgs
+
+Trigger a menu on some client and passes an argument
+===============
+*/
+void G_TriggerMenuArgs( int clientNum, dynMenu_t menu, int arg )
+{
+ char buffer[ 64 ];
+
+ Com_sprintf( buffer, sizeof( buffer ), "servermenu %d %d", menu, arg );
+ trap_SendServerCommand( clientNum, buffer );
+}
/*
===============
@@ -847,3 +1009,179 @@ void G_CloseMenus( int clientNum )
Com_sprintf( buffer, 32, "serverclosemenus" );
trap_SendServerCommand( clientNum, buffer );
}
+
+
+/*
+===============
+G_AddressParse
+
+Make an IP address more usable
+===============
+*/
+static const char *addr4parse( const char *str, addr_t *addr )
+{
+ int i;
+ int octet = 0;
+ int num = 0;
+ memset( addr, 0, sizeof( addr_t ) );
+ addr->type = IPv4;
+ for( i = 0; octet < 4; i++ )
+ {
+ if( isdigit( str[ i ] ) )
+ num = num * 10 + str[ i ] - '0';
+ else
+ {
+ if( num < 0 || num > 255 )
+ return NULL;
+ addr->addr[ octet ] = (byte)num;
+ octet++;
+ if( str[ i ] != '.' || str[ i + 1 ] == '.' )
+ break;
+ num = 0;
+ }
+ }
+ if( octet < 1 )
+ return NULL;
+ return str + i;
+}
+
+static const char *addr6parse( const char *str, addr_t *addr )
+{
+ int i;
+ qboolean seen = qfalse;
+ /* keep track of the parts before and after the ::
+ it's either this or even uglier hacks */
+ byte a[ ADDRLEN ], b[ ADDRLEN ];
+ size_t before = 0, after = 0;
+ int num = 0;
+ /* 8 hexadectets unless :: is present */
+ for( i = 0; before + after <= 8; i++ )
+ {
+ //num = num << 4 | str[ i ] - '0';
+ if( isdigit( str[ i ] ) )
+ num = num * 16 + str[ i ] - '0';
+ else if( str[ i ] >= 'A' && str[ i ] <= 'F' )
+ num = num * 16 + 10 + str[ i ] - 'A';
+ else if( str[ i ] >= 'a' && str[ i ] <= 'f' )
+ num = num * 16 + 10 + str[ i ] - 'a';
+ else
+ {
+ if( num < 0 || num > 65535 )
+ return NULL;
+ if( i == 0 )
+ {
+ //
+ }
+ else if( seen ) // :: has been seen already
+ {
+ b[ after * 2 ] = num >> 8;
+ b[ after * 2 + 1 ] = num & 0xff;
+ after++;
+ }
+ else
+ {
+ a[ before * 2 ] = num >> 8;
+ a[ before * 2 + 1 ] = num & 0xff;
+ before++;
+ }
+ if( !str[ i ] )
+ break;
+ if( str[ i ] != ':' || before + after == 8 )
+ break;
+ if( str[ i + 1 ] == ':' )
+ {
+ // ::: or multiple ::
+ if( seen || str[ i + 2 ] == ':' )
+ break;
+ seen = qtrue;
+ i++;
+ }
+ else if( i == 0 ) // starts with : but not ::
+ return NULL;
+ num = 0;
+ }
+ }
+ if( seen )
+ {
+ // there have to be fewer than 8 hexadectets when :: is present
+ if( before + after == 8 )
+ return NULL;
+ }
+ else if( before + after < 8 ) // require exactly 8 hexadectets
+ return NULL;
+ memset( addr, 0, sizeof( addr_t ) );
+ addr->type = IPv6;
+ if( before )
+ memcpy( addr->addr, a, before * 2 );
+ if( after )
+ memcpy( addr->addr + ADDRLEN - 2 * after, b, after * 2 );
+ return str + i;
+}
+
+qboolean G_AddressParse( const char *str, addr_t *addr )
+{
+ const char *p;
+ int max;
+ if( strchr( str, ':' ) )
+ {
+ p = addr6parse( str, addr );
+ max = 128;
+ }
+ else if( strchr( str, '.' ) )
+ {
+ p = addr4parse( str, addr );
+ max = 32;
+ }
+ else
+ return qfalse;
+ Q_strncpyz( addr->str, str, sizeof( addr->str ) );
+ if( !p )
+ return qfalse;
+ if( *p == '/' )
+ {
+ addr->mask = atoi( p + 1 );
+ if( addr->mask < 1 || addr->mask > max )
+ addr->mask = max;
+ }
+ else
+ {
+ if( *p )
+ return qfalse;
+ addr->mask = max;
+ }
+ return qtrue;
+}
+
+/*
+===============
+G_AddressCompare
+
+Based largely on NET_CompareBaseAdrMask from ioq3 revision 1557
+===============
+*/
+qboolean G_AddressCompare( const addr_t *a, const addr_t *b )
+{
+ int i, netmask;
+ if( a->type != b->type )
+ return qfalse;
+ netmask = a->mask;
+ if( a->type == IPv4 )
+ {
+ if( netmask < 1 || netmask > 32 )
+ netmask = 32;
+ }
+ else if( a->type == IPv6 )
+ {
+ if( netmask < 1 || netmask > 128 )
+ netmask = 128;
+ }
+ for( i = 0; netmask > 7; i++, netmask -= 8 )
+ if( a->addr[ i ] != b->addr[ i ] )
+ return qfalse;
+ if( netmask )
+ {
+ netmask = ( ( 1 << netmask ) - 1 ) << ( 8 - netmask );
+ return ( a->addr[ i ] & netmask ) == ( b->addr[ i ] & netmask );
+ }
+ return qtrue;
+}