/* ============== RB_DrawSun (SA) FIXME: sun should render behind clouds, so passing dark areas cover it up ============== */ void RB_DrawSun( void ) { float size; float dist; vec3_t origin, vec1, vec2; vec3_t temp; byte color[4]; if ( !tr.sunShader ) { return; } if ( !backEnd.skyRenderedThisView ) { return; } if ( !r_drawSun->integer ) { return; } qglPushMatrix(); qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); qglTranslatef( backEnd.viewParms.orientation.origin[0], backEnd.viewParms.orientation.origin[1], backEnd.viewParms.orientation.origin[2] ); dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3) // (SA) shrunk the size of the sun size = dist * 0.2; VectorScale( tr.sunDirection, dist, origin ); PerpendicularVector( vec1, tr.sunDirection ); CrossProduct( tr.sunDirection, vec1, vec2 ); VectorScale( vec1, size, vec1 ); VectorScale( vec2, size, vec2 ); // farthest depth range qglDepthRange( 1.0, 1.0 ); color[0] = color[1] = color[2] = color[3] = 255; // (SA) simpler sun drawing RB_BeginSurface( tr.sunShader, tess.fogNum ); RB_AddQuadStamp( origin, vec1, vec2, color ); /* VectorCopy( origin, temp ); VectorSubtract( temp, vec1, temp ); VectorSubtract( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes].v ); tess.texCoords0[tess.numVertexes].v[0] = 0; tess.texCoords0[tess.numVertexes].v[1] = 0; tess.vertexColors[tess.numVertexes].v[0] = 255; tess.vertexColors[tess.numVertexes].v[1] = 255; tess.vertexColors[tess.numVertexes].v[2] = 255; tess.numVertexes++; VectorCopy( origin, temp ); VectorAdd( temp, vec1, temp ); VectorSubtract( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes].v ); tess.texCoords0[tess.numVertexes].v[0] = 0; tess.texCoords0[tess.numVertexes].v[1] = 1; tess.vertexColors[tess.numVertexes].v[0] = 255; tess.vertexColors[tess.numVertexes].v[1] = 255; tess.vertexColors[tess.numVertexes].v[2] = 255; tess.numVertexes++; VectorCopy( origin, temp ); VectorAdd( temp, vec1, temp ); VectorAdd( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes].v ); tess.texCoords0[tess.numVertexes].v[0] = 1; tess.texCoords0[tess.numVertexes].v[1] = 1; tess.vertexColors[tess.numVertexes].v[0] = 255; tess.vertexColors[tess.numVertexes].v[1] = 255; tess.vertexColors[tess.numVertexes].v[2] = 255; tess.numVertexes++; VectorCopy( origin, temp ); VectorSubtract( temp, vec1, temp ); VectorAdd( temp, vec2, temp ); VectorCopy( temp, tess.xyz[tess.numVertexes].v ); tess.texCoords0[tess.numVertexes].v[0] = 1; tess.texCoords0[tess.numVertexes].v[1] = 0; tess.vertexColors[tess.numVertexes].v[0] = 255; tess.vertexColors[tess.numVertexes].v[1] = 255; tess.vertexColors[tess.numVertexes].v[2] = 255; tess.numVertexes++; tess.indexes[tess.numIndexes++] = 0; tess.indexes[tess.numIndexes++] = 1; tess.indexes[tess.numIndexes++] = 2; tess.indexes[tess.numIndexes++] = 0; tess.indexes[tess.numIndexes++] = 2; tess.indexes[tess.numIndexes++] = 3; */ RB_EndSurface(); if ( r_drawSun->integer > 1 ) { // draw flare effect // (SA) FYI: This is cheezy and was only a test so far. // If we decide to use the flare business I will /definatly/ improve all this // get a point a little closer dist = dist * 0.7; VectorScale( tr.sunDirection, dist, origin ); // and make the flare a little smaller VectorScale( vec1, 0.5f, vec1 ); VectorScale( vec2, 0.5f, vec2 ); // add the vectors to give an 'off angle' result VectorAdd( tr.sunDirection, backEnd.viewParms.orientation.axis[0], temp ); VectorNormalize( temp ); // amplify the result origin[0] += temp[0] * 500.0; origin[1] += temp[1] * 500.0; origin[2] += temp[2] * 500.0; // (SA) FIXME: todo: flare effect should render last (on top of everything else) and only when sun is in view (sun moving out of camera past degree n should start to cause flare dimming until view angle to sun is off by angle n + x. // draw the flare RB_BeginSurface( tr.sunflareShader[0], tess.fogNum ); RB_AddQuadStamp( origin, vec1, vec2, color ); RB_EndSurface(); } // back to normal depth range qglDepthRange( 0.0, 1.0 ); qglPopMatrix(); }
// Change a polygon into a bunch of text polygons void DeformText( const char *text ) { int i; vector3 origin, width, height; int len; int ch; byte color[4]; float bottom, top; vector3 mid; height.x = 0; height.y = 0; height.z = -1; CrossProduct( &tess.normal[0], &height, &width ); // find the midpoint of the box VectorClear( &mid ); bottom = 999999; top = -999999; for ( i = 0 ; i < 4 ; i++ ) { VectorAdd( &tess.xyz[i], &mid, &mid ); if ( tess.xyz[i].z < bottom ) { bottom = tess.xyz[i].z; } if ( tess.xyz[i].z > top ) { top = tess.xyz[i].z; } } VectorScale( &mid, 0.25f, &origin ); // determine the individual character size height.x = 0; height.y = 0; height.z = ( top - bottom ) * 0.5f; VectorScale( &width, height.z * -0.75f, &width ); // determine the starting position len = strlen( text ); VectorMA( &origin, (float)(len-1), &width, &origin ); // clear the shader indexes tess.numIndexes = 0; tess.numVertexes = 0; color[0] = color[1] = color[2] = color[3] = 255; // draw each character for ( i = 0 ; i < len ; i++ ) { ch = text[i]; ch &= 255; if ( ch != ' ' ) { int row, col; float frow, fcol, size; row = ch>>4; col = ch&15; frow = row*0.0625f; fcol = col*0.0625f; size = 0.0625f; RB_AddQuadStampExt( &origin, &width, &height, color, fcol, frow, fcol + size, frow + size ); } VectorMA( &origin, -2, &width, &origin ); }
void AddReflection (edict_t *ent) { gclient_t *cl; edict_t *mirror; float roll; int i, m; qboolean is_reflected; vec3_t forward; vec3_t org; for(i=0; i<6; i++) { is_reflected = false; for(m=0; m<level.num_reflectors && !is_reflected; m++) { mirror = g_mirror[m]; if(!mirror->inuse) continue; if(mirror->spawnflags & SF_REFLECT_OFF) continue; if(mirror->style != i) continue; VectorCopy(ent->s.origin,org); switch(i) { case 0: org[2] = 2*mirror->absmax[2] - ent->s.origin[2] - mirror->moveinfo.distance - 2; break; case 1: org[2] = 2*mirror->absmin[2] - ent->s.origin[2] + mirror->moveinfo.distance + 2; break; case 2: org[0] = 2*mirror->absmin[0] - ent->s.origin[0] + mirror->moveinfo.distance + 2; break; case 3: org[0] = 2*mirror->absmax[0] - ent->s.origin[0] - mirror->moveinfo.distance - 2; break; case 4: org[1] = 2*mirror->absmin[1] - ent->s.origin[1] + mirror->moveinfo.distance + 2; break; case 5: org[1] = 2*mirror->absmax[1] - ent->s.origin[1] - mirror->moveinfo.distance - 2; break; } if(org[0] < mirror->absmin[0]) continue; if(org[0] > mirror->absmax[0]) continue; if(org[1] < mirror->absmin[1]) continue; if(org[1] > mirror->absmax[1]) continue; if(org[2] < mirror->absmin[2]) continue; if(org[2] > mirror->absmax[2]) continue; is_reflected = true; } if(is_reflected) { if (!ent->reflection[i]) { ent->reflection[i] = G_Spawn(); if(ent->s.effects & EF_ROTATE) { ent->s.effects &= ~EF_ROTATE; gi.linkentity(ent); } ent->reflection[i]->movetype = MOVETYPE_NONE; ent->reflection[i]->solid = SOLID_NOT; ent->reflection[i]->classname = "reflection"; ent->reflection[i]->flags = FL_REFLECT; ent->reflection[i]->takedamage = DAMAGE_NO; } if (ent->client && !ent->reflection[i]->client) { cl = (gclient_t *)malloc(sizeof(gclient_t)); ent->reflection[i]->client = cl; } if (ent->client && ent->reflection[i]->client) { // Lazarus: Hmm.. this crashes when loading saved game. // Not sure what use pers is anyhow? // ent->reflection[i]->client->pers = ent->client->pers; ent->reflection[i]->s = ent->s; } ent->reflection[i]->s.number = ent->reflection[i] - g_edicts; ent->reflection[i]->s.modelindex = ent->s.modelindex; ent->reflection[i]->s.modelindex2 = ent->s.modelindex2; ent->reflection[i]->s.modelindex3 = ent->s.modelindex3; ent->reflection[i]->s.modelindex4 = ent->s.modelindex4; #ifdef KMQUAKE2_ENGINE_MOD ent->reflection[i]->s.modelindex5 = ent->s.modelindex5; ent->reflection[i]->s.modelindex6 = ent->s.modelindex6; #ifndef LOOP_SOUND_ATTENUATION ent->reflection[i]->s.modelindex7 = ent->s.modelindex7; ent->reflection[i]->s.modelindex8 = ent->s.modelindex8; #endif ent->reflection[i]->s.alpha = ent->s.alpha; #endif ent->reflection[i]->s.skinnum = ent->s.skinnum; ent->reflection[i]->s.frame = ent->s.frame; ent->reflection[i]->s.effects = ent->s.effects; ent->reflection[i]->s.renderfx = ent->s.renderfx; ent->reflection[i]->s.renderfx &= ~RF_IR_VISIBLE; #ifdef KMQUAKE2_ENGINE_MOD // Don't flip if left handed player model if ( !(ent->s.modelindex == (MAX_MODELS-1) && hand->value == 1) ) ent->reflection[i]->s.renderfx |= RF_MIRRORMODEL; // Knightmare added- flip reflected models #endif VectorCopy (ent->s.angles, ent->reflection[i]->s.angles); switch(i) { case 0: case 1: ent->reflection[i]->s.angles[0]+=180; ent->reflection[i]->s.angles[1]+=180; ent->reflection[i]->s.angles[2]=360-ent->reflection[i]->s.angles[2]; break; case 2: case 3: AngleVectors(ent->reflection[i]->s.angles,forward,NULL,NULL); roll = ent->reflection[i]->s.angles[2]; forward[0] = -forward[0]; vectoangles(forward,ent->reflection[i]->s.angles); ent->reflection[i]->s.angles[2] = 360-roll; break; case 4: case 5: AngleVectors(ent->reflection[i]->s.angles,forward,NULL,NULL); roll = ent->reflection[i]->s.angles[2]; forward[1] = -forward[1]; vectoangles(forward,ent->reflection[i]->s.angles); ent->reflection[i]->s.angles[2] = 360-roll; } VectorCopy (org, ent->reflection[i]->s.origin); if(ent->s.renderfx & RF_BEAM) { vec3_t delta; VectorSubtract(ent->reflection[i]->s.origin,ent->s.origin,delta); VectorAdd(ent->s.old_origin,delta,ent->reflection[i]->s.old_origin); } else VectorCopy(ent->reflection[i]->s.origin, ent->reflection[i]->s.old_origin); gi.linkentity (ent->reflection[i]); } else if (ent->reflection[i]) { DeleteReflection(ent,i); } } }
/* ================ G_BounceItem ================ */ void G_BounceItem( gentity_t *ent, trace_t *trace ) { vec3_t velocity; float dot; int hitTime; qboolean droppedSaber = qtrue; if ( ent->item && ent->item->giType == IT_WEAPON && ent->item->giTag == WP_SABER && (ent->flags&FL_DROPPED_ITEM) ) { droppedSaber = qtrue; } // reflect the velocity on the trace plane hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction; EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity ); dot = DotProduct( velocity, trace->plane.normal ); VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta ); // cut the velocity to keep from bouncing forever VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta ); if ( droppedSaber ) {//a dropped saber item //FIXME: use NPC_type (as saberType) to get proper bounce sound? WP_SaberFallSound( NULL, ent ); } // check for stop if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {//stop G_SetOrigin( ent, trace->endpos ); ent->s.groundEntityNum = trace->entityNum; if ( droppedSaber ) {//a dropped saber item //stop rotation VectorClear( ent->s.apos.trDelta ); ent->currentAngles[PITCH] = SABER_PITCH_HACK; ent->currentAngles[ROLL] = 0; if ( ent->NPC_type && ent->NPC_type[0] ) {//we have a valid saber for this saberInfo_t saber; if ( WP_SaberParseParms( ent->NPC_type, &saber ) ) { if ( (saber.saberFlags&SFL_BOLT_TO_WRIST) ) { ent->currentAngles[PITCH] = 0; } } } pitch_roll_for_slope( ent, trace->plane.normal, ent->currentAngles, qtrue ); G_SetAngles( ent, ent->currentAngles ); } return; } //bounce if ( droppedSaber ) {//a dropped saber item //change rotation VectorCopy( ent->currentAngles, ent->s.apos.trBase ); ent->s.apos.trType = TR_LINEAR; ent->s.apos.trTime = level.time; VectorSet( ent->s.apos.trDelta, Q_irand( -300, 300 ), Q_irand( -300, 300 ), Q_irand( -300, 300 ) ); } VectorAdd( ent->currentOrigin, trace->plane.normal, ent->currentOrigin); VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; }
//FIXME since we need to test end position contents here, can we avoid doing //it again later in catagorize position? qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink) { float dz; vec3_t oldorg, neworg, end; trace_t trace; int i; float stepsize; vec3_t test; int contents; // try the move VectorCopy (ent->s.origin, oldorg); VectorAdd (ent->s.origin, move, neworg); // flying monsters don't step up if ( ent->flags & (FL_SWIM | FL_FLY) ) { // try one move with vertical motion, then one without for (i=0 ; i<2 ; i++) { VectorAdd (ent->s.origin, move, neworg); if (i == 0 && ent->enemy) { if (!ent->goalentity) ent->goalentity = ent->enemy; dz = ent->s.origin[2] - ent->goalentity->s.origin[2]; if (ent->goalentity->client) { if (dz > 40) neworg[2] -= 8; if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2))) if (dz < 30) neworg[2] += 8; } else { if (dz > 8) neworg[2] -= 8; else if (dz > 0) neworg[2] -= dz; else if (dz < -8) neworg[2] += 8; else neworg[2] += dz; } } trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID); // fly monsters don't enter water voluntarily if (ent->flags & FL_FLY) { if (!ent->waterlevel) { test[0] = trace.endpos[0]; test[1] = trace.endpos[1]; test[2] = trace.endpos[2] + ent->mins[2] + 1; contents = gi.pointcontents(test); if (contents & MASK_WATER) return false; } } // swim monsters don't exit water voluntarily if (ent->flags & FL_SWIM) { if (ent->waterlevel < 2) { test[0] = trace.endpos[0]; test[1] = trace.endpos[1]; test[2] = trace.endpos[2] + ent->mins[2] + 1; contents = gi.pointcontents(test); if (!(contents & MASK_WATER)) return false; } } if (trace.fraction == 1) { VectorCopy (trace.endpos, ent->s.origin); if (relink) { gi.linkentity (ent); G_TouchTriggers (ent); } return true; } if (!ent->enemy) break; } return false; } // push down from a step height above the wished position if (!(ent->monsterinfo.aiflags & AI_NOSTEP)) stepsize = STEPSIZE; else stepsize = 1; neworg[2] += stepsize; VectorCopy (neworg, end); end[2] -= stepsize*2; trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID); if (trace.allsolid) return false; if (trace.startsolid) { neworg[2] -= stepsize; trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID); if (trace.allsolid || trace.startsolid) return false; } // don't go in to water if (ent->waterlevel == 0) { test[0] = trace.endpos[0]; test[1] = trace.endpos[1]; test[2] = trace.endpos[2] + ent->mins[2] + 1; contents = gi.pointcontents(test); if (contents & MASK_WATER) return false; } if (trace.fraction == 1) { // if monster had the ground pulled out, go ahead and fall if ( ent->flags & FL_PARTIALGROUND ) { VectorAdd (ent->s.origin, move, ent->s.origin); if (relink) { gi.linkentity (ent); G_TouchTriggers (ent); } ent->groundentity = NULL; return true; } return false; // walked off an edge } // check point traces down for dangling corners VectorCopy (trace.endpos, ent->s.origin); if (!M_CheckBottom (ent)) { if ( ent->flags & FL_PARTIALGROUND ) { // entity had floor mostly pulled out from underneath it // and is trying to correct if (relink) { gi.linkentity (ent); G_TouchTriggers (ent); } return true; } VectorCopy (oldorg, ent->s.origin); return false; } if ( ent->flags & FL_PARTIALGROUND ) { ent->flags &= ~FL_PARTIALGROUND; } ent->groundentity = trace.ent; ent->groundentity_linkcount = trace.ent->linkcount; // the move is ok if (relink) { gi.linkentity (ent); G_TouchTriggers (ent); } return true; }
/* ============= SV_CalcBlend ============= */ void SV_CalcBlend (edict_t *ent) { int contents; vec3_t vieworg; int remaining; ent->client->ps.blend[0] = ent->client->ps.blend[1] = ent->client->ps.blend[2] = ent->client->ps.blend[3] = 0; // add for contents VectorAdd (ent->s.origin, ent->client->ps.viewoffset, vieworg); contents = gi.pointcontents (vieworg); if (contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER) ) ent->client->ps.rdflags |= RDF_UNDERWATER; else ent->client->ps.rdflags &= ~RDF_UNDERWATER; if (contents & (CONTENTS_SOLID|CONTENTS_LAVA)) SV_AddBlend (1.0, 0.3, 0.0, 0.6, ent->client->ps.blend); else if (contents & CONTENTS_SLIME) SV_AddBlend (0.0, 0.1, 0.05, 0.6, ent->client->ps.blend); else if (contents & CONTENTS_WATER) SV_AddBlend (0.5, 0.3, 0.2, 0.4, ent->client->ps.blend); // add for powerups if (ent->client->quad_framenum > level.framenum) { remaining = ent->client->quad_framenum - level.framenum; if (remaining == 30) // beginning to fade gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage2.wav"), 1, ATTN_NORM, 0); if (remaining > 30 || (remaining & 4) ) SV_AddBlend (0, 0, 1, 0.08, ent->client->ps.blend); } // PMM - double damage else if (ent->client->double_framenum > level.framenum) { remaining = ent->client->double_framenum - level.framenum; if (remaining == 30) // beginning to fade gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/ddamage2.wav"), 1, ATTN_NORM, 0); if (remaining > 30 || (remaining & 4) ) SV_AddBlend (0.9, 0.7, 0, 0.08, ent->client->ps.blend); } // PMM else if (ent->client->invincible_framenum > level.framenum) { remaining = ent->client->invincible_framenum - level.framenum; if (remaining == 30) // beginning to fade gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect2.wav"), 1, ATTN_NORM, 0); if (remaining > 30 || (remaining & 4) ) SV_AddBlend (1, 1, 0, 0.08, ent->client->ps.blend); } else if (ent->client->enviro_framenum > level.framenum) { remaining = ent->client->enviro_framenum - level.framenum; if (remaining == 30) // beginning to fade gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0); if (remaining > 30 || (remaining & 4) ) SV_AddBlend (0, 1, 0, 0.08, ent->client->ps.blend); } else if (ent->client->breather_framenum > level.framenum) { remaining = ent->client->breather_framenum - level.framenum; if (remaining == 30) // beginning to fade gi.sound(ent, CHAN_ITEM, gi.soundindex("items/airout.wav"), 1, ATTN_NORM, 0); if (remaining > 30 || (remaining & 4) ) SV_AddBlend (0.4, 1, 0.4, 0.04, ent->client->ps.blend); } //PGM if(ent->client->nuke_framenum > level.framenum) { float brightness; brightness = (ent->client->nuke_framenum - level.framenum) / 20.0; SV_AddBlend (1, 1, 1, brightness, ent->client->ps.blend); } if (ent->client->ir_framenum > level.framenum) { remaining = ent->client->ir_framenum - level.framenum; if(remaining > 30 || (remaining & 4)) { ent->client->ps.rdflags |= RDF_IRGOGGLES; SV_AddBlend (1, 0, 0, 0.2, ent->client->ps.blend); } else ent->client->ps.rdflags &= ~RDF_IRGOGGLES; } else { ent->client->ps.rdflags &= ~RDF_IRGOGGLES; } //PGM // add for damage if (ent->client->damage_alpha > 0) SV_AddBlend (ent->client->damage_blend[0],ent->client->damage_blend[1] ,ent->client->damage_blend[2], ent->client->damage_alpha, ent->client->ps.blend); if (ent->client->bonus_alpha > 0) SV_AddBlend (0.85, 0.7, 0.3, ent->client->bonus_alpha, ent->client->ps.blend); // drop the damage value ent->client->damage_alpha -= 0.06; if (ent->client->damage_alpha < 0) ent->client->damage_alpha = 0; // drop the bonus value ent->client->bonus_alpha -= 0.1; if (ent->client->bonus_alpha < 0) ent->client->bonus_alpha = 0; }
qboolean PM_SlideMove( qboolean gravity ) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity; vec3_t clipVelocity; int i, j, k; trace_t trace; vec3_t end; float time_left; float into; vec3_t endVelocity; vec3_t endClipVelocity; numbumps = 4; VectorCopy (pm->ps->velocity, primal_velocity); if ( gravity ) { VectorCopy( pm->ps->velocity, endVelocity ); endVelocity[2] -= pm->ps->gravity * pml.frametime; pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5; primal_velocity[2] = endVelocity[2]; if ( pml.groundPlane ) { // slide along the ground plane PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP ); } } time_left = pml.frametime; // never turn against the ground plane if ( pml.groundPlane ) { numplanes = 1; VectorCopy( pml.groundTrace.plane.normal, planes[0] ); } else { numplanes = 0; } // never turn against original velocity VectorNormalize2( pm->ps->velocity, planes[numplanes] ); numplanes++; for ( bumpcount=0 ; bumpcount < numbumps ; bumpcount++ ) { // calculate position we are trying to move to VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end ); // see if we can make it there pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask); if (trace.allsolid) { // entity is completely trapped in another solid pm->ps->velocity[2] = 0; // don't build up falling damage, but allow sideways acceleration return qtrue; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, pm->ps->origin); } if (trace.fraction == 1) { break; // moved the entire distance } // save entity for contact PM_AddTouchEnt( trace.entityNum ); time_left -= time_left * trace.fraction; if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorClear( pm->ps->velocity ); return qtrue; } // // if this is the same plane we hit before, nudge velocity // out along it, which fixes some epsilon issues with // non-axial planes // for ( i = 0 ; i < numplanes ; i++ ) { if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) { VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity ); break; } } if ( i < numplanes ) { continue; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; // // modify velocity so it parallels all of the clip planes // // find a plane that it enters for ( i = 0 ; i < numplanes ; i++ ) { into = DotProduct( pm->ps->velocity, planes[i] ); if ( into >= 0.1 ) { continue; // move doesn't interact with the plane } // see how hard we are hitting things if ( -into > pml.impactSpeed ) { pml.impactSpeed = -into; } // slide along the plane PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP ); // slide along the plane PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); // see if there is a second plane that the new move enters for ( j = 0 ; j < numplanes ; j++ ) { if ( j == i ) { continue; } if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); // see if it goes back into the first clip plane if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) { continue; } // slide the original velocity along the crease CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, pm->ps->velocity ); VectorScale( dir, d, clipVelocity ); CrossProduct (planes[i], planes[j], dir); VectorNormalize( dir ); d = DotProduct( dir, endVelocity ); VectorScale( dir, d, endClipVelocity ); // see if there is a third plane the the new move enters for ( k = 0 ; k < numplanes ; k++ ) { if ( k == i || k == j ) { continue; } if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) { continue; // move doesn't interact with the plane } // stop dead at a tripple plane interaction VectorClear( pm->ps->velocity ); return qtrue; } } // if we have fixed all interactions, try another move VectorCopy( clipVelocity, pm->ps->velocity ); VectorCopy( endClipVelocity, endVelocity ); break; } } if ( gravity ) { VectorCopy( endVelocity, pm->ps->velocity ); } // don't change velocity if in a timer (FIXME: is this correct?) if ( pm->ps->pm_time ) { VectorCopy( primal_velocity, pm->ps->velocity ); } return ( bumpcount != 0 ); }
/* =============== GrabFrame =============== */ static void GrabFrame( char *frame ){ triangle_t *ptri; int i, j; trivert_t *ptrivert; int num_tris; char file1[1024]; frame_t *fr; vertexnormals_t vnorms[MAX_VERTS]; int index_xyz; char *framefile; // the frame 'run1' will be looked for as either // run.1 or run1.tri, so the new alias sequence save // feature an be used framefile = FindFrameFile( frame ); sprintf( file1, "%s/%s", cdarchive, framefile ); ExpandPathAndArchive( file1 ); sprintf( file1, "%s/%s",cddir, framefile ); printf( "grabbing %s ", file1 ); if ( model.num_frames >= MAX_FRAMES ) { Error( "model.num_frames >= MAX_FRAMES" ); } fr = &g_frames[model.num_frames]; model.num_frames++; strcpy( fr->name, frame ); // // load the frame // if ( do3ds ) { Load3DSTriangleList( file1, &ptri, &num_tris, NULL, NULL ); } else{ LoadTriangleList( file1, &ptri, &num_tris, NULL, NULL ); } if ( num_tris != model.num_tris ) { Error( "%s: number of triangles doesn't match base frame\n", file1 ); } // // allocate storage for the frame's vertices // ptrivert = fr->v; for ( i = 0 ; i < model.num_xyz ; i++ ) { vnorms[i].numnormals = 0; VectorClear( vnorms[i].normalsum ); } ClearBounds( fr->mins, fr->maxs ); // // store the frame's vertices in the same order as the base. This assumes the // triangles and vertices in this frame are in exactly the same order as in the // base // for ( i = 0 ; i < num_tris ; i++ ) { vec3_t vtemp1, vtemp2, normal; float ftemp; VectorSubtract( ptri[i].verts[0], ptri[i].verts[1], vtemp1 ); VectorSubtract( ptri[i].verts[2], ptri[i].verts[1], vtemp2 ); CrossProduct( vtemp1, vtemp2, normal ); VectorNormalize( normal, normal ); // rotate the normal so the model faces down the positive x axis ftemp = normal[0]; normal[0] = -normal[1]; normal[1] = ftemp; for ( j = 0 ; j < 3 ; j++ ) { index_xyz = triangles[i].index_xyz[j]; // rotate the vertices so the model faces down the positive x axis // also adjust the vertices to the desired origin ptrivert[index_xyz].v[0] = ( ( -ptri[i].verts[j][1] ) * scale_up ) + adjust[0]; ptrivert[index_xyz].v[1] = ( ptri[i].verts[j][0] * scale_up ) + adjust[1]; ptrivert[index_xyz].v[2] = ( ptri[i].verts[j][2] * scale_up ) + adjust[2]; AddPointToBounds( ptrivert[index_xyz].v, fr->mins, fr->maxs ); VectorAdd( vnorms[index_xyz].normalsum, normal, vnorms[index_xyz].normalsum ); vnorms[index_xyz].numnormals++; } } // // calculate the vertex normals, match them to the template list, and store the // index of the best match // for ( i = 0 ; i < model.num_xyz ; i++ ) { int j; vec3_t v; float maxdot; int maxdotindex; int c; c = vnorms[i].numnormals; if ( !c ) { Error( "Vertex with no triangles attached" ); } VectorScale( vnorms[i].normalsum, 1.0 / c, v ); VectorNormalize( v, v ); maxdot = -999999.0; maxdotindex = -1; for ( j = 0 ; j < NUMVERTEXNORMALS ; j++ ) { float dot; dot = DotProduct( v, avertexnormals[j] ); if ( dot > maxdot ) { maxdot = dot; maxdotindex = j; } } ptrivert[i].lightnormalindex = maxdotindex; } free( ptri ); }
void SV_LinkEntity(sharedEntity_t *gEnt) { worldSector_t *node; int leafs[MAX_TOTAL_ENT_LEAFS]; int cluster; int num_leafs; int i, j, k; int area; int lastLeaf; float *origin, *angles; svEntity_t *ent; ent = SV_SvEntityForGentity(gEnt); // sanity check for possible currentOrigin being reset bug if (!gEnt->r.bmodel && VectorCompare(gEnt->r.currentOrigin, vec3_origin)) { Com_DPrintf("WARNING: BBOX entity is being linked at world origin, this is probably a bug\n"); } if (ent->worldSector) { SV_UnlinkEntity(gEnt); // unlink from old position } // encode the size into the entityState_t for client prediction if (gEnt->r.bmodel) { gEnt->s.solid = SOLID_BMODEL; // a solid_box will never create this value } else if (gEnt->r.contents & (CONTENTS_SOLID|CONTENTS_BODY)) { // assume that x/y are equal and symetric i = gEnt->r.maxs[0]; if (i < 1) { i = 1; } if (i > 255) { i = 255; } // z is not symetric j = (-gEnt->r.mins[2]); if (j < 1) { j = 1; } if (j > 255) { j = 255; } // and z maxs can be negative... k = (gEnt->r.maxs[2] + 32); if (k < 1) { k = 1; } if (k > 255) { k = 255; } gEnt->s.solid = (k << 16)|(j << 8)|i; } else { gEnt->s.solid = 0; } // get the position origin = gEnt->r.currentOrigin; angles = gEnt->r.currentAngles; // set the abs box if (gEnt->r.bmodel && (angles[0] || angles[1] || angles[2])) { // expand for rotation float max; max = RadiusFromBounds(gEnt->r.mins, gEnt->r.maxs); for (i = 0; i < 3; i++) { gEnt->r.absmin[i] = origin[i] - max; gEnt->r.absmax[i] = origin[i] + max; } } else { // normal VectorAdd(origin, gEnt->r.mins, gEnt->r.absmin); VectorAdd(origin, gEnt->r.maxs, gEnt->r.absmax); } // because movement is clipped an epsilon away from an actual edge, we must fully check even when bounding boxes don't quite touch gEnt->r.absmin[0] -= 1; gEnt->r.absmin[1] -= 1; gEnt->r.absmin[2] -= 1; gEnt->r.absmax[0] += 1; gEnt->r.absmax[1] += 1; gEnt->r.absmax[2] += 1; // link to PVS leafs ent->numClusters = 0; ent->lastCluster = 0; ent->areanum = -1; ent->areanum2 = -1; // get all leafs, including solids num_leafs = CM_BoxLeafnums(gEnt->r.absmin, gEnt->r.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &lastLeaf); // if none of the leafs were inside the map, the entity is outside the world and can be considered unlinked if (!num_leafs) { return; } // set areas, even from clusters that don't fit in the entity array for (i = 0; i < num_leafs; i++) { area = CM_LeafArea(leafs[i]); if (area != -1) { // doors may legally straggle two areas, but nothing should evern need more than that if (ent->areanum != -1 && ent->areanum != area) { if (ent->areanum2 != -1 && ent->areanum2 != area && sv.state == SS_LOADING) { Com_DPrintf("Object %i touching 3 areas at %f %f %f\n", gEnt->s.number, gEnt->r.absmin[0], gEnt->r.absmin[1], gEnt->r.absmin[2]); } ent->areanum2 = area; } else { ent->areanum = area; } } } // store as many explicit clusters as we can ent->numClusters = 0; for (i = 0; i < num_leafs; i++) { cluster = CM_LeafCluster(leafs[i]); if (cluster != -1) { ent->clusternums[ent->numClusters++] = cluster; if (ent->numClusters == MAX_ENT_CLUSTERS) { break; } } } // store off a last cluster if we need to if (i != num_leafs) { ent->lastCluster = CM_LeafCluster(lastLeaf); } gEnt->r.linkcount++; // find the first world sector node that the ent's box crosses node = sv_worldSectors; while (1) { if (node->axis == -1) { break; } if (gEnt->r.absmin[node->axis] > node->dist) { node = node->children[0]; } else if (gEnt->r.absmax[node->axis] < node->dist) { node = node->children[1]; } else { break; // crosses the node } } // link it in ent->worldSector = node; ent->nextEntityInWorldSector = node->entities; node->entities = ent; gEnt->r.linked = qtrue; }
/* ================= fire_lead This is an internal support routine used for bullet/pellet based weapons. ================= */ static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod) { trace_t tr; vec3_t dir; vec3_t forward, right, up; vec3_t end; float r; float u; vec3_t water_start; qboolean water = false; int content_mask = MASK_SHOT | MASK_WATER; tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); if (!(tr.fraction < 1.0)) { vectoangles (aimdir, dir); AngleVectors (dir, forward, right, up); r = crandom()*hspread; u = crandom()*vspread; VectorMA (start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); if (gi.pointcontents (start) & MASK_WATER) { water = true; VectorCopy (start, water_start); content_mask &= ~MASK_WATER; } tr = gi.trace (start, NULL, NULL, end, self, content_mask); // see if we hit water if (tr.contents & MASK_WATER) { int color; water = true; VectorCopy (tr.endpos, water_start); if (!VectorCompare (start, tr.endpos)) { if (tr.contents & CONTENTS_WATER) { if (strcmp(tr.surface->name, "*brwater") == 0) color = SPLASH_BROWN_WATER; else color = SPLASH_BLUE_WATER; } else if (tr.contents & CONTENTS_SLIME) color = SPLASH_SLIME; else if (tr.contents & CONTENTS_LAVA) color = SPLASH_LAVA; else color = SPLASH_UNKNOWN; if (color != SPLASH_UNKNOWN) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_SPLASH); gi.WriteByte (8); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.WriteByte (color); gi.multicast (tr.endpos, MULTICAST_PVS); } // change bullet's course when it enters water VectorSubtract (end, start, dir); vectoangles (dir, dir); AngleVectors (dir, forward, right, up); r = crandom()*hspread*2; u = crandom()*vspread*2; VectorMA (water_start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); } // re-trace ignoring water this time tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT); } } // send gun puff / flash if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) { if (tr.fraction < 1.0) { if (tr.ent->takedamage) { T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod); } else { if (strncmp (tr.surface->name, "sky", 3) != 0) { gi.WriteByte (svc_temp_entity); gi.WriteByte (te_impact); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.multicast (tr.endpos, MULTICAST_PVS); if (self->client) PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } } } } // if went through water, determine where the end and make a bubble trail if (water) { vec3_t pos; VectorSubtract (tr.endpos, water_start, dir); VectorNormalize (dir); VectorMA (tr.endpos, -2, dir, pos); if (gi.pointcontents (pos) & MASK_WATER) VectorCopy (pos, tr.endpos); else tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); VectorAdd (water_start, tr.endpos, pos); VectorScale (pos, 0.5, pos); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BUBBLETRAIL); gi.WritePosition (water_start); gi.WritePosition (tr.endpos); gi.multicast (pos, MULTICAST_PVS); } }
/* ======================== func_explosive_objective ======================== */ void func_explosive_objective_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { vec3_t origin; vec3_t chunkorigin; vec3_t size; int count; int mass; int enemy; int otherteam; //gi.dprintf(DEVELOPER_MSG_GAME, "self: %s\ninflictor: %s\n attacker: %s\n", // self->classname, inflictor->classname, attacker->classname); if (!attacker->client || !attacker->client->resp.mos) return; // bmodel origins are (0 0 0), we need to adjust that here VectorScale (self->size, 0.5, size); VectorAdd (self->absmin, size, origin); VectorCopy (origin, self->s.origin); self->takedamage = DAMAGE_NO; if (self->dmg) T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE); VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity); VectorNormalize (self->velocity); VectorScale (self->velocity, 150, self->velocity); // start chunks towards the center VectorScale (size, 0.5, size); mass = self->mass; if (!mass) mass = 75; // big chunks if (mass >= 100) { count = mass / 100; if (count > 8) count = 8; while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin); } } // small chunks count = mass / 25; if (count > 16) count = 16; while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin); } G_UseTargets (self, attacker); // hack for 2 team games if (self->obj_owner != 99) { team_list[self->obj_owner]->score -= self->obj_loss; enemy = (self->obj_owner) ? 0 : 1; } else enemy = 99; if (self->obj_owner != attacker->client->resp.team_on->index) team_list[attacker->client->resp.team_on->index]->score += self->obj_gain; else if (self->obj_owner == attacker->client->resp.team_on->index && enemy != 99) team_list[enemy]->score += self->obj_gain; if (dedicated->value) safe_cprintf(NULL, PRINT_HIGH, "%s destroyed by %s [%s]\n", self->obj_name, attacker->client->pers.netname, team_list[attacker->client->resp.team_on->index]->teamname); centerprintall("%s destroyed by:\n\n%s\n%s", self->obj_name, attacker->client->pers.netname, team_list[attacker->client->resp.team_on->index]->teamname); otherteam = (self->obj_owner+1)%2; if ((!team_list[otherteam]->kills_and_points && team_list[otherteam]->score < team_list[otherteam]->need_points) || (team_list[otherteam]->kills_and_points && team_list[otherteam]->kills < team_list[otherteam]->need_kills)) gi.sound(self, CHAN_NO_PHS_ADD, gi.soundindex(va("%s/objectives/touch_cap.wav", team_list[otherteam]->teamid)), 1, 0, 0); // gi.dprintf(DEVELOPER_MSG_GAME, "pts:%i ndpts:%i kills:%i ndkills:%i\n",team_list[(self->obj_owner+1)%2]->score,team_list[(self->obj_owner+1)%2]->need_points, //team_list[(self->obj_owner+1)%2]->kills,team_list[(self->obj_owner+1)%2]->need_kills); if (self->deathtarget) { self->target = self->deathtarget; if (self->target) G_UseTargets (self, attacker); } if (self->dmg) BecomeExplosion1 (self); else G_FreeEdict (self); }
/* ================= fire_bfg ================= */ void bfg_explode (edict_t *self) { edict_t *ent; float points; vec3_t v; float dist; if (!self) { return; } if (self->s.frame == 0) { /* the BFG effect */ ent = NULL; while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL) { if (!ent->takedamage) { continue; } if (ent == self->owner) { continue; } if (!CanDamage(ent, self)) { continue; } if (!CanDamage(ent, self->owner)) { continue; } VectorAdd(ent->mins, ent->maxs, v); VectorMA(ent->s.origin, 0.5, v, v); VectorSubtract(self->s.origin, v, v); dist = VectorLength(v); points = self->radius_dmg * (1.0 - sqrt(dist / self->dmg_radius)); if (ent == self->owner) { points = points * 0.5; } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_BFG_EXPLOSION); gi.WritePosition(ent->s.origin); gi.multicast(ent->s.origin, MULTICAST_PHS); T_Damage(ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT); } } self->nextthink = level.time + FRAMETIME; self->s.frame++; if (self->s.frame == 5) { self->think = G_FreeEdict; } }
/* ================= fire_grenade ================= */ void Grenade_Explode (edict_t *ent) { vec3_t origin; int mod; if (!ent) { return; } if (ent->owner->client) { PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); } if (ent->enemy) { float points; vec3_t v; vec3_t dir; VectorAdd(ent->enemy->mins, ent->enemy->maxs, v); VectorMA(ent->enemy->s.origin, 0.5, v, v); VectorSubtract(ent->s.origin, v, v); points = ent->dmg - 0.5 * VectorLength(v); VectorSubtract(ent->enemy->s.origin, ent->s.origin, dir); if (ent->spawnflags & 1) { mod = MOD_HANDGRENADE; } else { mod = MOD_GRENADE; } T_Damage(ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); } if (ent->spawnflags & 2) { mod = MOD_HELD_GRENADE; } else if (ent->spawnflags & 1) { mod = MOD_HG_SPLASH; } else { mod = MOD_G_SPLASH; } T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod); VectorMA(ent->s.origin, -0.02, ent->velocity, origin); gi.WriteByte(svc_temp_entity); if (ent->waterlevel) { if (ent->groundentity) { gi.WriteByte(TE_GRENADE_EXPLOSION_WATER); } else { gi.WriteByte(TE_ROCKET_EXPLOSION_WATER); } } else { if (ent->groundentity) { gi.WriteByte(TE_GRENADE_EXPLOSION); } else { gi.WriteByte(TE_ROCKET_EXPLOSION); } } gi.WritePosition(origin); gi.multicast(ent->s.origin, MULTICAST_PHS); G_FreeEdict(ent); }
// RAFAEL void Trap_Think (edict_t *ent) { edict_t *target = NULL; edict_t *best = NULL; vec3_t vec; int len, i; int oldlen = 8000; vec3_t forward, right, up; if (!ent) { return; } if (ent->timestamp < level.time) { BecomeExplosion1(ent); return; } ent->nextthink = level.time + 0.1; if (!ent->groundentity) { return; } /* ok lets do the blood effect */ if (ent->s.frame > 4) { if (ent->s.frame == 5) { if (ent->wait == 64) { gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/trapdown.wav"), 1, ATTN_IDLE, 0); } ent->wait -= 2; ent->delay += level.time; for (i = 0; i < 3; i++) { best = G_Spawn(); if (strcmp(ent->enemy->classname, "monster_gekk") == 0) { best->s.modelindex = gi.modelindex("models/objects/gekkgib/torso/tris.md2"); best->s.effects |= TE_GREENBLOOD; } else if (ent->mass > 200) { best->s.modelindex = gi.modelindex("models/objects/gibs/chest/tris.md2"); best->s.effects |= TE_BLOOD; } else { best->s.modelindex = gi.modelindex("models/objects/gibs/sm_meat/tris.md2"); best->s.effects |= TE_BLOOD; } AngleVectors(ent->s.angles, forward, right, up); RotatePointAroundVector(vec, up, right, ((360.0 / 3) * i) + ent->delay); VectorMA(vec, ent->wait / 2, vec, vec); VectorAdd(vec, ent->s.origin, vec); VectorAdd(vec, forward, best->s.origin); best->s.origin[2] = ent->s.origin[2] + ent->wait; VectorCopy(ent->s.angles, best->s.angles); best->solid = SOLID_NOT; best->s.effects |= EF_GIB; best->takedamage = DAMAGE_YES; best->movetype = MOVETYPE_TOSS; best->svflags |= SVF_MONSTER; best->deadflag = DEAD_DEAD; VectorClear(best->mins); VectorClear(best->maxs); best->watertype = gi.pointcontents(best->s.origin); if (best->watertype & MASK_WATER) { best->waterlevel = 1; } best->nextthink = level.time + 0.1; best->think = G_FreeEdict; gi.linkentity(best); } if (ent->wait < 19) { ent->s.frame++; } return; } ent->s.frame++; if (ent->s.frame == 8) { ent->nextthink = level.time + 1.0; ent->think = G_FreeEdict; best = G_Spawn(); SP_item_foodcube(best); VectorCopy(ent->s.origin, best->s.origin); best->s.origin[2] += 16; best->velocity[2] = 400; best->count = ent->mass; gi.linkentity(best); return; } return; } ent->s.effects &= ~EF_TRAP; if (ent->s.frame >= 4) { ent->s.effects |= EF_TRAP; VectorClear(ent->mins); VectorClear(ent->maxs); } if (ent->s.frame < 4) { ent->s.frame++; } while ((target = findradius(target, ent->s.origin, 256)) != NULL) { if (target == ent) { continue; } if (!(target->svflags & SVF_MONSTER) && !target->client) { continue; } if (target->health <= 0) { continue; } if (!visible(ent, target)) { continue; } if (!best) { best = target; continue; } VectorSubtract(ent->s.origin, target->s.origin, vec); len = VectorLength(vec); if (len < oldlen) { oldlen = len; best = target; } } /* pull the enemy in */ if (best) { vec3_t forward; if (best->groundentity) { best->s.origin[2] += 1; best->groundentity = NULL; } VectorSubtract(ent->s.origin, best->s.origin, vec); len = VectorLength(vec); if (best->client) { VectorNormalize(vec); VectorMA(best->velocity, 250, vec, best->velocity); } else { best->ideal_yaw = vectoyaw(vec); M_ChangeYaw(best); AngleVectors(best->s.angles, forward, NULL, NULL); VectorScale(forward, 256, best->velocity); } gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/trapsuck.wav"), 1, ATTN_IDLE, 0); if (len < 32) { if (best->mass < 400) { T_Damage(best, ent, ent->owner, vec3_origin, best->s.origin, vec3_origin, 100000, 1, 0, MOD_TRAP); ent->enemy = best; ent->wait = 64; VectorCopy(ent->s.origin, ent->s.old_origin); ent->timestamp = level.time + 30; if (deathmatch->value) { ent->mass = best->mass / 4; } else { ent->mass = best->mass / 10; } /* ok spawn the food cube */ ent->s.frame = 5; } else { BecomeExplosion1(ent); return; } } } }
void SplitPolygon(vec3_t *polygon,int *signs, int vnum, plane_t *plane, vec3_t *inpts, int *innum, vec3_t *outpts, int *outnum) { int out_c = 0; int in_c = 0; vec3_t *ptA = &polygon[vnum-1]; vec3_t *ptB, v, newVert; int sideA = signs[vnum-1]; int sideB; int i; float sect; for (i=0; i<vnum; i++) { ptB = &polygon[i]; sideB = signs[i]; //is b on "right side" if (sideB == 2) { if (sideA == 1) { // compute the intersection point of the line // from point A to point B with the partition // plane. This is a simple ray-plane intersection. VectorSubtract ((*ptB), (*ptA), v); sect = - (DotProduct (plane->normal, (*ptA) )-plane->dist) / DotProduct (plane->normal, v); VectorScale (v,sect,v); //add a new vertex VectorAdd ((*ptA), v, newVert); VectorCopy (newVert, inpts[in_c]); VectorCopy (newVert, outpts[out_c]); out_c++; in_c++; } VectorCopy (polygon[i], outpts[out_c]); out_c++; } //b is on "left" side else if (sideB ==1) { if (sideA == 2) { // compute the intersection point of the line // from point A to point B with the partition // plane. This is a simple ray-plane intersection. VectorSubtract ((*ptB), (*ptA), v); sect = - (DotProduct (plane->normal, (*ptA) )-plane->dist) / DotProduct (plane->normal, v); VectorScale (v,sect,v); //add a new vertex VectorAdd ((*ptA), v, newVert); VectorCopy (newVert, inpts[in_c]); VectorCopy (newVert, outpts[out_c]); out_c++; in_c++; } VectorCopy (polygon[i], inpts[in_c]); in_c++; } //b is almost on plane else { VectorCopy (polygon[i], inpts[in_c]); VectorCopy (inpts[in_c], outpts[out_c]); in_c++; out_c++; } ptA = ptB; sideA = sideB; if ((out_c > MAX_POLY_VERT) || (in_c > MAX_POLY_VERT)) { Con_Printf ("MAX_POLY_VERT exceeded: %i %i\n", in_c, out_c); //just return what we've got (*innum) = in_c; (*outnum) = out_c; return; } } (*innum) = in_c; (*outnum) = out_c; }
void FanFaceSurface( mapDrawSurface_t *ds ){ int i, j, k, a, b, c, color[ MAX_LIGHTMAPS ][ 4 ]; bspDrawVert_t *verts, *centroid, *dv; double iv; /* try to early out */ if ( !ds->numVerts || ( ds->type != SURFACE_FACE && ds->type != SURFACE_DECAL ) ) { return; } /* add a new vertex at the beginning of the surface */ verts = safe_malloc( ( ds->numVerts + 1 ) * sizeof( bspDrawVert_t ) ); memset( verts, 0, sizeof( bspDrawVert_t ) ); memcpy( &verts[ 1 ], ds->verts, ds->numVerts * sizeof( bspDrawVert_t ) ); free( ds->verts ); ds->verts = verts; /* add up the drawverts to create a centroid */ centroid = &verts[ 0 ]; memset( color, 0, 4 * MAX_LIGHTMAPS * sizeof( int ) ); for ( i = 1, dv = &verts[ 1 ]; i < ( ds->numVerts + 1 ); i++, dv++ ) { VectorAdd( centroid->xyz, dv->xyz, centroid->xyz ); VectorAdd( centroid->normal, dv->normal, centroid->normal ); for ( j = 0; j < 4; j++ ) { for ( k = 0; k < MAX_LIGHTMAPS; k++ ) color[ k ][ j ] += dv->color[ k ][ j ]; if ( j < 2 ) { centroid->st[ j ] += dv->st[ j ]; for ( k = 0; k < MAX_LIGHTMAPS; k++ ) centroid->lightmap[ k ][ j ] += dv->lightmap[ k ][ j ]; } } } /* average the centroid */ iv = 1.0f / ds->numVerts; VectorScale( centroid->xyz, iv, centroid->xyz ); if ( VectorNormalize( centroid->normal, centroid->normal ) <= 0 ) { VectorCopy( verts[ 1 ].normal, centroid->normal ); } for ( j = 0; j < 4; j++ ) { for ( k = 0; k < MAX_LIGHTMAPS; k++ ) { color[ k ][ j ] /= ds->numVerts; centroid->color[ k ][ j ] = ( color[ k ][ j ] < 255.0f ? color[ k ][ j ] : 255 ); } if ( j < 2 ) { centroid->st[ j ] *= iv; for ( k = 0; k < MAX_LIGHTMAPS; k++ ) centroid->lightmap[ k ][ j ] *= iv; } } /* add to vert count */ ds->numVerts++; /* fill indexes in triangle fan order */ ds->numIndexes = 0; ds->indexes = safe_malloc( ds->numVerts * 3 * sizeof( int ) ); for ( i = 1; i < ds->numVerts; i++ ) { a = 0; b = i; c = ( i + 1 ) % ds->numVerts; c = c ? c : 1; ds->indexes[ ds->numIndexes++ ] = a; ds->indexes[ ds->numIndexes++ ] = b; ds->indexes[ ds->numIndexes++ ] = c; } /* add to count */ numFanSurfaces++; /* classify it */ ClassifySurfaces( 1, ds ); }
/* =============== SV_CalcViewOffset Auto pitching on slopes? fall from 128: 400 = 160000 fall from 256: 580 = 336400 fall from 384: 720 = 518400 fall from 512: 800 = 640000 fall from 640: 960 = damage = deltavelocity*deltavelocity * 0.0001 =============== */ void SV_CalcViewOffset (edict_t *ent) { float *angles; float bob; float ratio; float delta; vec3_t v; //=================================== // base angles angles = ent->client->ps.kick_angles; // if dead, fix the angle and don't add any kick if (ent->deadflag) { VectorClear (angles); if(ent->flags & FL_SAM_RAIMI) { ent->client->ps.viewangles[ROLL] = 0; ent->client->ps.viewangles[PITCH] = 0; } else { ent->client->ps.viewangles[ROLL] = 40; ent->client->ps.viewangles[PITCH] = -15; } ent->client->ps.viewangles[YAW] = ent->client->killer_yaw; } else { // add angles based on weapon kick VectorCopy (ent->client->kick_angles, angles); // add angles based on damage kick ratio = (ent->client->v_dmg_time - level.time) / DAMAGE_TIME; if (ratio < 0) { ratio = 0; ent->client->v_dmg_pitch = 0; ent->client->v_dmg_roll = 0; } angles[PITCH] += ratio * ent->client->v_dmg_pitch; angles[ROLL] += ratio * ent->client->v_dmg_roll; // add pitch based on fall kick ratio = (ent->client->fall_time - level.time) / FALL_TIME; if (ratio < 0) ratio = 0; angles[PITCH] += ratio * ent->client->fall_value; // add angles based on velocity delta = DotProduct (ent->velocity, forward); angles[PITCH] += delta*run_pitch->value; delta = DotProduct (ent->velocity, right); angles[ROLL] += delta*run_roll->value; // add angles based on bob delta = bobfracsin * bob_pitch->value * xyspeed; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) delta *= 6; // crouching angles[PITCH] += delta; delta = bobfracsin * bob_roll->value * xyspeed; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) delta *= 6; // crouching if (bobcycle & 1) delta = -delta; angles[ROLL] += delta; } //=================================== // base origin VectorClear (v); // add view height v[2] += ent->viewheight; // add fall height ratio = (ent->client->fall_time - level.time) / FALL_TIME; if (ratio < 0) ratio = 0; v[2] -= ratio * ent->client->fall_value * 0.4; // add bob height bob = bobfracsin * xyspeed * bob_up->value; if (bob > 6) bob = 6; //gi.DebugGraph (bob *2, 255); v[2] += bob; // add kick offset VectorAdd (v, ent->client->kick_origin, v); // absolutely bound offsets // so the view can never be outside the player box if (v[0] < -14) v[0] = -14; else if (v[0] > 14) v[0] = 14; if (v[1] < -14) v[1] = -14; else if (v[1] > 14) v[1] = 14; if (v[2] < -22) v[2] = -22; else if (v[2] > 30) v[2] = 30; VectorCopy (v, ent->client->ps.viewoffset); }
/* ================= R_DrawAliasModel ================= */ void R_DrawAliasModel (entity_t *e) { int i; int lnum; vec3_t dist; float add; model_t *clmodel; vec3_t mins, maxs; aliashdr_t *paliashdr; float an; int anim; clmodel = currententity->model; VectorAdd (currententity->origin, clmodel->mins, mins); VectorAdd (currententity->origin, clmodel->maxs, maxs); if (R_CullBox (mins, maxs)) return; VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); // // get lighting information // ambientlight = shadelight = R_LightPoint (currententity->origin); // allways give the gun some light if (e == &cl.viewent && ambientlight < 24) ambientlight = shadelight = 24; for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++) { if (cl_dlights[lnum].die >= cl.time) { VectorSubtract (currententity->origin, cl_dlights[lnum].origin, dist); add = cl_dlights[lnum].radius - Length(dist); if (add > 0) { ambientlight += add; //ZOID models should be affected by dlights as well shadelight += add; } } } // clamp lighting so it doesn't overbright as much if (ambientlight > 128) ambientlight = 128; if (ambientlight + shadelight > 192) shadelight = 192 - ambientlight; // ZOID: never allow players to go totally black i = currententity - cl_entities; if (i >= 1 && i<=cl.maxclients /* && !strcmp (currententity->model->name, "progs/player.mdl") */) if (ambientlight < 8) ambientlight = shadelight = 8; // HACK HACK HACK -- no fullbright colors, so make torches full light if (!Q_strcmp (clmodel->name, "progs/flame2.mdl") || !Q_strcmp (clmodel->name, "progs/flame.mdl") ) ambientlight = shadelight = 256; shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; shadelight = shadelight / 200.0; an = e->angles[1]/180*M_PI; shadevector[0] = cos(-an); shadevector[1] = sin(-an); shadevector[2] = 1; VectorNormalize (shadevector); // // locate the proper data // paliashdr = (aliashdr_t *)Mod_Extradata (currententity->model); c_alias_polys += paliashdr->numtris; // // draw all the triangles // GL_DisableMultitexture(); glPushMatrix (); R_RotateForEntity (e); if (!Q_strcmp (clmodel->name, "progs/eyes.mdl") && gl_doubleeyes.value) { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] - (22 + 8)); // double size of eyes, since they are really hard to see in gl glScalef (paliashdr->scale[0]*2, paliashdr->scale[1]*2, paliashdr->scale[2]*2); } else { glTranslatef (paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2]); glScalef (paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2]); } anim = (int)(cl.time*10) & 3; GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]); // we can't dynamically colormap textures, so they are cached // seperately for the players. Heads are just uncolored. if (currententity->colormap != vid.colormap && !gl_nocolors.value) { i = currententity - cl_entities; if (i >= 1 && i<=cl.maxclients /* && !Q_strcmp (currententity->model->name, "progs/player.mdl") */) GL_Bind(playertextures - 1 + i); } if (gl_smoothmodels.value) glShadeModel (GL_SMOOTH); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); R_SetupAliasFrame (currententity->frame, paliashdr); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glShadeModel (GL_FLAT); if (gl_affinemodels.value) glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glPopMatrix (); if (r_shadows.value) { glPushMatrix (); R_RotateForEntity (e); glDisable (GL_TEXTURE_2D); glEnable (GL_BLEND); glColor4f (0,0,0,0.5); GL_DrawAliasShadow (paliashdr, lastposenum); glEnable (GL_TEXTURE_2D); glDisable (GL_BLEND); glColor4f (1,1,1,1); glPopMatrix (); } }
void AAS_AddTeleporterPortals(void) { int j, area2num, facenum, otherareanum; char *target, *targetname, *classname; bsp_entity_t *entities, *ent, *dest; vec3_t origin, destorigin, mins, maxs, end; vec3_t bbmins, bbmaxs; aas_area_t *area; aas_face_t *face; aas_trace_t trace; aas_link_t *areas, *link; entities = AAS_ParseBSPEntities(); for (ent = entities; ent; ent = ent->next) { classname = AAS_ValueForBSPEpairKey(ent, "classname"); if (classname && !strcmp(classname, "misc_teleporter")) { if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) { botimport.Print(PRT_ERROR, "teleporter (%s) without origin\n", target); continue; } //end if // target = AAS_ValueForBSPEpairKey(ent, "target"); if (!target) { botimport.Print(PRT_ERROR, "teleporter (%s) without target\n", target); continue; } //end if for (dest = entities; dest; dest = dest->next) { classname = AAS_ValueForBSPEpairKey(dest, "classname"); if (classname && !strcmp(classname, "misc_teleporter_dest")) { targetname = AAS_ValueForBSPEpairKey(dest, "targetname"); if (targetname && !strcmp(targetname, target)) { break; } //end if } //end if } //end for if (!dest) { botimport.Print(PRT_ERROR, "teleporter without destination (%s)\n", target); continue; } //end if if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin)) { botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target); continue; } //end if destorigin[2] += 24; //just for q2e1m2, the dork has put the telepads in the ground VectorCopy(destorigin, end); end[2] -= 100; trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1); if (trace.startsolid) { botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target); continue; } //end if VectorCopy(trace.endpos, destorigin); area2num = AAS_PointAreaNum(destorigin); //reset all cluster fields for (j = 0; j < aasworld.numareas; j++) { aasworld.areasettings[j].cluster = 0; } //end for // VectorSet(mins, -8, -8, 8); VectorSet(maxs, 8, 8, 24); // AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs); // VectorAdd(origin, mins, mins); VectorAdd(origin, maxs, maxs); //add bounding box size VectorSubtract(mins, bbmaxs, mins); VectorSubtract(maxs, bbmins, maxs); //link an invalid (-1) entity areas = AAS_AASLinkEntity(mins, maxs, -1); // for (link = areas; link; link = link->next_area) { if (!AAS_AreaGrounded(link->areanum)) continue; //add the teleporter portal mark aasworld.areasettings[link->areanum].contents |= AREACONTENTS_CLUSTERPORTAL | AREACONTENTS_TELEPORTAL; } //end for // for (link = areas; link; link = link->next_area) { if (!AAS_AreaGrounded(link->areanum)) continue; //find a non-portal area adjacent to the portal area and flood //the cluster from there area = &aasworld.areas[link->areanum]; for (j = 0; j < area->numfaces; j++) { facenum = abs(aasworld.faceindex[area->firstface + j]); face = &aasworld.faces[facenum]; // if (face->frontarea != link->areanum) otherareanum = face->frontarea; else otherareanum = face->backarea; // if (!otherareanum) continue; // if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) { continue; } //end if // AAS_FloodCluster_r(otherareanum, 1); } //end for } //end for //if the teleport destination IS in the same cluster if (aasworld.areasettings[area2num].cluster) { for (link = areas; link; link = link->next_area) { if (!AAS_AreaGrounded(link->areanum)) continue; //add the teleporter portal mark aasworld.areasettings[link->areanum].contents &= ~(AREACONTENTS_CLUSTERPORTAL | AREACONTENTS_TELEPORTAL); } //end for } //end if } //end if } //end for AAS_FreeBSPEntities(entities); } //end of the function AAS_AddTeleporterPortals
qboolean isEntVisible(entityState_t *ent) { trace_t tr; vec3_t start, end, temp; vec3_t forward, up, right, right2; float view_height; VectorCopy(cl.cgameClientLerpOrigin, start); start[2] += (cl.snap.ps.viewheight - 1); if (cl.snap.ps.leanf != 0) { vec3_t lright, v3ViewAngles; VectorCopy(cl.snap.ps.viewangles, v3ViewAngles); v3ViewAngles[2] += cl.snap.ps.leanf / 2.0f; AngleVectors(v3ViewAngles, NULL, lright, NULL); VectorMA(start, cl.snap.ps.leanf, lright, start); } VectorCopy(ent->pos.trBase, end); // Compute vector perpindicular to view to ent VectorSubtract(end, start, forward); VectorNormalizeFast(forward); VectorSet(up, 0, 0, 1); CrossProduct(forward, up, right); VectorNormalizeFast(right); VectorScale(right, 10, right2); VectorScale(right, 18, right); // Set viewheight if (ent->animMovetype) { view_height = 16; } else { view_height = 40; } // First, viewpoint to viewpoint end[2] += view_height; CM_BoxTrace(&tr, start, end, NULL, NULL, 0, CONTENTS_SOLID, qfalse); if (tr.fraction == 1.f) { return qtrue; } // First-b, viewpoint to top of head end[2] += 16; CM_BoxTrace(&tr, start, end, NULL, NULL, 0, CONTENTS_SOLID, qfalse); if (tr.fraction == 1.f) { return qtrue; } end[2] -= 16; // Second, viewpoint to ent's origin end[2] -= view_height; CM_BoxTrace(&tr, start, end, NULL, NULL, 0, CONTENTS_SOLID, qfalse); if (tr.fraction == 1.f) { return qtrue; } // Third, to ent's right knee VectorAdd(end, right, temp); temp[2] += 8; CM_BoxTrace(&tr, start, temp, NULL, NULL, 0, CONTENTS_SOLID, qfalse); if (tr.fraction == 1.f) { return qtrue; } // Fourth, to ent's right shoulder VectorAdd(end, right2, temp); if (ent->animMovetype) { temp[2] += 28; } else { temp[2] += 52; } CM_BoxTrace(&tr, start, temp, NULL, NULL, 0, CONTENTS_SOLID, qfalse); if (tr.fraction == 1.f) { return qtrue; } // Fifth, to ent's left knee VectorScale(right, -1, right); VectorScale(right2, -1, right2); VectorAdd(end, right2, temp); temp[2] += 2; CM_BoxTrace(&tr, start, temp, NULL, NULL, 0, CONTENTS_SOLID, qfalse); if (tr.fraction == 1.f) { return qtrue; } // Sixth, to ent's left shoulder VectorAdd(end, right, temp); if (ent->animMovetype) { temp[2] += 16; } else { temp[2] += 36; } CM_BoxTrace(&tr, start, temp, NULL, NULL, 0, CONTENTS_SOLID, qfalse); if (tr.fraction == 1.f) { return qtrue; } return qfalse; }
/* ================= MakeMeshNormals Handles all the complicated wrapping and degenerate cases ================= */ static void MakeMeshNormals( int width, int height, drawVert_t ctrl[ MAX_GRID_SIZE ][ MAX_GRID_SIZE ] ) { int i, j, k, dist; vec3_t normal; vec3_t sum; int count; vec3_t base; vec3_t delta; int x, y; drawVert_t *dv; vec3_t around[ 8 ], temp; qboolean good[ 8 ]; qboolean wrapWidth, wrapHeight; float len; static int neighbors[ 8 ][ 2 ] = { { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 }, { -1, 0 }, { -1, 1 } }; wrapWidth = qfalse; for ( i = 0; i < height; i++ ) { VectorSubtract( ctrl[ i ][ 0 ].xyz, ctrl[ i ][ width - 1 ].xyz, delta ); len = VectorLengthSquared( delta ); if ( len > 1.0 ) { break; } } if ( i == height ) { wrapWidth = qtrue; } wrapHeight = qfalse; for ( i = 0; i < width; i++ ) { VectorSubtract( ctrl[ 0 ][ i ].xyz, ctrl[ height - 1 ][ i ].xyz, delta ); len = VectorLengthSquared( delta ); if ( len > 1.0 ) { break; } } if ( i == width ) { wrapHeight = qtrue; } for ( i = 0; i < width; i++ ) { for ( j = 0; j < height; j++ ) { count = 0; dv = &ctrl[ j ][ i ]; VectorCopy( dv->xyz, base ); for ( k = 0; k < 8; k++ ) { VectorClear( around[ k ] ); good[ k ] = qfalse; for ( dist = 1; dist <= 3; dist++ ) { x = i + neighbors[ k ][ 0 ] * dist; y = j + neighbors[ k ][ 1 ] * dist; if ( wrapWidth ) { if ( x < 0 ) { x = width - 1 + x; } else if ( x >= width ) { x = 1 + x - width; } } if ( wrapHeight ) { if ( y < 0 ) { y = height - 1 + y; } else if ( y >= height ) { y = 1 + y - height; } } if ( x < 0 || x >= width || y < 0 || y >= height ) { break; // edge of patch } VectorSubtract( ctrl[ y ][ x ].xyz, base, temp ); if ( VectorNormalize2( temp, temp ) == 0 ) { continue; // degenerate edge, get more dist } else { good[ k ] = qtrue; VectorCopy( temp, around[ k ] ); break; // good edge } } } VectorClear( sum ); for ( k = 0; k < 8; k++ ) { if ( !good[ k ] || !good[( k + 1 ) & 7 ] ) { continue; // didn't get two points } CrossProduct( around[( k + 1 ) & 7 ], around[ k ], normal ); if ( VectorNormalize2( normal, normal ) == 0 ) { continue; } VectorAdd( normal, sum, sum ); count++; } if ( count == 0 ) { //printf("bad normal\n"); count = 1; } VectorNormalize2( sum, dv->normal ); } } }
/* ============== RB_SurfaceBeam ============== */ static void RB_SurfaceBeam( void ) { #define NUM_BEAM_SEGS 6 refEntity_t *e; shaderProgram_t *sp = &tr.textureColorShader; int i; vec3_t perpvec; vec3_t direction, normalized_direction; vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; vec3_t oldorigin, origin; e = &backEnd.currentEntity->e; oldorigin[0] = e->oldorigin[0]; oldorigin[1] = e->oldorigin[1]; oldorigin[2] = e->oldorigin[2]; origin[0] = e->origin[0]; origin[1] = e->origin[1]; origin[2] = e->origin[2]; normalized_direction[0] = direction[0] = oldorigin[0] - origin[0]; normalized_direction[1] = direction[1] = oldorigin[1] - origin[1]; normalized_direction[2] = direction[2] = oldorigin[2] - origin[2]; if ( VectorNormalize( normalized_direction ) == 0 ) return; PerpendicularVector( perpvec, normalized_direction ); VectorScale( perpvec, 4, perpvec ); for ( i = 0; i < NUM_BEAM_SEGS ; i++ ) { RotatePointAroundVector( start_points[i], normalized_direction, perpvec, (360.0/NUM_BEAM_SEGS)*i ); // VectorAdd( start_points[i], origin, start_points[i] ); VectorAdd( start_points[i], direction, end_points[i] ); } GL_BindToTMU( tr.whiteImage, TB_COLORMAP ); GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); // FIXME: Quake3 doesn't use this, so I never tested it tess.numVertexes = 0; tess.numIndexes = 0; tess.firstIndex = 0; tess.minIndex = 0; tess.maxIndex = 0; for ( i = 0; i <= NUM_BEAM_SEGS; i++ ) { VectorCopy(start_points[ i % NUM_BEAM_SEGS ], tess.xyz[tess.numVertexes++]); VectorCopy(end_points [ i % NUM_BEAM_SEGS ], tess.xyz[tess.numVertexes++]); } for ( i = 0; i < NUM_BEAM_SEGS; i++ ) { tess.indexes[tess.numIndexes++] = i * 2; tess.indexes[tess.numIndexes++] = (i + 1) * 2; tess.indexes[tess.numIndexes++] = 1 + i * 2; tess.indexes[tess.numIndexes++] = 1 + i * 2; tess.indexes[tess.numIndexes++] = (i + 1) * 2; tess.indexes[tess.numIndexes++] = 1 + (i + 1) * 2; } tess.minIndex = 0; tess.maxIndex = tess.numVertexes; // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function RB_UpdateTessVao(ATTR_POSITION); GLSL_BindProgram(sp); GLSL_SetUniformMat4(sp, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); GLSL_SetUniformVec4(sp, UNIFORM_COLOR, colorRed); R_DrawElementsVao(tess.numIndexes, tess.firstIndex, tess.minIndex, tess.maxIndex); tess.numIndexes = 0; tess.numVertexes = 0; tess.firstIndex = 0; tess.minIndex = 0; tess.maxIndex = 0; }
//=========================================================================== // predicts the movement // assumes regular bounding box sizes // NOTE: out of water jumping is not included // NOTE: grappling hook is not included // // Parameter: origin : origin to start with // presencetype : presence type to start with // velocity : velocity to start with // cmdmove : client command movement // cmdframes : number of frame cmdmove is valid // maxframes : maximum number of predicted frames // frametime : duration of one predicted frame // stopevent : events that stop the prediction // stopareanum : stop as soon as entered this area // Returns: aas_clientmove_t // Changes Globals: - //=========================================================================== int AAS_ClientMovementPrediction(struct aas_clientmove_s *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, int stopevent, int stopareanum, vec3_t mins, vec3_t maxs, int visualize) { float phys_friction, phys_stopspeed, phys_gravity, phys_waterfriction; float phys_watergravity; float phys_walkaccelerate, phys_airaccelerate, phys_swimaccelerate; float phys_maxwalkvelocity, phys_maxcrouchvelocity, phys_maxswimvelocity; float phys_maxstep, phys_maxsteepness, phys_jumpvel, friction; float gravity, delta, maxvel, wishspeed, accelerate; //float velchange, newvel; int n, i, j, pc, step, swimming, crouch, event, jump_frame, areanum; int areas[20], numareas; vec3_t points[20]; vec3_t org, end, feet, start, stepend, lastorg, wishdir; vec3_t frame_test_vel, old_frame_test_vel, left_test_vel; vec3_t up = {0, 0, 1}; aas_plane_t *plane, *plane2; aas_trace_t trace, steptrace; if (frametime <= 0) frametime = 0.1f; // phys_friction = aassettings.phys_friction; phys_stopspeed = aassettings.phys_stopspeed; phys_gravity = aassettings.phys_gravity; phys_waterfriction = aassettings.phys_waterfriction; phys_watergravity = aassettings.phys_watergravity; phys_maxwalkvelocity = aassettings.phys_maxwalkvelocity;// * frametime; phys_maxcrouchvelocity = aassettings.phys_maxcrouchvelocity;// * frametime; phys_maxswimvelocity = aassettings.phys_maxswimvelocity;// * frametime; phys_walkaccelerate = aassettings.phys_walkaccelerate; phys_airaccelerate = aassettings.phys_airaccelerate; phys_swimaccelerate = aassettings.phys_swimaccelerate; phys_maxstep = aassettings.phys_maxstep; phys_maxsteepness = aassettings.phys_maxsteepness; phys_jumpvel = aassettings.phys_jumpvel * frametime; // Com_Memset(move, 0, sizeof(aas_clientmove_t)); Com_Memset(&trace, 0, sizeof(aas_trace_t)); //start at the current origin VectorCopy(origin, org); org[2] += 0.25; //velocity to test for the first frame VectorScale(velocity, frametime, frame_test_vel); // jump_frame = -1; //predict a maximum of 'maxframes' ahead for (n = 0; n < maxframes; n++) { swimming = AAS_Swimming(org); //get gravity depending on swimming or not gravity = swimming ? phys_watergravity : phys_gravity; //apply gravity at the START of the frame frame_test_vel[2] = frame_test_vel[2] - (gravity * 0.1 * frametime); //if on the ground or swimming if (onground || swimming) { friction = swimming ? phys_friction : phys_waterfriction; //apply friction VectorScale(frame_test_vel, 1/frametime, frame_test_vel); AAS_ApplyFriction(frame_test_vel, friction, phys_stopspeed, frametime); VectorScale(frame_test_vel, frametime, frame_test_vel); } //end if crouch = qfalse; //apply command movement if (n < cmdframes) { maxvel = phys_maxwalkvelocity; accelerate = phys_airaccelerate; VectorCopy(cmdmove, wishdir); if (onground) { if (cmdmove[2] < -300) { crouch = qtrue; maxvel = phys_maxcrouchvelocity; } //end if //if not swimming and upmove is positive then jump if (!swimming && cmdmove[2] > 1) { //jump velocity minus the gravity for one frame + 5 for safety frame_test_vel[2] = phys_jumpvel - (gravity * 0.1 * frametime) + 5; jump_frame = n; //jumping so air accelerate accelerate = phys_airaccelerate; } //end if else { accelerate = phys_walkaccelerate; } //end else } //end if if (swimming) { maxvel = phys_maxswimvelocity; accelerate = phys_swimaccelerate; } //end if else { wishdir[2] = 0; } //end else // wishspeed = VectorNormalize(wishdir); if (wishspeed > maxvel) wishspeed = maxvel; VectorScale(frame_test_vel, 1/frametime, frame_test_vel); AAS_Accelerate(frame_test_vel, frametime, wishdir, wishspeed, accelerate); VectorScale(frame_test_vel, frametime, frame_test_vel); } //end if if (crouch) { presencetype = PRESENCE_CROUCH; } //end if else if (presencetype == PRESENCE_CROUCH) { if (AAS_PointPresenceType(org) & PRESENCE_NORMAL) { presencetype = PRESENCE_NORMAL; } //end if } //end else //save the current origin VectorCopy(org, lastorg); //move linear during one frame VectorCopy(frame_test_vel, left_test_vel); j = 0; do { VectorAdd(org, left_test_vel, end); //trace a bounding box trace = AAS_TraceClientBBox(org, end, presencetype, entnum); // //#ifdef AAS_MOVE_DEBUG if (visualize) { if (trace.startsolid) botimport.Print(PRT_MESSAGE, "PredictMovement: start solid\n"); AAS_DebugLine(org, trace.endpos, LINECOLOR_RED); } //end if //#endif //AAS_MOVE_DEBUG // if (stopevent & (SE_ENTERAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_TOUCHCLUSTERPORTAL)) { numareas = AAS_TraceAreas(org, trace.endpos, areas, points, 20); for (i = 0; i < numareas; i++) { if (stopevent & SE_ENTERAREA) { if (areas[i] == stopareanum) { VectorCopy(points[i], move->endpos); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->endarea = areas[i]; move->trace = trace; move->stopevent = SE_ENTERAREA; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if //NOTE: if not the first frame if ((stopevent & SE_TOUCHJUMPPAD) && n) { if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_JUMPPAD) { VectorCopy(points[i], move->endpos); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->endarea = areas[i]; move->trace = trace; move->stopevent = SE_TOUCHJUMPPAD; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if if (stopevent & SE_TOUCHTELEPORTER) { if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_TELEPORTER) { VectorCopy(points[i], move->endpos); move->endarea = areas[i]; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_TOUCHTELEPORTER; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if if (stopevent & SE_TOUCHCLUSTERPORTAL) { if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_CLUSTERPORTAL) { VectorCopy(points[i], move->endpos); move->endarea = areas[i]; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_TOUCHCLUSTERPORTAL; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end for } //end if // if (stopevent & SE_HITBOUNDINGBOX) { if (AAS_ClipToBBox(&trace, org, trace.endpos, presencetype, mins, maxs)) { VectorCopy(trace.endpos, move->endpos); move->endarea = AAS_PointAreaNum(move->endpos); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_HITBOUNDINGBOX; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if //move the entity to the trace end point VectorCopy(trace.endpos, org); //if there was a collision if (trace.fraction < 1.0) { //get the plane the bounding box collided with plane = AAS_PlaneFromNum(trace.planenum); // if (stopevent & SE_HITGROUNDAREA) { if (DotProduct(plane->normal, up) > phys_maxsteepness) { VectorCopy(org, start); start[2] += 0.5; if (AAS_PointAreaNum(start) == stopareanum) { VectorCopy(start, move->endpos); move->endarea = stopareanum; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_HITGROUNDAREA; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end if //assume there's no step step = qfalse; //if it is a vertical plane and the bot didn't jump recently if (plane->normal[2] == 0 && (jump_frame < 0 || n - jump_frame > 2)) { //check for a step VectorMA(org, -0.25, plane->normal, start); VectorCopy(start, stepend); start[2] += phys_maxstep; steptrace = AAS_TraceClientBBox(start, stepend, presencetype, entnum); // if (!steptrace.startsolid) { plane2 = AAS_PlaneFromNum(steptrace.planenum); if (DotProduct(plane2->normal, up) > phys_maxsteepness) { VectorSubtract(end, steptrace.endpos, left_test_vel); left_test_vel[2] = 0; frame_test_vel[2] = 0; //#ifdef AAS_MOVE_DEBUG if (visualize) { if (steptrace.endpos[2] - org[2] > 0.125) { VectorCopy(org, start); start[2] = steptrace.endpos[2]; AAS_DebugLine(org, start, LINECOLOR_BLUE); } //end if } //end if //#endif //AAS_MOVE_DEBUG org[2] = steptrace.endpos[2]; step = qtrue; } //end if } //end if } //end if // if (!step) { //velocity left to test for this frame is the projection //of the current test velocity into the hit plane VectorMA(left_test_vel, -DotProduct(left_test_vel, plane->normal), plane->normal, left_test_vel); //store the old velocity for landing check VectorCopy(frame_test_vel, old_frame_test_vel); //test velocity for the next frame is the projection //of the velocity of the current frame into the hit plane VectorMA(frame_test_vel, -DotProduct(frame_test_vel, plane->normal), plane->normal, frame_test_vel); //check for a landing on an almost horizontal floor if (DotProduct(plane->normal, up) > phys_maxsteepness) { onground = qtrue; } //end if if (stopevent & SE_HITGROUNDDAMAGE) { delta = 0; if (old_frame_test_vel[2] < 0 && frame_test_vel[2] > old_frame_test_vel[2] && !onground) { delta = old_frame_test_vel[2]; } //end if else if (onground) { delta = frame_test_vel[2] - old_frame_test_vel[2]; } //end else if (delta) { delta = delta * 10; delta = delta * delta * 0.0001; if (swimming) delta = 0; // never take falling damage if completely underwater /* if (ent->waterlevel == 3) return; if (ent->waterlevel == 2) delta *= 0.25; if (ent->waterlevel == 1) delta *= 0.5; */ if (delta > 40) { VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorCopy(frame_test_vel, move->velocity); move->trace = trace; move->stopevent = SE_HITGROUNDDAMAGE; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end if } //end if } //end if //extra check to prevent endless loop if (++j > 20) return qfalse; //while there is a plane hit } while(trace.fraction < 1.0); //if going down if (frame_test_vel[2] <= 10) { //check for a liquid at the feet of the bot VectorCopy(org, feet); feet[2] -= 22; pc = AAS_PointContents(feet); //get event from pc event = SE_NONE; if (pc & CONTENTS_LAVA) event |= SE_ENTERLAVA; if (pc & CONTENTS_SLIME) event |= SE_ENTERSLIME; if (pc & CONTENTS_WATER) event |= SE_ENTERWATER; // areanum = AAS_PointAreaNum(org); if (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA) event |= SE_ENTERLAVA; if (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME) event |= SE_ENTERSLIME; if (aasworld.areasettings[areanum].contents & AREACONTENTS_WATER) event |= SE_ENTERWATER; //if in lava or slime if (event & stopevent) { VectorCopy(org, move->endpos); move->endarea = areanum; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->stopevent = event & stopevent; move->presencetype = presencetype; move->endcontents = pc; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if // onground = AAS_OnGround(org, presencetype, entnum); //if onground and on the ground for at least one whole frame if (onground) { if (stopevent & SE_HITGROUND) { VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_HITGROUND; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if else if (stopevent & SE_LEAVEGROUND) { VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_LEAVEGROUND; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end else if else if (stopevent & SE_GAP) { aas_trace_t gaptrace; VectorCopy(org, start); VectorCopy(start, end); end[2] -= 48 + aassettings.phys_maxbarrier; gaptrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); //if solid is found the bot cannot walk any further and will not fall into a gap if (!gaptrace.startsolid) { //if it is a gap (lower than one step height) if (gaptrace.endpos[2] < org[2] - aassettings.phys_maxstep - 1) { if (!(AAS_PointContents(end) & CONTENTS_WATER)) { VectorCopy(lastorg, move->endpos); move->endarea = AAS_PointAreaNum(lastorg); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_GAP; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end if } //end else if } //end for // VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->stopevent = SE_NONE; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; // return qtrue; } //end of the function AAS_ClientMovementPrediction
static void DoRailDiscs( int numSegs, const vec3_t start, const vec3_t dir, const vec3_t right, const vec3_t up ) { int i; vec3_t pos[4]; vec3_t v; int spanWidth = r_railWidth->integer; float c, s; float scale; if ( numSegs > 1 ) numSegs--; if ( !numSegs ) return; scale = 0.25; for ( i = 0; i < 4; i++ ) { c = cos( DEG2RAD( 45 + i * 90 ) ); s = sin( DEG2RAD( 45 + i * 90 ) ); v[0] = ( right[0] * c + up[0] * s ) * scale * spanWidth; v[1] = ( right[1] * c + up[1] * s ) * scale * spanWidth; v[2] = ( right[2] * c + up[2] * s ) * scale * spanWidth; VectorAdd( start, v, pos[i] ); if ( numSegs > 1 ) { // offset by 1 segment if we're doing a long distance shot VectorAdd( pos[i], dir, pos[i] ); } } RB_CheckVao(tess.vao); for ( i = 0; i < numSegs; i++ ) { int j; RB_CHECKOVERFLOW( 4, 6 ); for ( j = 0; j < 4; j++ ) { VectorCopy( pos[j], tess.xyz[tess.numVertexes] ); tess.texCoords[tess.numVertexes][0] = (j < 2); tess.texCoords[tess.numVertexes][1] = (j && j != 3); tess.color[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] * 257; tess.color[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] * 257; tess.color[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] * 257; tess.numVertexes++; VectorAdd( pos[j], dir, pos[j] ); } tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 0; tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 2; } }
qboolean M_CheckBottom (edict_t *ent) { vec3_t mins, maxs, start, stop; trace_t trace; int x, y; float mid, bottom; VectorAdd (ent->s.origin, ent->mins, mins); VectorAdd (ent->s.origin, ent->maxs, maxs); // if all of the points under the corners are solid world, don't bother // with the tougher checks // the corners must be within 16 of the midpoint start[2] = mins[2] - 1; for (x=0 ; x<=1 ; x++) for (y=0 ; y<=1 ; y++) { start[0] = x ? maxs[0] : mins[0]; start[1] = y ? maxs[1] : mins[1]; if (gi.pointcontents (start) != CONTENTS_SOLID) goto realcheck; } c_yes++; return true; // we got out easy realcheck: c_no++; // // check it for real... // start[2] = mins[2]; // the midpoint must be within 16 of the bottom start[0] = stop[0] = (mins[0] + maxs[0])*0.5; start[1] = stop[1] = (mins[1] + maxs[1])*0.5; stop[2] = start[2] - 2*STEPSIZE; trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID); if (trace.fraction == 1.0f) return false; mid = bottom = trace.endpos[2]; // the corners must be within 16 of the midpoint for (x=0 ; x<=1 ; x++) for (y=0 ; y<=1 ; y++) { start[0] = stop[0] = x ? maxs[0] : mins[0]; start[1] = stop[1] = y ? maxs[1] : mins[1]; trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID); if (trace.fraction != 1.0f && trace.endpos[2] > bottom) bottom = trace.endpos[2]; if (trace.fraction == 1.0f || mid - trace.endpos[2] > STEPSIZE) return false; } c_yes++; return true; }
void Think_Arty (edict_t *ent) { vec3_t start; vec3_t forward; vec3_t end; vec3_t targetdir; vec3_t tempvec; trace_t tr; trace_t tr_2; if (!ent->owner || !ent->owner->client || !ent->owner->inuse || !ent->owner->client->resp.team_on || ent->owner->flyingnun) { G_FreeEdict(ent); return; } if (!ent->owner->client->airstrike) { G_FreeEdict(ent); return; } if (ent->owner->client->airstrike && ent->owner->client->airstrike != ent) { G_FreeEdict(ent); return; } // find the target point VectorCopy(ent->owner->s.origin, start); start[2] += ent->owner->viewheight; AngleVectors(ent->owner->client->v_angle, forward, NULL, NULL); VectorMA(start, 8192, forward, end); tr = gi.trace(start, NULL, NULL, end, ent->owner, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA); (void)tr;/*** FIXME: 'tr' IS UNUSED HERE! ***/ // find the direction from the entry point to the target VectorSubtract(ent->owner->client->arty_target, ent->owner->client->arty_entry, targetdir); VectorNormalize(targetdir); VectorAdd(ent->owner->client->arty_entry, targetdir, start); // check we have a clear line of fire tr_2 = gi.trace(start, NULL, NULL, ent->owner->client->arty_target, ent, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA); // check to make sure we're not materializing in a solid if ( gi.pointcontents(start) == CONTENTS_SOLID || tr_2.fraction < 1.0 ) { safe_cprintf(ent->owner, PRINT_HIGH, "Artillery fire was unsuccessful, sir!\n"); return; } // gi.sound(ent, CHAN_AUTO, gi.soundindex(va("%s/arty/hit%i.wav", ent->client->resp.team_on->teamid, ent->client->arty_location)), 1, ATTN_NORM, 0); // if (IsValidPlayer(ent)) // gi.sound(ent->owner, CHAN_AUTO, gi.soundindex(va("%s/arty/hit%i.wav", ent->owner->client->resp.team_on->teamid, 1)), 1, ATTN_NORM, 0); // fire away! /* old arty code fire_rocket(ent, start, targetdir, 700, 250, 300, 450); fire_rocket(ent, start, targetdir, 600, 450, 200, 430); fire_rocket(ent, start, targetdir, 400, 150, 400, 500); fire_rocket(ent, start, targetdir, 600, 210, 250, 500); fire_rocket(ent, start, targetdir, 300, 430, 200, 450); fire_rocket(ent, start, targetdir, 600, 240, 320, 480); */ fire_airstrike(ent->owner, start, targetdir, 700, 250, 300, 450); VectorSet(tempvec, 8, 8, 0); VectorAdd(tempvec, start, tempvec); fire_airstrike(ent->owner, tempvec, targetdir, 600, 450, 200, 430); VectorSet(tempvec, 16, 16, 0); VectorAdd(tempvec, start, tempvec); fire_airstrike(ent->owner, tempvec, targetdir, 400, 150, 400, 500); VectorSet(tempvec, 24, 24, 0); VectorAdd(tempvec, start, tempvec); fire_airstrike(ent->owner, tempvec, targetdir, 600, 210, 250, 500); VectorSet(tempvec, 32, 32, 0); VectorAdd(tempvec, start, tempvec); fire_airstrike(ent->owner, tempvec, targetdir, 300, 430, 200, 450); VectorSet(tempvec, 40, 40, 0); VectorAdd(tempvec, start, tempvec); fire_airstrike(ent->owner, tempvec, targetdir, 600, 240, 320, 480); //fire_shell(ent, start, targetdir, 250, ((rand()%500)+900), 300, 75); safe_cprintf(ent->owner, PRINT_HIGH, "Artillery fire confirmed, sir!\n"); ent->think = G_FreeEdict; ent->nextthink = level.time +.1; ent->owner->client->airstrike = NULL; ent->owner->client->arty_time_restrict = level.time + arty_time->value; // delay for user defined minutes }
void CG_AddTrailToScene( trailJunc_t *trail, int iteration, int numJuncs ) { int k, i, n, l, numOutVerts; polyVert_t mid; float mod[4]; float sInc, s; trailJunc_t *j, *jNext; vec3_t fwd, up, p, v; // clipping vars #define TRAIL_FADE_CLOSE_DIST 64.0 #define TRAIL_FADE_FAR_SCALE 4.0 vec3_t viewProj; float viewDist, fadeAlpha; // add spark shader at head position if (trail->flags & TJFL_SPARKHEADFLARE) { j = trail; VectorCopy( j->pos, p ); VectorMA (p, -j->width*2, vup, p); VectorMA (p, -j->width*2, vright, p); VectorCopy (p, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = (unsigned char)(j->alpha*255.0); VectorCopy( j->pos, p ); VectorMA (p, -j->width*2, vup, p); VectorMA (p, j->width*2, vright, p); VectorCopy (p, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = (unsigned char)(j->alpha*255.0); VectorCopy( j->pos, p ); VectorMA (p, j->width*2, vup, p); VectorMA (p, j->width*2, vright, p); VectorCopy (p, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = (unsigned char)(j->alpha*255.0); VectorCopy( j->pos, p ); VectorMA (p, j->width*2, vup, p); VectorMA (p, -j->width*2, vright, p); VectorCopy (p, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = (unsigned char)(j->alpha*255.0); trap_R_AddPolyToScene( cgs.media.sparkFlareShader, 4, verts ); } // if (trail->flags & TJFL_CROSSOVER && iteration < 1) { // iteration = 1; // } sInc = 0; if (!numJuncs) { // first count the number of juncs in the trail j = trail; numJuncs = 0; sInc = 0; while (j) { numJuncs++; // check for a dead next junc if (!j->inuse && j->nextJunc && !j->nextJunc->inuse) { CG_KillTrail( j ); } else if (j->nextJunc && j->nextJunc->freed) { // not sure how this can happen, but it does, and causes infinite loops j->nextJunc = NULL; } if (j->nextJunc) sInc += VectorDistance( j->nextJunc->pos, j->pos ); j = j->nextJunc; } } if (numJuncs < 2) { return; } s = 0; if (trail->sType == STYPE_STRETCH) { //sInc = ((1.0 - 0.1) / (float)(numJuncs)); // hack, the end of funnel shows a bit of the start (looping) s = 0.05; //s = 0.05; } else if (trail->sType == STYPE_REPEAT) { s = trail->sTex; } // now traverse the list j = trail; jNext = j->nextJunc; i = 0; while (jNext) { // first get the directional vectors to the next junc VectorSubtract( jNext->pos, j->pos, fwd ); GetPerpendicularViewVector( cg.refdef.vieworg, j->pos, jNext->pos, up ); // if it's a crossover, draw it twice if (j->flags & TJFL_CROSSOVER) { if (iteration > 0) { ProjectPointOntoVector( cg.refdef.vieworg, j->pos, jNext->pos, viewProj ); VectorSubtract( cg.refdef.vieworg, viewProj, v ); VectorNormalize( v ); if (iteration == 1) { VectorMA( up, 0.3, v, up ); } else { VectorMA( up, -0.3, v, up ); } VectorNormalize( up ); } } // do fading when moving towards the projection point onto the trail segment vector else if (!(j->flags & TJFL_NOCULL) && (j->widthEnd > 4 || jNext->widthEnd > 4)) { ProjectPointOntoVector( cg.refdef.vieworg, j->pos, jNext->pos, viewProj ); viewDist = Distance( viewProj, cg.refdef.vieworg ); if (viewDist < (TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE)) { if (viewDist < TRAIL_FADE_CLOSE_DIST) { fadeAlpha = 0.0; } else { fadeAlpha = (viewDist - TRAIL_FADE_CLOSE_DIST) / (TRAIL_FADE_CLOSE_DIST * TRAIL_FADE_FAR_SCALE); } if (fadeAlpha < j->alpha) { j->alpha = fadeAlpha; } if (fadeAlpha < jNext->alpha) { jNext->alpha = fadeAlpha; } } } // now output the QUAD for this segment // 1 ---- VectorMA( j->pos, 0.5*j->width, up, p ); VectorCopy( p, verts[i].xyz ); verts[i].st[0] = s; verts[i].st[1] = 1.0; for (k=0; k<3; k++) verts[i].modulate[k] = (unsigned char)(j->color[k]*255.0); verts[i].modulate[3] = (unsigned char)(j->alpha*255.0); // blend this with the previous junc if (j != trail) { VectorAdd( verts[i].xyz, verts[i-1].xyz, verts[i].xyz ); VectorScale( verts[i].xyz, 0.5, verts[i].xyz ); VectorCopy( verts[i].xyz, verts[i-1].xyz ); } else if (j->flags & TJFL_FADEIN) { verts[i].modulate[3] = 0; // fade in } i++; // 2 ---- VectorMA( p, -1*j->width, up, p ); VectorCopy( p, verts[i].xyz ); verts[i].st[0] = s; verts[i].st[1] = 0.0; for (k=0; k<3; k++) verts[i].modulate[k] = (unsigned char)(j->color[k]*255.0); verts[i].modulate[3] = (unsigned char)(j->alpha*255.0); // blend this with the previous junc if (j != trail) { VectorAdd( verts[i].xyz, verts[i-3].xyz, verts[i].xyz ); VectorScale( verts[i].xyz, 0.5, verts[i].xyz ); VectorCopy( verts[i].xyz, verts[i-3].xyz ); } else if (j->flags & TJFL_FADEIN) { verts[i].modulate[3] = 0; // fade in } i++; if (trail->sType == STYPE_REPEAT) s = jNext->sTex; else { //s += sInc; s += VectorDistance( j->pos, jNext->pos ) / sInc; if (s > 1.0) s = 1.0; } // 3 ---- VectorMA( jNext->pos, -0.5*jNext->width, up, p ); VectorCopy( p, verts[i].xyz ); verts[i].st[0] = s; verts[i].st[1] = 0.0; for (k=0; k<3; k++) verts[i].modulate[k] = (unsigned char)(jNext->color[k]*255.0); verts[i].modulate[3] = (unsigned char)(jNext->alpha*255.0); i++; // 4 ---- VectorMA( p, jNext->width, up, p ); VectorCopy( p, verts[i].xyz ); verts[i].st[0] = s; verts[i].st[1] = 1.0; for (k=0; k<3; k++) verts[i].modulate[k] = (unsigned char)(jNext->color[k]*255.0); verts[i].modulate[3] = (unsigned char)(jNext->alpha*255.0); i++; if (i+4 > MAX_TRAIL_VERTS) break; j = jNext; jNext = j->nextJunc; } if (trail->flags & TJFL_FIXDISTORT) { // build the list of outVerts, by dividing up the QUAD's into 4 Tri's each, so as to allow // any shaped (convex) Quad without bilinear distortion for (k=0, numOutVerts=0; k<i; k+=4) { VectorCopy(verts[k].xyz, mid.xyz); mid.st[0] = verts[k].st[0]; mid.st[1] = verts[k].st[1]; for (l=0; l<4; l++) { mod[l] = (float)verts[k].modulate[l]; } for (n=1; n<4; n++) { VectorAdd( verts[k+n].xyz, mid.xyz, mid.xyz ); mid.st[0] += verts[k+n].st[0]; mid.st[1] += verts[k+n].st[1]; for (l=0; l<4; l++) { mod[l] += (float)verts[k+n].modulate[l]; } } VectorScale( mid.xyz, 0.25, mid.xyz ); mid.st[0] *= 0.25; mid.st[1] *= 0.25; for (l=0; l<4; l++) { mid.modulate[l] = (unsigned char)(mod[l]/4.0); } // now output the tri's for (n=0; n<4; n++) { outVerts[numOutVerts++] = verts[k+n]; outVerts[numOutVerts++] = mid; if (n<3) { outVerts[numOutVerts++] = verts[k+n+1]; } else { outVerts[numOutVerts++] = verts[k]; } } } if (!(trail->flags & TJFL_NOPOLYMERGE)) { trap_R_AddPolysToScene( trail->shader, 3, &outVerts[0], numOutVerts/3 ); } else { int k; for (k=0; k<numOutVerts/3; k++) { trap_R_AddPolyToScene( trail->shader, 3, &outVerts[k*3] ); } } } else { // send the polygons // FIXME: is it possible to send a GL_STRIP here? We are actually sending 2x the verts we really need to if (!(trail->flags & TJFL_NOPOLYMERGE)) { trap_R_AddPolysToScene( trail->shader, 4, &verts[0], i/4 ); } else { int k; for (k=0; k<i/4; k++) { trap_R_AddPolyToScene( trail->shader, 4, &verts[k*4] ); } } } // do we need to make another pass? if (trail->flags & TJFL_CROSSOVER) { if (iteration < 2) { CG_AddTrailToScene( trail, iteration + 1, numJuncs ); } } }
/* ============== TDM_GetPlayerIdView ============== Find the best player for the id view and return a configstring index that contains their info. */ int TDM_GetPlayerIdView (edict_t *ent) { edict_t *ignore; edict_t *target; vec3_t forward; trace_t tr; vec3_t start; vec3_t mins = {-4,-4,-4}; vec3_t maxs = {4,4,4}; qboolean ignoreConfigStringUpdate; int i; int powerarmor; qboolean show_health_info; ignoreConfigStringUpdate = false; if (ent->client->chase_target) { target = ent->client->chase_target->client->resp.last_id_client; } else { int tracemask; VectorCopy (ent->s.origin, start); start[2] += ent->viewheight; AngleVectors (ent->client->v_angle, forward, NULL, NULL); VectorScale (forward, 4096, forward); VectorAdd (ent->s.origin, forward, forward); ignore = ent; target = NULL; tracemask = CONTENTS_SOLID|CONTENTS_MONSTER|MASK_WATER; //find best player through tracing for (i = 0; i < 10; i++) { tr = gi.trace (start, mins, maxs, forward, ignore, tracemask); //hit transparent water if (tr.ent == world && tr.surface && (tr.surface->flags & (SURF_TRANS33|SURF_TRANS66))) { tracemask &= ~MASK_WATER; VectorCopy (tr.endpos, start); continue; } if (tr.ent == world || tr.fraction == 1.0f) break; //we hit something that's a player and it's alive and on our team! //note, we trace twice so we hit water planes if (tr.ent && tr.ent->client && tr.ent->health > 0 && visible (tr.ent, ent, CONTENTS_SOLID | MASK_WATER)) { target = tr.ent; break; } else { VectorCopy (tr.endpos, start); ignore = tr.ent; } } //if trace was unsuccessful, try guessing based on angles if (!target) { edict_t *who, *best; vec3_t dir; float distance, bdistance = 0.0f; float bd = 0.0f, d; AngleVectors (ent->client->v_angle, forward, NULL, NULL); best = NULL; for (who = g_edicts + 1; who <= g_edicts + game.maxclients; who++) { if (!who->inuse) continue; if (who->health <= 0) continue; if (who == ent) continue; VectorSubtract (who->s.origin, ent->s.origin, dir); distance = VectorLength (dir); VectorNormalize (dir); d = DotProduct (forward, dir); //note, we trace twice so we hit water planes if (d > bd && visible (ent, who, CONTENTS_SOLID | MASK_WATER) && visible (who, ent, CONTENTS_SOLID | MASK_WATER)) { bdistance = distance; bd = d; best = who; } } if (best) { if (!best->client->pers.team) TDM_Error ("TDM_GetPlayerIdView: Got a spectator via DotProduct with %d health (%s)", best->health, best->client->pers.netname); //allow variable slop based on proximity if ((bdistance < 150 && bd > 0.50f) || (bdistance < 250 && bd > 0.90f) || (bdistance < 600 && bd > 0.96f) || bd > 0.98f) target = best; } } else if (!target->client->pers.team) TDM_Error ("TDM_GetPlayerIdView: Got a spectator via trace with %d health (%s)", target->health, target->client->pers.netname); } if (!target) return 0; show_health_info = false; if (ent->client->pers.team == target->client->pers.team) { //same team, show health info show_health_info = true; } else if (ent->client->pers.team == TEAM_SPEC) { /*if (!ent->client->chase_target) { //free floating camera, DON'T show health info show_health_info = false; } else */ if (ent->client->chase_target && ent->client->chase_target->client->pers.team == target->client->pers.team) { //viewing someone on the same team as our chase target, show health info show_health_info = true; } } //check for power armor if (target->client->inventory[ITEM_ITEM_POWER_SCREEN] > 0 || target->client->inventory[ITEM_ITEM_POWER_SHIELD] > 0) powerarmor = target->client->inventory[ITEM_AMMO_CELLS]; else powerarmor = -1; //don't spam configstring if they haven't changed since last time if (ent->client->resp.last_id_client == target && ent->client->resp.last_id_health == target->health && ent->client->resp.last_id_armor == TDM_GetArmorValue (target) && ent->client->resp.last_id_powerarmor == powerarmor) ignoreConfigStringUpdate = true; ent->client->resp.last_id_client = target; ent->client->resp.last_id_health = target->health; ent->client->resp.last_id_armor = TDM_GetArmorValue (target); ent->client->resp.last_id_powerarmor = powerarmor; if (!ignoreConfigStringUpdate) { char *string; if (show_health_info) { char buff[16]; //show power armor if they have it if (powerarmor != -1) sprintf (buff, " P:%d", powerarmor); else buff[0] = '\0'; if (ent->client->pers.config.id_highlight) string = TDM_SetColorText (va ("%16s H:%d A:%d%s", target->client->pers.netname, target->health, TDM_GetArmorValue (target), buff)); else string = va ("%16s H:%d A:%d%s", target->client->pers.netname, target->health, TDM_GetArmorValue (target), buff); } else { if (ent->client->pers.config.id_highlight) string = TDM_SetColorText (va ("%16s", target->client->pers.netname)); else string = va ("%16s", target->client->pers.netname); } gi.WriteByte (svc_configstring); gi.WriteShort (CS_TDM_ID_VIEW); gi.WriteString (string); gi.unicast (ent, false); } return target - g_edicts; }
/*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST Any brush that you want to explode or break apart. If you want an ex0plosion, set dmg and it will do a radius explosion of that amount at the center of the bursh. If targeted it will not be shootable. health defaults to 100. mass defaults to 75. This determines how much debris is emitted when it explodes. You get one large chunk per 100 of mass (up to 8) and one small chunk per 25 of mass (up to 16). So 800 gives the most. */ void func_explosive_explode (edict_t * self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point) { vec3_t origin; vec3_t chunkorigin; vec3_t size; int count; int mass; // bmodel origins are (0 0 0), we need to adjust that here VectorScale (self->size, 0.5, size); VectorAdd (self->absmin, size, origin); VectorCopy (origin, self->s.origin); self->takedamage = DAMAGE_NO; if (self->dmg) T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg + 40, MOD_EXPLOSIVE); VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity); VectorNormalize (self->velocity); VectorScale (self->velocity, 150, self->velocity); // start chunks towards the center VectorScale (size, 0.5, size); mass = self->mass; if (!mass) mass = 75; // big chunks if (mass >= 100) { count = mass / 100; if (count > 8) count = 8; while (count--) { chunkorigin[0] = origin[0] + crandom () * size[0]; chunkorigin[1] = origin[1] + crandom () * size[1]; chunkorigin[2] = origin[2] + crandom () * size[2]; ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin); } } // small chunks count = mass / 25; if (count > 16) count = 16; while (count--) { chunkorigin[0] = origin[0] + crandom () * size[0]; chunkorigin[1] = origin[1] + crandom () * size[1]; chunkorigin[2] = origin[2] + crandom () * size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin); } G_UseTargets (self, attacker); if (self->dmg) BecomeExplosion1 (self); else G_FreeEdict (self); }
/* ================ AddSkyPolygon ================ */ static void AddSkyPolygon( int nump, vec3_t vecs ) { int i,j; vec3_t v, av; float s, t, dv; int axis; float *vp; // s = [0]/[2], t = [1]/[2] static int vec_to_st[6][3] = { {-2,3,1}, {2,3,-1}, {1,3,2}, {-1,3,-2}, {-2,-1,3}, {-2,1,-3} // {-1,2,3}, // {1,2,-3} }; // decide which face it maps to VectorCopy( vec3_origin, v ); for ( i = 0, vp = vecs ; i < nump ; i++, vp += 3 ) { VectorAdd( vp, v, v ); } av[0] = Q_fabs( v[0] ); av[1] = Q_fabs( v[1] ); av[2] = Q_fabs( v[2] ); if ( av[0] > av[1] && av[0] > av[2] ) { if ( v[0] < 0 ) { axis = 1; } else { axis = 0; } } else if ( av[1] > av[2] && av[1] > av[0] ) { if ( v[1] < 0 ) { axis = 3; } else { axis = 2; } } else { if ( v[2] < 0 ) { axis = 5; } else { axis = 4; } } // project new texture coords for ( i = 0 ; i < nump ; i++, vecs += 3 ) { j = vec_to_st[axis][2]; if ( j > 0 ) { dv = vecs[j - 1]; } else { dv = -vecs[-j - 1]; } if ( dv < 0.001 ) { continue; // don't divide by zero } j = vec_to_st[axis][0]; if ( j < 0 ) { s = -vecs[-j - 1] / dv; } else { s = vecs[j - 1] / dv; } j = vec_to_st[axis][1]; if ( j < 0 ) { t = -vecs[-j - 1] / dv; } else { t = vecs[j - 1] / dv; } if ( s < sky_mins[0][axis] ) { sky_mins[0][axis] = s; } if ( t < sky_mins[1][axis] ) { sky_mins[1][axis] = t; } if ( s > sky_maxs[0][axis] ) { sky_maxs[0][axis] = s; } if ( t > sky_maxs[1][axis] ) { sky_maxs[1][axis] = t; } } }