Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/BaseObject.cc @ 6169

Last change on this file since 6169 was 5929, checked in by rgrieder, 15 years ago

Merged core5 branch back to the trunk.
Key features include clean level unloading and an extended XML event system.

Two important notes:
Delete your keybindings.ini files! * or you will still get parser errors when loading the key bindings.
Delete build_dir/lib/modules/libgamestates.module! * or orxonox won't start.
Best thing to do is to delete the build folder ;)

  • Property svn:eol-style set to native
File size: 16.2 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29/**
30    @file
31    @brief Implementation of the BaseObject class.
32*/
33
34#include "BaseObject.h"
35
36#include <tinyxml/tinyxml.h>
37
38#include "util/StringUtils.h"
39#include "CoreIncludes.h"
40#include "Event.h"
41#include "EventIncludes.h"
42#include "Functor.h"
43#include "Iterator.h"
44#include "Template.h"
45#include "XMLFile.h"
46#include "XMLNameListener.h"
47#include "XMLPort.h"
48
49namespace orxonox
50{
51    CreateFactory(BaseObject);
52
53    /**
54        @brief Constructor: Registers the object in the BaseObject-list.
55    */
56    BaseObject::BaseObject(BaseObject* creator) : bInitialized_(false)
57    {
58        RegisterRootObject(BaseObject);
59
60        this->bInitialized_ = true;
61
62        this->bActive_ = true;
63        this->bVisible_ = true;
64        this->oldGametype_ = 0;
65        this->bRegisteredEventStates_ = false;
66
67        this->lastLoadedXMLElement_ = 0;
68
69        this->mainStateFunctor_ = 0;
70
71        this->setCreator(creator);
72        if (this->creator_)
73        {
74            this->setFile(this->creator_->getFile());
75            this->setNamespace(this->creator_->getNamespace());
76            this->setScene(this->creator_->getScene(), this->creator_->getSceneID());
77            this->setGametype(this->creator_->getGametype());
78        }
79        else
80        {
81            this->file_ = 0;
82            this->namespace_ = 0;
83            this->scene_ = 0;
84            this->sceneID_ = OBJECTID_UNKNOWN;
85            this->gametype_ = 0;
86        }
87    }
88
89    /**
90        @brief Destructor
91    */
92    BaseObject::~BaseObject()
93    {
94        if (this->isInitialized())
95        {
96            for (std::map<BaseObject*, std::string>::const_iterator it = this->eventSources_.begin(); it != this->eventSources_.end(); )
97                this->removeEventSource((it++)->first);
98
99            for (std::set<BaseObject*>::const_iterator it = this->eventListeners_.begin(); it != this->eventListeners_.end(); )
100                (*(it++))->removeEventSource(this);
101
102            for (std::map<std::string, EventState*>::const_iterator it = this->eventStates_.begin(); it != this->eventStates_.end(); ++it)
103                delete it->second;
104        }
105    }
106
107    /**
108        @brief XML loading and saving.
109        @param xmlelement The XML-element
110        @param loading Loading (true) or saving (false)
111    */
112    void BaseObject::XMLPort(Element& xmlelement, XMLPort::Mode mode)
113    {
114        XMLPortParam(BaseObject, "name", setXMLName, getName, xmlelement, mode);
115        XMLPortParam(BaseObject, "visible", setVisible, isVisible, xmlelement, mode);
116        XMLPortParam(BaseObject, "active", setActive, isActive, xmlelement, mode);
117        XMLPortParam(BaseObject, "mainstate", setMainStateName, getMainStateName, xmlelement, mode);
118
119        XMLPortObjectTemplate(BaseObject, Template, "templates", addTemplate, getTemplate, xmlelement, mode, Template*);
120        XMLPortObject(BaseObject, BaseObject, "eventlisteners", addEventListener, getEventListener, xmlelement, mode);
121       
122        Element* events = 0;
123        if (mode == XMLPort::LoadObject || mode == XMLPort::ExpandObject)
124            events = xmlelement.FirstChildElement("events", false);
125        else if (mode == XMLPort::SaveObject)
126            {}
127        if (events)
128            this->XMLEventPort(*events, mode);
129    }
130
131    /**
132        @brief Defines the possible event states of this object and parses eventsources from an XML file.
133        @param xmlelement The XML-element
134        @param loading Loading (true) or saving (false)
135    */
136    void BaseObject::XMLEventPort(Element& xmlelement, XMLPort::Mode mode)
137    {
138        XMLPortEventState(BaseObject, BaseObject, "activity", setActive, xmlelement, mode);
139        XMLPortEventState(BaseObject, BaseObject, "visibility", setVisible, xmlelement, mode);
140        XMLPortEventState(BaseObject, BaseObject, "mainstate", setMainState, xmlelement, mode);
141       
142        this->bRegisteredEventStates_ = true;
143    }
144
145    /**
146        @brief Loads the name of the object through XML and calls all XMLNameListener.
147        @param name The name of the object
148    */
149    void BaseObject::setXMLName(const std::string& name)
150    {
151        this->setName(name);
152
153        for (ObjectList<XMLNameListener>::iterator it = ObjectList<XMLNameListener>::begin(); it != ObjectList<XMLNameListener>::end(); ++it)
154            it->loadedNewXMLName(this);
155    }
156
157    /**
158        @brief Returns the levelfile that loaded this object.
159        @return The levelfile
160    */
161    const std::string& BaseObject::getFilename() const
162    {
163        if (this->file_)
164            return this->file_->getFilename();
165        else
166            return BLANKSTRING;
167    }
168
169    /**
170        @brief Adds a Template to the object.
171        @param name The name of the Template
172    */
173    void BaseObject::addTemplate(const std::string& name)
174    {
175        Template* temp = Template::getTemplate(name);
176        if (temp)
177            this->addTemplate(temp);
178        else
179            COUT(1) << "Error: \"" << name << "\" is not a valid Template name (in class: " << this->getIdentifier()->getName() << ", name: " << this->getName() << ")." << std::endl;
180    }
181
182    /**
183        @brief Adds a Template to the object.
184        @param temp The Template
185    */
186    void BaseObject::addTemplate(Template* temp)
187    {
188        this->templates_.insert(temp);
189        temp->applyOn(this);
190    }
191
192    /**
193        @brief Returns the Template with the given index.
194        @param index The index
195    */
196    Template* BaseObject::getTemplate(unsigned int index) const
197    {
198        unsigned int i = 0;
199        for (std::set<Template*>::const_iterator it = this->templates_.begin(); it != this->templates_.end(); ++it)
200        {
201            if (i == index)
202                return (*it);
203            i++;
204        }
205        return 0;
206    }
207
208    /**
209        @brief Adds a new event source for a specific state.
210        @param source The object which sends events to this object
211        @param state The state of this object which will be affected by the events
212    */
213    void BaseObject::addEventSource(BaseObject* source, const std::string& state)
214    {
215        this->eventSources_[source] = state;
216        source->registerEventListener(this);
217    }
218
219    /**
220        @brief Removes an eventsource (but doesn't unregister itself at the source).
221    */
222    void BaseObject::removeEventSource(BaseObject* source)
223    {
224        this->eventSources_.erase(source);
225        source->unregisterEventListener(this);
226    }
227
228    /**
229        @brief Returns an eventsource with a given index.
230    */
231    BaseObject* BaseObject::getEventSource(unsigned int index, const std::string& state) const
232    {
233        unsigned int i = 0;
234        for (std::map<BaseObject*, std::string>::const_iterator it = this->eventSources_.begin(); it != this->eventSources_.end(); ++it)
235        {
236            if (it->second != state)
237                continue;
238           
239            if (i == index)
240                return it->first;
241            ++i;
242        }
243        return 0;
244    }
245
246    /**
247        @brief Adds an object which listens to the events of this object. The events are sent to the other objects mainstate.
248    */
249    void BaseObject::addEventListener(BaseObject* listener)
250    {
251        this->eventListenersXML_.insert(listener);
252        listener->addEventSource(this, "mainstate");
253    }
254   
255    /**
256        @brief Returns an event listener with a given index.
257    */
258    BaseObject* BaseObject::getEventListener(unsigned int index) const
259    {
260        unsigned int i = 0;
261        for (std::set<BaseObject*>::const_iterator it = this->eventListenersXML_.begin(); it != this->eventListenersXML_.end(); ++it)
262        {
263            if (i == index)
264                return *it;
265            ++i;
266        }
267        return 0;
268    }
269
270    /**
271        @brief Adds a new event-state to the object. Event-states are states which can be changed by events.
272        @param name  The name of the event
273        @param state The object containing information about the event-state
274    */
275    void BaseObject::addEventState(const std::string& name, EventState* state)
276    {
277        std::map<std::string, EventState*>::const_iterator it = this->eventStates_.find(name);
278        if (it != this->eventStates_.end())
279        {
280            COUT(2) << "Warning: Overwriting EventState in class " << this->getIdentifier()->getName() << "." << std::endl;
281            delete (it->second);
282        }
283
284        this->eventStates_[name] = state;
285    }
286
287    /**
288        @brief Returns the event-state with the given name.
289    */
290    EventState* BaseObject::getEventState(const std::string& name) const
291    {
292        std::map<std::string, EventState*>::const_iterator it = this->eventStates_.find(name);
293        if (it != this->eventStates_.end())
294            return ((*it).second);
295        else
296            return 0;
297    }
298
299    /**
300        @brief Fires an event (without a state).
301    */
302    void BaseObject::fireEvent(const std::string& name)
303    {
304        this->fireEvent(true, name);
305        this->fireEvent(false, name);
306    }
307
308    /**
309        @brief Fires an event which activates or deactivates a state.
310    */
311    void BaseObject::fireEvent(bool activate, const std::string& name)
312    {
313        this->fireEvent(activate, this, name);
314    }
315
316    /**
317        @brief Fires an event which activates or deactivates a state with agiven originator (the object which triggered the event).
318    */
319    void BaseObject::fireEvent(bool activate, BaseObject* originator, const std::string& name)
320    {
321        Event event(activate, originator, name);
322
323        for (std::set<BaseObject*>::iterator it = this->eventListeners_.begin(); it != this->eventListeners_.end(); ++it)
324        {
325            event.statename_ = (*it)->eventSources_[this];
326            (*it)->processEvent(event);
327        }
328    }
329
330    /**
331        @brief Fires an event, using the Event struct.
332    */
333    void BaseObject::fireEvent(Event& event)
334    {
335        for (std::set<BaseObject*>::iterator it = this->eventListeners_.begin(); it != this->eventListeners_.end(); ++it)
336            (*it)->processEvent(event);
337    }
338
339    /**
340        @brief Processing an event by calling the right main state.
341        @param event The event struct which contains the information about the event
342    */
343    void BaseObject::processEvent(Event& event)
344    {
345        this->registerEventStates();
346       
347        std::map<std::string, EventState*>::const_iterator it = this->eventStates_.find(event.statename_);
348        if (it != this->eventStates_.end())
349            it->second->process(event, this);
350        else if (event.statename_ != "")
351            COUT(2) << "Warning: \"" << event.statename_ << "\" is not a valid state in object \"" << this->getName() << "\" of class " << this->getIdentifier()->getName() << "." << std::endl;
352        else
353            COUT(2) << "Warning: Event with invalid source sent to object \"" << this->getName() << "\" of class " << this->getIdentifier()->getName() << "." << std::endl;
354    }
355
356    /**
357        @brief Sets the main state of the object to a given boolean value.
358       
359        Note: The main state of an object can be set with the @ref setMainStateName function.
360        It's part of the eventsystem and used for event forwarding (when the target object can't specify a specific state,
361        the main state is used by default).
362    */
363    void BaseObject::setMainState(bool state)
364    {
365        if (this->mainStateFunctor_)
366        {
367            if (this->mainStateFunctor_->getParamCount() == 0)
368            {
369                if (state)
370                    (*this->mainStateFunctor_)();
371            }
372            else
373            {
374                (*this->mainStateFunctor_)(state);
375            }
376        }
377        else
378            COUT(2) << "Warning: No MainState defined in object \"" << this->getName() << "\" (" << this->getIdentifier()->getName() << ")" << std::endl;
379    }
380
381    /**
382        @brief This function gets called if the main state name of the object changes.
383    */
384    void BaseObject::changedMainStateName()
385    {
386        this->mainStateFunctor_ = 0;
387
388        if (this->mainStateName_ != "")
389        {
390            this->registerEventStates();
391           
392            std::map<std::string, EventState*>::const_iterator it = this->eventStates_.find(this->mainStateName_);
393            if (it != this->eventStates_.end() && it->second->getFunctor())
394            {
395                if (it->second->getFunctor()->getParamCount() <= 1)
396                    this->mainStateFunctor_ = it->second->getFunctor();
397                else
398                    COUT(2) << "Warning: Can't use \"" << this->mainStateName_ << "\" as MainState because it needs a second argument." << std::endl;
399            }
400            else
401                COUT(2) << "Warning: \"" << this->mainStateName_ << "\" is not a valid MainState." << std::endl;
402        }
403    }
404   
405    /**
406        @brief Calls XMLEventPort with an empty XML-element to register the event states if necessary.
407    */
408    void BaseObject::registerEventStates()
409    {
410        if (!this->bRegisteredEventStates_)
411        {
412            Element xmlelement;
413            this->XMLEventPort(xmlelement, XMLPort::NOP);
414        }
415    }
416   
417    /**
418        @brief Manually loads all event states, even if the class doesn't officially support them. This is needed by some classes like @ref EventDispatcher or @ref EventTarget.
419    */
420    void BaseObject::loadAllEventStates(Element& xmlelement, XMLPort::Mode mode, BaseObject* object, Identifier* identifier)
421    {
422        Element* events = xmlelement.FirstChildElement("events", false);
423        if (events)
424        {
425            // get the list of all states present
426            std::list<std::string> eventnames;
427            if (mode == XMLPort::LoadObject || mode == XMLPort::ExpandObject)
428            {
429                for (ticpp::Iterator<ticpp::Element> child = events->FirstChildElement(false); child != child.end(); child++)
430                    eventnames.push_back(child->Value());
431            }
432            else if (mode == XMLPort::SaveObject)
433            {
434            }
435
436            // iterate through all states and get the event sources
437            for (std::list<std::string>::iterator it = eventnames.begin(); it != eventnames.end(); ++it)
438            {
439                std::string statename = (*it);
440
441                // if the event state is already known, continue with the next state
442                orxonox::EventState* eventstate = object->getEventState(statename);
443                if (eventstate)
444                    continue;
445
446                XMLPortClassObjectContainer<BaseObject, BaseObject>* container = (XMLPortClassObjectContainer<BaseObject, BaseObject>*)(identifier->getXMLPortObjectContainer(statename));
447                if (!container)
448                {
449                    ExecutorMember<BaseObject>* setfunctor = createExecutor(createFunctor(&BaseObject::addEventSource), std::string( "BaseObject" ) + "::" + "addEventSource" + "(" + statename + ")");
450                    ExecutorMember<BaseObject>* getfunctor = createExecutor(createFunctor(&BaseObject::getEventSource), std::string( "BaseObject" ) + "::" + "getEventSource" + "(" + statename + ")");
451                    setfunctor->setDefaultValue(1, statename);
452                    getfunctor->setDefaultValue(1, statename);
453
454                    container = new XMLPortClassObjectContainer<BaseObject, BaseObject>(statename, identifier, setfunctor, getfunctor, false, true);
455                    identifier->addXMLPortObjectContainer(statename, container);
456                }
457                container->port(object, *events, mode);
458            }
459        }
460    }
461}
Note: See TracBrowser for help on using the repository browser.