/// Returns true if indicated position is a suitable landing spot bool CHoverAirMoveType::CanLandAt(const float3& pos) const { if (forceHeading) return true; if (!CanLand(false)) return false; if (!pos.IsInBounds()) return false; const UnitDef* ud = owner->unitDef; const float gah = CGround::GetApproximateHeight(pos.x, pos.z); if ((gah < 0.0f) && !(ud->floatOnWater || ud->canSubmerge)) { return false; } const int2 mp = owner->GetMapPos(pos); for (int z = mp.y; z < mp.y + owner->zsize; z++) { for (int x = mp.x; x < mp.x + owner->xsize; x++) { if (groundBlockingObjectMap->GroundBlocked(x, z, owner)) { return false; } } } return true; }
void Game_Vehicle::Update() { Game_Character::Update(); Game_Character::UpdateSprite(); SyncWithPlayer(); if (type == Airship) { if (IsAscending()) { data.remaining_ascent -= 8; if (!IsAscending()) walk_animation = true; } else if (IsDescending()) { data.remaining_descent -= 8; if (!IsDescending()) { if (CanLand()) { SetLayer(RPG::EventPage::Layers_same); driving = false; data.flying = false; walk_animation = false; pattern = 1; } else { // Can't land here, ascend again data.remaining_ascent = SCREEN_TILE_WIDTH; } } } } }
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::ExecuteStop() { wantToStop = false; wantedSpeed = ZeroVector; switch (aircraftState) { case AIRCRAFT_TAKEOFF: { if (CanLand()) { SetState(AIRCRAFT_LANDING); // trick to land directly waitCounter = GAME_SPEED; break; } } // fall through case AIRCRAFT_FLYING: { goalPos = owner->pos; if (CanLand()) { SetState(AIRCRAFT_LANDING); } else { SetState(AIRCRAFT_HOVERING); } } break; case AIRCRAFT_LANDING: {} break; case AIRCRAFT_LANDED: {} break; case AIRCRAFT_CRASHING: {} break; case AIRCRAFT_HOVERING: { if (CanLand()) { // land immediately SetState(AIRCRAFT_LANDING); waitCounter = GAME_SPEED; } } break; } }
void CHoverAirMoveType::SetAllowLanding(bool allowLanding) { dontLand = !allowLanding; if (CanLand(false)) return; if (aircraftState != AIRCRAFT_LANDED && aircraftState != AIRCRAFT_LANDING) return; // do not start hovering if still (un)loading a unit if (forceHeading) return; SetState(AIRCRAFT_HOVERING); }
void CHoverAirMoveType::SetAllowLanding(bool allowLanding) { dontLand = !allowLanding; if (CanLand()) return; if (aircraftState != AIRCRAFT_LANDED && aircraftState != AIRCRAFT_LANDING) return; // do not start hovering if still loading units if (loadingUnits) return; SetState(AIRCRAFT_HOVERING); }
void Game_Vehicle::Update(bool process_movement) { if (!process_movement) { return; } if (IsAboard()) { SyncWithPlayer(); } else { Game_Character::UpdateMovement(); } if (type == Airship) { if (IsStopping()) { if (IsAscending()) { data()->remaining_ascent = data()->remaining_ascent - 8; } else if (IsDescending()) { data()->remaining_descent = data()->remaining_descent - 8; if (!IsDescending()) { if (CanLand()) { Main_Data::game_player->UnboardingFinished(); SetFlying(false); Main_Data::game_player->SetFlying(false); } else { // Can't land here, ascend again data()->remaining_ascent = SCREEN_TILE_SIZE; } } } } } if (type == Airship) { UpdateAnimationAirship(); } else { UpdateAnimationShip(); } }
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); } }