Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/campaignHS15/src/orxonox/controllers/ActionpointController.cc @ 10934

Last change on this file since 10934 was 10934, checked in by gania, 8 years ago

fixed bugs that are not supposed to even exist by initializing a variable in the constructor

File size: 25.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 *      Gani Aliguzhinov
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "ActionpointController.h"
30
31#include "core/XMLPort.h"
32#include <algorithm>
33#include "worldentities/Actionpoint.h"
34
35namespace orxonox
36{
37
38    RegisterClass(ActionpointController);
39
40    ActionpointController::ActionpointController(Context* context) : FightingController(context)
41    {
42        this->actionpointControllerId_ = 0;
43        ActionpointController::sTicks_ = 0;
44        ActionpointController::nextActionpointControllerId_ = 0;
45        this->ticks_ = 0;
46        this->bPatrolling_ = false;
47        this->bInLoop_ = false;
48        this->bLoop_ = false;
49        this->bEndLoop_ = false;
50        loopActionpoints_.clear();
51        parsedActionpoints_.clear();
52        actionpoints_.clear();
53        this->bTakenOver_ = false;
54        this->action_ = Action::NONE;
55        this->squaredaccuracy_ = 2500;
56        this->bFirstTick_ = true;
57        this->bStartedDodging_ = false;
58        this->bDefaultPatrol_ = true;
59        this->bDefaultFightAll_ = true;
60        this->stop_ = false;
61        RegisterObject(ActionpointController);
62
63    }
64    void ActionpointController::XMLPort( Element& xmlelement, XMLPort::Mode mode )
65    {
66        SUPER( ActionpointController, XMLPort, xmlelement, mode );
67        // XMLPortEventSink(ActionpointController, BaseObject, "stop", stop, xmlelement, mode);
68        // XMLPortEventSink(ActionpointController, BaseObject, "go", stop, xmlelement, mode);
69
70        XMLPortObject(ActionpointController, WorldEntity, "actionpoints", addActionpoint, getActionpoint,  xmlelement, mode);
71        XMLPortParam(ActionpointController, "defaultFightAll", setDefaultFightAll, getDefaultFightAll, xmlelement, mode).defaultValues(true);
72        XMLPortParam(ActionpointController, "defaultPatrol", setDefaultPatrol, getDefaultPatrol, xmlelement, mode).defaultValues(true);
73    }
74    // void ActionpointController::XMLEventPort(Element& xmlelement, XMLPort::Mode mode)
75    // {
76    //     SUPER(ActionpointController, XMLEventPort, xmlelement, mode);
77    //     XMLPortEventSink(ActionpointController, BaseObject, "stop", stop, xmlelement, mode);
78    //     XMLPortEventSink(ActionpointController, BaseObject, "go", stop, xmlelement, mode);
79
80    // }
81    // bool ActionpointController::stop(bool bTriggered, BaseObject* trigger)
82    // {
83    //     this->stop_ = true;
84    //     return true;
85    // }
86    // bool ActionpointController::go(bool bTriggered, BaseObject* trigger)
87    // {
88    //     this->stop_ = false;
89    //     return true;
90    // }
91    ActionpointController::~ActionpointController()
92    {
93        loopActionpoints_.clear();
94        parsedActionpoints_.clear();
95        actionpoints_.clear();
96        this->actionpointControllerId_ = 0;
97        /*ActionpointController::nextActionpointControllerId_--;
98        if (ActionpointController::nextActionpointControllerId_ < 0)
99            ActionpointController::nextActionpointControllerId_ = 0;*/
100    }
101    void ActionpointController::tick(float dt)
102    {
103        if (!this || !this->getControllableEntity() || !this->isActive())
104            return;
105        ++this->ticks_;
106        // orxout (internal_error) << "this id = " << this->actionpointControllerId_ << endl;
107        if (ActionpointController::sTicks_ < this->ticks_)
108        {
109            // orxout (internal_error) << "total id's = " << ActionpointController::nextActionpointControllerId_ << endl;
110            ++ActionpointController::sTicks_;
111        }
112
113        if (!this || !this->getControllableEntity())
114            return;
115
116        if (ActionpointController::sTicks_ == 1)
117        {
118
119            this->actionpointControllerId_ = ActionpointController::nextActionpointControllerId_++;
120            std::reverse(parsedActionpoints_.begin(), parsedActionpoints_.end());
121            std::reverse(actionpoints_.begin(), actionpoints_.end());
122            if (this->parsedActionpoints_.empty())
123            {
124                this->action_ = Action::FIGHTALL;
125            }
126            //orxout (internal_error) << "first tick was called by id = " << this->actionpointControllerId_ << ", total = " << ActionpointController::nextActionpointControllerId_ << endl;
127        }
128        if (ActionpointController::sTicks_ == 1)
129        {   
130            this->bActionCalled_ = false;
131           
132            this->bFirstTick_ = false;
133        }
134/*        if (ActionpointController::sTicks_ % (ActionpointController::nextActionpointControllerId_+1) != this->actionpointControllerId_)
135        {
136            return;
137        }*/
138        if (!this || !this->getControllableEntity())
139            return;
140       
141        if (this->bHasTargetPosition_)
142        {
143            this->moveToTargetPosition(dt);
144        }
145        else if (this->bLookAtTarget_)
146        {
147            this->lookAtTarget(dt);
148        }
149       
150
151        if (!this || !this->getControllableEntity())
152            return;
153       
154        if (stop_)
155            return;
156       
157
158        if (timeout_ <= 0)
159            this->bFiredRocket_ = false;
160       
161
162        if (!this || !this->getControllableEntity())
163            return;
164        if (this->timeout_ > 0 && this->bFiredRocket_)
165        {
166            --this->timeout_;
167        }
168   
169
170        if (!this || !this->getControllableEntity())
171            return;
172        //maneuver every 0.25 sec ->
173        int step =  4;
174        if (ActionpointController::sTicks_ % 100 <= 10)
175        {
176            this->bDodge_ = false;
177        }
178        else
179        {
180            this->bDodge_ = true;
181        }
182        if (ActionpointController::sTicks_ % 100 == step * this->actionpointControllerId_ + 1)
183        {
184            //orxout (internal_error) << "Team " << this->getControllableEntity()->getTeam() << (this->hasTarget() ? ", got " : ", don't have") << " target" << endl;
185            this->action();
186        }
187
188
189        if (!this || !this->getControllableEntity())
190            return;
191       /* orxout (internal_error) << "id = " << this->actionpointControllerId_ << ", in total # ids = " << ActionpointController::nextActionpointControllerId_
192        << ", I " << (this->hasTarget() ? "have" : "don't have") << " a target, my team is " << this->getControllableEntity()->getTeam() << endl;
193       */ if (this->hasTarget() &&  ActionpointController::sTicks_ % (ActionpointController::nextActionpointControllerId_ + 1) == (this->actionpointControllerId_))
194        {
195
196         
197            if (!this || !this->getControllableEntity())
198                return;
199            this->maneuver();
200            if (!this || !this->getControllableEntity())
201                return;
202            this->bShooting_ = this->canFire();
203
204            if (!this || !this->getControllableEntity())
205                return;
206
207            if (this->bShooting_)
208            {
209                this->doFire();
210            }
211            this->deltaHp = orxonox_cast<Pawn*> (this->getControllableEntity())->getHealth() - this->previousHp;
212            this->previousHp = orxonox_cast<Pawn*> (this->getControllableEntity())->getHealth();
213        }
214       
215
216        SUPER(ActionpointController, tick, dt);
217    }
218     
219
220
221    void ActionpointController::action()
222    {
223        if (!this || !this->getControllableEntity())
224            return;
225        if (!this->getControllableEntity() || !orxonox_cast<Pawn*> (this->getControllableEntity()))
226            return;
227        if (this->bDefaultPatrol_ || (this->action_ != Action::FLY && this->action_ != Action::NONE))
228        {
229            this->startAttackingEnemiesThatAreClose();
230        }
231        if (!this || !this->getControllableEntity())
232            return;
233
234
235        if (!this || !this->getControllableEntity())
236            return;
237
238        // if (this->actionCounter_ % 2 == 0)
239        //No action -> pop one from stack
240        if (this->action_ == Action::NONE || this->bTakenOver_)
241        {
242            if (this->parsedActionpoints_.empty() && this->loopActionpoints_.empty() && this->bDefaultFightAll_)
243            {
244                Point p = { Action::FIGHTALL, "", Vector3::ZERO, false };
245                this->parsedActionpoints_.push_back (p);
246            }
247            this->executeActionpoint();
248            this->bTakenOver_ = false;
249            // this->action();
250        }
251        if (!this || !this->getControllableEntity())
252            return;
253
254        //Action fightall -> fight till nobody alive
255        if (this->action_ == Action::FIGHTALL)
256        {
257
258            if (!this->hasTarget())
259            {
260                ControllableEntity* newTarget = this->closestTarget();   
261                if (newTarget)
262                {
263                    this->setAction (Action::FIGHTALL, newTarget);
264                    //this->action();
265                }
266                else
267                {
268                    this->nextActionpoint();
269                    this->executeActionpoint();
270   
271                }
272            }
273        }
274        //Action fight -> fight as long as enemies in range
275        else if (this->action_ == Action::FIGHT)
276        {
277
278            if (!this->hasTarget() )
279            {
280                //----find a target----
281                ControllableEntity* newTarget = this->closestTarget();   
282                if (newTarget && 
283                        CommonController::distance (this->getControllableEntity(), newTarget) < this->attackRange_)
284                {
285                    this->setAction (Action::FIGHT, newTarget);
286                    //this->action();
287                }
288                else
289                {
290                    this->nextActionpoint();
291                    this->executeActionpoint();
292                }
293            }
294            else if (this->hasTarget())
295            {
296                //----fly in formation if far enough----
297                Vector3 diffVector = this->positionOfTarget_ - this->getControllableEntity()->getWorldPosition();         
298                   
299                if (diffVector.length() > this->attackRange_)
300                {
301                    ControllableEntity* newTarget = this->closestTarget();
302                   
303                    if (newTarget && 
304                        CommonController::distance (this->getControllableEntity(), newTarget) < this->attackRange_)
305                    {
306                        this->setAction (Action::FIGHT, newTarget);
307                    }
308                    else
309                    {
310                        this->nextActionpoint();
311                        this->executeActionpoint();
312                    }
313                }
314            }
315        }
316        else if (this->action_ == Action::FLY)
317        {
318            if (this->squaredDistanceToTarget() <= this->squaredaccuracy_)
319            {
320                this->nextActionpoint();   
321                this->executeActionpoint();
322            }
323        }
324        else if (this->action_ == Action::PROTECT)
325        {
326            if (!this->getProtect())
327            {
328                this->nextActionpoint();
329                this->executeActionpoint(); 
330            }
331            this->stayNearProtect();
332        }
333        else if (this->action_ == Action::ATTACK)
334        {   
335
336            if (!this->hasTarget())
337            {
338                this->nextActionpoint();
339                this->executeActionpoint();
340            }
341        }
342
343
344    }
345    void ActionpointController::setProtect (ControllableEntity* protect)
346    {
347        this->protect_ = protect;
348    }
349    ControllableEntity* ActionpointController::getProtect ()
350    {
351        return this->protect_;
352    }
353    void ActionpointController::addActionpoint(WorldEntity* actionpoint)
354    {
355        std::string actionName;
356        Vector3 position;
357        std::string targetName;
358        bool inLoop = false;
359        Point p;
360        if (actionpoint->getIdentifier()->getName() == "Actionpoint")
361        {
362            Actionpoint* ap = orxonox_cast<Actionpoint*> (actionpoint);
363            actionName = ap->getActionXML();
364            targetName = ap->getName();
365            position = ap->getWorldPosition();
366
367            if (this->bEndLoop_)
368            {
369                this->bInLoop_ = false;
370            }
371            if (!this->bInLoop_ && ap->getLoopStart())
372            {
373                this->bInLoop_ = true;
374            }
375            if (this->bInLoop_ && ap->getLoopEnd())
376            {
377                this->bEndLoop_ = true;
378            }
379            inLoop = this->bInLoop_;
380
381            Action::Value value;
382           
383            if ( actionName == "FIGHT" )
384            { value = Action::FIGHT; }
385            else if ( actionName == "FLY" )
386            { value = Action::FLY; }
387            else if ( actionName == "PROTECT" )
388            { value = Action::PROTECT; }
389            else if ( actionName == "NONE" )
390            { value = Action::NONE; }
391            else if ( actionName == "FIGHTALL" )
392            { value = Action::FIGHTALL; }
393            else if ( actionName == "ATTACK" )
394            { value = Action::ATTACK; }
395            else
396                ThrowException( ParseError, std::string( "Attempting to set an unknown Action: '" )+ actionName + "'." );
397            p.action = value; p.name = targetName; p.position = position; p.inLoop = inLoop;
398        }
399        else
400        {
401            inLoop = true;
402            p.action = Action::FLY; p.name = ""; p.position = actionpoint->getWorldPosition(); p.inLoop = inLoop;
403        }
404            parsedActionpoints_.push_back(p);
405            this->actionpoints_.push_back(actionpoint);
406    }
407    WorldEntity* ActionpointController::getActionpoint(unsigned int index) const
408    {
409        if (index < this->actionpoints_.size())
410            return this->actionpoints_[index];
411        else
412            return 0;
413    }
414
415    Action::Value ActionpointController::getAction ()
416    {
417        return this->action_;
418    }
419    std::string ActionpointController::getActionName()
420    {
421        switch ( this->action_ )
422        {
423            case Action::FIGHT:
424            { return "FIGHT"; }
425            case Action::FLY:
426            { return "FLY"; }
427            case Action::PROTECT:
428            { return "PROTECT"; }
429            case Action::NONE:
430            { return "NONE"; }
431            case Action::FIGHTALL:
432            { return "FIGHTALL"; }
433            case Action::ATTACK:
434            { return "ATTACK"; }
435            default:
436                return "NONE";
437                break;
438        }
439    }
440    void ActionpointController::setAction (Action::Value action)
441    {
442        this->action_ = action;
443    }
444    void ActionpointController::setAction (Action::Value action, ControllableEntity* target)
445    {
446        this->action_ = action;
447        if (action == Action::FIGHT || action == Action::FIGHTALL || action == Action::ATTACK)
448        {   
449            if (target)
450                this->setTarget (target);
451        }
452        else if (action == Action::PROTECT)
453        {
454            if (target)
455                this->setProtect (target);
456        }
457    }
458    void ActionpointController::setAction (Action::Value action, const Vector3& target)
459    {
460        this->action_ = action;
461        if (action == Action::FLY)
462        {
463            this->setTargetPosition (target);
464        }
465    }
466    void ActionpointController::setAction (Action::Value action, const Vector3& target,  const Quaternion& orient )
467    {
468        this->action_ = action;
469        if (action == Action::FLY)
470        {
471            this->setTargetPosition (target);
472            this->setTargetOrientation (orient);
473        } 
474    }
475   
476    //------------------------------------------------------------------------------
477    //------------------------------Actionpoint methods-----------------------------
478    //------------------------------------------------------------------------------
479
480    //POST: this starts doing what was asked by the last element of parsedActionpoints_,
481    //if last element was failed to be parsed, next element will be executed.
482    void ActionpointController::executeActionpoint()
483    {
484        if (!this || !this->getControllableEntity())
485            return;
486
487        Point p;
488        if (this->bLoop_ && !loopActionpoints_.empty())
489        {
490            p = loopActionpoints_.back();
491        }
492        else if (this->bLoop_)
493        {
494            this->bLoop_ = false;
495            return;
496        }
497        else if (!this->bLoop_ && !parsedActionpoints_.empty())
498        {
499            p = parsedActionpoints_.back();
500        }
501        else
502        {
503            if (!this || !this->getControllableEntity())
504                return;
505
506            this->setTarget(0);
507            this->setTargetPosition(this->getControllableEntity()->getWorldPosition());
508            this->action_ = Action::NONE;
509            return;
510        }
511        if (!this->bLoop_ && this->parsedActionpoints_.back().inLoop)
512        {
513            //MOVES all points that are in loop to a loop vector
514            this->fillLoop();
515            this->bLoop_ = true;
516            executeActionpoint();
517            return;
518        }
519        this->setAction (p.action);
520        if (!this || !this->getControllableEntity())
521            return;
522
523        switch (this->action_)
524        {
525            case Action::FIGHT:
526            {
527                std::string targetName = p.name;
528                if (targetName == "")
529                    break;
530                for (ObjectList<Pawn>::iterator itP = ObjectList<Pawn>::begin(); itP; ++itP)
531                {
532                    if (CommonController::getName(*itP) == targetName)
533                    {
534                        this->setTarget (static_cast<ControllableEntity*>(*itP));
535                    }
536                }
537                break;
538            }
539            case Action::FLY:
540            {
541                this->setTargetPosition( p.position );
542                if (this->squaredDistanceToTarget() <= this->squaredaccuracy_)
543                {
544                    this->nextActionpoint();
545                    this->executeActionpoint();
546                }
547                break;
548            }
549            case Action::PROTECT:
550            {
551                if (!this || !this->getControllableEntity())
552                    return;
553
554                std::string protectName = p.name;
555                if (protectName == "reservedKeyword:human")
556                {
557                    for (ObjectList<Pawn>::iterator itP = ObjectList<Pawn>::begin(); itP; ++itP)
558                    {
559                        if (orxonox_cast<ControllableEntity*>(*itP) && ((*itP)->getController()) && ((*itP)->getController()->getIdentifier()->getName() == "NewHumanController"))
560                        {
561                            this->setProtect (static_cast<ControllableEntity*>(*itP));
562                        }
563                    }
564                }
565                else
566                {
567                    for (ObjectList<Pawn>::iterator itP = ObjectList<Pawn>::begin(); itP; ++itP)
568                    {
569                        if (CommonController::getName(*itP) == protectName)
570                        {
571                            this->setProtect (static_cast<ControllableEntity*>(*itP));
572                        }
573                    }                           
574                }
575                if (!this->getProtect())
576                {
577                    this->nextActionpoint();
578                    this->executeActionpoint();
579                }
580                break;
581            }
582            case Action::NONE:
583            {
584                break;
585            }
586            case Action::FIGHTALL:
587            {
588                break;
589            }
590            case Action::ATTACK:
591            {
592                std::string targetName = p.name;
593
594                for (ObjectList<Pawn>::iterator itP = ObjectList<Pawn>::begin(); itP; ++itP)
595                {
596                    if (CommonController::getName(*itP) == targetName)
597                    {
598                        this->setTarget (static_cast<ControllableEntity*>(*itP));
599                    }
600                }
601                if (!this->hasTarget())
602                {
603                    this->nextActionpoint();
604                    this->executeActionpoint();
605                }
606                break;
607            }
608            default:
609                break;
610        }   
611    }
612
613   
614    void ActionpointController::stayNearProtect()
615    {
616        if (!this || !this->getControllableEntity())
617            return;
618
619        Vector3 targetRelativePosition(0, 300, 300);
620        if (!this->getProtect())
621        {
622            this->nextActionpoint();
623            return;
624        } 
625        Vector3 targetAbsolutePosition = ((this->getProtect()->getWorldPosition()) + 
626            (this->getProtect()->getWorldOrientation()* (targetRelativePosition)));
627        this->setTargetPosition(targetAbsolutePosition);
628        if (!this->getProtect())
629        {
630            this->nextActionpoint();
631            return;
632        } 
633        this->setTargetOrientation(this->getProtect()->getWorldOrientation());
634    }
635    void ActionpointController::nextActionpoint()
636    {
637        if (!this || !this->getControllableEntity())
638            return;
639        if (this->bLoop_)
640        {
641            if (this->bPatrolling_)
642            {
643                this->loopActionpoints_.pop_back();
644                this->bPatrolling_ = false;
645            }
646            else if (!this->loopActionpoints_.empty())
647            {
648                this->moveBackToTop();
649            }
650        }
651        else
652        {
653            if (!this->parsedActionpoints_.empty())
654            {
655                this->parsedActionpoints_.pop_back();
656            }           
657        }
658        this->setAction(Action::NONE);
659        this->bHasTargetPosition_ = false;
660    }
661    void ActionpointController::moveBackToTop()
662    {
663        if (!this || !this->getControllableEntity())
664            return;
665
666        Point temp = loopActionpoints_.back();
667        loopActionpoints_.pop_back();
668        std::reverse (loopActionpoints_.begin(), loopActionpoints_.end());
669        loopActionpoints_.push_back(temp);
670        std::reverse (loopActionpoints_.begin(), loopActionpoints_.end());
671    }
672    void ActionpointController::fillLoop()
673    {
674        loopActionpoints_.clear();
675        fillLoopReversed();
676        std::reverse (loopActionpoints_.begin(), loopActionpoints_.end());
677    }
678    void ActionpointController::fillLoopReversed()
679    {
680        if (parsedActionpoints_.back().inLoop)
681        {
682            loopActionpoints_.push_back(parsedActionpoints_.back());
683            parsedActionpoints_.pop_back();
684        }
685        if (parsedActionpoints_.back().inLoop)
686        {
687            fillLoopReversed();
688        }
689    }
690   
691    void ActionpointController::takeActionpoints (const std::vector<Point>& vector, const std::vector<Point>& loop, bool b)
692    {
693      this->parsedActionpoints_ = vector;
694      this->loopActionpoints_ = loop;
695      this->bLoop_ = b;
696      this->bTakenOver_ = true;
697    }
698    void ActionpointController::setClosestTarget()
699    {
700        this->setTarget (static_cast<ControllableEntity*>( closestTarget() ) ); 
701    }
702    Pawn* ActionpointController::closestTarget()
703    {
704        if (!this || !this->getControllableEntity())
705            return 0;
706
707        Pawn* closestTarget = 0;
708        float minDistance =  std::numeric_limits<float>::infinity();
709        Gametype* gt = this->getGametype();
710        for (ObjectList<Pawn>::iterator itP = ObjectList<Pawn>::begin(); itP; ++itP)
711        {
712            if ( CommonController::sameTeam (this->getControllableEntity(), static_cast<ControllableEntity*>(*itP), gt) )
713                continue;
714
715            float distance = CommonController::distance (*itP, this->getControllableEntity());
716            if (distance < minDistance)
717            {
718                closestTarget = *itP;
719                minDistance = distance;
720            }
721        }
722        if (closestTarget)
723        {
724           return closestTarget;
725        } 
726        return 0; 
727    }
728    void ActionpointController::startAttackingEnemiesThatAreClose()
729    {
730        if (!this || !this->getControllableEntity())
731            return;
732
733        //if (this->action_ != Action::FIGHT && this->action_ != Action::FIGHTALL)
734        {
735            if (!this->target_ || (this->target_ && CommonController::distance (this->getControllableEntity(), this->target_) > this->attackRange_))
736            {
737                Pawn* newTarget = this->closestTarget();
738                if ( newTarget && 
739                    CommonController::distance (this->getControllableEntity(), static_cast<ControllableEntity*>(newTarget))
740                        <= this->attackRange_ )
741                {
742                    if (!this || !this->getControllableEntity())
743                        return;
744                    this->setTarget(newTarget);
745                    if (this->bLoop_ && !this->bPatrolling_)
746                    {
747                        Point p = { Action::FIGHT, "", Vector3::ZERO, true };
748                        this->loopActionpoints_.push_back(p);
749                    }
750                    else if (!this->bPatrolling_)
751                    {
752                        //orxout (internal_error) << "found new target " << CommonController::getName(newTarget) <<endl;
753                        Point p = { Action::FIGHT, "", Vector3::ZERO, false };
754                        this->parsedActionpoints_.push_back(p);
755                    }
756                    this->bPatrolling_ = true;
757                    this->executeActionpoint();
758                }
759            }
760        }
761    }
762    int ActionpointController::nextActionpointControllerId_ = 0;
763    int ActionpointController::sTicks_ = 0;
764}   
Note: See TracBrowser for help on using the repository browser.