Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Adding changes made to DistanceTrigger also in trunk.
Also documenting trigger.

  • Property svn:eol-style set to native
File size: 22.9 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Damian 'Mozork' Frick
24 *   Co-authors:
25 *      ...
26 *
27*/
28
29/**
30    @file MultiTrigger.cc
31    @brief Implementation of the MultiTrigger class.
32    @ingroup MultiTrigger
33*/
34
35#include "MultiTrigger.h"
36
37#include "core/CoreIncludes.h"
38#include "core/XMLPort.h"
39
40#include "MultiTriggerContainer.h"
41
42namespace orxonox
43{
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    MultiTrigger::MultiTrigger(BaseObject* creator) : TriggerBase(creator)
54    {
55        RegisterObject(MultiTrigger);
56
57        this->maxNumSimultaneousTriggerers_ = INF_s;
58
59        this->bBroadcast_ = false;
60
61        this->targetMask_.exclude(Class(BaseObject));
62
63        this->bMultiTrigger_ = true;
64
65        this->setSyncMode(0x0);
66    }
67
68    /**
69    @brief
70        Destructor. Cleans up the state queue.
71    */
72    MultiTrigger::~MultiTrigger()
73    {
74        COUT(4) << "Destroying MultiTrigger &" << this << ". " << this->stateQueue_.size() << " states still in queue. Deleting." << std::endl;
75        while(this->stateQueue_.size() > 0)
76        {
77            MultiTriggerState* state = this->stateQueue_.front().second;
78            this->stateQueue_.pop_front();
79            delete state;
80        }
81    }
82
83    /**
84    @brief
85        Method for creating a MultiTrigger object through XML.
86        For a detailed description of the parameters please see the class description in the header file.
87    */
88    void MultiTrigger::XMLPort(Element& xmlelement, XMLPort::Mode mode)
89    {
90        SUPER(MultiTrigger, XMLPort, xmlelement, mode);
91
92        XMLPortParam(MultiTrigger, "simultaneousTriggerers", setSimultaneousTriggerers, getSimultaneousTriggerers, xmlelement, mode);
93        XMLPortParam(MultiTrigger, "broadcast", setBroadcast, getBroadcast, xmlelement, mode);
94        XMLPortParamLoadOnly(MultiTrigger, "target", addTarget, xmlelement, mode).defaultValues("Pawn"); //TODO: Remove load only
95
96        COUT(4) << "MultiTrigger '" << this->getName() << "' (&" << this << ") created." << std::endl;
97    }
98
99
100    /**
101    @brief
102        A method that is executed each tick.
103        Most of the magic of MultiTriggers happens here.
104    @param dt
105        The duration of the last tick.
106    */
107    //TODO: Segment into some helper methods?
108    void MultiTrigger::tick(float dt)
109    {
110        // If this is the first tick.
111        if(this->bFirstTick_)
112        {
113            this->bFirstTick_ = false;
114            // Fire for all objects that are targets.
115            this->broadcast(false);
116        }
117
118        // Check if the object is active (this is NOT MultiTrigger::isActive()!), it is whether the MultiTrigger actually does anything, ever.
119        if (!this->BaseObject::isActive())
120            return;
121
122        SUPER(MultiTrigger, tick, dt);
123
124        // Let the MultiTrigger return the states that trigger and process the new states if there are any.
125        std::queue<MultiTriggerState*>* queue  = this->letTrigger();
126        if(queue != NULL)
127        {
128            while(queue->size() > 0)
129            {
130                MultiTriggerState* state = queue->front();
131                // If the state is NULL. (This really shouldn't happen)
132                if(state == NULL)
133                {
134                    COUT(1) << "In MultiTrigger '" << this->getName() << "' (&" << this << "), Error: State of new states queue was NULL. State ignored." << std::endl;
135                    queue->pop();
136                    continue;
137                }
138
139                // The new triggered state dependent on the requested state, the mode and the invert-mode.
140                bool bTriggered = (state->bTriggered & this->isModeTriggered(state->originator)) ^ this->getInvert();
141
142                // If the 'triggered' state has changed or the MultiTrigger has delay and thus we don't know whether this state will actually change the 'triggered' state, a new state is added to the state queue.
143                if(this->getDelay() > 0.0f || bTriggered ^ this->isTriggered(state->originator))
144                {
145                    state->bTriggered = bTriggered;
146                    this->addState(state);
147                }
148                // Else the change is irrelevant.
149                else
150                    delete state;
151
152                queue->pop();
153            }
154            delete queue;
155        }
156
157        // Go through the state queue and activate all pending states whose remaining time has expired.
158        if(this->stateQueue_.size() > 0)
159        {
160            MultiTriggerState* state;
161            float timeRemaining;
162
163            // Go through all pending states.
164            for(int size = this->stateQueue_.size(); size >= 1; size--)
165            {
166                timeRemaining = this->stateQueue_.front().first;
167                state = this->stateQueue_.front().second;
168
169                // If the remaining time has expired, the state has to be set as the state of the MultiTrigger.
170                if(timeRemaining <= dt)
171                {
172                    // If the maximum number of objects simultaneously triggering this MultiTrigger is not exceeded.
173                    if(this->getSimultaneousTriggerers() == TriggerBase::INF_s || this->triggered_.size() < (unsigned int)this->getSimultaneousTriggerers())
174                    {
175                        bool bStateChanged = false;
176                        // If the 'triggered' state is different from what it is now, change it.
177                        if(state->bTriggered ^ this->isTriggered(state->originator))
178                        {
179                            // Add the originator to the objects triggering this MultiTrigger.
180                            if(state->bTriggered == true)
181                                this->triggered_.insert(state->originator);
182                            // Remove the originator from the objects triggering this MultiTrigger.
183                            else
184                                this->triggered_.erase(state->originator);
185
186                            bStateChanged = true;
187                        }
188
189                        // Get the activity of the new state.
190                        bool bActive;
191                        // If the MultiTrigger is in switch mode the 'active'-state only changes of the state changed to triggered.
192                        if(this->getSwitch() && !state->bTriggered)
193                            bActive = this->isActive(state->originator);
194                        else
195                            bActive = !this->isActive(state->originator);
196
197                        // If the activity is different from what it is now, change it and fire an Event.
198                        if(bActive ^ this->isActive(state->originator))
199                        {
200
201                            bool bFire = true;
202
203                            // Add the originator to the objects activating this MultiTrigger.
204                            if(bActive == true)
205                            {
206                                // If the MultiTrigger has not exceeded its remaining activations.
207                                if(this->hasRemainingActivations())
208                                {
209                                    this->active_.insert(state->originator);
210                                    if(this->remainingActivations_ != INF_s)
211                                        this->remainingActivations_--; // Decrement the remaining activations.
212                                }
213                                else
214                                    bFire = false;
215                            }
216                            // Remove the originator from the objects activating this MultiTrigger.
217                            else
218                            {
219                                // If the MultiTrigger doesn't stay active or hasn't' exceeded its remaining activations.
220                                if(!this->getStayActive() || this->hasRemainingActivations())
221                                    this->active_.erase(state->originator);
222                                else
223                                    bFire = false;
224                            }
225
226                            // Fire the Event if the activity has changed.
227                            if(bFire)
228                            {
229                                // If the MultiTrigger is set to broadcast and has no originator a boradcast is fired.
230                                if(this->getBroadcast() && state->originator == NULL)
231                                    this->broadcast(bActive);
232                                // Else a normal event is fired.
233                                else
234                                    this->fire(bActive, state->originator);
235
236                                bStateChanged = true;
237                            }
238                        }
239
240                        if(bStateChanged)
241                        {
242                            // Print some debug output if the state has changed.
243                            if(state->originator != NULL)
244                                COUT(4) << "MultiTrigger '" << this->getName() << "' (&" << this << ") changed state. originator: " << state->originator->getIdentifier()->getName() << " (&" << state->originator << "), active: " << bActive << ", triggered: " << state->bTriggered << "." << std::endl;
245                            else
246                                COUT(4) << "MultiTrigger '" << this->getName() << "' (&" << this << ") changed state. originator: NULL, active: " << bActive << ", triggered: " << state->bTriggered << "." << std::endl;
247
248                            // If the MultiTrigger has a parent trigger, that is itself a MultiTrigger, it needs to call a method to notify him, that its activity has changed.
249                            if(this->parent_ != NULL && this->parent_->isMultiTrigger())
250                                static_cast<MultiTrigger*>(this->parent_)->childActivityChanged(state->originator);
251                        }
252
253                        // If the MultiTrigger has exceeded its amount of activations and it doesn't stay active, it has to be deactivated.
254                        if(this->remainingActivations_ == 0 && !bActive)
255                        {
256                            this->BaseObject::setActive(false);
257                            COUT(4) << "MultiTrigger '" << this->getName() << "' (&" << this << ") ran out of activations. Setting it to inactive." << std::endl;
258                        }
259                    }
260
261                    // Remove the state from the state queue.
262                    this->stateQueue_.pop_front();
263                    delete state;
264                }
265                // If the remaining time has not yet expired. Decrement the remainig time and put the state at the end of the queue.
266                else
267                {
268                    this->stateQueue_.push_back(std::pair<float, MultiTriggerState*>(timeRemaining-dt, state));
269                    this->stateQueue_.pop_front();
270                }
271            }
272        }
273    }
274
275    /**
276    @brief
277        Check whether the MultiTrigger is active for a given object.
278    @param triggerer
279        A pointer to the object.
280    @return
281        Returns true if the MultiTrigger is active, false if not.
282    */
283    bool MultiTrigger::isActive(BaseObject* triggerer) const
284    {
285        std::set<BaseObject*>::const_iterator it = this->active_.find(triggerer);
286        if(it == this->active_.end())
287            return false;
288        return true;
289    }
290
291    /**
292    @brief
293        Add some target to the MultiTrigger.
294    @param targetStr
295        The target class name as a string.
296    */
297    void MultiTrigger::addTarget(const std::string& targetStr)
298    {
299        Identifier* target = ClassByString(targetStr);
300
301        // If the target is not a valid class name display an error.
302        if (target == NULL)
303        {
304            COUT(1) << "Error: '" << targetStr << "' is not a valid class name to include in ClassTreeMask (in " << this->getName() << ", class " << this->getIdentifier()->getName() << ")" << std::endl;
305            return;
306        }
307
308        this->targetMask_.include(target);
309
310        // A MultiTrigger shouldn't react to itself or other triggers.
311        this->targetMask_.exclude(Class(TriggerBase), true);
312
313        // We only want WorldEntities
314        //TODO: Really?
315        ClassTreeMask WEMask;
316        WEMask.include(Class(WorldEntity));
317        this->targetMask_ *= WEMask;
318    }
319
320    /**
321    @brief
322        Remove some target from the MultiTrigger.
323    @param targetStr
324        The target to be removed as a string.
325    */
326    void MultiTrigger::removeTarget(const std::string& targetStr)
327    {
328        Identifier* target = ClassByString(targetStr);
329
330        // If the target is not a valid class name display an error.
331        if (target == NULL)
332        {
333            COUT(1) << "Error: '" << targetStr << "' is not a valid class name to include in ClassTreeMask (in " << this->getName() << ", class " << this->getIdentifier()->getName() << ")" << std::endl;
334            return;
335        }
336
337        this->targetMask_.exclude(target);
338    }
339
340    /**
341    @brief
342        This method is called by the MultiTrigger to get information about new trigger events that need to be looked at.
343        This method is the device for the behavior (the conditions under which the MultiTrigger triggers) of any derived class of MultiTrigger.
344    @return
345        Returns a pointer to a queue of MultiTriggerState pointers, containing all the necessary information to decide whether these states should indeed become new states of the MultiTrigger.
346        Please be aware that both the queue and the states in the queue need to be deleted once 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.
347    */
348    std::queue<MultiTriggerState*>* MultiTrigger::letTrigger(void)
349    {
350        return NULL;
351    }
352
353    /**
354    @brief
355        This method can be called by any class inheriting from MultiTrigger to change it's triggered status for a specified originator.
356
357        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.
358    @param originator
359        The originator which triggered the state change.
360    */
361    void MultiTrigger::changeTriggered(BaseObject* originator)
362    {
363        MultiTriggerState* state = new MultiTriggerState;
364        state->bTriggered = (!this->isTriggered(originator) & this->isModeTriggered(originator)) ^ this->getInvert();
365        state->originator = originator;
366        this->addState(state);
367    }
368
369    /**
370    @brief
371        This method is called by any child to advertise changes in its state to its parent.
372    @param originator
373        The object that caused the change in activity.
374    */
375    void MultiTrigger::childActivityChanged(BaseObject* originator)
376    {
377        MultiTriggerState* state = new MultiTriggerState;
378        state->bTriggered = (this->isTriggered(originator) & this->isModeTriggered(originator)) ^ this->getInvert();
379        state->originator = originator;
380        this->addState(state);
381    }
382
383    /**
384    @brief
385        Checks whether the children are in such a way that, according to the mode of the MultiTrigger, the MultiTrigger is triggered (only considering the children, not the state of MultiTrigger itself), for a given object.
386        To make an example: When the mode is <em>and</em>, then this would be true or a given object if all the children were triggered for the given object.
387    @param triggerer
388        The object.
389    @return
390        Returns true if the MultiTrigger is triggered concerning it's children.
391    */
392    bool MultiTrigger::isModeTriggered(BaseObject* triggerer)
393    {
394        if(this->children_.size() != 0)
395        {
396            bool triggered = false;
397
398            switch(this->mode_)
399            {
400                case TriggerMode::EventTriggerAND:
401                    triggered = checkAnd(triggerer);
402                    break;
403                case TriggerMode::EventTriggerOR:
404                    triggered = checkOr(triggerer);
405                    break;
406                case TriggerMode::EventTriggerXOR:
407                    triggered = checkXor(triggerer);
408                    break;
409                default: // This will never happen.
410                    triggered = false;
411                    break;
412            }
413
414            return triggered;
415        }
416
417        return true;
418    }
419
420    /**
421    @brief
422        Get whether the MultiTrigger is triggered for a given object.
423    @param triggerer
424        The object.
425    @return
426        Returns true if the MultiTrigger is triggered for the given object.
427    */
428    bool MultiTrigger::isTriggered(BaseObject* triggerer)
429    {
430        std::set<BaseObject*>::iterator it = this->triggered_.find(triggerer);
431        if(it == this->triggered_.end())
432            return false;
433        return true;
434    }
435
436    /**
437    @brief
438        Helper method. Creates an Event for the given status and originator and fires it.
439        Or more precisely creates a MultiTriggerContainer to encompass all neccesary information, creates an Event from that and sends it.
440    @param status
441        The status of the Event to be fired. This is equivalent to the activity of the MultiTrigger.
442    @param originator
443        The object that triggered the MultiTrigger to fire this Event.
444    */
445    void MultiTrigger::fire(bool status, BaseObject* originator)
446    {
447        // If the originator is NULL, a normal event without MultiTriggerContainer is sent.
448        if(originator == NULL)
449        {
450            this->fireEvent(status);
451            COUT(4) << "MultiTrigger '" <<  this->getName() << "' (&" << this << "): Fired event. status: " << status << "." << std::endl;
452            return;
453        }
454
455        MultiTriggerContainer* container = new MultiTriggerContainer(this, this, originator);
456        this->fireEvent(status, container);
457        COUT(4) << "MultiTrigger '" <<  this->getName() << "' (&" << this << "): Fired event. originator: " << originator->getIdentifier()->getName() << " (&" << originator << "), status: " << status << "." << std::endl;
458        delete container;
459    }
460
461    /**
462    @brief
463        Helper method. Broadcasts an Event for every object that is a target.
464    @param status
465        The status of the Events to be fired. This is equivalent to the activity of the MultiTrigger.
466    */
467    void MultiTrigger::broadcast(bool status)
468    {
469        for(ClassTreeMaskObjectIterator it = this->getTargetMask().begin(); it != this->getTargetMask().end(); ++it)
470            this->fire(status, static_cast<BaseObject*>(*it));
471    }
472
473    /**
474    @brief
475        Helper method. Adds a state to the state queue, where the state will wait to become active.
476    @param state
477        The state to be added.
478    @return
479        Returns true if the state has been added, false if not. If the state has not been added this method destroys it.
480    */
481    bool MultiTrigger::addState(MultiTriggerState* state)
482    {
483        assert(state); // The state really shouldn't be NULL.
484
485        // If the originator is no target of this MultiTrigger.
486        if(!this->isTarget(state->originator))
487        {
488            delete state;
489            return false;
490        }
491
492        // Add it ot the state queue with the delay specified for the MultiTrigger.
493        this->stateQueue_.push_back(std::pair<float, MultiTriggerState*>(this->getDelay(), state));
494
495        return true;
496    }
497
498    /**
499    @brief
500        Checks whether the children amount to true for the <em>and</em> mode for a given object.
501    @param triggerer
502        The object.
503    @return
504        Returns true if all the sub-triggers are active.
505    */
506    bool MultiTrigger::checkAnd(BaseObject* triggerer)
507    {
508        for(std::set<TriggerBase*>::iterator it = this->children_.begin(); it != this->children_.end(); ++it)
509        {
510            TriggerBase* trigger = *it;
511            if(trigger->isMultiTrigger())
512            {
513                if(!static_cast<MultiTrigger*>(trigger)->isActive(triggerer))
514                    return false;
515            }
516            else
517            {
518                if(!trigger->isActive())
519                    return false;
520            }
521        }
522        return true;
523    }
524
525    /**
526    @brief
527        Checks whether the children amount to true for the <em>or</em> mode for a given object.
528    @param triggerer
529        The object.
530    @return
531        Returns true if at least one sub-trigger is active.
532    */
533    bool MultiTrigger::checkOr(BaseObject* triggerer)
534    {
535        for(std::set<TriggerBase*>::iterator it = this->children_.begin(); it != this->children_.end(); ++it)
536        {
537            TriggerBase* trigger = *it;
538            if(trigger->isMultiTrigger())
539            {
540                if(static_cast<MultiTrigger*>(trigger)->isActive(triggerer))
541                    return true;
542            }
543            else
544            {
545                if(trigger->isActive())
546                    return true;
547            }
548        }
549        return false;
550    }
551
552    /**
553    @brief
554        Checks whether the children amount to true for the <em>xor</em> mode for a given object.
555    @param triggerer
556        The object.
557    @return
558        Returns true if exactly one sub-trigger is active.
559    */
560    bool MultiTrigger::checkXor(BaseObject* triggerer)
561    {
562        bool triggered = false;
563        for(std::set<TriggerBase*>::iterator it = this->children_.begin(); it != this->children_.end(); ++it)
564        {
565            TriggerBase* trigger = *it;
566            if(triggered)
567            {
568                if(trigger->isMultiTrigger())
569                {
570                    if(static_cast<MultiTrigger*>(trigger)->isActive(triggerer))
571                        return false;
572                }
573                else
574                {
575                    if(trigger->isActive())
576                        return false;
577                }
578            }
579
580            if(trigger->isMultiTrigger())
581            {
582                if(static_cast<MultiTrigger*>(trigger)->isActive(triggerer))
583                    triggered = true;
584            }
585            else
586            {
587                if(trigger->isActive())
588                    triggered = true;
589            }
590        }
591        return triggered;
592    }
593
594}
Note: See TracBrowser for help on using the repository browser.