/* ================ CG_Trace ================ */ void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int skipNumber, int mask ) { trace_t t; trap_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask); t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; // check all other solid models CG_ClipMoveToEntities( start, mins, maxs, end, skipNumber, mask, &t, TT_AABB ); *result = t; }
/* ================ 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 ); */ } }
void CGSyscall_CM_Trace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, int brushmask, int capsule ) { if ( capsule ) trap_CM_CapsuleTrace( results, start, end, mins, maxs, model, brushmask ); else trap_CM_BoxTrace( results, start, end, mins, maxs, model, brushmask ); }
/* ========================= CG_TouchTriggerPrediction Predict push triggers and items ========================= */ static void CG_TouchTriggerPrediction( void ) { int i; trace_t trace; entityState_t *ent; clipHandle_t cmodel; centity_t *cent; qboolean spectator; // dead players don't activate triggers if ( cg.cur_lc->predictedPlayerState.stats[STAT_HEALTH] <= 0 ) { return; } spectator = ( cg.cur_lc->predictedPlayerState.pm_type == PM_SPECTATOR ); if ( cg.cur_lc->predictedPlayerState.pm_type != PM_NORMAL && !spectator ) { return; } for ( i = 0 ; i < cg_numTriggerEntities ; i++ ) { cent = cg_triggerEntities[ i ]; ent = ¢->currentState; if ( ent->eType == ET_ITEM && !spectator ) { CG_TouchItem( cent ); continue; } if ( ent->collisionType != CT_SUBMODEL ) { continue; } cmodel = trap_CM_InlineModel( ent->modelindex ); if ( !cmodel ) { continue; } if ( cg.cur_lc->predictedPlayerState.collisionType == CT_CAPSULE ) { trap_CM_CapsuleTrace( &trace, cg.cur_lc->predictedPlayerState.origin, cg.cur_lc->predictedPlayerState.origin, cg.cur_lc->predictedPlayerState.mins, cg.cur_lc->predictedPlayerState.maxs, cmodel, -1 ); } else { trap_CM_BoxTrace( &trace, cg.cur_lc->predictedPlayerState.origin, cg.cur_lc->predictedPlayerState.origin, cg.cur_lc->predictedPlayerState.mins, cg.cur_lc->predictedPlayerState.maxs, cmodel, -1 ); } if ( !trace.startsolid ) { continue; } if ( ent->eType == ET_TELEPORT_TRIGGER ) { cg.cur_lc->hyperspace = qtrue; } else if ( ent->eType == ET_PUSH_TRIGGER ) { BG_TouchJumpPad( &cg.cur_lc->predictedPlayerState, ent ); } } // if we didn't touch a jump pad this pmove frame if ( cg.cur_lc->predictedPlayerState.jumppad_frame != cg.cur_lc->predictedPlayerState.pmove_framecount ) { cg.cur_lc->predictedPlayerState.jumppad_frame = 0; cg.cur_lc->predictedPlayerState.jumppad_ent = 0; } }