Position AGPosition::compute2dPosition() const { // P = From + pos*(To - From) = pos*To + (1-pos)*From Position From = street->edge->getFromNode()->getPosition(); Position To = street->edge->getToNode()->getPosition(); Position position2d(To); position2d.sub(From); position2d.mul(position / street->getLength()); position2d.add(From); return position2d; }
void VehicleAi::OnTick() { if (!game_->GetVehicle() || !game_->GetVehicle()->IsLoaded() || !game_->GetLevel() || !game_->GetLevel()->IsLoaded() || game_->GetFlybyMode() != Game::kFlybyInactive) { return; } const cure::TimeManager* _time = GetManager()->GetGameManager()->GetTimeManager(); const int mode_run_delta_frame_count = _time->GetCurrentPhysicsFrameDelta(mode_start_frame_); const float mode_run_time = _time->ConvertPhysicsFramesToSeconds(mode_run_delta_frame_count); const float aim_distance = AIM_DISTANCE; float strength = 1.0f; const vec3 _position = game_->GetVehicle()->GetPosition(); const vec3 _velocity = game_->GetVehicle()->GetVelocity(); switch (mode_) { case kModeFindBestPath: case kModeFindPathOffElevator: { float start_time = 0.5f; if (active_path_ != -1) { // Synchronize all paths. Spline* _path = game_->GetLevel()->QueryPath()->GetPath(active_path_); start_time = _path->GetCurrentInterpolationTime(); active_path_ = -1; } log_.Headlinef("Trying to find new path... starting iterating from %.2f.", start_time); vec3 elevator_direction; if (mode_ == kModeFindPathOffElevator) { game_->GetVehicle()->SetEnginePower(0, 0); game_->GetVehicle()->SetEnginePower(2, -strength); // Negative = use full brakes, not only hand brake. const cure::Elevator* _nearest_elevator; const vec3 elevator_position = GetClosestElevatorPosition(_position, _nearest_elevator); if (elevator_position.GetDistanceSquared(_position) > ELEVATOR_TOO_CLOSE_DISTANCE*ELEVATOR_TOO_CLOSE_DISTANCE) { log_.AHeadline("Fell off elevator while looking for get-off route. Looking for somewhere else to go."); SetMode(kModeFindBestPath); return; } elevator_direction = _nearest_elevator->GetVelocity().GetNormalized(0.5f); } float best_path_distance = 1000000; std::vector<PathIndexLikeliness> relevant_paths; bool lifting_towards_goal = false; const int path_count = game_->GetLevel()->QueryPath()->GetPathCount(); for (int x = 0; x < path_count; ++x) { bool current_lifting_towards_goal = false; Spline* _path = game_->GetLevel()->QueryPath()->GetPath(x); _path->GotoAbsoluteTime(start_time); float _likeliness = 1; const float nearest_distance = GetClosestPathDistance(_position, x, &_likeliness)/SCALE_FACTOR/2; log_.Infof(" - Path %2i is %2.2f units away.", x, nearest_distance); if (mode_ == kModeFindPathOffElevator) { if (_path->GetCurrentInterpolationTime() > 0.7f) { // This path is probably the one I used to get ON the elevator (or one // just like it from another direction), we're not using that! log_.AInfo(" (Not relevant, too close to path end.)"); continue; } else { const float towards_distance = GetClosestPathDistance(_position+elevator_direction, x)/SCALE_FACTOR/2; if (towards_distance < nearest_distance) { current_lifting_towards_goal = true; if (!lifting_towards_goal) { lifting_towards_goal = true; best_path_distance = 1000000; } } } } if (!current_lifting_towards_goal && lifting_towards_goal) { // This elevator isn't heading in the right direction, but at least one other is. continue; } PathIndexLikeliness pl; pl.path_index_ = x; pl.likeliness_ = _likeliness; pl.distance_ = nearest_distance; relevant_paths.push_back(pl); if (nearest_distance < best_path_distance) { best_path_distance = nearest_distance; } } // Sort out those that are too far away. float total_likeliness = 0; std::vector<PathIndexLikeliness>::iterator x; for (x = relevant_paths.begin(); x != relevant_paths.end();) { if (x->distance_ < best_path_distance+2.0f) { total_likeliness += x->likeliness_; ++x; } else { x = relevant_paths.erase(x); } } if (mode_ == kModeFindPathOffElevator) { if (best_path_distance > 5 || relevant_paths.size() != 1) { if (relevant_paths.size() == 1) { // Point wheels in the right direction for us to get off safely. Spline* _path = game_->GetLevel()->QueryPath()->GetPath(relevant_paths[0].path_index_); const vec3 _direction = game_->GetVehicle()->GetOrientation() * vec3(0,1,0); const vec3 wanted_direction = _path->GetValue() - _position; const float angle = LEPRA_XY_ANGLE(wanted_direction, _direction); game_->GetVehicle()->SetEnginePower(1, angle*0.5f); } log_.Headlinef("On elevator: too long distance to path %.1f, or too many paths %u.", best_path_distance, relevant_paths.size()); if (best_path_distance > 15) { const cure::Elevator* _nearest_elevator; const vec3 nearest_lift_position = GetClosestElevatorPosition(_position, _nearest_elevator); if (nearest_lift_position.GetDistanceSquared(_position) > ELEVATOR_TOO_CLOSE_DISTANCE*ELEVATOR_TOO_CLOSE_DISTANCE) { // DUCK!!! We fell off! log_.AHeadline("Was on elevator: I'm far from the elevator, so must've fallen off!"); SetMode(kModeFindBestPath); } } if (mode_run_time >= 20) { log_.AHeadline("On elevator: been here too long, getting off!"); SetMode(kModeFindBestPath); } return; } log_.Headlinef("Getting off elevator: distance to path %.1f.", best_path_distance); } deb_assert(!relevant_paths.empty()); if (relevant_paths.empty()) { return; } const float picked_likeliness = Random::Uniform(0.0f, total_likeliness); total_likeliness = 0; for (x = relevant_paths.begin(); x != relevant_paths.end(); ++x) { const float next_likeliness = total_likeliness + x->likeliness_; if (picked_likeliness >= total_likeliness && picked_likeliness <= next_likeliness) { active_path_ = x->path_index_; break; } total_likeliness = next_likeliness; } if (active_path_ < 0) { active_path_ = relevant_paths[Random::GetRandomNumber() % relevant_paths.size()].path_index_; } Spline* _path = game_->GetLevel()->QueryPath()->GetPath(active_path_); const float wanted_distance = aim_distance; float step = wanted_distance * _path->GetDistanceNormal(); if (step + _path->GetCurrentInterpolationTime() > 1) { step = 1 - _path->GetCurrentInterpolationTime(); } _path->StepInterpolation(step); // Fetch ending position. const float t = _path->GetCurrentInterpolationTime(); _path->GotoAbsoluteTime(END_PATH_TIME); elevator_get_on_position_ = _path->GetValue(); _path->GotoAbsoluteTime(t); log_.Headlinef("Picked path %i (%i pickable."), active_path_, relevant_paths.size()); if (mode_ == kModeFindPathOffElevator) { SetMode(kModeGetOffElevator); } else { SetMode(kModeHeadingBackOnTrack); } } break; case kModeHeadingBackOnTrack: { if (mode_run_delta_frame_count%5 == 2) { const float velocity_scale_factor = std::min(1.0f, _velocity.GetLength() / 2.5f); Spline* _path = game_->GetLevel()->QueryPath()->GetPath(active_path_); const float current_time = _path->GetCurrentInterpolationTime(); const float nearest_path_distance = GetClosestPathDistance(_position, active_path_, 0, 1); if (nearest_path_distance > 3.0f) { // First verify that we haven't ended up under path somehow. We do that by checking // steepness, since pure Z-distance may be big when going over ditches. const vec3 path_position = _path->GetValue(); const float steepness = (path_position.z - _position.z) / nearest_path_distance; //log_.Infof("Checking steepness, nearest path distance is %.3f, steepness is %.3f.", nearest_path_distance, steepness); if (steepness > 0.6f) { log_.Infof("Searching for new, better path, we seem to have ended up under the path. Beneath a bridge perhaps? Nearest path is %.2f, steepness is %.2f.", nearest_path_distance, steepness); SetMode(kModeFindBestPath); return; } } _path->GotoAbsoluteTime(current_time); if (nearest_path_distance < SCALE_FACTOR * OFF_COURSE_DISTANCE * velocity_scale_factor) { // We were able to return to normal, keep on running. SetMode(kModeNormal); return; } /*else if (nearest_path_distance > SCALE_FACTOR * OFF_COURSE_DISTANCE * velocity_scale_factor * 5) { // We're far off, perhaps we fell down from a plateu. active_path_ = -1; SetMode(kModeFindBestPath); return; }*/ else if (mode_run_time > 7.0f) { SetMode(kModeFindBestPath); return; } } } // TRICKY: fall through. case kModeNormal: case kModeGetOnElevator: case kModeGetOffElevator: { if (mode_ == kModeGetOnElevator) { if (mode_run_time > 4.5) { log_.Headlinef("Something presumably hinders me getting on the elevator, back square one. (mode run time=%f"), mode_run_time); SetMode(kModeFindBestPath); return; } const cure::Elevator* _nearest_elevator; const vec3 nearest_lift_position = GetClosestElevatorPosition(elevator_get_on_position_, _nearest_elevator); if (nearest_lift_position.z > _position.z+0.5f) { log_.AHeadline("Couldn't get on in time, going back to waiting."); SetMode(kModeWaitingForElevator); return; } } if (mode_ != kModeHeadingBackOnTrack && mode_ != kModeGetOnElevator && mode_run_delta_frame_count%20 == 19) { const float _distance = GetClosestPathDistance(_position); if (_distance > SCALE_FACTOR * TOTALLY_OFF_COURSE_DISTANCE) { log_.AHeadline("Fell off something. Trying some new path."); SetMode(kModeFindBestPath); return; } const float velocity_scale_factor = ((mode_ == kModeNormal)? 1.0f : 3.0f) * Math::Clamp(_velocity.GetLength() / 2.5f, 0.3f, 1.0f); if (_distance > SCALE_FACTOR * OFF_COURSE_DISTANCE * velocity_scale_factor) { log_.AHeadline("Going about my way, but got offside somehow. Heading back."); SetMode(kModeHeadingBackOnTrack); return; } } Spline* _path = game_->GetLevel()->QueryPath()->GetPath(active_path_); vec3 target = _path->GetValue(); // Check if vehicle stopped. That would mean either crashed against something or too steep hill. if (mode_run_delta_frame_count%7 == 4 && game_->GetVehicle()->GetHealth() > 0) { if (QueryVehicleHindered(_time, _velocity)) { const vec3 _direction = game_->GetVehicle()->GetOrientation() * vec3(0,1,0); const vec3 wanted_direction = target-_position; const float forward_angle = LEPRA_XY_ANGLE(wanted_direction, _direction); // Amplify angle to be either full left or full right. const float angle = (forward_angle < 0)? -1.0f : 1.0f; game_->GetVehicle()->SetEnginePower(1, -angle); SetMode(kModeBackingUp); return; } } // Are we heading towards an elevator? if (mode_ != kModeGetOnElevator && mode_ != kModeGetOffElevator && _path->GetType() == "to_elevator") { if (_path->GetDistanceLeft() <= ELEVATOR_WAIT_DISTANCE) { if (elevator_get_on_position_.GetDistanceSquared(_position) <= ELEVATOR_WAIT_DISTANCE*ELEVATOR_WAIT_DISTANCE) { log_.AHeadline("Normal mode close to end of path to elevator, changing mode."); SetMode(kModeWaitingForElevator); return; } } } // Did we just pass (fly by?) the goal? if (mode_run_delta_frame_count%3 == 0) { const vec3 goal_direction = game_->GetGoal()->GetPosition() - _position; if (::fabs(goal_direction.z) < 2 && goal_direction.GetLengthSquared() < ELEVATOR_FAR_DISTANCE*ELEVATOR_FAR_DISTANCE && _velocity.GetLengthSquared() < 6*6) { const vec3 vehicle_direction = game_->GetVehicle()->GetOrientation() * vec3(0,1,0); const float delta_angle = ::fabs(LEPRA_XY_ANGLE(goal_direction, vehicle_direction)); if (delta_angle >= PIF-PIF/4 && delta_angle <= PIF+PIF/4) { log_.AHeadline("Passed goal, it's right behind me!"); SetMode(kModeBackingUpToGoal); return; } } } // Step target (aim) ahead. { const float actual_distance2 = target.GetDistanceSquared(_position); const float max_aim_factor = (mode_ == kModeGetOffElevator)? 1.0f : 1.5f; const float wanted_distance = aim_distance * Math::Clamp(_velocity.GetLength() / 2.5f, 0.5f, max_aim_factor); if (actual_distance2 < wanted_distance*wanted_distance) { const float move_ahead = wanted_distance*1.1f - ::sqrt(actual_distance2); _path->StepInterpolation(move_ahead * _path->GetDistanceNormal()); log_volatile(log_.Debugf("Stepping %f (=%f m from %f."), move_ahead*_path->GetDistanceNormal(), move_ahead, _path->GetCurrentInterpolationTime())); } // Check if we're there yet. const float t = _path->GetCurrentInterpolationTime(); _path->GotoAbsoluteTime(1.0f); const float target_distance = (mode_ == kModeGetOnElevator)? ON_ELEVATOR_DISTANCE : ON_GOAL_DISTANCE + game_->GetVehicle()->GetForwardSpeed()/4; if (IsCloseToTarget(_position, target_distance)) { const bool towards_elevator = (_path->GetType() == "to_elevator"); if (towards_elevator) { if (mode_ == kModeGetOnElevator) { SetMode(kModeOnElevator); return; } else if (mode_ != kModeGetOffElevator) { // We got off track somewhere, try to shape up! log_.AHeadline("Normal mode target wrapped on our way to an elevator, changing mode."); SetMode(kModeWaitingForElevator); return; } } else { SetMode(kModeStoppingAtGoal); return; } } _path->GotoAbsoluteTime(t); target = _path->GetValue(); } const float get_off_delay_time = 0.4f; if (!(mode_ == kModeGetOffElevator && mode_run_time < get_off_delay_time)) { // Move forward. game_->GetVehicle()->SetEnginePower(0, +strength); game_->GetVehicle()->SetEnginePower(2, 0); } // Steer. const vec3 _direction = game_->GetVehicle()->GetOrientation() * vec3(0,1,0); const vec3 wanted_direction = target-_position; float angle = LEPRA_XY_ANGLE(wanted_direction, _direction); if (mode_ == kModeGetOffElevator) { // Aborting too early might cause us to stop, waiting for the next ride in mid-air. const float get_off_distance = GetClosestElevatorRadius() + ELEVATOR_GOT_OFF_EXTRA_DISTANCE; vec2 elevator_get_off2d(elevator_get_off_position_.x, elevator_get_off_position_.y); vec2 position2d(_position.x, _position.y); log_.Infof("ElevatorGetOff (%f;%f, pos (%f;%f)"), elevator_get_off2d.x, elevator_get_off2d.y, position2d.x, position2d.y); if (elevator_get_off2d.GetDistanceSquared(position2d) > get_off_distance*get_off_distance) { SetMode(kModeNormal); } angle *= 2; // Make steering more powerful while getting off. } game_->GetVehicle()->SetEnginePower(1, +angle); last_average_angle_ = Math::Lerp(last_average_angle_, angle, 0.5f); // Check if we need to slow down. const float high_speed = SCALE_FACTOR * 2.7f; const float abs_angle = ::fabs(angle); if (_velocity.GetLengthSquared() > high_speed*high_speed) { if (abs_angle > 0.2f) { float factor = 0.10f; game_->GetVehicle()->SetEnginePower(2, abs_angle*factor + _velocity.GetLength()*factor*0.1f); } else if (_path->GetCurrentInterpolationTime() >= DOUBLE_OFF_END_PATH_TIME && IsCloseToTarget(_position, SLOW_DOWN_DISTANCE)) { game_->GetVehicle()->SetEnginePower(2, 0.2f); } } } break; case kModeBackingUp: { // Brake or move backward. const bool is_moving_forward = (game_->GetVehicle()->GetForwardSpeed() > 0.1f*SCALE_FACTOR); game_->GetVehicle()->SetEnginePower(0, is_moving_forward? 0.0f : -strength); game_->GetVehicle()->SetEnginePower(2, is_moving_forward? strength : 0.0f); const float back_time = 1.7f; if (!is_moving_forward && mode_run_time > back_time) { SetMode(kModeHeadingBackOnTrack); return; } } break; case kModeBackingUpToGoal: { vec3 wanted_direction = game_->GetGoal()->GetPosition() - _position; const float distance2 = wanted_direction.GetLengthSquared(); if (distance2 <= ON_GOAL_DISTANCE*ON_GOAL_DISTANCE) { Spline* _path = game_->GetLevel()->QueryPath()->GetPath(active_path_); _path->GotoAbsoluteTime(END_PATH_TIME); SetMode(kModeStoppingAtGoal); return; } else if (distance2 >= TOTALLY_OFF_COURSE_DISTANCE*TOTALLY_OFF_COURSE_DISTANCE) { SetMode(kModeFindBestPath); return; } // Brake or move backward. const bool is_moving_forward = (game_->GetVehicle()->GetForwardSpeed() > 0.1f*SCALE_FACTOR); game_->GetVehicle()->SetEnginePower(0, is_moving_forward? 0.0f : -strength); game_->GetVehicle()->SetEnginePower(2, is_moving_forward? strength : 0.0f); // Turn steering wheel. const vec3 _direction = game_->GetVehicle()->GetOrientation() * vec3(0,1,0); float angle = LEPRA_XY_ANGLE(wanted_direction, _direction); angle += (angle < 0)? +PIF : -PIF; angle *= 3; game_->GetVehicle()->SetEnginePower(1, -angle); if (mode_run_time > 15) { log_.AHeadline("Not getting back to goal. F**k it."); SetMode(kModeRotateOnTheSpot); return; } } break; case kModeFlee: { // Pedal to the metal. game_->GetVehicle()->SetEnginePower(0, +strength); game_->GetVehicle()->SetEnginePower(1, 0); game_->GetVehicle()->SetEnginePower(2, 0); if (mode_run_time > 3.0f) { SetMode(kModeFindBestPath); return; } } break; case kModeStoppingAtGoal: case kModeAtGoal: { Spline* _path = game_->GetLevel()->QueryPath()->GetPath(active_path_); if (!IsCloseToTarget(_position, ON_GOAL_DISTANCE)) { // If either already stopped at goal, OR stopped but at the wrong spot. if (mode_ != kModeStoppingAtGoal || game_->GetVehicle()->GetForwardSpeed() < 0.5f*SCALE_FACTOR) { _path->GotoAbsoluteTime(DOUBLE_OFF_END_PATH_TIME); // Close to end, but not at end. SetMode(kModeHeadingBackOnTrack); return; } } if (mode_ != kModeAtGoal) { SetMode(kModeAtGoal); } // Brake! game_->GetVehicle()->SetEnginePower(0, 0); game_->GetVehicle()->SetEnginePower(2, -strength); // Negative = use full brakes, not only hand brake. } break; case kModeWaitingForElevator: { if (mode_run_time > 25.0f) { log_.AHeadline("Movin' on, I've waited for the elevator too long."); SetMode(kModeFlee); return; } if (::fabs(last_average_angle_) > 0.1f) { strength *= SMOOTH_BRAKING_FACTOR; // Smooth braking when turning, we can always back up if necessary. } const float elevator_distance2 = elevator_get_on_position_.GetDistanceSquared(_position); if (elevator_distance2 < ELEVATOR_TOO_CLOSE_DISTANCE*ELEVATOR_TOO_CLOSE_DISTANCE) { log_.AHeadline("Got too close to the elevator stop position, backing up."); // Back up parallel to the spline direction. const vec3 _direction = game_->GetVehicle()->GetOrientation() * vec3(0,1,0); Spline* _path = game_->GetLevel()->QueryPath()->GetPath(active_path_); const vec3 wanted_direction = _path->GetSlope(); const float angle = LEPRA_XY_ANGLE(wanted_direction, _direction); game_->GetVehicle()->SetEnginePower(1, +angle); const bool is_moving_forward = (game_->GetVehicle()->GetForwardSpeed() > 0.1f*SCALE_FACTOR); game_->GetVehicle()->SetEnginePower(0, is_moving_forward? 0.0f : -strength); game_->GetVehicle()->SetEnginePower(2, is_moving_forward? strength : 0.0f); const cure::Elevator* _nearest_elevator; vec3 _nearest_lift_position2d; float _elevator_xy_distance2_to_elevator_stop; const bool is_elevator_here = HasElevatorArrived(_nearest_elevator, _position.z, _nearest_lift_position2d, _elevator_xy_distance2_to_elevator_stop); if (is_elevator_here) { SetMode(kModeGetOnElevator); } else if (QueryVehicleHindered(_time, _velocity)) { last_average_angle_ = (Random::Uniform(0.0f, 1.0f) > 0.5f)? +2.0f : -2.0f; SetMode(kModeRotateOnTheSpot); } return; } if (::fabs(elevator_get_on_position_.z-_position.z) >= 3 || elevator_distance2 > ELEVATOR_FAR_DISTANCE*ELEVATOR_FAR_DISTANCE) { log_.AHeadline("Somehow got away from the elevator wait position, doing something else."); SetMode(kModeFindBestPath); return; } // Check that we're headed towards the elevator center. if (_velocity.GetLengthSquared() < 0.5f) { vec3 up(0, 0, 1); up = game_->GetVehicle()->GetOrientation() * up; if (up.z > 0.7f) { const vec3 _direction = game_->GetVehicle()->GetOrientation() * vec3(0,1,0); const vec3 wanted_direction = elevator_get_on_position_ - _position; const float angle = LEPRA_XY_ANGLE(wanted_direction, _direction); if (::fabs(angle) > PIF/12) { rotate_angle_ = -angle; SetMode(kModeRotateOnTheSpotWaiting); return; } } } const cure::Elevator* _nearest_elevator; vec3 _nearest_lift_position2d; float _elevator_xy_distance2_to_elevator_stop; if (HasElevatorArrived(_nearest_elevator, _position.z, _nearest_lift_position2d, _elevator_xy_distance2_to_elevator_stop)) { vec3 velocity_xy = _nearest_elevator->GetVelocity(); bool try_get_on = false; // Check if elevator is on it's way out. if (IsVertical(velocity_xy)) { try_get_on = true; } else { velocity_xy.x *= 0.1f; velocity_xy.y *= 0.1f; velocity_xy.z = 0; if (_elevator_xy_distance2_to_elevator_stop+0.1f >= elevator_get_on_position_.GetDistanceSquared(_nearest_lift_position2d+velocity_xy)) { try_get_on = true; } } if (try_get_on) { log_.AInfo("Elevator here - getting on!"); SetMode(kModeGetOnElevator); return; } else { log_.AInfo("Waiting for elevator: not getting on, since elevator is departing!"); } } game_->GetVehicle()->SetEnginePower(1, 0); // Brake! game_->GetVehicle()->SetEnginePower(0, 0); game_->GetVehicle()->SetEnginePower(2, -strength); // Negative = use full brakes, not only hand brake. } break; case kModeOnElevator: { strength *= SMOOTH_BRAKING_FACTOR; // Smooth braking, we can always back up if necessary. // Brake! game_->GetVehicle()->SetEnginePower(0, 0); game_->GetVehicle()->SetEnginePower(1, 0); game_->GetVehicle()->SetEnginePower(2, -strength); // Negative = use full brakes, not only hand brake. // Check if elevator departed. const float minimum_velocity2 = 0.5f*0.5f; if (mode_run_time > 0.7f && _velocity.GetLengthSquared() > minimum_velocity2) { const cure::Elevator* _nearest_elevator; const vec3 nearest_lift_position = GetClosestElevatorPosition(elevator_get_on_position_, _nearest_elevator); if (nearest_lift_position.z > _position.z+0.2f) { // Crap, we missed it! log_.AHeadline("Must have missed the elevator (it's not close!), waiting for it again!"); SetMode(kModeWaitingForElevator); return; } // Vehicle speed check not enouch (bouncy wheels), so check elevator speed too. vec3 elevator_velocity = _nearest_elevator->GetVelocity(); if (elevator_velocity.GetLengthSquared() > minimum_velocity2) { const bool is_horizontal = !IsVertical(elevator_velocity); const vec3 _direction = is_horizontal? elevator_velocity : game_->GetVehicle()->GetOrientation() * vec3(0,1,0); rotate_angle_ = -GetRelativeDriveOnAngle(_direction); if (::fabs(rotate_angle_) > PIF/6 || is_horizontal) { if (is_horizontal) { rotate_angle_ = (rotate_angle_ < 0)? -1.3f : +1.3f; } SetMode(kModeRotateOnTheSpotDuring); return; } SetMode(kModeFindPathOffElevator); return; } } else if (mode_run_time > 4.5f) { // Crap, we missed it! log_.AHeadline("Must have missed the elevator (I'm still here!), waiting for it again!"); SetMode(kModeWaitingForElevator); return; } if (mode_run_time > 0.8f) { // Check if we should adjust pos. const vec3 forward = game_->GetVehicle()->GetOrientation() * vec3(0,1,0); const float dist = elevator_get_on_position_.GetDistanceSquared(_position); if (dist > elevator_get_on_position_.GetDistanceSquared(_position+forward)) { game_->GetVehicle()->SetEnginePower(0, +strength); game_->GetVehicle()->SetEnginePower(2, 0); } else if (dist > elevator_get_on_position_.GetDistanceSquared(_position-forward)) { game_->GetVehicle()->SetEnginePower(0, -strength); game_->GetVehicle()->SetEnginePower(2, 0); } } } break; case kModeRotateOnTheSpot: case kModeRotateOnTheSpotDuring: case kModeRotateOnTheSpotWaiting: { float angle = rotate_angle_; const float min_angle = 0.3f; if (::fabs(angle) < min_angle) { angle = (angle < 0)? -min_angle : +min_angle; } float steer_end_time = 0.4f; float forward_end_time = steer_end_time + 0.9f; float other_steer_end_time = forward_end_time + steer_end_time; float period = other_steer_end_time + 0.8f; // A monster truck's steering impared. angle *= 2; steer_end_time = 0.7f; forward_end_time = steer_end_time + 0.7f; other_steer_end_time = forward_end_time + steer_end_time; period = other_steer_end_time + 1.0f; strength *= SMOOTH_BRAKING_FACTOR; if (mode_ == kModeRotateOnTheSpotWaiting) { const cure::Elevator* _nearest_elevator; vec3 _nearest_lift_position2d; float _elevator_xy_distance2_to_elevator_stop; if (HasElevatorArrived(_nearest_elevator, _position.z, _nearest_lift_position2d, _elevator_xy_distance2_to_elevator_stop)) { log_.AHeadline("Elevator arrived while rotating on the spot, getting on instead!"); SetMode(kModeGetOnElevator); return; } } // Finish this rotation show if we're getting there. const int iterations = (mode_ == kModeRotateOnTheSpotWaiting)? 1 : 2; if (mode_run_time > iterations*period+steer_end_time) { game_->GetVehicle()->SetEnginePower(0, 0); game_->GetVehicle()->SetEnginePower(1, -angle); game_->GetVehicle()->SetEnginePower(2, -1); if (mode_ == kModeRotateOnTheSpot) { SetMode(kModeHeadingBackOnTrack); } else if (mode_ == kModeRotateOnTheSpotDuring) { SetMode(kModeFindPathOffElevator); } else { SetMode(kModeWaitingForElevator); } return; } for (int x = 0; x < iterations+1; ++x) { const float base = x*period; if (mode_run_time >= base && mode_run_time < base+steer_end_time) { // Brake and turn in "forward direction". game_->GetVehicle()->SetEnginePower(0, 0); game_->GetVehicle()->SetEnginePower(1, -angle); game_->GetVehicle()->SetEnginePower(2, -strength); break; } else if (mode_run_time >= base+steer_end_time && mode_run_time < base+forward_end_time) { // Drive forward. game_->GetVehicle()->SetEnginePower(0, +strength); game_->GetVehicle()->SetEnginePower(2, 0); break; } else if (mode_run_time >= base+forward_end_time && mode_run_time < base+other_steer_end_time) { // Brake and turn in "backward direction". game_->GetVehicle()->SetEnginePower(0, 0); game_->GetVehicle()->SetEnginePower(1, +angle); game_->GetVehicle()->SetEnginePower(2, -strength); break; } else if (mode_run_time >= base+other_steer_end_time && mode_run_time < base+period) { // Drive backward. game_->GetVehicle()->SetEnginePower(0, -0.7f*strength); game_->GetVehicle()->SetEnginePower(2, 0); break; } } } break; }