Logo Search packages:      
Sourcecode: race version File versions

car.cpp

/*
 * This Game is distributed under the GNU GENERAL PUBLIC LICENSE 
 * version 2. See COPYING for details.                           
 *                                                               
 * Copyright (C) 1999, 2000, 2001 Harry Storbacka                
 *                                                               
 * car.cpp                                                   
*/

#include <ClanLib/core.h>
#include <ClanLib/application.h>
#include <ClanLib/display.h>
#include <iostream>
#include <cmath>

#include <algorithm>

#include "config.h"
#include "graphics.h"

#include "tile_enums.h"
#include "track.h"
#include "map.h"
#include "race.h"
#include "camera.h"
#include "dust.h"
#include "attractor.h"
#include "season.h"
#include "rect.h"
#include "height_map.h"
#include "game_config.h"
#include "car_config.h"
#include "game_data.h"
#include "height_map.h"
#include "debug.h"

#include "map_objects/all.h"

#include "object_3d.h"
#include "sound_controller.h"
#include "track_sfx.h"
#include "car_sfx.h"


#include "car.h"


Car::Car(float _x, float _y, int _id)
{
      RaceDebug::print( "Car::Car()", 5 );
      
/*    x = _x;
      y = _y;
      z = 0;*/

      x = y = z = 0;
      
      id = _id;
      
      sound_controller.init(id);
      
      control_points_passed = 0;
      control_points_passed_total = 0;
      
      next_race_start_pos = id;
      rot_x = rot_y = rot_count = 0;
      angle                   = 0;    // some explanations in car.h
      accel                   = 0;
      c_angle                       = 0;
      speed                   = 0;
      wanted_angle            = 0;
      Frame                   = 0;
      slideDirection          = 0;
      angleDifference         = 0;
      bad_design_counter      = 0;
      slideDirection          = 0;
      max_speed               = 0;
      dx = dy                       = 0;
      laptime_start           = 0;
      time_elapsed            = 0;
      laps                    = 0;
      points                        = 0;
      bounced_from            = 0;
      dust_time           = 40;
      last_dust_time      = 0;
      
      dist                    = 0;
      old_dist                = 0;
      
      gravity_velocity    = 0.0f;
      
      best_lap                = 9999999.0f;
      
      sliding                       = false;
      in_goal                       = false;
      checktime               = true;
      end_race_ok         = false;
      
      update_dir_timer    = CL_System::get_time();

      for( int i=0; i<10; i++)
            smooth_rot_x[i] = smooth_rot_y[i] = 0.0f;
      
      under_bridge = false;
      
      RaceDebug::print( "Car: ::Car() -- done", 5 );
}

// ****************************************************************   

void Car::reset_all()
{
      reset();
      
      next_race_start_pos = id;
      points = 0;
}

void Car::set_pos(float _x, float _y)
{
      x = _x;
      y = _y;
}






void Car::reset()
{
      angle = 0;   accel  = 0;
      c_angle     = 0; speed  = 0;
      wanted_angle          = 0;
      Frame               = 0;
      slideDirection      = 0;
      angleDifference     = 0;
      sliding                 = false;
      bad_design_counter  = 0;
      slideDirection      = 0;
      max_speed           = 0;
      dx = dy                 = 0;
      dist                = 0;
      old_dist            = 0;
      laptime_start       = 0;
      best_lap            = 11111.11f;
      update_dir_timer    = CL_System::get_time();
      laps                = 0;
      in_goal                       = false;
      under_bridge        = false;
      checktime               = true;
      end_race_ok         = false;

      bounced_from            = 0;
      position                = -1;
      x = y = 0;

      last_race_points    = 0;
      gravity_velocity    = 0.0f;

      control_points_passed = 0;
      control_points_passed_total = 0;
      
      reset_special();

      sound_controller.stop();
}


// ****************************************************************   


void Car::check_check_points()
{
      if( Track::track->attractors[control_points_passed].on_attractor( x, y )
         && (unsigned)control_points_passed < Track::track->attractors.size() -1 )
      {
            control_points_passed++;
            control_points_passed_total++;
      }

      if( Track::track->attractors.size() -1 == (unsigned)control_points_passed )
      {
            checktime = true;
            control_points_passed = 0;
      }
}


char Car::calc_angle_difference()
{
      char steps = 0;
      char tmpAngle = (char)angle;
      while( tmpAngle != wanted_angle )
      {
            steps++;

            if( slideDirection == 1 )
                  tmpAngle--;
            if( slideDirection == -1 )
                  tmpAngle++;

            if( tmpAngle == 32 )
                  tmpAngle = 0;
            if( tmpAngle == -1 )
                  tmpAngle = 31;
      }
      if( steps > Config::tyre_grip ) sliding = true;
      else sliding = false;
   
      if( steps >= 2 ) dust = true;
      else dust = false;

      return steps;
}

void Car::turn()
{
      RaceDebug::print("Car::turn()",1);

      update_wanted_dir();
      check_current_tile();
      update_speed();

      RaceDebug::print("Car::turn() -- almost done", 1);

      update_sound();
      update_move_dir();
      update_extras();

      RaceDebug::print("Car::turn() -- done",1);
}


void Car::check_current_tile()
{
      RaceDebug::print("Car::check_current_tile()", 1);

      // check points
      update_check_points();

      // drive under/over bridge
      //check_bridge();

      // jumpping
      //check_ramp();

      // collision with houses, trees, fences
      collide();

      // dust 
      add_dust();
      
      if( Track::track->get_data(x+0.5f,y+0.5f) == Items(FINISH) && checktime == true )
      {
            double laptime = ( CL_System::get_time() - laptime_start ) / 1000;

            if( laptime < best_lap && laps > 0 )
                  best_lap = laptime;
            
            if( laptime < Track::track->track_record && laps > 0 )
            {
                  Track::track->track_record = laptime;
            }
            
            laptime_start = CL_System::get_time();
            control_points_passed = 0;
            laps++;
            
            checktime = false;
            
            if( laps > Config::race_length && in_goal == false )
            {
                  in_goal = true;
                  end_race_ok = true;
                  next_race_start_pos = (Config::num_cars-1) - GameData::cars_in_goal;
      
                  if( next_race_start_pos < 0 )
                        std::cout << "Error: Car: next_race_start_pos < 0" << std::endl;

                  GameData::cars_in_goal++;
            
                  last_race_points = 11-GameData::cars_in_goal;
                  points += last_race_points;
            }
      }
      RaceDebug::print("Car::check_current_tile() -- done", 1);
}

void Car::update_move_dir()
{
      // disable steering when car in air and on the map. todo: fix
/*    if( x > 0  && y > 0  &&
            x < 40 && y < 40 &&
            z - HeightMap::get_height(x,y) < -0.0125 )
            return;
*/

      if( speed > 4 )
      {
            if( (CL_System::get_time() - update_dir_timer ) < Config::update_dir_timer + Config::cc->turn_delay[id] )
                  return;
            else
                  update_dir_timer = CL_System::get_time();
      }
      
      calc_slide_direction();
      angle_difference = calc_angle_difference();
      
      bad_design_counter++;

      if( bad_design_counter == 100 )
            bad_design_counter = 0;
      
      if( sliding )
      {
            update_frame(2);
      }
      else
      {
            update_frame(1);
      }
      
      if( sliding && angle != wanted_angle )
      {
            if( bad_design_counter % 2 > 0 )
            {
                  if( slideDirection == -1 )
                  {
                        angle++; 
                  }
                  if( slideDirection == 1 )
                  {
                        angle--;
                  }
                  
                  if( Frame == 32 ) Frame = 0;
                  if( Frame == -1 ) Frame = 31;
            }
      }
      
      if( !sliding && angle != wanted_angle )
      {
            if( bad_design_counter % 3 > 0 )
            {
                  if( (slideDirection == -1) )
                  {
                        angle++;
                  }
                  if( (slideDirection == 1) )
                  {
                        angle--;
                  }
            }
      }
      if( Frame > 32 ) Frame -= 32;
      if( Frame < 0 ) Frame += 32;
      
      if( angle == -1 ) angle = 31;
      if( angle == 32 ) angle = 0;
}

void Car::add_dust()
{
      if( CL_System::get_time() - last_dust_time < dust_time )
            return;
      
      // on road
      if( dust || (speed < max_speed -1 ) )
      {
            if( speed > 0.1 && Track::track->get_tmp_map(x+0.5f,y+0.5f) == '#' )
            {
                  GameData::dust_list.push_back( Dust( x, y, z, speed, angle, false ));
            }
      }
      
      // in water
      if( HeightMap::under_water( x,y,z) )
      {
            GameData::dust_list.push_back( Dust( x, y, z, speed, angle, true ));
      }

      last_dust_time = CL_System::get_time();
}


void Car::calc_slide_direction()
{
      int tmpAngle = angle;
      int steps = 0;
      while( (tmpAngle - wanted_angle) != 0 )
      {
            steps++;
            if( steps > 20 )
              break;
            tmpAngle++;
            if( tmpAngle == 32 )
              tmpAngle = 0;
    }
      if( steps <  15 ) slideDirection = -1;
      if( steps >= 15 ) slideDirection =  1;
}

void Car::move()
{
      x += dx;
      y += dy;
      
      // gravity on cars...
      // divide by 2 because the heightmap is 500x500 
      float terra_z = HeightMap::get_height( x*Config::tile_size/2, y*Config::tile_size/2 );
      
      gravity_velocity += 0.2f * time_elapsed;
      
      z -= gravity_velocity;

      if( z < terra_z )
      {
            z = terra_z;
            gravity_velocity = 0;
      }
      
      terrain_rotate(); // rotate car to follow terrain
      
      camera.update( x, y, z );
      
      angle = c_angle;
}


void Car::terrain_rotate()
{
      int tx = (int)(x*Config::tile_size/2);
      int ty = (int)(y*Config::tile_size/2);
      
      float front_x = Gfx::CarGfx->car[id]->get_max_x();
      float front_y = Gfx::CarGfx->car[id]->get_max_y();
      float back_x  = Gfx::CarGfx->car[id]->get_min_x();
      float back_y  = Gfx::CarGfx->car[id]->get_min_y();
      
      float f_rx = tx + (front_x * cos( angle* M_PI/16 )); //front rotated x
      float f_ry = ty + (front_y * sin( angle * M_PI/16 ));
      float b_rx = tx + (back_x * cos( angle * M_PI/16 ));  //back rotated x
      float b_ry = ty + (back_y * sin( angle * M_PI/16 ));
      
      float f_z = HeightMap::get_height(f_rx, f_ry);
      float b_z = HeightMap::get_height(b_rx, b_ry);
      
      char rot_dir_x = 0;
      char rot_dir_y = 0;
      
      if( angle*11.25 > 0 && angle*11.25 < 180 ) rot_dir_x = -1;
      else rot_dir_x = 1;

      if( angle*11.25 > 180 && angle*11.25 < 360 ) rot_dir_y = 1;
      else rot_dir_y = -1;
      
      smooth_rot_x[rot_count] = rot_dir_x * (atan( (f_z-b_z)/(f_rx-b_rx+0.0001f) ) * (180/M_PI));
      smooth_rot_y[rot_count] = rot_dir_y * (atan( (f_z-b_z)/(f_ry-b_ry+0.0001f) ) * (180/M_PI));

      if( ++rot_count > 9 ) rot_count = 0;

      float sum_rx=0, sum_ry=0;

      for( int i=0; i<10;i++ )
      {
            sum_rx += smooth_rot_x[i];          
            sum_ry += smooth_rot_y[i];          
      }
   
      rot_x = sum_rx/10.0f;
      rot_y = sum_ry/10.0f;

      // limit rotation hack...
      // disables flickering in rotations
      
      if( rot_x >  20.0f ) rot_x =  20.0f;
      if( rot_y >  20.0f ) rot_y =  20.0f;
      if( rot_x < -20.0f ) rot_x = -20.0f;
      if( rot_y < -20.0f ) rot_y = -20.0f;
}



bool Car::hit_check()
{
      bool collision = false;

      for(
            std::vector<Car*>::iterator it = GameData::cars.begin();
            it != GameData::cars.end();
            it++)
      {
            if( (*it)->id != id )
            {
                  if( x < ( (*it)->x + 0.6f ) &&
                      x > ( (*it)->x - 0.6f ) &&
                      y < ( (*it)->y + 0.6f ) &&
                      y > ( (*it)->y - 0.6f ))
                  {
                        collision = true;

                        c_angle = (*it)->get_angle();

                        if( x <= ( (*it)->get_x())
                              && y <= ( (*it)->get_y()))
                              set_move_delta( (*it)->get_dx() -0.1f, (*it)->get_dy() +0.1f );
                    
                        if( x >= ( (*it)->get_x())
                              && y <= ( (*it)->get_y()))
                              set_move_delta( (*it)->get_dx() +0.1f, (*it)->get_dy() +0.1f );
              
                        if( x <= ((*it)->get_x())
                              && y >= ( (*it)->get_y()))
                              set_move_delta( (*it)->get_dx() -0.1f, (*it)->get_dy() -0.1f );
                  
                        if( x >= ( (*it)->get_x())
                              && y >= ( (*it)->get_y()))
                              set_move_delta( (*it)->get_dx() +0.1f, (*it)->get_dy() -0.1f );
                  }
            }
      }
      if( !collision )
            c_angle = angle;
      else  // collision
      {
            if( Config::sfx_on )
                  if( speed > 0.3f )
                        TrackSfx::play_collision();

            return true;
      }
   
      return false;
}


void Car::set_move_delta( float _dx, float _dy ) // TODO: is this needed? remove?
{
   dx = _dx;
   dy = _dy;
}



void Car::update_speed()
{
      RaceDebug::print("Car::update_speed()", 1);

      if( speed < 4.6 ) // TODO: add road_min_slide_speed option to themes
      {
            sliding = false;
      }
      
      if( speed < 0.1 ) // use road_min_slide_speed instead?
      {
            wanted_angle = angle = Frame;
      }
      
      update_object_speed(); // player and ai specific update_speed()
      
      if(Track::track->get_tmp_map(x+0.5f, y+0.5f) == '|' )// TODO: rename tmp map. It's not temporary'
            max_speed = (Config::terrain_speed * Config::cc->terrain_speed[id] );
      
      if( HeightMap::under_water(x, y, z) && speed > Config::terrain_speed )
            max_speed = (Config::terrain_speed * Config::cc->terrain_speed[id] );
      
      if( speed > max_speed ) speed = max_speed;

      if( sliding && speed > 2.0 )
      {
            speed -= Config::skid_speed_decrease;
            if( speed < 0 )
                  speed = 0;
      }
      
      RaceDebug::print("Car::update_speed() -- done", 1); 
}



void Car::update_check_points()
{
      check_check_points(); // TODO: there would probably be some better name...
}



void Car::collide()
{
      bounce_from_walls();
      bounce_from_objects();
}



void Car::bounce_from_walls()
{
      if( bounced_from != Track::track->get_data((int)x,(int)y))
            bounced_from = 0;

      if( Track::track->get_data((int)x,(int)y) == Items(FENCE_H)
            && bounced_from != Items(FENCE_H) ) // horizontal fences
      {
            angle = 16+(16-angle);
            bounced_from = Items(FENCE_H);
      }
      if( Track::track->get_data((int)x,(int)y) == Items(FENCE_V)
            && bounced_from != Items(FENCE_V)  ) // vertical fence
      {
            angle = 8+(8-angle);
            bounced_from = Items(FENCE_V);
      }
      
      if( angle > 31 ) angle -= 32;
      if( angle < 0  ) angle += 32;
}




void Car::bounce_from_objects()
{

}



void Car::check_bridge()
{
/*
      Items item = Items(Track::track->get_data( (int)x, (int)y ));

      if( item == UNDER_BRIDGE )
            under_bridge = true;
      if( item == OVER_BRIDGE )
            under_bridge = false;
*/
}



void Car::check_ramp()
{
      //
}



bool Car::get_rotate_dir(int f, int wa)
{
      // 1: clockwise
      // 0: counter clockwise

      if( wa < 16 )
      {
            for( int i=0;i<16;i++ )
            {
                  if( i == wa )
                  {
                        if( f > i && f < (i+16))
                              return 0;
                        else
                              return 1;
                  }
            }
      }
      if( wa >= 16 )
      {
            for( int i=16;i<32;i++ )
            {
                  if( i == wa )
                  {
                        if( f < i && f > (i-16))
                              return 1;
                        else
                              return 0;
                  }
            }
      }
      return 1;
}



float Car::get_distance_to_next_attractor( int ax, int ay )
{
      float a = get_x() - ax;
      float b = get_y() - ay;

      float dist = sqrt(a*a+b*b);
      return dist;
}


void Car::update_sound()
{
      if( !Config::sfx_on ) return;
      
      sound_controller.update_frequency( speed );
      sound_controller.play();
}




Generated by  Doxygen 1.6.0   Back to index