/* * BotUpdateInput */ void BotUpdateInput(bot_state_t *bs, int time, int elapsed_time) { bot_input_t bi; int j; /* add the delta angles to the bot's current view angles */ for(j = 0; j < 3; j++) bs->viewangles[j] = modeuler(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); /* change the bot view angles */ BotChangeViewAngles(bs, (float)elapsed_time / 1000); /* retrieve the bot input */ trap_EA_GetInput(bs->client, (float)time / 1000, &bi); /* respawn hack */ if(bi.actionflags & ACTION_RESPAWN) if(bs->lastucmd.buttons & BUTTON_PRIATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK); /* convert the bot input to a usercmd */ BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time); /* subtract the delta angles */ for(j = 0; j < 3; j++) bs->viewangles[j] = modeuler(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); }
/* ============== BotUpdateInput ============== */ void BotUpdateInput(bot_state_t *bs, int time, int elapsed_time) { bot_input_t bi; int j; //add the delta angles to the bot's current view angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //change the bot view angles BotChangeViewAngles(bs, (float) elapsed_time / 1000); //retrieve the bot input botlib_export->ea.EA_GetInput(bs->client, (float) time / 1000, &bi); //respawn hack if (bi.actionflags & ACTION_RESPAWN) { if (bs->lastucmd.buttons & BUTTON_ATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK); } //convert the bot input to a usercmd BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time); //subtract the delta angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } }
/* =================== MoveCmdToViewAngles Given a movement command, computes the floating point view angles requested by that command, given the inputted delta angles for that entity. The computed angles are stored in "view" NOTE: Remember that the deltas (ps->delta_angles) have the same type as the command angles-- they are stored as integers, not floating points. NOTE: This code is based on PM_UpdateViewAngles() in bg_pmove.c. =================== */ void MoveCmdToViewAngles(usercmd_t *cmd, int *delta, vec3_t view) { int short_angle; // The pitch angle has extra boundary checks short_angle = cmd->angles[PITCH] + delta[PITCH]; if (short_angle > 16000) short_angle = 16000; else if (short_angle < -16000) short_angle = -16000; view[PITCH] = SHORT2ANGLE(short_angle); // Translate the yaw and roll angles from shorts to floats view[YAW ] = SHORT2ANGLE(cmd->angles[YAW ] + delta[YAW ]); view[ROLL] = SHORT2ANGLE(cmd->angles[ROLL] + delta[ROLL]); // Make sure the angles are bounded in the standard manner // // NOTE: If the rest of the code is written well, this shouldn't // matter. Bounding in [0,360) should be just as good as [-180,+180). // But better safe than sorry. view[PITCH] = AngleNormalize180(view[PITCH]); view[YAW ] = AngleNormalize180(view[YAW ]); view[ROLL ] = AngleNormalize180(view[ROLL ]); }
void PM_ScaleUcmd( playerState_t *ps, usercmd_t *cmd, gentity_t *gent ) { if ( ps->vehicleModel != 0 ) {//driving a vehicle //clamp the turn rate int maxPitchSpeed = MAX_PITCHSPEED_X_WING;//switch, eventually? Or read from file? int diff = AngleNormalize180(SHORT2ANGLE((cmd->angles[PITCH]+ps->delta_angles[PITCH]))) - floor(ps->viewangles[PITCH]); if ( diff > maxPitchSpeed ) { cmd->angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] + maxPitchSpeed ) - ps->delta_angles[PITCH]; } else if ( diff < -maxPitchSpeed ) { cmd->angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] - maxPitchSpeed ) - ps->delta_angles[PITCH]; } //Um, WTF? When I turn in a certain direction, I start going backwards? Or strafing? int maxYawSpeed = MAX_YAWSPEED_X_WING;//switch, eventually? Or read from file? diff = AngleNormalize180(SHORT2ANGLE(cmd->angles[YAW]+ps->delta_angles[YAW]) - floor(ps->viewangles[YAW])); //clamp the turn rate if ( diff > maxYawSpeed ) { cmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] + maxYawSpeed ) - ps->delta_angles[YAW]; } else if ( diff < -maxYawSpeed ) { cmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] - maxYawSpeed ) - ps->delta_angles[YAW]; } } }
/* ============== BotUpdateInput ============== */ void BotUpdateInput( bot_state_t *bs, int time ) { bot_input_t bi; int j; //add the delta angles to the bot's current view angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] + SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } // BotChangeViewAngles( bs, (float) time / 1000 ); trap_EA_GetInput( bs->client, (float) time / 1000, &bi ); //respawn hack if ( bi.actionflags & ACTION_RESPAWN ) { if ( bs->lastucmd.buttons & BUTTON_ATTACK ) { bi.actionflags &= ~( ACTION_RESPAWN | ACTION_ATTACK ); } } // BotInputToUserCommand( &bi, &bs->lastucmd, bs->cur_ps.delta_angles, time ); bs->lastucmd.serverTime = time; //subtract the delta angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } }
void PM_ClampAngles (void) { short temp; int i; if (pm->s.pm_flags & PMF_TIME_TELEPORT) { pm->viewangles[YAW] = SHORT2ANGLE(pm->cmd.angles[YAW] + pm->s.delta_angles[YAW]); pm->viewangles[PITCH] = 0; pm->viewangles[ROLL] = 0; } else { /* circularly clamp the angles with deltas */ for (i=0 ; i<3 ; i++) { temp = pm->cmd.angles[i] + pm->s.delta_angles[i]; pm->viewangles[i] = SHORT2ANGLE(temp); } /* don't let the player look up or down more than 90 degrees */ if (pm->viewangles[PITCH] > 89 && pm->viewangles[PITCH] < 180) pm->viewangles[PITCH] = 89; else if (pm->viewangles[PITCH] < 271 && pm->viewangles[PITCH] >= 180) pm->viewangles[PITCH] = 271; } AngleVectors (pm->viewangles, pml.forward, pml.right, pml.up); }
void demoMoveUpdateAngles(void) { float oldAngle, newAngle;int i; for (i=0;i<3;i++) { oldAngle = SHORT2ANGLE( demo.oldcmd.angles[i] ); newAngle = SHORT2ANGLE( demo.cmd.angles[i] ); demo.cmdDeltaAngles[i] = AngleNormalize180( newAngle - oldAngle ); } }
qboolean NPC_FacePosition( vec3_t position, qboolean doPitch ) { vec3_t muzzle; vec3_t angles; float yawDelta; qboolean facing = qtrue; //Get the positions if ( NPC->client && (NPC->client->NPC_class == CLASS_RANCOR || NPC->client->NPC_class == CLASS_WAMPA) )// || NPC->client->NPC_class == CLASS_SAND_CREATURE) ) { CalcEntitySpot( NPC, SPOT_ORIGIN, muzzle ); muzzle[2] += NPC->r.maxs[2] * 0.75f; } else if ( NPC->client && NPC->client->NPC_class == CLASS_GALAKMECH ) { CalcEntitySpot( NPC, SPOT_WEAPON, muzzle ); } else { CalcEntitySpot( NPC, SPOT_HEAD_LEAN, muzzle );//SPOT_HEAD } //Find the desired angles GetAnglesForDirection( muzzle, position, angles ); NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); NPCInfo->desiredPitch = AngleNormalize360( angles[PITCH] ); if ( NPC->enemy && NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_ATST ) { // FIXME: this is kind of dumb, but it was the easiest way to get it to look sort of ok NPCInfo->desiredYaw += flrand( -5, 5 ) + sin( level.time * 0.004f ) * 7; NPCInfo->desiredPitch += flrand( -2, 2 ); } //Face that yaw NPC_UpdateAngles( qtrue, qtrue ); //Find the delta between our goal and our current facing yawDelta = AngleNormalize360( NPCInfo->desiredYaw - ( SHORT2ANGLE( ucmd.angles[YAW] + client->ps.delta_angles[YAW] ) ) ); //See if we are facing properly if ( fabs( yawDelta ) > VALID_ATTACK_CONE ) facing = qfalse; if ( doPitch ) { //Find the delta between our goal and our current facing float currentAngles = ( SHORT2ANGLE( ucmd.angles[PITCH] + client->ps.delta_angles[PITCH] ) ); float pitchDelta = NPCInfo->desiredPitch - currentAngles; //See if we are facing properly if ( fabs( pitchDelta ) > VALID_ATTACK_CONE ) facing = qfalse; } return facing; }
/* * CG_DemoCam_FreeFly */ int CG_DemoCam_FreeFly( void ) { usercmd_t cmd; const float SPEED = 500; if( cgs.demoPlaying && CamIsFree ) { vec3_t wishvel, wishdir, forward, right, up, moveangles; float fmove, smove, upmove, wishspeed, maxspeed; int i; maxspeed = 250; // run frame trap_NET_GetUserCmd( trap_NET_GetCurrentUserCmdNum() - 1, &cmd ); cmd.msec = cg.realFrameTime * 1000; cmd.forwardfrac = ( (float)cmd.forwardmove/(float)cmd.msec ); cmd.sidefrac = ( (float)cmd.sidemove/(float)cmd.msec ); cmd.upfrac = ( (float)cmd.upmove/(float)cmd.msec ); for( i = 0; i < 3; i++ ) moveangles[i] = SHORT2ANGLE( cmd.angles[i] ) + SHORT2ANGLE( freecam_delta_angles[i] ); AngleVectors( moveangles, forward, right, up ); VectorCopy( moveangles, cam_angles ); fmove = cmd.forwardfrac * SPEED; smove = cmd.sidefrac * SPEED; upmove = cmd.upfrac * SPEED; if( cmd.buttons & BUTTON_SPECIAL ) maxspeed *= 2; for( i = 0; i < 3; i++ ) wishvel[i] = forward[i] * fmove + right[i] * smove; wishvel[2] += upmove; wishspeed = VectorNormalize2( wishvel, wishdir ); if( wishspeed > maxspeed ) { wishspeed = maxspeed/wishspeed; VectorScale( wishvel, wishspeed, wishvel ); wishspeed = maxspeed; } VectorMA( cam_origin, cg.realFrameTime, wishvel, cam_origin ); cam_POVent = 0; cam_3dPerson = false; return VIEWDEF_CAMERA; } return VIEWDEF_PLAYERVIEW; }
/* ================ PM_UpdateViewAngles This can be used as another entry point when only the viewangles are being updated isntead of a full move ================ */ void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) { short temp; int i; if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) { return; // no view changes at all } if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) { return; // no view changes at all } // circularly clamp the angles with deltas for (i=0 ; i<3 ; i++) { temp = cmd->angles[i] + ps->delta_angles[i]; if ( i == PITCH ) { // don't let the player look up or down more than 90 degrees if ( temp > 16000 ) { ps->delta_angles[i] = 16000 - cmd->angles[i]; temp = 16000; } else if ( temp < -16000 ) { ps->delta_angles[i] = -16000 - cmd->angles[i]; temp = -16000; } } ps->viewangles[i] = SHORT2ANGLE(temp); } }
/* * Cl_PredictMovement * * Run the latest movement command through the player movement code locally, * using the resulting origin and angles to reduce perceived latency. */ void Cl_PredictMovement(void) { int i, ack, current; pm_move_t pm; float step; if (cls.state != CL_ACTIVE) return; if (!cl_predict->value || (cl.frame.ps.pmove.pm_flags & PMF_NO_PREDICTION)) { for (i = 0; i < 3; i++) { // just set angles cl.predicted_angles[i] = cl.angles[i] + SHORT2ANGLE(cl.frame.ps.pmove.delta_angles[i]); } return; } ack = cls.netchan.incoming_acknowledged; current = cls.netchan.outgoing_sequence; // if we are too far out of date, just freeze if (current - ack >= CMD_BACKUP) { Com_Warn("Cl_PredictMovement: Exceeded CMD_BACKUP.\n"); return; } // copy current state to pmove memset(&pm, 0, sizeof(pm)); pm.Trace = Cl_Trace; pm.PointContents = Cl_Pointcontents; pm.s = cl.frame.ps.pmove; pm.s.gravity = cl_gravity; // run frames while (++ack <= current) { const int frame = ack & CMD_MASK; const user_cmd_t *cmd = &cl.cmds[frame]; if (!cmd->msec) continue; pm.cmd = *cmd; Pmove(&pm); // save for debug checking VectorCopy(pm.s.origin, cl.predicted_origins[frame]); } step = pm.s.origin[2] * 0.125 - cl.predicted_origin[2]; if ((pm.s.pm_flags & PMF_ON_STAIRS) && step > 4.0) { // save for stair lerping cl.predicted_step_time = cls.real_time; cl.predicted_step = step; } // copy results out for rendering VectorScale(pm.s.origin, 0.125, cl.predicted_origin); VectorCopy(pm.angles, cl.predicted_angles); cl.underwater = pm.water_level > 2; }
void CL_ClampPitch(void) { float pitch; pitch = SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]); if (pitch > 180) { pitch -= 360; } if (cl.viewangles[PITCH] + pitch < -360) { cl.viewangles[PITCH] += 360; /* wrapped */ } if (cl.viewangles[PITCH] + pitch > 360) { cl.viewangles[PITCH] -= 360; /* wrapped */ } if (cl.viewangles[PITCH] + pitch > 89) { cl.viewangles[PITCH] = 89 - pitch; } if (cl.viewangles[PITCH] + pitch < -89) { cl.viewangles[PITCH] = -89 - pitch; } }
static void IN_MLookUp() { in_mlooking = qfalse; if ( !cl_freelook->integer ) { cl.viewangles[PITCH] = -SHORT2ANGLE(cl.snap.ps.delta_angles[PITCH]); } }
void Player::Think() { speed *= player_speed.GetFloat(); orgin.x += _usercmd.forwardmove * speed.x; orgin.y += _usercmd.forwardmove * speed.y; orgin.x += _usercmd.rightmove * speed.y; orgin.y += -_usercmd.rightmove * speed.x; orgin.z += _usercmd.upmove * player_speed.GetFloat(); for (int i = 0; i < 3; i++ ) { cmdAngles[i] = SHORT2ANGLE( _usercmd.angles[i] ); viewAngles[i] = idMath::AngleNormalize180( SHORT2ANGLE( _usercmd.angles[i])); } CalculateRenderView(); }
void IN_CenterView( void ) { qboolean ok = qtrue; if ( cgvm ) { ok = VM_Call( cgvm, CG_CHECKCENTERVIEW ); } if ( ok ) { cl.viewangles[PITCH] = -SHORT2ANGLE( cl.snap.ps.delta_angles[PITCH] ); } }
/* * IN_CenterView */ void IN_CenterView( void ) { if( cl.currentSnapNum > 0 ) { player_state_t *playerState; playerState = &cl.snapShots[cl.currentSnapNum & UPDATE_MASK].playerState; cl.viewangles[PITCH] = -SHORT2ANGLE( playerState->pmove.delta_angles[PITCH] ); } }
void sdVehicleInput::SetPlayer( idPlayer* player ) { data.player = player; if ( data.player != NULL ) { data.usercmd = gameLocal.usercmds[ player->entityNumber ]; } for ( int i = 0; i < 3; i++ ) { data.cmdAngles[ i ] = SHORT2ANGLE( data.usercmd.angles[ i ] ); } }
void IN_CenterView( int localPlayerNum ) { playerState_t *ps; if ( !cg.snap || cg.snap->playerNums[localPlayerNum] == -1 ) { return; } ps = &cg.snap->pss[localPlayerNum]; cg.localPlayers[localPlayerNum].viewangles[PITCH] = -SHORT2ANGLE(ps->delta_angles[PITCH]); }
/* ============== sdDeployMenu::UsercommandCallback ============== */ void sdDeployMenu::UsercommandCallback( usercmd_t& cmd ) { if ( modeToggle ) { cmd.forwardmove = 0; cmd.rightmove = 0; cmd.upmove = 0; for ( int i = 0; i < 3; i++ ) { if ( !gameLocal.IsPaused() ) { rotation += SHORT2ANGLE( cmd.angles[ i ] - lockedAngles[ i ] ); } cmd.angles[ i ] = lockedAngles[ i ]; } } }
static void CL_ClampPitch( void ) { float pitch; pitch = SHORT2ANGLE( cl.frame.ps.pmove.delta_angles[PITCH] ); if (pitch > 180) pitch -= 360; if (cl.viewangles[PITCH] + pitch < -360) cl.viewangles[PITCH] += 360; // wrapped if (cl.viewangles[PITCH] + pitch > 360) cl.viewangles[PITCH] -= 360; // wrapped if (cl.viewangles[PITCH] + pitch > 89) cl.viewangles[PITCH] = 89 - pitch; if (cl.viewangles[PITCH] + pitch < -89) cl.viewangles[PITCH] = -89 - pitch; }
/* =============== CG_OffsetShoulderView =============== */ void CG_OffsetShoulderView( void ) { int i; int cmdNum; usercmd_t cmd, oldCmd; vec3_t rotationAngles; vec3_t axis[ 3 ], rotaxis[ 3 ]; float deltaMousePitch; static float mousePitch; vec3_t forward, right, up; classConfig_t* classConfig; // Ignore following pitch; it's too jerky otherwise. if( !cg_thirdPersonPitchFollow.integer ) cg.refdefViewAngles[ PITCH ] = 0.0f; AngleVectors( cg.refdefViewAngles, forward, right, up ); classConfig = BG_ClassConfig( cg.snap->ps.stats[ STAT_CLASS ] ); VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 0 ], forward, cg.refdef.vieworg ); VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 1 ], right, cg.refdef.vieworg ); VectorMA( cg.refdef.vieworg, classConfig->shoulderOffsets[ 2 ], up, cg.refdef.vieworg ); // If someone is playing like this, the rest is already taken care of // so just get the firstperson effects and leave. if( !cg.demoPlayback && !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) { CG_OffsetFirstPersonView(); return; } // Get mouse input for camera rotation. cmdNum = trap_GetCurrentCmdNumber(); trap_GetUserCmd( cmdNum, &cmd ); trap_GetUserCmd( cmdNum - 1, &oldCmd ); // Prevent pitch from wrapping and clamp it within a [30, -50] range. // Cgame has no access to ps.delta_angles[] here, so we need to reproduce // it ourselves here. deltaMousePitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] ); if( fabs(deltaMousePitch) < 200.0f ) mousePitch += deltaMousePitch; // Handle pitch. rotationAngles[ PITCH ] = mousePitch; rotationAngles[ PITCH ] = AngleNormalize180( rotationAngles[ PITCH ] + AngleNormalize180( cg.refdefViewAngles[ PITCH ] ) ); if( rotationAngles [ PITCH ] < -90.0f ) rotationAngles [ PITCH ] = -90.0f; if( rotationAngles [ PITCH ] > 90.0f ) rotationAngles [ PITCH ] = 90.0f; // Yaw and Roll are much easier. rotationAngles[ YAW ] = SHORT2ANGLE( cmd.angles[ YAW ] ) + cg.refdefViewAngles[ YAW ]; rotationAngles[ ROLL ] = 0.0f; // Perform the rotations. AnglesToAxis( rotationAngles, axis ); if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) || !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse, cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) ) AxisCopy( axis, rotaxis ); AxisToAngles( rotaxis, rotationAngles ); // Actually set the viewangles. for( i = 0; i < 3; i++ ) cg.refdefViewAngles[ i ] = rotationAngles[ i ]; // Now run the first person stuff so we get various effects added. CG_OffsetFirstPersonView( ); }
/* =============== CG_OffsetThirdPersonView =============== */ void CG_OffsetThirdPersonView( void ) { int i; vec3_t forward, right, up; vec3_t view; trace_t trace; static vec3_t mins = { -8, -8, -8 }; static vec3_t maxs = { 8, 8, 8 }; vec3_t focusPoint; vec3_t surfNormal; int cmdNum; usercmd_t cmd, oldCmd; float range; vec3_t mouseInputAngles; vec3_t rotationAngles; vec3_t axis[ 3 ], rotaxis[ 3 ]; float deltaPitch; static float pitch; static vec3_t killerPos = { 0, 0, 0 }; // If cg_thirdpersonShoulderViewMode == 2, do shoulder view instead // If cg_thirdpersonShoulderViewMode == 1, do shoulder view when chasing // a wallwalker because it's really erratic to watch if( ( cg_thirdPersonShoulderViewMode.integer == 2 ) || ( ( cg_thirdPersonShoulderViewMode.integer == 1 ) && ( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) && ( cg.snap->ps.stats[ STAT_HEALTH ] > 0 ) ) ) { CG_OffsetShoulderView( ); return; } BG_GetClientNormal( &cg.predictedPlayerState, surfNormal ); // Set the view origin to the class's view height VectorMA( cg.refdef.vieworg, cg.predictedPlayerState.viewheight, surfNormal, cg.refdef.vieworg ); // Set the focus point where the camera will look (at the player's vieworg) VectorCopy( cg.refdef.vieworg, focusPoint ); // If player is dead, we want the player to be between us and the killer // so pretend that the player was looking at the killer, then place cam behind them. if( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 ) { int killerEntNum = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ]; // already looking at ourself if( killerEntNum != cg.snap->ps.clientNum ) { vec3_t lookDirection; if( cg.wasDeadLastFrame == qfalse || !cg_staticDeathCam.integer ) { VectorCopy( cg_entities[ killerEntNum ].lerpOrigin, killerPos ); cg.wasDeadLastFrame = qtrue; } VectorSubtract( killerPos, cg.refdef.vieworg, lookDirection ); vectoangles( lookDirection, cg.refdefViewAngles ); } } // get and rangecheck cg_thirdPersonRange range = cg_thirdPersonRange.value; if( range > 150.0f ) range = 150.0f; if( range < 30.0f ) range = 30.0f; // Calculate the angle of the camera's position around the player. // Unless in demo, PLAYING in third person, or in dead-third-person cam, allow the player // to control camera position offsets using the mouse position. if( cg.demoPlayback || ( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) && ( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) ) ) { // Collect our input values from the mouse. cmdNum = trap_GetCurrentCmdNumber(); trap_GetUserCmd( cmdNum, &cmd ); trap_GetUserCmd( cmdNum - 1, &oldCmd ); // Prevent pitch from wrapping and clamp it within a [-75, 90] range. // Cgame has no access to ps.delta_angles[] here, so we need to reproduce // it ourselves. deltaPitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] ); if( fabs(deltaPitch) < 200.0f ) { pitch += deltaPitch; } mouseInputAngles[ PITCH ] = pitch; mouseInputAngles[ YAW ] = -1.0f * SHORT2ANGLE( cmd.angles[ YAW ] ); // yaw is inverted mouseInputAngles[ ROLL ] = 0.0f; for( i = 0; i < 3; i++ ) mouseInputAngles[ i ] = AngleNormalize180( mouseInputAngles[ i ] ); // Set the rotation angles to be the view angles offset by the mouse input // Ignore the original pitch though; it's too jerky otherwise if( !cg_thirdPersonPitchFollow.integer ) cg.refdefViewAngles[ PITCH ] = 0.0f; for( i = 0; i < 3; i++ ) { rotationAngles[ i ] = AngleNormalize180(cg.refdefViewAngles[ i ]) + mouseInputAngles[ i ]; AngleNormalize180( rotationAngles[ i ] ); } // Don't let pitch go too high/too low or the camera flips around and // that's really annoying. // However, when we're not on the floor or ceiling (wallwalk) pitch // may not be pitch, so just let it go. if( surfNormal[ 2 ] > 0.5f || surfNormal[ 2 ] < -0.5f ) { if( rotationAngles[ PITCH ] > 85.0f ) rotationAngles[ PITCH ] = 85.0f; else if( rotationAngles[ PITCH ] < -85.0f ) rotationAngles[ PITCH ] = -85.0f; } // Perform the rotations specified by rotationAngles. AnglesToAxis( rotationAngles, axis ); if( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) || !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse, cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) ) AxisCopy( axis, rotaxis ); // Convert the new axis back to angles. AxisToAngles( rotaxis, rotationAngles ); } else { if( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) { // If we're playing the game in third person, the viewangles already // take care of our mouselook, so just use them. for( i = 0; i < 3; i++ ) rotationAngles[ i ] = cg.refdefViewAngles[ i ]; } else // dead { rotationAngles[ PITCH ] = 20.0f; rotationAngles[ YAW ] = cg.refdefViewAngles[ YAW ]; } } rotationAngles[ YAW ] -= cg_thirdPersonAngle.value; // Move the camera range distance back. AngleVectors( rotationAngles, forward, right, up ); VectorCopy( cg.refdef.vieworg, view ); VectorMA( view, -range, forward, view ); // Ensure that the current camera position isn't out of bounds and that there // is nothing between the camera and the player. if( !cg_cameraMode.integer ) { // Trace a ray from the origin to the viewpoint to make sure the view isn't // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); if( trace.fraction != 1.0f ) { VectorCopy( trace.endpos, view ); view[ 2 ] += ( 1.0f - trace.fraction ) * 32; // Try another trace to this position, because a tunnel may have the ceiling // close enogh that this is poking out. CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); VectorCopy( trace.endpos, view ); } } // Set the camera position to what we calculated. VectorCopy( view, cg.refdef.vieworg ); // The above checks may have moved the camera such that the existing viewangles // may not still face the player. Recalculate them to do so. // but if we're dead, don't bother because we'd rather see what killed us if( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) { VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint ); vectoangles( focusPoint, cg.refdefViewAngles ); } }
void IN_CenterView (void) { cl.viewangles[PITCH] = -SHORT2ANGLE(cl.snap.ps.delta_angles[PITCH]); }
/* ============== BotAI ============== */ int BotAI(int playernum, float thinktime) { bot_state_t *bs; char buf[1024], *args; int j; EA_ResetInput(playernum); // bs = botstates[playernum]; if (!bs || !bs->inuse) { BotAI_Print(PRT_FATAL, "BotAI: player %d is not setup\n", playernum); return qfalse; } //retrieve the current player state if (!BotAI_GetPlayerState( playernum, &bs->cur_ps )) { BotAI_Print(PRT_FATAL, "BotAI: failed to get player state for player %d\n", playernum); return qfalse; } //retrieve any waiting server commands while( trap_BotGetServerCommand(playernum, buf, sizeof(buf)) ) { //have buf point to the command and args to the command arguments args = strchr( buf, ' '); if (!args) continue; *args++ = '\0'; //remove color espace sequences from the arguments RemoveColorEscapeSequences( args ); if (!Q_stricmp(buf, "cp ")) { /*CenterPrintf*/ } else if (!Q_stricmp(buf, "cs")) { /*ConfigStringModified*/ } else if (!Q_stricmp(buf, "print")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; BotQueueConsoleMessage(bs->cs, CMS_NORMAL, args); } else if (!Q_stricmp(buf, "chat") || !Q_stricmp(buf, "tell")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); } else if (!Q_stricmp(buf, "tchat")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); } #ifdef MISSIONPACK else if (!Q_stricmp(buf, "vchat")) { BotVoiceChatCommand(bs, SAY_ALL, args); } else if (!Q_stricmp(buf, "vtchat")) { BotVoiceChatCommand(bs, SAY_TEAM, args); } else if (!Q_stricmp(buf, "vtell")) { BotVoiceChatCommand(bs, SAY_TELL, args); } #endif else if (!Q_stricmp(buf, "scores")) { /*FIXME: parse scores?*/ } else if (!Q_stricmp(buf, "clientLevelShot")) { /*ignore*/ } } //add the delta angles to the bot's current view angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //increase the local time of the bot bs->ltime += thinktime; // bs->thinktime = thinktime; //origin of the bot VectorCopy(bs->cur_ps.origin, bs->origin); //eye coordinates of the bot VectorCopy(bs->cur_ps.origin, bs->eye); bs->eye[2] += bs->cur_ps.viewheight; //get the area the bot is in bs->areanum = BotPointAreaNum(bs->origin); //the real AI BotDeathmatchAI(bs, thinktime); //set the weapon selection every AI frame EA_SelectWeapon(bs->playernum, bs->weaponnum); //subtract the delta angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //everything was ok return qtrue; }
/* ============== ClientThink This will be called once for each client frame, which will usually be a couple times for each server frame. ============== */ void ClientThink (edict_t *ent, usercmd_t *ucmd) { gclient_t *client; edict_t *other; int i, j; pmove_t pm; #if defined(_DEBUG) && defined(_Z_TESTMODE) if(testitemOriginMove && testItemDroped) { if(ucmd->forwardmove > 0) { testItemDroped->s.origin[2]++; } else if(ucmd->forwardmove < 0) { testItemDroped->s.origin[2]--; } return; } #endif level.current_entity = ent; client = ent->client; #ifdef GAME_MOD Blinky_BeginClientThink(ent, ucmd); #endif if (level.intermissiontime) { client->ps.pmove.pm_type = PM_FREEZE; // can exit intermission after five seconds if (level.time > level.intermissiontime + 5.0 && (ucmd->buttons & BUTTON_ANY) ) level.exitintermission = true; return; } if(ent->movetype == MOVETYPE_FREEZE) { client->ps.pmove.pm_type = PM_FREEZE; return; } pm_passent = ent; // set up for pmove memset (&pm, 0, sizeof(pm)); if (ent->movetype == MOVETYPE_NOCLIP) client->ps.pmove.pm_type = PM_SPECTATOR; else if (ent->s.modelindex != 255) client->ps.pmove.pm_type = PM_GIB; else if (ent->deadflag) client->ps.pmove.pm_type = PM_DEAD; else client->ps.pmove.pm_type = PM_NORMAL; client->ps.pmove.gravity = sv_gravity->value; pm.s = client->ps.pmove; for (i=0 ; i<3 ; i++) { pm.s.origin[i] = ent->s.origin[i]*8; pm.s.velocity[i] = ent->velocity[i]*8; } if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s))) { pm.snapinitial = true; // gi.dprintf ("pmove changed!\n"); } pm.cmd = *ucmd; pm.trace = PM_trace; // adds default parms pm.pointcontents = gi.pointcontents; // perform a pmove gi.Pmove (&pm); // save results of pmove client->ps.pmove = pm.s; client->old_pmove = pm.s; for (i=0 ; i<3 ; i++) { ent->s.origin[i] = pm.s.origin[i]*0.125; ent->velocity[i] = pm.s.velocity[i]*0.125; } VectorCopy (pm.mins, ent->mins); VectorCopy (pm.maxs, ent->maxs); client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) { gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0); PlayerNoise(ent, ent->s.origin, PNOISE_SELF); } ent->viewheight = pm.viewheight; ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; ent->groundentity = pm.groundentity; if (pm.groundentity) ent->groundentity_linkcount = pm.groundentity->linkcount; if (ent->deadflag) { client->ps.viewangles[ROLL] = 40; client->ps.viewangles[PITCH] = -15; client->ps.viewangles[YAW] = client->killer_yaw; } else { VectorCopy (pm.viewangles, client->v_angle); VectorCopy (pm.viewangles, client->ps.viewangles); } gi.linkentity (ent); if (ent->movetype != MOVETYPE_NOCLIP) G_TouchTriggers (ent); // touch other objects for (i=0 ; i<pm.numtouch ; i++) { other = pm.touchents[i]; for (j=0 ; j<i ; j++) if (pm.touchents[j] == other) break; if (j != i) continue; // duplicated if (!other->touch) continue; other->touch (other, ent, NULL, NULL); } client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->oldbuttons; // save light level the player is standing on for // monster sighting AI ent->light_level = ucmd->lightlevel; // fire weapon from final position if needed if (client->latched_buttons & BUTTON_ATTACK) { if (!client->weapon_thunk) { client->weapon_thunk = true; Think_Weapon (ent); } } #ifdef WITH_ACEBOT // ACEBOT_ADD if (!ent->is_bot && !ent->deadflag && !ent->client->resp.spectator) ACEND_PathMap(ent); // ACEBOT_END #endif }
/* ============== BotAI ============== */ int BotAI( int client, float thinktime ) { bot_state_t *bs; char buf[1024], *args; int j; trap_EA_ResetInput( client, NULL ); // bs = botstates[client]; if ( !bs || !bs->inuse ) { BotAI_Print( PRT_FATAL, "client %d hasn't been setup\n", client ); return BLERR_AICLIENTNOTSETUP; } //retrieve the current client state BotAI_GetClientState( client, &bs->cur_ps ); //retrieve any waiting console messages while ( trap_BotGetServerCommand( client, buf, sizeof( buf ) ) ) { //have buf point to the command and args to the command arguments args = strchr( buf, ' ' ); if ( !args ) { continue; } *args++ = '\0'; //remove color espace sequences from the arguments Q_CleanStr( args ); //botai_import.Print(PRT_MESSAGE, "ConsoleMessage: \"%s\"\n", buf); if ( !Q_stricmp( buf, "cp " ) ) { /*CenterPrintf*/ } else if ( !Q_stricmp( buf, "cs" ) ) { /*ConfigStringModified*/ } else if ( !Q_stricmp( buf, "print" ) ) { trap_BotQueueConsoleMessage( bs->cs, CMS_NORMAL, args ); } else if ( !Q_stricmp( buf, "chat" ) ) { trap_BotQueueConsoleMessage( bs->cs, CMS_CHAT, args ); } else if ( !Q_stricmp( buf, "tchat" ) ) { trap_BotQueueConsoleMessage( bs->cs, CMS_CHAT, args ); } else if ( !Q_stricmp( buf, "scores" ) ) { /*FIXME: parse scores?*/ } else if ( !Q_stricmp( buf, "clientLevelShot" ) ) { /*ignore*/ } } //add the delta angles to the bot's current view angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] + SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } //increase the local time of the bot bs->ltime += thinktime; // bs->thinktime = thinktime; //origin of the bot VectorCopy( bs->cur_ps.origin, bs->origin ); //eye coordinates of the bot VectorCopy( bs->cur_ps.origin, bs->eye ); bs->eye[2] += bs->cur_ps.viewheight; //get the area the bot is in bs->areanum = BotPointAreaNum( bs->origin ); //the real AI BotDeathmatchAI( bs, thinktime ); //set the weapon selection every AI frame trap_EA_SelectWeapon( bs->client, bs->weaponnum ); //subtract the delta angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } //everything was ok return BLERR_NOERROR; }
void CL_FinishMove( usercmd_t *cmd ) { int i; // copy the state that the cgame is currently sending cmd->weapon = cl.cgameUserCmdValue; cmd->forcesel = cl.cgameForceSelection; cmd->invensel = cl.cgameInvenSelection; if (cl.gcmdSendValue) { cmd->generic_cmd = cl.gcmdValue; //cl.gcmdSendValue = qfalse; cl.gcmdSentValue = qtrue; } else { cmd->generic_cmd = 0; } // send the current server time so the amount of movement // can be determined without allowing cheating cmd->serverTime = cl.serverTime; if (cl.cgameViewAngleForceTime > cl.serverTime) { cl.cgameViewAngleForce[YAW] -= SHORT2ANGLE(cl.snap.ps.delta_angles[YAW]); cl.viewangles[YAW] = cl.cgameViewAngleForce[YAW]; cl.cgameViewAngleForceTime = 0; } if ( cl_crazyShipControls ) { float pitchSubtract, pitchDelta, yawDelta; yawDelta = AngleSubtract(cl.viewangles[YAW],cl_lastViewAngles[YAW]); //yawDelta *= (4.0f*pVeh->m_fTimeModifier); cl_sendAngles[ROLL] -= yawDelta; float nRoll = fabs(cl_sendAngles[ROLL]); pitchDelta = AngleSubtract(cl.viewangles[PITCH],cl_lastViewAngles[PITCH]); //pitchDelta *= (2.0f*pVeh->m_fTimeModifier); pitchSubtract = pitchDelta * (nRoll/90.0f); cl_sendAngles[PITCH] += pitchDelta-pitchSubtract; //yaw-roll calc should be different if (nRoll > 90.0f) { nRoll -= 180.0f; } if (nRoll < 0.0f) { nRoll = -nRoll; } pitchSubtract = pitchDelta * (nRoll/90.0f); if ( cl_sendAngles[ROLL] > 0.0f ) { cl_sendAngles[YAW] += pitchSubtract; } else { cl_sendAngles[YAW] -= pitchSubtract; } cl_sendAngles[PITCH] = AngleNormalize180( cl_sendAngles[PITCH] ); cl_sendAngles[YAW] = AngleNormalize360( cl_sendAngles[YAW] ); cl_sendAngles[ROLL] = AngleNormalize180( cl_sendAngles[ROLL] ); for (i=0 ; i<3 ; i++) { cmd->angles[i] = ANGLE2SHORT(cl_sendAngles[i]); } } else { for (i=0 ; i<3 ; i++) { cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]); } //in case we switch to the cl_crazyShipControls VectorCopy( cl.viewangles, cl_sendAngles ); } //always needed in for the cl_crazyShipControls VectorCopy( cl.viewangles, cl_lastViewAngles ); }
/* ================ PM_UpdateViewAngles This can be used as another entry point when only the viewangles are being updated isntead of a full move //FIXME: Now that they pmove twice per think, they snap-look really fast ================ */ void PM_UpdateViewAngles( playerState_t *ps, usercmd_t *cmd, gentity_t *gent ) { short temp; float pitchMin=-75, pitchMax=75, yawMin=0, yawMax=0; //just to shut up warnings int i; vec3_t start, end, tmins, tmaxs, right; trace_t trace; qboolean lockedYaw = qfalse; if ( ps->pm_type == PM_INTERMISSION ) { return; // no view changes at all } if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) { return; // no view changes at all } // if ( player_locked ) // {//can't turn // return; // } if ( ps->eFlags & EF_NPC && gent != NULL && gent->client != NULL ) { if(gent->client->renderInfo.renderFlags & RF_LOCKEDANGLE) { pitchMin = 0 - gent->client->renderInfo.headPitchRangeUp - gent->client->renderInfo.torsoPitchRangeUp; pitchMax = gent->client->renderInfo.headPitchRangeDown + gent->client->renderInfo.torsoPitchRangeDown; yawMin = 0 - gent->client->renderInfo.headYawRangeLeft - gent->client->renderInfo.torsoYawRangeLeft; yawMax = gent->client->renderInfo.headYawRangeRight + gent->client->renderInfo.torsoYawRangeRight; lockedYaw = qtrue; } else { pitchMin = -gent->client->renderInfo.headPitchRangeUp-gent->client->renderInfo.torsoPitchRangeUp; pitchMax = gent->client->renderInfo.headPitchRangeDown+gent->client->renderInfo.torsoPitchRangeDown; } } if ( ps->eFlags & EF_LOCKED_TO_WEAPON ) { // Emplaced guns have different pitch capabilities pitchMin = -35; pitchMax = 30; } const short pitchClampMin = ANGLE2SHORT(pitchMin); const short pitchClampMax = ANGLE2SHORT(pitchMax); // circularly clamp the angles with deltas for (i=0 ; i<3 ; i++) { temp = cmd->angles[i] + ps->delta_angles[i]; if ( i == PITCH ) { //FIXME get this limit from the NPCs stats? // don't let the player look up or down more than 90 degrees if ( temp > pitchClampMax ) { ps->delta_angles[i] = (pitchClampMax - cmd->angles[i]) & 0xffff; //& clamp to short temp = pitchClampMax; } else if ( temp < pitchClampMin ) { ps->delta_angles[i] = (pitchClampMin - cmd->angles[i]) & 0xffff; //& clamp to short temp = pitchClampMin; } } if ( i == ROLL && ps->vehicleModel != 0 ) { if ( temp > pitchClampMax ) { ps->delta_angles[i] = (pitchClampMax - cmd->angles[i]) & 0xffff; temp = pitchClampMax; } else if ( temp < pitchClampMin ) { ps->delta_angles[i] = (pitchClampMin - cmd->angles[i]) & 0xffff; temp = pitchClampMin; } } //FIXME: Are we losing precision here? Is this why it jitters? ps->viewangles[i] = SHORT2ANGLE(temp); if ( i == YAW && lockedYaw) { // don't let the player look left or right more than the clamp, if any if ( AngleSubtract(ps->viewangles[i], gent->client->renderInfo.lockYaw) > yawMax ) { ps->viewangles[i] = yawMax; } else if ( AngleSubtract(ps->viewangles[i], gent->client->renderInfo.lockYaw) < yawMin ) { ps->viewangles[i] = yawMin; } } } if ( (!cg.renderingThirdPerson||cg.zoomMode) && (cmd->buttons & BUTTON_USE) && cmd->rightmove != 0 && !cmd->forwardmove && cmd->upmove <= 0 ) {//Only lean if holding use button, strafing and not moving forward or back and not jumping if ( gent ) { int leanofs = 0; vec3_t viewangles; if ( cmd->rightmove > 0 ) { /* if( pm->ps->legsAnim != LEGS_LEAN_RIGHT1) { PM_SetAnim(pm, SETANIM_LEGS, LEGS_LEAN_RIGHT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); } pm->ps->legsAnimTimer = 500;//Force it to hold the anim for at least half a sec */ if ( ps->leanofs <= 28 ) { leanofs = ps->leanofs + 4; } else { leanofs = 32; } } else { /* if ( pm->ps->legsAnim != LEGS_LEAN_LEFT1 ) { PM_SetAnim(pm, SETANIM_LEGS, LEGS_LEAN_LEFT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); } pm->ps->legsAnimTimer = 500;//Force it to hold the anim for at least half a sec */ if ( ps->leanofs >= -28 ) { leanofs = ps->leanofs - 4; } else { leanofs = -32; } } VectorCopy( ps->origin, start ); start[2] += ps->viewheight; VectorCopy( ps->viewangles, viewangles ); viewangles[ROLL] = 0; AngleVectors( ps->viewangles, NULL, right, NULL ); VectorNormalize( right ); right[2] = (leanofs<0)?0.25:-0.25; VectorMA( start, leanofs, right, end ); VectorSet( tmins, -8, -8, -4 ); VectorSet( tmaxs, 8, 8, 4 ); //if we don't trace EVERY frame, can TURN while leaning and //end up leaning into solid architecture (sigh) gi.trace( &trace, start, tmins, tmaxs, end, gent->s.number, MASK_PLAYERSOLID, (EG2_Collision)0, 0 ); ps->leanofs = floor((float)leanofs * trace.fraction); ps->leanStopDebounceTime = 20; } } else { if ( gent && (cmd->forwardmove || cmd->upmove > 0) ) { if( ( pm->ps->legsAnim == LEGS_LEAN_RIGHT1) || ( pm->ps->legsAnim == LEGS_LEAN_LEFT1) ) { pm->ps->legsAnimTimer = 0;//Force it to stop the anim } } if ( ps->leanofs > 0 ) { //FIXME: play lean anim backwards? ps->leanofs-=4; if ( ps->leanofs < 0 ) { ps->leanofs = 0; } } else if ( ps->leanofs < 0 ) { //FIXME: play lean anim backwards? ps->leanofs+=4; if ( ps->leanofs > 0 ) { ps->leanofs = 0; } } } if ( ps->leanStopDebounceTime ) { ps->leanStopDebounceTime -= 1; cmd->rightmove = 0; cmd->buttons &= ~BUTTON_USE; } }
/* ============== ClientThink This will be called once for each client frame, which will usually be a couple times for each server frame. ============== */ void ClientThink (edict_t *ent, usercmd_t *ucmd) { gclient_t *client; edict_t *other; int i, j; pmove_t pm; level.current_entity = ent; client = ent->client; if (level.intermissiontime) { client->ps.pmove.pm_type = PM_FREEZE; // can exit intermission after five seconds if (level.time > level.intermissiontime + 5.0 && (ucmd->buttons & BUTTON_ANY) ) level.exitintermission = true; return; } pm_passent = ent; if (ent->client->chase_target) { client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); } else { // set up for pmove memset (&pm, 0, sizeof(pm)); if (ent->movetype == MOVETYPE_NOCLIP) client->ps.pmove.pm_type = PM_SPECTATOR; else if (ent->s.modelindex != 255) client->ps.pmove.pm_type = PM_GIB; else if (ent->deadflag) client->ps.pmove.pm_type = PM_DEAD; else client->ps.pmove.pm_type = PM_NORMAL; client->ps.pmove.gravity = sv_gravity->value; pm.s = client->ps.pmove; for (i=0 ; i<3 ; i++) { pm.s.origin[i] = ent->s.origin[i]*8; pm.s.velocity[i] = ent->velocity[i]*8; } if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s))) { pm.snapinitial = true; // gi.dprintf ("pmove changed!\n"); } pm.cmd = *ucmd; pm.trace = PM_trace; // adds default parms pm.pointcontents = gi.pointcontents; // perform a pmove gi.Pmove (&pm); // save results of pmove client->ps.pmove = pm.s; client->old_pmove = pm.s; for (i=0 ; i<3 ; i++) { ent->s.origin[i] = pm.s.origin[i]*0.125; ent->velocity[i] = pm.s.velocity[i]*0.125; } VectorCopy (pm.mins, ent->mins); VectorCopy (pm.maxs, ent->maxs); client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) { gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0); PlayerNoise(ent, ent->s.origin, PNOISE_SELF); } ent->viewheight = pm.viewheight; ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; ent->groundentity = pm.groundentity; if (pm.groundentity) ent->groundentity_linkcount = pm.groundentity->linkcount; if (ent->deadflag) { client->ps.viewangles[ROLL] = 40; client->ps.viewangles[PITCH] = -15; client->ps.viewangles[YAW] = client->killer_yaw; } else { VectorCopy (pm.viewangles, client->v_angle); VectorCopy (pm.viewangles, client->ps.viewangles); } gi.linkentity (ent); if (ent->movetype != MOVETYPE_NOCLIP) G_TouchTriggers (ent); // touch other objects for (i=0 ; i<pm.numtouch ; i++) { other = pm.touchents[i]; for (j=0 ; j<i ; j++) if (pm.touchents[j] == other) break; if (j != i) continue; // duplicated if (!other->touch) continue; other->touch (other, ent, NULL, NULL); } } client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->oldbuttons; // save light level the player is standing on for // monster sighting AI ent->light_level = ucmd->lightlevel; // fire weapon from final position if needed if (client->latched_buttons & BUTTON_ATTACK) { if (client->resp.spectator) { client->latched_buttons = 0; if (client->chase_target) { client->chase_target = NULL; client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; } else GetChaseTarget(ent); } else if (!client->weapon_thunk) { client->weapon_thunk = true; Think_Weapon (ent); } } if (client->resp.spectator) { if (ucmd->upmove >= 10) { if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) { client->ps.pmove.pm_flags |= PMF_JUMP_HELD; if (client->chase_target) ChaseNext(ent); else GetChaseTarget(ent); } } else client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD; } // update chase cam if being followed for (i = 1; i <= maxclients->value; i++) { other = g_edicts + i; if (other->inuse && other->client->chase_target == ent) UpdateChaseCam(other); } }
/* ============ AICast_CreateCharacter returns 0 if unable to create the character ============ */ gentity_t *AICast_CreateCharacter( gentity_t *ent, float *attributes, cast_weapon_info_t *weaponInfo, char *castname, char *model, char *head, char *sex, char *color, char *handicap ) { gentity_t *newent; gclient_t *client; cast_state_t *cs; char **ppStr; int j; if (g_gametype.integer != GT_SINGLE_PLAYER) { // no cast AI in multiplayer return NULL; } // are bots enabled? if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) { G_Printf( S_COLOR_RED "ERROR: Unable to spawn %s, 'bot_enable' is not set\n", ent->classname ); return NULL; } // // make sure we have a free slot for them // if (level.numPlayingClients+1 > aicast_maxclients) { G_Error( "Exceeded sv_maxclients (%d), unable to create %s\n", aicast_maxclients, ent->classname ); return NULL; } // // add it to the list (only do this if everything else passed) // newent = AICast_AddCastToGame( ent, castname, model, head, sex, color, handicap ); if (!newent) { return NULL; } client = newent->client; // // setup the character.. // cs = AICast_GetCastState( newent->s.number ); // // setup the attributes memcpy( cs->attributes, attributes, sizeof(cs->attributes) ); ppStr = &ent->aiAttributes; AICast_CheckLevelAttributes( cs, ent, ppStr ); // AICast_SetAASIndex( cs ); // make sure they face the right direction VectorCopy( ent->s.angles, cs->bs->ideal_viewangles ); // factor in the delta_angles for (j = 0; j < 3; j++) { cs->bs->viewangles[j] = AngleMod(newent->s.angles[j] - SHORT2ANGLE(newent->client->ps.delta_angles[j])); } VectorCopy( ent->s.angles, newent->s.angles ); VectorCopy( ent->s.origin, cs->startOrigin ); // cs->lastEnemy = -1; cs->bs->enemy = -1; cs->leaderNum = -1; cs->castScriptStatus.scriptGotoEnt = -1; cs->aiCharacter = ent->aiCharacter; // newent->aiName = ent->aiName; newent->aiTeam = ent->aiTeam; newent->targetname = ent->targetname; // newent->AIScript_AlertEntity = ent->AIScript_AlertEntity; newent->aiInactive = ent->aiInactive; newent->aiCharacter = cs->aiCharacter; // // parse the AI script for this character (if applicable) cs->aiFlags |= AIFL_CORPSESIGHTING; // this is on by default for all characters, disabled if they have a "friendlysightcorpse" script event AICast_ScriptParse( cs ); // // setup bounding boxes //VectorCopy( mins, client->ps.mins ); //VectorCopy( maxs, client->ps.maxs ); AIChar_SetBBox( newent, cs ); client->ps.friction = cs->attributes[RUNNING_SPEED]/300.0; // // clear weapons/ammo client->ps.weapon = 0; memcpy( client->ps.weapons, weaponInfo->startingWeapons, sizeof(weaponInfo->startingWeapons) ); memcpy( client->ps.ammo, weaponInfo->startingAmmo, sizeof(client->ps.ammo) ); // // starting health if (ent->health) { newent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] = ent->health; } else { newent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] = cs->attributes[STARTING_HEALTH]; } // cs->weaponInfo = weaponInfo; // cs->lastThink = level.time; // newent->pain = AICast_Pain; newent->die = AICast_Die; // //update the attack inventory values AICast_UpdateBattleInventory(cs, cs->bs->enemy); //----(SA) make sure all clips are loaded so we don't hear everyone loading up // (we don't want to do this inside AICast_UpdateBattleInventory(), only on spawn or giveweapon) for (j=0; j<MAX_WEAPONS; j++) { Fill_Clip (&client->ps, j); } //----(SA) end // select a weapon AICast_ChooseWeapon( cs, qfalse ); // // set the default function, overwrite if necessary cs->aiFlags |= AIFL_JUST_SPAWNED; AIFunc_DefaultStart( cs ); // numcast++; // return newent; }