Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 12018 was 11103, checked in by landauf, 9 years ago

fixed warnings (release build).
fixed error in GravityBombField

  • Property svn:eol-style set to native
File size: 17.9 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 "CoreIncludes.h"
39#include "Event.h"
40#include "EventIncludes.h"
41#include "Template.h"
42#include "XMLFile.h"
43#include "XMLNameListener.h"
44#include "XMLPort.h"
45#include "command/Functor.h"
46#include "object/Iterator.h"
47
48#include "class/OrxonoxInterface.h" // we include this only to include OrxonoxInterface.h at least once in core to keep MSVC happy...
49
50namespace orxonox
51{
52    RegisterClass(BaseObject);
53
54    /**
55        @brief Constructor: Registers the object in the BaseObject-list.
56    */
57    BaseObject::BaseObject(Context* context) : bInitialized_(false)
58    {
59        RegisterObject(BaseObject);
60
61        this->bInitialized_ = true;
62
63        this->bActive_ = true;
64        this->bVisible_ = true;
65        this->bRegisteredEventStates_ = false;
66
67        this->lastLoadedXMLElement_ = nullptr;
68
69        this->mainStateFunctor_ = nullptr;
70
71        if (context)
72            this->setContext(context);
73
74        BaseObject* creator = orxonox_cast<BaseObject*>(context);
75        this->setCreator(creator);
76        if (this->creator_)
77        {
78            this->setFile(this->creator_->getFile());
79
80            // store strong-pointers on all four base objects by default (can be overwritten with weak-ptr after the constructor if necessary)
81            this->setNamespace(this->creator_->namespace_.createStrongPtr());
82            this->setScene    (this->creator_->scene_    .createStrongPtr(), this->creator_->sceneID_);
83            this->setGametype (this->creator_->gametype_ .createStrongPtr());
84            this->setLevel    (this->creator_->level_    .createStrongPtr());
85        }
86        else
87        {
88            this->file_ = nullptr;
89            this->sceneID_ = OBJECTID_UNKNOWN;
90        }
91    }
92
93    /**
94        @brief Destructor
95    */
96    BaseObject::~BaseObject()
97    {
98        if (this->isInitialized())
99        {
100            for (std::map<BaseObject*, std::string>::const_iterator it = this->eventSources_.begin(); it != this->eventSources_.end(); )
101                this->removeEventSource((it++)->first);
102
103            for (std::set<BaseObject*>::const_iterator it = this->eventListeners_.begin(); it != this->eventListeners_.end(); )
104                (*(it++))->removeEventSource(this);
105
106            for (std::map<std::string, EventState*>::const_iterator it = this->eventStates_.begin(); it != this->eventStates_.end(); ++it)
107                delete it->second;
108        }
109    }
110
111    /** @brief Adds an object which listens to the events of this object. */
112    void BaseObject::registerEventListener(BaseObject* object)
113    {
114        orxout(verbose, context::events) << "New EventListener: " << object->getIdentifier()->getName() << " &(" << object << ")." << endl;
115        this->eventListeners_.insert(object);
116    }
117
118    /**
119        @brief XML loading and saving.
120        @param xmlelement The XML-element
121        @param mode The mode defines the operation that is being executed: loading or saving the object (from or to XML respectively)
122    */
123    void BaseObject::XMLPort(Element& xmlelement, XMLPort::Mode mode)
124    {
125        XMLPortParam(BaseObject, "name", setXMLName, getName, xmlelement, mode);
126        XMLPortParam(BaseObject, "visible", setVisible, isVisible, xmlelement, mode);
127        XMLPortParam(BaseObject, "active", setActive, isActive, xmlelement, mode);
128        XMLPortParam(BaseObject, "mainstate", setMainStateName, getMainStateName, xmlelement, mode);
129        XMLPortParamTemplate(BaseObject, "template", addTemplate, getSingleTemplate, xmlelement, mode, const std::string&);
130
131        XMLPortObjectTemplate(BaseObject, Template, "templates", addTemplate, getTemplate, xmlelement, mode, Template*);
132        XMLPortObject(BaseObject, BaseObject, "eventlisteners", addEventListener, getEventListener, xmlelement, mode);
133
134        Element* events = nullptr;
135        if (mode == XMLPort::LoadObject || mode == XMLPort::ExpandObject)
136            events = xmlelement.FirstChildElement("events", false);
137        else if (mode == XMLPort::SaveObject)
138            {}
139        if (events)
140            this->XMLEventPort(*events, mode);
141    }
142
143    /**
144        @brief Defines the possible event states of this object and parses eventsources from an XML file.
145        @param xmlelement The XML-element
146        @param mode The mode defines the operation that is being executed: loading or saving the object (from or to XML respectively)
147    */
148    void BaseObject::XMLEventPort(Element& xmlelement, XMLPort::Mode mode)
149    {
150        XMLPortEventState(BaseObject, BaseObject, "activity", setActive, xmlelement, mode);
151        XMLPortEventState(BaseObject, BaseObject, "visibility", setVisible, xmlelement, mode);
152        XMLPortEventState(BaseObject, BaseObject, "mainstate", setMainState, xmlelement, mode);
153
154        this->bRegisteredEventStates_ = true;
155    }
156
157    /**
158        @brief Loads the name of the object through XML and calls all XMLNameListener.
159        @param name The name of the object
160    */
161    void BaseObject::setXMLName(const std::string& name)
162    {
163        this->setName(name);
164
165        for (XMLNameListener* listener : ObjectList<XMLNameListener>())
166            listener->loadedNewXMLName(this);
167    }
168
169    /**
170        @brief Returns the levelfile that loaded this object.
171        @return The levelfile
172    */
173    const std::string& BaseObject::getFilename() const
174    {
175        if (this->file_)
176            return this->file_->getFilename();
177        else
178            return BLANKSTRING;
179    }
180
181    /**
182        @brief Adds a Template to the object.
183        @param name The name of the Template
184    */
185    void BaseObject::addTemplate(const std::string& name)
186    {
187        Template* temp = Template::getTemplate(name);
188        if (temp)
189            this->addTemplate(temp);
190        else
191            orxout(internal_error) << "\"" << name << "\" is not a valid Template name (in class: " << this->getIdentifier()->getName() << ", name: " << this->getName() << ")." << endl;
192    }
193
194    /**
195        @brief Adds a Template to the object.
196        @param temp The Template
197    */
198    void BaseObject::addTemplate(Template* temp)
199    {
200        // network
201        if (temp->isLink())
202        {
203            this->networkTemplateNames_.insert(temp->getLink());
204
205            Template* link;
206            assert(!(link = Template::getTemplate(temp->getLink())) || !link->isLink());
207            (void)link; // avoid compiler warning
208        }
209        else
210            this->networkTemplateNames_.insert(temp->getName());
211
212        // add template
213        this->templates_.insert(temp);
214        temp->applyOn(this);
215    }
216
217    /**
218        @brief Returns the name of the first Template.
219        @return The name as string.
220    */
221    const std::string& BaseObject::getSingleTemplate(void) const
222    {
223        if(this->templates_.empty())
224            return BLANKSTRING;
225
226        return (*this->templates_.begin())->getName();
227    }
228
229    /**
230        @brief Returns the Template with the given index.
231        @param index The index
232    */
233    Template* BaseObject::getTemplate(unsigned int index) const
234    {
235        unsigned int i = 0;
236        for (Template* temp : this->templates_)
237        {
238            if (i == index)
239                return temp;
240            i++;
241        }
242        return nullptr;
243    }
244
245    /**
246        @brief Adds a new event source for a specific state.
247        @param source The object which sends events to this object
248        @param state The state of this object which will be affected by the events
249    */
250    void BaseObject::addEventSource(BaseObject* source, const std::string& state)
251    {
252        this->eventSources_[source] = state;
253        source->registerEventListener(this);
254    }
255
256    /**
257        @brief Removes an eventsource (but doesn't unregister itself at the source).
258    */
259    void BaseObject::removeEventSource(BaseObject* source)
260    {
261        this->eventSources_.erase(source);
262        source->unregisterEventListener(this);
263    }
264
265    /**
266        @brief Returns an eventsource with a given index.
267    */
268    BaseObject* BaseObject::getEventSource(unsigned int index, const std::string& state) const
269    {
270        unsigned int i = 0;
271        for (const auto& mapEntry : this->eventSources_)
272        {
273            if (mapEntry.second != state)
274                continue;
275
276            if (i == index)
277                return mapEntry.first;
278            ++i;
279        }
280        return nullptr;
281    }
282
283    /**
284        @brief Adds an object which listens to the events of this object. The events are sent to the other objects mainstate.
285    */
286    void BaseObject::addEventListener(BaseObject* listener)
287    {
288        this->eventListenersXML_.insert(listener);
289        listener->addEventSource(this, "mainstate");
290    }
291
292    /**
293        @brief Returns an event listener with a given index.
294    */
295    BaseObject* BaseObject::getEventListener(unsigned int index) const
296    {
297        unsigned int i = 0;
298        for (BaseObject* listener : this->eventListenersXML_)
299        {
300            if (i == index)
301                return listener;
302            ++i;
303        }
304        return nullptr;
305    }
306
307    /**
308        @brief Adds a new event-state to the object. Event-states are states which can be changed by events.
309        @param name  The name of the event
310        @param state The object containing information about the event-state
311    */
312    void BaseObject::addEventState(const std::string& name, EventState* state)
313    {
314        std::map<std::string, EventState*>::const_iterator it = this->eventStates_.find(name);
315        if (it != this->eventStates_.end())
316        {
317            orxout(internal_warning, context::events) << "Overwriting EventState in class " << this->getIdentifier()->getName() << '.' << endl;
318            delete (it->second);
319        }
320
321        this->eventStates_[name] = state;
322    }
323
324    /**
325        @brief Returns the event-state with the given name.
326    */
327    EventState* BaseObject::getEventState(const std::string& name) const
328    {
329        std::map<std::string, EventState*>::const_iterator it = this->eventStates_.find(name);
330        if (it != this->eventStates_.end())
331            return (it->second);
332        else
333            return nullptr;
334    }
335
336    /**
337        @brief Fires an event (without a state).
338    */
339    void BaseObject::fireEvent(const std::string& name)
340    {
341        this->fireEvent(true, name);
342        this->fireEvent(false, name);
343    }
344
345    /**
346        @brief Fires an event which activates or deactivates a state.
347    */
348    void BaseObject::fireEvent(bool activate, const std::string& name)
349    {
350        this->fireEvent(activate, this, name);
351    }
352
353    /**
354        @brief Fires an event which activates or deactivates a state with agiven originator (the object which triggered the event).
355    */
356    void BaseObject::fireEvent(bool activate, BaseObject* originator, const std::string& name)
357    {
358        Event event(activate, originator, name);
359
360        for (BaseObject* listener : this->eventListeners_)
361        {
362            event.statename_ = listener->eventSources_[this];
363            listener->processEvent(event);
364        }
365    }
366
367    /**
368        @brief Fires an event, using the Event struct.
369    */
370    void BaseObject::fireEvent(Event& event)
371    {
372        for (BaseObject* listener : this->eventListeners_)
373            listener->processEvent(event);
374    }
375
376    /**
377        @brief Processing an event by calling the right main state.
378        @param event The event struct which contains the information about the event
379    */
380    void BaseObject::processEvent(Event& event)
381    {
382        this->registerEventStates();
383
384        orxout(verbose, context::events) << this->getIdentifier()->getName() << " (&" << this << ") processing event. originator: " << event.originator_->getIdentifier()->getName() << " (&" << event.originator_ << "), activate: " << event.activate_ << ", name: " << event.name_ << ", statename: " << event.statename_ << "." << endl;
385
386        std::map<std::string, EventState*>::const_iterator it = this->eventStates_.find(event.statename_);
387        if (it != this->eventStates_.end())
388            it->second->process(event, this);
389        else if (!event.statename_.empty())
390            orxout(internal_warning, context::events) << "\"" << event.statename_ << "\" is not a valid state in object \"" << this->getName() << "\" of class " << this->getIdentifier()->getName() << "." << endl;
391        else
392            orxout(internal_warning, context::events) << "Event with invalid source sent to object \"" << this->getName() << "\" of class " << this->getIdentifier()->getName() << "." << endl;
393    }
394
395    /**
396        @brief Sets the main state of the object to a given boolean value.
397
398        Note: The main state of an object can be set with the @ref setMainStateName function.
399        It's part of the eventsystem and used for event forwarding (when the target object can't specify a specific state,
400        the main state is used by default).
401    */
402    void BaseObject::setMainState(bool state)
403    {
404        if (this->mainStateFunctor_)
405        {
406            if (this->mainStateFunctor_->getParamCount() == 0)
407            {
408                if (state)
409                    (*this->mainStateFunctor_)();
410            }
411            else
412            {
413                (*this->mainStateFunctor_)(state);
414            }
415        }
416        else
417            orxout(internal_warning, context::events) << "No MainState defined in object \"" << this->getName() << "\" (" << this->getIdentifier()->getName() << ")" << endl;
418    }
419
420    /**
421        @brief This function gets called if the main state name of the object changes.
422    */
423    void BaseObject::changedMainStateName()
424    {
425        this->mainStateFunctor_ = nullptr;
426
427        if (!this->mainStateName_.empty())
428        {
429            this->registerEventStates();
430
431            std::map<std::string, EventState*>::const_iterator it = this->eventStates_.find(this->mainStateName_);
432            if (it != this->eventStates_.end() && it->second->getFunctor())
433            {
434                if (it->second->getFunctor()->getParamCount() <= 1)
435                    this->mainStateFunctor_ = it->second->getFunctor();
436                else
437                    orxout(internal_warning, context::events) << "Can't use \"" << this->mainStateName_ << "\" as MainState because it needs a second argument." << endl;
438            }
439            else
440                orxout(internal_warning, context::events) << "\"" << this->mainStateName_ << "\" is not a valid MainState." << endl;
441        }
442    }
443
444    /**
445        @brief Calls XMLEventPort with an empty XML-element to register the event states if necessary.
446    */
447    void BaseObject::registerEventStates()
448    {
449        if (!this->bRegisteredEventStates_)
450        {
451            Element xmlelement;
452            this->XMLEventPort(xmlelement, XMLPort::NOP);
453        }
454    }
455
456    /**
457        @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.
458    */
459    void BaseObject::loadAllEventStates(Element& xmlelement, XMLPort::Mode mode, BaseObject* object, Identifier* identifier)
460    {
461        Element* events = xmlelement.FirstChildElement("events", false);
462        if (events)
463        {
464            // get the list of all states present
465            std::list<std::string> eventnames;
466            if (mode == XMLPort::LoadObject || mode == XMLPort::ExpandObject)
467            {
468                for (ticpp::Iterator<ticpp::Element> child = events->FirstChildElement(false); child != child.end(); child++)
469                    eventnames.push_back(child->Value());
470            }
471            else if (mode == XMLPort::SaveObject)
472            {
473            }
474
475            // iterate through all states and get the event sources
476            for (const std::string& statename : eventnames)
477            {
478                // if the event state is already known, continue with the next state
479                orxonox::EventState* eventstate = object->getEventState(statename);
480                if (eventstate)
481                    continue;
482
483                XMLPortClassObjectContainer<BaseObject, BaseObject>* container = (XMLPortClassObjectContainer<BaseObject, BaseObject>*)(identifier->getXMLPortObjectContainer(statename));
484                if (!container)
485                {
486                    const ExecutorMemberPtr<BaseObject>& setfunctor = createExecutor(createFunctor(&BaseObject::addEventSource), std::string( "BaseObject" ) + "::" + "addEventSource" + '(' + statename + ')');
487                    const ExecutorMemberPtr<BaseObject>& getfunctor = createExecutor(createFunctor(&BaseObject::getEventSource), std::string( "BaseObject" ) + "::" + "getEventSource" + '(' + statename + ')');
488                    setfunctor->setDefaultValue(1, statename);
489                    getfunctor->setDefaultValue(1, statename);
490
491                    container = new XMLPortClassObjectContainer<BaseObject, BaseObject>(statename, identifier, setfunctor, getfunctor, false, true);
492                    identifier->addXMLPortObjectContainer(statename, container);
493                }
494                container->port(object, *events, mode);
495            }
496        }
497    }
498}
Note: See TracBrowser for help on using the repository browser.