/* ================ G_RunItem ================ */ void G_RunItem( gentity_t *ent ) { vec3_t origin; trace_t tr; int contents; int mask; // if groundentity has been set to -1, it may have been pushed off an edge if ( ent->s.groundEntityNum == -1 ) { 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 ) { // 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_PLAYERSOLID & ~CONTENTS_BODY;//MASK_SOLID; } trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->r.ownerNum, mask ); 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 ); }
/* ================ 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; } // 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; } 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 } } // 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; } } // check think function after bouncing G_RunThink( ent ); }
/* * @brief A moving object that doesn't obey physics */ static void G_Physics_Noclip(g_edict_t *ent) { if (!G_RunThink(ent)) return; VectorMA(ent->s.angles, gi.frame_seconds, ent->locals.avelocity, ent->s.angles); VectorMA(ent->s.origin, gi.frame_seconds, ent->locals.velocity, ent->s.origin); gi.LinkEdict(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 ); // 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 ); ent->r.contents = CONTENTS_SOLID; //trick trap_LinkEntity into... trap_LinkEntity( ent ); ent->r.contents = 0; //...encoding bbox information 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 } // check think function after bouncing G_RunThink( ent ); }
void G_RunStuckMissile( gentity_t *ent ) { if ( ent->takedamage ) { if ( ent->s.groundEntityNum >= 0 && ent->s.groundEntityNum < ENTITYNUM_WORLD ) { gentity_t *other = &g_entities[ent->s.groundEntityNum]; if ( (!VectorCompare( &vec3_origin, &other->s.pos.trDelta ) && other->s.pos.trType != TR_STATIONARY) || (!VectorCompare( &vec3_origin, &other->s.apos.trDelta ) && other->s.apos.trType != TR_STATIONARY) ) {//thing I stuck to is moving or rotating now, kill me G_Damage( ent, other, other, NULL, NULL, 99999, 0, MOD_CRUSH ); return; } } } // check think function G_RunThink( ent ); }
/* ================ G_RunMover ================ */ void G_RunMover( gentity_t *ent ) { // if not a team captain, don't do anything, because // the captain will handle everything if ( ent->flags & FL_TEAMSLAVE ) { return; } // if stationary at one of the positions, don't move anything if ( ent->s.pos.trType != TR_STATIONARY || ent->s.apos.trType != TR_STATIONARY ) { G_MoverTeam( ent ); } // check think function G_RunThink( ent ); }
/* * @brief Bmodel objects don't interact with each other, but push all box objects */ static void G_Physics_Pusher(g_edict_t *ent) { vec3_t move, amove; g_edict_t *part, *mv; // if not a team captain, so movement will be handled elsewhere if (ent->locals.flags & FL_TEAM_SLAVE) return; // make sure all team slaves can move before committing // any moves or calling any think functions // if the move is blocked, all moved objects will be backed out // retry: g_pushed_p = g_pushed; for (part = ent; part; part = part->locals.team_chain) { if (!VectorCompare(part->locals.velocity, vec3_origin) || !VectorCompare( part->locals.avelocity, vec3_origin)) { // object is moving VectorScale(part->locals.velocity, gi.frame_seconds, move); VectorScale(part->locals.avelocity, gi.frame_seconds, amove); if (!G_Push(part, move, amove)) break; // move was blocked } } if (g_pushed_p > &g_pushed[MAX_EDICTS]) gi.Error("MAX_EDICTS exceeded\n"); if (part) { // the move failed, bump all next_think times and back out moves for (mv = ent; mv; mv = mv->locals.team_chain) { if (mv->locals.next_think > 0) mv->locals.next_think += gi.frame_millis; } // if the pusher has a "blocked" function, call it // otherwise, just stay in place until the obstacle is gone if (part->locals.Blocked) part->locals.Blocked(part, obstacle); } else { // the move succeeded, so call all think functions for (part = ent; part; part = part->locals.team_chain) { G_RunThink(part); } } }
void G_RunMover( gentity_t *ent ) { // if not a team captain, don't do anything, because // the captain will handle everything if ( ent->flags & FL_TEAMSLAVE ) { return; } // if stationary at one of the positions, don't move anything if ( ent->s.pos.trType != TR_STATIONARY || ent->s.apos.trType != TR_STATIONARY ) { //OSP: pause if ( level.pause.state == PAUSE_NONE ) G_MoverTeam( ent ); else ent->s.pos.trTime += level.time - level.previousTime; } // check think function G_RunThink( ent ); }
/* ================ G_RunItem ================ */ void G_RunItem( gentity_t *ent ) { vec3_t origin; trace_t tr; int contents; int mask; // if groundentity has been set to -1, 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 ) { // check think function G_RunThink( ent ); if ( !g_gravity->value ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; ent->s.pos.trDelta[0] += crandom() * 40.0f; // I dunno, just do this?? ent->s.pos.trDelta[1] += crandom() * 40.0f; ent->s.pos.trDelta[2] += random() * 20.0f; } else if ( (ent->flags&FL_DROPPED_ITEM) && ent->item && ent->item->giType == IT_WEAPON && ent->item->giTag == WP_SABER ) {//a dropped saber item, check below, just in case int ignore = ENTITYNUM_NONE; if ( ent->clipmask ) { mask = ent->clipmask; } else { mask = MASK_SOLID|CONTENTS_PLAYERCLIP;//shouldn't be able to get anywhere player can't } if ( ent->owner ) { ignore = ent->owner->s.number; } else if ( ent->activator ) { ignore = ent->activator->s.number; } VectorSet( origin, ent->currentOrigin[0], ent->currentOrigin[1], ent->currentOrigin[2]-1 ); gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ignore, mask, (EG2_Collision)0, 0 ); if ( !tr.allsolid && !tr.startsolid && tr.fraction > 0.001f ) {//wha? fall.... ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; } } return; } // get current position EvaluateTrajectory( &ent->s.pos, level.time, origin ); if ( ent->s.apos.trType != TR_STATIONARY ) { EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles ); G_SetAngles( ent, ent->currentAngles ); } // trace a line from the previous position to the current position if ( ent->clipmask ) { mask = ent->clipmask; } else { mask = MASK_SOLID|CONTENTS_PLAYERCLIP;//shouldn't be able to get anywhere player can't } int ignore = ENTITYNUM_NONE; if ( ent->owner ) { ignore = ent->owner->s.number; } else if ( ent->activator ) { ignore = ent->activator->s.number; } gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ignore, mask, (EG2_Collision)0, 0 ); VectorCopy( tr.endpos, ent->currentOrigin ); if ( tr.startsolid ) { tr.fraction = 0; } gi.linkentity( ent ); // FIXME: avoid this for stationary? // check think function G_RunThink( ent ); if ( tr.fraction == 1 ) { if ( g_gravity->value <= 0 ) { if ( ent->s.apos.trType != TR_LINEAR ) { VectorCopy( ent->currentAngles, ent->s.apos.trBase ); ent->s.apos.trType = TR_LINEAR; ent->s.apos.trDelta[1] = Q_flrand( -300, 300 ); ent->s.apos.trDelta[0] = Q_flrand( -10, 10 ); ent->s.apos.trDelta[2] = Q_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.1 ) { friction = 0.1f; } */ VectorScale( ent->s.pos.trDelta, friction, ent->s.pos.trDelta ); VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; } return; } // if it is in a nodrop volume, remove it contents = gi.pointcontents( ent->currentOrigin, -1 ); if ( contents & CONTENTS_NODROP ) { G_FreeEntity( ent ); return; } if ( !tr.startsolid ) { G_BounceItem( ent, &tr ); } }
/* ================ G_RunFrame Advances the non-player objects in the world ================ */ void G_RunFrame( int levelTime ) { int i; // if we are waiting for the level to restart, do nothing if( theLevel.restarted_ ) return; theLevel.framenum_++; theLevel.previousTime_ = theLevel.time_; theLevel.time_ = levelTime; int msec = theLevel.time_ - theLevel.previousTime_; // get any cvar changes G_UpdateCvars(); // // go through all allocated objects // int start = Sys_Milliseconds(); GameEntity* ent = 0;//&g_entities[0]; for( i=0 ; i<theLevel.num_entities_ ; i++ ) { ent = theLevel.getEntity(i); if( !ent || !ent->inuse_ ) continue; // clear events that are too old if( theLevel.time_ - ent->eventTime_ > EVENT_VALID_MSEC ) { if( ent->s.event ) { ent->s.event = 0; // &= EV_EVENT_BITS; if( ent->client_ ) { ent->client_->ps_.externalEvent = 0; // predicted events should never be set to zero //ent->client->ps.events[0] = 0; //ent->client->ps.events[1] = 0; } } if( ent->freeAfterEvent_ ) { // tempEntities or dropped items completely go away after their event ent->freeUp();// former G_FreeEntity continue; } else if( ent->unlinkAfterEvent_ ) { // items that will respawn will hide themselves after their pickup event ent->unlinkAfterEvent_ = false; SV_UnlinkEntity( ent ); } } // temporary entities don't think if( ent->freeAfterEvent_ ) continue; if( ent->s.eType == ET_MISSILE || ent->s.eType == ET_BULLET ) { G_RunMissile( ent ); continue; } if( ent->s.eType == ET_ITEM || ent->physicsObject_ ) { G_RunItem( ent ); continue; } if( ent->s.eType == ET_MOVER ) { G_RunMover( ent ); continue; } if( ent->client_ ) { G_RunClient( ent ); continue; } G_RunThink( ent ); } int end = Sys_Milliseconds(); start = Sys_Milliseconds(); // perform final fixups on the players //ent = &g_entities[0]; for( i=1 ; i <= theLevel.maxclients_ ; i++ ) { ent = theLevel.getEntity(i); if( ent && ent->inuse_ ) ClientEndFrame( ent ); } end = Sys_Milliseconds(); // see if it is time to do a tournement restart CheckTournament(); // see if it is time to end the level CheckExitRules(); // update to team status? CheckTeamStatus(); // cancel vote if timed out CheckVote(); // check team votes CheckTeamVote( ClientBase::TEAM_RED ); CheckTeamVote( ClientBase::TEAM_BLUE ); // for tracking changes CheckCvars(); if( g_listEntity.integer ) { for( i = 0; i < MAX_GENTITIES; i++ ) { ent = theLevel.getEntity(i); Com_Printf("%4i: %s\n", i, ent->classname_); } Cvar_Set("g_listEntity", "0"); } }
/* ================ G_Physics ================ */ void G_Physics( gentity_t *ent, int msec ) { vec3_t origin; trace_t tr; int contents; // if groundentity has been set to ENTITYNUM_NONE, it may have been pushed off an edge if ( ent->s.groundEntityNum == ENTITYNUM_NONE ) { if ( ent->s.eType == ET_BUILDABLE ) { if ( ent->s.pos.trType != BG_Buildable( ent->s.modelindex )->traj ) { ent->s.pos.trType = BG_Buildable( ent->s.modelindex )->traj; ent->s.pos.trTime = level.time; } } else 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 ) { // check think function G_RunThink( ent ); //check floor infrequently if ( ent->nextPhysicsTime < level.time ) { VectorCopy( ent->r.currentOrigin, origin ); VectorMA( origin, -2.0f, ent->s.origin2, origin ); trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, ent->clipmask ); if ( tr.fraction == 1.0f ) { ent->s.groundEntityNum = ENTITYNUM_NONE; } ent->nextPhysicsTime = level.time + PHYSICS_TIME; } return; } // trace a line from the previous position to the current position // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, ent->clipmask ); 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.0f ) { return; } // if it is in a nodrop volume, remove it contents = trap_PointContents( ent->r.currentOrigin, -1 ); if ( contents & CONTENTS_NODROP ) { G_FreeEntity( ent ); return; } G_Bounce( ent, &tr ); }
void G_RunMissile( gentity_t *ent ) { vec3_t oldOrg; trace_t tr; int trHitLoc=HL_NONE; if ( (ent->s.eFlags&EF_HELD_BY_SAND_CREATURE) ) {//in a sand creature's mouth if ( ent->activator ) { mdxaBone_t boltMatrix; // Getting the bolt here //in hand vec3_t scAngles = {0}; scAngles[YAW] = ent->activator->currentAngles[YAW]; gi.G2API_GetBoltMatrix( ent->activator->ghoul2, ent->activator->playerModel, ent->activator->gutBolt, &boltMatrix, scAngles, ent->activator->currentOrigin, (cg.time?cg.time:level.time), NULL, ent->activator->s.modelScale ); // Storing ent position, bolt position, and bolt axis gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent->currentOrigin ); G_SetOrigin( ent, ent->currentOrigin ); } // check think function G_RunThink( ent ); return; } VectorCopy( ent->currentOrigin, oldOrg ); // get current position if ( ent->s.pos.trType == TR_INTERPOLATE ) {//rolling missile? //FIXME: WTF?!! Sticks to stick missiles? //FIXME: they stick inside the player G_RollMissile( ent ); if ( ent->s.eType != ET_GENERAL ) {//didn't explode VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); gi.trace( &tr, oldOrg, ent->mins, ent->maxs, ent->currentOrigin, ent->s.number, ent->clipmask, G2_RETURNONHIT, 10 ); if ( VectorCompare( ent->s.pos.trDelta, vec3_origin ) ) { //VectorCopy( ent->currentAngles, ent->s.apos.trBase ); VectorClear( ent->s.apos.trDelta ); } else { vec3_t ang, fwdDir, rtDir; float speed; ent->s.apos.trType = TR_INTERPOLATE; VectorSet( ang, 0, ent->s.apos.trBase[1], 0 ); AngleVectors( ang, fwdDir, rtDir, NULL ); speed = VectorLength( ent->s.pos.trDelta )*4; //HMM, this works along an axis-aligned dir, but not along diagonals //This is because when roll gets to 90, pitch becomes yaw, and vice-versa //Maybe need to just set the angles directly? ent->s.apos.trDelta[0] = DotProduct( fwdDir, ent->s.pos.trDelta ); ent->s.apos.trDelta[1] = 0;//never spin! ent->s.apos.trDelta[2] = DotProduct( rtDir, ent->s.pos.trDelta ); VectorNormalize( ent->s.apos.trDelta ); VectorScale( ent->s.apos.trDelta, speed, ent->s.apos.trDelta ); ent->s.apos.trTime = level.previousTime; } } } else { vec3_t origin; EvaluateTrajectory( &ent->s.pos, level.time, origin ); // trace a line from the previous position to the current position, // ignoring interactions with the missile owner gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ent->owner ? ent->owner->s.number : ent->s.number, ent->clipmask, G2_COLLIDE, 10 ); if ( tr.entityNum != ENTITYNUM_NONE ) { gentity_t *other = &g_entities[tr.entityNum]; // check for hitting a lightsaber if ( other->contents & CONTENTS_LIGHTSABER ) {//hit a lightsaber bbox if ( other->owner && other->owner->client && !other->owner->client->ps.saberInFlight && ( Q_irand( 0, (other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE]*other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE]) ) == 0 || !InFront( ent->currentOrigin, other->owner->currentOrigin, other->owner->client->ps.viewangles, SABER_REFLECT_MISSILE_CONE ) ) )//other->owner->s.number == 0 && {//Jedi cannot block shots from behind! //re-trace from here, ignoring the lightsaber gi.trace( &tr, tr.endpos, ent->mins, ent->maxs, origin, tr.entityNum, ent->clipmask, G2_RETURNONHIT, 10 ); } } } VectorCopy( tr.endpos, ent->currentOrigin ); } // get current angles VectorMA( ent->s.apos.trBase, (level.time - ent->s.apos.trTime) * 0.001, ent->s.apos.trDelta, ent->s.apos.trBase ); //FIXME: Rolling things hitting G2 polys is weird /////////////////////////////////////////////////////// //? if ( tr.fraction != 1 ) { // did we hit or go near a Ghoul2 model? // qboolean hitModel = qfalse; for (int i=0; i < MAX_G2_COLLISIONS; i++) { if (tr.G2CollisionMap[i].mEntityNum == -1) { break; } CCollisionRecord &coll = tr.G2CollisionMap[i]; gentity_t *hitEnt = &g_entities[coll.mEntityNum]; // process collision records here... // make sure we only do this once, not for all the entrance wounds we might generate if ((coll.mFlags & G2_FRONTFACE)/* && !(hitModel)*/ && hitEnt->health) { if (trHitLoc==HL_NONE) { G_GetHitLocFromSurfName( &g_entities[coll.mEntityNum], gi.G2API_GetSurfaceName( &g_entities[coll.mEntityNum].ghoul2[coll.mModelIndex], coll.mSurfaceIndex ), &trHitLoc, coll.mCollisionPosition, NULL, NULL, ent->methodOfDeath ); } break; // NOTE: the way this whole section was working, it would only get inside of this IF once anyway, might as well break out now } } } ///////////////////////////////////////////////////////// if ( tr.startsolid ) { tr.fraction = 0; } gi.linkentity( ent ); if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) ) {//stuck missiles should check some special stuff G_RunStuckMissile( ent ); return; } // check think function G_RunThink( ent ); if ( ent->s.eType != ET_MISSILE ) { return; // exploded } if ( ent->mass ) { G_MoverTouchPushTriggers( ent, oldOrg ); } /* if ( !(ent->s.eFlags & EF_TELEPORT_BIT) ) { G_MoverTouchTeleportTriggers( ent, oldOrg ); if ( ent->s.eFlags & EF_TELEPORT_BIT ) {//was teleported return; } } else { ent->s.eFlags &= ~EF_TELEPORT_BIT; } */ AddSightEvent( ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED, 75 );//wakes them up when see a shot passes in front of them if ( !Q_irand( 0, 10 ) ) {//not so often... if ( ent->splashDamage && ent->splashRadius ) {//I'm an exploder, let people around me know danger is coming if ( ent->s.weapon == WP_TRIP_MINE ) {//??? } else { if ( ent->s.weapon == WP_ROCKET_LAUNCHER && ent->e_ThinkFunc == thinkF_rocketThink ) {//homing rocket- run like hell! AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER_GREAT, 50 ); } else { AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER, 50 ); } AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER ); } } else {//makes them run from near misses AddSightEvent( ent->owner, ent->currentOrigin, 48, AEL_DANGER, 50 ); } } if ( tr.fraction == 1 ) { if ( ent->s.weapon == WP_THERMAL && ent->s.pos.trType == TR_INTERPOLATE ) {//a rolling thermal that didn't hit anything G_MissileAddAlerts( ent ); } return; } // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { G_FreeEntity( ent ); return; } G_MissileImpact( ent, &tr, trHitLoc ); }
/* ================ G_RunFrame Advances the non-player objects in the world ================ */ void G_RunFrame( int levelTime ) { int i; gentity_t *ent; // if we are waiting for the level to restart, do nothing if ( level.restarted ) { return; } level.framenum++; level.previousTime = level.time; level.time = levelTime; // get any cvar changes G_UpdateCvars(); // // go through all allocated objects // ent = &g_entities[0]; for (i=0 ; i<level.num_entities ; i++, ent++) { if ( !ent->inuse ) { continue; } // clear events that are too old if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) { if ( ent->s.event ) { ent->s.event = 0; // &= EV_EVENT_BITS; if ( ent->client ) { ent->client->ps.externalEvent = 0; // predicted events should never be set to zero //ent->client->ps.events[0] = 0; //ent->client->ps.events[1] = 0; } } if ( ent->freeAfterEvent ) { // tempEntities or dropped items completely go away after their event G_FreeEntity( ent ); continue; } else if ( ent->unlinkAfterEvent ) { // items that will respawn will hide themselves after their pickup event ent->unlinkAfterEvent = qfalse; trap_UnlinkEntity( ent ); } } // temporary entities don't think if ( ent->freeAfterEvent ) { continue; } if ( !ent->r.linked && ent->neverFree ) { continue; } if ( ent->s.eType == ET_MISSILE ) { G_RunMissile( ent ); continue; } if ( ent->s.eType == ET_ITEM || ent->physicsObject ) { G_RunItem( ent ); continue; } if ( ent->s.eType == ET_MOVER ) { G_RunMover( ent ); continue; } if ( i < MAX_CLIENTS ) { G_RunClient( ent ); continue; } G_RunThink( ent ); } // perform final fixups on the players ent = &g_entities[0]; for (i=0 ; i < level.maxclients ; i++, ent++ ) { if ( ent->inuse ) { ClientEndFrame( ent ); } } // see if it is time to do a tournement restart CheckTournament(); // see if it is time to end the level CheckExitRules(); // update to team status? CheckTeamStatus(); // cancel vote if timed out CheckVote(); // check team votes CheckTeamVote( TEAM_RED ); CheckTeamVote( TEAM_BLUE ); // for tracking changes CheckCvars(); if (g_listEntity.integer) { for (i = 0; i < MAX_GENTITIES; i++) { G_Printf("%4i: %s\n", i, g_entities[i].classname); } trap_Cvar_Set("g_listEntity", "0"); } }
/* * @brief Non moving objects can only think */ static void G_Physics_None(g_edict_t *ent) { // regular thinking G_RunThink(ent); }
/* * @brief Toss, bounce, and fly movement. When on ground, do nothing. */ static void G_Physics_Toss(g_edict_t *ent) { vec3_t org, move; // regular thinking G_RunThink(ent); // if not a team captain, movement will be handled elsewhere if (ent->locals.flags & FL_TEAM_SLAVE) return; // check for the ground entity going away if (ent->locals.ground_entity) { if (!ent->locals.ground_entity->in_use) ent->locals.ground_entity = NULL; else if (ent->locals.velocity[2] > ent->locals.ground_entity->locals.velocity[2] + 0.1) ent->locals.ground_entity = NULL; else return; } // if on ground, or intentionally floating, return without moving if (ent->locals.ground_entity || (ent->locals.item && (ent->locals.spawn_flags & 4))) return; // enforce max velocity values G_ClampVelocity(ent); // move angles VectorMA(ent->s.angles, gi.frame_seconds, ent->locals.avelocity, ent->s.angles); // move origin VectorCopy(ent->s.origin, org); VectorScale(ent->locals.velocity, gi.frame_seconds, move); // push through the world, interacting with triggers and other ents c_trace_t trace = G_PushEntity(ent, move); if (!ent->in_use) return; if (trace.fraction < 1.0) { // move was blocked // if it was a floor, we might bounce or come to rest vec_t *vel = ent->locals.velocity; if (G_ClipVelocity(vel, trace.plane.normal, vel, 1.3) & 1) { VectorSubtract(ent->s.origin, org, move); // if we're approaching a stop, clear our velocity and set ground if (VectorLength(move) < STOP_EPSILON) { VectorClear(ent->locals.velocity); ent->locals.ground_entity = trace.ent; ent->locals.ground_entity_link_count = trace.ent->link_count; } else { // bounce and slide along the floor vec_t bounce, speed = VectorLength(ent->locals.velocity); bounce = sqrt(speed); if (ent->locals.velocity[2] < bounce) ent->locals.velocity[2] = bounce; } } // all impacts reduce velocity and angular velocity VectorScale(ent->locals.velocity, 0.9, ent->locals.velocity); VectorScale(ent->locals.avelocity, 0.9, ent->locals.avelocity); } // check for water transition const _Bool was_in_water = (ent->locals.water_type & MASK_WATER); ent->locals.water_type = gi.PointContents(ent->s.origin); const _Bool is_in_water = ent->locals.water_type & MASK_WATER; if (is_in_water) ent->locals.water_level = 1; else ent->locals.water_level = 0; // add gravity if (ent->locals.move_type == MOVE_TYPE_FLY) G_AddFlying(ent); else G_AddGravity(ent); if (!was_in_water && is_in_water) { gi.PositionedSound(ent->s.origin, g_game.edicts, gi.SoundIndex("world/water_in"), ATTEN_NORM); VectorScale(ent->locals.velocity, 0.66, ent->locals.velocity); } else if (was_in_water && !is_in_water) gi.PositionedSound(ent->s.origin, g_game.edicts, gi.SoundIndex("world/water_out"), ATTEN_NORM); // move team slaves g_edict_t *slave = ent->locals.team_chain; while (slave) { VectorCopy(ent->s.origin, slave->s.origin); gi.LinkEdict(slave); slave = slave->locals.team_chain; } }
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; }
void G_RunMissile( gentity_t *ent ) { vec3_t origin; trace_t tr; int passent; bool impact = false; // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // ignore interactions with the missile owner passent = ent->r.ownerNum; // general trace to see if we hit anything at all trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask, 0 ); if ( tr.startsolid || tr.allsolid ) { tr.fraction = 0.0f; VectorCopy( ent->r.currentOrigin, tr.endpos ); } if ( tr.fraction < 1.0f ) { if ( !ent->pointAgainstWorld || (tr.contents & CONTENTS_BODY) ) { // We hit an entity or we don't care impact = true; } else { trap_Trace( &tr, ent->r.currentOrigin, nullptr, nullptr, origin, passent, ent->clipmask, 0 ); if ( tr.fraction < 1.0f ) { // Hit the world with point trace impact = true; } else { if ( tr.contents & CONTENTS_BODY ) { // Hit an entity impact = true; } else { trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, CONTENTS_BODY, 0 ); if ( tr.fraction < 1.0f ) { impact = true; } } } } } VectorCopy( tr.endpos, ent->r.currentOrigin ); if ( impact ) { // Never explode or bounce when hitting the sky. if ( tr.surfaceFlags & SURF_NOIMPACT ) { G_FreeEntity( ent ); return; } // Check for impact damage and effects. MissileImpact( ent, &tr ); // Check if the entity was freed during impact. if ( !ent->inuse ) { return; } // HACK: The missile has turned into an explosion and will free itself later. // See MissileImpact for more. if ( ent->s.eType != ET_MISSILE ) { return; } } ent->r.contents = CONTENTS_SOLID; //trick trap_LinkEntity into... trap_LinkEntity( ent ); ent->r.contents = 0; //...encoding bbox information if ( ent->flightSplashDamage ) { G_RadiusDamage( tr.endpos, ent->parent, ent->flightSplashDamage, ent->flightSplashRadius, ent->parent, 0, ent->splashMethodOfDeath ); } // check think function after bouncing G_RunThink( ent ); }
/* ================ 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 ); }
void G_RunMissile( gentity_t *ent ) { vec3_t origin, oldOrg; trace_t tr; int trHitLoc=HL_NONE; VectorCopy( ent->currentOrigin, oldOrg ); // get current position if ( ent->s.pos.trType == TR_INTERPOLATE ) {//rolling missile? //FIXME: WTF?!! Sticks to stick missiles? //FIXME: they stick inside the player G_RollMissile( ent ); if ( ent->s.eType != ET_GENERAL ) {//didn't explode VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); gi.trace( &tr, oldOrg, ent->mins, ent->maxs, ent->currentOrigin, ent->s.number, ent->clipmask, G2_RETURNONHIT, 10 ); if ( VectorCompare( ent->s.pos.trDelta, vec3_origin ) ) { //VectorCopy( ent->currentAngles, ent->s.apos.trBase ); VectorClear( ent->s.apos.trDelta ); } else { vec3_t ang, fwdDir, rtDir; float speed; ent->s.apos.trType = TR_INTERPOLATE; VectorSet( ang, 0, ent->s.apos.trBase[1], 0 ); AngleVectors( ang, fwdDir, rtDir, NULL ); speed = VectorLength( ent->s.pos.trDelta )*4; //HMM, this works along an axis-aligned dir, but not along diagonals //This is because when roll gets to 90, pitch becomes yaw, and vice-versa //Maybe need to just set the angles directly? ent->s.apos.trDelta[0] = DotProduct( fwdDir, ent->s.pos.trDelta ); ent->s.apos.trDelta[1] = 0;//never spin! ent->s.apos.trDelta[2] = DotProduct( rtDir, ent->s.pos.trDelta ); VectorNormalize( ent->s.apos.trDelta ); VectorScale( ent->s.apos.trDelta, speed, ent->s.apos.trDelta ); ent->s.apos.trTime = level.previousTime; } } } else { EvaluateTrajectory( &ent->s.pos, level.time, origin ); // trace a line from the previous position to the current position, // ignoring interactions with the missile owner /* gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask, G2_RETURNONHIT, 10 ); */ gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ent->owner ? ent->owner->s.number : ent->s.number, ent->clipmask, G2_COLLIDE, 10 ); /* if ( !VectorCompare( ent->mins, vec3_origin ) || !VectorCompare( ent->maxs, vec3_origin ) ) {//don't do ghoul trace if ent has size because g2 just ignores that anyway gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask, G2_NOCOLLIDE, 10 ); } else //Now we always do ghoul trace, regardless of bbox size of missile, this is presuming that non-point ghoul traces will be possible...? { gi.trace( &tr, ent->currentOrigin, vec3_origin, vec3_origin, origin, ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask, G2_RETURNONHIT, 10 ); } */ /* if ( tr.fraction == 0.0f && tr.plane.normal[2] == 1.0f && origin[2] < ent->currentOrigin[2] ) { if ( ent->s.pos.trType == TR_GRAVITY && !(ent->s.eFlags&EF_BOUNCE) && !(ent->s.eFlags&EF_BOUNCE_HALF) && ent->s.weapon == WP_THERMAL )//FIXME: EF_ROLLING { //FIXME: Needs to stop sometime! ent->s.pos.trType = TR_LINEAR; ent->s.pos.trDelta[2] = 0; EvaluateTrajectory( &ent->s.pos, level.time, origin ); // trace a line from the previous position to the current position, // ignoring interactions with the missile owner gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask | CONTENTS_GHOUL2 ); if ( tr.fraction == 1.0f ) { VectorCopy( tr.endpos, ent->s.pos.trBase ); VectorScale( ent->s.pos.trDelta, 0.975f, ent->s.pos.trDelta ); ent->s.pos.trTime = level.time; } ent->s.pos.trType = TR_GRAVITY; } } */ if ( tr.entityNum != ENTITYNUM_NONE && &g_entities[tr.entityNum] != NULL ) { gentity_t *other = &g_entities[tr.entityNum]; // check for hitting a lightsaber if ( other->contents & CONTENTS_LIGHTSABER ) {//hit a lightsaber bbox if ( other->owner && other->owner->client && !other->owner->client->ps.saberInFlight && !InFront( ent->currentOrigin, other->owner->currentOrigin, other->owner->client->ps.viewangles, SABER_REFLECT_MISSILE_CONE ) )//other->owner->s.number == 0 && {//Jedi cannot block shots from behind! //re-trace from here, ignoring the lightsaber gi.trace( &tr, tr.endpos, ent->mins, ent->maxs, origin, tr.entityNum, ent->clipmask, G2_RETURNONHIT, 10 ); } } } VectorCopy( tr.endpos, ent->currentOrigin ); } // get current angles VectorMA( ent->s.apos.trBase, (level.time - ent->s.apos.trTime) * 0.001, ent->s.apos.trDelta, ent->s.apos.trBase ); //FIXME: Rolling things hitting G2 polys is weird /////////////////////////////////////////////////////// //? if ( tr.fraction != 1 ) { // did we hit or go near a Ghoul2 model? // qboolean hitModel = qfalse; for (int i=0; i < MAX_G2_COLLISIONS; i++) { if (tr.G2CollisionMap[i].mEntityNum == -1) { break; } CCollisionRecord &coll = tr.G2CollisionMap[i]; gentity_t *hitEnt = &g_entities[coll.mEntityNum]; /* Sorry...this was just getting in the way.... #if _DEBUG vec3_t delta; VectorSubtract(origin, coll.mCollisionPosition, delta); VectorNormalize(delta); VectorScale(delta, 30, delta); if (coll.mFlags & G2_BACKFACE) { VectorAdd(delta, coll.mCollisionPosition, delta); G_DebugLine(coll.mCollisionPosition, delta, 10000, 0x00ff0000, qtrue); } else { VectorSubtract(coll.mCollisionPosition, delta, delta); G_DebugLine(coll.mCollisionPosition, delta, 10000, 0x0000ff00, qtrue); } //loadsavecrash // VectorCopy(hitEnt->mins, hitEnt->s.mins); // VectorCopy(hitEnt->maxs, hitEnt->s.maxs); #endif */ // process collision records here... // make sure we only do this once, not for all the entrance wounds we might generate if ((coll.mFlags & G2_FRONTFACE)/* && !(hitModel)*/ && hitEnt->health) { // create a new surface using the details of what poly/surface/model we hit // int newSurface = gi.G2API_AddSurface(&hitEnt->ghoul2[coll.mModelIndex], coll.mSurfaceIndex, coll.mPolyIndex, coll.mBarycentricI, coll.mBarycentricJ, 10); // surfaceInfo_t *newSuf = &hitEnt->ghoul2[coll.mModelIndex].mSlist[newSurface]; // attach a bolt to this surface // int newBolt = gi.G2API_AddBoltSurfNum(&hitEnt->ghoul2[coll.mModelIndex], newSurface); // now attach an effect to this new bolt // Bolting on this effect just looks dumb and adds lots of unnecessary effects to the scene // // G_PlayEffect( G_EffectIndex( "blaster/smoke_bolton") , coll.mModelIndex, newBolt, hitEnt->s.number); // // // G_SetBoltSurfaceRemoval(coll.mEntityNum, coll.mModelIndex, newBolt, newSurface, 10000); // hitModel = qtrue; if (trHitLoc==HL_NONE) { G_GetHitLocFromSurfName( &g_entities[coll.mEntityNum], gi.G2API_GetSurfaceName( &g_entities[coll.mEntityNum].ghoul2[coll.mModelIndex], coll.mSurfaceIndex ), &trHitLoc, coll.mCollisionPosition, NULL, NULL, ent->methodOfDeath ); } break; // NOTE: the way this whole section was working, it would only get inside of this IF once anyway, might as well break out now } } } ///////////////////////////////////////////////////////// if ( tr.startsolid ) { tr.fraction = 0; } gi.linkentity( ent ); if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) ) {//stuck missiles should check some special stuff G_RunStuckMissile( ent ); return; } // check think function G_RunThink( ent ); if ( ent->s.eType != ET_MISSILE ) { return; // exploded } if ( ent->mass ) { G_MoverTouchPushTriggers( ent, oldOrg ); } /* if ( !(ent->s.eFlags & EF_TELEPORT_BIT) ) { G_MoverTouchTeleportTriggers( ent, oldOrg ); if ( ent->s.eFlags & EF_TELEPORT_BIT ) {//was teleported return; } } else { ent->s.eFlags &= ~EF_TELEPORT_BIT; } */ AddSightEvent( ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED, 75 );//wakes them up when see a shot passes in front of them if ( !Q_irand( 0, 10 ) ) {//not so often... if ( ent->splashDamage && ent->splashRadius ) {//I'm an exploder, let people around me know danger is coming if ( ent->s.weapon == WP_TRIP_MINE ) {//??? } else { if ( ent->s.weapon == WP_ROCKET_LAUNCHER && ent->e_ThinkFunc == thinkF_rocketThink ) {//homing rocket- run like hell! AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER_GREAT, 50 ); } else { AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER, 50 ); } AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER ); } } else {//makes them run from near misses AddSightEvent( ent->owner, ent->currentOrigin, 48, AEL_DANGER, 50 ); } } if ( tr.fraction == 1 ) { return; } // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { G_FreeEntity( ent ); return; } G_MissileImpact( ent, &tr, trHitLoc ); }
/* ================ 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 impactDamage; // Ridah, make AI aware of this danger // DHM - Nerve :: Only in single player if ( g_gametype.integer == GT_SINGLE_PLAYER ) { AICast_CheckDangerousEntity( ent, DANGER_MISSILE, ent->splashRadius, 0.1, 0.99, qtrue ); } // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); if ( ( ent->clipmask & CONTENTS_BODY ) && ( ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_ARTY || ent->s.weapon == WP_GRENADE_LAUNCHER || ent->s.weapon == WP_GRENADE_PINEAPPLE ) ) { if ( !ent->s.pos.trDelta[0] && !ent->s.pos.trDelta[1] && !ent->s.pos.trDelta[2] ) { ent->clipmask &= ~CONTENTS_BODY; } } // 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->r.ownerNum, ent->clipmask ); VectorCopy( tr.endpos, ent->r.currentOrigin ); if ( tr.startsolid ) { tr.fraction = 0; } 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; } if ( ent->s.weapon == WP_ROCKET_LAUNCHER || ent->s.weapon == WP_PANZERFAUST ) { impactDamage = 999; // goes through pretty much any func_explosives } else { impactDamage = 20; // "grenade"/"dynamite" // probably adjust this based on velocity } G_MissileImpact( ent, &tr, impactDamage ); if ( ent->s.eType != ET_MISSILE ) { // JPW NERVE if ( g_gametype.integer != GT_SINGLE_PLAYER ) { Ground_Shaker( ent->r.currentOrigin, ent->splashDamage * 4 ); } // jpw return; // exploded } } // check think function after bouncing G_RunThink( ent ); }
void G_RunFrame( int levelTime ) { int i; gentity_t *ent; int msec; int ents_inuse=0; // someone's gonna be pissed I put this here... #if AI_TIMERS AITime = 0; navTime = 0; #endif// AI_TIMERS level.framenum++; level.previousTime = level.time; level.time = levelTime; msec = level.time - level.previousTime; NAV_CheckCalcPaths(); //ResetTeamCounters(); AI_UpdateGroups(); if ( d_altRoutes->integer ) { navigator.CheckAllFailedEdges(); } navigator.ClearCheckedNodes(); //remember last waypoint, clear current one // for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++) for ( i = 0; i < globals.num_entities ; i++) { // if ( !ent->inuse ) // continue; if(!PInUse(i)) continue; ent = &g_entities[i]; if ( ent->waypoint != WAYPOINT_NONE && ent->noWaypointTime < level.time ) { ent->lastWaypoint = ent->waypoint; ent->waypoint = WAYPOINT_NONE; } if ( d_altRoutes->integer ) { navigator.CheckFailedNodes( ent ); } } //Look to clear out old events ClearPlayerAlertEvents(); //Run the frame for all entities // for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++) for ( i = 0; i < globals.num_entities ; i++) { // if ( !ent->inuse ) // continue; if(!PInUse(i)) continue; ents_inuse++; ent = &g_entities[i]; // clear events that are too old if ( level.time - ent->eventTime > EVENT_VALID_MSEC ) { if ( ent->s.event ) { ent->s.event = 0; // &= EV_EVENT_BITS; if ( ent->client ) { ent->client->ps.externalEvent = 0; } } if ( ent->freeAfterEvent ) { // tempEntities or dropped items completely go away after their event G_FreeEntity( ent ); continue; } else if ( ent->unlinkAfterEvent ) { // items that will respawn will hide themselves after their pickup event ent->unlinkAfterEvent = qfalse; gi.unlinkentity( ent ); } } // temporary entities don't think if ( ent->freeAfterEvent ) continue; G_CheckTasksCompleted(ent); G_Roff( ent ); if( !ent->client ) { if ( !(ent->svFlags & SVF_SELF_ANIMATING) ) {//FIXME: make sure this is done only for models with frames? //Or just flag as animating? if ( ent->s.eFlags & EF_ANIM_ONCE ) { ent->s.frame++; } else if ( !(ent->s.eFlags & EF_ANIM_ALLFAST) ) { G_Animate( ent ); } } } G_CheckSpecialPersistentEvents( ent ); if ( ent->s.eType == ET_MISSILE ) { G_RunMissile( ent ); continue; } if ( ent->s.eType == ET_ITEM ) { G_RunItem( ent ); continue; } if ( ent->s.eType == ET_MOVER ) { if ( ent->model && Q_stricmp( "models/test/mikeg/tie_fighter.md3", ent->model ) == 0 ) { TieFighterThink( ent ); } G_RunMover( ent ); continue; } //The player if ( i == 0 ) { // decay batteries if the goggles are active if ( cg.zoomMode == 1 && ent->client->ps.batteryCharge > 0 ) { ent->client->ps.batteryCharge--; } else if ( cg.zoomMode == 3 && ent->client->ps.batteryCharge > 0 ) { ent->client->ps.batteryCharge -= 2; if ( ent->client->ps.batteryCharge < 0 ) { ent->client->ps.batteryCharge = 0; } } G_CheckEndLevelTimers( ent ); //Recalculate the nearest waypoint for the coming NPC updates NAV_FindPlayerWaypoint(); if( ent->taskManager && !stop_icarus ) { ent->taskManager->Update(); } //dead if ( ent->health <= 0 ) { if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE ) {//on the ground pitch_roll_for_slope( ent, NULL ); } } continue; // players are ucmd driven } G_RunThink( ent ); // be aware that ent may be free after returning from here, at least one func frees them ClearNPCGlobals(); // but these 2 funcs are ok //UpdateTeamCounters( ent ); // to call anyway on a freed ent. } // perform final fixups on the player ent = &g_entities[0]; if ( ent->inuse ) { ClientEndFrame( ent ); } if( g_numEntities->integer ) { gi.Printf( S_COLOR_WHITE"Number of Entities in use : %d\n", ents_inuse ); } //DEBUG STUFF NAV_ShowDebugInfo(); NPC_ShowDebugInfo(); G_DynamicMusicUpdate(); #if AI_TIMERS AITime -= navTime; if ( AITime > 20 ) { gi.Printf( S_COLOR_RED"ERROR: total AI time: %d\n", AITime ); } else if ( AITime > 10 ) { gi.Printf( S_COLOR_YELLOW"WARNING: total AI time: %d\n", AITime ); } else if ( AITime > 2 ) { gi.Printf( S_COLOR_GREEN"total AI time: %d\n", AITime ); } if ( navTime > 20 ) { gi.Printf( S_COLOR_RED"ERROR: total nav time: %d\n", navTime ); } else if ( navTime > 10 ) { gi.Printf( S_COLOR_YELLOW"WARNING: total nav time: %d\n", navTime ); } else if ( navTime > 2 ) { gi.Printf( S_COLOR_GREEN"total nav time: %d\n", navTime ); } #endif// AI_TIMERS #ifndef FINAL_BUILD if ( delayedShutDown != 0 && delayedShutDown < level.time ) { G_Error( "Game Errors. Scroll up the console to read them.\n" ); } #endif #ifdef _DEBUG if(!(level.framenum&0xff)) { ValidateInUseBits(); } #endif }
void G_RunFlamechunk( gentity_t *ent ) { vec3_t vel, add; vec3_t neworg; trace_t tr; float speed, dot; // Adust the current speed of the chunk if ( level.time - ent->timestamp > 50 ) { VectorCopy( ent->s.pos.trDelta, vel ); speed = VectorNormalize( vel ); speed -= ( 50.f / 1000.f ) * FLAME_FRICTION_PER_SEC; if ( speed < FLAME_MIN_SPEED ) { speed = FLAME_MIN_SPEED; } VectorScale( vel, speed, ent->s.pos.trDelta ); } else { speed = FLAME_START_SPEED; } // Move the chunk VectorScale( ent->s.pos.trDelta, 50.f / 1000.f, add ); VectorAdd( ent->r.currentOrigin, add, neworg ); trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, neworg, ent->r.ownerNum, MASK_SHOT | MASK_WATER ); // JPW NERVE if ( tr.startsolid ) { VectorCopy( vec3_origin, ent->s.pos.trDelta ); } else if ( tr.fraction != 1.0f && !( tr.surfaceFlags & SURF_NOIMPACT ) ) { VectorCopy( tr.endpos, ent->r.currentOrigin ); dot = DotProduct( vel, tr.plane.normal ); VectorMA( vel, -2 * dot, tr.plane.normal, vel ); VectorNormalize( vel ); speed *= 0.5 * ( 0.25 + 0.75 * ( ( dot + 1.0 ) * 0.5 ) ); VectorScale( vel, speed, ent->s.pos.trDelta ); } else { VectorCopy( neworg, ent->r.currentOrigin ); } // Do damage to nearby entities, every 100ms if ( ent->flameQuotaTime <= level.time ) { ent->flameQuotaTime = level.time + 100; G_FlameDamage( ent ); } // Show debugging bbox if ( g_debugBullets.integer > 3 ) { gentity_t *bboxEnt; float size = ent->speed / 2; vec3_t b1, b2; vec3_t temp; VectorSet( temp, -size, -size, -size ); VectorCopy( ent->r.currentOrigin, b1 ); VectorCopy( ent->r.currentOrigin, b2 ); VectorAdd( b1, temp, b1 ); VectorSet( temp, size, size, size ); VectorAdd( b2, temp, b2 ); bboxEnt = G_TempEntity( b1, EV_RAILTRAIL ); VectorCopy( b2, bboxEnt->s.origin2 ); bboxEnt->s.dmgFlags = 1; // ("type") } // Adjust the size if ( ent->speed < FLAME_START_MAX_SIZE ) { ent->speed += 10.f; if ( ent->speed > FLAME_START_MAX_SIZE ) { ent->speed = FLAME_START_MAX_SIZE; } } // Remove after 2 seconds if ( level.time - ent->timestamp > ( FLAME_LIFETIME - 150 ) ) { // JPW NERVE increased to 350 from 250 to match visuals better G_FreeEntity( ent ); return; } 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 ); }
/* ================ 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) { // check think function G_RunThink(ent); return; } // get current position G_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_PLAYERSOLID & ~CONTENTS_BODY; //MASK_SOLID; } trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->r.ownerNum, mask); VectorCopy(tr.endpos, ent->r.currentOrigin); if (tr.startsolid) { tr.fraction = 0; } if (ent->flags & FL_DROPPED_ITEM && VectorLength(ent->s.pos.trDelta) != 0 && (ent->item->giType == IT_WEAPON || ent->item->giType == IT_HOLDABLE)) { // calculate spin -- should be identical to cg.autoAngles //cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f; ent->s.angles[1] = (level.time & 1023) * 360 / 1024.0f; } trap_LinkEntity(ent); // FIXME: avoid this for stationary? // check think function G_RunThink(ent); if (tr.fraction == 1) { return; } //Elder: debug //if (ent->item && ent->item->giType == IT_WEAPON) { //G_Printf("item velocity: %s\n", vtos(ent->s.pos.trDelta)); //} // 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 if (ent->item && ent->item->giType == IT_WEAPON) { //Elder: force-call the weaponthink function RQ3_DroppedWeaponThink(ent); } else if (ent->item && ent->item->giType == IT_HOLDABLE) { RQ3_DroppedItemThink(ent); } else { G_FreeEntity(ent); } return; } G_BounceItem(ent, &tr); }
/* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin; trace_t tr; int passent; qboolean impact = qfalse; // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // ignore interactions with the missile owner passent = ent->r.ownerNum; // general trace to see if we hit anything at all trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); if ( tr.startsolid || tr.allsolid ) { tr.fraction = 0.0f; VectorCopy( ent->r.currentOrigin, tr.endpos ); } if ( tr.fraction < 1.0f ) { if ( !ent->pointAgainstWorld || (tr.contents & CONTENTS_BODY) ) { // We hit an entity or we don't care impact = qtrue; } else { trap_Trace( &tr, ent->r.currentOrigin, NULL, NULL, origin, passent, ent->clipmask ); if ( tr.fraction < 1.0f ) { // Hit the world with point trace impact = qtrue; } else { if ( tr.contents & CONTENTS_BODY ) { // Hit an entity impact = qtrue; } else { trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, CONTENTS_BODY ); if ( tr.fraction < 1.0f ) { impact = qtrue; } } } } } VectorCopy( tr.endpos, ent->r.currentOrigin ); if ( impact ) { if ( tr.surfaceFlags & SURF_NOIMPACT ) { // Never explode or bounce on sky G_FreeEntity( ent ); return; } G_MissileImpact( ent, &tr ); if ( ent->s.eType != ET_MISSILE ) { return; // exploded } } ent->r.contents = CONTENTS_SOLID; //trick trap_LinkEntity into... trap_LinkEntity( ent ); ent->r.contents = 0; //...encoding bbox information if ( ent->flightSplashDamage ) { G_RadiusDamage( tr.endpos, ent->parent, ent->flightSplashDamage * G_DoMissileTimePowerReduce( ent ), ent->splashRadius, ent->parent, ent->splashMethodOfDeath ); } // check think function after bouncing G_RunThink( ent ); }
//[/RealTrace] 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; //[SaberThrowSys] if(!(ent->s.eFlags & EF_MISSILE_STICK) ) { //only go into gravity mode if we're not stuck to something ent->s.pos.trType = TR_GRAVITY; } //[/SaberThrowSys] } // 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 //[RealTrace] G_RealTrace( ent, &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask, -1, -1 ); //[/RealTrace] if ( !tr.startsolid && !tr.allsolid ) VectorCopy( tr.endpos, ent->r.currentOrigin ); if (ent->passThroughNum && tr.entityNum == (ent->passThroughNum-1)) { VectorCopy( origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); goto passthrough; } trap_LinkEntity( ent ); //racc - assign groundEntityNum for body parts. 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 ); 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; } //racc - make dropped sabers think when they hit a non-impact surface. if ((ent->s.weapon == WP_SABER && ent->isSaberEntity) || isKnockedSaber) { G_RunThink( ent ); return; } //just kill off other weapon shots when they hit a non-impact surface. 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)) { //racc - don't allow the current origin/predicted origin be the same. ent->s.origin2[2] += 2.0f; //whatever, at least it won't mess up. } } //[DodgeSys] //changed G_MissileImpact to qboolean so that dodges will cause passthru behavor. if(!G_MissileImpact( ent, &tr )) { //target dodged the damage. VectorCopy( origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); return; } //G_MissileImpact( ent, &tr ); //[/DodgeSys] 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; } } } if(ent->damageDecreaseTime && ent->damageDecreaseTime <= level.time) { ent->damage-=4; ent->damageDecreaseTime = level.time + 300; } // check think function after bouncing G_RunThink( ent ); }