/* ================ CG_AddFuseSparkElements ================ */ void CG_AddFuseSparkElements( localEntity_t *le ) { float FUSE_SPARK_WIDTH = 1.0; int step = 10; float time; float lifeFrac; static vec3_t whiteColor = {1,1,1}; time = (float)(le->lastTrailTime); while (time < cg.time) { // calculate new position BG_EvaluateTrajectory( &le->pos, time, le->refEntity.origin ); lifeFrac = (float)(time - le->startTime) / (float)(le->endTime - le->startTime); //if (lifeFrac > 0.2) { // add a trail le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex, cgs.media.sparkParticleShader, time, STYPE_STRETCH, le->refEntity.origin, (int)(lifeFrac*(float)(le->endTime - le->startTime)/2.0), 1.0/*(1.0 - lifeFrac)*/, 0.0, FUSE_SPARK_WIDTH*(1.0 - lifeFrac), FUSE_SPARK_WIDTH*(1.0 - lifeFrac), TJFL_SPARKHEADFLARE, whiteColor, whiteColor, 0, 0 ); //} time += step; le->lastTrailTime = time; } }
/* ================ CG_AddClientCritter ================ */ void CG_AddClientCritter( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; int time, step = 25, i; vec3_t v, ang, v2, oDelta; localEntity_t backup; float oldSpeed, enemyDist, of; vec3_t enemyPos; float alpha; if (cg_entities[le->ownerNum].currentState.otherEntityNum2 == cg.snap->ps.clientNum) { VectorCopy( cg.snap->ps.origin, enemyPos ); enemyPos[2] += cg.snap->ps.viewheight; } else { VectorCopy( cg_entities[le->ownerNum].currentState.origin2, enemyPos ); } VectorCopy( le->pos.trDelta, oDelta ); // vary the enemyPos to create a psuedo-randomness of = (float)cg.time + le->startTime; enemyPos[0] += 12 * (sin(of/100) * cos(of/78)); enemyPos[1] += 12 * (sin(of/70) * cos(of/82)); enemyPos[2] += 12 * (sin(of/67) * cos(of/98)); time = le->lastTrailTime+step; while (time <= cg.time) { if (time > le->refEntity.fadeStartTime) { alpha = (float)(time - le->refEntity.fadeStartTime)/(float)(le->refEntity.fadeEndTime - le->refEntity.fadeStartTime); if (alpha < 0) alpha = 0; else if (alpha > 1) alpha = 1; } else { alpha = 1.0; } // calculate new position BG_EvaluateTrajectory( &le->pos, time, newOrigin ); VectorSubtract( enemyPos, le->refEntity.origin, v ); enemyDist = VectorNormalize( v ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, le->ownerNum, MASK_SHOT ); // if stuck, kill it if (trace.startsolid || (trace.fraction < 1.0)) { // kill it CG_FreeLocalEntity( le ); return; } // moved some distance VectorCopy( trace.endpos, le->refEntity.origin ); if (le->leType == LE_ZOMBIE_SPIRIT) { le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex, cgs.media.zombieSpiritTrailShader, time, STYPE_STRETCH, le->refEntity.origin, (int)le->effectWidth, // trail life 0.3 * alpha, 0.0, le->radius, 0, 0,//TJFL_FIXDISTORT, colorWhite, colorWhite, 1.0, 1 ); } // tracking factor if (le->leType == LE_ZOMBIE_BAT) le->bounceFactor = 3.0*(float)step/1000.0; else le->bounceFactor = 5.0*(float)step/1000.0; oldSpeed = VectorLength( le->pos.trDelta ); // track the enemy backup = *le; VectorSubtract( enemyPos, le->refEntity.origin, v ); enemyDist = VectorNormalize( v ); if (alpha > 0.5 && (le->lastSpiritDmgTime < time - 100) && enemyDist < 24) { // inflict the damage! CG_ClientDamage( cg_entities[le->ownerNum].currentState.otherEntityNum2, le->ownerNum, CLDMG_SPIRIT ); le->lastSpiritDmgTime = time; } VectorMA( le->pos.trDelta, le->bounceFactor*oldSpeed, v, le->pos.trDelta ); //VectorCopy( v, le->pos.trDelta ); if (VectorLength(le->pos.trDelta) < 1) { CG_FreeLocalEntity( le ); return; } le->bounceFactor = 5.0*(float)step/1000.0; // avoidance factor // the intersection is a fraction of the frametime le->pos.trTime = time; VectorCopy( le->refEntity.origin, le->pos.trBase ); VectorNormalize( le->pos.trDelta ); VectorScale( le->pos.trDelta, oldSpeed, le->pos.trDelta ); // now trace ahead of time, if we're going to hit something, then avoid it // only avoid dangers if we don't have direct sight to the enemy trap_CM_BoxTrace( &trace, le->refEntity.origin, enemyPos, NULL, NULL, 0, MASK_SOLID ); if (trace.fraction < 1.0) { BG_EvaluateTrajectory( &le->pos, time+1000, newOrigin ); // if we would go passed the enemy, don't bother if (VectorDistance( le->refEntity.origin, enemyPos) > VectorDistance( le->refEntity.origin, newOrigin )) { trap_CM_BoxTrace( &trace, le->refEntity.origin, newOrigin, NULL, NULL, 0, MASK_SOLID ); if (trace.fraction < 1.0) { // make sure we are not heading away from the enemy too much VectorNormalize2( le->pos.trDelta, v2 ); if (DotProduct( v, v2 ) > 0.7) { // avoid world geometry backup = *le; le->bounceFactor = (1.0 - trace.fraction)*10.0*(float)step/1000.0; // tracking and avoidance factor // reflect the velocity on the trace plane VectorMA( le->pos.trDelta, le->bounceFactor*oldSpeed, trace.plane.normal, le->pos.trDelta ); if (VectorLength(le->pos.trDelta) < 1) { CG_FreeLocalEntity( le ); return; } // the intersection is a fraction of the frametime le->pos.trTime = time; VectorCopy( le->refEntity.origin, le->pos.trBase ); VectorNormalize( le->pos.trDelta ); VectorScale( le->pos.trDelta, oldSpeed, le->pos.trDelta ); // // double check end velocity VectorNormalize2( le->pos.trDelta, v2 ); if (DotProduct( v, v2 ) <= 0.2) { // restore *le = backup; } } } } } // set the angles VectorNormalize2( le->pos.trDelta, v ); // HACK!!! skull model is back-to-front, need to fix if (le->leType == LE_ZOMBIE_SPIRIT) VectorInverse( v ); vectoangles( v, ang ); AnglesToAxis( ang, le->refEntity.axis ); // lean when turning if (le->leType == LE_ZOMBIE_BAT) { VectorSubtract( le->pos.trDelta, oDelta, v2 ); ang[ROLL] = -5.0 * DotProduct( le->refEntity.axis[1], v2 ); if (fabs(ang[ROLL]) > 80) { if (ang[ROLL] > 80) ang[ROLL] = 80; else ang[ROLL] = -80; } } AnglesToAxis( ang, le->refEntity.axis ); // HACK: the skull is slightly higher than the origin if (le->leType == LE_ZOMBIE_SPIRIT) { // set the size scale for (i=0; i<3; i++) VectorScale( le->refEntity.axis[i], 0.35, le->refEntity.axis[i] ); VectorMA( le->refEntity.origin, -10, le->refEntity.axis[2], le->refEntity.origin ); } le->lastTrailTime = time; time += step; } // Bats, set the frame if (le->leType == LE_ZOMBIE_BAT) { #define BAT_ANIM_FRAMETIME 30 le->refEntity.frame = (cg.time/BAT_ANIM_FRAMETIME+1)%19; le->refEntity.oldframe = (cg.time/BAT_ANIM_FRAMETIME)%19; le->refEntity.backlerp = 1.0 - ((float)(cg.time%BAT_ANIM_FRAMETIME)/(float)BAT_ANIM_FRAMETIME); } // add the sound if (le->loopingSound) { if (cg.time > le->refEntity.fadeStartTime) trap_S_AddLoopingSound( 0, le->refEntity.origin, vec3_origin, le->loopingSound, 255 - (int)(255.0 * (float)(cg.time - le->refEntity.fadeStartTime) / (float)(le->refEntity.fadeEndTime - le->refEntity.fadeStartTime)) ); else if (le->startTime + 1000 > cg.time) trap_S_AddLoopingSound( 0, le->refEntity.origin, vec3_origin, le->loopingSound, (int)(255.0 * (float)(cg.time - le->startTime) / 1000.0) ); else trap_S_AddLoopingSound( 0, le->refEntity.origin, vec3_origin, le->loopingSound, 255); } trap_R_AddRefEntityToScene( &le->refEntity ); /* // HACK: the skull is slightly higher than the origin if (le->leType == LE_ZOMBIE_SPIRIT) { // set the size scale for (i=0; i<3; i++) VectorScale( le->refEntity.axis[i], 1.0/0.35, le->refEntity.axis[i] ); VectorMA( le->refEntity.origin, 10, le->refEntity.axis[2], le->refEntity.origin ); } */ // Bats, add the flame if (le->leType == LE_ZOMBIE_BAT) { // float lightSize, alpha; // le->refEntity.shaderRGBA[3] = 255; VectorNormalize2( le->pos.trDelta, v ); VectorInverse( v ); v[2] += 1; VectorNormalize2( v, le->refEntity.fireRiseDir ); le->refEntity.customShader = cgs.media.onFireShader2; trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.shaderTime = 1434; trap_R_AddRefEntityToScene( &le->refEntity ); // le->refEntity.customShader = cgs.media.onFireShader; // le->refEntity.shaderTime = 0; // trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.customShader = 0; le->refEntity.shaderTime = 0; /* // drop a dlight lightSize = 1.0 + 0.2*(sin(1.0*cg.time/50.0) * cos(1.0*cg.time/43.0)); alpha = 0.2 * (lightSize / 1.2); trap_R_AddLightToScene( le->refEntity.origin, 150.0 + 80.0*lightSize, 1.000000*alpha, 0.603922*alpha, 0.207843*alpha, 0 ); // add some sound trap_S_AddLoopingSound( -1, le->refEntity.origin, vec3_origin, cgs.media.flameSound, 100 ); trap_S_AddLoopingSound( -1, le->refEntity.origin, vec3_origin, cgs.media.flameBlowSound, 100 ); */ } }
/* =============== CG_DynamicLightningBolt =============== */ void CG_DynamicLightningBolt( qhandle_t shader, vec3_t start, vec3_t pend, int numBolts, float maxWidth, qboolean fade, float startAlpha, int recursion, int randseed ) { int i,j; float segMin, segMax, length; float thisSeg, distLeft, thisWidth; vec3_t pos, vec, end; int curJunc; vec3_t c, bc = {0.8,0.9,1}; const float rndSize = 12.0; const float maxSTscale = 30.0; float stScale; float alpha, viewDist; const int trailLife = 1; int forks = 0; #define STYPE_LIGHTNING STYPE_REPEAT // ST mapping for trail #define FORK_CHANCE 0.5 #define VIEW_SCALE_DIST 128 VectorCopy( pend, end ); // need this so recursive calls don't override stacked endpoints // HACK, updated sprite, so downscale all widths maxWidth *= 0.6; length = Distance( start, end ); //if (length > 128) { segMin = length / 10.0; if ( segMin < 8 ) { segMin = 8; } segMax = segMin * 1.2; //segMax = length / 30.0; //} else { // segMin = length / 3.0; // segMax = length / 1.5; //} if ( startAlpha > 1.0 ) { startAlpha = 1.0; } alpha = startAlpha; // change only if fading for ( i = 0; i < numBolts; i++ ) { distLeft = length; VectorCopy( start, pos ); // drop a start junction stScale = maxSTscale * ( 0.5 + lt_random( randseed,i + 1 ) * 0.5 ); if ( fade ) { if ( startAlpha == 1.0 ) { alpha = startAlpha * ( distLeft / length ); } else { alpha = 1.0 - 1.0 * fabs( ( 1.0 - ( distLeft / length ) ) - startAlpha ); if ( alpha < 0 ) { alpha = 0; } if ( alpha > 1 ) { alpha = 1; } } } thisWidth = maxWidth * ( 0.5 + 0.5 * alpha ); if ( ( viewDist = VectorDistance( pos, cg.refdef.vieworg ) ) < VIEW_SCALE_DIST ) { thisWidth *= 0.5 + ( 0.5 * ( viewDist / VIEW_SCALE_DIST ) ); if ( thisWidth < 4.0 && thisWidth < maxWidth ) { thisWidth = 4.0; } } else { // scale it wider with distance so it remains visible thisWidth *= 0.5 + ( 0.5 * ( viewDist / VIEW_SCALE_DIST ) ); if ( thisWidth > maxWidth * 2 ) { // thisWidth > maxWidth*2; thisWidth = maxWidth * 2; } } // VectorScale( bc, alpha, c ); c[2] *= 1.0 + ( 1.0 - alpha ); if ( c[2] > 1.0 ) { c[2] = 1.0; } // curJunc = CG_AddTrailJunc( 0, shader, cg.time, STYPE_LIGHTNING, pos, trailLife, 1, 1, thisWidth, thisWidth, TJFL_NOCULL, c, c, stScale, 20.0 ); while ( distLeft > 0 ) { // create this bolt thisSeg = segMin + ( segMax - segMin ) * lt_random( randseed,2 ); thisWidth = maxWidth * ( 0.5 + 0.5 * alpha ); if ( thisSeg >= distLeft - rndSize ) { // go directly to the end point VectorCopy( end, pos ); thisWidth *= 0.6; } else if ( (float)distLeft / length < 0.3 ) { VectorSubtract( end, pos, vec ); VectorNormalize( vec ); VectorMA( pos, thisSeg, vec, pos ); // randomize the position a bit thisSeg *= 0.05; if ( thisSeg > rndSize ) { thisSeg = rndSize; } for ( j = 0; j < 3; j++ ) { viewDist = lt_crandom( randseed * randseed,j * j + i * i + 3 ); if ( fabs( viewDist ) < 0.5 ) { if ( viewDist > 0 ) { viewDist = 0.5; } else { viewDist = -0.5;} } pos[j] += viewDist * thisSeg; } } else { VectorSubtract( end, pos, vec ); VectorNormalize( vec ); VectorMA( pos, thisSeg, vec, pos ); // randomize the position a bit thisSeg *= 0.25; if ( thisSeg > rndSize ) { thisSeg = rndSize; } for ( j = 0; j < 3; j++ ) { viewDist = lt_crandom( randseed,j * j + i * i + 3 ); if ( fabs( viewDist ) < 0.5 ) { if ( viewDist > 0 ) { viewDist = 0.5; } else { viewDist = -0.5;} } pos[j] += viewDist * thisSeg; } } distLeft = Distance( pos, end ); if ( fade ) { if ( startAlpha == 1.0 ) { alpha = startAlpha * ( distLeft / length ); } else { alpha = 1.0 - 1.0 * fabs( ( 1.0 - ( distLeft / length ) ) - startAlpha ); if ( alpha < 0 ) { alpha = 0; } if ( alpha > 1 ) { alpha = 1; } } } //thisWidth *= alpha; if ( ( viewDist = VectorDistance( pos, cg.refdef.vieworg ) ) < VIEW_SCALE_DIST ) { thisWidth *= 0.5 + ( 0.5 * ( viewDist / VIEW_SCALE_DIST ) ); if ( thisWidth < 4.0 && thisWidth < maxWidth ) { thisWidth = 4.0; } } else { // scale it wider with distance so it remains visible thisWidth *= 0.5 + ( 0.5 * ( viewDist / VIEW_SCALE_DIST ) ); if ( thisWidth > maxWidth * 2 ) { // thisWidth > maxWidth*2; thisWidth = maxWidth * 2; } } // VectorScale( bc, alpha, c ); c[2] *= 1.0 + ( 1.0 - alpha ); if ( c[2] > 1.0 ) { c[2] = 1.0; } // //stScale = maxSTscale * (0.4 + lt_random(randseed,1)*0.6); curJunc = CG_AddTrailJunc( curJunc, shader, cg.time, STYPE_LIGHTNING, pos, trailLife, 1, 1, thisWidth, thisWidth, TJFL_NOCULL, c, c, stScale, 20.0 ); // fork from here? if ( thisWidth < 4 && distLeft > 10 && recursion < 3 && forks < 3 && lt_random( randseed,383 + i + forks ) < FORK_CHANCE ) { vec3_t fend; forks++; VectorSet( fend, distLeft * 0.3 * lt_crandom( randseed,56 + i + forks ), distLeft * 0.3 * lt_crandom( randseed,160 + i + forks ), distLeft * 0.3 * lt_crandom( randseed,190 + i + forks ) ); VectorAdd( fend, end, fend ); VectorSubtract( fend, pos, fend ); VectorMA( pos, 0.2 + 0.7 * lt_random( randseed,6 + i + forks ), fend, fend ); //if (recursion > 0 && recursion < 2) { // CG_DynamicLightningBolt( cgs.media.lightningBoltShader, pos, fend, 1, maxWidth, qtrue, alpha, recursion, randseed ); // return; // divert bolt rather than split //} else { CG_DynamicLightningBolt( shader, pos, fend, 1, maxWidth, qtrue, alpha, recursion + 1, randseed + 765 ); //} } randseed++; } } }
// use this to change between particle and trail code //#define BLOOD_PARTICLE_TRAIL void CG_BloodTrail( localEntity_t *le ) { int t; int t2; int step; vec3_t newOrigin; #ifndef BLOOD_PARTICLE_TRAIL static vec3_t col = {1,1,1}; #endif centity_t *cent; cent = &cg_entities[le->ownerNum]; if ( !cg_blood.integer ) { return; } // step = 150; #ifdef BLOOD_PARTICLE_TRAIL step = 10; #else // time it takes to move 3 units step = (1000*3)/VectorLength(le->pos.trDelta); #endif if (cent && cent->currentState.aiChar == AICHAR_ZOMBIE) step = 30; t = step * ( (cg.time - cg.frametime + step ) / step ); t2 = step * ( cg.time / step ); for ( ; t <= t2; t += step ) { BG_EvaluateTrajectory( &le->pos, t, newOrigin ); #ifdef BLOOD_PARTICLE_TRAIL CG_Particle_Bleed (cgs.media.smokePuffShader, newOrigin, vec3_origin, 0, 500+rand()%200); #else if (cent && cent->currentState.aiChar == AICHAR_ZOMBIE) { CG_Particle_Bleed (cgs.media.smokePuffShader, newOrigin, vec3_origin, 1, 500+rand()%200); } else // Ridah, blood trail using trail code (should be faster since we don't have to spawn as many) le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex, cgs.media.bloodTrailShader, t, STYPE_STRETCH, newOrigin, 180, 1.0, // start alpha 0.0, // end alpha 12.0, 12.0, TJFL_NOCULL, col, col, 0, 0 ); #endif } }
// use this to change between particle and trail code //#define BLOOD_PARTICLE_TRAIL void CG_BloodTrail( localEntity_t *le ) { int t; int t2; int step; vec3_t newOrigin; float vl; //bani #ifndef BLOOD_PARTICLE_TRAIL static vec3_t col = { 1, 1, 1 }; #endif centity_t *cent; cent = &cg_entities[ le->ownerNum ]; if ( !cg_blood.integer ) { return; } // step = 150; #ifdef BLOOD_PARTICLE_TRAIL step = 10; #else // time it takes to move 3 units vl = VectorLength( le->pos.trDelta ); // rain - need to check FLT_EPSILON because floating-point math sucks <3 if ( vl < FLT_EPSILON ) { return; } step = ( 1000 * 3 ) / vl; //bani - avoid another div by 0 //zinx - check against <= 0 instead of == 0, because it can still wrap; (3000 / (FLT_EPSILON*11.7f)) < 0 if ( step <= 0 ) { return; } #endif t = step * ( ( cg.time - cg.frametime + step ) / step ); t2 = step * ( cg.time / step ); for ( ; t <= t2; t += step ) { BG_EvaluateTrajectory( &le->pos, t, newOrigin, qfalse, -1 ); #ifdef BLOOD_PARTICLE_TRAIL CG_Particle_Bleed( cgs.media.smokePuffShader, newOrigin, vec3_origin, 0, 500 + rand() % 200 ); #else // Ridah, blood trail using trail code (should be faster since we don't have to spawn as many) le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex, le, // rain - zinx's trail fix cgs.media.bloodTrailShader, t, STYPE_STRETCH, newOrigin, 180, 1.0, // start alpha 0.0, // end alpha 12.0, 12.0, TJFL_NOCULL, col, col, 0, 0 ); #endif } }