extremetuxracer/src/racing.cpp

389 lines
10 KiB
C++

/* --------------------------------------------------------------------
EXTREME TUXRACER
Copyright (C) 1999-2001 Jasmin F. Patry (Tuxracer)
Copyright (C) 2010 Extreme Tuxracer Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
---------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#include <etr_config.h>
#endif
#include "racing.h"
#include "audio.h"
#include "course_render.h"
#include "ogl.h"
#include "view.h"
#include "env.h"
#include "track_marks.h"
#include "hud.h"
#include "course.h"
#include "particles.h"
#include "textures.h"
#include "game_ctrl.h"
#include "game_over.h"
#include "paused.h"
#include "reset.h"
#include "winsys.h"
#include "physics.h"
#include "tux.h"
#include <algorithm>
#define MAX_JUMP_AMT 1.0
#define ROLL_DECAY 0.2
#define JUMP_MAX_START_HEIGHT 0.30
CRacing Racing;
static bool right_turn;
static bool left_turn;
static bool stick_turn;
static float stick_turnfact;
static bool key_paddling;
static bool stick_paddling;
static bool key_charging;
static bool stick_charging;
static bool key_braking;
static bool stick_braking;
static double charge_start_time;
static bool trick_modifier;
static bool sky = true;
static bool fog = true;
static bool terr = true;
static bool trees = true;
static int newsound = -1;
static int lastsound = -1;
void CRacing::Keyb(unsigned int key, bool special, bool release, int x, int y) {
switch (key) {
// steering flipflops
case SDLK_UP:
key_paddling = !release;
break;
case SDLK_DOWN:
key_braking = !release;
break;
case SDLK_LEFT:
left_turn = !release;
break;
case SDLK_RIGHT:
right_turn = !release;
break;
case SDLK_SPACE:
key_charging = !release;
break;
case SDLK_t:
trick_modifier = !release;
break;
// mode changing and other actions
case SDLK_ESCAPE:
if (!release) {
g_game.raceaborted = true;
g_game.race_result = -1;
State::manager.RequestEnterState(GameOver);
}
break;
case SDLK_p:
if (!release) State::manager.RequestEnterState(Paused);
break;
case SDLK_r:
if (!release) State::manager.RequestEnterState(Reset);
break;
case SDLK_s:
if (!release) ScreenshotN();
break;
// view changing
case SDLK_1:
if (!release) {
set_view_mode(g_game.player->ctrl, ABOVE);
param.view_mode = ABOVE;
}
break;
case SDLK_2:
if (!release) {
set_view_mode(g_game.player->ctrl, FOLLOW);
param.view_mode = FOLLOW;
}
break;
case SDLK_3:
if (!release) {
set_view_mode(g_game.player->ctrl, BEHIND);
param.view_mode = BEHIND;
}
break;
// toggle
case SDLK_h:
if (!release) param.show_hud = !param.show_hud;
break;
case SDLK_f:
if (!release) param.display_fps = !param.display_fps;
break;
case SDLK_F5:
if (!release) sky = !sky;
break;
case SDLK_F6:
if (!release) fog = !fog;
break;
case SDLK_F7:
if (!release) terr = !terr;
break;
case SDLK_F8:
if (!release) trees = !trees;
break;
}
}
void CRacing::Jaxis(int axis, float value) {
if (axis == 0) { // left and right
stick_turn = ((value < -0.2) || (value > 0.2));
if (stick_turn) stick_turnfact = value;
else stick_turnfact = 0.0;
} else if (axis == 1) { // paddling and braking
stick_paddling = (value < -0.3);
stick_braking = (value > 0.3);
}
}
void CRacing::Jbutt(int button, int state) {
if (button == 0) {
key_charging = state != 0;
} else if (button == 1) {
// key_charging = (bool) state;
}
}
void CalcJumpEnergy(double time_step) {
CControl *ctrl = g_game.player->ctrl;
if (ctrl->jump_charging) {
ctrl->jump_amt = min(MAX_JUMP_AMT, g_game.time - charge_start_time);
} else if (ctrl->jumping) {
ctrl->jump_amt *= (1.0 - (g_game.time - ctrl->jump_start_time) /
JUMP_FORCE_DURATION);
} else {
ctrl->jump_amt = 0;
}
}
int CalcSoundVol(float fact) {
float vv = (float) param.sound_volume * fact;
if (vv > 120) vv = 120;
return (int) vv;
}
void SetSoundVolumes() {
Sound.SetVolume("pickup1", CalcSoundVol(1.0));
Sound.SetVolume("pickup2", CalcSoundVol(0.8));
Sound.SetVolume("pickup3", CalcSoundVol(0.8));
Sound.SetVolume("snow_sound", CalcSoundVol(1.5));
Sound.SetVolume("ice_sound", CalcSoundVol(0.6));
Sound.SetVolume("rock_sound", CalcSoundVol(1.1));
}
// ---------------------------- init ----------------------------------
void CRacing::Enter() {
CControl *ctrl = g_game.player->ctrl;
if (param.view_mode < 0 || param.view_mode >= NUM_VIEW_MODES) {
param.view_mode = ABOVE;
}
set_view_mode(ctrl, param.view_mode);
left_turn = right_turn = trick_modifier = false;
ctrl->turn_fact = 0.0;
ctrl->turn_animation = 0.0;
ctrl->is_braking = false;
ctrl->is_paddling = false;
ctrl->jumping = false;
ctrl->jump_charging = false;
lastsound = -1;
newsound = -1;
if (State::manager.PreviousState() != &Paused) ctrl->Init();
g_game.raceaborted = false;
SetSoundVolumes();
Music.PlayTheme(g_game.theme_id, MUS_RACING);
g_game.finish = false;
}
// -------------------- sound -----------------------------------------
// this function is not used yet.
int SlideVolume(CControl *ctrl, double speed, int typ) {
if (typ == 1) { // only at paddling or braking
return (int)(min((((pow(ctrl->turn_fact, 2) * 128)) +
(ctrl->is_braking ? 128:0) +
(ctrl->jumping ? 128:0) + 20) * (speed / 10), 128.0));
} else { // always
return (int)(128 * pow((speed/2),2));
}
}
void PlayTerrainSound(CControl *ctrl, bool airborne) {
if (airborne == false) {
int terridx = Course.GetTerrainIdx(ctrl->cpos.x, ctrl->cpos.z, 0.5);
if (terridx >= 0) {
newsound = (int)Course.TerrList[terridx].sound;
} else newsound = -1;
} else newsound = -1;
if ((newsound != lastsound) && (lastsound >= 0)) Sound.Halt(lastsound);
if (newsound >= 0) Sound.Play(newsound, -1);
lastsound = newsound;
}
// ----------------------- controls -----------------------------------
void CalcSteeringControls(CControl *ctrl, double time_step) {
if (stick_turn) {
ctrl->turn_fact = stick_turnfact;
ctrl->turn_animation += ctrl->turn_fact * 2 * time_step;
ctrl->turn_animation = clamp(-1.0, ctrl->turn_animation, 1.0);
} else if (left_turn ^ right_turn) {
if (left_turn) ctrl->turn_fact = -1.0;
else ctrl->turn_fact = 1.0;
ctrl->turn_animation += ctrl->turn_fact * 2 * time_step;
ctrl->turn_animation = clamp(-1.0, ctrl->turn_animation, 1.0);
} else {
ctrl->turn_fact = 0.0;
if (time_step < ROLL_DECAY) {
ctrl->turn_animation *= 1.0 - time_step / ROLL_DECAY;
} else {
ctrl->turn_animation = 0.0;
}
}
bool paddling = key_paddling || stick_paddling;
if (paddling && ctrl->is_paddling == false) {
ctrl->is_paddling = true;
ctrl->paddle_time = g_game.time;
}
bool braking = key_braking || stick_braking;
ctrl->is_braking = braking;
bool charge = key_charging || stick_charging;
bool invcharge = !key_charging && !stick_charging;
CalcJumpEnergy(time_step);
if ((charge) && !ctrl->jump_charging && !ctrl->jumping) {
ctrl->jump_charging = true;
charge_start_time = g_game.time;
}
if ((invcharge) && ctrl->jump_charging) {
ctrl->jump_charging = false;
ctrl->begin_jump = true;
}
}
void CalcFinishControls(CControl *ctrl, double timestep, bool airborne) {
double speed = ctrl->cvel.Length();
double dir_angle = RADIANS_TO_ANGLES(atan(ctrl->cvel.x / ctrl->cvel.z));
if (fabs(dir_angle) > 5 && speed > 5) {
ctrl->turn_fact = dir_angle / 20;
if (ctrl->turn_fact < -1) ctrl->turn_fact = -1;
if (ctrl->turn_fact > 1) ctrl->turn_fact = 1;
ctrl->turn_animation += ctrl->turn_fact * 2 * timestep;
} else {
ctrl->turn_fact = 0;
if (timestep < ROLL_DECAY) {
ctrl->turn_animation *= 1.0 - timestep / ROLL_DECAY;
} else ctrl->turn_animation = 0.0;
}
}
// ----------------------- trick --------------------------------------
void CalcTrickControls(CControl *ctrl, double time_step, bool airborne) {
if (airborne && trick_modifier) {
if (left_turn) ctrl->roll_left = true;
if (right_turn) ctrl->roll_right = true;
if (key_paddling) ctrl->front_flip = true;
if (ctrl->is_braking) ctrl->back_flip = true;
}
if (ctrl->roll_left || ctrl->roll_right) {
ctrl->roll_factor += (ctrl->roll_left ? -1 : 1) * 0.15 * time_step / 0.05;
if (ctrl->roll_factor > 1 || ctrl->roll_factor < -1) {
ctrl->roll_factor = 0;
ctrl->roll_left = ctrl->roll_right = false;
}
}
if (ctrl->front_flip || ctrl->back_flip) {
ctrl->flip_factor += (ctrl->back_flip ? -1 : 1) * 0.15 * time_step / 0.05;
if (ctrl->flip_factor > 1 || ctrl->flip_factor < -1) {
ctrl->flip_factor = 0;
ctrl->front_flip = ctrl->back_flip = false;
}
}
}
// ====================================================================
// loop
// ====================================================================
void CRacing::Loop(double time_step) {
CControl *ctrl = g_game.player->ctrl;
double ycoord = Course.FindYCoord(ctrl->cpos.x, ctrl->cpos.z);
bool airborne = (bool)(ctrl->cpos.y > (ycoord + JUMP_MAX_START_HEIGHT));
ClearRenderContext();
Env.SetupFog();
Music.Update();
CalcTrickControls(ctrl, time_step, airborne);
if (!g_game.finish) CalcSteeringControls(ctrl, time_step);
else CalcFinishControls(ctrl, time_step, airborne);
PlayTerrainSound(ctrl, airborne);
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ctrl->UpdatePlayerPos(time_step);
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
if (g_game.finish) IncCameraDistance(time_step);
update_view(ctrl, time_step);
UpdateTrackmarks(ctrl);
SetupViewFrustum(ctrl);
if (sky) Env.DrawSkybox(ctrl->viewpos);
if (fog) Env.DrawFog();
void SetupLight();
if (terr) RenderCourse();
DrawTrackmarks();
if (trees) DrawTrees();
if (param.perf_level > 2) {
update_particles(time_step);
draw_particles(ctrl);
}
g_game.character->shape->Draw();
UpdateWind(time_step);
UpdateSnow(time_step, ctrl);
DrawSnow(ctrl);
DrawHud(ctrl);
Reshape(Winsys.resolution.width, Winsys.resolution.height);
Winsys.SwapBuffers();
if (g_game.finish == false) g_game.time += time_step;
}
// ---------------------------------- term ------------------
void CRacing::Exit() {
Sound.HaltAll();
break_track_marks();
}