////////////////////////////////////////////////////////////////////////
// antistuck_h - Anti-Stuck Movement replacement
// By Deva Bryson Winblood.  04/22/2005
////////////////////////////////////////////////////////////////////////
/* Bioware ActionMoveToObject() can become stuck in places and it turns
    out that it uses a tremendous amount of CPU time in some case while
    it continues to try to move.   This header has a replacement
    function that can be used in place of the ActionMoveToObject().  It
    will attempt to assist the NPC in becoming unstuck.  It will
    eventually teleport the NPC if the NPC cannot appear to get to
    the destination any other way.  This function will use up slightly
    more CPU than ActionMoveToObject() but, the idea is that it may
    head off some of the larger CPU eating stuck issues. */

////////////////////
// PROTOTYPES
////////////////////

// FILE: antistuck_h            FUNCTION: ASActionMoveToObject()
// This function is a replacement function for ActionMoveToObject()
// that will attempt to keep an NPC from becoming stuck and continuously
// consuming additional CPU cycles.
void ASActionMoveToObject(object oDest,int bRun=FALSE,float fRange=1.0);

// FILE: antistuck_h            FUNCTION: ASActionMoveToLocation()
// This function is a replacement function for ActionMoveToLocation()
// that will attempt to keep an NPC from becoming stuck and continuously
// consuming additional CPU cycles.
void ASActionMoveToLocation(location lLoc,int bRun=FALSE);

////////////////////
// FUNCTIONS
////////////////////

void fnASClearASVariables()
{ // PURPOSE: Deallocate variables
  object oMe=OBJECT_SELF;
  DeleteLocalObject(oMe,"oASDest");
  DeleteLocalLocation(oMe,"lASRelative");
  DeleteLocalFloat(oMe,"fASLDist");
  DeleteLocalInt(oMe,"nASSC");
  DeleteLocalInt(oMe,"nASSR");
  DeleteLocalLocation(oMe,"lASLoc");
} // fnASClearASVariables()

void fnASError(string sMsg)
{ // PURPOSE: Report and log the error
  string sPrefix="Error: (antistuck_h): ";
  //SendMessageToPC(GetFirstPC(),sPrefix+sMsg);
  PrintString(sPrefix+sMsg);
} // fnASError()

void fnTeleport(object oDest,float fRange=3.0)
{ // PURPOSE: Try to teleport
  object oMe=OBJECT_SELF;
  int nC=GetLocalInt(oMe,"nASTCount");
  if (GetArea(oMe)!=GetArea(oDest)||GetDistanceBetween(oMe,oDest)>fRange&&nC<6)
  { // teleport
    nC++;
    SetLocalInt(oMe,"nASTCount",nC);
    AssignCommand(oMe,ClearAllActions(TRUE));
    AssignCommand(oMe,JumpToObject(oDest));
    DelayCommand(1.0,fnTeleport(oDest,fRange));
  } // teleport
  else
  { // count
    DeleteLocalInt(oMe,"nASTCount");
  } // count
} // fnTeleport()

void fnASHandleAntiStuck(object oDest,int bRun,float fRange,location lLoc,int nType=0)
{ // PURPOSE: The anti-stuck portion of this script
  object oMe=OBJECT_SELF;
  object oOb;
  int nASSC=GetLocalInt(oMe,"nASSC");
  int nASSR=GetLocalInt(oMe,"nASSR");
  vector vCur=GetPosition(oMe);
  string sS;
  int nX;
  int nY;
  nASSC++;
  if (nASSC>1&&nASSC<3)
  { // do nothing
    AssignCommand(oMe,ClearAllActions());
    if (nType==0) AssignCommand(oMe,ActionMoveToObject(oDest,bRun,fRange));
    else if (nType==1) AssignCommand(oMe,ActionMoveToLocation(lLoc,bRun));
  } // do nothing
  if (nASSC>2&&nASSR<3)
  { // try to move to nearby object first
    oOb=GetNearestObject(OBJECT_TYPE_WAYPOINT,oMe,d100());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_WAYPOINT,oMe,d20());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_WAYPOINT,oMe,d12());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_PLACEABLE,oMe,d20());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_PLACEABLE,oMe,d12());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_TRIGGER,oMe,d20());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_TRIGGER,oMe,d12());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_DOOR,oMe,d20());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_DOOR,oMe,d12());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_ALL,oMe,d20());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_ALL,oMe,d8());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_ALL,oMe,d4());
    if (oOb==OBJECT_INVALID) oOb=GetNearestObject(OBJECT_TYPE_ALL,oMe,1);
    if (oOb==OBJECT_INVALID)
    { // no objects in area
      fnASError(GetName(GetArea(oMe))+" has no objects to assist in anti-stuck pathing.  Place some waypoints about the area.");
    } // no objects in area
    else if (oOb!=OBJECT_INVALID)
    { // move
      AssignCommand(oMe,ClearAllActions(TRUE));
      AssignCommand(oMe,ActionMoveToObject(oOb,TRUE,2.0));
      DelayCommand(5.0,ClearAllActions(TRUE));
    } // move
    nASSC++;
    nASSR++;
  } // try to move to nearby object first
  else if (nASSC>2&&nASSR<6)
  { // try to teleport
    AssignCommand(oMe,ClearAllActions(TRUE));
    if (nType==0) AssignCommand(oMe,fnTeleport(oDest,fRange));
    else if (nType==1) AssignCommand(oMe,JumpToLocation(lLoc));
    nASSR++;
  } // try to teleport
  else if (nASSC>2&&nASSR>5)
  { // destination unreachable
    nX=FloatToInt(vCur.x);
    nY=FloatToInt(vCur.y);
    fnASError(GetName(oMe)+" in area "+GetName(GetArea(oMe))+" destination unreachable "+GetTag(oDest)+" currently at location X:"+IntToString(nX)+",Y:"+IntToString(nY));
    fnASClearASVariables();
  } // destination unreachable
  SetLocalInt(oMe,"nASSC",nASSC);
  SetLocalInt(oMe,"nASSR",nASSR);
} // fnASHandleAntiStuck()

void ASActionMoveToObject(object oDest,int bRun=FALSE,float fRange=1.0)
{ // PURPOSE: Replacement anti-stuck version of ActionMoveToObject()
  object oMe=OBJECT_SELF;
  object oASDest=GetLocalObject(oMe,"oASDest");
  float fLDist=GetLocalFloat(oMe,"fASLDist");
  float fDist;
  location lRelative=GetLocalLocation(oMe,"lASRelative");
  if (oDest==OBJECT_INVALID)
  { // invalid destination
    fnASError(GetName(oMe)+" tried to move to a destination that does not exist.  In area ["+GetName(GetArea(oMe))+"]");
    return;
  } // invalid destination
  if (GetCurrentAction(oMe)!=ACTION_MOVETOPOINT) AssignCommand(oMe,ActionMoveToObject(oDest,bRun,fRange));
  if (oDest!=oASDest)
  { // new destination
    fnASClearASVariables();
    SetLocalObject(oMe,"oASDest",oDest);
    fLDist=10000.0;
  } // new destination
  if (GetArea(oDest)==GetArea(oMe))
  { // same area pathing
    fDist=GetDistanceBetween(oDest,oMe);
    if (fDist==fLDist)
    { // we might be stuck
      fnASHandleAntiStuck(oDest,bRun,fRange,lRelative);
    } // we might be stuck
    else
    { // not stuck
      DeleteLocalInt(oMe,"nASSC");
    } // not stuck
    SetLocalFloat(oMe,"fASLDist",fDist);
  } // same area pathing
  else
  { // different area pathing
    if (GetAreaFromLocation(lRelative)!=GetArea(oMe))
    { // need to set the relative location
      lRelative=GetLocation(oMe);
      SetLocalLocation(oMe,"lASRelative",lRelative);
    } // need to set the relative location
    fDist=GetDistanceBetweenLocations(lRelative,GetLocation(oMe));
    if (fDist==fLDist)
    { // we might be stuck
      fnASHandleAntiStuck(oDest,bRun,fRange,lRelative);
    } // we might be stuck
    else
    { // not stuck
      DeleteLocalInt(oMe,"nASSC");
    } // not stuck
    SetLocalFloat(oMe,"fASLDist",fDist);
  } // different area pathing
} // ASActionMoveToObject()

void ASActionMoveToLocation(location lLoc,int bRun=FALSE)
{ // PURPOSE: Replacement anti-stuck version of ActionMoveToLocation()
  object oMe=OBJECT_SELF;
  location lASLLoc=GetLocalLocation(oMe,"lASLLoc");
  float fLDist=GetLocalFloat(oMe,"fASLDist");
  float fDist;
  location lRelative=GetLocalLocation(oMe,"lASRelative");
  if (GetAreaFromLocation(lLoc)==OBJECT_INVALID)
  { // invalid destination
    fnASError(GetName(oMe)+" tried to move to a destination that does not exist");
    return;
  } // invalid destination
  if (GetCurrentAction(oMe)!=ACTION_MOVETOPOINT) AssignCommand(oMe,ActionMoveToLocation(lLoc,bRun));
  if (lLoc!=lASLLoc)
  { // new destination
    fnASClearASVariables();
    SetLocalLocation(oMe,"lASLLoc",lLoc);
    fLDist=10000.0;
  } // new destination
  if (GetAreaFromLocation(lLoc)==GetArea(oMe))
  { // same area pathing
    fDist=GetDistanceBetweenLocations(lLoc,GetLocation(oMe));
    if (fDist==fLDist)
    { // we might be stuck
      fnASHandleAntiStuck(OBJECT_INVALID,bRun,0.0,lLoc,1);
    } // we might be stuck
    else
    { // not stuck
      DeleteLocalInt(oMe,"nASSC");
    } // not stuck
    SetLocalFloat(oMe,"fASLDist",fDist);
  } // same area pathing
  else
  { // different area pathing
    if (GetAreaFromLocation(lRelative)!=GetArea(oMe))
    { // need to set the relative location
      lRelative=GetLocation(oMe);
      SetLocalLocation(oMe,"lASRelative",lRelative);
    } // need to set the relative location
    fDist=GetDistanceBetweenLocations(lRelative,GetLocation(oMe));
    if (fDist==fLDist)
    { // we might be stuck
      fnASHandleAntiStuck(OBJECT_INVALID,bRun,0.0,lLoc,1);
    } // we might be stuck
    else
    { // not stuck
      DeleteLocalInt(oMe,"nASSC");
    } // not stuck
    SetLocalFloat(oMe,"fASLDist",fDist);
  } // different area pathing
} // ASActionMoveToLocation()

//void main(){}