1 /* --------------------------------------------------------------------
2 EXTREME TUXRACER
3 
4 Copyright (C) 1999-2001 Jasmin F. Patry (Tuxracer)
5 Copyright (C) 2010 Extreme Tux Racer Team
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 ---------------------------------------------------------------------*/
17 
18 #ifdef HAVE_CONFIG_H
19 #include <etr_config.h>
20 #endif
21 
22 #include "physics.h"
23 #include "course.h"
24 #include "tux.h"
25 #include "audio.h"
26 #include "particles.h"
27 #include "game_ctrl.h"
28 #include "game_over.h"
29 #include <algorithm>
30 
CControl()31 CControl::CControl() :
32 	cnet_force(0, 0, 0) {
33 	minSpeed = 0;
34 	minFrictspeed = 0;
35 	turn_fact = 0;
36 	turn_animation = 0;
37 	is_braking = false;
38 	jump_amt = 0;
39 	is_paddling = false;
40 	jumping = false;
41 	jump_charging = false;
42 	last_pos = cpos;
43 	orientation_initialized = false;
44 	cairborne = false;
45 	way = 0.0;
46 	front_flip = false;
47 	back_flip = false;
48 	roll_left = false;
49 	roll_right = false;
50 	roll_factor = 0;
51 	flip_factor = 0;
52 
53 	ode_time_step = -1;
54 	jump_start_time = 0;
55 	begin_jump = false;
56 	paddle_time = 0;
57 	view_init = false;
58 	finish_speed = 0;
59 
60 	viewmode = ABOVE;
61 }
62 
63 // --------------------------------------------------------------------
64 // 					init
65 // --------------------------------------------------------------------
66 
Init()67 void CControl::Init() {
68 	TVector3d nml = Course.FindCourseNormal(cpos.x, cpos.z);
69 	TMatrix<4, 4> rotMat;
70 	rotMat.SetRotationMatrix(-90.0, 'x');
71 	TVector3d init_vel = TransformVector(rotMat, nml);
72 	init_vel *= INIT_TUX_SPEED;
73 
74 	turn_fact = 0;
75 	turn_animation = 0;
76 	is_braking = false;
77 	jump_amt = 0;
78 	is_paddling = false;
79 	jumping = false;
80 	jump_charging = false;
81 	cpos.y = Course.FindYCoord(cpos.x, cpos.z);
82 	cvel = init_vel;
83 	last_pos = cpos;
84 	cnet_force = TVector3d(0, 0, 0);
85 	orientation_initialized = false;
86 	plane_nml = nml;
87 	cdirection = init_vel;
88 	cairborne = false;
89 	way = 0.0;
90 
91 	// tricks
92 	front_flip = false;
93 	back_flip = false;
94 	roll_left = false;
95 	roll_right = false;
96 	roll_factor = 0;
97 	flip_factor = 0;
98 
99 	ode_time_step = -1;
100 }
101 // --------------------------------------------------------------------
102 //					collision
103 // --------------------------------------------------------------------
104 
CheckTreeCollisions(const TVector3d & pos,TVector3d * tree_loc) const105 bool CControl::CheckTreeCollisions(const TVector3d& pos, TVector3d *tree_loc) const {
106 	// These variables are used to cache collision detection results
107 	static bool last_collision = false;
108 	static TVector3d last_collision_tree_loc(-999, -999, -999);
109 	static TVector3d last_collision_pos(-999, -999, -999);
110 
111 	TVector3d dist_vec = pos - last_collision_pos;
112 	if (MAG_SQD(dist_vec) < COLL_TOLERANCE) {
113 		if (last_collision && !cairborne) {
114 			if (tree_loc != nullptr) *tree_loc = last_collision_tree_loc;
115 			return true;
116 		} else return false;
117 	}
118 
119 	TVector3d loc(0, 0, 0);
120 	bool hit = false;
121 	TMatrix<4, 4> mat;
122 
123 	for (std::size_t i = 0; i<Course.CollArr.size(); i++) {
124 		double diam = Course.CollArr[i].diam;
125 		double height = Course.CollArr[i].height;
126 		loc = Course.CollArr[i].pt;
127 		TVector3d distvec(loc.x - pos.x, 0.0, loc.z - pos.z);
128 
129 		// check distance from tree; .6 is the radius of a bounding sphere
130 		double squared_dist = (diam / 2.0 + 0.6);
131 		squared_dist *= squared_dist;
132 		if (MAG_SQD(distvec) > squared_dist) continue;
133 
134 		TPolyhedron ph2 = Course.GetPoly(Course.CollArr[i].tree_type);
135 		mat.SetScalingMatrix(diam, height, diam);
136 		TransPolyhedron(mat, ph2);
137 		mat.SetTranslationMatrix(loc.x, loc.y, loc.z);
138 		TransPolyhedron(mat, ph2);
139 
140 		hit = g_game.character->shape->Collision(pos, ph2);
141 		if (hit == true) {
142 			if (tree_loc != nullptr) *tree_loc = loc;
143 			Sound.Play("tree_hit", 0);
144 			break;
145 		}
146 	}
147 
148 	last_collision_tree_loc = loc;
149 	last_collision_pos = pos;
150 	last_collision = hit;
151 
152 	return hit;
153 }
154 
AdjustTreeCollision(const TVector3d & pos,TVector3d * vel) const155 void CControl::AdjustTreeCollision(const TVector3d& pos, TVector3d *vel) const {
156 	TVector3d treeLoc;
157 
158 	if (CheckTreeCollisions(pos, &treeLoc)) {
159 		TVector3d treeNml(
160 		    pos.x - treeLoc.x,
161 		    0,
162 		    pos.z - treeLoc.z);
163 		treeNml.Norm();
164 
165 		double speed = vel->Norm();
166 		speed *= 0.8;  // original 0.7
167 
168 		double costheta = DotProduct(*vel, treeNml);
169 		if (costheta < 0) {
170 			double factor;
171 			if (cairborne) factor = 0.5;
172 			else factor = 1.5;
173 			*vel += (-factor * costheta) * treeNml;
174 			vel->Norm();
175 		}
176 		speed = std::max(speed, minSpeed);
177 		*vel *= speed;
178 	}
179 }
180 
CheckItemCollection(const TVector3d & pos)181 void CControl::CheckItemCollection(const TVector3d& pos) {
182 	std::size_t num_items = Course.NocollArr.size();
183 
184 	for (std::size_t i=0; i<num_items; i++) {
185 		if (Course.NocollArr[i].collectable != 1) continue;
186 
187 		double diam = Course.NocollArr[i].diam;
188 		const TVector3d& loc = Course.NocollArr[i].pt;
189 
190 		TVector3d distvec(loc.x - pos.x, loc.y - pos.y, loc.z - pos.z);
191 		double squared_dist = (diam / 2. + 0.7);
192 		squared_dist *= squared_dist;
193 		if (MAG_SQD(distvec) <= squared_dist) {  // Check collision using a bounding sphere
194 			Course.NocollArr[i].collectable = 0;
195 			g_game.herring += 1;
196 			Sound.Play("pickup1", 0);
197 			Sound.Play("pickup2", 0);
198 			Sound.Play("pickup3", 0);
199 		}
200 	}
201 }
202 // --------------------------------------------------------------------
203 //				position and velocity  ***
204 // --------------------------------------------------------------------
205 
AdjustVelocity()206 void CControl::AdjustVelocity() {
207 	double speed = cvel.Norm();
208 	speed = std::max(minSpeed, speed);
209 	cvel *= speed;
210 
211 	if (g_game.finish == true) {
212 /// --------------- finish ------------------------------------
213 		if (speed < 3) State::manager.RequestEnterState(GameOver);
214 /// -----------------------------------------------------------
215 	}
216 }
217 
AdjustPosition(const TPlane & surf_plane,double dist_from_surface)218 void CControl::AdjustPosition(const TPlane& surf_plane, double dist_from_surface) {
219 	if (dist_from_surface < -MAX_SURF_PEN) {
220 		double displace = -MAX_SURF_PEN - dist_from_surface;
221 		cpos += displace * surf_plane.nml;
222 	}
223 }
224 
SetTuxPosition(double speed)225 void CControl::SetTuxPosition(double speed) {
226 	CCharShape *shape = g_game.character->shape;
227 
228 	TVector2d playSize = Course.GetPlayDimensions();
229 	TVector2d courseSize = Course.GetDimensions();
230 	double boundaryWidth = (courseSize.x - playSize.x) / 2;
231 	if (cpos.x < boundaryWidth) cpos.x = boundaryWidth;
232 	if (cpos.x > courseSize.x - boundaryWidth) cpos.x = courseSize.x - boundaryWidth;
233 	if (cpos.z > 0) cpos.z = 0;
234 
235 	if (g_game.finish == false) {
236 /// ------------------- finish --------------------------------
237 		if (-cpos.z >= playSize.y) {
238 			if (g_game.use_keyframe) {
239 				g_game.finish = true;
240 				finish_speed = speed;
241 //				SetStationaryCamera (true);
242 			} else State::manager.RequestEnterState(GameOver);
243 		}
244 /// -----------------------------------------------------------
245 	}
246 	double disp_y = cpos.y + TUX_Y_CORR;
247 	shape->ResetNode(0);
248 	shape->TranslateNode(0, TVector3d(cpos.x, disp_y, cpos.z));
249 }
250 // --------------------------------------------------------------------
251 //			forces ***
252 // --------------------------------------------------------------------
253 
CalcRollNormal(double speed)254 TVector3d CControl::CalcRollNormal(double speed) {
255 	TVector3d vel = ProjectToPlane(ff.surfnml, ff.vel);
256 	vel.Norm();
257 
258 	double roll_angle = MAX_ROLL_ANGLE;
259 	if (is_braking) roll_angle = BRAKING_ROLL_ANGLE;
260 
261 	double angle = turn_fact * roll_angle *
262 	               std::min(1.0, std::max(0.0, ff.frict_coeff) / IDEAL_ROLL_FRIC) *
263 	               std::min(1.0, std::max(0.0, speed - minSpeed) / (IDEAL_ROLL_SPEED - minSpeed));
264 
265 	TMatrix<4, 4> rot_mat = RotateAboutVectorMatrix(vel, angle);
266 	return TransformVector(rot_mat, ff.surfnml);
267 }
268 
269 const double airlog[]  = {-1, 0, 1, 2, 3, 4, 5, 6};
270 const double airdrag[] = {2.25, 1.35, 0.6, 0, -0.35, -0.45, -0.33, -0.9};
271 
CalcAirForce()272 TVector3d CControl::CalcAirForce() {
273 	TVector3d windvec = -ff.vel;
274 	if (g_game.wind_id > 0)
275 		windvec += WIND_FACTOR * Wind.WindDrift();
276 
277 	double windspeed = windvec.Length();
278 	double re = 34600 * windspeed;
279 	int tablesize = sizeof(airdrag) / sizeof(airdrag[0]);
280 	double interpol = LinearInterp(airlog, airdrag, std::log10(re), tablesize);
281 	double dragcoeff = std::pow(10.0, interpol);
282 	double airfact = 0.104 * dragcoeff *  windspeed;
283 	return airfact * windvec;
284 }
285 
CalcSpringForce()286 TVector3d CControl::CalcSpringForce() {
287 	double springvel = DotProduct(ff.vel, ff.rollnml);
288 	double springfact = std::min(ff.compression, 0.05) * 1500;
289 	springfact += clamp(0.0, ff.compression - 0.05, 0.12) * 3000;
290 	springfact += std::max(0.0, ff.compression - 0.12 - 0.05) * 10000;
291 	springfact -= springvel * (ff.compression <= 0.05 ? 1500 : 500);
292 	springfact = clamp(0.0, springfact, 3000.0);
293 	return springfact * ff.rollnml;
294 }
295 
CalcNormalForce()296 TVector3d CControl::CalcNormalForce() {
297 	if (ff.surfdistance <= -ff.comp_depth) {
298 		ff.compression = -ff.surfdistance - ff.comp_depth;
299 		return CalcSpringForce();
300 	}
301 	return TVector3d(0, 0, 0);
302 }
303 
CalcJumpForce()304 TVector3d CControl::CalcJumpForce() {
305 	TVector3d jumpforce;
306 	if (begin_jump == true) {
307 		begin_jump = false;
308 		if (cairborne == false) {
309 			jumping = true;
310 			jump_start_time = g_game.time;
311 		} else jumping = false;
312 	}
313 	if ((jumping) && (g_game.time - jump_start_time < JUMP_FORCE_DURATION)) {
314 		double y = 294 + jump_amt * 294; // jump_amt goes from 0 to 1
315 		jumpforce.y = y;
316 
317 	} else {
318 		jumping = false;
319 	}
320 	return jumpforce; // normally scaled with 1.0
321 }
322 
CalcFrictionForce(double speed,const TVector3d & nmlforce)323 TVector3d CControl::CalcFrictionForce(double speed, const TVector3d& nmlforce) {
324 	if ((cairborne == false && speed > minFrictspeed) || g_game.finish) {
325 		double fric_f_mag = nmlforce.Length() * ff.frict_coeff;
326 		fric_f_mag = std::min(MAX_FRICT_FORCE, fric_f_mag);
327 		TVector3d frictforce = fric_f_mag * ff.frictdir;
328 
329 		double steer_angle = turn_fact * MAX_TURN_ANGLE;
330 
331 		if (std::fabs(fric_f_mag * std::sin(steer_angle * M_PI / 180)) > MAX_TURN_PERP) {
332 			steer_angle = RADIANS_TO_ANGLES(std::asin(MAX_TURN_PERP / fric_f_mag)) *
333 			              turn_fact / std::fabs(turn_fact);
334 		}
335 		TMatrix<4, 4> fric_rot_mat = RotateAboutVectorMatrix(ff.surfnml, steer_angle);
336 		frictforce = TransformVector(fric_rot_mat, frictforce);
337 		return (1.0 + MAX_TURN_PEN) * frictforce;
338 	}
339 	return TVector3d(0, 0, 0);
340 }
341 
CalcBrakeForce(double speed)342 TVector3d CControl::CalcBrakeForce(double speed) {
343 	if (g_game.finish == false) {
344 		if (cairborne == false && speed > minFrictspeed) {
345 			if (speed > minSpeed && is_braking) {
346 				return ff.frict_coeff * BRAKE_FORCE * ff.frictdir;
347 			}
348 		}
349 		return TVector3d(0, 0, 0);
350 	} else {
351 /// ------------------- finish --------------------------------
352 		if (cairborne == false) {
353 			is_braking = true;
354 			return finish_speed * g_game.finish_brake * ff.frictdir;
355 		} else {
356 			return finish_speed * FIN_AIR_BRAKE * ff.frictdir;
357 		}
358 /// -----------------------------------------------------------
359 	}
360 	return TVector3d(0, 0, 0);
361 }
362 
CalcPaddleForce(double speed)363 TVector3d CControl::CalcPaddleForce(double speed) {
364 	TVector3d paddleforce(0, 0, 0);
365 	if (is_paddling)
366 		if (g_game.time - paddle_time >= PADDLING_DURATION) is_paddling = false;
367 
368 	if (is_paddling) {
369 		if (cairborne) {
370 			paddleforce.z = -TUX_MASS * EARTH_GRAV / 4.0;
371 			paddleforce = RotateVector(corientation, paddleforce);
372 		} else {
373 			double factor = -std::min(MAX_PADD_FORCE, MAX_PADD_FORCE
374 			                          * (MAX_PADDLING_SPEED - speed) / MAX_PADDLING_SPEED
375 			                          * std::min(1.0, ff.frict_coeff / IDEAL_PADD_FRIC));
376 			paddleforce = factor * ff.frictdir;
377 		}
378 	} else return paddleforce;
379 	return PADDLE_FACT * paddleforce;
380 }
381 
CalcGravitationForce()382 TVector3d CControl::CalcGravitationForce() {
383 	if (g_game.finish == false) {
384 		return TVector3d(0, -EARTH_GRAV * TUX_MASS, 0);
385 	} else {
386 /// ---------------- finish -----------------------------------
387 		if (cairborne) return TVector3d(0, -FIN_AIR_GRAV, 0);
388 		else return TVector3d(0, -FIN_GRAV, 0);
389 /// -----------------------------------------------------------
390 	}
391 }
392 
CalcNetForce(const TVector3d & pos,const TVector3d & vel)393 TVector3d CControl::CalcNetForce(const TVector3d& pos, const TVector3d& vel) {
394 	// pos and vel are temporary, see ODE solver
395 
396 	ff.pos = pos;
397 	ff.vel = vel;
398 
399 	ff.frictdir = ff.vel;
400 	double speed = ff.frictdir.Norm();
401 	ff.frictdir *= -1.0;
402 
403 	static std::vector<double> surfweights;
404 	if (surfweights.size() != Course.TerrList.size())
405 		surfweights.resize(Course.TerrList.size());
406 	Course.GetSurfaceType(ff.pos.x, ff.pos.z, &surfweights[0]);
407 	ff.frict_coeff = ff.comp_depth = 0;
408 	for (std::size_t i=0; i<Course.TerrList.size(); i++) {
409 		ff.frict_coeff += surfweights[i] * Course.TerrList[i].friction;
410 		ff.comp_depth += surfweights[i] * Course.TerrList[i].depth;
411 	}
412 
413 	TPlane surfplane = Course.GetLocalCoursePlane(ff.pos);
414 	ff.surfnml = surfplane.nml;
415 	ff.rollnml = CalcRollNormal(speed);
416 	ff.surfdistance = DistanceToPlane(surfplane, ff.pos);
417 	cairborne = (bool)(ff.surfdistance > 0);
418 
419 	// don't change this order:
420 	TVector3d gravforce = CalcGravitationForce();
421 	TVector3d nmlforce = CalcNormalForce();
422 	TVector3d jumpforce = CalcJumpForce();
423 	TVector3d frictforce = CalcFrictionForce(speed, nmlforce);
424 	TVector3d brakeforce = CalcBrakeForce(speed);
425 	TVector3d airforce = CalcAirForce();
426 	TVector3d paddleforce = CalcPaddleForce(speed);
427 
428 	return jumpforce + gravforce + nmlforce + frictforce + airforce + brakeforce + paddleforce;
429 }
430 
431 // --------------------------------------------------------------------
432 //				ODE solver
433 // --------------------------------------------------------------------
434 
AdjustTimeStep(double h,const TVector3d & vel)435 double CControl::AdjustTimeStep(double h, const TVector3d& vel) {
436 	double speed = vel.Length();
437 	h = clamp(MIN_TIME_STEP, h, MAX_STEP_DIST / speed);
438 	h = std::min(h, MAX_TIME_STEP);
439 	return h;
440 }
441 
SolveOdeSystem(double timestep)442 void CControl::SolveOdeSystem(double timestep) {
443 	double pos_err[3], vel_err[3], tot_pos_err, tot_vel_err;
444 	double err=0, tol=0;
445 
446 	static const TOdeSolver solver;
447 	double h = ode_time_step;
448 	if (h < 0 || solver.EstimateError == nullptr)
449 		h = AdjustTimeStep(timestep, cvel);
450 	double t = 0;
451 	double tfinal = timestep;
452 
453 	TOdeData x;
454 	TOdeData y;
455 	TOdeData z;
456 	TOdeData vx;
457 	TOdeData vy;
458 	TOdeData vz;
459 
460 	TVector3d new_pos = cpos;
461 	TVector3d new_vel = cvel;
462 	TVector3d new_f   = cnet_force;
463 
464 	bool done = false;
465 	while (!done) {
466 		if (t >= tfinal) {
467 			Message("t >= tfinal in ode_system()");
468 			break;
469 		}
470 		if (1.1 * h > tfinal - t) {
471 			h = tfinal-t;
472 			done = true;
473 		}
474 		TVector3d saved_pos = new_pos;
475 		TVector3d saved_vel = new_vel;
476 		TVector3d saved_f = new_f;
477 
478 		bool failed = false;
479 		for (;;) {
480 			solver.InitOdeData(&x, new_pos.x, h);
481 			solver.InitOdeData(&y, new_pos.y, h);
482 			solver.InitOdeData(&z, new_pos.z, h);
483 			solver.InitOdeData(&vx, new_vel.x, h);
484 			solver.InitOdeData(&vy, new_vel.y, h);
485 			solver.InitOdeData(&vz, new_vel.z, h);
486 
487 			solver.UpdateEstimate(&x, 0, new_vel.x);
488 			solver.UpdateEstimate(&y, 0, new_vel.y);
489 			solver.UpdateEstimate(&z, 0, new_vel.z);
490 			solver.UpdateEstimate(&vx, 0, new_f.x / TUX_MASS);
491 			solver.UpdateEstimate(&vy, 0, new_f.y / TUX_MASS);
492 			solver.UpdateEstimate(&vz, 0, new_f.z / TUX_MASS);
493 
494 			for (int i=1; i < solver.NumEstimates(); i++) {
495 				new_pos.x = solver.NextValue(&x, i);
496 				new_pos.y = solver.NextValue(&y, i);
497 				new_pos.z = solver.NextValue(&z, i);
498 				new_vel.x = solver.NextValue(&vx, i);
499 				new_vel.y = solver.NextValue(&vy, i);
500 				new_vel.z = solver.NextValue(&vz, i);
501 
502 				solver.UpdateEstimate(&x, i, new_vel.x);
503 				solver.UpdateEstimate(&y, i, new_vel.y);
504 				solver.UpdateEstimate(&z, i, new_vel.z);
505 
506 				new_f = CalcNetForce(new_pos, new_vel);
507 
508 				solver.UpdateEstimate(&vx, i, new_f.x / TUX_MASS);
509 				solver.UpdateEstimate(&vy, i, new_f.y / TUX_MASS);
510 				solver.UpdateEstimate(&vz, i, new_f.z / TUX_MASS);
511 			}
512 
513 			new_pos.x = solver.FinalEstimate(&x);
514 			new_pos.y = solver.FinalEstimate(&y);
515 			new_pos.z = solver.FinalEstimate(&z);
516 
517 			new_vel.x = solver.FinalEstimate(&vx);
518 			new_vel.y = solver.FinalEstimate(&vy);
519 			new_vel.z = solver.FinalEstimate(&vz);
520 
521 			if (solver.EstimateError != nullptr) {
522 				pos_err[0] = solver.EstimateError(&x);
523 				pos_err[1] = solver.EstimateError(&y);
524 				pos_err[2] = solver.EstimateError(&z);
525 
526 				vel_err[0] = solver.EstimateError(&vx);
527 				vel_err[1] = solver.EstimateError(&vy);
528 				vel_err[2] = solver.EstimateError(&vz);
529 
530 				tot_pos_err = 0.;
531 				tot_vel_err = 0.;
532 
533 				for (int i=0; i<3; i++) {
534 					pos_err[i] *= pos_err[i];
535 					tot_pos_err += pos_err[i];
536 					vel_err[i] *= vel_err[i];
537 					tot_vel_err += vel_err[i];
538 				}
539 				tot_pos_err = std::sqrt(tot_pos_err);
540 				tot_vel_err = std::sqrt(tot_vel_err);
541 				if (tot_pos_err / MAX_POS_ERR > tot_vel_err / MAX_VEL_ERR) {
542 					err = tot_pos_err;
543 					tol = MAX_POS_ERR;
544 				} else {
545 					err = tot_vel_err;
546 					tol = MAX_VEL_ERR;
547 				}
548 
549 				if (err > tol  && h > MIN_TIME_STEP + EPS) {
550 					done = false;
551 					if (!failed) {
552 						failed = true;
553 						h *=  std::max(0.5, 0.8 * std::pow(tol/err, solver.TimestepExponent()));
554 					} else h *= 0.5;
555 
556 					h = AdjustTimeStep(h, saved_vel);
557 					new_pos = saved_pos;
558 					new_vel = saved_vel;
559 					new_f = saved_f;
560 				} else break;
561 			} else break;
562 		}
563 
564 		t = t + h;
565 		double speed = new_vel.Length();
566 		if (param.perf_level > 2) generate_particles(this, h, new_pos, speed);
567 
568 		new_f = CalcNetForce(new_pos, new_vel);
569 
570 		if (!failed && solver.EstimateError != nullptr) {
571 			double temp = 1.25 * std::pow(err / tol, solver.TimestepExponent());
572 			if (temp > 0.2) h = h / temp;
573 			else h = 5.0 * h;
574 		}
575 		h = AdjustTimeStep(h, new_vel);
576 		AdjustTreeCollision(new_pos, &new_vel);
577 //		if (g_game.finish) new_vel = ScaleVector (0.99,new_vel);
578 		CheckItemCollection(new_pos);
579 	}
580 	ode_time_step = h;
581 	cnet_force = new_f;
582 
583 	cvel = new_vel;
584 	last_pos = cpos;
585 	cpos = new_pos;
586 
587 	double step = (cpos - last_pos).Length();
588 	way += step;
589 }
590 
591 // --------------------------------------------------------------------
592 //				update tux position
593 // --------------------------------------------------------------------
594 
UpdatePlayerPos(float timestep)595 void CControl::UpdatePlayerPos(float timestep) {
596 	CCharShape *shape = g_game.character->shape;
597 	double paddling_factor;
598 	double flap_factor;
599 	double dist_from_surface;
600 
601 	if (g_game.finish) {
602 /// --------------------- finish ------------------------------
603 		minSpeed = 0;
604 		minFrictspeed = 0;
605 /// -----------------------------------------------------------
606 	} else {
607 		minSpeed = MIN_TUX_SPEED;
608 		minFrictspeed = MIN_FRICT_SPEED;
609 	}
610 
611 	if (timestep > 2 * EPS) SolveOdeSystem(timestep);
612 
613 	TPlane surf_plane = Course.GetLocalCoursePlane(cpos);
614 	TVector3d surf_nml = surf_plane.nml; // normal vector of terrain
615 	dist_from_surface = DistanceToPlane(surf_plane, cpos);
616 
617 	double speed = cvel.Length();
618 	AdjustVelocity();
619 	AdjustPosition(surf_plane, dist_from_surface);
620 	SetTuxPosition(speed);	// speed only to set finish_speed
621 	shape->AdjustOrientation(this, timestep, dist_from_surface, surf_nml);
622 
623 	flap_factor = 0;
624 	if (is_paddling) {
625 		double factor;
626 		factor = (g_game.time - paddle_time) / PADDLING_DURATION;
627 		if (cairborne) {
628 			paddling_factor = 0;
629 			flap_factor = factor;
630 		} else {
631 			paddling_factor = factor;
632 			flap_factor = 0;
633 		}
634 	} else {
635 		paddling_factor = 0;
636 	}
637 
638 	TVector3d local_force = RotateVector
639 	                        (ConjugateQuaternion(corientation), cnet_force);
640 
641 	if (jumping)
642 		flap_factor = (g_game.time - jump_start_time) / JUMP_FORCE_DURATION;
643 
644 	shape->AdjustJoints(turn_animation, is_braking, paddling_factor, speed,
645 	                    local_force, flap_factor);
646 }
647