diff --git a/lib/irrlicht/include/IAnimatedMeshSceneNode.h b/lib/irrlicht/include/IAnimatedMeshSceneNode.h index 89c84d740..6929dd347 100644 --- a/lib/irrlicht/include/IAnimatedMeshSceneNode.h +++ b/lib/irrlicht/include/IAnimatedMeshSceneNode.h @@ -177,8 +177,9 @@ namespace scene \param newManager An optional new scene manager. \return The newly created clone of this node. */ virtual ISceneNode* clone(ISceneNode* newParent=0, ISceneManager* newManager=0) = 0; - + virtual void setFrameLoopOnce(s32 begin, s32 end) = 0; virtual u32 getAnimationSetNum() = 0; + virtual s32 getAnimationSet() const = 0; virtual void addAnimationSet(u32 start, u32 end) = 0; virtual void useAnimationSet(u32 set_num) = 0; }; diff --git a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp index 3d6b08aa5..030b107e2 100644 --- a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp +++ b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp @@ -1002,5 +1002,60 @@ void CAnimatedMeshSceneNode::useAnimationSet(u32 set_num) setFrameLoop(m_animation_set[set_num * 2], m_animation_set[set_num * 2 + 1]); } +void CAnimatedMeshSceneNode::setFrameLoopOnce(s32 begin, s32 end) +{ + if (LoopCallBack != NULL || !Looping) + { + return; + } + Looping = false; + class MiniLoopSetter : public IAnimationEndCallBack + { + private: + int m_old_start, m_old_end, m_new_start, m_new_end; + + bool m_run_cb; + public: + MiniLoopSetter(int old_start, int old_end, int new_start, int new_end) + : m_old_start(old_start), m_old_end(old_end), + m_new_start(new_start), m_new_end(new_end), m_run_cb(false) {} + virtual void OnAnimationEnd(IAnimatedMeshSceneNode* node) + { + if (!m_run_cb) + { + m_run_cb = true; + node->setFrameLoop(m_new_start, m_new_end); + return; + } + if (m_run_cb) + { + node->setFrameLoop(m_old_start, m_old_end); + node->setLoopMode(true); + node->setAnimationEndCallback(NULL); + return; + } + } + }; + MiniLoopSetter* mls = new MiniLoopSetter(StartFrame, EndFrame, + begin, end); + setAnimationEndCallback(mls); + mls->drop(); + +} + +s32 CAnimatedMeshSceneNode::getAnimationSet() const +{ + for (u32 i = 0; i < m_animation_set.size(); i += 2) + { + if (m_animation_set[i] == (u32)StartFrame && + m_animation_set[i + 1] == (u32)EndFrame) + { + return (s32)(i >> 1); + } + } + return -1; +} + + } // end namespace scene } // end namespace irr diff --git a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h index 1ea8f5112..68dc93748 100644 --- a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h +++ b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h @@ -161,13 +161,14 @@ namespace scene virtual ISceneNode* clone(ISceneNode* newParent=0, ISceneManager* newManager=0); virtual u32 getAnimationSetNum() { return m_animation_set.size() / 2; } + virtual s32 getAnimationSet() const; virtual void addAnimationSet(u32 start, u32 end) { m_animation_set.push_back(start); m_animation_set.push_back(end); } virtual void useAnimationSet(u32 set_num); - + virtual void setFrameLoopOnce(s32 begin, s32 end); protected: //! Get a static mesh for the current frame of this animated mesh diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index e4ad0e7cd..114657ba7 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -127,6 +127,23 @@ namespace Scripting #endif } + /** Function for re-enable a trigger after a specific timeout*/ + void setTriggerReenableTimeout(std::string* triggerID, std::string* lib_id, + float reenable_time) + { + ::TrackObject* tobj = ::Track::getCurrentTrack()->getTrackObjectManager() + ->getTrackObject(*lib_id, *triggerID); + if (tobj != NULL) + { + TrackObjectPresentationActionTrigger* topat = + tobj->getPresentation(); + if (topat != NULL) + { + topat->setReenableTimeout(reenable_time); + } + } + } + /** Exits the race to the main menu */ void exitRace() { @@ -231,22 +248,52 @@ namespace Scripting /** Sets a loop for a skeletal animation */ // TODO: can we use a type and avoid void* ? - void setLoop(int start, int end /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) + void setFrameLoop(int start, int end /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) { - ((TrackObjectPresentationMesh*)(memory))->setLoop(start, end); + if (memory) + { + ((scene::IAnimatedMeshSceneNode*)(memory))->setFrameLoop(start, end); + } + } + + /** Sets a loop once for a skeletal animation */ + void setFrameLoopOnce(int start, int end /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) + { + if (memory) + { + ((scene::IAnimatedMeshSceneNode*)(memory))->setFrameLoopOnce(start, end); + } + } + + /** Get current frame in a skeletal animation */ + int getFrameNr(/** \cond DOXYGEN_IGNORE */void *memory /** \endcond */) + { + if (memory) + { + return ((scene::IAnimatedMeshSceneNode*)(memory))->getFrameNr(); + } + return -1; + } + + /** Gets the animation set for a skeletal animation */ + int getAnimationSet(/** \cond DOXYGEN_IGNORE */void *memory /** \endcond */) + { + if (memory) + { + return ((scene::IAnimatedMeshSceneNode*)(memory))->getAnimationSet(); + } + return -1; } /** Sets the current frame for a skeletal animation */ void setCurrentFrame(int frame /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) { - ((TrackObjectPresentationMesh*)(memory))->setCurrentFrame(frame); + if (memory) + { + ((scene::IAnimatedMeshSceneNode*)(memory))->setCurrentFrame(frame); + } } - /** Get current frame in a skeletal animation */ - int getCurrentFrame(/** \cond DOXYGEN_IGNORE */void *memory /** \endcond */) - { - return ((TrackObjectPresentationMesh*)(memory))->getCurrentFrame(); - } /** @} */ } @@ -384,6 +431,8 @@ namespace Scripting asFUNCTION(createTrigger), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void createTextBillboard(const string &in, const Vec3 &in)", asFUNCTION(createTextBillboard), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("void setTriggerReenableTimeout(const string &in, const string &in, float reenable_time)", + asFUNCTION(setTriggerReenableTimeout), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("TrackObject@ getTrackObject(const string &in, const string &in)", asFUNCTION(getTrackObject), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void exitRace()", asFUNCTION(exitRace), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void pauseRace()", asFUNCTION(pauseRace), asCALL_CDECL); assert(r >= 0); @@ -410,9 +459,11 @@ namespace Scripting r = engine->RegisterObjectMethod("PhysicalObject", "void disable()", asMETHOD(PhysicalObject, disable), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("PhysicalObject", "void enable()", asMETHOD(PhysicalObject, enable), asCALL_THISCALL); assert(r >= 0); - // TrackObjectPresentationMesh (Mesh or Skeletal Animation) - r = engine->RegisterObjectMethod("Mesh", "void setLoop(int start, int end)", asFUNCTION(Mesh::setLoop), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterObjectMethod("Mesh", "int getCurrentFrame()", asFUNCTION(Mesh::getCurrentFrame), asCALL_CDECL_OBJLAST); assert(r >= 0); + // Animated Mesh + r = engine->RegisterObjectMethod("Mesh", "void setFrameLoop(int start, int end)", asFUNCTION(Mesh::setFrameLoop), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("Mesh", "void setFrameLoopOnce(int start, int end)", asFUNCTION(Mesh::setFrameLoopOnce), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("Mesh", "int getFrameNr()", asFUNCTION(Mesh::getFrameNr), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("Mesh", "int getAnimationSet()", asFUNCTION(Mesh::getAnimationSet), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterObjectMethod("Mesh", "void setCurrentFrame(int frame)", asFUNCTION(Mesh::setCurrentFrame), asCALL_CDECL_OBJLAST); assert(r >= 0); //r = engine->RegisterObjectMethod("Mesh", "void move(Vec3 &in)", asFUNCTION(movePresentation), asCALL_CDECL_OBJLAST); assert(r >= 0); diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index ada404ae6..f68dc3f0c 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -20,6 +20,7 @@ #include "animations/three_d_animation.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/lod_node.hpp" #include "graphics/material.hpp" #include "graphics/material_manager.hpp" #include "graphics/render_info.hpp" @@ -32,6 +33,7 @@ #include "scriptengine/script_engine.hpp" #include "tracks/model_definition_loader.hpp" +#include #include /** A track object: any additional object on the track. This object implements @@ -156,6 +158,7 @@ void TrackObject::init(const XMLNode &xml_node, scene::ISceneNode* parent, } else if (xml_node.getName() == "library") { + xml_node.get("name", &m_name); m_presentation = new TrackObjectPresentationLibraryNode(this, xml_node, model_def_loader); } else if (type == "sfx-emitter") @@ -170,7 +173,7 @@ void TrackObject::init(const XMLNode &xml_node, scene::ISceneNode* parent, std::string action; xml_node.get("action", &action); m_name = action; //adds action as name so that it can be found by using getName() - m_presentation = new TrackObjectPresentationActionTrigger(xml_node); + m_presentation = new TrackObjectPresentationActionTrigger(xml_node, parent_library); } else if (type == "billboard") { @@ -661,3 +664,35 @@ void TrackObject::moveTo(const Scripting::SimpleVec3* pos, bool isAbsoluteCoord) isAbsoluteCoord); } } + +// ---------------------------------------------------------------------------- +scene::IAnimatedMeshSceneNode* TrackObject::getMesh() +{ + if (getPresentation()) + { + LODNode* ln = dynamic_cast + (getPresentation()->getNode()); + if (ln && !ln->getAllNodes().empty()) + { + scene::IAnimatedMeshSceneNode* an = + dynamic_cast + (ln->getFirstNode()); + if (an) + { + return an; + } + } + } + else if (getPresentation()) + { + scene::IAnimatedMeshSceneNode* an = + dynamic_cast + (getPresentation()->getNode()); + if (an) + { + return an; + } + } + Log::debug("TrackObject", "No animated mesh"); + return NULL; +} // getMesh diff --git a/src/tracks/track_object.hpp b/src/tracks/track_object.hpp index ceabc0c31..c8b7b5acc 100644 --- a/src/tracks/track_object.hpp +++ b/src/tracks/track_object.hpp @@ -190,7 +190,7 @@ public: /** Should only be used on mesh track objects. * On the script side, the returned object is of type : @ref Scripting_Mesh */ - TrackObjectPresentationMesh* getMesh() { return getPresentation(); } + scene::IAnimatedMeshSceneNode* getMesh(); /** Should only be used on particle emitter track objects. * On the script side, the returned object is of type : @ref Scripting_ParticleEmitter */ diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 7a2914171..8f70e1ee6 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -358,11 +358,13 @@ void TrackObjectPresentationLOD::reset() dynamic_cast(node); if (a_node) { + a_node->setLoopMode(true); + a_node->setAnimationEndCallback(NULL); RandomGenerator rg; int animation_set = 0; if (a_node->getAnimationSetNum() > 0) animation_set = rg.get(a_node->getAnimationSetNum()); - a_node->useAnimationSet(animation_set); + a_node->useAnimationSet(animation_set); } } } @@ -642,6 +644,7 @@ void TrackObjectPresentationMesh::reset() a_node->setRotation(m_init_hpr); a_node->setScale(m_init_scale); a_node->setLoopMode(m_is_looped); + a_node->setAnimationEndCallback(NULL); a_node->setCurrentFrame((float)(a_node->getStartFrame())); // trick to reset the animation AND also the timer inside it @@ -658,49 +661,6 @@ void TrackObjectPresentationMesh::reset() } } // reset -// ---------------------------------------------------------------------------- -int TrackObjectPresentationMesh::getCurrentFrame() -{ - if (m_node->getType() == scene::ESNT_ANIMATED_MESH) - { - scene::IAnimatedMeshSceneNode *a_node = - (scene::IAnimatedMeshSceneNode*)m_node; - - return (int)a_node->getFrameNr(); - } - return -1; //Not a skeletal animation -} // getCurrentFrame - -// ---------------------------------------------------------------------------- -void TrackObjectPresentationMesh::setCurrentFrame(int frame) -{ - if (m_node->getType() == scene::ESNT_ANIMATED_MESH) - { - scene::IAnimatedMeshSceneNode *a_node = - (scene::IAnimatedMeshSceneNode*)m_node; - - a_node->setCurrentFrame((f32)frame); - } -} // setCurrentFrame - -// ---------------------------------------------------------------------------- -/** Set custom loops, as well as pause by scripts. - * \param start Start frame. - * \param end End frame. - */ -void TrackObjectPresentationMesh::setLoop(int start, int end) -{ - if (m_node->getType() == scene::ESNT_ANIMATED_MESH) - { - scene::IAnimatedMeshSceneNode *a_node = - (scene::IAnimatedMeshSceneNode*)m_node; - - // irrlicht's "setFrameLoop" is a misnomer, it just sets the first and - // last frame, even if looping is disabled - a_node->setFrameLoop(start, end); - } -} // setLoop - // ---------------------------------------------------------------------------- TrackObjectPresentationSound::TrackObjectPresentationSound( const XMLNode& xml_node, @@ -1094,7 +1054,8 @@ void TrackObjectPresentationLight::setEnergy(float energy) } // ---------------------------------------------------------------------------- TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger( - const XMLNode& xml_node) + const XMLNode& xml_node, + TrackObject* parent) : TrackObjectPresentation(xml_node) { float trigger_distance = 1.0f; @@ -1115,11 +1076,36 @@ TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger( { assert(false); } + m_xml_reenable_timeout = 999999.9f; + xml_node.get("reenable-timeout", &m_xml_reenable_timeout); + m_reenable_timeout = 0.0f; - m_action_active = true; - - if (m_action.size() == 0) + if (m_action.empty()) + { Log::warn("TrackObject", "Action-trigger has no action defined."); + return; + } + + if (parent != NULL) + { + core::vector3df parent_xyz = parent->getInitXYZ(); + core::vector3df parent_rot = parent->getInitRotation(); + core::vector3df parent_scale = parent->getInitScale(); + core::matrix4 lm, sm, rm; + lm.setTranslation(parent_xyz); + sm.setScale(parent_scale); + rm.setRotationDegrees(parent_rot); + core::matrix4 abs_trans = lm * rm * sm; + + m_library_id = parent->getID(); + m_library_name = parent->getName(); + xml_node.get("triggered-object", &m_triggered_object); + if (!m_library_id.empty() && !m_triggered_object.empty() && + !m_library_name.empty()) + { + abs_trans.transformVect(m_init_xyz); + } + } if (m_type == TRIGGER_TYPE_POINT) { @@ -1149,7 +1135,8 @@ TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger( m_init_scale = core::vector3df(1, 1, 1); float trigger_distance = distance; m_action = script_name; - m_action_active = true; + m_xml_reenable_timeout = 999999.9f; + m_reenable_timeout = 0.0f; m_type = TRIGGER_TYPE_POINT; ItemManager::get()->newItem(m_init_xyz, trigger_distance, this); } // TrackObjectPresentationActionTrigger @@ -1157,13 +1144,36 @@ TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger( // ---------------------------------------------------------------------------- void TrackObjectPresentationActionTrigger::onTriggerItemApproached() { - if (!m_action_active) return; + if (m_reenable_timeout > 0.0f) + { + return; + } + m_reenable_timeout = m_xml_reenable_timeout; - m_action_active = false; // TODO: allow auto re-activating? - int idKart = 0; + int kart_id = 0; Camera* camera = Camera::getActiveCamera(); if (camera != NULL && camera->getKart() != NULL) - idKart = camera->getKart()->getWorldKartId(); - Scripting::ScriptEngine::getInstance()->runFunction(true, "void " + m_action + "(int)", - [=](asIScriptContext* ctx) { ctx->SetArgDWord(0, idKart); }); + { + kart_id = camera->getKart()->getWorldKartId(); + } + if (!m_library_id.empty() && !m_triggered_object.empty() && + !m_library_name.empty()) + { + Scripting::ScriptEngine::getInstance()->runFunction(true, "void " + + m_library_name + "::" + m_action + + "(int, const string, const string)", [=](asIScriptContext* ctx) + { + ctx->SetArgDWord(0, kart_id); + ctx->SetArgObject(1, &m_library_id); + ctx->SetArgObject(2, &m_triggered_object); + }); + } + else + { + Scripting::ScriptEngine::getInstance()->runFunction(true, + "void " + m_action + "(int)", [=](asIScriptContext* ctx) + { + ctx->SetArgDWord(0, kart_id); + }); + } } // onTriggerItemApproached diff --git a/src/tracks/track_object_presentation.hpp b/src/tracks/track_object_presentation.hpp index 000ee5071..ed87ad248 100644 --- a/src/tracks/track_object_presentation.hpp +++ b/src/tracks/track_object_presentation.hpp @@ -251,9 +251,6 @@ public: const core::vector3df& hpr, const core::vector3df& scale); virtual ~TrackObjectPresentationMesh(); - void setLoop(int start, int end); - void setCurrentFrame(int frame); - int getCurrentFrame(); virtual void reset() OVERRIDE; // ------------------------------------------------------------------------ /** Returns the mode file name. */ @@ -382,14 +379,15 @@ class TrackObjectPresentationActionTrigger : public TrackObjectPresentation, { private: /** For action trigger objects */ - std::string m_action; + std::string m_action, m_library_id, m_triggered_object, m_library_name; - bool m_action_active; + float m_xml_reenable_timeout, m_reenable_timeout; ActionTriggerType m_type; public: - TrackObjectPresentationActionTrigger(const XMLNode& xml_node); + TrackObjectPresentationActionTrigger(const XMLNode& xml_node, + TrackObject* parent); TrackObjectPresentationActionTrigger(const core::vector3df& xyz, const std::string& scriptname, float distance); @@ -399,11 +397,23 @@ public: virtual void onTriggerItemApproached() OVERRIDE; // ------------------------------------------------------------------------ /** Reset the trigger (i.e. sets it to active again). */ - virtual void reset() OVERRIDE { m_action_active = true; } + virtual void reset() OVERRIDE { m_reenable_timeout = 0.0f; } + // ------------------------------------------------------------------------ + virtual void update(float dt) OVERRIDE + { + if (m_reenable_timeout < 900000.0f) + { + m_reenable_timeout -= dt; + } + } // ------------------------------------------------------------------------ /** Sets the trigger to be enabled or disabled. */ - virtual void setEnable(bool status) OVERRIDE{ m_action_active = status; } + virtual void setEnable(bool status) OVERRIDE + { m_reenable_timeout = status ? 0.0f : 999999.9f; } + // ------------------------------------------------------------------------ + void setReenableTimeout(float time) { m_reenable_timeout = time; } }; // class TrackObjectPresentationActionTrigger #endif // TRACKOBJECTPRESENTATION_HPP +