void ParticleRenderer::CreateExplosion(const vec3& position, float strength, const vec3& velocity, float falloff, float time, const vec3& start_fire_color, const vec3& fire_color, const vec3& start_smoke_color, const vec3& smoke_color, const vec3& sharpnel_color, int fires, int smokes, int sparks, int shrapnels) { ScopeLock lock(lock_); const float random_xy_end_speed = 1.0f; const float strength2 = (strength>1)? ::sqrt(strength) : strength*strength; const float particle_size = strength2*0.5f; const float speed = velocity.GetLength() * 0.01f; CreateBillboards(position, 7*strength2+ 1*speed, velocity, Math::Lerp(velocity,gravity_*-0.2f,falloff), random_xy_end_speed, 5.3f/time, particle_size, start_fire_color, fire_color, fires_, fires); CreateBillboards(position, 8*strength2+ 1*speed, velocity, Math::Lerp(velocity,gravity_*+0.2f,falloff), random_xy_end_speed, 3/time, particle_size*2, start_smoke_color, smoke_color, smokes_, smokes); CreateBillboards(position, 20*strength2+20*speed, 1.2f*velocity, Math::Lerp(velocity,gravity_*+0.8f,falloff), random_xy_end_speed, 4.5f/time, particle_size*0.4f, vec3(), vec3(), sparks_, sparks); CreateBillboards(position, 9*strength2+10*speed, 1.1f*velocity, Math::Lerp(velocity,gravity_*+1.1f,falloff), random_xy_end_speed, 1.1f/time, particle_size*0.5f, sharpnel_color, sharpnel_color, shrapnels_, shrapnels); const float min_spark_velocity2 = strength2*100; const vec3 cam_plane = renderer_->GetCameraTransformation().GetOrientation() * vec3(0,1,0); BillboardArray::reverse_iterator x = sparks_.rbegin(); for (int y = 0; y < sparks; ++y, ++x) { x->velocity_ = x->velocity_.ProjectOntoPlane(cam_plane); float speed2 = x->velocity_.GetLengthSquared(); if (speed2 < min_spark_velocity2) { x->velocity_.Mul(::sqrt(min_spark_velocity2/speed2)); } } if (fires > 0) { const Billboard& fire = fires_.back(); CreateTempLight(fire_color, strength, fire.position_, fire.velocity_, fire.target_velocity_, fire.time_factor_); } }
void Game::OnCollision(const vec3& force, const vec3& torque, const vec3& position, cure::ContextObject* object1, cure::ContextObject* object2, tbc::PhysicsManager::BodyID body1_id, tbc::PhysicsManager::BodyID body2_id) { (void)body2_id; collision_sound_manager_->OnCollision(force, torque, position, object1, object2, body1_id, 2000, false); const float _force = force.GetLength(); if (object1 == ball_ && _force > 1.0f) { score_ += ::sqrt(_force) * 34; } }
void ServerMine::OnForceApplied(cure::ContextObject* other_object, tbc::PhysicsManager::BodyID own_body_id, tbc::PhysicsManager::BodyID other_body_id, const vec3& force, const vec3& torque, const vec3& position, const vec3& relative_velocity) { (void)other_object; (void)own_body_id; (void)other_body_id; (void)torque; (void)position; (void)relative_velocity; float _force = force.GetLength()/GetMass() * 0.002f; if (ticks_til_fully_activated_ == 0 || (other_object->GetPhysics()->GetPhysicsType() == tbc::ChunkyPhysics::kDynamic && !dynamic_cast<ServerMine*>(other_object))) { _force *= 10; } if (_force > 1) { cure::Health::Add(this, _force * -0.045f, true); } }
bool Game::MoveRacket() { if (GetRacket() && GetRacket()->IsLoaded() && GetBall() && GetBall()->IsLoaded()) { xform racket_transform; GameTicker::GetPhysicsManager(IsThreadSafe())->GetBodyTransform( GetRacket()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), racket_transform); vec3 racket_linear_velocity; GameTicker::GetPhysicsManager(IsThreadSafe())->GetBodyVelocity( GetRacket()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), racket_linear_velocity); vec3 racket_angular_velocity; GameTicker::GetPhysicsManager(IsThreadSafe())->GetBodyAngularVelocity( GetRacket()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), racket_angular_velocity); // Calculate where ball will be as it passes z = racket z. vec3 ball_position = GameTicker::GetPhysicsManager(IsThreadSafe())->GetBodyPosition(GetBall()->GetPhysics()->GetBoneGeometry(0)->GetBodyId()); vec3 ball_velocity; GameTicker::GetPhysicsManager(IsThreadSafe())->GetBodyVelocity( GetBall()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), ball_velocity); if (ball_position.z < -2) { ball_position.Set(0, 0, 0.4f); GameTicker::GetPhysicsManager(IsThreadSafe())->SetBodyTransform(GetBall()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), xform(quat(), ball_position)); ball_velocity.Set(0, 0, 2.0f); GameTicker::GetPhysicsManager(IsThreadSafe())->SetBodyVelocity(GetBall()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), ball_velocity); GameTicker::GetPhysicsManager(IsThreadSafe())->SetBodyAngularVelocity(GetBall()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), vec3()); racket_transform.SetIdentity(); GameTicker::GetPhysicsManager(IsThreadSafe())->SetBodyTransform(GetRacket()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), racket_transform); racket_linear_velocity.Set(0, 0, 0); GameTicker::GetPhysicsManager(IsThreadSafe())->SetBodyVelocity(GetRacket()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), racket_linear_velocity); racket_angular_velocity.Set(0, 0, 0); GameTicker::GetPhysicsManager(IsThreadSafe())->SetBodyAngularVelocity(GetRacket()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), racket_angular_velocity); return false; } vec3 home; const float h = ball_position.z - racket_transform.GetPosition().z; if (h > -0.5f) { home.Set(ball_position.x*0.8f, ball_position.y*0.8f, 0); } const float vup = ball_velocity.z; const float a = +9.82f / 2; const float b = -vup; const float c = +h; const float b2 = b*b; const float _4ac = 4*a*c; if (b2 < _4ac || _4ac < 0) { // Does not compute. } else { const float t = (-b + sqrt(b2 - _4ac)) / (2*a); if (t > 0) { home.x += ball_velocity.x * t; home.y += ball_velocity.y * t; } } // Set linear force. const vec3 direction_home = home - racket_transform.GetPosition(); float f = direction_home.GetLength(); f *= 50; f *= f; vec3 _force = direction_home * f; _force -= racket_linear_velocity * 10; float user_force_factor = ::fabs(racket_lift_factor_) * 1.7f; user_force_factor = std::min(1.0f, user_force_factor); const float racket_acceleration = 100.0f * racket_lift_factor_; const float zForce = Math::Lerp(_force.z, racket_acceleration, user_force_factor); _force.z = zForce; f = _force.GetLength(); if (f > 100) { _force *= 100 / f; } //mLog.Infof("force = (%f, %f, %f"), lForce.x, lForce.y, lForce.z); GameTicker::GetPhysicsManager(IsThreadSafe())->AddForce( GetRacket()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), _force); // Set torque. Note that racket is "flat" along the XY-plane. //const float lTiltAngleFactor = 1.2f; //const float dx = direction_home.x * lTiltAngleFactor; //const float dy = direction_home.y * lTiltAngleFactor; //const float dx = -racket_transform.GetPosition().x * lTiltAngleFactor; //const float dy = -racket_transform.GetPosition().y * lTiltAngleFactor; racket_down_direction_.Normalize(); const vec3 home_torque = vec3(::acos(racket_down_direction_.y), ::acos(racket_down_direction_.x), 0); vec3 racket_torque = racket_transform.GetOrientation() * vec3(0,0,1); racket_torque = vec3(::acos(racket_torque.y), ::acos(racket_torque.x), 0); vec3 angle_home = home_torque - racket_torque; angle_home.y = -angle_home.y; angle_home.z = 0; f = Math::Clamp(-ball_velocity.z, 0.0f, 4.0f) / 4.0f; f = Math::Lerp(0.8f, 0.3f, f); f = angle_home.GetLength() * f; f *= f; f = 1; vec3 _torque = angle_home * f; _torque -= racket_angular_velocity * 0.2f; //mLog.Infof("torque = (%f, %f, %f"), lTorque.x, lTorque.y, lTorque.z); GameTicker::GetPhysicsManager(IsThreadSafe())->AddTorque( GetRacket()->GetPhysics()->GetBoneGeometry(0)->GetBodyId(), _torque); } return true; }
void JetEngineEmitter::EmitFromTag(const CppContextObject* object, const uitbc::ChunkyClass::Tag& tag, float frame_time) { bool particles_enabled; v_get(particles_enabled, =, UiCure::GetSettings(), kRtvarUi3DEnableparticles, false); if (!particles_enabled) { return; } enum FloatValue { kFvStartR = 0, kFvStartG, kFvStartB, kFvEndR, kFvEndG, kFvEndB, kFvX, kFvY, kFvZ, kFvRadiusX, kFvRadiusY, kFvRadiusZ, kFvScaleX, kFvScaleY, kFvScaleZ, kFvDirectionX, kFvDirectionY, kFvDirectionZ, kFvDensity, kFvOpacity, kFvOvershootOpacity, kFvOvershootCutoffDot, kFvOvershootDistanceUpscale, kFvOvershootEngineFactorBase, kFvCount }; if (tag.float_value_list_.size() != kFvCount || tag.string_value_list_.size() != 0 || tag.body_index_list_.size() != 0 || tag.engine_index_list_.size() != 1 || tag.mesh_index_list_.size() < 1) { log_.Errorf("The fire tag '%s' has the wrong # of parameters.", tag.tag_name_.c_str()); deb_assert(false); return; } const int engine_index = tag.engine_index_list_[0]; if (engine_index >= object->GetPhysics()->GetEngineCount()) { return; } const tbc::PhysicsEngine* engine = object->GetPhysics()->GetEngine(engine_index); const float throttle_up_speed = Math::GetIterateLerpTime(tag.float_value_list_[kFvOvershootEngineFactorBase]*0.5f, frame_time); const float throttle_down_speed = Math::GetIterateLerpTime(tag.float_value_list_[kFvOvershootEngineFactorBase], frame_time); const float engine_throttle = engine->GetLerpThrottle(throttle_up_speed, throttle_down_speed, true); const quat orientation = object->GetOrientation(); vec3 _radius(tag.float_value_list_[kFvRadiusX], tag.float_value_list_[kFvRadiusY], tag.float_value_list_[kFvRadiusZ]); _radius.x *= Math::Lerp(1.0f, tag.float_value_list_[kFvScaleX], engine_throttle); _radius.y *= Math::Lerp(1.0f, tag.float_value_list_[kFvScaleY], engine_throttle); _radius.z *= Math::Lerp(1.0f, tag.float_value_list_[kFvScaleZ], engine_throttle); vec3 _position(tag.float_value_list_[kFvX], tag.float_value_list_[kFvY], tag.float_value_list_[kFvZ]); _position = orientation * _position; const vec3 _color(tag.float_value_list_[kFvEndR], tag.float_value_list_[kFvEndB], tag.float_value_list_[kFvEndB]); bool create_particle = false; const float density = tag.float_value_list_[kFvDensity]; float exhaust_intensity; v_get(exhaust_intensity, =(float), UiCure::GetSettings(), kRtvarUi3DExhaustintensity, 1.0); interleave_timeout_ -= Math::Lerp(0.3f, 1.0f, engine_throttle) * exhaust_intensity * frame_time; if (interleave_timeout_ <= 0) { // Release particle this frame? create_particle = true; interleave_timeout_ = 0.05f / density; } else { create_particle = false; } const float dx = tag.float_value_list_[kFvRadiusX]; const float dy = tag.float_value_list_[kFvRadiusY]; const float dz = tag.float_value_list_[kFvRadiusZ]; const vec3 start_color(tag.float_value_list_[kFvStartR], tag.float_value_list_[kFvStartB], tag.float_value_list_[kFvStartB]); const float _opacity = tag.float_value_list_[kFvOpacity]; const vec3 direction = orientation * vec3(tag.float_value_list_[kFvDirectionX], tag.float_value_list_[kFvDirectionY], tag.float_value_list_[kFvDirectionZ]); const vec3 velocity = direction + object->GetVelocity(); uitbc::ParticleRenderer* particle_renderer = (uitbc::ParticleRenderer*)ui_manager_->GetRenderer()->GetDynamicRenderer("particle"); const float particle_time = density; float particle_size; // Pick second size. if (dx > dy && dy > dz) { particle_size = dy; } else if (dy > dx && dx > dz) { particle_size = dx; } else { particle_size = dz; } particle_size *= 0.2f; const float _distance_scale_factor = tag.float_value_list_[kFvOvershootDistanceUpscale]; for (size_t y = 0; y < tag.mesh_index_list_.size(); ++y) { tbc::GeometryBase* mesh = object->GetMesh(tag.mesh_index_list_[y]); if (mesh) { int phys_index = -1; str mesh_name; xform transform; float mesh_scale; ((uitbc::ChunkyClass*)object->GetClass())->GetMesh(tag.mesh_index_list_[y], phys_index, mesh_name, transform, mesh_scale); transform = mesh->GetBaseTransformation() * transform; vec3 mesh_pos = transform.GetPosition() + _position; const vec3 cam_distance = mesh_pos - ui_manager_->GetRenderer()->GetCameraTransformation().GetPosition(); const float distance = cam_distance.GetLength(); const vec3 cam_direction(cam_distance / distance); float overshoot_factor = -(cam_direction*direction); if (overshoot_factor > tag.float_value_list_[kFvOvershootCutoffDot]) { overshoot_factor = Math::Lerp(overshoot_factor*0.5f, overshoot_factor, engine_throttle); const float opacity = (overshoot_factor+0.6f) * tag.float_value_list_[kFvOvershootOpacity]; DrawOvershoot(mesh_pos, _distance_scale_factor*distance, _radius, _color, opacity, cam_direction); } if (create_particle) { const float sx = Random::Normal(0.0f, dx*0.5f, -dx, +dx); const float sy = Random::Normal(0.0f, dy*0.5f, -dy, +dy); const float sz = Random::Normal(0.0f, dz*0.5f, -dz, +dz); mesh_pos += orientation * vec3(sx, sy, sz); particle_renderer->CreateGlow(particle_time, particle_size, start_color, _color, _opacity, mesh_pos, velocity); } } } }
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; }