1 //
2 // SuperTuxKart - a fun racing game with go-kart
3 // Copyright (C) 2004-2016 Steve Baker <sjbaker1@airmail.net>
4 // Copyright (C) 2006-2016 SuperTuxKart-Team, Joerg Henrichs, Steve Baker
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 3
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 #include "karts/kart.hpp"
21
22 #include "audio/sfx_manager.hpp"
23 #include "audio/sfx_base.hpp"
24 #include "challenges/challenge_status.hpp"
25 #include "challenges/unlock_manager.hpp"
26 #include "config/player_manager.hpp"
27 #include "config/user_config.hpp"
28 #include "font/bold_face.hpp"
29 #include "font/font_manager.hpp"
30 #include "graphics/camera.hpp"
31 #include "graphics/central_settings.hpp"
32 #include "graphics/explosion.hpp"
33 #include "graphics/irr_driver.hpp"
34 #include "graphics/material.hpp"
35 #include "graphics/material_manager.hpp"
36 #include "graphics/particle_emitter.hpp"
37 #include "graphics/particle_kind_manager.hpp"
38 #include "graphics/shadow.hpp"
39 #include "graphics/skid_marks.hpp"
40 #include "graphics/slip_stream.hpp"
41 #include "graphics/stk_text_billboard.hpp"
42 #include "graphics/stars.hpp"
43 #include "guiengine/scalable_font.hpp"
44 #include "io/file_manager.hpp"
45 #include "items/attachment.hpp"
46 #include "items/item_manager.hpp"
47 #include "items/powerup.hpp"
48 #include "items/projectile_manager.hpp"
49 #include "karts/abstract_characteristic.hpp"
50 #include "karts/abstract_kart_animation.hpp"
51 #include "karts/cached_characteristic.hpp"
52 #include "karts/controller/local_player_controller.hpp"
53 #include "karts/controller/end_controller.hpp"
54 #include "karts/controller/spare_tire_ai.hpp"
55 #include "karts/explosion_animation.hpp"
56 #include "karts/kart_gfx.hpp"
57 #include "karts/kart_model.hpp"
58 #include "karts/kart_properties.hpp"
59 #include "karts/kart_properties_manager.hpp"
60 #include "karts/kart_rewinder.hpp"
61 #include "karts/max_speed.hpp"
62 #include "karts/rescue_animation.hpp"
63 #include "karts/skidding.hpp"
64 #include "main_loop.hpp"
65 #include "modes/capture_the_flag.hpp"
66 #include "modes/linear_world.hpp"
67 #include "modes/overworld.hpp"
68 #include "modes/soccer_world.hpp"
69 #include "network/compress_network_body.hpp"
70 #include "network/network_config.hpp"
71 #include "network/protocols/client_lobby.hpp"
72 #include "network/race_event_manager.hpp"
73 #include "network/rewind_info.hpp"
74 #include "network/rewind_manager.hpp"
75 #include "physics/btKart.hpp"
76 #include "physics/btKartRaycast.hpp"
77 #include "physics/physics.hpp"
78 #include "race/history.hpp"
79 #include "tracks/terrain_info.hpp"
80 #include "tracks/drive_graph.hpp"
81 #include "tracks/drive_node.hpp"
82 #include "tracks/track.hpp"
83 #include "tracks/track_manager.hpp"
84 #include "tracks/track_sector.hpp"
85 #include "utils/constants.hpp"
86 #include "utils/helpers.hpp"
87 #include "utils/log.hpp" //TODO: remove after debugging is done
88 #include "utils/profiler.hpp"
89 #include "utils/string_utils.hpp"
90 #include "utils/translation.hpp"
91 #include "utils/vs.hpp"
92
93 #include <ICameraSceneNode.h>
94 #include <ISceneManager.h>
95
96 #include <algorithm> // for min and max
97 #include <iostream>
98 #include <limits>
99 #include <cmath>
100
101
102 #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
103 // Disable warning for using 'this' in base member initializer list
104 # pragma warning(disable:4355)
105 #endif
106
107 /** The kart constructor.
108 * \param ident The identifier for the kart model to use.
109 * \param position The position (or rank) for this kart (between 1 and
110 * number of karts). This is used to determine the start position.
111 * \param init_transform The initial position and rotation for this kart.
112 */
Kart(const std::string & ident,unsigned int world_kart_id,int position,const btTransform & init_transform,HandicapLevel handicap,std::shared_ptr<RenderInfo> ri)113 Kart::Kart (const std::string& ident, unsigned int world_kart_id,
114 int position, const btTransform& init_transform,
115 HandicapLevel handicap, std::shared_ptr<RenderInfo> ri)
116 : AbstractKart(ident, world_kart_id, position, init_transform,
117 handicap, ri)
118
119 #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
120 # pragma warning(1:4355)
121 #endif
122 {
123 m_max_speed = new MaxSpeed(this);
124 m_terrain_info = new TerrainInfo();
125 m_powerup = new Powerup(this);
126 m_initial_position = position;
127 m_race_result = false;
128 m_wheel_box = NULL;
129 m_collision_particles = NULL;
130 m_controller = NULL;
131 m_saved_controller = NULL;
132 m_consumption_per_tick = stk_config->ticks2Time(1) *
133 m_kart_properties->getNitroConsumption();
134 m_fire_clicked = 0;
135 m_default_suspension_force = 0.0f;
136 m_boosted_ai = false;
137 m_type = RaceManager::KT_AI;
138 m_flying = false;
139
140 m_xyz_history_size = stk_config->time2Ticks(XYZ_HISTORY_TIME);
141
142 Vec3 initial_position = getXYZ();
143 for (int i=0;i<m_xyz_history_size;i++)
144 {
145 m_previous_xyz.push_back(initial_position);
146 m_previous_xyz_times.push_back(0.0f);
147 }
148
149 // Initialize custom sound vector (TODO: add back when properly done)
150 // m_custom_sounds.resize(SFXManager::NUM_CUSTOMS);
151
152 // Set position and heading:
153 m_reset_transform = init_transform;
154 m_last_factor_engine_sound = 0.0f;
155
156 m_kart_model->setKart(this);
157
158 // Create SFXBase for each custom sound (TODO: add back when properly done)
159 /*
160 for (int n = 0; n < SFXManager::NUM_CUSTOMS; n++)
161 {
162 int id = m_kart_properties->getCustomSfxId((SFXManager::CustomSFX)n);
163
164 // If id == -1 the custom sound was not defined in the .irrkart config file
165 if (id != -1)
166 {
167 m_custom_sounds[n] = SFXManager::get()->newSFX(id);
168 }
169 }*/
170
171 m_horn_sound = SFXManager::get()->getBuffer("horn");
172 m_crash_sounds[0] = SFXManager::get()->getBuffer("crash");
173 m_crash_sounds[1] = SFXManager::get()->getBuffer("crash2");
174 m_crash_sounds[2] = SFXManager::get()->getBuffer("crash3");
175 m_goo_sound = SFXManager::get()->getBuffer("goo");
176 m_boing_sound = SFXManager::get()->getBuffer("boing");
177
178 m_engine_sound = SFXManager::get()->createSoundSource(m_kart_properties->getEngineSfxType());
179
180 for (int i = 0; i < EMITTER_COUNT; i++)
181 m_emitters[i] = SFXManager::get()->createSoundSource("crash");
182
183 m_skid_sound = SFXManager::get()->createSoundSource( "skid" );
184 m_nitro_sound = SFXManager::get()->createSoundSource( "nitro" );
185 m_terrain_sound = NULL;
186 m_last_sound_material = NULL;
187 m_previous_terrain_sound = NULL;
188 } // Kart
189
190 // -----------------------------------------------------------------------------
191 /** This is a second initialisation phase, necessary since in the constructor
192 * virtual functions are not called for any superclasses.
193 * \param type Type of the kart.
194 */
init(RaceManager::KartType type)195 void Kart::init(RaceManager::KartType type)
196 {
197 m_type = type;
198
199 // In multiplayer mode, sounds are NOT positional
200 if (RaceManager::get()->getNumLocalPlayers() > 1)
201 {
202 float factor = 1.0f / RaceManager::get()->getNumberOfKarts();
203 // players have louder sounds than AIs
204 if (type == RaceManager::KT_PLAYER)
205 factor = std::min(1.0f, RaceManager::get()->getNumLocalPlayers()/2.0f);
206
207 for (int i = 0; i < EMITTER_COUNT; i++)
208 m_emitters[i]->setVolume(factor);
209
210 m_skid_sound->setVolume(factor);
211 m_nitro_sound->setVolume(factor);
212 } // if getNumLocalPlayers > 1
213
214 if(!m_engine_sound)
215 {
216 Log::error("Kart","Could not allocate a sfx object for the kart. Further errors may ensue!");
217 }
218
219 loadData(type, UserConfigParams::m_animated_characters);
220 reset();
221 } // init
222
223 // ----------------------------------------------------------------------------
changeKart(const std::string & new_ident,HandicapLevel handicap,std::shared_ptr<RenderInfo> ri)224 void Kart::changeKart(const std::string& new_ident,
225 HandicapLevel handicap,
226 std::shared_ptr<RenderInfo> ri)
227 {
228 AbstractKart::changeKart(new_ident, handicap, ri);
229 m_kart_model->setKart(this);
230
231 scene::ISceneNode* old_node = m_node;
232 loadData(m_type, UserConfigParams::m_animated_characters);
233 m_wheel_box = NULL;
234
235 if (LocalPlayerController* lpc =
236 dynamic_cast<LocalPlayerController*>(getController()))
237 lpc->initParticleEmitter();
238
239 if (old_node)
240 old_node->remove();
241
242 // Reset 1 more time (add back the body)
243 reset();
244
245 for (int i = 0; i < m_vehicle->getNumWheels(); i++)
246 {
247 btWheelInfo &wi = m_vehicle->getWheelInfo(i);
248 wi.m_raycastInfo.m_suspensionLength = m_default_suspension_force /
249 m_vehicle->getNumWheels();
250 }
251 m_graphical_y_offset = -m_default_suspension_force /
252 m_vehicle->getNumWheels() + m_kart_model->getLowestPoint();
253 m_kart_model->setDefaultSuspension();
254 startEngineSFX();
255 } // changeKart
256
257 // ----------------------------------------------------------------------------
258 /** The destructor frees the memory of this kart, but note that the actual kart
259 * model is still stored in the kart_properties (m_kart_model variable), so
260 * it is not reloaded).
261 */
~Kart()262 Kart::~Kart()
263 {
264 // Delete all custom sounds (TODO: add back when properly done)
265 /*
266 for (int n = 0; n < SFXManager::NUM_CUSTOMS; n++)
267 {
268 if (m_custom_sounds[n] != NULL)
269 SFXManager::get()->deleteSFX(m_custom_sounds[n]);
270 }*/
271
272 m_engine_sound->deleteSFX();
273 m_skid_sound ->deleteSFX();
274
275 for (int i = 0; i < EMITTER_COUNT; i++)
276 m_emitters[i]->deleteSFX();
277
278 m_nitro_sound ->deleteSFX();
279 if(m_terrain_sound) m_terrain_sound->deleteSFX();
280 if(m_previous_terrain_sound) m_previous_terrain_sound->deleteSFX();
281 if(m_collision_particles) delete m_collision_particles;
282
283 if (m_wheel_box) m_wheel_box->remove();
284
285 // Ghost karts don't have a body
286 if(m_body)
287 {
288 Physics::get()->removeKart(this);
289 }
290
291 delete m_max_speed;
292 delete m_terrain_info;
293 delete m_powerup;
294
295 if(m_controller)
296 delete m_controller;
297 if(m_saved_controller)
298 delete m_saved_controller;
299 } // ~Kart
300
301 //-----------------------------------------------------------------------------
302 /** Reset before a new race. It will remove all attachments, and
303 * puts the kart back at its original start position.
304 */
reset()305 void Kart::reset()
306 {
307 if (m_flying && !isGhostKart())
308 {
309 m_flying = false;
310 stopFlying();
311 }
312
313 m_network_finish_check_ticks = 0;
314 m_network_confirmed_finish_ticks = 0;
315 // Add karts back in case that they have been removed (i.e. in battle
316 // mode) - but only if they actually have a body (e.g. ghost karts
317 // don't have one).
318 if(m_body)
319 {
320 Physics::get()->removeKart(this);
321 Physics::get()->addKart(this);
322 }
323
324 m_min_nitro_ticks = 0;
325 m_energy_to_min_ratio = 0;
326 m_consumption_per_tick = stk_config->ticks2Time(1) *
327 m_kart_properties->getNitroConsumption();
328
329 // Reset star effect in case that it is currently being shown.
330 if (m_stars_effect)
331 m_stars_effect->reset();
332 m_max_speed->reset();
333 m_powerup->reset();
334
335 // Reset animations and wheels
336 m_kart_model->reset();
337
338 // If the controller was replaced (e.g. replaced by end controller),
339 // restore the original controller.
340 if(m_saved_controller)
341 {
342 delete m_controller;
343 m_controller = m_saved_controller;
344 m_saved_controller = NULL;
345 }
346 m_kart_model->setAnimation(KartModel::AF_DEFAULT);
347 m_attachment->reset();
348 m_kart_gfx->reset();
349 m_skidding->reset();
350
351 m_weight = 0.0f;
352 updateWeight();
353
354 #ifndef SERVER_ONLY
355 if (m_collision_particles)
356 m_collision_particles->setCreationRateAbsolute(0.0f);
357 #endif
358
359 unsetSquash();
360
361 m_last_used_powerup = PowerupManager::POWERUP_NOTHING;
362 m_race_position = m_initial_position;
363 m_finished_race = false;
364 m_eliminated = false;
365 m_finish_time = 0.0f;
366 m_bubblegum_ticks = 0;
367 m_bubblegum_torque_sign = true;
368 m_invulnerable_ticks = 0;
369 m_min_nitro_ticks = 0;
370 m_energy_to_min_ratio = 0;
371 m_collected_energy = 0;
372 m_bounce_back_ticks = 0;
373 m_brake_ticks = 0;
374 m_ticks_last_crash = 0;
375 m_ticks_last_zipper = 0;
376 m_speed = 0.0f;
377 m_current_lean = 0.0f;
378 m_falling_time = 0.0f;
379 m_view_blocked_by_plunger = 0;
380 m_has_caught_nolok_bubblegum = false;
381 m_is_jumping = false;
382 m_flying = false;
383 m_startup_boost = 0.0f;
384
385 if (m_node)
386 m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f));
387
388 for (int i=0;i<m_xyz_history_size;i++)
389 {
390 m_previous_xyz[i] = getXYZ();
391 m_previous_xyz_times[i] = 0.0f;
392 }
393 m_time_previous_counter = 0.0f;
394
395 // In case that the kart was in the air, in which case its
396 // linear damping is 0
397 if(m_body)
398 m_body->setDamping(m_kart_properties->getStabilityChassisLinearDamping(),
399 m_kart_properties->getStabilityChassisAngularDamping());
400
401 if(m_terrain_sound)
402 {
403 m_terrain_sound->deleteSFX();
404 m_terrain_sound = NULL;
405 }
406 if(m_previous_terrain_sound)
407 {
408 m_previous_terrain_sound->deleteSFX();
409 m_previous_terrain_sound = NULL;
410 }
411
412 if(m_engine_sound)
413 m_engine_sound->stop();
414
415 m_controls.reset();
416 m_slipstream->reset();
417
418 if(m_vehicle)
419 {
420 for (unsigned int i = 0; i < 4; i++)
421 {
422 m_vehicle->getWheelInfo(i).m_steering = 0;
423 }
424
425 m_vehicle->reset();
426 }
427
428 setTrans(m_reset_transform);
429
430 applyEngineForce (0.0f);
431
432 AbstractKart::reset();
433 #ifndef SERVER_ONLY
434 if (m_skidmarks)
435 {
436 m_skidmarks->reset();
437 }
438 #endif
439
440 Vec3 front(0, 0, getKartLength()*0.5f);
441 m_xyz_front = getTrans()(front);
442
443 // Base on update() below, require if starting point of kart is not near
444 // 0, 0, 0 (like in battle arena)
445 m_terrain_info->update(getTrans().getBasis(),
446 getTrans().getOrigin() + getTrans().getBasis() * Vec3(0, 0.3f, 0));
447
448 // Reset is also called when the kart is created, at which time
449 // m_controller is not yet defined, so this has to be tested here.
450 if(m_controller)
451 m_controller->reset();
452
453 // 3 strikes mode can hide the wheels
454 scene::ISceneNode** wheels = getKartModel()->getWheelNodes();
455 if(wheels[0]) wheels[0]->setVisible(true);
456 if(wheels[1]) wheels[1]->setVisible(true);
457 if(wheels[2]) wheels[2]->setVisible(true);
458 if(wheels[3]) wheels[3]->setVisible(true);
459
460 } // reset
461
462 // -----------------------------------------------------------------------------
setXYZ(const Vec3 & a)463 void Kart::setXYZ(const Vec3& a)
464 {
465 AbstractKart::setXYZ(a);
466 Vec3 front(0, 0, getKartLength()*0.5f);
467 m_xyz_front = getTrans()(front);
468 } // setXYZ
469
470 // -----------------------------------------------------------------------------
increaseMaxSpeed(unsigned int category,float add_speed,float engine_force,int duration,int fade_out_time)471 void Kart::increaseMaxSpeed(unsigned int category, float add_speed,
472 float engine_force, int duration,
473 int fade_out_time)
474 {
475 m_max_speed->increaseMaxSpeed(category, add_speed, engine_force,
476 duration, fade_out_time);
477 } // increaseMaxSpeed
478
479 // -----------------------------------------------------------------------------
instantSpeedIncrease(unsigned int category,float add_max_speed,float speed_boost,float engine_force,int duration,int fade_out_time)480 void Kart::instantSpeedIncrease(unsigned int category, float add_max_speed,
481 float speed_boost, float engine_force,
482 int duration, int fade_out_time)
483 {
484 m_max_speed->instantSpeedIncrease(category, add_max_speed, speed_boost,
485 engine_force, duration, fade_out_time);
486 } // instantSpeedIncrease
487
488 // -----------------------------------------------------------------------------
setSlowdown(unsigned int category,float max_speed_fraction,int fade_in_time)489 void Kart::setSlowdown(unsigned int category, float max_speed_fraction,
490 int fade_in_time)
491 {
492 m_max_speed->setSlowdown(category, max_speed_fraction, fade_in_time);
493 } // setSlowdown
494
495 // -----------------------------------------------------------------------------
getCurrentMaxSpeed() const496 float Kart::getCurrentMaxSpeed() const
497 {
498 return m_max_speed->getCurrentMaxSpeed();
499 } // getCurrentMaxSpeed
500 // -----------------------------------------------------------------------------
getSpeedIncreaseTicksLeft(unsigned int category) const501 int Kart::getSpeedIncreaseTicksLeft(unsigned int category) const
502 {
503 return m_max_speed->getSpeedIncreaseTicksLeft(category);
504 } // getSpeedIncreaseTimeLeft
505
506 // -----------------------------------------------------------------------------
setBoostAI(bool boosted)507 void Kart::setBoostAI(bool boosted)
508 {
509 m_boosted_ai = boosted;
510 } // setBoostAI
511 // -----------------------------------------------------------------------------
getBoostAI() const512 bool Kart::getBoostAI() const
513 {
514 return m_boosted_ai;
515 } // getBoostAI
516
517 // -----------------------------------------------------------------------------
518 /** Returns the current material the kart is on. */
getMaterial() const519 const Material *Kart::getMaterial() const
520 {
521 return m_terrain_info->getMaterial();
522 } // getMaterial
523
524 // -----------------------------------------------------------------------------
525 /** Returns the previous material the kart was one (which might be
526 * the same as getMaterial() ). */
getLastMaterial() const527 const Material *Kart::getLastMaterial() const
528 {
529 return m_terrain_info->getLastMaterial();
530 } // getLastMaterial
531 // -----------------------------------------------------------------------------
532 /** Returns the pitch of the terrain depending on the heading. */
getTerrainPitch(float heading) const533 float Kart::getTerrainPitch(float heading) const
534 {
535 return m_terrain_info->getTerrainPitch(heading);
536 } // getTerrainPitch
537
538 // -----------------------------------------------------------------------------
539 /** Returns the height of the terrain. we're currently above */
getHoT() const540 float Kart::getHoT() const
541 {
542 return m_terrain_info->getHoT();
543 } // getHoT
544
545 // ----------------------------------------------------------------------------
546 /** Sets the powerup this kart has collected.
547 * \param t Type of the powerup.
548 * \param n Number of powerups collected.
549 */
setPowerup(PowerupManager::PowerupType t,int n)550 void Kart::setPowerup(PowerupManager::PowerupType t, int n)
551 {
552 m_powerup->set(t, n);
553 } // setPowerup
554
555 // ----------------------------------------------------------------------------
556 /** Sets the powerup this kart has last used. Number is always 1.
557 * \param t Type of the powerup.
558 */
setLastUsedPowerup(PowerupManager::PowerupType t)559 void Kart::setLastUsedPowerup(PowerupManager::PowerupType t)
560 {
561 m_last_used_powerup = t;
562 } // setLastUsedPowerup
563
564 // -----------------------------------------------------------------------------
getNumPowerup() const565 int Kart::getNumPowerup() const
566 {
567 return m_powerup->getNum();
568 } // getNumPowerup
569
570 // -----------------------------------------------------------------------------
571 /** Saves the old controller in m_saved_controller and stores a new
572 * controller. The save controller is needed in case of a reset.
573 * \param controller The new controller to use (atm it's always an
574 * end controller).
575 */
setController(Controller * controller)576 void Kart::setController(Controller *controller)
577 {
578 assert(m_saved_controller==NULL);
579 m_saved_controller = m_controller;
580 m_controller = controller;
581 } // setController
582
583 // -----------------------------------------------------------------------------
584 /** Sets the position in race this kart has .
585 * The position in this race for this kart (1<=p<=n)
586 */
setPosition(int p)587 void Kart::setPosition(int p)
588 {
589 m_controller->setPosition(p);
590 m_race_position = p;
591 } // setPosition
592
593 // -----------------------------------------------------------------------------
594 /** Sets that the view is blocked by a plunger. The duration depends on
595 * the difficulty, see KartPorperties getPlungerInFaceTime.
596 */
blockViewWithPlunger()597 void Kart::blockViewWithPlunger()
598 {
599 // Avoid that a plunger extends the plunger time
600 if(m_view_blocked_by_plunger<=0 && !isShielded())
601 {
602 m_view_blocked_by_plunger = (int16_t)
603 stk_config->time2Ticks(m_kart_properties->getPlungerInFaceTime());
604 }
605 if(isShielded())
606 {
607 decreaseShieldTime();
608 }
609 } // blockViewWithPlunger
610
611 // -----------------------------------------------------------------------------
612 /** Returns a transform that will align an object with the kart: the heading
613 * and the pitch will be set appropriately. A custom pitch value can be
614 * specified in order to overwrite the terrain pitch (which would be used
615 * otherwise).
616 * \param custom_pitch Pitch value to overwrite the terrain pitch. A value of
617 * -1 indicates that the custom_pitch value should not be used, instead
618 * the actual pitch of the terrain is to be used.
619 */
getAlignedTransform(const float custom_pitch)620 btTransform Kart::getAlignedTransform(const float custom_pitch)
621 {
622 btTransform trans = getTrans();
623 /*
624 float pitch = (custom_pitch == -1 ? getTerrainPitch(getHeading())
625 : custom_pitch);
626
627 btMatrix3x3 m;
628 m.setEulerZYX(pitch, getHeading()+m_skidding->getVisualSkidRotation(),
629 0.0f);
630 trans.setBasis(m);
631 */
632 btTransform trans2;
633 trans2.setIdentity();
634 trans2.setRotation(btQuaternion(m_skidding->getVisualSkidRotation(), 0, 0));
635 trans *= trans2;
636
637 return trans;
638 } // getAlignedTransform
639
640 // ----------------------------------------------------------------------------
getTimeFullSteer(float steer) const641 float Kart::getTimeFullSteer(float steer) const
642 {
643 return m_kart_properties->getTurnTimeFullSteer().get(steer);
644 } // getTimeFullSteer
645
646 // ----------------------------------------------------------------------------
647 /** Creates the physical representation of this kart. Atm it uses the actual
648 * extention of the kart model to determine the size of the collision body.
649 */
createPhysics()650 void Kart::createPhysics()
651 {
652 // First: Create the chassis of the kart
653 // -------------------------------------
654 const float kart_length = getKartLength();
655 const float kart_width = getKartWidth();
656 float kart_height = getKartHeight();
657
658 // improve physics for tall karts
659 if (kart_height > kart_length*0.6f)
660 {
661 kart_height = kart_length*0.6f;
662 }
663
664 const Vec3 &bevel = m_kart_properties->getBevelFactor();
665 Vec3 wheel_pos[4];
666
667 Vec3 orig_factor(1, 1, 1 - bevel.getZ());
668 Vec3 bevel_factor(1.0f - bevel.getX(), 1.0f - bevel.getY(), 1.0f);
669 btConvexHullShape *hull = new btConvexHullShape();
670 for (int y = -1; y <= 1; y += 2)
671 {
672 for (int z = -1; z <= 1; z += 2)
673 {
674 for (int x = -1; x <= 1; x += 2)
675 {
676 Vec3 p(x*kart_width *0.5f,
677 y*kart_height *0.5f,
678 z*kart_length *0.5f);
679
680 hull->addPoint(p*orig_factor);
681 // Only add bevelled point if bevel is defined (i.e.!=0)
682 if(bevel.length2()>0)
683 hull->addPoint(p*bevel_factor);
684 if (y == -1)
685 {
686 int index = (x + 1) / 2 + 1 - z; // get index of wheel
687 float f = m_kart_properties->getPhysicalWheelPosition();
688 // f < 0 indicates to use the old physics position, i.e.
689 // to place the wheels outside of the chassis
690 if(f<0)
691 {
692 // All wheel positions are relative to the center of
693 // the collision shape.
694 wheel_pos[index].setX(x*0.5f*kart_width);
695 wheel_pos[index].setZ((0.5f*kart_length-0.25f)* z);
696 }
697 else
698 {
699 // Store the x/z position for the wheels as a weighted average
700 // of the two bevelled points (y is set below).
701 wheel_pos[index] = p*(orig_factor*(1.0f - f) + bevel_factor*f);
702 }
703 // The y position of the wheels (i.e. the points where
704 // the suspension is attached to) is just at the
705 // bottom of the kart (independent of collision shape).
706 // That is half the kart height down.
707 wheel_pos[index].setY(-0.5f*kart_height);
708 } // if y==-1
709 } // for x
710 } // for z
711 } // for y
712
713 // This especially enables proper drawing of the point cloud
714 hull->initializePolyhedralFeatures();
715
716 btTransform shiftCenterOfGravity;
717 shiftCenterOfGravity.setIdentity();
718 // Shift center of gravity downwards, so that the kart
719 // won't topple over too easy.
720 shiftCenterOfGravity.setOrigin(m_kart_properties->getGravityCenterShift());
721 m_kart_chassis.reset(new btCompoundShape());
722 m_kart_chassis->addChildShape(shiftCenterOfGravity, hull);
723
724 // Set mass and inertia
725 // --------------------
726 float mass = m_kart_properties->getMass();
727
728 // Position the chassis
729 // --------------------
730 btTransform trans;
731 trans.setIdentity();
732 createBody(mass, trans, m_kart_chassis.get(),
733 m_kart_properties->getRestitution(0.0f));
734 std::vector<float> ang_fact = m_kart_properties->getStabilityAngularFactor();
735 // The angular factor (with X and Z values <1) helps to keep the kart
736 // upright, especially in case of a collision.
737 m_body->setAngularFactor(Vec3(ang_fact[0], ang_fact[1], ang_fact[2]));
738 m_body->setFriction(m_kart_properties->getFrictionKartFriction());
739 m_user_pointer.set(this);
740 m_body->setDamping(m_kart_properties->getStabilityChassisLinearDamping(),
741 m_kart_properties->getStabilityChassisAngularDamping() );
742
743 // Reset velocities
744 // ----------------
745 m_body->setLinearVelocity (btVector3(0.0f,0.0f,0.0f));
746 m_body->setAngularVelocity(btVector3(0.0f,0.0f,0.0f));
747
748 // Create the actual vehicle
749 // -------------------------
750 m_vehicle_raycaster.reset(
751 new btKartRaycaster(Physics::get()->getPhysicsWorld(),
752 stk_config->m_smooth_normals &&
753 Track::getCurrentTrack()->smoothNormals()));
754 m_vehicle.reset(new btKart(m_body.get(), m_vehicle_raycaster.get(), this));
755
756 // never deactivate the vehicle
757 m_body->setActivationState(DISABLE_DEACTIVATION);
758
759 // Add wheels
760 // ----------
761 float suspension_rest = m_kart_properties->getSuspensionRest();
762
763 btVector3 wheel_direction(0.0f, -1.0f, 0.0f);
764 btVector3 wheel_axle(-1.0f, 0.0f, 0.0f);
765
766 btKart::btVehicleTuning tuning;
767 tuning.m_maxSuspensionTravel =
768 m_kart_properties->getSuspensionTravel();
769 tuning.m_maxSuspensionForce =
770 m_kart_properties->getSuspensionMaxForce();
771
772 const Vec3 &cs = m_kart_properties->getGravityCenterShift();
773 for(unsigned int i=0; i<4; i++)
774 {
775 bool is_front_wheel = i<2;
776 btWheelInfo& wheel = m_vehicle->addWheel(
777 wheel_pos[i]+cs,
778 wheel_direction, wheel_axle, suspension_rest,
779 m_kart_model->getWheelGraphicsRadius(i),
780 tuning, is_front_wheel);
781 wheel.m_suspensionStiffness = m_kart_properties->getSuspensionStiffness();
782 wheel.m_wheelsDampingRelaxation = m_kart_properties->getWheelsDampingRelaxation();
783 wheel.m_wheelsDampingCompression = m_kart_properties->getWheelsDampingCompression();
784 wheel.m_frictionSlip = m_kart_properties->getFrictionSlip();
785 wheel.m_rollInfluence = m_kart_properties->getStabilityRollInfluence();
786 }
787 // Body to be added in reset() which allows complete reset kart when
788 // restarting the race
789
790 } // createPhysics
791
792 // ----------------------------------------------------------------------------
793
flyUp()794 void Kart::flyUp()
795 {
796 m_flying = true;
797 Moveable::flyUp();
798 } // flyUp
799
800 // ----------------------------------------------------------------------------
flyDown()801 void Kart::flyDown()
802 {
803 if (isNearGround())
804 {
805 stopFlying();
806 m_flying = false;
807 }
808 else
809 {
810 Moveable::flyDown();
811 }
812 } // flyDown
813
814 // ----------------------------------------------------------------------------
815 /** Starts the engine sound effect. Called once the track intro phase is over.
816 */
startEngineSFX()817 void Kart::startEngineSFX()
818 {
819 if(!m_engine_sound)
820 return;
821
822 // In multiplayer mode, sounds are NOT positional (because we have
823 // multiple listeners) so the engine sounds of all AIs is constantly
824 // heard. So reduce volume of all sounds.
825 if (RaceManager::get()->getNumLocalPlayers() > 1)
826 {
827 const int np = RaceManager::get()->getNumLocalPlayers();
828 const int nai = RaceManager::get()->getNumberOfKarts() - np;
829
830 // player karts twice as loud as AIs toghether
831 const float players_volume = (np * 2.0f) / (np*2.0f + np);
832
833 if (m_controller->isLocalPlayerController())
834 m_engine_sound->setVolume( players_volume / np );
835 else
836 m_engine_sound->setVolume( (1.0f - players_volume) / nai );
837 }
838
839 m_engine_sound->setSpeed(0.6f);
840 m_engine_sound->setLoop(true);
841 m_engine_sound->play();
842 } // startEngineSFX
843
844 //-----------------------------------------------------------------------------
845 /** Returns true if the kart is 'resting', i.e. (nearly) not moving.
846 */
isInRest() const847 bool Kart::isInRest() const
848 {
849 return fabsf(m_body->getLinearVelocity ().y())<0.2f;
850 } // isInRest
851
852 //-----------------------------------------------------------------------------
853 /** Multiplies the velocity of the kart by a factor f (both linear
854 * and angular). This is used by anvils, which suddenly slow down the kart
855 * when they are attached.
856 */
adjustSpeed(float f)857 void Kart::adjustSpeed(float f)
858 {
859 m_body->setLinearVelocity(m_body->getLinearVelocity()*f);
860 m_body->setAngularVelocity(m_body->getAngularVelocity()*f);
861 } // adjustSpeed
862
863 //-----------------------------------------------------------------------------
864 /** This method is to be called every time the mass of the kart is updated,
865 * which includes attaching an anvil to the kart (and detaching).
866 */
updateWeight()867 void Kart::updateWeight()
868 {
869 if (!m_body)
870 return;
871 float mass = m_kart_properties->getMass() + m_attachment->weightAdjust();
872 if (m_weight != mass)
873 {
874 m_weight = mass;
875 btVector3 inertia;
876 m_kart_chassis->calculateLocalInertia(mass, inertia);
877 m_body->setMassProps(mass, inertia);
878 }
879 } // updateWeight
880
881 // ------------------------------------------------------------------------
882 /** Returns the (maximum) speed for a given turn radius.
883 * \param radius The radius for which the speed needs to be computed. */
getSpeedForTurnRadius(float radius) const884 float Kart::getSpeedForTurnRadius(float radius) const
885 {
886 InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius();
887 // Convert the turn radius into turn angle
888 for(int i = 0; i < (int)turn_angle_at_speed.size(); i++)
889 turn_angle_at_speed.setY(i, sinf( 1.0f / turn_angle_at_speed.getY(i)));
890
891 float angle = sinf(1.0f / radius);
892 return turn_angle_at_speed.getReverse(angle);
893 } // getSpeedForTurnRadius
894
895 // ------------------------------------------------------------------------
896 /** Returns the maximum steering angle (depending on speed).
897 This is proportional to kart length because physics reverse this effect,
898 the results of this function should not be used to determine the
899 real raw steer angle. */
getMaxSteerAngle(float speed) const900 float Kart::getMaxSteerAngle(float speed) const
901 {
902 InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius();
903 // Convert the turn radius into turn angle
904 // We multiply by wheel base to keep turn radius identical
905 // across karts of different lengths sharing the same
906 // turn radius properties
907 for(int i = 0; i < (int)turn_angle_at_speed.size(); i++)
908 turn_angle_at_speed.setY(i, sinf( 1.0f / turn_angle_at_speed.getY(i))
909 * m_kart_properties->getWheelBase());
910
911 return turn_angle_at_speed.get(speed);
912 } // getMaxSteerAngle
913
914 //-----------------------------------------------------------------------------
915 /** Sets that this kart has finished the race and finishing time. It also
916 * notifies the race_manager about the race completion for this kart.
917 * \param time The finishing time for this kart. It can either be the
918 * actual time when the kart finished (in which case time() =
919 * world->getTime()), or the estimated time in case that all
920 * player kart have finished the race and all AI karts get
921 * an estimated finish time set.
922 * \param from_server In a network game, only the server can notify
923 * about a kart finishing a race. This parameter is to distinguish
924 * between a local detection (which is ignored on clients in a
925 * network game), and a server notification.
926 */
finishedRace(float time,bool from_server)927 void Kart::finishedRace(float time, bool from_server)
928 {
929 // m_finished_race can be true if e.g. an AI kart was set to finish
930 // because the race was over (i.e. estimating the finish time). If
931 // this kart then crosses the finish line (with the end controller)
932 // it would trigger a race end again.
933 if (m_finished_race) return;
934
935 const bool is_linear_race =
936 RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
937 RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL ||
938 RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER;
939 if (NetworkConfig::get()->isNetworking() && !from_server)
940 {
941 if (NetworkConfig::get()->isServer())
942 {
943 RaceEventManager::get()->kartFinishedRace(this, time);
944 } // isServer
945
946 // Ignore local detection of a kart finishing a race in a
947 // network game.
948 else if (NetworkConfig::get()->isClient())
949 {
950 if (is_linear_race && m_saved_controller == NULL &&
951 !RewindManager::get()->isRewinding())
952 {
953 m_network_finish_check_ticks =
954 World::getWorld()->getTicksSinceStart() +
955 stk_config->time2Ticks(1.0f);
956 EndController* ec = new EndController(this, m_controller);
957 Controller* old_controller = m_controller;
958 setController(ec);
959 // Seamless endcontroller replay
960 RewindManager::get()->addRewindInfoEventFunction(new
961 RewindInfoEventFunction(
962 World::getWorld()->getTicksSinceStart(),
963 /*undo_function*/[old_controller, this]()
964 {
965 if (m_network_finish_check_ticks == -1)
966 return;
967
968 m_controller = old_controller;
969 },
970 /*replay_function*/[ec, old_controller, this]()
971 {
972 if (m_network_finish_check_ticks == -1)
973 return;
974
975 m_saved_controller = old_controller;
976 ec->reset();
977 m_controller = ec;
978 }));
979 }
980 return;
981 }
982 } // !from_server
983
984 if (NetworkConfig::get()->isClient())
985 {
986 m_network_confirmed_finish_ticks =
987 World::getWorld()->getTicksSinceStart();
988 }
989
990 m_finished_race = true;
991
992 m_finish_time = time;
993
994 m_controller->finishedRace(time);
995 m_kart_model->finishedRace();
996 RaceManager::get()->kartFinishedRace(this, time);
997
998 // If this is spare tire kart, end now
999 if (dynamic_cast<SpareTireAI*>(m_controller) != NULL) return;
1000
1001 if (is_linear_race && m_controller->isPlayerController() && !m_eliminated)
1002 {
1003 RaceGUIBase* m = World::getWorld()->getRaceGUI();
1004 if (m)
1005 {
1006 bool won_the_race = false, too_slow = false;
1007 unsigned int win_position = 1;
1008
1009 if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER)
1010 win_position = 2;
1011
1012 if (getPosition() == (int)win_position &&
1013 World::getWorld()->getNumKarts() > win_position)
1014 won_the_race = true;
1015
1016 if (RaceManager::get()->hasTimeTarget() && m_finish_time > RaceManager::get()->getTimeTarget())
1017 too_slow = true;
1018
1019 m->addMessage((too_slow ? _("You were too slow!") :
1020 won_the_race ? _("You won the race!") :
1021 _("You finished the race!")),
1022 this, 2.0f, video::SColor(255, 255, 255, 255), true, true, true);
1023 }
1024 }
1025
1026 if (RaceManager::get()->isLinearRaceMode() || RaceManager::get()->isBattleMode() ||
1027 RaceManager::get()->isSoccerMode() || RaceManager::get()->isEggHuntMode())
1028 {
1029 // Save for music handling in race result gui
1030 setRaceResult();
1031 if (!isGhostKart())
1032 {
1033 if (m_saved_controller == NULL)
1034 {
1035 setController(new EndController(this, m_controller));
1036 }
1037 else
1038 m_saved_controller->finishedRace(time);
1039 }
1040 // Skip animation if this kart is eliminated
1041 if (m_eliminated || isGhostKart()) return;
1042
1043 m_kart_model->setAnimation(m_race_result ?
1044 KartModel::AF_WIN_START : KartModel::AF_LOSE_START);
1045 }
1046 } // finishedRace
1047
1048 //-----------------------------------------------------------------------------
setRaceResult()1049 void Kart::setRaceResult()
1050 {
1051 if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
1052 RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL)
1053 {
1054 if (m_controller->isLocalPlayerController()) // if player is on this computer
1055 {
1056 PlayerProfile *player = PlayerManager::getCurrentPlayer();
1057 const ChallengeStatus *challenge = player->getCurrentChallengeStatus();
1058 // In case of a GP challenge don't make the end animation depend
1059 // on if the challenge is fulfilled
1060 if (challenge && !challenge->getData()->isGrandPrix())
1061 {
1062 if (challenge->getData()->isChallengeFulfilled())
1063 m_race_result = true;
1064 else
1065 m_race_result = false;
1066 }
1067 else if (this->getPosition() <= 0.5f *
1068 World::getWorld()->getCurrentNumKarts() ||
1069 this->getPosition() == 1)
1070 m_race_result = true;
1071 else
1072 m_race_result = false;
1073 }
1074 else
1075 {
1076 if (this->getPosition() <= 0.5f *
1077 World::getWorld()->getCurrentNumKarts() ||
1078 this->getPosition() == 1)
1079 m_race_result = true;
1080 else
1081 m_race_result = false;
1082 }
1083 }
1084 else if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER ||
1085 RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES)
1086 {
1087 // the kart wins if it isn't eliminated
1088 m_race_result = !this->isEliminated();
1089 }
1090 else if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_FREE_FOR_ALL)
1091 {
1092 FreeForAll* ffa = dynamic_cast<FreeForAll*>(World::getWorld());
1093 m_race_result = ffa->getKartFFAResult(getWorldKartId());
1094 }
1095 else if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_CAPTURE_THE_FLAG)
1096 {
1097 CaptureTheFlag* ctf = dynamic_cast<CaptureTheFlag*>(World::getWorld());
1098 m_race_result = ctf->getKartCTFResult(getWorldKartId());
1099 }
1100 else if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
1101 {
1102 SoccerWorld* sw = dynamic_cast<SoccerWorld*>(World::getWorld());
1103 m_race_result = sw->getKartSoccerResult(this->getWorldKartId());
1104 }
1105 else if (RaceManager::get()->getMinorMode() == RaceManager::MINOR_MODE_EASTER_EGG)
1106 {
1107 // Easter egg mode only has one player, so always win
1108 m_race_result = true;
1109 }
1110 else
1111 Log::warn("Kart", "Unknown game mode given.");
1112
1113 } // setRaceResult
1114
1115 //-----------------------------------------------------------------------------
1116 /** Called when an item is collected. It will either adjust the collected
1117 * energy, or update the attachment or powerup for this kart.
1118 * \param item_state The item that was hit.
1119 */
collectedItem(ItemState * item_state)1120 void Kart::collectedItem(ItemState *item_state)
1121 {
1122 float old_energy = m_collected_energy;
1123 const Item::ItemType type = item_state->getType();
1124
1125 switch (type)
1126 {
1127 case Item::ITEM_BANANA:
1128 m_attachment->hitBanana(item_state);
1129 break;
1130 case Item::ITEM_NITRO_SMALL:
1131 m_collected_energy += m_kart_properties->getNitroSmallContainer();
1132 break;
1133 case Item::ITEM_NITRO_BIG:
1134 m_collected_energy += m_kart_properties->getNitroBigContainer();
1135 break;
1136 case Item::ITEM_BONUS_BOX :
1137 {
1138 m_powerup->hitBonusBox(*item_state);
1139 break;
1140 }
1141 case Item::ITEM_BUBBLEGUM:
1142 m_has_caught_nolok_bubblegum =
1143 (item_state->getPreviousOwner()&&
1144 item_state->getPreviousOwner()->getIdent() == "nolok");
1145
1146 // slow down
1147 m_bubblegum_ticks = (int16_t)stk_config->time2Ticks(
1148 m_kart_properties->getBubblegumDuration());
1149 m_bubblegum_torque_sign =
1150 ((World::getWorld()->getTicksSinceStart() / 10) % 2 == 0) ?
1151 true : false;
1152 m_max_speed->setSlowdown(MaxSpeed::MS_DECREASE_BUBBLE,
1153 m_kart_properties->getBubblegumSpeedFraction() ,
1154 stk_config->time2Ticks(m_kart_properties->getBubblegumFadeInTime()),
1155 m_bubblegum_ticks);
1156 if (!RewindManager::get()->isRewinding())
1157 getNextEmitter()->play(getSmoothedXYZ(), m_goo_sound);
1158
1159 // Play appropriate custom character sound
1160 playCustomSFX(SFXManager::CUSTOM_GOO);
1161 break;
1162 default : break;
1163 } // switch TYPE
1164
1165 if ( m_collected_energy > m_kart_properties->getNitroMax())
1166 m_collected_energy = m_kart_properties->getNitroMax();
1167 m_controller->collectedItem(*item_state, old_energy);
1168
1169 } // collectedItem
1170
1171 //-----------------------------------------------------------------------------
1172 /** Called the first time a kart accelerates after 'ready'. It searches
1173 * through the startup times to find the appropriate slot, and returns the
1174 * speed-boost from the corresponding entry.
1175 * If the kart started too slow (i.e. slower than the longest time in the
1176 * startup times list), it returns 0.
1177 */
getStartupBoostFromStartTicks(int ticks) const1178 float Kart::getStartupBoostFromStartTicks(int ticks) const
1179 {
1180 int ticks_since_ready = ticks - stk_config->time2Ticks(1.0f);
1181 if (ticks_since_ready < 0)
1182 return 0.0f;
1183 float t = stk_config->ticks2Time(ticks_since_ready);
1184 std::vector<float> startup_times = m_kart_properties->getStartupTime();
1185 for (unsigned int i = 0; i < startup_times.size(); i++)
1186 {
1187 if (t <= startup_times[i])
1188 return m_kart_properties->getStartupBoost()[i];
1189 }
1190 return 0.0f;
1191 } // getStartupBoostFromStartTicks
1192
1193 //-----------------------------------------------------------------------------
1194 /** Simulates gears by adjusting the force of the engine. It also takes the
1195 * effect of the zipper into account.
1196 */
getActualWheelForce()1197 float Kart::getActualWheelForce()
1198 {
1199 float add_force = m_max_speed->getCurrentAdditionalEngineForce();
1200 assert(!std::isnan(add_force));
1201 const std::vector<float>& gear_ratio=m_kart_properties->getGearSwitchRatio();
1202 for(unsigned int i=0; i<gear_ratio.size(); i++)
1203 {
1204 if(m_speed <= m_kart_properties->getEngineMaxSpeed() * gear_ratio[i])
1205 {
1206 assert(!std::isnan(m_kart_properties->getEnginePower()));
1207 assert(!std::isnan(m_kart_properties->getGearPowerIncrease()[i]));
1208 return m_kart_properties->getEnginePower()
1209 * m_kart_properties->getGearPowerIncrease()[i]
1210 + add_force;
1211 }
1212 }
1213 assert(!std::isnan(m_kart_properties->getEnginePower()));
1214 return m_kart_properties->getEnginePower() + add_force * 2;
1215
1216 } // getActualWheelForce
1217
1218 //-----------------------------------------------------------------------------
1219 /** The kart is on ground if all 4 wheels touch the ground, and if no special
1220 * animation (rescue, explosion etc.) is happening).
1221 */
isOnGround() const1222 bool Kart::isOnGround() const
1223 {
1224 return ((int)m_vehicle->getNumWheelsOnGround() == m_vehicle->getNumWheels()
1225 && !getKartAnimation());
1226 } // isOnGround
1227
1228 //-----------------------------------------------------------------------------
1229 /** The kart is near the ground, but not necessarily on it (small jumps). This
1230 * is used to determine when to stop flying.
1231 */
isNearGround() const1232 bool Kart::isNearGround() const
1233 {
1234 if((m_terrain_info->getHitPoint() - getXYZ()).length() ==Track::NOHIT)
1235 return false;
1236 else
1237 return ((getXYZ().getY() - m_terrain_info->getHoT())
1238 < stk_config->m_near_ground);
1239 } // isNearGround
1240
1241 // ------------------------------------------------------------------------
1242 /** Enables a kart shield protection for a certain amount of time.
1243 */
setShieldTime(float t)1244 void Kart::setShieldTime(float t)
1245 {
1246 if(isShielded())
1247 {
1248 getAttachment()->setTicksLeft(stk_config->time2Ticks(t));
1249 }
1250 } // setShieldTime
1251
1252 // ------------------------------------------------------------------------
1253 /**
1254 * Returns true if the kart is protected by a shield.
1255 */
isShielded() const1256 bool Kart::isShielded() const
1257 {
1258 if(getAttachment() != NULL)
1259 {
1260 Attachment::AttachmentType type = getAttachment()->getType();
1261 return type == Attachment::ATTACH_BUBBLEGUM_SHIELD ||
1262 type == Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD;
1263 }
1264 else
1265 {
1266 return false;
1267 }
1268 } // isShielded
1269
1270 // ------------------------------------------------------------------------
1271 /**
1272 *Returns the remaining time the kart is protected by a shield.
1273 */
getShieldTime() const1274 float Kart::getShieldTime() const
1275 {
1276 if (isShielded())
1277 return stk_config->ticks2Time(getAttachment()->getTicksLeft());
1278 else
1279 return 0.0f;
1280 } // getShieldTime
1281
1282 // ------------------------------------------------------------------------
1283 /**
1284 * Decreases the kart's shield time.
1285 * \param t The time subtracted from the shield timer. If t == 0.0f, the
1286 default amout of time is subtracted.
1287 */
decreaseShieldTime()1288 void Kart::decreaseShieldTime()
1289 {
1290 if (isShielded())
1291 {
1292 getAttachment()->setTicksLeft(0);
1293 }
1294 } // decreaseShieldTime
1295
1296 //-----------------------------------------------------------------------------
1297 /** Shows the star effect for a certain time.
1298 * \param t Time to show the star effect for.
1299 */
showStarEffect(float t)1300 void Kart::showStarEffect(float t)
1301 {
1302 if (m_stars_effect)
1303 m_stars_effect->showFor(t);
1304 } // showStarEffect
1305
1306 //-----------------------------------------------------------------------------
eliminate()1307 void Kart::eliminate()
1308 {
1309 if (!getKartAnimation())
1310 {
1311 Physics::get()->removeKart(this);
1312 }
1313 if (m_stars_effect)
1314 {
1315 m_stars_effect->reset();
1316 m_stars_effect->update(1);
1317 }
1318
1319 if (m_attachment)
1320 m_attachment->clear();
1321
1322 if (m_slipstream)
1323 m_slipstream->reset();
1324
1325 m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_TERRAIN, 0);
1326 m_kart_gfx->setGFXInvisible();
1327
1328 if (m_engine_sound)
1329 m_engine_sound->stop();
1330
1331 m_eliminated = true;
1332
1333 #ifndef SERVER_ONLY
1334 if (m_shadow)
1335 m_shadow->update(false);
1336 #endif
1337 if (m_node)
1338 m_node->setVisible(false);
1339 } // eliminate
1340
1341 //-----------------------------------------------------------------------------
1342 /** Updates the kart in each time step. It updates the physics setting,
1343 * particle effects, camera position, etc.
1344 * \param dt Time step size.
1345 */
update(int ticks)1346 void Kart::update(int ticks)
1347 {
1348 if (m_network_finish_check_ticks > 0 &&
1349 World::getWorld()->getTicksSinceStart() >
1350 m_network_finish_check_ticks &&
1351 !m_finished_race && m_saved_controller != NULL)
1352 {
1353 Log::warn("Kart", "Missing finish race from server.");
1354 m_network_finish_check_ticks = -1;
1355 delete m_controller;
1356 m_controller = m_saved_controller;
1357 m_saved_controller = NULL;
1358 }
1359
1360 m_powerup->update(ticks);
1361
1362 // Reset any instant speed increase in the bullet kart
1363 m_vehicle->resetMaxSpeed();
1364
1365 if (m_bubblegum_ticks > 0)
1366 m_bubblegum_ticks -= ticks;
1367
1368 // This is to avoid a rescue immediately after an explosion
1369 const bool has_animation_before = m_kart_animation != NULL;
1370 // A kart animation can change the xyz position. This needs to be done
1371 // before updating the graphical position (which is done in
1372 // Moveable::update() ), otherwise 'stuttering' can happen (caused by
1373 // graphical and physical position not being the same).
1374 if (has_animation_before)
1375 {
1376 m_kart_animation->update(ticks);
1377 }
1378 else if (NetworkConfig::get()->roundValuesNow())
1379 CompressNetworkBody::compress(m_body.get(), m_motion_state.get());
1380
1381 float dt = stk_config->ticks2Time(ticks);
1382 if (!RewindManager::get()->isRewinding())
1383 {
1384 m_time_previous_counter += dt;
1385 while (m_time_previous_counter > stk_config->ticks2Time(1))
1386 {
1387 m_previous_xyz[0] = getXYZ();
1388 m_previous_xyz_times[0] = World::getWorld()->getTime();
1389 for (int i=m_xyz_history_size-1;i>0;i--)
1390 {
1391 m_previous_xyz[i] = m_previous_xyz[i-1];
1392 m_previous_xyz_times[i] = m_previous_xyz_times[i-1];
1393 }
1394 m_time_previous_counter -= stk_config->ticks2Time(1);
1395 }
1396 }
1397
1398 // Update the position and other data taken from the physics (or
1399 // an animation which calls setXYZ(), which also updates the kart
1400 // physical position).
1401 Moveable::update(ticks);
1402
1403 Vec3 front(0, 0, getKartLength()*0.5f);
1404 m_xyz_front = getTrans()(front);
1405
1406 // Hover the kart above reset position before entering the game
1407 // Add invulnerability depends on kart
1408 if (m_live_join_util != 0 &&
1409 ((m_live_join_util > World::getWorld()->getTicksSinceStart() &&
1410 World::getWorld()->isActiveRacePhase()) ||
1411 World::getWorld()->isLiveJoinWorld()))
1412 {
1413 btRigidBody *body = getBody();
1414 body->clearForces();
1415 body->setLinearVelocity(Vec3(0.0f));
1416 body->setAngularVelocity(Vec3(0.0f));
1417 btTransform hovering = m_starting_transform;
1418 hovering.setOrigin(hovering.getOrigin() +
1419 m_starting_transform.getBasis().getColumn(1) * 3.0f);
1420 body->proceedToTransform(hovering);
1421 setTrans(hovering);
1422 float time = getKartProperties()->getExplosionInvulnerabilityTime();
1423 m_invulnerable_ticks = stk_config->time2Ticks(time);
1424 }
1425
1426 // Update the locally maintained speed of the kart (m_speed), which
1427 // is used furthermore for engine power, camera distance etc
1428 updateSpeed();
1429 // Make the restitution depend on speed: this avoids collision issues,
1430 // otherwise a collision with high speed can see a kart being push
1431 // high up in the air (and out of control). So for higher speed we
1432 // reduce the restitution, meaning the karts will get less of a push
1433 // based on the collision speed.
1434 m_body->setRestitution(m_kart_properties->getRestitution(fabsf(m_speed)));
1435
1436 m_controller->update(ticks);
1437
1438 #ifndef SERVER_ONLY
1439 #undef DEBUG_CAMERA_SHAKE
1440 #ifdef DEBUG_CAMERA_SHAKE
1441 Log::verbose("camera", "%s t %f %d xyz %f %f %f v %f %f %f d3 %f d2 %f",
1442 getIdent().c_str(),
1443 World::getWorld()->getTime(), ticks,
1444 getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(),
1445 getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(),
1446 (Camera::getCamera(0)->getXYZ()-getXYZ()).length(),
1447 (Camera::getCamera(0)->getXYZ()-getXYZ()).length_2d()
1448 );
1449 #endif
1450 #endif
1451
1452 #undef DEBUG_TO_COMPARE_KART_PHYSICS
1453 #ifdef DEBUG_TO_COMPARE_KART_PHYSICS
1454 // This information is useful when comparing kart physics, e.g. to
1455 // see top speed, acceleration (i.e. time to top speed) etc.
1456 Log::verbose("physics", " %s t %f %d xyz(9-11) %f %f %f v(13-15) %f %f %f steerf(17) %f maxangle(19) %f speed(21) %f steering(23-24) %f %f clock %lf",
1457 getIdent().c_str(),
1458 World::getWorld()->getTime(), ticks,
1459 getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(),
1460 getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(), //13,14,15
1461 m_skidding->getSteeringFraction(), //19
1462 getMaxSteerAngle(), //20
1463 m_speed, //21
1464 m_vehicle->getWheelInfo(0).m_steering, //23
1465 m_vehicle->getWheelInfo(1).m_steering, //24
1466 StkTime::getRealTime()
1467 );
1468 #endif
1469
1470 // if its view is blocked by plunger, decrease remaining time
1471 if(m_view_blocked_by_plunger > 0)
1472 {
1473 m_view_blocked_by_plunger -= ticks;
1474 //unblock the view if kart just became shielded
1475 if(isShielded())
1476 m_view_blocked_by_plunger = 0;
1477 }
1478
1479 // Decrease the remaining invulnerability time
1480 if(m_invulnerable_ticks>0)
1481 m_invulnerable_ticks -= ticks;
1482
1483 if (!RewindManager::get()->isRewinding())
1484 m_slipstream->update(ticks);
1485 m_slipstream->updateSpeedIncrease();
1486
1487 // TODO: hiker said this probably will be moved to btKart or so when updating bullet engine.
1488 // Neutralize any yaw change if the kart leaves the ground, so the kart falls more or less
1489 // straight after jumping, but still allowing some "boat shake" (roll and pitch).
1490 // Otherwise many non perfect jumps end in a total roll over or a serious change of
1491 // direction, sometimes 90 or even full U turn (real but less fun for a karting game).
1492 // As side effect steering becames a bit less responsive (any wheel on air), but not too bad.
1493 if(!isOnGround()) {
1494 btVector3 speed = m_body->getAngularVelocity();
1495 speed.setX(speed.getX() * 0.95f);
1496 speed.setY(speed.getY() * 0.25f); // or 0.0f for sharp neutralization of yaw
1497 speed.setZ(speed.getZ() * 0.95f);
1498 m_body->setAngularVelocity(speed);
1499
1500 // When the kart is jumping, linear damping reduces the falling speed
1501 // of a kart so much that it can appear to be in slow motion. So
1502 // disable linear damping if a kart is in the air
1503 m_body->setDamping(0, m_kart_properties->getStabilityChassisAngularDamping());
1504 }
1505 else
1506 {
1507 m_body->setDamping(m_kart_properties->getStabilityChassisLinearDamping(),
1508 m_kart_properties->getStabilityChassisAngularDamping());
1509 }
1510
1511 // Used to prevent creating a rescue animation after an explosion animation got deleted
1512
1513 m_attachment->update(ticks);
1514
1515 // Make sure that the ray doesn't hit the kart. This is done by
1516 // resetting the collision filter group, so that this collision
1517 // object is ignored during raycasting.
1518 short int old_group = 0;
1519 if (m_body->getBroadphaseHandle())
1520 {
1521 old_group = m_body->getBroadphaseHandle()->m_collisionFilterGroup;
1522 m_body->getBroadphaseHandle()->m_collisionFilterGroup = 0;
1523 }
1524
1525 // After the physics step was done, the position of the wheels (as stored
1526 // in wheelInfo) is actually outdated, since the chassis was moved
1527 // according to the force acting from the wheels. So the center of the
1528 // chassis is not at the center of the wheels anymore, it is somewhat
1529 // moved forward (depending on speed and fps). In very extreme cases
1530 // (see bug 2246) the center of the chassis can actually be ahead of the
1531 // front wheels. So if we do a raycast to detect the terrain from the
1532 // current chassis, that raycast might be ahead of the wheels - which
1533 // results in incorrect rescues (the wheels are still on the ground,
1534 // but the raycast happens ahead of the front wheels and are over
1535 // a rescue texture).
1536 // To avoid this problem, we do the raycast for terrain detection from
1537 // the center of the 4 wheel positions (in world coordinates).
1538
1539 if (!has_animation_before)
1540 {
1541 Vec3 from(0.0f, 0.0f, 0.0f);
1542 for (unsigned int i = 0; i < 4; i++)
1543 from += m_vehicle->getWheelInfo(i).m_raycastInfo.m_hardPointWS;
1544
1545 // Add a certain epsilon (0.3) to the height of the kart. This avoids
1546 // problems of the ray being cast from under the track (which happened
1547 // e.g. on tux tollway when jumping down from the ramp, when the chassis
1548 // partly tunnels through the track). While tunneling should not be
1549 // happening (since Z velocity is clamped), the epsilon is left in place
1550 // just to be on the safe side (it will not hit the chassis itself).
1551 from = from/4 + (getTrans().getBasis() * Vec3(0.0f, 0.3f, 0.0f));
1552
1553 m_terrain_info->update(getTrans().getBasis(), from);
1554 }
1555 else
1556 {
1557 // Use kart transform directly as wheel info is not updated when
1558 // there is an animation
1559 m_terrain_info->update(getTrans().getBasis(),
1560 getXYZ() + getTrans().getBasis().getColumn(1) * 0.1f);
1561 }
1562
1563 if (m_body->getBroadphaseHandle())
1564 m_body->getBroadphaseHandle()->m_collisionFilterGroup = old_group;
1565
1566 // Check if a kart is (nearly) upside down and not moving much -->
1567 // automatic rescue
1568 // But only do this if auto-rescue is enabled (i.e. it will be disabled in
1569 // battle mode), and the material the kart is driving on does not have
1570 // gravity (which atm affects the roll angle).
1571
1572 // To be used later
1573 float dist_to_sector = 0.0f;
1574 LinearWorld* lw = dynamic_cast<LinearWorld*>(World::getWorld());
1575 if (lw && DriveGraph::get())
1576 {
1577 const int sector =
1578 lw->getTrackSector(getWorldKartId())->getCurrentGraphNode();
1579 dist_to_sector = getXYZ().distance
1580 (DriveGraph::get()->getNode(sector)->getCenter());
1581
1582 const Vec3& quad_normal = DriveGraph::get()->getNode(sector)
1583 ->getNormal();
1584 const btQuaternion& q = getTrans().getRotation();
1585 const float roll = quad_normal.angle
1586 ((Vec3(0, 1, 0).rotate(q.getAxis(), q.getAngle())));
1587
1588 if (Track::getCurrentTrack()->isAutoRescueEnabled() &&
1589 (!m_terrain_info->getMaterial() ||
1590 !m_terrain_info->getMaterial()->hasGravity()) &&
1591 !has_animation_before && fabs(roll) > 60 * DEGREE_TO_RAD &&
1592 fabs(getSpeed()) < 3.0f)
1593 {
1594 RescueAnimation::create(this, /*is_auto_rescue*/true);
1595 m_last_factor_engine_sound = 0.0f;
1596 }
1597 }
1598
1599 // Update physics from newly updated material
1600 PROFILER_PUSH_CPU_MARKER("Kart::updatePhysics", 0x60, 0x34, 0x7F);
1601 const Material* material = m_terrain_info->getMaterial();
1602
1603 // First update the gravity of the kart, as updateSliding in updatePhysics
1604 // need the newly set gravity to test for sliding.
1605 if (!m_flying)
1606 {
1607 float g = Track::getCurrentTrack()->getGravity();
1608 Vec3 gravity(0.0f, -g, 0.0f);
1609 btRigidBody *body = getVehicle()->getRigidBody();
1610
1611 // If the material should overwrite the gravity,
1612 if (material && material->hasGravity())
1613 {
1614 Vec3 normal = m_terrain_info->getNormal();
1615 gravity = normal * -g;
1616 }
1617
1618 body->setGravity(gravity);
1619 }
1620 updatePhysics(ticks);
1621 PROFILER_POP_CPU_MARKER();
1622
1623 if(!m_controls.getFire()) m_fire_clicked = 0;
1624
1625 if(m_controls.getFire() && !m_fire_clicked && !m_kart_animation)
1626 {
1627 if (m_powerup->getType() != PowerupManager::POWERUP_NOTHING)
1628 {
1629 setLastUsedPowerup(m_powerup->getType());
1630 }
1631 // use() needs to be called even if there currently is no collecteable
1632 // since use() can test if something needs to be switched on/off.
1633 if (!World::getWorld()->isStartPhase())
1634 m_powerup->use();
1635 else
1636 {
1637 if(!getKartAnimation())
1638 beep();
1639 }
1640 World::getWorld()->onFirePressed(getController());
1641 m_fire_clicked = 1;
1642 }
1643
1644 #undef XX
1645 #ifdef XX
1646 Log::verbose("physicsafter", "%s t %f %d xyz(9-11) %f %f %f %f %f %f "
1647 "v(16-18) %f %f %f steerf(20) %f maxangle(22) %f speed(24) %f "
1648 "steering(26-27) %f %f clock(29) %lf skidstate(31) %d factor(33) %f "
1649 "maxspeed(35) %f engf(37) %f braketick(39) %d brakes(41) %d heading(43) %f "
1650 "bubticks(45) %d",
1651 getIdent().c_str(),
1652 World::getWorld()->getTime(), World::getWorld()->getTicksSinceStart(),
1653 getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(),
1654 m_body->getWorldTransform().getOrigin().getX(),
1655 m_body->getWorldTransform().getOrigin().getY(),
1656 m_body->getWorldTransform().getOrigin().getZ(),
1657 getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(), //16-18
1658 m_skidding->getSteeringFraction(), //20
1659 getMaxSteerAngle(), //22
1660 m_speed, //24
1661 m_vehicle->getWheelInfo(0).m_steering, //26
1662 m_vehicle->getWheelInfo(1).m_steering, //27
1663 StkTime::getRealTime(), //29
1664 m_skidding->getSkidState(), //31
1665 m_skidding->getSkidFactor(), //33
1666 m_max_speed->getCurrentMaxSpeed(),
1667 m_max_speed->getCurrentAdditionalEngineForce(), // 37
1668 m_brake_ticks, //39
1669 m_controls.getButtonsCompressed(), //41
1670 getHeading(), //43
1671 m_bubblegum_ticks // 45
1672 );
1673 #endif
1674
1675 PROFILER_PUSH_CPU_MARKER("Kart::Update (material)", 0x60, 0x34, 0x7F);
1676 if (!material) // kart falling off the track
1677 {
1678 // let kart fall a bit before rescuing
1679 const Vec3 *min, *max;
1680 Track::getCurrentTrack()->getAABB(&min, &max);
1681
1682 if((min->getY() - getXYZ().getY() > 17 || dist_to_sector > 25) && !m_flying &&
1683 !has_animation_before)
1684 {
1685 RescueAnimation::create(this);
1686 m_last_factor_engine_sound = 0.0f;
1687 }
1688 }
1689 else
1690 {
1691 if (!has_animation_before && material->isDriveReset() && isOnGround())
1692 {
1693 RescueAnimation::create(this);
1694 m_last_factor_engine_sound = 0.0f;
1695 }
1696 else if(material->isZipper() && isOnGround())
1697 {
1698 handleZipper(material);
1699 showZipperFire();
1700 }
1701 else
1702 {
1703 m_max_speed->setSlowdown(MaxSpeed::MS_DECREASE_TERRAIN,
1704 material->getMaxSpeedFraction(),
1705 material->getSlowDownTicks() );
1706 #ifdef DEBUG
1707 if(UserConfigParams::m_material_debug)
1708 {
1709 Log::info("Kart","World %d %s\tfraction %f\ttime %d.",
1710 World::getWorld()->getTicksSinceStart(),
1711 material->getTexFname().c_str(),
1712 material->getMaxSpeedFraction(),
1713 material->getSlowDownTicks() );
1714 }
1715 #endif
1716 }
1717 } // if there is material
1718 PROFILER_POP_CPU_MARKER();
1719
1720 Track::getCurrentTrack()->getItemManager()->checkItemHit(this);
1721
1722 const bool emergency = has_animation_before;
1723
1724 if (emergency)
1725 {
1726 m_view_blocked_by_plunger = 0;
1727 if (m_flying)
1728 {
1729 stopFlying();
1730 m_flying = false;
1731 }
1732 }
1733
1734 if (RewindManager::get()->isRewinding())
1735 return;
1736 // Remove the shadow if the kart is not on the ground (if a kart
1737 // is rescued isOnGround might still be true, since the kart rigid
1738 // body was removed from the physics, but still retain the old
1739 // values for the raycasts).
1740 if (!isOnGround() && !has_animation_before)
1741 {
1742 const Material *m = getMaterial();
1743 const Material *last_m = getLastMaterial();
1744
1745 // A jump starts only the kart isn't already jumping, is on a new
1746 // (or no) texture.
1747 if (!m_is_jumping && last_m && last_m != m &&
1748 m_kart_model->getAnimation() == KartModel::AF_DEFAULT)
1749 {
1750 float v = getVelocity().getY();
1751 float force = Track::getCurrentTrack()->getGravity();
1752 // Velocity / force is the time it takes to reach the peak
1753 // of the jump (i.e. when vertical speed becomes 0). Assuming
1754 // that jump start height and end height are the same, it will
1755 // take the same time again to reach the bottom
1756 float t = 2.0f * v/force;
1757
1758 // Jump if either the jump is estimated to be long enough, or
1759 // the texture has the jump property set.
1760 if (t > m_kart_properties->getJumpAnimationTime() ||
1761 last_m->isJumpTexture())
1762 {
1763 m_kart_model->setAnimation(KartModel::AF_JUMP_START);
1764 }
1765
1766 m_is_jumping = true;
1767 }
1768 }
1769 else if (m_is_jumping)
1770 {
1771 // Kart touched ground again
1772 m_is_jumping = false;
1773 m_kart_model->setAnimation(KartModel::AF_DEFAULT);
1774
1775 if (!GUIEngine::isNoGraphics() && !has_animation_before)
1776 {
1777 HitEffect *effect = new Explosion(getXYZ(), "jump",
1778 "jump_explosion.xml");
1779 ProjectileManager::get()->addHitEffect(effect);
1780 }
1781 }
1782
1783 } // update
1784
1785 //-----------------------------------------------------------------------------
1786 /** Updates the local speed based on the current physical velocity. The value
1787 * is smoothed exponentially to avoid camera stuttering (camera distance
1788 * is dependent on speed)
1789 */
updateSpeed()1790 void Kart::updateSpeed()
1791 {
1792 // Compute the speed of the kart. Smooth it with previous speed to make
1793 // the camera smoother (because of capping the speed in m_max_speed
1794 // the speed value jitters when approaching maximum speed. This results
1795 // in the distance between kart and camera to jitter as well (typically
1796 // only in the order of centimetres though). Smoothing the speed value
1797 // gets rid of this jitter, and also r
1798 m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length();
1799
1800 // calculate direction of m_speed
1801 const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform();
1802 btVector3 forwardW(
1803 chassisTrans.getBasis()[0][2],
1804 chassisTrans.getBasis()[1][2],
1805 chassisTrans.getBasis()[2][2]);
1806
1807 // In theory <0 should be sufficient, but floating point errors can cause
1808 // flipping from +eps to -eps and back, resulting in animation flickering
1809 // if the kart has backpedal animations.
1810 if (forwardW.dot(getVehicle()->getRigidBody()->getLinearVelocity())
1811 < btScalar(-0.01f))
1812 {
1813 m_speed = -m_speed;
1814 }
1815
1816 // At low velocity, forces on kart push it back and forth so we ignore this
1817 // - quick'n'dirty workaround for bug 1776883
1818 if (fabsf(m_speed) < 0.2f ||
1819 dynamic_cast<RescueAnimation*> ( getKartAnimation() ) ||
1820 dynamic_cast<ExplosionAnimation*>( getKartAnimation() ) )
1821 {
1822 m_speed = 0;
1823 }
1824 } // updateSpeed
1825
1826 //-----------------------------------------------------------------------------
1827 /** Show fire to go with a zipper.
1828 */
showZipperFire()1829 void Kart::showZipperFire()
1830 {
1831 m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_ZIPPER, 800.0f);
1832 }
1833
1834 //-----------------------------------------------------------------------------
1835 /** Squashes this kart: it will scale the kart in up direction, and causes
1836 * a slowdown while this kart is squashed.
1837 * Returns true if the squash is successful, false otherwise.
1838 * \param time How long the kart will be squashed. A value of 0 will reset
1839 * the kart to be unsquashed.
1840 * \param slowdown Reduction of max speed.
1841 */
setSquash(float time,float slowdown)1842 bool Kart::setSquash(float time, float slowdown)
1843 {
1844 if (isInvulnerable() || getKartAnimation()) return false;
1845
1846 if (isShielded())
1847 {
1848 decreaseShieldTime();
1849 return false;
1850 }
1851
1852 if(m_attachment->getType()==Attachment::ATTACH_BOMB && time>0)
1853 {
1854 ExplosionAnimation::create(this);
1855 return true;
1856 }
1857
1858 m_max_speed->setSlowdown(MaxSpeed::MS_DECREASE_SQUASH, slowdown,
1859 stk_config->time2Ticks(0.1f),
1860 stk_config->time2Ticks(time));
1861 return true;
1862 } // setSquash
1863
1864 //-----------------------------------------------------------------------------
setSquashGraphics()1865 void Kart::setSquashGraphics()
1866 {
1867 #ifndef SERVER_ONLY
1868 if (isGhostKart() || GUIEngine::isNoGraphics()) return;
1869
1870 m_node->setScale(core::vector3df(1.0f, 0.5f, 1.0f));
1871 if (m_vehicle->getNumWheels() > 0)
1872 {
1873 if (!m_wheel_box)
1874 {
1875 m_wheel_box = irr_driver->getSceneManager()
1876 ->addDummyTransformationSceneNode(m_node);
1877 }
1878 scene::ISceneNode **wheels = m_kart_model->getWheelNodes();
1879 for (int i = 0; i < 4 && i < m_vehicle->getNumWheels(); i++)
1880 {
1881 if (wheels[i])
1882 wheels[i]->setParent(m_wheel_box);
1883 }
1884 m_wheel_box->getRelativeTransformationMatrix()
1885 .setScale(core::vector3df(1.0f, 2.0f, 1.0f));
1886 }
1887 #endif
1888 } // setSquashGraphics
1889
1890 //-----------------------------------------------------------------------------
unsetSquash()1891 void Kart::unsetSquash()
1892 {
1893 #ifndef SERVER_ONLY
1894 if (isGhostKart() || GUIEngine::isNoGraphics()) return;
1895
1896 m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f));
1897 if (m_vehicle && m_vehicle->getNumWheels() > 0)
1898 {
1899 scene::ISceneNode** wheels = m_kart_model->getWheelNodes();
1900 scene::ISceneNode* node = m_kart_model->getAnimatedNode() ?
1901 m_kart_model->getAnimatedNode() : m_node;
1902
1903 for (int i = 0; i < 4 && i < m_vehicle->getNumWheels(); i++)
1904 {
1905 if (wheels[i])
1906 {
1907 wheels[i]->setParent(node);
1908 }
1909 }
1910 }
1911 #endif
1912 } // unsetSquash
1913
1914 //-----------------------------------------------------------------------------
1915 /** Returns if the kart is currently being squashed
1916 */
isSquashed() const1917 bool Kart::isSquashed() const
1918 {
1919 return
1920 m_max_speed->isSpeedDecreaseActive(MaxSpeed::MS_DECREASE_SQUASH) == 1;
1921 } // setSquash
1922
1923 //-----------------------------------------------------------------------------
1924 /** Plays any terrain specific sound effect.
1925 */
handleMaterialSFX()1926 void Kart::handleMaterialSFX()
1927 {
1928 // If a terrain specific sfx is already being played, when a new
1929 // terrain is entered, an old sfx should be finished (once, not
1930 // looped anymore of course). The m_terrain_sound is then copied
1931 // to a m_previous_terrain_sound, for which looping is disabled.
1932 // In case that three sfx needed to be played (i.e. a previous is
1933 // playing, a current is playing, and a new terrain with sfx is
1934 // entered), the oldest (previous) sfx is stopped and deleted.
1935
1936 // FIXME: if there are already two sfx playing, don't add another
1937 // one. This should reduce the performance impact when driving
1938 // on the bridge in Cocoa.
1939 const Material* material =
1940 isOnGround() ? m_terrain_info->getMaterial() : NULL;
1941
1942 // We can not use getLastMaterial() since, since the last material might
1943 // be updated several times during the physics updates, not indicating
1944 // that we have reached a new material with regards to the sound effect.
1945 // So we separately save the material last used for a sound effect and
1946 // then use this for comparison.
1947 if(m_last_sound_material!=material)
1948 {
1949 // First stop any previously playing terrain sound
1950 // and remove it, so that m_previous_terrain_sound
1951 // can be used again.
1952 if(m_previous_terrain_sound)
1953 {
1954 m_previous_terrain_sound->deleteSFX();
1955 }
1956
1957 // Disable looping for the current terrain sound, and
1958 // make it the previous terrain sound.
1959 if (m_terrain_sound) m_terrain_sound->setLoop(false);
1960 m_previous_terrain_sound = m_terrain_sound;
1961
1962 const std::string &sound_name = material ? material->getSFXName() : "";
1963
1964 // In multiplayer mode sounds are NOT positional, because we have
1965 // multiple listeners. This would make the sounds of all AIs be
1966 // audible at all times. So silence AI karts.
1967 if (!sound_name.empty() && (RaceManager::get()->getNumPlayers()==1 ||
1968 m_controller->isLocalPlayerController() ) )
1969 {
1970 m_terrain_sound = SFXManager::get()->createSoundSource(sound_name);
1971 m_terrain_sound->play();
1972 m_terrain_sound->setLoop(true);
1973 }
1974 else
1975 {
1976 m_terrain_sound = NULL;
1977 }
1978 }
1979
1980 // Check if a previous terrain sound (now not looped anymore)
1981 // is finished and can be deleted.
1982 if(m_previous_terrain_sound &&
1983 m_previous_terrain_sound->getStatus()==SFXBase::SFX_STOPPED)
1984 {
1985 // We don't modify the position of m_previous_terrain_sound
1986 // anymore, so that it keeps on playing at the place where the
1987 // kart left the material.
1988 m_previous_terrain_sound->deleteSFX();
1989 m_previous_terrain_sound = NULL;
1990 }
1991
1992 bool m_schedule_pause = m_flying ||
1993 dynamic_cast<RescueAnimation*>(getKartAnimation()) ||
1994 dynamic_cast<ExplosionAnimation*>(getKartAnimation());
1995
1996 // terrain sound is not necessarily a looping sound so check its status before
1997 // setting its speed, to avoid 'ressuscitating' sounds that had already stopped
1998 if(m_terrain_sound &&
1999 (m_terrain_sound->getStatus()==SFXBase::SFX_PLAYING ||
2000 m_terrain_sound->getStatus()==SFXBase::SFX_PAUSED) )
2001 {
2002 m_terrain_sound->setPosition(getSmoothedXYZ());
2003 if(material)
2004 material->setSFXSpeed(m_terrain_sound, m_speed, m_schedule_pause);
2005 }
2006
2007 m_last_sound_material = material;
2008 } // handleMaterialSFX
2009
2010 //-----------------------------------------------------------------------------
2011 /** Handles material specific GFX, mostly particle effects. Particle
2012 * effects can be triggered by two different situations: either
2013 * because a kart drives on top of a terrain with a special effect,
2014 * or because the kart is driving or falling under a surface (e.g.
2015 * water), and the terrain effect is coming from that surface. Those
2016 * effects are exclusive - you either get the effect from the terrain
2017 * you are driving on, or the effect from a surface the kart is
2018 * (partially) under. The surface effect is triggered, if either the
2019 * kart is falling, or if the surface the kart is driving on has
2020 * the 'isBelowSurface' property set. This function is called once
2021 * per rendered frame from updateGraphics().
2022 * \param dt Time step size.
2023 */
handleMaterialGFX(float dt)2024 void Kart::handleMaterialGFX(float dt)
2025 {
2026 const Material *material = getMaterial();
2027
2028 // First test: give the terrain effect, if the kart is
2029 // on top of a surface (i.e. not falling), actually touching
2030 // something with the wheels, and the material has not the
2031 // below surface property set.
2032 if (material && isOnGround() && !material->isBelowSurface() &&
2033 !getKartAnimation() && UserConfigParams::m_particles_effects > 1)
2034 {
2035
2036 // Get the appropriate particle data depending on
2037 // wether the kart is skidding or driving.
2038 const ParticleKind* pk =
2039 material->getParticlesWhen(m_skidding->isSkidding()
2040 ? Material::EMIT_ON_SKID
2041 : Material::EMIT_ON_DRIVE);
2042 if(!pk)
2043 {
2044 // Disable potentially running particle effects
2045 m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_TERRAIN, 0);
2046 return; // no particle effect, return
2047 }
2048 m_kart_gfx->updateTerrain(pk);
2049 return;
2050 }
2051
2052 // Now the kart is either falling, or driving on a terrain which
2053 // has the 'below surface' flag set. Detect if there is a surface
2054 // on top of the kart.
2055 // --------------------------------------------------------------
2056 if (m_controller->isLocalPlayerController() && !hasFinishedRace())
2057 {
2058 bool falling = material && material->hasFallingEffect() && !m_flying;
2059 if (falling)
2060 {
2061 m_falling_time -= dt;
2062 if (m_falling_time < 0)
2063 m_falling_time = 0;
2064 }
2065 else
2066 m_falling_time = 0.35f;
2067
2068 for(unsigned int i=0; i<Camera::getNumCameras(); i++)
2069 {
2070 Camera *camera = Camera::getCamera(i);
2071 if(camera->getKart()!=this) continue;
2072
2073 if (falling && m_falling_time <= 0)
2074 {
2075 camera->setMode(Camera::CM_FALLING);
2076 }
2077 else if (camera->getMode() != Camera::CM_NORMAL &&
2078 camera->getMode() != Camera::CM_REVERSE)
2079 {
2080 camera->setMode(Camera::CM_NORMAL);
2081 }
2082 } // for i in all cameras for this kart
2083 } // camera != final camera
2084
2085 if (UserConfigParams::m_particles_effects < 2)
2086 return;
2087
2088 // Use the middle of the contact points of the two rear wheels
2089 // as the point from which to cast the ray upwards
2090 const btWheelInfo::RaycastInfo &ri2 =
2091 getVehicle()->getWheelInfo(2).m_raycastInfo;
2092 const btWheelInfo::RaycastInfo &ri3 =
2093 getVehicle()->getWheelInfo(3).m_raycastInfo;
2094 Vec3 from = (ri2.m_contactPointWS + ri3.m_contactPointWS)*0.5f;
2095 Vec3 xyz;
2096 const Material *surface_material;
2097 if(!m_terrain_info->getSurfaceInfo(from, &xyz, &surface_material))
2098 {
2099 m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_TERRAIN, 0);
2100 return;
2101 }
2102 const ParticleKind *pk =
2103 surface_material->getParticlesWhen(Material::EMIT_ON_DRIVE);
2104
2105 if(!pk || m_flying || dynamic_cast<RescueAnimation*>(getKartAnimation()))
2106 return;
2107
2108 // Now the kart is under a surface, and there is a surface effect
2109 // --------------------------------------------------------------
2110 m_kart_gfx->setParticleKind(KartGFX::KGFX_TERRAIN, pk);
2111 m_kart_gfx->setXYZ(KartGFX::KGFX_TERRAIN, xyz);
2112
2113 const float distance = xyz.distance2(from);
2114 float ratio;
2115 if (distance < 2.0f) ratio = 1.0f;
2116 else if (distance < 4.0f) ratio = (4.0f-distance)*0.5f;
2117 else ratio = -1.0f; // No more particles
2118 m_kart_gfx->setCreationRateRelative(KartGFX::KGFX_TERRAIN, ratio);
2119
2120 // Play special sound effects for this terrain
2121 // -------------------------------------------
2122 const std::string &s = surface_material->getSFXName();
2123 if (s != "" && !dynamic_cast<RescueAnimation*>(getKartAnimation())&&
2124 (m_terrain_sound == NULL ||
2125 m_terrain_sound->getStatus() == SFXBase::SFX_STOPPED))
2126 {
2127 if (m_previous_terrain_sound) m_previous_terrain_sound->deleteSFX();
2128 m_previous_terrain_sound = m_terrain_sound;
2129 if(m_previous_terrain_sound)
2130 m_previous_terrain_sound->setLoop(false);
2131
2132 m_terrain_sound = SFXManager::get()->createSoundSource(s);
2133 m_terrain_sound->play();
2134 m_terrain_sound->setLoop(false);
2135 }
2136
2137 } // handleMaterialGFX
2138
2139 //-----------------------------------------------------------------------------
2140 /** Sets zipper time, and apply one time additional speed boost. It can be
2141 * used with a specific material, in which case the zipper parmaters are
2142 * taken from this material (parameters that are <0 will be using the
2143 * kart-specific values from kart-properties.
2144 * \param material If not NULL, will be used to determine the zipper
2145 * parameters, otherwise the defaults from kart properties
2146 * will be used.
2147 * \param play_sound If true this will cause a sfx to be played even if the
2148 * terrain hasn't changed. It is used by the zipper powerup.
2149 */
handleZipper(const Material * material,bool play_sound)2150 void Kart::handleZipper(const Material *material, bool play_sound)
2151 {
2152 /** The additional speed allowed on top of the kart-specific maximum kart
2153 * speed. */
2154 float max_speed_increase;
2155
2156 /**Time the zipper stays activated. */
2157 float duration;
2158 /** A one time additional speed gain - the kart will instantly add this
2159 * amount of speed to its current speed. */
2160 float speed_gain;
2161 /** Time it takes for the zipper advantage to fade out. */
2162 float fade_out_time;
2163 /** Additional engine force. */
2164 float engine_force;
2165
2166 if(material)
2167 {
2168 material->getZipperParameter(&max_speed_increase, &duration,
2169 &speed_gain, &fade_out_time, &engine_force);
2170 if(max_speed_increase<0)
2171 max_speed_increase = m_kart_properties->getZipperMaxSpeedIncrease();
2172 if(duration<0)
2173 duration = m_kart_properties->getZipperDuration();
2174 if(speed_gain<0)
2175 speed_gain = m_kart_properties->getZipperSpeedGain();
2176 if(fade_out_time<0)
2177 fade_out_time = m_kart_properties->getZipperFadeOutTime();
2178 if(engine_force<0)
2179 engine_force = m_kart_properties->getZipperForce();
2180 }
2181 else
2182 {
2183 max_speed_increase = m_kart_properties->getZipperMaxSpeedIncrease();
2184 duration = m_kart_properties->getZipperDuration();
2185 speed_gain = m_kart_properties->getZipperSpeedGain();
2186 fade_out_time = m_kart_properties->getZipperFadeOutTime();
2187 engine_force = m_kart_properties->getZipperForce();
2188 }
2189 // Ignore a zipper that's activated while braking
2190 if(m_controls.getBrake() || m_speed<0) return;
2191
2192 m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_ZIPPER,
2193 max_speed_increase, speed_gain,
2194 engine_force,
2195 stk_config->time2Ticks(duration),
2196 stk_config->time2Ticks(fade_out_time));
2197 // Play custom character sound (weee!)
2198 int zipper_ticks = World::getWorld()->getTicksSinceStart();
2199 if (zipper_ticks > m_ticks_last_zipper)
2200 {
2201 m_ticks_last_zipper = zipper_ticks;
2202 playCustomSFX(SFXManager::CUSTOM_ZIPPER);
2203 m_controller->handleZipper(play_sound);
2204 }
2205
2206 } // handleZipper
2207
2208 // -----------------------------------------------------------------------------
2209 /** Updates the current nitro status.
2210 * \param ticks Number of physics time steps - should be 1.
2211 */
updateNitro(int ticks)2212 void Kart::updateNitro(int ticks)
2213 {
2214 if (m_collected_energy == 0)
2215 m_min_nitro_ticks = 0;
2216
2217 if (m_controls.getNitro() && m_min_nitro_ticks <= 0 && m_collected_energy > 0)
2218 {
2219 m_min_nitro_ticks = m_kart_properties->getNitroMinConsumptionTicks();
2220 float min_consumption = m_min_nitro_ticks * m_consumption_per_tick;
2221 m_energy_to_min_ratio = std::min<float>(1, m_collected_energy/min_consumption);
2222 }
2223 if (m_min_nitro_ticks > 0)
2224 {
2225 m_min_nitro_ticks -= ticks;
2226
2227 // when pressing the key, don't allow the min time to go under zero.
2228 // If it went under zero, it would be reset
2229 // As the time deduction happens before, it can be an arbitrarily
2230 // small number > 0. Smaller means more responsive controls.
2231 if (m_controls.getNitro() && m_min_nitro_ticks <= 0)
2232 m_min_nitro_ticks = 1;
2233 }
2234
2235 bool rewinding = RewindManager::get()->isRewinding();
2236 bool increase_speed = (m_min_nitro_ticks > 0 && isOnGround());
2237 if (!increase_speed && m_min_nitro_ticks <= 0)
2238 {
2239 if (m_nitro_sound->getStatus() == SFXBase::SFX_PLAYING && !rewinding)
2240 m_nitro_sound->stop();
2241 return;
2242 }
2243
2244
2245 m_collected_energy -= m_consumption_per_tick*ticks;
2246 if (m_collected_energy < 0)
2247 {
2248 if(m_nitro_sound->getStatus() == SFXBase::SFX_PLAYING && !rewinding)
2249 m_nitro_sound->stop();
2250 m_collected_energy = 0;
2251 return;
2252 }
2253
2254 if (increase_speed)
2255 {
2256 if(m_nitro_sound->getStatus() != SFXBase::SFX_PLAYING && !rewinding)
2257 m_nitro_sound->play();
2258
2259 m_max_speed->increaseMaxSpeed(MaxSpeed::MS_INCREASE_NITRO,
2260 m_kart_properties->getNitroMaxSpeedIncrease(),
2261 m_kart_properties->getNitroEngineForce(),
2262 stk_config->time2Ticks(m_kart_properties->getNitroDuration()*m_energy_to_min_ratio),
2263 stk_config->time2Ticks(m_kart_properties->getNitroFadeOutTime()));
2264 }
2265 else
2266 {
2267 if(m_nitro_sound->getStatus() == SFXBase::SFX_PLAYING && !rewinding)
2268 m_nitro_sound->stop();
2269 }
2270 } // updateNitro
2271
2272 // -----------------------------------------------------------------------------
2273 /** Activates a slipstream effect */
setSlipstreamEffect(float f)2274 void Kart::setSlipstreamEffect(float f)
2275 {
2276 m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_ZIPPER, f);
2277 } // setSlipstreamEffect
2278
2279 // -----------------------------------------------------------------------------
2280 /** Called when the kart crashes against another kart.
2281 * \param k The kart that was hit.
2282 * \param update_attachments If true the attachment of this kart and the
2283 * other kart hit will be updated (e.g. bombs will be moved)
2284 */
crashed(AbstractKart * k,bool update_attachments)2285 void Kart::crashed(AbstractKart *k, bool update_attachments)
2286 {
2287 if(update_attachments)
2288 {
2289 assert(k);
2290 getAttachment()->handleCollisionWithKart(k);
2291 }
2292 m_controller->crashed(k);
2293 playCrashSFX(NULL, k);
2294 } // crashed(Kart, update_attachments
2295
2296 // -----------------------------------------------------------------------------
2297 /** Kart hits the track with a given material.
2298 * \param m Material hit, can be NULL if no specific material exists.
2299 * \param normal The normal of the hit (used to push a kart back, which avoids
2300 * that karts sometimes can get stuck).
2301 */
crashed(const Material * m,const Vec3 & normal)2302 void Kart::crashed(const Material *m, const Vec3 &normal)
2303 {
2304 if (m && !(m->getCollisionReaction() == Material::RESCUE))
2305 playCrashSFX(m, NULL);
2306 #ifdef DEBUG
2307 // Simple debug output for people playing without sound.
2308 // This makes it easier to see if a kart hit the track (esp.
2309 // after a jump).
2310 // FIXME: This should be removed once the physics are fixed.
2311 if(UserConfigParams::m_physics_debug)
2312 {
2313 // Add a counter to make it easier to see if a new line of
2314 // output was added.
2315 static int counter=0;
2316 Log::info("Kart","Kart %s hit track: %d material %s.",
2317 getIdent().c_str(), counter++,
2318 m ? m->getTexFname().c_str() : "None");
2319 }
2320 #endif
2321
2322 const LinearWorld *lw = dynamic_cast<LinearWorld*>(World::getWorld());
2323 if(m_kart_properties->getTerrainImpulseType()
2324 ==KartProperties::IMPULSE_NORMAL &&
2325 m_vehicle->getCentralImpulseTicks()<=0 )
2326 {
2327 // Restrict impule to plane defined by gravity (i.e. X/Z plane).
2328 // This avoids the problem that karts can be pushed up, e.g. above
2329 // a fence.
2330 btVector3 gravity = m_body->getGravity();
2331 gravity.normalize();
2332 Vec3 impulse = normal - gravity* btDot(normal, gravity);
2333 if(impulse.getX() || impulse.getZ())
2334 impulse.normalize();
2335 else
2336 impulse = Vec3(0, 0, -1); // Arbitrary
2337 // Impulse depends of kart speed - and speed can be negative
2338 // If the speed is too low, karts can still get stuck into a wall
2339 // so make sure there is always enough impulse to avoid this
2340 float abs_speed = fabsf(getSpeed());
2341 impulse *= ( abs_speed<10 ? 10.0f : sqrt(abs_speed) )
2342 * m_kart_properties->getCollisionTerrainImpulse();
2343 m_bounce_back_ticks = 0;
2344 impulse = Vec3(0, 0, 0);
2345 //m_vehicle->setTimedCentralImpulse(0.1f, impulse);
2346 m_vehicle->setTimedCentralImpulse(0, impulse);
2347 }
2348 // If there is a quad graph, push the kart towards the previous
2349 // graph node center (we have to use the previous point since the
2350 // kart might have only now reached the new quad, meaning the kart
2351 // would be pushed forward).
2352 else if(m_kart_properties->getTerrainImpulseType()
2353 ==KartProperties::IMPULSE_TO_DRIVELINE &&
2354 lw && m_vehicle->getCentralImpulseTicks()<=0 &&
2355 Track::getCurrentTrack()->isPushBackEnabled())
2356 {
2357 int sector = lw->getSectorForKart(this);
2358 if(sector!=Graph::UNKNOWN_SECTOR)
2359 {
2360 // Use the first predecessor node, which is the most
2361 // natural one (i.e. the one on the main driveline).
2362 const DriveNode* dn = DriveGraph::get()->getNode(
2363 DriveGraph::get()->getNode(sector)->getPredecessor(0));
2364 Vec3 impulse = dn->getCenter() - getXYZ();
2365 impulse.setY(0);
2366 if(impulse.getX() || impulse.getZ())
2367 impulse.normalize();
2368 else
2369 impulse = Vec3(0, 0, -1); // Arbitrary
2370 impulse *= m_kart_properties->getCollisionTerrainImpulse();
2371 m_bounce_back_ticks = (uint8_t)stk_config->time2Ticks(0.2f);
2372 m_vehicle->setTimedCentralImpulse(
2373 (uint16_t)stk_config->time2Ticks(0.1f), impulse);
2374 }
2375
2376 }
2377 /** If a kart is crashing against the track, the collision is often
2378 * reported more than once, resulting in a machine gun effect, and too
2379 * long disabling of the engine. Therefore, this reaction is disabled
2380 * for 0.5 seconds after a crash.
2381 */
2382 if(m && m->getCollisionReaction() != Material::NORMAL &&
2383 !getKartAnimation())
2384 {
2385 #ifndef SERVER_ONLY
2386 std::string particles = m->getCrashResetParticles();
2387 if (!GUIEngine::isNoGraphics() &&
2388 particles.size() > 0 && UserConfigParams::m_particles_effects > 0)
2389 {
2390 ParticleKind* kind =
2391 ParticleKindManager::get()->getParticles(particles);
2392 if (kind != NULL)
2393 {
2394 if (m_collision_particles == NULL)
2395 {
2396 Vec3 position(-getKartWidth()*0.35f, 0.06f,
2397 getKartLength()*0.5f);
2398 m_collision_particles =
2399 new ParticleEmitter(kind, position, getNode());
2400 }
2401 else
2402 {
2403 m_collision_particles->setParticleType(kind);
2404 }
2405 }
2406 else
2407 {
2408 Log::error("Kart","Unknown particles kind <%s> in material "
2409 "crash-reset properties\n", particles.c_str());
2410 }
2411 }
2412 #endif
2413 if (m->getCollisionReaction() == Material::RESCUE)
2414 {
2415 RescueAnimation::create(this);
2416 m_last_factor_engine_sound = 0.0f;
2417 }
2418 else if (m->getCollisionReaction() == Material::PUSH_BACK)
2419 {
2420 // This variable is set to 0.2 in case of a kart-terrain collision
2421 if (m_bounce_back_ticks <= (uint8_t)stk_config->time2Ticks(0.2f))
2422 {
2423 btVector3 push = m_body->getLinearVelocity().normalized();
2424 push[1] = 0.1f;
2425 m_body->applyCentralImpulse( -4000.0f*push );
2426 m_bounce_back_ticks = (uint8_t)stk_config->time2Ticks(2.0f);
2427 } // if m_bounce_back_ticks <= 0.2f
2428 } // if (m->getCollisionReaction() == Material::PUSH_BACK)
2429 } // if(m && m->getCollisionReaction() != Material::NORMAL &&
2430 // !getKartAnimation())
2431 m_controller->crashed(m);
2432 } // crashed(Material)
2433
2434 // -----------------------------------------------------------------------------
2435 /** Common code used when a kart or a material was hit.
2436 * @param m The material collided into, or NULL if none
2437 * @param k The kart collided into, or NULL if none
2438 */
playCrashSFX(const Material * m,AbstractKart * k)2439 void Kart::playCrashSFX(const Material* m, AbstractKart *k)
2440 {
2441 int ticks_since_start = World::getWorld()->getTicksSinceStart();
2442 if(ticks_since_start-m_ticks_last_crash < 60) return;
2443
2444 m_ticks_last_crash = ticks_since_start;
2445 // After a collision disable the engine for a short time so that karts
2446 // can 'bounce back' a bit (without this the engine force will prevent
2447 // karts from bouncing back, they will instead stuck towards the obstable).
2448 if(m_bounce_back_ticks == 0)
2449 {
2450 if (getVelocity().length()> 0.555f)
2451 {
2452 const float speed_for_max_volume = 15; //The speed at which the sound plays at maximum volume
2453 const float max_volume = 1; //The maximum volume a sound is played at
2454 const float min_volume = 0.2f; //The minimum volume a sound is played at
2455
2456 float volume; //The volume the crash sound will be played at
2457
2458 if (k == NULL) //Collision with wall
2459 {
2460 volume = sqrt( abs(m_speed / speed_for_max_volume));
2461 }
2462 else
2463 {
2464 const Vec3 ThisKartVelocity = getVelocity();
2465 const Vec3 OtherKartVelocity = k->getVelocity();
2466 const Vec3 VelocityDifference = ThisKartVelocity - OtherKartVelocity;
2467 const float LengthOfDifference = VelocityDifference.length();
2468
2469 volume = sqrt( abs(LengthOfDifference / speed_for_max_volume));
2470 }
2471
2472 if (volume > max_volume) { volume = max_volume; }
2473 else if (volume < min_volume) { volume = min_volume; }
2474
2475 SFXBase* crash_sound_emitter = getNextEmitter();
2476 crash_sound_emitter->setVolume(volume);
2477
2478 // In case that the sfx is longer than 0.5 seconds, only play it if
2479 // it's not already playing.
2480 if (isShielded() || (k != NULL && k->isShielded()))
2481 {
2482 crash_sound_emitter->play(getSmoothedXYZ(), m_boing_sound);
2483 }
2484 else
2485 {
2486 int idx = rand() % CRASH_SOUND_COUNT;
2487
2488 SFXBuffer* buffer = m_crash_sounds[idx];
2489 crash_sound_emitter->play(getSmoothedXYZ(), buffer);
2490 }
2491 } // if lin_vel > 0.555
2492 } // if m_bounce_back_ticks == 0
2493 } // playCrashSFX
2494
2495 // -----------------------------------------------------------------------------
2496 /** Plays a beep sfx.
2497 */
beep()2498 void Kart::beep()
2499 {
2500 // If the custom horn can't play (isn't defined) then play the default one
2501 if (!playCustomSFX(SFXManager::CUSTOM_HORN) &&
2502 !RewindManager::get()->isRewinding())
2503 {
2504 getNextEmitter()->play(getSmoothedXYZ(), m_horn_sound);
2505 }
2506
2507 } // beep
2508
2509 // -----------------------------------------------------------------------------
2510 /*
2511 playCustomSFX()
2512
2513 This function will play a particular character voice for this kart. It
2514 returns whether or not a character voice sample exists for the particular
2515 event. If there is no voice sample, a default can be played instead.
2516
2517 Use entries from the CustomSFX enumeration as a parameter (see
2518 SFXManager::get().hpp). eg. playCustomSFX(SFXManager::CUSTOM_CRASH)
2519
2520 Obviously we don't want a certain character voicing multiple phrases
2521 simultaneously. It just sounds bad. There are two ways of avoiding this:
2522
2523 1. If there is already a voice sample playing for the character
2524 don't play another until it is finished.
2525
2526 2. If there is already a voice sample playing for the character
2527 stop the sample, and play the new one.
2528
2529 Currently we're doing #2.
2530
2531 rforder
2532
2533 */
2534
playCustomSFX(unsigned int type)2535 bool Kart::playCustomSFX(unsigned int type)
2536 {
2537 // (TODO: add back when properly done)
2538 return false;
2539
2540 /*
2541 bool ret = false;
2542
2543 // Stop all other character voices for this kart before playing a new one
2544 // we don't want overlapping phrases coming from the same kart
2545 for (unsigned int n = 0; n < SFXManager::NUM_CUSTOMS; n++)
2546 {
2547 if (m_custom_sounds[n] != NULL)
2548 {
2549 // If the sound we're trying to play is already playing
2550 // don't stop it, we'll just let it finish.
2551 if (type != n) m_custom_sounds[n]->stop();
2552 }
2553 }
2554
2555 if (type < SFXManager::NUM_CUSTOMS)
2556 {
2557 if (m_custom_sounds[type] != NULL)
2558 {
2559 ret = true;
2560 //printf("Kart SFX: playing %s for %s.\n",
2561 // SFXManager::get()->getCustomTagName(type),
2562 // m_kart_properties->getIdent().c_str());
2563 // If it's already playing, let it finish
2564 if (m_custom_sounds[type]->getStatus() != SFXManager::SFX_PLAYING)
2565 {
2566 m_custom_sounds[type]->play();
2567 }
2568 }
2569 }
2570 return ret;
2571 */
2572 }
2573 // ----------------------------------------------------------------------------
2574 /** Updates the physics for this kart: computing the driving force, set
2575 * steering, handles skidding, terrain impact on kart, ...
2576 * \param ticks Number if physics time steps - should be 1.
2577 */
updatePhysics(int ticks)2578 void Kart::updatePhysics(int ticks)
2579 {
2580 if (m_controls.getAccel() > 0.0f &&
2581 World::getWorld()->getTicksSinceStart() == 1)
2582 {
2583 if (m_startup_boost > 0.0f)
2584 {
2585 m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_ZIPPER,
2586 100.0f * m_startup_boost);
2587 m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_ZIPPER,
2588 0.9f * m_startup_boost, m_startup_boost,
2589 /*engine_force*/200.0f,
2590 /*duration*/stk_config->time2Ticks(5.0f),
2591 /*fade_out_time*/stk_config->time2Ticks(5.0f));
2592 }
2593 }
2594 if (m_bounce_back_ticks > 0)
2595 m_bounce_back_ticks -= ticks;
2596
2597 updateEnginePowerAndBrakes(ticks);
2598
2599 // apply flying physics if relevant
2600 if (m_flying)
2601 updateFlying();
2602
2603 m_skidding->update(ticks, isOnGround(), m_controls.getSteer(),
2604 m_controls.getSkidControl());
2605 if( ( m_skidding->getSkidState() == Skidding::SKID_ACCUMULATE_LEFT ||
2606 m_skidding->getSkidState() == Skidding::SKID_ACCUMULATE_RIGHT ) &&
2607 !m_skidding->isJumping() )
2608 {
2609 if(m_skid_sound->getStatus()!=SFXBase::SFX_PLAYING && !isWheeless())
2610 m_skid_sound->play(getSmoothedXYZ());
2611 }
2612 else if(m_skid_sound->getStatus()==SFXBase::SFX_PLAYING)
2613 {
2614 m_skid_sound->stop();
2615 }
2616
2617 float steering = getMaxSteerAngle() * m_skidding->getSteeringFraction();
2618 m_vehicle->setSteeringValue(steering, 0);
2619 m_vehicle->setSteeringValue(steering, 1);
2620
2621 updateSliding();
2622
2623 // Cap speed if necessary
2624 const Material *m = getMaterial();
2625
2626 float min_speed = m && m->isZipper() ? m->getZipperMinSpeed() : -1.0f;
2627 m_max_speed->setMinSpeed(min_speed);
2628 m_max_speed->update(ticks);
2629
2630 #ifdef XX
2631 Log::info("Kart","angVel %f %f %f heading %f suspension %f %f %f %f"
2632 ,m_body->getAngularVelocity().getX()
2633 ,m_body->getAngularVelocity().getY()
2634 ,m_body->getAngularVelocity().getZ()
2635 ,getHeading()
2636 ,m_vehicle->getWheelInfo(0).m_raycastInfo.m_suspensionLength
2637 ,m_vehicle->getWheelInfo(1).m_raycastInfo.m_suspensionLength
2638 ,m_vehicle->getWheelInfo(2).m_raycastInfo.m_suspensionLength
2639 ,m_vehicle->getWheelInfo(3).m_raycastInfo.m_suspensionLength
2640 );
2641 #endif
2642
2643 } // updatephysics
2644
2645 //-----------------------------------------------------------------------------
2646 /** Adjust the engine sound effect depending on the speed of the kart. This
2647 * is called during updateGraphics, i.e. once per rendered frame only.
2648 * \param dt Time step size.
2649 */
updateEngineSFX(float dt)2650 void Kart::updateEngineSFX(float dt)
2651 {
2652 // Only update SFX during the last substep (otherwise too many SFX commands
2653 // in one frame), and if sfx are enabled
2654 if(!m_engine_sound || !SFXManager::get()->sfxAllowed() )
2655 return;
2656
2657 // when going faster, use higher pitch for engine
2658 if(isOnGround())
2659 {
2660 float max_speed = m_kart_properties->getEngineMaxSpeed();
2661
2662 // Engine noise is based half in total speed, half in fake gears:
2663 // With a sawtooth graph like /|/|/| we get 3 even spaced gears,
2664 // ignoring the gear settings from stk_config, but providing a
2665 // good enough brrrBRRRbrrrBRRR sound effect. Speed factor makes
2666 // it a "staired sawtooth", so more acoustically rich.
2667 float f = max_speed > 0 ? m_speed/max_speed : 1.0f;
2668 // Speed at this stage is not yet capped, reduce the amount beyond 1
2669 if (f> 1.0f) f = 1.0f + (1.0f-1.0f/f);
2670
2671 float fc = f;
2672 if (fc>1.0f) fc = 1.0f;
2673 float gears = 3.0f * fmod(fc, 0.333334f);
2674 assert(!std::isnan(f));
2675 m_last_factor_engine_sound = (0.9f*f + gears) * 0.35f;
2676 m_engine_sound->setSpeedPosition(0.6f + m_last_factor_engine_sound, getSmoothedXYZ());
2677 }
2678 else
2679 {
2680 // When flying, reduce progressively the sound engine (since we can't accelerate)
2681 m_last_factor_engine_sound *= (1.0f-0.1f*dt);
2682 m_engine_sound->setSpeedPosition(0.6f + m_last_factor_engine_sound, getSmoothedXYZ());
2683 if (m_speed < 0.1f) m_last_factor_engine_sound = 0.0f;
2684 }
2685 } // updateEngineSFX
2686
2687
2688
2689 //-----------------------------------------------------------------------------
2690 /** Reduces the engine power according to speed
2691 *
2692 * TODO : find where the physics already apply a linear force decrease
2693 * TODO : While this work fine, it should ideally be in physics
2694 * However, the function use some kart properties and parachute
2695 * effect needs to be applied, so keep both working if moving
2696 * \param engine_power : the engine power on which to apply the decrease
2697 */
applyAirFriction(float engine_power)2698 float Kart::applyAirFriction(float engine_power)
2699 {
2700 //The physics already do that a certain amount of engine force is needed to keep going
2701 //at a given speed (~39,33 engine force = 1 speed for a mass of 350)
2702 //But it's either too slow to accelerate to a target speed or makes it
2703 //too easy to accelerate farther.
2704 //Instead of making increasing gears have enormous power gaps, apply friction
2705
2706 float mass_factor = m_kart_properties->getMass()/350.0f;
2707 float compense_linear_slowdown = 39.33f*fabsf(getSpeed())*mass_factor;
2708
2709 engine_power += compense_linear_slowdown;
2710
2711 // The result will always be a positive number
2712 float friction_intensity = fabsf(getSpeed());
2713
2714 // Not a pure quadratic evolution as it would be too brutal
2715 friction_intensity *= sqrt(friction_intensity)*5;
2716
2717 // Apply parachute physics
2718 // Currently, all karts have the same base friction
2719 // If this is changed, a compensation needs to be added here
2720 if(m_attachment->getType()==Attachment::ATTACH_PARACHUTE)
2721 friction_intensity *= m_kart_properties->getParachuteFriction();
2722
2723 if (friction_intensity < 0.0f) friction_intensity = 0.0f;
2724
2725 // We subtract the friction from the engine power
2726 // 1)This is the logical behavior
2727 // 2)That way, engine boosts remain useful at high speed
2728 // 3)It helps heavier karts, who have an higher engine power
2729
2730 engine_power-=friction_intensity;
2731
2732 return engine_power;
2733 } //applyAirFriction
2734
2735 //-----------------------------------------------------------------------------
2736 /** Sets the engine power. It considers the engine specs, items that influence
2737 * the available power, and braking/steering.
2738 */
updateEnginePowerAndBrakes(int ticks)2739 void Kart::updateEnginePowerAndBrakes(int ticks)
2740 {
2741 updateWeight();
2742 updateNitro(ticks);
2743 float engine_power = getActualWheelForce();
2744
2745 // apply nitro boost if relevant
2746 if(getSpeedIncreaseTicksLeft(MaxSpeed::MS_INCREASE_NITRO) > 0)
2747 {
2748 engine_power*= m_kart_properties->getNitroEngineMult();
2749 }
2750
2751 // apply bubblegum physics if relevant
2752 if (m_bubblegum_ticks > 0)
2753 {
2754 engine_power = 0.0f;
2755 m_body->applyTorque(btVector3(0.0,
2756 m_kart_properties->getBubblegumTorque() *
2757 (m_bubblegum_torque_sign ? 1.0f : -1.0f), 0.0));
2758 }
2759
2760 if(m_controls.getAccel()) // accelerating
2761 {
2762 // For a short time after a collision disable the engine,
2763 // so that the karts can bounce back a bit from the obstacle.
2764 if (m_bounce_back_ticks > 0)
2765 engine_power = 0.0f;
2766 // let a player going backwards accelerate quickly (e.g. if a player
2767 // hits a wall, he needs to be able to start again quickly after
2768 // going backwards)
2769 else if(m_speed < 0.0f)
2770 engine_power *= 5.0f;
2771
2772 // Lose some traction when skidding, to balance the advantage
2773 if (m_controls.getSkidControl() &&
2774 m_kart_properties->getSkidVisualTime() == 0)
2775 engine_power *= 0.5f;
2776
2777 // This also applies parachute physics if relevant
2778 engine_power = applyAirFriction(engine_power);
2779
2780 applyEngineForce(engine_power*m_controls.getAccel());
2781
2782 // Either all or no brake is set, so test only one to avoid
2783 // resetting all brakes most of the time.
2784 if(m_vehicle->getWheelInfo(0).m_brake &&
2785 !World::getWorld()->isStartPhase())
2786 m_vehicle->setAllBrakes(0);
2787 m_brake_ticks = 0;
2788 }
2789 else // not accelerating
2790 {
2791 //The engine power is still guaranteed >= 0 at this point
2792 float braking_power = engine_power;
2793
2794 // This also applies parachute physics if relevant
2795 engine_power = applyAirFriction(engine_power);
2796
2797 if(m_controls.getBrake())
2798 { // check if the player is currently only slowing down
2799 // or moving backwards
2800 if(m_speed > 0.0f)
2801 { // Still going forward while braking
2802 applyEngineForce(engine_power-braking_power*3);
2803 m_brake_ticks += ticks;
2804 // Apply the brakes - include the time dependent brake increase
2805 float f = 1.0f + stk_config->ticks2Time(m_brake_ticks)
2806 * m_kart_properties->getEngineBrakeTimeIncrease();
2807 m_vehicle->setAllBrakes(m_kart_properties->getEngineBrakeFactor() * f);
2808 }
2809 else // m_speed < 0
2810 {
2811 m_vehicle->setAllBrakes(0);
2812 // going backward, apply reverse gear ratio (unless he goes
2813 // too fast backwards)
2814 if ( -m_speed < m_max_speed->getCurrentMaxSpeed()
2815 *m_kart_properties->getEngineMaxSpeedReverseRatio())
2816 {
2817 // The backwards acceleration is artificially increased to
2818 // allow players to get "unstuck" quicker if they hit e.g.
2819 // a wall.
2820 applyEngineForce(engine_power-braking_power*3);
2821 }
2822 else // -m_speed >= max speed on this terrain
2823 {
2824 applyEngineForce(0.0f);
2825 }
2826
2827 } // m_speed <00
2828 }
2829 else // no braking and no acceleration
2830 {
2831 m_brake_ticks = 0;
2832 // lift the foot from throttle, let friction slow it down
2833 assert(!std::isnan(m_controls.getAccel()));
2834 assert(!std::isnan(engine_power));
2835
2836 // If not giving power (forward or reverse gear), and speed is low
2837 // we are "parking" the kart, so in battle mode we can ambush people
2838 if (std::abs(m_speed) < 5.0f)
2839 {
2840 // Engine must be 0, otherwise braking is not used at all
2841 applyEngineForce(0);
2842 m_vehicle->setAllBrakes(20.0f);
2843 }
2844 else
2845 {
2846 // This ensure that parachute and air friction are applied
2847 // and that the kart gracefully slows down
2848 applyEngineForce(engine_power-braking_power);
2849 m_vehicle->setAllBrakes(0);
2850 }
2851 } // no braking and no acceleration
2852 } // not accelerating
2853 } // updateEnginePowerAndBrakes
2854
2855 // ----------------------------------------------------------------------------
2856 /** Handles sliding, i.e. the kart sliding off terrain that is too steep.
2857 * Dynamically determine friction so that the kart looses its traction
2858 * when trying to drive on too steep surfaces. Below angles of 0.25 rad,
2859 * you have full traction; above 0.5 rad angles you have absolutely none;
2860 * inbetween there is a linear change in friction. This is done for each
2861 * wheel individually (since otherwise karts were still able with enough
2862 * speed to drive on walls - one wheel 'on a wall' would not tilt the
2863 * kart chassis enough to trigger sliding, but since that wheel had still
2864 * full friction, give the kart an upwards velocity).
2865 */
updateSliding()2866 void Kart::updateSliding()
2867 {
2868 // Allow the sliding to be disabled per material (for so called
2869 // high adhesion material), which is useful for e.g. banked curves.
2870 // We don't have per-wheel material, so the test for special material
2871 // with high adhesion is done per kart (not per wheel).
2872 const Material * material = getMaterial();
2873 if (material && material->highTireAdhesion())
2874 {
2875 for (int i = 0; i < m_vehicle->getNumWheels(); i++)
2876 {
2877 btWheelInfo &wheel = m_vehicle->getWheelInfo(i);
2878 wheel.m_frictionSlip = m_kart_properties->getFrictionSlip();
2879 }
2880 m_vehicle->setSliding(false);
2881
2882 }
2883
2884 // Now test for each wheel if it should be sliding
2885 // -----------------------------------------------
2886 bool enable_sliding = false;
2887
2888 // We need the 'up' vector, which can be affected by material
2889 // with gravity. So use the body's gravity to determine up:
2890 Vec3 up = -m_body->getGravity();
2891 up.normalize();
2892 for (int i = 0; i < m_vehicle->getNumWheels(); i++)
2893 {
2894 const btWheelInfo &wheel = m_vehicle->getWheelInfo(i);
2895 if (!wheel.m_raycastInfo.m_isInContact) continue;
2896
2897 const btVector3 &norm = m_vehicle->getWheelInfo(i).m_raycastInfo.m_contactNormalWS;
2898 float distanceFromUp = norm.dot(up);
2899 float friction;
2900 if (distanceFromUp < 0.85f)
2901 {
2902 friction = 0.0f;
2903 enable_sliding = true;
2904 }
2905 else if (distanceFromUp > 0.9f)
2906 {
2907 friction = 1.0f;
2908 }
2909 else
2910 {
2911 friction = (distanceFromUp - 0.85f) / 0.5f;
2912 enable_sliding = true;
2913 }
2914 m_vehicle->getWheelInfo(i).m_frictionSlip = friction * m_kart_properties->getFrictionSlip();
2915 } // for i < numWheels
2916
2917 m_vehicle->setSliding(enable_sliding);
2918 } // updateSliding
2919
2920 // ----------------------------------------------------------------------------
2921 /** Adjusts kart translation if the kart is flying (in debug mode).
2922 */
updateFlying()2923 void Kart::updateFlying()
2924 {
2925 m_body->setLinearVelocity(m_body->getLinearVelocity() * 0.99f);
2926
2927 if (m_controls.getAccel())
2928 {
2929 btVector3 velocity = m_body->getLinearVelocity();
2930 if (velocity.length() < 25)
2931 {
2932 float orientation = getHeading();
2933 m_body->applyCentralImpulse(btVector3(100.0f*sinf(orientation), 0.0,
2934 100.0f*cosf(orientation)));
2935 }
2936 }
2937 else if (m_controls.getBrake())
2938 {
2939 btVector3 velocity = m_body->getLinearVelocity();
2940 if (velocity.length() > -15)
2941 {
2942 float orientation = getHeading();
2943 m_body->applyCentralImpulse(btVector3(-100.0f*sinf(orientation), 0.0,
2944 -100.0f*cosf(orientation)));
2945 }
2946 }
2947
2948 if (m_controls.getSteer()!= 0.0f)
2949 {
2950 m_body->applyTorque(btVector3(0.0, m_controls.getSteer()*3500.0f, 0.0));
2951 }
2952
2953 // dampen any roll while flying, makes the kart hard to control
2954 btVector3 velocity = m_body->getAngularVelocity();
2955 velocity.setX(0);
2956 velocity.setZ(0);
2957 m_body->setAngularVelocity(velocity);
2958
2959 } // updateFlying
2960
2961 // ----------------------------------------------------------------------------
2962 /** Attaches the right model, creates the physics and loads all special
2963 * effects (particle systems etc.)
2964 * \param type Type of the kart.
2965 * \param is_animated_model True if the model is animated.
2966 */
loadData(RaceManager::KartType type,bool is_animated_model)2967 void Kart::loadData(RaceManager::KartType type, bool is_animated_model)
2968 {
2969 bool always_animated = (type == RaceManager::KT_PLAYER &&
2970 RaceManager::get()->getNumLocalPlayers() == 1);
2971 if (!GUIEngine::isNoGraphics())
2972 m_node = m_kart_model->attachModel(is_animated_model, always_animated);
2973
2974 #ifdef DEBUG
2975 if (m_node)
2976 m_node->setName( (getIdent()+"(lod-node)").c_str() );
2977 #endif
2978
2979 // Attachment must be created after attachModel, since only then the
2980 // scene node will exist (to which the attachment is added). But the
2981 // attachment is needed in createPhysics (which gets the mass, which
2982 // is dependent on the attachment).
2983 m_attachment.reset(new Attachment(this));
2984 createPhysics();
2985
2986 m_slipstream.reset(new SlipStream(this));
2987
2988 #ifndef SERVER_ONLY
2989 m_skidmarks = nullptr;
2990 m_shadow = nullptr;
2991 if (!GUIEngine::isNoGraphics() &&
2992 m_kart_properties->getSkidEnabled() && CVS->isGLSL())
2993 {
2994 m_skidmarks.reset(new SkidMarks(*this));
2995 }
2996
2997 if (!GUIEngine::isNoGraphics() &&
2998 CVS->isGLSL() && !CVS->isShadowEnabled() && m_kart_properties
2999 ->getShadowMaterial()->getSamplerPath(0) != "unicolor_white")
3000 {
3001 m_shadow.reset(new Shadow(m_kart_properties->getShadowMaterial(),
3002 *this));
3003 }
3004 #endif
3005 World::getWorld()->kartAdded(this, m_node);
3006 m_kart_gfx.reset(
3007 new KartGFX(this, Track::getCurrentTrack()->getIsDuringDay()));
3008 m_skidding.reset(new Skidding(this));
3009 // Create the stars effect
3010 if (!GUIEngine::isNoGraphics())
3011 m_stars_effect.reset(new Stars(this));
3012
3013 } // loadData
3014
3015 // ----------------------------------------------------------------------------
3016 /** Applies engine power to all the wheels that are traction capable,
3017 * so other parts of code do not have to be adjusted to simulate different
3018 * kinds of vehicles in the general case, only if they are trying to
3019 * simulate traction control, diferentials or multiple independent electric
3020 * engines, they will have to tweak the power in a per wheel basis.
3021 */
applyEngineForce(float force)3022 void Kart::applyEngineForce(float force)
3023 {
3024 assert(!std::isnan(force));
3025 // Split power to simulate a 4WD 40-60, other values possible
3026 // FWD or RWD is a matter of putting a 0 and 1 in the right place
3027 float frontForce = force*0.4f;
3028 float rearForce = force*0.6f;
3029 // Front wheels
3030 for(unsigned int i=0; i<2; i++)
3031 {
3032 m_vehicle->applyEngineForce (frontForce, i);
3033 }
3034 // Rear wheels
3035 for(unsigned int i=2; i<4; i++)
3036 {
3037 m_vehicle->applyEngineForce (rearForce, i);
3038 }
3039 } // applyEngineForce
3040
3041 //-----------------------------------------------------------------------------
3042 /** Computes the transform of the graphical kart chasses with regards to the
3043 * physical chassis. This function is called once the kart comes to rest
3044 * before the race starts (see World::resetAllKarts). Based on the current
3045 * physical kart position it computes an (at this stage Y-only) offset by
3046 * which the graphical chassis is moved so that it appears the way it is
3047 * designed in blender. This means that the distance of the wheels from the
3048 * chassis (i.e. suspension) appears as in blender when karts are in rest.
3049 * See updateGraphics for more details.
3050 */
kartIsInRestNow()3051 void Kart::kartIsInRestNow()
3052 {
3053 AbstractKart::kartIsInRestNow();
3054 m_default_suspension_force = 0.0f;
3055 for (int i = 0; i < m_vehicle->getNumWheels(); i++)
3056 {
3057 const btWheelInfo &wi = m_vehicle->getWheelInfo(i);
3058 m_default_suspension_force += wi.m_raycastInfo.m_suspensionLength;
3059 }
3060
3061 // The offset 'lowest point' is added to avoid that the
3062 // visual chassis appears in the ground (it could be any
3063 // constant, there is no real reason to use the lowest point
3064 // but that value has worked good in the past). See documentation
3065 // for updateGraphics() for full details.
3066 m_graphical_y_offset = -m_default_suspension_force /
3067 m_vehicle->getNumWheels() + m_kart_model->getLowestPoint();
3068
3069 m_kart_model->setDefaultSuspension();
3070 } // kartIsInRestNow
3071
3072 //-----------------------------------------------------------------------------
3073
getNextEmitter()3074 SFXBase* Kart::getNextEmitter()
3075 {
3076 m_emitter_id = (m_emitter_id + 1) % EMITTER_COUNT;
3077
3078 // The emitter is requested when a new sound is to be played.
3079 // Always reset the volume to 1.0f (full), as crashes may
3080 // have altered it. See issue #3596
3081 m_emitters[m_emitter_id]->setVolume(1.0f);
3082
3083 return m_emitters[m_emitter_id];
3084 }
3085
3086 //-----------------------------------------------------------------------------
3087 /** Updates the graphics model. It is responsible for positioning the graphical
3088 * chasses at an 'appropriate' position: typically, the physical model has
3089 * much longer suspension, so if the graphical chassis would be at the same
3090 * location as the physical chassis, the wheels would be too far away.
3091 * Instead the default suspension length is saved once the kart have been
3092 * settled at start up (see World::resetAllKarts, which just runs several
3093 * physics-only simulation steps so that karts come to a rest). Then at
3094 * race time, only the difference between the current suspension length and
3095 * this default suspension length is used. The graphical kart chassis will be
3096 * offset so that when the kart is in rest, i.e. suspension length ==
3097 * default suspension length, the kart will look the way it was modelled in
3098 * blender. To explain the various offsets used, here a view from the side
3099 * focusing on the Y axis only (X/Z position of the graphical chassis is
3100 * identical to the physical chassis):
3101 *
3102 * Y| | visual kart | physical kart
3103 * | | |
3104 * | | |
3105 * | | |
3106 * | | +-------------COG---------------
3107 * | | :
3108 * | +---------low------ :
3109 * | O :
3110 * +--------------------------------------------------------------------------
3111 * X
3112 * 'O' : visual wheel ':' : raycast from physics
3113 * 'low' : lowest Y coordinate of COG : Center of gravity (at bottom of
3114 * model chassis)
3115 *
3116 * The visual kart is stored so that if it is shown at (0,0,0) it would be
3117 * the same as in blender. This on the other hand means, if the kart is shown
3118 * at the position of the physical chassis (which is at COG in the picture
3119 * above), the kart and its wheels would be floating in the air (exactly by
3120 * as much as the suspension length), and the wheels would be exactly at the
3121 * bottom of the physical chassis (i.e. just on the plane going through COG
3122 * and parallel to the ground).
3123 * If we want to align the visual chassis to be the same as the physical
3124 * chassis, we would need to subtract 'low' from the physical position.
3125 * If the kart is then displayed at COG.y-low, the bottom of the kart (which
3126 * is at 'low' above ground) would be at COG.y-low + low = COG.y --> visual
3127 * and physical chassis are identical.
3128 *
3129 * Unfortunately, the suspension length used in the physics is much too high,
3130 * the karts would be way above their wheels, basically disconneccted
3131 * (typical physical suspension length is around 0.28, while the distance
3132 * between wheel and chassis in blender is in the order of 0.10 --> so there
3133 * would be an additional distance of around 0.18 between wheel chassis as
3134 * designed in blender and in stk - even more if the kart is driving downhill
3135 * when the suspension extends further to keep contact with the ground).
3136 * To make the visuals look closer to what they are in blender, an additional
3137 * offset is added: before the start of a race the physics simulation is run
3138 * to find a stable position for each kart (see World::resetAllKarts). Once
3139 * a kart is stable, we save this suspension length in m_graphical_y_offset.
3140 * This offset is subtracted from the COG of the kart. So if the kart is in
3141 * rest (i.e. suspenion == default_suspension == m_graphical_y_offset),
3142 * The kart is showen exactly at the same height above ground as it is in
3143 * blender. If the suspension is shorter by DY (because the kart is
3144 * accelerating, the ground goes up, ...), the visual chassis is lowered by
3145 * DY as well.
3146 *
3147 * While the above algorithm indeed results in the right position of the
3148 * visual chassis, in reality the visual chassis is too low. For example,
3149 * nolok's chassis has its lowest point at the rear at around 0.10 above the
3150 * ground (and the lowest point overall is 0.05, though this is at the front
3151 * and so not easily visible), so if the suspension is compressed by more than
3152 * that, the chassiswill appear to be in the ground. Testing on the sand track
3153 * shows that the suspension is compressed by 0.12 (and up to 0.16 in some
3154 * extreme points), which means that the chassis will appear to be in the
3155 * ground quite easily. Therefore the chassis is actually moved up a bit to
3156 * avoid this problem. Historically (due to never sorting out that formula
3157 * properly) the chassis was moved twice as high as its lowest point, e.g.
3158 * nolok's lowest point is at 0.05, so the whole chassis was raised by 0.05
3159 * (this was not done by design, but because of a bug ;) ). Since this visual
3160 * offset has worked well in the past, the visual chassis is moved by the
3161 * same amount higher.
3162 *
3163 * Of course this means that the Y position of the wheels (relative to the
3164 * visual kart chassis) needs to be adjusted: if the kart is in rest, the
3165 * wheels are exactly on the ground. If the suspension is shorter, that wheel
3166 * would appear to be partly in the ground, and if the suspension is longer,
3167 * the wheel would not touch the ground.
3168 *
3169 * The wheels must be offset by how much the current suspension length is
3170 * longer or shorter than the default (i.e. at rest) suspension length.
3171 * This is done in KartModel (pos is the position of the wheel relative
3172 * to the visual kart chassis):
3173 * pos.Y += m_default_physics_suspension[i]
3174 * - wi.m_raycastInfo.m_suspensionLength
3175 * But since the chassis is raised an additional 'getLowestPoint' (see
3176 * desctiption two paragraphs above), the wheels need to be lowered by that
3177 * amount so that they still touch the ground (the wheel nodes are child of
3178 * the chassis scene node, so if the chassis is raised by X, the wheels need
3179 * to be lowered by X).
3180 * This function also takes additional graphical effects into account, e.g.
3181 * a (visual only) jump when skidding, and leaning of the kart.
3182 */
updateGraphics(float dt)3183 void Kart::updateGraphics(float dt)
3184 {
3185 /* (TODO: add back when properly done)
3186 for (int n = 0; n < SFXManager::NUM_CUSTOMS; n++)
3187 {
3188 if (m_custom_sounds[n] != NULL) m_custom_sounds[n]->position(getXYZ());
3189 }
3190 */
3191 #ifndef SERVER_ONLY
3192 if (m_node && isSquashed() &&
3193 m_node->getScale() != core::vector3df(1.0f, 0.5f, 1.0f))
3194 setSquashGraphics();
3195 else if (m_node && !isSquashed() &&
3196 m_node->getScale() != core::vector3df(1.0f, 1.0f, 1.0f))
3197 unsetSquash();
3198 #endif
3199
3200 // Disable smoothing network body so it doesn't smooth the animation
3201 // for karts in client
3202 if (NetworkConfig::get()->isNetworking() &&
3203 NetworkConfig::get()->isClient() &&
3204 (!getController() || !getController()->isLocalPlayerController()))
3205 {
3206 if (m_kart_animation && SmoothNetworkBody::isEnabled())
3207 {
3208 SmoothNetworkBody::setEnable(false);
3209 }
3210 else if (!m_kart_animation && !SmoothNetworkBody::isEnabled())
3211 {
3212 SmoothNetworkBody::setEnable(true);
3213 SmoothNetworkBody::reset();
3214 SmoothNetworkBody::setSmoothedTransform(getTrans());
3215 }
3216 }
3217
3218 if (m_kart_animation)
3219 m_kart_animation->updateGraphics(dt);
3220
3221 for (int i = 0; i < EMITTER_COUNT; i++)
3222 m_emitters[i]->setPosition(getXYZ());
3223 m_skid_sound->setPosition(getSmoothedXYZ());
3224 m_nitro_sound->setPosition(getSmoothedXYZ());
3225
3226 m_attachment->updateGraphics(dt);
3227
3228 // update star effect (call will do nothing if stars are not activated)
3229 // Remove it if no invulnerability
3230 if (m_stars_effect)
3231 {
3232 if (!isInvulnerable() && m_stars_effect->isEnabled())
3233 {
3234 m_stars_effect->reset();
3235 m_stars_effect->update(1);
3236 }
3237 else
3238 m_stars_effect->update(dt);
3239 }
3240
3241 // Update particle effects (creation rate, and emitter size
3242 // depending on speed)
3243 m_kart_gfx->update(dt);
3244 if (m_collision_particles) m_collision_particles->update(dt);
3245
3246 // --------------------------------------------------------
3247 float nitro_frac = 0;
3248 if ( (m_controls.getNitro() || m_min_nitro_ticks > 0) &&
3249 m_collected_energy > 0 )
3250 {
3251 // fabs(speed) is important, otherwise the negative number will
3252 // become a huge unsigned number in the particle scene node!
3253 nitro_frac = fabsf(getSpeed()) / (m_kart_properties->getEngineMaxSpeed());
3254 // The speed of the kart can be higher (due to powerups) than
3255 // the normal maximum speed of the kart.
3256 if(nitro_frac>1.0f) nitro_frac = 1.0f;
3257 }
3258 m_kart_gfx->updateNitroGraphics(nitro_frac);
3259
3260 // Handle leaning of karts
3261 // -----------------------
3262 // Note that we compare with maximum speed of the kart, not
3263 // maximum speed including terrain effects. This avoids that
3264 // leaning might get less if a kart gets a special that increases
3265 // its maximum speed, but not the current speed (by much). On the
3266 // other hand, that ratio can often be greater than 1.
3267
3268 float speed_frac = m_speed / m_kart_properties->getEngineMaxSpeed();
3269 float steer_frac = m_skidding->getSteeringFraction();
3270 const float roll_speed = m_kart_properties->getLeanSpeed() * DEGREE_TO_RAD;
3271
3272 if(speed_frac > 0.8f && fabsf(steer_frac)>0.2f)
3273 {
3274 // Use steering and speed ^ 2,
3275 // which means less effect at lower steering and speed.
3276 speed_frac = std::min(speed_frac - 0.6f, 1.0f);
3277 steer_frac = (steer_frac+0.25f)*0.8f;
3278 const float f = m_skidding->getSteeringFraction();
3279 const float max_lean = -m_kart_properties->getLeanMax() * DEGREE_TO_RAD
3280 * f * speed_frac * speed_frac;
3281
3282 int max_lean_sign = extract_sign(max_lean);
3283 m_current_lean += max_lean_sign * dt* roll_speed;
3284 if( (max_lean > 0 && m_current_lean > max_lean)
3285 ||(max_lean < 0 && m_current_lean < max_lean))
3286 m_current_lean = max_lean;
3287 }
3288 else if(m_current_lean!=0.0f)
3289 {
3290 // Disable any potential roll factor that is still applied
3291 int lean_sign = extract_sign(m_current_lean);
3292 m_current_lean -= lean_sign * dt * roll_speed;
3293 if (lean_sign != extract_sign(m_current_lean))
3294 m_current_lean = 0.0f;
3295 }
3296
3297 // If the kart is leaning, part of the kart might end up 'in' the track.
3298 // To avoid this, raise the kart enough to offset the leaning.
3299 float lean_height = tanf(m_current_lean) * getKartWidth()*0.5f;
3300
3301 Moveable::updateSmoothedGraphics(dt);
3302
3303 // Update the skidding jump height:
3304 Vec3 center_shift(0, 0, 0);
3305 float jump_height = m_skidding->updateGraphics(dt);
3306 center_shift.setY(jump_height + fabsf(lean_height) + m_graphical_y_offset);
3307 center_shift = getSmoothedTrans().getBasis() * center_shift;
3308
3309 float heading = m_skidding->getVisualSkidRotation();
3310 Moveable::updateGraphics(center_shift,
3311 btQuaternion(heading, 0, -m_current_lean));
3312
3313 static video::SColor pink(255, 255, 133, 253);
3314 static video::SColor green(255, 61, 87, 23);
3315
3316 #ifndef SERVER_ONLY
3317 // draw skidmarks if relevant (we force pink skidmarks on when hitting
3318 // a bubblegum)
3319 if (World::getWorld()->getPhase() !=
3320 WorldStatus::IN_GAME_MENU_PHASE &&
3321 m_kart_properties->getSkidEnabled() && m_skidmarks)
3322 {
3323 m_skidmarks->update(dt,
3324 m_bubblegum_ticks > 0,
3325 (m_bubblegum_ticks > 0
3326 ? (m_has_caught_nolok_bubblegum ? &green
3327 : &pink)
3328 : NULL));
3329 }
3330 #endif
3331
3332 // m_speed * dt is the distance the kart has moved, which determines
3333 // how much the wheels need to rotate.
3334 m_kart_model->update(dt, m_speed * dt, getSteerPercent(), m_speed,
3335 m_current_lean);
3336
3337 #ifndef SERVER_ONLY
3338 // Determine the shadow position from the terrain Y position. This
3339 // leaves the shadow on the ground even if the kart is jumping because
3340 // of skidding (shadows are disabled when wheel are not on the track).
3341 if (m_shadow)
3342 {
3343 const bool emergency = getKartAnimation() != NULL;
3344 m_shadow->update(isOnGround() && !emergency);
3345 }
3346 #endif
3347
3348 handleMaterialGFX(dt);
3349 updateEngineSFX(dt);
3350 handleMaterialSFX();
3351 } // updateGraphics
3352
3353 // ----------------------------------------------------------------------------
getVisualRotation() const3354 btQuaternion Kart::getVisualRotation() const
3355 {
3356 return getRotation()
3357 * btQuaternion(m_skidding->getVisualSkidRotation(), 0, 0);
3358 } // getVisualRotation
3359
3360 // ----------------------------------------------------------------------------
3361 /** Sets a text that is being displayed on top of a kart. This can be 'leader'
3362 * for the leader kart in a FTL race, the name of a driver, or even debug
3363 * output.
3364 * \param text The text to display
3365 */
setOnScreenText(const core::stringw & text)3366 void Kart::setOnScreenText(const core::stringw& text)
3367 {
3368 #ifndef SERVER_ONLY
3369 if (GUIEngine::isNoGraphics())
3370 return;
3371
3372 BoldFace* bold_face = font_manager->getFont<BoldFace>();
3373 STKTextBillboard* tb =
3374 new STKTextBillboard(
3375 GUIEngine::getSkin()->getColor("font::bottom"),
3376 GUIEngine::getSkin()->getColor("font::top"),
3377 getNode(), irr_driver->getSceneManager(), -1,
3378 core::vector3df(0.0f, 1.5f, 0.0f),
3379 core::vector3df(0.5f, 0.5f, 0.5f));
3380 if (CVS->isGLSL())
3381 tb->init(text, bold_face);
3382 else
3383 tb->initLegacy(text, bold_face);
3384 tb->drop();
3385 // No need to store the reference to the billboard scene node:
3386 // It has one reference to the parent, and will get deleted
3387 // when the parent is deleted.
3388 #endif
3389 } // setOnScreenText
3390
3391 // ------------------------------------------------------------------------
3392 /** Returns the normal of the terrain the kart is over atm. This is
3393 * defined even if the kart is flying. */
getNormal() const3394 const Vec3& Kart::getNormal() const
3395 {
3396 return m_terrain_info->getNormal();
3397 } // getNormal
3398
3399 // ------------------------------------------------------------------------
3400 /** Returns a more recent different previous position */
getRecentPreviousXYZ() const3401 const Vec3& Kart::getRecentPreviousXYZ() const
3402 {
3403 //Not the most recent, because the angle variations would be too
3404 //irregular on some tracks whose roads are not smooth enough
3405 return m_previous_xyz[m_xyz_history_size/5];
3406 } // getRecentPreviousXYZ
3407
3408 // ------------------------------------------------------------------------
playSound(SFXBuffer * buffer)3409 void Kart::playSound(SFXBuffer* buffer)
3410 {
3411 if (!RewindManager::get()->isRewinding())
3412 getNextEmitter()->play(getSmoothedXYZ(), buffer);
3413 } // playSound
3414
3415 // ------------------------------------------------------------------------
getColor() const3416 const video::SColor& Kart::getColor() const
3417 {
3418 return m_kart_properties->getColor();
3419 } // getColor
3420
3421 // ------------------------------------------------------------------------
isVisible() const3422 bool Kart::isVisible() const
3423 {
3424 return m_node && m_node->isVisible();
3425 } // isVisible
3426
3427 /* EOF */
3428