void CL_FinishMove( usercmd_t *cmd ) { int i; // copy the state that the cgame is currently sending cmd->weapon = cl.cgameUserCmdValue; cmd->forcesel = cl.cgameForceSelection; cmd->invensel = cl.cgameInvenSelection; if (cl.gcmdSendValue) { cmd->generic_cmd = cl.gcmdValue; //cl.gcmdSendValue = qfalse; cl.gcmdSentValue = qtrue; } else { cmd->generic_cmd = 0; } // send the current server time so the amount of movement // can be determined without allowing cheating cmd->serverTime = cl.serverTime; if (cl.cgameViewAngleForceTime > cl.serverTime) { cl.cgameViewAngleForce[YAW] -= SHORT2ANGLE(cl.snap.ps.delta_angles[YAW]); cl.viewangles[YAW] = cl.cgameViewAngleForce[YAW]; cl.cgameViewAngleForceTime = 0; } if ( cl_crazyShipControls ) { float pitchSubtract, pitchDelta, yawDelta; yawDelta = AngleSubtract(cl.viewangles[YAW],cl_lastViewAngles[YAW]); //yawDelta *= (4.0f*pVeh->m_fTimeModifier); cl_sendAngles[ROLL] -= yawDelta; float nRoll = fabs(cl_sendAngles[ROLL]); pitchDelta = AngleSubtract(cl.viewangles[PITCH],cl_lastViewAngles[PITCH]); //pitchDelta *= (2.0f*pVeh->m_fTimeModifier); pitchSubtract = pitchDelta * (nRoll/90.0f); cl_sendAngles[PITCH] += pitchDelta-pitchSubtract; //yaw-roll calc should be different if (nRoll > 90.0f) { nRoll -= 180.0f; } if (nRoll < 0.0f) { nRoll = -nRoll; } pitchSubtract = pitchDelta * (nRoll/90.0f); if ( cl_sendAngles[ROLL] > 0.0f ) { cl_sendAngles[YAW] += pitchSubtract; } else { cl_sendAngles[YAW] -= pitchSubtract; } cl_sendAngles[PITCH] = AngleNormalize180( cl_sendAngles[PITCH] ); cl_sendAngles[YAW] = AngleNormalize360( cl_sendAngles[YAW] ); cl_sendAngles[ROLL] = AngleNormalize180( cl_sendAngles[ROLL] ); for (i=0 ; i<3 ; i++) { cmd->angles[i] = ANGLE2SHORT(cl_sendAngles[i]); } } else { for (i=0 ; i<3 ; i++) { cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]); } //in case we switch to the cl_crazyShipControls VectorCopy( cl.viewangles, cl_sendAngles ); } //always needed in for the cl_crazyShipControls VectorCopy( cl.viewangles, cl_lastViewAngles ); }
//----------------------------------------------------- static void turret_aim( gentity_t *self ) //----------------------------------------------------- { vec3_t enemyDir, org, org2; vec3_t desiredAngles, setAngle; float diffYaw = 0.0f, diffPitch = 0.0f, turnSpeed; const float pitchCap = 40.0f; gentity_t *top = &g_entities[self->r.ownerNum]; if ( !top ) { return; } // move our gun base yaw to where we should be at this time.... BG_EvaluateTrajectory( &top->s.apos, level.time, top->r.currentAngles ); top->r.currentAngles[YAW] = AngleNormalize180( top->r.currentAngles[YAW] ); top->r.currentAngles[PITCH] = AngleNormalize180( top->r.currentAngles[PITCH] ); turnSpeed = top->speed; if ( self->painDebounceTime > level.time ) { desiredAngles[YAW] = top->r.currentAngles[YAW]+flrand(-45,45); desiredAngles[PITCH] = top->r.currentAngles[PITCH]+flrand(-10,10); if (desiredAngles[PITCH] < -pitchCap) { desiredAngles[PITCH] = -pitchCap; } else if (desiredAngles[PITCH] > pitchCap) { desiredAngles[PITCH] = pitchCap; } diffYaw = AngleSubtract( desiredAngles[YAW], top->r.currentAngles[YAW] ); diffPitch = AngleSubtract( desiredAngles[PITCH], top->r.currentAngles[PITCH] ); turnSpeed = flrand( -5, 5 ); } else if ( self->enemy ) { // ...then we'll calculate what new aim adjustments we should attempt to make this frame // Aim at enemy VectorCopy( self->enemy->r.currentOrigin, org ); org[2]+=self->enemy->r.maxs[2]*0.5f; if (self->enemy->s.eType == ET_NPC && self->enemy->s.NPC_class == CLASS_VEHICLE && self->enemy->m_pVehicle && self->enemy->m_pVehicle->m_pVehicleInfo->type == VH_WALKER) { //hack! org[2] += 32.0f; } /* mdxaBone_t boltMatrix; // Getting the "eye" here gi.G2API_GetBoltMatrix( self->ghoul2, self->playerModel, self->torsoBolt, &boltMatrix, self->r.currentAngles, self->s.origin, (cg.time?cg.time:level.time), NULL, self->s.modelScale ); gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, org2 ); */ VectorCopy( top->r.currentOrigin, org2 ); VectorSubtract( org, org2, enemyDir ); vectoangles( enemyDir, desiredAngles ); desiredAngles[PITCH] = AngleNormalize180(desiredAngles[PITCH]); if (desiredAngles[PITCH] < -pitchCap) { desiredAngles[PITCH] = -pitchCap; } else if (desiredAngles[PITCH] > pitchCap) { desiredAngles[PITCH] = pitchCap; } diffYaw = AngleSubtract( desiredAngles[YAW], top->r.currentAngles[YAW] ); diffPitch = AngleSubtract( desiredAngles[PITCH], top->r.currentAngles[PITCH] ); } else {//FIXME: Pan back and forth in original facing // no enemy, so make us slowly sweep back and forth as if searching for a new one desiredAngles[YAW] = sin( level.time * 0.0001f + top->count ); desiredAngles[YAW] *= 60.0f; desiredAngles[YAW] += self->s.angles[YAW]; desiredAngles[YAW] = AngleNormalize180( desiredAngles[YAW] ); diffYaw = AngleSubtract( desiredAngles[YAW], top->r.currentAngles[YAW] ); diffPitch = AngleSubtract( 0, top->r.currentAngles[PITCH] ); turnSpeed = 1.0f; } if ( diffYaw ) { // cap max speed.... if ( fabs(diffYaw) > turnSpeed ) { diffYaw = ( diffYaw >= 0 ? turnSpeed : -turnSpeed ); } } if ( diffPitch ) { if ( fabs(diffPitch) > turnSpeed ) { // cap max speed diffPitch = (diffPitch > 0.0f ? turnSpeed : -turnSpeed ); } } // ...then set up our desired yaw VectorSet( setAngle, diffPitch, diffYaw, 0 ); VectorCopy( top->r.currentAngles, top->s.apos.trBase ); VectorScale( setAngle, (1000/FRAMETIME), top->s.apos.trDelta ); top->s.apos.trTime = level.time; top->s.apos.trType = TR_LINEAR_STOP; top->s.apos.trDuration = FRAMETIME; if ( diffYaw || diffPitch ) { top->s.loopSound = G_SoundIndex( "sound/vehicles/weapons/hoth_turret/turn.wav" ); } else { top->s.loopSound = 0; } }
void PM_VehicleImpact(bgEntity_t *pEnt, trace_t *trace) { // See if the vehicle has crashed into the ground. Vehicle_t *pSelfVeh = pEnt->m_pVehicle; float magnitude = VectorLength( pm->ps->velocity ) * pSelfVeh->m_pVehicleInfo->mass / 50.0f; qboolean forceSurfDestruction = qfalse; #ifdef QAGAME gentity_t *hitEnt = trace!=NULL?&g_entities[trace->entityNum]:NULL; if (!hitEnt || (pSelfVeh && pSelfVeh->m_pPilot && hitEnt && hitEnt->s.eType == ET_MISSILE && hitEnt->inuse && hitEnt->r.ownerNum == pSelfVeh->m_pPilot->s.number) ) { return; } if ( pSelfVeh//I have a vehicle struct && pSelfVeh->m_iRemovedSurfaces )//vehicle has bits removed {//spiralling to our deaths, explode on any solid impact if ( hitEnt->s.NPC_class == CLASS_VEHICLE ) {//hit another vehicle, explode! //Give credit to whoever got me into this death spiral state gentity_t *parent = (gentity_t *)pSelfVeh->m_pParentEntity; gentity_t *killer = NULL; if (parent->client->ps.otherKiller < ENTITYNUM_WORLD && parent->client->ps.otherKillerTime > level.time) { gentity_t *potentialKiller = &g_entities[parent->client->ps.otherKiller]; if (potentialKiller->inuse && potentialKiller->client) { //he's valid I guess killer = potentialKiller; } } //FIXME: damage hitEnt, some, too? Our explosion should hurt them some, but... G_Damage( (gentity_t *)pEnt, killer, killer, NULL, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT return; } else if ( !VectorCompare( trace->plane.normal, vec3_origin ) && (trace->entityNum == ENTITYNUM_WORLD || hitEnt->r.bmodel ) ) {//have a valid hit plane and we hit a solid brush vec3_t moveDir; float impactDot; VectorCopy( pm->ps->velocity, moveDir ); VectorNormalize( moveDir ); impactDot = DotProduct( moveDir, trace->plane.normal ); if ( impactDot <= -0.7f )//hit rather head-on and hard {// Just DIE now //Give credit to whoever got me into this death spiral state gentity_t *parent = (gentity_t *)pSelfVeh->m_pParentEntity; gentity_t *killer = NULL; if (parent->client->ps.otherKiller < ENTITYNUM_WORLD && parent->client->ps.otherKillerTime > level.time) { gentity_t *potentialKiller = &g_entities[parent->client->ps.otherKiller]; if (potentialKiller->inuse && potentialKiller->client) { //he's valid I guess killer = potentialKiller; } } G_Damage( (gentity_t *)pEnt, killer, killer, NULL, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT return; } } } if ( trace->entityNum < ENTITYNUM_WORLD && hitEnt->s.eType == ET_MOVER && hitEnt->s.apos.trType != TR_STATIONARY//rotating && (hitEnt->spawnflags&16) //IMPACT && Q_stricmp( "func_rotating", hitEnt->classname ) == 0 ) {//hit a func_rotating that is supposed to destroy anything it touches! //guarantee the hit will happen, thereby taking off a piece of the ship forceSurfDestruction = qtrue; } else if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f && pm->ps->velocity[2] > -100.0f ) #else if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f && pm->ps->velocity[2] > -100.0f ) #endif /* if ( (pSelfVeh->m_ulFlags&VEH_GEARSOPEN) && trace->plane.normal[2] > 0.7f && fabs(pSelfVeh->m_vOrientation[PITCH]) < 0.2f && fabs(pSelfVeh->m_vOrientation[ROLL]) < 0.2f )*/ {//we're landing, we're cool //this was annoying me -rww //FIXME: this shouldn't even be getting called when the vehicle is at rest! #ifdef QAGAME if (hitEnt && (hitEnt->s.eType == ET_PLAYER || hitEnt->s.eType == ET_NPC) && pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //always smack players } else #endif { return; } } if ( pSelfVeh && (pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) && //this is kind of weird on tauntauns and atst's.. (magnitude >= 100||forceSurfDestruction) ) { if ( pEnt->m_pVehicle->m_iHitDebounce < pm->cmd.serverTime || forceSurfDestruction ) {//a bit of a hack, may conflict with getting shot, but... //FIXME: impact sound and effect should be gotten from g_vehicleInfo...? //FIXME: should pass in trace.endpos and trace.plane.normal vec3_t vehUp; #ifndef QAGAME bgEntity_t *hitEnt; #endif if ( trace && !pSelfVeh->m_iRemovedSurfaces && !forceSurfDestruction ) { qboolean turnFromImpact = qfalse, turnHitEnt = qfalse; float l = pm->ps->speed*0.5f; vec3_t bounceDir; #ifndef QAGAME bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum); #endif if ( (trace->entityNum == ENTITYNUM_WORLD || hitEnt->s.solid == SOLID_BMODEL)//bounce off any brush && !VectorCompare(trace->plane.normal, vec3_origin) )//have a valid plane to bounce off of { //bounce off in the opposite direction of the impact if (pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER) { pm->ps->speed *= pml.frametime; VectorCopy(trace->plane.normal, bounceDir); } else if ( trace->plane.normal[2] >= MIN_LANDING_SLOPE//flat enough to land on && pSelfVeh->m_LandTrace.fraction < 1.0f //ground present && pm->ps->speed <= MIN_LANDING_SPEED ) {//could land here, don't bounce off, in fact, return altogether! return; } else { if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { turnFromImpact = qtrue; } VectorCopy(trace->plane.normal, bounceDir); } } else if ( pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER ) {//check for impact with another fighter #ifndef QAGAME bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum); #endif if ( hitEnt->s.NPC_class == CLASS_VEHICLE && hitEnt->m_pVehicle && hitEnt->m_pVehicle->m_pVehicleInfo && hitEnt->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER ) {//two vehicles hit each other, turn away from the impact turnFromImpact = qtrue; turnHitEnt = qtrue; #ifndef QAGAME VectorSubtract( pm->ps->origin, hitEnt->s.origin, bounceDir ); #else VectorSubtract( pm->ps->origin, hitEnt->r.currentOrigin, bounceDir ); #endif VectorNormalize( bounceDir ); } } if ( turnFromImpact ) {//bounce off impact surf and turn away vec3_t pushDir={0}, turnAwayAngles, turnDelta; float turnStrength, pitchTurnStrength, yawTurnStrength; vec3_t moveDir; float bounceDot, turnDivider; //bounce if ( !turnHitEnt ) {//hit wall VectorScale(bounceDir, (pm->ps->speed*0.25f/pSelfVeh->m_pVehicleInfo->mass), pushDir); } else {//hit another fighter #ifndef QAGAME VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, bounceDir ); #else if ( hitEnt->client ) { VectorScale( bounceDir, (pm->ps->speed+hitEnt->client->ps.speed)*0.5f, pushDir ); } else { VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, pushDir ); } #endif VectorScale(pushDir, (l/pSelfVeh->m_pVehicleInfo->mass), pushDir); VectorScale(pushDir, 0.1f, pushDir); } VectorNormalize2( pm->ps->velocity, moveDir ); bounceDot = DotProduct( moveDir, bounceDir )*-1; if ( bounceDot < 0.1f ) { bounceDot = 0.1f; } VectorScale( pushDir, bounceDot, pushDir ); VectorAdd(pm->ps->velocity, pushDir, pm->ps->velocity); //turn turnDivider = (pSelfVeh->m_pVehicleInfo->mass/400.0f); if ( turnHitEnt ) {//don't turn as much when hit another ship turnDivider *= 4.0f; } if ( turnDivider < 0.5f ) { turnDivider = 0.5f; } turnStrength = (magnitude/2000.0f); if ( turnStrength < 0.1f ) { turnStrength = 0.1f; } else if ( turnStrength > 2.0f ) { turnStrength = 2.0f; } //get the angles we are going to turn towards vectoangles( bounceDir, turnAwayAngles ); //get the delta from our current angles to those new angles AnglesSubtract( turnAwayAngles, pSelfVeh->m_vOrientation, turnDelta ); //now do pitch if ( !bounceDir[2] ) {//shouldn't be any pitch } else { pitchTurnStrength = turnStrength*turnDelta[PITCH]; if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE; } pSelfVeh->m_vFullAngleVelocity[PITCH] = AngleNormalize180(pSelfVeh->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //now do yaw if ( !bounceDir[0] && !bounceDir[1] ) {//shouldn't be any yaw } else { yawTurnStrength = turnStrength*turnDelta[YAW]; if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = -MAX_IMPACT_TURN_ANGLE; } pSelfVeh->m_vFullAngleVelocity[ROLL] = AngleNormalize180(pSelfVeh->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } #ifdef QAGAME//server-side, turn the guy we hit away from us, too if ( turnHitEnt//make the other guy turn and get pushed && hitEnt->client //must be a valid client && !FighterIsLanded( hitEnt->m_pVehicle, &hitEnt->client->ps )//but not if landed && !(hitEnt->spawnflags&2) )//and not if suspended { l = hitEnt->client->ps.speed; //now bounce *them* away and turn them //flip the bounceDir VectorScale( bounceDir, -1, bounceDir ); //do bounce VectorScale( bounceDir, (pm->ps->speed+l)*0.5f, pushDir ); VectorScale(pushDir, (l*0.5f/hitEnt->m_pVehicle->m_pVehicleInfo->mass), pushDir); VectorNormalize2( hitEnt->client->ps.velocity, moveDir ); bounceDot = DotProduct( moveDir, bounceDir )*-1; if ( bounceDot < 0.1f ) { bounceDot = 0.1f; } VectorScale( pushDir, bounceDot, pushDir ); VectorAdd(hitEnt->client->ps.velocity, pushDir, hitEnt->client->ps.velocity); //turn turnDivider = (hitEnt->m_pVehicle->m_pVehicleInfo->mass/400.0f); if ( turnHitEnt ) {//don't turn as much when hit another ship turnDivider *= 4.0f; } if ( turnDivider < 0.5f ) { turnDivider = 0.5f; } //get the angles we are going to turn towards vectoangles( bounceDir, turnAwayAngles ); //get the delta from our current angles to those new angles AnglesSubtract( turnAwayAngles, hitEnt->m_pVehicle->m_vOrientation, turnDelta ); //now do pitch if ( !bounceDir[2] ) {//shouldn't be any pitch } else { pitchTurnStrength = turnStrength*turnDelta[PITCH]; if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE; } hitEnt->m_pVehicle->m_vFullAngleVelocity[PITCH] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //now do yaw if ( !bounceDir[0] && !bounceDir[1] ) {//shouldn't be any yaw } else { yawTurnStrength = turnStrength*turnDelta[YAW]; if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = -MAX_IMPACT_TURN_ANGLE; } hitEnt->m_pVehicle->m_vFullAngleVelocity[ROLL] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //NOTE: will these angle changes stick or will they be stomped // when the vehicle goes through its own update and re-grabs // its angles from its pilot...? Should we do a // SetClientViewAngles on the pilot? } #endif } } #ifdef QAGAME if (!hitEnt) { return; } AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp ); if ( pSelfVeh->m_pVehicleInfo->iImpactFX ) { //tempent use bad! G_AddEvent((gentity_t *)pEnt, EV_PLAY_EFFECT_ID, pSelfVeh->m_pVehicleInfo->iImpactFX); } pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200; magnitude /= pSelfVeh->m_pVehicleInfo->toughness * 50.0f; if (hitEnt && (hitEnt->s.eType != ET_TERRAIN || !(hitEnt->spawnflags & 1) || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER)) { //don't damage the vehicle from terrain that doesn't want to damage vehicles if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //increase the damage... float mult = (pSelfVeh->m_vOrientation[PITCH]*0.1f); if (mult < 1.0f) { mult = 1.0f; } if (hitEnt->inuse && hitEnt->takedamage) { //if the other guy takes damage, don't hurt us a lot for ramming him //unless it's a vehicle, then we get 1.5 times damage if (hitEnt->s.eType == ET_NPC && hitEnt->s.NPC_class == CLASS_VEHICLE && hitEnt->m_pVehicle) { mult = 1.5f; } else { mult = 0.5f; } } magnitude *= mult; } pSelfVeh->m_iLastImpactDmg = magnitude; //FIXME: what about proper death credit to the guy who shot you down? //FIXME: actually damage part of the ship that impacted? G_Damage( (gentity_t *)pEnt, NULL, NULL, NULL, pm->ps->origin, magnitude*5, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT if (pSelfVeh->m_pVehicleInfo->surfDestruction) { G_FlyVehicleSurfaceDestruction((gentity_t *)pEnt, trace, magnitude, forceSurfDestruction ); } pSelfVeh->m_ulFlags |= VEH_CRASHING; } if (hitEnt && hitEnt->inuse && hitEnt->takedamage) { //damage this guy because we hit him float pmult = 1.0f; int finalD; gentity_t *attackEnt; if ( (hitEnt->s.eType == ET_PLAYER && hitEnt->s.number < MAX_CLIENTS) || (hitEnt->s.eType == ET_NPC && hitEnt->s.NPC_class != CLASS_VEHICLE) ) { //probably a humanoid, or something if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //player die good.. if me fighter pmult = 2000.0f; } else { pmult = 40.0f; } if (hitEnt->client && BG_KnockDownable(&hitEnt->client->ps) && G_CanBeEnemy((gentity_t *)pEnt, hitEnt)) { //smash! if (hitEnt->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN) { hitEnt->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN; hitEnt->client->ps.forceHandExtendTime = pm->cmd.serverTime + 1100; hitEnt->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim } hitEnt->client->ps.otherKiller = pEnt->s.number; hitEnt->client->ps.otherKillerTime = pm->cmd.serverTime + 5000; hitEnt->client->ps.otherKillerDebounceTime = pm->cmd.serverTime + 100; //add my velocity into his to force him along in the correct direction from impact VectorAdd(hitEnt->client->ps.velocity, pm->ps->velocity, hitEnt->client->ps.velocity); //upward thrust hitEnt->client->ps.velocity[2] += 200.0f; } } if (pSelfVeh->m_pPilot) { attackEnt = (gentity_t *)pSelfVeh->m_pPilot; } else { attackEnt = (gentity_t *)pEnt; } finalD = magnitude*pmult; if (finalD < 1) { finalD = 1; } G_Damage( hitEnt, attackEnt, attackEnt, NULL, pm->ps->origin, finalD, 0, MOD_MELEE );//FIXME: MOD_IMPACT } #else //this is gonna result in "double effects" for the client doing the prediction. //it doesn't look bad though. could just use predicted events, but I'm too lazy. hitEnt = PM_BGEntForNum(trace->entityNum); if (!hitEnt || hitEnt->s.owner != pEnt->s.number) { //don't hit your own missiles! AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp ); pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200; trap_FX_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, vehUp, -1, -1 ); pSelfVeh->m_ulFlags |= VEH_CRASHING; } #endif } } }
/* ============== R_CalcBone ============== */ void R_CalcBone(mdsHeader_t *header, const refEntity_t *refent, int boneNum) { thisBoneInfo = &boneInfo[boneNum]; if (thisBoneInfo->torsoWeight) { cTBonePtr = &cBoneListTorso[boneNum]; isTorso = qtrue; if (thisBoneInfo->torsoWeight == 1.0f) { fullTorso = qtrue; } } else { isTorso = qfalse; fullTorso = qfalse; } cBonePtr = &cBoneList[boneNum]; bonePtr = &bones[boneNum]; // we can assume the parent has already been uncompressed for this frame + lerp if (thisBoneInfo->parent >= 0) { parentBone = &bones[thisBoneInfo->parent]; parentBoneInfo = &boneInfo[thisBoneInfo->parent]; } else { parentBone = NULL; parentBoneInfo = NULL; } #ifdef HIGH_PRECISION_BONES // rotation if (fullTorso) { VectorCopy(cTBonePtr->angles, angles); } else { VectorCopy(cBonePtr->angles, angles); if (isTorso) { VectorCopy(cTBonePtr->angles, tangles); // blend the angles together for (j = 0; j < 3; j++) { diff = tangles[j] - angles[j]; if (Q_fabs(diff) > 180) { diff = AngleNormalize180(diff); } angles[j] = angles[j] + thisBoneInfo->torsoWeight * diff; } } } #else // rotation if (fullTorso) { sh = (short *)cTBonePtr->angles; pf = angles; ANGLES_SHORT_TO_FLOAT(pf, sh); } else { sh = (short *)cBonePtr->angles; pf = angles; ANGLES_SHORT_TO_FLOAT(pf, sh); if (isTorso) { int j; sh = (short *)cTBonePtr->angles; pf = tangles; ANGLES_SHORT_TO_FLOAT(pf, sh); // blend the angles together for (j = 0; j < 3; j++) { diff = tangles[j] - angles[j]; if (Q_fabs(diff) > 180) { diff = AngleNormalize180(diff); } angles[j] = angles[j] + thisBoneInfo->torsoWeight * diff; } } } #endif AnglesToAxis(angles, bonePtr->matrix); // translation if (parentBone) { #ifdef HIGH_PRECISION_BONES if (fullTorso) { angles[0] = cTBonePtr->ofsAngles[0]; angles[1] = cTBonePtr->ofsAngles[1]; angles[2] = 0; LocalAngleVector(angles, vec); LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } else { angles[0] = cBonePtr->ofsAngles[0]; angles[1] = cBonePtr->ofsAngles[1]; angles[2] = 0; LocalAngleVector(angles, vec); if (isTorso) { tangles[0] = cTBonePtr->ofsAngles[0]; tangles[1] = cTBonePtr->ofsAngles[1]; tangles[2] = 0; LocalAngleVector(tangles, v2); // blend the angles together SLerp_Normal(vec, v2, thisBoneInfo->torsoWeight, vec); LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } else // legs bone { LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } } #else if (fullTorso) { sh = (short *)cTBonePtr->ofsAngles; pf = angles; *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = 0; LocalAngleVector(angles, vec); LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } else { sh = (short *)cBonePtr->ofsAngles; pf = angles; *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = 0; LocalAngleVector(angles, vec); if (isTorso) { sh = (short *)cTBonePtr->ofsAngles; pf = tangles; *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = 0; LocalAngleVector(tangles, v2); // blend the angles together SLerp_Normal(vec, v2, thisBoneInfo->torsoWeight, vec); LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } else // legs bone { LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation); } } #endif } else // just use the frame position { bonePtr->translation[0] = frame->parentOffset[0]; bonePtr->translation[1] = frame->parentOffset[1]; bonePtr->translation[2] = frame->parentOffset[2]; } if (boneNum == header->torsoParent) // this is the torsoParent { VectorCopy(bonePtr->translation, torsoParentOffset); } validBones[boneNum] = 1; rawBones[boneNum] = *bonePtr; newBones[boneNum] = 1; }
/* ============== R_CalcBoneLerp ============== */ void R_CalcBoneLerp(mdsHeader_t *header, const refEntity_t *refent, int boneNum) { if (!refent || !header || boneNum < 0 || boneNum >= MDS_MAX_BONES) { return; } thisBoneInfo = &boneInfo[boneNum]; if (!thisBoneInfo) { return; } if (thisBoneInfo->parent >= 0) { parentBone = &bones[thisBoneInfo->parent]; parentBoneInfo = &boneInfo[thisBoneInfo->parent]; } else { parentBone = NULL; parentBoneInfo = NULL; } if (thisBoneInfo->torsoWeight) { cTBonePtr = &cBoneListTorso[boneNum]; cOldTBonePtr = &cOldBoneListTorso[boneNum]; isTorso = qtrue; if (thisBoneInfo->torsoWeight == 1.0f) { fullTorso = qtrue; } } else { isTorso = qfalse; fullTorso = qfalse; } cBonePtr = &cBoneList[boneNum]; cOldBonePtr = &cOldBoneList[boneNum]; bonePtr = &bones[boneNum]; newBones[boneNum] = 1; // rotation (take into account 170 to -170 lerps, which need to take the shortest route) if (fullTorso) { sh = (short *)cTBonePtr->angles; sh2 = (short *)cOldTBonePtr->angles; pf = angles; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; } else { sh = (short *)cBonePtr->angles; sh2 = (short *)cOldBonePtr->angles; pf = angles; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - backlerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - backlerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - backlerp * diff; if (isTorso) { int j; sh = (short *)cTBonePtr->angles; sh2 = (short *)cOldTBonePtr->angles; pf = tangles; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; a1 = SHORT2ANGLE(*(sh++)); a2 = SHORT2ANGLE(*(sh2++)); diff = AngleNormalize180(a1 - a2); *(pf++) = a1 - torsoBacklerp * diff; // blend the angles together for (j = 0; j < 3; j++) { diff = tangles[j] - angles[j]; if (Q_fabs(diff) > 180) { diff = AngleNormalize180(diff); } angles[j] = angles[j] + thisBoneInfo->torsoWeight * diff; } } } AnglesToAxis(angles, bonePtr->matrix); if (parentBone) { if (fullTorso) { sh = (short *)cTBonePtr->ofsAngles; sh2 = (short *)cOldTBonePtr->ofsAngles; } else { sh = (short *)cBonePtr->ofsAngles; sh2 = (short *)cOldBonePtr->ofsAngles; } pf = angles; *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = 0; LocalAngleVector(angles, v2); // new pf = angles; *(pf++) = SHORT2ANGLE(*(sh2++)); *(pf++) = SHORT2ANGLE(*(sh2++)); *(pf++) = 0; LocalAngleVector(angles, vec); // old // blend the angles together if (fullTorso) { SLerp_Normal(vec, v2, torsoFrontlerp, dir); } else { SLerp_Normal(vec, v2, frontlerp, dir); } // translation if (!fullTorso && isTorso) // partial legs/torso, need to lerp according to torsoWeight { // calc the torso frame sh = (short *)cTBonePtr->ofsAngles; sh2 = (short *)cOldTBonePtr->ofsAngles; pf = angles; *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = SHORT2ANGLE(*(sh++)); *(pf++) = 0; LocalAngleVector(angles, v2); // new pf = angles; *(pf++) = SHORT2ANGLE(*(sh2++)); *(pf++) = SHORT2ANGLE(*(sh2++)); *(pf++) = 0; LocalAngleVector(angles, vec); // old // blend the angles together SLerp_Normal(vec, v2, torsoFrontlerp, v2); // blend the torso/legs together SLerp_Normal(dir, v2, thisBoneInfo->torsoWeight, dir); } LocalVectorMA(parentBone->translation, thisBoneInfo->parentDist, dir, bonePtr->translation); } else // just interpolate the frame positions { bonePtr->translation[0] = frontlerp * frame->parentOffset[0] + backlerp * oldFrame->parentOffset[0]; bonePtr->translation[1] = frontlerp * frame->parentOffset[1] + backlerp * oldFrame->parentOffset[1]; bonePtr->translation[2] = frontlerp * frame->parentOffset[2] + backlerp * oldFrame->parentOffset[2]; } if (boneNum == header->torsoParent) // this is the torsoParent { VectorCopy(bonePtr->translation, torsoParentOffset); } validBones[boneNum] = 1; rawBones[boneNum] = *bonePtr; newBones[boneNum] = 1; }
float AngleDelta(float angle1, float angle2) { return AngleNormalize180(angle1 - angle2); }
/* ======================================================================================================================================= AIFunc_Helga_Melee ======================================================================================================================================= */ char *AIFunc_Helga_Melee(cast_state_t *cs) { gentity_t *ent = &g_entities[cs->entityNum]; gentity_t *enemy; cast_state_t *ecs; int hitDelay = -1, anim; trace_t tr; float enemyDist; aicast_predictmove_t move; vec3_t vec; cs->aiFlags |= AIFL_SPECIAL_FUNC; if (!ent->client->ps.torsoTimer || !ent->client->ps.legsTimer) { cs->aiFlags &= ~AIFL_SPECIAL_FUNC; return AIFunc_DefaultStart(cs); } if (cs->enemyNum < 0) { ent->client->ps.legsTimer = 0; // allow legs us to move ent->client->ps.torsoTimer = 0; // allow legs us to move cs->aiFlags &= ~AIFL_SPECIAL_FUNC; return AIFunc_DefaultStart(cs); } ecs = AICast_GetCastState(cs->enemyNum); enemy = &g_entities[cs->enemyNum]; anim = (ent->client->ps.torsoAnim & ~ANIM_TOGGLEBIT) - BG_AnimationIndexForString("attack3", cs->entityNum); if (anim < 0 || anim >= NUM_HELGA_ANIMS) { // animation interupted cs->aiFlags &= ~AIFL_SPECIAL_FUNC; return AIFunc_DefaultStart(cs); // G_Error("AIFunc_HelgaZombieMelee: helgaBoss using invalid or unknown attack anim"); } if (cs->animHitCount < MAX_HELGA_IMPACTS && helgaHitTimes[anim][cs->animHitCount] >= 0) { // face them VectorCopy(cs->bs->origin, vec); vec[2] += ent->client->ps.viewheight; VectorSubtract(enemy->client->ps.origin, vec, vec); VectorNormalize(vec); vectoangles(vec, cs->ideal_viewangles); cs->ideal_viewangles[PITCH] = AngleNormalize180(cs->ideal_viewangles[PITCH]); // get hitDelay if (!cs->animHitCount) { hitDelay = helgaHitTimes[anim][cs->animHitCount]; } else { hitDelay = helgaHitTimes[anim][cs->animHitCount] - helgaHitTimes[anim][cs->animHitCount - 1]; } // check for inflicting damage if (level.time - cs->weaponFireTimes[cs->weaponNum] > hitDelay) { // do melee damage enemyDist = VectorDistance(enemy->r.currentOrigin, ent->r.currentOrigin); enemyDist -= g_entities[cs->enemyNum].r.maxs[0]; enemyDist -= ent->r.maxs[0]; if (enemyDist < 10 + AICast_WeaponRange(cs, cs->weaponNum)) { trap_Trace(&tr, ent->r.currentOrigin, NULL, NULL, enemy->r.currentOrigin, ent->s.number, MASK_SHOT); if (tr.entityNum == cs->enemyNum) { G_Damage(&g_entities[tr.entityNum], ent, ent, vec3_origin, tr.endpos, helgaHitDamage[anim], 0, MOD_GAUNTLET); G_AddEvent(enemy, EV_GENERAL_SOUND, G_SoundIndex(aiDefaults[ent->aiCharacter].soundScripts[STAYSOUNDSCRIPT])); } } cs->weaponFireTimes[cs->weaponNum] = level.time; cs->animHitCount++; } } // if they are outside range, move forward AICast_PredictMovement(ecs, 2, 0.3, &move, &g_entities[cs->enemyNum].client->pers.cmd, -1); VectorSubtract(move.endpos, cs->bs->origin, vec); vec[2] = 0; enemyDist = VectorLength(vec); enemyDist -= g_entities[cs->enemyNum].r.maxs[0]; enemyDist -= ent->r.maxs[0]; if (enemyDist > 8) { // we can get closer // if (!ent->client->ps.legsTimer) { // cs->castScriptStatus.scriptNoMoveTime = 0; trap_EA_MoveForward(cs->entityNum); //} // ent->client->ps.legsTimer = 0; // allow legs us to move } return NULL; }
/* =============== CG_OffsetThirdPersonView =============== */ void CG_OffsetThirdPersonView( void ) { int i; vec3_t forward, right, up; vec3_t view; trace_t trace; static vec3_t mins = { -8, -8, -8 }; static vec3_t maxs = { 8, 8, 8 }; vec3_t focusPoint; vec3_t surfNormal; int cmdNum; usercmd_t cmd, oldCmd; float range; vec3_t mouseInputAngles; vec3_t rotationAngles; vec3_t axis[ 3 ], rotaxis[ 3 ]; float deltaPitch; static float pitch; static vec3_t killerPos = { 0, 0, 0 }; // If cg_thirdpersonShoulderViewMode == 2, do shoulder view instead // If cg_thirdpersonShoulderViewMode == 1, do shoulder view when chasing // a wallwalker because it's really erratic to watch if ( cg_thirdPersonShoulderViewMode.integer == 2 ) { CG_OffsetShoulderView(); return; } BG_GetClientNormal( &cg.predictedPlayerState, surfNormal ); // Set the view origin to the class's view height VectorMA( cg.refdef.vieworg, cg.predictedPlayerState.viewheight, surfNormal, cg.refdef.vieworg ); // Set the focus point where the camera will look (at the player's vieworg) VectorCopy( cg.refdef.vieworg, focusPoint ); // If player is dead, we want the player to be between us and the killer // so pretend that the player was looking at the killer, then place cam behind them. if ( cg.predictedPlayerState.stats[ STAT_HEALTH ] <= 0 ) { int killerEntNum = cg.predictedPlayerState.stats[ STAT_VIEWLOCK ]; // already looking at ourself if ( killerEntNum != cg.snap->ps.clientNum ) { vec3_t lookDirection; if ( cg.wasDeadLastFrame == qfalse || !cg_staticDeathCam.integer ) { VectorCopy( cg_entities[ killerEntNum ].lerpOrigin, killerPos ); cg.wasDeadLastFrame = qtrue; } VectorSubtract( killerPos, cg.refdef.vieworg, lookDirection ); vectoangles( lookDirection, cg.refdefViewAngles ); } } // get cg_thirdPersonRange range = cg_thirdPersonRange.value; // Calculate the angle of the camera's position around the player. // Unless in demo, PLAYING in third person, or in dead-third-person cam, allow the player // to control camera position offsets using the mouse position. if ( cg.demoPlayback || ( ( cg.snap->ps.pm_flags & PMF_FOLLOW ) && ( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) ) ) { // Collect our input values from the mouse. cmdNum = trap_GetCurrentCmdNumber(); trap_GetUserCmd( cmdNum, &cmd ); trap_GetUserCmd( cmdNum - 1, &oldCmd ); // Prevent pitch from wrapping and clamp it within a [-75, 90] range. // Cgame has no access to ps.delta_angles[] here, so we need to reproduce // it ourselves. deltaPitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] ); if ( fabs( deltaPitch ) < 200.0f ) { pitch += deltaPitch; } mouseInputAngles[ PITCH ] = pitch; mouseInputAngles[ YAW ] = -1.0f * SHORT2ANGLE( cmd.angles[ YAW ] ); // yaw is inverted mouseInputAngles[ ROLL ] = 0.0f; for ( i = 0; i < 3; i++ ) { mouseInputAngles[ i ] = AngleNormalize180( mouseInputAngles[ i ] ); } // Set the rotation angles to be the view angles offset by the mouse input // Ignore the original pitch though; it's too jerky otherwise if ( !cg_thirdPersonPitchFollow.integer ) { cg.refdefViewAngles[ PITCH ] = 0.0f; } for ( i = 0; i < 3; i++ ) { rotationAngles[ i ] = AngleNormalize180( cg.refdefViewAngles[ i ] ) + mouseInputAngles[ i ]; AngleNormalize180( rotationAngles[ i ] ); } // Don't let pitch go too high/too low or the camera flips around and // that's really annoying. // However, when we're not on the floor or ceiling (wallwalk) pitch // may not be pitch, so just let it go. if ( surfNormal[ 2 ] > 0.5f || surfNormal[ 2 ] < -0.5f ) { if ( rotationAngles[ PITCH ] > 85.0f ) { rotationAngles[ PITCH ] = 85.0f; } else if ( rotationAngles[ PITCH ] < -85.0f ) { rotationAngles[ PITCH ] = -85.0f; } } // Perform the rotations specified by rotationAngles. AnglesToAxis( rotationAngles, axis ); if ( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) || !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse, cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) ) { AxisCopy( axis, rotaxis ); } // Convert the new axis back to angles. AxisToAngles( rotaxis, rotationAngles ); } else { if ( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) { // If we're playing the game in third person, the viewangles already // take care of our mouselook, so just use them. for ( i = 0; i < 3; i++ ) { rotationAngles[ i ] = cg.refdefViewAngles[ i ]; } } else // dead { rotationAngles[ PITCH ] = 20.0f; rotationAngles[ YAW ] = cg.refdefViewAngles[ YAW ]; } } rotationAngles[ YAW ] -= cg_thirdPersonAngle.value; // Move the camera range distance back. AngleVectors( rotationAngles, forward, right, up ); VectorCopy( cg.refdef.vieworg, view ); VectorMA( view, -range, forward, view ); // Ensure that the current camera position isn't out of bounds and that there // is nothing between the camera and the player. // Trace a ray from the origin to the viewpoint to make sure the view isn't // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); if ( trace.fraction != 1.0f ) { VectorCopy( trace.endpos, view ); view[ 2 ] += ( 1.0f - trace.fraction ) * 32; // Try another trace to this position, because a tunnel may have the ceiling // close enogh that this is poking out. CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID ); VectorCopy( trace.endpos, view ); } // Set the camera position to what we calculated. VectorCopy( view, cg.refdef.vieworg ); // The above checks may have moved the camera such that the existing viewangles // may not still face the player. Recalculate them to do so. // but if we're dead, don't bother because we'd rather see what killed us if ( cg.predictedPlayerState.stats[ STAT_HEALTH ] > 0 ) { VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint ); vectoangles( focusPoint, cg.refdefViewAngles ); } }
/* =============== CG_OffsetShoulderView =============== */ void CG_OffsetShoulderView( void ) { int i; int cmdNum; usercmd_t cmd, oldCmd; vec3_t rotationAngles; vec3_t axis[ 3 ], rotaxis[ 3 ]; float deltaMousePitch; static float mousePitch; vec3_t forward, right, up; classModelConfig_t *classModelConfig; // Ignore following pitch; it's too jerky otherwise. if ( !cg_thirdPersonPitchFollow.integer ) { cg.refdefViewAngles[ PITCH ] = 0.0f; } AngleVectors( cg.refdefViewAngles, forward, right, up ); classModelConfig = BG_ClassModelConfig( cg.snap->ps.stats[ STAT_CLASS ] ); VectorMA( cg.refdef.vieworg, classModelConfig->shoulderOffsets[ 0 ], forward, cg.refdef.vieworg ); VectorMA( cg.refdef.vieworg, classModelConfig->shoulderOffsets[ 1 ], right, cg.refdef.vieworg ); VectorMA( cg.refdef.vieworg, classModelConfig->shoulderOffsets[ 2 ], up, cg.refdef.vieworg ); // If someone is playing like this, the rest is already taken care of // so just get the firstperson effects and leave. if ( !cg.demoPlayback && !( cg.snap->ps.pm_flags & PMF_FOLLOW ) ) { CG_OffsetFirstPersonView(); return; } // Get mouse input for camera rotation. cmdNum = trap_GetCurrentCmdNumber(); trap_GetUserCmd( cmdNum, &cmd ); trap_GetUserCmd( cmdNum - 1, &oldCmd ); // Prevent pitch from wrapping and clamp it within a [30, -50] range. // Cgame has no access to ps.delta_angles[] here, so we need to reproduce // it ourselves here. deltaMousePitch = SHORT2ANGLE( cmd.angles[ PITCH ] - oldCmd.angles[ PITCH ] ); if ( fabs( deltaMousePitch ) < 200.0f ) { mousePitch += deltaMousePitch; } // Handle pitch. rotationAngles[ PITCH ] = mousePitch; rotationAngles[ PITCH ] = AngleNormalize180( rotationAngles[ PITCH ] + AngleNormalize180( cg.refdefViewAngles[ PITCH ] ) ); if ( rotationAngles [ PITCH ] < -90.0f ) { rotationAngles [ PITCH ] = -90.0f; } if ( rotationAngles [ PITCH ] > 90.0f ) { rotationAngles [ PITCH ] = 90.0f; } // Yaw and Roll are much easier. rotationAngles[ YAW ] = SHORT2ANGLE( cmd.angles[ YAW ] ) + cg.refdefViewAngles[ YAW ]; rotationAngles[ ROLL ] = 0.0f; // Perform the rotations. AnglesToAxis( rotationAngles, axis ); if ( !( cg.snap->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) || !BG_RotateAxis( cg.snap->ps.grapplePoint, axis, rotaxis, qfalse, cg.snap->ps.eFlags & EF_WALLCLIMBCEILING ) ) { AxisCopy( axis, rotaxis ); } AxisToAngles( rotaxis, rotationAngles ); // Actually set the viewangles. for ( i = 0; i < 3; i++ ) { cg.refdefViewAngles[ i ] = rotationAngles[ i ]; } // Now run the first person stuff so we get various effects added. CG_OffsetFirstPersonView(); }
void GunRackAddItem( gitem_t *gun, vec3_t org, vec3_t angs, float ffwd, float fright, float fup ) { vec3_t fwd, right; gentity_t *it_ent = G_Spawn(); qboolean rotate = qtrue; AngleVectors( angs, fwd, right, NULL ); if ( it_ent && gun ) { // FIXME: scaling the ammo will probably need to be tweaked to a reasonable amount...adjust as needed // Set base ammo per type if ( gun->giType == IT_WEAPON ) { it_ent->spawnflags |= 16;// VERTICAL switch( gun->giTag ) { case WP_BLASTER: it_ent->count = 15; break; case WP_REPEATER: it_ent->count = 100; break; case WP_ROCKET_LAUNCHER: it_ent->count = 4; break; } } else { rotate = qfalse; // must deliberately make it small, or else the objects will spawn inside of each other. VectorSet( it_ent->maxs, 6.75f, 6.75f, 6.75f ); VectorScale( it_ent->maxs, -1, it_ent->mins ); } it_ent->spawnflags |= 1;// ITMSF_SUSPEND it_ent->classname = G_NewString(gun->classname); //copy it so it can be freed safely G_SpawnItem( it_ent, gun ); // FinishSpawningItem handles everything, so clear the thinkFunc that was set in G_SpawnItem FinishSpawningItem( it_ent ); if ( gun->giType == IT_AMMO ) { if ( gun->giTag == AMMO_BLASTER ) // I guess this just has to use different logic?? { if ( g_spskill->integer >= 2 ) { it_ent->count += 10; // give more on higher difficulty because there will be more/harder enemies? } } else { // scale ammo based on skill switch ( g_spskill->integer ) { case 0: // do default break; case 1: it_ent->count *= 0.75f; break; case 2: it_ent->count *= 0.5f; break; } } } it_ent->nextthink = 0; VectorCopy( org, it_ent->s.origin ); VectorMA( it_ent->s.origin, fright, right, it_ent->s.origin ); VectorMA( it_ent->s.origin, ffwd, fwd, it_ent->s.origin ); it_ent->s.origin[2] += fup; VectorCopy( angs, it_ent->s.angles ); // by doing this, we can force the amount of ammo we desire onto the weapon for when it gets picked-up it_ent->flags |= ( FL_DROPPED_ITEM | FL_FORCE_PULLABLE_ONLY ); it_ent->physicsBounce = 0.1f; for ( int t = 0; t < 3; t++ ) { if ( rotate ) { if ( t == YAW ) { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 180 + Q_flrand(-1.0f, 1.0f) * 14 ); } else { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + Q_flrand(-1.0f, 1.0f) * 4 ); } } else { if ( t == YAW ) { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 90 + Q_flrand(-1.0f, 1.0f) * 4 ); } } } G_SetAngles( it_ent, it_ent->s.angles ); G_SetOrigin( it_ent, it_ent->s.origin ); gi.linkentity( it_ent ); } }
//MP RULE - ALL PROCESSORIENTCOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!! //If you really need to violate this rule for SP, then use ifdefs. //By BG-compatible, I mean no use of game-specific data - ONLY use //stuff available in the MP bgEntity (in SP, the bgEntity is #defined //as a gentity, but the MP-compatible access restrictions are based //on the bgEntity structure in the MP codebase) -rww // ProcessOrientCommands the Vehicle. static void ProcessOrientCommands( Vehicle_t *pVeh ) { /********************************************************************************/ /* BEGIN Here is where make sure the vehicle is properly oriented. BEGIN */ /********************************************************************************/ bgEntity_t *parent = pVeh->m_pParentEntity; playerState_t *parentPS, *riderPS; #ifdef _JK2MP bgEntity_t *rider = NULL; if (parent->s.owner != ENTITYNUM_NONE) { rider = PM_BGEntForNum(parent->s.owner); //&g_entities[parent->r.ownerNum]; } #else gentity_t *rider = parent->owner; #endif // Bucking so we can't do anything. #ifndef _JK2MP //bad for prediction - fixme if ( pVeh->m_ulFlags & VEH_BUCKING || pVeh->m_ulFlags & VEH_FLYING || pVeh->m_ulFlags & VEH_CRASHING ) { return; } #endif #ifdef _JK2MP if ( !rider ) #else if ( !rider || !rider->client ) #endif { rider = parent; } #ifdef _JK2MP parentPS = parent->playerState; riderPS = rider->playerState; #else parentPS = &parent->client->ps; riderPS = &rider->client->ps; #endif if (rider) { #ifdef _JK2MP float angDif = AngleSubtract(pVeh->m_vOrientation[YAW], riderPS->viewangles[YAW]); if (parentPS && parentPS->speed) { float s = parentPS->speed; float maxDif = pVeh->m_pVehicleInfo->turningSpeed*4.0f; //magic number hackery if (s < 0.0f) { s = -s; } angDif *= s/pVeh->m_pVehicleInfo->speedMax; if (angDif > maxDif) { angDif = maxDif; } else if (angDif < -maxDif) { angDif = -maxDif; } pVeh->m_vOrientation[YAW] = AngleNormalize180(pVeh->m_vOrientation[YAW] - angDif*(pVeh->m_fTimeModifier*0.2f)); } #else pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW]; #endif } /* speed = VectorLength( parentPS->velocity ); // If the player is the rider... if ( rider->s.number < MAX_CLIENTS ) {//FIXME: use the vehicle's turning stat in this calc pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW]; } else { float turnSpeed = pVeh->m_pVehicleInfo->turningSpeed; if ( !pVeh->m_pVehicleInfo->turnWhenStopped && !parentPS->speed )//FIXME: or !pVeh->m_ucmd.forwardmove? {//can't turn when not moving //FIXME: or ramp up to max turnSpeed? turnSpeed = 0.0f; } #ifdef _JK2MP if (rider->s.eType == ET_NPC) #else if ( !rider || rider->NPC ) #endif {//help NPCs out some turnSpeed *= 2.0f; #ifdef _JK2MP if (parentPS->speed > 200.0f) #else if ( parent->client->ps.speed > 200.0f ) #endif { turnSpeed += turnSpeed * parentPS->speed/200.0f*0.05f; } } turnSpeed *= pVeh->m_fTimeModifier; //default control scheme: strafing turns, mouselook aims if ( pVeh->m_ucmd.rightmove < 0 ) { pVeh->m_vOrientation[YAW] += turnSpeed; } else if ( pVeh->m_ucmd.rightmove > 0 ) { pVeh->m_vOrientation[YAW] -= turnSpeed; } if ( pVeh->m_pVehicleInfo->malfunctionArmorLevel && pVeh->m_iArmor <= pVeh->m_pVehicleInfo->malfunctionArmorLevel ) {//damaged badly } }*/ /********************************************************************************/ /* END Here is where make sure the vehicle is properly oriented. END */ /********************************************************************************/ }
//----------------------------------------------------- qboolean VEH_TurretAim( Vehicle_t *pVeh, gentity_t *parent, gentity_t *turretEnemy, turretStats_t *turretStats, vehWeaponInfo_t *vehWeapon, int turretNum, int curMuzzle, vec3_t desiredAngles ) //----------------------------------------------------- { vec3_t curAngles, addAngles, newAngles, yawAngles, pitchAngles; float aimCorrect = qfalse; WP_CalcVehMuzzle( parent, curMuzzle ); //get the current absolute angles of the turret right now vectoangles( pVeh->m_vMuzzleDir[curMuzzle], curAngles ); //subtract out the vehicle's angles to get the relative alignment AnglesSubtract( curAngles, pVeh->m_vOrientation, curAngles ); if ( turretEnemy ) { aimCorrect = qtrue; // ...then we'll calculate what new aim adjustments we should attempt to make this frame // Aim at enemy VEH_TurretAnglesToEnemy( pVeh, curMuzzle, vehWeapon->fSpeed, turretEnemy, turretStats->bAILead, desiredAngles ); } //subtract out the vehicle's angles to get the relative desired alignment AnglesSubtract( desiredAngles, pVeh->m_vOrientation, desiredAngles ); //Now clamp the desired relative angles //clamp yaw desiredAngles[YAW] = AngleNormalize180( desiredAngles[YAW] ); if ( pVeh->m_pVehicleInfo->turret[turretNum].yawClampLeft && desiredAngles[YAW] > pVeh->m_pVehicleInfo->turret[turretNum].yawClampLeft ) { aimCorrect = qfalse; desiredAngles[YAW] = pVeh->m_pVehicleInfo->turret[turretNum].yawClampLeft; } if ( pVeh->m_pVehicleInfo->turret[turretNum].yawClampRight && desiredAngles[YAW] < pVeh->m_pVehicleInfo->turret[turretNum].yawClampRight ) { aimCorrect = qfalse; desiredAngles[YAW] = pVeh->m_pVehicleInfo->turret[turretNum].yawClampRight; } //clamp pitch desiredAngles[PITCH] = AngleNormalize180( desiredAngles[PITCH] ); if ( pVeh->m_pVehicleInfo->turret[turretNum].pitchClampDown && desiredAngles[PITCH] > pVeh->m_pVehicleInfo->turret[turretNum].pitchClampDown ) { aimCorrect = qfalse; desiredAngles[PITCH] = pVeh->m_pVehicleInfo->turret[turretNum].pitchClampDown; } if ( pVeh->m_pVehicleInfo->turret[turretNum].pitchClampUp && desiredAngles[PITCH] < pVeh->m_pVehicleInfo->turret[turretNum].pitchClampUp ) { aimCorrect = qfalse; desiredAngles[PITCH] = pVeh->m_pVehicleInfo->turret[turretNum].pitchClampUp; } //Now get the offset we want from our current relative angles AnglesSubtract( desiredAngles, curAngles, addAngles ); //Now cap the addAngles for our fTurnSpeed if ( addAngles[PITCH] > turretStats->fTurnSpeed ) { //aimCorrect = qfalse;//??? addAngles[PITCH] = turretStats->fTurnSpeed; } else if ( addAngles[PITCH] < -turretStats->fTurnSpeed ) { //aimCorrect = qfalse;//??? addAngles[PITCH] = -turretStats->fTurnSpeed; } if ( addAngles[YAW] > turretStats->fTurnSpeed ) { //aimCorrect = qfalse;//??? addAngles[YAW] = turretStats->fTurnSpeed; } else if ( addAngles[YAW] < -turretStats->fTurnSpeed ) { //aimCorrect = qfalse;//??? addAngles[YAW] = -turretStats->fTurnSpeed; } //Now add the additional angles back in to our current relative angles //FIXME: add some AI aim error randomness...? newAngles[PITCH] = AngleNormalize180( curAngles[PITCH]+addAngles[PITCH] ); newAngles[YAW] = AngleNormalize180( curAngles[YAW]+addAngles[YAW] ); //Now set the bone angles to the new angles //set yaw if ( turretStats->yawBone ) { VectorClear( yawAngles ); yawAngles[turretStats->yawAxis] = newAngles[YAW]; NPC_SetBoneAngles( parent, turretStats->yawBone, yawAngles ); } //set pitch if ( turretStats->pitchBone ) { VectorClear( pitchAngles ); pitchAngles[turretStats->pitchAxis] = newAngles[PITCH]; NPC_SetBoneAngles( parent, turretStats->pitchBone, pitchAngles ); } //force muzzle to recalc next check pVeh->m_iMuzzleTime[curMuzzle] = 0; return aimCorrect; }
void CGCam_FollowUpdate ( void ) { vec3_t center, dir, cameraAngles, vec, focus[MAX_CAMERA_GROUP_SUBJECTS];//No more than 16 subjects in a cameraGroup gentity_t *from = NULL; centity_t *fromCent = NULL; int num_subjects = 0, i; qboolean focused = qfalse; if ( client_camera.cameraGroup && client_camera.cameraGroup[0] ) { //Stay centered in my cameraGroup, if I have one while( NULL != (from = G_Find(from, FOFS(cameraGroup), client_camera.cameraGroup))) { /* if ( from->s.number == client_camera.aimEntNum ) {//This is the misc_camera_focus, we'll be removing this ent altogether eventually continue; } */ if ( num_subjects >= MAX_CAMERA_GROUP_SUBJECTS ) { gi.Printf(S_COLOR_RED"ERROR: Too many subjects in shot composition %s", client_camera.cameraGroup); break; } fromCent = &cg_entities[from->s.number]; if ( !fromCent ) { continue; } focused = qfalse; if ( from->client && client_camera.cameraGroupTag && client_camera.cameraGroupTag[0] && fromCent->gent->ghoul2.size() ) { int newBolt = gi.G2API_AddBolt( &fromCent->gent->ghoul2[from->playerModel], client_camera.cameraGroupTag ); if ( newBolt != -1 ) { mdxaBone_t boltMatrix; vec3_t fromAngles = {0,from->client->ps.legsYaw,0}; gi.G2API_GetBoltMatrix( fromCent->gent->ghoul2, from->playerModel, newBolt, &boltMatrix, fromAngles, fromCent->lerpOrigin, cg.time, cgs.model_draw, fromCent->currentState.modelScale ); gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, focus[num_subjects] ); focused = qtrue; } } if ( !focused ) { if ( from->s.pos.trType != TR_STATIONARY ) // if ( from->s.pos.trType == TR_INTERPOLATE ) {//use interpolated origin? if ( !VectorCompare( vec3_origin, fromCent->lerpOrigin ) ) {//hunh? Somehow we've never seen this gentity on the client, so there is no lerpOrigin, so cheat over to the game and use the currentOrigin VectorCopy( from->currentOrigin, focus[num_subjects] ); } else { VectorCopy( fromCent->lerpOrigin, focus[num_subjects] ); } } else { VectorCopy(from->currentOrigin, focus[num_subjects]); } //FIXME: make a list here of their s.numbers instead so we can do other stuff with the list below if ( from->client ) {//Track to their eyes - FIXME: maybe go off a tag? //FIXME: //Based on FOV and distance to subject from camera, pick the point that //keeps eyes 3/4 up from bottom of screen... what about bars? focus[num_subjects][2] += from->client->ps.viewheight; } } if ( client_camera.cameraGroupZOfs ) { focus[num_subjects][2] += client_camera.cameraGroupZOfs; } num_subjects++; } if ( !num_subjects ) // Bad cameragroup { #ifndef FINAL_BUILD gi.Printf(S_COLOR_RED"ERROR: Camera Focus unable to locate cameragroup: %s\n", client_camera.cameraGroup); #endif return; } //Now average all points VectorCopy( focus[0], center ); for( i = 1; i < num_subjects; i++ ) { VectorAdd( focus[i], center, center ); } VectorScale( center, 1.0f/((float)num_subjects), center ); } else { return; } //Need to set a speed to keep a distance from //the subject- fixme: only do this if have a distance //set VectorSubtract( client_camera.subjectPos, center, vec ); client_camera.subjectSpeed = VectorLengthSquared( vec ) * 100.0f / cg.frametime; /* if ( !cg_skippingcin.integer ) { Com_Printf( S_COLOR_RED"org: %s\n", vtos(center) ); } */ VectorCopy( center, client_camera.subjectPos ); VectorSubtract( center, cg.refdef.vieworg, dir );//can't use client_camera.origin because it's not updated until the end of the move. //Get desired angle vectoangles(dir, cameraAngles); if ( client_camera.followInitLerp ) {//Lerping float frac = cg.frametime/100.0f * client_camera.followSpeed/100.f; for( i = 0; i < 3; i++ ) { cameraAngles[i] = AngleNormalize180( cameraAngles[i] ); cameraAngles[i] = AngleNormalize180( client_camera.angles[i] + frac * AngleNormalize180(cameraAngles[i] - client_camera.angles[i]) ); cameraAngles[i] = AngleNormalize180( cameraAngles[i] ); } #if 0 Com_Printf( "%s\n", vtos(cameraAngles) ); #endif } else {//Snapping, should do this first time if follow_lerp_to_start_duration is zero //will lerp from this point on client_camera.followInitLerp = qtrue; for( i = 0; i < 3; i++ ) {//normalize so that when we start lerping, it doesn't freak out cameraAngles[i] = AngleNormalize180( cameraAngles[i] ); } //So tracker doesn't move right away thinking the first angle change //is the subject moving... FIXME: shouldn't set this until lerp done OR snapped? client_camera.subjectSpeed = 0; } //Point camera to lerp angles /* if ( !cg_skippingcin.integer ) { Com_Printf( "ang: %s\n", vtos(cameraAngles) ); } */ VectorCopy( cameraAngles, client_camera.angles ); }
/* ============== R_CalcBones The list of bones[] should only be built and modified from within here ============== */ void R_CalcBones( mdsHeader_t *header, const refEntity_t *refent, int *boneList, int numBones, int renderend ) { int i, j; int *boneRefs; float torsoWeight; mdsBoneFrame_t *bones, *bonePtr, *parentBone; mdsFrame_t *frame, *torsoFrame; mdsBoneInfo_t *boneInfo, *thisBoneInfo, *parentBoneInfo; mdsBoneFrameCompressed_t *cBonePtr, *cTBonePtr, *cBoneList, *cBoneListTorso; vec3_t t, torsoAxis[3], tmpAxis[3]; vec3_t torsoParentOffset = {0}; vec4_t m1[4]; vec4_t m2[4] = {{0}, {0}, {0}, {0}}; int frameSize; bones = smpbones[renderend]; frameSize = (int) ( sizeof( mdsFrame_t ) + ( header->numBones - 1 ) * sizeof( mdsBoneFrameCompressed_t ) ); frame = ( mdsFrame_t * )( (byte *)header + header->ofsFrames + refent->frame * frameSize ); torsoFrame = ( mdsFrame_t * )( (byte *)header + header->ofsFrames + refent->torsoFrame * frameSize ); boneInfo = ( mdsBoneInfo_t * )( (byte *)header + header->ofsBones ); boneRefs = boneList; Matrix3Transpose( refent->torsoAxis, torsoAxis ); cBoneList = frame->bones; cBoneListTorso = torsoFrame->bones; for ( i = 0; i < numBones; i++, boneRefs++ ) { // R_CalcBone( header, refent, *boneRefs ); int boneNum; short *sh; float *pf, diff; vec3_t tangles, angles, vec, v2; qboolean isTorso, fullTorso; fullTorso = qfalse; boneNum = *boneRefs; thisBoneInfo = &boneInfo[boneNum]; if ( thisBoneInfo->torsoWeight ) { isTorso = qtrue; if ( thisBoneInfo->torsoWeight == 1.0f ) { fullTorso = qtrue; } } else { isTorso = qfalse; } cTBonePtr = &cBoneListTorso[boneNum]; cBonePtr = &cBoneList[boneNum]; bonePtr = &bones[ boneNum ]; // we can assume the parent has already been uncompressed for this frame + lerp if ( thisBoneInfo->parent >= 0 ) { parentBone = &bones[ thisBoneInfo->parent ]; parentBoneInfo = &boneInfo[ thisBoneInfo->parent ]; } else { parentBone = NULL; parentBoneInfo = NULL; } // rotation if ( fullTorso ) { sh = (short *)cTBonePtr->angles; pf = angles; ANGLES_SHORT_TO_FLOAT( pf, sh ); } else { sh = (short *)cBonePtr->angles; pf = angles; ANGLES_SHORT_TO_FLOAT( pf, sh ); if ( isTorso ) { sh = (short *)cTBonePtr->angles; pf = tangles; ANGLES_SHORT_TO_FLOAT( pf, sh ); // blend the angles together for ( j = 0; j < 3; j++ ) { diff = tangles[j] - angles[j]; if ( fabs( diff ) > 180 ) { diff = AngleNormalize180( diff ); } angles[j] = angles[j] + thisBoneInfo->torsoWeight * diff; } } } AnglesToAxis( angles, bonePtr->matrix ); // translation if ( parentBone ) { if ( fullTorso ) { sh = (short *)cTBonePtr->ofsAngles; pf = angles; *( pf++ ) = SHORT2ANGLE( *( sh++ ) ); *( pf++ ) = SHORT2ANGLE( *( sh++ ) ); *( pf++ ) = 0; LocalAngleVector( angles, vec ); } else { sh = (short *)cBonePtr->ofsAngles; pf = angles; *( pf++ ) = SHORT2ANGLE( *( sh++ ) ); *( pf++ ) = SHORT2ANGLE( *( sh++ ) ); *( pf++ ) = 0; LocalAngleVector( angles, vec ); if ( isTorso ) { sh = (short *)cTBonePtr->ofsAngles; pf = tangles; *( pf++ ) = SHORT2ANGLE( *( sh++ ) ); *( pf++ ) = SHORT2ANGLE( *( sh++ ) ); *( pf++ ) = 0; LocalAngleVector( tangles, v2 ); // blend the angles together SLerp_Normal( vec, v2, thisBoneInfo->torsoWeight, vec ); } } LocalVectorMA( parentBone->translation, thisBoneInfo->parentDist, vec, bonePtr->translation ); } else { // just use the frame position bonePtr->translation[0] = frame->parentOffset[0]; bonePtr->translation[1] = frame->parentOffset[1]; bonePtr->translation[2] = frame->parentOffset[2]; } if ( boneNum == header->torsoParent ) { VectorCopy( bonePtr->translation, torsoParentOffset ); } } // adjust for torso rotations torsoWeight = 0; boneRefs = boneList; for ( i = 0; i < numBones; i++, boneRefs++ ) { thisBoneInfo = &boneInfo[ *boneRefs ]; bonePtr = &bones[ *boneRefs ]; // add torso rotation if ( thisBoneInfo->torsoWeight > 0 ) { if ( !( thisBoneInfo->flags & BONEFLAG_TAG ) ) { // 1st multiply with the bone->matrix // 2nd translation for rotation relative to bone around torso parent offset VectorSubtract( bonePtr->translation, torsoParentOffset, t ); Matrix4FromAxisPlusTranslation( bonePtr->matrix, t, m1 ); // 3rd scaled rotation // 4th translate back to torso parent offset // use previously created matrix if available for the same weight if ( torsoWeight != thisBoneInfo->torsoWeight ) { Matrix4FromScaledAxisPlusTranslation( torsoAxis, thisBoneInfo->torsoWeight, torsoParentOffset, m2 ); torsoWeight = thisBoneInfo->torsoWeight; } // multiply matrices to create one matrix to do all calculations Matrix4MultiplyInto3x3AndTranslation( m2, m1, bonePtr->matrix, bonePtr->translation ); } else { // tag's require special handling // rotate each of the axis by the torsoAngles LocalScaledMatrixTransformVector( bonePtr->matrix[0], thisBoneInfo->torsoWeight, torsoAxis, tmpAxis[0] ); LocalScaledMatrixTransformVector( bonePtr->matrix[1], thisBoneInfo->torsoWeight, torsoAxis, tmpAxis[1] ); LocalScaledMatrixTransformVector( bonePtr->matrix[2], thisBoneInfo->torsoWeight, torsoAxis, tmpAxis[2] ); memcpy( bonePtr->matrix, tmpAxis, sizeof( tmpAxis ) ); // rotate the translation around the torsoParent VectorSubtract( bonePtr->translation, torsoParentOffset, t ); LocalScaledMatrixTransformVector( t, thisBoneInfo->torsoWeight, torsoAxis, bonePtr->translation ); VectorAdd( bonePtr->translation, torsoParentOffset, bonePtr->translation ); } } } }
void ProcessOrientCommands( Vehicle_t *pVeh ) { /********************************************************************************/ /* BEGIN Here is where make sure the vehicle is properly oriented. BEGIN */ /********************************************************************************/ playerState_t *riderPS; playerState_t *parentPS; #ifdef _JK2MP float angDif; if (pVeh->m_pPilot) { riderPS = pVeh->m_pPilot->playerState; } else { riderPS = pVeh->m_pParentEntity->playerState; } parentPS = pVeh->m_pParentEntity->playerState; //pVeh->m_vOrientation[YAW] = 0.0f;//riderPS->viewangles[YAW]; angDif = AngleSubtract(pVeh->m_vOrientation[YAW], riderPS->viewangles[YAW]); if (parentPS && parentPS->speed) { float s = parentPS->speed; float maxDif = pVeh->m_pVehicleInfo->turningSpeed*4.0f; //magic number hackery if (s < 0.0f) { s = -s; } angDif *= s/pVeh->m_pVehicleInfo->speedMax; if (angDif > maxDif) { angDif = maxDif; } else if (angDif < -maxDif) { angDif = -maxDif; } pVeh->m_vOrientation[YAW] = AngleNormalize180(pVeh->m_vOrientation[YAW] - angDif*(pVeh->m_fTimeModifier*0.2f)); if (parentPS->electrifyTime > pm->cmd.serverTime) { //do some crazy stuff pVeh->m_vOrientation[YAW] += (sin(pm->cmd.serverTime/1000.0f)*3.0f)*pVeh->m_fTimeModifier; } } #else gentity_t *rider = pVeh->m_pParentEntity->owner; if ( !rider || !rider->client ) { riderPS = &pVeh->m_pParentEntity->client->ps; } else { riderPS = &rider->client->ps; } parentPS = &pVeh->m_pParentEntity->client->ps; if (pVeh->m_ulFlags & VEH_FLYING) { pVeh->m_vOrientation[YAW] += pVeh->m_vAngularVelocity; } else if ( (pVeh->m_ulFlags & VEH_SLIDEBREAKING) || // No Angles Control While Out Of Control (pVeh->m_ulFlags & VEH_OUTOFCONTROL) // No Angles Control While Out Of Control ) { // Any ability to change orientation? } else if ( (pVeh->m_ulFlags & VEH_STRAFERAM) // No Angles Control While Strafe Ramming ) { if (parentPS->hackingTime>0) { parentPS->hackingTime--; pVeh->m_vOrientation[ROLL] += (parentPS->hackingTime<( STRAFERAM_DURATION/2))?(-STRAFERAM_ANGLE):( STRAFERAM_ANGLE); } else if (pVeh->hackingTime<0) { parentPS->hackingTime++; pVeh->m_vOrientation[ROLL] += (parentPS->hackingTime>(-STRAFERAM_DURATION/2))?( STRAFERAM_ANGLE):(-STRAFERAM_ANGLE); } } else { pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW]; } #endif /********************************************************************************/ /* END Here is where make sure the vehicle is properly oriented. END */ /********************************************************************************/ }
//MP RULE - ALL PROCESSORIENTCOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!! //If you really need to violate this rule for SP, then use ifdefs. //By BG-compatible, I mean no use of game-specific data - ONLY use //stuff available in the MP bgEntity (in SP, the bgEntity is #defined //as a gentity, but the MP-compatible access restrictions are based //on the bgEntity structure in the MP codebase) -rww // ProcessOrientCommands the Vehicle. static void ProcessOrientCommands( Vehicle_t *pVeh ) { /********************************************************************************/ /* BEGIN Here is where make sure the vehicle is properly oriented. BEGIN */ /********************************************************************************/ bgEntity_t *parent = pVeh->m_pParentEntity; playerState_t *parentPS, *riderPS; bgEntity_t *rider = NULL; if ( parent->s.owner != ENTITYNUM_NONE ) { rider = PM_BGEntForNum( parent->s.owner ); //&g_entities[parent->r.ownerNum]; } if ( !rider ) { rider = parent; } parentPS = parent->playerState; riderPS = rider->playerState; if ( rider ) { float angDif = AngleSubtract( pVeh->m_vOrientation->yaw, riderPS->viewangles.yaw ); if ( parentPS && (int)parentPS->speed ) { float s = parentPS->speed; float maxDif = pVeh->m_pVehicleInfo->turningSpeed*4.0f; //magic number hackery if ( s < 0.0f ) { s = -s; } angDif *= s / pVeh->m_pVehicleInfo->speedMax; if ( angDif > maxDif ) { angDif = maxDif; } else if ( angDif < -maxDif ) { angDif = -maxDif; } pVeh->m_vOrientation->yaw = AngleNormalize180( pVeh->m_vOrientation->yaw - angDif*(pVeh->m_fTimeModifier*0.2f) ); } } /* speed = VectorLength( parentPS->velocity ); // If the player is the rider... if ( rider->s.number < MAX_CLIENTS ) {//FIXME: use the vehicle's turning stat in this calc pVeh->m_vOrientation->yaw = riderPS->viewangles->yaw; } else { float turnSpeed = pVeh->m_pVehicleInfo->turningSpeed; if ( !pVeh->m_pVehicleInfo->turnWhenStopped && !parentPS->speed )//FIXME: or !pVeh->m_ucmd.forwardmove? {//can't turn when not moving //FIXME: or ramp up to max turnSpeed? turnSpeed = 0.0f; } if (rider->s.eType == ET_NPC) {//help NPCs out some turnSpeed *= 2.0f; if (parentPS->speed > 200.0f) { turnSpeed += turnSpeed * parentPS->speed/200.0f*0.05f; } } turnSpeed *= pVeh->m_fTimeModifier; //default control scheme: strafing turns, mouselook aims if ( pVeh->m_ucmd.rightmove < 0 ) { pVeh->m_vOrientation->yaw += turnSpeed; } else if ( pVeh->m_ucmd.rightmove > 0 ) { pVeh->m_vOrientation->yaw -= turnSpeed; } if ( pVeh->m_pVehicleInfo->malfunctionArmorLevel && pVeh->m_iArmor <= pVeh->m_pVehicleInfo->malfunctionArmorLevel ) {//damaged badly } }*/ /********************************************************************************/ /* END Here is where make sure the vehicle is properly oriented. END */ /********************************************************************************/ }