/************************************************************************************/
        protected void TrackNearestActors(int iActorCount, bool bDumpActorInfo, string strSearchSubstring)
        {
            TrackType eTrackType = TrackType.None;

            if (strSearchSubstring == "named")
            {
                eTrackType = TrackType.Named;
            }
            else if (strSearchSubstring == "resource")
            {
                eTrackType = TrackType.Resource;
            }
            else if (string.IsNullOrEmpty(strSearchSubstring.Trim()))
            {
                eTrackType = TrackType.EmptyName;
            }

            List <Actor> ActorList          = new List <Actor>();
            int          iValidActorCount   = 0;
            int          iInvalidActorCount = 0;

            foreach (Actor ThisActor in EnumActors())
            {
                bool bAddThisActor = false;

                switch (eTrackType)
                {
                case TrackType.Named:
                {
                    if (ThisActor.IsNamed && !ThisActor.IsAPet)
                    {
                        bAddThisActor = true;
                    }
                    break;
                }

                case TrackType.EmptyName:
                {
                    if (string.IsNullOrEmpty(ThisActor.Name.Trim()))
                    {
                        bAddThisActor = true;
                    }
                    break;
                }

                case TrackType.Resource:
                {
                    if (ThisActor.Type == "Resource")
                    {
                        bAddThisActor = true;
                    }
                    break;
                }

                default:
                {
                    string strActorName = ThisActor.Name.ToLower();
                    if (strActorName.Contains(strSearchSubstring))
                    {
                        bAddThisActor = true;
                    }
                    break;
                }
                }

                if (bAddThisActor)
                {
                    ActorList.Add(ThisActor);
                    iValidActorCount++;
                }
                else
                {
                    iInvalidActorCount++;
                }
            }

            if (ActorList.Count == 0)
            {
                Program.Log("Unable to find actor \"{0}\".", strSearchSubstring);
                RunCommand("/waypoint_cancel");
                return;
            }

            ActorList.Sort(new ActorDistanceComparer());

            /// Run the waypoint on the nearest actor.
            RunCommand("/waypoint {0}, {1}, {2}", ActorList[0].X, ActorList[0].Y, ActorList[0].Z);

            if (bDumpActorInfo)
            {
                FlexStringBuilder SummaryBuilder = new FlexStringBuilder();
                SummaryBuilder.AppendLine("{0} actor(s) found ({1} invalid) using \"{2}\".", iValidActorCount, iInvalidActorCount, strSearchSubstring);

                /// Now display the individual results.
                for (int iIndex = 0; iIndex < ActorList.Count && iIndex < 5; iIndex++)
                {
                    Actor ThisActor = ActorList[iIndex];
                    AppendActorInfo(SummaryBuilder, iIndex + 1, ThisActor);
                }

                Program.Log(SummaryBuilder.ToString());
            }
            return;
        }
        /************************************************************************************/
        protected virtual bool OnCustomSlashCommand(string strCommand, string[] astrParameters)
        {
            string strCondensedParameters = string.Join(" ", astrParameters).Trim();

            switch (strCommand)
            {
            case "gc_assist":
            {
                Actor OffensiveTargetActor = GetNestedCombatAssistTarget(astrParameters[0]);
                if (OffensiveTargetActor != null)
                {
                    OffensiveTargetActor.DoTarget();
                }
                return(true);
            }

            /// Directly acquire an offensive target.
            case "gc_attack":
            {
                Actor MyTargetActor = MeActor.Target();
                if (MyTargetActor.IsValid)
                {
                    /// Don't bother filtering. Just grab the ID, let the other code determine if it's a legit target.
                    m_iOffensiveTargetID = MyTargetActor.ID;
                    Program.Log("Offensive target set to \"{0}\" ({1}).", MyTargetActor.Name, m_iOffensiveTargetID);
                }
                else
                {
                    Program.Log("Invalid target selected.");
                }
                return(true);
            }

            /// This is the assist call; direct the bot to begin combat.
            /// Essentially combines gc_assist and gc_attack.
            case "gc_attackassist":
            {
                if (astrParameters.Length != 1)
                {
                    Program.Log("gc_attackassist: Missing name of player to assist!");
                    return(true);
                }

                /// Force a recalculation.
                m_OffensiveTargetEncounterActorDictionary.Clear();

                Actor OffensiveTargetActor = GetNestedCombatAssistTarget(astrParameters[0]);
                if (OffensiveTargetActor == null)
                {
                    /// Combat is now cancelled.
                    /// Maybe the commanding player misclicked or clicked off intentionally, but it doesn't matter.
                    WithdrawFromCombat();
                }
                else
                {
                    /// Successful target acquisition.
                    m_iOffensiveTargetID = OffensiveTargetActor.ID;
                    Program.Log("New offensive target: {0}", OffensiveTargetActor.Name);

                    /// An assist command promotes AFK into neutral positioning.
                    if (m_ePositioningStance == PositioningStance.DoNothing)
                    {
                        ChangePositioningStance(PositioningStance.NeutralPosition);
                    }
                }

                return(true);
            }

            /// Begin dropping all group buffs.
            case "gc_cancelgroupbuffs":
            {
                Program.Log("Cancelling all maintained group effects...");
                m_bClearGroupMaintained = true;
                return(true);
            }

            /// Arbitrarily change an INI setting on the fly.
            case "gc_changesetting":
            {
                string strKey   = string.Empty;
                string strValue = string.Empty;

                int iEqualsPosition = strCondensedParameters.IndexOf(" ");
                if (iEqualsPosition == -1)
                {
                    strKey = strCondensedParameters;
                }
                else
                {
                    strKey   = strCondensedParameters.Substring(0, iEqualsPosition).TrimEnd();
                    strValue = strCondensedParameters.Substring(iEqualsPosition + 1).TrimStart();
                }

                if (!string.IsNullOrEmpty(strKey))
                {
                    Program.Log("Changing setting \"{0}\" to new value \"{1}\"...", strKey, strValue);

                    IniFile FakeIniFile = new IniFile();
                    FakeIniFile.WriteString(strKey, strValue);
                    FakeIniFile.Mode = IniFile.TransferMode.Read;
                    TransferINISettings(FakeIniFile);
                    ApplySettings();
                }
                return(true);
            }

            case "gc_debug":
            {
                if (astrParameters.Length == 0)
                {
                    Program.Log("gc_debug: No option specified!");
                    return(true);
                }

                switch (astrParameters[0])
                {
                case "whopet":
                    Program.Log("gc_debug whopet: Listing player's primary pet...");
                    FlexStringBuilder ThisBuilder = new FlexStringBuilder();
                    AppendActorInfo(ThisBuilder, 1, Me.Pet());
                    Program.Log("{0}", ThisBuilder.ToString());
                    return(true);

                case "fullaffinities":
                    Program.Log("Attempting to remove all affinity constraints on the process and its threads...");
                    uint   uiCurrentProcessID = KERNEL32.GetCurrentProcessId();
                    IntPtr hProcess           = KERNEL32.OpenProcess(KERNEL32.ProcessAccess.SetInformation | KERNEL32.ProcessAccess.QueryInformation, false, uiCurrentProcessID);
                    if (hProcess == KERNEL32.INVALID_HANDLE_VALUE)
                    {
                        Program.Log("Unable to open process {0}.", uiCurrentProcessID);
                        return(true);
                    }

                    UIntPtr uiProcessAffinityMask;
                    UIntPtr uiSystemProcessorMask;
                    KERNEL32.GetProcessAffinityMask(hProcess, out uiProcessAffinityMask, out uiSystemProcessorMask);
                    Program.Log("Process {0} previous affinity mask: 0x{1:X8}", uiCurrentProcessID, uiProcessAffinityMask);

                    KERNEL32.SetProcessAffinityMask(hProcess, uiSystemProcessorMask);
                    KERNEL32.CloseHandle(hProcess);

                    Program.Log("Attempting to remove all affinity constraints on the process threads...");
                    foreach (KERNEL32.THREADENTRY32 ThisThread in KERNEL32.EnumProcessThreads(uiCurrentProcessID))
                    {
                        IntPtr hThread = KERNEL32.OpenThread(KERNEL32.ThreadAccess.SetInformation | KERNEL32.ThreadAccess.QueryInformation, false, ThisThread.th32ThreadID);
                        if (hThread != IntPtr.Zero)
                        {
                            UIntPtr uiOldThreadAffinityMask = KERNEL32.SetThreadAffinityMask(hThread, uiSystemProcessorMask);
                            Program.Log("Thread {0} previous affinity mask: 0x{1:X8}", ThisThread.th32ThreadID, uiOldThreadAffinityMask);
                            KERNEL32.CloseHandle(hThread);
                        }
                        else
                        {
                            Program.Log("Can't open thread {0}.", ThisThread.th32ThreadID);
                        }
                    }
                    return(true);

                case "memstat":
                    Process CurrentProcess = Process.GetCurrentProcess();
                    Program.Log("Virtual Allocation: {0}", CustomFormatter.FormatByteCount(CurrentProcess.VirtualMemorySize64, "0.00"));
                    Program.Log("Working Set: {0}", CustomFormatter.FormatByteCount(CurrentProcess.WorkingSet64, "0.00"));
                    Program.Log(".NET Heap Allocation: {0}", CustomFormatter.FormatByteCount(GC.GetTotalMemory(false), "0.00"));
                    Program.Log("Threads: {0}", CurrentProcess.Threads.Count);
                    return(true);
                }

                Program.Log("Usage: gc_debug (whopet|fullaffinities|memstat)");
                return(true);
            }

            case "gc_defaultverb":
            {
                string strActorName        = strCondensedParameters;
                Actor  IntendedTargetActor = null;

                /// Grab the targetted actor.
                if (string.IsNullOrEmpty(strActorName))
                {
                    Actor CurrentTargetActor = MeActor.Target();
                    if (CurrentTargetActor.IsValid)
                    {
                        IntendedTargetActor = CurrentTargetActor;
                    }
                }

                /// Grab the so-named actor nearest to the commander.
                else
                {
                    double fNearestDistance = 50.0;
                    foreach (Actor ThisActor in EnumActors(strActorName))
                    {
                        if (ThisActor.Name != strActorName)
                        {
                            continue;
                        }

                        double fThisDistance = ThisActor.Distance;
                        if (fThisDistance < fNearestDistance)
                        {
                            fNearestDistance    = fThisDistance;
                            IntendedTargetActor = ThisActor;
                        }
                    }
                }

                /// No such actor found.
                if (IntendedTargetActor == null)
                {
                    Program.Log("gc_defaultverb: actor not found.");
                }
                else
                {
                    Program.Log("gc_defaultverb: Attempting to apply default verb on actor \"{0}\" ({1}).", strActorName, IntendedTargetActor.ID);
                    IntendedTargetActor.DoubleClick();
                }

                return(true);
            }

            case "gc_distance":
            {
                Actor MyTargetActor = MeActor.Target();
                if (MyTargetActor.IsValid)
                {
                    string strOutput = string.Format("Distance to target \"{0}\" ({1}): {2} meter(s).", MyTargetActor.Name, MyTargetActor.ID, MyTargetActor.Distance);
                    RunCommand("/announce {0}", strOutput);
                    Program.Log(strOutput);
                }
                return(true);
            }

            case "gc_dumpabilities":
            {
                /// Dump all abilities to a .CSV file specified as a parameter, on the next knowledge book refresh.
                s_strKnowledgeBookDumpPath = strCondensedParameters;
                s_bRefreshKnowledgeBook    = true;
                return(true);
            }

            case "gc_exit":
            {
                Program.Log("Exit command received!");
                s_bContinueBot = false;
                return(true);
            }

            case "gc_exitprocess":
            {
                Process.GetCurrentProcess().Kill();
                return(true);
            }

            case "gc_findactor":
            {
                string strSearchString = strCondensedParameters.ToLower();

                TrackNearestActors(5, true, strSearchString);
                return(true);
            }

            case "gc_openini":
            {
                Program.SafeShellExecute(s_strCurrentINIFilePath);
                return(true);
            }

            case "gc_openoverridesini":
            {
                Program.SafeShellExecute(s_strSharedOverridesINIFilePath);
                return(true);
            }

            case "gc_reloadsettings":
            {
                ReadINISettings();
                ReleaseAllKeys();                         /// If there's a bug, this will cure it. If not, no loss.
                s_bRefreshKnowledgeBook = true;
                return(true);
            }

            case "gc_stance":
            {
                if (astrParameters.Length < 1)
                {
                    Program.Log("gc_stance failed: Missing stance parameter.");
                    return(true);
                }

                string strStance = astrParameters[0].ToLower();
                switch (strStance)
                {
                case "afk":
                    Program.Log("Now ignoring all non-stance commands.");
                    ChangePositioningStance(PositioningStance.DoNothing);
                    break;

                case "customfollow":
                    m_fCurrentMovementTargetCoordinateTolerance = m_fCustomAutoFollowMaximumRange;
                    m_strPositionalCommandingPlayer             = GetFirstExistingPartyMember(m_astrAutoFollowTargets, false);
                    ChangePositioningStance(PositioningStance.CustomAutoFollow);
                    break;

                case "shadow":
                    m_fCurrentMovementTargetCoordinateTolerance = m_fStayInPlaceTolerance;
                    m_strPositionalCommandingPlayer             = GetFirstExistingPartyMember(m_astrAutoFollowTargets, false);
                    ChangePositioningStance(PositioningStance.CustomAutoFollow);
                    break;

                case "normal":
                case "follow":
                    Program.Log("Positioning is now regular auto-follow.");
                    ChangePositioningStance(PositioningStance.AutoFollow);
                    break;

                case "neutral":
                    Program.Log("Positioning is now neutral.");
                    ChangePositioningStance(PositioningStance.NeutralPosition);
                    break;

                case "dash":
                    Program.Log("Dashing forward.");
                    ChangePositioningStance(PositioningStance.ForwardDash);
                    break;

                case "stay":
                    Program.Log("Locking position to where the commanding player is now standing.");
                    m_strPositionalCommandingPlayer = GetFirstExistingPartyMember(m_astrAutoFollowTargets, false);
                    ChangePositioningStance(PositioningStance.StayInPlace);
                    break;

                case "stayat":
                    if (astrParameters.Length >= 4)
                    {
                        Point3D NewSpot = new Point3D();
                        if (double.TryParse(astrParameters[1], out NewSpot.X) &&
                            double.TryParse(astrParameters[2], out NewSpot.Y) &&
                            double.TryParse(astrParameters[3], out NewSpot.Z))
                        {
                            m_strPositionalCommandingPlayer = string.Empty;
                            m_ptStayLocation = NewSpot;
                            ChangePositioningStance(PositioningStance.StayInPlace);
                        }
                        else
                        {
                            Program.Log("Bad coordinate input format.");
                        }
                    }
                    else
                    {
                        Program.Log("You need 3 values to form a 3-D coordinate, but you specified {0}.", astrParameters.Length - 1);
                    }
                    break;

                case "stayself":
                    Program.Log("Locking position to where you are now standing.");
                    m_strPositionalCommandingPlayer = Name;
                    ChangePositioningStance(PositioningStance.StayInPlace);
                    break;

                default:
                    Program.Log("Stance not recognized.");
                    break;
                }

                return(true);
            }

            case "gc_spawnwatch":
            {
                m_bSpawnWatchTargetAnnounced = false;
                m_strSpawnWatchTarget        = strCondensedParameters.ToLower().Trim();
                Program.Log("Bot will now scan for actor \"{0}\".", m_strSpawnWatchTarget);
                ChangePositioningStance(PositioningStance.SpawnWatch);
                return(true);
            }

            /// A super-powerful direct targetter.
            case "gc_target":
            {
                Actor  TargetActor     = null;
                string strSearchString = strCondensedParameters.ToLower();

                /// If they give an actual actor ID, then that's awesome.
                int iActorID = -1;
                if (int.TryParse(strSearchString, out iActorID))
                {
                    TargetActor = GetActor(iActorID);
                }
                else
                {
                    foreach (Actor ThisActor in EnumActors())
                    {
                        if (ThisActor.Name.ToLower().Contains(strSearchString))
                        {
                            TargetActor = ThisActor;
                            break;
                        }
                    }
                }

                if (TargetActor != null)
                {
                    TargetActor.DoTarget();
                }
                return(true);
            }

            /// Update the waypoint at a regular interval with the nearest location of an actor search string.
            case "gc_trackactor":
            {
                if (astrParameters.Length < 1)
                {
                    Program.Log("gc_trackactor failed: Missing parameter.");
                    return(true);
                }

                string strInterval = astrParameters[0].Trim().ToLower();
                if (strInterval == "off")
                {
                    RunCommand("/waypoint_cancel");
                    Program.Log("Actor tracking mode disabled.");
                    m_bTrackActor = false;
                    return(true);
                }

                double fIntervalSeconds = 0;
                if (!double.TryParse(strInterval, out fIntervalSeconds) || fIntervalSeconds < 0.0)
                {
                    Program.Log("gc_trackactor failed: Invalid interval parameter.");
                    return(false);
                }

                List <string> astrRemainingParameters = new List <string>(astrParameters);
                if (astrRemainingParameters.Count > 0)
                {
                    astrRemainingParameters.RemoveAt(0);
                }

                string strRemainingCondensedParameters = string.Join(" ", astrRemainingParameters.ToArray()).Trim().ToLower();

                /// Now we turn the mode on.
                m_TrackActorInterval        = TimeSpan.FromSeconds(fIntervalSeconds);
                m_NextTrackActorAttemptTime = CurrentCycleTimestamp;
                m_strTrackActorSubstring    = strRemainingCondensedParameters;
                m_bTrackActor = true;
                return(true);
            }

            /// Test the text-to-speech synthesizer using the current settings.
            case "gc_tts":
            {
                SayText(strCondensedParameters);
                return(true);
            }

            case "gc_version":
            {
                Program.DisplayVersion();
                Program.Log("ISXEQ2 Version: " + s_ISXEQ2.Version);
                return(true);
            }

            /// Clear the offensive target.
            case "gc_withdraw":
            {
                Program.Log("Offensive target withdrawn.");
                WithdrawFromCombat();
                return(true);
            }
            }

            return(false);
        }