#include "../MQ2Plugin.h"
#include "math.h"

//uncomment this line to enable debugspew spam
//#define DEBUGALL //not really all
//debugspew for stucklogic
//#define DEBUGSTUCK
//debugspew for trivial messages
//#define DEBUGOTHER
//debugspew for nohott
//#define DEBUGNOHOTT

//version information
#define MODULE_NAME "MQ2MoveUtils"
#define MODULE_VERSION 9.0314
PreSetup(MODULE_NAME);
PLUGIN_VERSION(MODULE_VERSION);

// **************************************
// Aesthetics in the function calls
// ---------------------------------
// check calling command
const unsigned char USED_STICK = 1;
const unsigned char USED_MOVETO = 2;
const unsigned char USED_CIRCLE = 3;
const unsigned char USED_MAKECAMP = 4;
const unsigned char APPLY_TO_ALL = 5;
// reset altcamp or make new altcamp
const unsigned char SET_ALT = 20;
const unsigned char RESET_ALT = 21;
// direction to move
const unsigned char GO_FORWARDS = 30;
const unsigned char GO_BACKWARDS = 31;
const unsigned char GO_LEFT = 32;
const unsigned char GO_RIGHT = 33;
// WriteLine verbosity level to check
const unsigned char VERB_ALL = 40;
const unsigned char VERB_VERB = 41;
const unsigned char VERB_FULL = 42;
const unsigned char VERB_ENFORCE = 43;
// help output
const unsigned char HELP_SETTINGS = 50;
// debug output
const unsigned char DBG_ALL = 200;
const unsigned char DBG_STUCK = 201;
const unsigned char DBG_OTHER = 202;
const unsigned char DBG_NOHOTT = 203;
const unsigned char DBG_DISABLE = 204;
// so 265.0 never happens again
const float CIRCLE_QUARTER  = 90.0f;
const float CIRCLE_HALF     = 180.0f;
const float CIRCLE_MAX      = 360.0f;
const float HEADING_QUARTER = 128.0f;
const float HEADING_HALF    = 256.0f;
const float HEADING_MAX     = 512.0f;
// **************************************

// import / export
PLUGIN_API bool bStickOn = false; // stick active or off, exported for other plugins to have a sure way of always knowing
// note to any developers: if you intend to use these exports and want to shut off stick, do not flip this bool off directly,
// call StickCommand with "off" or call EndPreviousCmd(bool, 1, false), where bool= do you want to halt movement?
PLUGIN_API void EndPreviousCmd(bool bKillAllMovement = false, unsigned char ucCmdUsed = APPLY_TO_ALL, bool bDontResetSelf = false);
PLUGIN_API void StickCommand(PSPAWNINFO pChar,    char* szLine);
PLUGIN_API void MoveToCommand(PSPAWNINFO pChar,   char* szLine);
PLUGIN_API void MakeCampCommand(PSPAWNINFO pChar, char* szLine);
PLUGIN_API void CircleCommand(PSPAWNINFO pChar,   char* szLine);
PLUGIN_API void StickStatusOutput(void);
bool* pbMULoaded; // imported from mq2melee on initialize
char szLastStickCmd[MAX_STRING] = {0}; // stores last /stick parameter issued, displayed by mq2melee's /mutils cmd
// end import / export

// main variables
char szMsg[MAX_STRING] = {0}; // use for generic msg output
char szDebugName[MAX_STRING] = {0}; // debug file name
char szReturnMsg[200] = "/moveto location"; // used to swap messages for moveto and makecamp [alt]return
char* pNotNum = {0}; // pointer for checking non-numerics
bool bMoveBindsLoaded = false; // loaded binds or not, used on plugin load/unload
bool bImAggro = false; // used by MoveUtils.Aggro TLO
bool bAllPaused = false; // pauses all operations, used with /[any_command] 'pause' param
bool bAutoPause = true; // pause automatically when casting if true, loads with ini
bool bPauseOnMove = false; // used to implement 'mpause', if active or not, loads with ini
bool bPauseOnMouse = false; // used to implement 'mousepause' if mouselooking, loads with ini
bool bBreakOnMouse = false; // used to implement break from command if mouselooking
bool bBreakOnKB = true; // used to implement break from command if key pressed
bool bManuallyMoving = false; // used to implement 'mpause', true while keys are pressed
bool bCheckStuck = true; // stuck check logic active or off, loads with ini
bool bVerbosity = true; // stick verbosity, loads with ini
bool bFullVerbosity = true; // used to output info about almost every action taken by plugin, loads with ini
bool bTotalSilence = false; // used to prevent all chat output entirely
bool bAutoSave = true; // used to autosave ini file changes upon command usage
bool bFeignSupport = false; // dont stand if currently FD, loads with ini
bool bSaveByChar = true; // save ini settings for individual characters or not, loads with ini
bool bDisregardMe = false; // ignore char settings on a char-by-char basis
bool bHideHelp = false; // never display help output
bool bBreakOnWarp = true; // break all operations when a mob warps out of reach, loads with ini
bool bBreakOnGate = true; // break all operations if "mob_name Gates." message occurs, loads with ini
bool bBreakOnSummon = false; // used to break all operations when you're coh'd/summoned
bool bBrokeOnSummon = false; // set to halt future command use if this took place

// ****************************************
// bools controlled by command usage
// ----------------------------------------
// makecamp
bool bMakeCamp = false; // makecamp active or off, makecamp works in conjunction with stick, circle, moveto
bool bAltCamp = false; // true if alt camp values are in place
bool bCampPlayer = false; // makecamp player on or off
bool bMakeCampReturn = false; // true when manually forcing a return to camp
bool bAltCampReturn = false; // true when returning to alt camp
bool bReturnInProcess = false; // used by makecamp if return is active
bool bCampReturning = false; // state awareness for returning to camp if not manual
// moveto
bool bMoveToOn = false; // moveto active or off
bool bLooseMoveTo = true; // used by moveto to simulate more natural movement
bool bYPrecision = false; // used by moveto to determine if arrivaldist is only checked against Y
bool bXPrecision = false; // used by moveto to determine if arrivaldist is only checked against X
// stick
bool bSetDist = false;  // used by stick to determine if dist from mob has been set yet
bool bCompareMobAgain = false; // used in nohott checking
bool bImCheckingAggro = false; // used in nohott checking
bool bLooseStick = true; // used by stick to simulate more natural movement (/stick loose)
bool bMoveBack = false; // used by stick to maintain stick distance if you get too close, back up a few steps (/stick moveback)
bool bStickBehind = false; // used by stick to always stick to the rear arc of target (/stick behind)
bool bStickBehindOnce = false; // used by stick to stick behind mob once then not care about position (/stick movebehind once)
bool bStickFront = false; // used by stick to move only to the front arc of target (/stick front)
bool bStickNotFront = false; // used by stick to move anywhere but the front arc of target (/stick !front)
bool bStickPin = false; // used by stick to stick from the left or right side of the target (/stick pin)
bool bStickSnaproll = false; // used by stick to snaproll to the rear of the mob (/stick snaproll)
bool bStickHold = false; // used by stick to keep sticking to old target if target changes (/stick hold)
bool bUnderwater = false; // used by stick to determine if we want to look up or down on z axis (/stick uw)
bool bStickNewMob = false; // keep stick active, and restick when new target acquired (wont work with stick id) - the [always] param
bool bStickNewTarget = false; // set to true if stick always and have a target
// circle
bool bCircling = false; // circle active or off, disables stick & moveto
bool bBackwards = false; // used by circle to run backwards instead of forwards
bool bClockwise = true; // used by circle to move clockwise or counter-clockwise
bool bDrunken = false; // used by circle to move in turns at random intervals instead of a perfect circle
// ***************************************************************
// -------- COMMAND-RELATED INI SETTINGS ----------------------
bool bLeash = false; // user-value: camp leashing active or off
bool bScatter = false; // user-value: camp return scattering
bool bReturnNoAggro = false; // user-value: if true return to camp only if not aggro, false = return if no target
bool bReturnNotLooting = false; // user-vaue: if true, check to be sure we are not looting before returning to camp
bool bReturnNoTarget = false; // user-value: if true, target check does not apply
bool bAlwaysLooseMoveTo = true; // user-value: always use loose /moveto
bool bAlwaysLooseStick = true; // user-value: always use loose /stick
bool bNoHoTT = false; // user-value: stick, check if aggro without HoTT for pin/!front/behind
bool bSpinInCircles = false; // user-value: if true, stick front ignores being on HoTT
bool bAlwaysBack = false; // user-value: circle, always backwards
bool bAlwaysDrunk = false; // user-value: circle, always drunken
bool bAlwaysCCW = false; // user-value: circle, always counter-clockwise
bool bWalkMoveTo = true; // user-value: walk if close to moveto arrival dist
// ***************************************************************

// stucklogic bools
bool bStickWasOn = false; // used by autopause to process if stick was on
bool bCirclingWasOn = false; // used by autopause to process if circle was on
bool bCmdMovedFwd = false; // used by stuck logic to determine if any command has moved forward
bool bCmdMovedSide = false; // stucklogic sideways
bool bTryToJump = true; // user-value: if true, try to jump when stuck
bool bTurnHalf = true; // user-value: if true, reset heading and try other dir if we go halfway without correction
// stuck handling: state check
unsigned short usiPulseCheck = 6; // user-value: how often/pulses to check for stuck/unstuck correction, loads with ini
unsigned short usiPulseUnstuck = 10; // user-value: if sistuckdec == this, force unstuck
unsigned short usiStuck = 0; // increments each pulse we haven't moved beyond fDistStuck until we reach usiPulseCheck value which == we are stuck
unsigned short usiStuckDec = 0;  // increments each pulse we've moved again after being stuck, enough consecutive increments = consider unstuck
// stuck handling: distance variables
float fDistStuck = 0.2f; // user-value: dist needed to move per pulse or else stuck
float fPrevX = 0.0f; // stores your X loc for comparison per pulse
float fPrevY = 0.0f; // stores your Y loc for comparison per pulse
float fPrevZ = 0.0f; // stores your Z loc for comparison per pulse
float fPulseAvg = 1.0f; // calculates average movement per pulse, 2.0 is a baseline, it will autoadjust as needed
float fStuckTurn = 14.0f; // stores turn increment values for left/right
float fOrigHead = 0.0f; // stores original heading for optional half turning
float fHalfHead = 0.0f; // stores value for halfway from orig head
float fCompPosHead = 0.0f; // stores value for comparison for halfhead
float fCompNegHead = 0.0f; // stores values for comparison for halfhead
// MovingAvg max pulses to average
#define MAXRINGSIZE 32

// makecamp floats
float fAnchorX = 0.0f; // anchor planted at X
float fAnchorY = 0.0f; // anchor planted at Y
float fAltAnchorX = 0.0f; // alt anchor X
float fAltAnchorY = 0.0f; // alt anchor Y
float fLeashLength = 50.0f; // length of leash checked against anchor if bLeash on
float fCampRadius = 40.0f; // if we exceed this value and have no target, makecamp returns
float fAltCampRadius = 0.0f; // altcamp radius
float fCampDist = 0.0f; // used by MakeCamp.Distance TLO
float fAltCampDist = 0.0f; // used by MakeCamp.AltDistance TLO
float fGotoX = 0.0f; // loc for return
float fGotoY = 0.0f; // loc for return
float fBearing = 0.0f; // bearing for scatter
float fScatDist = 10.0f; // dist for scatter
float fScatter = 10.0f; // size of scatter
// moveto floats
float fLocX = 0.0f; // location to move to
float fLocY = 0.0f; // location to move to
float fMoveDist = 5.0f; // how close to moveto location is considered acceptable, loads with ini
float fMoveDistY = 5.0f; // how close to moveto Y location is acceptable, loads with ini
float fMoveDistX = 5.0f; // how close to moveto X location is acceptable, loads with ini
float fMoveDistINI = 5.0f; // preserves value read from ini
float fMoveDistMod = 0.0f; 
float fMoveDistModINI = 0.0f; // preserves value read from ini
float fGetMoveToDist = 0.0f; // stores calculation for distance to moveto loc onpulse
// stick
float fStickDist = 0.0f; // defaults to melee range or set to 10 by /stick 10 for example
float fBreakDist = 250.0f; // distance target can get away from you before you break from command, loads with ini
float fCurrentDist = 0.0f; // set onpulse, stores your current distance from stick target
float fStickDistMod = 0.0f; // set by user, modifier to stickdist (best used if plugin is auto-setting dist)
float fStickDistModP = 1.0f; // set by user, modifier to stickdist using percent (best used if plugin is auto-setting dist)
float fDistFromStick = 0.0f; // set in wegetsignal
unsigned long ulOldStickID = 0; // set in wegetsignal
// circle floats
float fCircleY = 0.0f; // center of your circle when active
float fCircleX = 0.0f; // center of your circle when active
float fCircleRadiusMod = 0.0f;
float fCircleRadius = 30.0f; // stores value of radius size, loads with ini
// breakonsummon floats
float fBreakSummonDist = 8.0f; // distance your character can move in a single pulse before command halting
float fNewCompare = 0.0f; // set onpulse, stores your current distance comparison to previous pulse
float fMyCurX = 0.0f; // set onpulse, value of X
float fMyCurY = 0.0f; // set onpulse, value of Y
// AdjustHeading floats
float fTurnRate = 14.0f; // loads with ini, rate at which to turn using loose
float fAdjustHead = 10000.0f; // change to adjust heading onpulse
// used by snaproll
float fSnapHeading = 0.0f;
float fSnapX = 0.0f;
float fSnapY = 0.0f;
float fSnapBearing = HEADING_HALF;

// time calculations
int iMinPauseDelay = 500; // min random delay for resume from mpause/mousepause
int iMaxPauseDelay = 5000; // max random delay for resume from mpause/mousepause
int iMinCampDelay = 500; // min random delay for returning to camp
int iMaxCampDelay = 1500; // max random delay for returning to camp
int iCampReturnTime = 0; // diff_in_ms result is stored here, return to camp
int iPauseReturnTime = 0; // diff_in_ms result stored here, return from mpause/mousepause

// used for timers
SYSTEMTIME stReturnTime;
SYSTEMTIME stPrevStick;
SYSTEMTIME stPrevCirc;
SYSTEMTIME stNoHoTTCheck;

// stick id/hold & makecamp player retention
unsigned long ulStickID = 0; // stores id of target when using stick hold
eSpawnType stickTarget_Type; // stores spawn type of target when using stick hold (ie if target changes to corpse it will stop sticking to it)
unsigned long ulCampPlayerID = 0; // stores id of makecamp player
eSpawnType campPlayer_Type; // stores spawn type of makecamp player so you dont camp a player that has become a corpse

// function prototypes
void SpewDebug(unsigned char ucDbgType, char* szOuput, ...);
void OutputHelp(unsigned char ucCmdUsed, bool bOnlyCmdHelp = false);
void ResetCamp(bool bOffMsg, unsigned char ucAltCamp = 0);
void HandleOurCmd(unsigned char ucCmdUsed, char* szInput);
void ChangeSetting(unsigned char ucCmdUsed, bool bToggle, char szSetting[MAX_STRING]);
void AdjustHeading(float fNewHead);
void DoUnstickBind(char* szName, bool bDown);
void Save_INI(void);
void Load_INI(void);
void DebugToWnd(unsigned char ucCmdUsed);
void DebugToINI(unsigned char ucCmdUsed);
void DoMovement(unsigned char ucDirection, bool bHoldKey = true, bool bUseWalking = false);
void WriteLine(char szOutput[MAX_STRING], unsigned char ucFilterLevel = VERB_ALL);
void CampReturn(float fX, float fY);
float GetRandomNumber(float fN);
float CalcAngularDist(float fH1, float fH2);
int diff_in_ms(SYSTEMTIME &stCurr, SYSTEMTIME &stPrev);
void DoUnstickBind(char* szName, bool bKeyPressed);
void CreateBinds(void);
void DestroyBinds(void);

///////////////////////////////////////
// Begin Custom Top-Level Objects

class MQ2MakeCampType*   pMakeCampType   = 0;
class MQ2StickType*      pStickType      = 0;
class MQ2MoveToType*     pMoveToType     = 0;
class MQ2CircleType*     pCircleType     = 0;
class MQ2MoveUtilsType*  pMoveUtilsType  = 0;

class MQ2MakeCampType : public MQ2Type
{
public:
    enum MakeCampMembers
    {
        Status = 1,
        Leash = 2,
        AnchorX = 3,
        AnchorY = 4,
        LeashLength = 5,
        CampRadius = 6,
        MinDelay = 7,
        MaxDelay = 8,
        Returning = 9,
        AltAnchorX = 10,
        AltAnchorY = 11,
        CampDist = 12,
        AltCampDist = 13,
        AltRadius = 14,
        Scatter = 15,
        ReturnNoAggro = 16,
        ReturnNotLooting = 17,
        ReturnNoTarget = 18,
        Bearing = 19,
        ScatDist = 20,
        ScatSize = 21,
    };

    MQ2MakeCampType():MQ2Type("makecamp")
    {
        TypeMember(Status);
        TypeMember(Leash);
        TypeMember(AnchorX);
        TypeMember(AnchorY);
        TypeMember(LeashLength);
        TypeMember(CampRadius);
        TypeMember(MinDelay);
        TypeMember(MaxDelay);
        TypeMember(Returning);
        TypeMember(AltAnchorX);
        TypeMember(AltAnchorY);
        TypeMember(CampDist);
        TypeMember(AltCampDist);
        TypeMember(AltRadius);
        TypeMember(Scatter);
        TypeMember(ReturnNoAggro);
        TypeMember(ReturnNotLooting);
        TypeMember(ReturnNoTarget);
        TypeMember(Bearing);
        TypeMember(ScatDist);
        TypeMember(ScatSize);
    }

    ~MQ2MakeCampType()
    {
    }

    bool GetMember(MQ2VARPTR VarPtr, char* Member, char* Index, MQ2TYPEVAR &Dest)
    {
        PMQ2TYPEMEMBER pMember = MQ2MakeCampType::FindMember(Member);
        PSPAWNINFO pMe = (PSPAWNINFO)pCharSpawn;
        if (!pMember || !pMe) return false;
        switch((MakeCampMembers)pMember->ID)
        {
        case Status:
            strcpy(DataTypeTemp, "OFF");
            if (bAllPaused)
            {
                strcpy(DataTypeTemp, "PAUSED");
            }
            else if (bMakeCamp)
            {
                strcpy(DataTypeTemp, "ON");
            }
            Dest.Ptr = DataTypeTemp;
            Dest.Type = pStringType;
            return true;
        case Leash:
            Dest.DWord = bLeash;
            Dest.Type = pBoolType;
            return true;
        case AnchorX:
            Dest.Float = fAnchorX;
            Dest.Type = pFloatType;
            return true;
        case AnchorY:
            Dest.Float = fAnchorY;
            Dest.Type = pFloatType;
            return true;
        case LeashLength:
            Dest.Float = fLeashLength;
            Dest.Type = pFloatType;
            return true;
        case CampRadius:
            Dest.Float = fCampRadius;
            Dest.Type = pFloatType;
            return true;
        case MinDelay:
            Dest.DWord = iMinCampDelay;
            Dest.Type = pIntType;
            return true;
        case MaxDelay:
            Dest.DWord = iMaxCampDelay;
            Dest.Type = pIntType;
            return true;
        case Returning:
            Dest.DWord = bMakeCampReturn;
            Dest.Type = pBoolType;
            return true;
        case AltAnchorX:
            Dest.Float = fAltAnchorX;
            Dest.Type = pFloatType;
            return true;
        case AltAnchorY:
            Dest.Float = fAltAnchorY;
            Dest.Type = pFloatType;
            return true;
        case CampDist:
            if (!bMakeCamp) fCampDist = 0.0f;
            else fCampDist = GetDistance(pMe->X, pMe->Y, fAnchorX, fAnchorY);
            Dest.Float = fCampDist;
            Dest.Type = pFloatType;
            return true;
        case AltCampDist:
            if (!bAltCamp) fAltCampDist = 0.0f;
            else fAltCampDist = GetDistance(pMe->X, pMe->Y, fAltAnchorX, fAltAnchorY);
            Dest.Float = fAltCampDist;
            Dest.Type = pFloatType;
            return true;
        case AltRadius:
            Dest.Float = fAltCampRadius;
            Dest.Type = pFloatType;
            return true;
        case Scatter:
            Dest.DWord = bScatter;
            Dest.Type = pBoolType;
            return true;
        case ReturnNoAggro:
            Dest.DWord = bReturnNoAggro;
            Dest.Type = pBoolType;
            return true;
        case ReturnNotLooting:
            Dest.DWord = bReturnNotLooting;
            Dest.Type = pBoolType;
            return true;
        case ReturnNoTarget:
            Dest.DWord = bReturnNoTarget;
            Dest.Type = pBoolType;
            return true;
        case Bearing:
            Dest.Float = fBearing;
            Dest.Type = pFloatType;
            return true;
        case ScatDist:
            Dest.Float = fScatDist;
            Dest.Type = pFloatType;
            return true;
        case ScatSize:
            Dest.Float = fScatter;
            Dest.Type = pFloatType;
            return true;
        }
        return false;
    }

    bool ToString(MQ2VARPTR VarPtr, char* Destination)
    {
        strcpy(Destination, "OFF");
        if (bAllPaused)
        {
            strcpy(Destination, "PAUSED");
        }
        else if (bMakeCamp)
        {
            strcpy(Destination, "ON");
        }
        return true;
    }

    bool FromData(MQ2VARPTR &VarPtr, MQ2TYPEVAR &Source)
    {
        return false;
    }
    bool FromString(MQ2VARPTR &VarPtr, char* Source)
    {
        return false;
    }
};

int dataMakeCamp(char* szName, MQ2TYPEVAR &Ret)
{
    Ret.DWord = 1;
    Ret.Type = pMakeCampType;
    return true;
}

class MQ2StickType : public MQ2Type
{
public:
    enum StickMembers
    {
        Status = 1,
        Active = 2,
        Distance = 3,
        MoveBehind = 4,
        MovePause = 5,
        MoveBack = 6,
        Loose = 7,
        Paused = 8,
        Behind = 9,
        Stopped = 10,
        Pin = 11,
        StickTarget = 12,
        StickTargetName = 13,
        DistMod = 14,
        DistModPercent = 15,
        Always = 16,
    };

    MQ2StickType():MQ2Type("stick")
    {
        TypeMember(Status);
        TypeMember(Active);
        TypeMember(Distance);
        TypeMember(MoveBehind);
        TypeMember(MovePause);
        TypeMember(MoveBack);
        TypeMember(Loose);
        TypeMember(Paused);
        TypeMember(Behind);
        TypeMember(Stopped);
        TypeMember(Pin);
        TypeMember(StickTarget);
        TypeMember(StickTargetName);
        TypeMember(DistMod);
        TypeMember(DistModPercent);
        TypeMember(Always);
    }

    ~MQ2StickType()
    {
    }

    bool GetMember(MQ2VARPTR VarPtr, char* Member, char* Index, MQ2TYPEVAR &Dest)
    {
        PMQ2TYPEMEMBER pMember = MQ2StickType::FindMember(Member);
        if (!pMember) return false;
        switch((StickMembers)pMember->ID)
        {
        case Status:
            strcpy(DataTypeTemp, "OFF");
            if (bAllPaused)
            {
                strcpy(DataTypeTemp, "PAUSED");
            }
            else if (bStickOn)
            {
                strcpy(DataTypeTemp, "ON");
            }
            Dest.Ptr = DataTypeTemp;
            Dest.Type = pStringType;
            return true;
        case Active:
            Dest.DWord = bStickOn;
            Dest.Type = pBoolType;
            return true;
        case Distance:
            Dest.Float = fStickDist;
            Dest.Type = pFloatType;
            return true;
        case MoveBehind:
            Dest.DWord = bStickBehind;
            Dest.Type = pBoolType;
            return true;
        case MovePause:
            Dest.DWord = bPauseOnMove;
            Dest.Type = pBoolType;
            return true;
        case MoveBack:
            Dest.DWord = bMoveBack;
            Dest.Type = pBoolType;
            return true;
        case Loose:
            Dest.DWord = bLooseStick;
            Dest.Type = pBoolType;
            return true;
        case Paused:
            Dest.DWord = bAllPaused;
            Dest.Type = pBoolType;
            return true;
        case Behind:
            if (pTarget)
            {
                PSPAWNINFO psTarget = (PSPAWNINFO)pTarget;
                PSPAWNINFO pChSpawn = (PSPAWNINFO) pCharSpawn;
                Dest.DWord = (GetDistance(pChSpawn, psTarget) > fStickDist || fabs(CalcAngularDist(psTarget->Heading, pChSpawn->Heading)) > 45.0 ) ? false : true;
            }
            else Dest.DWord = false;
            Dest.Type = pBoolType;
            return true;
        case Stopped:
            if (pTarget)
            {
                PSPAWNINFO psTarget = (PSPAWNINFO) (bStickHold ? GetSpawnByID(ulStickID) : pTarget);
                PSPAWNINFO pChSpawn = (PSPAWNINFO) pCharSpawn;
                Dest.DWord = (GetDistance(pChSpawn, psTarget) <= fStickDist) ? true : false;
            }
            else Dest.DWord = false;
            Dest.Type = pBoolType;
            return true;
        case Pin:
            Dest.DWord = bStickPin;
            Dest.Type = pBoolType;
            return true;
        case StickTarget:
            if (bStickHold) Dest.Int = ulStickID;
            else if (pTarget) Dest.Int = ((PSPAWNINFO)pTarget)->SpawnID;
            else Dest.Int = 0;
            Dest.Type = pIntType;
            return true;
        case StickTargetName:
            strcpy(DataTypeTemp, "NONE");
            if (bStickHold && ulStickID > 0)
            {
                PSPAWNINFO pStickID = (PSPAWNINFO)GetSpawnByID(ulStickID);
                if (pStickID) strcpy(DataTypeTemp, pStickID->DisplayedName);
            }
            else if (pTarget) strcpy(DataTypeTemp, ((PSPAWNINFO)pTarget)->DisplayedName);
            Dest.Ptr = DataTypeTemp;
            Dest.Type = pStringType;
            return true;
        case DistMod:
            Dest.Float = fStickDistMod;
            Dest.Type = pFloatType;
            return true;
        case DistModPercent:
            Dest.Float = fStickDistModP;
            Dest.Type = pFloatType;
            return true;
        case Always:
            Dest.DWord = bStickNewMob;
            Dest.Type = pBoolType;
            return true;
        }
        return false;
    }

    bool ToString(MQ2VARPTR VarPtr, char* Destination)
    {
        strcpy(Destination, "OFF");
        if (bAllPaused)
        {
            strcpy(Destination, "PAUSED");
        }
        else if (bStickOn)
        {
            strcpy(Destination, "ON");
        }

        return true;
    }

    bool FromData(MQ2VARPTR &VarPtr, MQ2TYPEVAR &Source)
    {
        return false;
    }
    bool FromString(MQ2VARPTR &VarPtr, char* Source)
    {
        return false;
    }
};

int dataStick(char* szName, MQ2TYPEVAR &Ret)
{
    Ret.DWord = 1;
    Ret.Type = pStickType;
    return true;
}

class MQ2MoveToType : public MQ2Type
{
public:
    enum MoveToMembers
    {
        Moving = 1,
        Stopped = 2,
        UseWalk = 3,
        ArrivalDist = 4,
        ArrivalDistY = 5,
        ArrivalDistX = 6,
    };

    MQ2MoveToType():MQ2Type("moveto")
    {
        TypeMember(Moving);
        TypeMember(Stopped);
        TypeMember(UseWalk);
        TypeMember(ArrivalDist);
        TypeMember(ArrivalDistY);
        TypeMember(ArrivalDistX);
    }

    ~MQ2MoveToType()
    {
    }

    bool GetMember(MQ2VARPTR VarPtr, char* Member, char* Index, MQ2TYPEVAR &Dest)
    {
        PMQ2TYPEMEMBER pMember = MQ2MoveToType::FindMember(Member);
        if (!pMember) return false;
        PSPAWNINFO pChSpawn = (PSPAWNINFO) pCharSpawn;
        switch((MoveToMembers)pMember->ID)
        {
        case Moving:
            Dest.DWord = bMoveToOn;
            Dest.Type = pBoolType;
            return true;
        case Stopped:
            Dest.DWord = (GetDistance(pChSpawn->X, pChSpawn->Y, fLocX, fLocY) <= fMoveDist) ? true : false;
            Dest.Type = pBoolType;
            return true;
        case UseWalk:
            Dest.DWord = bWalkMoveTo;
            Dest.Type = pBoolType;
            return true;
        case ArrivalDist:
            Dest.Float = fMoveDist;
            Dest.Type = pFloatType;
            return true;
        case ArrivalDistY:
            Dest.Float = fMoveDistY;
            Dest.Type = pFloatType;
            return true;
        case ArrivalDistX:
            Dest.Float = fMoveDistX;
            Dest.Type = pFloatType;
            return true;
        }
        return false;
    }

    bool ToString(MQ2VARPTR VarPtr, char* Destination)
    {
        if (bMoveToOn) strcpy(Destination, "ON");
        else strcpy(Destination, "OFF");
        return true;
    }

    bool FromData(MQ2VARPTR &VarPtr, MQ2TYPEVAR &Source)
    {
        return false;
    }
    bool FromString(MQ2VARPTR &VarPtr, char* Source)
    {
        return false;
    }
};

int dataMoveTo(char* szName, MQ2TYPEVAR &Ret)
{
    Ret.DWord = 1;
    Ret.Type = pMoveToType;
    return true;
}

class MQ2CircleType : public MQ2Type
{
public:
    enum CircleMembers
    {
        Status = 1,
        CircleY = 2,
        CircleX = 3,
        Drunken = 4,
        Rotation = 5,
        Direction = 6,
        Clockwise = 7,
        Backwards = 8,
    };

    MQ2CircleType():MQ2Type("circle")
    {
        TypeMember(Status);
        TypeMember(CircleY);
        TypeMember(CircleX);
        TypeMember(Drunken);
        TypeMember(Rotation);
        TypeMember(Direction);
        TypeMember(Clockwise);
        TypeMember(Backwards);
    }

    ~MQ2CircleType()
    {
    }

    bool GetMember(MQ2VARPTR VarPtr, char* Member, char* Index, MQ2TYPEVAR &Dest)
    {
        PMQ2TYPEMEMBER pMember = MQ2CircleType::FindMember(Member);
        if (!pMember) return false;
        switch((CircleMembers)pMember->ID)
        {
        case Status:
            strcpy(DataTypeTemp, "OFF");
            if (bAllPaused)
            {
                strcpy(DataTypeTemp, "PAUSED");
            }
            else if (bCircling)
            {
                strcpy(DataTypeTemp, "ON");
            }
            Dest.Ptr = DataTypeTemp;
            Dest.Type = pStringType;
            return true;
        case CircleY:
            Dest.Float = fCircleY;
            Dest.Type = pFloatType;
            return true;
        case CircleX:
            Dest.Float = fCircleX;
            Dest.Type = pFloatType;
            return true;
        case Drunken:
            Dest.DWord = bDrunken;
            Dest.Type = pBoolType;
            return true;
        case Rotation:
            strcpy(DataTypeTemp, "CW");
            if (!bClockwise)
            {
                strcpy(DataTypeTemp, "CCW");
            }
            Dest.Ptr = DataTypeTemp;
            Dest.Type = pStringType;
            return true;
        case Direction:
            strcpy(DataTypeTemp, "FORWARDS");
            if (bBackwards)
            {
                strcpy(DataTypeTemp, "BACKWARDS");
            }
            Dest.Ptr = DataTypeTemp;
            Dest.Type = pStringType;
            return true;
        case Clockwise:
            Dest.DWord = bClockwise;
            Dest.Type = pBoolType;
            return true;
        case Backwards:
            Dest.DWord = bBackwards;
            Dest.Type = pBoolType;
            return true;
        }
        return false;
    }

    bool ToString(MQ2VARPTR VarPtr, char* Destination)
    {
        strcpy(Destination, "OFF");
        if (bAllPaused)
        {
            strcpy(Destination, "PAUSED");
        }
        else if (bCircling)
        {
            strcpy(Destination, "ON");
        }
        return true;
    }

    bool FromData(MQ2VARPTR &VarPtr, MQ2TYPEVAR &Source)
    {
        return false;
    }
    bool FromString(MQ2VARPTR &VarPtr, char* Source)
    {
        return false;
    }
};

int dataCircling(char* szName, MQ2TYPEVAR &Ret)
{
    Ret.DWord = 1;
    Ret.Type = pCircleType;
    return true;
}

class MQ2MoveUtilsType : public MQ2Type
{
public:
    enum MoveUtilsMembers
    {
        Command = 1,
        Stuck = 2,
        Summoned = 3,
        StuckLogic = 4,
        Verbosity = 5,
        FullVerbosity = 6,
        TotalSilence = 7,
        Aggro = 8,
        PauseMinDelay = 9,
        PauseMaxDelay = 10,
        PulseCheck = 11,
        PulseUnstuck = 12,
        TryToJump = 13,
        DistStuck = 14,
        Version = 15,
    };

    MQ2MoveUtilsType():MQ2Type("moveutils")
    {
        TypeMember(Command);
        TypeMember(Stuck);
        TypeMember(Summoned);
        TypeMember(StuckLogic);
        TypeMember(Verbosity);
        TypeMember(FullVerbosity);
        TypeMember(TotalSilence);
        TypeMember(Aggro);
        TypeMember(PauseMinDelay);
        TypeMember(PauseMaxDelay);
        TypeMember(PulseCheck);
        TypeMember(PulseUnstuck);
        TypeMember(TryToJump);
        TypeMember(DistStuck);
        TypeMember(Version);
    }

    ~MQ2MoveUtilsType()
    {
    }

    bool GetMember(MQ2VARPTR VarPtr, char* Member, char* Index, MQ2TYPEVAR &Dest)
    {
        PMQ2TYPEMEMBER pMember = MQ2MoveUtilsType::FindMember(Member);
        if (!pMember) return false;
        switch((MoveUtilsMembers)pMember->ID)
        {
        case Command:
            strcpy(DataTypeTemp, "NONE");
            if (bStickOn)
            {
                strcpy(DataTypeTemp, "STICK");
            }
            else if (bCircling)
            {
                strcpy(DataTypeTemp, "CIRCLE");
            }
            else if (bMoveToOn)
            {
                strcpy(DataTypeTemp, "MOVETO");
            }
            else if (bMakeCamp)
            {
                strcpy(DataTypeTemp, "MAKECAMP");
            }
            Dest.Ptr = DataTypeTemp;
            Dest.Type = pStringType;
            return true;
        case Stuck:
            Dest.DWord = false;
            if (usiStuck > 0)
            {
                Dest.DWord = true;
            }
            Dest.Type = pBoolType;
            return true;
        case Summoned:
            Dest.DWord = bBrokeOnSummon;
            Dest.Type = pBoolType;
            return true;
        case StuckLogic:
            Dest.DWord = bCheckStuck;
            Dest.Type = pBoolType;
            return true;
        case Verbosity:
            Dest.DWord = bVerbosity;
            Dest.Type = pBoolType;
            return true;
        case FullVerbosity:
            Dest.DWord = bFullVerbosity;
            Dest.Type = pBoolType;
            return true;
        case TotalSilence:
            Dest.DWord = bTotalSilence;
            Dest.Type = pBoolType;
            return true;
        case Aggro:
            Dest.DWord = bImAggro;
            Dest.Type = pBoolType;
            return true;
        case PauseMinDelay:
            Dest.DWord = iMinPauseDelay;
            Dest.Type = pIntType;
            return true;
        case PauseMaxDelay:
            Dest.DWord = iMaxPauseDelay;
            Dest.Type = pIntType;
            return true;
        case PulseCheck:
            Dest.Int = usiPulseCheck;
            Dest.Type = pIntType;
            return true;
        case PulseUnstuck:
            Dest.Int = usiPulseUnstuck;
            Dest.Type = pIntType;
            return true;
        case TryToJump:
            Dest.DWord = bTryToJump;
            Dest.Type = pBoolType;
            return true;
        case DistStuck:
            Dest.Float = fDistStuck;
            Dest.Type = pFloatType;
            return true;
        case Version:
            sprintf(DataTypeTemp, "%1.4f", MODULE_VERSION);
            Dest.Ptr = DataTypeTemp;
            Dest.Type = pStringType;
            return true;
        }
        return false;
    }

    bool ToString(MQ2VARPTR VarPtr, char* Destination)
    {
        strcpy(Destination, "NONE");
        if (bStickOn)
        {
            strcpy(Destination, "STICK");
        }
        else if (bCircling)
        {
            strcpy(Destination, "CIRCLE");
        }
        else if (bMoveToOn)
        {
            strcpy(Destination, "MOVETO");
        }
        else if (bMakeCamp)
        {
            strcpy(Destination, "MAKECAMP");
        }
        return true;
    }

    bool FromData(MQ2VARPTR &VarPtr, MQ2TYPEVAR &Source)
    {
        return false;
    }
    bool FromString(MQ2VARPTR &VarPtr, char* Source)
    {
        return false;
    }
};

int dataMoveUtils(char* szName, MQ2TYPEVAR &Ret)
{
    Ret.DWord = 1;
    Ret.Type = pMoveUtilsType;
    return true;
}

// End Custom Top-Level Objects
///////////////////////////////////////
// Generic math & utility functions
float CalcAngularDist(float fH1, float fH2)
{
    if(fH1 == fH2) return 0.0;
    if(fabs(fH1 - fH2) > HEADING_HALF) 
        *(fH1 < fH2 ? &fH1 : &fH2) += HEADING_MAX;
    return (fabs(fH1 - fH2) > HEADING_HALF) ? (fH2 - fH1) : (fH1 - fH2);
}

float GetRandomNumber(float fN)
{
    // used by drunken movement
    return (float)(fN * rand() / (RAND_MAX + 1.0f));
}

int diff_in_ms(SYSTEMTIME &stCurr, SYSTEMTIME &stPrev)
{
    SYSTEMTIME stResult;
    FILETIME ftPrev, ftCurr, ftResult;
    ULARGE_INTEGER prev, curr, result;

    GetSystemTime(&stCurr);
    SystemTimeToFileTime(&stPrev,&ftPrev);
    SystemTimeToFileTime(&stCurr,&ftCurr);
    prev.HighPart = ftPrev.dwHighDateTime;
    prev.LowPart = ftPrev.dwLowDateTime;
    curr.HighPart = ftCurr.dwHighDateTime;
    curr.LowPart = ftCurr.dwLowDateTime;
    result.QuadPart = curr.QuadPart - prev.QuadPart;
    ftResult.dwHighDateTime = result.HighPart;
    ftResult.dwLowDateTime = result.LowPart;
    FileTimeToSystemTime(&ftResult,&stResult);

    return ((int)(stResult.wSecond * 1000 + stResult.wMilliseconds));
}

bool IsBardClass(void)
{
    if(strnicmp(pEverQuest->GetClassDesc(GetCharInfo2()->Class & 0xff), "Bard", 4))
    {
        return false;
    }
    else
    {
        return true;
    }
}

// we process this 10+ times, may as well make it a function -pms
float SaneHeading(float fHeading)
{
    if (fHeading >= HEADING_MAX) fHeading -= HEADING_MAX;
    if (fHeading < 0.0f) fHeading += HEADING_MAX;
    return fHeading;
}

// want to be able to call this where needed -pms
bool CanLooseMove(float fLooseHead, float fX, float fY)
{
    PSPAWNINFO pChSpawn = (PSPAWNINFO)pCharSpawn;
    if (!pChSpawn) return false;

    float fAdjHead = fabs(pChSpawn->Heading - fLooseHead);
    if (fAdjHead > 32.0f)
    {
        // if we are more than an 1/8th turn
        return false;
    }
    if ((fAdjHead / 2.0f) > GetDistance(pChSpawn->X, pChSpawn->Y, fX, fY))
    {
        // if half our needed adjustment is > distance between us and destination
        return false;
    }

    // else safe to move
    return true;
}

// campreturn credit: deadchicken
void CampReturn(float fX, float fY, float fUseRadius)
{
    float fRandHead = (float)((float)rand() / (float)RAND_MAX) * CIRCLE_MAX;
    float fRandDist = (float)((float)rand() / (float)RAND_MAX) * fUseRadius;

    SpewDebug(DBG_OTHER, "fRandHead = %.2f, fRandDist = %.2f", fRandHead, fRandDist);

    fGotoX = fX + (fRandDist * sin(fRandHead));
    fGotoY = fY + (fRandDist * cos(fRandHead));
}

// polarspot credit: deadchicken
// MOB: PolarSpot( targetX, targetY, targetHeading, desiredHeading, distanceAway, scatter);
// CAMP: (camp x, camp y, heading doesnt matter, bearing = which dir from center, dist = how far from center, scatter=size of scatter);
// PolarSpot( 100.0, 100.0, 0.0, 0.0, 0.0, 10.0, &gotoX, &gotoY );
void PolarSpot(float fX, float fY, float fPHeading, float fPBearing, float fPDist, float fPScatter)
{
    // if camp returning
    if (fPScatter > 0.0f)
    {
        float fRandHead = ((float)rand() / (float)RAND_MAX) * HEADING_MAX;
        fRandHead = SaneHeading(fRandHead);
        float fRandDist = ((float)rand() /(float)RAND_MAX) * fPScatter;

        fGotoY = fY + (fRandDist * cos(fRandHead));
        fGotoX = fX + (fRandDist * sin(fRandHead));
        // 0.0175f converts degrees to radians which sinf/cosf expect
        /*fGotoX = fOurGotoX + fRandDist * sinf(fRandHead*0.0175f));
        fGotoY = fOurGotoY + fRandDist * cosf(fRandHead*0.0175f));*/
        return;
    }
    // else snaproll

    //-----------------------------------------------------
    // 12-17-08: dont change this code again for the love of agnostic
    // 01-07-09: ok maybe one more time
    //float fRelHead = (fPHeading / fPBearing) * -(float)PI;
    //fSnapY = fY  - (float)cos(fRelHead)* fPDist;
    //fSnapX = fX  + (float)sin(fRelHead)* fPDist;
    float fRelHead = fPHeading - fPBearing;
    fRelHead = SaneHeading(fRelHead);
    fRelHead = ((fRelHead / HEADING_MAX) * CIRCLE_MAX) * 0.0175f;
    fSnapY = fY + (fPDist * cos(fRelHead));
    fSnapX = fX + (fPDist * sin(fRelHead));
    //-----------------------------------------------------
}

// MovingAvg returns a moving average of size iEntries by adding fNew to the ring and computing
// fNew = New value to add to the ring
// iEntries = number of entries in ring, used for divisior and to re-init ring on change/init.
//
// Returns the moving average based on said values
//
// Notes: MaxRing size is 32 by static declaration below and not checked, should be.
//
// credit: deadchicken
float MovingAvg(float fNew, int iEntries)
{
    static float fRing[MAXRINGSIZE];
    static int iOldStuck = 0;
    static int iRinger = 0;
    static int iRingFull = 0;
    int i = 0;
    float fAvg = 0.0f;

    // Bounds checking
    if (iEntries > MAXRINGSIZE || iEntries < 2 ) return fNew;
    // Do we have a new ring size?
    if (iOldStuck != iEntries)
    {
        // Do some shit to make us right
        SpewDebug(DBG_OTHER, "Entry # changed, filling ring with %3.2f!  %d != %d", fNew, iOldStuck, iEntries);
        // Fill the array with this latest value
        // maybe this should be our default preload value of 2.0f?
        for(i=0; i<iEntries; i++) fRing[i] = fNew;
        // update iOldStuck and reset counter to 0
        iRinger = 0;
        iOldStuck = iEntries;
        return fNew;
    }
    else
    {
        // Plain old ring buffer
        fRing[iRinger] = fNew;
        SpewDebug(DBG_OTHER, "Added %3.2f to fRing[%d]", fNew, iRinger);
        // Increment iRinger and wrap if needed, if we wrap then it's full
        iRinger++;
        if (iRinger >= iEntries)
        {
            iRinger = 0;
            iRingFull = 1;
        }
        // Get the sum of the ring
        //for( i=0; i<(iRingFull?iEntries:iRinger); i++) {  <-- this was a bad idea
        for(i=0; i<iEntries; i++)
        {
            SpewDebug(DBG_OTHER, "i=%d and fRing[%d]=%3.2f", i, i, fRing[i]);
            fAvg += fRing[i];
        }
    }
    return (fAvg / (float)iEntries);
}
// End math & utility functions
///////////////////////////////////////
// Begin Player adjustment functions

// Our version of gFaceAngle, used by loose heading
// set dAdjustHead and this will be called onpulse
void AdjustHeading(float fNewHead)
{
    PSPAWNINFO pChSpawn = (PSPAWNINFO)pCharSpawn;
    if (!pChSpawn || (pChSpawn && !pChSpawn->SpawnID) || GetGameState() != GAMESTATE_INGAME)
    {
        return;
    }

    SpewDebug(DBG_DISABLE, "AdjustHeading called with %.2f", fNewHead);
    if (fabs(pChSpawn->Heading - fNewHead) < fTurnRate)
    {
        // if we are within one turn away, set heading to desired heading
        // and reset fAdjustHead so this function is not called again onpulse
        pChSpawn->Heading = fNewHead;
        pChSpawn->SpeedHeading = 0.0f;
        fAdjustHead = 10000.0f;
    }
    else
    {
        float fCompHead = pChSpawn->Heading + HEADING_HALF;

        if (fNewHead < pChSpawn->Heading) fNewHead += HEADING_MAX;
        if (fNewHead < fCompHead)
        {
            pChSpawn->Heading = SaneHeading(pChSpawn->Heading + fTurnRate);
            pChSpawn->SpeedHeading = 12.0f;
        }
        else
        {
            pChSpawn->Heading = SaneHeading(pChSpawn->Heading - fTurnRate);
            pChSpawn->SpeedHeading = -12.0f;
        }
    }
    SpewDebug(DBG_DISABLE, "pChSpawn->Heading changed to %.2f", pChSpawn->Heading);
}

void StandIfNeeded(void)
{
    PSPAWNINFO pChSpawn = (PSPAWNINFO)pCharSpawn;
    if (!pChSpawn || (pChSpawn && !pChSpawn->SpawnID) || GetGameState() != GAMESTATE_INGAME)
    {
        // ExecuteCmd in any other state = CTD
        return;
    }
    SpewDebug(DBG_OTHER, "MoveUtils StandIfNeeded: pCharSpawn->StandState = %d", pChSpawn->StandState);

    if (pChSpawn->RespawnTimer > 0)
    {
        // setting standstate while hovering = bug
        SpewDebug(DBG_OTHER, "MoveUtils StandIfNeeded Returning as RespawnTimer = %d", pChSpawn->RespawnTimer);
        return;
    }
    if (pChSpawn->StandState == STANDSTATE_STAND)
    {
        // if already standing, we're fine
        return;
    }
    if (pChSpawn->StandState == STANDSTATE_DEAD || pChSpawn->StandState == STANDSTATE_BIND || pChSpawn->StandState == STANDSTATE_CASTING)
    {
        // dont do anything if dead, bind wound/looting, or casting
        return;
    }

    if (bFeignSupport && pChSpawn->StandState == STANDSTATE_FEIGN)
    {
        sprintf(szMsg, "\ay%s\aw:: Not standing as you are currently Feign Death", MODULE_NAME);
        WriteLine(szMsg, VERB_FULL);
        return;
    }

    if (pChSpawn->StandState == STANDSTATE_FEIGN || pChSpawn->StandState == STANDSTATE_SIT)
    {
        /*
        using this caused the sit/stand button to get desynched
        MQ2Globals::ExecuteCmd(FindMappableCommand("sit_stand"), 1, 0);
        MQ2Globals::ExecuteCmd(FindMappableCommand("sit_stand"), 0, 0);

        we cant do this either, the keypress is what sends the standstate change to the server.
        pChSpawn->StandState = STANDSTATE_STAND;
        */

        EzCommand("/stand"); // new fix for preventing sit/stand bug
        return;
    }

    if (pChSpawn->StandState == STANDSTATE_DUCK)
    {
        MQ2Globals::ExecuteCmd(FindMappableCommand("duck"), 1, 0);
        MQ2Globals::ExecuteCmd(FindMappableCommand("duck"), 0, 0);
        return;
    }

    SpewDebug(DBG_ALL, "MoveUtils StandIfNeeded: no StandState matches for %d", pChSpawn->StandState);
}

void AdjustWalking(bool bTurnWalkOn)
{
    PSPAWNINFO pChSpawn = (PSPAWNINFO)pCharSpawn;
    if (!pChSpawn || (pChSpawn && !pChSpawn->SpawnID) || GetGameState() != GAMESTATE_INGAME)
    {
        // ExecuteCmd in any other state = CTD
        return;
    }

    bool bWalking = (*EQADDR_RUNWALKSTATE) ? false : true;
    if (pChSpawn->SpeedMultiplier < 0.0f || pChSpawn->RunSpeed < 0.0f) //if negative speed, we are snared, and do not want walk on
    {
        bTurnWalkOn = false;
    }
    if ((bTurnWalkOn && !bWalking) || (!bTurnWalkOn && bWalking))
    {
        SpewDebug(DBG_OTHER, "MoveUtils: Toggled Run/Walk key");
        MQ2Globals::ExecuteCmd(FindMappableCommand("run_walk"), 1, 0);
        MQ2Globals::ExecuteCmd(FindMappableCommand("run_walk"), 0, 0);
    }
}

// DoMovement(GO_FORWARDS) will imply DoMovement(GO_FORWARDS, true, false)
void DoMovement(unsigned char ucDirection, bool bHoldKey, bool bUseWalking)
{
    if (GetGameState() != GAMESTATE_INGAME)
    {
        // ExecuteCmd in any other state = CTD
        return;
    }

    if (bHoldKey)
    {
        if (ucDirection == GO_FORWARDS)
        {
            bCmdMovedFwd = true;
            if (bUseWalking) AdjustWalking(bUseWalking); // only applies to moveforward
            MQ2Globals::ExecuteCmd(FindMappableCommand("back"), 0, 0);
            MQ2Globals::ExecuteCmd(FindMappableCommand("forward"), 1, 0);
        }
        else if (ucDirection == GO_BACKWARDS)
        {
            bCmdMovedFwd = false;
            MQ2Globals::ExecuteCmd(FindMappableCommand("forward"), 0, 0);
            MQ2Globals::ExecuteCmd(FindMappableCommand("back"), 1, 0);
        }
        else if (ucDirection == GO_LEFT)
        {
            bCmdMovedSide = true;
            MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_right"), 0, 0);
            MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_left"), 1, 0);
        }
        else if (ucDirection == GO_RIGHT)
        {
            bCmdMovedSide = true;
            MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_left"), 0, 0);
            MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_right"), 1, 0);
        }
        else
        {
            return;
        }
    }
    else
    {
        if (ucDirection == APPLY_TO_ALL)
        {
            SpewDebug(DBG_DISABLE, "MoveUtils: All movement halted.");
            bCmdMovedFwd = bCmdMovedSide = false;
            MQ2Globals::ExecuteCmd(FindMappableCommand("forward"), 0, 0);
            MQ2Globals::ExecuteCmd(FindMappableCommand("back"), 1, 0);
            MQ2Globals::ExecuteCmd(FindMappableCommand("back"), 0, 0);
            MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_left"), 0, 0);
            MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_right"), 1, 0);
            MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_right"), 0, 0);
        }
        else
        {
            if (ucDirection == GO_FORWARDS)
            {
                bCmdMovedFwd = false;
                MQ2Globals::ExecuteCmd(FindMappableCommand("back"), 0, 0);
                MQ2Globals::ExecuteCmd(FindMappableCommand("forward"), 1, 0);
                MQ2Globals::ExecuteCmd(FindMappableCommand("forward"), 0, 0);
            }
            else if (ucDirection == GO_BACKWARDS)
            {
                MQ2Globals::ExecuteCmd(FindMappableCommand("forward"), 0, 0);
                MQ2Globals::ExecuteCmd(FindMappableCommand("back"), 1, 0);
                MQ2Globals::ExecuteCmd(FindMappableCommand("back"), 0, 0);
            }
            else if (ucDirection == GO_LEFT)
            {
                bCmdMovedSide = false;
                MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_right"), 0, 0);
                MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_left"), 1, 0);
                MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_left"), 0, 0);
            }
            else if (ucDirection == GO_RIGHT)
            {
                bCmdMovedSide = false;
                MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_left"), 0, 0);
                MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_right"), 1, 0);
                MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_right"), 0, 0);
            }
        }
        AdjustWalking(bUseWalking);
    }
}
// End Player adjustment functions
///////////////////////////////////////
// Begin Command input handling

void StickCommand(PSPAWNINFO pChar, char* szLine)
{
    HandleOurCmd(USED_STICK, szLine);
    bRunNextCommand = true;
}
void MoveToCommand(PSPAWNINFO pChar, char* szLine)
{
    HandleOurCmd(USED_MOVETO, szLine);
    bRunNextCommand = true;
}
void CircleCommand(PSPAWNINFO pChar, char* szLine)
{
    HandleOurCmd(USED_CIRCLE, szLine);
    bRunNextCommand = true;
}
void MakeCampCommand(PSPAWNINFO pChar, char* szLine)
{
    HandleOurCmd(USED_MAKECAMP, szLine);
    bRunNextCommand = true;
}

void HandleOurCmd(unsigned char ucCmdUsed, char* szInput)
{
    char szCurrentArg[MAX_STRING] = {0}; // stores current argument from szInput
    char szTempID[MAX_STRING] = {0}; // stores output msg for stick
    static char sszCampName[MAX_STRING] = {0}; // stores makecamp player displayedname
    unsigned short usiArgNum = 1; // argument number to evaluate
    if (rand() % 100 > 50) fStuckTurn *= -1.0f;

    PSPAWNINFO pTargetUsed = NULL; // stick id, moveto id
    PSPAWNINFO pCampPlayer = NULL; // makecamp player
    PSPAWNINFO pChSpawn = (PSPAWNINFO)pCharSpawn;

    // don't allow commands from char select or cfg files that load before entering world
    if (!pChSpawn || (pChSpawn && !pChSpawn->SpawnID) || GetGameState() != GAMESTATE_INGAME) return;

    // store stick params for external status call
    if (ucCmdUsed == USED_STICK)
    {
        strcpy(szLastStickCmd, szInput);
    }

    GetArg(szCurrentArg, szInput, usiArgNum++);

    // if no argument supplied
    if (!*szCurrentArg)
    {
        bAllPaused = false;

        if (ucCmdUsed == USED_MAKECAMP)
        {
            //EndPreviousCmd(); // dont reset any commands if using makecamp
            if (!bMakeCamp)
            {
                bAllPaused = false;
                iPauseReturnTime = 0;
                ResetCamp(false);
                bMakeCamp = true;
                fAnchorY = pChSpawn->Y;
                fAnchorX = pChSpawn->X;
                fCampRadius = (fCampRadius < 10.0f) ? 10.0f : fCampRadius; // enforce min value of 10
                fLeashLength = (fLeashLength >= fCampRadius) ? fLeashLength : fCampRadius + 10.0f; // enforce leash greater than/equal to camp radius
                iMinCampDelay = (iMinCampDelay < 125) ? 125 : iMinCampDelay;
                iMaxCampDelay = (iMaxCampDelay < iMinCampDelay + 125) ? iMinCampDelay + 125 : iMaxCampDelay;
                sprintf(szMsg, "\ay%s\aw:: MakeCamp actived. Y(\ag%.2f\ax) X(\ag%.2f\ax) Radius(\ag%.2f\ax) Leash(%s) LeashLen(\ag%.2f\ax) Min(\ag%d\ax) Max(\ag%d\ax)", MODULE_NAME, fAnchorY, fAnchorX, fCampRadius, bLeash ? "\agon\ax" : "\aroff\ax", fLeashLength, iMinCampDelay, iMaxCampDelay);
                WriteLine(szMsg, VERB_FULL);
                return;
            }
            else
            {
                ResetCamp(true, SET_ALT);
                return;
            }
        }
        else if (ucCmdUsed == USED_STICK) 
        {
            EndPreviousCmd(true);
            if (pTarget)
            {
                if (((PSPAWNINFO)pTarget)->SpawnID == ((PSPAWNINFO)pLocalPlayer)->SpawnID)
                {
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) You cannot stick to yourself!", MODULE_NAME);
                    WriteLine(szMsg, VERB_VERB);
                    return;
                }
                bStickOn = true;
                fCurrentDist = GetDistance(pChSpawn, (PSPAWNINFO)pTarget);
                StandIfNeeded();
                sprintf(szMsg, "\ay%s\aw:: You are now sticking to \ag%s\ax.", MODULE_NAME, ((PSPAWNINFO)pTarget)->DisplayedName);
            }
            else
            {
                sprintf(szMsg, "\ay%s\aw:: You must specify something to stick to!", MODULE_NAME);
            }
        }
        else if (ucCmdUsed == USED_MOVETO || ucCmdUsed == USED_CIRCLE)
        {
            EndPreviousCmd(true);
            //possible future use, as-is '/circle' and '/moveto' designed to fail with no param, (/moveto id) to use target instead
            sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) /moveto or /circle command used with no parameter.", MODULE_NAME);
        }
        WriteLine(szMsg, VERB_VERB);
        return;
    }

    if (*szCurrentArg)
    {
        // generic parameters that we want to enforce first-parameter syntax
        if (!strnicmp(szCurrentArg, "help", 5))
        {
            unsigned char ucCaller = ucCmdUsed;
            GetArg(szCurrentArg, szInput, usiArgNum);

            if (!strnicmp(szCurrentArg, "settings", 9))
            {
                ucCaller = HELP_SETTINGS;
            }

            if (bHideHelp)
            {
                bHideHelp = false;
                OutputHelp(ucCaller);
                bHideHelp = true;
            }
            else
            {
                OutputHelp(ucCaller);
            }
            return;
        }
        else if (!strnicmp(szCurrentArg, "debug", 6))
        {
            sprintf(szMsg, "\ay%s\aw:: Outputting debug information.", MODULE_NAME);
            WriteLine(szMsg, VERB_ENFORCE);
            DebugToINI(ucCmdUsed);
            return;
        }
        else if (!strnicmp(szCurrentArg, "status", 7))
        {
            GetArg(szCurrentArg, szInput, usiArgNum);
            if (!strnicmp(szCurrentArg, "all", 4))
            {
                DebugToWnd(APPLY_TO_ALL);
            }
            else
            {
                DebugToWnd(ucCmdUsed);
            }
            return;
        }
        else if (!strnicmp(szCurrentArg, "pause", 6))
        {
            if (!bAllPaused)
            {
                bAllPaused = true;
                DoMovement(APPLY_TO_ALL, false, false);
                iCampReturnTime = 0;
                iPauseReturnTime = 0;
                sprintf(szMsg, "\ay%s\aw:: %s", MODULE_NAME, bAllPaused ? "\arPAUSED" : "\agRESUMED");
                WriteLine(szMsg, VERB_ALL);
            }
            else
            {
                sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) Plugin was already paused.", MODULE_NAME);
                WriteLine(szMsg, VERB_FULL);
            }
            return;
        }
        else if (!strnicmp(szCurrentArg, "unpause", 8))
        {
            if (bAllPaused)
            {
                bAllPaused = false;
                EndPreviousCmd(true, ucCmdUsed, true);
                iCampReturnTime = 0;
                iPauseReturnTime = 0; // to not confuse with mpause handling
                sprintf(szMsg, "\ay%s\aw:: %s", MODULE_NAME, bAllPaused ? "\arPAUSED" : "\agRESUMED");
                WriteLine(szMsg, VERB_ALL);
            }
            else
            {
                sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) Plugin was not paused.", MODULE_NAME);
                WriteLine(szMsg, VERB_FULL);
            }
            return;
        }
        else if (!strnicmp(szCurrentArg, "save", 5))
        {
            Save_INI();
            sprintf(szMsg, "\ay%s\aw:: Saving settings to MQ2MoveUtils.ini", MODULE_NAME);
            WriteLine(szMsg, VERB_FULL);
            return;
        }
        else if (!strnicmp(szCurrentArg, "load", 5))
        {
            Load_INI();
            sprintf(szMsg, "\ay%s\aw:: Loading settings from MQ2MoveUtils.ini", MODULE_NAME);
            WriteLine(szMsg, VERB_FULL);
            return;
        }
        else if (!strnicmp(szCurrentArg, "imsafe", 7))
        {
            bBrokeOnSummon = bAllPaused = false;
            EndPreviousCmd(true, ucCmdUsed, true); // reset stuck logic/distance variables
            sprintf(szMsg, "\ay%s\aw:: Command usage allowed once again.", MODULE_NAME);
            WriteLine(szMsg, VERB_ENFORCE);
            return;
        }
        else if (!strnicmp(szCurrentArg, "set", 4))
        {
            char szTempSet[MAX_STRING] = {0};
            sprintf(szTempSet, "%s", GetNextArg(szInput, 1, FALSE, 0));
            ChangeSetting(ucCmdUsed, false, szTempSet);
            return;
        }
        else if (!strnicmp(szCurrentArg, "toggle", 7))
        {
            char szTempSet[MAX_STRING] = {0};
            sprintf(szTempSet, "%s", GetNextArg(szInput, 1, FALSE, 0));
            ChangeSetting(ucCmdUsed, true, szTempSet);
            return;
        }

        // non-generic parameters that we want to enforce first-parameter syntax
        if (!strnicmp(szCurrentArg, "on", 3) && (ucCmdUsed == USED_MAKECAMP || ucCmdUsed == USED_CIRCLE))
        {
            if (ucCmdUsed == USED_MAKECAMP)
            {
                bAllPaused = false;
                iPauseReturnTime = 0;
                ResetCamp(false, SET_ALT);
                bMakeCamp = true;
                fAnchorX = pChSpawn->X;
                fAnchorY = pChSpawn->Y;
                iMinCampDelay = (iMinCampDelay < 125) ? 125 : iMinCampDelay;
                iMaxCampDelay = (iMaxCampDelay < iMinCampDelay + 125) ? iMinCampDelay + 125 : iMaxCampDelay;
                GetArg(szCurrentArg, szInput, usiArgNum++);
                if (isdigit(szCurrentArg[0]))
                {
                    fCampRadius = (float)atof(szCurrentArg);
                    GetArg(szCurrentArg, szInput, usiArgNum); // because when we break from this we enter while loop for NEW args
                }
                fCampRadius = (fCampRadius < 10.0f) ? 10.0f : fCampRadius;
                fLeashLength = (fLeashLength >= fCampRadius) ? fLeashLength : fCampRadius + 10.0f;
            }
            else
            {
                EndPreviousCmd(true);
                GetArg(szCurrentArg, szInput, usiArgNum++);
                if (isdigit(szCurrentArg[0]))
                {
                    fCircleRadius = (float)atof(szCurrentArg);
                    GetArg(szCurrentArg, szInput, usiArgNum); // because when we break from this we enter while loop for NEW args
                }
                bCircling = true;
                fCircleY = pChSpawn->Y + fCircleRadius * sin(pChSpawn->Heading * (float)PI / HEADING_HALF);
                fCircleX = pChSpawn->X + fCircleRadius * cos(pChSpawn->Heading * (float)PI / HEADING_HALF);
            }
        }
        else if (!strnicmp(szCurrentArg, "mod", 4) && ucCmdUsed == USED_STICK)
        {
            GetArg(szCurrentArg, szInput, usiArgNum++);
            if (isdigit(szCurrentArg[0]) || szCurrentArg[0] == '-' || szCurrentArg[0] == '.')
            {
                fStickDistMod = (float)atof(szCurrentArg);
                sprintf(szMsg, "\ay%s\aw:: Stick modifier changed to Mod(\ag%.2f\ax) Mod%%%%(\ag%.2f%%%%\ax)", MODULE_NAME, fStickDistMod, fStickDistModP); // % = 2 for printf, 2 for eq xml - thanks dkaa!
            }
            else
            {
                EndPreviousCmd(true);
                sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) \ay/stick mod [#]\ax supplied incorrectly.", MODULE_NAME);
            }
            WriteLine(szMsg, VERB_ALL);
            return;
        }
        else if(!strnicmp(szCurrentArg, "loc", 4) && ucCmdUsed != USED_STICK)
        {
            GetArg(szCurrentArg, szInput, usiArgNum++);
            if (ucCmdUsed == USED_MOVETO)
            {
                EndPreviousCmd(true);
                if (isdigit(szCurrentArg[0]) || szCurrentArg[0] == '-' || szCurrentArg[0] == '.')
                {
                    fLocY = (float)atof(szCurrentArg);
                    GetArg(szCurrentArg, szInput, usiArgNum++);
                    if (isdigit(szCurrentArg[0]) || szCurrentArg[0] == '-' || szCurrentArg[0] == '.')
                    {
                        fLocX = (float)atof(szCurrentArg);
                        bMoveToOn = true;
                        GetArg(szCurrentArg, szInput, usiArgNum);
                    }
                    else
                    {
                        sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) \ay/moveto loc [<Y> <X>]\ax was supplied incorrectly.", MODULE_NAME);
                        WriteLine(szMsg, VERB_ALL);
                        EndPreviousCmd(true);
                        return;
                    }
                }
                else
                {
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) \ay/moveto loc [<Y> <X>]\ax was supplied incorrectly.", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    EndPreviousCmd(true);
                    return;
                }
            }
            else if (ucCmdUsed == USED_MAKECAMP)
            {
                bAllPaused = false;
                iPauseReturnTime = 0;
                ResetCamp(false, SET_ALT);
                if (isdigit(szCurrentArg[0]) || szCurrentArg[0] == '-' || szCurrentArg[0] == '.')
                {
                    fAnchorY = (float)atof(szCurrentArg);
                    GetArg(szCurrentArg, szInput, usiArgNum++);
                    if (isdigit(szCurrentArg[0]) || szCurrentArg[0] == '-' || szCurrentArg[0] == '.')
                    {
                        fAnchorX = (float)atof(szCurrentArg);
                        bMakeCamp = true;
                        fCampRadius = (fCampRadius < 10.0f) ? 10.0f : fCampRadius;
                        fLeashLength = (fLeashLength >= fCampRadius) ? fLeashLength : fCampRadius + 10.0f;
                        GetArg(szCurrentArg, szInput, usiArgNum);
                    }
                    else
                    {
                        ResetCamp(false);
                        sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) \ay/makecamp loc [<Y> <X>]\ax was supplied incorrectly.", MODULE_NAME);
                        WriteLine(szMsg, VERB_ALL);
                        return;
                    }
                }
                else
                {
                    ResetCamp(false);
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) \ay/makecamp loc [<Y> <X>]\ax was supplied incorrectly.", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    return;
                }
            }
            else if (ucCmdUsed == USED_CIRCLE)
            {
                EndPreviousCmd(true, ucCmdUsed, true); // dont reset circle variables
                if (isdigit(szCurrentArg[0]) || szCurrentArg[0] == '-' || szCurrentArg[0] == '.')
                {
                    fCircleY = (float)atof(szCurrentArg);
                    GetArg(szCurrentArg, szInput, usiArgNum++);
                    if (isdigit(szCurrentArg[0]) || szCurrentArg[0] == '-' || szCurrentArg[0] == '.')
                    {
                        fCircleX = (float)atof(szCurrentArg);
                        bCircling = true;
                        GetArg(szCurrentArg, szInput, usiArgNum);
                    }
                    else
                    {
                        EndPreviousCmd(true);
                        sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) Usage \ay/circle loc [<y> <x>] [other options]\ax", MODULE_NAME);
                        WriteLine(szMsg, VERB_ALL);
                        return;
                    }
                }
                else
                {
                    EndPreviousCmd(true);
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) Usage \ay/circle loc [<y> <x>] [other options]\ax", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    return;
                }
            }
        }
        else if((!strnicmp(szCurrentArg, "yloc", 5) || !strnicmp(szCurrentArg, "xloc", 5)) && ucCmdUsed == USED_MOVETO)
        {
            bool bUsingY = false;
            if (!strnicmp(szCurrentArg, "yloc", 5)) bUsingY = true;
            EndPreviousCmd(true);
            GetArg(szCurrentArg, szInput, usiArgNum++);
            if (isdigit(szCurrentArg[0]) || szCurrentArg[0] == '-' || szCurrentArg[0] == '.' )
            {
                if (bUsingY)
                {
                    fLocY = (float)atof(szCurrentArg);
                    fLocX = pChSpawn->X;
                }
                else
                {
                    fLocX = (float)atof(szCurrentArg);
                    fLocY = pChSpawn->Y;
                }
                bMoveToOn = true;
                GetArg(szCurrentArg, szInput, usiArgNum);
            }
            else
            {
                sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) \ay/moveto %s\ax was supplied incorrectly.", MODULE_NAME, bUsingY ? "yloc [Y]" : "xloc [X]");
                WriteLine(szMsg, VERB_ALL);
                EndPreviousCmd(true);
                return;
            }
        }
    }

    while (*szCurrentArg)
    {
        if (!strnicmp(szCurrentArg, "off", 4))
        {
            if (ucCmdUsed == USED_MAKECAMP)
            {
                ResetCamp(true, SET_ALT);
                return;
            }
            // dont want to end current command/movement if disabling makecamp
            // so endpreviouscmd comes after makecamp processing
            EndPreviousCmd(true);
            if (ucCmdUsed == USED_STICK)
            {
                sprintf(szMsg, "\ay%s\aw:: You are no longer sticking to anything.", MODULE_NAME);
            }
            else if (ucCmdUsed == USED_CIRCLE)
            {
                sprintf(szMsg, " \ay%s\aw:: Circling radius (\ag%.2f\ax), center (\ag%.2f\ax, \ag%.2f\ax) : \arOFF", MODULE_NAME, fCircleRadius, fCircleY, fCircleX);
            }
            else if (ucCmdUsed == USED_MOVETO)
            {
                sprintf(szMsg, "\ay%s\aw:: Moveto off.", MODULE_NAME);
            }
            WriteLine(szMsg, VERB_VERB);
            return;
        }
        else if (!strnicmp(szCurrentArg, "id", 3) && (ucCmdUsed == USED_STICK || ucCmdUsed == USED_MOVETO))
        {
            EndPreviousCmd(true, ucCmdUsed, true);
            PSPAWNINFO pByID = NULL;
            GetArg(szCurrentArg, szInput, usiArgNum);
            if (*szCurrentArg)
            {
                // strtoul verfies the arg is 100% numerical, atoi/atof do not
                if (!strtoul(szCurrentArg, &pNotNum, 10) || *pNotNum)
                {
                    WriteChatf("\ay%s\aw:: (\arERROR\ax) SpawnID must be numerical.", MODULE_NAME);
                    EndPreviousCmd(true);
                    return;
                }
                pByID = (PSPAWNINFO)GetSpawnByID(atoi(szCurrentArg));
                if (pByID)
                {
                    if (pByID->SpawnID == ((PSPAWNINFO)pLocalPlayer)->SpawnID)
                    {
                        sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) You cannot use your own SpawnID.", MODULE_NAME);
                        WriteLine(szMsg, VERB_ALL);
                        EndPreviousCmd(true);
                        return;
                    }
                    pTargetUsed = (PSPAWNINFO)pByID;
                    usiArgNum++; // incremeted if # is valid, but not otherwise so that someone can use '/stick id behind' to use target. bad form but nonetheless
                }
                else
                {
                    pTargetUsed = NULL;
                }
            }
            else if (pTarget)
            {
                if (((PSPAWNINFO)pTarget)->SpawnID == ((PSPAWNINFO)pLocalPlayer)->SpawnID)
                {
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) Invalid SpawnID and do not have a valid target.", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    EndPreviousCmd(true);
                    return;
                }
                pTargetUsed = (PSPAWNINFO)pTarget; // only use target if its not ourself
            }
            if (!pTargetUsed)
            {
                sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) Invalid SpawnID and do not have a target.", MODULE_NAME);
                WriteLine(szMsg, VERB_ALL);
                EndPreviousCmd(true);
                return;
            }
            //if we've made it this far, pTargetUsed is valid
            if (ucCmdUsed == USED_STICK)
            {
                bStickNewMob = false; // turns off 'always' when using 'id'
                ulStickID = pTargetUsed->SpawnID;
                stickTarget_Type = GetSpawnType(pTargetUsed);
                fCurrentDist = GetDistance(pChSpawn, pTargetUsed);
                bStickOn = bStickHold = true;
            }
            else if (ucCmdUsed == USED_MOVETO)
            {
                fLocY = pTargetUsed->Y;
                fLocX = pTargetUsed->X;
                bMoveToOn = true;
            }
        }
        // stick specific parameters
        else if (ucCmdUsed == USED_STICK)
        {
            EndPreviousCmd(true, ucCmdUsed, true); // dont reset stick variables
            if (strstr(szCurrentArg, "%"))
            {
                fStickDistModP = (float)atof(szCurrentArg) / 100.0f;
                // shouldnt do this here, need logic to output this only if used by itself
                // cant do it on an 'else' for the pTarget 'if' because of 'always' param
                sprintf(szMsg, "\ay%s\aw:: Stick mod changed Mod(\ag%.2f\ax) ModPercent(\ag%.2f%%%%\ax)", MODULE_NAME, fStickDistMod, fStickDistModP); // % = 2 for printf, 2 for eq xml processing. thanks dkaa!
                WriteLine(szMsg, VERB_ALL);
                if (bSetDist && fStickDist * fStickDistModP > 0.0f) fStickDist *= fStickDistModP;
                bStickOn = true;
            }
            else if (szCurrentArg[0] == '-')
            {
                fStickDistMod = (float)atof(szCurrentArg);
                sprintf(szMsg, "\ay%s\aw:: Stick mod changed Mod(\ag%.2f\ax) ModPercent(\ag%.2f%%%%\ax)", MODULE_NAME, fStickDistMod, fStickDistModP);
                WriteLine(szMsg, VERB_ALL);
                if (bSetDist && fStickDist + fStickDistMod >= 0) fStickDist += fStickDistMod;
                bStickOn = true;
            }
            else if (isdigit(szCurrentArg[0]) || szCurrentArg[0] == '.' )
            {
                if ((float)atof(szCurrentArg) * fStickDistModP + fStickDistMod > 0.0f)
                {
                    fStickDist = (float)atof(szCurrentArg) * fStickDistModP + fStickDistMod;
                }
                bSetDist = bStickOn = true;
            }
            else if (!strnicmp(szCurrentArg, "moveback", 9))
            {
                bMoveBack = bStickOn = true;
            }
            else if (!strnicmp(szCurrentArg, "loose", 6))
            {
                bLooseStick = bStickOn = true;
            }
            else if (!strnicmp(szCurrentArg, "uw", 3) || !strnicmp(szCurrentArg, "underwater", 11))
            {
                bUnderwater = bStickOn = true;
            }
            else if (!strnicmp(szCurrentArg, "hold", 5))
            {
                if (pTarget)
                {
                    if (((PSPAWNINFO)pTarget)->SpawnID == ((PSPAWNINFO)pLocalPlayer)->SpawnID)
                    {
                        sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) You cannot stick hold to yourself.", MODULE_NAME);
                        WriteLine(szMsg, VERB_VERB);
                        EndPreviousCmd(true);
                        return;
                    }
                    ulStickID = ((PSPAWNINFO)pTarget)->SpawnID;
                    stickTarget_Type = GetSpawnType((PSPAWNINFO)pTarget);
                    bStickOn = bStickHold = true;
                }
                else
                {
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) You must specify something to stick to!", MODULE_NAME);
                    WriteLine(szMsg, VERB_VERB);
                    EndPreviousCmd(true);
                    return;
                }
            }
            else if (!strnicmp(szCurrentArg, "on", 3))
            {
                // useless param, but removing it breaks popular macros 
                bStickOn = true;
            }
            else if (!strnicmp(szCurrentArg, "behind", 7))
            {
                bStickBehind = bStickOn = true;
                bStickSnaproll = bStickBehindOnce = bStickPin = bStickNotFront = bStickFront = false;
            }
            else if (!strnicmp(szCurrentArg, "behindonce", 11))
            {
                bStickBehindOnce = bStickOn = true;
                bStickSnaproll = bStickBehind = bStickPin = bStickNotFront = bStickFront = false;
            }
            else if (!strnicmp(szCurrentArg, "!front", 7))
            {
                bStickNotFront = bStickOn = true;
                bStickSnaproll = bStickBehind = bStickPin = bStickBehindOnce = bStickFront = false;
            }
            else if (!strnicmp(szCurrentArg, "front", 6))
            {
                bStickFront = bStickOn = true;
                bStickSnaproll = bStickBehind = bStickPin = bStickBehindOnce = bStickNotFront = false;
            }
            else if (!strnicmp(szCurrentArg, "pin", 4))
            {
                bStickPin = bStickOn = true;
                bStickSnaproll = bStickBehind = bStickBehindOnce = bStickNotFront = bStickFront = false;
            }
            else if (!strnicmp(szCurrentArg, "snaproll", 9))
            {
                bStickSnaproll = bStickOn = true;
                bStickBehind = bStickBehindOnce = bStickNotFront = bStickFront = bStickPin = false;
                fSnapBearing = HEADING_HALF;
                GetArg(szCurrentArg, szInput, usiArgNum);
                if (!strnicmp(szCurrentArg, "face", 6))
                {
                    usiArgNum++;
                    fSnapBearing = 1.0f;
                }
                else if (!strnicmp(szCurrentArg, "left", 5))
                {
                    usiArgNum++;
                    fSnapBearing = HEADING_QUARTER;
                }
                else if (!strnicmp(szCurrentArg, "right", 6))
                {
                    usiArgNum++;
                    fSnapBearing = (HEADING_HALF + HEADING_QUARTER);
                }
                else if (!strnicmp(szCurrentArg, "rear", 5))
                {
                    usiArgNum++; // uses HEADING_HALF (set above)
                }
            }
            else if (!strnicmp(szCurrentArg, "always", 7))
            {
                ulStickID = 0;
                stickTarget_Type = NONE;
                bStickHold = false; // reset hold values, dont allow 'hold' or 'id' with 'always'
                bStickNewMob = bStickOn = true;
                if (pTarget) bStickNewTarget = true;
                sprintf(szMsg, "\ay%s\aw:: You will now stick to every valid NPC target supplied.", MODULE_NAME);
                WriteLine(szMsg, VERB_FULL);
                StandIfNeeded();
                return; // must be the last parameter
            }
            else
            {
                EndPreviousCmd(true);
                if (!bTotalSilence) OutputHelp(ucCmdUsed, true);
                return;
            }
        }
        // moveto specific parameters
        else if (ucCmdUsed == USED_MOVETO)
        {
            if (!strnicmp(szCurrentArg, "loose", 6))
            {
                bLooseMoveTo = true;
            }
            else if (!strnicmp(szCurrentArg, "dist", 5))
            {
                GetArg(szCurrentArg, szInput, usiArgNum++);
                if (szCurrentArg[0] == '-')
                {
                    fMoveDistMod = (float)atof(szCurrentArg);
                    fMoveDist += fMoveDistMod;
                }
                else if (isdigit(szCurrentArg[0]))
                {
                    fMoveDist = ((float)atof(szCurrentArg) >= 1.0f) ? (float)atof(szCurrentArg) : fMoveDist;
                }
                else
                {
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) Incorrectly used \ay/moveto dist [#]\ax", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    return;
                }
                fMoveDistINI = fMoveDist;
                fMoveDistModINI = fMoveDistMod;
                sprintf(szMsg, "\ay%s\aw:: Moveto distance mod changed to \ag%.2f\ax.", MODULE_NAME, fMoveDist);
                WriteLine(szMsg, VERB_ALL);
                return;
            }
            else if (!strnicmp(szCurrentArg, "precisey", 9))
            {
                bYPrecision = true;
                bXPrecision = false;
            }
            else if (!strnicmp(szCurrentArg, "precisex", 9))
            {
                bXPrecision = true;
                bYPrecision = false;
            }
            else
            {
                EndPreviousCmd(true);
                if (!bTotalSilence) OutputHelp(ucCmdUsed, true);
                return;
            }
        }
        // makecamp specific parameters
        else if (ucCmdUsed == USED_MAKECAMP)
        {
            if (!strnicmp(szCurrentArg, "leash", 6))
            {
                GetArg(szCurrentArg, szInput, usiArgNum++);
                if (isdigit(szCurrentArg[0]))
                {
                    fLeashLength = (float)atof(szCurrentArg);
                    bLeash = true;
                }
                else
                {
                    bLeash = !bLeash;
                }
                fLeashLength = (fLeashLength <= fCampRadius) ? fCampRadius + 10 : fLeashLength;
            }
            else if (!strnicmp(szCurrentArg, "radius", 7))
            {
                GetArg(szCurrentArg, szInput, usiArgNum++);
                if (isdigit(szCurrentArg[0]))
                {
                    fCampRadius = (float)atof(szCurrentArg);
                    fCampRadius = (fCampRadius < 10.0f) ? 10.0f : fCampRadius;
                    fLeashLength = (fLeashLength >= fCampRadius) ? fLeashLength : fCampRadius + 10.0f;
                }
                else
                {
                    ResetCamp(false);
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) \ay/makecamp [radius <dist>]\ax was supplied incorrectly.", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    return;
                }
            }
            else if (!strnicmp(szCurrentArg, "mindelay", 9))
            {
                GetArg(szCurrentArg, szInput, usiArgNum++);
                if (isdigit(szCurrentArg[0]))
                {
                    iMinCampDelay = (atoi(szCurrentArg) >= 125) ? atoi(szCurrentArg) : iMinCampDelay;
                }
                else
                {
                    ResetCamp(false);
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) \ay/makecamp mindelay [#]\ax was supplied incorrectly.", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    return;
                }
            }
            else if (!strnicmp(szCurrentArg, "maxdelay", 9))
            {
                GetArg(szCurrentArg, szInput, usiArgNum++);
                if (isdigit(szCurrentArg[0]))
                {
                    iMaxCampDelay = (atoi(szCurrentArg) >= iMinCampDelay + 125) ? atoi(szCurrentArg) : iMinCampDelay + 125;
                }
                else
                {
                    ResetCamp(false);
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) \ay/makecamp maxdelay [#]\ax was supplied incorrectly.", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    return;
                }
            }
            else if (!strnicmp(szCurrentArg, "return", 7))
            {
                if (!bMakeCamp && !bCampPlayer)
                {
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) You do not have an active camp.", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    return;
                }
                bMakeCampReturn = true;
                StandIfNeeded();
                sprintf(szMsg, "\ay%s\aw:: MakeCamp returning to within camp radius immediately.", MODULE_NAME);
                WriteLine(szMsg, VERB_FULL);
                sprintf(szReturnMsg, "camp from /makecamp return");
                return;
            }
            else if (!strnicmp(szCurrentArg, "altreturn", 10))
            {
                if (bCampPlayer)
                {
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) You cannot use this command with a player-camp active.", MODULE_NAME);
                    WriteLine(szMsg, VERB_FULL);
                    return;
                }
                if (!bAltCamp || (fAltAnchorX == 0.0f && fAltAnchorY == 0.0f))
                {
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) You cannot use this command until you've established an altcamp location.", MODULE_NAME);
                    WriteLine(szMsg, VERB_FULL);
                    return;
                }
                sprintf(szMsg, "\ay%s\aw:: MakeCamp returning to altcamp immediately.%s", MODULE_NAME, bMakeCamp ? " Current camp now \arOFF\ax." : "");
                ResetCamp(false);
                bAltCampReturn = true;
                StandIfNeeded();
                WriteLine(szMsg, VERB_FULL);
                sprintf(szReturnMsg, "camp from /makecamp altreturn");
                return;
            }
            else if (!strnicmp(szCurrentArg, "player", 7))
            {
                ResetCamp(false);
                GetArg(szCurrentArg, szInput, usiArgNum++);
                if (*szCurrentArg)
                {
                    pCampPlayer = (PSPAWNINFO)GetSpawnByName(szCurrentArg);
                }
                else if (pTarget && ((PSPAWNINFO)pTarget)->Type == SPAWN_PLAYER)
                {
                    pCampPlayer = (PSPAWNINFO)GetSpawnByID(((PSPAWNINFO)pTarget)->SpawnID);
                }
                if (!pCampPlayer)
                {
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) Invalid player name and do not have a valid player target.", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    return;
                }
                if (pCampPlayer->SpawnID == ((PSPAWNINFO)pLocalPlayer)->SpawnID)
                {
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) You cannot makecamp yourself.", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    return;
                }
                // if we made it this far, pCampPlayer is valid
                sprintf(sszCampName, "%s", pCampPlayer->DisplayedName); // to save it for output msgs when issuing future changes
                ulCampPlayerID = pCampPlayer->SpawnID;
                campPlayer_Type = GetSpawnType(pCampPlayer);
                bMakeCamp = bCampPlayer = true;
                bMakeCampReturn = bAltCampReturn = false;
                fCampRadius = (fCampRadius < 10.0f) ? 10.0f : fCampRadius;
                fLeashLength = (fLeashLength >= fCampRadius) ? fLeashLength : fCampRadius + 10.0f;
            }
            else
            {
                ResetCamp(false);
                if (!bTotalSilence) OutputHelp(ucCmdUsed, true);
                return;
            }
        }
        // circle specific parameters
        else if (ucCmdUsed == USED_CIRCLE)
        {
            if (!strnicmp(szCurrentArg, "drunken", 8))
            {
                bDrunken = !bDrunken;
            }
            else if (!strnicmp(szCurrentArg, "clockwise", 10) || !strnicmp(szCurrentArg, "cw", 3))
            {
                bClockwise = true;
            }
            else if (!strnicmp(szCurrentArg, "ccw", 4) || !strnicmp(szCurrentArg, "counterclockwise", 17) || !strnicmp(szCurrentArg, "reverse", 8))
            {
                bClockwise = false;
            }
            else if (!strnicmp(szCurrentArg, "forwards", 9))
            {
                bBackwards = false;
            }
            else if (!strnicmp(szCurrentArg, "backwards", 10))
            {
                bBackwards = true;
            }
            else if (!strnicmp(szCurrentArg, "radius", 7))
            {
                GetArg(szCurrentArg, szInput, usiArgNum++);
                if (isdigit(szCurrentArg[0]))
                {
                    fCircleRadius = (float)atof(szCurrentArg);
                    GetArg(szCurrentArg, szInput, usiArgNum);
                }
                else
                {
                    sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) Use \ay/circle radius [#]\ax to set radius.", MODULE_NAME);
                    WriteLine(szMsg, VERB_ALL);
                    return;
                }
            }
            else
            {
                EndPreviousCmd(true);
                if (!bTotalSilence) OutputHelp(ucCmdUsed, true);
                return;
            }
        }
        // no valid parameter given
        else
        {
            EndPreviousCmd(true);
            if (!bTotalSilence) OutputHelp(ucCmdUsed);
            return;
        }
        GetArg(szCurrentArg, szInput, usiArgNum++);
    }

    StandIfNeeded();

    //Output Messages
    if (ucCmdUsed == USED_STICK)
    {
        if (!pTarget && !bStickHold && !bStickNewMob) // dont let process continue on to output msg if no target unless 'stick always' or 'stick id/hold'
        {
            sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) You must specify something to stick to!", MODULE_NAME);
            WriteLine(szMsg, VERB_VERB);
            EndPreviousCmd(true);
            return;
        }
        else if (pTarget)
        {
            if (((PSPAWNINFO)pTarget)->SpawnID == pChSpawn->SpawnID)
            {
                sprintf(szMsg, "\ay%s\aw:: (\arERROR\ax) You cannot stick to yourself.", MODULE_NAME);
                WriteLine(szMsg, VERB_VERB);
                EndPreviousCmd(true);
                return;
            }
            fCurrentDist = GetDistance(pChSpawn, (PSPAWNINFO)pTarget);
        }

        char szDir[25] = "\agNormal\ax";
        if (bStickBehind)
        {
            sprintf(szDir, "\ayBehind\ax");
        }
        else if (bStickPin) 
        {
            sprintf(szDir, "\aySide\ax");
        }
        else if (bStickNotFront) 
        {
            sprintf(szDir, "\ayNot Front\ax");
        }
        else if (bStickFront)
        {
            sprintf(szDir, "\ayFront\ax");
        }

        if (bStickHold)
        {
            PSPAWNINFO pTempSpawn = (PSPAWNINFO)GetSpawnByID(ulStickID);
            if (pTempSpawn)
            {
                sprintf(szTempID, "%s", pTempSpawn->DisplayedName);
            }
            else
            {
                // error checking in command line parsing should prevent these from ever happening
                // if user reports this error, needs deep investigation
                WriteChatf("\ay%s\aw:: \ar/stick hold/id NULL POINTER ERROR", MODULE_NAME);
                EndPreviousCmd(true);
                return;
            }
        }
        else if (pTarget)
        {
            sprintf(szTempID, "%s", ((PSPAWNINFO)pTarget)->DisplayedName);
        }
        else
        {
            // error checking in command line parsing should prevent these from ever happening
            // if user reports this error, needs deep investigation
            WriteChatf("\ay%s\aw:: \ar/stick NO TARGET ERROR", MODULE_NAME);
            EndPreviousCmd(true);
            return;
        }

        sprintf(szMsg, "\ay%s\aw:: You are now sticking to %s.", MODULE_NAME, szTempID);
        WriteLine(szMsg, VERB_VERB);
        sprintf(szMsg, "\ay%s\aw:: Dir(%s) Dist(\ag%.2f\ax) Mods(\ag%.2f | %.2f%%%%\ax) Hold(%s) Loose(%s) Water(%s)", MODULE_NAME, szDir, fStickDist, fStickDistMod, fStickDistModP, bStickHold ? "\agyes\ax" : "\ayno\ax", bLooseStick ? "\agyes\ax" : "\ayno\ax", bUnderwater ? "\agyes\ax" : "\ayno\ax");
        WriteLine(szMsg, VERB_VERB);
    }
    else if (ucCmdUsed == USED_MOVETO)
    {
        char szInfoY[35] = {0};
        char szInfoX[35] = {0};
        sprintf(szInfoY, " YDist(\ag%.2f\ax)", fMoveDistY);
        sprintf(szInfoX, " XDist(\ag%.2f\ax)", fMoveDistX);
        if (fLocX == 0.0f && fLocY == 0.0f) return;  // if we dont have coords, we dont want output
        sprintf(szReturnMsg, "/moveto location");
        sprintf(szMsg, "\ay%s\aw:: Moving to loc %.2f %.2f. - Dist(\ag%.2f\ax) Loose(%s)%s%s", MODULE_NAME, fLocY, fLocX, fMoveDist, bLooseMoveTo ? "\agyes\ax" : "\arno\ax", bYPrecision ? szInfoY : "", bXPrecision ? szInfoX : "");
        WriteLine(szMsg, VERB_VERB);
    }
    else if (ucCmdUsed == USED_CIRCLE)
    {
        sprintf(szMsg, " \ay%s\aw:: Circling radius (\ag%.2f\ax), center (\ag%.2f\ax, \ag%.2f\ax)%s%s : %s", MODULE_NAME, fCircleRadius, fCircleY, fCircleX, bClockwise ? "" : ", Reverse", bBackwards ? ", Backwards" : "", bCircling ? "\agON" : "\arOFF");
        WriteLine(szMsg, VERB_VERB);
    }
    else if (ucCmdUsed == USED_MAKECAMP)
    {
        if (!bCampPlayer)
        {
            sprintf(szMsg, "\ay%s\aw:: MakeCamp (%s). Y(\ag%.2f\ax) X(\ag%.2f\ax) Radius(\ag%.2f\ax) Leash(%s) LeashLen(\ag%.2f\ax) Min(\ag%d\ax) Max(\ag%d\ax)", MODULE_NAME, bMakeCamp ? "\agon\ax" : "\aroff\ax", fAnchorY, fAnchorX, fCampRadius, bLeash ? "\agon\ax" : "\aroff\ax", fLeashLength, iMinCampDelay, iMaxCampDelay);
        }
        else if (bCampPlayer)
        {
            sprintf(szMsg, "\ay%s\aw:: MakeCamp Player (\ag%s\ax). Radius(\ag%.2f\ax) Leash(%s) LeashLen(\ag%.2f\ax) Min(\ag%d\ax) Max(\ag%d\ax)", MODULE_NAME, sszCampName, fCampRadius, bLeash ? "\agon\ax" : "\aroff\ax", fLeashLength, iMinCampDelay, iMaxCampDelay);
        }
        WriteLine(szMsg, VERB_ALL);
    }
}

void ChangeSetting(unsigned char ucCmdUsed, bool bToggle, char szSetting[MAX_STRING])
{
    char szParameter[MAX_STRING] = {0};
    char szSetState[MAX_STRING] = {0};
    char szSetDigit[MAX_STRING] = {0};
    char szSetError[MAX_STRING] = {0};
    char szOn[7] = "\agON\ax";
    char szOff[8] = "\arOFF\ax";
    bool bTurnOn = false;
    bool bSetDigit = false;
    bool bErrorMsg = false;
    unsigned short usiArgNum = 1;

    char szCommand[MAX_STRING] = {0};
    if (ucCmdUsed == USED_MAKECAMP)
    {
        sprintf(szCommand, "/makecamp");
    }
    else if (ucCmdUsed == USED_STICK)
    {
        sprintf(szCommand, "/stick");
    }
    else if (ucCmdUsed == USED_MOVETO)
    {
        sprintf(szCommand, "/moveto");
    }
    else if (ucCmdUsed == USED_CIRCLE)
    {
        sprintf(szCommand, "/circle");
    }

    GetArg(szParameter, szSetting, usiArgNum++);

    // check for valid parameter if "set" used (on, off, number)
    if (!bToggle)
    {
        GetArg(szSetState, szSetting, usiArgNum);
        if (isdigit(szSetState[0]) || szSetState[0] == '.' || szSetState[0] == '-')
        {
            bSetDigit = true;
            sprintf(szSetDigit, szSetState); // for naming clarification only, waste ram ftw.
        }
        else if (!strnicmp(szSetState, "on", 3))
        {
            bTurnOn = true;
        }
        else if (!strnicmp(szSetState, "off", 4))
        {
            bTurnOn = false; // serves no point other than to confirm valid input
        }
        else
        {
            bErrorMsg = true;
            sprintf(szSetError, "\ay%s\aw:: \arERROR\ax: Invalid '%s set' syntax ( \ar%s\ax ) [on|off|number]", MODULE_NAME, szCommand, szParameter);
        }
    }

    if (!bErrorMsg && (bToggle || !bSetDigit))
    {
        if (!strnicmp(szParameter, "mpause", 7))
        {
            bPauseOnMove = bToggle ? !bPauseOnMove : bTurnOn;
            if (bPauseOnMove) bBreakOnKB = false;
            sprintf(szMsg, "\ay%s\aw:: Pause from manual movement turned %s", MODULE_NAME, bPauseOnMove ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "mousepause", 11))
        {
            bPauseOnMouse = bToggle ? !bPauseOnMouse : bTurnOn;
            if (bPauseOnMouse) bBreakOnMouse = false;
            sprintf(szMsg, "\ay%s\aw:: Pause from mouse movement turned %s", MODULE_NAME, bPauseOnMouse ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "breakonkb", 10))
        {
            bBreakOnKB = bToggle ? !bBreakOnKB : bTurnOn;
            if (bBreakOnKB) bPauseOnMove = false;
            sprintf(szMsg, "\ay%s\aw:: Break from keyboard movement turned %s", MODULE_NAME, bBreakOnKB ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "breakonmouse", 13))
        {
            bBreakOnMouse = bToggle ? !bBreakOnMouse : bTurnOn;
            if (bBreakOnMouse) bPauseOnMouse = false;
            sprintf(szMsg, "\ay%s\aw:: Break from mouse movement turned %s", MODULE_NAME, bBreakOnMouse ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "autosave", 9))
        {
            bAutoSave = bToggle ? !bAutoSave : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Auto-save settings to INI file turned %s", MODULE_NAME, bAutoSave ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "feign", 6))
        {
            bFeignSupport = bToggle ? !bFeignSupport : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Remain Feign support turned %s", MODULE_NAME, bFeignSupport ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "autopause", 10))
        {
            bAutoPause = bToggle ? !bAutoPause : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: AutoPause upon spell cast turned %s", MODULE_NAME, bAutoPause ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "stucklogic", 13))
        {
            bCheckStuck = bToggle ? !bCheckStuck : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Stuck-checking logic turned %s", MODULE_NAME, bCheckStuck ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "verbosity", 10))
        {
            bVerbosity = bToggle ? !bVerbosity : bTurnOn;
            if (bVerbosity) bTotalSilence = false;
            sprintf(szMsg, "\ay%s\aw:: Stick/Moveto verbosity turned %s", MODULE_NAME, bVerbosity ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "fullverbosity", 14))
        {
            bFullVerbosity = bToggle ? !bFullVerbosity : bTurnOn;
            if (bFullVerbosity) bTotalSilence = false;
            sprintf(szMsg, "\ay%s\aw:: Plugin Enhanced Verbosity turned %s", MODULE_NAME, bFullVerbosity ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "totalsilence", 13))
        {
            bTotalSilence = bToggle ? !bTotalSilence : bTurnOn;
            if (bTotalSilence) bVerbosity = bFullVerbosity = false;
            sprintf(szMsg, "\ay%s\aw:: Plugin Silence turned %s", MODULE_NAME, bTotalSilence ? szOn : szOff);
            WriteLine(szMsg, VERB_ENFORCE);
            return;
        }
        else if (!strnicmp(szParameter, "nohott", 7))
        {
            bNoHoTT = bToggle ? !bNoHoTT : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: NoHoTT aggro checking turned %s", MODULE_NAME, bNoHoTT ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "hidehelp", 9))
        {
            bHideHelp = bToggle ? !bHideHelp : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Hide all help output %s", MODULE_NAME, bHideHelp ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "breakongate", 12))
        {
            bBreakOnGate = bToggle ? !bBreakOnGate : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Break command when NPC gates turned %s", MODULE_NAME, bBreakOnGate ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "breakonwarp", 12))
        {
            bBreakOnWarp = bToggle ? !bBreakOnWarp : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Break command when NPC warps away turned %s", MODULE_NAME, bBreakOnWarp ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "alwaysdrunk", 12))
        {
            bAlwaysDrunk = bToggle ? !bAlwaysDrunk : bTurnOn;
            bDrunken = bAlwaysDrunk;
            sprintf(szMsg, "\ay%s\aw:: Circle always drunken %s", MODULE_NAME, bAlwaysDrunk ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "alwaysbackwards", 16))
        {
            bAlwaysBack = bToggle ? !bAlwaysBack : bTurnOn;
            bBackwards = bAlwaysBack;
            sprintf(szMsg, "\ay%s\aw:: Circle always backwards %s", MODULE_NAME, bBackwards ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "alwaysccw", 10))
        {
            bAlwaysCCW = bToggle ? !bAlwaysCCW : bTurnOn;
            bClockwise = !bAlwaysCCW;
            sprintf(szMsg, "\ay%s\aw:: Circle always counter-clockwise %s", MODULE_NAME, bAlwaysCCW ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "alwaysloose", 12) && (ucCmdUsed == USED_MOVETO || ucCmdUsed == USED_STICK))
        {
            if (ucCmdUsed == USED_MOVETO)
            {
                bAlwaysLooseMoveTo = bToggle ? !bAlwaysLooseMoveTo : bTurnOn;
                bLooseMoveTo = bAlwaysLooseMoveTo;
                sprintf(szMsg, "\ay%s\aw:: Always Loose MoveTo %s", MODULE_NAME, bAlwaysLooseMoveTo ? szOn : szOff);
            }
            else      // if USED_STICK
            {
                bAlwaysLooseStick = bToggle ? !bAlwaysLooseStick : bTurnOn;
                bLooseStick = bAlwaysLooseStick;
                sprintf(szMsg, "\ay%s\aw:: Always Loose Stick %s", MODULE_NAME, bAlwaysLooseStick ? szOn : szOff);
            }
        }
        else if (!strnicmp(szParameter, "loose", 6) && (ucCmdUsed == USED_MOVETO || ucCmdUsed == USED_STICK))
        {
            if (ucCmdUsed == USED_MOVETO)
            {
                bLooseMoveTo = bToggle ? !bLooseMoveTo : bTurnOn;
                sprintf(szMsg, "\ay%s\aw:: MoveTo loose turned %s", MODULE_NAME, bLooseMoveTo ? szOn : szOff);
            }
            else      // if USED_STICK
            {
                bLooseStick = bToggle ? !bLooseStick : bTurnOn;
                sprintf(szMsg, "\ay%s\aw:: Stick loose turned %s", MODULE_NAME, bLooseStick ? szOn : szOff);
            }
        }
        else if (!strnicmp(szParameter, "breakonsummon", 14))
        {
            bBreakOnSummon = bToggle ? !bBreakOnSummon : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Break command when summoned \ag%.2f\ax distance: %s", MODULE_NAME, fBreakSummonDist, bBreakOnSummon ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "nohottfront", 12))
        {
            bSpinInCircles = bToggle ? !bSpinInCircles : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Spin in circles when I lose aggro with no HoTT using '/stick front': %s", MODULE_NAME, bSpinInCircles ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "savebychar", 11))
        {
            bSaveByChar = bToggle ? !bSaveByChar : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Save INI file Character Name section turned: %s", MODULE_NAME, bSaveByChar ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "returnnoaggro", 14) && ucCmdUsed == USED_MAKECAMP)
        {
            bReturnNoAggro = bToggle ? !bReturnNoAggro : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Return to camp when not aggro turned %s", MODULE_NAME, bReturnNoAggro ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "returnnotlooting", 17) && ucCmdUsed == USED_MAKECAMP)
        {
            bReturnNotLooting = bToggle ? !bReturnNotLooting : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Return to camp when not looting turned %s", MODULE_NAME, bReturnNotLooting ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "returnnotarget", 17) && ucCmdUsed == USED_MAKECAMP)
        {
            bReturnNoTarget = bToggle ? !bReturnNoTarget : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Return to camp regardless of target turned %s", MODULE_NAME, bReturnNoTarget ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "leash", 6) && ucCmdUsed == USED_MAKECAMP)
        {
            bLeash = bToggle ? !bLeash : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Leash to camp turned %s", MODULE_NAME, bLeash ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "usewalk", 8))
        {
            bWalkMoveTo = bToggle ? !bWalkMoveTo : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Walk when close to moveto/camp return destination turned %s", MODULE_NAME, bWalkMoveTo ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "trytojump", 10))
        {
            bTryToJump = bToggle ? !bTryToJump : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Try to jump when stuck turned %s", MODULE_NAME, bTryToJump ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "turnhalf", 9))
        {
            bTurnHalf = bToggle ? !bTurnHalf : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Reset heading and try other way if turned halfway (stucklogic) turned %s", MODULE_NAME, bTurnHalf ? szOn : szOff);
        }
        else if (!strnicmp(szParameter, "usescatter", 11))
        {
            bScatter = bToggle ? !bScatter : bTurnOn;
            sprintf(szMsg, "\ay%s\aw:: Scatter camp returns (%s) ScatDist(\ag%.2f\ax) Bearing(\ag%.2f\ax) ScatDist(\ag%.2f\ax) ScatSize(\ag%.2f\ax)", MODULE_NAME, bScatter ? szOn : szOff, fScatDist, fBearing, fScatter);
        }
        else
        {
            bErrorMsg = true;
            sprintf(szSetError, "\ay%s\aw:: \arERROR\ax: Not a valid %s %s ( \ar%s\ax ).", MODULE_NAME, szCommand, bToggle ? "toggle" : "set option", szParameter);
        }
    }
    else if (!bErrorMsg && bSetDigit)
    {
        if (!strnicmp(szParameter, "pulsecheck", 11))
        {
            usiPulseCheck = (atoi(szSetDigit) > 1) ? (unsigned short)atoi(szSetDigit) : usiPulseCheck;
            sprintf(szMsg, "\ay%s\aw:: StuckLogic pulse check rate set to \ag%d\ax pulses.", MODULE_NAME, usiPulseCheck);
        }
        else if (!strnicmp(szParameter, "pulseunstuck", 13))
        {
            usiPulseUnstuck = (atoi(szSetDigit) > 1) ? (unsigned short)atoi(szSetDigit) : usiPulseUnstuck;
            sprintf(szMsg, "\ay%s\aw:: StuckLogic pulse unstuck value set to \ag%d\ax pulses.", MODULE_NAME, usiPulseUnstuck);
        }
        else if (!strnicmp(szParameter, "diststuck", 10))
        {
            fDistStuck = ((float)atof(szSetDigit) > 0.0f) ? (float)atof(szSetDigit) : fDistStuck;
            sprintf(szMsg, "\ay%s\aw:: StuckLogic distance moved per pulse (else stuck) set to \ag%.3f", MODULE_NAME, fDistStuck);
        }
        else if (!strnicmp(szParameter, "campmindelay", 13))
        {
            iMinCampDelay = (atoi(szSetDigit) >= 125) ? atoi(szSetDigit) : iMinCampDelay;
            sprintf(szMsg, "\ay%s\aw:: Mindelay for camp return set to \ag%d", MODULE_NAME, iMinCampDelay);
        }
        else if (!strnicmp(szParameter, "campmaxdelay", 13))
        {
            iMaxCampDelay = (atoi(szSetDigit) >= iMinCampDelay + 125) ? atoi(szSetDigit) : iMinCampDelay + 125;
            sprintf(szMsg, "\ay%s\aw:: Maxdelay for camp return set to \ag%d", MODULE_NAME, iMaxCampDelay);
        }
        else if (!strnicmp(szParameter, "pausemindelay", 14))
        {
            iMinPauseDelay = (atoi(szSetDigit) >= 125) ? atoi(szSetDigit) : iMinPauseDelay;
            sprintf(szMsg, "\ay%s\aw:: Mindelay for mpause/mousepause set to \ag%d", MODULE_NAME, iMinPauseDelay);
        }
        else if (!strnicmp(szParameter, "pausemaxdelay", 14))
        {
            iMaxPauseDelay = (atoi(szSetDigit) >= iMinPauseDelay + 125) ? atoi(szSetDigit) : iMinPauseDelay + 125;
            sprintf(szMsg, "\ay%s\aw:: Maxdelay for mpause/mousepause set to \ag%d", MODULE_NAME, iMaxPauseDelay);
        }
        else if (!strnicmp(szParameter, "ydist", 6))
        {
            fMoveDistY = ((float)atof(szSetDigit) >= 1.0f) ? (float)atof(szSetDigit) : fMoveDistY;
            sprintf(szMsg, "\ay%s\aw:: MoveTo Y-Precision set to \ag%.2f", MODULE_NAME, fMoveDistY);
        }
        else if (!strnicmp(szParameter, "xdist", 6))
        {
            fMoveDistX = ((float)atof(szSetDigit) >= 1.0f) ? (float)atof(szSetDigit) : fMoveDistX;
            sprintf(szMsg, "\ay%s\aw:: MoveTo X-Precision set to \ag%.2f", MODULE_NAME, fMoveDistX);
        }
        else if (!strnicmp(szParameter, "dist", 5) && ucCmdUsed == USED_MOVETO)
        {
            fMoveDistINI = ((float)atof(szSetDigit) >= 1.0f) ? (float)atof(szSetDigit) : fMoveDistINI;
            fMoveDist = fMoveDistINI;
            sprintf(szMsg, "\ay%s\aw:: MoveTo ArrivalDist set to \ag%.2f", MODULE_NAME, fMoveDistINI);
        }
        else if (!strnicmp(szParameter, "turnrate", 9))
        {
            fTurnRate = ((float)atof(szSetDigit) >= 12.0f && (float)atof(szSetDigit) <= 24.0f) ? (float)atof(szSetDigit) : fTurnRate;
            sprintf(szMsg, "\ay%s\aw:: Loose Turn Rate set to \ag%.2f", MODULE_NAME, fTurnRate);
        }
        else if (!strnicmp(szParameter, "breakdist", 10))
        {
            fBreakDist = ((float)atof(szSetDigit) >= 1.0f) ? (float)atof(szSetDigit) : fBreakDist;
            sprintf(szMsg, "\ay%s\aw:: BreakOnWarp dist set to \ag%.2f", MODULE_NAME, fBreakDist);
        }
        else if (!strnicmp(szParameter, "campradius", 11))
        {
            fCampRadius = ((float)atof(szSetDigit) >= 10.0f) ? (float)atof(szSetDigit) : fCampRadius;
            sprintf(szMsg, "\ay%s\aw:: Camp radius set to \ag%.2f", MODULE_NAME, fCampRadius);
        }
        else if (!strnicmp(szParameter, "circleradius", 13))
        {
            fCircleRadius = ((float)atof(szSetDigit) >= 5.0) ? (float)atof(szSetDigit) : fCircleRadius;
            sprintf(szMsg, "\ay%s\aw:: Circle radius set to \ag%.2f", MODULE_NAME, fCircleRadius);
        }
        else if (!strnicmp(szParameter, "leashlength", 12))
        {
            fLeashLength = ((float)atof(szSetDigit) >= fCampRadius) ? (float)atof(szSetDigit) : fLeashLength;
            sprintf(szMsg, "\ay%s\aw:: Leash length set to \ag%.2f", MODULE_NAME, fLeashLength);
        }
        else if (!strnicmp(szParameter, "bearing", 8))
        {
            fBearing = (float)atof(szSetDigit);
            sprintf(szMsg, "\ay%s\aw:: Camp return scatter bearing set to \ag%.2f", MODULE_NAME, fBearing);
        }
        else if (!strnicmp(szParameter, "scatsize", 9))
        {
            fScatter = ((float)atof(szSetDigit) >= 1.0f) ? (float)atof(szSetDigit) : fScatter;
            sprintf(szMsg, "\ay%s\aw:: Camp return scatter size set to \ag%.2f", MODULE_NAME, fScatter);
        }
        else if (!strnicmp(szParameter, "scatdist", 9))
        {
            fScatDist = ((float)atof(szSetDigit) >= 1.0f) ? (float)atof(szSetDigit) : fScatDist;
            sprintf(szMsg, "\ay%s\aw:: Camp return scatter dist set to \ag%.2f", MODULE_NAME, fScatDist);
        }
        else
        {
            bErrorMsg = true;
            sprintf(szSetError, "\ay%s\aw:: \arERROR\ax: Invalid '%s set' parameter ( \ar%s\ax )", MODULE_NAME, szCommand, szParameter);
        }
    }

    if (bErrorMsg)
    {
        WriteLine(szSetError, VERB_ALL);
        return;
    }

    WriteLine(szMsg, VERB_ALL);
    if (bAutoSave) Save_INI();
}

void EndPreviousCmd(bool bKillAllMovement, unsigned char ucCmdUsed, bool bDontResetSelf)
{
    //reset pause
    bAllPaused = false;
    iPauseReturnTime = 0;
    //reset stuck logic
    fPrevX = 0.0f;
    fPrevY = 0.0f;
    fPulseAvg = 1.0f;
    usiStuck = 0;
    bCmdMovedFwd = bCmdMovedSide = false;
    fDistFromStick = 0.0f;
    fOrigHead = 0.0f;
    fCompNegHead = 0.0f;
    fCompPosHead = 0.0f;
    fHalfHead = 0.0f;
    // break on summon
    fNewCompare = 0.0f; // reset current dist
    fMyCurX = 0.0f;
    fMyCurY = 0.0f;
    // stick related
    fCurrentDist = 0.0f; // reset current dist so that breakonwarp doesn't fire when you first stick to a new target.
    // reset snaproll
    fSnapHeading = 0.0f;
    fSnapX = 0.0f;
    fSnapY = 0.0f;
    fSnapBearing = HEADING_HALF;
    // loose heading related
    if (fAdjustHead != 10000.0f) fAdjustHead = 10000.0f;

    // break any active commands
    // we check 'if' with bool & command because !bool & command if
    // would only process one if and we need all 'else' to process
    if (bDontResetSelf && ucCmdUsed == USED_CIRCLE)
    {
        //avoid circle
    }
    else
    {
        bCircling = bCirclingWasOn = false;
        bDrunken = bAlwaysDrunk;
        bBackwards = bAlwaysBack;
        bClockwise = !bAlwaysCCW;
    }
    if (bDontResetSelf && ucCmdUsed == USED_MOVETO)
    {
        //avoid moveto
    }
    else
    {
        bMoveToOn = bMakeCampReturn = bAltCampReturn = bCampReturning = bReturnInProcess = false;
        fMoveDist = fMoveDistINI;
        fMoveDistMod = fMoveDistModINI;
        bLooseMoveTo = bAlwaysLooseMoveTo;
        bXPrecision = bYPrecision = false;
        fGetMoveToDist = 0.0f;
    }
    if (bDontResetSelf && ucCmdUsed == USED_STICK)
    {
        //avoid stick
    }
    else
    {
        bStickOn = bStickWasOn = bStickHold = bSetDist = false;
        bStickBehind = bStickBehindOnce = bStickSnaproll = false;
        bStickPin = bStickNotFront = bStickFront = bMoveBack = false;
        bUnderwater = false;
        bStickNewMob = bStickNewTarget = false;
        bImAggro = bCompareMobAgain = false;
        ulStickID = 0;
        stickTarget_Type = NONE;

        bLooseStick = bAlwaysLooseStick;
    }

    if (GetGameState() != GAMESTATE_INGAME)
    {
        // reseting vars (above) is desired in any state. the below call only desired if ingame
        return;
    }
    if (bKillAllMovement) DoMovement(APPLY_TO_ALL, false, false);
}

void ResetCamp(bool bOffMsg, unsigned char ucAltCamp)
{
    // reset main camp vars
    bMakeCamp = bCampPlayer = false;
    iCampReturnTime = 0;
    ulCampPlayerID = 0;
    campPlayer_Type = NONE;

    if (ucAltCamp == SET_ALT)
    {
        //setup alt camp values if they exist
        if (fAnchorX != 0.0f && fAnchorY != 0.0f)
        {
            bAltCamp = true;
            fAltAnchorX = fAnchorX;
            fAltAnchorY = fAnchorY;
            fAltCampRadius = fCampRadius;
        }
    }
    else if (ucAltCamp == RESET_ALT)
    {
        // reset alt camp
        bAltCamp = false;
        fAltAnchorX = 0.0f;
        fAltAnchorY = 0.0f;
        fAltCampRadius = 0.0f;
    }

    // reset camp coords
    fAnchorX = 0.0f;
    fAnchorY = 0.0f;

    if (bMoveToOn && (bMakeCampReturn || bAltCampReturn || bCampReturning))
    {
        EndPreviousCmd(true);
    }

    // call ResetCamp(false) if resetting before setting up new camp to not display this
    if (bOffMsg)
    {
        sprintf(szMsg, "\ay%s\aw:: MakeCamp off.", MODULE_NAME);
        WriteLine(szMsg, VERB_VERB);
    }
}

// End Command input handling
///////////////////////////////////////
// Begin Main function

void WeGetSignal(unsigned char ucCmdUsed)
{
    if (bBrokeOnSummon) return;
    PSPAWNINFO pChSpawn    = (PSPAWNINFO)pCharSpawn;
    PCHARINFO  pChData     = (PCHARINFO)pCharData;
    PSPAWNINFO pLPlayer    = (PSPAWNINFO)pLocalPlayer;
    PSPAWNINFO psTarget    = (PSPAWNINFO)(bStickHold ? GetSpawnByID(ulStickID) : pTarget);
    PSPAWNINFO pCampPlayer = (PSPAWNINFO) GetSpawnByID(ulCampPlayerID);

    // handle null pointers for stick id, & stick hold
    if (ucCmdUsed == USED_STICK)
    {
        // prevent sticking to an id that changed without our knowledge
        if ((bStickOn && bStickHold && ulStickID && (!psTarget || psTarget && stickTarget_Type != GetSpawnType(psTarget))) || (bStickOn && !bStickNewMob && !bStickHold && !pTarget))
        {
            EndPreviousCmd(true);
            sprintf(szMsg, "\ay%s\aw:: You are no longer sticking to anything.", MODULE_NAME);
            WriteLine(szMsg, VERB_VERB);
            return;
        }
    }
    // end null stick pointers

    // handle /makecamp player if the player no longer exists or has died
    if (bCampPlayer && ulCampPlayerID && (!pCampPlayer || pCampPlayer && campPlayer_Type != GetSpawnType(pCampPlayer)))
    {
        sprintf(szMsg, "\ay%s\aw:: MakeCamp player ended due to player leaving/death.", MODULE_NAME);
        WriteLine(szMsg, VERB_ALL);
        ResetCamp(false);
        return;
    }
    // end /makecamp player handling

    // handle null pointers
    if (!pChSpawn || !pChData || !pLPlayer || (pChSpawn && !pChSpawn->SpawnID) || !GetCharInfo2() || (!psTarget && ucCmdUsed == USED_STICK) || (!pCampPlayer && bCampPlayer))
    {
        sprintf(szMsg, "\ay%s\aw:: Null pointer, turning off current command", MODULE_NAME);
        WriteLine(szMsg, VERB_ENFORCE);
        EndPreviousCmd(true);
        return;
    }
    // end null pointers

    SYSTEMTIME stCurr;
    GetSystemTime(&stCurr);
    int iElapsedTime = diff_in_ms(stCurr, stReturnTime); // used in calculating min/max return times
    float fNewHeading = 0.0f; // heading changes used by all commands
    float fSpeedMultiplier = pChSpawn->SpeedMultiplier;
    bool bMounted = pChSpawn->Mount ? true : false; // used by stucklogic
    bool bStunned = pChData->Stunned ? true : false; // used by stucklogic and autopause
    bool bRooted = (fSpeedMultiplier == -10000.0f || pChSpawn->RunSpeed < -0.4f) ? true : false;
    bool bSnared = (fSpeedMultiplier < 0.0f || pChSpawn->RunSpeed < 0.0f) ? true : false;
    SpewDebug(DBG_OTHER, "Snared %s  Rooted %s", bSnared ? "true" : "false", bRooted ? "true" : "false");
    bool bImFloating = (pChSpawn->Levitate == 2) ? true: false;
    SpewDebug(DBG_OTHER, "float: %s - Levitate = %d", bImFloating ? "true" : "false", pChSpawn->Levitate);
    static bool sbJumping = false; // if we executed a jump in stucklogic
    bool bInJump = ((pChSpawn->Animation == 19 || pChSpawn->Animation == 20) && !bImFloating) ? true : false;
    if (!bInJump) sbJumping = false;
    bool bUseStuck = (!bRooted && !bSnared); // set false by stick/moveto/circle if using stucklogic is not desired for this pulse
    SpewDebug(DBG_OTHER, "Initial UseStuck %s", bUseStuck ? "true" : "false");
    bool bIsBard = IsBardClass(); // used by autopause and might want this for stucklogic speedmultiplier
    bool bMoveToOOR = true; // used by moveto, set to false if in range
    float fDistance = 0.0f; // used by circle for stuck logic
    float fCompareMath = 0.0f; // used by breakonsummon & breakonwarp

    // handle breakonsummon
    // might want to rewrite this to use same idea as stucklogic with fPulseMoved ???
    if (bBreakOnSummon && ucCmdUsed != USED_MAKECAMP)
    {
        float fMyLastX = (fMyCurX > 0.0f ? fMyCurX : pChSpawn->X);
        float fMyLastY = (fMyCurY > 0.0f ? fMyCurY : pChSpawn->Y);
        float fOldCompare = fNewCompare;
        fMyCurX = pChSpawn->X;
        fMyCurY = pChSpawn->Y;
        float fCheckX = fMyCurX - fMyLastX;
        float fCheckY = fMyCurY - fMyLastY;
        fNewCompare = fabs(sqrtf(fCheckX*fCheckX + fCheckY*fCheckY));
        if (fOldCompare == 0.0f) fOldCompare = fNewCompare;
        fCompareMath = fabs(fNewCompare - fOldCompare);
        SpewDebug(DBG_OTHER, "BreakOnSummon Calculation: %.2f - %.2f > %.2f", fNewCompare, fOldCompare, fBreakSummonDist);
        if (fCompareMath > 0 && fCompareMath > fBreakSummonDist)
        {
            EndPreviousCmd(true);
            sprintf(szMsg, "\ay%s\aw:: \arWARNING\ax Command ending from character summoned beyond BreakSummonDist range.", MODULE_NAME);
            WriteLine(szMsg, VERB_ENFORCE);
            sprintf(szMsg, "\ay%s\aw:: \arWARNING\ax Verify you are not being monitored and type \ag/stick imsafe\ax to allow command usage.", MODULE_NAME);
            WriteLine(szMsg, VERB_ENFORCE);
            bBrokeOnSummon = bAllPaused = true;
            SpewDebug(DBG_ALL, "Summon Detection fired. All Commands Paused. fBreakSummonDist(%.2f) fCompareMath(%.2f)", fBreakSummonDist, fCompareMath);
            return;
        }
    }
    //end breakonsummon

    // set stick vars if needed
    if (ucCmdUsed == USED_STICK)
    {
        if (!bSetDist)
        {
            fStickDist = (psTarget->StandState ? get_melee_range(pLocalPlayer, (EQPlayer *)psTarget) : 15.0f) * fStickDistModP + fStickDistMod;
            bSetDist = true;
            SpewDebug(DBG_DISABLE, "setdist was not set, using melee range. fStickDist = %.2f", fStickDist);
        }

        float fPrevDist = fCurrentDist;
        fCurrentDist = fabs(GetDistance(pChSpawn, psTarget));
        fDistFromStick = fCurrentDist - fStickDist;
        //if (fDistFromStick < 10.0f) bUseStuck = false; // dont use stucklogic if we are within 10.0 from desired stickdist

        // if we've changed targets mid-stick, dont trigger stucklogic or breakonwarp
        if (ulOldStickID != psTarget->SpawnID)
        {
            bUseStuck = false;
            fPrevDist = 0.0f;
        }
        ulOldStickID = psTarget->SpawnID;

        // handle breakonwarp for stick
        if (bBreakOnWarp)
        {
            if (fPrevDist == 0.0f) fPrevDist = fCurrentDist; // added to prevent initial breakonwarp msg firing if mob is out of range to begin with
            //DebugSpew("BreakOnWarp Stick: fCurrentDist = %.2f fPrevDist = %.2f", fCurrentDist, fPrevDist);

            fCompareMath = fabs(fCurrentDist - fPrevDist);
            if (fCompareMath > 0 && fCompareMath > fBreakDist)
            {
                EndPreviousCmd(true);
                sprintf(szMsg, "\ay%s\aw:: Stick ending from mob warping out of BreakDist range.", MODULE_NAME);
                WriteLine(szMsg, VERB_ALL);
                SpewDebug(DBG_ALL, "MoveUtils: Stick ended from mob warping out of BreakDist range. fBreakDist = %.2f", fBreakDist);
                return;
            }
        }
        // end breakonwarp
    }
    // end stick vars

    // begin normal autopause
    if (bAutoPause)
    {
        // convert to long because spellid is defined as unsigned but data can be negative to represent not casting
        if (((long)(pChSpawn->CastingData.SpellID) >= 0 && !bIsBard) || (pChSpawn->StandState != STANDSTATE_STAND && pChSpawn->StandState != STANDSTATE_DUCK) || bStunned || bRooted || (ucCmdUsed == USED_STICK && psTarget->SpawnID == pChSpawn->SpawnID))
        {
            SpewDebug(DBG_ALL, "MoveUtils AutoPause: halting movement.");
            DoMovement(APPLY_TO_ALL, false, false);
            return; // no need to use a bool(casting) and cycle false conditions over and over , just return until above is false
        }
    }
    // end normal autopause

    // makecamp altreturn
    if (bAltCampReturn)
    {
        //DebugSpew("altcampreturn values setup");
        iCampReturnTime = 0;
        if (!bScatter) CampReturn(fAltAnchorX, fAltAnchorY, fAltCampRadius);
        else PolarSpot(fAltAnchorX, fAltAnchorY, 0.0f, fBearing, fScatDist, fScatter);
        fLocY = fGotoY;
        fLocX = fGotoX;
        bMoveToOn = true;
        bXPrecision = bYPrecision = false;
        bMakeCampReturn = bAltCampReturn = false;
        return;
    }
    // end altreturn
    // makecamp handling
    if (!bReturnInProcess && (bMakeCamp || bCampPlayer))
    {
        float fMyDistFromCamp = GetDistance(pChSpawn->X, pChSpawn->Y, bCampPlayer ? pCampPlayer->X : fAnchorX, bCampPlayer ? pCampPlayer->Y : fAnchorY);
        float fDestDistFromCamp = GetDistance(bStickOn ? psTarget->X : fLocX, bStickOn ? psTarget->Y : fLocY, bCampPlayer ? pCampPlayer->X : fAnchorX, bCampPlayer ? pCampPlayer->Y : fAnchorY);

        // break from command if it would exceed active leash
        if (bLeash && (bStickOn || bMoveToOn))
        {
            if (fMyDistFromCamp < fDestDistFromCamp && fDestDistFromCamp > fLeashLength)
            {
                DebugSpew("MakeCamp halted %s command for being out of leash range", bStickOn ? "stick" : "moveto");
                EndPreviousCmd(true);
                sprintf(szMsg, "\ay%s\aw:: Outside of leash length, breaking from current command", MODULE_NAME);
                WriteLine(szMsg, VERB_VERB);
                return;
            }
        }
        // end leash new command check

        // if makecamp return issued, or if makecamp on check to see if we need to move back
        if (!bManuallyMoving && !bStickOn && !bMoveToOn && !bCircling)
        {
            // check for in combat
            bool bInCombat = false;
            if (bReturnNoAggro)
            {
                if (((PCPLAYERWND)pPlayerWnd)->CombatState == 0 && ((CXWnd*)pPlayerWnd)->GetChildItem("PW_CombatStateAnim")) bInCombat = true;
            }
            //end incombat check

            // normal return
            if (fMyDistFromCamp > fCampRadius + 2.0f || bMakeCampReturn) // give leeway to avoid teetering
            {
                bool bDoReturn = true;
                if (!bMakeCampReturn)
                {
                    if (bReturnNoAggro)
                    {
                        // second if instead of (bReturnNoAggro && bInCombat) as we dont
                        // want to process returnnotarget unless returnnoaggro is off
                        if (bInCombat) bDoReturn = false;
                    }
                    else if (!bReturnNoTarget && pTarget)
                    {
                        bDoReturn = false;
                    }
                    if (bDoReturn) // no need to process if we already determined dont
                    {
                        /*if (bCampPlayer && ((PSPAWNINFO)pTarget)->SpawnID == pCampPlayer->SpawnID)
                        {
                        bDoReturn = false;
                        }*/
                        if (bReturnNotLooting && pActiveCorpse) bDoReturn = false;
                    }
                }

                // processed conditions in which not to return, if none are met, begin returning
                if (bDoReturn)
                {
                    if (bMakeCampReturn || (iCampReturnTime != 0 && iElapsedTime >= iCampReturnTime))
                    {
                        iCampReturnTime = 0;
                        if (!bScatter) CampReturn(bCampPlayer ? pCampPlayer->X : fAnchorX, bCampPlayer ? pCampPlayer->Y : fAnchorY, fCampRadius);
                        else PolarSpot(bCampPlayer ? pCampPlayer->X : fAnchorX, bCampPlayer ? pCampPlayer->Y : fAnchorY, 0.0f, fBearing, fScatDist, fScatter);
                        fLocY = fGotoY;
                        fLocX = fGotoX;
                        bMoveToOn = bReturnInProcess = true;
                        bXPrecision = bYPrecision = false;
                        if (!bMakeCampReturn) bCampReturning = true; // so the output msg isnt displayed unless user/macro issued command and used in pause
                        bMakeCampReturn = bAltCampReturn = false;
                    }
                    else if (iCampReturnTime == 0)
                    {
                        GetSystemTime(&stReturnTime);
                        iCampReturnTime = (int)rand() % (iMaxCampDelay - iMinCampDelay + 1) + iMinCampDelay;
                        return; // return here to begin waiting for return time
                    }
                }
            }
            // end normal return
        }

        // begin leash processing with active stick/circle
        if (!bManuallyMoving && bLeash && (bStickOn || bCircling || bStickWasOn || bCirclingWasOn))
        {
            float fHeadBackX = bCampPlayer ? pCampPlayer->X : fAnchorX;
            float fHeadBackY = bCampPlayer ? pCampPlayer->Y : fAnchorY;
            if (fMyDistFromCamp > fLeashLength + 2.0f) // give leeway if teetering
            {
                if (bStickOn || bCircling)
                {
                    if (bStickOn)
                    {
                        EndPreviousCmd(true, USED_STICK, true);
                        bStickOn = bImAggro = bCompareMobAgain = false; // disable stick but don't reset current cmd settings
                        bStickBehindOnce = bStickSnaproll = false; // disable these as well since locations will no longer be accurate
                        bStickWasOn = true;
                    }
                    else
                    {
                        EndPreviousCmd(true, USED_CIRCLE, true);
                        bCircling = false; // disable circling but don't reset current cmd settings
                        bCirclingWasOn = true;
                    }
                }
                if (psTarget && (bStickWasOn || bCirclingWasOn))
                {
                    bool bUseLooseReturn = ((bDrunken && bCirclingWasOn) || (bLooseStick && bStickWasOn));
                    bool bSafeReturn = false;
                    fNewHeading = (atan2(fHeadBackX - pChSpawn->X, fHeadBackY - pChSpawn->Y) * HEADING_HALF / (float)PI);
                    fNewHeading = SaneHeading(fNewHeading);
                    bUseLooseReturn ? fAdjustHead = fNewHeading : pChSpawn->Heading = fNewHeading;
                    bUseLooseReturn ? bSafeReturn = CanLooseMove(fNewHeading, fHeadBackX, fHeadBackY) : bSafeReturn = true;
                    DoMovement(GO_FORWARDS, bSafeReturn, false);
                }
            }
            else if (bStickWasOn || bCirclingWasOn)
            {
                EndPreviousCmd(false, (bStickWasOn ? USED_STICK : USED_CIRCLE), true);
                bStickWasOn ? bStickOn = true : bCircling = true;
                bStickWasOn = bCirclingWasOn = false;
                return;
            }
        }
        // end return to camp handling
        if (ucCmdUsed == USED_MAKECAMP) return; // nothing below applies makecamp, return turns bMoveTo on which calls with USED_MOVETO
    }

    // calculate distance for circle stuck checking
    if (ucCmdUsed == USED_CIRCLE)
    {
        float fUseCirX = pChSpawn->X - fCircleX;
        float fUseCirY = pChSpawn->Y - fCircleY;
        SpewDebug(DBG_DISABLE, "Setup Circle StuckLogic: Y = %.2f X = %.2f", fUseCirY, fUseCirX);
        fDistance = sqrt(fUseCirX * fUseCirX + fUseCirY * fUseCirY);
        SpewDebug(DBG_DISABLE, "Setup Circle StuckLogic: fDistance = %.2f", fDistance);
        if (fDistance < fCircleRadius * (2.0f / 3.0f)) bUseStuck = false;
    }
    // end circle stuck calculation

    if (ucCmdUsed == USED_MOVETO)
    {
        float fRangeCheck = 0.0f;
        if (bYPrecision)
        {
            fGetMoveToDist = GetDistance(0, pChSpawn->Y, 0, fLocY);
            fRangeCheck = fMoveDistY;
        }
        else if (bXPrecision)
        {
            fGetMoveToDist = GetDistance(pChSpawn->X, 0, fLocX, 0);
            fRangeCheck = fMoveDistX;
        }
        else
        {
            fGetMoveToDist = GetDistance(pChSpawn->X, pChSpawn->Y, fLocX, fLocY);
            fRangeCheck = fMoveDist;

        }

        // check for stucklogic
        if (fGetMoveToDist < fRangeCheck)
        {
            bMoveToOOR = bUseStuck = false;
        }
        else
        {
            bMoveToOOR = true;
        }
    }

    // main stucklogic check
    if (bCheckStuck)
    {
        // use 3D to compare Z so stucklogic doesn't fire if we are moving more z than x/y (up/down slopes)
        // if bJumping then dont check z axis movement
        float fPulseMoved = (sbJumping && bInJump)? GetDistance(pChSpawn->X, pChSpawn->Y, fPrevX, fPrevY) : GetDistance3D(pChSpawn->X, pChSpawn->Y, pChSpawn->Z, fPrevX, fPrevY, fPrevZ);

        // sanity check, if we've moved more than 5 (summon, knockback, user hacks)
        // it will throw off our readjustment, so keep the last value instead
        if (fPulseMoved < 5.0f) fPulseAvg = MovingAvg(fPulseMoved, usiPulseCheck);
        SpewDebug(DBG_STUCK, "fPulseAvg = %.2f, fPulseMoved %.2f", fPulseAvg, fPulseMoved);

        fPrevX = pChSpawn->X;
        fPrevY = pChSpawn->Y;
        fPrevZ = pChSpawn->Z;

        SpewDebug(DBG_DISABLE, "runspeed %.2f and walkspeed %.2f and speedrun %.2f and speedmultiplier %.2f", pChSpawn->RunSpeed, pChSpawn->WalkSpeed, pChSpawn->SpeedRun, pChSpawn->SpeedMultiplier);
        if (bSnared || bRooted)
        {
            // dont use stucklogic if snared
            bUseStuck = false;
            usiStuck = 0;
            usiStuckDec = 0;
        }
        else if (fSpeedMultiplier < 0.25f)
        {
            fSpeedMultiplier = 0.25f; // boost multiplier of those without runspeed
        }

        // if we were stuck last pulse and we moved more than stuckdist since (making progress to get unstuck)
        if (usiStuck && (fPulseAvg > fDistStuck))
        {
            usiStuck--;
            SpewDebug(DBG_STUCK, "Decremented usiStuck to (%d) because we moved (%3.2f)", usiStuck, fPulseAvg);

            //force unstuck after defined increments of not being stuck. we reset usiStuckDec if we get stuck again
            usiStuckDec++;
            if (usiStuckDec > usiPulseUnstuck)
            {
                usiStuck = 0;
                usiStuckDec = 0;
                SpewDebug(DBG_STUCK, "Zeroed usiStuck and usiStuckDec after 5 consecutive decrements");
            }
        }

        if (bStunned || fAdjustHead != 10000.0) bUseStuck = false; // dont analyze if stunned or plugin is adjusting heading
        SpewDebug(DBG_STUCK, "if fPulseAvg(%.2f) < fDistStuck(%.2f) * fSpeedMultiplier(%.2f) && bUse(%s) && Fwd(%s) or Side(%s)", fPulseAvg, fDistStuck, fSpeedMultiplier, bUseStuck ? "true" : "false", bCmdMovedFwd ? "true" : "false", bCmdMovedSide ? "true" : "false");

        if (((bCmdMovedFwd || bCmdMovedSide) && bUseStuck &&
            // if moved forward or strafe (not backwards)
            // bUseStuck is set false if using stucklogic this pulse is not desired

            // main stucklogic formula here
            (fPulseAvg < fDistStuck * fSpeedMultiplier && !pChSpawn->UnderWater && !bMounted) ||

            // maybe handle water and mounts on their own system?
            (pChSpawn->UnderWater && fPulseAvg == 0) ||
            (bMounted && fPulseAvg < (fDistStuck + fSpeedMultiplier) / 3)

            ))
        {
            SpewDebug(DBG_STUCK, "Im Stuck (usiStuck = %d) -- if fpulsemoved (%.2f) < fDistStuck(%.2f) * fSpeedMultiplier(%.2f) then increment", usiStuck, fPulseMoved, fDistStuck, fSpeedMultiplier);

            // 'big if' is true, if we've moved less than user-set dist * current speed multiplier
            if (fPulseMoved < fDistStuck * fSpeedMultiplier) // check if our movement this single pulse is still bad
            {
                usiStuck++;
                usiStuckDec = 0;
                SpewDebug(DBG_STUCK, "incremented usiStuck %d, reset usiStuckDec to zero", usiStuck);

                // bTryToJump user set value in INI
                // try to jump early (but not 1-2pulse misfires) and again after a few seconds/turns
                if (bTryToJump && !sbJumping && !bImFloating && fmod((float)usiStuck, 5.0f) == 0.0f)
                {
                    MQ2Globals::ExecuteCmd(FindMappableCommand("jump"), 1, 0);
                    MQ2Globals::ExecuteCmd(FindMappableCommand("jump"), 0, 0);
                    sbJumping = true;
                }

                // calculate original heading for turnhalf before first turn attempt
                if (usiStuck == 4)
                {
                    if (ucCmdUsed == USED_STICK)
                    {
                        fOrigHead = (atan2(psTarget->X - pChSpawn->X, psTarget->Y - pChSpawn->Y) * HEADING_HALF) / (float)PI;
                    }
                    else if (ucCmdUsed == USED_MOVETO)
                    {
                        fOrigHead = (atan2(fLocX - pChSpawn->X, fLocY - pChSpawn->Y) * HEADING_HALF) / (float)PI;
                    }
                    else if (ucCmdUsed == USED_CIRCLE)
                    {
                        fOrigHead = (atan2(pChSpawn->Y - fCircleY, fCircleX - pChSpawn->X) * CIRCLE_HALF) / (float)PI * CIRCLE_QUARTER;
                        fOrigHead += CIRCLE_QUARTER * (fCircleRadius / fDistance);
                        fOrigHead *= HEADING_MAX / CIRCLE_MAX;
                    }
                    fOrigHead = SaneHeading(fOrigHead);
                    fHalfHead = fOrigHead + HEADING_HALF;
                    fHalfHead = SaneHeading(fHalfHead);
                    SpewDebug(DBG_STUCK, "We've determined that halfway from destination is %.2f", fHalfHead);
                    fCompNegHead = fHalfHead - fabs(fStuckTurn / 2);
                    fCompNegHead = SaneHeading(fCompNegHead);
                    fCompPosHead = fHalfHead + fabs(fStuckTurn / 2);
                    fCompPosHead = SaneHeading(fCompPosHead);
                }

                // if usiStuck == multiple of 4 (try to turn 1 increment, every 4 pulses of being stuck)
                if (fmod((float)usiStuck, 4.0f) == 0.0f)
                {
                    fNewHeading = pChSpawn->Heading + fStuckTurn;
                    fNewHeading = SaneHeading(fNewHeading);
                    SpewDebug(DBG_STUCK, "Stucklogic turned, New heading is %.2f", fNewHeading);

                    // if enabled, check if we are halfway away from our destination, reset heading to original heading and start again
                    if (bTurnHalf)
                    {
                        SpewDebug(DBG_STUCK, "TURNHALF: Comparing desired heading (%.2f) > %.2f and < %.2f", fNewHeading, fCompNegHead, fCompPosHead);
                        if (fNewHeading > fCompNegHead && fNewHeading < fCompPosHead)
                        {
                            fNewHeading = fOrigHead;
                            fStuckTurn *= -1.0f;
                            SpewDebug(DBG_STUCK, "TRUE, so flipped fStuckTurn to other way (%.2f) and reset heading to %.2f", fStuckTurn, fNewHeading);
                        }
                    }
                    //set heading, either by increment or based on half-way logic
                    fAdjustHead = fNewHeading;
                    SpewDebug(DBG_STUCK, "Stucklogic heading change: %.2f", fAdjustHead);
                }
            }
            // end fPulseMoved < fDistStuck * speedmulti
        }
        // end 'big if'
    }
    // end if bCheckStuck (meaning use stucklogic or not)

    // if we are stuck or rooted dont process normal command handling
    if (usiStuck || bRooted)
    {
        SpewDebug(DBG_STUCK, "usiStuck %d bRooted %s fired, returning without trying to move", usiStuck, bRooted ? "true" : "false");
        return;
    }
    // end of main stucklogic checking

    // we arent stuck, or stucklogic is disabled, handle heading if not snaprolling (all commands)
    if (!bStickSnaproll)
    {
        //AdjustWalking(false); // to turn off stuck walking. stick will turn walk back on at the bottom of this function if it needs it
        if (ucCmdUsed == USED_STICK)
        {
            fNewHeading = atan2(psTarget->X - pChSpawn->X, psTarget->Y - pChSpawn->Y) * HEADING_HALF / (float)PI;
        }
        else if (ucCmdUsed == USED_MOVETO)
        {
            fNewHeading = atan2(fLocX - pChSpawn->X, fLocY - pChSpawn->Y) * HEADING_HALF / (float)PI;
        }
        else if (ucCmdUsed == USED_CIRCLE)
        {
            fNewHeading = (bClockwise != bBackwards) ? (atan2(pChSpawn->Y - fCircleY, fCircleX - pChSpawn->X) * CIRCLE_HALF) / (float)PI : (atan2(fCircleY - pChSpawn->Y, pChSpawn->X - fCircleX) * CIRCLE_HALF) / (float)PI;
            bClockwise ?  fNewHeading += CIRCLE_QUARTER + CIRCLE_QUARTER * (fCircleRadius / fDistance) : fNewHeading -= CIRCLE_QUARTER + CIRCLE_QUARTER * (fCircleRadius / fDistance);
            fNewHeading *= HEADING_MAX / CIRCLE_MAX;
        }
        fNewHeading = SaneHeading(fNewHeading);
        ((bLooseStick && bStickOn) || (bLooseMoveTo && bMoveToOn) || (bDrunken && bCircling)) ? fAdjustHead = fNewHeading : pChSpawn->Heading = fNewHeading;
    }

    // handle circle movement and exit, moveto handling follows
    if (ucCmdUsed == USED_CIRCLE)
    {
        bBackwards ? DoMovement(GO_BACKWARDS, true) : DoMovement(GO_FORWARDS, true);
        return;
    }
    // end of all circle processing

    // handle moveto and exit, stick handling follows
    if (ucCmdUsed == USED_MOVETO)
    {
        if (bMoveToOOR)
        {
            bool bSafeToMove = false;
            bool bCloseWalk = false;
            bLooseMoveTo ? bSafeToMove = CanLooseMove(fNewHeading, fLocX, fLocY) : bSafeToMove = true;
            (bWalkMoveTo && fGetMoveToDist < 20.0f) ? bCloseWalk = true : bCloseWalk = false;
            SpewDebug(DBG_DISABLE, "%.2f - %s", fGetMoveToDist, bCloseWalk ? "walking" : "running");
            DoMovement(GO_FORWARDS, bSafeToMove, bCloseWalk);
        }
        else
        {
            // bCampReturning set true if auto return (in which case we dont want to spew msg)
            if (!bCampReturning)
            {
                sprintf(szMsg, "\ay%s\aw:: Arrived at %s", MODULE_NAME, szReturnMsg);
                WriteLine(szMsg, VERB_VERB);
                sprintf(szReturnMsg, "/moveto location");
            }
            EndPreviousCmd(true);
        }
        return;
    }
    // end of all moveto processing

    // everything below this point relates to '/stick' ONLY
    if (ucCmdUsed != USED_STICK) return; // this should be redundant but just in case

    // uw or underwater parameter in stick
    if (bUnderwater)
    {
        double dLookAngle = (double)atan2(psTarget->Z + psTarget->AvatarHeight * StateHeightMultiplier(psTarget->StandState) -
            pChSpawn->Z - pChSpawn->AvatarHeight * StateHeightMultiplier(pChSpawn->StandState), fCurrentDist) * HEADING_HALF / (double)PI;
        bLooseStick ? gLookAngle = dLookAngle : pChSpawn->CameraAngle = (float)dLookAngle;
        SpewDebug(DBG_ALL, "Stick-Underwater: gLookAngle %g CameraAngle %.2f", gLookAngle, pChSpawn->CameraAngle);
    }
    // end underwater handling

    bool bCustomAngle = false;
    if (bStickBehind || bStickBehindOnce || bStickPin || bStickNotFront)
    {
        bCustomAngle = true;
    }

    // loose stick
    if (fCurrentDist > fStickDist && !bStickSnaproll)
    {
        bool bSafeToMove = false;
        bLooseStick ? bSafeToMove = CanLooseMove(fNewHeading, psTarget->X, psTarget->Y) : bSafeToMove = true;
        DoMovement(GO_FORWARDS, bSafeToMove);
        return;
    }

    // if too far away, don't do pin or behind or aggro checking (if awareness on)
    if ((bStickFront || bCustomAngle) && fCurrentDist < fStickDist * 3)
    {
        float fAngDist = CalcAngularDist(psTarget->Heading, pChSpawn->Heading);
        SpewDebug(DBG_OTHER, "fAngDist: %.2f", fAngDist);

        // handling for behind/pin/!front
        if (bCustomAngle)
        {
            // halt strafing if we are on HoTT
            if ((int)pChSpawn->SpawnID == pChSpawn->TargetOfTarget)
            {
                DoMovement(GO_LEFT, false);
                DoMovement(GO_RIGHT, false);
            }
            else if (bNoHoTT)
            {
                // begin delay calculations for nohott (aggroawareness)
                static int iCompareMob = 0; // used for time comparison
                if (bImCheckingAggro)
                {
                    if (!bCompareMobAgain)
                    {
                        iCompareMob = diff_in_ms(stCurr, stNoHoTTCheck);
                        if (iCompareMob > 350)
                        {
                            DoMovement(APPLY_TO_ALL, false);
                            GetSystemTime(&stNoHoTTCheck);
                            bCompareMobAgain = true;
                            SpewDebug(DBG_NOHOTT, "AggroAwareness: its been 200ms, stop moving");
                        }
                    }
                    else
                    {
                        iCompareMob = diff_in_ms(stCurr, stNoHoTTCheck);
                        if (iCompareMob > 5000)
                        {
                            SpewDebug(DBG_NOHOTT, "AggroAwareness: its been another 5500 ms, set everything false and check again");
                            bCompareMobAgain = bImCheckingAggro = false;
                        }
                    }
                    return;
                }
                // end calculations for nohott/aggroawareness
                else if (bImAggro)
                {
                    // a better check here would be to use PrevMobHead and compare if the mob heading is constantly changing
                    // relative to our movement instead of if our degrees fall within a certain range
                    // if aggroawareness is on, check if we are in the front radius when we shouldnt be, and slide sideways to see if mob comes with us
                    bImCheckingAggro = true;
                    GetSystemTime(&stNoHoTTCheck);
                    if (fAngDist < 0 && fAngDist < -190)
                    {
                        DoMovement(GO_LEFT, true);
                    }
                    else
                    {
                        DoMovement(GO_RIGHT, true);
                    }
                    SpewDebug(DBG_NOHOTT, "AggroAwareness has detected aggro");
                    return;
                }
                // end aggroawareness handling
            }
            else
            {
                if (bStickBehind || bStickBehindOnce)
                {
                    if (fabs(fAngDist) > 45.0)
                    {
                        if (fAngDist < 0.0)
                        {
                            SpewDebug(DBG_OTHER, "MoveUtils CustAngle: %.2f - Going Left", fAngDist);
                            DoMovement(GO_LEFT, true); // strafe left
                        }
                        else
                        {
                            SpewDebug(DBG_OTHER, "MoveUtils CustAngle: %.2f - Going Right", fAngDist);
                            DoMovement(GO_RIGHT, true); // strafe right
                        }
                    }
                    else
                    {
                        bStickBehindOnce = false;
                        DoMovement(GO_LEFT, false);
                        DoMovement(GO_RIGHT, false);
                    }
                }
                else if (bStickPin)
                {
                    if ((fAngDist > 0 && fAngDist <= 112) || fAngDist < -144)
                    {
                        SpewDebug(DBG_OTHER, "MoveUtils CustAngle: %.2f - Going Left", fAngDist);
                        DoMovement(GO_LEFT, true);
                    }
                    else if ((fAngDist < 0 && fAngDist > -112) || fAngDist > 144)
                    {
                        SpewDebug(DBG_OTHER, "MoveUtils CustAngle: %.2f - Going Right", fAngDist);
                        DoMovement(GO_RIGHT, true);
                    }
                    else
                    {
                        DoMovement(GO_RIGHT, false);
                        DoMovement(GO_LEFT, false);
                    }
                }
                else if (bStickNotFront)
                {
                    if (fabs(fAngDist) > 135.0)
                    {
                        int iRandT = rand() % 100;
                        if (iRandT > 85)
                        {
                            bStickBehindOnce = true;
                        }
                        else if (fAngDist < 0.0)
                        {
                            DoMovement(GO_LEFT, true); // strafe left
                        }
                        else
                        {
                            DoMovement(GO_RIGHT, true); // strafe right
                        }
                    }
                    else
                    {
                        DoMovement(GO_LEFT, false);
                        DoMovement(GO_RIGHT, false);
                    }
                }
            }
        }
        else if (bStickFront)
        {
            if (bSpinInCircles || (int)pChSpawn->SpawnID == pChSpawn->TargetOfTarget)
            {
                // if im the target of target or deliberately want to spin if lose aggro
                if ((fAngDist < 240 && fAngDist > 0) || (fAngDist > -240.0 && fAngDist < 0))
                {
                    if (fAngDist < 240 && fAngDist > 0)
                    {
                        DoMovement(GO_LEFT, true);
                    }
                    else
                    {
                        DoMovement(GO_RIGHT, true);
                    }
                }
                else
                {
                    DoMovement(GO_LEFT, false);
                    DoMovement(GO_RIGHT, false);
                }
            }
            else
            {
                    DoMovement(GO_LEFT, false);
                    DoMovement(GO_RIGHT, false);
            }
        }
    }
    else
    {
        DoMovement(GO_LEFT, false);
        DoMovement(GO_RIGHT, false);
     }
    //end pin/!front/behind/front

    // ***********SHOULDNT WE DO THIS AS PART OF STUCK LOGIC MAIN IF??***********
    // this may be down here to only unduck if using stick ?
    // other commands unduck on command issue so I don't get why this is here
    // if stucklogic is on and we are ducking, un-duck
    if (bCheckStuck && pChSpawn->StandState == STANDSTATE_DUCK) StandIfNeeded();
    // **************************************************************************

    // handling for stick snaproll
    if (bStickSnaproll)
    {
        PolarSpot(psTarget->X, psTarget->Y, psTarget->Heading, fSnapBearing, 10.0f, 0.0f); // calculate location we want
        float fGetSnapDist = GetDistance(pChSpawn->X, pChSpawn->Y, fSnapX, fSnapY); // get distance from that location
        if (fGetSnapDist <= 5.0f) // if distance is close enough on first pass, no need to snaproll, this also breaks in future pulses
        {
            DoMovement(APPLY_TO_ALL, false, false);
            bStickSnaproll = false;
            bStickOn = true;
            fAdjustHead = 10000.0f;
            fSnapHeading = 0.0f;
            fSnapX = 0.0f;
            fSnapY = 0.0f;
            return;
        }
        // determine heading to that location
        fSnapHeading = (atan2(fSnapX - pChSpawn->X, fSnapY - pChSpawn->Y) * HEADING_HALF) / (float)PI;
        fSnapHeading = SaneHeading(fSnapHeading);
        fAdjustHead = fSnapHeading;
        // start movement
        if (fGetSnapDist > 3.0f)
        {
            bool bSnapCanMove = false;
            bSnapCanMove = CanLooseMove(fSnapHeading, fSnapX, fSnapY);
            DoMovement(GO_FORWARDS, bSnapCanMove);
        }
        return;
    }
    // end snaproll handling


    // believe this was added to correct "overshooting" the target
    if (bCmdMovedFwd && fabs(pChSpawn->Heading - fNewHeading) >= 200 && fCurrentDist < 10)
    {
        DoMovement(GO_BACKWARDS, true);
        return;
    }
    else
    {
        DoMovement(GO_BACKWARDS, false);
    }

    // ******** main movement handling for stick *******
    // if we are outside stick distance
    if (fCurrentDist > fStickDist)
    {
        // if distance is less than 10 and target not fleeing, walk
        float fDistFlee = fabs(CalcAngularDist(pChSpawn->Heading, psTarget->Heading));
        bool bWalkMode = false;
        if (fDistFlee > 64 && fCurrentDist - fStickDist <= 10) 
        {
            bWalkMode = true;
            SpewDebug(DBG_OTHER, "WalkMode Check: fabs(CalcAngularDist(pChSpawn->Heading, psTarget->Heading)(%.2f) > 64 && fCurrentDist (%.2f) - fStickDist (%.2f) <= 10", fDistFlee, fCurrentDist, fStickDist);
        }
        DoMovement(GO_FORWARDS, true, bWalkMode);
        SpewDebug(DBG_DISABLE, "Stick: moving forwards using walk mode");
    }
    else if (bMoveBack && fCurrentDist < fStickDist - 5.0)
    {
        DoMovement(GO_BACKWARDS, true, true);
        SpewDebug(DBG_DISABLE, "Stick: moving backwards");
    }
    else
    {
        // we are in the desired place, turn off all movement until needed
        DoMovement(GO_FORWARDS, false, false);
        DoMovement(GO_BACKWARDS, false, false);
        SpewDebug(DBG_DISABLE, "Stick: turning movement keys off");
    }
    // *************************************************
}
// End Main function
///////////////////////////////////////
//Begin Output functions

void WriteLine(char szOutput[MAX_STRING], unsigned char ucFilterLevel)
{
    if (ucFilterLevel == VERB_ALL)
    {
        if (!bTotalSilence) WriteChatf(szOutput);
    }
    else if (ucFilterLevel == VERB_VERB)
    {
        if (bVerbosity && !bTotalSilence) WriteChatf(szOutput);
    }
    else if (ucFilterLevel == VERB_FULL)
    {
        if (bFullVerbosity && !bTotalSilence) WriteChatf(szOutput);
    }
    else if (ucFilterLevel == VERB_ENFORCE)
    {
        WriteChatf(szOutput);
    }
}

void OutputHelp(unsigned char ucCmdUsed, bool bOnlyCmdHelp)
{
    if (GetGameState() != GAMESTATE_INGAME || bHideHelp) return;

    char szCommand[MAX_STRING] = {0};
    bool bDisplaySettings = false;
    if (ucCmdUsed == USED_MAKECAMP)
    {
        sprintf(szCommand, "/makecamp");
    }
    else if (ucCmdUsed == USED_STICK)
    {
        sprintf(szCommand, "/stick");
    }
    else if (ucCmdUsed == USED_MOVETO)
    {
        sprintf(szCommand, "/moveto");
    }
    else if (ucCmdUsed == USED_CIRCLE)
    {
        sprintf(szCommand, "/circle");
    }
    else if (ucCmdUsed == HELP_SETTINGS)
    {
        bDisplaySettings = true;
    }
    else
    {
        WriteChatf("What happen?"); // this should never process
        return;
    }

    WriteChatf("\ay%s \agv%1.4f", MODULE_NAME, MODULE_VERSION);

    if (bDisplaySettings)
    {
        WriteChatf("\arThe following settings can be changed with any of the 4 commands. There are three options (using /stick as an example).");
        WriteChatf("\ay/stick [set] [\agsetting\ax] [on | off]\ax - This will turn the setting on or off");
        WriteChatf("\ay/stick [set] [\agsetting\ax] [#]\ax - This will change settings that use numerical values");
        WriteChatf("\ay/stick [toggle] [\agsetting\ax]\ax - This will toggle the setting on and off.");
        WriteChatf("\arThe following settings can be set on/off OR toggled:");
        WriteChatf("\ag[autosave]\ax - Automatic saving of configuration file each time you change a setting.");
        WriteChatf("\ag[feign]\ax - Stay-FD support, awaiting you to manually stand before resuming command.");
        WriteChatf("\ag[breakonkb|breakonmouse]\ax - End command from manual keyboard/mouse movement.");
        WriteChatf("\ag[mpause|mousepause]\ax - Pause from manual keyboard/mouse movement. Resumes after pausemindelay/pausemaxdelay values.");
        WriteChatf("\ag[autopause]\ax - Automatic pause&resume command when casting(non-bard), ducking, stunned, targeting self.");
        WriteChatf("\ag[verbosity|fullverbosity]\ax - MQ2ChatWnd command output and detailed ouput.");
        WriteChatf("\ag[hidehelp]\ax - Hides help output from showing automatically on command failures.");
        WriteChatf("\ag[totalsilence]\ax - Hide all MQ2ChatWnd output except critial warnings and requested information.");
        WriteChatf("\ag[stucklogic]\ax - Automatic detection of stuck movement and attempted correction.");
        WriteChatf("\ag[trytojump]\ax - Try to jump as part of stucklogic getting over obstacles.");
        WriteChatf("\ag[turnhalf]\ax - Reset heading and try the other way if halfway from destination in stucklogic.");
        WriteChatf("\ag[nohott|nohottfront]\ax - Aggro-awareness for stick (if gain aggro | if loose aggro).");
        WriteChatf("\ag[savebychar]\ax - Save character-specific settings to [CharName] section of configuration file.");
        WriteChatf("\ag[breakonsummon]\ax - Halt plugin if character moves beyond a certain distance in a single pulse.");
        WriteChatf("\ag[usewalk]\ax - Walk when closing in on moveto / camp return locations for precision.");
        WriteChatf("\arThe following settings use a numerical value:");
        WriteChatf("\ag[pulsecheck] [#]\ax - Set number of frames used to calculate moving average for stucklogic. (default 4)");
        WriteChatf("\ag[pulseunstuck] [#]\ax - Set number of frames successfully moved forward to consider unstuck (default 5).");
        WriteChatf("\ag[diststuck] [#.###]\ax - Set distance needed to move per pulse to not be considered stuck. (default 0.5)");
        WriteChatf("\ag[campmindelay|campmaxdelay|pausemindelay|pausemaxdelay] [#]\ax - Set camp return or mpause/mousepause delays.");
        WriteChatf("\ag[turnrate] [#.##]\ax (\ay12.0\ax to \ay24.0\ax) - Set increment used for loose heading turns.");
        return; // dont re-spam the output below
    }

    if (!bOnlyCmdHelp)
    {
        WriteChatf("\arThe following options work for all commands (\ay/makecamp\ax, \ay/stick\ax, \ay/moveto\ax, \ay/circle\ax).");
        WriteChatf("\ay%s [help]\ax - Displays help output for the command.", szCommand);
        WriteChatf("\ay%s [debug]\ax - Outputs debugging information to file.", szCommand);
        WriteChatf("\ay%s [load|save]\ax - Load settings from or save settings to the configuration file. (MQ2MoveUtils.ini)", szCommand);
        WriteChatf("\ay%s [pause|unpause]\ax - Pauses/Unpauses all aspects of this plugin.", szCommand);
        WriteChatf("\arFor detailed information on plugin settings, use \ay%s [help] [settings]\ax for more information.", szCommand);
        WriteChatf("\arThe remaining options apply to \ay%s\ax only.", szCommand);
    }

    WriteChatf("\ay%s [off]\ax - Ends the command.", szCommand);

    if (ucCmdUsed == USED_STICK)
    {
        WriteChatf("\ay%s\ax - Sticks to current target using default values.", szCommand);
        WriteChatf("\ay%s [id #]\ax - Sticks to a target by its SpawnID or your target if the SpawnID is invalid.", szCommand);
        WriteChatf("\ay%s [<#>]\ax - Sticks to target using the distance you supply, i.e. /stick 10 starts 10 range away.", szCommand);
        WriteChatf("\ay%s [<#%%%%>]\ax - Sticks to given %%%% of max melee range from target.", szCommand);
        WriteChatf("\ay%s [moveback]\ax - Backs you up to current distance value if you get closer to your target.", szCommand);
        WriteChatf("\ay%s [uw/underwater]\ax - Look Up/Down to face your target (underwater or not).", szCommand);
        WriteChatf("\ay%s [loose]\ax - Adjusts your heading legitimately instead of instantly.", szCommand);
        WriteChatf("\ay%s [snaproll]\ax - Moves you behind the target in a straight line, then proceeds with a basic stick.", szCommand);
        WriteChatf("\ay%s [behind]\ax - Sticks you behind your target. \ar*Will spin in circles if you get aggro and no HoTT", szCommand);
        WriteChatf("\ay%s [behindonce]\ax - Sticks you behind target immediately, then proceeds with a basic stick.", szCommand);
        WriteChatf("\ay%s [hold]\ax - Sticks you to current target even if your target changes.", szCommand);
        WriteChatf("\ay%s [!front]\ax - Sticks you anywhere but front melee arc of target.", szCommand);
        WriteChatf("\ay%s [front]\ax - Sticks you to the front melee arc of the target.", szCommand);
        WriteChatf("\ay%s [pin]\ax - Sticks you to the sides of target.", szCommand);
        WriteChatf("\ay%s [toggle] [breakongate|breakonwarp]\ax - Toggle command ending on mob gate/warp.", szCommand);
        WriteChatf("\ay%s [set] [breakdist] [#]\ax - Set distance for breakonwarp to trigger.", szCommand);
        WriteChatf("\ay%s [toggle] [alwaysloose]\ax - Toggle always using loose heading.", szCommand);
        WriteChatf("\ay%s <your_normal_options> [always]\ax - \arMust be at the end\ax. Will continue to stick to all new NPC targets supplied. Turns off hold/id.", szCommand);
    }
    else if (ucCmdUsed == USED_MOVETO)
    {
        WriteChatf("\ay%s [loc Y X|off]\ax - Move to location | stop moving",szCommand);
        WriteChatf("\ay%s ID [SpawnID]\ax - Move to spawnid. If not numeric value then move to current target", szCommand);
        WriteChatf("\ay%s [loose]\ax - Toggles more realistic movement", szCommand);
        WriteChatf("\ay%s [dist <#|-#>]\ax - Furthest distance to be considered 'at location', negative subtracts from value", szCommand);
        WriteChatf("\ay%s [set] [ydist|xdist] [#.##]\ax - Set distance used for precisey/precisex distance checking.", szCommand);
        WriteChatf("\ay%s [toggle] [alwaysloose]\ax - Toggle always using loose heading.", szCommand);
    }
    else if (ucCmdUsed == USED_MAKECAMP)
    {
        WriteChatf("\ay%s [on|off]\ax - Drops anchor at current location | removes anchor", szCommand);
        WriteChatf("\ay%s [<y> <x>]\ax - Makes camp at supplied anchor location", szCommand);
        WriteChatf("\ay%s [leash]\ax - Toggles leashing to current camp", szCommand);
        WriteChatf("\ay%s [leash [<dist>]]\ax - Set distance to exceed camp before leashing, always turns leash on", szCommand);
        WriteChatf("\ay%s [radius <dist>]\ax - Sets radius/size of camp", szCommand);
        WriteChatf("\ay%s [maxdelay <#>|mindelay <#>]\ax - Sets the max/minimum amount of time before returning to camp.", szCommand);
        WriteChatf("\ay%s [return]\ax - Immediately return to camp", szCommand);
        WriteChatf("\ay%s [altreturn]\ax - Immediately return to alternate camp (if established)", szCommand);
        WriteChatf("\ay%s [player [<name>]]\ax - Set dynamic camp around player name in zone, or current target", szCommand);
        WriteChatf("\ay%s [set] [leashlength] [#.##]\ax - Set leash length size.", szCommand);
        WriteChatf("\ay%s [set] [returnnoaggro] [on|off]\ax - Return to camp if not aggro (regardless of target).", szCommand);
        WriteChatf("\ay%s [set] [returnnotlooting] [on|off]\ax - Do not return to camp if looting.", szCommand);
        WriteChatf("\ay%s [set] [returnnotarget] [on|off]\ax - Return to camp if no target.", szCommand);
        WriteChatf("\ay%s [set] [campradius] [#]\ax - Change radius of existing camp.", szCommand);
        WriteChatf("\ay%s [set] [scatdist] [#.##]\ax - Set scatter distance from center.", szCommand);
        WriteChatf("\ay%s [set] [scatsize] [#.##]\ax - Set scatter radius size.", szCommand);
        WriteChatf("\ay%s [set] [bearing] [#.##]\ax - Set scatter bearing from center.", szCommand);
        WriteChatf("\ay%s [toggle] [usescatter]\ax - Use user-defined scatter values for camp returns.", szCommand);
    }
    else if (ucCmdUsed == USED_CIRCLE)
    {
        WriteChatf("\ay%s [on <radius>]\ax - Turn circle on at current loc. If radius supplied sets size of circle.", szCommand);
        WriteChatf("\ay%s [<y> <x>]\ax - Turn on circle at given anchor location. (Y X are the order /loc prints them).", szCommand);
        WriteChatf("\ay%s [radius <#>]\ax - Change the circle radius without resetting circle.", szCommand);
        WriteChatf("\ay%s [drunken]\ax - Toggles random turns.", szCommand);
        WriteChatf("\ay%s [clockwise|cw]\ax - Toggles circling clockwise.", szCommand);
        WriteChatf("\ay%s [counterclockwise|ccw|reverse]\ax - Toggles circling counter-clockwise.", szCommand);
        WriteChatf("\ay%s [forwards|backwards]\ax - Set movement direction forwards or backwards.", szCommand);
        WriteChatf("\ay%s [toggle] [alwaysccw]\ax - Always use counter-clockwise (default is clockwise).", szCommand);
        WriteChatf("\ay%s [toggle] [alwaysbackwards]\ax - Always use backwards (default is forwards).", szCommand);
        WriteChatf("\ay%s [toggle] [alwaysdrunk]\ax - Always use drunken movement.", szCommand);
        WriteChatf("\ay%s [set] [circleradius] [#]\ax - Change the circle radius without resetting circle.", szCommand);
    }
}

void DebugToWnd(unsigned char ucCmdUsed)
{
    char szTemp[MAX_STRING] = {0};
    char szTempID[MAX_STRING] = "NULL";
    char szYes[8] = "\agyes\ax";
    char szNo[7] = "\arno\ax";
    char szOn[7] = "\agon\ax";
    char szOff[8] = "\aroff\ax";
    char szDir[25] = "\agNormal\ax";
    char szLongLine[48] = "\ay---------------------------------------------";

    sprintf(szTemp, "\ay%s v%1.4f - Current Status", MODULE_NAME, MODULE_VERSION);
    WriteLine(szTemp, VERB_ENFORCE);
    if (ucCmdUsed == USED_STICK || ucCmdUsed == APPLY_TO_ALL)
    {
        if (bStickBehind)
        {
            sprintf(szDir, "\agBehind\ax");
        }
        else if (bStickPin) 
        {
            sprintf(szDir, "\agSide\ax");
        }
        else if (bStickNotFront) 
        {
            sprintf(szDir, "\agNot Front\ax");
        }
        else if (bStickFront)
        {
            sprintf(szDir, "\agFront\ax");
        }
        WriteLine(szLongLine, VERB_ENFORCE);
        sprintf(szTemp, "\ayStick\ax: Status(%s) Dir(%s) Dist(\ag%.2f\ax) Mod(\ag%.2f\ax) Mod%%%%(\ag%.2f%%%%\ax) Loose(%s) Water(%s) MoveBack(%s) Hold(%s) Always(%s)", bStickOn ? szOn : szOff, szDir, fStickDist, fStickDistMod, fStickDistModP, bLooseStick ? szYes : szNo, bUnderwater ? szYes : szNo, bMoveBack ? szYes : szNo, bStickHold ? szYes : szNo, bStickNewMob ? szOn : szOff);
        WriteLine(szTemp, VERB_ENFORCE);
        if (bStickHold)
        {
            PSPAWNINFO pStickThis = (PSPAWNINFO)GetSpawnByID(ulStickID);
            if (pStickThis)
            {
                sprintf(szTempID, "%s", pStickThis->DisplayedName);
            }
            sprintf(szTemp, "\ayStick\ax: Holding to ID(\ag%d\ax) Name(\ag%s\ax)", ulStickID, szTempID);
            WriteLine(szTemp, VERB_ENFORCE);
        }
        sprintf(szTemp, "\ayStick Options\ax: AlwaysLoose(%s) BreakOnWarp(%s) BreakDist(\ag%.2f\ax) BreakOnGate(%s) ", bAlwaysLooseStick ? szYes : szNo, bBreakOnWarp ? szOn : szOff, fBreakDist, bBreakOnGate ? szOn : szOff);
        WriteLine(szTemp, VERB_ENFORCE);
    }
    if (ucCmdUsed == USED_MOVETO || ucCmdUsed == APPLY_TO_ALL)
    {
        WriteLine(szLongLine, VERB_ENFORCE);
        sprintf(szTemp, "\ayMoveto\ax: Status(%s) Y(\ag%.2f\ax) X(\ay%.2f\ax) Dist(\ag%.2f\ax) Mod(\ag%.2f\ax) Loose(%s) YPrecise(%s) XPrecise(%s)", bMoveToOn ? szOn : szOff, fLocY, fLocX, fMoveDist, fMoveDistMod, bLooseMoveTo ? szYes : szNo, bYPrecision ? szYes : szNo, bXPrecision ? szYes : szNo);
        WriteLine(szTemp, VERB_ENFORCE);
        sprintf(szTemp, "\ayMoveto Options\ax: AlwaysLoose(%s) Y-Dist(\ag%.2f\ax) X-Dist(\ag%.2f\ax)", bAlwaysLooseMoveTo ? szYes : szNo, fMoveDistY, fMoveDistX);
        WriteLine(szTemp, VERB_ENFORCE);
    }
    if (ucCmdUsed == USED_CIRCLE || ucCmdUsed == APPLY_TO_ALL)
    {
        WriteLine(szLongLine, VERB_ENFORCE);
        sprintf(szTemp, "\ayCircle\ax: Status(%s) Y(\ag%.2f\ax) X(\ag%.2f\ax) Radius(\ag%.2f\ax) Drunken(%s) Backwards(%s) Clockwise(%s)", bCircling ? szOn : szOff, fCircleY, fCircleX, fCircleRadius, bDrunken ? szYes : szNo, bBackwards ? szYes : szNo, bClockwise ? szYes : szNo);
        WriteLine(szTemp, VERB_ENFORCE);
        sprintf(szTemp, "\ayCircle Options\ax: AlwaysDrunk(%s) AlwaysBackwards(%s) AlwaysCCW(%s)", bAlwaysDrunk ? szYes : szNo, bAlwaysBack ? szYes : szNo, bAlwaysCCW ? szYes : szNo);
        WriteLine(szTemp, VERB_ENFORCE);
    }
    if (ucCmdUsed == USED_MAKECAMP || ucCmdUsed == APPLY_TO_ALL)
    {
        WriteLine(szLongLine, VERB_ENFORCE);
        sprintf(szTemp, "\ayMakeCamp\ax: Status(%s) Player(%s) Y(\ag%.2f\ax) X(\ag%.2f\ax) Radius(\ag%.2f\ax) Leash(%s) LeashLen(\ag%.2f\ax) Returning(%s) ", bMakeCamp ? szOn : szOff, bCampPlayer ? szYes : szNo, fAnchorY, fAnchorX, fCampRadius, bLeash ? szOn : szOff, fLeashLength, bCampReturning ? szYes : szNo);
        WriteLine(szTemp, VERB_ENFORCE);
        sprintf(szTemp, "\ayMakeCamp\ax: Scatter(%s) Bearing(\ag%.2f\ax) ScatDist(\ag%.2f\ax) ScatSize(\ag%.2f\ax)", bScatter ? szOn : szOff, fBearing, fScatDist, fScatter);
        WriteLine(szTemp, VERB_ENFORCE);
        if (bCampPlayer)
        {
            PSPAWNINFO pCampThis = (PSPAWNINFO)GetSpawnByID(ulCampPlayerID);
            if (pCampThis)
            {
                sprintf(szTempID, "%s", pCampThis->DisplayedName);
            }
            sprintf(szTemp, "\ayMakeCamp\ax: Player Name(\ag%s\ax) ID(\ag%d\ax)", szTempID, ulCampPlayerID);
            WriteLine(szTemp, VERB_ENFORCE);
        }
        if (bAltCamp)
        {
            sprintf(szTemp, "\ayMakeCamp\ax: AlternateCamp(\agon\ax) AltAnchorY(\ag%.2f\ax) AltAnchorX(\ag%.2f\ax) AltRadius(\ag%.2f\ax)", fAltAnchorY, fAltAnchorX, fAltCampRadius);
            WriteLine(szTemp, VERB_ENFORCE);
        }
        sprintf(szTemp, "\ayMakeCamp Options\ax: ReturnNoAggro(%s) MinDelay(\ag%d\ax) MaxDelay(\ag%d\ax)", bReturnNoAggro ? szOn : szOff, iMinCampDelay, iMaxCampDelay);
        WriteLine(szTemp, VERB_ENFORCE);
    }

    if (ucCmdUsed == APPLY_TO_ALL)
    {
        WriteLine(szLongLine, VERB_ENFORCE);
        sprintf(szTemp, "\ayPlugin Status\ax: Paused(%s) MinPause(\ag%d\ax) MaxPause(\ag%d\ax)", bAllPaused ? szYes : szNo, iMinPauseDelay, iMaxPauseDelay);
        WriteLine(szTemp, VERB_ENFORCE);
        sprintf(szTemp, "\ayStuckLogic\ax: Enabled(%s) DistStuck(\ag%.3f\ax) PulseCheck(\ag%d\ax) PulseUnstuck(\ag%d\ax)", bCheckStuck ? szYes : szNo, fDistStuck, usiPulseCheck, usiPulseUnstuck);
        WriteLine(szTemp, VERB_ENFORCE);
        sprintf(szTemp, "\ayPause Options\ax: AutoPause(%s) MPause(%s) BreakOnKB(%s) MousePause(%s) BreakOnMouse(%s)", bAutoPause ? szOn : szOff, bPauseOnMove ? szOn : szOff, bPauseOnMouse ? szOn : szOff, bBreakOnKB ? szOn : szOff, bBreakOnMouse ? szOn : szOff);
        WriteLine(szTemp, VERB_ENFORCE);
        sprintf(szTemp, "\ayOutput Options\ax: Silent(%s) Verbosity(%s) FullVerbosity(%s) HideHelp(%s)", bTotalSilence ? szYes : szNo, bVerbosity ? szYes : szNo, bFullVerbosity ? szYes : szNo, bHideHelp ? szOn : szOff);
        WriteLine(szTemp, VERB_ENFORCE);
        sprintf(szTemp, "\ayPlugin Options\ax: AutoSave(%s) FeignSupport(%s) BreakOnSummon(%s) BreakSummonDist(\ag%.2f\ax) AwareAggro(%s) AwareNotAggro(%s)", bAutoSave ? szOn : szOff, bFeignSupport ? szOn : szOff, bBreakOnSummon ? szOn : szOff, fBreakSummonDist, bNoHoTT ? szOn : szOff, bSpinInCircles ? szOn : szOff);
        WriteLine(szTemp, VERB_ENFORCE);
    }
}

void DebugToINI(unsigned char ucCmdUsed)
{
    char szTemp[MAX_STRING] = {0};
    char szCommand[MAX_STRING] = {0};
    if (ucCmdUsed == USED_MAKECAMP)
    {
        sprintf(szCommand, "/makecamp");
    }
    else if (ucCmdUsed == USED_STICK)
    {
        sprintf(szCommand, "/stick");
    }
    else if (ucCmdUsed == USED_MOVETO)
    {
        sprintf(szCommand, "/moveto");
    }
    else if (ucCmdUsed == USED_CIRCLE)
    {
        sprintf(szCommand, "/circle");
    }

    sprintf(szTemp, "%s v%1.4f", MODULE_NAME, MODULE_VERSION);
    WritePrivateProfileString("Version", "Number", szTemp, szDebugName);
    WritePrivateProfileString("Commands", "CommandUsed", szCommand, szDebugName);
    WritePrivateProfileString("GenericBOOL", "bMoveBindsLoaded", bMoveBindsLoaded ? "true" : "false", szDebugName);
    WritePrivateProfileString("GenericBOOL", "bAllPaused", bAllPaused ? "true" : "false", szDebugName);
    WritePrivateProfileString("GenericBOOL", "bBrokeOnSummon", bBrokeOnSummon ? "true" : "false", szDebugName);
    WritePrivateProfileString("Strings", "szMsg", szMsg, szDebugName);
    WritePrivateProfileString("Strings", "szReturnMsg", szReturnMsg, szDebugName);
    WritePrivateProfileString("AggroChecking", "bNoHoTT", bNoHoTT ? "true" : "false", szDebugName);
    WritePrivateProfileString("AggroChecking", "bSpinInCircles", bSpinInCircles ? "true" : "false", szDebugName);
    WritePrivateProfileString("AggroChecking", "bImAggro", bImAggro ? "true" : "false", szDebugName);
    WritePrivateProfileString("AggroChecking", "bCompareMobAgain", bCompareMobAgain ? "true" : "false", szDebugName);
    WritePrivateProfileString("AggroChecking", "bImCheckingAggro", bImCheckingAggro ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bAutoPause", bAutoPause ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bPauseOnMove", bPauseOnMove ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bPauseOnMouse", bPauseOnMouse ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bBreakOnKB", bBreakOnKB ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bBreakOnMouse", bBreakOnMouse ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bCheckStuck", bCheckStuck ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bVerbosity", bVerbosity ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bFullVerbosity", bFullVerbosity ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bTotalSilence", bTotalSilence ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bAutoSave", bAutoSave ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bFeignSupport", bFeignSupport ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bSaveByChar", bSaveByChar ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bHideHelp", bHideHelp ? "true" : "false", szDebugName);
    WritePrivateProfileString("INISettings", "bBreakOnSummon", bBreakOnSummon ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fBreakSummonDist);
    WritePrivateProfileString("INISettings", "fBreakSummonDist", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fTurnRate);
    WritePrivateProfileString("INISettings", "fTurnRate", szTemp, szDebugName);
    WritePrivateProfileString("INISettings", "iMinPauseDelay", itoa(iMinPauseDelay, szTemp, 10), szDebugName);
    WritePrivateProfileString("INISettings", "iMaxPauseDelay", itoa(iMaxPauseDelay, szTemp, 10), szDebugName);
    WritePrivateProfileString("INISettings", "iMinCampDelay", itoa(iMinCampDelay, szTemp, 10), szDebugName);
    WritePrivateProfileString("INISettings", "iMaxCampDelay", itoa(iMaxCampDelay, szTemp, 10), szDebugName);
    sprintf(szTemp, "%.2f", fAdjustHead);
    WritePrivateProfileString("SetOnPulse", "fAdjustHead", szTemp, szDebugName);
    WritePrivateProfileString("SetOnPulse", "bManuallyMoving", bManuallyMoving ? "true" : "false", szDebugName);
    WritePrivateProfileString("SetOnPulse", "bReturnInProcess", bReturnInProcess ? "true" : "false", szDebugName);
    WritePrivateProfileString("SetOnPulse", "bCampReturning", bCampReturning ? "true" : "false", szDebugName);
    WritePrivateProfileString("SetOnPulse", "bStickNewTarget", bStickNewTarget ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fGetMoveToDist);
    WritePrivateProfileString("SetOnPulse", "fGetMoveToDist", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fCurrentDist);
    WritePrivateProfileString("SetOnPulse", "fCurrentDist", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fDistFromStick);
    WritePrivateProfileString("SetOnPulse", "fDistFromStick", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fNewCompare);
    WritePrivateProfileString("SetOnPulse", "fNewCompare", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fMyCurY);
    WritePrivateProfileString("SetOnPulse", "fMyCurY", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fMyCurX);
    WritePrivateProfileString("SetOnPulse", "fMyCurX", szTemp, szDebugName);
    WritePrivateProfileString("SetOnPulse", "iCampReturnTime", itoa(iCampReturnTime, szTemp, 10), szDebugName);
    WritePrivateProfileString("SetOnPulse", "iPauseReturnTime", itoa(iPauseReturnTime, szTemp, 10), szDebugName);
    WritePrivateProfileString("Stick", "bBreakOnGate", bBreakOnGate ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bBreakOnWarp", bBreakOnWarp ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fBreakDist);
    WritePrivateProfileString("Stick", "fBreakDist", szTemp, szDebugName);
    WritePrivateProfileString("Stick", "bStickOn", bStickOn ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bSetDist", bSetDist ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fStickDist);
    WritePrivateProfileString("Stick", "fStickDist", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fStickDistMod);
    WritePrivateProfileString("Stick", "fStickDistMod", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fStickDistModP);
    WritePrivateProfileString("Stick", "fStickDistModP", szTemp, szDebugName);
    WritePrivateProfileString("Stick", "bLooseStick", bLooseStick ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bAlwaysLooseStick", bAlwaysLooseStick ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bMoveBack", bMoveBack ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bStickBehind", bStickBehind ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bStickBehindOnce", bStickBehindOnce ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bStickFront", bStickFront ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bStickNotFront", bStickNotFront ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bStickPin", bStickPin ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bStickSnaproll", bStickSnaproll ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bStickHold", bStickHold ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "ulStickID", itoa((int)ulStickID, szTemp, 10), szDebugName);
    WritePrivateProfileString("Stick", "bUnderwater", bUnderwater ? "true" : "false", szDebugName);
    WritePrivateProfileString("Stick", "bStickNewMob", bStickNewMob ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fSnapHeading);
    WritePrivateProfileString("StickSnaproll", "fSnapHeading", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fSnapY);
    WritePrivateProfileString("StickSnaproll", "fSnapY", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fSnapX);
    WritePrivateProfileString("StickSnaproll", "fSnapX", szTemp, szDebugName);
    WritePrivateProfileString("MakeCamp", "bMakeCamp", bMakeCamp ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fAnchorY);
    WritePrivateProfileString("MakeCamp", "fAnchorY", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fAnchorX);
    WritePrivateProfileString("MakeCamp", "fAnchorX", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fCampRadius);
    WritePrivateProfileString("MakeCamp", "fCampRadius", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fCampDist);
    WritePrivateProfileString("MakeCamp", "fCampDist", szTemp, szDebugName);
    WritePrivateProfileString("MakeCamp", "bAltCamp", bAltCamp ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fAltAnchorY);
    WritePrivateProfileString("MakeCamp", "fAltAnchorY", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fAltAnchorX);
    WritePrivateProfileString("MakeCamp", "fAltAnchorX", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fAltCampRadius);
    WritePrivateProfileString("MakeCamp", "fAltCampRadius", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fAltCampDist);
    WritePrivateProfileString("MakeCamp", "fAltCampDist", szTemp, szDebugName);
    WritePrivateProfileString("MakeCamp", "bCampPlayer", bCampPlayer ? "true" : "false", szDebugName);
    WritePrivateProfileString("MakeCamp", "ulCampPlayerID", itoa((int)ulCampPlayerID, szTemp, 10), szDebugName);
    WritePrivateProfileString("MakeCamp", "bMakeCampReturn", bMakeCampReturn ? "true" : "false", szDebugName);
    WritePrivateProfileString("MakeCamp", "bAltCampReturn", bAltCampReturn ? "true" : "false", szDebugName);
    WritePrivateProfileString("MakeCamp", "bLeash", bLeash ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fLeashLength);
    WritePrivateProfileString("MakeCamp", "fLeashLength", szTemp, szDebugName);
    WritePrivateProfileString("MakeCamp", "bScatter", bScatter ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fBearing);
    WritePrivateProfileString("MakeCamp", "fBearing", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fScatter);
    WritePrivateProfileString("MakeCamp", "fScatter", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fScatDist);
    WritePrivateProfileString("MakeCamp", "fScatDist", szTemp, szDebugName);
    WritePrivateProfileString("MakeCamp", "bReturnNoAggro", bReturnNoAggro ? "true" : "false", szDebugName);
    WritePrivateProfileString("MakeCamp", "bReturnNoTarget", bReturnNoTarget ? "true" : "false", szDebugName);
    WritePrivateProfileString("MakeCamp", "bReturnNotLooting", bReturnNotLooting ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fGotoY);
    WritePrivateProfileString("MakeCamp", "fGotoY", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fGotoX);
    WritePrivateProfileString("MakeCamp", "fGotoX", szTemp, szDebugName);
    WritePrivateProfileString("MoveTo", "bMoveToOn", bMoveToOn ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fLocY);
    WritePrivateProfileString("MoveTo", "fLocY", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fLocX);
    WritePrivateProfileString("MoveTo", "fLocX", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fMoveDist);
    WritePrivateProfileString("MoveTo", "fMoveDist", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fMoveDistINI);
    WritePrivateProfileString("MoveTo", "fMoveDistINI", szTemp, szDebugName);
    WritePrivateProfileString("MoveTo", "bYPrecision", bYPrecision ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fMoveDistY);
    WritePrivateProfileString("MoveTo", "fMoveDistY", szTemp, szDebugName);
    WritePrivateProfileString("MoveTo", "bXPrecision", bXPrecision ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fMoveDistX);
    WritePrivateProfileString("MoveTo", "fMoveDistX", szTemp, szDebugName);
    WritePrivateProfileString("MoveTo", "bWalkMoveTo", bWalkMoveTo ? "true" : "false", szDebugName);
    WritePrivateProfileString("MoveTo", "bLooseMoveTo", bLooseMoveTo ? "true" : "false", szDebugName);
    WritePrivateProfileString("MoveTo", "bAlwaysLooseMoveTo", bAlwaysLooseMoveTo ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fMoveDistMod);
    WritePrivateProfileString("MoveTo", "fMoveDistMod", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fMoveDistModINI);
    WritePrivateProfileString("MoveTo", "fMoveDistModINI", szTemp, szDebugName);
    WritePrivateProfileString("Circle", "bCircling", bCircling ? "true" : "false", szDebugName);
    sprintf(szTemp, "%.2f", fCircleY);
    WritePrivateProfileString("Circle", "fCircleY", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fCircleX);
    WritePrivateProfileString("Circle", "fCircleX", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fCircleRadius);
    WritePrivateProfileString("Circle", "fCircleRadius", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fCircleRadiusMod);
    WritePrivateProfileString("Circle", "fCircleRadiusMod", szTemp, szDebugName);
    WritePrivateProfileString("Circle", "bBackwards", bBackwards ? "true" : "false", szDebugName);
    WritePrivateProfileString("Circle", "bClockwise", bClockwise ? "true" : "false", szDebugName);
    WritePrivateProfileString("Circle", "bDrunken", bDrunken ? "true" : "false", szDebugName);
    WritePrivateProfileString("Circle", "bAlwaysDrunk", bAlwaysDrunk ? "true" : "false", szDebugName);
    WritePrivateProfileString("Circle", "bAlwaysBack", bAlwaysBack ? "true" : "false", szDebugName);
    WritePrivateProfileString("Circle", "bAlwaysCCW", bAlwaysCCW ? "true" : "false", szDebugName);
    WritePrivateProfileString("StuckLogic", "bStickWasOn", bStickWasOn ? "true" : "false", szDebugName);
    WritePrivateProfileString("StuckLogic", "bCirclingWasOn", bCirclingWasOn ? "true" : "false", szDebugName);
    WritePrivateProfileString("StuckLogic", "bCmdMovedFwd", bCmdMovedFwd ? "true" : "false", szDebugName);
    WritePrivateProfileString("StuckLogic", "bTryToJump", bTryToJump ? "true" : "false", szDebugName);
    WritePrivateProfileString("StuckLogic", "bTurnHalf", bTurnHalf ? "true" : "false", szDebugName);
    WritePrivateProfileString("StuckLogic", "usiPulseCheck", itoa((int)usiPulseCheck, szTemp, 10), szDebugName);
    WritePrivateProfileString("StuckLogic", "usiPulseUnstuck", itoa((int)usiPulseUnstuck, szTemp, 10), szDebugName);
    WritePrivateProfileString("StuckLogic", "usiStuck", itoa((int)usiStuck, szTemp, 10), szDebugName);
    WritePrivateProfileString("StuckLogic", "usiStuckDec", itoa((int)usiStuckDec, szTemp, 10), szDebugName);
    sprintf(szTemp, "%.2f", fDistStuck);
    WritePrivateProfileString("StuckLogic", "fDistStuck", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fPulseAvg);
    WritePrivateProfileString("StuckLogic", "fPulseAvg", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fStuckTurn);
    WritePrivateProfileString("StuckLogic", "fStuckTurn", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fOrigHead);
    WritePrivateProfileString("StuckLogic", "fOrigHead", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fPrevY);
    WritePrivateProfileString("StuckLogic", "fPrevY", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fPrevX);
    WritePrivateProfileString("StuckLogic", "fPrevX", szTemp, szDebugName);
    sprintf(szTemp, "%.2f", fPrevZ);
    WritePrivateProfileString("StuckLogic", "fPrevZ", szTemp, szDebugName);
}

void Save_INI(void)
{
    char szTemp[MAX_STRING] = {0};
    char szCharName[MAX_STRING] = {0};
    sprintf(szCharName, "%s.%s", EQADDR_SERVERNAME, ((PCHARINFO)pCharData)->Name);

    // default settings
    WritePrivateProfileString("Defaults", "AutoPause", bAutoPause ? "on" : "off", INIFileName);
    WritePrivateProfileString("Defaults", "AutoSave", bAutoSave ? "on" : "off", INIFileName);
    WritePrivateProfileString("Defaults", "BreakKeyboard", bBreakOnKB ? "on" : "off", INIFileName);
    WritePrivateProfileString("Defaults", "BreakMouse", bBreakOnMouse ? "on" : "off", INIFileName);
    WritePrivateProfileString("Defaults", "BreakOnSummon", bBreakOnSummon ? "on" : "off", INIFileName);
    sprintf(szTemp, "%.2f", fBreakSummonDist);
    WritePrivateProfileString("Defaults", "BreakSummonDist", szTemp, INIFileName);
    WritePrivateProfileString("Defaults", "FeignSupport", bFeignSupport ? "on" : "off", INIFileName);
    WritePrivateProfileString("Defaults", "HideHelp", bHideHelp ? "on" : "off", INIFileName);
    WritePrivateProfileString("Defaults", "KeyboardPause", bPauseOnMove ? "on" : "off", INIFileName);
    WritePrivateProfileString("Defaults", "MousePause", bPauseOnMouse ? "on" : "off", INIFileName);
    WritePrivateProfileString("Defaults", "PauseMinDelay", itoa(iMinPauseDelay, szTemp, 10), INIFileName);
    WritePrivateProfileString("Defaults", "PauseMaxDelay", itoa(iMaxPauseDelay, szTemp, 10), INIFileName);
    WritePrivateProfileString("Defaults", "SaveByChar", bSaveByChar ? "on" : "off", INIFileName);
    sprintf(szTemp, "%.2f", fTurnRate);
    WritePrivateProfileString("Defaults", "TurnRate", szTemp, INIFileName);
    WritePrivateProfileString("Defaults", "Verbosity", bVerbosity ? "on" : "off", INIFileName);
    // FullVerbosity is more frequent, detailed output, and differs from Verbosity
    // Setting one does not include the text of the other.
    WritePrivateProfileString("Defaults", "FullVerbosity", bFullVerbosity ? "on" : "off", INIFileName);
    // Total Silence writes no output except critical or user-requested
    WritePrivateProfileString("Defaults", "TotalSilence", bTotalSilence ? "on" : "off", INIFileName);

    // stick settings
    WritePrivateProfileString("Stick", "AwareAggro", bNoHoTT ? "on" : "off", INIFileName);
    WritePrivateProfileString("Stick", "AwareNotAggro", bSpinInCircles ? "on" : "off", INIFileName);
    sprintf(szTemp, "%.2f", fBreakDist);
    WritePrivateProfileString("Stick", "BreakDist", szTemp, INIFileName);
    WritePrivateProfileString("Stick", "BreakOnGate", bBreakOnGate ? "on" : "off", INIFileName);
    WritePrivateProfileString("Stick", "BreakOnWarp", bBreakOnWarp ? "on" : "off", INIFileName);
    WritePrivateProfileString("Stick", "LooseStick", bAlwaysLooseStick ? "on" : "off", INIFileName);
    sprintf(szTemp, "%.2f", fStickDistMod);
    WritePrivateProfileString("Stick", "StickDistMod", szTemp, INIFileName);
    sprintf(szTemp, "%.2f", fStickDistModP);
    WritePrivateProfileString("Stick", "StickDistModPercent", szTemp, INIFileName);

    // makecamp settings
    sprintf(szTemp, "%.2f", fCampRadius);
    WritePrivateProfileString("MakeCamp", "CampRadius", szTemp, INIFileName);
    WritePrivateProfileString("MakeCamp", "MinDelay", itoa(iMinCampDelay, szTemp, 10), INIFileName);
    WritePrivateProfileString("MakeCamp", "MaxDelay", itoa(iMaxCampDelay, szTemp, 10), INIFileName);
    WritePrivateProfileString("MakeCamp", "ReturnNoAggro", bReturnNoAggro ? "on" : "off", INIFileName);
    WritePrivateProfileString("MakeCamp", "ReturnNoTarget", bReturnNoTarget ? "on" : "off", INIFileName);
    WritePrivateProfileString("MakeCamp", "ReturnNotLooting", bReturnNotLooting ? "on" : "off", INIFileName);
    WritePrivateProfileString("MakeCamp", "UseLeash", bLeash ? "on" : "off", INIFileName);
    sprintf(szTemp, "%.2f", fLeashLength);
    WritePrivateProfileString("MakeCamp", "LeashLength", szTemp, INIFileName);
    // scatter values
    WritePrivateProfileString("MakeCamp", "UseScatter", bScatter ? "on" : "off", INIFileName);
    sprintf(szTemp, "%.2f", fBearing);
    WritePrivateProfileString("MakeCamp", "Bearing", szTemp, INIFileName);
    sprintf(szTemp, "%.2f", fScatDist);
    WritePrivateProfileString("MakeCamp", "ScatDist", szTemp, INIFileName);
    sprintf(szTemp, "%.2f", fScatter);
    WritePrivateProfileString("MakeCamp", "ScatSize", szTemp, INIFileName);

    // moveto settings
    sprintf(szTemp, "%.2f", fMoveDistINI);
    WritePrivateProfileString("MoveTo", "ArrivalDist", szTemp, INIFileName);
    sprintf(szTemp, "%.2f", fMoveDistX);
    WritePrivateProfileString("MoveTo", "ArrivalDistX", szTemp, INIFileName);
    sprintf(szTemp, "%.2f", fMoveDistY);
    WritePrivateProfileString("MoveTo", "ArrivalDistY", szTemp, INIFileName);
    WritePrivateProfileString("MoveTo", "LooseMoveTo", bAlwaysLooseMoveTo ? "on" : "off", INIFileName);
    sprintf(szTemp, "%.2f", fMoveDistMod);
    WritePrivateProfileString("MoveTo", "MoveToMod", szTemp, INIFileName);
    WritePrivateProfileString("MoveTo", "UseWalk", bWalkMoveTo ? "on" : "off", INIFileName);

    // circle settings
    WritePrivateProfileString("Circle", "AlwaysBackwards",  bAlwaysBack ? "on" : "off", INIFileName);
    WritePrivateProfileString("Circle", "AlwaysCounterClockwise", bAlwaysCCW ? "on" : "off", INIFileName);
    WritePrivateProfileString("Circle", "AlwaysDrunken", bAlwaysDrunk ? "on" : "off", INIFileName);
    sprintf(szTemp, "%.2f", fCircleRadius);
    WritePrivateProfileString("Circle", "RadiusSize", szTemp, INIFileName);

    // stuck logic related
    WritePrivateProfileString("StuckLogic", "StuckLogic", bCheckStuck ? "on" : "off", INIFileName);
    sprintf(szTemp, "%.2f", fDistStuck);
    WritePrivateProfileString("StuckLogic", "DistStuck", szTemp, INIFileName);
    WritePrivateProfileString("StuckLogic", "PulseCheck", itoa((int)usiPulseCheck, szTemp, 10), INIFileName);
    WritePrivateProfileString("StuckLogic", "PulseUnstuck", itoa((int)usiPulseUnstuck, szTemp, 10), INIFileName);
    WritePrivateProfileString("StuckLogic", "TryToJump", bTryToJump ? "on" : "off", INIFileName);
    WritePrivateProfileString("StuckLogic", "TurnHalf", bTurnHalf ? "on" : "off", INIFileName);

    // Character specific
    if (bSaveByChar)
    {
        WritePrivateProfileString(szCharName, "AutoSave", bAutoSave ? "on" : "off", INIFileName);
        sprintf(szTemp, "%.2f", fBreakDist);
        WritePrivateProfileString(szCharName, "BreakDist", szTemp, INIFileName);
        WritePrivateProfileString(szCharName, "BreakOnGate", bBreakOnGate ? "on" : "off", INIFileName);
        WritePrivateProfileString(szCharName, "BreakOnWarp", bBreakOnWarp ? "on" : "off", INIFileName);
        WritePrivateProfileString(szCharName, "FeignSupport", bFeignSupport ? "on" : "off", INIFileName);
        sprintf(szTemp, "%.2f", fLeashLength);
        WritePrivateProfileString(szCharName, "LeashLength", szTemp, INIFileName);
        WritePrivateProfileString(szCharName, "UseLeash", bLeash ? "on" : "off", INIFileName);
        WritePrivateProfileString(szCharName, "Verbosity", bVerbosity ? "on" : "off", INIFileName);
        WritePrivateProfileString(szCharName, "FullVerbosity", bFullVerbosity ? "on" : "off", INIFileName);
        sprintf(szTemp, "%.2f", fCampRadius);
        WritePrivateProfileString(szCharName, "CampRadius", szTemp, INIFileName);
        // scatter values
        WritePrivateProfileString(szCharName, "UseScatter", bScatter ? "on" : "off", INIFileName);
        sprintf(szTemp, "%.2f", fBearing);
        WritePrivateProfileString(szCharName, "Bearing", szTemp, INIFileName);
        sprintf(szTemp, "%.2f", fScatDist);
        WritePrivateProfileString(szCharName, "ScatDist", szTemp, INIFileName);
        sprintf(szTemp, "%.2f", fScatter);
        WritePrivateProfileString(szCharName, "ScatSize", szTemp, INIFileName);
    }
}

void Load_INI(void)
{
    char szTemp[MAX_STRING] = {0};
    char szTemp2[MAX_STRING] = {0};
    char szCharName[MAX_STRING] = {0};
    sprintf(szCharName, "%s.%s", EQADDR_SERVERNAME, ((PCHARINFO)pCharData)->Name);
    bool bRewriteIni = false; // re-save if bad values were read

    // default settings
    GetPrivateProfileString("Defaults", "AutoPause", bAutoPause ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bAutoPause = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("Defaults", "AutoSave", bAutoSave ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bAutoSave = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("Defaults", "BreakKeyboard", bBreakOnKB ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bBreakOnKB = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("Defaults", "BreakMouse", bBreakOnMouse ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bBreakOnMouse = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("Defaults", "BreakOnSummon", bBreakOnSummon ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bBreakOnSummon = (!strnicmp(szTemp, "on", 3));
    sprintf(szTemp2, "%.2f", fBreakSummonDist);
    GetPrivateProfileString("Defaults", "BreakSummonDist", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 2.0f)
    {
        fBreakSummonDist = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    GetPrivateProfileString("Defaults", "FeignSupport", bFeignSupport ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bFeignSupport = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("Defaults", "HideHelp", bHideHelp ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bHideHelp = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("Defaults", "KeyboardPause", bPauseOnMove ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bPauseOnMove = (!strnicmp(szTemp, "on", 3));
    if (bPauseOnMove) bBreakOnKB = false;
    GetPrivateProfileString("Defaults", "MousePause", bPauseOnMouse ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bPauseOnMouse = (!strnicmp(szTemp, "on", 3));
    if (bPauseOnMouse) bBreakOnMouse = false;
    iMaxPauseDelay = GetPrivateProfileInt("Defaults", "PauseMaxDelay", iMaxPauseDelay, INIFileName);
    iMinPauseDelay = GetPrivateProfileInt("Defaults", "PauseMinDelay", iMinPauseDelay, INIFileName);
    GetPrivateProfileString("Defaults", "SaveByChar", bSaveByChar ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bSaveByChar = (!strnicmp(szTemp, "on", 3));
    sprintf(szTemp2, "%.2f", fTurnRate);
    GetPrivateProfileString("Defaults", "TurnRate", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 12.0f && (float)atof(szTemp) <= 24.0f)
    {
        fTurnRate = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    //support conversion of Verbosity to on/off
    GetPrivateProfileString("Defaults", "Verbosity", bVerbosity ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    if (!strnicmp(szTemp, "1", 2)) 
    {
        bVerbosity = bRewriteIni = true;
    }
    else if (!strnicmp(szTemp, "0", 2))
    {
        bVerbosity = false;
        bRewriteIni = true;
    }
    else
    {
        bVerbosity = (!strnicmp(szTemp, "on", 3));
    }
    // FullVerbosity is more frequent, detailed output, and differs from Verbosity
    // Setting one does not include the text of the other.
    GetPrivateProfileString("Defaults", "FullVerbosity", bFullVerbosity ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bFullVerbosity = (!strnicmp(szTemp, "on", 3));
    // Total Silence disables all output except extreme error messages and BreakOnSummon
    GetPrivateProfileString("Defaults", "TotalSilence", bTotalSilence ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bTotalSilence = (!strnicmp(szTemp, "on", 3));
    if (bTotalSilence && (bVerbosity || bFullVerbosity))
    {
        bRewriteIni = true;
        bVerbosity = bFullVerbosity = false;
    }

    // stick settings
    GetPrivateProfileString("Stick", "AwareAggro", bNoHoTT ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bNoHoTT = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("Stick", "AwareNotAggro", bSpinInCircles ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bSpinInCircles = (!strnicmp(szTemp, "on", 3));
    sprintf(szTemp2, "%.2f", fBreakDist);
    GetPrivateProfileString("Stick", "BreakDist", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 1.0f)
    {
        fBreakDist = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    GetPrivateProfileString("Stick", "BreakOnGate", bBreakOnGate ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bBreakOnGate = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("Stick", "BreakOnWarp", bBreakOnWarp ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bBreakOnWarp = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("Stick", "LooseStick", bAlwaysLooseStick ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bAlwaysLooseStick = (!strnicmp(szTemp, "on", 3));
    bLooseStick = bAlwaysLooseStick;
    sprintf(szTemp2, "%.2f", fStickDistMod);
    GetPrivateProfileString("Stick", "StickDistMod", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 0.0f)
    {
        fStickDistMod = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    sprintf(szTemp2, "%.2f", fStickDistModP);
    GetPrivateProfileString("Stick", "StickDistModPercent", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 0.0f)
    {
        fStickDistModP = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }

    // makecamp settings
    sprintf(szTemp2, "%.2f", fCampRadius);
    GetPrivateProfileString("MakeCamp", "CampRadius", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 10.0f)
    {
        fCampRadius = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    iMaxCampDelay = GetPrivateProfileInt("MakeCamp", "MaxDelay", iMaxCampDelay, INIFileName);
    iMinCampDelay = GetPrivateProfileInt("MakeCamp", "MinDelay", iMinCampDelay, INIFileName);
    GetPrivateProfileString("MakeCamp", "ReturnNoAggro", bReturnNoAggro ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bReturnNoAggro = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("MakeCamp", "ReturnNoTarget", bReturnNoTarget ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bReturnNoTarget = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("MakeCamp", "ReturnNotLooting", bReturnNotLooting ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bReturnNotLooting = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("MakeCamp", "UseLeash", bLeash ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bLeash = (!strnicmp(szTemp, "on", 3));
    sprintf(szTemp2, "%.2f", fLeashLength);
    GetPrivateProfileString("MakeCamp", "LeashLength", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= fCampRadius)
    {
        fLeashLength = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    // scatter configuration (makecamp)
    GetPrivateProfileString("MakeCamp", "UseScatter", bScatter ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bScatter = (!strnicmp(szTemp, "on", 3));
    sprintf(szTemp2, "%.2f", fBearing);
    GetPrivateProfileString("MakeCamp", "Bearing", szTemp2, szTemp, MAX_STRING, INIFileName);
    fBearing = (float)atof(szTemp);
    sprintf(szTemp2, "%.2f", fScatDist);
    GetPrivateProfileString("MakeCamp", "ScatDist", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 1.0f)
    {
        fScatDist = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    sprintf(szTemp2, "%.2f", fScatter);
    GetPrivateProfileString("MakeCamp", "ScatSize", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 1.0f)
    {
        fScatter = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }

    // moveto settings
    sprintf(szTemp2, "%.2f", fMoveDistINI);
    GetPrivateProfileString("MoveTo", "ArrivalDist", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 1.0f)
    {
        fMoveDistINI = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    fMoveDist = fMoveDistINI; // hold settings for endpreviouscmd
    sprintf(szTemp2, "%.2f", fMoveDistX);
    GetPrivateProfileString("MoveTo", "ArrivalDistX", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 1.0f)
    {
        fMoveDistX = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    sprintf(szTemp2, "%.2f", fMoveDistY);
    GetPrivateProfileString("MoveTo", "ArrivalDistY", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 1.0f)
    {
        fMoveDistY = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    GetPrivateProfileString("MoveTo", "LooseMoveTo", bAlwaysLooseMoveTo ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bAlwaysLooseMoveTo = (!strnicmp(szTemp, "on", 3));
    bLooseMoveTo = bAlwaysLooseMoveTo;
    sprintf(szTemp2, "%.2f", fMoveDistModINI);
    GetPrivateProfileString("MoveTo", "MoveToMod", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 0.0f)
    {
        fMoveDistModINI = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    fMoveDistMod = fMoveDistModINI; // save value for endpreviouscmd
    GetPrivateProfileString("MoveTo", "UseWalk", bWalkMoveTo ? "on" : "off", szTemp, MAX_STRING, INIFileName);

    // circle settings
    GetPrivateProfileString("Circle", "AlwaysBackwards", bAlwaysBack ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bAlwaysBack = (!strnicmp(szTemp, "on", 3));
    if (bAlwaysBack) bBackwards = true; // turn these on initially, use state bool in endcmd to enforce
    GetPrivateProfileString("Circle", "AlwaysCounterClockwise", bAlwaysCCW ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bAlwaysCCW = (!strnicmp(szTemp, "on", 3));
    if (bAlwaysCCW) bClockwise = false; // turn these on initially, use state bool in endcmd to enforce
    GetPrivateProfileString("Circle", "AlwaysDrunken", bAlwaysDrunk ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bAlwaysDrunk = (!strnicmp(szTemp, "on", 3));
    if (bAlwaysDrunk) bDrunken = true; // turn these on initially, use state bool in endcmd to enforce
    sprintf(szTemp, "%.2f", fCircleRadius);
    GetPrivateProfileString("Circle", "RadiusSize", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) >= 5.0f)
    {
        fCircleRadius = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }

    // stuck logic related
    GetPrivateProfileString("StuckLogic", "StuckLogic", "on", szTemp, MAX_STRING, INIFileName);
    bCheckStuck = (!strnicmp(szTemp, "on", 3));
    sprintf(szTemp2, "%.2f", fDistStuck);
    GetPrivateProfileString("StuckLogic", "DistStuck", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((float)atof(szTemp) > 0.0f)
    {
        fDistStuck = (float)atof(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    sprintf(szTemp2, "%uh", usiPulseCheck);
    GetPrivateProfileString("StuckLogic", "PulseCheck", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((unsigned short)atoi(szTemp) > 1)
    {
        usiPulseCheck = (unsigned short)atoi(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    sprintf(szTemp2, "%uh", usiPulseUnstuck);
    GetPrivateProfileString("StuckLogic", "PulseUnstuck", szTemp2, szTemp, MAX_STRING, INIFileName);
    if ((unsigned short)atoi(szTemp) > 1)
    {
        usiPulseUnstuck = (unsigned short)atoi(szTemp);
    }
    else
    {
        bRewriteIni = true;
    }
    GetPrivateProfileString("StuckLogic", "TryToJump", bTryToJump ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bTryToJump = (!strnicmp(szTemp, "on", 3));
    GetPrivateProfileString("StuckLogic", "TurnHalf", bTurnHalf ? "on" : "off", szTemp, MAX_STRING, INIFileName);
    bTurnHalf = (!strnicmp(szTemp, "on", 3));

    // check if we want to explicitly ignore this char's custom ini
    GetPrivateProfileString(szCharName, "DisregardMe", "false", szTemp, MAX_STRING, INIFileName);
    bDisregardMe = (!strnicmp(szTemp, "true", 4));

    // Character specific
    if (bSaveByChar && !bDisregardMe)
    {
        GetPrivateProfileString(szCharName, "AutoSave", bAutoSave ? "on" : "off", szTemp, MAX_STRING, INIFileName);
        bAutoSave = (!strnicmp(szTemp, "on", 3));
        GetPrivateProfileString(szCharName, "BreakOnWarp", bBreakOnWarp ? "on" : "off", szTemp, MAX_STRING, INIFileName);
        bBreakOnWarp = (!strnicmp(szTemp, "on", 3));
        sprintf(szTemp2, "%.2f", fBreakDist);
        GetPrivateProfileString(szCharName, "BreakDist", szTemp2, szTemp, MAX_STRING, INIFileName);
        if ((float)atof(szTemp) >= 1.0f)
        {
            fBreakDist = (float)atof(szTemp);
        }
        else
        {
            bRewriteIni = true;
        }
        GetPrivateProfileString(szCharName, "BreakOnGate", bBreakOnGate ? "on" : "off", szTemp, MAX_STRING, INIFileName);
        bBreakOnGate = (!strnicmp(szTemp, "on", 3));
        GetPrivateProfileString(szCharName, "FeignSupport", bFeignSupport ? "on" : "off", szTemp, MAX_STRING, INIFileName);
        bFeignSupport = (!strnicmp(szTemp, "on", 3));
        sprintf(szTemp2, "%.2f", fLeashLength);
        GetPrivateProfileString(szCharName, "LeashLength", szTemp2, szTemp, MAX_STRING, INIFileName);
        if ((float)atof(szTemp) >= fCampRadius)
        {
            fLeashLength = (float)atof(szTemp);
        }
        else
        {
            bRewriteIni = true;
        }
        GetPrivateProfileString(szCharName, "UseLeash", bLeash ? "on" : "off", szTemp, MAX_STRING, INIFileName);
        bLeash = (!strnicmp(szTemp, "on", 3));
        //support conversion of Verbosity to on/off
        GetPrivateProfileString(szCharName, "Verbosity", bVerbosity ? "on" : "off", szTemp, MAX_STRING, INIFileName);
        if (!strnicmp(szTemp, "1", 2))
        {
            bVerbosity = bRewriteIni = true;
        }
        else if (!strnicmp(szTemp, "0", 2))
        {
            bVerbosity = false;
            bRewriteIni = true;
        }
        else
        {
            GetPrivateProfileString(szCharName, "Verbosity", bVerbosity ? "on" : "off", szTemp, MAX_STRING, INIFileName);
            bVerbosity = (!strnicmp(szTemp, "on", 3));
        }
        sprintf(szTemp2, "%.2f", fCampRadius);
        GetPrivateProfileString(szCharName, "CampRadius", szTemp2, szTemp, MAX_STRING, INIFileName);
        if ((float)atof(szTemp) >= 10.0f)
        {
            fCampRadius = (float)atof(szTemp);
        }
        else
        {
            bRewriteIni = true;
        }
        // scatter configuration (makecamp)
        GetPrivateProfileString(szCharName, "UseScatter", bScatter ? "on" : "off", szTemp, MAX_STRING, INIFileName);
        bScatter = (!strnicmp(szTemp, "on", 3));
        sprintf(szTemp2, "%.2f", fBearing);
        GetPrivateProfileString(szCharName, "Bearing", szTemp2, szTemp, MAX_STRING, INIFileName);
        fBearing = (float)atof(szTemp);
        sprintf(szTemp2, "%.2f", fScatDist);
        GetPrivateProfileString(szCharName, "ScatDist", szTemp2, szTemp, MAX_STRING, INIFileName);
        if ((float)atof(szTemp) >= 1.0f)
        {
            fScatDist = (float)atof(szTemp);
        }
        else
        {
            bRewriteIni = true;
        }
        sprintf(szTemp2, "%.2f", fScatter);
        GetPrivateProfileString(szCharName, "ScatSize", szTemp2, szTemp, MAX_STRING, INIFileName);
        if ((float)atof(szTemp) >= 1.0f)
        {
            fScatter = (float)atof(szTemp);
        }
        else
        {
            bRewriteIni = true;
        }
    }

    if (bRewriteIni) Save_INI();
}

void SpewDebug(unsigned char ucDbgType, char* szOutput, ...)
{
    int i = 1;
    if (ucDbgType == DBG_ALL)
    {
#ifdef DEBUGALL
        DebugSpew(szOutput);
#endif
    }
    else if (ucDbgType == DBG_STUCK)
    {
#ifdef DEBUGSTUCK
        DebugSpew(szOutput);
#endif
    }
    else if (ucDbgType == DBG_OTHER)
    {
#ifdef DEBUGOTHER
        DebugSpew(szOutput);
#endif
    }
    else if (ucDbgType == DBG_DISABLE)
    {
        // do nothing with output
        i++;
    }
    else if (ucDbgType == DBG_NOHOTT)
    {
#ifdef DEBUGNOHOTT
        DebugSpew(szOutput);
#endif
    }
    else
    {
        i++;
    }
}

// End Output functions
///////////////////////////////////////
// Begin MQ2 Functions

void DoUnstickBind(char* szName, int iKeyPressed)
{
    PSPAWNINFO pChSpawn = (PSPAWNINFO)pCharSpawn;
    if (!pChSpawn || (pChSpawn && !pChSpawn->SpawnID) || GetGameState() != GAMESTATE_INGAME) return;

    static bool sbFwdDown = false;
    static bool sbBckDown = false;
    static bool sbLftDown = false;
    static bool sbRgtDown = false;
    static bool sbSLftDown = false;
    static bool sbSRgtDown = false;
    static bool sbStoppedMovement = false;
    static bool sbRedoKeys = false;

    bool bCmdActive = false;
    if (bMoveToOn || bCircling || (bStickOn && !bStickNewMob) || (bStickOn && bStickNewMob && bStickNewTarget))
    {
        bCmdActive = true;
    }

    if (!iKeyPressed)
    {
        if (!strncmp(szName, "UNSTICK_FWD", 12))
        {
            sbFwdDown = false;
        }
        else if (!strncmp(szName, "UNSTICK_BCK", 12))
        {
            sbBckDown = false;
        }
        else if (!strncmp(szName, "UNSTICK_LFT", 12))
        {
            sbLftDown = false;
        }
        else if (!strncmp(szName, "UNSTICK_RGT", 12))
        {
            sbRgtDown = false;
        }
        else if (!strncmp(szName, "UNSTICK_STRAFE_LFT", 19))
        {
            sbSLftDown = false;
        }
        else if (!strncmp(szName, "UNSTICK_STRAFE_RGT", 19))
        {
            sbSRgtDown = false;
        }

        if (!sbFwdDown && !sbBckDown && !sbLftDown && !sbRgtDown && !sbSLftDown && !sbSRgtDown)
        {
            bManuallyMoving = sbStoppedMovement = sbRedoKeys = false;
            if (bPauseOnMove && bCmdActive)
            {
                GetSystemTime(&stReturnTime);
                iPauseReturnTime = rand() % (iMaxPauseDelay - iMinPauseDelay + 1) + iMinPauseDelay;
            }
        }
    }
    else
    {
        // dont stop movement unless command is active (since keybinds fire every time pressed)
        // the problem with using a if (!stickon !circle etc then return) up top is that if they press a key down
        // and end the command before the key up, the vars wont reset correctly.
        if (!sbStoppedMovement && bCmdActive)
        {
            sbStoppedMovement = sbRedoKeys = true;
            DoMovement(APPLY_TO_ALL, false, false);
        }

        // UNSTICK_ (9)
        if (!strncmp(szName, "UNSTICK_FWD", 12))
        {
            sbFwdDown = true;
            if (sbRedoKeys)
            {
                MQ2Globals::ExecuteCmd(FindMappableCommand("forward"), 1, 0);
                sbRedoKeys = false;
            }
        }
        else if (!strncmp(szName, "UNSTICK_BCK", 12))
        {
            sbBckDown = true;
            if (sbRedoKeys)
            {
                MQ2Globals::ExecuteCmd(FindMappableCommand("back"), 1, 0);
                sbRedoKeys = false;
            }
        }
        else if (!strncmp(szName, "UNSTICK_LFT", 12))
        {
            sbLftDown = true;
            if (sbRedoKeys)
            {
                MQ2Globals::ExecuteCmd(FindMappableCommand("left"), 1, 0);
                sbRedoKeys = false;
            }
        }
        else if (!strncmp(szName, "UNSTICK_RGT", 12))
        {
            sbRgtDown = true;
            if (sbRedoKeys)
            {
                MQ2Globals::ExecuteCmd(FindMappableCommand("right"), 1, 0);
                sbRedoKeys = false;
            }
        }
        else if (!strncmp(szName, "UNSTICK_STRAFE_LFT", 19))
        {
            sbSLftDown = true;
            if (sbRedoKeys)
            {
                MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_left"), 1, 0);
                sbRedoKeys = false;
            }
        }
        else if (!strncmp(szName, "UNSTICK_STRAFE_RGT", 19))
        {
            sbSRgtDown = true;
            if (sbRedoKeys)
            {
                MQ2Globals::ExecuteCmd(FindMappableCommand("strafe_right"), 1, 0);
                sbRedoKeys = false;
            }
        }

        bManuallyMoving = true;
        if (iPauseReturnTime > 0)
        {
            iPauseReturnTime = 0; // stops return time from kicking in if you let go of the key and repress again.
        }

        //bPauseOnMove on if we set mpause on
        if (bPauseOnMove && bCmdActive)
        {
            if (bMoveToOn && bLeash && bMakeCamp && !bCampReturning) 
            {
                sprintf(szMsg, "\ay%s\aw:: Ended '/moveto' or '/makecamp return' because leash is on.", MODULE_NAME);
                WriteLine(szMsg, VERB_FULL);
                EndPreviousCmd(true);
            }
            bAllPaused = true;
            if ((bStickOn && bLooseStick) || (bMoveToOn && bLooseMoveTo) || (bDrunken && bCircling)) fAdjustHead = 10000.0f; // have to reset angle or loose fights player for heading
        }
        else if (bBreakOnKB)
        {
            if (bCmdActive)
            {
                sprintf(szMsg, "\ay%s\aw:: Current command ended from manual movement.", MODULE_NAME);
                if (!bCampReturning) WriteLine(szMsg, VERB_FULL);
                EndPreviousCmd(false); //EndPreviousCmd(true); // this stops kb input
                AdjustWalking(false); // added to prevent walk from being left on when breaking
            }
        }
    }
}

void CreateBinds(void)
{
    if (bMoveBindsLoaded) return;
    AddMQ2KeyBind("UNSTICK_FWD",        DoUnstickBind);
    AddMQ2KeyBind("UNSTICK_BCK",        DoUnstickBind);
    AddMQ2KeyBind("UNSTICK_LFT",        DoUnstickBind);
    AddMQ2KeyBind("UNSTICK_RGT",        DoUnstickBind);
    AddMQ2KeyBind("UNSTICK_STRAFE_LFT", DoUnstickBind);
    AddMQ2KeyBind("UNSTICK_STRAFE_RGT", DoUnstickBind);
    SetMQ2KeyBind("UNSTICK_FWD",        FALSE, pKeypressHandler->NormalKey[FindMappableCommand("forward")]);
    SetMQ2KeyBind("UNSTICK_FWD",        TRUE,  pKeypressHandler->AltKey[FindMappableCommand("forward")]);
    SetMQ2KeyBind("UNSTICK_BCK",        FALSE, pKeypressHandler->NormalKey[FindMappableCommand("back")]);
    SetMQ2KeyBind("UNSTICK_BCK",        TRUE,  pKeypressHandler->AltKey[FindMappableCommand("back")]);
    SetMQ2KeyBind("UNSTICK_LFT",        FALSE, pKeypressHandler->NormalKey[FindMappableCommand("left")]);
    SetMQ2KeyBind("UNSTICK_LFT",        TRUE,  pKeypressHandler->AltKey[FindMappableCommand("left")]);
    SetMQ2KeyBind("UNSTICK_RGT",        FALSE, pKeypressHandler->NormalKey[FindMappableCommand("right")]);
    SetMQ2KeyBind("UNSTICK_RGT",        TRUE,  pKeypressHandler->AltKey[FindMappableCommand("right")]);
    SetMQ2KeyBind("UNSTICK_STRAFE_LFT", FALSE, pKeypressHandler->NormalKey[FindMappableCommand("strafe_left")]);
    SetMQ2KeyBind("UNSTICK_STRAFE_LFT", TRUE,  pKeypressHandler->AltKey[FindMappableCommand("strafe_left")]);
    SetMQ2KeyBind("UNSTICK_STRAFE_RGT", FALSE, pKeypressHandler->NormalKey[FindMappableCommand("strafe_right")]);
    SetMQ2KeyBind("UNSTICK_STRAFE_RGT", TRUE,  pKeypressHandler->AltKey[FindMappableCommand("strafe_right")]);
    bMoveBindsLoaded = true;
}

void DestroyBinds(void)
{
    if (!bMoveBindsLoaded) return;
    RemoveMQ2KeyBind("UNSTICK_FWD");
    RemoveMQ2KeyBind("UNSTICK_BCK");
    RemoveMQ2KeyBind("UNSTICK_LFT");
    RemoveMQ2KeyBind("UNSTICK_RGT");
    RemoveMQ2KeyBind("UNSTICK_STRAFE_LFT");
    RemoveMQ2KeyBind("UNSTICK_STRAFE_RGT");
}

PMQPLUGIN FindPlugin(char* PluginName)
{
    unsigned int uiLength = strlen(PluginName) + 1;
    PMQPLUGIN pLook = pPlugins;
    while (pLook && strnicmp(PluginName, pLook->szFilename, uiLength))
    {
        pLook=pLook->pNext;
    }
    return pLook;
}

PLUGIN_API void InitializePlugin(void)
{
    AddCommand("/makecamp", MakeCampCommand, FALSE, TRUE, TRUE);
    AddCommand("/moveto",   MoveToCommand,   FALSE, TRUE, TRUE);
    AddCommand("/stick",    StickCommand,    FALSE, TRUE, TRUE);
    AddCommand("/circle",   CircleCommand,   FALSE, TRUE, TRUE);

    AddMQ2Data("Stick",     dataStick);
    AddMQ2Data("MakeCamp",  dataMakeCamp);
    AddMQ2Data("MoveTo",    dataMoveTo);
    AddMQ2Data("Circle",    dataCircling);
    AddMQ2Data("MoveUtils", dataMoveUtils);

    pStickType     = new MQ2StickType;
    pMakeCampType  = new MQ2MakeCampType;
    pMoveToType    = new MQ2MoveToType;
    pCircleType    = new MQ2CircleType;
    pMoveUtilsType = new MQ2MoveUtilsType;

    srand((unsigned int)time(0));
    GetSystemTime(&stPrevCirc);
    GetSystemTime(&stPrevStick);

    sprintf(szDebugName, "%s\\MQ2MoveUtils-debug.ini", gszINIPath);
    DebugSpewAlways("%s v%1.4f Loaded", MODULE_NAME, MODULE_VERSION);

    pbMULoaded = NULL;
    if (PMQPLUGIN pLook = FindPlugin("mq2melee"))
    {
        pbMULoaded = (bool *)GetProcAddress(pLook->hModule, "bMULoaded");
        if (pbMULoaded) *pbMULoaded = true;
    }
}

PLUGIN_API void ShutdownPlugin(void)
{
    RemoveCommand("/makecamp");
    RemoveCommand("/moveto");
    RemoveCommand("/stick");
    RemoveCommand("/circle");

    RemoveMQ2Data("Stick");
    RemoveMQ2Data("MakeCamp");
    RemoveMQ2Data("MoveTo");
    RemoveMQ2Data("Circle");
    RemoveMQ2Data("MoveUtils");

    delete pStickType;
    delete pMoveToType;
    delete pMakeCampType;
    delete pCircleType;
    delete pMoveUtilsType;

    DestroyBinds();
    AdjustWalking(false); // sometimes the plugin turns walk on and if plugin unloaded while in game, turn walk off
    DebugSpewAlways("%s v%1.4f Unloaded", MODULE_NAME, MODULE_VERSION);

    pbMULoaded = NULL;
    if (PMQPLUGIN pLook = FindPlugin("mq2melee"))
    {
        pbMULoaded = (bool *)GetProcAddress(pLook->hModule, "bMULoaded");
        if (pbMULoaded) *pbMULoaded = false;
    }
}

PLUGIN_API void SetGameState(unsigned long ulGameState)
{
    if (ulGameState == GAMESTATE_INGAME)
    {
        CreateBinds();
        Load_INI();
    }
    else
    {
        if (bStickOn || bMoveToOn || bCircling || bMakeCamp)
        {
            sprintf(szMsg, "\ay%s\aw:: GameState change ended previous command.", MODULE_NAME);
            WriteLine(szMsg, VERB_FULL);
        }
        EndPreviousCmd(false); // using true here causes ctd from ExecuteCmd calls
        ResetCamp(false, RESET_ALT);
    }
}

PLUGIN_API void OnZoned(void)
{
    EndPreviousCmd(false); // dont call ExecuteCmd on zone
    ResetCamp(false, RESET_ALT);
}

PLUGIN_API void OnRemoveSpawn(PSPAWNINFO pSpawn)
{
    if (pSpawn->SpawnID == ulStickID)
    {
        ulStickID = 0;
        stickTarget_Type = NONE;
    }
    if (pSpawn->SpawnID == ulCampPlayerID)
    {
        ResetCamp(true);
    }
}

PLUGIN_API unsigned long OnIncomingChat(char* szLine, unsigned long ulColor)
{
    if (bBreakOnGate && ((bStickHold && ulStickID && GetSpawnByID(ulStickID)) || pTarget))
    {
        char szTemp[MAX_STRING] = {0};

        sprintf(szTemp, "%s Gates.", ((PSPAWNINFO)(bStickHold ? GetSpawnByID(ulStickID) : pTarget))->DisplayedName);
        if (!strcmp(szTemp, szLine))
        {
            EndPreviousCmd(true);
            sprintf(szMsg, "\ay%s\aw:: Mob gating ended previous command.", MODULE_NAME);
            WriteLine(szMsg, VERB_FULL);
        }
    }

    return 0;
}

PLUGIN_API void OnPulse(void)
{
    PSPAWNINFO pChSpawn = (PSPAWNINFO)pCharSpawn;
    PSPAWNINFO psTarget = (PSPAWNINFO)pTarget;
    if (!pChSpawn || (pChSpawn && !pChSpawn->SpawnID) || GetGameState() != GAMESTATE_INGAME)
    {
        SpewDebug(DBG_ALL, "MQ2MoveUtils: Null pointer to CharSpawn or CharSpawn has no Spawn ID - OnPulse");
        return;
    }

    // don't process commands if dead
    if (pChSpawn->RespawnTimer > 0)
    {
        SpewDebug(DBG_DISABLE, "ChSpawn->Respawntimer > 0 processed");
        if (bStickOn || bMoveToOn || bCircling || bMakeCampReturn || bAltCampReturn || bCampReturning)
        {
            sprintf(szMsg, "\ay%s\aw:: \arYour untimely death has ended the previous command\ax.", MODULE_NAME);
            WriteLine(szMsg, VERB_ALL);
            EndPreviousCmd(true);
        }
        return;
    }

    /*char szHead[100] = {0};
    sprintf(szHead, "%.4f", pChSpawn->Heading);
    DebugSpew("Heading %s", szHead);
    return;*/
    /*if (!psTarget) return;
    float fTempCalc = CalcAngularDist(psTarget->Heading, pChSpawn->Heading);
    DebugSpew("fAngDist: %.2f", fTempCalc);
    return;*/
    // 19 = jump while moving, 20 = downwards fall from jump only
    // 24 = duck and START of jump, 71/32 = normal stand, 34 = turning left/right
    // 33 = on the way to sit, 38 = has sat down
    // 72 = shake the head shit after 5-6 seconds of standing still
    // 18 = run forward, 17=slow run (backwards, or end of run forward as decelerating, or strafe sideways)
    // 21 = falling through air
    //DebugSpew("Anim: %d, StandState: %d, pspawn anim: %d", pChSpawn->Animation, pChSpawn->StandState, pChSpawn->pSpawn->Animation);
    // values for  ->Animation and ->pSpawn->Animation are always the same
    // uncomment to spew real turn values using right/left keys
    // they do gradually increase if any want to try to solve the real scaling formula
    // otherwise 16.0 seems to be the max and a safe turn value
    /*static float fHeadChangeTime = pChSpawn->Heading;
    char szAnotherTemp[MAX_STRING] = {0};
    sprintf(szAnotherTemp, "%.2f", fHeadChangeTime - pChSpawn->Heading);
    DebugSpew("Heading Change %s Speed %.2f", szAnotherTemp, pChSpawn->SpeedHeading);
    fHeadChangeTime = pChSpawn->Heading;
    return;*/

    // if fAdjustHead is set, continue heading adjustment
    if (fAdjustHead != 10000.0f) AdjustHeading(fAdjustHead);

    SYSTEMTIME stCurr;
    GetSystemTime(&stCurr);

    static bool sbStickNewMobMsg = false; // output if '/stick __ always' is on
    static bool sbHoldingMouse = false; // used by mousepause

    // check for ${MoveUtils.Aggro} TLO
    if (psTarget)
    {
        float fAngDist = CalcAngularDist(psTarget->Heading, pChSpawn->Heading);
        if ((fAngDist < 0 && fAngDist < -190) || (fAngDist > 0 &&  fAngDist > 190))
        {
            bImAggro = true;
        }
        else
        {
            bImAggro = false;
        }
    }

    if (bMouseLook && (bPauseOnMouse || bBreakOnMouse) && (bCircling || (bStickOn && !bStickNewMob) || (bStickOn && bStickNewMob && bStickNewTarget) || bMoveToOn || bCampReturning))
    {
        iPauseReturnTime = 0; // stops return time from kicking in if you let go of mouse and reclick
        if (bPauseOnMouse)
        {
            if (!sbHoldingMouse)
            {
                bAllPaused = sbHoldingMouse = true;
                DoMovement(APPLY_TO_ALL, false, false); // stop walk/move when holding mouse
                if ((bLooseStick && bStickOn) || (bLooseMoveTo && bMoveToOn)) fAdjustHead = 10000.0f; // stops loose from trying to fight with pause
            }
        }
        else    //breakonmouse
        {
            if ((bLooseStick && bStickOn) || (bLooseMoveTo && bMoveToOn)) fAdjustHead = 10000.0f;
            EndPreviousCmd(true);
            sprintf(szMsg, "\ay%s\aw:: Current command ended from manual movement.", MODULE_NAME);
            WriteLine(szMsg, VERB_FULL);
        }
        return;
    }

    if (sbHoldingMouse)     // mouselook has been letgo, set returntime, sbHoldingMouse is only set true if mousepause enabled
    {
        sbHoldingMouse = false;
        if (bMoveToOn || bCircling || bStickOn || bCampReturning)
        {
            GetSystemTime(&stReturnTime);
            iPauseReturnTime = rand() % (iMaxPauseDelay - iMinPauseDelay + 1) + iMinPauseDelay;
        }
    }

    //if currently paused, mpause on, manually moving released, and current time >= time set by release of movement key
    //this may fail if manual /cmd pause is set and returntime has a value from something else...
    //checking against !zero value as /cmd pause will set iReturnTime to 0
    if (bAllPaused && (bPauseOnMove || bPauseOnMouse) && !bManuallyMoving && iPauseReturnTime != 0)
    {
        int iElapsedTime = diff_in_ms(stCurr, stReturnTime);
        // stReturnTime set from releasing keys in DoUnstickBind
        if (iElapsedTime >= iPauseReturnTime)
        {
            bAllPaused = false;
            sprintf(szMsg, "\ay%s\aw:: Resuming previous command from movement pause.", MODULE_NAME);
            if (!bCampReturning) WriteLine(szMsg, VERB_FULL);
            usiStuck = 0;
            bCmdMovedFwd = bCmdMovedSide = false;
            fNewCompare = 0.0f;
            fMyCurX = 0.0f;
            fMyCurY = 0.0f;
        }
        else
        {
            return;
        }
    }

    // don't process commands if plugin is paused
    if (!bAllPaused)
    {
        if (bStickOn)
        {
            // handling for /stick __ always
            if (bStickNewMob && (!psTarget || (psTarget && psTarget->Type != SPAWN_NPC)))
            {
                if (!sbStickNewMobMsg)
                {
                    sprintf(szMsg, "\ay%s\aw:: Stick awaiting next valid NPC target...", MODULE_NAME);
                    WriteLine(szMsg, VERB_FULL);
                    sbStickNewMobMsg = true;
                    bStickNewTarget = false;
                    DoMovement(APPLY_TO_ALL, false, false);
                }
                return;
            }
            else if (bStickNewMob && psTarget && psTarget->Type == SPAWN_NPC)
            {
                if (sbStickNewMobMsg)
                {
                    EndPreviousCmd(true, USED_STICK, true);
                    sbStickNewMobMsg = false;
                    bStickNewTarget = true;
                    fCurrentDist = GetDistance(pChSpawn, psTarget);
                    StandIfNeeded();
                    return;
                }
            }
            //end handling for /stick ___ always
            WeGetSignal(USED_STICK);
        }
        else if (bMoveToOn)
        {
            WeGetSignal(USED_MOVETO);
        }
        else if (bCircling)
        {
            if (bDrunken)
            {
                if (diff_in_ms(stCurr, stPrevCirc) > 900 + (int)GetRandomNumber(600.0f))
                {
                    GetSystemTime(&stPrevCirc);
                }
                else
                {
                    return;
                }
            }
            WeGetSignal(USED_CIRCLE);
        }
        else if (bMakeCamp || bMakeCampReturn || bAltCampReturn)
        {
            WeGetSignal(USED_MAKECAMP); // this will process /makecamp return/altreturn, if no other commands active
        }
    }
}
// End MQ2 Functions
///////////////////////////////////////

// MQ2Melee testing
void StickStatusOutput(void)
{
    WriteChatf("\ayMoveUtils\ax: Stick is ( %s\ax ) - last command received: \ag/stick %s", bStickOn ? "\agON" : "\arOFF", szLastStickCmd);
}