diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index e0b122844..e18b37f18 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -67,13 +67,14 @@ Flyable::Flyable(AbstractKart *kart, PowerupManager::PowerupType type, m_type = type; m_has_hit_something = false; m_shape = NULL; + m_animation = NULL; m_mass = mass; m_adjust_up_velocity = true; m_time_since_thrown = 0; m_position_offset = Vec3(0,0,0); m_owner_has_temporary_immunity = true; m_do_terrain_info = true; - m_max_lifespan = -1; + m_max_lifespan = -1; // Add the graphical model #ifndef SERVER_ONLY @@ -353,6 +354,24 @@ void Flyable::getLinearKartItemIntersection (const Vec3 &origin, + ( target_y_speed); } // getLinearKartItemIntersection +//----------------------------------------------------------------------------- + +void Flyable::setAnimation(AbstractKartAnimation *animation) +{ + if (animation) + { + assert(m_animation == NULL); + Physics::getInstance()->removeBody(m_body); + } + else // animation = NULL + { + assert(m_animation != NULL); + m_body->setWorldTransform(getTrans()); + Physics::getInstance()->addBody(m_body); + } + m_animation = animation; +} // addAnimation + //----------------------------------------------------------------------------- /** Updates this flyable. It calls Moveable::update. If this function returns * true, the flyable will be deleted by the projectile manager. @@ -361,6 +380,13 @@ void Flyable::getLinearKartItemIntersection (const Vec3 &origin, */ bool Flyable::updateAndDelete(float dt) { + if (hasAnimation()) + { + m_animation->update(dt); + Moveable::update(dt); + return false; + } // if animation + m_time_since_thrown += dt; if(m_max_lifespan > -1 && m_time_since_thrown > m_max_lifespan) hit(NULL); diff --git a/src/items/flyable.hpp b/src/items/flyable.hpp index 8daa0fdb5..3b46b7ae6 100644 --- a/src/items/flyable.hpp +++ b/src/items/flyable.hpp @@ -34,6 +34,7 @@ using namespace irr; #include "tracks/terrain_info.hpp" class AbstractKart; +class AbstractKartAnimation; class HitEffect; class PhysicalObject; class XMLNode; @@ -64,6 +65,11 @@ private: * terrain yourself (e.g. order of operations is important) * set this to false with a call do setDoTerrainInfo(). */ bool m_do_terrain_info; + + /** If the flyable is in a cannon, this is the pointer to the cannon + * animation. NULL otherwise. */ + AbstractKartAnimation *m_animation; + protected: /** Kart which shot this flyable. */ AbstractKart* m_owner; @@ -162,11 +168,16 @@ public: static void init (const XMLNode &node, scene::IMesh *model, PowerupManager::PowerupType type); virtual bool updateAndDelete(float); + virtual void setAnimation(AbstractKartAnimation *animation); virtual HitEffect* getHitEffect() const; bool isOwnerImmunity(const AbstractKart *kart_hit) const; virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); void explode(AbstractKart* kart, PhysicalObject* obj=NULL, bool secondary_hits=true); + unsigned int getOwnerId(); + // ------------------------------------------------------------------------ + /** Returns if this flyable has an animation playing (e.g. cannon). */ + bool hasAnimation() const { return m_animation != NULL; } // ------------------------------------------------------------------------ /** If true the up velocity of the flyable will be adjust so that the * flyable stays at a height close to the average height. @@ -194,15 +205,16 @@ public: void reset () { Moveable::reset(); } // ------------------------------------------------------------------------ /** Returns the type of flyable. */ - PowerupManager::PowerupType - getType() const {return m_type;} + PowerupManager::PowerupType getType() const {return m_type;} // ------------------------------------------------------------------------ /** Sets wether Flyable should update TerrainInfo as part of its update * call, or if the inheriting object will update TerrainInfo itself * (or perhaps not at all if it is not needed). */ void setDoTerrainInfo(bool d) { m_do_terrain_info = d; } // ------------------------------------------------------------------------ - unsigned int getOwnerId(); + /** Returns the size (extend) of the mesh. */ + const Vec3 &getExtend() const { return m_extend; } + // ------------------------------------------------------------------------ }; // Flyable #endif diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index 2f69e3c43..2b010ac4d 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -29,6 +29,7 @@ #include "modes/linear_world.hpp" #include "physics/btKart.hpp" #include "physics/triangle_mesh.hpp" +#include "tracks/check_manager.hpp" #include "tracks/drive_graph.hpp" #include "tracks/drive_node.hpp" #include "tracks/track.hpp" @@ -103,7 +104,7 @@ RubberBall::RubberBall(AbstractKart *kart) DriveGraph::get()->getNode(getCurrentGraphNode())->getNormal(); TerrainInfo::update(getXYZ(), -normal); initializeControlPoints(m_owner->getXYZ()); - + CheckManager::get()->addFlyableToCannons(this); } // RubberBall // ---------------------------------------------------------------------------- @@ -114,6 +115,7 @@ RubberBall::~RubberBall() if(m_ping_sfx->getStatus()==SFXBase::SFX_PLAYING) m_ping_sfx->stop(); m_ping_sfx->deleteSFX(); + CheckManager::get()->removeFlyableFromCannons(this); } // ~RubberBall // ---------------------------------------------------------------------------- @@ -145,6 +147,14 @@ void RubberBall::initializeControlPoints(const Vec3 &xyz) m_t_increase = m_speed/m_length_cp_1_2; } // initializeControlPoints +// ---------------------------------------------------------------------------- +void RubberBall::setAnimation(AbstractKartAnimation *animation) +{ + if (!animation) + initializeControlPoints(getXYZ()); + Flyable::setAnimation(animation); +} // setAnimation + // ---------------------------------------------------------------------------- /** Determines the first kart that is still in the race. */ @@ -310,6 +320,7 @@ bool RubberBall::updateAndDelete(float dt) // FIXME: what does the rubber ball do in case of battle mode?? if(!world) return true; + if(m_delete_timer>0) { m_delete_timer -= dt; @@ -323,6 +334,14 @@ bool RubberBall::updateAndDelete(float dt) } } + if (hasAnimation()) + { + // Flyable will call update() of the animation to + // update the ball's position. + m_previous_xyz = getXYZ(); + return Flyable::updateAndDelete(dt); + } + // Update the target in case that the first kart was overtaken (or has // finished the race). computeTarget(); diff --git a/src/items/rubber_ball.hpp b/src/items/rubber_ball.hpp index 2d354136b..5575786e7 100644 --- a/src/items/rubber_ball.hpp +++ b/src/items/rubber_ball.hpp @@ -204,6 +204,7 @@ public: static void init(const XMLNode &node, scene::IMesh *rubberball); virtual bool updateAndDelete(float dt); virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); + virtual void setAnimation(AbstractKartAnimation *animation); static float getTimeBetweenRubberBalls() {return m_time_between_balls;} // ------------------------------------------------------------------------ /** This object does not create an explosion, all affects on diff --git a/src/karts/abstract_kart_animation.cpp b/src/karts/abstract_kart_animation.cpp index 54cc9b09c..f78fdeeba 100644 --- a/src/karts/abstract_kart_animation.cpp +++ b/src/karts/abstract_kart_animation.cpp @@ -24,6 +24,11 @@ #include "karts/skidding.hpp" #include "physics/physics.hpp" +/** Constructor. Note that kart can be NULL in case that the animation is + * used for a basket ball in a cannon animation. + * \param kart Pointer to the kart that is animated, or NULL if the + * the animation is meant for a basket ball etc. + */ AbstractKartAnimation::AbstractKartAnimation(AbstractKart *kart, const std::string &name) { @@ -37,7 +42,7 @@ AbstractKartAnimation::AbstractKartAnimation(AbstractKart *kart, // up animations) if this should happen. In debug mode this condition // is caught by setKartAnimation(), and useful error messages are // printed - if (kart->getKartAnimation()) + if (kart && kart->getKartAnimation()) { AbstractKartAnimation* ka = kart->getKartAnimation(); kart->setKartAnimation(NULL); @@ -46,21 +51,23 @@ AbstractKartAnimation::AbstractKartAnimation(AbstractKart *kart, #endif // Register this animation with the kart (which will free it // later). - kart->setKartAnimation(this); - Physics::getInstance()->removeKart(m_kart); - kart->getSkidding()->reset(); - kart->getSlipstream()->reset(); - if(kart->isSquashed()) + if (kart) { - // A time of 0 reset the squashing - kart->setSquash(0.0f, 0.0f); + kart->setKartAnimation(this); + Physics::getInstance()->removeKart(m_kart); + kart->getSkidding()->reset(); + kart->getSlipstream()->reset(); + if (kart->isSquashed()) + { + // A time of 0 reset the squashing + kart->setSquash(0.0f, 0.0f); + } + + // Reset the wheels (and any other animation played for that kart) + // This avoid the effect that some wheels might be way below the kart + // which is very obvious in the rescue animation. + m_kart->getKartModel()->resetVisualWheelPosition(); } - - // Reset the wheels (and any other animation played for that kart) - // This avoid the effect that some wheels might be way below the kart - // which is very obvious in the rescue animation. - m_kart->getKartModel()->resetVisualWheelPosition(); - } // AbstractKartAnimation // ---------------------------------------------------------------------------- @@ -70,7 +77,7 @@ AbstractKartAnimation::~AbstractKartAnimation() // is deleted (at the end of a race), which means that // world is in the process of being deleted. In this case // we can't call getPhysics() anymore. - if(m_timer < 0) + if(m_timer < 0 && m_kart) { m_kart->getBody()->setAngularVelocity(btVector3(0,0,0)); Physics::getInstance()->addKart(m_kart); @@ -91,7 +98,7 @@ void AbstractKartAnimation::update(float dt) m_timer -= dt; if(m_timer<0) { - m_kart->setKartAnimation(NULL); + if(m_kart) m_kart->setKartAnimation(NULL); delete this; } } // update diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index cd3caced9..60ddbbb94 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -21,6 +21,7 @@ #include "animations/animation_base.hpp" #include "animations/ipo.hpp" #include "animations/three_d_animation.hpp" +#include "items/flyable.hpp" #include "karts/abstract_kart.hpp" #include "karts/kart_properties.hpp" #include "modes/world.hpp" @@ -28,7 +29,8 @@ #include "LinearMath/btTransform.h" /** The constructor for the cannon animation. - * \param kart The kart to be animated. + * \param kart The kart to be animated. Can also be NULL if a basket ball + * etc is animated (e.g. cannon animation). * \param ipo The IPO (blender interpolation curve) which the kart * should follow. * \param start_left, start_right: Left and right end points of the line @@ -45,9 +47,41 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, float skid_rot) : AbstractKartAnimation(kart, "CannonAnimation") { - m_curve = new AnimationBase(ipo); - m_timer = ipo->getEndTime(); - + m_flyable = NULL; + init(ipo, start_left, start_right, end_left, end_right, skid_rot); +} // CannonAnimation + +// ---------------------------------------------------------------------------- +/** Constructor for a flyable. It sets the kart data to NULL. + */ +CannonAnimation::CannonAnimation(Flyable *flyable, Ipo *ipo, + const Vec3 &start_left, const Vec3 &start_right, + const Vec3 &end_left, const Vec3 &end_right ) + : AbstractKartAnimation(NULL, "CannonAnimation") +{ + m_flyable = flyable; + init(ipo, start_left, start_right, end_left, end_right, /*skid_rot*/0); +} // CannonAnimation(Flyable*...) + +// ---------------------------------------------------------------------------- +/** Common initialisation for kart-based and flyable-based animations. + * \param ipo The IPO (blender interpolation curve) which the kart + * should follow. + * \param start_left, start_right: Left and right end points of the line + * that the kart just crossed. + * \param end_left, end_right: Left and right end points of the line at + * which the kart finishes. + * \param skid_rot Visual rotation of the kart due to skidding (while this + * value can be queried, the AbstractkartAnimation constructor + * resets the value to 0, so it needs to be passed in. + */ +void CannonAnimation::init(Ipo *ipo, const Vec3 &start_left, + const Vec3 &start_right, const Vec3 &end_left, + const Vec3 &end_right, float skid_rot) +{ + m_curve = new AnimationBase(ipo); + m_timer = ipo->getEndTime(); + // First make sure that left and right points are indeed correct // ------------------------------------------------------------- Vec3 my_start_left = start_left; @@ -57,12 +91,17 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, // (the curve's origin must be in the middle of the line. m_curve->getAt(0, &p0); m_curve->getAt(0.1f, &p1); - Vec3 p2 = 0.5f*(p0 + p1) + m_kart->getNormal(); + Vec3 p2; + if (m_kart) + p2 = 0.5f*(p0 + p1) + m_kart->getNormal(); + else + p2 = 0.5f*(p0 + p1) + m_flyable->getNormal(); + if (start_left.sideofPlane(p0, p1, p2) < 0) { // Left and right start line needs to be swapped my_start_left = start_right; - my_start_right = start_left; + my_start_right = start_left; } // First adjust start and end points to take on each side half the kart @@ -70,8 +109,9 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, Vec3 direction = my_start_right - my_start_left; direction.normalize(); - float kw = m_kart->getKartModel()->getWidth(); - Vec3 adj_start_left = my_start_left + (0.5f*kw) * direction; + float kw = m_kart ? m_kart->getKartModel()->getWidth() + : m_flyable->getExtend().getX(); + Vec3 adj_start_left = my_start_left + (0.5f*kw) * direction; Vec3 adj_start_right = my_start_right - (0.5f*kw) * direction; // Store the length of the start and end line, which is used @@ -99,16 +139,17 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, // This delta is rotated with the kart and added to the interpolated curve // position to get the actual kart position during the animation. Vec3 curve_xyz; + Vec3 xyz = m_kart ? m_kart->getXYZ() : m_flyable->getXYZ(); m_curve->update(0, &curve_xyz); - m_delta = kart->getXYZ() - curve_xyz; - + m_delta = xyz - curve_xyz; + // Compute on which fraction of the start line the kart is, to get the // second component of the kart position: distance along start line Vec3 v = adj_start_left - adj_start_right; float l = v.length(); v /= l; - float f = v.dot(adj_start_left - kart->getXYZ()); + float f = v.dot(adj_start_left - xyz); if (f <= 0) f = 0; else if (f >= l) @@ -138,28 +179,36 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, Vec3 tangent; m_curve->getDerivativeAt(0, &tangent); // Get the current kart orientation - Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); + const btTransform &trans = m_kart ? m_kart->getTrans() + : m_flyable->getBody()->getWorldTransform(); + Vec3 forward = trans.getBasis().getColumn(2); Vec3 v1(tangent), v2(forward); v1.setY(0); v2.setY(0); - m_delta_heading = shortestArcQuatNormalize2(v1, v2) - * btQuaternion(Vec3(0,1,0), skid_rot); + m_delta_heading = shortestArcQuatNormalize2(v1, v2) + * btQuaternion(Vec3(0, 1, 0), skid_rot); // The previous call to m_curve->update will set the internal timer // of the curve to dt. Reset it to 0 to make sure the timer is in // synch with the timer of the CanonAnimation m_curve->reset(); -} // CannonAnimation +} // init // ---------------------------------------------------------------------------- CannonAnimation::~CannonAnimation() { delete m_curve; - - btTransform pos = m_kart->getTrans(); - m_kart->getBody()->setCenterOfMassTransform(pos); - Vec3 v(0, 0, m_kart->getKartProperties()->getEngineMaxSpeed()); - m_kart->setVelocity(pos.getBasis()*v); + if (m_kart) + { + btTransform pos = m_kart->getTrans(); + m_kart->getBody()->setCenterOfMassTransform(pos); + Vec3 v(0, 0, m_kart->getKartProperties()->getEngineMaxSpeed()); + m_kart->setVelocity(pos.getBasis()*v); + } + else + { + m_flyable->setAnimation(NULL); + } } // ~CannonAnimation // ---------------------------------------------------------------------------- @@ -184,7 +233,9 @@ void CannonAnimation::update(float dt) m_curve->getDerivativeAt(m_curve->getAnimationDuration() - m_timer, &tangent); // Get the current kart orientation - Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); + const btTransform &trans = m_kart ? m_kart->getTrans() + : m_flyable->getBody()->getWorldTransform(); + Vec3 forward = trans.getBasis().getColumn(2); // Heading // ------- @@ -200,10 +251,14 @@ void CannonAnimation::update(float dt) // ------------------ // While start and end line have to have the same 'up' vector, karts can // sometimes be not parallel to them. So slowly adjust this over time - Vec3 up = m_kart->getTrans().getBasis().getColumn(1); + Vec3 up = trans.getBasis().getColumn(1); up.normalize(); - Vec3 gravity = -m_kart->getBody()->getGravity(); - gravity.normalize(); + Vec3 gravity = m_kart ? -m_kart->getBody()->getGravity() + : -m_flyable->getBody()->getGravity(); + if (gravity.length2() > 0) + gravity.normalize(); + else + gravity.setValue(0, -1, 0); // Adjust only 5% towards the real up vector. This will smoothly // adjust the kart while the kart is in the air Vec3 target_up_vector = (gravity*0.05f + up*0.95f).normalize(); @@ -220,27 +275,25 @@ void CannonAnimation::update(float dt) // The timer counts backwards, so the fraction goes from 1 to 0 float f = m_timer / m_curve->getAnimationDuration(); - - btQuaternion zero(gravity, 0); - btQuaternion current_delta_heading = zero.slerp(m_delta_heading, f); - - btQuaternion all_heading = m_kart->getRotation()*current_delta_heading*heading; - - m_kart->setRotation(q_up * all_heading); - - - // Then compute the new location of the kart - // ----------------------------------------- float f_current_width = m_start_line_length * f + m_end_line_length * (1.0f - f); - // Adjust the horizontal location based on steering - m_fraction_of_line += m_kart->getSteerPercent()*dt*2.0f; - btClamp(m_fraction_of_line, -1.0f, 1.0f); + // Then compute the new location of the kart + // ----------------------------------------- + btQuaternion all_heading; + if (m_kart) + { + btQuaternion zero(gravity, 0); + btQuaternion current_delta_heading = zero.slerp(m_delta_heading, f); + all_heading = m_kart->getRotation()*current_delta_heading*heading; + m_kart->setRotation(q_up * all_heading); - // horiz_delta is in kart coordinates, the rotation by q will - // transform it to the global coordinate system - Vec3 horiz_delta = Vec3(0.5f*m_fraction_of_line * f_current_width, 0, 0); + // Adjust the horizontal location based on steering + m_fraction_of_line += m_kart->getSteerPercent()*dt*2.0f; + btClamp(m_fraction_of_line, -1.0f, 1.0f); + } // if m_kart + else + all_heading.setValue(0, 0, 0, 1); // Determine direction orthogonal to the curve for the sideway movement // of the kart. @@ -251,5 +304,8 @@ void CannonAnimation::update(float dt) Vec3 curve_xyz; m_curve->update(dt, &curve_xyz); - m_kart->setXYZ(curve_xyz+rotated_delta); + if (m_kart) + m_kart->setXYZ(curve_xyz + rotated_delta); + else + m_flyable->setXYZ(curve_xyz + rotated_delta); } // update diff --git a/src/karts/cannon_animation.hpp b/src/karts/cannon_animation.hpp index f36098d32..37853da81 100644 --- a/src/karts/cannon_animation.hpp +++ b/src/karts/cannon_animation.hpp @@ -26,6 +26,7 @@ class AbstractKart; class AnimationBase; +class Flyable; class Ipo; @@ -46,6 +47,10 @@ protected: /** Stores the curve interpolation for the cannon. */ AnimationBase *m_curve; + /** If this animation is used for a flyable (e.g. basket ball) instead + * of a kart, m_flyable is defined and m_kart is NULL. */ + Flyable *m_flyable; + /** Length of the (adjusted, i.e. taking kart width into account) * start line. */ float m_start_line_length; @@ -61,12 +66,19 @@ protected: /** The initial heading of the kart when crossing the line. This is * used to smoothly orient the kart towards the normal of the cuve. */ btQuaternion m_delta_heading; + + void init(Ipo *ipo, const Vec3 &start_left, const Vec3 &start_right, + const Vec3 &end_left, const Vec3 &end_right, float skid_rot); + public: CannonAnimation(AbstractKart *kart, Ipo *ipo, const Vec3 &start_left, const Vec3 &start_right, const Vec3 &end_left, const Vec3 &end_right, float skid_rot); - virtual ~CannonAnimation(); + CannonAnimation(Flyable *flyable, Ipo *ipo, + const Vec3 &start_left, const Vec3 &start_right, + const Vec3 &end_left, const Vec3 &end_right); + virtual ~CannonAnimation(); virtual void update(float dt); }; // CannonAnimation diff --git a/src/tracks/check_cannon.cpp b/src/tracks/check_cannon.cpp index 9daa715b9..1eb76c8dc 100644 --- a/src/tracks/check_cannon.cpp +++ b/src/tracks/check_cannon.cpp @@ -25,6 +25,7 @@ #include "graphics/show_curve.hpp" #include "graphics/stk_tex_manager.hpp" #include "io/xml_node.hpp" +#include "items/flyable.hpp" #include "karts/abstract_kart.hpp" #include "karts/cannon_animation.hpp" #include "karts/skidding.hpp" @@ -88,9 +89,12 @@ CheckCannon::CheckCannon(const XMLNode &node, unsigned int index) : video::SColor(128, 128, 128, 128); } buffer->recalculateBoundingBox(); - buffer->getMaterial().setTexture(0, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(128, 255, 105, 180))); - buffer->getMaterial().setTexture(1, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(0, 0, 0, 0))); - buffer->getMaterial().setTexture(2, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(0, 0, 0, 0))); + buffer->getMaterial().setTexture(0, STKTexManager::getInstance() + ->getUnicolorTexture(video::SColor(128, 255, 105, 180))); + buffer->getMaterial().setTexture(1, STKTexManager::getInstance() + ->getUnicolorTexture(video::SColor(0, 0, 0, 0))); + buffer->getMaterial().setTexture(2, STKTexManager::getInstance() + ->getUnicolorTexture(video::SColor(0, 0, 0, 0))); buffer->getMaterial().BackfaceCulling = false; //mesh->setBoundingBox(buffer->getBoundingBox()); m_debug_target_node = irr_driver->addMesh(mesh, "checkdebug"); @@ -114,6 +118,8 @@ CheckCannon::~CheckCannon() } // ~CheckCannon // ---------------------------------------------------------------------------- +/** Changes the colour of a check cannon depending on state. + */ void CheckCannon::changeDebugColor(bool is_active) { #if defined(DEBUG) && !defined(SERVER_ONLY) @@ -133,6 +139,55 @@ void CheckCannon::changeDebugColor(bool is_active) #endif } // changeDebugColor +// ---------------------------------------------------------------------------- +/** Adds a flyable to be tested for crossing a cannon checkline. + * \param flyable The flyable to be tested. + */ +void CheckCannon::addFlyable(Flyable *flyable) +{ + m_all_flyables.push_back(flyable); + m_flyable_previous_position.push_back(flyable->getXYZ()); +} // addFlyable + +// ---------------------------------------------------------------------------- +/** Removes a flyable from the tests if it crosses a checkline. Used when + * the flyable is removed (e.g. explodes). + */ +void CheckCannon::removeFlyable(Flyable *flyable) +{ + std::vector::iterator i = std::find(m_all_flyables.begin(), + m_all_flyables.end(), + flyable); + assert(i != m_all_flyables.end()); + int index = i - m_all_flyables.begin(); // get the index + m_all_flyables.erase(i); + m_flyable_previous_position.erase(m_flyable_previous_position.begin() + index); +} // removeFlyable + +// ---------------------------------------------------------------------------- +/** Overriden to also check all flyables registered with the cannon. + */ +void CheckCannon::update(float dt) +{ + CheckLine::update(dt); + for (unsigned int i = 0; i < m_all_flyables.size(); i++) + { + setIgnoreHeight(true); + bool triggered = isTriggered(m_flyable_previous_position[i], + m_all_flyables[i]->getXYZ(), + /*kart index - ignore*/ -1 ); + setIgnoreHeight(false); + m_flyable_previous_position[i] = m_all_flyables[i]->getXYZ(); + if(!triggered) continue; + + // Cross the checkline - add the cannon animation + CannonAnimation *animation = + new CannonAnimation(m_all_flyables[i], m_curve->clone(), + getLeftPoint(), getRightPoint(), + m_target_left, m_target_right); + m_all_flyables[i]->setAnimation(animation); + } // for i in all flyables +} // update // ---------------------------------------------------------------------------- /** Called when the check line is triggered. This function creates a cannon * animation object and attaches it to the kart. diff --git a/src/tracks/check_cannon.hpp b/src/tracks/check_cannon.hpp index 0708d944a..dec83aafc 100644 --- a/src/tracks/check_cannon.hpp +++ b/src/tracks/check_cannon.hpp @@ -21,8 +21,10 @@ #include "animations/animation_base.hpp" #include "tracks/check_line.hpp" +#include "utils/cpp2011.hpp" class CheckManager; +class Flyable; class Ipo; class ShowCurve; class XMLNode; @@ -51,12 +53,18 @@ private: /** Used to display debug information about checklines. */ scene::IMeshSceneNode *m_debug_target_node; #endif + std::vector m_all_flyables; + std::vector m_flyable_previous_position; public: CheckCannon(const XMLNode &node, unsigned int index); virtual ~CheckCannon(); - virtual void trigger(unsigned int kart_index); - virtual void changeDebugColor(bool is_active); + virtual void trigger(unsigned int kart_index) OVERRIDE; + virtual void changeDebugColor(bool is_active) OVERRIDE; + virtual void update(float dt) OVERRIDE; + + void addFlyable(Flyable *flyable); + void removeFlyable(Flyable *flyable); }; // CheckLine #endif diff --git a/src/tracks/check_cylinder.cpp b/src/tracks/check_cylinder.cpp index 669d271ae..c3ad9f35a 100644 --- a/src/tracks/check_cylinder.cpp +++ b/src/tracks/check_cylinder.cpp @@ -46,16 +46,16 @@ CheckCylinder::CheckCylinder(const XMLNode &node, unsigned int index, TriggerIte } // CheckCylinder // ---------------------------------------------------------------------------- -/** True if going from old_pos to new_pos enters or leaves this sphere. This +/** True if going from old_pos to new_pos enters or leaves this cylinder. This * function is called from update (of the checkline structure). It also * updates the flag about which karts are inside * \param old_pos Position in previous frame. * \param new_pos Position in current frame. - * \param kart_id Index of the kart, can be used to store kart specific + * \param kart_id Index of the kart, can be used to store kart specific * additional data. */ bool CheckCylinder::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_id) + int kart_id) { // TODO: this is the code for a sphere, rewrite for cylinder Vec3 old_pos_xz(old_pos.x(), 0.0f, old_pos.z()); diff --git a/src/tracks/check_cylinder.hpp b/src/tracks/check_cylinder.hpp index 4af674561..6c6f9802a 100644 --- a/src/tracks/check_cylinder.hpp +++ b/src/tracks/check_cylinder.hpp @@ -52,7 +52,7 @@ public: TriggerItemListener* listener); virtual ~CheckCylinder() {}; virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_id); + int kart_id); // ------------------------------------------------------------------------ /** Returns if kart indx is currently inside of the sphere. */ bool isInside(int index) const { return m_is_inside[index]; } diff --git a/src/tracks/check_goal.cpp b/src/tracks/check_goal.cpp index 496048f41..538f18916 100644 --- a/src/tracks/check_goal.cpp +++ b/src/tracks/check_goal.cpp @@ -55,7 +55,8 @@ void CheckGoal::update(float dt) if (world) { - if (isTriggered(m_previous_ball_position, world->getBallPosition(), -1)) + if (isTriggered(m_previous_ball_position, world->getBallPosition(), + /*kart index - ignore*/-1) ) { if (UserConfigParams::m_check_debug) { @@ -86,7 +87,7 @@ void CheckGoal::trigger(unsigned int i) // ---------------------------------------------------------------------------- bool CheckGoal::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kartIndex) + int kart_index) { core::vector2df cross_point; diff --git a/src/tracks/check_goal.hpp b/src/tracks/check_goal.hpp index d1c36cb7c..a310ca60c 100644 --- a/src/tracks/check_goal.hpp +++ b/src/tracks/check_goal.hpp @@ -65,7 +65,7 @@ public: virtual void update(float dt) OVERRIDE; virtual void trigger(unsigned int kart_index) OVERRIDE; virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int indx) OVERRIDE; + int indx) OVERRIDE; virtual void reset(const Track &track) OVERRIDE; // ------------------------------------------------------------------------ diff --git a/src/tracks/check_lap.cpp b/src/tracks/check_lap.cpp index ab2d4bb1c..ee0f8ce8a 100644 --- a/src/tracks/check_lap.cpp +++ b/src/tracks/check_lap.cpp @@ -53,13 +53,13 @@ void CheckLap::reset(const Track &track) // ---------------------------------------------------------------------------- /** True if going from old_pos to new_pos crosses this checkline. This function * is called from update (of the checkline structure). - * \param old_pos Position in previous frame. - * \param new_pos Position in current frame. + * \param old_pos Position in previous frame. + * \param new_pos Position in current frame. * \param kart_index Index of the kart, can be used to store kart specific - * additional data. + * additional data. */ bool CheckLap::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_index) + int kart_index) { World* w = World::getWorld(); LinearWorld* lin_world = dynamic_cast(w); diff --git a/src/tracks/check_lap.hpp b/src/tracks/check_lap.hpp index b6d19a78a..d8b6f82f3 100644 --- a/src/tracks/check_lap.hpp +++ b/src/tracks/check_lap.hpp @@ -40,7 +40,7 @@ public: CheckLap(const XMLNode &node, unsigned int index); virtual ~CheckLap() {}; virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int indx); + int indx); virtual void reset(const Track &track); }; // CheckLine diff --git a/src/tracks/check_line.cpp b/src/tracks/check_line.cpp index bc817dc53..197b586b0 100644 --- a/src/tracks/check_line.cpp +++ b/src/tracks/check_line.cpp @@ -40,6 +40,7 @@ CheckLine::CheckLine(const XMLNode &node, unsigned int index) : CheckStructure(node, index) { + m_ignore_height = false; // Note that when this is called the karts have not been allocated // in world, so we can't call world->getNumKarts() m_previous_sign.resize(race_manager->getNumberOfKarts()); @@ -169,13 +170,14 @@ void CheckLine::changeDebugColor(bool is_active) // ---------------------------------------------------------------------------- /** True if going from old_pos to new_pos crosses this checkline. This function * is called from update (of the checkline structure). - * \param old_pos Position in previous frame. - * \param new_pos Position in current frame. - * \param indx Index of the kart, can be used to store kart specific - * additional data. + * \param old_pos Position in previous frame. + * \param new_pos Position in current frame. + * \param kart_indx Index of the kart, can be used to store kart specific + * additional data. If set to a negative number it will + * be ignored (used for e.g. soccer ball, and basket ball). */ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_index) + int kart_index) { World* w = World::getWorld(); core::vector2df p=new_pos.toIrrVector2d(); @@ -184,7 +186,7 @@ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, bool previous_sign; - if (kart_index == UINT_MAX) + if (kart_index < 0) { core::vector2df p = old_pos.toIrrVector2d(); previous_sign = (m_line.getPointOrientation(p) >= 0); @@ -196,18 +198,20 @@ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, // If the sign has changed, i.e. the infinite line was crossed somewhere, // check if the finite line was actually crossed: + core::vector2df cross_point; if (sign != previous_sign && m_line.intersectWith(core::line2df(old_pos.toIrrVector2d(), new_pos.toIrrVector2d()), - m_cross_point) ) + cross_point) ) { // Now check the minimum height: the kart position must be within a // reasonable distance in the Z axis - 'reasonable' for now to be // between -1 and 4 units (negative numbers are unlikely, but help // in case that the kart is 'somewhat' inside of the track, or the // checklines are a bit off in Z direction. - result = new_pos.getY()-m_min_height-m_under_min_height; + result = m_ignore_height || + (new_pos.getY()-m_min_height-m_under_min_height ); if(UserConfigParams::m_check_debug && !result) { if(World::getWorld()->getNumKarts()>0) @@ -219,20 +223,20 @@ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, Log::info("CheckLine", "Kart %d crosses line, but wrong height " "(%f vs %f).", kart_index, new_pos.getY(), m_min_height); - } } else result = false; - if (kart_index != UINT_MAX) - m_previous_sign[kart_index] = sign; - - if (result && kart_index != UINT_MAX) + if (kart_index >= 0) { - LinearWorld* lw = dynamic_cast(w); - if (lw != NULL) - lw->setLastTriggeredCheckline(kart_index, m_index); + m_previous_sign[kart_index] = sign; + if (result) + { + LinearWorld* lw = dynamic_cast(w); + if (lw != NULL) + lw->setLastTriggeredCheckline(kart_index, m_index); + } } return result; } // isTriggered diff --git a/src/tracks/check_line.hpp b/src/tracks/check_line.hpp index 3b06e2ca8..8eee967af 100644 --- a/src/tracks/check_line.hpp +++ b/src/tracks/check_line.hpp @@ -46,7 +46,10 @@ private: /** The line that is tested for being crossed. */ core::line2df m_line; - core::vector2df m_cross_point; + /** True if this line should ignore the height test. This is required + * e.g. for basketball cannons, since the ball can be too height to + * otherwise trigger the cannon. */ + bool m_ignore_height; /** The minimum height of the checkline. */ float m_min_height; @@ -79,17 +82,18 @@ public: CheckLine(const XMLNode &node, unsigned int index); virtual ~CheckLine(); virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int indx); + int indx); virtual void reset(const Track &track); virtual void resetAfterKartMove(unsigned int kart_index); virtual void changeDebugColor(bool is_active); + // ------------------------------------------------------------------------ /** Returns the actual line data for this checkpoint. */ const core::line2df &getLine2D() const {return m_line;} // ------------------------------------------------------------------------ - /** Returns the 2d point at which the line was crossed. Note that this - * value is ONLY valid after isTriggered is called and inside of - * trigger(). */ - const core::vector2df &getCrossPoint() const { return m_cross_point; } + /** Sets if this check line should not do a height test for testing + * if a line is crossed. Used for basket calls in cannon (the ball can + * be too heigh to otherwise trigger he cannon). */ + void setIgnoreHeight(bool b) { m_ignore_height = b; } }; // CheckLine #endif diff --git a/src/tracks/check_manager.cpp b/src/tracks/check_manager.cpp index 502a84006..19c9a85c3 100644 --- a/src/tracks/check_manager.cpp +++ b/src/tracks/check_manager.cpp @@ -131,6 +131,37 @@ void CheckManager::resetAfterKartMove(AbstractKart *kart) (*i)->resetAfterKartMove(kart->getWorldKartId()); } // resetAfterKartMove +// ---------------------------------------------------------------------------- +/** Adds a flyable object to be tested against cannons. This will allow + * bowling- and rubber-balls to fly in a cannon. + * \param flyable Pointer to the flyable to be added. + */ +void CheckManager::addFlyableToCannons(Flyable *flyable) +{ + for (unsigned int i = 0; i < m_all_checks.size(); i++) + { + CheckCannon *cc = dynamic_cast(m_all_checks[i]); + if (cc) + cc->addFlyable(flyable); + } +} // addFlyable + +// ---------------------------------------------------------------------------- +/** Removes a flyable from all cannons. Used when this flyable is removed + * (e.g. explodes). + * \param flyable Pointer to the flyable to be removed. + */ +void CheckManager::removeFlyableFromCannons(Flyable *flyable) +{ + for (unsigned int i = 0; i < m_all_checks.size(); i++) + { + CheckCannon *cc = dynamic_cast(m_all_checks[i]); + if (cc) + cc->removeFlyable(flyable); + } + +} // addFlyable + // ---------------------------------------------------------------------------- /** Updates all animations. Called one per time step. * \param dt Time since last call. diff --git a/src/tracks/check_manager.hpp b/src/tracks/check_manager.hpp index 93b97cdf8..88664c110 100644 --- a/src/tracks/check_manager.hpp +++ b/src/tracks/check_manager.hpp @@ -27,6 +27,7 @@ class AbstractKart; class CheckStructure; +class Flyable; class Track; class XMLNode; class Vec3; @@ -46,6 +47,8 @@ private: ~CheckManager(); public: void add(CheckStructure* strct) { m_all_checks.push_back(strct); } + void addFlyableToCannons(Flyable *flyable); + void removeFlyableFromCannons(Flyable *flyable); void load(const XMLNode &node); void update(float dt); void reset(const Track &track); diff --git a/src/tracks/check_sphere.cpp b/src/tracks/check_sphere.cpp index bd07fcf24..2ba2e32ad 100644 --- a/src/tracks/check_sphere.cpp +++ b/src/tracks/check_sphere.cpp @@ -53,11 +53,11 @@ CheckSphere::CheckSphere(const XMLNode &node, unsigned int index) * updates the flag about which karts are inside * \param old_pos Position in previous frame. * \param new_pos Position in current frame. - * \param kart_id Index of the kart, can be used to store kart specific + * \param kart_id Index of the kart, can be used to store kart specific * additional data. */ bool CheckSphere::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_id) + int kart_id) { float old_dist2 = (old_pos-m_center_point).length2(); float new_dist2 = (new_pos-m_center_point).length2(); diff --git a/src/tracks/check_sphere.hpp b/src/tracks/check_sphere.hpp index 4780ed681..b11e05fd9 100644 --- a/src/tracks/check_sphere.hpp +++ b/src/tracks/check_sphere.hpp @@ -48,7 +48,7 @@ public: CheckSphere(const XMLNode &node, unsigned int index); virtual ~CheckSphere() {}; virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_id); + int kart_id); // ------------------------------------------------------------------------ /** Returns if kart indx is currently inside of the sphere. */ bool isInside(int index) const { return m_is_inside[index]; } diff --git a/src/tracks/check_structure.hpp b/src/tracks/check_structure.hpp index 921c7b3bc..bb7e29661 100644 --- a/src/tracks/check_structure.hpp +++ b/src/tracks/check_structure.hpp @@ -115,7 +115,7 @@ public: * additional data. */ virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int indx)=0; + int indx)=0; virtual void trigger(unsigned int kart_index); virtual void reset(const Track &track);