Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/archive/tutorial3/src/libraries/core/BaseObject.cc @ 12373

Last change on this file since 12373 was 8858, checked in by landauf, 13 years ago

merged output branch back to trunk.

Changes:

  • you have to include util/Output.h instead of util/Debug.h
  • COUT(x) is now called orxout(level)
  • output levels are now defined by an enum instead of numbers. see util/Output.h for the definition
  • it's possible to use output contexts with orxout(level, context). see util/Output.h for some common contexts. you can define more contexts
  • you must use 'endl' at the end of an output message, '\n' does not flush the message

Output levels:

  • instead of COUT(0) use orxout()
  • instead of COUT(1) use orxout(user_error) or orxout(internal_error)
  • instead of COUT(2) use orxout(user_warning) or orxout(internal_warning)
  • instead of COUT(3) use orxout(user_status/user_info) or orxout(internal_status/internal_info)
  • instead of COUT(4) use orxout(verbose)
  • instead of COUT(5) use orxout(verbose_more)
  • instead of COUT(6) use orxout(verbose_ultra)

Guidelines:

  • user_* levels are for the user, visible in the console and the log-file
  • internal_* levels are for developers, visible in the log-file
  • verbose_* levels are for debugging, only visible if the context of the output is activated

Usage in C++:

  • orxout() << "message" << endl;
  • orxout(level) << "message" << endl;
  • orxout(level, context) << "message" << endl;

Usage in Lua:

  • orxout("message")
  • orxout(orxonox.level.levelname, "message")
  • orxout(orxonox.level.levelname, "context", "message")

Usage in Tcl (and in the in-game-console):

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