Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Created a new MutliTrigger: EventMultiTrigger, which essentially does the same as EventTrigger but for every object that is a target as specified by teh EventMultiTrigger an Event is fired with information as being triggered by the target object.
This allows, e.g. to use the EventMultiTrigger to trigger QuestEffectBeacons.
I've also added a fireEvent() to Pawn such that an Event is fired when the Pawn is destroyed (forcebly). With EventMultiTrigger (but also with EventTrigger) one can now find out whether a Pawn died.
Here a little piece of example XML code to illustrate the usage:
<EventMutliTrigger name="trigger1">

<events>

<trigger>

<Spaceship />

</trigger>

</events>

</EventMultiTrigger>
To be able to implement this another feature was added to MultiTrigger, which is called broadcast. If enabled the MultiTrigger sends all Events triggered by NULL as to have come by all possible triggerers.

Additionally I've fixed a bug:
In DistanceMultiTrigger, an infinite loop occured under some circumstances. Not any more.

And did some niceifying (aka. documenting, renaming and code restructuring).

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