/* * CG_ProjectileTrail */ void CG_ProjectileTrail( centity_t *cent ) { lentity_t *le; float len; vec3_t vec; int contents; int trailTime; float radius = 6.5f, alpha = 0.35f; #if 0 struct shader_s *shader = CG_MediaShader( cgs.media.shaderRocketTrailSmokePuff ); #else struct shader_s *shader = CG_MediaShader( cgs.media.shaderSmokePuff ); #endif CG_ProjectileFireTrail( cent ); // add fire trail if( !cg_projectileTrail->integer ) return; // didn't move VectorSubtract( cent->ent.origin, cent->trailOrigin, vec ); len = VectorNormalize( vec ); if( !len ) return; // density is found by quantity per second trailTime = (int)(1000.0f / cg_projectileTrail->value ); if( trailTime < 1 ) trailTime = 1; // we don't add more than one sprite each frame. If frame // ratio is too slow, people will prefer having less sprites on screen if( cent->localEffects[LOCALEFFECT_ROCKETTRAIL_LAST_DROP] + trailTime < cg.time ) { cent->localEffects[LOCALEFFECT_ROCKETTRAIL_LAST_DROP] = cg.time; contents = ( CG_PointContents( cent->trailOrigin ) & CG_PointContents( cent->ent.origin ) ); if( contents & MASK_WATER ) { shader = CG_MediaShader( cgs.media.shaderWaterBubble ); radius = 3 + crandom(); alpha = 1.0f; } // racesow if( cg_raceGhosts->integer && (unsigned int)cent->current.ownerNum != cg.predictedPlayerState.POVnum ) alpha *= cg_raceGhostsAlpha->value; // !racesow clamp( alpha, 0.0f, 1.0f ); le = CG_AllocSprite( LE_PUFF_SHRINK, cent->trailOrigin, radius, 20, 1.0f, 1.0f, 1.0f, alpha, 0, 0, 0, 0, shader ); VectorSet( le->velocity, -vec[0] * 5 + crandom()*5, -vec[1] * 5 + crandom()*5, -vec[2] * 5 + crandom()*5 + 3 ); le->ent.rotation = rand () % 360; } }
/* * CG_NewBloodTrail */ void CG_NewBloodTrail( centity_t *cent ) { lentity_t *le; float len; vec3_t vec; int contents; int trailTime; float radius = 2.5f, alpha = cg_bloodTrailAlpha->value; struct shader_s *shader = CG_MediaShader( cgs.media.shaderBloodTrailPuff ); if( !cg_showBloodTrail->integer ) return; if( !cg_bloodTrail->integer ) return; // didn't move VectorSubtract( cent->ent.origin, cent->trailOrigin, vec ); len = VectorNormalize( vec ); if( !len ) return; // density is found by quantity per second trailTime = (int)( 1000.0f / cg_bloodTrail->value ); if( trailTime < 1 ) trailTime = 1; // we don't add more than one sprite each frame. If frame // ratio is too slow, people will prefer having less sprites on screen if( cent->localEffects[LOCALEFFECT_BLOODTRAIL_LAST_DROP] + trailTime < cg.time ) { cent->localEffects[LOCALEFFECT_BLOODTRAIL_LAST_DROP] = cg.time; contents = ( CG_PointContents( cent->trailOrigin ) & CG_PointContents( cent->ent.origin ) ); if( contents & MASK_WATER ) { shader = CG_MediaShader( cgs.media.shaderBloodTrailLiquidPuff ); radius = 4 + crandom(); alpha = 0.5f * cg_bloodTrailAlpha->value; } clamp( alpha, 0.0f, 1.0f ); le = CG_AllocSprite( LE_SCALE_ALPHA_FADE, cent->trailOrigin, radius, 8, 1.0f, 1.0f, 1.0f, alpha, 0, 0, 0, 0, shader ); VectorSet( le->velocity, -vec[0] * 5 + crandom()*5, -vec[1] * 5 + crandom()*5, -vec[2] * 5 + crandom()*5 + 3 ); le->ent.rotation = rand() % 360; } }
void CG_Dash( entity_state_t *state ) { lentity_t *le; vec3_t pos, dvect, angle = { 0, 0, 0 }; if( !(cg_cartoonEffects->integer & 4) ) return; // KoFFiE: Calculate angle based on relative position of the previous origin state of the player entity VectorSubtract( state->origin, cg_entities[state->number].prev.origin, dvect ); // ugly inline define -> Ignore when difference between 2 positions was less than this value. #define IGNORE_DASH 6.0 if( ( dvect[0] > -IGNORE_DASH ) && ( dvect[0] < IGNORE_DASH ) && ( dvect[1] > -IGNORE_DASH ) && ( dvect[1] < IGNORE_DASH ) ) return; VecToAngles( dvect, angle ); VectorCopy( state->origin, pos ); angle[1] += 270; // Adjust angle pos[2] -= 24; // Adjust the position to ground height if( CG_PointContents( pos ) & MASK_WATER ) return; // no smoke under water :) le = CG_AllocModel( LE_DASH_SCALE, pos, angle, 7, //5 1.0, 1.0, 1.0, 1.0, 0, 0, 0, 0, CG_MediaModel( cgs.media.modDash ), NULL ); le->ent.scale = 0.01f; le->ent.axis[2][2] *= 2.0f; }
void CG_DustCircle( vec3_t pos, vec3_t dir, float radius, int count ) { vec3_t dir_per1; vec3_t dir_per2; vec3_t dir_temp = { 0.0f, 0.0f, 0.0f }; int i; float angle; if( CG_PointContents( pos ) & MASK_WATER ) return; // no smoke under water :) PerpendicularVector( dir_per2, dir ); CrossProduct( dir, dir_per2, dir_per1 ); VectorScale( dir_per1, VectorNormalize( dir_per1 ), dir_per1 ); VectorScale( dir_per2, VectorNormalize( dir_per2 ), dir_per2 ); for( i = 0; i < count; i++ ) { angle = (float)( 6.2831f / count * i ); VectorSet( dir_temp, 0.0f, 0.0f, 0.0f ); VectorMA( dir_temp, sin( angle ), dir_per1, dir_temp ); VectorMA( dir_temp, cos( angle ), dir_per2, dir_temp ); //VectorScale(dir_temp, VectorNormalize(dir_temp),dir_temp ); VectorScale( dir_temp, crandom()*10 + radius, dir_temp ); CG_Explosion_Puff_2( pos, dir_temp, 10 ); } }
static int CG_CalcFov( void ) { float fov; int contents; // check if underwater contents = CG_PointContents( cg.refdef.vieworg, -1 ); if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){ cg.refdef.rdflags |= RDF_UNDERWATER; } else { cg.refdef.rdflags &= ~RDF_UNDERWATER; } // set world fov fov = cg_fov.value; CG_CalcFov2( &cg.refdef, &fov, &cg.refdef.fov_x, &cg.refdef.fov_y ); // set view weapon fov cg.viewWeaponFov = cg_weaponFov.value ? cg_weaponFov.value : cg_fov.value; CG_CalcFov2( &cg.refdef, &cg.viewWeaponFov, &cg.refdef.weapon_fov_x, &cg.refdef.weapon_fov_y ); if ( !cg.cur_lc->zoomed ) { cg.cur_lc->zoomSensitivity = 1; } else { cg.cur_lc->zoomSensitivity = cg.refdef.fov_y / 75.0; } return (cg.refdef.rdflags & RDF_UNDERWATER); }
/* * CG_ImpactSmokePuff */ void CG_ImpactSmokePuff( vec3_t origin, vec3_t dir, float radius, float alpha, int time, int speed ) { #define SMOKEPUFF_MAXVIEWDIST 700 lentity_t *le; struct shader_s *shader = CG_MediaShader( cgs.media.shaderSmokePuff ); if( CG_PointContents( origin ) & MASK_WATER ) { return; } if( DistanceFast( origin, cg.view.origin ) * cg.view.fracDistFOV > SMOKEPUFF_MAXVIEWDIST ) return; if( !VectorLength( dir ) ) { VectorCopy( cg.view.axis[FORWARD], dir ); VectorInverse( dir ); } VectorNormalize( dir ); //offset the origin by half of the radius VectorMA( origin, radius*0.5f, dir, origin ); le = CG_AllocSprite( LE_SCALE_ALPHA_FADE, origin, radius + crandom(), time, 1, 1, 1, alpha, 0, 0, 0, 0, shader ); le->ent.rotation = rand() % 360; VectorScale( dir, speed, le->velocity ); }
static qboolean CG_RainParticleGenerate(cg_atmosphericParticle_t * particle, vec3_t currvec, float currweight) { // Attempt to 'spot' a raindrop somewhere below a sky texture. float angle, distance, origz; vec3_t testpoint, testend; trace_t tr; angle = random() * 2 * M_PI; distance = 20 + MAX_ATMOSPHERIC_DISTANCE * random(); testpoint[0] = testend[0] = cg.refdef.vieworg[0] + sin(angle) * distance; testpoint[1] = testend[1] = cg.refdef.vieworg[1] + cos(angle) * distance; testpoint[2] = origz = cg.refdef.vieworg[2]; testend[2] = testpoint[2] + MAX_ATMOSPHERIC_HEIGHT; while (1) { if (testpoint[2] >= MAX_ATMOSPHERIC_HEIGHT) return (qfalse); if (testend[2] >= MAX_ATMOSPHERIC_HEIGHT) testend[2] = MAX_ATMOSPHERIC_HEIGHT - 1; CG_Trace(&tr, testpoint, NULL, NULL, testend, ENTITYNUM_NONE, MASK_SOLID | MASK_WATER); if (tr.startsolid) // Stuck in something, skip over it. { testpoint[2] += 64; testend[2] = testpoint[2] + MAX_ATMOSPHERIC_HEIGHT; } else if (tr.fraction == 1) // Didn't hit anything, we're (probably) outside the world return (qfalse); else if (tr.surfaceFlags & SURF_SKY) // Hit sky, this is where we start. break; else return (qfalse); } particle->active = qtrue; particle->colour[0] = 0.6 + 0.2 * random(); particle->colour[1] = 0.6 + 0.2 * random(); particle->colour[2] = 0.6 + 0.2 * random(); VectorCopy(tr.endpos, particle->pos); VectorCopy(currvec, particle->delta); particle->delta[2] += crandom() * 100; VectorNormalize2(particle->delta, particle->deltaNormalized); particle->height = ATMOSPHERIC_RAIN_HEIGHT + crandom() * 100; particle->weight = currweight; particle->effectshader = &cg_atmFx.effectshaders[0]; distance = ((float) (tr.endpos[2] - MIN_ATMOSPHERIC_HEIGHT)) / -particle->delta[2]; VectorMA(tr.endpos, distance, particle->delta, testend); CG_Trace(&tr, particle->pos, NULL, NULL, testend, ENTITYNUM_NONE, MASK_SOLID | MASK_WATER); particle->minz = tr.endpos[2]; tr.endpos[2]--; VectorCopy(tr.plane.normal, particle->surfacenormal); particle->surface = tr.surfaceFlags; particle->contents = CG_PointContents(tr.endpos, ENTITYNUM_NONE); return (qtrue); }
/* ================ CG_WaterLevel Returns waterlevel for entity origin ================ */ int CG_WaterLevel(centity_t *cent) { vec3_t point; int contents, sample1, sample2, anim, waterlevel; int viewheight; anim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT; if (anim == LEGS_WALKCR || anim == LEGS_IDLECR) { viewheight = CROUCH_VIEWHEIGHT; } else { viewheight = DEFAULT_VIEWHEIGHT; } // // get waterlevel, accounting for ducking // waterlevel = 0; point[0] = cent->lerpOrigin[0]; point[1] = cent->lerpOrigin[1]; point[2] = cent->lerpOrigin[2] + MINS_Z + 1; contents = CG_PointContents(point, -1); if (contents & MASK_WATER) { sample2 = viewheight - MINS_Z; sample1 = sample2 / 2; waterlevel = 1; point[2] = cent->lerpOrigin[2] + MINS_Z + sample1; contents = CG_PointContents(point, -1); if (contents & MASK_WATER) { waterlevel = 2; point[2] = cent->lerpOrigin[2] + MINS_Z + sample2; contents = CG_PointContents(point, -1); if (contents & MASK_WATER) { waterlevel = 3; } } } return waterlevel; }
/* * CG_RenderFlags */ static int CG_RenderFlags( void ) { int rdflags, contents; rdflags = 0; // set the RDF_UNDERWATER and RDF_CROSSINGWATER bitflags contents = CG_PointContents( cg.view.origin ); if( contents & MASK_WATER ) { rdflags |= RDF_UNDERWATER; // undewater, check above contents = CG_PointContents( tv( cg.view.origin[0], cg.view.origin[1], cg.view.origin[2] + 9 ) ); if( !(contents & MASK_WATER) ) rdflags |= RDF_CROSSINGWATER; } else { // look down a bit contents = CG_PointContents( tv( cg.view.origin[0], cg.view.origin[1], cg.view.origin[2] - 9 ) ); if( contents & MASK_WATER ) rdflags |= RDF_CROSSINGWATER; } if( cg.oldAreabits ) rdflags |= RDF_OLDAREABITS; if( cg.portalInView ) rdflags |= RDF_PORTALINVIEW; if( cg_outlineWorld->integer ) rdflags |= RDF_WORLDOUTLINES; if( cg.view.flipped ) rdflags |= RDF_FLIPPED; rdflags |= CG_SkyPortal(); return rdflags; }
/* ================ CG_WaterLevel Returns waterlevel for entity origin ================ */ int CG_WaterLevel(centity_t *cent) { vec3_t point; int contents, sample1, sample2, anim, waterlevel; // get waterlevel, accounting for ducking waterlevel = 0; VectorCopy(cent->lerpOrigin, point); point[2] += MINS_Z + 1; anim = cent->currentState.legsAnim & ~ANIM_TOGGLEBIT; if (anim == LEGS_WALKCR || anim == LEGS_IDLECR) { point[2] += CROUCH_VIEWHEIGHT; } else { point[2] += DEFAULT_VIEWHEIGHT; } contents = CG_PointContents(point, -1); if (contents & MASK_WATER) { sample2 = point[2] - MINS_Z; sample1 = sample2 / 2; waterlevel = 1; point[2] = cent->lerpOrigin[2] + MINS_Z + sample1; contents = CG_PointContents(point, -1); if (contents & MASK_WATER) { waterlevel = 2; point[2] = cent->lerpOrigin[2] + MINS_Z + sample2; contents = CG_PointContents(point, -1); if (contents & MASK_WATER) { waterlevel = 3; } } } return waterlevel; }
/* * CG_BloodDamageEffect */ void CG_BloodDamageEffect( const vec3_t origin, const vec3_t dir, int damage ) { lentity_t *le; int count, i; float radius = 5.0f, alpha = cg_bloodTrailAlpha->value; int time = 8; struct shader_s *shader = CG_MediaShader( cgs.media.shaderBloodImpactPuff ); vec3_t local_dir; if( !cg_showBloodTrail->integer ) return; if( !cg_bloodTrail->integer ) return; count = (int)( damage * 0.25f ); clamp( count, 1, 10 ); if( CG_PointContents( origin ) & MASK_WATER ) { shader = CG_MediaShader( cgs.media.shaderBloodTrailLiquidPuff ); radius += ( 1 + crandom() ); alpha = 0.5f * cg_bloodTrailAlpha->value; } if( !VectorLength( dir ) ) { VectorNegate( &cg.view.axis[AXIS_FORWARD], local_dir ); } else { VectorNormalize2( dir, local_dir ); } for( i = 0; i < count; i++ ) { le = CG_AllocSprite( LE_PUFF_SHRINK, origin, radius + crandom(), time, 1, 1, 1, alpha, 0, 0, 0, 0, shader ); le->ent.rotation = rand() % 360; // randomize dir VectorSet( le->velocity, -local_dir[0] * 5 + crandom()*5, -local_dir[1] * 5 + crandom()*5, -local_dir[2] * 5 + crandom()*5 + 3 ); VectorMA( local_dir, min( 6, count ), le->velocity, le->velocity ); } }
/* * CG_CalcViewBob */ static void CG_CalcViewBob( void ) { float bobMove, bobTime, bobScale; if( !cg.view.drawWeapon ) return; // calculate speed and cycle to be used for all cyclic walking effects cg.xyspeed = sqrt( cg.predictedPlayerState.pmove.velocity[0]*cg.predictedPlayerState.pmove.velocity[0] + cg.predictedPlayerState.pmove.velocity[1]*cg.predictedPlayerState.pmove.velocity[1] ); bobScale = 0; if( cg.xyspeed < 5 ) cg.oldBobTime = 0; // start at beginning of cycle again else if( cg_gunbob->integer ) { if( !ISVIEWERENTITY( cg.view.POVent ) ) bobScale = 0.0f; else if( CG_PointContents( cg.view.origin ) & MASK_WATER ) bobScale = 0.75f; else { centity_t *cent; vec3_t mins, maxs; trace_t trace; cent = &cg_entities[cg.view.POVent]; GS_BBoxForEntityState( ¢->current, mins, maxs ); maxs[2] = mins[2]; mins[2] -= ( 1.6f*STEPSIZE ); CG_Trace( &trace, cg.predictedPlayerState.pmove.origin, mins, maxs, cg.predictedPlayerState.pmove.origin, cg.view.POVent, MASK_PLAYERSOLID ); if( trace.startsolid || trace.allsolid ) { if( cg.predictedPlayerState.pmove.stats[PM_STAT_CROUCHTIME] ) bobScale = 1.5f; else bobScale = 2.5f; } } } bobMove = cg.frameTime * bobScale; bobTime = ( cg.oldBobTime += bobMove ); cg.bobCycle = (int)bobTime; cg.bobFracSin = fabs( sin( bobTime*M_PI ) ); }
static int Player_GetUnderwater( lua_State *L, jpluaEntity_t *ent ) { qboolean underwater = qfalse; #if defined(PROJECT_GAME) if ( ent->waterlevel == 3 ) { underwater = qtrue; } #elif defined(PROJECT_CGAME) const vector3 *pos = ((int)(ent - ents) == cg.clientNum) ? &cg.predictedPlayerState.origin : &ent->currentState.pos.trBase; // not cent->lerpOrigin? if ( CG_PointContents( pos, -1 ) & (CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA) ) { underwater = qtrue; } #endif lua_pushboolean( L, underwater ); return 1; }
/* * CG_LeadBubbleTrail */ static void CG_LeadBubbleTrail( trace_t *tr, vec3_t water_start ) { // if went through water, determine where the end and make a bubble trail vec3_t dir, pos; VectorSubtract( tr->endpos, water_start, dir ); VectorNormalize( dir ); VectorMA( tr->endpos, -2, dir, pos ); if( CG_PointContents( pos ) & MASK_WATER ) VectorCopy( pos, tr->endpos ); else CG_Trace( tr, pos, vec3_origin, vec3_origin, water_start, tr->ent ? cg_entities[tr->ent].current.number : 0, MASK_WATER ); VectorAdd( water_start, tr->endpos, pos ); VectorScale( pos, 0.5, pos ); CG_BubbleTrail( water_start, tr->endpos, 32 ); }
/* ======================================================================================================================================= CG_BubbleThink ======================================================================================================================================= */ void CG_BubbleThink(localEntity_t *le) { int contents; vec3_t newOrigin; trace_t trace; // 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); contents = CG_PointContents(trace.endpos, -1); if (!(contents & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))) { // bubble isn't in liquid anymore, remove it CG_FreeLocalEntity(le); return; } CG_AddMoveScaleFade(le); }
void CG_ExplosionsDust( vec3_t pos, vec3_t dir, float radius ) { const int count = 32; /* Number of sprites used to create the circle */ lentity_t *le; struct shader_s *shader = CG_MediaShader( cgs.media.shaderSmokePuff3 ); vec3_t dir_per1; vec3_t dir_per2; vec3_t dir_temp = { 0.0f, 0.0f, 0.0f }; int i; float angle; if( CG_PointContents( pos ) & MASK_WATER ) return; // no smoke under water :) PerpendicularVector( dir_per2, dir ); CrossProduct( dir, dir_per2, dir_per1 ); //VectorScale( dir_per1, VectorNormalize( dir_per1 ), dir_per1 ); //VectorScale( dir_per2, VectorNormalize( dir_per2 ), dir_per2 ); // make a circle out of the specified number (int count) of sprites for( i = 0; i < count; i++ ) { angle = (float)( 6.2831f / count * i ); VectorSet( dir_temp, 0.0f, 0.0f, 0.0f ); VectorMA( dir_temp, sin( angle ), dir_per1, dir_temp ); VectorMA( dir_temp, cos( angle ), dir_per2, dir_temp ); //VectorScale(dir_temp, VectorNormalize(dir_temp),dir_temp ); VectorScale( dir_temp, crandom()*8 + radius + 16.0f, dir_temp ); // make the sprite smaller & alpha'd le = CG_AllocSprite( LE_ALPHA_FADE, pos, 10, 10, 1.0f, 1.0f, 1.0f, 1.0f, 0, 0, 0, 0, shader ); VectorCopy( dir_temp, le->velocity ); } }
static int CG_CalcFov( void ) { float x; float phase; float v; int contents; float fov_x, fov_y; float zoomFov; float f; int inwater; if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { // if in intermission, use a fixed value fov_x = 90; } else { // user selectable if ( cgs.dmflags & DF_FIXED_FOV ) { // dmflag to prevent wide fov for all clients fov_x = 90; } else { fov_x = cg_fov.value; if ( fov_x < 1 ) { fov_x = 1; } else if ( fov_x > 160 ) { fov_x = 160; } } // account for zooms zoomFov = cg_zoomFov.value; if ( zoomFov < 1 ) { zoomFov = 1; } else if ( zoomFov > 160 ) { zoomFov = 160; } if ( cg.zoomed ) { f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; if ( f > 1.0 ) { fov_x = zoomFov; } else { fov_x = fov_x + f * ( zoomFov - fov_x ); } } else { f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; if ( f > 1.0 ) { fov_x = fov_x; } else { fov_x = zoomFov + f * ( fov_x - zoomFov ); } } } x = cg.refdef.width / tan( fov_x / 360 * M_PI ); fov_y = atan2( cg.refdef.height, x ); fov_y = fov_y * 360 / M_PI; // warp if underwater contents = CG_PointContents( cg.refdef.vieworg, -1 ); if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){ phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2; v = WAVE_AMPLITUDE * sin( phase ); fov_x += v; fov_y -= v; inwater = qtrue; } else { inwater = qfalse; } // set it cg.refdef.fov_x = fov_x; cg.refdef.fov_y = fov_y; if ( !cg.zoomed ) { cg.zoomSensitivity = 1; } else { cg.zoomSensitivity = cg.refdef.fov_y / 75.0; } return inwater; }
/* ================ 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 ); CG_AddRefEntityWithMinLight( &le->refEntity ); le->refEntity.origin[2] = oldZ; } else { CG_AddRefEntityWithMinLight( &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 ); } CG_AddRefEntityWithMinLight( &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 ); CG_AddRefEntityWithMinLight( &le->refEntity ); }
static int CG_CalcFov( void ) { float y; float phase; float v; int contents; float fov_x, fov_y; float zoomFov; float f; int inwater; int attribFov; usercmd_t cmd; usercmd_t oldcmd; int cmdNum; cmdNum = trap_GetCurrentCmdNumber(); trap_GetUserCmd( cmdNum, &cmd ); trap_GetUserCmd( cmdNum - 1, &oldcmd ); // switch follow modes if necessary: cycle between free -> follow -> third-person follow if ( usercmdButtonPressed( cmd.buttons, BUTTON_USE_HOLDABLE ) && !usercmdButtonPressed( oldcmd.buttons, BUTTON_USE_HOLDABLE ) ) { if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { if ( !cg.chaseFollow ) { cg.chaseFollow = qtrue; } else { cg.chaseFollow = qfalse; trap_SendClientCommand( "follow\n" ); } } else if ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ) { trap_SendClientCommand( "follow\n" ); } } if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION || ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ) || ( cg.renderingThirdPerson ) ) { // if in intermission or third person, use a fixed value fov_y = BASE_FOV_Y; } else { // don't lock the fov globally - we need to be able to change it if ( ( attribFov = trap_Cvar_VariableIntegerValue( BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->fovCvar ) ) ) { if ( attribFov < 80 ) { attribFov = 80; } else if ( attribFov >= 140 ) { attribFov = 140; } } else { attribFov = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->fov; } attribFov *= 0.75; fov_y = attribFov; if ( fov_y < 1.0f ) { fov_y = 1.0f; } else if ( fov_y > MAX_FOV_Y ) { fov_y = MAX_FOV_Y; } if ( cg.spawnTime > ( cg.time - FOVWARPTIME ) && BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_CLASS ], SCA_FOVWARPS ) ) { float fraction = ( float )( cg.time - cg.spawnTime ) / FOVWARPTIME; fov_y = MAX_FOV_WARP_Y - ( ( MAX_FOV_WARP_Y - fov_y ) * fraction ); } // account for zooms zoomFov = BG_Weapon( cg.predictedPlayerState.weapon )->zoomFov * 0.75f; if ( zoomFov < 1.0f ) { zoomFov = 1.0f; } else if ( zoomFov > attribFov ) { zoomFov = attribFov; } // only do all the zoom stuff if the client CAN zoom // FIXME: zoom control is currently hard coded to WBUTTON_ATTACK2 if ( BG_Weapon( cg.predictedPlayerState.weapon )->canZoom ) { if ( cg.zoomed ) { f = ( cg.time - cg.zoomTime ) / ( float ) ZOOM_TIME; if ( f > 1.0f ) { fov_y = zoomFov; } else { fov_y = fov_y + f * ( zoomFov - fov_y ); } // WBUTTON_ATTACK2 isn't held so unzoom next time if ( !usercmdButtonPressed( cmd.buttons, BUTTON_ATTACK2 ) || cg.snap->ps.weaponstate == WEAPON_RELOADING ) { cg.zoomed = qfalse; cg.zoomTime = MIN( cg.time, cg.time + cg.time - cg.zoomTime - ZOOM_TIME ); } } else { f = ( cg.time - cg.zoomTime ) / ( float ) ZOOM_TIME; if ( f < 1.0f ) { fov_y = zoomFov + f * ( fov_y - zoomFov ); } // WBUTTON_ATTACK2 is held so zoom next time if ( usercmdButtonPressed( cmd.buttons, BUTTON_ATTACK2 ) && cg.snap->ps.weaponstate != WEAPON_RELOADING ) { cg.zoomed = qtrue; cg.zoomTime = MIN( cg.time, cg.time + cg.time - cg.zoomTime - ZOOM_TIME ); } } } else if ( cg.zoomed ) { cg.zoomed = qfalse; } } y = cg.refdef.height / tan( 0.5f * DEG2RAD( fov_y ) ); fov_x = atan2( cg.refdef.width, y ); fov_x = 2.0f * RAD2DEG( fov_x ); // warp if underwater contents = CG_PointContents( cg.refdef.vieworg, -1 ); if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { phase = cg.time / 1000.0f * WAVE_FREQUENCY * M_PI * 2.0f; v = WAVE_AMPLITUDE * sin( phase ); fov_x += v; fov_y -= v; inwater = qtrue; } else { inwater = qfalse; } // set it cg.refdef.fov_x = fov_x; cg.refdef.fov_y = fov_y; if ( !cg.zoomed ) { cg.zoomSensitivity = 1.0f; } else { cg.zoomSensitivity = cg.refdef.fov_y / 75.0f; } return inwater; }
/* ==================== CG_ParseSpawnVars Parses a brace bounded set of key / value pairs out of the level's entity strings into cg.spawnVars[] This does not actually spawn an entity. ==================== */ qboolean CG_ParseSpawnVars( void ) { char keyname[MAX_TOKEN_CHARS]; char com_token[MAX_TOKEN_CHARS]; char token[MAX_TOKEN_CHARS]; //char buf[MAX_TOKEN_CHARS]; const char *line; qboolean newLine; vec3_t origin; int wait; titem_t ti; int val; int skipItem; int i; //char *gametypeName; //char *value; int angle; int spawnflags; qboolean spawnPoint; qboolean redSpawn; qboolean blueSpawn; qboolean initial; qboolean deathmatch; qboolean portal; qboolean portalHasTarget; qboolean startTimer; qboolean stopTimer; qboolean gotOrigin; int pointContents; static char *gametypeNames[] = { "ffa", "tournament", "single", "team", /*FIXME clanarena*/ "ca", "ctf", "oneflag", "obelisk", "harvester", "ft", "dom", "ad", "rr", "race" }; //Com_Printf("cgs.gametype : %d\n", cgs.gametype); cg.numSpawnVars = 0; cg.numSpawnVarChars = 0; // parse the opening brace if ( !trap_GetEntityToken( com_token, sizeof( com_token ) ) ) { // end of spawn string return qfalse; } if ( com_token[0] != '{' ) { CG_Error( "CG_ParseSpawnVars: found %s when expecting {",com_token ); } // go through all the key / value pairs while ( 1 ) { // parse key if ( !trap_GetEntityToken( keyname, sizeof( keyname ) ) ) { CG_Error( "CG_ParseSpawnVars: EOF without closing brace" ); } if ( keyname[0] == '}' ) { break; } // parse value if ( !trap_GetEntityToken( com_token, sizeof( com_token ) ) ) { CG_Error( "CG_ParseSpawnVars: EOF without closing brace" ); } if ( com_token[0] == '}' ) { CG_Error( "CG_ParseSpawnVars: closing brace without data" ); } if ( cg.numSpawnVars == MAX_SPAWN_VARS ) { CG_Error( "CG_ParseSpawnVars: MAX_SPAWN_VARS" ); } cg.spawnVars[ cg.numSpawnVars ][0] = CG_AddSpawnVarToken( keyname ); cg.spawnVars[ cg.numSpawnVars ][1] = CG_AddSpawnVarToken( com_token ); cg.numSpawnVars++; if (!Q_stricmp(keyname, "enableDust")) { cg.mapEnableDust = atoi(com_token); } else if (!Q_stricmp(keyname, "enableBreath")) { cg.mapEnableBreath = atoi(com_token); } //CG_Printf("spawnvars %s : %s\n", cg.spawnVars[ cg.numSpawnVars - 1 ][0], cg.spawnVars[ cg.numSpawnVars - 1 ][1]); } if (cgs.gametype == GT_CA) { //return qtrue; } ti = noitem; wait = 0; skipItem = 0; angle = 0; spawnflags = 0; spawnPoint = qfalse; redSpawn = qfalse; blueSpawn = qfalse; initial = qfalse; deathmatch = qfalse; portal = qfalse; portalHasTarget = qfalse; startTimer = qfalse; stopTimer = qfalse; gotOrigin = qfalse; // get the rest while (1) { // parse key if ( !trap_GetEntityToken( keyname, sizeof( keyname ) ) ) { //CG_Printf("all done parsing ents\n"); break; } //Com_Printf("'%s'\n", keyname); if (keyname[0] == '{') { //CG_Printf("{\n"); // clear values ti = noitem; wait = 0; skipItem = 0; angle = 0; spawnflags = 0; spawnPoint = qfalse; redSpawn = qfalse; blueSpawn = qfalse; initial = qfalse; deathmatch = qfalse; portal = qfalse; portalHasTarget = qfalse; startTimer = qfalse; stopTimer = qfalse; gotOrigin = qfalse; continue; } if ( keyname[0] == '}' ) { //CG_Printf("}\n"); if (spawnPoint) { //Com_Printf("spawn: %f %f %f angle:%d spawnflags:%d r:%d b:%d\n", origin[0], origin[1], origin[2], angle, spawnflags, redSpawn, blueSpawn); i = cg.numSpawnPoints; if (i >= MAX_SPAWN_POINTS) { continue; } VectorCopy(origin, cg.spawnPoints[i].origin); cg.spawnPoints[i].angle = angle; cg.spawnPoints[i].spawnflags = spawnflags; cg.spawnPoints[i].redSpawn = redSpawn; cg.spawnPoints[i].blueSpawn = blueSpawn; cg.spawnPoints[i].initial = initial; cg.spawnPoints[i].deathmatch = deathmatch; cg.numSpawnPoints++; continue; } if (portal) { if (!portalHasTarget) { if (cg.numMirrorSurfaces > MAX_MIRROR_SURFACES) { Com_Printf("^3max mirror surfaces (%d)\n", MAX_MIRROR_SURFACES); continue; } VectorCopy(origin, cg.mirrorSurfaces[cg.numMirrorSurfaces]); cg.numMirrorSurfaces++; //Com_Printf("origin: %f %f %f\n", origin[0], origin[1], origin[2]); } //Com_Printf("^1origin: %f %f %f (hasTarget %d)\n", origin[0], origin[1], origin[2], portalHasTarget); continue; } if (startTimer) { cg.hasStartTimer = qtrue; VectorCopy(origin, cg.startTimerOrigin); Com_Printf("^5start timer %f %f %f\n", origin[0], origin[1], origin[2]); continue; } if (stopTimer) { cg.hasStopTimer = qtrue; VectorCopy(origin, cg.stopTimerOrigin); Com_Printf("^5stop timer %f %f %f\n", origin[0], origin[1], origin[2]); continue; } if (gotOrigin) { pointContents = CG_PointContents(origin, 0); } else { pointContents = 0; } if (skipItem) { continue; } else if (pointContents & 0x1) { // it's in an invalid place, some maps have this qzdm20 has 2 megas continue; } else if (ti == redArmor) { if (!wait) wait = 25; AddTimedItem(&cg.numRedArmors, cg.redArmors, origin, wait); } else if (ti == yellowArmor) { if (!wait) wait = 25; AddTimedItem(&cg.numYellowArmors, cg.yellowArmors, origin, wait); } else if (ti == greenArmor) { if (!wait) wait = 25; AddTimedItem(&cg.numGreenArmors, cg.greenArmors, origin, wait); } else if (ti == megaHealth) { if (!wait) wait = 35; AddTimedItem(&cg.numMegaHealths, cg.megaHealths, origin, wait); } else if (ti == quad) { if (!wait) wait = 120; AddTimedItem(&cg.numQuads, cg.quads, origin, wait); } else if (ti == battleSuit) { if (!wait) wait = 120; AddTimedItem(&cg.numBattleSuits, cg.battleSuits, origin, wait); } continue; } // parse value if ( !trap_GetEntityToken( com_token, sizeof( com_token ) ) ) { CG_Error( "CG_ParseSpawnVars: EOF without closing brace" ); } if ( com_token[0] == '}' ) { CG_Error( "CG_ParseSpawnVars: closing brace without data" ); } //CG_Printf("^3ent %s : %s\n", keyname, com_token); if (!Q_stricmp(keyname, "classname")) { if (!Q_stricmp(com_token, "info_player_deathmatch") || !Q_stricmp(com_token, "info_player_start")) { //FIXME spawnflags 4 spawnPoint = qtrue; deathmatch = qtrue; blueSpawn = qtrue; redSpawn = qtrue; //Com_Printf("deathmatch spawn %d\n", cg.numSpawnPoints); } else if (!Q_stricmp(com_token, "team_CTF_redspawn")) { spawnPoint = qtrue; redSpawn = qtrue; } else if (!Q_stricmp(com_token, "team_CTF_bluespawn")) { spawnPoint = qtrue; blueSpawn = qtrue; } else if (!Q_stricmp(com_token, "team_CTF_redplayer")) { spawnPoint = qtrue; redSpawn = qtrue; initial = qtrue; } else if (!Q_stricmp(com_token, "team_CTF_blueplayer")) { spawnPoint = qtrue; blueSpawn = qtrue; initial = qtrue; } else if (!Q_stricmp(com_token, "item_armor_body")) { // red armor //Com_Printf("red armor\n"); ti = redArmor; } else if (!Q_stricmp(com_token, "item_armor_combat")) { // yellow armor //Com_Printf("yellow armor\n"); ti = yellowArmor; } else if (!Q_stricmp(com_token, "item_armor_jacket")) { ti = greenArmor; } else if (!Q_stricmp(com_token, "item_health_mega")) { //Com_Printf("mega health\n"); ti = megaHealth; } else if (!Q_stricmp(com_token, "item_quad")) { //Com_Printf("quad\n"); ti = quad; } else if (!Q_stricmp(com_token, "item_enviro")) { // battlesuit //Com_Printf("battlesuit?\n"); ti = battleSuit; // count ?? } else if (!Q_stricmp(com_token, "misc_portal_surface")) { //Com_Printf("^3portal surface\n"); portal = qtrue; } else if (!Q_stricmp(com_token, "target_starttimer")) { startTimer = qtrue; } else if (!Q_stricmp(com_token, "target_stoptimer")) { stopTimer = qtrue; } else { //FIXME other? regen? } } else if (!Q_stricmp(keyname, "origin")) { if (SC_ParseVec3FromStr(com_token, origin) == -1) { //Com_Printf("FIXME getting origin for entitytoken %s", com_token); origin[0] = 0.0; origin[1] = 0.0; origin[2] = 0.0; } gotOrigin = qtrue; //Com_Printf("*** origin: %f %f %f\n", origin[0], origin[1], origin[2]); } else if (!Q_stricmp(keyname, "target")) { portalHasTarget = qtrue; //Q_strncpyz(buf, com_token, sizeof(buf)); //Com_Printf("^5target == '%s'\n", buf); } else if (!Q_stricmp(keyname, "wait")) { wait = atoi(com_token); } else if (!Q_stricmp(keyname, "notteam")) { val = atoi(com_token); //if (val && cgs.gametype >= GT_TEAM) { if (val && CG_IsTeamGame(cgs.gametype)) { skipItem = 1; } } else if (!Q_stricmp(keyname, "not_gametype")) { const char *value = com_token; const char *gametypeName; const char *s; //FIXME could be more than one digit in the list ?? if (isdigit(com_token[0])) { val = atoi(com_token); if (cgs.protocol == PROTOCOL_QL) { if (cgs.gametype == GT_RACE) { if (val == 2) { skipItem = 1; } } else { if (cgs.gametype == val) { skipItem = 1; } } } else { if (cgs.gametype == val) { skipItem = 1; } } } else { // string value s = NULL; if (cgs.gametype < ARRAY_LEN(gametypeNames)) { gametypeName = gametypeNames[cgs.gametype]; s = strstr(value, gametypeName); } if (!s) { // try alternate quake live gametype names if (cgs.gametype == GT_TEAM) { s = strstr(value, "tdm"); } else if (cgs.gametype == GT_TOURNAMENT) { s = strstr(value, "duel"); } else if (cgs.gametype == GT_HARVESTER) { s = strstr(value, "har"); } else if (cgs.gametype == GT_1FCTF) { s = strstr(value, "1f"); } else if (cgs.gametype == GT_OBELISK) { s = strstr(value, "obj"); } } if (s) { skipItem = 1; } } } else if (!Q_stricmp(keyname, "gametype")) { //FIXME this is wrong, see g_spawn.c //val = atoi(com_token); //if (val != cgs.gametype) { // skipItem = 1; //} qboolean found = qfalse; skipItem = 1; line = com_token; newLine = qfalse; while (*line && newLine == qfalse) { int ln; //Com_Printf("line1: '%s'\n", line); //FIXME const line = (char *)CG_GetTokenGameType(line, token, qfalse, &newLine); //Com_Printf("newline: %d\n", newLine); //Com_Printf("line2: '%s'\n", line); //Com_Printf(" token: '%s'\n", token); ln = strlen(token); if (ln && *token) { if (token[ln - 1] == ',') { token[ln - 1] = '\0'; } } for (i = 0; i < (sizeof(gametypeNames) / sizeof(char *)); i++) { if (!Q_stricmp(token, gametypeNames[i])) { found = qtrue; break; } } if (!found) { // try alternate quakelive name for 'team' if (!Q_stricmp(token, "tdm")) { if (cgs.gametype == GT_TEAM) { skipItem = 0; } } else if (!Q_stricmp(token, "duel")) { if (cgs.gametype == GT_TOURNAMENT) { skipItem = 0; } } else if (!Q_stricmp(token, "har")) { if (cgs.gametype == GT_HARVESTER) { skipItem = 0; } } else if (!Q_stricmp(token, "1f")) { if (cgs.gametype == GT_1FCTF) { skipItem = 0; } } else if (!Q_stricmp(token, "ob")) { if (cgs.gametype == GT_OBELISK) { skipItem = 0; } } else { Com_Printf("FIXME gametype : '%s' '%s'\n", com_token, token); } } else if (cgs.gametype < ARRAY_LEN(gametypeNames) && !Q_stricmp(token, gametypeNames[cgs.gametype])) { skipItem = 0; } } #if 0 if (!found) { Com_Printf("FIXME gametype : %s\n", com_token); } if (cgs.gametype >= ARRAY_LEN(gametypeNames) || Q_stricmp(com_token, gametypeNames[cgs.gametype])) { skipItem = 1; } #endif } else if (!Q_stricmp(keyname, "notfree")) { val = atoi(com_token); if (val && (cgs.gametype == GT_FFA || cgs.gametype == GT_TOURNAMENT)) { skipItem = 1; } } else if (!Q_stricmp(keyname, "notsingle")) { val = atoi(com_token); if (val && cgs.gametype == GT_SINGLE_PLAYER) { skipItem = 1; } } else if (!Q_stricmp(keyname, "angle")) { angle = atoi(com_token); } else if (!Q_stricmp(keyname, "spawnflags")) { spawnflags = atoi(com_token); } // count } return qtrue; }
static int CG_CalcFov( void ) { static float lastfov = 90; // for transitions back from zoomed in modes float x; float phase; float v; int contents; float fov_x, fov_y; float zoomFov; float f; int inwater; qboolean dead; CG_Zoom(); if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { dead = qtrue; cg.zoomedBinoc = qfalse; cg.zoomTime = 0; cg.zoomval = 0; } else { dead = qfalse; } if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { // if in intermission, use a fixed value fov_x = 90; } else { // user selectable if ( cgs.dmflags & DF_FIXED_FOV ) { // dmflag to prevent wide fov for all clients fov_x = 90; } else { fov_x = cg_fov.value; if ( fov_x < 1 ) { fov_x = 1; } else if ( fov_x > 160 ) { fov_x = 160; } } // account for zooms if ( cg.zoomval ) { zoomFov = cg.zoomval; // (SA) use user scrolled amount if ( zoomFov < 1 ) { zoomFov = 1; } else if ( zoomFov > 160 ) { zoomFov = 160; } } else { zoomFov = lastfov; } // do smooth transitions for the binocs if ( cg.zoomedBinoc ) { // binoc zooming in f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; if ( f > 1.0 ) { fov_x = zoomFov; } else { fov_x = fov_x + f * ( zoomFov - fov_x ); } lastfov = fov_x; } else if ( cg.zoomval ) { // zoomed by sniper/snooper fov_x = cg.zoomval; lastfov = fov_x; } else { // binoc zooming out f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; if ( f <= 1.0 ) { fov_x = zoomFov + f * ( fov_x - zoomFov ); } } } // DHM - Nerve :: zoom in for Limbo or Spectator if ( cgs.gametype == GT_WOLF ) { if ( cg.snap->ps.pm_flags & PMF_FOLLOW && cg.snap->ps.weapon == WP_SNIPERRIFLE ) { fov_x = cg_zoomDefaultSniper.value; } } // dhm - end if ( !dead && ( cg.weaponSelect == WP_SNOOPERSCOPE ) ) { cg.refdef.rdflags |= RDF_SNOOPERVIEW; } else { cg.refdef.rdflags &= ~RDF_SNOOPERVIEW; } if ( cg.snap->ps.persistant[PERS_HWEAPON_USE] ) { fov_x = 55; } x = cg.refdef.width / tan( fov_x / 360 * M_PI ); fov_y = atan2( cg.refdef.height, x ); fov_y = fov_y * 360 / M_PI; // warp if underwater contents = CG_PointContents( cg.refdef.vieworg, -1 ); if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2; v = WAVE_AMPLITUDE * sin( phase ); fov_x += v; fov_y -= v; inwater = qtrue; cg.refdef.rdflags |= RDF_UNDERWATER; } else { cg.refdef.rdflags &= ~RDF_UNDERWATER; inwater = qfalse; } contents = CG_PointContents( cg.refdef.vieworg, -1 ); if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { cg.refdef.rdflags |= RDF_UNDERWATER; } else { cg.refdef.rdflags &= ~RDF_UNDERWATER; } // set it cg.refdef.fov_x = fov_x; cg.refdef.fov_y = fov_y; if ( !cg.zoomedBinoc ) { // NERVE - SMF - fix for zoomed in/out movement bug if ( cg.zoomval ) { if ( cg.snap->ps.weapon == WP_SNOOPERSCOPE ) { cg.zoomSensitivity = 0.3f * ( cg.zoomval / 90.f ); // NERVE - SMF - changed to get less sensitive as you zoom in; } // cg.zoomSensitivity = 0.2; else { cg.zoomSensitivity = 0.6 * ( cg.zoomval / 90.f ); // NERVE - SMF - changed to get less sensitive as you zoom in } // cg.zoomSensitivity = 0.1; } else { cg.zoomSensitivity = 1; } // -NERVE - SMF } else { cg.zoomSensitivity = cg.refdef.fov_y / 75.0; } return inwater; }
//---------------------------- // Draw //---------------------------- void CEmitter::Draw() { // Emitters don't draw themselves, but they may need to add an attached model if ( mFlags & FX_ATTACHED_MODEL ) { mRefEnt.nonNormalizedAxes = qtrue; VectorCopy( mOrigin1, mRefEnt.origin ); // ensure that we are sized for ( int i = 0; i < 3; i++ ) { VectorScale( mRefEnt.axis[i], mRefEnt.radius, mRefEnt.axis[i] ); } theFxHelper.AddFxToScene( &mRefEnt ); } // If we are emitting effects, we had better be careful because just calling it every cgame frame could // either choke up the effects system on a fast machine, or look really nasty on a low end one. if ( mFlags & FX_EMIT_FX ) { vec3_t org, v; float ftime, time2, step; int i, t, dif; #define TRAIL_RATE 8 // we "think" at about a 60hz rate // Pick a target step distance and square it step = mDensity + crandom() * mVariance; step *= step; dif = 0; for ( t = mOldTime; t <= theFxHelper.mTime; t += TRAIL_RATE ) { dif += TRAIL_RATE; // ?Not sure if it's better to update this before or after updating the origin VectorMA( mOldVelocity, dif * 0.001f, mAccel, v ); // Calc the time differences ftime = dif * 0.001f; time2 = ftime * ftime * 0.5f; // Predict the new position for ( i = 0 ; i < 3 ; i++ ) { org[i] = mOldOrigin[i] + ftime * v[i] + time2 * v[i]; } // Only perform physics if this object is tagged to do so if ( (mFlags & FX_APPLY_PHYSICS) ) { bool solid; if ( mFlags & FX_EXPENSIVE_PHYSICS ) { solid = true; // by setting this to true, we force a real trace to happen } else { // if this returns solid, we need to do a trace solid = !!(CG_PointContents( org, ENTITYNUM_WORLD ) & MASK_SHOT); } if ( solid ) { trace_t trace; if ( mFlags & FX_USE_BBOX ) { theFxHelper.Trace( &trace, mOldOrigin, mMin, mMax, org, -1, MASK_SHOT ); } else { theFxHelper.Trace( &trace, mOldOrigin, NULL, NULL, org, -1, MASK_SHOT ); } // Hit something if ( trace.fraction < 1.0f || trace.startsolid || trace.allsolid ) { return; } } } // Is it time to draw an effect? if ( DistanceSquared( org, mOldOrigin ) >= step ) { // Pick a new target step distance and square it step = mDensity + crandom() * mVariance; step *= step; // We met the step criteria so, we should add in the effect theFxScheduler.PlayEffect( mEmitterFxID, org, mRefEnt.axis ); VectorCopy( org, mOldOrigin ); VectorCopy( v, mOldVelocity ); dif = 0; mOldTime = t; } } } drawnFx++; }
/* ========================== CG_MachineGunEjectBrass ========================== */ static void CG_MachineGunEjectBrass( centity_t *cent ) { localEntity_t *le; refEntity_t *re; vec3_t velocity, xvelocity; vec3_t offset, xoffset; float waterScale = 1.0f; vec3_t v[3]; if ( cg_brassTime.integer <= 0 ) { return; } le = CG_AllocLocalEntity(); re = &le->refEntity; velocity[0] = 0; velocity[1] = -50 + 40 * crandom(); velocity[2] = 100 + 50 * crandom(); le->leType = LE_FRAGMENT; le->startTime = cg.time; le->endTime = le->startTime + cg_brassTime.integer + ( cg_brassTime.integer / 4 ) * random(); le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time - (rand()&15); AnglesToAxis( cent->lerpAngles, v ); offset[0] = 8; offset[1] = -4; offset[2] = 24; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( cent->lerpOrigin, xoffset, re->origin ); VectorCopy( re->origin, le->pos.trBase ); if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { waterScale = 0.10f; } xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; VectorScale( xvelocity, waterScale, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->hModel = cgs.media.machinegunBrassModel; le->bounceFactor = 0.4 * waterScale; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angles.trBase[0] = rand()&31; le->angles.trBase[1] = rand()&31; le->angles.trBase[2] = rand()&31; le->angles.trDelta[0] = 2; le->angles.trDelta[1] = 1; le->angles.trDelta[2] = 0; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_BRASS; le->leMarkType = LEMT_NONE; }
static void CG_PlasmaTrail( centity_t *cent, const weaponInfo_t *wi ) { localEntity_t *le; refEntity_t *re; entityState_t *es; vec3_t velocity, xvelocity, origin; vec3_t offset, xoffset; vec3_t v[3]; int t, startTime, step; float waterScale = 1.0f; if ( cg_noProjectileTrail.integer || cg_oldPlasma.integer ) { return; } step = 50; es = ¢->currentState; startTime = cent->trailTime; t = step * ( (startTime + step) / step ); BG_EvaluateTrajectory( &es->pos, cg.time, origin ); le = CG_AllocLocalEntity(); re = &le->refEntity; velocity[0] = 60 - 120 * crandom(); velocity[1] = 40 - 80 * crandom(); velocity[2] = 100 - 200 * crandom(); le->leType = LE_MOVE_SCALE_FADE; le->leFlags = LEF_TUMBLE; le->leBounceSoundType = LEBS_NONE; le->leMarkType = LEMT_NONE; le->startTime = cg.time; le->endTime = le->startTime + 600; le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time; AnglesToAxis( cent->lerpAngles, v ); offset[0] = 2; offset[1] = 2; offset[2] = 2; xoffset[0] = offset[0] * v[0][0] + offset[1] * v[1][0] + offset[2] * v[2][0]; xoffset[1] = offset[0] * v[0][1] + offset[1] * v[1][1] + offset[2] * v[2][1]; xoffset[2] = offset[0] * v[0][2] + offset[1] * v[1][2] + offset[2] * v[2][2]; VectorAdd( origin, xoffset, re->origin ); VectorCopy( re->origin, le->pos.trBase ); if ( CG_PointContents( re->origin, -1 ) & CONTENTS_WATER ) { waterScale = 0.10f; } xvelocity[0] = velocity[0] * v[0][0] + velocity[1] * v[1][0] + velocity[2] * v[2][0]; xvelocity[1] = velocity[0] * v[0][1] + velocity[1] * v[1][1] + velocity[2] * v[2][1]; xvelocity[2] = velocity[0] * v[0][2] + velocity[1] * v[1][2] + velocity[2] * v[2][2]; VectorScale( xvelocity, waterScale, le->pos.trDelta ); AxisCopy( axisDefault, re->axis ); re->shaderTime = cg.time / 1000.0f; re->reType = RT_SPRITE; re->radius = 0.25f; re->customShader = cgs.media.railRingsShader; le->bounceFactor = 0.3f; re->shaderRGBA[0] = wi->flashDlightColor[0] * 63; re->shaderRGBA[1] = wi->flashDlightColor[1] * 63; re->shaderRGBA[2] = wi->flashDlightColor[2] * 63; re->shaderRGBA[3] = 63; le->color[0] = wi->flashDlightColor[0] * 0.2; le->color[1] = wi->flashDlightColor[1] * 0.2; le->color[2] = wi->flashDlightColor[2] * 0.2; le->color[3] = 0.25f; le->angles.trType = TR_LINEAR; le->angles.trTime = cg.time; le->angles.trBase[0] = rand()&31; le->angles.trBase[1] = rand()&31; le->angles.trBase[2] = rand()&31; le->angles.trDelta[0] = 1; le->angles.trDelta[1] = 0.5; le->angles.trDelta[2] = 0; }
/* * CG_GS_PointContents */ static int CG_GS_PointContents( vec3_t point, int timeDelta ) { assert( !timeDelta ); return CG_PointContents( point ); }
static int CG_CalcFov( void ) { float x; float phase; float v; int contents; float fov_x, fov_y; float zoomFov; float f; if ( cg.cur_lc->predictedPlayerState.pm_type == PM_INTERMISSION ) { // if in intermission, use a fixed value cg.fov = fov_x = 90; } else { // user selectable if ( cgs.dmflags & DF_FIXED_FOV ) { // dmflag to prevent wide fov for all clients fov_x = 90; } else { fov_x = cg_fov.value; if ( fov_x < 1 ) { fov_x = 1; } else if ( fov_x > 160 ) { fov_x = 160; } } cg.fov = fov_x; // account for zooms zoomFov = cg_zoomFov.value; if ( zoomFov < 1 ) { zoomFov = 1; } else if ( zoomFov > 160 ) { zoomFov = 160; } if ( cg.cur_lc->zoomed ) { f = ( cg.time - cg.cur_lc->zoomTime ) / (float)ZOOM_TIME; if ( f > 1.0 ) { fov_x = zoomFov; } else { fov_x = fov_x + f * ( zoomFov - fov_x ); } } else { f = ( cg.time - cg.cur_lc->zoomTime ) / (float)ZOOM_TIME; if ( f <= 1.0 ) { fov_x = zoomFov + f * ( fov_x - zoomFov ); } } } // Do FOV Correction for some viewports if ((cg.numViewports == 2) || (cg.numViewports == 3 && cg.viewport == 2)) { if (cg_splitviewVertical.integer == 1) { // Tall/narrow view fov_x *= 0.6f; // 0.5 would be correct, but fov gets real small. } else { // Short/wide view fov_x *= 1.4f; // 1.5 would be correct, but fov gets real big. } } x = cg.refdef.width / tan( fov_x / 360 * M_PI ); fov_y = atan2( cg.refdef.height, x ); fov_y = fov_y * 360 / M_PI; // warp if underwater contents = CG_PointContents( cg.refdef.vieworg, -1 ); if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){ phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2; v = WAVE_AMPLITUDE * sin( phase ); fov_x += v; fov_y -= v; cg.refdef.rdflags |= RDF_UNDERWATER; } else { cg.refdef.rdflags &= ~RDF_UNDERWATER; } // set it cg.refdef.fov_x = fov_x; cg.refdef.fov_y = fov_y; if ( !cg.cur_lc->zoomed ) { cg.cur_lc->zoomSensitivity = 1; } else { cg.cur_lc->zoomSensitivity = cg.refdef.fov_y / 75.0; } return (cg.refdef.rdflags & RDF_UNDERWATER); }
/* ================ CG_AddFragment ================ */ void CG_AddFragment( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; if (le->forceAlpha) { le->refEntity.renderfx |= RF_FORCE_ENT_ALPHA; le->refEntity.shaderRGBA[3] = le->forceAlpha; } if ( le->pos.trType == TR_STATIONARY ) { // sink into the ground if near the removal time int t; float t_e; t = le->endTime - cg.time; if ( t < (SINK_TIME*2) ) { le->refEntity.renderfx |= RF_FORCE_ENT_ALPHA; t_e = (float)((float)(le->endTime - cg.time)/(SINK_TIME*2)); t_e = (int)((t_e)*255); if (t_e > 255) { t_e = 255; } if (t_e < 1) { t_e = 1; } if (le->refEntity.shaderRGBA[3] && t_e > le->refEntity.shaderRGBA[3]) { t_e = le->refEntity.shaderRGBA[3]; } le->refEntity.shaderRGBA[3] = t_e; trap->R_AddRefEntityToScene( &le->refEntity ); } 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 ); ScaleModelAxis(&le->refEntity); } 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; } if (!trace.startsolid) { // leave a mark CG_FragmentBounceMark( le, &trace ); // do a bouncy sound CG_FragmentBounceSound( le, &trace ); if (le->bounceSound) { //specified bounce sound (debris) trap->S_StartSound(le->pos.trBase, ENTITYNUM_WORLD, CHAN_AUTO, le->bounceSound); } // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); trap->R_AddRefEntityToScene( &le->refEntity ); } }
static int CG_CalcFov( void ) { float x; float phase; float v; int contents; float fov_x, fov_y; float f; int inwater; float cgFov = cg_fov.value; if (cgFov < 1) { cgFov = 1; } if (cgFov > 97) { cgFov = 97; } if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) { // if in intermission, use a fixed value fov_x = 80;//90; } else { // user selectable if ( cgs.dmflags & DF_FIXED_FOV ) { // dmflag to prevent wide fov for all clients fov_x = 80;//90; } else { fov_x = cgFov; if ( fov_x < 1 ) { fov_x = 1; } else if ( fov_x > 160 ) { fov_x = 160; } } if (cg.predictedPlayerState.zoomMode == 2) { //binoculars if (zoomFov > 40.0f) { zoomFov -= cg.frametime * 0.075f; if (zoomFov < 40.0f) { zoomFov = 40.0f; } else if (zoomFov > cgFov) { zoomFov = cgFov; } } fov_x = zoomFov; } else if (cg.predictedPlayerState.zoomMode) { if (!cg.predictedPlayerState.zoomLocked) { if (zoomFov > 50) { //Now starting out at nearly half zoomed in zoomFov = 50; } zoomFov -= cg.frametime * 0.035f;//0.075f; if (zoomFov < MAX_ZOOM_FOV) { zoomFov = MAX_ZOOM_FOV; } else if (zoomFov > cgFov) { zoomFov = cgFov; } else { // Still zooming static zoomSoundTime = 0; if (zoomSoundTime < cg.time || zoomSoundTime > cg.time + 10000) { trap_S_StartSound(cg.refdef.vieworg, ENTITYNUM_WORLD, CHAN_LOCAL, cgs.media.disruptorZoomLoop); zoomSoundTime = cg.time + 300; } } } fov_x = zoomFov; } else { zoomFov = 80; f = ( cg.time - cg.predictedPlayerState.zoomTime ) / ZOOM_OUT_TIME; if ( f > 1.0 ) { fov_x = fov_x; } else { fov_x = cg.predictedPlayerState.zoomFov + f * ( fov_x - cg.predictedPlayerState.zoomFov ); } } } x = cg.refdef.width / tan( fov_x / 360 * M_PI ); fov_y = atan2( cg.refdef.height, x ); fov_y = fov_y * 360 / M_PI; // warp if underwater contents = CG_PointContents( cg.refdef.vieworg, -1 ); if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){ phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2; v = WAVE_AMPLITUDE * sin( phase ); fov_x += v; fov_y -= v; inwater = qtrue; } else { inwater = qfalse; } // set it cg.refdef.fov_x = fov_x; cg.refdef.fov_y = fov_y; if (cg.predictedPlayerState.zoomMode) { cg.zoomSensitivity = zoomFov/cgFov; } else if ( !cg.zoomed ) { cg.zoomSensitivity = 1; } else { cg.zoomSensitivity = cg.refdef.fov_y / 75.0; } return inwater; }
static int CG_CalcFov( void ) { float y; float phase; float v; int contents; float fov_x, fov_y; float zoomFov; float f; int inwater; int attribFov; usercmd_t cmd; usercmd_t oldcmd; int cmdNum; cmdNum = trap_GetCurrentCmdNumber( ); trap_GetUserCmd( cmdNum, &cmd ); trap_GetUserCmd( cmdNum - 1, &oldcmd ); // switch follow modes if necessary: cycle between free -> follow -> third-person follow if( cmd.buttons & BUTTON_USE_HOLDABLE && !( oldcmd.buttons & BUTTON_USE_HOLDABLE ) ) { if ( cg.snap->ps.pm_flags & PMF_FOLLOW ) { if( !cg.chaseFollow ) cg.chaseFollow = qtrue; else { cg.chaseFollow = qfalse; trap_SendClientCommand( "follow\n" ); } } else if ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ) trap_SendClientCommand( "follow\n" ); } if( cg.predictedPlayerState.pm_type == PM_INTERMISSION || ( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT ) || ( cg.renderingThirdPerson ) ) { // if in intermission or third person, use a fixed value fov_y = BASE_FOV_Y; } else { // don't lock the fov globally - we need to be able to change it attribFov = BG_Class( cg.predictedPlayerState.stats[ STAT_CLASS ] )->fov * 0.75f; fov_y = attribFov; if ( fov_y < 1.0f ) fov_y = 1.0f; else if ( fov_y > MAX_FOV_Y ) fov_y = MAX_FOV_Y; if( cg.spawnTime > ( cg.time - FOVWARPTIME ) && BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_CLASS ], SCA_FOVWARPS ) ) { float fraction = (float)( cg.time - cg.spawnTime ) / FOVWARPTIME; fov_y = MAX_FOV_WARP_Y - ( ( MAX_FOV_WARP_Y - fov_y ) * fraction ); } // account for zooms zoomFov = BG_Weapon( cg.predictedPlayerState.weapon )->zoomFov * 0.75f; if ( zoomFov < 1.0f ) zoomFov = 1.0f; else if ( zoomFov > attribFov ) zoomFov = attribFov; // only do all the zoom stuff if the client CAN zoom // FIXME: zoom control is currently hard coded to BUTTON_ATTACK2 if( BG_Weapon( cg.predictedPlayerState.weapon )->canZoom ) { if ( cg.zoomed ) { f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; if ( f > 1.0f ) fov_y = zoomFov; else fov_y = fov_y + f * ( zoomFov - fov_y ); // BUTTON_ATTACK2 isn't held so unzoom next time if( !( cmd.buttons & BUTTON_ATTACK2 ) ) { cg.zoomed = qfalse; cg.zoomTime = MIN( cg.time, cg.time + cg.time - cg.zoomTime - ZOOM_TIME ); } } else { f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME; if ( f <= 1.0f ) fov_y = zoomFov + f * ( fov_y - zoomFov ); // BUTTON_ATTACK2 is held so zoom next time if( cmd.buttons & BUTTON_ATTACK2 ) { cg.zoomed = qtrue; cg.zoomTime = MIN( cg.time, cg.time + cg.time - cg.zoomTime - ZOOM_TIME ); } } } } y = cg.refdef.height / tan( 0.5f * DEG2RAD( fov_y ) ); fov_x = atan2( cg.refdef.width, y ); fov_x = 2.0f * RAD2DEG( fov_x ); // warp if underwater contents = CG_PointContents( cg.refdef.vieworg, -1 ); if( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ) { phase = cg.time / 1000.0f * WAVE_FREQUENCY * M_PI * 2.0f; v = WAVE_AMPLITUDE * sin( phase ); fov_x += v; fov_y -= v; inwater = qtrue; } else inwater = qfalse; if( ( cg.predictedPlayerEntity.currentState.eFlags & EF_POISONCLOUDED ) && ( cg.time - cg.poisonedTime < PCLOUD_DISORIENT_DURATION) && cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 && !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) { float scale = 1.0f - (float)( cg.time - cg.poisonedTime ) / BG_PlayerPoisonCloudTime( &cg.predictedPlayerState ); phase = ( cg.time - cg.poisonedTime ) / 1000.0f * PCLOUD_ZOOM_FREQUENCY * M_PI * 2.0f; v = PCLOUD_ZOOM_AMPLITUDE * sin( phase ) * scale; fov_x += v; fov_y += v; } // set it cg.refdef.fov_x = fov_x; cg.refdef.fov_y = fov_y; if( !cg.zoomed ) cg.zoomSensitivity = 1.0f; else cg.zoomSensitivity = cg.refdef.fov_y / 75.0f; return inwater; }
//---------------------------- // Update Origin //---------------------------- bool CParticle::UpdateOrigin() { vec3_t new_origin; // float ftime, time2; UpdateVelocity(); // Calc the time differences // ftime = theFxHelper.mFrameTime * 0.001f; //time2 = ftime * ftime * 0.5f; // time2=0; // Predict the new position new_origin[0] = mOrigin1[0] + theFxHelper.mFloatFrameTime * mVel[0];// + time2 * mVel[0]; new_origin[1] = mOrigin1[1] + theFxHelper.mFloatFrameTime * mVel[1];// + time2 * mVel[1]; new_origin[2] = mOrigin1[2] + theFxHelper.mFloatFrameTime * mVel[2];// + time2 * mVel[2]; // Only perform physics if this object is tagged to do so if ( (mFlags & FX_APPLY_PHYSICS) ) { bool solid; if ( mFlags & FX_EXPENSIVE_PHYSICS ) { solid = true; // by setting this to true, we force a real trace to happen } else { // if this returns solid, we need to do a trace solid = !!(CG_PointContents( new_origin, ENTITYNUM_WORLD ) & ( MASK_SHOT | CONTENTS_WATER )); } if ( solid ) { trace_t trace; float dot; if ( mFlags & FX_USE_BBOX ) { theFxHelper.Trace( &trace, mOrigin1, mMin, mMax, new_origin, -1, ( MASK_SHOT | CONTENTS_WATER ) ); } else { theFxHelper.Trace( &trace, mOrigin1, NULL, NULL, new_origin, -1, ( MASK_SHOT | CONTENTS_WATER ) ); } // Hit something if ( trace.fraction < 1.0f )//|| trace.startsolid || trace.allsolid ) { if ( mFlags & FX_IMPACT_RUNS_FX && !(trace.surfaceFlags & SURF_NOIMPACT )) { theFxScheduler.PlayEffect( mImpactFxID, trace.endpos, trace.plane.normal ); } if ( mFlags & FX_KILL_ON_IMPACT ) { // time to die return false; } VectorMA( mVel, theFxHelper.mFloatFrameTime * trace.fraction, mAccel, mVel ); dot = DotProduct( mVel, trace.plane.normal ); VectorMA( mVel, -2 * dot, trace.plane.normal, mVel ); VectorScale( mVel, mElasticity, mVel ); // If the velocity is too low, make it stop moving, rotating, and turn off physics to avoid // doing expensive operations when they aren't needed if ( trace.plane.normal[2] > 0 && mVel[2] < 4 ) { VectorClear( mVel ); VectorClear( mAccel ); mFlags &= ~(FX_APPLY_PHYSICS|FX_IMPACT_RUNS_FX); } // Set the origin to the exact impact point VectorCopy( trace.endpos, mOrigin1 ); return true; } } } // No physics were done to this object, move it VectorCopy( new_origin, mOrigin1 ); return true; }