669 lines
23 KiB
C++
669 lines
23 KiB
C++
//
|
|
// SuperTuxKart - a fun racing game with go-kart
|
|
// Copyright (C) 2006-2015 Joerg Henrichs
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 3
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
#include "items/attachment.hpp"
|
|
|
|
#include <algorithm>
|
|
#include "achievements/achievement_info.hpp"
|
|
#include "audio/sfx_base.hpp"
|
|
#include "config/player_manager.hpp"
|
|
#include "config/stk_config.hpp"
|
|
#include "config/user_config.hpp"
|
|
#include "graphics/explosion.hpp"
|
|
#include "graphics/irr_driver.hpp"
|
|
#include "items/attachment_manager.hpp"
|
|
#include "items/item_manager.hpp"
|
|
#include "items/projectile_manager.hpp"
|
|
#include "items/swatter.hpp"
|
|
#include "karts/abstract_kart.hpp"
|
|
#include "karts/controller/controller.hpp"
|
|
#include "karts/explosion_animation.hpp"
|
|
#include "karts/kart_properties.hpp"
|
|
#include "modes/three_strikes_battle.hpp"
|
|
#include "modes/world.hpp"
|
|
#include "network/rewind_manager.hpp"
|
|
#include "physics/triangle_mesh.hpp"
|
|
#include "tracks/track.hpp"
|
|
#include "physics/triangle_mesh.hpp"
|
|
#include "tracks/track.hpp"
|
|
#include "utils/constants.hpp"
|
|
|
|
/** Initialises the attachment each kart has.
|
|
*/
|
|
Attachment::Attachment(AbstractKart* kart)
|
|
: EventRewinder()
|
|
{
|
|
m_type = ATTACH_NOTHING;
|
|
m_ticks_left = 0;
|
|
m_plugin = NULL;
|
|
m_kart = kart;
|
|
m_previous_owner = NULL;
|
|
m_bomb_sound = NULL;
|
|
m_bubble_explode_sound = NULL;
|
|
m_node_scale = 1.0f;
|
|
m_initial_speed = 0.0f;
|
|
|
|
// If we attach a NULL mesh, we get a NULL scene node back. So we
|
|
// have to attach some kind of mesh, but make it invisible.
|
|
m_node = irr_driver->addAnimatedMesh(
|
|
attachment_manager->getMesh(Attachment::ATTACH_BOMB), "bomb");
|
|
#ifdef DEBUG
|
|
std::string debug_name = kart->getIdent()+" (attachment)";
|
|
m_node->setName(debug_name.c_str());
|
|
#endif
|
|
m_node->setAnimationEndCallback(this);
|
|
m_node->setParent(m_kart->getNode());
|
|
m_node->setVisible(false);
|
|
} // Attachment
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Removes the attachment object. It removes the scene node used to display
|
|
* the attachment, and stops any sfx from being played.
|
|
*/
|
|
Attachment::~Attachment()
|
|
{
|
|
if(m_node)
|
|
irr_driver->removeNode(m_node);
|
|
|
|
if (m_bomb_sound)
|
|
{
|
|
m_bomb_sound->deleteSFX();
|
|
m_bomb_sound = NULL;
|
|
}
|
|
|
|
if (m_bubble_explode_sound)
|
|
{
|
|
m_bubble_explode_sound->deleteSFX();
|
|
m_bubble_explode_sound = NULL;
|
|
}
|
|
} // ~Attachment
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Sets the attachment a kart has. This will also handle animation to be
|
|
* played, e.g. when a swatter replaces a bomb.
|
|
* \param type The type of the new attachment.
|
|
* \param time How long this attachment should stay with the kart.
|
|
* \param current_kart The kart from which an attachment is transferred.
|
|
* This is currently used for the bomb (to avoid that a bomb
|
|
* can be passed back to the previous owner). NULL if a no
|
|
* previous owner exists.
|
|
*/
|
|
void Attachment::set(AttachmentType type, int ticks,
|
|
AbstractKart *current_kart)
|
|
{
|
|
bool was_bomb = (m_type == ATTACH_BOMB);
|
|
scene::ISceneNode* bomb_scene_node = NULL;
|
|
if (was_bomb && type == ATTACH_SWATTER)
|
|
{
|
|
// let's keep the bomb node, and create a new one for
|
|
// the new attachment
|
|
bomb_scene_node = m_node;
|
|
|
|
m_node = irr_driver->addAnimatedMesh(
|
|
attachment_manager->getMesh(Attachment::ATTACH_BOMB), "bomb");
|
|
#ifdef DEBUG
|
|
std::string debug_name = m_kart->getIdent() + " (attachment)";
|
|
m_node->setName(debug_name.c_str());
|
|
#endif
|
|
m_node->setAnimationEndCallback(this);
|
|
m_node->setParent(m_kart->getNode());
|
|
m_node->setVisible(false);
|
|
}
|
|
|
|
clear();
|
|
m_node_scale = 0.3f;
|
|
|
|
// If necessary create the appropriate plugin which encapsulates
|
|
// the associated behavior
|
|
switch(type)
|
|
{
|
|
case ATTACH_SWATTER :
|
|
if (m_kart->getIdent() == "nolok")
|
|
m_node->setMesh(attachment_manager->getMesh(ATTACH_NOLOKS_SWATTER));
|
|
else
|
|
m_node->setMesh(attachment_manager->getMesh(type));
|
|
m_plugin = new Swatter(m_kart, was_bomb, bomb_scene_node);
|
|
break;
|
|
case ATTACH_BOMB:
|
|
m_node->setMesh(attachment_manager->getMesh(type));
|
|
m_node->setAnimationSpeed(0);
|
|
if (m_bomb_sound) m_bomb_sound->deleteSFX();
|
|
m_bomb_sound = SFXManager::get()->createSoundSource("clock");
|
|
m_bomb_sound->setLoop(true);
|
|
m_bomb_sound->setPosition(m_kart->getXYZ());
|
|
m_bomb_sound->play();
|
|
break;
|
|
default:
|
|
m_node->setMesh(attachment_manager->getMesh(type));
|
|
break;
|
|
} // switch(type)
|
|
|
|
if (UserConfigParams::m_particles_effects < 2)
|
|
{
|
|
m_node->setAnimationSpeed(0);
|
|
m_node->setCurrentFrame(0);
|
|
}
|
|
|
|
m_node->setScale(core::vector3df(m_node_scale,m_node_scale,m_node_scale));
|
|
|
|
m_type = type;
|
|
m_ticks_left = ticks;
|
|
m_previous_owner = current_kart;
|
|
m_node->setRotation(core::vector3df(0, 0, 0));
|
|
|
|
// A parachute can be attached as result of the usage of an item. In this
|
|
// case we have to save the current kart speed so that it can be detached
|
|
// by slowing down.
|
|
if(m_type==ATTACH_PARACHUTE)
|
|
{
|
|
const KartProperties *kp = m_kart->getKartProperties();
|
|
float speed_mult;
|
|
|
|
m_initial_speed = m_kart->getSpeed();
|
|
// if going very slowly or backwards, braking won't remove parachute
|
|
if(m_initial_speed <= 1.5) m_initial_speed = 1.5;
|
|
|
|
float f = m_initial_speed / kp->getParachuteMaxSpeed();
|
|
float temp_mult = kp->getParachuteDurationSpeedMult();
|
|
|
|
// duration can't be reduced by higher speed
|
|
if (temp_mult < 1.0f) temp_mult = 1.0f;
|
|
|
|
if (f > 1.0f) f = 1.0f; // cap fraction
|
|
|
|
speed_mult = 1.0f + (f * (temp_mult - 1.0f));
|
|
|
|
m_ticks_left = int(m_ticks_left * speed_mult);
|
|
|
|
if (UserConfigParams::m_particles_effects > 1)
|
|
{
|
|
// .blend was created @25 (<10 real, slow computer), make it faster
|
|
m_node->setAnimationSpeed(50);
|
|
}
|
|
}
|
|
m_node->setVisible(true);
|
|
#ifndef SERVER_ONLY
|
|
// Save event about the new attachment
|
|
RewindManager *rwm = RewindManager::get();
|
|
if(rwm->isEnabled() && !rwm->isRewinding())
|
|
{
|
|
BareNetworkString *buffer = new BareNetworkString(2);
|
|
saveState(buffer);
|
|
rwm->addEvent(this, buffer, /*confirmed*/true);
|
|
}
|
|
#endif
|
|
} // set
|
|
|
|
// -----------------------------------------------------------------------------
|
|
/** Removes any attachement currently on the kart. As for the anvil attachment,
|
|
* takes care of resetting the owner kart's physics structures to account for
|
|
* the updated mass.
|
|
*/
|
|
void Attachment::clear()
|
|
{
|
|
if(m_plugin)
|
|
{
|
|
delete m_plugin;
|
|
m_plugin = NULL;
|
|
}
|
|
|
|
if (m_bomb_sound)
|
|
{
|
|
m_bomb_sound->deleteSFX();
|
|
m_bomb_sound = NULL;
|
|
}
|
|
|
|
m_type=ATTACH_NOTHING;
|
|
|
|
m_ticks_left = 0;
|
|
m_node->setVisible(false);
|
|
m_node->setPosition(core::vector3df());
|
|
m_node->setRotation(core::vector3df());
|
|
|
|
// Resets the weight of the kart if the previous attachment affected it
|
|
// (e.g. anvil). This must be done *after* setting m_type to
|
|
// ATTACH_NOTHING in order to reset the physics parameters.
|
|
m_kart->updateWeight();
|
|
} // clear
|
|
|
|
// -----------------------------------------------------------------------------
|
|
/** Saves the attachment state. Called as part of the kart saving its state.
|
|
* \param buffer The kart rewinder's state buffer.
|
|
*/
|
|
void Attachment::saveState(BareNetworkString *buffer) const
|
|
{
|
|
// We use bit 7 to indicate if a previous owner is defined for a bomb
|
|
assert(ATTACH_MAX<=127);
|
|
uint8_t type = m_type | (( (m_type==ATTACH_BOMB) && (m_previous_owner!=NULL) )
|
|
? 0x80 : 0 );
|
|
buffer->addUInt8(type);
|
|
if(m_type!=ATTACH_NOTHING)
|
|
{
|
|
buffer->addUInt32(m_ticks_left);
|
|
if(m_type==ATTACH_BOMB && m_previous_owner)
|
|
buffer->addUInt8(m_previous_owner->getWorldKartId());
|
|
// m_initial_speed is not saved, on restore state it will
|
|
// be set to the kart speed, which has already been restored
|
|
}
|
|
} // saveState
|
|
|
|
// -----------------------------------------------------------------------------
|
|
/** Called from the kart rewinder when resetting to a certain state.
|
|
* \param buffer The kart rewinder's buffer with the attachment state next.
|
|
*/
|
|
void Attachment::rewindTo(BareNetworkString *buffer)
|
|
{
|
|
uint8_t type = buffer->getUInt8();
|
|
|
|
AttachmentType new_type = AttachmentType(type & 0x7f); // mask out bit 7
|
|
// FIXME Sometimes type == 255 is returned, reason unknown
|
|
if (new_type > ATTACH_NOTHING)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If there is no attachment, clear the attachment if necessary and exit
|
|
if(new_type==ATTACH_NOTHING)
|
|
{
|
|
if(m_type!=new_type) clear();
|
|
return;
|
|
}
|
|
|
|
int ticks_left = buffer->getUInt32();
|
|
|
|
// Attaching an object can be expensive (loading new models, ...)
|
|
// so avoid doing this if there is no change in attachment type
|
|
if(new_type == m_type)
|
|
{
|
|
setTicksLeft(ticks_left);
|
|
return;
|
|
}
|
|
|
|
// Now it is a new attachment:
|
|
|
|
if (type == (ATTACH_BOMB | 0x80)) // we have previous owner information
|
|
{
|
|
uint8_t kart_id = buffer->getUInt8();
|
|
m_previous_owner = World::getWorld()->getKart(kart_id);
|
|
}
|
|
else
|
|
{
|
|
m_previous_owner = NULL;
|
|
}
|
|
set(new_type, ticks_left, m_previous_owner);
|
|
} // rewindTo
|
|
// -----------------------------------------------------------------------------
|
|
/** Called when going forwards in time during a rewind.
|
|
* \param buffer Buffer with the rewind information.
|
|
*/
|
|
void Attachment::rewind(BareNetworkString *buffer)
|
|
{
|
|
// Event has same info as a state, so re-use the restore function
|
|
rewindTo(buffer);
|
|
} // rewind
|
|
|
|
// -----------------------------------------------------------------------------
|
|
/** Randomly selects the new attachment. For a server process, the
|
|
* attachment can be passed into this function.
|
|
* \param item The item that was collected.
|
|
* \param new_attachment Optional: only used on the clients, it
|
|
* specifies the new attachment to use
|
|
*/
|
|
void Attachment::hitBanana(Item *item, int new_attachment)
|
|
{
|
|
if(m_kart->getController()->canGetAchievements())
|
|
PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_BANANA,
|
|
"banana",1 );
|
|
//Bubble gum shield effect:
|
|
if(m_type == ATTACH_BUBBLEGUM_SHIELD ||
|
|
m_type == ATTACH_NOLOK_BUBBLEGUM_SHIELD)
|
|
{
|
|
m_ticks_left = 0;
|
|
return;
|
|
}
|
|
|
|
int leftover_ticks = 0;
|
|
|
|
bool add_a_new_item = true;
|
|
|
|
if (dynamic_cast<ThreeStrikesBattle*>(World::getWorld()) != NULL)
|
|
{
|
|
World::getWorld()->kartHit(m_kart->getWorldKartId());
|
|
ExplosionAnimation::create(m_kart);
|
|
return;
|
|
}
|
|
|
|
const KartProperties *kp = m_kart->getKartProperties();
|
|
switch(getType()) // If there already is an attachment, make it worse :)
|
|
{
|
|
case ATTACH_BOMB:
|
|
{
|
|
add_a_new_item = false;
|
|
HitEffect *he = new Explosion(m_kart->getXYZ(), "explosion", "explosion_bomb.xml");
|
|
if(m_kart->getController()->isLocalPlayerController())
|
|
he->setLocalPlayerKartHit();
|
|
projectile_manager->addHitEffect(he);
|
|
ExplosionAnimation::create(m_kart);
|
|
clear();
|
|
if(new_attachment==-1)
|
|
new_attachment = m_random.get(3);
|
|
// Disable the banana on which the kart just is for more than the
|
|
// default time. This is necessary to avoid that a kart lands on the
|
|
// same banana again once the explosion animation is finished, giving
|
|
// the kart the same penalty twice.
|
|
int ticks = std::max(item->getDisableTicks(),
|
|
stk_config->time2Ticks(kp->getExplosionDuration() + 2.0f));
|
|
item->setDisableTicks(ticks);
|
|
break;
|
|
}
|
|
case ATTACH_ANVIL:
|
|
// if the kart already has an anvil, attach a new anvil,
|
|
// and increase the overall time
|
|
new_attachment = 1;
|
|
leftover_ticks = m_ticks_left;
|
|
break;
|
|
case ATTACH_PARACHUTE:
|
|
new_attachment = 0;
|
|
leftover_ticks = m_ticks_left;
|
|
break;
|
|
default:
|
|
// There is no attachment currently, but there will be one
|
|
// so play the character sound ("Uh-Oh")
|
|
m_kart->playCustomSFX(SFXManager::CUSTOM_ATTACH);
|
|
|
|
if(new_attachment==-1)
|
|
{
|
|
if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL)
|
|
new_attachment = m_random.get(2);
|
|
else
|
|
new_attachment = m_random.get(3);
|
|
}
|
|
} // switch
|
|
|
|
if (add_a_new_item)
|
|
{
|
|
switch (new_attachment)
|
|
{
|
|
case 0:
|
|
set(ATTACH_PARACHUTE, kp->getParachuteDuration() + leftover_ticks);
|
|
m_initial_speed = m_kart->getSpeed();
|
|
|
|
// if going very slowly or backwards,
|
|
// braking won't remove parachute
|
|
if(m_initial_speed <= 1.5) m_initial_speed = 1.5;
|
|
break ;
|
|
case 1:
|
|
set(ATTACH_ANVIL, stk_config->time2Ticks(kp->getAnvilDuration())
|
|
+ leftover_ticks );
|
|
// if ( m_kart == m_kart[0] )
|
|
// sound -> playSfx ( SOUND_SHOOMF ) ;
|
|
// Reduce speed once (see description above), all other changes are
|
|
// handled in Kart::updatePhysics
|
|
m_kart->adjustSpeed(kp->getAnvilSpeedFactor());
|
|
m_kart->updateWeight();
|
|
break ;
|
|
case 2:
|
|
set( ATTACH_BOMB, stk_config->time2Ticks(stk_config->m_bomb_time)
|
|
+ leftover_ticks );
|
|
|
|
break ;
|
|
} // switch
|
|
}
|
|
} // hitBanana
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Updates the attachments in case of a kart-kart collision. This must only
|
|
* be called for one of the karts in the collision, since it will update
|
|
* the attachment for both karts.
|
|
* \param other Pointer to the other kart hit.
|
|
*/
|
|
void Attachment::handleCollisionWithKart(AbstractKart *other)
|
|
{
|
|
Attachment *attachment_other=other->getAttachment();
|
|
|
|
if(getType()==Attachment::ATTACH_BOMB)
|
|
{
|
|
// Don't attach a bomb when the kart is shielded
|
|
if(other->isShielded())
|
|
{
|
|
other->decreaseShieldTime();
|
|
return;
|
|
}
|
|
// If both karts have a bomb, explode them immediately:
|
|
if(attachment_other->getType()==Attachment::ATTACH_BOMB)
|
|
{
|
|
setTicksLeft(0);
|
|
attachment_other->setTicksLeft(0);
|
|
}
|
|
else // only this kart has a bomb, move it to the other
|
|
{
|
|
// if there are only two karts, let them switch bomb from one to other
|
|
if (getPreviousOwner() != other || World::getWorld()->getNumKarts() <= 2)
|
|
{
|
|
// Don't move if this bomb was from other kart originally
|
|
other->getAttachment()
|
|
->set(ATTACH_BOMB,
|
|
getTicksLeft()+stk_config->time2Ticks(
|
|
stk_config->m_bomb_time_increase),
|
|
m_kart);
|
|
other->playCustomSFX(SFXManager::CUSTOM_ATTACH);
|
|
clear();
|
|
}
|
|
}
|
|
} // type==BOMB
|
|
else if(attachment_other->getType()==Attachment::ATTACH_BOMB &&
|
|
(attachment_other->getPreviousOwner()!=m_kart || World::getWorld()->getNumKarts() <= 2))
|
|
{
|
|
// Don't attach a bomb when the kart is shielded
|
|
if(m_kart->isShielded())
|
|
{
|
|
m_kart->decreaseShieldTime();
|
|
return;
|
|
}
|
|
set(ATTACH_BOMB,
|
|
other->getAttachment()->getTicksLeft()+
|
|
stk_config->time2Ticks(stk_config->m_bomb_time_increase),
|
|
other);
|
|
other->getAttachment()->clear();
|
|
m_kart->playCustomSFX(SFXManager::CUSTOM_ATTACH);
|
|
}
|
|
else
|
|
{
|
|
m_kart->playCustomSFX(SFXManager::CUSTOM_CRASH);
|
|
other->playCustomSFX(SFXManager::CUSTOM_CRASH);
|
|
}
|
|
|
|
} // handleCollisionWithKart
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void Attachment::update(int ticks)
|
|
{
|
|
if(m_type==ATTACH_NOTHING) return;
|
|
|
|
// suspend the bomb during animations to avoid having 2 animations at the
|
|
// same time should the bomb explode before the previous animation is done
|
|
if (m_type == ATTACH_BOMB && m_kart->getKartAnimation() != NULL)
|
|
return;
|
|
|
|
m_ticks_left -= ticks;
|
|
|
|
|
|
bool is_shield = m_type == ATTACH_BUBBLEGUM_SHIELD ||
|
|
m_type == ATTACH_NOLOK_BUBBLEGUM_SHIELD;
|
|
float m_wanted_node_scale = is_shield
|
|
? std::max(1.0f, m_kart->getHighestPoint()*1.1f)
|
|
: 1.0f;
|
|
int slow_flashes = stk_config->time2Ticks(3.0f);
|
|
if (is_shield && m_ticks_left < slow_flashes)
|
|
{
|
|
int ticks_per_flash = stk_config->time2Ticks(0.2f);
|
|
|
|
int fast_flashes = stk_config->time2Ticks(0.5f);
|
|
if (m_ticks_left < fast_flashes)
|
|
{
|
|
ticks_per_flash = stk_config->time2Ticks(0.07f);
|
|
}
|
|
|
|
int division = (m_ticks_left / ticks_per_flash);
|
|
m_node->setVisible((division & 0x1) == 0);
|
|
}
|
|
|
|
float dt = stk_config->ticks2Time(ticks);
|
|
if (m_node_scale < m_wanted_node_scale)
|
|
{
|
|
m_node_scale += dt*1.5f;
|
|
if (m_node_scale > m_wanted_node_scale)
|
|
m_node_scale = m_wanted_node_scale;
|
|
m_node->setScale(core::vector3df(m_node_scale,m_node_scale,
|
|
m_node_scale) );
|
|
}
|
|
|
|
if(m_plugin)
|
|
{
|
|
bool discard = m_plugin->updateAndTestFinished(ticks);
|
|
if(discard)
|
|
{
|
|
clear(); // also removes the plugin
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (m_type)
|
|
{
|
|
case ATTACH_PARACHUTE:
|
|
{
|
|
// Partly handled in Kart::updatePhysics
|
|
// Otherwise: disable if a certain percantage of
|
|
// initial speed was lost
|
|
// This percentage is based on the ratio of
|
|
// initial_speed / initial_max_speed
|
|
|
|
const KartProperties *kp = m_kart->getKartProperties();
|
|
|
|
float f = m_initial_speed / kp->getParachuteMaxSpeed();
|
|
if (f > 1.0f) f = 1.0f; // cap fraction
|
|
if (m_kart->getSpeed() <= m_initial_speed *
|
|
(kp->getParachuteLboundFraction() +
|
|
f * (kp->getParachuteUboundFraction()
|
|
- kp->getParachuteLboundFraction())))
|
|
{
|
|
m_ticks_left = -1;
|
|
}
|
|
}
|
|
break;
|
|
case ATTACH_ANVIL: // handled in Kart::updatePhysics
|
|
case ATTACH_NOTHING: // Nothing to do, but complete all cases for switch
|
|
case ATTACH_MAX:
|
|
break;
|
|
case ATTACH_SWATTER:
|
|
// Everything is done in the plugin.
|
|
break;
|
|
case ATTACH_NOLOKS_SWATTER:
|
|
case ATTACH_SWATTER_ANIM:
|
|
// Should never be called, these symbols are only used as an index for
|
|
// the model, Nolok's attachment type is ATTACH_SWATTER
|
|
assert(false);
|
|
break;
|
|
case ATTACH_BOMB:
|
|
{
|
|
if (m_bomb_sound) m_bomb_sound->setPosition(m_kart->getXYZ());
|
|
|
|
// Mesh animation frames are 1 to 61 frames (60 steps)
|
|
// The idea is change second by second, counterclockwise 60 to 0 secs
|
|
// If longer times needed, it should be a surprise "oh! bomb activated!"
|
|
float time_left = stk_config->ticks2Time(m_ticks_left);
|
|
if (time_left <= (m_node->getEndFrame() - m_node->getStartFrame() - 1))
|
|
{
|
|
m_node->setCurrentFrame(m_node->getEndFrame()
|
|
- m_node->getStartFrame() - 1 - time_left);
|
|
}
|
|
if (m_ticks_left <= 0)
|
|
{
|
|
HitEffect *he = new Explosion(m_kart->getXYZ(), "explosion",
|
|
"explosion_bomb.xml");
|
|
if (m_kart->getController()->isLocalPlayerController())
|
|
he->setLocalPlayerKartHit();
|
|
projectile_manager->addHitEffect(he);
|
|
ExplosionAnimation::create(m_kart);
|
|
|
|
if (m_bomb_sound)
|
|
{
|
|
m_bomb_sound->deleteSFX();
|
|
m_bomb_sound = NULL;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ATTACH_BUBBLEGUM_SHIELD:
|
|
case ATTACH_NOLOK_BUBBLEGUM_SHIELD:
|
|
if (m_ticks_left < 0)
|
|
{
|
|
m_ticks_left = 0;
|
|
if (m_bubble_explode_sound) m_bubble_explode_sound->deleteSFX();
|
|
m_bubble_explode_sound =
|
|
SFXManager::get()->createSoundSource("bubblegum_explode");
|
|
m_bubble_explode_sound->setPosition(m_kart->getXYZ());
|
|
m_bubble_explode_sound->play();
|
|
|
|
// drop a small bubble gum
|
|
Vec3 hit_point;
|
|
Vec3 normal;
|
|
const Material* material_hit;
|
|
|
|
Track::getCurrentTrack()->getTriangleMesh().castRay(m_kart->getXYZ(),
|
|
m_kart->getTrans().getBasis() * Vec3(0, -10000, 0),
|
|
&hit_point,&material_hit, &normal );
|
|
// This can happen if the kart is 'over nothing' when dropping
|
|
// the bubble gum
|
|
if(material_hit)
|
|
{
|
|
normal.normalize();
|
|
|
|
Vec3 pos = hit_point + m_kart->getTrans().getBasis()
|
|
* Vec3(0, -0.05f, 0);
|
|
ItemManager::get()->newItem(Item::ITEM_BUBBLEGUM, pos,
|
|
normal, m_kart );
|
|
}
|
|
}
|
|
break;
|
|
} // switch
|
|
|
|
// Detach attachment if its time is up.
|
|
if ( m_ticks_left <= 0)
|
|
clear();
|
|
} // update
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Return the additional weight of the attachment (some attachments slow
|
|
* karts down by also making them heavier).
|
|
*/
|
|
float Attachment::weightAdjust() const
|
|
{
|
|
return m_type == ATTACH_ANVIL
|
|
? m_kart->getKartProperties()->getAnvilWeight()
|
|
: 0.0f;
|
|
} // weightAdjust
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Inform any eventual plugin when an animation is done. */
|
|
void Attachment::OnAnimationEnd(scene::IAnimatedMeshSceneNode* node)
|
|
{
|
|
if(m_plugin)
|
|
m_plugin->onAnimationEnd();
|
|
} // OnAnimationEnd
|