/* ============== BotChangeViewAngles ============== */ void BotChangeViewAngles(bot_state_t *bs, float thinktime) { float diff, factor, maxchange, anglespeed, disired_speed; int i; if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360; // if (bs->enemy >= 0) { factor = botlib_export->ai.Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_FACTOR, 0.01f, 1); maxchange = botlib_export->ai.Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_MAXCHANGE, 1, 1800); } else { factor = 0.05f; maxchange = 360; } if (maxchange < 240) maxchange = 240; maxchange *= thinktime; for (i = 0; i < 2; i++) { // if (bot_challenge->integer) { //smooth slowdown view model diff = fabs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i])); anglespeed = diff * factor; if (anglespeed > maxchange) anglespeed = maxchange; bs->viewangles[i] = BotChangeViewAngle(bs->viewangles[i], bs->ideal_viewangles[i], anglespeed); } else { //over reaction view model bs->viewangles[i] = AngleMod(bs->viewangles[i]); bs->ideal_viewangles[i] = AngleMod(bs->ideal_viewangles[i]); diff = AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]); disired_speed = diff * factor; bs->viewanglespeed[i] += (bs->viewanglespeed[i] - disired_speed); if (bs->viewanglespeed[i] > 180) bs->viewanglespeed[i] = maxchange; if (bs->viewanglespeed[i] < -180) bs->viewanglespeed[i] = -maxchange; anglespeed = bs->viewanglespeed[i]; if (anglespeed > maxchange) anglespeed = maxchange; if (anglespeed < -maxchange) anglespeed = -maxchange; bs->viewangles[i] += anglespeed; bs->viewangles[i] = AngleMod(bs->viewangles[i]); //demping bs->viewanglespeed[i] *= 0.45 * (1 - factor); } //BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);` //bs->viewangles[i] = bs->ideal_viewangles[i]; } //bs->viewangles[PITCH] = 0; if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360; //elementary action: view botlib_export->ea.EA_View(bs->client, bs->viewangles); }
/* ============== BotChangeViewAngles ============== */ void BotChangeViewAngles( bot_state_t *bs, float thinktime ) { float diff, factor, maxchange, anglespeed; int i; if ( bs->ideal_viewangles[PITCH] > 180 ) { bs->ideal_viewangles[PITCH] -= 360; } // if ( bs->enemy >= 0 ) { factor = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_VIEW_FACTOR, 0.01, 1 ); maxchange = trap_Characteristic_BFloat( bs->character, CHARACTERISTIC_VIEW_MAXCHANGE, 1, 1800 ); } else { factor = 0.25; maxchange = 300; } maxchange *= thinktime; for ( i = 0; i < 2; i++ ) { diff = abs( AngleDifference( bs->viewangles[i], bs->ideal_viewangles[i] ) ); anglespeed = diff * factor; if ( anglespeed > maxchange ) { anglespeed = maxchange; } bs->viewangles[i] = BotChangeViewAngle( bs->viewangles[i], bs->ideal_viewangles[i], anglespeed ); //BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);` //bs->viewangles[i] = bs->ideal_viewangles[i]; } if ( bs->viewangles[PITCH] > 180 ) { bs->viewangles[PITCH] -= 360; } //elementary action: view trap_EA_View( bs->client, bs->viewangles ); }
float InterpolateAngle(float a1, float a2, const float pour) { float a = MAKEANGLE(a1); float diff = AngleDifference(a, MAKEANGLE(a2)); return MAKEANGLE(a + (diff * pour)); }
void EntityPlayer::Flash(Vector Position) { Vector dist = Position - Pos; float Angle = 180 / 3.14 * atan2f(dist.Y, dist.X); FlashTime = worldObj->FlashManager->MaxFlashTime * (1 - (abs(AngleDifference(Rot,Angle))/90)); if (FlashTime < 2) { FlashTime = 2; } //Make entire screen white worldObj->FlashManager->AddFlashBangEvent(this,Position); }
void EntityPlayer::UpdatePlayerAngle() { float RotSpeed = 0.1; Vector LookVector = MousePosition; float Angle = 180/3.14 * atan2f(LookVector.Y, LookVector.X); if (Rot < 170 && Angle > 170) { int i = 0; } RotOld += 0.01 * AngleDifference(Rot,Angle); //RotOld -= 0.1; }
/* ============== AICast_ChangeViewAngles ============== */ void AICast_ChangeViewAngles( cast_state_t *cs, float thinktime ) { float diff, factor, maxchange, anglespeed; int i; bot_state_t *bs; bs = cs->bs; // // restoire locked viewangles if required if ( cs->aiFlags & AIFL_VIEWLOCKED ) { VectorCopy( cs->viewlock_viewangles, bs->ideal_viewangles ); } // if ( bs->ideal_viewangles[PITCH] > 180 ) { bs->ideal_viewangles[PITCH] -= 360; } // maxchange = cs->attributes[YAW_SPEED]; //300; if ( cs->aiState >= AISTATE_COMBAT ) { factor = 2.0; maxchange *= 2.0; } else { factor = 0.7; } // if ( cs->lockViewAnglesTime < level.time ) { maxchange *= thinktime; for ( i = 0; i < 3; i++ ) { diff = c::fabs( AngleDifference( bs->viewangles[i], bs->ideal_viewangles[i] ) ); anglespeed = diff * factor; if ( anglespeed > maxchange ) { anglespeed = maxchange; } bs->viewangles[i] = BotChangeViewAngle( bs->viewangles[i], bs->ideal_viewangles[i], anglespeed ); //BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);` //bs->viewangles[i] = bs->ideal_viewangles[i]; } } if ( bs->viewangles[PITCH] > 180 ) { bs->viewangles[PITCH] -= 360; } //elementary action: view trap_EA_View( bs->client, bs->viewangles ); }
/* ============== BotChangeViewAngles ============== */ void BotChangeViewAngles(bot_state_t *bs, float thinktime) { float diff, factor, maxchange, anglespeed, disired_speed; int i; if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360; factor = bs->skills.turnspeed; if (factor > 1) { factor = 1; } if (factor < 0.25) { factor = 0.25f; } maxchange = bs->skills.maxturn; //if (maxchange < 240) maxchange = 240; maxchange *= thinktime; for (i = 0; i < 2; i++) { bs->viewangles[i] = AngleMod(bs->viewangles[i]); bs->ideal_viewangles[i] = AngleMod(bs->ideal_viewangles[i]); diff = AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]); disired_speed = diff * factor; bs->viewanglespeed[i] += (bs->viewanglespeed[i] - disired_speed); if (bs->viewanglespeed[i] > 180) bs->viewanglespeed[i] = maxchange; if (bs->viewanglespeed[i] < -180) bs->viewanglespeed[i] = -maxchange; anglespeed = bs->viewanglespeed[i]; if (anglespeed > maxchange) anglespeed = maxchange; if (anglespeed < -maxchange) anglespeed = -maxchange; bs->viewangles[i] += anglespeed; bs->viewangles[i] = AngleMod(bs->viewangles[i]); bs->viewanglespeed[i] *= 0.45 * (1 - factor); } if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360; trap_EA_View(bs->client, bs->viewangles); }
bool CStructure::IsAvailableAreaActive(int iArea) const { if (iArea != 0) return BaseClass::IsAvailableAreaActive(iArea); if (!GetDigitanksPlayer()) return false; if (!GetDigitanksPlayer()->GetPrimaryCPU()) return false; if (DigitanksGame()->GetCurrentLocalDigitanksPlayer() == GetDigitanksPlayer()) return false; if (DigitanksGame()->GetControlMode() != MODE_AIM) return false; CDigitank* pTank = DigitanksGame()->GetCurrentLocalDigitanksPlayer()->GetPrimarySelectionTank(); if (!pTank) return false; if (!pTank->IsInsideMaxRange(GetGlobalOrigin())) return false; if (GetVisibility(pTank->GetDigitanksPlayer()) < 0.1f) return false; if (pTank->FiringCone() < 360 && fabs(AngleDifference(pTank->GetGlobalAngles().y, VectorAngles((GetGlobalOrigin() - pTank->GetGlobalOrigin()).Normalized()).y)) > pTank->FiringCone()) return false; if (pTank->GetCurrentWeapon() == PROJECTILE_TREECUTTER) return false; return true; }
void SensorFusion::handleMessage(const MessageBodyFrame& msg) { if (msg.Type != Message_BodyFrame) return; // Put the sensor readings into convenient local variables Vector3f angVel = msg.RotationRate; Vector3f rawAccel = msg.Acceleration; Vector3f mag = msg.MagneticField; // Set variables accessible through the class API DeltaT = msg.TimeDelta; AngV = msg.RotationRate; AngV.y *= YawMult; // Warning: If YawMult != 1, then AngV is not true angular velocity A = rawAccel; // Allow external access to uncalibrated magnetometer values RawMag = mag; // Apply the calibration parameters to raw mag if (HasMagCalibration()) { mag.x += MagCalibrationMatrix.M[0][3]; mag.y += MagCalibrationMatrix.M[1][3]; mag.z += MagCalibrationMatrix.M[2][3]; } // Provide external access to calibrated mag values // (if the mag is not calibrated, then the raw value is returned) CalMag = mag; float angVelLength = angVel.Length(); float accLength = rawAccel.Length(); // Acceleration in the world frame (Q is current HMD orientation) Vector3f accWorld = Q.Rotate(rawAccel); // Keep track of time Stage++; float currentTime = Stage * DeltaT; // Assumes uniform time spacing // Insert current sensor data into filter history FRawMag.AddElement(RawMag); FAccW.AddElement(accWorld); FAngV.AddElement(angVel); // Update orientation Q based on gyro outputs. This technique is // based on direct properties of the angular velocity vector: // Its direction is the current rotation axis, and its magnitude // is the rotation rate (rad/sec) about that axis. Our sensor // sampling rate is so fast that we need not worry about integral // approximation error (not yet, anyway). if (angVelLength > 0.0f) { Vector3f rotAxis = angVel / angVelLength; float halfRotAngle = angVelLength * DeltaT * 0.5f; float sinHRA = sin(halfRotAngle); Quatf deltaQ(rotAxis.x*sinHRA, rotAxis.y*sinHRA, rotAxis.z*sinHRA, cos(halfRotAngle)); Q = Q * deltaQ; } // The quaternion magnitude may slowly drift due to numerical error, // so it is periodically normalized. if (Stage % 5000 == 0) Q.Normalize(); // Maintain the uncorrected orientation for later use by predictive filtering QUncorrected = Q; // Perform tilt correction using the accelerometer data. This enables // drift errors in pitch and roll to be corrected. Note that yaw cannot be corrected // because the rotation axis is parallel to the gravity vector. if (EnableGravity) { // Correcting for tilt error by using accelerometer data const float gravityEpsilon = 0.4f; const float angVelEpsilon = 0.1f; // Relatively slow rotation const int tiltPeriod = 50; // Req'd time steps of stability const float maxTiltError = 0.05f; const float minTiltError = 0.01f; // This condition estimates whether the only measured acceleration is due to gravity // (the Rift is not linearly accelerating). It is often wrong, but tends to average // out well over time. if ((fabs(accLength - 9.81f) < gravityEpsilon) && (angVelLength < angVelEpsilon)) TiltCondCount++; else TiltCondCount = 0; // After stable measurements have been taken over a sufficiently long period, // estimate the amount of tilt error and calculate the tilt axis for later correction. if (TiltCondCount >= tiltPeriod) { // Update TiltErrorEstimate TiltCondCount = 0; // Use an average value to reduce noice (could alternatively use an LPF) Vector3f accWMean = FAccW.Mean(); // Project the acceleration vector into the XZ plane Vector3f xzAcc = Vector3f(accWMean.x, 0.0f, accWMean.z); // The unit normal of xzAcc will be the rotation axis for tilt correction Vector3f tiltAxis = Vector3f(xzAcc.z, 0.0f, -xzAcc.x).Normalized(); Vector3f yUp = Vector3f(0.0f, 1.0f, 0.0f); // This is the amount of rotation float tiltAngle = yUp.Angle(accWMean); // Record values if the tilt error is intolerable if (tiltAngle > maxTiltError) { TiltErrorAngle = tiltAngle; TiltErrorAxis = tiltAxis; } } // This part performs the actual tilt correction as needed if (TiltErrorAngle > minTiltError) { if ((TiltErrorAngle > 0.4f)&&(Stage < 8000)) { // Tilt completely to correct orientation Q = Quatf(TiltErrorAxis, -TiltErrorAngle) * Q; TiltErrorAngle = 0.0f; } else { //LogText("Performing tilt correction - Angle: %f Axis: %f %f %f\n", // TiltErrorAngle,TiltErrorAxis.x,TiltErrorAxis.y,TiltErrorAxis.z); //float deltaTiltAngle = -Gain*TiltErrorAngle*0.005f; // This uses agressive correction steps while your head is moving fast float deltaTiltAngle = -Gain*TiltErrorAngle*0.005f*(5.0f*angVelLength+1.0f); // Incrementally "untilt" by a small step size Q = Quatf(TiltErrorAxis, deltaTiltAngle) * Q; TiltErrorAngle += deltaTiltAngle; } } } // Yaw drift correction based on magnetometer data. This corrects the part of the drift // that the accelerometer cannot handle. // This will only work if the magnetometer has been enabled, calibrated, and a reference // point has been set. const float maxAngVelLength = 3.0f; const int magWindow = 5; const float yawErrorMax = 0.1f; const float yawErrorMin = 0.01f; const int yawErrorCountLimit = 50; const float yawRotationStep = 0.00002f; if (angVelLength < maxAngVelLength) MagCondCount++; else MagCondCount = 0; YawCorrectionInProgress = false; if (EnableYawCorrection && MagReady && (currentTime > 2.0f) && (MagCondCount >= magWindow) && (Q.Distance(MagRefQ) < MagRefDistance)) { // Use rotational invariance to bring reference mag value into global frame Vector3f grefmag = MagRefQ.Rotate(GetCalibratedMagValue(MagRefM)); // Bring current (averaged) mag reading into global frame Vector3f gmag = Q.Rotate(GetCalibratedMagValue(FRawMag.Mean())); // Calculate the reference yaw in the global frame float gryaw = atan2(grefmag.x,grefmag.z); // Calculate the current yaw in the global frame float gyaw = atan2(gmag.x,gmag.z); //LogText("Yaw error estimate: %f\n",YawErrorAngle); // The difference between reference and current yaws is the perceived error YawErrorAngle = AngleDifference(gyaw,gryaw); // If the perceived error is large, keep count if ((fabs(YawErrorAngle) > yawErrorMax) && (!YawCorrectionActivated)) YawErrorCount++; // After enough iterations of high perceived error, start the correction process if (YawErrorCount > yawErrorCountLimit) YawCorrectionActivated = true; // If the perceived error becomes small, turn off the yaw correction if ((fabs(YawErrorAngle) < yawErrorMin) && YawCorrectionActivated) { YawCorrectionActivated = false; YawErrorCount = 0; } // Perform the actual yaw correction, due to previously detected, large yaw error if (YawCorrectionActivated) { YawCorrectionInProgress = true; int sign = (YawErrorAngle > 0.0f) ? 1 : -1; // Incrementally "unyaw" by a small step size Q = Quatf(Vector3f(0.0f,1.0f,0.0f), -yawRotationStep * sign) * Q; } } }
void AICast_InputToUserCommand( cast_state_t *cs, bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3] ) { vec3_t angles, forward, right, up; short temp; int j; signed char movechar; gentity_t *ent; ent = &g_entities[cs->entityNum]; //clear the whole structure memset( ucmd, 0, sizeof( usercmd_t ) ); // //Com_Printf("dir = %f %f %f speed = %f\n", bi->dir[0], bi->dir[1], bi->dir[2], bi->speed); //the duration for the user command in milli seconds ucmd->serverTime = serverTime; //crouch/movedown if ( aiDefaults[cs->aiCharacter].attributes[ATTACK_CROUCH] ) { // only crouch if this character is physically able to //RF, disabled this bit so truck guy in forest_6 doesn't get stuck and then gib if ( /*cs->bs->cur_ps.groundEntityNum != ENTITYNUM_NONE &&*/ bi->actionflags & ACTION_CROUCH ) { ucmd->upmove -= 127; } } // // actions not effected by script pausing // // set zoom button if ( cs->aiFlags & AIFL_ZOOMING ) { ucmd->wbuttons |= WBUTTON_ZOOM; } //set the buttons if ( bi->actionflags & ACTION_ATTACK ) { vec3_t ofs; // don't fire if we are not facing the right direction yet if ( ( cs->triggerReleaseTime < level.time ) && ( ( cs->lockViewAnglesTime >= level.time ) || ( c::fabs( AngleDifference( cs->bs->ideal_viewangles[YAW], cs->bs->viewangles[YAW] ) ) < 20 ) ) && // check for radid luger firing by skilled users (release fire between shots) ( ( ( level.time + cs->entityNum * 500 ) / 2000 ) % 2 || !( rand() % ( 1 + g_gameskill.integer ) ) || ( cs->attributes[ATTACK_SKILL] < 0.5 ) || ( cs->bs->weaponnum != WP_LUGER ) || ( cs->bs->cur_ps.weaponTime == 0 ) || ( cs->bs->cur_ps.releasedFire ) ) ) { ucmd->buttons |= BUTTON_ATTACK; // do some swaying around for some weapons AICast_WeaponSway( cs, ofs ); VectorAdd( bi->viewangles, ofs, bi->viewangles ); } } // //set the view angles //NOTE: the ucmd->angles are the angles WITHOUT the delta angles ucmd->angles[PITCH] = ANGLE2SHORT( bi->viewangles[PITCH] ); ucmd->angles[YAW] = ANGLE2SHORT( bi->viewangles[YAW] ); ucmd->angles[ROLL] = ANGLE2SHORT( bi->viewangles[ROLL] ); //subtract the delta angles for ( j = 0; j < 3; j++ ) { temp = ucmd->angles[j] - delta_angles[j]; ucmd->angles[j] = temp; } //----(SA) modified slightly for DM/DK ucmd->weapon = bi->weapon; // // relaxed mode show no weapons if ( cs->aiState <= AISTATE_QUERY ) { if ( WEAPS_ONE_HANDED & ( 1 << ucmd->weapon ) ) { // one-handed wepons don't draw, others do ucmd->weapon = WP_NONE; } } //----(SA) end // if ( bi->actionflags & ACTION_GESTURE ) { ucmd->buttons |= BUTTON_GESTURE; } if ( bi->actionflags & ACTION_RELOAD ) { ucmd->wbuttons |= WBUTTON_RELOAD; } // // if we are locked down, don't do anything // if ( cs->pauseTime > level.time ) { return; } // // if scripted pause, no movement // if ( cs->castScriptStatus.scriptNoMoveTime > level.time ) { return; } // // if viewlock, wait until we are facing ideal angles before we move if ( cs->aiFlags & AIFL_VIEWLOCKED ) { if ( c::fabs( AngleDifference( cs->bs->ideal_viewangles[YAW], cs->bs->viewangles[YAW] ) ) > 10 ) { return; } } // if ( bi->actionflags & ACTION_DELAYEDJUMP ) { bi->actionflags |= ACTION_JUMP; bi->actionflags &= ~ACTION_DELAYEDJUMP; } // // only move if we are in combat or we are facing where our ideal angles if ( bi->speed ) { if ( ( !( cs->aiFlags & AIFL_WALKFORWARD ) && cs->bs->enemy >= 0 ) || ( c::fabs( AngleDifference( cs->bs->ideal_viewangles[YAW], cs->bs->viewangles[YAW] ) ) < 60 ) ) { //NOTE: movement is relative to the REAL view angles //get the horizontal forward and right vector //get the pitch in the range [-180, 180] if ( bi->dir[2] ) { angles[PITCH] = bi->viewangles[PITCH]; } else { angles[PITCH] = 0;} angles[YAW] = bi->viewangles[YAW]; angles[ROLL] = 0; AngleVectors( angles, forward, right, up ); //bot input speed is in the range [0, 400] bi->speed = bi->speed * 127 / 400; //set the view independent movement ucmd->forwardmove = DotProduct( forward, bi->dir ) * bi->speed; ucmd->rightmove = DotProduct( right, bi->dir ) * bi->speed; // RF, changed this to fix stim soldier flying attack if ( !ucmd->upmove ) { // only change it if we don't already have an upmove set ucmd->upmove = DotProduct( up, bi->dir ) * bi->speed; } //if (!ucmd->upmove) // only change it if we don't already have an upmove set // ucmd->upmove = c::abs(forward[2]) * bi->dir[2] * bi->speed; } } // //normal keyboard movement if ( cs->actionFlags & CASTACTION_WALK ) { movechar = 70; } else { movechar = 127; } if ( bi->actionflags & ACTION_MOVEFORWARD ) { ucmd->forwardmove = movechar; } if ( !( cs->aiFlags & AIFL_WALKFORWARD ) ) { // only do other movements if we are allowed to if ( bi->actionflags & ACTION_MOVEBACK ) { ucmd->forwardmove = -movechar; } if ( bi->actionflags & ACTION_MOVELEFT ) { ucmd->rightmove = -movechar; } if ( bi->actionflags & ACTION_MOVERIGHT ) { ucmd->rightmove = movechar; } } //jump/moveup if ( bi->actionflags & ACTION_JUMP ) { ucmd->upmove = 127; // JUMP always takes preference over ducking } if ( bi->actionflags & ACTION_MOVEDOWN ) { ucmd->upmove = -127; // JUMP always takes preference over ducking } if ( bi->actionflags & ACTION_MOVEUP ) { ucmd->upmove = 127; // JUMP always takes preference over ducking } // //Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove); }
void EntityLiving::Flash(Vector position) { Vector dist = position - Pos; float Angle = 180 / 3.14 * atan2f(dist.Y, dist.X); FlashTime = worldObj->FlashManager->MaxFlashTime * (1 - (abs(AngleDifference(Rot, Angle)) / 90)); }
/* ================= G_ScriptAction_FaceAngles syntax: faceangles <pitch> <yaw> <roll> <duration/GOTOTIME> [ACCEL/DECCEL] The entity will face the given angles, taking <duration> to get there. If the GOTOTIME is given instead of a timed duration, the duration calculated from the last gotomarker command will be used instead. ================= */ qboolean G_ScriptAction_FaceAngles( gentity_t *ent, char *params ) { char *pString, *token; int duration, i; vec3_t diff; vec3_t angles; int trType = TR_LINEAR_STOP; if ( !params || !params[0] ) { G_Error( "G_Scripting: syntax: faceangles <pitch> <yaw> <roll> <duration/GOTOTIME>\n" ); } if ( ent->scriptStatus.scriptStackChangeTime == level.time ) { pString = params; for ( i = 0; i < 3; i++ ) { token = COM_Parse( &pString ); if ( !token || !token[0] ) { G_Error( "G_Scripting: syntax: faceangles <pitch> <yaw> <roll> <duration/GOTOTIME>\n" ); } angles[i] = atoi( token ); } token = COM_Parse( &pString ); if ( !token || !token[0] ) { G_Error( "G_Scripting: faceangles requires a <pitch> <yaw> <roll> <duration/GOTOTIME>\n" ); } if ( !Q_strcasecmp( token, "gototime" ) ) { duration = ent->s.pos.trDuration; } else { duration = atoi( token ); } token = COM_Parse( &pString ); if ( token && token[0] ) { if ( !Q_strcasecmp( token, "accel" ) ) { trType = TR_ACCELERATE; } if ( !Q_strcasecmp( token, "deccel" ) ) { trType = TR_DECCELERATE; } } for ( i = 0; i < 3; i++ ) { diff[i] = AngleDifference( angles[i], ent->s.angles[i] ); while ( diff[i] > 180 ) diff[i] -= 360; while ( diff[i] < -180 ) diff[i] += 360; } VectorCopy( ent->s.angles, ent->s.apos.trBase ); if ( duration ) { VectorScale( diff, 1000.0 / (float)duration, ent->s.apos.trDelta ); } else { VectorClear( ent->s.apos.trDelta ); } ent->s.apos.trDuration = duration; ent->s.apos.trTime = level.time; ent->s.apos.trType = TR_LINEAR_STOP; if ( trType != TR_LINEAR_STOP ) { // accel / deccel logic // calc the speed from duration and start/end delta for ( i = 0; i < 3; i++ ) { ent->s.apos.trDelta[i] = 2.0 * 1000.0 * diff[i] / (float)duration; } ent->s.apos.trType = trType; } } else if ( ent->s.apos.trTime + ent->s.apos.trDuration <= level.time ) { // finished turning BG_EvaluateTrajectory( &ent->s.apos, ent->s.apos.trTime + ent->s.apos.trDuration, ent->s.angles ); VectorCopy( ent->s.angles, ent->s.apos.trBase ); VectorCopy( ent->s.angles, ent->r.currentAngles ); ent->s.apos.trTime = level.time; ent->s.apos.trDuration = 0; ent->s.apos.trType = TR_STATIONARY; VectorClear( ent->s.apos.trDelta ); script_linkentity( ent ); return qtrue; } BG_EvaluateTrajectory( &ent->s.apos, level.time, ent->r.currentAngles ); script_linkentity( ent ); return qfalse; }
/* =============== G_ScriptAction_GotoMarker syntax: gotomarker <targetname> <speed> [accel/deccel] [turntotarget] [wait] NOTE: speed may be modified to round the duration to the next 50ms for smooth transitions =============== */ qboolean G_ScriptAction_GotoMarker( gentity_t *ent, char *params ) { char *pString, *token; gentity_t *target; vec3_t vec; float speed, dist; qboolean wait = qfalse, turntotarget = qfalse; int trType; int duration, i; vec3_t diff; vec3_t angles; if ( params && ( ent->scriptStatus.scriptFlags & SCFL_GOING_TO_MARKER ) ) { // we can't process a new movement until the last one has finished return qfalse; } if ( !params || ent->scriptStatus.scriptStackChangeTime < level.time ) { // we are waiting for it to reach destination if ( ent->s.pos.trTime + ent->s.pos.trDuration <= level.time ) { // we made it ent->scriptStatus.scriptFlags &= ~SCFL_GOING_TO_MARKER; // set the angles at the destination BG_EvaluateTrajectory( &ent->s.apos, ent->s.apos.trTime + ent->s.apos.trDuration, ent->s.angles ); VectorCopy( ent->s.angles, ent->s.apos.trBase ); VectorCopy( ent->s.angles, ent->r.currentAngles ); ent->s.apos.trTime = level.time; ent->s.apos.trDuration = 0; ent->s.apos.trType = TR_STATIONARY; VectorClear( ent->s.apos.trDelta ); // stop moving BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); ent->s.pos.trTime = level.time; ent->s.pos.trDuration = 0; ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); script_linkentity( ent ); return qtrue; } } else { // we have just started this command pString = params; token = COM_ParseExt( &pString, qfalse ); if ( !token[0] ) { G_Error( "G_Scripting: gotomarker must have an targetname\n" ); } // find the entity with the given "targetname" target = G_Find( NULL, FOFS( targetname ), token ); if ( !target ) { G_Error( "G_Scripting: can't find entity with \"targetname\" = \"%s\"\n", token ); } VectorSubtract( target->r.currentOrigin, ent->r.currentOrigin, vec ); token = COM_ParseExt( &pString, qfalse ); if ( !token[0] ) { G_Error( "G_Scripting: gotomarker must have a speed\n" ); } speed = atof( token ); trType = TR_LINEAR_STOP; while ( token[0] ) { token = COM_ParseExt( &pString, qfalse ); if ( token[0] ) { if ( !Q_stricmp( token, "accel" ) ) { trType = TR_ACCELERATE; } else if ( !Q_stricmp( token, "deccel" ) ) { trType = TR_DECCELERATE; } else if ( !Q_stricmp( token, "wait" ) ) { wait = qtrue; } else if ( !Q_stricmp( token, "turntotarget" ) ) { turntotarget = qtrue; } } } // start the movement if ( ent->s.eType == ET_MOVER ) { VectorCopy( vec, ent->movedir ); VectorCopy( ent->r.currentOrigin, ent->pos1 ); VectorCopy( target->r.currentOrigin, ent->pos2 ); ent->speed = speed; dist = VectorDistance( ent->pos1, ent->pos2 ); // setup the movement with the new parameters InitMover( ent ); // start the movement SetMoverState( ent, MOVER_1TO2, level.time ); if ( trType != TR_LINEAR_STOP ) { // allow for acceleration/decceleration ent->s.pos.trDuration = 1000.0 * dist / ( speed / 2.0 ); ent->s.pos.trType = trType; } ent->reached = NULL; if ( turntotarget ) { duration = ent->s.pos.trDuration; VectorCopy( target->s.angles, angles ); for ( i = 0; i < 3; i++ ) { diff[i] = AngleDifference( angles[i], ent->s.angles[i] ); while ( diff[i] > 180 ) diff[i] -= 360; while ( diff[i] < -180 ) diff[i] += 360; } VectorCopy( ent->s.angles, ent->s.apos.trBase ); if ( duration ) { VectorScale( diff, 1000.0 / (float)duration, ent->s.apos.trDelta ); } else { VectorClear( ent->s.apos.trDelta ); } ent->s.apos.trDuration = duration; ent->s.apos.trTime = level.time; ent->s.apos.trType = TR_LINEAR_STOP; if ( trType != TR_LINEAR_STOP ) { // allow for acceleration/decceleration ent->s.pos.trDuration = 1000.0 * dist / ( speed / 2.0 ); ent->s.pos.trType = trType; } } } else { // calculate the trajectory ent->s.pos.trType = TR_LINEAR_STOP; ent->s.pos.trTime = level.time; VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase ); dist = VectorNormalize( vec ); VectorScale( vec, speed, ent->s.pos.trDelta ); ent->s.pos.trDuration = 1000 * ( dist / speed ); if ( turntotarget ) { duration = ent->s.pos.trDuration; VectorCopy( target->s.angles, angles ); for ( i = 0; i < 3; i++ ) { diff[i] = AngleDifference( angles[i], ent->s.angles[i] ); while ( diff[i] > 180 ) diff[i] -= 360; while ( diff[i] < -180 ) diff[i] += 360; } VectorCopy( ent->s.angles, ent->s.apos.trBase ); if ( duration ) { VectorScale( diff, 1000.0 / (float)duration, ent->s.apos.trDelta ); } else { VectorClear( ent->s.apos.trDelta ); } ent->s.apos.trDuration = duration; ent->s.apos.trTime = level.time; ent->s.apos.trType = TR_LINEAR_STOP; } } if ( !wait ) { // round the duration to the next 50ms if ( ent->s.pos.trDuration % 50 ) { float frac; frac = (float)( ( ( ent->s.pos.trDuration / 50 ) * 50 + 50 ) - ent->s.pos.trDuration ) / (float)( ent->s.pos.trDuration ); if ( frac < 1 ) { VectorScale( ent->s.pos.trDelta, 1.0 / ( 1.0 + frac ), ent->s.pos.trDelta ); ent->s.pos.trDuration = ( ent->s.pos.trDuration / 50 ) * 50 + 50; } } // set the goto flag, so we can keep processing the move until we reach the destination ent->scriptStatus.scriptFlags |= SCFL_GOING_TO_MARKER; return qtrue; // continue to next command } } BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin ); BG_EvaluateTrajectory( &ent->s.apos, level.time, ent->r.currentAngles ); script_linkentity( ent ); return qfalse; }