void CHoverAirMoveType::ExecuteStop() { wantToStop = false; wantedSpeed = ZeroVector; SetGoal(owner->pos); ClearLandingPos(); switch (aircraftState) { case AIRCRAFT_TAKEOFF: { if (CanLand(IsUnitBusy(owner))) { SetState(AIRCRAFT_LANDING); // trick to land directly waitCounter = GAME_SPEED; break; } } // fall through case AIRCRAFT_FLYING: { if (CanLand(IsUnitBusy(owner))) { SetState(AIRCRAFT_LANDING); } else { SetState(AIRCRAFT_HOVERING); } } break; case AIRCRAFT_LANDING: { if (!CanLand(IsUnitBusy(owner))) SetState(AIRCRAFT_HOVERING); } break; case AIRCRAFT_LANDED: {} break; case AIRCRAFT_CRASHING: {} break; case AIRCRAFT_HOVERING: { if (CanLand(IsUnitBusy(owner))) { // land immediately, otherwise keep hovering SetState(AIRCRAFT_LANDING); waitCounter = GAME_SPEED; } } break; } }
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); } }