/* ================ CG_AddFragment ================ */ void CG_AddFragment( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; if ( le->pos.trType == TR_STATIONARY ) { // sink into the ground if near the removal time int t; float oldZ; t = le->endTime - cg.time; if ( t < SINK_TIME ) { // we must use an explicit lighting origin, otherwise the // lighting would be lost as soon as the origin went // into the ground VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin ); le->refEntity.renderfx |= RF_LIGHTING_ORIGIN; oldZ = le->refEntity.origin[2]; le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME ); trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.origin[2] = oldZ; } else { trap_R_AddRefEntityToScene( &le->refEntity ); } return; } // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin ); if ( le->leFlags & LEF_TUMBLE ) { vec3_t angles; BG_EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); } trap_R_AddRefEntityToScene( &le->refEntity ); // add a blood trail if ( le->leBounceSoundType == LEBS_BLOOD ) { CG_BloodTrail( le ); } return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if ( CG_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { CG_FreeLocalEntity( le ); return; } // leave a mark CG_FragmentBounceMark( le, &trace ); // do a bouncy sound CG_FragmentBounceSound( le, &trace ); // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); trap_R_AddRefEntityToScene( &le->refEntity ); }
//------------------------------------------------------ void SFxHelper::Trace( trace_t *tr, vec3_t start, vec3_t min, vec3_t max, vec3_t end, int skipEntNum, int flags ) { CG_Trace( tr, start, min, max, end, skipEntNum, flags ); }
/* ================ CG_AddFragment ================ */ void CG_AddFragment( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; // used to sink into the ground, but it looks better to maybe just fade them out int t; t = le->endTime - cg.time; if ( t < FRAG_FADE_TIME ) { le->refEntity.renderfx |= RF_ALPHA_FADE; le->refEntity.shaderRGBA[0] = le->refEntity.shaderRGBA[1] = le->refEntity.shaderRGBA[2] = 255; le->refEntity.shaderRGBA[3] = ((float)t / FRAG_FADE_TIME) * 255.0f; } if ( le->pos.trType == TR_STATIONARY ) { if ( !(cgi_CM_PointContents( le->refEntity.origin, 0 ) & CONTENTS_SOLID )) { // thing is no longer in solid, so let gravity take it back VectorCopy( le->refEntity.origin, le->pos.trBase ); VectorClear( le->pos.trDelta ); le->pos.trTime = cg.time; le->pos.trType = TR_GRAVITY; } cgi_R_AddRefEntityToScene( &le->refEntity ); return; } // calculate new position EvaluateTrajectory( &le->pos, cg.time, newOrigin ); le->refEntity.renderfx |= RF_LIGHTING_ORIGIN; VectorCopy( newOrigin, le->refEntity.lightingOrigin ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, le->ownerGentNum, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin ); if ( le->leFlags & LEF_TUMBLE ) { vec3_t angles; EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); for(int k = 0; k < 3; k++) { VectorScale(le->refEntity.axis[k], le->radius, le->refEntity.axis[k]); } } cgi_R_AddRefEntityToScene( &le->refEntity ); return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if ( cgi_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { CG_FreeLocalEntity( le ); return; } // do a bouncy sound CG_FragmentBounceSound( le, &trace ); // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); //FIXME: if LEF_TUMBLE, change avelocity too? cgi_R_AddRefEntityToScene( &le->refEntity ); }
/* ================ CG_DrawRadarSymbols ================ */ static void CG_DrawRadarSymbols_AIR( int vehicle ) { unsigned int i; centity_t *self = &cg.predictedPlayerEntity; vec3_t pos, pos1, pos2; vec3_t dir; float dist; float range = availableVehicles[vehicle].radarRange; float scale; float hdg = self->currentState.angles[1]; float angle; vec3_t angles; int otherveh; vec3_t otherpos; int icon; int drawnTargets = 0; trace_t res; // radar screen and mode CG_DrawPic( 440, 280, 200, 200, cgs.media.HUDradar ); CG_DrawSmallString( 615, 340, "AIR", 1.0f); // current range if( cg.RADARRangeSetting ) { range /= (2 * cg.RADARRangeSetting); } // adjust for radar screen size scale = range/64; // tell range CG_DrawSmallString( 595, 320, va("%.0f", range), 1.0f); // ground vehicle adjustment for angle if( (availableVehicles[vehicle].cat & CAT_GROUND) || (availableVehicles[vehicle].cat & CAT_BOAT) ) { hdg += self->currentState.angles2[ROLL]; if( hdg > 360 ) hdg -= 360; else if( hdg < 0 ) hdg += 360; } // pos VectorCopy( self->currentState.pos.trBase, pos ); // walk through list of targets for( i = 0; i < cg.radarTargets; i++ ) { // dont show self if( cg.radarEnts[i] == &cg.predictedPlayerEntity ) continue; // dont show dead ones if( cg.radarEnts[i]->currentState.eFlags & EF_DEAD ) continue; // get other vehicle if( cg.radarEnts[i]->currentState.eType == ET_MISC_VEHICLE ) { otherveh = cg.radarEnts[i]->currentState.modelindex; } else { otherveh = cgs.clientinfo[cg.radarEnts[i]->currentState.clientNum].vehicle; } VectorCopy( cg.radarEnts[i]->currentState.pos.trBase, otherpos ); // get dir and dist VectorSet( pos1, otherpos[0], otherpos[1], 0 ); VectorSet( pos2, pos[0], pos[1], 0 ); VectorSubtract( pos1, pos2, dir ); dist = VectorNormalize(dir); // check out of range if( dist > range ) continue; // scale dist to radar screen dist /= scale; // get screen dir vectoangles( dir, angles ); angle = angles[1]-hdg; if( angle > 360 ) angle -= 360; else if( angle < 0 ) angle += 360; if( angle >= 90 && angle <= 270 ) { if( !(cg.radarEnts[i]->currentState.ONOFF & OO_RADAR) ) continue; } angles[1] -= hdg - 90; if( angles[1] > 360 ) angles[1] -= 360; else if( angles[1] < 0 ) angles[1] += 360; angles[0] = angles[2] = 0; AngleVectors( angles, dir, 0, 0 ); VectorScale( dir, dist, dir ); // check LOS and/or RADAR on if( !(cg.radarEnts[i]->currentState.ONOFF & OO_RADAR) ) { CG_Trace( &res, pos, NULL, NULL, otherpos, cg.snap->ps.clientNum, MASK_SOLID ); if( res.fraction < 1.0f ) continue; } // which icon (if at all, dont show lqms) if( (availableVehicles[otherveh].cat & CAT_PLANE) || (availableVehicles[otherveh].cat & CAT_HELO) ) { float alt = otherpos[2] - pos[2]; icon = RD_AIR_SAME_ENEMY; if( alt > 1000 ) icon += 3; else if( alt < -1000 ) icon += 6; } else { continue; } // friend or foe if( cgs.gametype >= GT_TEAM ) { if( cgs.clientinfo[cg.radarEnts[i]->currentState.clientNum].team == cgs.clientinfo[self->currentState.clientNum].team ) icon++; } // draw CG_DrawPic( 558 + dir[0], 386 - dir[1], 8, 8, cgs.media.radarIcons[icon] ); // check if we should draw any more targets drawnTargets++; if( drawnTargets >= cg_radarTargets.integer ) break; } cg.radarTargets = 0; }
/* ================ CG_DrawStatusBar_MFQ3 ================ */ void CG_DrawStatusBar_MFQ3( void ) { int color; centity_t *cent; playerState_t *ps; int value; int vehicle = cgs.clientinfo[cg.predictedPlayerState.clientNum].vehicle; int w; static vec4_t colors[] = { // { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} }; { 1.0f, 0.89f, 0.0f, 1.0f } , // normal, yellow { 1.0f, 0.0f, 0.0f, 1.0f }, // low health, red {0.5f, 0.5f, 0.5f, 1.0f}, // weapon firing, grey { 1.0f, 1.0f, 1.0f, 1.0f }, // health > 100, white { 0.3f, 1.0f, 0.3f, 1.0f } // green }; if ( cg_drawStatus.integer == 0 ) { return; } cent = &cg_entities[cg.snap->ps.clientNum]; ps = &cg.snap->ps; // aiming/targetting reticles CG_Draw_Reticles(); // main background CG_DrawPic( 0, 430, 640, 50, cgs.media.HUDmain ); // // flag // if( cg.predictedPlayerState.objectives & OB_REDFLAG ) { CG_DrawStatusBarFlag( 640 - ICON_SIZE, ClientBase::TEAM_RED ); } else if( cg.predictedPlayerState.objectives & OB_BLUEFLAG ) { CG_DrawStatusBarFlag( 640 - ICON_SIZE, ClientBase::TEAM_BLUE ); } // // RADAR // if( cent->currentState.ONOFF & OO_RADAR_AIR ) { CG_DrawRadarSymbols_AIR(vehicle); } else if( cent->currentState.ONOFF & OO_RADAR_GROUND ) { CG_DrawRadarSymbols_GROUND(vehicle); } else if( availableVehicles[vehicle].radarRange || availableVehicles[vehicle].radarRange2 ) { CG_DrawPic( 440, 280, 200, 200, cgs.media.HUDradar ); CG_DrawSmallString( 595, 320, "OFF", 1.0f); } // // GPS // if( cg.GPS ) { trace_t tr; vec3_t start, end; // main CG_DrawPic( -14, 380, 512, 102, cgs.media.HUDgps ); // heading value = 360 - (int)ps->vehicleAngles[1]; CG_DrawSmallString( 234, 420, va("%i",value), 1.0f ); // altitude VectorCopy( ps->origin, start ); start[2] += availableVehicles[vehicle].mins[2]; VectorCopy( start, end ); end[2] -= 2000; CG_Trace( &tr, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, MASK_SOLID|MASK_WATER ); value = (int)(tr.fraction * 2000); if( value > 100 ) color = 3; else if( value > 50 ) color = 4; else if( value > 25 ) color = 0; else color = 1; if( value < 2000 ) { CG_DrawSmallStringColor( 374, 420, va("%i",value), colors[color] ); } else { CG_DrawSmallStringColor( 374, 420, "XXXX", colors[color] ); } // x coord value = (int)ps->origin[0]; CG_DrawSmallString( 264, 423, va("%i",value), 0.7f ); // y coord value = (int)ps->origin[1]; CG_DrawSmallString( 320, 423, va("%i",value), 0.7f ); } // // INFO // if( !cg.INFO ) { // bit dirty to have NOT but oh well... // main CG_DrawPic( 0, 330, 150, 150, cgs.media.HUDext ); if( availableVehicles[vehicle].caps & HC_GEAR ) { if( cent->currentState.ONOFF & OO_GEAR ) { CG_DrawSmallStringColor( 2, 390, "Gear", colors[4] ); } else { CG_DrawSmallString( 2, 390, "Gear", 1.0f ); } } if( availableVehicles[vehicle].caps & HC_SPEEDBRAKE ) { if( cent->currentState.ONOFF & OO_SPEEDBRAKE ) { CG_DrawSmallStringColor( 2, 410, "Speedbrakes", colors[4] ); } else { CG_DrawSmallString( 2, 410, "Speedbrakes", 1.0f ); } } else { if( cent->currentState.ONOFF & OO_SPEEDBRAKE ) { CG_DrawSmallStringColor( 2, 410, "Brakes", colors[4] ); } else { CG_DrawSmallString( 2, 410, "Brakes", 1.0f ); } } value = ps->stats[STAT_FUEL]; if ( value > 10 ) { color = 3; // white } else if (value > 5) { color = 0; // yellow } else { color = 1; // red } if( (availableVehicles[vehicle].cat & CAT_PLANE) || (availableVehicles[vehicle].cat & CAT_HELO) || (availableVehicles[vehicle].cat & CAT_BOAT) ) { value *= 100; } CG_DrawSmallStringColor( 2, 430, va("Fuel %i", value), colors[color]); } // // FLARES // if( ps->ammo[WP_FLARE+8] > 0 ) { CG_DrawBigString( 600, 462, va("%i", ps->ammo[WP_FLARE]), 1.0f ); } // // speed and throttle // value = ps->speed/10; if ( value > availableVehicles[vehicle].stallspeed * SPEED_GREEN_ARC ) { color = 3; // white } else if (value > availableVehicles[vehicle].stallspeed * SPEED_YELLOW_ARC ) { color = 4; // green } else if (value >= availableVehicles[vehicle].stallspeed) { color = 0; // yellow } else { color = 1; // red } w = CG_DrawStrlen(va("%i",value)) * BIGCHAR_WIDTH; CG_DrawBigStringColor(285-w, 460, va("%i",value), colors[color]); if( ps->throttle > availableVehicles[vehicle].maxthrottle ) { value = ps->throttle - availableVehicles[vehicle].maxthrottle; } else { value = ps->throttle; } CG_DrawPic(295, 443, 50, 37, cgs.media.throttle[value]); // // health // value = (100*ps->stats[STAT_HEALTH]/ps->stats[STAT_MAX_HEALTH]); if( value > 100 ) value = 100; if ( value < 10 ) { color = 1; } else if (value < 25) { color = 0; } else { color = 3; } w = CG_DrawStrlen(va("%i",value)) * BIGCHAR_WIDTH; CG_DrawBigStringColor( 398-w, 460, va("%i",value), colors[color]); // // ammo // if( cent->currentState.weaponNum >= 0 ) { value = ps->ammo[cent->currentState.weaponNum]; w = CG_DrawStrlen(va("%i",value)) * BIGCHAR_WIDTH; CG_DrawBigString (500-w, 460, va("%i",value),1.0f); CG_DrawPic( 416, 440, 24, 45, availableWeapons[availableVehicles[vehicle].weapons[cent->currentState.weaponNum]].iconHandle ); } // // ammo mg // if( ps->ammo[WP_MACHINEGUN+8] > 0 ) { if( cent->currentState.weaponNum >= 0 ) { value = ps->ammo[0]; w = CG_DrawStrlen(va("%i",value)) * BIGCHAR_WIDTH; CG_DrawBigString (180-w, 460, va("%i",value),1.0f); CG_DrawPic( 200, 440, 24, 45, availableWeapons[availableVehicles[vehicle].weapons[0]].iconHandle ); } } }
/* ================ CG_AddFragment ================ */ void CG_AddFragment(localEntity_t * le) { vec3_t newOrigin; trace_t trace; if(le->pos.trType == TR_STATIONARY) { // sink into the ground if near the removal time int t; float oldZ; t = le->endTime - cg.time; if(t < SINK_TIME) { // we must use an explicit lighting origin, otherwise the // lighting would be lost as soon as the origin went // into the ground VectorCopy(le->refEntity.origin, le->refEntity.lightingOrigin); le->refEntity.renderfx |= RF_LIGHTING_ORIGIN; oldZ = le->refEntity.origin[2]; le->refEntity.origin[2] -= 16 * (1.0 - (float)t / SINK_TIME); trap_R_AddRefEntityToScene(&le->refEntity); le->refEntity.origin[2] = oldZ; } else { trap_R_AddRefEntityToScene(&le->refEntity); } return; } // calculate new position BG_EvaluateTrajectory(&le->pos, cg.time, newOrigin); // trace a line from previous position to new position CG_Trace(&trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID); if(trace.fraction == 1.0) { // still in free fall VectorCopy(newOrigin, le->refEntity.origin); if(le->leFlags & LEF_TUMBLE) { #if 0 vec3_t angles; BG_EvaluateTrajectory(&le->angles, cg.time, angles); AnglesToAxis(angles, le->refEntity.axis); #else // Tr3B - new quaternion code quat_t qrot; // angular rotation for this frame float angle = le->angVel * (cg.time - le->angles.trTime) * 0.001 / 2; // create the rotation quaternion qrot[3] = cos(angle); // real part VectorScale(le->rotAxis, sin(angle), qrot); // imaginary part QuatNormalize(qrot); // create the new orientation QuatMultiply0(le->quatOrient, qrot); // apply the combined previous rotations around other axes QuatMultiply1(le->quatOrient, le->quatRot, qrot); // convert the orientation into the form the renderer wants QuatToAxis(qrot, le->refEntity.axis); le->angles.trTime = cg.time; #endif } trap_R_AddRefEntityToScene(&le->refEntity); // add a blood trail if(le->leBounceSoundType == LEBS_BLOOD) { CG_BloodTrail(le); } return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if(trap_CM_PointContents(trace.endpos, 0) & CONTENTS_NODROP) { CG_FreeLocalEntity(le); return; } // leave a mark CG_FragmentBounceMark(le, &trace); // do a bouncy sound CG_FragmentBounceSound(le, &trace); // reflect the velocity on the trace plane CG_ReflectVelocity(le, &trace); trap_R_AddRefEntityToScene(&le->refEntity); }
static void CG_OffsetThirdPersonView( void ) { vec3_t forward, right, up; vec3_t view; vec3_t focusAngles; trace_t trace; static vec3_t mins = { -4, -4, -4 }; static vec3_t maxs = { 4, 4, 4 }; vec3_t focusPoint; float focusDist; float forwardScale, sideScale; cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight; VectorCopy( cg.refdefViewAngles, focusAngles ); // if dead, look at killer if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW]; cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW]; } if ( focusAngles[PITCH] > 45 ) { focusAngles[PITCH] = 45; // don't go too far overhead } AngleVectors( focusAngles, forward, NULL, NULL ); VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint ); VectorCopy( cg.refdef.vieworg, view ); view[2] += 8; cg.refdefViewAngles[PITCH] *= 0.5; AngleVectors( cg.refdefViewAngles, forward, right, up ); forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI ); sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI ); VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view ); VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view ); // 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.0 ) { VectorCopy( trace.endpos, view ); view[2] += ( 1.0 - trace.fraction ) * 32; // try another trace to this position, because a tunnel may have the ceiling // close enough that this is poking out CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); VectorCopy( trace.endpos, view ); } VectorCopy( view, cg.refdef.vieworg ); // select pitch to look at focus point from vieword VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint ); focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] ); if ( focusDist < 1 ) { focusDist = 1; // should never happen } cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist ); cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value; }
/* =============== 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_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 cg_thirdPersonRange range = cg_thirdPersonRange.value; // 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. // 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 ); } }
// This can be called every interval, at the user's discretion. static void CG_UpdateThirdPersonCameraDamp(void) { trace_t trace; vec3_t locdiff; float dampfactor, dtime, ratio; // Set the cameraIdealLoc CG_CalcIdealThirdPersonViewLocation(); // First thing we do is calculate the appropriate damping factor for the camera. dampfactor=0.0; if (cg_thirdPersonCameraDamp.value != 0.0) { double pitch; // Note that the camera pitch has already been capped off to 89. pitch = Q_fabs(cameraFocusAngles[PITCH]); // The higher the pitch, the larger the factor, so as you look up, it damps a lot less. pitch /= 89.0; dampfactor = (1.0-cg_thirdPersonCameraDamp.value)*(pitch*pitch); dampfactor += cg_thirdPersonCameraDamp.value; // Now we also multiply in the stiff factor, so that faster yaw changes are stiffer. if (cameraStiffFactor > 0.0f) { // The cameraStiffFactor is how much of the remaining damp below 1 should be shaved off, i.e. approach 1 as stiffening increases. dampfactor += (1.0-dampfactor)*cameraStiffFactor; } } if (dampfactor>=1.0) { // No damping. VectorCopy(cameraIdealLoc, cameraCurLoc); } else if (dampfactor>=0.0) { // Calculate the difference from the current position to the new one. VectorSubtract(cameraIdealLoc, cameraCurLoc, locdiff); // Now we calculate how much of the difference we cover in the time allotted. // The equation is (Damp)^(time) dampfactor = 1.0-dampfactor; // We must exponent the amount LEFT rather than the amount bled off dtime = (float)(cg.time-cameraLastFrame) * (1.0/(float)CAMERA_DAMP_INTERVAL); // Our dampfactor is geared towards a time interval equal to "1". // Note that since there are a finite number of "practical" delta millisecond values possible, // the ratio should be initialized into a chart ultimately. ratio = JK2_powf(dampfactor, dtime); // This value is how much distance is "left" from the ideal. VectorMA(cameraIdealLoc, -ratio, locdiff, cameraCurLoc); ///////////////////////////////////////////////////////////////////////////////////////////////////////// } // Now we trace from the new target location to the new view location, to make sure there is nothing in the way. CG_Trace(&trace, cameraCurTarget, cameramins, cameramaxs, cameraCurLoc, cg.snap->ps.clientNum, MASK_CAMERACLIP); if (trace.fraction < 1.0) { VectorCopy( trace.endpos, cameraCurLoc ); //FIXME: when the trace hits movers, it gets very very jaggy... ? /* //this doesn't actually help any if ( trace.entityNum != ENTITYNUM_WORLD ) { centity_t *cent = &cg_entities[trace.entityNum]; gentity_t *gent = &g_entities[trace.entityNum]; if ( cent != NULL && gent != NULL ) { if ( cent->currentState.pos.trType == TR_LINEAR || cent->currentState.pos.trType == TR_LINEAR_STOP ) { vec3_t diff; VectorSubtract( cent->lerpOrigin, gent->currentOrigin, diff ); VectorAdd( cameraCurLoc, diff, cameraCurLoc ); } } } */ } // Note that previously there was an upper limit to the number of physics traces that are done through the world // for the sake of camera collision, since it wasn't calced per frame. Now it is calculated every frame. // This has the benefit that the camera is a lot smoother now (before it lerped between tested points), // however two full volume traces each frame is a bit scary to think about. }
/* * CG_AddLocalEntities */ void CG_AddLocalEntities( void ) { #define FADEINFRAMES 2 int f; lentity_t *le, *next, *hnode; entity_t *ent; float scale, frac, fade, time, scaleIn, fadeIn; float backlerp; vec3_t angles; time = cg.frameTime; backlerp = 1.0f - cg.lerpfrac; hnode = &cg_localents_headnode; for( le = hnode->next; le != hnode; le = next ) { next = le->next; frac = ( cg.time - le->start ) * 0.01f; f = ( int )floor( frac ); clamp_low( f, 0 ); // it's time to DIE if( f >= le->frames - 1 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); continue; } if( le->frames > 1 ) { scale = 1.0f - frac / ( le->frames - 1 ); scale = bound( 0.0f, scale, 1.0f ); fade = scale * 255.0f; // quick fade in, if time enough if( le->frames > FADEINFRAMES * 2 ) { scaleIn = frac / (float)FADEINFRAMES; clamp( scaleIn, 0.0f, 1.0f ); fadeIn = scaleIn * 255.0f; } else fadeIn = 255.0f; } else { scale = 1.0f; fade = 255.0f; fadeIn = 255.0f; } ent = &le->ent; if( le->light && scale ) CG_AddLightToScene( ent->origin, le->light * scale, le->lightcolor[0], le->lightcolor[1], le->lightcolor[2] ); if( le->type == LE_LASER ) { CG_QuickPolyBeam( ent->origin, ent->origin2, ent->radius, ent->customShader ); // wsw : jalfixme: missing the color (comes inside ent->skinnum) continue; } if( le->type == LE_DASH_SCALE ) { if( f < 1 ) ent->scale = 0.2 * frac; else { VecToAngles( &ent->axis[AXIS_RIGHT], angles ); ent->axis[1*3+1] += 0.005f *sin( DEG2RAD ( angles[YAW] ) ); //length ent->axis[1*3+0] += 0.005f *cos( DEG2RAD ( angles[YAW] ) ); //length ent->axis[0*3+1] += 0.008f *cos( DEG2RAD ( angles[YAW] ) ); //width ent->axis[0*3+0] -= 0.008f *sin( DEG2RAD ( angles[YAW] ) ); //width ent->axis[2*3+2] -= 0.052f; //height if( ent->axis[AXIS_UP+2] <= 0 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); } } } if( le->type == LE_DASH_SCALE_2 ) { if( f < 1 ) ent->scale = 0.25 * frac; else { VecToAngles( &ent->axis[AXIS_RIGHT], angles ); ent->axis[1*3+1] += 0.005f *sin( DEG2RAD ( angles[YAW] ) ); //length ent->axis[1*3+0] += 0.005f *cos( DEG2RAD ( angles[YAW] ) ); //length ent->axis[0*3+1] += 0.008f *cos( DEG2RAD ( angles[YAW] ) ); //width ent->axis[0*3+0] -= 0.008f *sin( DEG2RAD ( angles[YAW] ) ); //width ent->axis[2*3+2] -= 0.018f; //height ent->origin[1] -= 0.6f *sin( DEG2RAD ( angles[YAW] ) ); //velocity ent->origin[0] -= 0.6f *cos( DEG2RAD ( angles[YAW] ) ); //velocity if( ent->axis[AXIS_UP+2] <= 0 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); } } } if( le->type == LE_PUFF_SCALE ) { if( frac < 1 ) ent->scale = 7.0f*frac; else ent->scale = 7.0f - 4.0f*( frac-1 ); if( ent->scale < 0 ) { le->type = LE_FREE; CG_FreeLocalEntity( le ); } } if( le->type == LE_PUFF_SCALE_2 ) { if( le->frames - f < 4 ) ent->scale = 1.0f - 1.0f * ( frac - abs( 4-le->frames ) )/4; } if( le->type == LE_PUFF_SHRINK ) { if( frac < 3 ) ent->scale = 1.0f - 0.2f * frac/4; else { ent->scale = 0.8 - 0.8*( frac-3 )/3; VectorScale( le->velocity, 0.85f, le->velocity ); } } if( le->type == LE_EXPLOSION_TRACER ) { if( cg.time - ent->rotation > 10.0f ) { ent->rotation = cg.time; if( ent->radius - 16*frac > 4 ) CG_Explosion_Puff( ent->origin, ent->radius-16*frac, le->frames - f ); } } switch( le->type ) { case LE_NO_FADE: break; case LE_RGB_FADE: fade = min( fade, fadeIn ); ent->shaderRGBA[0] = ( uint8_t )( fade * le->color[0] ); ent->shaderRGBA[1] = ( uint8_t )( fade * le->color[1] ); ent->shaderRGBA[2] = ( uint8_t )( fade * le->color[2] ); break; case LE_SCALE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->scale = 1.0f + 1.0f / scale; ent->scale = min( ent->scale, 5.0f ); ent->shaderRGBA[3] = ( uint8_t )( fade * le->color[3] ); break; case LE_INVERSESCALE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->scale = scale + 0.1f; clamp( ent->scale, 0.1f, 1.0f ); ent->shaderRGBA[3] = ( uint8_t )( fade * le->color[3] ); break; case LE_ALPHA_FADE: fade = min( fade, fadeIn ); ent->shaderRGBA[3] = ( uint8_t )( fade * le->color[3] ); break; default: break; } ent->backlerp = backlerp; if ( le->avelocity[0] || le->avelocity[1] || le->avelocity[2] ) { VectorMA( le->angles, time, le->avelocity, le->angles ); AnglesToAxis( le->angles, le->ent.axis ); } // apply rotational friction if( le->bounce ) { // FIXME? int i; const float adj = 100 * 6 * time; // magic constants here for( i = 0; i < 3; i++ ) { if( le->avelocity[i] > 0.0f ) { le->avelocity[i] -= adj; if( le->avelocity[i] < 0.0f ) { le->avelocity[i] = 0.0f; } } else if ( le->avelocity[i] < 0.0f ) { le->avelocity[i] += adj; if ( le->avelocity[i] > 0.0f ) { le->avelocity[i] = 0.0f; } } } } if( le->bounce ) { trace_t trace; vec3_t next_origin; VectorMA( ent->origin, time, le->velocity, next_origin ); CG_Trace( &trace, ent->origin, debris_mins, debris_maxs, next_origin, 0, MASK_SOLID ); // remove the particle when going out of the map if( ( trace.contents & CONTENTS_NODROP ) || ( trace.surfFlags & SURF_SKY ) ) { le->frames = 0; } else if( trace.fraction != 1.0 ) // found solid { float dot; float xyzspeed, orig_xyzspeed; float bounce; orig_xyzspeed = VectorLength( le->velocity ); // Reflect velocity dot = DotProduct( le->velocity, trace.plane.normal ); VectorMA( le->velocity, -2.0f * dot, trace.plane.normal, le->velocity ); //put new origin in the impact point, but move it out a bit along the normal VectorMA( trace.endpos, 1, trace.plane.normal, ent->origin ); // make sure we don't gain speed from bouncing off bounce = 2.0f * le->bounce * 0.01f; if( bounce < 1.5f ) bounce = 1.5f; xyzspeed = orig_xyzspeed / bounce; VectorNormalize( le->velocity ); VectorScale( le->velocity, xyzspeed, le->velocity ); //the entity has not speed enough. Stop checks if( xyzspeed * time < 1.0f ) { trace_t traceground; vec3_t ground_origin; //see if we have ground VectorCopy( ent->origin, ground_origin ); ground_origin[2] += ( debris_mins[2] - 4 ); CG_Trace( &traceground, ent->origin, debris_mins, debris_maxs, ground_origin, 0, MASK_SOLID ); if( traceground.fraction != 1.0 ) { le->bounce = 0; VectorClear( le->velocity ); VectorClear( le->accel ); VectorClear( le->avelocity ); if( le->type == LE_EXPLOSION_TRACER ) { // blx le->type = LE_FREE; CG_FreeLocalEntity( le ); } } } } else { VectorCopy( ent->origin, ent->origin2 ); VectorCopy( next_origin, ent->origin ); } } else { VectorCopy( ent->origin, ent->origin2 ); VectorMA( ent->origin, time, le->velocity, ent->origin ); } VectorCopy( ent->origin, ent->lightingOrigin ); VectorMA( le->velocity, time, le->accel, le->velocity ); CG_AddEntityToScene( ent ); } }
/* * CG_BulletExplosion */ void CG_BulletExplosion( const vec3_t pos, const vec_t *dir, const trace_t *trace ) { lentity_t *le; vec3_t angles; vec3_t local_dir, end; trace_t local_trace; const trace_t *tr; assert( dir || trace ); if( dir ) { vec3_t local_pos; // find what are we hitting VectorCopy( pos, local_pos ); VectorMA( pos, -1.0, dir, end ); CG_Trace( &local_trace, local_pos, vec3_origin, vec3_origin, end, cg.view.POVent, MASK_SHOT ); if( local_trace.fraction == 1.0 ) return; tr = &local_trace; VectorCopy( dir, local_dir ); } else { tr = trace; VectorCopy( tr->plane.normal, local_dir ); } VecToAngles( local_dir, angles ); if( tr->surfFlags & SURF_FLESH || ( tr->ent > 0 && cg_entities[tr->ent].current.type == ET_PLAYER ) || ( tr->ent > 0 && cg_entities[tr->ent].current.type == ET_CORPSE ) ) { le = CG_AllocModel( LE_ALPHA_FADE, pos, angles, 3, //3 frames for weak 1, 0, 0, 1, //full white no inducted alpha 0, 0, 0, 0, //dlight CG_MediaModel( cgs.media.modBulletExplode ), NULL ); le->ent.rotation = rand() % 360; le->ent.scale = 1.0f; if( ISVIEWERENTITY( tr->ent ) ) le->ent.renderfx |= RF_VIEWERMODEL; } else if( cg_particles->integer && (tr->surfFlags & SURF_DUST) ) { // throw particles on dust CG_ImpactSmokePuff( tr->endpos, tr->plane.normal, 4, 0.6f, 6, 8 ); } else { le = CG_AllocModel( LE_ALPHA_FADE, pos, angles, 3, //3 frames for weak 1, 1, 1, 1, //full white no inducted alpha 0, 0, 0, 0, //dlight CG_MediaModel( cgs.media.modBulletExplode ), NULL ); le->ent.rotation = rand() % 360; le->ent.scale = 1.0f; if( cg_particles->integer ) { CG_ImpactSmokePuff( tr->endpos, tr->plane.normal, 2, 0.6f, 6, 8 ); } if( !( tr->surfFlags & SURF_NOMARKS ) ) CG_SpawnDecal( pos, local_dir, random()*360, 8, 1, 1, 1, 1, 10, 1, false, CG_MediaShader( cgs.media.shaderBulletMark ) ); } }
/* ================== CG_BuildableStatusDisplay ================== */ static void CG_BuildableStatusDisplay( centity_t *cent ) { entityState_t *es = ¢->currentState; vec3_t origin; float healthScale; int health; float x, y; vec4_t color; qboolean powered, marked; trace_t tr; float d; buildStat_t *bs; int i, j; int entNum; vec3_t trOrigin; vec3_t right; qboolean visible = qfalse; vec3_t mins, maxs; entityState_t *hit; int anim; if( BG_Buildable( es->modelindex )->team == TEAM_ALIENS ) bs = &cgs.alienBuildStat; else bs = &cgs.humanBuildStat; if( !bs->loaded ) return; d = Distance( cent->lerpOrigin, cg.refdef.vieworg ); if( d > STATUS_MAX_VIEW_DIST ) return; Vector4Copy( bs->foreColor, color ); // trace for center point BG_BuildableBoundingBox( es->modelindex, mins, maxs ); // hack for shrunken barricades anim = es->torsoAnim & ~( ANIM_FORCEBIT | ANIM_TOGGLEBIT ); if( es->modelindex == BA_A_BARRICADE && ( anim == BANIM_DESTROYED || !( es->eFlags & EF_B_SPAWNED ) ) ) maxs[ 2 ] = (int)( maxs[ 2 ] * BARRICADE_SHRINKPROP ); VectorCopy( cent->lerpOrigin, origin ); // center point origin[ 2 ] += mins[ 2 ]; origin[ 2 ] += ( abs( mins[ 2 ] ) + abs( maxs[ 2 ] ) ) / 2; entNum = cg.predictedPlayerState.clientNum; // if first try fails, step left, step right for( j = 0; j < 3; j++ ) { VectorCopy( cg.refdef.vieworg, trOrigin ); switch( j ) { case 1: // step right AngleVectors( cg.refdefViewAngles, NULL, right, NULL ); VectorMA( trOrigin, STATUS_PEEK_DIST, right, trOrigin ); break; case 2: // step left AngleVectors( cg.refdefViewAngles, NULL, right, NULL ); VectorMA( trOrigin, -STATUS_PEEK_DIST, right, trOrigin ); break; default: break; } // look through up to 3 players and/or transparent buildables for( i = 0; i < 3; i++ ) { CG_Trace( &tr, trOrigin, NULL, NULL, origin, entNum, MASK_SHOT ); if( tr.entityNum == cent->currentState.number ) { visible = qtrue; break; } if( tr.entityNum == ENTITYNUM_WORLD ) break; hit = &cg_entities[ tr.entityNum ].currentState; if( tr.entityNum < MAX_CLIENTS || ( hit->eType == ET_BUILDABLE && ( !( es->eFlags & EF_B_SPAWNED ) || BG_Buildable( hit->modelindex )->transparentTest ) ) ) { entNum = tr.entityNum; VectorCopy( tr.endpos, trOrigin ); } else break; } } // hack to make the kit obscure view if( cg_drawGun.integer && visible && cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS && CG_WorldToScreen( origin, &x, &y ) ) { if( x > 450 && y > 290 ) visible = qfalse; } if( !visible && cent->buildableStatus.visible ) { cent->buildableStatus.visible = qfalse; cent->buildableStatus.lastTime = cg.time; } else if( visible && !cent->buildableStatus.visible ) { cent->buildableStatus.visible = qtrue; cent->buildableStatus.lastTime = cg.time; } // Fade up if( cent->buildableStatus.visible ) { if( cent->buildableStatus.lastTime + STATUS_FADE_TIME > cg.time ) color[ 3 ] = (float)( cg.time - cent->buildableStatus.lastTime ) / STATUS_FADE_TIME; } // Fade down if( !cent->buildableStatus.visible ) { if( cent->buildableStatus.lastTime + STATUS_FADE_TIME > cg.time ) color[ 3 ] = 1.0f - (float)( cg.time - cent->buildableStatus.lastTime ) / STATUS_FADE_TIME; else return; } health = es->generic1; healthScale = (float)health / BG_Buildable( es->modelindex )->health; if( health > 0 && healthScale < 0.01f ) healthScale = 0.01f; else if( healthScale < 0.0f ) healthScale = 0.0f; else if( healthScale > 1.0f ) healthScale = 1.0f; if( CG_WorldToScreen( origin, &x, &y ) ) { float picH = bs->frameHeight; float picW = bs->frameWidth; float picX = x; float picY = y; float scale; float subH, subY; float clipX, clipY, clipW, clipH; vec4_t frameColor; // this is fudged to get the width/height in the cfg to be more realistic scale = ( picH / d ) * 3; powered = es->eFlags & EF_B_POWERED; marked = es->eFlags & EF_B_MARKED; picH *= scale; picW *= scale; picX -= ( picW * 0.5f ); picY -= ( picH * 0.5f ); // sub-elements such as icons and number subH = picH - ( picH * bs->verticalMargin ); subY = picY + ( picH * 0.5f ) - ( subH * 0.5f ); clipW = ( 640.0f * cg_viewsize.integer ) / 100.0f; clipH = ( 480.0f * cg_viewsize.integer ) / 100.0f; clipX = 320.0f - ( clipW * 0.5f ); clipY = 240.0f - ( clipH * 0.5f ); CG_SetClipRegion( clipX, clipY, clipW, clipH ); if( bs->frameShader ) { Vector4Copy( bs->backColor, frameColor ); frameColor[ 3 ] = color[ 3 ]; trap_R_SetColor( frameColor ); CG_DrawPic( picX, picY, picW, picH, bs->frameShader ); trap_R_SetColor( NULL ); } if( health > 0 ) { float hX, hY, hW, hH; vec4_t healthColor; hX = picX + ( bs->healthPadding * scale ); hY = picY + ( bs->healthPadding * scale ); hH = picH - ( bs->healthPadding * 2.0f * scale ); hW = picW * healthScale - ( bs->healthPadding * 2.0f * scale ); if( healthScale == 1.0f ) Vector4Copy( bs->healthLowColor, healthColor ); else if( healthScale >= 0.75f ) Vector4Copy( bs->healthGuardedColor, healthColor ); else if( healthScale >= 0.50f ) Vector4Copy( bs->healthElevatedColor, healthColor ); else if( healthScale >= 0.25f ) Vector4Copy( bs->healthHighColor, healthColor ); else Vector4Copy( bs->healthSevereColor, healthColor ); healthColor[ 3 ] = color[ 3 ]; trap_R_SetColor( healthColor ); CG_DrawPic( hX, hY, hW, hH, cgs.media.whiteShader ); trap_R_SetColor( NULL ); } if( bs->overlayShader ) { float oW = bs->overlayWidth; float oH = bs->overlayHeight; float oX = x; float oY = y; oH *= scale; oW *= scale; oX -= ( oW * 0.5f ); oY -= ( oH * 0.5f ); trap_R_SetColor( frameColor ); CG_DrawPic( oX, oY, oW, oH, bs->overlayShader ); trap_R_SetColor( NULL ); } trap_R_SetColor( color ); if( !powered ) { float pX; pX = picX + ( subH * bs->horizontalMargin ); CG_DrawPic( pX, subY, subH, subH, bs->noPowerShader ); } if( marked ) { float mX; mX = picX + picW - ( subH * bs->horizontalMargin ) - subH; CG_DrawPic( mX, subY, subH, subH, bs->markedShader ); } { float nX; int healthMax; int healthPoints; healthMax = BG_Buildable( es->modelindex )->health; healthPoints = (int)( healthScale * healthMax ); if( health > 0 && healthPoints < 1 ) healthPoints = 1; nX = picX + ( picW * 0.5f ) - 2.0f - ( ( subH * 4 ) * 0.5f ); if( healthPoints > 999 ) nX -= 0.0f; else if( healthPoints > 99 ) nX -= subH * 0.5f; else if( healthPoints > 9 ) nX -= subH * 1.0f; else nX -= subH * 1.5f; CG_DrawField( nX, subY, 4, subH, subH, healthPoints ); } trap_R_SetColor( NULL ); CG_ClearClipRegion( ); } }
/* ================ CG_AddMissile ================ */ static void CG_AddMissile( localEntity_t *le ) { refEntity_t *re; const weaponInfo_t *weapon; trace_t trace; centity_t *other; qboolean inWater; // just existing for server entity deletion if ( le->leFlags & LEF_FINISHED ) { return; } // get weapon info if ( le->ti.weapon > WP_NUM_WEAPONS ) { le->ti.weapon = 0; } weapon = &cg_weapons[le->ti.weapon]; re = &le->refEntity; // calculate position BG_EvaluateTrajectory( &le->pos, cg.time, re->origin ); // special case for flames if ( re->reType == RT_SPRITE ) { int deltaTime; // check for water if ( trap_CM_PointContents( re->origin, 0 ) & CONTENTS_WATER ) { // do a trace to get water surface normals CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_WATER ); CG_FreeLocalEntity( le ); CG_MakeExplosion( trace.endpos, trace.plane.normal, cgs.media.ringFlashModel, cgs.media.vaporShader, 500, qfalse, qtrue ); return; } // change radius over time deltaTime = cg.time - le->startTime; re->radius = deltaTime * deltaTime * ( random()*0.4f + 0.8f ) / 2000.0f + 9; // do a trace sometimes if ( le->ti.trailTime++ > 5 ) { le->ti.trailTime = 0; CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_SHOT ); VectorCopy( re->origin, re->oldorigin ); // hit something if ( trace.fraction < 1.0 ) { CG_MissileHitWall( le->ti.weapon, 0, trace.endpos, trace.plane.normal, IMPACTSOUND_DEFAULT ); CG_FreeLocalEntity( le ); return; } } // add to refresh list trap_R_AddRefEntityToScene( re ); return; } // add trails if ( weapon->missileTrailFunc ) weapon->missileTrailFunc( &le->ti, cg.time ); // add dynamic light if ( weapon->missileDlight ) { trap_R_AddLightToScene( re->origin, weapon->missileDlight, weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] ); } // flicker between two skins re->skinNum = cg.clientFrame & 1; // convert direction of travel into axis if ( VectorNormalize2( le->pos.trDelta, re->axis[0] ) == 0 ) { re->axis[0][2] = 1; } // spin as it moves if ( le->pos.trType != TR_STATIONARY ) { if ( le->pos.trType == TR_GRAVITY ) { RotateAroundDirection( re->axis, cg.time / 4 ); } else if ( le->pos.trType == TR_WATER_GRAVITY ) { RotateAroundDirection( re->axis, cg.time / 8 ); } else { RotateAroundDirection( re->axis, cg.time ); } } else { RotateAroundDirection( re->axis, 0 ); } // trace a line from previous position to new position CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_SHOT ); VectorCopy( re->origin, re->oldorigin ); // draw BIG grenades if ( weLi[le->ti.weapon].category == CT_EXPLOSIVE ) { AxisScale( re->axis, GRENADE_SCALE, re->axis ); } if ( trace.fraction != 1.0 ) { // hit the sky or something like that if ( trace.surfaceFlags & SURF_NOIMPACT ) { le->leFlags |= LEF_FINISHED; le->endTime = cg.time + 500; return; } // impact other = &cg_entities[trace.entityNum]; if ( le->bounceFactor > 0 && ( le->bounceFactor == BOUNCE_FACTOR_HALF || other->currentState.eType != ET_PLAYER ) ) { // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); if ( cg.predictedImpacts < MAX_PREDICTED_IMPACTS ) { cg.predictedImpacts++; cg.predictedImpactsDecTime = cg.time; } // do bounce sound if ( rand() & 1 ) { trap_S_StartSound( le->pos.trBase, 0, CHAN_AUTO, cgs.media.hgrenb1aSound ); } else { trap_S_StartSound( le->pos.trBase, 0, CHAN_AUTO, cgs.media.hgrenb2aSound ); } } else { // explode missile if ( cg.predictedImpacts < MAX_PREDICTED_IMPACTS ) { cg.predictedImpacts++; cg.predictedImpactsDecTime = cg.time; } if ( other->currentState.eType == ET_PLAYER ) { CG_MissileHitPlayer( le->ti.weapon, 0, trace.endpos, trace.plane.normal, trace.entityNum ); } else if ( !(trace.surfaceFlags & SURF_NOIMPACT) ) { CG_MissileHitWall( le->ti.weapon, 0, trace.endpos, trace.plane.normal, (trace.surfaceFlags & SURF_METALSTEPS) ? IMPACTSOUND_METAL : IMPACTSOUND_DEFAULT ); } // store the entity for deleting the server entity le->leFlags |= LEF_FINISHED; le->endTime = cg.time + 500; return; } } // check for medium change if ( trap_CM_PointContents( re->origin, 0 ) & CONTENTS_WATER ) inWater = qtrue; else inWater = qfalse; if ( ( !inWater && le->pos.trType == TR_WATER_GRAVITY ) || ( inWater && le->pos.trType == TR_GRAVITY ) ) { // setup new tr vec3_t newDelta; BG_EvaluateTrajectoryDelta( &le->pos, cg.time, newDelta ); VectorCopy( re->origin, le->pos.trBase ); VectorCopy( newDelta, le->pos.trDelta ); le->pos.trTime = cg.time; if ( inWater ) le->pos.trType = TR_WATER_GRAVITY; else le->pos.trType = TR_GRAVITY; } // add to refresh list trap_R_AddRefEntityToScene( re ); }
/* ================== CG_AddMoveScale ================== */ static void CG_AddMoveScale( localEntity_t *le ) { refEntity_t *re; vec3_t delta; float len; re = &le->refEntity; if ( !( le->leFlags & (LEF_DONT_SCALE | LEF_ALT_SCALE) ) ) { re->radius = le->radius * ( cg.time - le->startTime ) * le->lifeRate + 8; } if ( le->leFlags & LEF_ALT_SCALE ) { if ( re->hModel ) { if ( (le->leFlags & LEF_AXIS_ALIGNED) && le->radius ) { // it has a variing scale AxisClear( re->axis ); AxisScale( re->axis, re->radius + le->radius * (cg.time - le->startTime) / (le->endTime - le->startTime), re->axis ); } if ( !(le->leFlags & LEF_AXIS_ALIGNED) ) { // model looks in flight direction vec3_t delta, angles; BG_EvaluateTrajectoryDelta( &le->pos, cg.time, delta ); vectoangles( delta, angles ); AnglesToAxis( angles, re->axis ); if ( re->radius || le->radius ) AxisScale( re->axis, re->radius + le->radius * (cg.time - le->startTime) / (le->endTime - le->startTime), re->axis ); } } else { // sprites scale like this re->radius += le->radius * cg.frametime / (le->endTime - le->startTime); } } BG_EvaluateTrajectory( &le->pos, cg.time, re->origin ); // check for collisions if ( le->leFlags & LEF_COLLISIONS ) { // do a trace sometimes if ( le->ti.trailTime++ > 5 ) { trace_t trace; le->ti.trailTime = 0; CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_SHOT ); VectorCopy( re->origin, re->oldorigin ); // hit something if ( trace.fraction < 1.0 ) { entityState_t *s1; int life; // do impact effect s1 = &cg_entities[le->owner].currentState; life = s1->groundEntityNum; if ( !( trace.surfaceFlags & SURF_NOIMPACT ) ) { if ( s1->generic1 & PF_IMPACT_MODEL ) { CG_MakeExplosion( trace.endpos, trace.plane.normal, cgs.gameModels[s1->modelindex2], cgs.gameShaders[s1->otherEntityNum2], life, qfalse, qfalse ); } else if ( s1->modelindex2 ) { CG_MakeExplosion( trace.endpos, trace.plane.normal, cgs.media.dishFlashModel, cgs.gameShaders[s1->modelindex2], life, qtrue, qtrue ); } } CG_FreeLocalEntity( le ); return; } } } // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( re->origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); if ( len < le->radius ) { CG_FreeLocalEntity( le ); return; } trap_R_AddRefEntityToScene( re ); }
static void CG_OffsetThirdPersonView( void ) { vec3_t forward, right, up; vec3_t view; vec3_t focusAngles; trace_t trace; static vec3_t mins = { -8, -8, -8 }; static vec3_t maxs = { 8, 8, 8 }; vec3_t focusPoint; float focusDist; float forwardScale, sideScale; vec3_t surfNormal; if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_WALLCLIMBING ) { if( cg.predictedPlayerState.stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING ) VectorSet( surfNormal, 0.0f, 0.0f, -1.0f ); else VectorCopy( cg.predictedPlayerState.grapplePoint, surfNormal ); } else VectorSet( surfNormal, 0.0f, 0.0f, 1.0f ); VectorMA( cg.refdef.vieworg, cg.predictedPlayerState.viewheight, surfNormal, cg.refdef.vieworg ); VectorCopy( cg.refdefViewAngles, focusAngles ); // if dead, look at killer if( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 ) { focusAngles[ YAW ] = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ]; cg.refdefViewAngles[ YAW ] = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ]; } //if ( focusAngles[PITCH] > 45 ) { // focusAngles[PITCH] = 45; // don't go too far overhead //} AngleVectors( focusAngles, forward, NULL, NULL ); VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint ); VectorCopy( cg.refdef.vieworg, view ); VectorMA( view, cg_thirdpersonheight.integer, surfNormal, view ); //12 //cg.refdefViewAngles[PITCH] *= 0.5; AngleVectors( cg.refdefViewAngles, forward, right, up ); forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI ); sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI ); VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view ); VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view ); // 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 if( !cg_cameraMode.integer ) { CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); if( trace.fraction != 1.0 ) { VectorCopy( trace.endpos, view ); view[ 2 ] += ( 1.0 - 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 ); } } VectorCopy( view, cg.refdef.vieworg ); // select pitch to look at focus point from vieword VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint ); focusDist = sqrt( focusPoint[ 0 ] * focusPoint[ 0 ] + focusPoint[ 1 ] * focusPoint[ 1 ] ); if ( focusDist < 1 ) { focusDist = 1; // should never happen } cg.refdefViewAngles[ PITCH ] = -180 / M_PI * atan2( focusPoint[ 2 ], focusDist ); cg.refdefViewAngles[ YAW ] -= cg_thirdPersonAngle.value; }
static void CG_DumpSpeaker_f( void ) { /* char sscrfilename[MAX_QPATH]; char soundfile[MAX_STRING_CHARS]; int i, wait, random; char *extptr, *buffptr; fileHandle_t f; // Check for argument if( trap_Argc() < 2 || trap_Argc() > 4 ) { CG_Printf( "Usage: dumpspeaker <soundfile> ( <wait=value>|<random=value> )\n" ); return; } wait = random = 0; // parse the other parameters for( i = 2; i < trap_Argc(); i++ ) { char *valueptr = NULL; trap_Argv( i, soundfile, sizeof(soundfile) ); for( buffptr = soundfile; *buffptr; buffptr++ ) { if( *buffptr == '=' ) { valueptr = buffptr + 1; break; } } Q_strncpyz( soundfile, soundfile, buffptr - soundfile + 1 ); if( !Q_stricmp( soundfile, "wait" ) ) wait = atoi( valueptr ); else if( !Q_stricmp( soundfile, "random" ) ) random = atoi( valueptr ); } // parse soundfile trap_Argv( 1, soundfile, sizeof(soundfile) ); // Open soundfile Q_strncpyz( sscrfilename, cgs.mapname, sizeof(sscrfilename) ); extptr = sscrfilename + strlen( sscrfilename ) - 4; if( extptr < sscrfilename || Q_stricmp( extptr, ".bsp" ) ) { CG_Printf( "Unable to dump, unknown map name?\n" ); return; } Q_strncpyz( extptr, ".sscr", 5 ); trap_FS_FOpenFile( sscrfilename, &f, FS_APPEND_SYNC ); if( !f ) { CG_Printf( "Failed to open '%s' for writing.\n", sscrfilename ); return; } // Build the entity definition Com_sprintf( soundfile, sizeof(soundfile), "{\n\"classname\" \"target_speaker\"\n\"origin\" \"%i %i %i\"\n\"noise\" \"%s\"\n", (int) cg.snap->ps.origin[0], (int) cg.snap->ps.origin[1], (int) cg.snap->ps.origin[2], soundfile ); if( wait ) { Q_strcat( soundfile, sizeof(soundfile), va( "\"wait\" \"%i\"\n", wait ) ); } if( random ) { Q_strcat( soundfile, sizeof(soundfile), va( "\"random\" \"%i\"\n", random ) ); } Q_strcat( soundfile, sizeof(soundfile), "}\n\n" ); // And write out/acknowledge trap_FS_Write( soundfile, strlen( soundfile ), f ); trap_FS_FCloseFile( f ); CG_Printf( "Entity dumped to '%s' (%i %i %i).\n", sscrfilename, (int) cg.snap->ps.origin[0], (int) cg.snap->ps.origin[1], (int) cg.snap->ps.origin[2] );*/ bg_speaker_t speaker; trace_t tr; vec3_t end; if( !cg.editingSpeakers ) { CG_Printf( "Speaker Edit mode needs to be activated to dump speakers\n" ); return; } memset( &speaker, 0, sizeof(speaker) ); speaker.volume = 127; speaker.range = 1250; VectorMA( cg.refdef_current->vieworg, 32, cg.refdef_current->viewaxis[0], end ); CG_Trace( &tr, cg.refdef_current->vieworg, NULL, NULL, end, -1, MASK_SOLID ); if( tr.fraction < 1.f ) { VectorCopy( tr.endpos, speaker.origin ); VectorMA( speaker.origin, -4, cg.refdef_current->viewaxis[0], speaker.origin ); } else { VectorCopy( tr.endpos, speaker.origin ); } if( !BG_SS_StoreSpeaker( &speaker ) ) { CG_Printf( S_COLOR_RED "ERROR: Failed to store speaker\n" ); } }