// 24Feb2013-08:10UTC chinajade
        public override void OnFinished()
        {
            // Defend against being called multiple times (just in case)...
            if (!IsOnFinishedRun)
            {
                if (Targeting.Instance != null)
                {
                    Targeting.Instance.IncludeTargetsFilter -= TargetFilter_IncludeTargets;
                    Targeting.Instance.RemoveTargetsFilter  -= TargetFilter_RemoveTargets;
                    Targeting.Instance.WeighTargetsFilter   -= TargetFilter_WeighTargets;
                }


                // NB: we don't unhook _behaviorTreeHook_Main
                // This was installed when HB created the behavior, and its up to HB to unhook it

                BehaviorHookRemove("Combat_Main", ref _behaviorTreeHook_CombatMain);
                BehaviorHookRemove("Combat_Only", ref _behaviorTreeHook_CombatOnly);
                BehaviorHookRemove("Death_Main", ref _behaviorTreeHook_DeathMain);
                BehaviorHookRemove("Questbot_Main", ref _behaviorTreeHook_QuestbotMain);

                // Remove temporary blackspots...
                if (_temporaryBlackspots != null)
                {
                    BlackspotManager.RemoveBlackspots(_temporaryBlackspots.GetBlackspots());
                    _temporaryBlackspots = null;
                }

                // Restore configuration...
                if (_configMemento != null)
                {
                    _configMemento.Dispose();
                    _configMemento = null;
                }

                // Make sure we don't leave stale POIs set after finishing a QB.
                // This could happen if the user used a TerminateWhen to finish it
                // for example.
                BotPoi.Clear("Finished " + GetType().Name);

                TreeRoot.GoalText   = string.Empty;
                TreeRoot.StatusText = string.Empty;

                // Report the behavior run time...
                if (_behaviorRunTimer.IsRunning)
                {
                    _behaviorRunTimer.Stop();
                    QBCLog.DeveloperInfo("Behavior completed in {0}", Utility.PrettyTime(_behaviorRunTimer.Elapsed));
                }

                base.OnFinished();
                QBCLog.BehaviorLoggingContext = null;
                IsOnFinishedRun = true;
            }
        }
        protected QuestBehaviorBase(Dictionary <string, string> args)
            : base(args)
        {
            QBCLog.BehaviorLoggingContext = this;

            try
            {
                // Quest handling...
                // QuestRequirement* attributes are explained here...
                //    http://www.thebuddyforum.com/mediawiki/index.php?title=Honorbuddy_Programming_Cookbook:_QuestId_for_Custom_Behaviors
                // ...and also used for IsDone processing.
                // NB: quest ID is stored in a field which will be used for coherency checks.
                var questId         = GetAttributeAsNullable <int>("QuestId", false, ConstrainAs.QuestId(this), null) ?? 0;
                var variantQuestIds =
                    new HashSet <int>(
                        GetAttributeAsArray("VariantQuestIds", false, ConstrainAs.QuestId(this), null, null) ??
                        new int[0]);

                if (questId != 0)
                {
                    if (variantQuestIds.Any())
                    {
                        _providedQuestIdAndQuestVariantIds = true;
                    }
                    variantQuestIds.Add(questId);
                }
                VariantQuestIds          = variantQuestIds;
                QuestRequirementComplete = GetAttributeAsNullable <QuestCompleteRequirement>("QuestCompleteRequirement", false, null, null) ?? QuestCompleteRequirement.NotComplete;
                QuestRequirementInLog    = GetAttributeAsNullable <QuestInLogRequirement>("QuestInLogRequirement", false, null, null) ?? QuestInLogRequirement.InLog;
                QuestObjectiveIndex      = GetAttributeAsNullable <int>("QuestObjectiveIndex", false, new ConstrainTo.Domain <int>(1, 10), null) ?? 0;

                // Tunables...
                IgnoreMobsInBlackspots = GetAttributeAsNullable <bool>("IgnoreMobsInBlackspots", false, null, null) ?? true;
                MovementBy             = GetAttributeAsNullable <MovementByType>("MovementBy", false, null, null) ?? MovementByType.FlightorPreferred;
                NonCompeteDistance     = GetAttributeAsNullable <double>("NonCompeteDistance", false, new ConstrainTo.Domain <double>(0.0, 50.0), null) ?? 20.0;

                TerminateAtMaxRunTimeSecs = GetAttributeAsNullable <int>("TerminateAtMaxRunTimeSecs", false, new ConstrainTo.Domain <int>(0, int.MaxValue), null) ?? int.MaxValue;

                // Go ahead and compile the "TerminateWhen" expression to look for problems...
                // Doing this in the constructor allows us to catch 'blind change'problems when ProfileDebuggingMode is turned on.
                // If there is a problem, an exception will be thrown (and handled here).
                var terminateWhenExpression = GetAttributeAs <string>("TerminateWhen", false, ConstrainAs.StringNonEmpty, null);
                TerminateWhenCompiledExpression = Utility.ProduceParameterlessCompiledExpression <bool>(terminateWhenExpression);
                TerminateWhen = Utility.ProduceCachedValueFromCompiledExpression(TerminateWhenCompiledExpression, false);

                TerminationChecksQuestProgress = GetAttributeAsNullable <bool>("TerminationChecksQuestProgress", false, null, null) ?? true;

                // Dummy attributes...
                // These attributes are accepted, but not used.  They are here to help the profile writer document without
                // causing "unknown attribute" warnings to be emitted.
                GetAttributeAs <string>("QuestName", false, ConstrainAs.StringNonEmpty, null);

                // XML types

                // Add temporary avoid mobs, if any were specified...
                // NB: ConfigMemento will restore the orginal list in OnFinished
                _temporaryAvoidMobs = AvoidMobsType.GetOrCreate(Element, "AvoidMobs");

                // Add temporary blackspots, if any were specified...
                // NB: Ideally, we'd save and restore the original blackspot list.  However,
                // BlackspotManager does not currently give us a way to "see" what is currently
                // on the list.
                _temporaryBlackspots = BlackspotsType.GetOrCreate(Element, "Blackspots");
                PursuitList          = PursuitListType.GetOrCreate(Element, "PursuitList");
            }

            catch (Exception except)
            {
                if (Query.IsExceptionReportingNeeded(except))
                {
                    // Maintenance problems occur for a number of reasons.  The primary two are...
                    // * Changes were made to the behavior, and boundary conditions weren't properly tested.
                    // * The Honorbuddy core was changed, and the behavior wasn't adjusted for the new changes.
                    // In any case, we pinpoint the source of the problem area here, and hopefully it can be quickly
                    // resolved.
                    QBCLog.Exception(except);
                }
                IsAttributeProblem = true;
            }
        }