Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Resolved a bug in MultiTriggerContainer that caused the MultiTriggerContainer not to be identified as a BaseObject. Also some performance optimization in MultiTrigger.

File size: 22.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/**
30    @file MultiTrigger.cc
31    @brief Implementation of the MultiTrigger class.
32*/
33
34#include "MultiTrigger.h"
35
36#include "core/CoreIncludes.h"
37#include "core/XMLPort.h"
38
39#include "MultiTriggerContainer.h"
40
41namespace orxonox
42{
43
44    // Initialization of some static (magic) variables.
45    /*static*/ const int MultiTrigger::INF_s = -1;
46    /*static*/ const std::string MultiTrigger::and_s = "and";
47    /*static*/ const std::string MultiTrigger::or_s = "or";
48    /*static*/ const std::string MultiTrigger::xor_s = "xor";
49   
50    CreateFactory(MultiTrigger);
51
52    /**
53    @brief
54        Constructor. Registers the objects and initializes default values.
55    @param creator
56        The creator.
57    */
58    MultiTrigger::MultiTrigger(BaseObject* creator) : StaticEntity(creator)
59    {
60        RegisterObject(MultiTrigger);
61
62        this->bFirstTick_ = true;
63
64        this->delay_ = 0.0f;
65        this->bSwitch_ = false;
66        this->bStayActive_ = false;
67       
68        this->remainingActivations_ = INF_s;
69        this->maxNumSimultaniousTriggerers_ = INF_s;
70
71        this->bInvertMode_ = false;
72        this->mode_ = MultiTriggerMode::EventTriggerAND;
73
74        this->parentTrigger_ = NULL;
75       
76        this->targetMask_.exclude(Class(BaseObject));
77
78        this->setSyncMode(0x0);
79    }
80   
81    /**
82    @brief
83        Destructor. Cleans up the state queue.
84    */
85    MultiTrigger::~MultiTrigger()
86    {
87        COUT(4) << "Destorying MultiTrigger &" << this << ". " << this->stateQueue_.size() << " states still in queue. Deleting." << std::endl;
88        while(this->stateQueue_.size() > 0)
89        {
90            MultiTriggerState* state = this->stateQueue_.front().second;
91            this->stateQueue_.pop_front();
92            delete state;
93        }
94    }
95   
96    /**
97    @brief
98        Method for creating a MultiTrigger object through XML.
99        For a detailed description of the parameters please see the class description in the header file.
100    */
101    void MultiTrigger::XMLPort(Element& xmlelement, XMLPort::Mode mode)
102    {
103        SUPER(MultiTrigger, XMLPort, xmlelement, mode);
104
105        XMLPortParam(MultiTrigger, "delay", setDelay, getDelay, xmlelement, mode);
106        XMLPortParam(MultiTrigger, "switch", setSwitch, getSwitch, xmlelement, mode);
107        XMLPortParam(MultiTrigger, "stayactive", setStayActive, getStayActive, xmlelement, mode);
108        XMLPortParam(MultiTrigger, "activations", setActivations, getActivations, xmlelement, mode);
109        XMLPortParam(MultiTrigger, "simultaniousTriggerers", setSimultaniousTriggerers, getSimultaniousTriggerers, xmlelement, mode);
110        XMLPortParam(MultiTrigger, "invert", setInvert, getInvert, xmlelement, mode);
111        XMLPortParamTemplate(MultiTrigger, "mode", setMode, getModeString, xmlelement, mode, const std::string&);
112        XMLPortParamLoadOnly(MultiTrigger, "target", addTargets, xmlelement, mode).defaultValues("Pawn"); //TODO: Remove load only
113
114        //TODO: Maybe nicer with explicit subgroup, e.g. triggers
115        XMLPortObject(MultiTrigger, MultiTrigger, "", addTrigger, getTrigger, xmlelement, mode);
116       
117        COUT(4) << "MultiTrigger '" << this->getName() << "' (&" << this << ") created." << std::endl;
118    }
119   
120
121    /**
122    @brief
123        A method that is executed each tick.
124    @param dt
125        The duration of the last tick.
126    */
127    void MultiTrigger::tick(float dt)
128    {
129        // If this is the first tick.
130        //TODO: Determine need for this, else kick it out.
131        if(this->bFirstTick_)
132        {
133            this->bFirstTick_ = false;
134            this->fire(false);
135        }
136
137        // Check if the object is active (this is NOT MultiTrigger::isActive()!)
138        if (!this->BaseObject::isActive())
139            return;
140
141        SUPER(MultiTrigger, tick, dt);
142
143        // Let the MultiTrigger return the states that trigger and process the new states if there are any.
144        std::queue<MultiTriggerState*>* queue  = this->letTrigger();
145        if(queue != NULL)
146        {
147            while(queue->size() > 0)
148            {
149                MultiTriggerState* state = queue->front();
150                // If the state is NULL. (This really shouldn't happen)
151                if(state == NULL)
152                {
153                    COUT(1) << "In MultiTrigger '" << this->getName() << "' (&" << this << "), Error: State of new states queue was NULL." << std::endl;
154                    queue->pop();
155                    continue;
156                }
157
158                // The new triggered state dependent on the requested state, the mode and the invert-mode.
159                bool bTriggered = (state->bTriggered & this->isModeTriggered(state->originator)) ^ this->bInvertMode_;
160
161                // If the 'triggered' state has changed a new state is added to the state queue.
162                //TODO: Do something against flooding, when there is delay.
163                if(this->delay_ != 0.0f || bTriggered ^ this->isTriggered(state->originator))
164                {
165                    state->bTriggered = bTriggered;
166                    this->addState(state);
167                }
168                else
169                {
170                    COUT(1) << "BUH" << std::endl;
171                    delete state;
172                }
173               
174                queue->pop();
175            }
176            delete queue;
177        }
178
179        // Go through the state queue and activate all pending states whose remaining time has expired.
180        if (this->stateQueue_.size() > 0)
181        {
182            MultiTriggerState* state;
183            float timeRemaining;
184           
185            // Go through all pending states.
186            for(int size = this->stateQueue_.size(); size >= 1; size--)
187            {
188                timeRemaining = this->stateQueue_.front().first;
189                state = this->stateQueue_.front().second;
190
191                // If the remaining time has expired, the state has to be set as the state of the MultiTrigger.
192                if(timeRemaining <= dt)
193                {
194                    // If the maximum number of objects simultaniously triggering this MultiTrigger is not exceeded.
195                    if(this->maxNumSimultaniousTriggerers_ == INF_s || this->triggered_.size() < (unsigned int)this->maxNumSimultaniousTriggerers_)
196                    {
197                        bool bStateChanged = false;
198                        // If the 'triggered' state is different form what it is now, change it.
199                        if(state->bTriggered ^ this->isTriggered(state->originator))
200                        {
201                            // Add the originator to the objects triggering this MultiTrigger.
202                            if(state->bTriggered == true)
203                            {
204                                this->triggered_.insert(state->originator);
205                            }
206                            // Remove the originator from the objects triggering this MultiTrigger.
207                            else
208                            {
209                                this->triggered_.erase(state->originator);
210                            }
211                           
212                            bStateChanged = true;
213                        }
214
215                        // Get the activity of the new state.
216                        bool bActive;
217                        // If the MultiTrigger is in switch mode.
218                        if(this->bSwitch_ && !state->bTriggered)
219                            bActive = this->isActive(state->originator);
220                        else
221                            bActive = !this->isActive(state->originator);
222
223                        // If the activity is different from what it is now, change it and fire an Event.
224                        if(bActive ^ this->isActive(state->originator))
225                        {
226
227                            bool bFire = true;
228                           
229                            // Add the originator to the objects activating this MultiTrigger.
230                            if(bActive == true)
231                            {
232                                if(this->remainingActivations_ != 0)
233                                {
234                                    this->active_.insert(state->originator);
235                                    if(this->remainingActivations_ != INF_s)
236                                        this->remainingActivations_--; // Decrement the remaining activations.
237                                }
238                                else
239                                {
240                                    bFire = false;
241                                }
242                            }
243                            // Remove the originator from the objects activating this MultiTrigger.
244                            else
245                            {
246                                if(!this->bStayActive_ || this->remainingActivations_ != 0)
247                                {
248                                    this->active_.erase(state->originator);
249                                }
250                                else
251                                {
252                                    bFire = false;
253                                }
254                            }
255
256                            // Fire the Event if the activity has changed.
257                            if(bFire)
258                            {
259                                this->fire(bActive, state->originator);
260                                bStateChanged = true;
261                            }
262                        }
263
264                        // Print some debug output if the state has changed.
265                        if(bStateChanged)
266                        {
267                            COUT(4) << "MultiTrigger '" << this->getName() << "' (&" << this << ") changed state. originator: " << state->originator->getIdentifier()->getName() << " (&" << state->originator << "), active: " << bActive << ", triggered: " << state->bTriggered << "." << std::endl;
268                            if(this->parentTrigger_ != NULL)
269                                this->parentTrigger_->activityChanged(state->originator);
270                        }
271
272                        // If the MultiTrigger has exceeded its amount of activations and it doesn't stay active, it has to be destroyed,
273                        if(this->remainingActivations_ == 0 && !bActive)
274                        {
275                            this->BaseObject::setActive(false);
276                            COUT(4) << "MultiTrigger '" << this->getName() << "' (&" << this << ") ran out of activations. Setting it to inactive." << std::endl;
277                        }
278                    }
279                   
280                    // Remove the state from the state queue.
281                    this->stateQueue_.pop_front();
282                    delete state;
283                    size -= 1;
284                }
285                // If the remaining time has not yet expired. Decrement the remainig time.
286                else
287                {
288                    this->stateQueue_.push_back(std::pair<float, MultiTriggerState*>(timeRemaining-dt, state));
289                    this->stateQueue_.pop_front();
290                }
291            }
292        }
293    }
294
295    /**
296    @brief
297        Get whether the MultiTrigger is active for a given object.
298    @param triggerers
299        A pointer to the object.
300    @return
301        Returns true if the MultiTrigger is active, false if not.
302    */
303    bool MultiTrigger::isActive(BaseObject* triggerer)
304    {
305        std::set<BaseObject*>::iterator it = this->active_.find(triggerer);
306        if(it == this->active_.end())
307            return false;
308        return true;
309    }
310
311    /**
312    @brief
313        Set the mode of the MultiTrigger.
314    @param modeName
315        The name of the mode as a string.
316    */
317    void MultiTrigger::setMode(const std::string& modeName)
318    {
319        if (modeName == MultiTrigger::and_s)
320            this->setMode(MultiTriggerMode::EventTriggerAND);
321        else if (modeName == MultiTrigger::or_s)
322            this->setMode(MultiTriggerMode::EventTriggerOR);
323        else if (modeName == MultiTrigger::xor_s)
324            this->setMode(MultiTriggerMode::EventTriggerXOR);
325    }
326
327    /**
328    @brief
329        Get the mode of the MultiTrigger.
330    @return
331        Returns the mode as a string.
332    */
333    const std::string& MultiTrigger::getModeString() const
334    {
335        if (this->mode_ == MultiTriggerMode::EventTriggerAND)
336            return MultiTrigger::and_s;
337        else if (this->mode_ == MultiTriggerMode::EventTriggerOR)
338            return MultiTrigger::or_s;
339        else if (this->mode_ == MultiTriggerMode::EventTriggerXOR)
340            return MultiTrigger::xor_s;
341        else
342            return MultiTrigger::and_s;
343    }
344
345    /**
346    @brief
347        Add some target to the MultiTrigger.
348    @param targetStr
349        The target as a string.
350    */
351    void MultiTrigger::addTargets(const std::string& targetStr)
352    {
353        Identifier* target = ClassByString(targetStr);
354
355        if (target == NULL)
356        {
357            COUT(1) << "Error: \"" << targetStr << "\" is not a valid class name to include in ClassTreeMask (in " << this->getName() << ", class " << this->getIdentifier()->getName() << ')' << std::endl;
358            return;
359        }
360
361        this->targetMask_.include(target);
362
363        // A MultiTriggers shouldn't react to itself or other MultiTriggers.
364        this->targetMask_.exclude(Class(MultiTrigger), true);
365
366        // We only want WorldEntities
367        ClassTreeMask WEMask;
368        WEMask.include(Class(WorldEntity));
369        this->targetMask_ *= WEMask;
370
371        this->notifyMaskUpdate();
372    }
373
374    /**
375    @brief
376        Remove some target from the MultiTrigger.
377    @param targetStr
378        The target to be removed as a string.
379    */
380    void MultiTrigger::removeTargets(const std::string& targetStr)
381    {
382        Identifier* target = ClassByString(targetStr);
383        this->targetMask_.exclude(target);
384    }
385
386    /**
387    @brief
388        Adds a MultiTrigger as a sub-trigger to the trigger.
389        Beware: Loops are not prevented and potentially very bad, so just don't create any loops.
390    @param trigger
391        The trigger to be added.
392    */
393    void MultiTrigger::addTrigger(MultiTrigger* trigger)
394    {
395        if (this != trigger && trigger != NULL)
396            this->subTriggers_.insert(trigger);
397        trigger->addParentTrigger(this);
398    }
399
400    /**
401    @brief
402        Get the sub-trigger of this MultiTrigger at the given index.
403    @param index
404        The index.
405    @return
406        Returns a pointer ot the MultiTrigger. NULL if no such trigger exists.
407    */
408    const MultiTrigger* MultiTrigger::getTrigger(unsigned int index) const
409    {
410        // If the index is greater than the number of sub-triggers.
411        if (this->subTriggers_.size() <= index)
412            return NULL;
413
414        std::set<MultiTrigger*>::const_iterator it;
415        it = this->subTriggers_.begin();
416
417        for (unsigned int i = 0; i != index; ++i)
418            ++it;
419
420        return (*it);
421    }
422
423    /**
424    @brief
425        This method is called by the MultiTrigger to get information about new trigger events that need to be looked at.
426        This method is the device for the behaviour (the conditions under which the MultiTrigger triggers) of any derived class from MultiTrigger.
427
428        In this implementation it collects all objects triggering all sub-triggers nd the MultiTrigger itself and creates a state for each of them.
429    @return
430        A pointer to a queue of MultiTriggerState pointers is returned, containing all the neccessary information to decide whether these states should indeed become new states of the MultiTrigger.
431        Please be aware that both the queue and the states in the queue need to be deleted one they have been used. This is already done in the tick() method of this class but would have to be done by any method calling this method.
432    */
433    std::queue<MultiTriggerState*>* MultiTrigger::letTrigger(void)
434    {
435        return NULL;
436    }
437   
438    void MultiTrigger::activityChanged(BaseObject* originator)
439    {
440        MultiTriggerState* state = new MultiTriggerState;
441        state->bTriggered = (this->isTriggered(originator) & this->isModeTriggered(originator)) ^ this->bInvertMode_;
442        state->originator = originator;
443        this->addState(state);
444    }
445
446    /**
447    @brief
448        Checks whether the sub-triggers are in such a way that, according to the mode of the MultiTrigger, the MultiTrigger is triggered (only considering the sub-triggers, not the state of MultiTrigger itself), for a given object.
449        To make an example: When the mode is 'and', then this would be true or a given object if all the sub-triggers were triggered ofr the given object.
450    @param triggerer
451        The object.
452    @return
453        Returns true if the MultiTrigger is triggered concerning it's sub-triggers.
454    */
455    bool MultiTrigger::isModeTriggered(BaseObject* triggerer)
456    {
457        if (this->subTriggers_.size() != 0)
458        {
459            bool returnVal = false;
460
461            switch (this->mode_)
462            {
463                case MultiTriggerMode::EventTriggerAND:
464                    returnVal = checkAnd(triggerer);
465                    break;
466                case MultiTriggerMode::EventTriggerOR:
467                    returnVal = checkOr(triggerer);
468                    break;
469                case MultiTriggerMode::EventTriggerXOR:
470                    returnVal = checkXor(triggerer);
471                    break;
472                default:
473                    returnVal = false;
474                    break;
475            }
476
477            return returnVal;
478        }
479       
480        return true;
481    }
482   
483    /**
484    @brief
485        Get whether the MultiTrigger is triggered for a given object.
486    @param triggerer
487        The object.
488    @return
489        Returns true if the MultiTrigger is triggered for the given object.
490    */
491    bool MultiTrigger::isTriggered(BaseObject* triggerer)
492    {
493        std::set<BaseObject*>::iterator it = this->triggered_.find(triggerer);
494        if(it == this->triggered_.end())
495            return false;
496        return true;
497    }
498
499    /**
500    @brief
501        Helper method. Creates an event for the given status and originator and fires it.
502        Or more precisely creates a MultiTriggerContainer to encompass all neccesary information and creates an Event from that and sends it.
503    @param status
504        The status of the event to be fired. This is equivalent to the activity of the MultiTrigger.
505    @param originator
506        The object that triggered the MultiTrigger to fire this Event.
507    */
508    void MultiTrigger::fire(bool status, BaseObject* originator)
509    {
510        // If the originator is NULL, a normal event without MultiTriggerContainer is sent.
511        if(originator == NULL)
512        {
513            this->fireEvent(status);
514            return;
515        }
516       
517        MultiTriggerContainer* container = new MultiTriggerContainer(this, this, originator);
518        this->fireEvent(status, container);
519        COUT(4) << "MultiTrigger '" <<  this->getName() << "' (&" << this << "): Fired event. originator: " << originator->getIdentifier()->getName() << " (&" << originator << "), status: " << status << "." << std::endl;
520        delete container;
521    }
522
523    /**
524    @brief
525        Helper method. Adds a state to the state queue, where the state will wait to become active.
526    @param state
527        The state to be added.
528    */
529    bool MultiTrigger::addState(MultiTriggerState* state)
530    {
531        assert(state);
532       
533        // If the originator is no target of this MultiTrigger.
534        if(!this->isTarget(state->originator))
535            return false;
536       
537        // Add it ot the state queue.
538        this->stateQueue_.push_back(std::pair<float, MultiTriggerState*>(this->delay_, state));
539
540        return true;
541    }
542
543    /**
544    @brief
545        Checks whether the sub-triggers amount to true for the 'and' mode for a given object.
546    @param triggerer
547        The object.
548    @return
549        Returns true if they do.
550    */
551    bool MultiTrigger::checkAnd(BaseObject* triggerer)
552    {
553        std::set<MultiTrigger*>::iterator it;
554        for(it = this->subTriggers_.begin(); it != this->subTriggers_.end(); ++it)
555        {
556            if (!(*it)->isActive(triggerer))
557                return false;
558        }
559        return true;
560    }
561
562    /**
563    @brief
564        Checks whether the sub-triggers amount to true for the 'or' mode for a given object.
565    @param triggerer
566        The object.
567    @return
568        Returns true if they do.
569    */
570    bool MultiTrigger::checkOr(BaseObject* triggerer)
571    {
572        std::set<MultiTrigger*>::iterator it;
573        for(it = this->subTriggers_.begin(); it != this->subTriggers_.end(); ++it)
574        {
575            if ((*it)->isActive(triggerer))
576                return true;
577        }
578        return false;
579    }
580
581    /**
582    @brief
583        Checks whether the sub-triggers amount to true for the 'xor' mode for a given object.
584    @param triggerer
585        The object.
586    @return
587        Returns true if they do.
588    */
589    bool MultiTrigger::checkXor(BaseObject* triggerer)
590    {
591        std::set<MultiTrigger*>::iterator it;
592        bool test = false;
593        for(it = this->subTriggers_.begin(); it != this->subTriggers_.end(); ++it)
594        {
595            if (test && (*it)->isActive(triggerer))
596                return false;
597            if ((*it)->isActive(triggerer))
598                test = true;
599        }
600        return test;
601    }
602
603}
Note: See TracBrowser for help on using the repository browser.