void FX_RegenBeam( vec3_t origin, vec3_t dir, int clientNum, qboolean alt_fire ) { trace_t tr; vec3_t end; VectorMA( origin, REGEN_BEAM_LENGTH, dir, end ); CG_Trace( &tr, origin, NULL, NULL, end, clientNum, CONTENTS_SOLID ); trap_R_AddLightToScene( origin, 30, 235.0f / 255, 74.0f / 255, 102.0f / 255 ); if ( tr.fraction != 1.0f ) { float radius; if ( alt_fire ) radius = flrandom(1.5f, 3.0f) * (1.0 - (tr.fraction*0.3)); else radius = flrandom(0.5f, 1.5f) * (1.0 - (tr.fraction*0.3)); if ( !radius ) return; CG_ImpactMark( cgs.media.regenDecal, tr.endpos, tr.plane.normal, 0, 1, 1, 1, 0.2*(1.0-tr.fraction), qfalse, radius, qtrue ); trap_R_AddLightToScene( origin, radius*5, 235.0f / 255, 74.0f / 255, 102.0f / 255 ); } }
//TiM - Beam FX for the Neutrino Probe weapon void FX_ProbeBeam( vec3_t origin, vec3_t dir, int clientNum, qboolean alt_fire ) { trace_t tr; refEntity_t beam; vec3_t end; float scale; memset( &beam, 0, sizeof( beam ) ); if ( alt_fire ) scale = flrandom(7.0f, 12.0f); else scale = Q_fabs( 12.0f * sin( cg.time * 0.1f ) ); VectorMA( origin, PROBE_BEAM_LENGTH, dir, end ); CG_Trace( &tr, origin, NULL, NULL, end, clientNum, CONTENTS_SOLID ); trap_R_AddLightToScene( origin, 20, 114.0f / 255, 164.0f / 255, 1.0f ); VectorCopy( origin, beam.origin); VectorCopy( tr.endpos, beam.oldorigin ); beam.reType = RT_LINE; beam.customShader = cgs.media.probeBeam; beam.shaderRGBA[0] = 0xff; beam.shaderRGBA[1] = 0xff; beam.shaderRGBA[2] = 0xff; beam.shaderRGBA[3] = 0xff; AxisClear( beam.axis ); beam.data.line.width = scale*0.1; beam.data.line.width2 = scale; beam.data.line.stscale = 1.0; trap_R_AddRefEntityToScene( &beam ); if ( tr.fraction != 1.0f ) { float radius; if ( alt_fire ) radius = flrandom(1.5f, 3.0f) * (1.0 - (tr.fraction*0.3)); else radius = flrandom(0.5f, 1.5f) * (1.0 - (tr.fraction*0.3)); if ( !radius ) return; CG_ImpactMark( cgs.media.probeDecal, tr.endpos, tr.plane.normal, 0, 1, 1, 1, 0.2*(1.0-tr.fraction), qfalse, radius, qtrue ); trap_R_AddLightToScene( origin, radius*5, 114.0f / 255, 164.0f / 255, 1.0f ); } }
static void CG_AddSpawner( localEntity_t *le ) { refEntity_t *re; vec3_t dir; trace_t trace; re = &le->refEntity; if (le->leFlags & LEF_MOVE) { // kef -- do these two lines _before_ copying origin into oldorigin VectorSubtract(re->oldorigin, re->origin, dir); VectorNormalize(dir); VectorCopy(re->origin, re->oldorigin); BG_EvaluateTrajectory( &le->pos, cg.time, re->origin ); if (le->leFlags & LEF_USE_COLLISION) { // trace a line from previous position to new position CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, -1, CONTENTS_SOLID ); if ( trace.fraction != 1.0 ) { // Hit something. // 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; } // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); } VectorSubtract(re->oldorigin, re->origin, dir); VectorNormalize(dir); } } // kef -- here's where I, in my infinite wisdom, have decided to emulate the singleplayer //particle think function if (cg.time < le->data.spawner.nextthink) { return; } le->data.spawner.nextthink = cg.time + (le->data.spawner.delay + (le->data.spawner.delay*flrandom(-le->data.spawner.variance,le->data.spawner.variance))); if (le->data.spawner.thinkFn) { le->data.spawner.thinkFn(le); } if (le->data.spawner.dontDie) { le->endTime = le->endTime + 10000; } }
void FX_ExplodeBits( vec3_t org) { float width, length; vec3_t vel, pos; int i; FX_EnergyGibs(org); for (i = 0; i < 32; i++) { VectorSet(vel, flrandom(-320,320), flrandom(-320,320), flrandom(-100,320)); VectorCopy(org, pos); pos[2] += flrandom(-8, 8); length = flrandom(10,20); width = flrandom(2.0,4.0); FX_AddTrail( pos, vel, qtrue, length, -length, width, -width, 1.0f, 1.0f, 0.5f, 1000.0f, cgs.media.orangeTrailShader); } }
void FX_PhaserAltFire( vec3_t start, vec3_t end, vec3_t normal, qboolean spark, qboolean impact, qboolean empty ) { float scale = flrandom(13.0f, 17.0f), scale2 = flrandom(2.0f, 6.0f); vec3_t vel, diff, end2; int i = 0, sparks = 0; refEntity_t beam; vec3_t rgb = { 1,0.6,0.5}, rgb2={1,0.3,0}; float len; int color; VectorSubtract(end, start, diff); len = VectorNormalize(diff); color = 0xff * flrandom(0.75, 1.0); if (empty) { // More faint and shaky line. scale *= flrandom(0.25,0.75); } if (len > PHASER_ALT_CONE_LEN) { // Draw beam in two parts... // Draw main beam first. VectorMA(start, PHASER_ALT_CONE_LEN, diff, end2); // Draw starting cone memset( &beam, 0, sizeof( beam ) ); VectorCopy( start, beam.origin); VectorCopy( end2, beam.oldorigin ); beam.reType = RT_LINE2; if (empty) { beam.customShader = cgs.media.phaserAltEmptyShader; } else { beam.customShader = cgs.media.phaserAltShader; } AxisClear( beam.axis ); beam.shaderRGBA[0] = 0xff; beam.shaderRGBA[1] = 0xff*0.3; beam.shaderRGBA[2] = 0; beam.shaderRGBA[3] = 0xff; beam.data.line.width = scale*0.1; beam.data.line.width2 = scale; beam.data.line.stscale = 1.0; trap_R_AddRefEntityToScene( &beam ); // Draw big thick normal beam for the rest. memset( &beam, 0, sizeof( beam ) ); VectorCopy( end2, beam.oldorigin); VectorCopy( end, beam.origin ); beam.reType = RT_LINE; if (empty) { beam.customShader = cgs.media.phaserAltEmptyShader; } else { beam.customShader = cgs.media.phaserAltShader; } AxisClear( beam.axis ); beam.shaderRGBA[0] = 0xff; beam.shaderRGBA[1] = 0xff*0.3; beam.shaderRGBA[2] = 0; beam.shaderRGBA[3] = 0xff; beam.data.line.width = scale; beam.data.line.stscale = 1.0; trap_R_AddRefEntityToScene( &beam ); // Draw beam core, all one bit. memset( &beam, 0, sizeof( beam ) ); VectorCopy( start, beam.origin); VectorCopy( end, beam.oldorigin ); beam.reType = RT_LINE2; beam.customShader = cgs.media.phaserShader; AxisClear( beam.axis ); beam.shaderRGBA[0] = color*0.75f; beam.shaderRGBA[1] = 0xff*0.5f; beam.shaderRGBA[2] = 0xff*0.5f; beam.shaderRGBA[3] = 0xff; beam.data.line.width = scale2*0.2; beam.data.line.width2 = scale2; beam.data.line.stscale = 1.0; trap_R_AddRefEntityToScene( &beam ); } else { // Draw beam in two parts... // Draw beam first. memset( &beam, 0, sizeof( beam ) ); VectorCopy( start, beam.origin); VectorCopy( end, beam.oldorigin ); beam.reType = RT_LINE2; beam.customShader = cgs.media.phaserAltShader; AxisClear( beam.axis ); beam.shaderRGBA[0] = 0xff; beam.shaderRGBA[1] = 0xff*0.3; beam.shaderRGBA[2] = 0; beam.shaderRGBA[3] = 0xff; beam.data.line.width = scale*0.1; beam.data.line.width2 = scale; beam.data.line.stscale = 1.0; trap_R_AddRefEntityToScene( &beam ); // just one beam is never enough memset( &beam, 0, sizeof( beam ) ); VectorCopy( start, beam.origin); VectorCopy( end, beam.oldorigin ); beam.reType = RT_LINE2; beam.customShader = cgs.media.phaserShader; AxisClear( beam.axis ); beam.shaderRGBA[0] = color*0.75f; beam.shaderRGBA[1] = 0xff*0.5f; beam.shaderRGBA[2] = 0xff*0.5f; beam.shaderRGBA[3] = 0xff; beam.data.line.width = scale2*0.2; beam.data.line.width2 = scale2; beam.data.line.stscale = 1.0; trap_R_AddRefEntityToScene( &beam ); } // Phaser beam // FX_AddLine( start, end, 1.0f, scale, 0.0f, 0.9f, 0.9f, 2, cgs.media.phaserShader ); // FX_AddLine( start, end, 1.0f, scale * 0.5f, 0.0f, 0.8f, 0.8f, 2, cgs.media.phaserShader ); // Per frame impact mark FX_AddQuad( end, normal, random() * 1.5 + 1.75f, 0.0f, 1.0f, 0.0f, 0.0f, 1, cgs.media.sparkShader ); FX_AddQuad( end, normal, random() * 5 + 2.75f, 0.0f, 1.0f, 0.0f, 0.0f, 1, cgs.media.yellowParticleShader ); // Multi frame impacts--never do this when it hits a player because it will just look stupid if ( impact ) { FX_AddQuad2( end, normal, random() * 2.0 + 5.0f, 2.5f, 0.6f, 0.0f, rgb, rgb2, 0.0f, 500 + random() * 200, cgs.media.sunnyFlareShader ); CG_ImpactMark( cgs.media.scavMarkShader, end, normal, random()*360, 1,1,1,0.1, qfalse, random() + 6.0, qfalse ); } // "Fun" sparks if ( spark ) { // kef -- fixme. dunno what the deal is with this velocity vector VectorClear(vel); sparks = (rand() & 3) + 1; // Set random starting pos... end2[0] = flrandom(-1.0, 1.0) + end[0]; end2[1] = flrandom(-1.0, 1.0) + end[1]; end2[2] = flrandom(-1.0, 1.0) + end[2]; for( i = 0; i < sparks; i++ ) { scale = 0.5f + (random() * 0.5); FXE_Spray( normal, 200, 75, 0.8f, /*1024*/vel); FX_AddTrail2( end2, vel, qfalse, 8.0f, -8.0f, scale, -scale, 0.5f, 0.0f, rgb, rgb2, 0.4f, 500.0f, cgs.media.sparkShader ); } VectorMA(end, -8, diff, end2); // Add a hit sprite over everything... memset( &beam, 0, sizeof( beam ) ); VectorCopy( end2, beam.origin); beam.reType = RT_SPRITE; beam.customShader = cgs.media.sunnyFlareShader; AxisClear( beam.axis ); beam.shaderRGBA[0] = 0xff*1.0f; beam.shaderRGBA[1] = 0xff*0.9f; beam.shaderRGBA[2] = 0xff*0.8f; beam.shaderRGBA[3] = 0xff; beam.data.sprite.radius = random()*2.0 + 9.0; trap_R_AddRefEntityToScene( &beam ); } }
/** * \brief Alt quantum burst projectile think functiom. * * Alt quantum burst projectile think function. * * @param ent the projectile */ static void WP_QuantumAltThink(gentity_t *ent) { vec3_t start, newdir, targetdir, lup={0,0,1}, lright, search; float dot, dot2; ent->health--; if (ent->health<=0) { G_FreeEntity(ent); return; } if (ent->target_ent) { /* Already have a target, start homing. */ if (ent->health <= 0 || !ent->inuse) { /* No longer target this */ ent->target_ent = NULL; ent->nextthink = level.time + 1000; ent->health -= 5; return; } VectorSubtract(ent->target_ent->r.currentOrigin, ent->r.currentOrigin, targetdir); VectorNormalize(targetdir); /* Now the rocket can't do a 180 in space, so we'll limit the turn to about 45 degrees. */ dot = DotProduct(targetdir, ent->movedir); /* a dot of 1.0 means right-on-target. */ if (dot < 0.0) { /* Go in the direction opposite, start a 180. */ CrossProduct(ent->movedir, lup, lright); dot2 = DotProduct(targetdir, lright); if (dot2 > 0) { /* Turn 45 degrees right. */ VectorAdd(ent->movedir, lright, newdir); } else { /* Turn 45 degrees left. */ VectorSubtract(ent->movedir, lright, newdir); } /* Yeah we've adjusted horizontally, but let's split the difference vertically, so we kinda try to move towards it. */ newdir[2] = (targetdir[2] + ent->movedir[2]) * 0.5; VectorNormalize(newdir); } else if (dot < 0.7) { /* Need about one correcting turn. Generate by meeting the target direction "halfway". */ /* Note, this is less than a 45 degree turn, but it is sufficient. We do this because the rocket may have to go UP. */ VectorAdd(ent->movedir, targetdir, newdir); VectorNormalize(newdir); } else { /* else adjust to right on target. */ VectorCopy(targetdir, newdir); } VectorScale(newdir, rpg_altPhotonSpeed.integer, ent->s.pos.trDelta); VectorCopy(newdir, ent->movedir); SnapVector(ent->s.pos.trDelta); /* save net bandwidth */ VectorCopy(ent->r.currentOrigin, ent->s.pos.trBase); SnapVector(ent->s.pos.trBase); ent->s.pos.trTime = level.time; /* Home at a reduced frequency. */ ent->nextthink = level.time + QUANTUM_ALT_THINK_TIME; /* Nothing at all spectacular happened, continue. */ } else { /* Search in front of the missile for targets. */ VectorCopy(ent->r.currentOrigin, start); CrossProduct(ent->movedir, lup, lright); /* Search straight ahead. */ VectorMA(start, QUANTUM_ALT_SEARCH_DIST, ent->movedir, search); /* Add some small randomness to the search Z height, to give a bit of variation to where we are searching. */ search[2] += flrandom(-QUANTUM_ALT_SEARCH_DIST*0.075, QUANTUM_ALT_SEARCH_DIST*0.075); if (SearchTarget(ent, start, search)) return; /* Search to the right. */ VectorMA(search, QUANTUM_ALT_SEARCH_DIST*0.1, lright, search); if (SearchTarget(ent, start, search)) return; /* Search to the left. */ VectorMA(search, -QUANTUM_ALT_SEARCH_DIST*0.2, lright, search); if (SearchTarget(ent, start, search)) return; /* Search at a higher rate than correction. */ ent->nextthink = level.time + QUANTUM_ALT_SEARCH_TIME; /* Nothing at all spectacular happened, continue. */ } return; }
void FX_EnergyGibs(vec3_t origin ) { localEntity_t *le; refEntity_t *re; vec3_t dir; int i, j, k; int chunkModel=0; float baseScale = 0.7f, dist; int numChunks; numChunks = irandom( 10, 15 ); VectorSubtract(cg.snap->ps.origin, origin, dir); dist = VectorLength(dir); if (dist > 512) { numChunks *= 512.0/dist; // 1/2 at 1024, 1/4 at 2048, etc. } for ( i = 0; i < numChunks; i++ ) { chunkModel = cgs.media.chunkModels[MT_METAL][irandom(0,5)]; le = CG_AllocLocalEntity(); re = &le->refEntity; le->leType = LE_FRAGMENT; le->endTime = cg.time + 2000; VectorCopy( origin, re->origin ); for ( j = 0; j < 3; j++ ) { re->origin[j] += crandom() * 12; } VectorCopy( re->origin, le->pos.trBase ); //Velocity VectorSet( dir, crandom(), crandom(), crandom() ); VectorScale( dir, flrandom( 300, 500 ), le->pos.trDelta ); //Angular Velocity VectorSet( le->angles.trBase, crandom() * 360, crandom() * 360, crandom() * 360 ); VectorSet( le->angles.trDelta, crandom() * 90, crandom() * 90, crandom() * 90 ); AxisCopy( axisDefault, re->axis ); le->data.fragment.radius = flrandom(baseScale * 0.4f, baseScale * 0.8f ); re->nonNormalizedAxes = qtrue; re->hModel = chunkModel; re->renderfx |= RF_CAP_FRAMES; re->customShader = cgs.media.quantumDisruptorShader; re->shaderTime = cg.time/1000.0f; le->pos.trType = TR_GRAVITY; le->pos.trTime = cg.time; le->angles.trType = TR_INTERPOLATE; le->angles.trTime = cg.time; le->bounceFactor = 0.2f + random() * 0.2f; le->leFlags |= LEF_TUMBLE; re->shaderRGBA[0] = re->shaderRGBA[1] = re->shaderRGBA[2] = re->shaderRGBA[3] = 255; // Make sure that we have the desired start size set for( k = 0; k < 3; k++) { VectorScale(le->refEntity.axis[k], le->data.fragment.radius, le->refEntity.axis[k]); } } }
/*** Get a random float from the range of floats defined by and including i and j. @function flrandom @param i Number, lower limit. @param j Number, upper limit. @return A random float from the range of floats defined by and including i and j. */ static int Qmath_FLrandom(lua_State * L) { lua_pushnumber(L, flrandom(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); return 1; }