Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Some bugfixes in MultiTrigger.
All MultiTrigger features should work now, but testing has not been done as rigorously as could be desired.

File size: 16.2 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Damian 'Mozork' Frick
24 *   Co-authors:
25 *      Benjamin Knecht
26 *
27*/
28
29#include "MultiTrigger.h"
30
31#include "core/CoreIncludes.h"
32#include "core/XMLPort.h"
33
34#include "MultiTriggerContainer.h"
35
36namespace orxonox
37{
38   
39    /*static*/ const int MultiTrigger::INF_s = -1;
40    /*static*/ const std::string MultiTrigger::or_s = "or";
41    /*static*/ const std::string MultiTrigger::and_s = "and";
42    /*static*/ const std::string MultiTrigger::xor_s = "xor";
43   
44    CreateFactory(MultiTrigger);
45   
46    //TODO: Clean up.
47    MultiTrigger::MultiTrigger(BaseObject* creator) : StaticEntity(creator)
48    {
49        RegisterObject(MultiTrigger);
50       
51        this->mode_ = MultiTriggerMode::EventTriggerAND;
52
53        this->bFirstTick_ = true;
54
55        this->bInvertMode_ = false;
56        this->bSwitch_ = false;
57        this->bStayActive_ = false;
58        this->delay_ = 0.0f;
59        this->remainingActivations_ = INF_s;
60        this->maxNumSimultaniousTriggerers_ = INF_s;
61       
62        this->targetMask_.exclude(Class(BaseObject));
63
64        this->setSyncMode(0x0);
65       
66    }
67   
68    //TODO: Document
69    MultiTrigger::~MultiTrigger()
70    {
71        COUT(4) << "Destorying MultiTrigger &" << this << ". " << this->stateQueue_.size() << " states still in queue. Deleting." << std::endl;
72        while(this->stateQueue_.size() > 0)
73        {
74            MultiTriggerState* state = this->stateQueue_.front().second;
75            this->stateQueue_.pop_front();
76            delete state;
77        }
78    }
79   
80    //TODO: Document.
81    void MultiTrigger::XMLPort(Element& xmlelement, XMLPort::Mode mode)
82    {
83        SUPER(MultiTrigger, XMLPort, xmlelement, mode);
84
85        XMLPortParam(MultiTrigger, "delay", setDelay, getDelay, xmlelement, mode).defaultValues(0.0f);
86        XMLPortParam(MultiTrigger, "switch", setSwitch, getSwitch, xmlelement, mode).defaultValues(false);
87        XMLPortParam(MultiTrigger, "stayactive", setStayActive, getStayActive, xmlelement, mode).defaultValues(false);
88        XMLPortParam(MultiTrigger, "activations", setActivations, getActivations, xmlelement, mode).defaultValues(INF_s);
89        XMLPortParam(MultiTrigger, "invert", setInvert, getInvert, xmlelement, mode).defaultValues(false);
90        XMLPortParam(MultiTrigger, "simultaniousTriggerers", setSimultaniousTriggerers, getSimultaniousTriggerers, xmlelement, mode).defaultValues(INF_s);
91        XMLPortParamTemplate(MultiTrigger, "mode", setMode, getModeString, xmlelement, mode, const std::string&).defaultValues(MultiTrigger::and_s);
92        XMLPortParamLoadOnly(MultiTrigger, "target", addTargets, xmlelement, mode).defaultValues("ControllableEntity"); //TODO: Remove load only
93
94        XMLPortObject(MultiTrigger, MultiTrigger, "", addTrigger, getTrigger, xmlelement, mode);
95       
96        COUT(4) << "MultiTrigger '" << this->getName() << "' (&" << this << ") created." << std::endl;
97    }
98   
99    //TODO: Document
100    void MultiTrigger::tick(float dt)
101    {
102        if(this->bFirstTick_) // If this is the first tick.
103        {
104            this->bFirstTick_ = false;
105            this->fire(false); //TODO: Does this work? Resp. Is it correct?
106        }
107
108        // Check if the object is active (this is NOT Trigger::isActive()!)
109        if (!this->BaseObject::isActive())
110            return;
111
112        SUPER(MultiTrigger, tick, dt);
113       
114        std::queue<MultiTriggerState*>* queue  = this->letTrigger();
115       
116        if(queue != NULL)
117        {
118            while(queue->size() > 0)
119            {
120                //TODO: Be more efficient, Don't delete a state and create a new one immediately after that. Reuse!
121                MultiTriggerState* state = queue->front();
122                if(state == NULL)
123                    break;
124
125                bool bTriggered = (state->bTriggered & this->isModeTriggered(state->originator)) ^ this->bInvertMode_;
126                if(bTriggered ^ this->isTriggered(state->originator))
127                    this->addState(bTriggered, state->originator);
128                queue->pop();
129                delete state;
130            }
131            delete queue;
132        }       
133
134        if (this->stateQueue_.size() > 0)
135        {
136            MultiTriggerState* state;
137            float timeRemaining;
138            for(int size = this->stateQueue_.size(); size >= 1; size--)
139            {
140                timeRemaining = this->stateQueue_.front().first;
141                state = this->stateQueue_.front().second;
142                if(timeRemaining <= dt)
143                {
144                    if(this->maxNumSimultaniousTriggerers_ == INF_s || this->triggered_.size() < (unsigned int)this->maxNumSimultaniousTriggerers_)
145                    {
146                        // Add the originator to the objects triggering this MultiTrigger.
147                        if(state->bTriggered == true)
148                        {
149                            this->triggered_.insert(state->originator);
150                        }
151                        // Remove the originator from the objects triggering this MultiTrigger.
152                        else
153                        {
154                            this->triggered_.erase(state->originator);
155                        }
156                       
157                        // Add the originator to the objects activating this MultiTrigger.
158                        if(state->bActive == true)
159                        {
160                            this->active_.insert(state->originator);
161                        }
162                        // Remove the originator from the objects activating this MultiTrigger.
163                        else
164                        {
165                            this->active_.erase(state->originator);
166                        }
167                       
168                        // Fire the event.
169                        this->fire(state->bActive, state->originator);
170                    }
171                   
172                    // Remove the state from the state queue.
173                    this->stateQueue_.pop_front();
174                    COUT(4) << "MultiTrigger '" << this->getName() << "' &" << this << ": State processed, removing from state queue. originator: " << state->originator->getIdentifier()->getName() << " (&" << state->originator << "), active: " << state->bActive << "|" << this->isActive(state->originator) << ", triggered: " << state->bTriggered << "|" << this->isTriggered(state->originator) << "." << std::endl;
175                    delete state;
176                    size -= 1;
177                }
178                else
179                {
180                    this->stateQueue_.push_back(std::pair<float, MultiTriggerState*>(timeRemaining-dt, state));
181                    this->stateQueue_.pop_front();
182                    COUT(4) << "MultiTrigger '" << this->getName() << "' &" << this << ": State processed, decreasing time remaining. originator: " << state->originator->getIdentifier()->getName() << " (&" << state->originator << "), active: " << state->bActive << ", triggered: " << state->bTriggered << ", time remaining: " << timeRemaining-dt << "." << std::endl;
183                }
184            }
185        }
186    }
187
188    //TODO: Document
189    bool MultiTrigger::isModeTriggered(BaseObject* triggerer)
190    {
191        if (this->children_.size() != 0)
192        {
193            bool returnVal = false;
194
195            switch (this->mode_)
196            {
197                case MultiTriggerMode::EventTriggerAND:
198                    returnVal = checkAnd(triggerer);
199                    break;
200                case MultiTriggerMode::EventTriggerOR:
201                    returnVal = checkOr(triggerer);
202                    break;
203                case MultiTriggerMode::EventTriggerXOR:
204                    returnVal = checkXor(triggerer);
205                    break;
206                default:
207                    returnVal = false;
208                    break;
209            }
210
211            return returnVal;
212        }
213       
214        return true;
215    }
216   
217    //TODO: Document
218    std::queue<MultiTriggerState*>* MultiTrigger::letTrigger(void)
219    {
220        // Goes through all trigger children and gets the objects triggering them.
221        std::set<BaseObject*>* triggerers = new std::set<BaseObject*>();
222        std::set<BaseObject*>::iterator objIt;
223        for(std::set<MultiTrigger*>::iterator it = this->children_.begin(); it != this->children_.end(); it ++)
224        {
225            std::set<BaseObject*> set = (*it)->getActive();
226            for(objIt = set.begin(); objIt != set.end(); objIt++)
227            {
228                triggerers->insert(*objIt);
229            }
230        }
231
232        // Goes through all the triggerers of this trigger.
233        for(objIt = this->active_.begin(); objIt != this->active_.end(); objIt++)
234        {
235            triggerers->insert(*objIt);
236        }
237
238        if(triggerers->size() == 0)
239            return NULL;
240       
241        std::queue<MultiTriggerState*>* queue = new std::queue<MultiTriggerState*>();
242        MultiTriggerState* state = NULL;
243        for(std::set<BaseObject*>::iterator it = triggerers->begin(); it != triggerers->end(); it++)
244        {
245            state = new MultiTriggerState;
246            state->bTriggered = true;
247            state->originator = *it;
248            queue->push(state);
249        }
250        delete triggerers;
251       
252        return queue;
253    }
254   
255    //TODO: Document
256    bool MultiTrigger::isTriggered(BaseObject* triggerer)
257    {
258        std::set<BaseObject*>::iterator it = this->triggered_.find(triggerer);
259        if(it == this->triggered_.end())
260            return false;
261        return true;
262    }
263
264    //TODO: Document
265    bool MultiTrigger::checkAnd(BaseObject* triggerer)
266    {
267        std::set<MultiTrigger*>::iterator it;
268        for(it = this->children_.begin(); it != this->children_.end(); ++it)
269        {
270            if (!(*it)->isActive(triggerer))
271                return false;
272        }
273        return true;
274    }
275
276    //TODO: Document
277    bool MultiTrigger::checkOr(BaseObject* triggerer)
278    {
279        std::set<MultiTrigger*>::iterator it;
280        for(it = this->children_.begin(); it != this->children_.end(); ++it)
281        {
282            if ((*it)->isActive(triggerer))
283                return true;
284        }
285        return false;
286    }
287
288    //TODO: Document
289    bool MultiTrigger::checkXor(BaseObject* triggerer)
290    {
291        std::set<MultiTrigger*>::iterator it;
292        bool test = false;
293        for(it = this->children_.begin(); it != this->children_.end(); ++it)
294        {
295            if (test && (*it)->isActive(triggerer))
296                return false;
297            if ((*it)->isActive(triggerer))
298                test = true;
299        }
300        return test;
301    }
302   
303    //TODO: Document
304    bool MultiTrigger::addState(bool bTriggered, BaseObject* originator)
305    {
306        if(!this->isTarget(originator))
307            return false;
308       
309        // If the state doesn't change.
310        if(this->isTriggered() && bTriggered)
311            return false;
312       
313        bool bActive = !this->isActive(originator);
314       
315        // If the MultiTrigger is in switch mode.
316        if(this->bSwitch_ && !bTriggered)
317        {
318            bActive = this->isActive(originator);
319        }
320        // If the state changes to active.
321        else if(this->remainingActivations_ != INF_s && bActive)
322        {
323            if(this->remainingActivations_ == 0)
324                return false;
325            this->remainingActivations_--;
326        }
327        else
328        {
329            // If the MultiTrigger should stay active if there are no more remaining activations.
330            //TODO: Find out how this applies to infinitely many activations.
331            if(this->bStayActive_ && this->remainingActivations_ == 0)
332                return false;
333        }
334       
335        COUT(4) << "MultiTrigger &" << this << ": State added to state queue. originator: " << originator->getIdentifier()->getName() << " (&" << originator << "), active: " << bActive << "|" << this->isActive(originator) << ", triggered: " << bTriggered << "|" << this->isTriggered(originator) << ", remaining activations: " << this->remainingActivations_ << "." << std::endl;
336       
337        // Create state.
338        MultiTriggerState* state = new MultiTriggerState;
339        state->bActive = bActive;
340        state->bTriggered = bTriggered;
341        state->originator = originator;
342        this->stateQueue_.push_back(std::pair<float, MultiTriggerState*>(this->delay_, state));
343       
344        return true;
345    }
346
347    //TODO: Document
348    void MultiTrigger::setMode(const std::string& modeName)
349    {
350        if (modeName == MultiTrigger::and_s)
351            this->setMode(MultiTriggerMode::EventTriggerAND);
352        else if (modeName == MultiTrigger::or_s)
353            this->setMode(MultiTriggerMode::EventTriggerOR);
354        else if (modeName == MultiTrigger::xor_s)
355            this->setMode(MultiTriggerMode::EventTriggerXOR);
356    }
357
358    //TODO: Document
359    const std::string& MultiTrigger::getModeString() const
360    {
361        if (this->mode_ == MultiTriggerMode::EventTriggerAND)
362            return MultiTrigger::and_s;
363        else if (this->mode_ == MultiTriggerMode::EventTriggerOR)
364            return MultiTrigger::or_s;
365        else if (this->mode_ == MultiTriggerMode::EventTriggerXOR)
366            return MultiTrigger::xor_s;
367        else
368            return MultiTrigger::and_s;
369    }
370   
371    //TODO: Document
372    bool MultiTrigger::isActive(BaseObject* triggerer)
373    {
374        std::set<BaseObject*>::iterator it = this->active_.find(triggerer);
375        if(it == this->active_.end())
376            return false;
377        return true;
378    }
379
380    //TODO: Document
381    void MultiTrigger::addTrigger(MultiTrigger* trigger)
382    {
383        if (this != trigger && trigger != NULL)
384            this->children_.insert(trigger);
385    }
386
387    //TODO: Document
388    const MultiTrigger* MultiTrigger::getTrigger(unsigned int index) const
389    {
390        if (this->children_.size() <= index)
391            return NULL;
392
393        std::set<MultiTrigger*>::const_iterator it;
394        it = this->children_.begin();
395
396        for (unsigned int i = 0; i != index; ++i)
397            ++it;
398
399        return (*it);
400    }
401   
402    //TODO: Document
403    void MultiTrigger::fire(bool status, BaseObject* originator)
404    {
405        if(originator == NULL)
406        {
407            this->fireEvent(status);
408            return;
409        }
410        MultiTriggerContainer* container = new MultiTriggerContainer(this, this, originator);
411        this->fireEvent(status, container);
412        COUT(4) << "MultiTrigger &" << this << ": Fired event. originator: " << originator->getIdentifier()->getName() << " (&" << originator << "), status: " << status << "." << std::endl;
413        delete container;
414    }
415   
416    //TODO: Document
417    void MultiTrigger::addTargets(const std::string& targets)
418    {
419        Identifier* target = ClassByString(targets);
420
421        if (target == NULL)
422        {
423            COUT(1) << "Error: \"" << targets << "\" is not a valid class name to include in ClassTreeMask (in " << this->getName() << ", class " << this->getIdentifier()->getName() << ')' << std::endl;
424            return;
425        }
426
427        this->targetMask_.include(target);
428
429        // trigger shouldn't react on itself or other triggers
430        this->targetMask_.exclude(Class(MultiTrigger), true);
431
432        // we only want WorldEntities
433        ClassTreeMask WEMask;
434        WEMask.include(Class(WorldEntity));
435        this->targetMask_ *= WEMask;
436
437        this->notifyMaskUpdate();
438    }
439
440    //TODO: Document
441    void MultiTrigger::removeTargets(const std::string& targets)
442    {
443        Identifier* target = ClassByString(targets);
444        this->targetMask_.exclude(target);
445    }
446   
447}
Note: See TracBrowser for help on using the repository browser.