void CTAAirMoveType::UpdateFlying() { float3 &pos = owner->pos; float3 &speed = owner->speed; // Direction to where we would like to be float3 dir = goalPos - pos; owner->restTime = 0; // don't change direction for waypoints we just flew over and missed slightly if (flyState != FLY_LANDING && owner->commandAI->HasMoreMoveCommands() && dir != ZeroVector && dir.SqLength2D() < 10000.0f) { float3 ndir = dir; ndir = ndir.UnsafeANormalize(); if (ndir.SqDistance(dir) < 1.0f) { dir = owner->frontdir; } } const float gHeight = UseSmoothMesh()? std::max(smoothGround->GetHeight(pos.x, pos.z), ground->GetApproximateHeight(pos.x, pos.z)): ground->GetHeightAboveWater(pos.x, pos.z); // are we there yet? bool closeToGoal = (dir.SqLength2D() < maxDrift * maxDrift) && (fabs(gHeight - pos.y + wantedHeight) < maxDrift); if (flyState == FLY_ATTACKING) { closeToGoal = (dir.SqLength2D() < 400); } if (closeToGoal) { // pretty close switch (flyState) { case FLY_CRUISING: { bool trans = dynamic_cast<CTransportUnit*>(owner); bool noland = dontLand || !autoLand; // should CMD_LOAD_ONTO be here? bool hasLoadCmds = trans && !owner->commandAI->commandQue.empty() && (owner->commandAI->commandQue.front().id == CMD_LOAD_ONTO || owner->commandAI->commandQue.front().id == CMD_LOAD_UNITS); if (noland || (trans && ++waitCounter < 55 && hasLoadCmds)) { // transport aircraft need some time to detect that they can pickup if (trans) { wantedSpeed = ZeroVector; SetState(AIRCRAFT_HOVERING); if (waitCounter > 60) { wantedHeight = orgWantedHeight; } } else { wantedSpeed = ZeroVector; if (!owner->commandAI->HasMoreMoveCommands()) wantToStop = true; SetState(AIRCRAFT_HOVERING); } } else { wantedHeight = orgWantedHeight; SetState(AIRCRAFT_LANDING); } return; } case FLY_CIRCLING: // break; waitCounter++; if (waitCounter > 100) { if (owner->unitDef->airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } relPos.y = 0.0f; relPos.Normalize(); static CMatrix44f rot(0.0f,fastmath::PI/4.0f,0.0f); float3 newPos = rot.Mul(relPos); // Make sure the point is on the circle newPos = newPos * goalDistance; // Go there in a straight line goalPos = circlingPos + newPos; } waitCounter = 0; } break; case FLY_ATTACKING: { if (owner->unitDef->airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } relPos.y = 0; relPos.Normalize(); CMatrix44f rot; if (gs->randFloat() > 0.5f) { rot.RotateY(0.6f + gs->randFloat() * 0.6f); } else { rot.RotateY(-(0.6f + gs->randFloat() * 0.6f)); } float3 newPos = rot.Mul(relPos); newPos = newPos * goalDistance; // Go there in a straight line goalPos = circlingPos + newPos; } break; } case FLY_LANDING: { break; } } } // not there yet, so keep going dir.y = 0; float realMax = maxSpeed; float dist = dir.Length() + 0.1f; // If we are close to our goal, we should go slow enough to be able to break in time // new additional rule: if in attack mode or if we have more move orders then this is // an intermediate waypoint, don't slow down (FIXME) if (flyState != FLY_ATTACKING && dist < brakeDistance) { realMax = dist / (speed.Length2D() + 0.01f) * decRate; } wantedSpeed = (dir / dist) * realMax; UpdateAirPhysics(); // Point toward goal or forward - unless we just passed it to get to another goal if ((flyState == FLY_ATTACKING) || (flyState == FLY_CIRCLING)) { dir = circlingPos - pos; } else if (flyState != FLY_LANDING && (owner->commandAI->HasMoreMoveCommands() && dist < 120) && (goalPos - pos).Normalize().SqDistance(dir) > 1) { dir = owner->frontdir; } else { dir = goalPos - pos; } if (dir.SqLength2D() > 1) { const int h = GetHeadingFromVector(dir.x, dir.z); wantedHeading = (h == 0)? wantedHeading : h; } }
void CTAAirMoveType::UpdateAirPhysics() { float3& pos = owner->pos; float3& speed = owner->speed; if (!((gs->frameNum + owner->id) & 3)) { CheckForCollision(); } const float yspeed = speed.y; speed.y = 0.0f; float3 delta = wantedSpeed - speed; const float deltaDotSpeed = (speed != ZeroVector)? delta.dot(speed): 1.0f; if (deltaDotSpeed == 0.0f) { // we have the wanted speed } else if (deltaDotSpeed > 0.0f) { // accelerate const float sqdl = delta.SqLength(); if (sqdl < Square(accRate)) { speed = wantedSpeed; } else { speed += delta / math::sqrt(sqdl) * accRate; } } else { // break const float sqdl = delta.SqLength(); if (sqdl < Square(decRate)) { speed = wantedSpeed; } else { speed += delta / math::sqrt(sqdl) * decRate; } } speed.y = yspeed; float h; if (UseSmoothMesh()) { h = pos.y - std::max( smoothGround->GetHeightAboveWater(pos.x, pos.z), smoothGround->GetHeightAboveWater(pos.x + speed.x * 20.0f, pos.z + speed.z * 20.0f)); } else { h = pos.y - std::max( ground->GetHeightAboveWater(pos.x, pos.z), ground->GetHeightAboveWater(pos.x + speed.x * 40.0f, pos.z + speed.z * 40.0f)); } if (h < 4.0f) { speed.x *= 0.95f; speed.z *= 0.95f; } float wh = wantedHeight; if (lastColWarningType == 2) { const float3 dir = lastColWarning->midPos - owner->midPos; const float3 sdir = lastColWarning->speed - speed; if (speed.dot(dir + sdir * 20.0f) < 0.0f) { if (lastColWarning->midPos.y > owner->pos.y) { wh -= 30.0f; } else { wh += 50.0f; } } } float ws = 0.0f; if (h < wh) { ws = altitudeRate; if (speed.y > 0.0001f && (wh - h) / speed.y * accRate * 1.5f < speed.y) { ws = 0.0f; } } else { ws = -altitudeRate; if (speed.y < -0.0001f && (wh - h) / speed.y * accRate * 0.7f < -speed.y) { ws = 0.0f; } } if (fabs(wh - h) > 2.0f) { if (speed.y > ws) { speed.y = std::max(ws, speed.y - accRate * 1.5f); } else if (!owner->beingBuilt) { // let them accelerate upward faster if close to ground speed.y = std::min(ws, speed.y + accRate * (h < 20.0f? 2.0f: 0.7f)); } } else { speed.y = speed.y * 0.95; } if (modInfo.allowAirPlanesToLeaveMap || (pos+speed).CheckInBounds()) { pos += speed; } }
void CHoverAirMoveType::UpdateFlying() { const float3& pos = owner->pos; // const float4& spd = owner->speed; // Direction to where we would like to be float3 goalVec = goalPos - pos; owner->restTime = 0; // don't change direction for waypoints we just flew over and missed slightly if (flyState != FLY_LANDING && owner->commandAI->HasMoreMoveCommands()) { float3 goalDir = goalVec; if ((goalDir != ZeroVector) && (goalVec.dot(goalDir.UnsafeANormalize()) < 1.0f)) { goalVec = owner->frontdir; } } const float goalDistSq2D = goalVec.SqLength2D(); const float gHeight = UseSmoothMesh()? std::max(smoothGround->GetHeight(pos.x, pos.z), CGround::GetApproximateHeight(pos.x, pos.z)): CGround::GetHeightAboveWater(pos.x, pos.z); const bool closeToGoal = (flyState == FLY_ATTACKING)? (goalDistSq2D < ( 400.0f)): (goalDistSq2D < (maxDrift * maxDrift)) && (math::fabs(gHeight + wantedHeight - pos.y) < maxDrift); if (closeToGoal) { switch (flyState) { case FLY_CRUISING: { // NOTE: should CMD_LOAD_ONTO be here? const bool isTransporter = (dynamic_cast<CTransportUnit*>(owner) != NULL); const bool hasLoadCmds = isTransporter && !owner->commandAI->commandQue.empty() && (owner->commandAI->commandQue.front().GetID() == CMD_LOAD_ONTO || owner->commandAI->commandQue.front().GetID() == CMD_LOAD_UNITS); // [?] transport aircraft need some time to detect that they can pickup const bool canLoad = isTransporter && (++waitCounter < ((GAME_SPEED << 1) - 5)); const bool isBusy = IsUnitBusy(owner); if (!CanLand(isBusy) || (canLoad && hasLoadCmds)) { wantedSpeed = ZeroVector; if (isTransporter) { if (waitCounter > (GAME_SPEED << 1)) { wantedHeight = orgWantedHeight; } SetState(AIRCRAFT_HOVERING); return; } else { if (!isBusy) { wantToStop = true; // NOTE: // this is not useful, next frame UpdateFlying() // will change it to _LANDING because wantToStop // is now true SetState(AIRCRAFT_HOVERING); return; } } } else { wantedHeight = orgWantedHeight; SetState(AIRCRAFT_LANDING); return; } } break; case FLY_CIRCLING: { if ((++waitCounter) > ((GAME_SPEED * 3) + 10)) { if (airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } static CMatrix44f rot(0.0f, fastmath::PI / 4.0f, 0.0f); // make sure the point is on the circle, go there in a straight line goalPos = circlingPos + (rot.Mul(relPos.Normalize2D()) * goalDistance); } waitCounter = 0; } } break; case FLY_ATTACKING: { if (airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } CMatrix44f rot; if (gs->randFloat() > 0.5f) { rot.RotateY(0.6f + gs->randFloat() * 0.6f); } else { rot.RotateY(-(0.6f + gs->randFloat() * 0.6f)); } // Go there in a straight line goalPos = circlingPos + (rot.Mul(relPos.Normalize2D()) * goalDistance); } } break; case FLY_LANDING: { } break; } } // not "close" to goal yet, so keep going // use 2D math since goal is on the ground // but we are not goalVec.y = 0.0f; // if we are close to our goal, we should // adjust speed st. we never overshoot it // (by respecting current brake-distance) const float curSpeed = owner->speed.Length2D(); const float brakeDist = (0.5f * curSpeed * curSpeed) / decRate; const float goalDist = goalVec.Length() + 0.1f; const float goalSpeed = (maxSpeed ) * (goalDist > brakeDist) + (curSpeed - decRate) * (goalDist <= brakeDist); if (goalDist > goalSpeed) { // update our velocity and heading so long as goal is still // further away than the distance we can cover in one frame // we must use a variable-size "dead zone" to avoid freezing // in mid-air or oscillation behavior at very close distances // NOTE: // wantedSpeed is a vector, so even aircraft with turnRate=0 // are coincidentally able to reach any goal by side-strafing wantedHeading = GetHeadingFromVector(goalVec.x, goalVec.z); wantedSpeed = (goalVec / goalDist) * goalSpeed; } else { // switch to hovering (if !CanLand())) if (flyState != FLY_ATTACKING) { ExecuteStop(); } } // redundant, done in Update() // UpdateHeading(); UpdateAirPhysics(); // Point toward goal or forward - unless we just passed it to get to another goal if ((flyState == FLY_ATTACKING) || (flyState == FLY_CIRCLING)) { goalVec = circlingPos - pos; } else { const bool b0 = (flyState != FLY_LANDING && (owner->commandAI->HasMoreMoveCommands())); const bool b1 = (goalDist < brakeDist && goalDist > 1.0f); if (b0 && b1) { goalVec = owner->frontdir; } else { goalVec = goalPos - pos; } } if (goalVec.SqLength2D() > 1.0f) { // update heading again in case goalVec changed wantedHeading = GetHeadingFromVector(goalVec.x, goalVec.z); } }
void CHoverAirMoveType::UpdateAirPhysics() { const float3& pos = owner->pos; const float4& spd = owner->speed; // copy vertical speed const float yspeed = spd.y; if (((gs->frameNum + owner->id) & 3) == 0) { CheckForCollision(); } // cancel out vertical speed, acc and dec are applied in xz-plane owner->SetVelocity(spd * XZVector); const float3 deltaSpeed = wantedSpeed - spd; const float deltaDotSpeed = deltaSpeed.dot(spd); const float deltaSpeedSq = deltaSpeed.SqLength(); if (deltaDotSpeed >= 0.0f) { // accelerate if (deltaSpeedSq < Square(accRate)) { owner->SetVelocity(wantedSpeed); } else { if (deltaSpeedSq > 0.0f) { owner->SetVelocity(spd + (deltaSpeed / math::sqrt(deltaSpeedSq) * accRate)); } } } else { // deccelerate if (deltaSpeedSq < Square(decRate)) { owner->SetVelocity(wantedSpeed); } else { if (deltaSpeedSq > 0.0f) { owner->SetVelocity(spd + (deltaSpeed / math::sqrt(deltaSpeedSq) * decRate)); } } } // absolute and relative ground height at (pos.x, pos.z) // if this aircraft uses the smoothmesh, these values are // calculated with respect to that (for changing vertical // speed, but not for ground collision) float curAbsHeight = owner->unitDef->canSubmerge? CGround::GetHeightReal(pos.x, pos.z): CGround::GetHeightAboveWater(pos.x, pos.z); // always stay above the actual terrain (therefore either the value of // <midPos.y - radius> or pos.y must never become smaller than the real // ground height) // note: unlike StrafeAirMoveType, UpdateTakeoff and UpdateLanding call // UpdateAirPhysics() so we ignore terrain while we are in those states if (modInfo.allowAircraftToHitGround) { const bool groundContact = (curAbsHeight > (owner->midPos.y - owner->radius)); const bool handleContact = (aircraftState != AIRCRAFT_LANDED && aircraftState != AIRCRAFT_TAKEOFF && padStatus == PAD_STATUS_FLYING); if (groundContact && handleContact) { owner->Move(UpVector * (curAbsHeight - (owner->midPos.y - owner->radius) + 0.01f), true); } } if (UseSmoothMesh()) { curAbsHeight = owner->unitDef->canSubmerge? smoothGround->GetHeight(pos.x, pos.z): smoothGround->GetHeightAboveWater(pos.x, pos.z); } // restore original vertical speed, then compute new UpdateVerticalSpeed(spd, pos.y - curAbsHeight, yspeed); if (modInfo.allowAircraftToLeaveMap || (pos + spd).IsInBounds()) { owner->Move(spd, true); } }
void CHoverAirMoveType::UpdateAirPhysics() { const float3& pos = owner->pos; float3& speed = owner->speed; if (!((gs->frameNum + owner->id) & 3)) { CheckForCollision(); } const float yspeed = speed.y; speed.y = 0.0f; const float3 deltaSpeed = wantedSpeed - speed; const float deltaDotSpeed = (speed != ZeroVector)? deltaSpeed.dot(speed): 1.0f; if (deltaDotSpeed == 0.0f) { // we have the wanted speed } else if (deltaDotSpeed > 0.0f) { // accelerate const float sqdl = deltaSpeed.SqLength(); if (sqdl < Square(accRate)) { speed = wantedSpeed; } else { speed += (deltaSpeed / math::sqrt(sqdl) * accRate); } } else { // break const float sqdl = deltaSpeed.SqLength(); if (sqdl < Square(decRate)) { speed = wantedSpeed; } else { speed += (deltaSpeed / math::sqrt(sqdl) * decRate); } } // absolute and relative ground height at (pos.x, pos.z) // if this aircraft uses the smoothmes, these values are // calculated with respect to that for changing vertical // speed, but not for ground collision float curAbsHeight = owner->unitDef->canSubmerge? ground->GetHeightReal(pos.x, pos.z): ground->GetHeightAboveWater(pos.x, pos.z); float curRelHeight = 0.0f; float wh = wantedHeight; // wanted RELATIVE height (altitude) float ws = 0.0f; // wanted vertical speed // always stay above the actual terrain (therefore either the value of // <midPos.y - radius> or pos.y must never become smaller than the real // ground height) // note: unlike StrafeAirMoveType, UpdateTakeoff calls UpdateAirPhysics // so we ignore terrain while we are in the takeoff state to avoid jumps if (modInfo.allowAircraftToHitGround) { const bool groundContact = (curAbsHeight > (owner->midPos.y - owner->radius)); const bool handleContact = (aircraftState != AIRCRAFT_LANDED && aircraftState != AIRCRAFT_TAKEOFF); if (groundContact && handleContact) { owner->Move1D(curAbsHeight - (owner->midPos.y - owner->radius) + 0.01f, 1, true); } } if (UseSmoothMesh()) { curAbsHeight = owner->unitDef->canSubmerge? smoothGround->GetHeight(pos.x, pos.z): smoothGround->GetHeightAboveWater(pos.x, pos.z); } speed.y = yspeed; curRelHeight = pos.y - curAbsHeight; if (lastColWarningType == 2) { const float3 dir = lastColWarning->midPos - owner->midPos; const float3 sdir = lastColWarning->speed - speed; if (speed.dot(dir + sdir * 20.0f) < 0.0f) { if (lastColWarning->midPos.y > owner->pos.y) { wh -= 30.0f; } else { wh += 50.0f; } } } if (curRelHeight < wh) { ws = altitudeRate; if ((speed.y > 0.0001f) && (((wh - curRelHeight) / speed.y) * accRate * 1.5f) < speed.y) { ws = 0.0f; } } else { ws = -altitudeRate; if ((speed.y < -0.0001f) && (((wh - curRelHeight) / speed.y) * accRate * 0.7f) < -speed.y) { ws = 0.0f; } } if (!owner->beingBuilt) { if (math::fabs(wh - curRelHeight) > 2.0f) { if (speed.y > ws) { speed.y = std::max(ws, speed.y - accRate * 1.5f); } else { // accelerate upward faster if close to ground speed.y = std::min(ws, speed.y + accRate * ((curRelHeight < 20.0f)? 2.0f: 0.7f)); } } else { speed.y *= 0.95; } } if (modInfo.allowAircraftToLeaveMap || (pos + speed).IsInBounds()) { owner->Move3D(speed, true); } }
void CHoverAirMoveType::UpdateFlying() { const float3& pos = owner->pos; const float3& speed = owner->speed; // Direction to where we would like to be float3 goalVec = goalPos - pos; owner->restTime = 0; // don't change direction for waypoints we just flew over and missed slightly if (flyState != FLY_LANDING && owner->commandAI->HasMoreMoveCommands()) { float3 goalDir = goalVec; if ((goalDir != ZeroVector) && (goalDir.UnsafeANormalize()).SqDistance(goalVec) < 1.0f) { goalVec = owner->frontdir; } } const float goalDistSq2D = goalVec.SqLength2D(); const float gHeight = UseSmoothMesh()? std::max(smoothGround->GetHeight(pos.x, pos.z), ground->GetApproximateHeight(pos.x, pos.z)): ground->GetHeightAboveWater(pos.x, pos.z); const bool closeToGoal = (flyState == FLY_ATTACKING)? (goalDistSq2D < ( 400.0f)): (goalDistSq2D < (maxDrift * maxDrift)) && (math::fabs(gHeight - pos.y + wantedHeight) < maxDrift); if (closeToGoal) { switch (flyState) { case FLY_CRUISING: { const bool hasMoreMoveCmds = owner->commandAI->HasMoreMoveCommands(); const bool noland = dontLand || !autoLand || hasMoreMoveCmds; // NOTE: should CMD_LOAD_ONTO be here? const bool trans = (dynamic_cast<CTransportUnit*>(owner) != NULL); const bool hasLoadCmds = trans && !owner->commandAI->commandQue.empty() && (owner->commandAI->commandQue.front().GetID() == CMD_LOAD_ONTO || owner->commandAI->commandQue.front().GetID() == CMD_LOAD_UNITS); // [?] transport aircraft need some time to detect that they can pickup const bool canLoad = trans && (++waitCounter < ((GAME_SPEED << 1) - 5)); if (noland || (canLoad && hasLoadCmds)) { if (trans) { wantedSpeed = ZeroVector; SetState(AIRCRAFT_HOVERING); if (waitCounter > (GAME_SPEED << 1)) { wantedHeight = orgWantedHeight; } } else { wantedSpeed = ZeroVector; if (!hasMoreMoveCmds) { wantToStop = true; SetState(AIRCRAFT_HOVERING); } } } else { wantedHeight = orgWantedHeight; SetState(AIRCRAFT_LANDING); } return; } case FLY_CIRCLING: // break; if ((++waitCounter) > ((GAME_SPEED * 3) + 10)) { if (airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } relPos.y = 0.0f; relPos.Normalize(); static CMatrix44f rot(0.0f,fastmath::PI/4.0f,0.0f); float3 newPos = rot.Mul(relPos); // Make sure the point is on the circle newPos = newPos * goalDistance; // Go there in a straight line goalPos = circlingPos + newPos; } waitCounter = 0; } break; case FLY_ATTACKING: { if (airStrafe) { float3 relPos = pos - circlingPos; if (relPos.x < 0.0001f && relPos.x > -0.0001f) { relPos.x = 0.0001f; } relPos.y = 0; relPos.Normalize(); CMatrix44f rot; if (gs->randFloat() > 0.5f) { rot.RotateY(0.6f + gs->randFloat() * 0.6f); } else { rot.RotateY(-(0.6f + gs->randFloat() * 0.6f)); } float3 newPos = rot.Mul(relPos); newPos = newPos * goalDistance; // Go there in a straight line goalPos = circlingPos + newPos; } break; } case FLY_LANDING: { break; } } } // not there yet, so keep going goalVec.y = 0.0f; // if we are close to our goal and not in attack mode, // we should go slow enough to be able to break in time const float goalDist = goalVec.Length() + 0.1f; const float approachSpeed = (flyState != FLY_ATTACKING && goalDist < brakeDistance)? (goalDist / (speed.Length2D() + 0.01f) * decRate): maxSpeed; wantedSpeed = (goalVec / goalDist) * approachSpeed; UpdateAirPhysics(); // Point toward goal or forward - unless we just passed it to get to another goal if ((flyState == FLY_ATTACKING) || (flyState == FLY_CIRCLING)) { goalVec = circlingPos - pos; } else { const bool b0 = (flyState != FLY_LANDING && (owner->commandAI->HasMoreMoveCommands())); const bool b1 = (goalDist < 120.0f) && ((goalVec.SafeNormalize()).SqDistance(goalVec) > 1.0f); if (b0 && b1) { goalVec = owner->frontdir; } else { goalVec = goalPos - pos; } } if (goalVec.SqLength2D() > 1.0f) { wantedHeading = GetHeadingFromVector(goalVec.x, goalVec.z); } }
void CHoverAirMoveType::UpdateAirPhysics() { const float3& pos = owner->pos; const float4& spd = owner->speed; // copy vertical speed const float yspeed = spd.y; if (((gs->frameNum + owner->id) & 3) == 0) { CheckForCollision(); } owner->SetVelocity(spd * XZVector); const float3 deltaSpeed = wantedSpeed - spd; const float deltaDotSpeed = deltaSpeed.dot(spd); const float deltaSpeedSq = deltaSpeed.SqLength(); if (deltaDotSpeed >= 0.0f) { // accelerate if (deltaSpeedSq < Square(accRate)) { owner->SetVelocity(wantedSpeed); } else { if (deltaSpeedSq > 0.0f) { owner->SetVelocity(spd + (deltaSpeed / math::sqrt(deltaSpeedSq) * accRate)); } } } else { // deccelerate if (deltaSpeedSq < Square(decRate)) { owner->SetVelocity(wantedSpeed); } else { if (deltaSpeedSq > 0.0f) { owner->SetVelocity(spd + (deltaSpeed / math::sqrt(deltaSpeedSq) * decRate)); } } } // absolute and relative ground height at (pos.x, pos.z) // if this aircraft uses the smoothmesh, these values are // calculated with respect to that (for changing vertical // speed, but not for ground collision) float curAbsHeight = owner->unitDef->canSubmerge? ground->GetHeightReal(pos.x, pos.z): ground->GetHeightAboveWater(pos.x, pos.z); float curRelHeight = 0.0f; float wh = wantedHeight; // wanted RELATIVE height (altitude) float ws = 0.0f; // wanted vertical speed // always stay above the actual terrain (therefore either the value of // <midPos.y - radius> or pos.y must never become smaller than the real // ground height) // note: unlike StrafeAirMoveType, UpdateTakeoff and UpdateLanding call // UpdateAirPhysics() so we ignore terrain while we are in those states if (modInfo.allowAircraftToHitGround) { const bool groundContact = (curAbsHeight > (owner->midPos.y - owner->radius)); const bool handleContact = (aircraftState != AIRCRAFT_LANDED && aircraftState != AIRCRAFT_TAKEOFF && padStatus == PAD_STATUS_FLYING); if (groundContact && handleContact) { owner->Move(UpVector * (curAbsHeight - (owner->midPos.y - owner->radius) + 0.01f), true); } } if (UseSmoothMesh()) { curAbsHeight = owner->unitDef->canSubmerge? smoothGround->GetHeight(pos.x, pos.z): smoothGround->GetHeightAboveWater(pos.x, pos.z); } // restore original vertical speed owner->SetVelocity((spd * XZVector) + (UpVector * yspeed)); if (lastColWarningType == 2) { const float3 dir = lastColWarning->midPos - owner->midPos; const float3 sdir = lastColWarning->speed - spd; if (spd.dot(dir + sdir * 20.0f) < 0.0f) { if (lastColWarning->midPos.y > owner->pos.y) { wh -= 30.0f; } else { wh += 50.0f; } } } curRelHeight = pos.y - curAbsHeight; if (curRelHeight < wh) { ws = altitudeRate; if ((spd.y > 0.0001f) && (((wh - curRelHeight) / spd.y) * accRate * 1.5f) < spd.y) { ws = 0.0f; } } else { ws = -altitudeRate; if ((spd.y < -0.0001f) && (((wh - curRelHeight) / spd.y) * accRate * 0.7f) < -spd.y) { ws = 0.0f; } } if (!owner->beingBuilt) { if (math::fabs(wh - curRelHeight) > 2.0f) { if (spd.y > ws) { owner->SetVelocity((spd * XZVector) + (UpVector * std::max(ws, spd.y - accRate * 1.5f))); } else { // accelerate upward faster if close to ground owner->SetVelocity((spd * XZVector) + (UpVector * std::min(ws, spd.y + accRate * ((curRelHeight < 20.0f)? 2.0f: 0.7f)))); } } else { owner->SetVelocity((spd * XZVector) + (UpVector * spd.y * 0.95f)); } } owner->SetSpeed(spd); if (modInfo.allowAircraftToLeaveMap || (pos + spd).IsInBounds()) { owner->Move(spd, true); } }
void CAirMoveType::UpdateFlying(float wantedHeight, float engine) { float3& pos = owner->pos; SyncedFloat3& rightdir = owner->rightdir; SyncedFloat3& frontdir = owner->frontdir; SyncedFloat3& updir = owner->updir; float3& speed = owner->speed; float speedf = speed.Length(); float3 goalDir = (goalPos - pos); float goalLength = std::max(0.001f, goalDir.Length2D()); goalDir /= goalLength; float3 adjustedGoalDir = float3(goalPos.x, 0, goalPos.z) - float3(pos.x, 0, pos.z); adjustedGoalDir.SafeANormalize(); float aileron = 0.0f; float rudder = 0.0f; float elevator = 0.0f; // do not check if the plane can be submerged here, since it'll cause // ground collisions later on float gHeight; if (UseSmoothMesh()) gHeight = std::max(smoothGround->GetHeight(pos.x, pos.z), ground->GetApproximateHeight(pos.x, pos.z)); else gHeight = ground->GetHeight(pos.x, pos.z); if (!((gs->frameNum + owner->id) & 3)) CheckForCollision(); float otherThreat = 0.0f; float3 otherDir; if (lastColWarning) { float3 otherDif = lastColWarning->pos - pos; float otherLength = otherDif.Length(); otherDir = (otherLength > 0.0f)? (otherDif / otherLength): ZeroVector; otherThreat = (otherLength > 0.0f)? std::max(1200.0f, goalLength) / otherLength * 0.036f: 0.0f; } float goalDotRight = rightdir.dot(adjustedGoalDir); float goalDotFront = adjustedGoalDir.dot(frontdir) * 0.5f + 0.501f; if (goalDotFront > 0.01f) { goalDotRight /= goalDotFront; } if (adjustedGoalDir.dot(frontdir) < -0.1f && goalLength < turnRadius && (!owner->directControl || owner->directControl->mouse2)) goalDotRight = -goalDotRight; if (lastColWarning) { goalDotRight -= otherDir.dot(rightdir) * otherThreat; } // roll if (speedf > 1.5f && pos.y + speed.y * 10 > gHeight + wantedHeight * 0.6f) { float goalBankDif = goalDotRight + rightdir.y * 0.5f; if (goalBankDif > maxAileron*speedf * 4 && rightdir.y > -maxBank) { aileron = 1; } else if (goalBankDif < -maxAileron * speedf * 4 && rightdir.y < maxBank) { aileron = -1; } else { if (fabs(rightdir.y) < maxBank) { aileron = goalBankDif / (maxAileron * speedf * 4); } else { if (rightdir.y < 0.0f && goalBankDif < 0.0f) { aileron = -1; } else if (rightdir.y > 0.0f && goalBankDif > 0.0f) { aileron = 1; } } } } else { if (rightdir.y > 0.01f) { aileron = 1; } else if (rightdir.y < -0.01f) { aileron = -1; } } // yaw if (pos.y > gHeight + 15) { if (goalDotRight < -maxRudder * speedf * 2) { rudder = -1; } else if (goalDotRight > maxRudder * speedf * 2) { rudder = 1; } else { if (speedf > 0.0f && maxRudder > 0.0f) { rudder = goalDotRight / (maxRudder * speedf * 2); } else { rudder = 0; } } } // pitch if (speedf > 0.8f) { bool notColliding = true; if (lastColWarningType == 2) { const float3 dir = lastColWarning->midPos - owner->midPos; const float3 sdir = lastColWarning->speed - owner->speed; if (frontdir.dot(dir + sdir * 20) < 0) { elevator = updir.dot(dir) > 0 ? -1 : 1; notColliding = false; } } if (notColliding) { float gHeight2 = ground->GetHeight(pos.x + speed.x * 40, pos.z + speed.z * 40); float hdif = std::max(gHeight, gHeight2) + wantedHeight - pos.y - frontdir.y * speedf * 20; if (hdif < -(maxElevator * speedf * speedf * 20) && frontdir.y > -maxPitch) { elevator = -1; } else if (hdif > (maxElevator * speedf * speedf * 20) && frontdir.y < maxPitch) { elevator = 1; } else { if (fabs(frontdir.y) < maxPitch) elevator = hdif / (maxElevator * speedf * speedf * 20); } } } else { if (frontdir.y < -0.1f) { elevator = 1; } else if (frontdir.y > 0.15f) { elevator = -1; } } UpdateAirPhysics(rudder, aileron, elevator, engine, owner->frontdir); }
void CHoverAirMoveType::UpdateAirPhysics() { const float3& pos = owner->pos; float3& speed = owner->speed; if (!((gs->frameNum + owner->id) & 3)) { CheckForCollision(); } const float yspeed = speed.y; speed.y = 0.0f; const float3 deltaSpeed = wantedSpeed - speed; const float deltaDotSpeed = (speed != ZeroVector)? deltaSpeed.dot(speed): 1.0f; if (deltaDotSpeed == 0.0f) { // we have the wanted speed } else if (deltaDotSpeed > 0.0f) { // accelerate const float sqdl = deltaSpeed.SqLength(); if (sqdl < Square(accRate)) { speed = wantedSpeed; } else { speed += (deltaSpeed / math::sqrt(sqdl) * accRate); } } else { // break const float sqdl = deltaSpeed.SqLength(); if (sqdl < Square(decRate)) { speed = wantedSpeed; } else { speed += (deltaSpeed / math::sqrt(sqdl) * decRate); } } float minHeight = 0.0f; // absolute ground height at (pos.x, pos.z) float curHeight = 0.0f; // relative ground height at (pos.x, pos.z) == pos.y - minHeight (altitude) float wh = wantedHeight; // wanted RELATIVE height (altitude) float ws = 0.0f; // wanted vertical speed if (UseSmoothMesh()) { minHeight = owner->unitDef->canSubmerge? smoothGround->GetHeight(pos.x, pos.z): smoothGround->GetHeightAboveWater(pos.x, pos.z); } else { minHeight = owner->unitDef->canSubmerge? ground->GetHeightReal(pos.x, pos.z): ground->GetHeightAboveWater(pos.x, pos.z); } // [?] aircraft should never be able to end up below terrain // if (pos.y < minHeight) // owner->Move1D(std::min(minHeight - pos.y, altitudeRate), 1, true); speed.y = yspeed; curHeight = pos.y - minHeight; if (curHeight < 4.0f) { speed.x *= 0.95f; speed.z *= 0.95f; } if (lastColWarningType == 2) { const float3 dir = lastColWarning->midPos - owner->midPos; const float3 sdir = lastColWarning->speed - speed; if (speed.dot(dir + sdir * 20.0f) < 0.0f) { if (lastColWarning->midPos.y > owner->pos.y) { wh -= 30.0f; } else { wh += 50.0f; } } } if (curHeight < wh) { ws = altitudeRate; if ((speed.y > 0.0001f) && (((wh - curHeight) / speed.y) * accRate * 1.5f) < speed.y) { ws = 0.0f; } } else { ws = -altitudeRate; if ((speed.y < -0.0001f) && (((wh - curHeight) / speed.y) * accRate * 0.7f) < -speed.y) { ws = 0.0f; } } if (!owner->beingBuilt) { if (math::fabs(wh - curHeight) > 2.0f) { if (speed.y > ws) { speed.y = std::max(ws, speed.y - accRate * 1.5f); } else { // accelerate upward faster if close to ground speed.y = std::min(ws, speed.y + accRate * ((curHeight < 20.0f)? 2.0f: 0.7f)); } } else { speed.y *= 0.95; } } if (modInfo.allowAircraftToLeaveMap || (pos + speed).IsInBounds()) { owner->Move3D(speed, true); } }