diff options
Diffstat (limited to 'src/game')
-rw-r--r-- | src/game/bg_misc.c | 30 | ||||
-rw-r--r-- | src/game/bg_pmove.c | 23 | ||||
-rw-r--r-- | src/game/bg_public.h | 1 | ||||
-rw-r--r-- | src/game/bg_slidemove.c | 10 | ||||
-rw-r--r-- | src/game/g_buildable.c | 295 | ||||
-rw-r--r-- | src/game/g_combat.c | 5 | ||||
-rw-r--r-- | src/game/g_local.h | 2 | ||||
-rw-r--r-- | src/game/g_weapon.c | 11 |
8 files changed, 201 insertions, 176 deletions
diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c index 85c1f47a..c4d374ae 100644 --- a/src/game/bg_misc.c +++ b/src/game/bg_misc.c @@ -5123,6 +5123,26 @@ qboolean BG_RotateAxis( vec3_t surfNormal, vec3_t inAxis[ 3 ], /* =============== +BG_GetClientNormal + +Get the normal for the surface the client is walking on +=============== +*/ +void BG_GetClientNormal( const playerState_t *ps, vec3_t normal ) +{ + if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBING ) + { + if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) + VectorSet( normal, 0.0f, 0.0f, -1.0f ); + else + VectorCopy( ps->grapplePoint, normal ); + } + else + VectorSet( normal, 0.0f, 0.0f, 1.0f ); +} + +/* +=============== BG_PositionBuildableRelativeToPlayer Find a place to build a buildable @@ -5138,15 +5158,7 @@ void BG_PositionBuildableRelativeToPlayer( const playerState_t *ps, vec3_t angles, playerOrigin, playerNormal; float buildDist; - if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBING ) - { - if( ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) - VectorSet( playerNormal, 0.0f, 0.0f, -1.0f ); - else - VectorCopy( ps->grapplePoint, playerNormal ); - } - else - VectorSet( playerNormal, 0.0f, 0.0f, 1.0f ); + BG_GetClientNormal( ps, playerNormal ); VectorCopy( ps->viewangles, angles ); VectorCopy( ps->origin, playerOrigin ); diff --git a/src/game/bg_pmove.c b/src/game/bg_pmove.c index ae9e3073..80ca7715 100644 --- a/src/game/bg_pmove.c +++ b/src/game/bg_pmove.c @@ -658,6 +658,8 @@ PM_CheckJump */ static qboolean PM_CheckJump( void ) { + vec3_t normal; + if( BG_FindJumpMagnitudeForClass( pm->ps->stats[ STAT_PCLASS ] ) == 0.0f ) return qfalse; @@ -712,18 +714,10 @@ static qboolean PM_CheckJump( void ) pm->ps->groundEntityNum = ENTITYNUM_NONE; // jump away from wall - if( pm->ps->stats[ STAT_STATE ] & SS_WALLCLIMBING ) - { - vec3_t normal = { 0, 0, -1 }; + BG_GetClientNormal( pm->ps, normal ); - if( !( pm->ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) ) - VectorCopy( pm->ps->grapplePoint, normal ); - - VectorMA( pm->ps->velocity, BG_FindJumpMagnitudeForClass( pm->ps->stats[ STAT_PCLASS ] ), - normal, pm->ps->velocity ); - } - else - pm->ps->velocity[ 2 ] = BG_FindJumpMagnitudeForClass( pm->ps->stats[ STAT_PCLASS ] ); + VectorMA( pm->ps->velocity, BG_FindJumpMagnitudeForClass( pm->ps->stats[ STAT_PCLASS ] ), + normal, pm->ps->velocity ); PM_AddEvent( EV_JUMP ); @@ -1716,12 +1710,7 @@ static void PM_GroundClimbTrace( void ) float ldDOTtCs, d; vec3_t abc; - // If we're on the ceiling then grapplePoint is a rotation normal.. otherwise its a surface normal. - // would have been nice if Carmack had left a few random variables in the ps struct for mod makers - if( pm->ps->stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) - VectorCopy( ceilingNormal, surfNormal ); - else - VectorCopy( pm->ps->grapplePoint, surfNormal ); + BG_GetClientNormal( pm->ps, surfNormal ); //construct a vector which reflects the direction the player is looking wrt the surface normal ProjectPointOnPlane( movedir, pml.forward, surfNormal ); diff --git a/src/game/bg_public.h b/src/game/bg_public.h index 0d6caefa..a48669d6 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -1091,6 +1091,7 @@ void BG_DeactivateUpgrade( int item, int stats[ ] ); qboolean BG_UpgradeIsActive( int item, int stats[ ] ); qboolean BG_RotateAxis( vec3_t surfNormal, vec3_t inAxis[ 3 ], vec3_t outAxis[ 3 ], qboolean inverse, qboolean ceiling ); +void BG_GetClientNormal( const playerState_t *ps, vec3_t normal ); void BG_PositionBuildableRelativeToPlayer( const playerState_t *ps, const vec3_t mins, const vec3_t maxs, void (*trace)( trace_t *, const vec3_t, const vec3_t, diff --git a/src/game/bg_slidemove.c b/src/game/bg_slidemove.c index aa32a6ae..0b3f8f0c 100644 --- a/src/game/bg_slidemove.c +++ b/src/game/bg_slidemove.c @@ -298,15 +298,7 @@ qboolean PM_StepSlideMove( qboolean gravity, qboolean predictive ) 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 ); + BG_GetClientNormal( pm->ps, normal ); VectorCopy( pm->ps->origin, start_o ); VectorCopy( pm->ps->velocity, start_v ); diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c index 12129699..9c36e4c8 100644 --- a/src/game/g_buildable.c +++ b/src/game/g_buildable.c @@ -185,7 +185,7 @@ static qboolean G_FindPower( gentity_t *self ) self->parentNode = NULL; //iterate through entities - for ( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) + for( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) { if( ent->s.eType != ET_BUILDABLE ) continue; @@ -197,20 +197,25 @@ static qboolean G_FindPower( gentity_t *self ) VectorSubtract( self->s.origin, ent->s.origin, temp_v ); distance = VectorLength( temp_v ); - if( distance < minDistance && ent->powered && - ( ( ent->s.modelindex == BA_H_REACTOR && - distance <= REACTOR_BASESIZE ) || - ( ent->s.modelindex == BA_H_REPEATER && - distance <= REPEATER_BASESIZE ) ) ) { - - closestPower = ent; - minDistance = distance; + // Always prefer a reactor if there is one in range + if( ent->s.modelindex == BA_H_REACTOR && + distance <= REACTOR_BASESIZE ) + { + self->parentNode = ent; + return qtrue; + } + else if( distance < minDistance && ent->powered && + distance <= REPEATER_BASESIZE ) + { + closestPower = ent; + minDistance = distance; } } } //if there were no power items nearby give up - if( closestPower ) { + if( closestPower ) + { self->parentNode = closestPower; return qtrue; } @@ -220,12 +225,13 @@ static qboolean G_FindPower( gentity_t *self ) /* ================ -G_IsPowered +G_PowerEntityForPoint -Simple wrapper to G_FindPower to check if a location has power +Simple wrapper to G_FindPower to find the entity providing +power for the specified point ================ */ -qboolean G_IsPowered( vec3_t origin ) +static gentity_t *G_PowerEntityForPoint( vec3_t origin ) { gentity_t dummy; @@ -234,7 +240,28 @@ qboolean G_IsPowered( vec3_t origin ) dummy.s.modelindex = BA_NONE; VectorCopy( origin, dummy.s.origin ); - return G_FindPower( &dummy ); + if( G_FindPower( &dummy ) ) + return dummy.parentNode; + else + return NULL; +} + +/* +================ +G_IsPowered + +Check if a location has power, returning the entity type +that is providing it +================ +*/ +buildable_t G_IsPowered( vec3_t origin ) +{ + gentity_t *ent = G_PowerEntityForPoint( origin ); + + if( ent ) + return ent->s.modelindex; + else + return BA_NONE; } /* @@ -1848,9 +1875,6 @@ void HMedistat_Think( gentity_t *self ) if( self->enemy->client && self->enemy->client->ps.stats[ STAT_STATE ] & SS_POISONED ) self->enemy->client->ps.stats[ STAT_STATE ] &= ~SS_POISONED; - if( self->enemy->client && self->enemy->client->ps.stats[ STAT_STATE ] & SS_MEDKIT_ACTIVE ) - self->enemy->client->ps.stats[ STAT_STATE ] &= ~SS_MEDKIT_ACTIVE; - self->enemy->health++; //if they're completely healed, give them a medkit @@ -2532,6 +2556,30 @@ qboolean G_BuildableRange( vec3_t origin, float r, buildable_t buildable ) } /* +================ +G_FindBuildable + +Finds a buildable of the specified type +================ +*/ +static gentity_t *G_FindBuildable( buildable_t buildable ) +{ + int i; + gentity_t *ent; + + for ( i = 1, ent = g_entities + i; i < level.num_entities; i++, ent++ ) + { + if( ent->s.eType != ET_BUILDABLE ) + continue; + + if( ent->s.modelindex == buildable ) + return ent; + } + + return NULL; +} + +/* =============== G_BuildablesIntersect @@ -2592,20 +2640,19 @@ static int G_CompareBuildablesForRemoval( const void *a, const void *b ) gentity_t *buildableA, *buildableB; int i; int aPrecedence = 0, bPrecedence = 0; - qboolean aCollides = qfalse, bCollides = qfalse; qboolean aMatches = qfalse, bMatches = qfalse; buildableA = *(gentity_t **)a; buildableB = *(gentity_t **)b; // Prefer the one that collides with the thing we're building - aCollides = G_BuildablesIntersect( cmpBuildable, cmpOrigin, + aMatches = G_BuildablesIntersect( cmpBuildable, cmpOrigin, buildableA->s.modelindex, buildableA->s.origin ); - bCollides = G_BuildablesIntersect( cmpBuildable, cmpOrigin, + bMatches = G_BuildablesIntersect( cmpBuildable, cmpOrigin, buildableB->s.modelindex, buildableB->s.origin ); - if( aCollides && !bCollides ) + if( aMatches && !bMatches ) return -1; - else if( !aCollides && bCollides ) + else if( !aMatches && bMatches ) return 1; // If one matches the thing we're building, prefer it @@ -2616,9 +2663,22 @@ static int G_CompareBuildablesForRemoval( const void *a, const void *b ) else if( !aMatches && bMatches ) return 1; - // If they're the same type then pick the one marked earliest + // They're the same type if( buildableA->s.modelindex == buildableB->s.modelindex ) + { + gentity_t *powerEntity = G_PowerEntityForPoint( cmpOrigin ); + + // Prefer the entity that is providing power for this point + aMatches = ( powerEntity == buildableA ); + bMatches = ( powerEntity == buildableB ); + if( aMatches && !bMatches ) + return -1; + else if( !aMatches && bMatches ) + return 1; + + // Pick the one marked earliest return buildableA->deconstructTime - buildableB->deconstructTime; + } // Resort to preference list for( i = 0; i < sizeof( precedence ) / sizeof( precedence[ 0 ] ); i++ ) @@ -2676,6 +2736,8 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, int remainingBP, remainingSpawns; qboolean collision = qfalse; int collisionCount = 0; + qboolean repeaterInRange = qfalse; + int repeaterInRangeCount = 0; itemBuildError_t bpError; buildable_t spawn; buildable_t core; @@ -2750,6 +2812,17 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, collisionCount++; } + // Check if this is a repeater and it's in range + if( buildable == BA_H_REPEATER && + buildable == ent->s.modelindex && + Distance( ent->s.origin, origin ) < REPEATER_BASESIZE ) + { + repeaterInRange = qtrue; + repeaterInRangeCount++; + } + else + repeaterInRange = qfalse; + if( !ent->inuse ) continue; @@ -2772,12 +2845,20 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, { level.markedBuildables[ numBuildables++ ] = ent; - if( collision ) + // Buildables that are marked here will always end up at the front of the + // removal list, so just incrementing numBuildablesForRemoval is sufficient + if( collision || repeaterInRange ) { - // Collided with something, so we definitely have to remove it - // It will always end up at the front of the removal list, so - // incrementing numBuildablesForRemoval is sufficient - collisionCount--; + // Collided with something, so we definitely have to remove it or + // it's a repeater that intersects the new repeater's power area, + // so it must be removed + + if( collision ) + collisionCount--; + + if( repeaterInRange ) + repeaterInRangeCount--; + pointsYielded += BG_FindBuildPointsForBuildable( ent->s.modelindex ); level.numBuildablesForRemoval++; } @@ -2799,6 +2880,10 @@ static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, if( collisionCount > 0 ) return IBE_NOROOM; + // There are one or more repeaters we can't remove + if( repeaterInRangeCount > 0 ) + return IBE_RPTWARN2; + // Sort the list cmpBuildable = buildable; VectorCopy( origin, cmpOrigin ); @@ -2867,7 +2952,6 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance vec3_t entity_origin, normal; vec3_t mins, maxs; trace_t tr1, tr2, tr3; - int i; itemBuildError_t reason = IBE_NONE, tempReason; gentity_t *tempent; float minNormal; @@ -2909,15 +2993,13 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance { //alien criteria - if( buildable == BA_A_HOVEL ) + // Check there is an Overmind + if( buildable != BA_A_OVERMIND ) { - vec3_t builderMins, builderMaxs; - - //this assumes the adv builder is the biggest thing that'll use the hovel - BG_FindBBoxForClass( PCL_ALIEN_BUILDER0_UPG, builderMins, builderMaxs, NULL, NULL, NULL ); + tempent = G_FindBuildable( BA_A_OVERMIND ); - if( APropHovel_Blocked( angles, origin, normal, ent ) ) - reason = IBE_HOVELEXIT; + if( tempent == NULL || !tempent->spawned || tempent->health <= 0 ) + reason = IBE_OVERMIND; } //check there is creep near by for building on @@ -2927,59 +3009,27 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance reason = IBE_NOCREEP; } - //check permission to build here - if( tr1.surfaceFlags & SURF_NOALIENBUILD || tr1.surfaceFlags & SURF_NOBUILD || - contents & CONTENTS_NOALIENBUILD || contents & CONTENTS_NOBUILD ) - reason = IBE_PERMISSION; - - //look for an Overmind - for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) - { - if( tempent->s.eType != ET_BUILDABLE ) - continue; - if( tempent->s.modelindex == BA_A_OVERMIND && tempent->spawned && - tempent->health > 0 ) - break; - } - - //if none found... - if( i >= level.num_entities && buildable != BA_A_OVERMIND ) - reason = IBE_NOOVERMIND; - - //can we only have one of these? - if( BG_FindUniqueTestForBuildable( buildable ) ) + if( buildable == BA_A_HOVEL ) { - for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) - { - if( tempent->s.eType != ET_BUILDABLE ) - continue; - - if( tempent->s.modelindex == buildable && !tempent->deconstruct ) - { - switch( buildable ) - { - case BA_A_OVERMIND: - reason = IBE_OVERMIND; - break; - - case BA_A_HOVEL: - reason = IBE_HOVEL; - break; + vec3_t builderMins, builderMaxs; - default: - Com_Error( ERR_FATAL, "No reason for denying build of %d\n", buildable ); - break; - } + //this assumes the adv builder is the biggest thing that'll use the hovel + BG_FindBBoxForClass( PCL_ALIEN_BUILDER0_UPG, builderMins, builderMaxs, NULL, NULL, NULL ); - break; - } - } + if( APropHovel_Blocked( angles, origin, normal, ent ) ) + reason = IBE_HOVELEXIT; } + + // Check permission to build here + if( tr1.surfaceFlags & SURF_NOALIENBUILD || contents & CONTENTS_NOALIENBUILD ) + reason = IBE_PERMISSION; } else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) { //human criteria - if( !G_IsPowered( entity_origin ) ) + + // Check for power + if( G_IsPowered( entity_origin ) == BA_NONE ) { //tell player to build a repeater to provide power if( buildable != BA_H_REACTOR && buildable != BA_H_REPEATER ) @@ -2993,58 +3043,48 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance //check that there is a parent reactor when building a repeater if( buildable == BA_H_REPEATER ) { - for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) - { - if( tempent->s.eType != ET_BUILDABLE ) - continue; + tempent = G_FindBuildable( BA_H_REACTOR ); - if( tempent->s.modelindex == BA_H_REACTOR ) - break; - } - - if( i >= level.num_entities ) - { - //no reactor present - - //check for other nearby repeaters - for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) - { - if( tempent->s.eType != ET_BUILDABLE ) - continue; - - if( tempent->s.modelindex == BA_H_REPEATER && - Distance( tempent->s.origin, entity_origin ) < REPEATER_BASESIZE ) - { - reason = IBE_RPTWARN2; - break; - } - } - - if( reason == IBE_NONE ) - reason = IBE_RPTWARN; - } - else if( G_IsPowered( entity_origin ) ) + if( tempent == NULL ) // No reactor + reason = IBE_RPTWARN; + else if( g_markDeconstruct.integer && G_IsPowered( entity_origin ) == BA_H_REACTOR ) + reason = IBE_RPTWARN2; + else if( !g_markDeconstruct.integer && G_IsPowered( entity_origin ) ) reason = IBE_RPTWARN2; } - //check permission to build here - if( tr1.surfaceFlags & SURF_NOHUMANBUILD || tr1.surfaceFlags & SURF_NOBUILD || - contents & CONTENTS_NOHUMANBUILD || contents & CONTENTS_NOBUILD ) + // Check permission to build here + if( tr1.surfaceFlags & SURF_NOHUMANBUILD || contents & CONTENTS_NOHUMANBUILD ) reason = IBE_PERMISSION; + } + + // Check permission to build here + if( tr1.surfaceFlags & SURF_NOBUILD || contents & CONTENTS_NOBUILD ) + reason = IBE_PERMISSION; - //can we only build one of these? - if( BG_FindUniqueTestForBuildable( buildable ) ) + // Can we only have one of these? + if( BG_FindUniqueTestForBuildable( buildable ) ) + { + tempent = G_FindBuildable( buildable ); + if( tempent && !tempent->deconstruct ) { - for ( i = 1, tempent = g_entities + i; i < level.num_entities; i++, tempent++ ) + switch( buildable ) { - if( tempent->s.eType != ET_BUILDABLE ) - continue; + case BA_A_OVERMIND: + reason = IBE_OVERMIND; + break; - if( tempent->s.modelindex == BA_H_REACTOR && !tempent->deconstruct ) - { + case BA_A_HOVEL: + reason = IBE_HOVEL; + break; + + case BA_H_REACTOR: reason = IBE_REACTOR; break; - } + + default: + Com_Error( ERR_FATAL, "No reason for denying build of %d\n", buildable ); + break; } } } @@ -3112,16 +3152,7 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, vec3_t ori else { // in-game building by a player - - if( builder->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) - { - if( builder->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) - VectorSet( normal, 0.0f, 0.0f, -1.0f ); - else - VectorCopy( builder->client->ps.grapplePoint, normal ); - } - else - VectorSet( normal, 0.0f, 0.0f, 1.0f ); + BG_GetClientNormal( &builder->client->ps, normal ); } // when building the initial layout, spawn the entity slightly off its diff --git a/src/game/g_combat.c b/src/game/g_combat.c index 5f889b0e..6c8dcc2c 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -701,10 +701,7 @@ static float G_CalcDamageModifier( vec3_t point, gentity_t *targ, gentity_t *att clientHeight = targ->r.maxs[ 2 ] - targ->r.mins[ 2 ]; - if( targ->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) - VectorCopy( targ->client->ps.grapplePoint, normal ); - else - VectorSet( normal, 0, 0, 1 ); + BG_GetClientNormal( &targ->client->ps, normal ); VectorMA( targOrigin, targ->r.mins[ 2 ], normal, floor ); VectorSubtract( point, floor, pMINUSfloor ); diff --git a/src/game/g_local.h b/src/game/g_local.h index 45f649d0..643757aa 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -749,7 +749,7 @@ qboolean AHovel_Blocked( gentity_t *hovel, gentity_t *player, qboolean gentity_t *G_CheckSpawnPoint( int spawnNum, vec3_t origin, vec3_t normal, buildable_t spawn, vec3_t spawnOrigin ); -qboolean G_IsPowered( vec3_t origin ); +buildable_t G_IsPowered( vec3_t origin ); qboolean G_IsDCCBuilt( void ); qboolean G_IsOvermindBuilt( void ); diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c index cfc7cc1f..6ff825f7 100644 --- a/src/game/g_weapon.c +++ b/src/game/g_weapon.c @@ -768,8 +768,9 @@ void buildFire( gentity_t *ent, dynMenu_t menu ) ent->client->ps.stats[ STAT_MISC ] += BG_FindBuildDelayForWeapon( ent->s.weapon ) * 2; } - else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS && !G_IsPowered( muzzle ) && - ( ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) != BA_H_REPEATER ) //hack + else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS && + G_IsPowered( muzzle ) == BA_NONE && + ( ent->client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) != BA_H_REPEATER ) //hack { ent->client->ps.stats[ STAT_MISC ] += BG_FindBuildDelayForWeapon( ent->s.weapon ) * 2; @@ -1430,10 +1431,12 @@ set muzzle location relative to pivoting eye */ void CalcMuzzlePoint( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) { + vec3_t normal; + VectorCopy( ent->s.pos.trBase, muzzlePoint ); - muzzlePoint[ 2 ] += ent->client->ps.viewheight; + BG_GetClientNormal( &ent->client->ps, normal ); + VectorMA( muzzlePoint, ent->client->ps.viewheight, normal, muzzlePoint ); VectorMA( muzzlePoint, 1, forward, muzzlePoint ); - VectorMA( muzzlePoint, 1, right, muzzlePoint ); // snap to integer coordinates for more efficient network bandwidth usage SnapVector( muzzlePoint ); } |