Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

merged output branch back to trunk.

Changes:

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

Output levels:

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

Guidelines:

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

Usage in C++:

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

Usage in Lua:

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

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

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