/* =============== findEmptySpot Finds an empty spot radius units from origin ============== */ static void findEmptySpot(vec3_t origin, float radius, vec3_t spot) { int i, j, k; vec3_t delta, test, total; trace_t tr; VectorClear(total); // 54(!) traces to test for empty spots for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { for (k = -1; k <= 1; k++) { VectorSet(delta, (i * radius), (j * radius), (k * radius)); VectorAdd(origin, delta, test); G_Trace(&tr, test, NULL, NULL, test, -1, MASK_SOLID); if (!tr.allsolid) { G_Trace(&tr, test, NULL, NULL, origin, -1, MASK_SOLID); VectorScale(delta, tr.fraction, delta); VectorAdd(total, delta, total); } } } } VectorNormalize(total); VectorScale(total, radius, total); VectorAdd(origin, total, spot); }
// // Name: CanSeeEntity() // Parameters: Entity *start - The entity trying to see // Entity *target - The entity that needs to be seen // qboolean useFOV - take FOV into consideration or not // qboolean useVisionDistance - take visionDistance into consideration // Description: Wraps a lot of the different CanSee Functions into one // qboolean SensoryPerception::CanSeeEntity( Entity *start, const Entity *target, qboolean useFOV, qboolean useVisionDistance ) { // Check for NULL if ( !start || !target ) return false; // Check if This Actor can even see at all if ( !ShouldRespondToStimuli( StimuliSight ) ) return false; // Check for FOV if ( useFOV ) { if ( !InFOV( target ) ) return false; } // Check for vision distance if ( useVisionDistance ) { if ( !WithinVisionDistance( target ) ) return false; } // Do Trace auto p = target->centroid; auto eyePos = vec_zero; // If the start entity is an actor, then we want to add in the eyeposition if ( start->isSubclassOf ( Actor ) ) { Actor *a; a = dynamic_cast<Actor*>(start); if ( !a ) return false; eyePos = a->EyePosition(); } // Check if he's visible auto trace = G_Trace( eyePos, vec_zero, vec_zero, p, target, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" ); if ( trace.fraction == 1.0f || trace.ent == target->edict ) return true; // Check if his head is visible p.z = target->absmax.z; trace = G_Trace( eyePos, vec_zero, vec_zero, p, target, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" ); if ( trace.fraction == 1.0f || trace.ent == target->edict ) return true; return false; }
/* * G_ProjectThirdPersonView */ static void G_ProjectThirdPersonView( vec3_t vieworg, vec3_t viewangles, edict_t *passent ) { float thirdPersonRange = 60; float thirdPersonAngle = 0; float dist, f, r; vec3_t dest, stop; vec3_t chase_dest; trace_t trace; vec3_t mins = { -4, -4, -4 }; vec3_t maxs = { 4, 4, 4 }; vec3_t v_forward, v_right, v_up; AngleVectors( viewangles, v_forward, v_right, v_up ); // calc exact destination VectorCopy( vieworg, chase_dest ); r = DEG2RAD( thirdPersonAngle ); f = -cos( r ); r = -sin( r ); VectorMA( chase_dest, thirdPersonRange * f, v_forward, chase_dest ); VectorMA( chase_dest, thirdPersonRange * r, v_right, chase_dest ); chase_dest[2] += 8; // find the spot the player is looking at VectorMA( vieworg, 512, v_forward, dest ); G_Trace( &trace, vieworg, mins, maxs, dest, passent, MASK_SOLID ); // calculate pitch to look at the same spot from camera VectorSubtract( trace.endpos, vieworg, stop ); dist = sqrt( stop[0] * stop[0] + stop[1] * stop[1] ); if( dist < 1 ) dist = 1; viewangles[PITCH] = RAD2DEG( -atan2( stop[2], dist ) ); viewangles[YAW] -= thirdPersonAngle; AngleVectors( viewangles, v_forward, v_right, v_up ); // move towards destination G_Trace( &trace, vieworg, mins, maxs, chase_dest, passent, MASK_SOLID ); if( trace.fraction != 1.0 ) { VectorCopy( trace.endpos, stop ); stop[2] += ( 1.0 - trace.fraction ) * 32; G_Trace( &trace, vieworg, mins, maxs, stop, passent, MASK_SOLID ); VectorCopy( trace.endpos, chase_dest ); } VectorCopy( chase_dest, vieworg ); }
// // Name: CanSeeEntity() // Parameters: Vector &start -- The starting position // Entity *target - The entity that needs to be seen // qboolean useFOV - take FOV into consideration or not // qboolean useVisionDistance - take visionDistance into consideration // Description: Wraps a lot of the different CanSee Functions into one // qboolean SensoryPerception::CanSeeEntity( const Vector &start , const Entity *target, qboolean useFOV , qboolean useVisionDistance ) { // Check for NULL if ( !target ) return false; // Check if This Actor can even see at all if ( !ShouldRespondToStimuli( StimuliSight ) ) return false; // Check for FOV if ( useFOV ) { if ( !InFOV( target ) ) return false; } // Check for vision distance if ( useVisionDistance ) { if ( !WithinVisionDistance( target ) ) return false; } // Do Trace auto realStart = start; auto p = target->centroid; // Add in the eye offset auto eyePos = act->EyePosition() - act->origin; realStart += eyePos; // Check if he's visible auto trace = G_Trace( realStart, vec_zero, vec_zero, p, act, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" ); if ( trace.fraction == 1.0f || trace.ent == target->edict ) return true; // Check if his head is visible p.z = target->absmax.z; trace = G_Trace( realStart, vec_zero, vec_zero, p, act, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" ); if ( trace.fraction == 1.0f || trace.ent == target->edict ) return true; return false; }
/* * AI_FlagsForNode * check the world and set up node flags */ int AI_FlagsForNode( vec3_t origin, edict_t *passent ) { trace_t trace; int flagsmask = 0; int contents; contents = G_PointContents( origin ); //water if( contents & MASK_WATER ) flagsmask |= NODEFLAGS_WATER; if( contents & CONTENTS_DONOTENTER ) flagsmask |= NODEFLAGS_DONOTENTER; //floor G_Trace( &trace, origin, tv( -15, -15, 0 ), tv( 15, 15, 0 ), tv( origin[0], origin[1], origin[2] - AI_JUMPABLE_HEIGHT ), passent, MASK_NODESOLID ); if( trace.fraction < 1.0 ) flagsmask &= ~NODEFLAGS_FLOAT; //ok, it wasn't set, I know... else flagsmask |= NODEFLAGS_FLOAT; //ladder // G_Trace( &trace, origin, tv(-18, -18, -16), tv(18, 18, 16), origin, passent, MASK_ALL ); // if( trace.startsolid && trace.contents & CONTENTS_LADDER ) // flagsmask |= NODEFLAGS_LADDER; return flagsmask; }
//========================================== // AI_CanMove // Checks if bot can move (really just checking the ground) // Also, this is not a real accurate check, but does a // pretty good job and looks for lava/slime. //========================================== qboolean AI_CanMove( edict_t *self, int direction ) { vec3_t forward, right; vec3_t offset, start, end; vec3_t angles; trace_t tr; // Now check to see if move will move us off an edge VectorCopy( self->s.angles, angles ); if( direction == BOT_MOVE_LEFT ) angles[1] += 90; else if( direction == BOT_MOVE_RIGHT ) angles[1] -= 90; else if( direction == BOT_MOVE_BACK ) angles[1] -= 180; // Set up the vectors AngleVectors( angles, forward, right, NULL ); VectorSet( offset, 36, 0, 24 ); G_ProjectSource( self->s.origin, offset, forward, right, start ); VectorSet( offset, 36, 0, -100 ); G_ProjectSource( self->s.origin, offset, forward, right, end ); G_Trace( &tr, start, NULL, NULL, end, self, MASK_AISOLID ); if( tr.fraction == 1.0 || tr.contents & ( CONTENTS_LAVA|CONTENTS_SLIME ) ) return qfalse; return qtrue; // yup, can move }
/** * @brief Spawns a smoke field that is available for some rounds * @param[in] vec The position in the world that is the center of the smoke field * @param[in] particle The id of the particle (see ptl_*.ufo script files in base/ufos) * @param[in] rounds The number of rounds the particle will last * @todo Does '2 rounds' mean: created in player's turn, last through the aliens turn, vanish before the 2nd player's turn ?? * @param[in] radius The max distance of a cell from the center to get a particle */ void G_SpawnSmokeField (const vec3_t vec, const char *particle, int rounds, vec_t radius) { vec_t x, y; G_SpawnSmoke(vec, particle, rounds); /* for all cells in a square of +/- radius */ for (x = vec[0] - radius; x <= vec[0] + radius; x += UNIT_SIZE) { for (y = vec[1] - radius; y <= vec[1] + radius; y += UNIT_SIZE) { vec3_t end; trace_t tr; VectorSet(end, x, y, vec[2]); /* cut off the edges of the square to resemble a circle */ if (VectorDist(end, vec) > radius) continue; tr = G_Trace(vec, end, NULL, MASK_SMOKE_AND_FIRE); /* trace didn't reach the target - something was hit before */ if (tr.fraction < 1.0 || (tr.contentFlags & CONTENTS_WATER)) { continue; } G_SpawnSmoke(end, particle, rounds); } } }
void G_SpawnStunSmokeField (const vec3_t vec, const char *particle, int rounds, int damage, vec_t radius) { vec_t x, y; G_SpawnStunSmoke(vec, particle, rounds, damage); for (x = vec[0] - radius; x <= vec[0] + radius; x += UNIT_SIZE) { for (y = vec[1] - radius; y <= vec[1] + radius; y += UNIT_SIZE) { vec3_t end; trace_t tr; VectorSet(end, x, y, vec[2]); if (VectorDist(end, vec) > radius) continue; tr = G_Trace(vec, end, NULL, MASK_SMOKE_AND_FIRE); /* trace didn't reach the target - something was hit before */ if (tr.fraction < 1.0 || (tr.contentFlags & CONTENTS_WATER)) { continue; } G_SpawnStunSmoke(end, particle, rounds, damage); } } }
qboolean SensoryPerception::CanSeePosition( const Vector &start, const Vector &position, qboolean useFOV , qboolean useVisionDistance ) { // Check if This Actor can even see at all if ( !ShouldRespondToStimuli( StimuliSight ) ) return false; // Check for FOV if ( useFOV ) { if ( !InFOV( position ) ) return false; } // Check for vision distance if ( useVisionDistance ) { if ( !WithinVisionDistance( position ) ) return false; } // Do Trace // Check if he's visible auto trace = G_Trace( start, vec_zero, vec_zero, position, act, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" ); if ( trace.fraction == 1.0f ) return true; return false; }
bool BotTacticalSpotsCache::FindRunAwayElevatorOrigin( const Vec3 &origin, const Vec3 &enemyOrigin, vec3_t result[2] ) { ReachableEntities reachableEntities; FindReachableClassEntities( origin, 128.0f + 384.0f * Skill(), "func_plat", reachableEntities ); trace_t trace; const auto *pvsCache = EntitiesPvsCache::Instance(); edict_t *enemyEnt = const_cast<edict_t *>( self->ai->botRef->selectedEnemies.Ent() ); edict_t *gameEdicts = game.edicts; for( const auto &entAndScore: reachableEntities ) { edict_t *ent = gameEdicts + entAndScore.entNum; // Can't run away via elevator if the elevator has been always activated if( ent->moveinfo.state != STATE_BOTTOM ) { continue; } if( !pvsCache->AreInPvs( enemyEnt, ent ) ) { continue; } G_Trace( &trace, enemyEnt->s.origin, nullptr, nullptr, ent->moveinfo.end_origin, enemyEnt, MASK_AISOLID ); if( trace.fraction == 1.0f || trace.ent > 0 ) { continue; } // Copy trigger origin VectorCopy( ent->s.origin, result[0] ); // Drop origin to the elevator bottom result[0][2] = ent->r.absmin[2] + 16; // Copy trigger destination VectorCopy( ent->moveinfo.end_origin, result[1] ); return true; } return false; }
//========================================== // BOT_DMclass_CheckShot // Checks if shot is blocked (doesn't verify it would hit) //========================================== static bool BOT_DMclass_CheckShot( edict_t *ent, vec3_t point ) { trace_t tr; vec3_t start, forward, right, offset; if( random() > ent->ai->pers.cha.firerate ) return false; AngleVectors( ent->r.client->ps.viewangles, forward, right, NULL ); VectorSet( offset, 0, 0, ent->viewheight ); G_ProjectSource( ent->s.origin, offset, forward, right, start ); // blocked, don't shoot G_Trace( &tr, start, vec3_origin, vec3_origin, point, ent, MASK_AISOLID ); if( tr.fraction < 0.8f ) { if( tr.ent < 1 || !game.edicts[tr.ent].takedamage || game.edicts[tr.ent].movetype == MOVETYPE_PUSH ) return false; // check if the player we found is at our team if( game.edicts[tr.ent].s.team == ent->s.team && GS_TeamBasedGametype() ) return false; } return true; }
void Gib::SprayBlood ( Vector start ) { Vector norm; trace_t trace; Vector trace_end; trace_end = velocity; trace_end.normalize(); trace_end *= 1000; trace_end += start; trace = G_Trace( start, vec_zero, vec_zero, trace_end, this, MASK_SOLID, false, "Gib::SprayBlood" ); if ( HitSky( &level.impact_trace ) || ( !level.impact_trace.ent ) || ( level.impact_trace.ent->solid != SOLID_BSP ) ) { return; } // Do a bloodsplat if ( blood_splat_name.length() ) { Decal *decal = new Decal; decal->setShader( blood_splat_name ); decal->setOrigin( Vector( trace.endpos ) + ( Vector( level.impact_trace.plane.normal ) * 0.2f ) ); decal->setDirection( level.impact_trace.plane.normal ); decal->setOrientation( "random" ); decal->setRadius( blood_splat_size + G_Random( blood_splat_size ) ); } }
bool BotTacticalSpotsCache::FindRunAwayJumppadOrigin( const Vec3 &origin, const Vec3 &enemyOrigin, vec3_t result[2] ) { ReachableEntities reachableEntities; FindReachableClassEntities( origin, 128.0f + 384.0f * Skill(), "trigger_push", reachableEntities ); trace_t trace; const auto *pvsCache = EntitiesPvsCache::Instance(); edict_t *enemyEnt = const_cast<edict_t *>( self->ai->botRef->selectedEnemies.Ent() ); for( const auto &entAndScore: reachableEntities ) { edict_t *ent = game.edicts + entAndScore.entNum; if( !pvsCache->AreInPvs( enemyEnt, ent ) ) { continue; } G_Trace( &trace, enemyEnt->s.origin, nullptr, nullptr, ent->target_ent->s.origin, enemyEnt, MASK_AISOLID ); if( trace.fraction == 1.0f || trace.ent > 0 ) { continue; } // Copy trigger origin VectorCopy( ent->s.origin, result[0] ); // Copy trigger destination VectorCopy( ent->target_ent->s.origin, result[1] ); return true; } return false; }
static bool BOT_DMclass_FindRocket( edict_t *self, vec3_t away_from_rocket ) { #define AI_ROCKET_DETECT_RADIUS 1000 #define AI_ROCKET_DANGER_RADIUS 200 int i, numtargets; int targets[MAX_EDICTS]; edict_t *target; float min_roxx_time = 1.0f; bool any_rocket = false; numtargets = GClip_FindRadius( self->s.origin, AI_ROCKET_DETECT_RADIUS, targets, MAX_EDICTS ); for( i = 0; i < numtargets; i++ ) { target = game.edicts + targets[i]; // Missile detection code if( target->r.svflags & SVF_PROJECTILE && target->s.type != ET_PLASMA ) // (plasmas come in bunchs so are too complex for the bot to dodge) { if( target->r.owner && target->r.owner != self ) { vec3_t end; trace_t trace; VectorMA( target->s.origin, 2, target->velocity, end ); G_Trace( &trace, target->s.origin, target->r.mins, target->r.maxs, end, target, MASK_SOLID ); if( trace.fraction < min_roxx_time ) { vec_t l; any_rocket = true; min_roxx_time = trace.fraction; VectorSubtract( trace.endpos, self->s.origin, end ); // ok... end is where the impact will be. // trace.fraction is the time. if( ( l = VectorLengthFast( end ) ) < AI_ROCKET_DANGER_RADIUS ) { RotatePointAroundVector( away_from_rocket, &axis_identity[AXIS_UP], end, -self->s.angles[YAW] ); VectorNormalize( away_from_rocket ); if( fabs( away_from_rocket[0] ) < 0.3 ) away_from_rocket[0] = 0; if( fabs( away_from_rocket[1] ) < 0.3 ) away_from_rocket[1] = 0; away_from_rocket[2] = 0; away_from_rocket[0] *= -1.0f; away_from_rocket[1] *= -1.0f; if( nav.debugMode && bot_showcombat->integer > 2 ) G_PrintChasersf( self, "%s: ^1projectile dodge: ^2%f, %f d=%f^7\n", self->ai->pers.netname, away_from_rocket[0], away_from_rocket[1], l ); } } } } } return any_rocket; #undef AI_ROCKET_DETECT_RADIUS #undef AI_ROCKET_DANGER_RADIUS }
bool AI_ReachabilityVisible( edict_t *self, vec3_t point ) { trace_t trace; G_Trace( &trace, self->s.origin, vec3_origin, vec3_origin, point, self, MASK_DEADSOLID ); if( trace.ent < 0 ) return true; return false; }
/* * AI_AddNode_Platform_FindLowerLinkableCandidate * helper to AI_AddNode_Platform */ static int AI_AddNode_Platform_FindLowerLinkableCandidate( edict_t *ent ) { trace_t trace; float plat_dist; float platlip; int numtries = 0, maxtries = 10; int candidate; vec3_t candidate_origin, virtualorigin; float mindist = 0; if( ent->flags & FL_TEAMSLAVE ) return NODE_INVALID; plat_dist = ent->moveinfo.start_origin[2] - ent->moveinfo.end_origin[2]; platlip = ( ent->r.maxs[2] - ent->r.mins[2] ) - plat_dist; //find a good candidate for lower candidate_origin[0] = ( ent->r.maxs[0] - ent->r.mins[0] ) / 2 + ent->r.mins[0]; candidate_origin[1] = ( ent->r.maxs[1] - ent->r.mins[1] ) / 2 + ent->r.mins[1]; candidate_origin[2] = ent->r.mins[2] + platlip; //try to find the closer reachable node to the bottom of the plat do { candidate = AI_FindClosestNode( candidate_origin, mindist, NODE_DENSITY * 2, NODE_ALL ); if( candidate != NODE_INVALID ) { mindist = DistanceFast( candidate_origin, nodes[candidate].origin ); //check to see if it would be valid if( fabs( candidate_origin[2] - nodes[candidate].origin[2] ) < ( fabs( platlip ) + AI_JUMPABLE_HEIGHT ) ) { //put at linkable candidate height virtualorigin[0] = candidate_origin[0]; virtualorigin[1] = candidate_origin[1]; virtualorigin[2] = nodes[candidate].origin[2]; G_Trace( &trace, virtualorigin, vec3_origin, vec3_origin, nodes[candidate].origin, ent, MASK_NODESOLID ); //trace = gi.trace( virtualorigin, vec3_origin, vec3_origin, nodes[candidate].origin, ent, MASK_NODESOLID ); if( trace.fraction == 1.0 && !trace.startsolid ) { #ifdef SHOW_JUMPAD_GUESS AI_JumpadGuess_ShowPoint( virtualorigin, "models/objects/grenade/tris.md2" ); #endif return candidate; } } } } while( candidate != NODE_INVALID && numtries++ < maxtries ); return NODE_INVALID; }
/* * G_Client_DeadView */ static void G_Client_DeadView( edict_t *ent ) { edict_t *body; gclient_t *client; trace_t trace; client = ent->r.client; // find the body for( body = game.edicts + gs.maxclients; ENTNUM( body ) < gs.maxclients + BODY_QUEUE_SIZE + 1; body++ ) { if( !body->r.inuse || body->r.svflags & SVF_NOCLIENT ) continue; if( body->activator == ent ) // this is our body break; } if( body->activator != ent ) { // ran all the list and didn't find our body return; } // move us to body position VectorCopy( body->s.origin, ent->s.origin ); VectorCopy( body->s.origin, ent->s.old_origin ); ent->s.teleported = qtrue; client->ps.viewangles[ROLL] = 0; client->ps.viewangles[PITCH] = 0; // see if our killer is still in view if( body->enemy && ( body->enemy != ent ) ) { G_Trace( &trace, ent->s.origin, vec3_origin, vec3_origin, body->enemy->s.origin, body, MASK_OPAQUE ); if( trace.fraction != 1.0f ) { body->enemy = NULL; } else { client->ps.viewangles[YAW] = LookAtKillerYAW( ent, NULL, body->enemy ); } } else { // nobody killed us, so just circle around the body ? } G_ProjectThirdPersonView( ent->s.origin, client->ps.viewangles, body ); VectorCopy( client->ps.viewangles, ent->s.angles ); VectorCopy( ent->s.origin, client->ps.pmove.origin ); VectorClear( client->ps.pmove.velocity ); GS_SnapPosition( client->ps.pmove.origin, ent->r.mins, ent->r.maxs, ENTNUM( ent ), 0 ); }
//========================================== // AI_CheckEyes // Helper for ACEMV_SpecialMove. // Tries to turn when in front of obstacle //========================================== static qboolean AI_CheckEyes( edict_t *self, usercmd_t *ucmd ) { vec3_t forward, right; vec3_t leftstart, rightstart, focalpoint; vec3_t dir, offset; trace_t traceRight; trace_t traceLeft; // Get current angle and set up "eyes" VectorCopy( self->s.angles, dir ); AngleVectors( dir, forward, right, NULL ); if( !self->movetarget ) VectorSet( offset, 200, 0, self->r.maxs[2]*0.5 ); // focalpoint else VectorSet( offset, 64, 0, self->r.maxs[2]*0.5 ); // wander focalpoint G_ProjectSource( self->s.origin, offset, forward, right, focalpoint ); VectorSet( offset, 0, 18, self->r.maxs[2]*0.5 ); G_ProjectSource( self->s.origin, offset, forward, right, leftstart ); offset[1] -= 36; G_ProjectSource( self->s.origin, offset, forward, right, rightstart ); G_Trace( &traceRight, rightstart, NULL, NULL, focalpoint, self, MASK_AISOLID ); G_Trace( &traceLeft, leftstart, NULL, NULL, focalpoint, self, MASK_AISOLID ); // Find the side with more open space and turn if( traceRight.fraction != 1 || traceLeft.fraction != 1 ) { if( traceRight.fraction > traceLeft.fraction ) self->s.angles[YAW] += ( 1.0 - traceLeft.fraction ) * 45.0; else self->s.angles[YAW] += -( 1.0 - traceRight.fraction ) * 45.0; ucmd->forwardmove = 1; return qtrue; } return qfalse; }
static unsigned int G_FindPointedPlayer( edict_t *self ) { trace_t trace; int i, j, bestNum = 0; vec3_t boxpoints[8]; float value, dist, value_best = 0.90f; // if nothing better is found, print nothing edict_t *other; vec3_t vieworg, dir, viewforward; if( G_IsDead( self ) ) { return 0; } // we can't handle the thirdperson modifications in server side :/ VectorSet( vieworg, self->r.client->ps.pmove.origin[0], self->r.client->ps.pmove.origin[1], self->r.client->ps.pmove.origin[2] + self->r.client->ps.viewheight ); AngleVectors( self->r.client->ps.viewangles, viewforward, NULL, NULL ); for( i = 0; i < gs.maxclients; i++ ) { other = PLAYERENT( i ); if( !other->r.inuse ) { continue; } if( !other->r.client ) { continue; } if( other == self ) { continue; } if( !other->r.solid || ( other->r.svflags & SVF_NOCLIENT ) ) { continue; } VectorSubtract( other->s.origin, self->s.origin, dir ); dist = VectorNormalize2( dir, dir ); if( dist > 1000 ) { continue; } value = DotProduct( dir, viewforward ); if( value > value_best ) { BuildBoxPoints( boxpoints, other->s.origin, tv( 4, 4, 4 ), tv( 4, 4, 4 ) ); for( j = 0; j < 8; j++ ) { G_Trace( &trace, vieworg, vec3_origin, vec3_origin, boxpoints[j], self, MASK_SHOT | MASK_OPAQUE ); if( trace.ent && trace.ent == ENTNUM( other ) ) { value_best = value; bestNum = ENTNUM( other ); } } } } return bestNum; }
/** * @todo Use this function to enable footsteps over network * @note Only play the water sounds if actor is not in STATE_CROUCHED mode * we can assume, that moving slower and more carefully would also not produce * the water sounds */ void G_PhysicsStep (edict_t *ent) { /** * @todo don't play foot step sounds for flying units. */ if (ent->moveinfo.currentStep < ent->moveinfo.steps) { const int contentFlags = ent->contentFlags; const vismask_t visflags = ent->moveinfo.visflags[ent->moveinfo.currentStep]; /* Send the sound effect to everyone how's not seeing the actor */ if (!G_IsCrouched(ent)) { if (contentFlags & CONTENTS_WATER) { if (ent->moveinfo.contentFlags[ent->moveinfo.currentStep] & CONTENTS_WATER) { /* looks like we already are in the water */ /* send water moving sound */ G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, "footsteps/water_under"); } else { /* send water entering sound */ G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, "footsteps/water_in"); } } else if (ent->contentFlags & CONTENTS_WATER) { /* send water leaving sound */ G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, "footsteps/water_out"); } else { trace_t trace; vec3_t from, to; VectorCopy(ent->origin, to); VectorCopy(ent->origin, from); /* we should really hit the ground with this */ to[2] -= UNIT_HEIGHT; trace = G_Trace(from, to, NULL, MASK_SOLID); if (trace.surface) { const char *snd = gi.GetFootstepSound(trace.surface->name); if (snd) G_EventSpawnSound(~G_VisToPM(visflags), true, ent, ent->origin, snd); } } } /* and now save the new contents */ ent->contentFlags = ent->moveinfo.contentFlags[ent->moveinfo.currentStep]; ent->moveinfo.currentStep++; /* Immediately re-think */ ent->nextthink = (level.framenum + 3) * SERVER_FRAME_SECONDS; } else { ent->moveinfo.currentStep = 0; ent->moveinfo.steps = 0; ent->think = NULL; } }
bool TacticalSpotsDetector::LooksLikeACoverArea(const aas_area_t &area, const OriginParams &originParams, const CoverProblemParams &problemParams) { edict_t *passent = const_cast<edict_t *>(problemParams.attackerEntity); float *attackerOrigin = const_cast<float *>(problemParams.attackerOrigin); float *areaCenter = const_cast<float *>(area.center); const edict_t *doNotHitEntity = originParams.originEntity; trace_t trace; G_Trace(&trace, attackerOrigin, nullptr, nullptr, areaCenter, passent, MASK_AISOLID); if (trace.fraction == 1.0f) return false; float harmfulRayThickness = problemParams.harmfulRayThickness; vec3_t bounds[2] = { { -harmfulRayThickness, -harmfulRayThickness, -harmfulRayThickness }, { +harmfulRayThickness, +harmfulRayThickness, +harmfulRayThickness } }; // Convert bounds from relative to absolute VectorAdd(bounds[0], area.center, bounds[0]); VectorAdd(bounds[1], area.center, bounds[1]); for (int i = 0; i < 8; ++i) { vec3_t traceEnd; traceEnd[0] = bounds[(i >> 2) & 1][0]; traceEnd[1] = bounds[(i >> 1) & 1][1]; traceEnd[2] = bounds[(i >> 0) & 1][2]; G_Trace(&trace, attackerOrigin, nullptr, nullptr, traceEnd, passent, MASK_AISOLID); if (trace.fraction == 1.0f || game.edicts + trace.ent == doNotHitEntity) return false; } return true; }
/* * AI_DropNodeOriginToFloor */ qboolean AI_DropNodeOriginToFloor( vec3_t origin, edict_t *passent ) { trace_t trace; G_Trace( &trace, origin, tv( item_box_mins[0], item_box_mins[1], 0 ), tv( item_box_maxs[0], item_box_maxs[1], 0 ), tv( origin[0], origin[1], world->r.mins[2] ), passent, MASK_NODESOLID ); if( trace.startsolid ) return qfalse; origin[0] = trace.endpos[0]; origin[1] = trace.endpos[1]; origin[2] = trace.endpos[2] + 2 + abs( playerbox_stand_mins[2] ); return qtrue; }
void AiEntityPhysicsState::UpdateAreaNums() { const AiAasWorld *aasWorld = AiAasWorld::Instance(); this->currAasAreaNum = ( decltype( this->currAasAreaNum ) )aasWorld->FindAreaNum( Origin() ); // Use a computation shortcut when entity is on ground if( this->groundEntNum >= 0 ) { this->droppedToFloorOriginOffset = ( decltype( this->droppedToFloorOriginOffset ) )( -playerbox_stand_mins[2] ); this->droppedToFloorOriginOffset += 4.0f; SetHeightOverGround( 0 ); Vec3 droppedOrigin( Origin() ); droppedOrigin.Z() -= this->droppedToFloorOriginOffset; this->droppedToFloorAasAreaNum = ( decltype( this->droppedToFloorAasAreaNum ) )aasWorld->FindAreaNum( droppedOrigin ); return; } // Use a computation shortcut when the current area is grounded if( aasWorld->AreaSettings()[this->currAasAreaNum].areaflags & AREA_GROUNDED ) { float areaMinsZ = aasWorld->Areas()[this->currAasAreaNum].mins[2]; float selfZ = Self()->s.origin[2]; float heightOverGround_ = selfZ - areaMinsZ + playerbox_stand_maxs[2]; clamp_high( heightOverGround_, GROUND_TRACE_DEPTH ); SetHeightOverGround( heightOverGround_ ); this->droppedToFloorOriginOffset = ( decltype( this->droppedToFloorOriginOffset ) )( heightOverGround_ - 4.0f ); this->droppedToFloorAasAreaNum = this->currAasAreaNum; return; } // Try drop an origin from air to floor trace_t trace; edict_t *ent = const_cast<edict_t *>( Self() ); Vec3 traceEnd( Origin() ); traceEnd.Z() -= GROUND_TRACE_DEPTH; G_Trace( &trace, this->origin, ent->r.mins, ent->r.maxs, traceEnd.Data(), ent, MASK_PLAYERSOLID ); // Check not only whether there is a hit but test whether is it really a ground (and not a wall or obstacle) if( trace.fraction != 1.0f && Origin()[2] - trace.endpos[2] > -playerbox_stand_mins[2] ) { float heightOverGround_ = trace.fraction * GROUND_TRACE_DEPTH + playerbox_stand_mins[2]; this->droppedToFloorOriginOffset = ( decltype( this->droppedToFloorOriginOffset ) )( -playerbox_stand_mins[2] ); this->droppedToFloorOriginOffset -= heightOverGround_ - 4.0f; SetHeightOverGround( heightOverGround_ ); Vec3 droppedOrigin( Origin() ); droppedOrigin.Z() -= this->droppedToFloorOriginOffset; this->droppedToFloorAasAreaNum = ( decltype( this->droppedToFloorAasAreaNum ) )aasWorld->FindAreaNum( droppedOrigin ); return; } this->droppedToFloorOriginOffset = 0; SetHeightOverGround( std::numeric_limits<float>::infinity() ); this->droppedToFloorAasAreaNum = this->currAasAreaNum; }
qboolean AI_visible( edict_t *self, edict_t *other ) { vec3_t spot1; vec3_t spot2; trace_t trace; VectorCopy( self->s.origin, spot1 ); spot1[2] += self->viewheight; VectorCopy( other->s.origin, spot2 ); spot2[2] += other->viewheight; G_Trace( &trace, spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE ); if( trace.fraction == 1.0 ) return qtrue; return qfalse; }
//========================================== // AI_ItemIsReachable // Can we get there? Jalfixme: this needs better checks a lot //========================================== qboolean AI_ShortRangeReachable( edict_t *self, vec3_t goal ) { trace_t trace; vec3_t v; VectorCopy( self->r.mins, v ); v[2] += AI_STEPSIZE; G_Trace( &trace, self->s.origin, v, self->r.maxs, goal, self, MASK_NODESOLID ); //trace = gi.trace ( self->s.origin, v, self->maxs, goal, self, MASK_NODESOLID ); // Yes we can see it if( trace.fraction == 1.0 ) return qtrue; else return qfalse; }
//=================== // AI_IsStep // Checks the floor one step below the player. Used to detect // if the player is really falling or just walking down a stair. //=================== qboolean AI_IsStep( edict_t *ent ) { vec3_t point; trace_t trace; //determine a point below point[0] = ent->s.origin[0]; point[1] = ent->s.origin[1]; point[2] = ent->s.origin[2] - ( 1.6*AI_STEPSIZE ); //trace to point G_Trace( &trace, ent->s.origin, ent->r.mins, ent->r.maxs, point, ent, MASK_PLAYERSOLID ); if( !ISWALKABLEPLANE( &trace.plane ) && !trace.startsolid ) return qfalse; //found solid. return qtrue; }
//========================================== // BOT_DMclass_PredictProjectileShot // predict target movement //========================================== static void BOT_DMclass_PredictProjectileShot( edict_t *self, vec3_t fire_origin, float projectile_speed, vec3_t target, vec3_t target_velocity ) { vec3_t predictedTarget; vec3_t targetMovedir; float targetSpeed; float predictionTime; float distance; trace_t trace; int contents; if( projectile_speed <= 0.0f ) return; targetSpeed = VectorNormalize2( target_velocity, targetMovedir ); // ok, this is not going to be 100% precise, since we will find the // time our projectile will take to travel to enemy's CURRENT position, // and them find enemy's position given his CURRENT velocity and his CURRENT dir // after prediction time. The result will be much better if the player // is moving to the sides (relative to us) than in depth (relative to us). // And, of course, when the player moves in a curve upwards it will totally miss (ie, jumping). // but in general it does a great job, much better than any human player :) distance = DistanceFast( fire_origin, target ); predictionTime = distance/projectile_speed; VectorMA( target, predictionTime*targetSpeed, targetMovedir, predictedTarget ); // if this position is inside solid, try finding a position at half of the prediction time contents = G_PointContents( predictedTarget ); if( contents & CONTENTS_SOLID && !( contents & CONTENTS_PLAYERCLIP ) ) { VectorMA( target, ( predictionTime * 0.5f )*targetSpeed, targetMovedir, predictedTarget ); contents = G_PointContents( predictedTarget ); if( contents & CONTENTS_SOLID && !( contents & CONTENTS_PLAYERCLIP ) ) return; // INVALID } // if we can see this point, we use it, otherwise we keep the current position G_Trace( &trace, fire_origin, vec3_origin, vec3_origin, predictedTarget, self, MASK_SHOT ); if( trace.fraction == 1.0f || ( trace.ent && game.edicts[trace.ent].takedamage ) ) VectorCopy( predictedTarget, target ); }
qboolean SensoryPerception::isInLineOfSight( const Vector &position , const int entNum ) { auto startPos = act->origin; startPos.z += 15; auto endPos = position; endPos.z += 15; Vector modMins; Vector modMaxs; modMins = act->mins; modMins *= 1.5; modMaxs = act->maxs; modMaxs *= 1.5; auto trace = G_Trace( startPos, modMins, modMaxs, endPos, act, act->edict->clipmask, false, "isInLineOfSight" ); //G_DebugLine( startPos , trace.endpos, 1.0f, 0.0f, 1.0f, 1.0f ); _lineOfSight.entNum = entNum; _lineOfSight.time = level.time; if ( trace.entityNum == entNum || entNum == ENTITYNUM_NONE ) { _lineOfSight.inLineOfSight = true; } else { auto traceEnt = G_GetEntity( trace.entityNum ); _lineOfSight.inLineOfSight = false; if ( traceEnt && traceEnt->isSubclassOf( Actor) ) { Actor *traceActor; traceActor = dynamic_cast<Actor*>(traceEnt); _lineOfSight.inLineOfSight = traceActor->sensoryPerception->checkInLineOfSight( position , entNum ); } } return _lineOfSight.inLineOfSight; }
int AI_FindClosestReachableNode( vec3_t origin, edict_t *passent, int range, unsigned int flagsmask ) { int i; float closest; float dist; int node = -1; trace_t tr; vec3_t maxs, mins; VectorSet( mins, -8, -8, -8 ); VectorSet( maxs, 8, 8, 8 ); // For Ladders, do not worry so much about reachability if( flagsmask & NODEFLAGS_LADDER ) { VectorCopy( vec3_origin, maxs ); VectorCopy( vec3_origin, mins ); } closest = range; for( i = 0; i < nav.num_nodes; i++ ) { if( flagsmask == NODE_ALL || nodes[i].flags & flagsmask ) { dist = DistanceFast( nodes[i].origin, origin ); if( dist < closest ) { // make sure it is visible G_Trace( &tr, origin, mins, maxs, nodes[i].origin, passent, MASK_NODESOLID ); if( tr.fraction == 1.0 ) { node = i; closest = dist; } } } } return node; }
/* * G_Teleport * * Teleports client to specified position * If client is not spectator teleporting is only done if position is free and teleport effects are drawn. */ static bool G_Teleport( edict_t *ent, vec3_t origin, vec3_t angles ) { int i; if( !ent->r.inuse || !ent->r.client ) return false; if( ent->r.client->ps.pmove.pm_type != PM_SPECTATOR ) { trace_t tr; G_Trace( &tr, origin, ent->r.mins, ent->r.maxs, origin, ent, MASK_PLAYERSOLID ); if( tr.fraction != 1.0f || tr.startsolid ) return false; G_TeleportEffect( ent, false ); } VectorCopy( origin, ent->s.origin ); VectorCopy( origin, ent->s.old_origin ); VectorCopy( origin, ent->olds.origin ); ent->s.teleported = qtrue; VectorClear( ent->velocity ); ent->r.client->ps.pmove.pm_time = 1; ent->r.client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; if( ent->r.client->ps.pmove.pm_type != PM_SPECTATOR ) G_TeleportEffect( ent, true ); // set angles VectorCopy( angles, ent->s.angles ); VectorCopy( angles, ent->r.client->ps.viewangles ); // set the delta angle for( i = 0; i < 3; i++ ) ent->r.client->ps.pmove.delta_angles[i] = ANGLE2SHORT( ent->r.client->ps.viewangles[i] ) - ent->r.client->ucmd.angles[i]; return true; }