/* ============ BG_PlayerTouchesItem Items can be picked up without actually touching their physical bounds to make grabbing them easier ============ */ qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ) { vec3_t origin; BG_EvaluateTrajectory( &item->pos, atTime, origin ); // we are ignoring ducked differences here if ( ps->origin[0] - origin[0] > 44 || ps->origin[0] - origin[0] < -50 || ps->origin[1] - origin[1] > 36 || ps->origin[1] - origin[1] < -36 || ps->origin[2] - origin[2] > 36 || ps->origin[2] - origin[2] < -36 ) { return qfalse; } return qtrue; }
int qlua_setpos(lua_State *L) { centity_t *luaentity; vec3_t origin; luaL_checktype(L,1,LUA_TUSERDATA); luaL_checktype(L,2,LUA_TVECTOR); luaentity = lua_toentity(L,1); if(luaentity != NULL) { BG_EvaluateTrajectory( &luaentity->currentState.pos, cg.time, origin ); lua_tovector(L,2,luaentity->currentState.pos.trBase); luaentity->currentState.pos.trDuration += (cg.time - luaentity->currentState.pos.trTime); luaentity->currentState.pos.trTime = cg.time; VectorCopy(luaentity->currentState.pos.trBase, luaentity->currentState.origin); return 1; } return 0; }
/* ================ G_ExplodeMissile Explode a missile without an impact ================ */ void G_ExplodeMissile( gentity_t *ent ) { vec3_t dir; vec3_t origin; BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); SnapVector( origin ); G_SetOrigin( ent, origin ); // we don't have a valid direction, so just point straight up dir[0] = dir[1] = 0; dir[2] = 1; ent->s.eType = ET_GENERAL; G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); ent->freeAfterEvent = qtrue; ent->takedamage = qfalse; // splash damage if ( ent->splashDamage ) { //NOTE: vehicle missiles don't have an ent->parent set, so check that here and set it if ( ent->s.eType == ET_MISSILE//missile && (ent->s.eFlags&EF_JETPACK_ACTIVE)//vehicle missile && ent->r.ownerNum < MAX_CLIENTS )//valid client owner {//set my parent to my owner for purposes of damage credit... ent->parent = &g_entities[ent->r.ownerNum]; } if( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, ent, ent->splashMethodOfDeath ) ) { if (ent->parent) { g_entities[ent->parent->s.number].client->accuracy_hits++; } else if (ent->activator) { g_entities[ent->activator->s.number].client->accuracy_hits++; } } } trap_LinkEntity( ent ); }
/* ================== CG_AddMoveScaleFade ================== */ static void CG_AddMoveScaleFade( localEntity_t *le ) { refEntity_t *re; float c; vec3_t delta; float len; re = &le->refEntity; // fade / grow time // c = ( le->endTime - cg.time ) * le->lifeRate; if ( le->fadeInTime > le->startTime && cg.time < le->fadeInTime ) { // fade / grow time c = 1.0 - (float) ( le->fadeInTime - cg.time ) / ( le->fadeInTime - le->startTime ); } else { // fade / grow time c = ( le->endTime - cg.time ) * le->lifeRate; } // Ridah, spark if ( !( le->leFlags & LEF_NOFADEALPHA ) ) // done. re->shaderRGBA[3] = 0xff * c * le->color[3]; if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) { c = ( le->endTime - cg.time ) * le->lifeRate; re->radius = le->radius * ( 1.0 - c ) + 8; } BG_EvaluateTrajectory( &le->pos, cg.time, re->origin ); // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( re->origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); if ( len < le->radius ) { CG_FreeLocalEntity( le ); return; } trap_R_AddRefEntityToScene( re ); }
/* ======================================================================================================================================= CG_BubbleThink ======================================================================================================================================= */ void CG_BubbleThink(localEntity_t *le) { int contents; vec3_t newOrigin; trace_t trace; // calculate new position BG_EvaluateTrajectory(&le->pos, cg.time, newOrigin); // trace a line from previous position to new position CG_Trace(&trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID); contents = CG_PointContents(trace.endpos, -1); if (!(contents & (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))) { // bubble isn't in liquid anymore, remove it CG_FreeLocalEntity(le); return; } CG_AddMoveScaleFade(le); }
/* ======================================================================================================================================= CG_BloodTrail Leave expanding blood puffs behind gibs. ======================================================================================================================================= */ void CG_BloodTrail(localEntity_t *le) { int t; int t2; int step; vec3_t newOrigin; localEntity_t *blood; step = 150; t = step * ((cg.time - cg.frametime + step) / step); t2 = step * (cg.time / step); for (; t <= t2; t += step) { BG_EvaluateTrajectory(&le->pos, t, newOrigin); blood = CG_SmokePuff(newOrigin, vec3_origin, 20, 1, 1, 1, 1, 2000, t, 0, 0, cgs.media.bloodTrailShader); // use the optimized version blood->leType = LE_FALL_SCALE_FADE; // drop a total of 40 units over its lifetime blood->pos.trDelta[2] = 40; } }
/*** Stops rotational movement on ent immediately. @function HaltAngles @param ent Entity or entity number. @return Success or failure. */ static int Mover_HaltAngles(lua_State * L) { lent_t *lent; gentity_t *ent = NULL; int id = 0; if(lua_isnumber(L, 1)) { id = luaL_checkint(L, 1); if(id < 0 || id > MAX_GENTITIES - 1) { lua_pushboolean(L, qfalse); return 1; } ent = &g_entities[id]; if(ent) { lua_pushboolean(L, qfalse); return 1; } } else { lent = Lua_GetEntity(L, 1); if(lent == NULL || lent->e == NULL) { lua_pushboolean(L, qfalse); return 1; } ent = lent->e; } LUA_DEBUG("Mover_HaltAngles - start: ent=%d", ent->s.number); if(ent) { BG_EvaluateTrajectory(&ent->s.apos, level.time, ent->s.apos.trBase); ent->s.apos.trType = TR_STATIONARY; ent->s.apos.trTime = level.time; trap_LinkEntity(ent); LUA_DEBUG("Mover_HaltAngles - return: halted ent"); } lua_pushboolean(L, qtrue); return 1; }
/* ============== CG_CheckEvents ============== */ void CG_CheckEvents( centity_t *cent ) { // check for event-only entities if ( cent->currentState.eType > ET_EVENTS ) { if ( cent->previousEvent ) { return; // already fired } cent->previousEvent = 1; // if this is a player event set the entity number of the client entity number if ( cent->currentState.eFlags & EF_PLAYER_EVENT ) { cent->currentState.number = cent->currentState.otherEntityNum; } cent->currentState.event = cent->currentState.eType - ET_EVENTS; } else { // check for events riding with another entity if ( cent->currentState.event == cent->previousEvent ) { return; } cent->previousEvent = cent->currentState.event; if ( ( cent->currentState.event & ~EV_EVENT_BITS ) == 0 ) { return; } } // calculate the position at exactly the frame time BG_EvaluateTrajectory( ¢->currentState.pos, cg.snap->serverTime, cent->lerpOrigin ); CG_SetEntitySoundPosition( cent ); CG_EntityEvent( cent, cent->lerpOrigin ); }
/*** Stops translational movement on ent immediately. @function Halt @param ent Entity or entity number. @return Success or failure. */ static int Mover_Halt(lua_State *L) { lent_t *lent; gentity_t *ent = NULL; int id = 0; if(lua_isnumber(L, 1)) { id = luaL_checkint(L, 1); if(id < 0 || id > MAX_GENTITIES - 1) { lua_pushboolean(L, qfalse); return 1; } ent = &g_entities[id]; if(ent == NULL) { lua_pushboolean(L, qfalse); return 1; } } else { lent = Lua_GetEntity(L, 1); if(lent == NULL || lent->e == NULL) { lua_pushboolean(L, qfalse); return 1; } ent = lent->e; } LUA_DEBUG("Mover_Halt - start: end=%d", ent->s.number); BG_EvaluateTrajectory(&ent->s.pos, level.time, ent->r.currentOrigin); VectorCopy(ent->r.currentOrigin, ent->s.pos.trBase); ent->s.pos.trType = TR_STATIONARY; ent->s.pos.trTime = level.time; ent->nextthink = 0; ent->think = NULL; ent->nextTrain = NULL; trap_LinkEntity(ent); LUA_DEBUG("Mover_Halt - return: halted ent"); lua_pushboolean(L, qtrue); return 1; }
static void CG_AddMoveScaleFade( localEntity_t *le ) { refEntity_t *re; float c; vector3 delta; float len; refdef_t *refdef = CG_GetRefdef(); re = &le->refEntity; if ( le->fadeInTime > le->startTime && cg.time < le->fadeInTime ) { // fade / grow time c = 1.0f - (float)(le->fadeInTime - cg.time) / (le->fadeInTime - le->startTime); } else { // fade / grow time c = (le->endTime - cg.time) * le->lifeRate; } re->shaderRGBA[3] = 0xff * c * le->color[3]; if ( !(le->leFlags & LEF_PUFF_DONT_SCALE) ) { re->radius = le->radius * (1.0f - c) + 8; } BG_EvaluateTrajectory( &le->pos, cg.time, &re->origin ); // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( &re->origin, &refdef->vieworg, &delta ); len = VectorLength( &delta ); if ( len < le->radius ) { CG_FreeLocalEntity( le ); return; } SE_R_AddRefEntityToScene( re, MAX_CLIENTS ); }
void G_minethink(gentity_t *ent) { trace_t tr; vec3_t end, origin, dir; gentity_t *traceEnt; ent->nextthink = level.time + 100; BG_EvaluateTrajectory(&ent->s.pos, level.time, origin); SnapVector(origin); G_SetOrigin(ent, origin); // set aiming directions VectorCopy(origin,end); end[2] += 10;//aim up trap_Trace(&tr, origin, NULL, NULL, end, ent->s.number, MASK_SHOT); if (tr.surfaceFlags & SURF_NOIMPACT) return; traceEnt = &g_entities[tr.entityNum]; dir[0] = dir[1] = 0; dir[2] = 1; if (traceEnt->client && (traceEnt->r.svFlags & SVF_BOT) && traceEnt->health > 0 && traceEnt->client->ps.stats[STAT_PTEAM] == PTE_ALIENS)//FIRE IN ZE HOLE! {//Might want to check team too ent->s.eType = ET_GENERAL; G_AddEvent(ent, EV_MISSILE_MISS, DirToByte(dir)); ent->freeAfterEvent = qtrue; G_RadiusDamage(ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, ent->splashMethodOfDeath); ent->parent->numMines -= 1; trap_LinkEntity(ent); } }
/* ================ G_ExplodeMissile Explode a missile without an impact ================ */ void G_ExplodeMissile( gentity_t *ent ) { vec3_t dir; vec3_t origin; BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); SnapVector( origin ); G_SetOrigin( ent, origin ); // we don't have a valid direction, so just point straight up dir[0] = dir[1] = 0; dir[2] = 1; ent->s.eType = ET_GENERAL; G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); ent->freeAfterEvent = qtrue; ent->takedamage = qfalse; // splash damage if ( ent->splashDamage ) { if( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, ent, ent->splashMethodOfDeath ) ) { if (ent->parent) { g_entities[ent->parent->s.number].client->accuracy_hits++; } else if (ent->activator) { g_entities[ent->activator->s.number].client->accuracy_hits++; } } } trap->LinkEntity( (sharedEntity_t *)ent ); }
/* ================== CG_AddMoveScaleFade ================== */ static void CG_AddMoveScaleFade( localEntity_t *le ) { refEntity_t *re; gfixed c; bvec3_t delta; bfixed len; re = &le->refEntity; if ( le->fadeInTime > le->startTime && cg.time < le->fadeInTime ) { // fade / grow time c = GFIXED_1 - MAKE_GFIXED( le->fadeInTime - cg.time ) / MAKE_GFIXED( le->fadeInTime - le->startTime ); } else { // fade / grow time c = MAKE_GFIXED( le->endTime - cg.time ) * le->lifeRate; } re->shaderRGBA[3] = FIXED_TO_INT( GFIXED(255,0) * c * le->color[3] ); if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) { re->radius = (le->radius * ( GFIXED_1 - c )) + BFIXED(8,0); } BG_EvaluateTrajectory( &le->pos, cg.time, re->origin ); // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( re->origin, cg.refdef.vieworg, delta ); len = FIXED_VEC3LEN( delta ); if ( len < le->radius ) { CG_FreeLocalEntity( le ); return; } _CG_trap_R_AddRefEntityToScene( re ); }
/* ================ CG_AddSparkElements ================ */ void CG_AddSparkElements( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; float time; float lifeFrac; time = (float)(cg.time - cg.frametime); while (1) { // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); // if ((le->endTime - le->startTime) > 500) { // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, MASK_SHOT ); // if stuck, kill it if (trace.startsolid) { // HACK, some walls screw up, so just pass through if starting in a solid VectorCopy( newOrigin, trace.endpos ); trace.fraction = 1.0; } // moved some distance VectorCopy( trace.endpos, le->refEntity.origin ); /* } else { // just move it there VectorCopy( newOrigin, le->refEntity.origin ); trace.fraction = 1.0; } */ time += cg.frametime * trace.fraction; lifeFrac = (float)(cg.time - le->startTime) / (float)(le->endTime - le->startTime); // add a trail le->headJuncIndex = CG_AddSparkJunc( le->headJuncIndex, le->refEntity.customShader, le->refEntity.origin, 200, 1.0 - lifeFrac, // start alpha 0.0,//1.0 - lifeFrac, // end alpha lifeFrac * 2.0 * (((le->endTime - le->startTime) > 400)+1)*1.5, lifeFrac * 2.0 * (((le->endTime - le->startTime) > 400)+1)*1.5 ); // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels // for some reason SFM1.BSP is one big NODROP zone // if ( trap_CM_PointContents( le->refEntity.origin, 0 ) & CONTENTS_NODROP ) { // CG_FreeLocalEntity( le ); // return; // } if (trace.fraction < 1.0) { // just kill it CG_FreeLocalEntity( le ); return; /* // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); // the intersection is a fraction of the frametime le->pos.trTime = (int)time; */ } if ( trace.fraction == 1.0 || time >= (float)cg.time ) { return; } } }
/* ================ CG_AddClientCritter ================ */ void CG_AddClientCritter( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; int time, step = 25, i; vec3_t v, ang, v2, oDelta; localEntity_t backup; float oldSpeed, enemyDist, of; vec3_t enemyPos; float alpha; if (cg_entities[le->ownerNum].currentState.otherEntityNum2 == cg.snap->ps.clientNum) { VectorCopy( cg.snap->ps.origin, enemyPos ); enemyPos[2] += cg.snap->ps.viewheight; } else { VectorCopy( cg_entities[le->ownerNum].currentState.origin2, enemyPos ); } VectorCopy( le->pos.trDelta, oDelta ); // vary the enemyPos to create a psuedo-randomness of = (float)cg.time + le->startTime; enemyPos[0] += 12 * (sin(of/100) * cos(of/78)); enemyPos[1] += 12 * (sin(of/70) * cos(of/82)); enemyPos[2] += 12 * (sin(of/67) * cos(of/98)); time = le->lastTrailTime+step; while (time <= cg.time) { if (time > le->refEntity.fadeStartTime) { alpha = (float)(time - le->refEntity.fadeStartTime)/(float)(le->refEntity.fadeEndTime - le->refEntity.fadeStartTime); if (alpha < 0) alpha = 0; else if (alpha > 1) alpha = 1; } else { alpha = 1.0; } // calculate new position BG_EvaluateTrajectory( &le->pos, time, newOrigin ); VectorSubtract( enemyPos, le->refEntity.origin, v ); enemyDist = VectorNormalize( v ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, le->ownerNum, MASK_SHOT ); // if stuck, kill it if (trace.startsolid || (trace.fraction < 1.0)) { // kill it CG_FreeLocalEntity( le ); return; } // moved some distance VectorCopy( trace.endpos, le->refEntity.origin ); if (le->leType == LE_ZOMBIE_SPIRIT) { le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex, cgs.media.zombieSpiritTrailShader, time, STYPE_STRETCH, le->refEntity.origin, (int)le->effectWidth, // trail life 0.3 * alpha, 0.0, le->radius, 0, 0,//TJFL_FIXDISTORT, colorWhite, colorWhite, 1.0, 1 ); } // tracking factor if (le->leType == LE_ZOMBIE_BAT) le->bounceFactor = 3.0*(float)step/1000.0; else le->bounceFactor = 5.0*(float)step/1000.0; oldSpeed = VectorLength( le->pos.trDelta ); // track the enemy backup = *le; VectorSubtract( enemyPos, le->refEntity.origin, v ); enemyDist = VectorNormalize( v ); if (alpha > 0.5 && (le->lastSpiritDmgTime < time - 100) && enemyDist < 24) { // inflict the damage! CG_ClientDamage( cg_entities[le->ownerNum].currentState.otherEntityNum2, le->ownerNum, CLDMG_SPIRIT ); le->lastSpiritDmgTime = time; } VectorMA( le->pos.trDelta, le->bounceFactor*oldSpeed, v, le->pos.trDelta ); //VectorCopy( v, le->pos.trDelta ); if (VectorLength(le->pos.trDelta) < 1) { CG_FreeLocalEntity( le ); return; } le->bounceFactor = 5.0*(float)step/1000.0; // avoidance factor // the intersection is a fraction of the frametime le->pos.trTime = time; VectorCopy( le->refEntity.origin, le->pos.trBase ); VectorNormalize( le->pos.trDelta ); VectorScale( le->pos.trDelta, oldSpeed, le->pos.trDelta ); // now trace ahead of time, if we're going to hit something, then avoid it // only avoid dangers if we don't have direct sight to the enemy trap_CM_BoxTrace( &trace, le->refEntity.origin, enemyPos, NULL, NULL, 0, MASK_SOLID ); if (trace.fraction < 1.0) { BG_EvaluateTrajectory( &le->pos, time+1000, newOrigin ); // if we would go passed the enemy, don't bother if (VectorDistance( le->refEntity.origin, enemyPos) > VectorDistance( le->refEntity.origin, newOrigin )) { trap_CM_BoxTrace( &trace, le->refEntity.origin, newOrigin, NULL, NULL, 0, MASK_SOLID ); if (trace.fraction < 1.0) { // make sure we are not heading away from the enemy too much VectorNormalize2( le->pos.trDelta, v2 ); if (DotProduct( v, v2 ) > 0.7) { // avoid world geometry backup = *le; le->bounceFactor = (1.0 - trace.fraction)*10.0*(float)step/1000.0; // tracking and avoidance factor // reflect the velocity on the trace plane VectorMA( le->pos.trDelta, le->bounceFactor*oldSpeed, trace.plane.normal, le->pos.trDelta ); if (VectorLength(le->pos.trDelta) < 1) { CG_FreeLocalEntity( le ); return; } // the intersection is a fraction of the frametime le->pos.trTime = time; VectorCopy( le->refEntity.origin, le->pos.trBase ); VectorNormalize( le->pos.trDelta ); VectorScale( le->pos.trDelta, oldSpeed, le->pos.trDelta ); // // double check end velocity VectorNormalize2( le->pos.trDelta, v2 ); if (DotProduct( v, v2 ) <= 0.2) { // restore *le = backup; } } } } } // set the angles VectorNormalize2( le->pos.trDelta, v ); // HACK!!! skull model is back-to-front, need to fix if (le->leType == LE_ZOMBIE_SPIRIT) VectorInverse( v ); vectoangles( v, ang ); AnglesToAxis( ang, le->refEntity.axis ); // lean when turning if (le->leType == LE_ZOMBIE_BAT) { VectorSubtract( le->pos.trDelta, oDelta, v2 ); ang[ROLL] = -5.0 * DotProduct( le->refEntity.axis[1], v2 ); if (fabs(ang[ROLL]) > 80) { if (ang[ROLL] > 80) ang[ROLL] = 80; else ang[ROLL] = -80; } } AnglesToAxis( ang, le->refEntity.axis ); // HACK: the skull is slightly higher than the origin if (le->leType == LE_ZOMBIE_SPIRIT) { // set the size scale for (i=0; i<3; i++) VectorScale( le->refEntity.axis[i], 0.35, le->refEntity.axis[i] ); VectorMA( le->refEntity.origin, -10, le->refEntity.axis[2], le->refEntity.origin ); } le->lastTrailTime = time; time += step; } // Bats, set the frame if (le->leType == LE_ZOMBIE_BAT) { #define BAT_ANIM_FRAMETIME 30 le->refEntity.frame = (cg.time/BAT_ANIM_FRAMETIME+1)%19; le->refEntity.oldframe = (cg.time/BAT_ANIM_FRAMETIME)%19; le->refEntity.backlerp = 1.0 - ((float)(cg.time%BAT_ANIM_FRAMETIME)/(float)BAT_ANIM_FRAMETIME); } // add the sound if (le->loopingSound) { if (cg.time > le->refEntity.fadeStartTime) trap_S_AddLoopingSound( 0, le->refEntity.origin, vec3_origin, le->loopingSound, 255 - (int)(255.0 * (float)(cg.time - le->refEntity.fadeStartTime) / (float)(le->refEntity.fadeEndTime - le->refEntity.fadeStartTime)) ); else if (le->startTime + 1000 > cg.time) trap_S_AddLoopingSound( 0, le->refEntity.origin, vec3_origin, le->loopingSound, (int)(255.0 * (float)(cg.time - le->startTime) / 1000.0) ); else trap_S_AddLoopingSound( 0, le->refEntity.origin, vec3_origin, le->loopingSound, 255); } trap_R_AddRefEntityToScene( &le->refEntity ); /* // HACK: the skull is slightly higher than the origin if (le->leType == LE_ZOMBIE_SPIRIT) { // set the size scale for (i=0; i<3; i++) VectorScale( le->refEntity.axis[i], 1.0/0.35, le->refEntity.axis[i] ); VectorMA( le->refEntity.origin, 10, le->refEntity.axis[2], le->refEntity.origin ); } */ // Bats, add the flame if (le->leType == LE_ZOMBIE_BAT) { // float lightSize, alpha; // le->refEntity.shaderRGBA[3] = 255; VectorNormalize2( le->pos.trDelta, v ); VectorInverse( v ); v[2] += 1; VectorNormalize2( v, le->refEntity.fireRiseDir ); le->refEntity.customShader = cgs.media.onFireShader2; trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.shaderTime = 1434; trap_R_AddRefEntityToScene( &le->refEntity ); // le->refEntity.customShader = cgs.media.onFireShader; // le->refEntity.shaderTime = 0; // trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.customShader = 0; le->refEntity.shaderTime = 0; /* // drop a dlight lightSize = 1.0 + 0.2*(sin(1.0*cg.time/50.0) * cos(1.0*cg.time/43.0)); alpha = 0.2 * (lightSize / 1.2); trap_R_AddLightToScene( le->refEntity.origin, 150.0 + 80.0*lightSize, 1.000000*alpha, 0.603922*alpha, 0.207843*alpha, 0 ); // add some sound trap_S_AddLoopingSound( -1, le->refEntity.origin, vec3_origin, cgs.media.flameSound, 100 ); trap_S_AddLoopingSound( -1, le->refEntity.origin, vec3_origin, cgs.media.flameBlowSound, 100 ); */ } }
/* ================ CG_AddFragment ================ */ void CG_AddFragment( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; refEntity_t *re; float flameAlpha = 0.0; // TTimo: init vec3_t flameDir; qboolean hasFlame = qfalse; int i; // Ridah re = &le->refEntity; if (!re->fadeStartTime || re->fadeEndTime < le->endTime) { if (le->endTime - cg.time > 5000) { re->fadeStartTime = le->endTime - 5000; } else { re->fadeStartTime = le->endTime - 1000; } re->fadeEndTime = le->endTime; } // Ridah, flaming gibs if (le->onFireStart && (le->onFireStart < cg.time && le->onFireEnd > cg.time)) { hasFlame = qtrue; // calc the alpha flameAlpha = 1.0 - ((float)(cg.time - le->onFireStart)/(float)(le->onFireEnd - le->onFireStart)); if (flameAlpha < 0.0) flameAlpha = 0.0; if (flameAlpha > 1.0) flameAlpha = 1.0; trap_S_AddLoopingSound( -1, le->refEntity.origin, vec3_origin, cgs.media.flameCrackSound, (int)(20.0*flameAlpha) ); } //----(SA) added if(le->leFlags & LEF_SMOKING) { float alpha; refEntity_t flash; // create a little less smoke // TODO: FIXME: this is not quite right, because it'll become fps dependant - in a bad way. // the slower the fps, the /more/ smoke there'll be, probably driving the fps lower. if(!(rand()%5)) { alpha = 1.0 - ((float)(cg.time - le->startTime)/(float)(le->endTime - le->startTime)); alpha *= 0.25f; memset (&flash, 0, sizeof (flash)); CG_PositionEntityOnTag( &flash, &le->refEntity, "tag_flash", 0, NULL); CG_ParticleImpactSmokePuffExtended(cgs.media.smokeParticleShader, flash.origin, 1000, 8, 20, 20, alpha); } } //----(SA) end if ( le->pos.trType == TR_STATIONARY ) { int t; // Ridah, add the flame if (hasFlame) { refEntity_t backupEnt; backupEnt = le->refEntity; VectorClear( flameDir ); flameDir[2] = 1; le->refEntity.shaderRGBA[3] = (unsigned char)(255.0*flameAlpha); VectorCopy( flameDir, le->refEntity.fireRiseDir ); le->refEntity.customShader = cgs.media.onFireShader; trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.customShader = cgs.media.onFireShader2; trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity = backupEnt; } t = le->endTime - cg.time; trap_R_AddRefEntityToScene( &le->refEntity ); return; } else if ( le->pos.trType == TR_GRAVITY_PAUSED ) { int t; // Ridah, add the flame if (hasFlame) { refEntity_t backupEnt; backupEnt = le->refEntity; VectorClear( flameDir ); flameDir[2] = 1; le->refEntity.shaderRGBA[3] = (unsigned char)(255.0*flameAlpha); VectorCopy( flameDir, le->refEntity.fireRiseDir ); le->refEntity.customShader = cgs.media.onFireShader; trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.customShader = cgs.media.onFireShader2; trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity = backupEnt; } t = le->endTime - cg.time; trap_R_AddRefEntityToScene( &le->refEntity ); // trace a line from previous position down, to see if I should start falling again VectorCopy(le->refEntity.origin, newOrigin); newOrigin [2] -= 5; CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_MISSILECLIP ); if(trace.fraction == 1.0) // it's clear, start moving again { VectorClear(le->pos.trDelta); VectorClear(le->angles.trDelta); le->pos.trType = TR_GRAVITY; // nothing below me, start falling again } else return; } // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); if (hasFlame) { // calc the flame dir VectorSubtract( le->refEntity.origin, newOrigin, flameDir ); if (VectorLength( flameDir ) == 0) { flameDir[2] = 1; // play a burning sound when not moving trap_S_AddLoopingSound( 0, newOrigin, vec3_origin, cgs.media.flameSound, (int)(0.3*255.0*flameAlpha) ); } else { VectorNormalize( flameDir ); // play a flame blow sound when moving trap_S_AddLoopingSound( 0, newOrigin, vec3_origin, cgs.media.flameBlowSound, (int)(0.3*255.0*flameAlpha) ); } } // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin ); if ( le->leFlags & LEF_TUMBLE || le->angles.trType == TR_LINEAR) { vec3_t angles; BG_EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); if (le->sizeScale && le->sizeScale != 1.0) { for (i=0;i<3;i++) VectorScale( le->refEntity.axis[i], le->sizeScale, le->refEntity.axis[i] ); } } // Ridah, add the flame if (hasFlame) { refEntity_t backupEnt; backupEnt = le->refEntity; le->refEntity.shaderRGBA[3] = (unsigned char)(255.0*flameAlpha); VectorCopy( flameDir, le->refEntity.fireRiseDir ); le->refEntity.customShader = cgs.media.onFireShader; trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.customShader = cgs.media.onFireShader2; trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity = backupEnt; } trap_R_AddRefEntityToScene( &le->refEntity ); // add a blood trail if ( le->leBounceSoundType == LEBS_BLOOD ) { CG_BloodTrail( le ); } return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { CG_FreeLocalEntity( le ); return; } // do a bouncy sound CG_FragmentBounceSound( le, &trace ); // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); // break on contact? if (le->breakCount) { clientInfo_t *ci; int clientNum; localEntity_t *nle; vec3_t dir; clientNum = le->ownerNum; if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { CG_Error( "Bad clientNum on player entity"); } ci = &cgs.clientinfo[ clientNum ]; // spawn some new fragments for (i=0;i<=le->breakCount;i++) { nle = CG_AllocLocalEntity(); memcpy( &(nle->leType), &(le->leType), sizeof(localEntity_t) - 2*sizeof(localEntity_t *) ); if (nle->breakCount-- < 2) nle->refEntity.hModel = ci->gibModels[rand()%2]; else nle->refEntity.hModel = ci->gibModels[rand()%4]; // make it smaller nle->endTime = cg.time + 5000 + rand()%2000; nle->sizeScale *= 0.8; if (nle->sizeScale < 0.7) { nle->sizeScale = 0.7; nle->leBounceSoundType = 0; } // move us a bit VectorNormalize2( nle->pos.trDelta, dir ); VectorMA( trace.endpos, 4.0*le->sizeScale*i, dir, nle->pos.trBase ); // randomize vel a bit VectorMA( nle->pos.trDelta, VectorLength(nle->pos.trDelta)*0.3, bytedirs[rand()%NUMVERTEXNORMALS], nle->pos.trDelta ); } // we're done CG_FreeLocalEntity( le ); return; } if (le->pos.trType == TR_STATIONARY && le->leMarkType == LEMT_BLOOD) { // RF, disabled for performance reasons in boss1 //if (le->leBounceSoundType) // CG_BloodPool (le, cgs.media.bloodPool, &trace); // leave a mark if (le->leMarkType) CG_FragmentBounceMark( le, &trace ); } // Ridah, add the flame if (hasFlame) { refEntity_t backupEnt; backupEnt = le->refEntity; le->refEntity.shaderRGBA[3] = (unsigned char)(255.0*flameAlpha); VectorCopy( flameDir, le->refEntity.fireRiseDir ); le->refEntity.customShader = cgs.media.onFireShader; trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.customShader = cgs.media.onFireShader2; trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity = backupEnt; } trap_R_AddRefEntityToScene( &le->refEntity ); }
//----------------------------------------------------- static void turret_aim( gentity_t *self ) //----------------------------------------------------- { vec3_t enemyDir, org, org2; vec3_t desiredAngles, setAngle; float diffYaw = 0.0f, diffPitch = 0.0f, turnSpeed; const float pitchCap = 40.0f; gentity_t *top = &g_entities[self->r.ownerNum]; if ( !top ) { return; } // move our gun base yaw to where we should be at this time.... BG_EvaluateTrajectory( &top->s.apos, level.time, top->r.currentAngles ); top->r.currentAngles[YAW] = AngleNormalize180( top->r.currentAngles[YAW] ); top->r.currentAngles[PITCH] = AngleNormalize180( top->r.currentAngles[PITCH] ); turnSpeed = top->speed; if ( self->painDebounceTime > level.time ) { desiredAngles[YAW] = top->r.currentAngles[YAW]+flrand(-45,45); desiredAngles[PITCH] = top->r.currentAngles[PITCH]+flrand(-10,10); if (desiredAngles[PITCH] < -pitchCap) { desiredAngles[PITCH] = -pitchCap; } else if (desiredAngles[PITCH] > pitchCap) { desiredAngles[PITCH] = pitchCap; } diffYaw = AngleSubtract( desiredAngles[YAW], top->r.currentAngles[YAW] ); diffPitch = AngleSubtract( desiredAngles[PITCH], top->r.currentAngles[PITCH] ); turnSpeed = flrand( -5, 5 ); } else if ( self->enemy ) { // ...then we'll calculate what new aim adjustments we should attempt to make this frame // Aim at enemy VectorCopy( self->enemy->r.currentOrigin, org ); org[2]+=self->enemy->r.maxs[2]*0.5f; if (self->enemy->s.eType == ET_NPC && self->enemy->s.NPC_class == CLASS_VEHICLE && self->enemy->m_pVehicle && self->enemy->m_pVehicle->m_pVehicleInfo->type == VH_WALKER) { //hack! org[2] += 32.0f; } /* mdxaBone_t boltMatrix; // Getting the "eye" here gi.G2API_GetBoltMatrix( self->ghoul2, self->playerModel, self->torsoBolt, &boltMatrix, self->r.currentAngles, self->s.origin, (cg.time?cg.time:level.time), NULL, self->s.modelScale ); gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, org2 ); */ VectorCopy( top->r.currentOrigin, org2 ); VectorSubtract( org, org2, enemyDir ); vectoangles( enemyDir, desiredAngles ); desiredAngles[PITCH] = AngleNormalize180(desiredAngles[PITCH]); if (desiredAngles[PITCH] < -pitchCap) { desiredAngles[PITCH] = -pitchCap; } else if (desiredAngles[PITCH] > pitchCap) { desiredAngles[PITCH] = pitchCap; } diffYaw = AngleSubtract( desiredAngles[YAW], top->r.currentAngles[YAW] ); diffPitch = AngleSubtract( desiredAngles[PITCH], top->r.currentAngles[PITCH] ); } else {//FIXME: Pan back and forth in original facing // no enemy, so make us slowly sweep back and forth as if searching for a new one desiredAngles[YAW] = sin( level.time * 0.0001f + top->count ); desiredAngles[YAW] *= 60.0f; desiredAngles[YAW] += self->s.angles[YAW]; desiredAngles[YAW] = AngleNormalize180( desiredAngles[YAW] ); diffYaw = AngleSubtract( desiredAngles[YAW], top->r.currentAngles[YAW] ); diffPitch = AngleSubtract( 0, top->r.currentAngles[PITCH] ); turnSpeed = 1.0f; } if ( diffYaw ) { // cap max speed.... if ( fabs(diffYaw) > turnSpeed ) { diffYaw = ( diffYaw >= 0 ? turnSpeed : -turnSpeed ); } } if ( diffPitch ) { if ( fabs(diffPitch) > turnSpeed ) { // cap max speed diffPitch = (diffPitch > 0.0f ? turnSpeed : -turnSpeed ); } } // ...then set up our desired yaw VectorSet( setAngle, diffPitch, diffYaw, 0 ); VectorCopy( top->r.currentAngles, top->s.apos.trBase ); VectorScale( setAngle, (1000/FRAMETIME), top->s.apos.trDelta ); top->s.apos.trTime = level.time; top->s.apos.trType = TR_LINEAR_STOP; top->s.apos.trDuration = FRAMETIME; if ( diffYaw || diffPitch ) { top->s.loopSound = G_SoundIndex( "sound/vehicles/weapons/hoth_turret/turn.wav" ); } else { top->s.loopSound = 0; } }
/* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin, groundSpot; trace_t tr; int passent; qboolean isKnockedSaber = qfalse; if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF)) { isKnockedSaber = qtrue; ent->s.pos.trType = TR_GRAVITY; } // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); //If its a rocket, and older than 500ms, make it solid to the shooter. if ((g_tweakWeapons.integer & WT_SOLID_ROCKET) && (ent->s.weapon == WP_ROCKET_LAUNCHER) && (!ent->raceModeShooter) && (ent->nextthink - level.time < 9500)) { ent->r.ownerNum = ENTITYNUM_WORLD; } // if this missile bounced off an invulnerability sphere if ( ent->target_ent ) { passent = ent->target_ent->s.number; } else { // ignore interactions with the missile owner if ( (ent->r.svFlags&SVF_OWNERNOTSHARED) && (ent->s.eFlags&EF_JETPACK_ACTIVE) ) { //A vehicle missile that should be solid to its owner //I don't care about hitting my owner passent = ent->s.number; } else { passent = ent->r.ownerNum; } } // trace a line from the previous position to the current position if (d_projectileGhoul2Collision.integer == 1) //JAPRO - Serverside - Weapons - New Hitbox Option { JP_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask, qfalse, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_THICK|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer ); if (tr.fraction != 1.0 && tr.entityNum < ENTITYNUM_WORLD) { gentity_t *g2Hit = &g_entities[tr.entityNum]; if (g2Hit->inuse && g2Hit->client && g2Hit->ghoul2) { //since we used G2TRFLAG_GETSURFINDEX, tr.surfaceFlags will actually contain the index of the surface on the ghoul2 model we collided with. g2Hit->client->g2LastSurfaceHit = tr.surfaceFlags; g2Hit->client->g2LastSurfaceTime = level.time; } if (g2Hit->ghoul2) { tr.surfaceFlags = 0; //clear the surface flags after, since we actually care about them in here. } } } else { JP_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask, qfalse, 0, 0 ); } if ( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in JP_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask, qfalse, 0, 0 ); tr.fraction = 0; } else { VectorCopy( tr.endpos, ent->r.currentOrigin ); } if (ent->passThroughNum && tr.entityNum == (ent->passThroughNum-1)) { VectorCopy( origin, ent->r.currentOrigin ); trap->LinkEntity( (sharedEntity_t *)ent ); goto passthrough; } trap->LinkEntity( (sharedEntity_t *)ent ); if (ent->s.weapon == G2_MODEL_PART && !ent->bounceCount) { vec3_t lowerOrg; trace_t trG; VectorCopy(ent->r.currentOrigin, lowerOrg); lowerOrg[2] -= 1; JP_Trace( &trG, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, lowerOrg, passent, ent->clipmask, qfalse, 0, 0 ); VectorCopy(trG.endpos, groundSpot); if (!trG.startsolid && !trG.allsolid && trG.entityNum == ENTITYNUM_WORLD) { ent->s.groundEntityNum = trG.entityNum; } else { ent->s.groundEntityNum = ENTITYNUM_NONE; } } if (tr.fraction != 1) { //Hit something maybe qboolean skip = qfalse; gentity_t *other = &g_entities[tr.entityNum]; //Check to see if we hit a lightsaber and they are in another dimension, if so dont do the hit code.. if (other && other->r.contents & CONTENTS_LIGHTSABER) { gentity_t *otherOwner = &g_entities[other->r.ownerNum]; gentity_t *owner = &g_entities[ent->r.ownerNum]; /* if (owner->s.bolt1 && !otherOwner->s.bolt1)//We are dueling/racing and they are not skip = qtrue; else if (!owner->s.bolt1 && otherOwner->s.bolt1)//They are dueling/racing and we are not skip = qtrue; */ if (owner->s.bolt1 != otherOwner->s.bolt1) //Dont impact if its from another dimension skip = qtrue; } if ( tr.fraction != 1 && !skip) { // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { // If grapple, reset owner if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) { ent->parent->client->hook = NULL; } if ((ent->s.weapon == WP_SABER && ent->isSaberEntity) || isKnockedSaber) { G_RunThink( ent ); return; } else if (ent->s.weapon != G2_MODEL_PART) { G_FreeEntity( ent ); return; } } if (ent->s.weapon > WP_NONE && ent->s.weapon < WP_NUM_WEAPONS && (tr.entityNum < MAX_CLIENTS || g_entities[tr.entityNum].s.eType == ET_NPC)) { //player or NPC, try making a mark on him //copy current pos to s.origin, and current projected traj to origin2 VectorCopy(ent->r.currentOrigin, ent->s.origin); BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin2 ); if (VectorCompare(ent->s.origin, ent->s.origin2)) { ent->s.origin2[2] += 2.0f; //whatever, at least it won't mess up. } } G_MissileImpact( ent, &tr ); if (tr.entityNum == ent->s.otherEntityNum) { //if the impact event other and the trace ent match then it's ok to do the g2 mark ent->s.trickedentindex = 1; } if ( ent->s.eType != ET_MISSILE && ent->s.weapon != G2_MODEL_PART ) { return; // exploded } } } passthrough: if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) ) { //stuck missiles should check some special stuff G_RunStuckMissile( ent ); return; } if (ent->s.weapon == G2_MODEL_PART) { if (ent->s.groundEntityNum == ENTITYNUM_WORLD) { ent->s.pos.trType = TR_LINEAR; VectorClear(ent->s.pos.trDelta); ent->s.pos.trTime = level.time; VectorCopy(groundSpot, ent->s.pos.trBase); VectorCopy(groundSpot, ent->r.currentOrigin); if (ent->s.apos.trType != TR_STATIONARY) { ent->s.apos.trType = TR_STATIONARY; ent->s.apos.trTime = level.time; ent->s.apos.trBase[ROLL] = 0; ent->s.apos.trBase[PITCH] = 0; } } } // check think function after bouncing G_RunThink( ent ); }
//TODO: When transition to 0 grav, push away from surface you were resting on //TODO: When free-floating in air, apply some friction to your trDelta (based on mass?) void G_RunObject( gentity_t *ent ) { vector3 origin, oldOrg; trace_t tr; gentity_t *traceEnt = NULL; //FIXME: floaters need to stop floating up after a while, even if gravity stays negative? if ( ent->s.pos.trType == TR_STATIONARY )//g_gravity.value <= 0 && { ent->s.pos.trType = TR_GRAVITY; VectorCopy( &ent->r.currentOrigin, &ent->s.pos.trBase ); ent->s.pos.trTime = level.previousTime;//?necc? if ( !g_gravity.value ) { ent->s.pos.trDelta.z += 100; } } ent->nextthink = level.time + FRAMETIME; VectorCopy( &ent->r.currentOrigin, &oldOrg ); // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, &origin ); //Get current angles? BG_EvaluateTrajectory( &ent->s.apos, level.time, &ent->r.currentAngles ); if ( VectorCompare( &ent->r.currentOrigin, &origin ) ) {//error - didn't move at all! return; } // trace a line from the previous position to the current position, // ignoring interactions with the missile owner trap->Trace( &tr, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &origin, ent->parent ? ent->parent->s.number : ent->s.number, ent->clipmask, qfalse, 0, 0 ); if ( !tr.startsolid && !tr.allsolid && tr.fraction ) { VectorCopy( &tr.endpos, &ent->r.currentOrigin ); trap->LinkEntity( (sharedEntity_t *)ent ); } else //if ( tr.startsolid ) { tr.fraction = 0; } G_MoverTouchPushTriggers( ent, &oldOrg ); /* if ( !(ent->s.eFlags & EF_TELEPORT_BIT) && !(ent->svFlags & SVF_NO_TELEPORT) ) { G_MoverTouchTeleportTriggers( ent, oldOrg ); if ( ent->s.eFlags & EF_TELEPORT_BIT ) {//was teleported return; } } else { ent->s.eFlags &= ~EF_TELEPORT_BIT; } */ if ( tr.fraction == 1 ) { if ( g_gravity.value <= 0 ) { if ( ent->s.apos.trType == TR_STATIONARY ) { VectorCopy( &ent->r.currentAngles, &ent->s.apos.trBase ); ent->s.apos.trType = TR_LINEAR; ent->s.apos.trDelta.y = flrand( -300, 300 ); ent->s.apos.trDelta.x = flrand( -10, 10 ); ent->s.apos.trDelta.z = flrand( -10, 10 ); ent->s.apos.trTime = level.time; } } //friction in zero-G if ( !g_gravity.value ) { float friction = 0.975f; //friction -= ent->mass/1000.0f; if ( friction < 0.1f ) { friction = 0.1f; } VectorScale( &ent->s.pos.trDelta, friction, &ent->s.pos.trDelta ); VectorCopy( &ent->r.currentOrigin, &ent->s.pos.trBase ); ent->s.pos.trTime = level.time; } return; } //hit something //Do impact damage traceEnt = &g_entities[tr.entityNum]; if ( tr.fraction || (traceEnt && traceEnt->takedamage) ) { if ( !VectorCompare( &ent->r.currentOrigin, &oldOrg ) ) {//moved and impacted if ( (traceEnt && traceEnt->takedamage) ) {//hurt someone // G_Sound( ent, G_SoundIndex( "sound/movers/objects/objectHurt.wav" ) ); } // G_Sound( ent, G_SoundIndex( "sound/movers/objects/objectHit.wav" ) ); } if ( ent->s.weapon != WP_SABER ) { DoImpact( ent, traceEnt, qtrue ); } } if ( !ent || (ent->takedamage&&ent->health <= 0) ) {//been destroyed by impact //chunks? // G_Sound( ent, G_SoundIndex( "sound/movers/objects/objectBreak.wav" ) ); return; } //do impact physics if ( ent->s.pos.trType == TR_GRAVITY )//tr.fraction < 1.0f && {//FIXME: only do this if no trDelta if ( g_gravity.value <= 0 || tr.plane.normal.z < 0.7f ) { if ( ent->flags&(FL_BOUNCE | FL_BOUNCE_HALF) ) { if ( tr.fraction <= 0.0f ) { VectorCopy( &tr.endpos, &ent->r.currentOrigin ); VectorCopy( &tr.endpos, &ent->s.pos.trBase ); VectorClear( &ent->s.pos.trDelta ); ent->s.pos.trTime = level.time; } else { G_BounceObject( ent, &tr ); } } else {//slide down? //FIXME: slide off the slope } } else { ent->s.apos.trType = TR_STATIONARY; pitch_roll_for_slope( ent, &tr.plane.normal ); //ent->r.currentAngles[0] = 0;//FIXME: match to slope //ent->r.currentAngles[2] = 0;//FIXME: match to slope VectorCopy( &ent->r.currentAngles, &ent->s.apos.trBase ); //okay, we hit the floor, might as well stop or prediction will //make us go through the floor! //FIXME: this means we can't fall if something is pulled out from under us... G_StopObjectMoving( ent ); } } else if ( ent->s.weapon != WP_SABER ) { ent->s.apos.trType = TR_STATIONARY; pitch_roll_for_slope( ent, &tr.plane.normal ); //ent->r.currentAngles[0] = 0;//FIXME: match to slope //ent->r.currentAngles[2] = 0;//FIXME: match to slope VectorCopy( &ent->r.currentAngles, &ent->s.apos.trBase ); } //call touch func ent->touch( ent, &g_entities[tr.entityNum], &tr ); }
void CCam::mortarCam(camInfo_t *ci) { if (eth32.cg.snap->ps.ammo == 0) return; // Set mortar trajectory from current view vec3_t angles, forward; VectorCopy(eth32.cg.refdef->viewaxis[ROLL], forward); VectorCopy(eth32.cg.snap->ps.viewangles, angles); angles[PITCH] -= 60.f; AngleVectors(angles, forward, NULL, NULL); forward[0] *= 3000 * 1.1f; forward[1] *= 3000 * 1.1f; forward[2] *= 1500 * 1.1f; trajectory_t mortarTrajectory; mortarTrajectory.trType = TR_GRAVITY; mortarTrajectory.trTime = eth32.cg.time; VectorCopy(eth32.cg.muzzle, mortarTrajectory.trBase); VectorCopy(forward, mortarTrajectory.trDelta); // Calculate mortar impact int timeOffset = 0; trace_t mortarTrace; vec3_t mortarImpact; VectorCopy(mortarTrajectory.trBase, mortarImpact); #define TIME_STEP 20 while (timeOffset < 10000) { vec3_t nextPos; timeOffset += TIME_STEP; BG_EvaluateTrajectory(&mortarTrajectory, eth32.cg.time + timeOffset, nextPos, qfalse, 0); orig_CG_Trace(&mortarTrace, mortarImpact, 0, 0, nextPos, eth32.cg.snap->ps.clientNum, MASK_MISSILESHOT); if ((mortarTrace.fraction != 1) // Stop if we hit sky && !((mortarTrace.surfaceFlags & SURF_NODRAW) || (mortarTrace.surfaceFlags & SURF_NOIMPACT)) && (mortarTrace.contents != 0)) { break; } VectorCopy(nextPos, mortarImpact); } memcpy(&camRefDef, ð32.cg.refdef, sizeof(refdef_t)); // kobject: add some angles vec3_t dpos; vec3_t camOrg; dpos[0] = eth32.cg.refdef->vieworg[0]-mortarImpact[0]; dpos[1] = eth32.cg.refdef->vieworg[1]-mortarImpact[1]; dpos[2] = 0.0f; VectorNormalizeFast( dpos ); VectorCopy( mortarImpact, camOrg ); VectorMA( camOrg, ci->distance * sinf(ci->angle * M_PI/180.0), zAxis, camOrg ); VectorMA( camOrg, ci->distance * cosf(ci->angle * M_PI/180.0), dpos, camOrg ); int w = ci->x2 - ci->x1; int h = ci->y2 - ci->y1; camRefDef.fov_x = (w>h) ? ci->fov : ci->fov * w / h; camRefDef.fov_y = (h>w) ? ci->fov : ci->fov * h / w; VectorCopy(camOrg, camRefDef.vieworg); vec3_t camAngle; VectorCopy(eth32.cg.refdefViewAngles, camAngle); camAngle[PITCH] = ci->angle; AnglesToAxis(camAngle, camRefDef.viewaxis); drawCam(ci->x1, ci->y1, w, h, &camRefDef, qtrue); // Draw impact time sprintf(this->str, "^7Impact Time: ^b%.1f ^7seconds", (float)timeOffset / 1000.0f); Draw.Text(ci->x1 + (w / 2) - (TEXTWIDTH(this->str) / 2), ci->y1 + h - 22 , 0.24f, str, GUI_FONTCOLOR1, qfalse, qtrue, ð32.cg.media.fontArial, true); }
/* ================ CG_AddFragment ================ */ void CG_AddFragment( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; if ( le->pos.trType == TR_STATIONARY ) { // sink into the ground if near the removal time int t; float oldZ; t = le->endTime - cg.time; if ( t < SINK_TIME ) { // we must use an explicit lighting origin, otherwise the // lighting would be lost as soon as the origin went // into the ground VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin ); le->refEntity.renderfx |= RF_LIGHTING_ORIGIN; oldZ = le->refEntity.origin[2]; le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME ); CG_AddRefEntityWithMinLight( &le->refEntity ); le->refEntity.origin[2] = oldZ; } else { CG_AddRefEntityWithMinLight( &le->refEntity ); } return; } // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin ); if ( le->leFlags & LEF_TUMBLE ) { vec3_t angles; BG_EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); } CG_AddRefEntityWithMinLight( &le->refEntity ); // add a blood trail if ( le->leBounceSoundType == LEBS_BLOOD ) { CG_BloodTrail( le ); } return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if ( CG_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { CG_FreeLocalEntity( le ); return; } // leave a mark CG_FragmentBounceMark( le, &trace ); // do a bouncy sound CG_FragmentBounceSound( le, &trace ); // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); CG_AddRefEntityWithMinLight( &le->refEntity ); }
/* ================ G_RunItem ================ */ void G_RunItem( gentity_t *ent ) { vec3_t origin; trace_t tr; int contents; int mask; // if its groundentity has been set to none, it may have been pushed off an edge if ( ent->s.groundEntityNum == ENTITYNUM_NONE ) { if ( ent->s.pos.trType != TR_GRAVITY ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; } } if ( ent->s.pos.trType == TR_STATIONARY || ent->s.pos.trType == TR_GRAVITY_PAUSED ) { //----(SA) // check think function G_RunThink( ent ); return; } // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // trace a line from the previous position to the current position if ( ent->clipmask ) { mask = ent->clipmask; } else { mask = MASK_SOLID; } trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->r.ownerNum, mask ); if ( ent->isProp && ent->takedamage ) { G_RunItemProp( ent, origin ); } VectorCopy( tr.endpos, ent->r.currentOrigin ); if ( tr.startsolid ) { tr.fraction = 0; } trap_LinkEntity( ent ); // FIXME: avoid this for stationary? // check think function G_RunThink( ent ); if ( tr.fraction == 1 ) { return; } // if it is in a nodrop volume, remove it contents = trap_PointContents( ent->r.currentOrigin, -1 ); if ( contents & CONTENTS_NODROP ) { if ( ent->item && ent->item->giType == IT_TEAM ) { Team_FreeEntity( ent ); } else { G_FreeEntity( ent ); } return; } G_BounceItem( ent, &tr ); }
void G_MissileImpact( gentity_t *ent, trace_t *trace ) { #else void G_MissileImpact( gentity_t *ent, trace_t *trace, int shaderNum ) { #endif gentity_t *other; qboolean hitClient = qfalse; #ifndef SMOKINGUNS #ifdef MISSIONPACK vec3_t forward, impactpoint, bouncedir; int eFlags; #endif #else qboolean hitKnife = qfalse; vec3_t bottledirs[ALC_COUNT]; #endif other = &g_entities[trace->entityNum]; #ifndef SMOKINGUNS // check for bounce if ( !other->takedamage && ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } #ifdef MISSIONPACK if ( other->takedamage ) { if ( ent->s.weapon != WP_PROX_LAUNCHER ) { if ( other->client && other->client->invulnerabilityTime > level.time ) { // VectorCopy( ent->s.pos.trDelta, forward ); VectorNormalize( forward ); if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) { VectorCopy( bouncedir, trace->plane.normal ); eFlags = ent->s.eFlags & EF_BOUNCE_HALF; ent->s.eFlags &= ~EF_BOUNCE_HALF; G_BounceMissile( ent, trace ); ent->s.eFlags |= eFlags; } ent->target_ent = other; return; } } } #endif // impact damage if (other->takedamage) { #else if(other->takedamage) hitKnife = qtrue; // check for bounce if ( ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); return; } if (other->takedamage && ent->s.weapon != WP_DYNAMITE) { #endif // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } #ifndef SMOKINGUNS G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); #else // you can't make dynamite exploding by using a knife if(!(ent->s.weapon == WP_KNIFE && other->s.weapon == WP_DYNAMITE && other->s.eType == ET_ITEM)){ // prepare breakable, if not already initialized if(!(other->flags & FL_BREAKABLE_INIT)) G_BreakablePrepare(other, shaderNum); G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); } #endif } } #ifndef SMOKINGUNS if( ent->s.weapon == WP_PROX_LAUNCHER ) { if( ent->s.pos.trType != TR_GRAVITY ) { return; } // if it's a player, stick it on to them (flag them and remove this entity) if( other->s.eType == ET_PLAYER && other->health > 0 ) { ProximityMine_Player( ent, other ); return; } SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); G_SetOrigin( ent, trace->endpos ); ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags ); ent->think = ProximityMine_Activate; ent->nextthink = level.time + 2000; vectoangles( trace->plane.normal, ent->s.angles ); ent->s.angles[0] += 90; // link the prox mine to the other entity ent->enemy = other; ent->die = ProximityMine_Die; VectorCopy(trace->plane.normal, ent->movedir); VectorSet(ent->r.mins, -4, -4, -4); VectorSet(ent->r.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } if (!strcmp(ent->classname, "hook")) { gentity_t *nent; vec3_t v; nent = G_Spawn(); if ( other->takedamage && other->client ) { G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); nent->s.otherEntityNum = other->s.number; ent->enemy = other; v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5; SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth } else { VectorCopy(trace->endpos, v); G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); ent->enemy = NULL; } SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth nent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact nent->s.eType = ET_GENERAL; ent->s.eType = ET_GRAPPLE; G_SetOrigin( ent, v ); G_SetOrigin( nent, v ); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL; VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint); trap_LinkEntity( ent ); trap_LinkEntity( nent ); return; } #endif // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? #ifndef SMOKINGUNS if ( other->takedamage && other->client ) { #else // alcoohol impact if( !Q_stricmp(ent->classname, "alcohol")){ // no event //G_AddEvent( ent, EV_MISSILE_ALCOHOL, DirToByte( trace->plane.normal)); } else if( !Q_stricmp(ent->classname, "molotov")){ // we have to launch the whiskey drops int i; // set the directions for(i = 0; i < ALC_COUNT; i++){ int temp; VectorSet(bottledirs[i], (rand()%10)-5, (rand()%10)-5, (rand()%10)-3); // direction has to be exactly the same (client and server) temp = DirToByte(bottledirs[i]); ByteToDir(temp, bottledirs[i]); } // dirs BG_DirsToEntityState(&ent->s, bottledirs); // burning if(ent->s.apos.trDelta[0]) G_AddEvent( ent, EV_MISSILE_FIRE, DirToByte( trace->plane.normal)); // not burning else G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal)); } else if ( other->takedamage && other->client ) { #endif G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; #ifndef SMOKINGUNS } else if( trace->surfaceFlags & SURF_METALSTEPS ) { #else } else if( trace->surfaceFlags & SURF_METAL ) { #endif G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } #ifndef SMOKINGUNS ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; #else if(Q_stricmp(ent->classname, "knife")){ ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; } else { vec3_t dir; gitem_t *item; item = BG_FindItemForWeapon(WP_KNIFE); ent->s.modelindex = item-bg_itemlist; ent->s.modelindex2 = 1; ent->item = item; ent->s.eType = ET_ITEM; ent->s.pos.trType = TR_GRAVITY; ent->physicsBounce = 0.01f; ent->r.contents = CONTENTS_TRIGGER; ent->touch = Touch_Item; ent->nextthink = level.time + 100; ent->think = G_KnifeThink; ent->wait = level.time + 30000; ent->flags |= FL_THROWN_ITEM; vectoangles(ent->s.pos.trDelta, dir); VectorCopy(dir, ent->s.apos.trBase); VectorCopy(dir, ent->r.currentAngles); } //modified by Spoon END #endif SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, trace->endpos ); // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) { if( !hitClient ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } // spawn alcohol missiles #ifdef SMOKINGUNS if(!Q_stricmp(ent->classname, "molotov")){ BottleBreak( ent, trace->endpos, trace->plane.normal, bottledirs); } #endif trap_LinkEntity( ent ); } /* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin; trace_t tr; int passent; #ifdef SMOKINGUNS int shaderNum; gentity_t *traceEnt; #endif // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // if this missile bounced off an invulnerability sphere if ( ent->target_ent ) { passent = ent->target_ent->s.number; } #ifndef SMOKINGUNS // prox mines that left the owner bbox will attach to anything, even the owner else if (ent->s.weapon == WP_PROX_LAUNCHER && ent->count) { passent = ENTITYNUM_NONE; } #endif else { // ignore interactions with the missile owner passent = ent->r.ownerNum; } // trace a line from the previous position to the current position #ifndef SMOKINGUNS trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); #else shaderNum = trap_Trace_New2( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); traceEnt = &g_entities[tr.entityNum]; #endif if ( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in #ifndef SMOKINGUNS trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask ); #else trap_Trace_New( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask ); #endif tr.fraction = 0; } else { VectorCopy( tr.endpos, ent->r.currentOrigin ); } trap_LinkEntity( ent ); if ( tr.fraction != 1 ) { #ifdef SMOKINGUNS VectorCopy(origin, ent->s.origin2); #endif // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { // If grapple, reset owner if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) { ent->parent->client->hook = NULL; } // if its a dynamite or molotov let it move 10 seconds before deleting it #ifdef SMOKINGUNS if(ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_MOLOTOV || ent->s.weapon == WP_KNIFE){ if(ent->mappart >= level.time && ent->mappart){ goto think; } else if(ent->mappart){ ent->mappart = 0; } else { ent->mappart = level.time + 5000; goto think; } } #endif G_FreeEntity( ent ); return; } #ifndef SMOKINGUNS G_MissileImpact( ent, &tr ); if ( ent->s.eType != ET_MISSILE ) { #else G_MissileImpact( ent, &tr, shaderNum ); if ( ent->s.eType != ET_MISSILE && ent->s.eType != ET_ITEM) { #endif return; // exploded } } #ifndef SMOKINGUNS // if the prox mine wasn't yet outside the player body if (ent->s.weapon == WP_PROX_LAUNCHER && !ent->count) { // check if the prox mine is outside the owner bbox trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ENTITYNUM_NONE, ent->clipmask ); if (!tr.startsolid || tr.entityNum != ent->r.ownerNum) { ent->count = 1; } } #endif think: // check think function after bouncing G_RunThink( ent ); } //============================================================================= #ifndef SMOKINGUNS /* ================= fire_plasma ================= */ gentity_t *fire_plasma (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "plasma"; bolt->nextthink = level.time + 10000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_PLASMAGUN; bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = 20; bolt->splashDamage = 15; bolt->splashRadius = 20; bolt->methodOfDeath = MOD_PLASMA; bolt->splashMethodOfDeath = MOD_PLASMA_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 2000, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; } //============================================================================= /* ================= fire_grenade ================= */ gentity_t *fire_grenade (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "grenade"; bolt->nextthink = level.time + 2500; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_GRENADE_LAUNCHER; bolt->s.eFlags = EF_BOUNCE_HALF; bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = 100; bolt->splashDamage = 100; bolt->splashRadius = 150; bolt->methodOfDeath = MOD_GRENADE; bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 700, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; }
static qboolean NPC_Jump( vec3_t dest, int goalEntNum ) {//FIXME: if land on enemy, knock him down & jump off again float targetDist, travelTime, impactDist, bestImpactDist = Q3_INFINITE;//fireSpeed, float originalShotSpeed, shotSpeed, speedStep = 50.0f, minShotSpeed = 30.0f, maxShotSpeed = 500.0f; qboolean belowBlocked = qfalse, aboveBlocked = qfalse; vec3_t targetDir, shotVel, failCase; trace_t trace; trajectory_t tr; qboolean blocked; int elapsedTime, timeStep = 250, hitCount = 0, aboveTries = 0, belowTries = 0, maxHits = 10; vec3_t lastPos, testPos, bottom; VectorSubtract( dest, NPC->r.currentOrigin, targetDir ); targetDist = VectorNormalize( targetDir ); //make our shotSpeed reliant on the distance originalShotSpeed = targetDist;//DistanceHorizontal( dest, NPC->currentOrigin )/2.0f; if ( originalShotSpeed > maxShotSpeed ) { originalShotSpeed = maxShotSpeed; } else if ( originalShotSpeed < minShotSpeed ) { originalShotSpeed = minShotSpeed; } shotSpeed = originalShotSpeed; while ( hitCount < maxHits ) { VectorScale( targetDir, shotSpeed, shotVel ); travelTime = targetDist/shotSpeed; shotVel[2] += travelTime * 0.5 * NPC->client->ps.gravity; if ( !hitCount ) {//save the first one as the worst case scenario VectorCopy( shotVel, failCase ); } if ( 1 )//tracePath ) {//do a rough trace of the path blocked = qfalse; VectorCopy( NPC->r.currentOrigin, tr.trBase ); VectorCopy( shotVel, tr.trDelta ); tr.trType = TR_GRAVITY; tr.trTime = level.time; travelTime *= 1000.0f; VectorCopy( NPC->r.currentOrigin, lastPos ); //This may be kind of wasteful, especially on long throws... use larger steps? Divide the travelTime into a certain hard number of slices? Trace just to apex and down? for ( elapsedTime = timeStep; elapsedTime < floor(travelTime)+timeStep; elapsedTime += timeStep ) { if ( (float)elapsedTime > travelTime ) {//cap it elapsedTime = floor( travelTime ); } BG_EvaluateTrajectory( &tr, level.time + elapsedTime, testPos ); //F**K IT, always check for do not enter... trap_Trace( &trace, lastPos, NPC->r.mins, NPC->r.maxs, testPos, NPC->s.number, NPC->clipmask|CONTENTS_BOTCLIP ); /* if ( testPos[2] < lastPos[2] && elapsedTime < floor( travelTime ) ) {//going down, haven't reached end, ignore botclip gi.trace( &trace, lastPos, NPC->mins, NPC->maxs, testPos, NPC->s.number, NPC->clipmask ); } else {//going up, check for botclip gi.trace( &trace, lastPos, NPC->mins, NPC->maxs, testPos, NPC->s.number, NPC->clipmask|CONTENTS_BOTCLIP ); } */ if ( trace.allsolid || trace.startsolid ) {//started in solid if ( NAVDEBUG_showCollision ) { G_DrawEdge( lastPos, trace.endpos, EDGE_RED_TWOSECOND ); } return qfalse;//you're hosed, dude } if ( trace.fraction < 1.0f ) {//hit something if ( NAVDEBUG_showCollision ) { G_DrawEdge( lastPos, trace.endpos, EDGE_RED_TWOSECOND ); // TryJump } if ( trace.entityNum == goalEntNum ) {//hit the enemy, that's bad! blocked = qtrue; /* if ( g_entities[goalEntNum].client && g_entities[goalEntNum].client->ps.groundEntityNum == ENTITYNUM_NONE ) {//bah, would collide in mid-air, no good blocked = qtrue; } else {//he's on the ground, good enough, I guess //Hmm, don't want to land on him, though...? } */ break; } else { if ( trace.contents & CONTENTS_BOTCLIP ) {//hit a do-not-enter brush blocked = qtrue; break; } if ( trace.plane.normal[2] > 0.7 && DistanceSquared( trace.endpos, dest ) < 4096 )//hit within 64 of desired location, should be okay {//close enough! break; } else {//FIXME: maybe find the extents of this brush and go above or below it on next try somehow? impactDist = DistanceSquared( trace.endpos, dest ); if ( impactDist < bestImpactDist ) { bestImpactDist = impactDist; VectorCopy( shotVel, failCase ); } blocked = qtrue; break; } } } else { if ( NAVDEBUG_showCollision ) { G_DrawEdge( lastPos, testPos, EDGE_WHITE_TWOSECOND ); // TryJump } } if ( elapsedTime == floor( travelTime ) ) {//reached end, all clear if ( trace.fraction >= 1.0f ) {//hmm, make sure we'll land on the ground... //FIXME: do we care how far below ourselves or our dest we'll land? VectorCopy( trace.endpos, bottom ); bottom[2] -= 128; trap_Trace( &trace, trace.endpos, NPC->r.mins, NPC->r.maxs, bottom, NPC->s.number, NPC->clipmask ); if ( trace.fraction >= 1.0f ) {//would fall too far blocked = qtrue; } } break; } else { //all clear, try next slice VectorCopy( testPos, lastPos ); } } if ( blocked ) {//hit something, adjust speed (which will change arc) hitCount++; //alternate back and forth between trying an arc slightly above or below the ideal if ( (hitCount%2) && !belowBlocked ) {//odd belowTries++; shotSpeed = originalShotSpeed - (belowTries*speedStep); } else if ( !aboveBlocked ) {//even aboveTries++; shotSpeed = originalShotSpeed + (aboveTries*speedStep); } else {//can't go any higher or lower hitCount = maxHits; break; } if ( shotSpeed > maxShotSpeed ) { shotSpeed = maxShotSpeed; aboveBlocked = qtrue; } else if ( shotSpeed < minShotSpeed ) { shotSpeed = minShotSpeed; belowBlocked = qtrue; } } else {//made it! break; } } else {//no need to check the path, go with first calc break; } } if ( hitCount >= maxHits ) {//NOTE: worst case scenario, use the one that impacted closest to the target (or just use the first try...?) return qfalse; //NOTE: or try failcase? //VectorCopy( failCase, NPC->client->ps.velocity ); //return qtrue; } VectorCopy( shotVel, NPC->client->ps.velocity ); return qtrue; }
/* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin, groundSpot; trace_t tr; int passent; qboolean isKnockedSaber = qfalse; if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF)) { isKnockedSaber = qtrue; ent->s.pos.trType = TR_GRAVITY; } // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // if this missile bounced off an invulnerability sphere if ( ent->target_ent ) { passent = ent->target_ent->s.number; } else { // ignore interactions with the missile owner if ( (ent->r.svFlags&SVF_OWNERNOTSHARED) && (ent->s.eFlags&EF_JETPACK_ACTIVE) ) { //A vehicle missile that should be solid to its owner //I don't care about hitting my owner passent = ent->s.number; } else { passent = ent->r.ownerNum; } } // trace a line from the previous position to the current position if (d_projectileGhoul2Collision.integer) { trap->Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask, qfalse, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_THICK|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer ); if (tr.fraction != 1.0 && tr.entityNum < ENTITYNUM_WORLD) { gentity_t *g2Hit = &g_entities[tr.entityNum]; if (g2Hit->inuse && g2Hit->client && g2Hit->ghoul2) { //since we used G2TRFLAG_GETSURFINDEX, tr.surfaceFlags will actually contain the index of the surface on the ghoul2 model we collided with. g2Hit->client->g2LastSurfaceHit = tr.surfaceFlags; g2Hit->client->g2LastSurfaceTime = level.time; } if (g2Hit->ghoul2) { tr.surfaceFlags = 0; //clear the surface flags after, since we actually care about them in here. } } } else { trap->Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask, qfalse, 0, 0 ); } if ( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in trap->Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask, qfalse, 0, 0 ); tr.fraction = 0; } else { VectorCopy( tr.endpos, ent->r.currentOrigin ); } if (ent->passThroughNum && tr.entityNum == (ent->passThroughNum-1)) { VectorCopy( origin, ent->r.currentOrigin ); trap->LinkEntity( (sharedEntity_t *)ent ); goto passthrough; } trap->LinkEntity( (sharedEntity_t *)ent ); if (ent->s.weapon == G2_MODEL_PART && !ent->bounceCount) { vec3_t lowerOrg; trace_t trG; VectorCopy(ent->r.currentOrigin, lowerOrg); lowerOrg[2] -= 1; trap->Trace( &trG, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, lowerOrg, passent, ent->clipmask, qfalse, 0, 0 ); VectorCopy(trG.endpos, groundSpot); if (!trG.startsolid && !trG.allsolid && trG.entityNum == ENTITYNUM_WORLD) { ent->s.groundEntityNum = trG.entityNum; } else { ent->s.groundEntityNum = ENTITYNUM_NONE; } } if ( tr.fraction != 1) { // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { // If grapple, reset owner if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) { ent->parent->client->hook = NULL; } if ((ent->s.weapon == WP_SABER && ent->isSaberEntity) || isKnockedSaber) { G_RunThink( ent ); return; } else if (ent->s.weapon != G2_MODEL_PART) { G_FreeEntity( ent ); return; } } #if 0 //will get stomped with missile impact event... if (ent->s.weapon > WP_NONE && ent->s.weapon < WP_NUM_WEAPONS && (tr.entityNum < MAX_CLIENTS || g_entities[tr.entityNum].s.eType == ET_NPC)) { //player or NPC, try making a mark on him /* gentity_t *evEnt = G_TempEntity(ent->r.currentOrigin, EV_GHOUL2_MARK); evEnt->s.owner = tr.entityNum; //the entity the mark should be placed on evEnt->s.weapon = ent->s.weapon; //the weapon used (to determine mark type) VectorCopy(ent->r.currentOrigin, evEnt->s.origin); //the point of impact //origin2 gets the predicted trajectory-based position. BG_EvaluateTrajectory( &ent->s.pos, level.time, evEnt->s.origin2 ); //If they are the same, there will be problems. if (VectorCompare(evEnt->s.origin, evEnt->s.origin2)) { evEnt->s.origin2[2] += 2; //whatever, at least it won't mess up. } */ //ok, let's try adding it to the missile ent instead (tempents bad!) G_AddEvent(ent, EV_GHOUL2_MARK, 0); //copy current pos to s.origin, and current projected traj to origin2 VectorCopy(ent->r.currentOrigin, ent->s.origin); BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin2 ); //the index for whoever we are hitting ent->s.otherEntityNum = tr.entityNum; if (VectorCompare(ent->s.origin, ent->s.origin2)) { ent->s.origin2[2] += 2.0f; //whatever, at least it won't mess up. } } #else if (ent->s.weapon > WP_NONE && ent->s.weapon < WP_NUM_WEAPONS && (tr.entityNum < MAX_CLIENTS || g_entities[tr.entityNum].s.eType == ET_NPC)) { //player or NPC, try making a mark on him //copy current pos to s.origin, and current projected traj to origin2 VectorCopy(ent->r.currentOrigin, ent->s.origin); BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin2 ); if (VectorCompare(ent->s.origin, ent->s.origin2)) { ent->s.origin2[2] += 2.0f; //whatever, at least it won't mess up. } } #endif G_MissileImpact( ent, &tr ); if (tr.entityNum == ent->s.otherEntityNum) { //if the impact event other and the trace ent match then it's ok to do the g2 mark ent->s.trickedentindex = 1; } if ( ent->s.eType != ET_MISSILE && ent->s.weapon != G2_MODEL_PART ) { return; // exploded } } passthrough: if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) ) { //stuck missiles should check some special stuff G_RunStuckMissile( ent ); return; } if (ent->s.weapon == G2_MODEL_PART) { if (ent->s.groundEntityNum == ENTITYNUM_WORLD) { ent->s.pos.trType = TR_LINEAR; VectorClear(ent->s.pos.trDelta); ent->s.pos.trTime = level.time; VectorCopy(groundSpot, ent->s.pos.trBase); VectorCopy(groundSpot, ent->r.currentOrigin); if (ent->s.apos.trType != TR_STATIONARY) { ent->s.apos.trType = TR_STATIONARY; ent->s.apos.trTime = level.time; ent->s.apos.trBase[ROLL] = 0; ent->s.apos.trBase[PITCH] = 0; } } } // check think function after bouncing G_RunThink( ent ); }
/* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin; trace_t tr; int passent; // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // if this missile bounced off an invulnerability sphere if ( ent->target_ent ) { passent = ent->target_ent->s.number; } #ifdef MISSIONPACK // prox mines that left the owner bbox will attach to anything, even the owner else if (ent->s.weapon == WP_PROX_LAUNCHER && ent->count) { passent = ENTITYNUM_NONE; } #endif else { // ignore interactions with the missile owner passent = ent->r.ownerNum; } // trace a line from the previous position to the current position trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); if ( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask ); tr.fraction = 0; } else { VectorCopy( tr.endpos, ent->r.currentOrigin ); } trap_LinkEntity( ent ); if ( tr.fraction != 1 ) { // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { // If grapple, reset owner if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) { ent->parent->client->hook = NULL; } G_FreeEntity( ent ); return; } G_MissileImpact( ent, &tr ); if ( ent->s.eType != ET_MISSILE ) { return; // exploded } } #ifdef MISSIONPACK // if the prox mine wasn't yet outside the player body if (ent->s.weapon == WP_PROX_LAUNCHER && !ent->count) { // check if the prox mine is outside the owner bbox trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ENTITYNUM_NONE, ent->clipmask ); if (!tr.startsolid || tr.entityNum != ent->r.ownerNum) { ent->count = 1; } } #endif // check think function after bouncing G_RunThink( ent ); }
void G_RunMissile( gentity_t *ent ) { vector3 origin, groundSpot; trace_t tr; int passent; qboolean isKnockedSaber = qfalse; if ( ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF) ) { isKnockedSaber = qtrue; ent->s.pos.trType = TR_GRAVITY; } // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, &origin ); // if this missile bounced off an invulnerability sphere if ( ent->target_ent ) { passent = ent->target_ent->s.number; } else { // ignore interactions with the missile owner if ( (ent->r.svFlags&SVF_OWNERNOTSHARED) && (ent->s.eFlags&EF_JETPACK_ACTIVE) ) {//A vehicle missile that should be solid to its owner //I don't care about hitting my owner passent = ent->s.number; } else { passent = ent->r.ownerNum; } } // trace a line from the previous position to the current position if ( d_projectileGhoul2Collision.integer ) { trap->Trace( &tr, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &origin, passent, ent->clipmask, qfalse, G2TRFLAG_DOGHOULTRACE | G2TRFLAG_GETSURFINDEX | G2TRFLAG_THICK | G2TRFLAG_HITCORPSES, g_g2TraceLod.integer ); if ( tr.fraction != 1.0f && tr.entityNum < ENTITYNUM_WORLD ) { gentity_t *g2Hit = &g_entities[tr.entityNum]; if ( g2Hit->inuse && g2Hit->client && g2Hit->ghoul2 ) { //since we used G2TRFLAG_GETSURFINDEX, tr.surfaceFlags will actually contain the index of the surface on the ghoul2 model we collided with. g2Hit->client->g2LastSurfaceHit = tr.surfaceFlags; g2Hit->client->g2LastSurfaceTime = level.time; } if ( g2Hit->ghoul2 ) { tr.surfaceFlags = 0; //clear the surface flags after, since we actually care about them in here. } //Raz: Portals! if ( g2Hit->s.eType == ET_SPECIAL && g2Hit->s.userInt1 ) { if ( g2Hit->touch ) { g2Hit->touch( g2Hit, ent, &tr ); } JPLua::Entity_CallFunction( g2Hit, JPLua::JPLUA_ENTITY_TOUCH, (intptr_t)ent, (intptr_t)&tr ); goto passthrough; } } } else { trap->Trace( &tr, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &origin, passent, ent->clipmask, qfalse, 0, 0 ); } if ( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in trap->Trace( &tr, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &ent->r.currentOrigin, passent, ent->clipmask, qfalse, 0, 0 ); tr.fraction = 0; } else { VectorCopy( &tr.endpos, &ent->r.currentOrigin ); } if ( ent->passThroughNum && tr.entityNum == (ent->passThroughNum - 1) ) { VectorCopy( &origin, &ent->r.currentOrigin ); trap->LinkEntity( (sharedEntity_t *)ent ); goto passthrough; } trap->LinkEntity( (sharedEntity_t *)ent ); if ( ent->s.weapon == G2_MODEL_PART && !ent->bounceCount ) { vector3 lowerOrg; trace_t trG; VectorCopy( &ent->r.currentOrigin, &lowerOrg ); lowerOrg.z -= 1; trap->Trace( &trG, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &lowerOrg, passent, ent->clipmask, qfalse, 0, 0 ); VectorCopy( &trG.endpos, &groundSpot ); if ( !trG.startsolid && !trG.allsolid && trG.entityNum == ENTITYNUM_WORLD ) { ent->s.groundEntityNum = trG.entityNum; } else { ent->s.groundEntityNum = ENTITYNUM_NONE; } } if ( ent->parent && ent->parent->client && ent->parent->client->hook && ent->parent->client->hook == ent && (ent->parent->client->ps.duelInProgress || BG_SaberInSpecial( ent->parent->client->ps.saberMove ) || !(japp_allowHook.integer & (1 << level.gametype)) || ent->parent->client->pers.adminData.isSlept || g_entities[tr.entityNum].client) ) { // not allowed to have hook out Weapon_HookFree( ent ); return; } if ( tr.fraction != 1 ) { // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { // If grapple, reset owner // if ( ent->parent && ent->parent->client && ent->parent->client->hook == ent ) // ent->parent->client->hook = NULL; if ( ent->parent && ent->parent->client && ent->parent->client->hook && ent->parent->client->hook == ent ) { Weapon_HookFree( ent->parent->client->hook ); } if ( (ent->s.weapon == WP_SABER && ent->isSaberEntity) || isKnockedSaber ) { G_RunThink( ent ); return; } else if ( ent->s.weapon != G2_MODEL_PART ) { G_FreeEntity( ent ); return; } } if ( ent->s.weapon > WP_NONE && ent->s.weapon < WP_NUM_WEAPONS && (tr.entityNum < MAX_CLIENTS || g_entities[tr.entityNum].s.eType == ET_NPC) ) { //player or NPC, try making a mark on him //copy current pos to s.origin, and current projected traj to origin2 VectorCopy( &ent->r.currentOrigin, &ent->s.origin ); BG_EvaluateTrajectory( &ent->s.pos, level.time, &ent->s.origin2 ); if ( VectorCompare( &ent->s.origin, &ent->s.origin2 ) ) { ent->s.origin2.z += 2.0f; //whatever, at least it won't mess up. } } G_MissileImpact( ent, &tr ); if ( tr.entityNum == ent->s.otherEntityNum ) { // if the impact event other and the trace ent match then it's ok to do the g2 mark ent->s.trickedEntIndex[0] = 1; } if ( ent->s.eType != ET_MISSILE && ent->s.weapon != G2_MODEL_PART ) { return; // exploded } } passthrough: if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags & EF_MISSILE_STICK) ) { // stuck missiles should check some special stuff G_RunStuckMissile( ent ); return; } if ( ent->s.weapon == G2_MODEL_PART ) { if ( ent->s.groundEntityNum == ENTITYNUM_WORLD ) { ent->s.pos.trType = TR_LINEAR; VectorClear( &ent->s.pos.trDelta ); ent->s.pos.trTime = level.time; VectorCopy( &groundSpot, &ent->s.pos.trBase ); VectorCopy( &groundSpot, &ent->r.currentOrigin ); if ( ent->s.apos.trType != TR_STATIONARY ) { ent->s.apos.trType = TR_STATIONARY; ent->s.apos.trTime = level.time; ent->s.apos.trBase.roll = 0; ent->s.apos.trBase.pitch = 0; } } } // check think function after bouncing G_RunThink( ent ); }
/* ================ CG_AddDebrisElements ================ */ void CG_AddDebrisElements( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; float lifeFrac; int t, step = 50; for (t = le->lastTrailTime + step; t < cg.time; t += step) { // calculate new position BG_EvaluateTrajectory( &le->pos, t, newOrigin ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, MASK_SHOT ); // if stuck, kill it if (trace.startsolid) { // HACK, some walls screw up, so just pass through if starting in a solid VectorCopy( newOrigin, trace.endpos ); trace.fraction = 1.0; } // moved some distance VectorCopy( trace.endpos, le->refEntity.origin ); // add a trail lifeFrac = (float)(t - le->startTime) / (float)(le->endTime - le->startTime); #if 0 // fire #if 1 // flame if (le->effectWidth > 0) { le->headJuncIndex = CG_AddSparkJunc( le->headJuncIndex, cgs.media.fireTrailShader, le->refEntity.origin, (int)(500.0 * (0.5 + 0.5*(1.0 - lifeFrac))), // trail life 1.0, // alpha 0.5, // end alpha 3, // start width le->effectWidth ); // end width #else // spark line if (le->effectWidth > 0) { le->headJuncIndex = CG_AddSparkJunc( le->headJuncIndex, cgs.media.sparkParticleShader, le->refEntity.origin, (int)(600.0 * (0.5 + 0.5*(0.5 - lifeFrac))), // trail life 1.0 - lifeFrac*2, // alpha 0.5 * (1.0 - lifeFrac), // end alpha 5.0 * (1.0 - lifeFrac), // start width 5.0 * (1.0 - lifeFrac) ); // end width #endif } #endif // smoke if (le->effectFlags & 1) { le->headJuncIndex2 = CG_AddSmokeJunc( le->headJuncIndex2, cgs.media.smokeTrailShader, le->refEntity.origin, (int)(2000.0 * (0.5 + 0.5*(1.0 - lifeFrac))), // trail life 1.0 * (trace.fraction == 1.0) * (0.5 + 0.5 * (1.0 - lifeFrac)), // alpha 1, // start width (int)(60.0 * (0.5 + 0.5*(1.0 - lifeFrac))) ); // end width } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels // if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { // CG_FreeLocalEntity( le ); // return; // } if (trace.fraction < 1.0) { // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); if (VectorLength(le->pos.trDelta) < 1) { CG_FreeLocalEntity( le ); return; } // the intersection is a fraction of the frametime le->pos.trTime = t; } le->lastTrailTime = t; } } // Rafael Shrapnel /* =============== CG_AddShrapnel =============== */ void CG_AddShrapnel (localEntity_t *le) { vec3_t newOrigin; trace_t trace; if ( le->pos.trType == TR_STATIONARY ) { // sink into the ground if near the removal time int t; float oldZ; t = le->endTime - cg.time; if ( t < SINK_TIME ) { // we must use an explicit lighting origin, otherwise the // lighting would be lost as soon as the origin went // into the ground VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin ); le->refEntity.renderfx |= RF_LIGHTING_ORIGIN; oldZ = le->refEntity.origin[2]; le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME ); trap_R_AddRefEntityToScene( &le->refEntity ); le->refEntity.origin[2] = oldZ; } else { trap_R_AddRefEntityToScene( &le->refEntity ); CG_AddParticleShrapnel (le); } return; } // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin ); if ( le->leFlags & LEF_TUMBLE ) { vec3_t angles; BG_EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); } trap_R_AddRefEntityToScene( &le->refEntity ); CG_AddParticleShrapnel (le); return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { CG_FreeLocalEntity( le ); return; } // leave a mark CG_FragmentBounceMark( le, &trace ); // do a bouncy sound CG_FragmentBounceSound( le, &trace ); // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); trap_R_AddRefEntityToScene( &le->refEntity ); CG_AddParticleShrapnel (le); }
/* ================ CG_AddFragment ================ */ void CG_AddFragment( localEntity_t *le ) { vec3_t newOrigin; trace_t trace; if (le->forceAlpha) { le->refEntity.renderfx |= RF_FORCE_ENT_ALPHA; le->refEntity.shaderRGBA[3] = le->forceAlpha; } if ( le->pos.trType == TR_STATIONARY ) { // sink into the ground if near the removal time int t; float t_e; t = le->endTime - cg.time; if ( t < (SINK_TIME*2) ) { le->refEntity.renderfx |= RF_FORCE_ENT_ALPHA; t_e = (float)((float)(le->endTime - cg.time)/(SINK_TIME*2)); t_e = (int)((t_e)*255); if (t_e > 255) { t_e = 255; } if (t_e < 1) { t_e = 1; } if (le->refEntity.shaderRGBA[3] && t_e > le->refEntity.shaderRGBA[3]) { t_e = le->refEntity.shaderRGBA[3]; } le->refEntity.shaderRGBA[3] = t_e; trap->R_AddRefEntityToScene( &le->refEntity ); } else { trap->R_AddRefEntityToScene( &le->refEntity ); } return; } // calculate new position BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin ); // trace a line from previous position to new position CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); if ( trace.fraction == 1.0 ) { // still in free fall VectorCopy( newOrigin, le->refEntity.origin ); if ( le->leFlags & LEF_TUMBLE ) { vec3_t angles; BG_EvaluateTrajectory( &le->angles, cg.time, angles ); AnglesToAxis( angles, le->refEntity.axis ); ScaleModelAxis(&le->refEntity); } trap->R_AddRefEntityToScene( &le->refEntity ); // add a blood trail if ( le->leBounceSoundType == LEBS_BLOOD ) { CG_BloodTrail( le ); } return; } // if it is in a nodrop zone, remove it // this keeps gibs from waiting at the bottom of pits of death // and floating levels if ( trap->CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) { CG_FreeLocalEntity( le ); return; } if (!trace.startsolid) { // leave a mark CG_FragmentBounceMark( le, &trace ); // do a bouncy sound CG_FragmentBounceSound( le, &trace ); if (le->bounceSound) { //specified bounce sound (debris) trap->S_StartSound(le->pos.trBase, ENTITYNUM_WORLD, CHAN_AUTO, le->bounceSound); } // reflect the velocity on the trace plane CG_ReflectVelocity( le, &trace ); trap->R_AddRefEntityToScene( &le->refEntity ); } }
// use this to change between particle and trail code //#define BLOOD_PARTICLE_TRAIL void CG_BloodTrail( localEntity_t *le ) { int t; int t2; int step; vec3_t newOrigin; #ifndef BLOOD_PARTICLE_TRAIL static vec3_t col = {1,1,1}; #endif centity_t *cent; cent = &cg_entities[le->ownerNum]; if ( !cg_blood.integer ) { return; } // step = 150; #ifdef BLOOD_PARTICLE_TRAIL step = 10; #else // time it takes to move 3 units step = (1000*3)/VectorLength(le->pos.trDelta); #endif if (cent && cent->currentState.aiChar == AICHAR_ZOMBIE) step = 30; t = step * ( (cg.time - cg.frametime + step ) / step ); t2 = step * ( cg.time / step ); for ( ; t <= t2; t += step ) { BG_EvaluateTrajectory( &le->pos, t, newOrigin ); #ifdef BLOOD_PARTICLE_TRAIL CG_Particle_Bleed (cgs.media.smokePuffShader, newOrigin, vec3_origin, 0, 500+rand()%200); #else if (cent && cent->currentState.aiChar == AICHAR_ZOMBIE) { CG_Particle_Bleed (cgs.media.smokePuffShader, newOrigin, vec3_origin, 1, 500+rand()%200); } else // Ridah, blood trail using trail code (should be faster since we don't have to spawn as many) le->headJuncIndex = CG_AddTrailJunc( le->headJuncIndex, cgs.media.bloodTrailShader, t, STYPE_STRETCH, newOrigin, 180, 1.0, // start alpha 0.0, // end alpha 12.0, 12.0, TJFL_NOCULL, col, col, 0, 0 ); #endif } }