From 72b409344e90c0937e50e0c7e731efba382c9478 Mon Sep 17 00:00:00 2001 From: auria Date: Wed, 11 Jan 2012 02:47:06 +0000 Subject: [PATCH] Start work to have trigger items, and for now use them to play a sound when crossing the force fields in overworld. This is WIP. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@10646 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/stk_config.xml | 4 +- src/config/stk_config.cpp | 2 +- src/items/item.cpp | 78 ++++++++++++++++++++++++++++++++----- src/items/item.hpp | 29 +++++++++++++- src/items/item_manager.cpp | 29 +++++++++++++- src/items/item_manager.hpp | 1 + src/tracks/track_object.cpp | 41 +++++++++++++++++-- src/tracks/track_object.hpp | 6 ++- 8 files changed, 170 insertions(+), 20 deletions(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index d7a415436..1d4ad75a8 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -90,8 +90,8 @@ - + Order: giftbox, banana, big-nitro, small-nitro, bubble-bum, trigger --> + diff --git a/src/config/stk_config.cpp b/src/config/stk_config.cpp index 84174ce21..2d9165bda 100644 --- a/src/config/stk_config.cpp +++ b/src/config/stk_config.cpp @@ -97,7 +97,7 @@ void STKConfig::load(const std::string &filename) if(m_switch_items.size()!=Item::ITEM_LAST-Item::ITEM_FIRST+1) { - fprintf(stderr,"No item switches defined in stk_config"); + fprintf(stderr,"Wrong number of item switches defined in stk_config"); exit(-1); } diff --git a/src/items/item.cpp b/src/items/item.cpp index f67c96f5d..26c5a0a9f 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -33,10 +33,13 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, scene::IMesh* mesh, scene::IMesh* lowres_mesh, unsigned int item_id) { + assert(type != ITEM_TRIGGER); // use other constructor for that + setType(type); m_event_handler = NULL; m_xyz = xyz; m_deactive_time = 0; + m_distance_2 = 0.8f; // Sets heading to 0, and sets pitch and roll depending on the normal. */ m_original_hpr = Vec3(0, normal); m_item_id = item_id; @@ -48,6 +51,7 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, : -1 ; m_original_mesh = mesh; m_original_lowmesh = lowres_mesh; + m_listener = NULL; LODNode* lodnode = new LODNode("item", irr_driver->getSceneManager()->getRootSceneNode(), @@ -81,6 +85,34 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, m_node->grab(); } // Item +//----------------------------------------------------------------------------- + +/** \brief Constructor to create a trigger item. + * Trigger items are invisible and can be used to trigger a behavior when + * approaching a point. + */ +Item::Item (const Vec3& xyz, float distance, TriggerItemListener* trigger, + unsigned int item_id) +{ + m_type = ITEM_TRIGGER; + m_event_handler = NULL; + m_xyz = xyz; + m_deactive_time = 0; + // Sets heading to 0, and sets pitch and roll depending on the normal. */ + m_original_hpr = Vec3(0, 0, 0); + m_item_id = item_id; + m_original_type = ITEM_NONE; + m_collected = false; + m_time_till_return = 0.0f; // not strictly necessary, see isCollected() + m_disappear_counter = -1; + m_original_mesh = NULL; + m_original_lowmesh = NULL; + m_node = NULL; + m_listener = trigger; + m_rotate = false; + m_distance_2 = distance*distance; +} + //----------------------------------------------------------------------------- /** Sets the type of this item, but also derived values, e.g. m_rotate. * (bubblegums do not return). @@ -99,6 +131,9 @@ void Item::setType(ItemType type) */ void Item::switchTo(ItemType type, scene::IMesh *mesh, scene::IMesh *lowmesh) { + // triggers should not be switched + if (m_type == ITEM_TRIGGER) return; + m_original_type = m_type; setType(type); @@ -118,6 +153,9 @@ void Item::switchTo(ItemType type, scene::IMesh *mesh, scene::IMesh *lowmesh) */ void Item::switchBack() { + // triggers should not be switched + if (m_type == ITEM_TRIGGER) return; + // If the item is not switched, do nothing. This can happen if a bubble // gum is dropped while items are switched - when switching back, this // bubble gum has no original type. @@ -144,8 +182,11 @@ void Item::switchBack() */ Item::~Item() { - irr_driver->removeNode(m_node); - m_node->drop(); + if (m_node != NULL) + { + irr_driver->removeNode(m_node); + m_node->drop(); + } } // ~Item //----------------------------------------------------------------------------- @@ -165,7 +206,10 @@ void Item::reset() m_original_type = ITEM_NONE; } - m_node->setScale(core::vector3df(1,1,1)); + if (m_node != NULL) + { + m_node->setScale(core::vector3df(1,1,1)); + } } // reset //----------------------------------------------------------------------------- @@ -194,20 +238,26 @@ void Item::update(float dt) if(m_time_till_return<0) { m_collected=false; - m_node->setScale(core::vector3df(1,1,1)); - + + if (m_node != NULL) + { + m_node->setScale(core::vector3df(1,1,1)); + } } // time till return <0 --> is fully visible again else if ( m_time_till_return <=1.0f ) { - // Make it visible by scaling it from 0 to 1: - m_node->setVisible(true); - m_node->setScale(core::vector3df(1,1,1)*(1-m_time_till_return)); + if (m_node != NULL) + { + // Make it visible by scaling it from 0 to 1: + m_node->setVisible(true); + m_node->setScale(core::vector3df(1,1,1)*(1-m_time_till_return)); + } } // time till return < 1 } // if collected else { // not m_collected - if(!m_rotate) return; + if(!m_rotate || m_node == NULL) return; // have it rotate Vec3 rotation(0, dt*M_PI, 0); core::vector3df r = m_node->getRotation(); @@ -244,7 +294,15 @@ void Item::collected(const Kart *kart, float t) // Note if the time is negative, in update the m_collected flag will // be automatically set to false again. m_time_till_return = t; - m_node->setVisible(false); + if (m_node != NULL) + { + m_node->setVisible(false); + } + } + + if (m_listener != NULL) + { + m_listener->onTriggerItemApproached(this); } if (dynamic_cast(World::getWorld()) != NULL) diff --git a/src/items/item.hpp b/src/items/item.hpp index 2cbf12144..2fb2e3626 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -34,9 +34,21 @@ using namespace irr; #include "utils/no_copy.hpp" class LODNode; +class Item; // ----------------------------------------------------------------------------- +/** + * \ingroup items + * \brief Listener class to go with Items of type ITEM_TRIGGER + */ +class TriggerItemListener +{ +public: + virtual ~TriggerItemListener() {} + virtual void onTriggerItemApproached(Item* who) = 0; +}; + /** * \ingroup items */ @@ -56,7 +68,11 @@ public: ITEM_NITRO_BIG, ITEM_NITRO_SMALL, ITEM_BUBBLEGUM, - ITEM_LAST = ITEM_BUBBLEGUM, + /** An invisible item that can be used to trigger some behavior when + * approaching a point + */ + ITEM_TRIGGER, + ITEM_LAST = ITEM_TRIGGER, ITEM_COUNT, ITEM_NONE }; @@ -115,9 +131,18 @@ private: int m_disappear_counter; void setType(ItemType type); + + /** callback used if type == ITEM_TRIGGER */ + TriggerItemListener* m_listener; + + /** square distance at which item is collected */ + float m_distance_2; + public: Item (ItemType type, const Vec3& xyz, const Vec3& normal, scene::IMesh* mesh, scene::IMesh* lowres_mesh, unsigned int item_id); + Item (const Vec3& xyz, float distance, TriggerItemListener* trigger, + unsigned int item_id); virtual ~Item (); void update (float delta); virtual void collected(const Kart *kart, float t=2.0f); @@ -130,7 +155,7 @@ public: bool hitKart (Kart* kart ) const { return (m_event_handler!=kart || m_deactive_time <=0) && - (kart->getXYZ()-m_xyz).length2()<0.8f; + (kart->getXYZ()-m_xyz).length2()getDataFile("items.xml"); const XMLNode *root = file_manager->createXMLTree(file_name); for(unsigned int i=Item::ITEM_FIRST; i<=Item::ITEM_LAST; i++) @@ -122,6 +122,10 @@ void ItemManager::loadDefaultItems() node->get("model", &model_filename); node->get("lowmodel", &lowres_model_filename); } + else + { + continue; + } scene::IMesh *mesh = irr_driver->getAnimatedMesh(model_filename); scene::IMesh *lowres_mesh = NULL; @@ -182,6 +186,29 @@ Item* ItemManager::newItem(Item::ItemType type, const Vec3& xyz, return item; } // newItem +//----------------------------------------------------------------------------- +/** Creates a new trigger item. + * \param xyz Position of the item. + */ +Item* ItemManager::newItem(const Vec3& xyz, float distance, TriggerItemListener* listener) +{ + // Find where the item can be stored in the index list: either in a + // previously deleted entry, otherwise at the end. + int index = -1; + for(index=m_all_items.size()-1; index>=0 && m_all_items[index]; index--) {} + + if(index==-1) index = m_all_items.size(); + Item* item; + item = new Item(xyz, distance, listener, index); + + if(index<(int)m_all_items.size()) + m_all_items[index] = item; + else + m_all_items.push_back(item); + + return item; +} // newItem + //----------------------------------------------------------------------------- /** Set an item as collected. * This function is called on the server when an item is collected, or on the diff --git a/src/items/item_manager.hpp b/src/items/item_manager.hpp index bc85d7780..754541675 100644 --- a/src/items/item_manager.hpp +++ b/src/items/item_manager.hpp @@ -62,6 +62,7 @@ public: void loadDefaultItems(); Item* newItem (Item::ItemType type, const Vec3& xyz, const Vec3 &normal, Kart* parent=NULL); + Item* newItem (const Vec3& xyz, float distance, TriggerItemListener* listener); void update (float delta); void checkItemHit (Kart* kart); void cleanup (); diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index d701362eb..d22c38adb 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -24,6 +24,7 @@ #include "graphics/irr_driver.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" +#include "items/item_manager.hpp" #include "modes/world.hpp" #include "tracks/track.hpp" @@ -56,6 +57,12 @@ TrackObject::TrackObject(const XMLNode &xml_node) xml_node.get("lod_group", &m_lod_group); + /** For sound effects */ + bool trigger_when_near = false; + + /** For sound effects */ + float trigger_distance = 1.0f; + // FIXME: at this time sound emitters are just disabled in multiplayer // otherwise the sounds would be constantly heard if (sound.size() > 0 && race_manager->getNumLocalPlayers() == 1) @@ -65,7 +72,18 @@ TrackObject::TrackObject(const XMLNode &xml_node) float volume = 1.0; xml_node.get("volume", &volume ); - SFXBuffer* buffer = new SFXBuffer(file_manager->getModelFile(sound), + xml_node.get("play-when-near", &trigger_when_near); + + xml_node.get("distance", &trigger_distance); + + // first try track dir, then global dir + std::string soundfile = file_manager->getModelFile(sound); + if (!file_manager->fileExists(soundfile)) + { + soundfile = file_manager->getSFXFile(sound); + } + + SFXBuffer* buffer = new SFXBuffer(soundfile, true /* positional */, rolloff, volume); @@ -75,8 +93,11 @@ TrackObject::TrackObject(const XMLNode &xml_node) if (m_sound != NULL) { m_sound->position(m_init_xyz); - m_sound->setLoop(true); - m_sound->play(); + if (!trigger_when_near) + { + m_sound->setLoop(true); + m_sound->play(); + } } else { @@ -91,6 +112,11 @@ TrackObject::TrackObject(const XMLNode &xml_node) { m_node = NULL; m_mesh = NULL; + + if (trigger_when_near) + { + item_manager->newItem(m_init_xyz, trigger_distance, this); + } } else { @@ -261,3 +287,12 @@ void TrackObject::update(float dt) { } // update // ---------------------------------------------------------------------------- + +/** Implement callback from TriggerItemListener. Not used by all track objects. */ +void TrackObject::onTriggerItemApproached(Item* who) +{ + if (m_sound != NULL && m_sound->getStatus() == SFXManager::SFX_STOPPED) + { + m_sound->play(); + } +} diff --git a/src/tracks/track_object.hpp b/src/tracks/track_object.hpp index 557948657..528d6b621 100644 --- a/src/tracks/track_object.hpp +++ b/src/tracks/track_object.hpp @@ -27,6 +27,7 @@ namespace irr } using namespace irr; +#include "items/item.hpp" #include "utils/no_copy.hpp" #include "utils/vec3.hpp" #include @@ -40,7 +41,8 @@ class SFXBase; * might also have a skeletal animation. This is used by objects that * have an IPO animation, as well as physical objects. */ -class TrackObject : public scene::IAnimationEndCallBack, public NoCopy +class TrackObject : public scene::IAnimationEndCallBack, public NoCopy, + public TriggerItemListener { //public: // The different type of track objects: physical objects, graphical @@ -115,6 +117,8 @@ public: const std::string& getLodGroup() const { return m_lod_group; } + virtual void onTriggerItemApproached(Item* who); + }; // TrackObject #endif