Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/modules/objects/triggers/MultiTrigger.cc @ 6853

Last change on this file since 6853 was 6853, checked in by dafrick, 14 years ago

Started documenting, also changed some of the implementation to avoid possible bugs.

File size: 19.1 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 *      Damian 'Mozork' Frick
24 *   Co-authors:
25 *      Benjamin Knecht
26 *
27*/
28
29#include "MultiTrigger.h"
30
31#include "core/CoreIncludes.h"
32#include "core/XMLPort.h"
33
34#include "MultiTriggerContainer.h"
35
36namespace orxonox
37{
38
39    // Initialization of some static (magic) variables.
40    /*static*/ const int MultiTrigger::INF_s = -1;
41    /*static*/ const std::string MultiTrigger::or_s = "or";
42    /*static*/ const std::string MultiTrigger::and_s = "and";
43    /*static*/ const std::string MultiTrigger::xor_s = "xor";
44   
45    CreateFactory(MultiTrigger);
46
47    /**
48    @brief
49        Constructor. Registers the objects and initializes default values.
50    @param creator
51        The creator.
52    */
53    //TODO: Clean up.
54    MultiTrigger::MultiTrigger(BaseObject* creator) : StaticEntity(creator)
55    {
56        RegisterObject(MultiTrigger);
57
58        this->bFirstTick_ = true;
59       
60        this->mode_ = MultiTriggerMode::EventTriggerAND;
61
62        this->bInvertMode_ = false;
63        this->bSwitch_ = false;
64        this->bStayActive_ = false;
65        this->delay_ = 0.0f;
66        this->remainingActivations_ = INF_s;
67        this->maxNumSimultaniousTriggerers_ = INF_s;
68       
69        this->targetMask_.exclude(Class(BaseObject));
70
71        this->setSyncMode(0x0);
72       
73    }
74   
75    /**
76    @brief
77        Destructor. Cleans up the state queue.
78    */
79    MultiTrigger::~MultiTrigger()
80    {
81        COUT(4) << "Destorying MultiTrigger &" << this << ". " << this->stateQueue_.size() << " states still in queue. Deleting." << std::endl;
82        while(this->stateQueue_.size() > 0)
83        {
84            MultiTriggerState* state = this->stateQueue_.front().second;
85            this->stateQueue_.pop_front();
86            delete state;
87        }
88
89        // Destroying the appended triggers
90        for(std::set<MultiTrigger*>::iterator it = this->children_.begin(); it != this->children_.end(); it++)
91        {
92            (*it)->destroy();
93        }
94        this->children_.clear();
95
96        // Telling everyone, that this trigger is gone.
97        for(std::set<BaseObject*>::iterator it = this->active_.begin(); it != this->active_.end(); it++)
98        {
99            this->fire(false, *it);
100        }
101        this->active_.clear();
102    }
103   
104    /**
105    @brief
106        Method for creating a MultiTrigger object through XML.
107        For a detailed description of the parameters please see the class description in the header file.
108    */
109    void MultiTrigger::XMLPort(Element& xmlelement, XMLPort::Mode mode)
110    {
111        SUPER(MultiTrigger, XMLPort, xmlelement, mode);
112
113        XMLPortParam(MultiTrigger, "delay", setDelay, getDelay, xmlelement, mode).defaultValues(0.0f);
114        XMLPortParam(MultiTrigger, "switch", setSwitch, getSwitch, xmlelement, mode).defaultValues(false);
115        XMLPortParam(MultiTrigger, "stayactive", setStayActive, getStayActive, xmlelement, mode).defaultValues(false);
116        XMLPortParam(MultiTrigger, "activations", setActivations, getActivations, xmlelement, mode).defaultValues(INF_s);
117        XMLPortParam(MultiTrigger, "invert", setInvert, getInvert, xmlelement, mode).defaultValues(false);
118        XMLPortParam(MultiTrigger, "simultaniousTriggerers", setSimultaniousTriggerers, getSimultaniousTriggerers, xmlelement, mode).defaultValues(INF_s);
119        XMLPortParamTemplate(MultiTrigger, "mode", setMode, getModeString, xmlelement, mode, const std::string&).defaultValues(MultiTrigger::and_s);
120        XMLPortParamLoadOnly(MultiTrigger, "target", addTargets, xmlelement, mode).defaultValues("ControllableEntity"); //TODO: Remove load only
121
122        //TODO: Maybe nicer with explicit subgroup, e.g. triggers
123        XMLPortObject(MultiTrigger, MultiTrigger, "", addTrigger, getTrigger, xmlelement, mode);
124       
125        COUT(4) << "MultiTrigger '" << this->getName() << "' (&" << this << ") created." << std::endl;
126    }
127   
128
129    /**
130    @brief
131        A method that is executed each tick.
132    @param dt
133        The duration of the last tick.
134    */
135    void MultiTrigger::tick(float dt)
136    {
137        // If this is the first tick.
138        //TODO: Determine need for this, else kick it out.
139        if(this->bFirstTick_)
140        {
141            this->bFirstTick_ = false;
142            this->fire(false);
143        }
144
145        // Check if the object is active (this is NOT MultiTrigger::isActive()!)
146        if (!this->BaseObject::isActive())
147            return;
148
149        SUPER(MultiTrigger, tick, dt);
150
151        // Let the MultiTrigger return the states that trigger and process the new states if there are any.
152        std::queue<MultiTriggerState*>* queue  = this->letTrigger();
153        if(queue != NULL)
154        {
155            while(queue->size() > 0)
156            {
157                //TODO: Be more efficient, Don't delete a state and create a new one immediately after that. Reuse!
158                MultiTriggerState* state = queue->front();
159                // If the state is NULL. (This really shouldn't happen)
160                if(state == NULL)
161                {
162                    COUT(1) << "In MultiTrigger '" << this->getName() << "' (&" << this << "), Error: State of new states queue was NULL." << std::endl;
163                    queue->pop();
164                    continue;
165                }
166
167                // The new triggered state dependent on the requested state, the mode and the invert-mode.
168                bool bTriggered = (state->bTriggered & this->isModeTriggered(state->originator)) ^ this->bInvertMode_;
169
170                // If the 'triggered' state has changed a new state is added to the state queue.
171                //TODO: Do something against flooding, when there is delay.
172                if(this->delay_ != 0.0f || bTriggered ^ this->isTriggered(state->originator))
173                    this->addState(bTriggered, state->originator);
174               
175                queue->pop();
176                delete state;
177            }
178            delete queue;
179        }
180
181        // Go through the state queue.
182        if (this->stateQueue_.size() > 0)
183        {
184            MultiTriggerState* state;
185            float timeRemaining;
186           
187            // Go through all pending states.
188            for(int size = this->stateQueue_.size(); size >= 1; size--)
189            {
190                timeRemaining = this->stateQueue_.front().first;
191                state = this->stateQueue_.front().second;
192
193                // If the remaining time has expired, the state has to be set as the state of the MultiTrigger.
194                if(timeRemaining <= dt)
195                {
196                    // If the maximum number of objects simultaniously triggering this MultiTrigger is not exceeded.
197                    if(this->maxNumSimultaniousTriggerers_ == INF_s || this->triggered_.size() < (unsigned int)this->maxNumSimultaniousTriggerers_)
198                    {
199                        bool bStateChanged = false;
200                        // If the 'triggered' state is different form what it is now, change it.
201                        if(state->bTriggered ^ this->isTriggered(state->originator))
202                        {
203                            // Add the originator to the objects triggering this MultiTrigger.
204                            if(state->bTriggered == true)
205                            {
206                                this->triggered_.insert(state->originator);
207                            }
208                            // Remove the originator from the objects triggering this MultiTrigger.
209                            else
210                            {
211                                this->triggered_.erase(state->originator);
212                            }
213                           
214                            bStateChanged = true;
215                        }
216
217                        // If the activity is different from what it is now, change it and fire an Event.
218                        if(state->bActive ^ this->isActive(state->originator))
219                        {
220
221                            bool bFire = true;
222                           
223                            // Add the originator to the objects activating this MultiTrigger.
224                            if(state->bActive == true)
225                            {
226                                if(this->remainingActivations_ != 0)
227                                {
228                                    this->active_.insert(state->originator);
229                                    if(this->remainingActivations_ != INF_s)
230                                        this->remainingActivations_--; // Decrement the remaining activations.
231                                }
232                                else
233                                {
234                                    bFire = false;
235                                }
236                            }
237                            // Remove the originator from the objects activating this MultiTrigger.
238                            else
239                            {
240                                if(!this->bStayActive_ || this->remainingActivations_ != 0)
241                                {
242                                    this->active_.erase(state->originator);
243                                }
244                                else
245                                {
246                                    bFire = false;
247                                }
248                            }
249
250                            // Fire the Event if the activity has changed.
251                            if(bFire)
252                            {
253                                this->fire(state->bActive, state->originator);
254                                bStateChanged = true;
255                            }
256                        }
257
258                        // Print some debug output if the state has changed.
259                        if(bStateChanged)
260                            COUT(4) << "MultiTrigger '" << this->getName() << "' (&" << this << ") changed state. originator: " << state->originator->getIdentifier()->getName() << " (&" << state->originator << "), active: " << state->bActive << ", triggered: " << state->bTriggered << "." << std::endl;
261
262                        // If the MultiTrigger has exceeded its amount of activations and it desn't stay active, it has to be destroyed,
263                        if(this->remainingActivations_ == 0 && bStateChanged && !state->bActive && !this->bStayActive_)
264                        {
265                            this->destroy();
266                        }
267                    }
268                   
269                    // Remove the state from the state queue.
270                    this->stateQueue_.pop_front();
271                    delete state;
272                    size -= 1;
273                }
274                // If the remaining time has not yet expired. Decrement the remainig time.
275                else
276                {
277                    this->stateQueue_.push_back(std::pair<float, MultiTriggerState*>(timeRemaining-dt, state));
278                    this->stateQueue_.pop_front();
279                }
280            }
281        }
282    }
283
284    //TODO: Document
285    bool MultiTrigger::isModeTriggered(BaseObject* triggerer)
286    {
287        if (this->children_.size() != 0)
288        {
289            bool returnVal = false;
290
291            switch (this->mode_)
292            {
293                case MultiTriggerMode::EventTriggerAND:
294                    returnVal = checkAnd(triggerer);
295                    break;
296                case MultiTriggerMode::EventTriggerOR:
297                    returnVal = checkOr(triggerer);
298                    break;
299                case MultiTriggerMode::EventTriggerXOR:
300                    returnVal = checkXor(triggerer);
301                    break;
302                default:
303                    returnVal = false;
304                    break;
305            }
306
307            return returnVal;
308        }
309       
310        return true;
311    }
312   
313    //TODO: Document
314    std::queue<MultiTriggerState*>* MultiTrigger::letTrigger(void)
315    {
316        // Goes through all trigger children and gets the objects triggering them.
317        std::set<BaseObject*>* triggerers = new std::set<BaseObject*>();
318        std::set<BaseObject*>::iterator objIt;
319        for(std::set<MultiTrigger*>::iterator it = this->children_.begin(); it != this->children_.end(); it ++)
320        {
321            std::set<BaseObject*> set = (*it)->getActive();
322            for(objIt = set.begin(); objIt != set.end(); objIt++)
323            {
324                triggerers->insert(*objIt);
325            }
326        }
327
328        // Goes through all the triggerers of this trigger.
329        for(objIt = this->active_.begin(); objIt != this->active_.end(); objIt++)
330        {
331            triggerers->insert(*objIt);
332        }
333
334        if(triggerers->size() == 0)
335            return NULL;
336       
337        std::queue<MultiTriggerState*>* queue = new std::queue<MultiTriggerState*>();
338        MultiTriggerState* state = NULL;
339        for(std::set<BaseObject*>::iterator it = triggerers->begin(); it != triggerers->end(); it++)
340        {
341            state = new MultiTriggerState;
342            state->bTriggered = true;
343            state->originator = *it;
344            queue->push(state);
345        }
346        delete triggerers;
347       
348        return queue;
349    }
350   
351    //TODO: Document
352    bool MultiTrigger::isTriggered(BaseObject* triggerer)
353    {
354        std::set<BaseObject*>::iterator it = this->triggered_.find(triggerer);
355        if(it == this->triggered_.end())
356            return false;
357        return true;
358    }
359
360    //TODO: Document
361    bool MultiTrigger::checkAnd(BaseObject* triggerer)
362    {
363        std::set<MultiTrigger*>::iterator it;
364        for(it = this->children_.begin(); it != this->children_.end(); ++it)
365        {
366            if (!(*it)->isActive(triggerer))
367                return false;
368        }
369        return true;
370    }
371
372    //TODO: Document
373    bool MultiTrigger::checkOr(BaseObject* triggerer)
374    {
375        std::set<MultiTrigger*>::iterator it;
376        for(it = this->children_.begin(); it != this->children_.end(); ++it)
377        {
378            if ((*it)->isActive(triggerer))
379                return true;
380        }
381        return false;
382    }
383
384    //TODO: Document
385    bool MultiTrigger::checkXor(BaseObject* triggerer)
386    {
387        std::set<MultiTrigger*>::iterator it;
388        bool test = false;
389        for(it = this->children_.begin(); it != this->children_.end(); ++it)
390        {
391            if (test && (*it)->isActive(triggerer))
392                return false;
393            if ((*it)->isActive(triggerer))
394                test = true;
395        }
396        return test;
397    }
398   
399    //TODO: Document
400    bool MultiTrigger::addState(bool bTriggered, BaseObject* originator)
401    {
402        if(!this->isTarget(originator))
403            return false;
404       
405        // If the state doesn't change.
406        if(this->isTriggered() && bTriggered)
407            return false;
408       
409        bool bActive = !this->isActive(originator);
410       
411        // If the MultiTrigger is in switch mode.
412        if(this->bSwitch_ && !bTriggered)
413        {
414            bActive = this->isActive(originator);
415        }
416       
417        // Create state.
418        MultiTriggerState* state = new MultiTriggerState;
419        state->bActive = bActive;
420        state->bTriggered = bTriggered;
421        state->originator = originator;
422        this->stateQueue_.push_back(std::pair<float, MultiTriggerState*>(this->delay_, state));
423       
424        return true;
425    }
426
427    //TODO: Document
428    void MultiTrigger::setMode(const std::string& modeName)
429    {
430        if (modeName == MultiTrigger::and_s)
431            this->setMode(MultiTriggerMode::EventTriggerAND);
432        else if (modeName == MultiTrigger::or_s)
433            this->setMode(MultiTriggerMode::EventTriggerOR);
434        else if (modeName == MultiTrigger::xor_s)
435            this->setMode(MultiTriggerMode::EventTriggerXOR);
436    }
437
438    //TODO: Document
439    const std::string& MultiTrigger::getModeString() const
440    {
441        if (this->mode_ == MultiTriggerMode::EventTriggerAND)
442            return MultiTrigger::and_s;
443        else if (this->mode_ == MultiTriggerMode::EventTriggerOR)
444            return MultiTrigger::or_s;
445        else if (this->mode_ == MultiTriggerMode::EventTriggerXOR)
446            return MultiTrigger::xor_s;
447        else
448            return MultiTrigger::and_s;
449    }
450   
451    //TODO: Document
452    bool MultiTrigger::isActive(BaseObject* triggerer)
453    {
454        std::set<BaseObject*>::iterator it = this->active_.find(triggerer);
455        if(it == this->active_.end())
456            return false;
457        return true;
458    }
459
460    //TODO: Document
461    void MultiTrigger::addTrigger(MultiTrigger* trigger)
462    {
463        if (this != trigger && trigger != NULL)
464            this->children_.insert(trigger);
465    }
466
467    //TODO: Document
468    const MultiTrigger* MultiTrigger::getTrigger(unsigned int index) const
469    {
470        if (this->children_.size() <= index)
471            return NULL;
472
473        std::set<MultiTrigger*>::const_iterator it;
474        it = this->children_.begin();
475
476        for (unsigned int i = 0; i != index; ++i)
477            ++it;
478
479        return (*it);
480    }
481   
482    //TODO: Document
483    void MultiTrigger::fire(bool status, BaseObject* originator)
484    {
485        if(originator == NULL)
486        {
487            this->fireEvent(status);
488            return;
489        }
490        MultiTriggerContainer* container = new MultiTriggerContainer(this, this, originator);
491        this->fireEvent(status, container);
492        COUT(4) << "MultiTrigger &" << this << ": Fired event. originator: " << originator->getIdentifier()->getName() << " (&" << originator << "), status: " << status << "." << std::endl;
493        delete container;
494    }
495   
496    //TODO: Document
497    void MultiTrigger::addTargets(const std::string& targets)
498    {
499        Identifier* target = ClassByString(targets);
500
501        if (target == NULL)
502        {
503            COUT(1) << "Error: \"" << targets << "\" is not a valid class name to include in ClassTreeMask (in " << this->getName() << ", class " << this->getIdentifier()->getName() << ')' << std::endl;
504            return;
505        }
506
507        this->targetMask_.include(target);
508
509        // trigger shouldn't react on itself or other triggers
510        this->targetMask_.exclude(Class(MultiTrigger), true);
511
512        // we only want WorldEntities
513        ClassTreeMask WEMask;
514        WEMask.include(Class(WorldEntity));
515        this->targetMask_ *= WEMask;
516
517        this->notifyMaskUpdate();
518    }
519
520    //TODO: Document
521    void MultiTrigger::removeTargets(const std::string& targets)
522    {
523        Identifier* target = ClassByString(targets);
524        this->targetMask_.exclude(target);
525    }
526   
527}
Note: See TracBrowser for help on using the repository browser.