/* * G_TeleportPlayer */ void G_TeleportPlayer( edict_t *player, edict_t *dest ) { int i; vec3_t velocity; mat3_t axis; float speed; gclient_t *client = player->r.client; if( !dest ) { return; } if( !client ) { return; } // draw the teleport entering effect G_TeleportEffect( player, false ); // // teleport the player // // from racesow - use old pmove velocity VectorCopy( client->old_pmove.velocity, velocity ); velocity[2] = 0; // ignore vertical velocity speed = VectorLengthFast( velocity ); AnglesToAxis( dest->s.angles, axis ); VectorScale( &axis[AXIS_FORWARD], speed, client->ps.pmove.velocity ); VectorCopy( dest->s.angles, client->ps.viewangles ); VectorCopy( dest->s.origin, client->ps.pmove.origin ); // set the delta angle for ( i = 0; i < 3; i++ ) client->ps.pmove.delta_angles[i] = ANGLE2SHORT( client->ps.viewangles[i] ) - client->ucmd.angles[i]; client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; client->ps.pmove.pm_time = 1; // force the minimum no control delay player->s.teleported = true; // update the entity from the pmove VectorCopy( client->ps.viewangles, player->s.angles ); VectorCopy( client->ps.pmove.origin, player->s.origin ); VectorCopy( client->ps.pmove.origin, player->s.old_origin ); VectorCopy( client->ps.pmove.origin, player->olds.origin ); VectorCopy( client->ps.pmove.velocity, player->velocity ); // unlink to make sure it can't possibly interfere with KillBox GClip_UnlinkEntity( player ); // kill anything at the destination KillBox( player ); GClip_LinkEntity( player ); // add the teleport effect at the destination G_TeleportEffect( player, true ); }
static bool BOT_DMclass_FindRocket( edict_t *self, vec3_t away_from_rocket ) { #define AI_ROCKET_DETECT_RADIUS 1000 #define AI_ROCKET_DANGER_RADIUS 200 int i, numtargets; int targets[MAX_EDICTS]; edict_t *target; float min_roxx_time = 1.0f; bool any_rocket = false; numtargets = GClip_FindRadius( self->s.origin, AI_ROCKET_DETECT_RADIUS, targets, MAX_EDICTS ); for( i = 0; i < numtargets; i++ ) { target = game.edicts + targets[i]; // Missile detection code if( target->r.svflags & SVF_PROJECTILE && target->s.type != ET_PLASMA ) // (plasmas come in bunchs so are too complex for the bot to dodge) { if( target->r.owner && target->r.owner != self ) { vec3_t end; trace_t trace; VectorMA( target->s.origin, 2, target->velocity, end ); G_Trace( &trace, target->s.origin, target->r.mins, target->r.maxs, end, target, MASK_SOLID ); if( trace.fraction < min_roxx_time ) { vec_t l; any_rocket = true; min_roxx_time = trace.fraction; VectorSubtract( trace.endpos, self->s.origin, end ); // ok... end is where the impact will be. // trace.fraction is the time. if( ( l = VectorLengthFast( end ) ) < AI_ROCKET_DANGER_RADIUS ) { RotatePointAroundVector( away_from_rocket, &axis_identity[AXIS_UP], end, -self->s.angles[YAW] ); VectorNormalize( away_from_rocket ); if( fabs( away_from_rocket[0] ) < 0.3 ) away_from_rocket[0] = 0; if( fabs( away_from_rocket[1] ) < 0.3 ) away_from_rocket[1] = 0; away_from_rocket[2] = 0; away_from_rocket[0] *= -1.0f; away_from_rocket[1] *= -1.0f; if( nav.debugMode && bot_showcombat->integer > 2 ) G_PrintChasersf( self, "%s: ^1projectile dodge: ^2%f, %f d=%f^7\n", self->ai->pers.netname, away_from_rocket[0], away_from_rocket[1], l ); } } } } } return any_rocket; #undef AI_ROCKET_DETECT_RADIUS #undef AI_ROCKET_DANGER_RADIUS }
/* * G_ClientAddDamageIndicatorImpact */ void G_ClientAddDamageIndicatorImpact( gclient_t *client, int damage, const vec3_t basedir ) { edict_t *ent; vec3_t dir; float frac; if( damage < 1 ) return; if( !client || client - game.clients < 0 || client - game.clients >= gs.maxclients ) return; ent = &game.edicts[ ( client - game.clients ) + 1 ]; if( !basedir ) { VectorCopy( vec3_origin, dir ); } else { VectorNormalize2( basedir, dir ); //#define ACCENT_SCALE 2.0f #ifdef ACCENT_SCALE // accent the vertical or horizontal aspect of the direction if( VectorLengthFast( tv( dir[0], dir[1], 0 ) ) > dir[2] ) { dir[0] *= ACCENT_SCALE; dir[1] *= ACCENT_SCALE; } else dir[2] *= ACCENT_SCALE; VectorNormalizeFast( dir ); #endif #undef ACCENT_SCALE } frac = damage / ( damage + client->resp.snap.damageTaken ); VectorLerp( client->resp.snap.damageTakenDir, frac, dir, client->resp.snap.damageTakenDir ); client->resp.snap.damageTaken += damage; }
static void UpdatePoint( edict_t *who ) { vec3_t angles, forward, diff; trace_t trace; edict_t *ent, *ent_best = NULL; int i, j; float value, value_best = 0.35f; // if nothing better is found, print nothing gclient_t *client = who->r.client; vec3_t boxpoints[8], viewpoint; AngleVectors( client->ps.viewangles, forward, NULL, NULL ); VectorCopy( who->s.origin, viewpoint ); viewpoint[2] += who->viewheight; for( i = 0; i < game.numentities; i++ ) { ent = game.edicts + i; if( !ent->r.inuse || !ent->s.modelindex || ent == who ) continue; if( G_ISGHOSTING( ent ) ) continue; if( ent->s.type != ET_PLAYER && ent->s.type != ET_ITEM ) continue; VectorSubtract( ent->s.origin, viewpoint, angles ); VectorNormalize( angles ); VectorSubtract( forward, angles, diff ); for( j = 0; j < 3; j++ ) if( diff[j] < 0 ) diff[j] = -diff[j]; value = VectorLengthFast( diff ); if( value < value_best ) { BuildBoxPoints( boxpoints, ent->s.origin, ent->r.mins, ent->r.maxs ); for( j = 0; j < 8; j++ ) { G_Trace( &trace, viewpoint, vec3_origin, vec3_origin, boxpoints[j], who, MASK_OPAQUE ); if( trace.fraction == 1 ) { value_best = value; ent_best = ent; break; } } } } if( ent_best != NULL ) { point = ent_best; VectorCopy( ent_best->s.origin, point_location ); } else { vec3_t dest; VectorMA( viewpoint, 8192, forward, dest ); G_Trace( &trace, viewpoint, vec3_origin, vec3_origin, dest, who, MASK_OPAQUE ); point = NULL; VectorCopy( trace.endpos, point_location ); } }
//========================================== // AI_Think // think funtion for AIs //========================================== void AI_Think( edict_t *self ) { if( !self->ai || self->ai->type == AI_INACTIVE ) return; if( level.spawnedTimeStamp + 5000 > game.realtime || !level.canSpawnEntities ) { self->nextThink = level.time + game.snapFrameTime; return; } // check for being blocked if( !G_ISGHOSTING( self ) ) { AI_CategorizePosition( self ); if( VectorLengthFast( self->velocity ) > 37 ) self->ai->blocked_timeout = level.time + 10000; // if completely stuck somewhere if( self->ai->blocked_timeout < level.time ) { self->ai->pers.blockedTimeout( self ); return; } } //update status information to feed up ai if( self->ai->statusUpdateTimeout <= level.time ) AI_UpdateStatus( self ); if( AI_NodeHasTimedOut( self ) ) AI_ClearGoal( self ); if( self->ai->goal_node == NODE_INVALID ) AI_PickLongRangeGoal( self ); //if( self == level.think_client_entity ) AI_PickShortRangeGoal( self ); self->ai->pers.RunFrame( self ); // Show the path if( nav.debugMode && bot_showpath->integer && self->ai->goal_node != NODE_INVALID ) { // only draw the path of those bots which are being chased edict_t *chaser; bool chaserFound = false; for( chaser = game.edicts + 1; ENTNUM( chaser ) < gs.maxclients; chaser++ ) { if( chaser->r.client->resp.chase.active && chaser->r.client->resp.chase.target == ENTNUM( self ) ) { AITools_DrawPath( self, self->ai->goal_node ); chaserFound = true; } } if( !chaserFound && game.numBots == 1 ) AITools_DrawPath( self, self->ai->goal_node ); } }
void BOT_DMclass_MoveWander( edict_t *self, usercmd_t *ucmd ) { vec3_t temp; if( self->deadflag ) return; if( !self->r.client->ps.pmove.stats[PM_STAT_MAXSPEED] ) { return; } // Special check for elevators, stand still until the ride comes to a complete stop. if( self->groundentity && self->groundentity->use == Use_Plat ) { if( self->groundentity->moveinfo.state != STATE_UP && self->groundentity->moveinfo.state != STATE_DOWN ) { self->velocity[0] = 0; self->velocity[1] = 0; self->velocity[2] = 0; return; } } // Move To Goal (Short Range Goal, not following paths) if( !AI_MoveToShortRangeGoalEntity( self, ucmd ) ) { // Swimming? VectorCopy( self->s.origin, temp ); temp[2] += 24; if( G_PointContents( temp ) & MASK_WATER ) { // If drowning and no node, move up if( self->r.client && self->r.client->resp.next_drown_time > 0 ) { ucmd->upmove = 1; self->s.angles[PITCH] = -45; } else ucmd->upmove = 1; ucmd->forwardmove = 1; } // else self->r.client->next_drown_time = 0; // probably shound not be messing with this, but // Lava? temp[2] -= 48; if( G_PointContents( temp ) & ( CONTENTS_LAVA|CONTENTS_SLIME ) ) { self->s.angles[YAW] += random() * 360 - 180; ucmd->forwardmove = 1; if( self->groundentity ) ucmd->upmove = 1; else ucmd->upmove = 0; return; } // Check for special movement if( VectorLengthFast( self->velocity ) < 37 ) { if( random() > 0.1 && AI_SpecialMove( self, ucmd ) ) //jumps, crouches, turns... return; self->s.angles[YAW] += random() * 180 - 90; if( !self->is_step ) // if there is ground continue otherwise wait for next move ucmd->forwardmove = 0; //0 else if( AI_CanMove( self, BOT_MOVE_FORWARD ) ) { ucmd->forwardmove = 1; ucmd->buttons |= BUTTON_WALK; } return; } // Otherwise move slowly, walking wondering what's going on ucmd->buttons |= BUTTON_WALK; } if( AI_CanMove( self, BOT_MOVE_FORWARD ) ) ucmd->forwardmove = 1; else ucmd->forwardmove = -1; }
void BOT_DMclass_Move( edict_t *self, usercmd_t *ucmd ) { #define BOT_FORWARD_EPSILON 0.5f int i; unsigned int linkType; bool printLink = false; bool nodeReached = false; bool specialMovement = false; vec3_t v1, v2; vec3_t lookdir, pathdir; float lookDot; if( self->ai->next_node == NODE_INVALID || self->ai->goal_node == NODE_INVALID ) { BOT_DMclass_MoveWander( self, ucmd ); return; } linkType = AI_CurrentLinkType( self ); specialMovement = ( self->ai->path.numNodes >= MIN_BUNNY_NODES ) ? true : false; if( AI_GetNodeFlags( self->ai->next_node ) & (NODEFLAGS_REACHATTOUCH|NODEFLAGS_ENTITYREACH) ) specialMovement = false; if( linkType & (LINK_JUMP|LINK_JUMPPAD|LINK_CROUCH|LINK_FALL|LINK_WATER|LINK_LADDER|LINK_ROCKETJUMP) ) specialMovement = false; if( self->ai->pers.skillLevel < 0.33f ) specialMovement = false; if( specialMovement == false || self->groundentity ) self->ai->is_bunnyhop = false; VectorSubtract( nodes[self->ai->next_node].origin, self->s.origin, self->ai->move_vector ); // 2D, normalized versions of look and path directions pathdir[0] = self->ai->move_vector[0]; pathdir[1] = self->ai->move_vector[1]; pathdir[2] = 0.0f; VectorNormalize( pathdir ); AngleVectors( self->s.angles, lookdir, NULL, NULL ); lookdir[2] = 0.0f; VectorNormalize( lookdir ); lookDot = DotProduct( lookdir, pathdir ); // Ladder movement if( self->is_ladder ) { ucmd->forwardmove = 0; ucmd->upmove = 1; ucmd->sidemove = 0; if( nav.debugMode && printLink ) G_PrintChasersf( self, "LINK_LADDER\n" ); nodeReached = AI_NodeReached_Generic( self ); } else if( linkType & LINK_JUMPPAD ) { VectorCopy( self->s.origin, v1 ); VectorCopy( nodes[self->ai->next_node].origin, v2 ); v1[2] = v2[2] = 0; if( DistanceFast( v1, v2 ) > 32 && lookDot > BOT_FORWARD_EPSILON ) { ucmd->forwardmove = 1; // push towards destination ucmd->buttons |= BUTTON_WALK; } nodeReached = self->groundentity != NULL && AI_NodeReached_Generic( self ); } // Platform riding - No move, riding elevator else if( linkType & LINK_PLATFORM ) { VectorCopy( self->s.origin, v1 ); VectorCopy( nodes[self->ai->next_node].origin, v2 ); v1[2] = v2[2] = 0; if( DistanceFast( v1, v2 ) > 32 && lookDot > BOT_FORWARD_EPSILON ) ucmd->forwardmove = 1; // walk to center ucmd->buttons |= BUTTON_WALK; ucmd->upmove = 0; ucmd->sidemove = 0; if( nav.debugMode && printLink ) G_PrintChasersf( self, "LINK_PLATFORM (riding)\n" ); self->ai->move_vector[2] = 0; // put view horizontal nodeReached = AI_NodeReached_PlatformEnd( self ); } // entering platform else if( AI_GetNodeFlags( self->ai->next_node ) & NODEFLAGS_PLATFORM ) { ucmd->forwardmove = 1; ucmd->upmove = 0; ucmd->sidemove = 0; if( lookDot <= BOT_FORWARD_EPSILON ) ucmd->buttons |= BUTTON_WALK; if( nav.debugMode && printLink ) G_PrintChasersf( self, "NODEFLAGS_PLATFORM (moving to plat)\n" ); // is lift down? for( i = 0; i < nav.num_navigableEnts; i++ ) { if( nav.navigableEnts[i].node == self->ai->next_node ) { //testing line //vec3_t tPoint; //int j; //for(j=0; j<3; j++)//center of the ent // tPoint[j] = nav.ents[i].ent->s.origin[j] + 0.5*(nav.ents[i].ent->r.mins[j] + nav.ents[i].ent->r.maxs[j]); //tPoint[2] = nav.ents[i].ent->s.origin[2] + nav.ents[i].ent->r.maxs[2]; //tPoint[2] += 8; //AITools_DrawLine( self->s.origin, tPoint ); //if not reachable, wait for it (only height matters) if( ( nav.navigableEnts[i].ent->s.origin[2] + nav.navigableEnts[i].ent->r.maxs[2] ) > ( self->s.origin[2] + self->r.mins[2] + AI_JUMPABLE_HEIGHT ) && nav.navigableEnts[i].ent->moveinfo.state != STATE_BOTTOM ) { self->ai->blocked_timeout = level.time + 10000; ucmd->forwardmove = 0; } } } nodeReached = AI_NodeReached_PlatformStart( self ); } // Falling off ledge or jumping else if( !self->groundentity && !self->is_step && !self->is_swim && !self->ai->is_bunnyhop ) { ucmd->upmove = 0; ucmd->sidemove = 0; ucmd->forwardmove = 0; if( lookDot > BOT_FORWARD_EPSILON ) { ucmd->forwardmove = 1; // add fake strafe accel if( !(linkType & LINK_FALL) || linkType & (LINK_JUMP|LINK_ROCKETJUMP) ) { if( linkType & LINK_JUMP ) { if( AI_AttemptWalljump( self ) ) { ucmd->buttons |= BUTTON_SPECIAL; } if( VectorLengthFast( tv( self->velocity[0], self->velocity[1], 0 ) ) < 600 ) VectorMA( self->velocity, 6.0f, lookdir, self->velocity ); } else { if( VectorLengthFast( tv( self->velocity[0], self->velocity[1], 0 ) ) < 450 ) VectorMA( self->velocity, 1.0f, lookdir, self->velocity ); } } } else if( lookDot < -BOT_FORWARD_EPSILON ) ucmd->forwardmove = -1; if( nav.debugMode && printLink ) G_PrintChasersf( self, "FLY MOVE\n" ); nodeReached = AI_NodeReached_Generic( self ); } else // standard movement { ucmd->forwardmove = 1; ucmd->upmove = 0; ucmd->sidemove = 0; // starting a jump if( ( linkType & LINK_JUMP ) ) { if( self->groundentity ) { trace_t trace; vec3_t v1, v2; if( nav.debugMode && printLink ) G_PrintChasersf( self, "LINK_JUMP\n" ); //check floor in front, if there's none... Jump! VectorCopy( self->s.origin, v1 ); VectorNormalize2( self->ai->move_vector, v2 ); VectorMA( v1, 18, v2, v1 ); v1[2] += self->r.mins[2]; VectorCopy( v1, v2 ); v2[2] -= AI_JUMPABLE_HEIGHT; G_Trace( &trace, v1, vec3_origin, vec3_origin, v2, self, MASK_AISOLID ); if( !trace.startsolid && trace.fraction == 1.0 ) { //jump! // prevent double jumping on crates VectorCopy( self->s.origin, v1 ); v1[2] += self->r.mins[2]; G_Trace( &trace, v1, tv( -12, -12, -8 ), tv( 12, 12, 0 ), v1, self, MASK_AISOLID ); if( trace.startsolid ) ucmd->upmove = 1; } } nodeReached = AI_NodeReached_Generic( self ); } // starting a rocket jump else if( ( linkType & LINK_ROCKETJUMP ) ) { if( nav.debugMode && printLink ) G_PrintChasersf( self, "LINK_ROCKETJUMP\n" ); if( !self->ai->rj_triggered && self->groundentity && ( self->s.weapon == WEAP_ROCKETLAUNCHER ) ) { self->s.angles[PITCH] = 170; ucmd->upmove = 1; ucmd->buttons |= BUTTON_ATTACK; self->ai->rj_triggered = true; } nodeReached = AI_NodeReached_Generic( self ); } else { // Move To Short Range goal (not following paths) // plats, grapple, etc have higher priority than SR Goals, cause the bot will // drop from them and have to repeat the process from the beginning if( AI_MoveToShortRangeGoalEntity( self, ucmd ) ) { nodeReached = AI_NodeReached_Generic( self ); } else if( specialMovement && !self->is_swim ) // bunny-hopping movement here { BOT_DMclass_SpecialMove( self, lookdir, pathdir, ucmd ); nodeReached = AI_NodeReached_Special( self ); } else { nodeReached = AI_NodeReached_Generic( self ); } } // if static assume blocked and try to get free if( VectorLengthFast( self->velocity ) < 37 && ( ucmd->forwardmove || ucmd->sidemove || ucmd->upmove ) ) { if( random() > 0.1 && AI_SpecialMove( self, ucmd ) ) // jumps, crouches, turns... return; self->s.angles[YAW] += brandom( -90, 90 ); } } // swimming if( self->is_swim ) { if( !( G_PointContents( nodes[self->ai->next_node].origin ) & MASK_WATER ) ) // Exit water ucmd->upmove = 1; } AI_ChangeAngle( self ); if( nodeReached ) AI_NodeReached( self ); #undef BOT_FORWARD_EPSILON }
void BOT_DMclass_SpecialMove( edict_t *self, vec3_t lookdir, vec3_t pathdir, usercmd_t *ucmd ) { bool wallJump = false; bool dash = true; bool bunnyhop = true; trace_t trace; vec3_t end; int n1, n2, nextMoveType; self->ai->is_bunnyhop = false; if( self->ai->path.numNodes < MIN_BUNNY_NODES ) return; // verify that the 2nd node is in front of us for dashing n1 = self->ai->path.nodes[self->ai->path.numNodes]; n2 = self->ai->path.nodes[self->ai->path.numNodes-1]; if( !AI_infront2D( lookdir, self->s.origin, nodes[n2].origin, 0.5 ) ) bunnyhop = false; // do not dash if the next link will be a fall, jump or // any other kind of special link nextMoveType = AI_PlinkMoveType( n1, n2 ); if( nextMoveType & (LINK_LADDER|LINK_PLATFORM|LINK_ROCKETJUMP|LINK_FALL|LINK_JUMP|LINK_CROUCH) ) dash = false; if( nextMoveType &(LINK_LADDER|LINK_PLATFORM|LINK_FALL|LINK_CROUCH) ) bunnyhop = false; #if 0 if( VectorLengthFast( self->velocity ) < AI_JUMP_SPEED ) { if( dash && self->groundentity ) // attempt dash { if( DotProduct( lookdir, pathdir ) > 0.9 ) { // do not dash unless both next nodes are visible if( AI_ReachabilityVisible( self, nodes[n1].origin ) && AI_ReachabilityVisible( self, nodes[n2].origin ) ) { ucmd->buttons |= BUTTON_SPECIAL; ucmd->sidemove = 0; ucmd->forwardmove = 1; self->ai->is_bunnyhop = true; } } } } else #endif if( bunnyhop && ( (nextMoveType &LINK_JUMP) || level.gametype.spawnableItemsMask == 0 ) ) { if( self->groundentity ) ucmd->upmove = 1; #if 0 // fake strafe-jumping acceleration if( VectorLengthFast( self->velocity ) < 700 && DotProduct( lookdir, pathdir ) > 0.6 ) VectorMA( self->velocity, 0.1f, lookdir, self->velocity ); #endif self->ai->is_bunnyhop = true; } if( wallJump ) { if( self->ai->move_vector[2] > 25 && DotProduct( self->velocity, pathdir ) < -0.2 ) { VectorMA( self->s.origin, 0.02, self->velocity, end ); G_Trace( &trace, self->s.origin, self->r.mins, self->r.maxs, end, self, MASK_AISOLID ); if( trace.fraction != 1.0f ) ucmd->buttons |= BUTTON_SPECIAL; } } // if pushing in the opposite direction of the path, reduce the push if( DotProduct( lookdir, pathdir ) < -0.33f ) ucmd->forwardmove = 0; }
static void old_teleporter_touch( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags ) { edict_t *dest; int i; vec3_t velocity, angles; mat3_t axis; float speed; vec3_t org; if( !other->r.client ) return; if( self->s.team && self->s.team != other->s.team ) return; if( other->r.client->ps.pmove.pm_type > PM_SPECTATOR ) return; if( self->spawnflags & 1 && other->r.client->ps.pmove.pm_type != PM_SPECTATOR ) return; // match countdown if( GS_MatchState() == MATCH_STATE_COUNTDOWN ) return; // wait delay if( self->timeStamp > level.time ) return; self->timeStamp = level.time + ( self->wait * 1000 ); dest = G_Find( NULL, FOFS( targetname ), self->target ); if( !dest ) { if( developer->integer ) G_Printf( "Couldn't find destination.\n" ); return; } if( self->s.modelindex ) { org[0] = self->s.origin[0] + 0.5 * ( self->r.mins[0] + self->r.maxs[0] ); org[1] = self->s.origin[1] + 0.5 * ( self->r.mins[1] + self->r.maxs[1] ); org[2] = self->s.origin[2] + 0.5 * ( self->r.mins[2] + self->r.maxs[2] ); } else VectorCopy( self->s.origin, org ); // play custom sound if any (played from the teleporter entrance) if( self->noise_index ) G_PositionedSound( org, CHAN_AUTO, self->noise_index, ATTN_NORM ); // draw the teleport entering effect G_TeleportEffect( other, false ); // // teleport the player // VectorCopy( other->r.client->ps.pmove.velocity, velocity ); velocity[2] = 0; // ignore vertical velocity speed = VectorLengthFast( velocity ); // if someone enters a portal backwards, inverse the destination YAW angle #if 0 VectorCopy( other->s.angles, angles ); angles[PITCH] = 0; AngleVectors( angles, axis[0], NULL, NULL ); VectorSubtract( org, other->s.origin, org ); VectorCopy( dest->s.angles, angles ); if( DotProduct( org, axis[0] ) < 0 ) angles[YAW] = anglemod( angles[YAW] - 180 ); #else VectorCopy( dest->s.angles, angles ); #endif AnglesToAxis( dest->s.angles, axis ); VectorScale( &axis[AXIS_FORWARD], speed, other->r.client->ps.pmove.velocity ); VectorCopy( angles, other->r.client->ps.viewangles ); VectorCopy( dest->s.origin, other->r.client->ps.pmove.origin ); // set the delta angle for( i = 0; i < 3; i++ ) other->r.client->ps.pmove.delta_angles[i] = ANGLE2SHORT( other->r.client->ps.viewangles[i] ) - other->r.client->ucmd.angles[i]; other->r.client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; other->s.teleported = qtrue; other->r.client->ps.pmove.pm_time = 1; // force the minimum no control delay // update the entity from the pmove VectorCopy( other->r.client->ps.viewangles, other->s.angles ); VectorCopy( other->r.client->ps.pmove.origin, other->s.origin ); VectorCopy( other->r.client->ps.pmove.origin, other->s.old_origin ); VectorCopy( other->r.client->ps.pmove.origin, other->olds.origin ); VectorCopy( other->r.client->ps.pmove.velocity, other->velocity ); // unlink to make sure it can't possibly interfere with KillBox GClip_UnlinkEntity( other ); // kill anything at the destination if( !KillBox( other ) ) { } GClip_LinkEntity( other ); // add the teleport effect at the destination G_TeleportEffect( other, true ); }