void NPC_DrawSquadPath( squadPath_t *squadPath ) { int i, j; vec3_t mins, maxs; for(i = 0; i < squadPath->numWaypoints; i++) { if ( gi.inPVS(squadPath->waypoints[i].origin, g_entities[0].currentOrigin) ) { VectorAdd( squadPath->waypoints[i].origin, playerMins, mins ); VectorAdd( squadPath->waypoints[i].origin, playerMaxs, maxs ); CG_Cube( mins, maxs, LIGHT_BLUE, 0.25 ); for(j = 0; j < MAX_PATH_BRANCHES; j++) { if( squadPath->waypoints[i].nextWp[j] != -1 ) { //don't draw 2-lines for 2-way connections... if ( i > squadPath->waypoints[i].nextWp[j] ) {//Would have already drawn this one continue; } CG_Line( squadPath->waypoints[i].origin, squadPath->waypoints[squadPath->waypoints[i].nextWp[j]].origin, LIGHT_BLUE, 0.25 ); } } } } }
void NPC_ShowDebugInfo (void) { if ( showBBoxes ) { gentity_t *found = NULL; vec3_t mins, maxs; while( (found = G_Find( found, FOFS(classname), "NPC" ) ) != NULL ) { if ( gi.inPVS( found->currentOrigin, g_entities[0].currentOrigin ) ) { VectorAdd( found->currentOrigin, found->mins, mins ); VectorAdd( found->currentOrigin, found->maxs, maxs ); CG_Cube( mins, maxs, RED, 0.25 ); } } } if ( showSPaths[0] ) { if ( Q_stricmp( "all", showSPaths ) == 0 ) { int i; for(i = 0; i < num_squad_paths; i++) { NPC_DrawSquadPath( &squadPaths[i] ); } } else { gentity_t *found = NULL; if ( (found = G_Find(found, FOFS(targetname), showSPaths)) != NULL && found->NPC && found->NPC->iSquadPathIndex != -1 ) { NPC_DrawSquadPath( &squadPaths[found->NPC->iSquadPathIndex] ); } } } }
void NPC_BSJump (void) { vec3_t dir, angles, p1, p2, apex; float time, height, forward, z, xy, dist, yawError, apexHeight; if( !NPCInfo->goalEntity ) {//Should have task completed the navgoal return; } if ( NPCInfo->jumpState != JS_JUMPING && NPCInfo->jumpState != JS_LANDING ) { //Face navgoal VectorSubtract(NPCInfo->goalEntity->currentOrigin, NPC->currentOrigin, dir); vectoangles(dir, angles); NPCInfo->desiredPitch = NPCInfo->lockedDesiredPitch = AngleNormalize360(angles[PITCH]); NPCInfo->desiredYaw = NPCInfo->lockedDesiredYaw = AngleNormalize360(angles[YAW]); } NPC_UpdateAngles ( qtrue, qtrue ); yawError = AngleDelta ( NPC->client->ps.viewangles[YAW], NPCInfo->desiredYaw ); //We don't really care about pitch here switch ( NPCInfo->jumpState ) { case JS_FACING: if ( yawError < MIN_ANGLE_ERROR ) {//Facing it, Start crouching NPC_SetAnim(NPC, SETANIM_LEGS, BOTH_CROUCH1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); NPCInfo->jumpState = JS_CROUCHING; } break; case JS_CROUCHING: if ( NPC->client->ps.legsAnimTimer > 0 ) {//Still playing crouching anim return; } //Create a parabola if ( NPC->currentOrigin[2] > NPCInfo->goalEntity->currentOrigin[2] ) { VectorCopy( NPC->currentOrigin, p1 ); VectorCopy( NPCInfo->goalEntity->currentOrigin, p2 ); } else if ( NPC->currentOrigin[2] < NPCInfo->goalEntity->currentOrigin[2] ) { VectorCopy( NPCInfo->goalEntity->currentOrigin, p1 ); VectorCopy( NPC->currentOrigin, p2 ); } else { VectorCopy( NPC->currentOrigin, p1 ); VectorCopy( NPCInfo->goalEntity->currentOrigin, p2 ); } //z = xy*xy VectorSubtract( p2, p1, dir ); dir[2] = 0; //Get xy and z diffs xy = VectorNormalize( dir ); z = p1[2] - p2[2]; apexHeight = APEX_HEIGHT/2; /* //Determine most desirable apex height apexHeight = (APEX_HEIGHT * PARA_WIDTH/xy) + (APEX_HEIGHT * z/128); if ( apexHeight < APEX_HEIGHT * 0.5 ) { apexHeight = APEX_HEIGHT*0.5; } else if ( apexHeight > APEX_HEIGHT * 2 ) { apexHeight = APEX_HEIGHT*2; } */ //FIXME: length of xy will change curve of parabola, need to account for this //somewhere... PARA_WIDTH z = (sqrt(apexHeight + z) - sqrt(apexHeight)); assert(z >= 0); // gi.Printf("apex is %4.2f percent from p1: ", (xy-z)*0.5/xy*100.0f); xy -= z; xy *= 0.5; assert(xy > 0); VectorMA( p1, xy, dir, apex ); apex[2] += apexHeight; VectorCopy(apex, NPC->pos1); //Now we have the apex, aim for it height = apex[2] - NPC->currentOrigin[2]; time = sqrt( height / ( .5 * NPC->client->ps.gravity ) ); if ( !time ) { // gi.Printf("ERROR no time in jump\n"); return; } // set s.origin2 to the push velocity VectorSubtract ( apex, NPC->currentOrigin, NPC->client->ps.velocity ); NPC->client->ps.velocity[2] = 0; dist = VectorNormalize( NPC->client->ps.velocity ); forward = dist / time; VectorScale( NPC->client->ps.velocity, forward, NPC->client->ps.velocity ); NPC->client->ps.velocity[2] = time * NPC->client->ps.gravity; // gi.Printf( "%s jumping %s, gravity at %4.0f percent\n", NPC->targetname, vtos(NPC->client->ps.velocity), NPC->client->ps.gravity/8.0f ); NPC->flags |= FL_NO_KNOCKBACK; NPCInfo->jumpState = JS_JUMPING; //FIXME: jumpsound? break; case JS_JUMPING: if ( showBBoxes ) { VectorAdd(NPC->mins, NPC->pos1, p1); VectorAdd(NPC->maxs, NPC->pos1, p2); CG_Cube( p1, p2, NPCDEBUG_BLUE, 0.5 ); } if ( NPC->s.groundEntityNum != ENTITYNUM_NONE) {//Landed, start landing anim //FIXME: if the VectorClear(NPC->client->ps.velocity); NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_LAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); NPCInfo->jumpState = JS_LANDING; //FIXME: landsound? } else if ( NPC->client->ps.legsAnimTimer > 0 ) {//Still playing jumping anim //FIXME: apply jump velocity here, a couple frames after start, not right away return; } else {//still in air, but done with jump anim, play inair anim NPC_SetAnim(NPC, SETANIM_BOTH, BOTH_INAIR1, SETANIM_FLAG_OVERRIDE); } break; case JS_LANDING: if ( NPC->client->ps.legsAnimTimer > 0 ) {//Still playing landing anim return; } else { NPCInfo->jumpState = JS_WAITING; //task complete no matter what... NPC_ClearGoal(); NPCInfo->goalTime = level.time; NPCInfo->aiFlags &= ~NPCAI_MOVING; ucmd.forwardmove = 0; NPC->flags &= ~FL_NO_KNOCKBACK; //Return that the goal was reached Q3_TaskIDComplete( NPC, TID_MOVE_NAV ); //Or should we keep jumping until reached goal? /* NPCInfo->goalEntity = UpdateGoal(); if ( !NPCInfo->goalEntity ) { NPC->flags &= ~FL_NO_KNOCKBACK; Q3_TaskIDComplete( NPC, TID_MOVE_NAV ); } */ } break; case JS_WAITING: default: NPCInfo->jumpState = JS_FACING; break; } }