gentity_t* G_BuildHead(gentity_t *ent) { gentity_t* head; orientation_t or; // DHM - Nerve head = G_Spawn (); if (trap_GetTag( ent->s.number, 0, "tag_head", &or )) { G_SetOrigin( head, or.origin ); } else { float height, dest; vec3_t v, angles, forward, up, right; G_SetOrigin (head, ent->r.currentOrigin); if( ent->client->ps.eFlags & EF_PRONE ) { height = ent->client->ps.viewheight - 56; } else if( ent->client->ps.pm_flags & PMF_DUCKED ) { // closer fake offset for 'head' box when crouching height = ent->client->ps.crouchViewHeight - 12; } else { height = ent->client->ps.viewheight; } // NERVE - SMF - this matches more closely with WolfMP models VectorCopy( ent->client->ps.viewangles, angles ); if ( angles[PITCH] > 180 ) { dest = (-360 + angles[PITCH]) * 0.75; } else { dest = angles[PITCH] * 0.75; } angles[PITCH] = dest; AngleVectors( angles, forward, right, up ); if( ent->client->ps.eFlags & EF_PRONE ) { VectorScale( forward, 24, v ); } else { VectorScale( forward, 5, v ); } VectorMA( v, 18, up, v ); VectorAdd( v, head->r.currentOrigin, head->r.currentOrigin ); head->r.currentOrigin[2] += height / 2; // -NERVE - SMF } VectorCopy (head->r.currentOrigin, head->s.origin); VectorCopy (ent->r.currentAngles, head->s.angles); VectorCopy (head->s.angles, head->s.apos.trBase); VectorCopy (head->s.angles, head->s.apos.trDelta); VectorSet (head->r.mins , -6, -6, -2); // JPW NERVE changed this z from -12 to -6 for crouching, also removed standing offset VectorSet (head->r.maxs , 6, 6, 10); // changed this z from 0 to 6 head->clipmask = CONTENTS_SOLID; head->r.contents = CONTENTS_SOLID; head->parent = ent; head->s.eType = ET_TEMPHEAD; trap_LinkEntity (head); return head; }
qboolean IsHeadShot( gentity_t *targ, qboolean isAICharacter, vec3_t dir, vec3_t point, int mod ) { gentity_t *head; trace_t tr; vec3_t start, end; gentity_t *traceEnt; orientation_t orient; // DHM - Nerve qboolean head_shot_weapon = qfalse; // not a player or critter so bail if ( !( targ->client ) ) { return qfalse; } if ( targ->health <= 0 ) { return qfalse; } head_shot_weapon = IsHeadShotWeapon( mod, isAICharacter ); if ( head_shot_weapon ) { head = G_Spawn(); if ( trap_GetTag( targ->s.number, "tag_head", &orient ) ) { G_SetOrigin( head, orient.origin ); } else { float height, dest; vec3_t v, angles, forward, up, right; G_SetOrigin( head, targ->r.currentOrigin ); if ( targ->client->ps.pm_flags & PMF_DUCKED ) { // closer fake offset for 'head' box when crouching height = targ->client->ps.crouchViewHeight - 12; } else { height = targ->client->ps.viewheight; } // NERVE - SMF - this matches more closely with WolfMP models VectorCopy( targ->client->ps.viewangles, angles ); if ( angles[PITCH] > 180 ) { dest = ( -360 + angles[PITCH] ) * 0.75; } else { dest = angles[PITCH] * 0.75; } angles[PITCH] = dest; AngleVectors( angles, forward, right, up ); VectorScale( forward, 5, v ); VectorMA( v, 18, up, v ); VectorAdd( v, head->r.currentOrigin, head->r.currentOrigin ); head->r.currentOrigin[2] += height / 2; // -NERVE - SMF } VectorCopy( head->r.currentOrigin, head->s.origin ); VectorCopy( targ->r.currentAngles, head->s.angles ); VectorCopy( head->s.angles, head->s.apos.trBase ); VectorCopy( head->s.angles, head->s.apos.trDelta ); VectorSet( head->r.mins, -6, -6, -2 ); // JPW NERVE changed this z from -12 to -6 for crouching, also removed standing offset VectorSet( head->r.maxs, 6, 6, 10 ); // changed this z from 0 to 6 head->clipmask = CONTENTS_SOLID; head->r.contents = CONTENTS_SOLID; trap_LinkEntity( head ); // trace another shot see if we hit the head VectorCopy( point, start ); VectorMA( start, 64, dir, end ); trap_Trace( &tr, start, NULL, NULL, end, targ->s.number, MASK_SHOT ); traceEnt = &g_entities[ tr.entityNum ]; if ( g_debugBullets.integer >= 3 ) { // show hit player head bb gentity_t *tent; vec3_t b1, b2; VectorCopy( head->r.currentOrigin, b1 ); VectorCopy( head->r.currentOrigin, b2 ); VectorAdd( b1, head->r.mins, b1 ); VectorAdd( b2, head->r.maxs, b2 ); tent = G_TempEntity( b1, EV_RAILTRAIL ); VectorCopy( b2, tent->s.origin2 ); tent->s.dmgFlags = 1; // show headshot trace // end the headshot trace at the head box if it hits if ( tr.fraction != 1 ) { VectorMA( start, ( tr.fraction * 64 ), dir, end ); } tent = G_TempEntity( start, EV_RAILTRAIL ); VectorCopy( end, tent->s.origin2 ); tent->s.dmgFlags = 0; } G_FreeEntity( head ); if ( traceEnt == head ) { level.totalHeadshots++; // NERVE - SMF return qtrue; } else { level.missedHeadshots++; // NERVE - SMF } } return qfalse; }
/* ============== AICast_CheckVisibility ============== */ qboolean AICast_CheckVisibility( gentity_t *srcent, gentity_t *destent ) { vec3_t dir, entangles, middle, eye, viewangles; cast_state_t *cs; float fov, dist; int viewer, ent; orientation_t or; if ( destent->flags & FL_NOTARGET ) { return qfalse; } viewer = srcent->s.number; ent = destent->s.number; // cs = AICast_GetCastState( viewer ); AICast_GetCastState( ent ); // set the FOV fov = cs->attributes[FOV] * aiStateFovScales[cs->aiState]; if ( !fov ) { // assume it's a player, give them a generic fov fov = 180; } if ( cs->aiFlags & AIFL_ZOOMING ) { fov *= 0.8; } //calculate middle of bounding box VectorAdd( destent->r.mins, destent->r.maxs, middle ); VectorScale( middle, 0.5, middle ); VectorAdd( destent->client->ps.origin, middle, middle ); // calculate eye position if ( srcent->r.svFlags & SVF_CASTAI ) { if ( trap_GetTag( srcent->s.number, "tag_head", &or ) ) { // use the actual direction the head is facing vectoangles( or.axis[0], viewangles ); // and the actual position of the head VectorCopy( or.origin, eye ); } else { VectorCopy( srcent->client->ps.origin, eye ); eye[2] += srcent->client->ps.viewheight; VectorCopy( srcent->client->ps.viewangles, viewangles ); } } else { VectorCopy( srcent->client->ps.origin, eye ); eye[2] += srcent->client->ps.viewheight; VectorCopy( srcent->client->ps.viewangles, viewangles ); } //check if entity is within field of vision VectorSubtract( middle, eye, dir ); vectoangles( dir, entangles ); // dist = VectorLength( dir ); // // alertness is visible range if ( cs->bs && dist > cs->attributes[ALERTNESS] ) { return qfalse; } // check FOV if ( !AICast_InFieldOfVision( viewangles, fov, entangles ) ) { return qfalse; } // if ( !AICast_VisibleFromPos( srcent->client->ps.origin, srcent->s.number, destent->client->ps.origin, destent->s.number, qtrue ) ) { return qfalse; } // return qtrue; }
/* ============== AICast_CheckVisibility ============== */ qboolean AICast_CheckVisibility( gentity_t *srcent, gentity_t *destent ) { vec3_t dir, entangles, middle, eye, viewangles; cast_state_t *cs, *ocs; float fov, dist; int viewer, ent; cast_visibility_t *vis; orientation_t or; if ( destent->flags & FL_NOTARGET ) { return qfalse; } // viewer = srcent->s.number; ent = destent->s.number; // cs = AICast_GetCastState( viewer ); ocs = AICast_GetCastState( ent ); // vis = &cs->vislist[ent]; // // if the destent is the client, and they have just loaded a savegame, ignore them temporarily if ( !destent->aiCharacter && level.lastLoadTime && ( level.lastLoadTime > level.time - 2000 ) && !vis->visible_timestamp ) { return qfalse; } // if we heard them /* if ( (vis->lastcheck_timestamp) && (ocs->lastWeaponFired) && (ocs->lastWeaponFired >= vis->lastcheck_timestamp) && (AICast_GetWeaponSoundRange( ocs->lastWeaponFiredWeaponNum ) > Distance( srcent->r.currentOrigin, ocs->lastWeaponFiredPos ))) { return qtrue; } */ // // set the FOV fov = cs->attributes[FOV] * aiStateFovScales[cs->aiState]; if ( !fov ) { // assume it's a player, give them a generic fov fov = 180; } if ( cs->aiFlags & AIFL_ZOOMING ) { fov *= 0.8; } else { if ( cs->lastEnemy >= 0 ) { // they've already been in a fight, so give them a very large fov if ( fov < 270 ) { fov = 270; } } } // RF, if they were visible last check, then give us a full FOV, since we are aware of them if ( cs->aiState >= AISTATE_ALERT && vis->visible_timestamp == vis->lastcheck_timestamp ) { fov = 360; } //calculate middle of bounding box VectorAdd( destent->r.mins, destent->r.maxs, middle ); VectorScale( middle, 0.5, middle ); VectorAdd( destent->client->ps.origin, middle, middle ); // calculate eye position if ( ( level.lastLoadTime < level.time - 4000 ) && ( srcent->r.svFlags & SVF_CASTAI ) ) { if ( clientHeadTagTimes[srcent->s.number] == level.time ) { // use the actual direction the head is facing vectoangles( clientHeadTags[srcent->s.number].axis[0], viewangles ); // and the actual position of the head VectorCopy( clientHeadTags[srcent->s.number].origin, eye ); } else if ( trap_GetTag( srcent->s.number, "tag_head", &or ) ) { // use the actual direction the head is facing vectoangles( or.axis[0], viewangles ); // and the actual position of the head VectorCopy( or.origin, eye ); VectorMA( eye, 12, or.axis[2], eye ); // save orientation data memcpy( &clientHeadTags[srcent->s.number], &or, sizeof( orientation_t ) ); clientHeadTagTimes[srcent->s.number] = level.time; } else { VectorCopy( srcent->client->ps.origin, eye ); eye[2] += srcent->client->ps.viewheight; VectorCopy( srcent->client->ps.viewangles, viewangles ); // save orientation data (so we dont keep checking for a tag when it doesn't exist) VectorCopy( eye, clientHeadTags[srcent->s.number].origin ); AnglesToAxis( viewangles, clientHeadTags[srcent->s.number].axis ); clientHeadTagTimes[srcent->s.number] = level.time; } } else { VectorCopy( srcent->client->ps.origin, eye ); eye[2] += srcent->client->ps.viewheight; VectorCopy( srcent->client->ps.viewangles, viewangles ); } //check if entity is within field of vision VectorSubtract( middle, eye, dir ); vectoangles( dir, entangles ); // dist = VectorLength( dir ); // // alertness is visible range if ( cs->bs && dist > cs->attributes[ALERTNESS] ) { return qfalse; } // check FOV if ( !AICast_InFieldOfVision( viewangles, fov, entangles ) ) { return qfalse; } // if ( !AICast_VisibleFromPos( srcent->client->ps.origin, srcent->s.number, destent->client->ps.origin, destent->s.number, qtrue ) ) { return qfalse; } // return qtrue; }
/* ============== IsHeadShot ============== */ qboolean IsHeadShot( gentity_t *targ, gentity_t *attacker, vec3_t dir, vec3_t point, int mod ) { gentity_t *head; trace_t tr; vec3_t start, end; gentity_t *traceEnt; orientation_t or; qboolean head_shot_weapon = qfalse; // not a player or critter so bail if ( !( targ->client ) ) { return qfalse; } if ( targ->health <= 0 ) { return qfalse; } head_shot_weapon = IsHeadShotWeapon( mod, targ, attacker ); if ( head_shot_weapon ) { head = G_Spawn(); G_SetOrigin( head, targ->r.currentOrigin ); // RF, if there is a valid tag_head for this entity, then use that if ( ( targ->r.svFlags & SVF_CASTAI ) && trap_GetTag( targ->s.number, "tag_head", &or ) ) { VectorCopy( or.origin, head->r.currentOrigin ); VectorMA( head->r.currentOrigin, 6, or.axis[2], head->r.currentOrigin ); // tag is at base of neck } else if ( targ->client->ps.pm_flags & PMF_DUCKED ) { // closer fake offset for 'head' box when crouching head->r.currentOrigin[2] += targ->client->ps.crouchViewHeight + 8; // JPW NERVE 16 is kludge to get head height to match up } //else if(targ->client->ps.legsAnim == LEGS_IDLE && targ->aiCharacter == AICHAR_SOLDIER) // standing with legs bent (about a head shorter than other leg poses) // head->r.currentOrigin[2] += targ->client->ps.viewheight; else { head->r.currentOrigin[2] += targ->client->ps.viewheight; // JPW NERVE pulled this // 6 is fudged "head height" value } VectorCopy( head->r.currentOrigin, head->s.origin ); VectorCopy( targ->r.currentAngles, head->s.angles ); VectorCopy( head->s.angles, head->s.apos.trBase ); VectorCopy( head->s.angles, head->s.apos.trDelta ); VectorSet( head->r.mins, -6, -6, -6 ); // JPW NERVE changed this z from -12 to -6 for crouching, also removed standing offset VectorSet( head->r.maxs, 6, 6, 6 ); // changed this z from 0 to 6 head->clipmask = CONTENTS_SOLID; head->r.contents = CONTENTS_SOLID; trap_LinkEntity( head ); // trace another shot see if we hit the head VectorCopy( point, start ); VectorMA( start, 64, dir, end ); trap_Trace( &tr, start, NULL, NULL, end, targ->s.number, MASK_SHOT ); traceEnt = &g_entities[ tr.entityNum ]; if ( g_debugBullets.integer >= 3 ) { // show hit player head bb gentity_t *tent; vec3_t b1, b2; VectorCopy( head->r.currentOrigin, b1 ); VectorCopy( head->r.currentOrigin, b2 ); VectorAdd( b1, head->r.mins, b1 ); VectorAdd( b2, head->r.maxs, b2 ); tent = G_TempEntity( b1, EV_RAILTRAIL ); VectorCopy( b2, tent->s.origin2 ); tent->s.dmgFlags = 1; // show headshot trace // end the headshot trace at the head box if it hits if ( tr.fraction != 1 ) { VectorMA( start, ( tr.fraction * 64 ), dir, end ); } tent = G_TempEntity( start, EV_RAILTRAIL ); VectorCopy( end, tent->s.origin2 ); tent->s.dmgFlags = 0; } G_FreeEntity( head ); if ( traceEnt == head ) { return qtrue; } } return qfalse; }