Merged redundant hak files. Moved hak scripts into module. Updated gitignore. Full Compile. Added release folder & archive.
874 lines
29 KiB
Plaintext
874 lines
29 KiB
Plaintext
///////////////////////////////////////////////////////////////////////////////////
|
|
// REAL TIME STRATEGY ADVENTURE - Kit
|
|
// FILE: rtsa_headera
|
|
// NAME: header functions for unit AI
|
|
// SCRIPTED BY: Deva Bryson Winblood
|
|
// DATE: 4/13/2003
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
// This header contains pathing and other AI critical parts for the AI
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
#include "rtsa_headere" // errors & debug
|
|
#include "rtsa_headers" // stacks
|
|
|
|
/////////////////////////////////////////
|
|
// FUNCTIONS
|
|
/////////////////////////////////////////
|
|
object fnRandomObject(object oArea,int nObType=0,object oOBT=OBJECT_INVALID)
|
|
{ // This will return a random object from the area of the specified type
|
|
// if none is specified it will return a random object placeable, door, waypoint,
|
|
// or trigger
|
|
// This WILL NOT return area transition objects use fnRandomTrans for that
|
|
object oRet=OBJECT_INVALID;
|
|
int nObT=nObType;
|
|
int nR;
|
|
int nC;
|
|
float fDist=0.0;
|
|
int nLim=0;
|
|
object oObB=GetFirstObjectInArea(oArea);
|
|
if (oOBT!=OBJECT_INVALID) oObB=oOBT;
|
|
fnDebug(" fnRandomObject("+GetName(oArea)+","+IntToString(nObType)+")");
|
|
fnDebug(" First Object in Area:"+GetTag(oObB)+" Type:"+IntToString(GetObjectType(oObB)));
|
|
while(fDist<4.0&&nLim<3)
|
|
{ // find something not close
|
|
if (nObType==0)
|
|
{
|
|
nR=d4();
|
|
if (nR==1) nObT=OBJECT_TYPE_PLACEABLE;
|
|
else if (nR==2) nObT=OBJECT_TYPE_WAYPOINT;
|
|
else if (nR==3) nObT=OBJECT_TYPE_DOOR;
|
|
else if (nR==4) nObT=OBJECT_TYPE_TRIGGER;
|
|
}
|
|
fnDebug(" Object type chosen:"+IntToString(nObT));
|
|
nC=0;
|
|
if (oOBT==OBJECT_INVALID)
|
|
while(nC<4&&(nObT==GetObjectType(oObB)||oObB==OBJECT_INVALID))
|
|
{ // make sure checker object is of a different type
|
|
if(nObT!=OBJECT_TYPE_WAYPOINT)
|
|
oObB=GetNearestObject(OBJECT_TYPE_WAYPOINT,oObB,1);
|
|
else
|
|
oObB=GetNearestObject(OBJECT_TYPE_PLACEABLE,oObB,1);
|
|
nC++;
|
|
} // make sure checker object is of a different type
|
|
fnDebug(" Object to compare against:"+GetTag(oObB));
|
|
if (nObT==OBJECT_TYPE_PLACEABLE)
|
|
nC=GetLocalInt(oArea,"nNumPlaceables");
|
|
else if (nObT==OBJECT_TYPE_WAYPOINT)
|
|
nC=GetLocalInt(oArea,"nNumWP");
|
|
else if (nObT==OBJECT_TYPE_DOOR)
|
|
nC=GetLocalInt(oArea,"nNumDoors");
|
|
else if (nObT==OBJECT_TYPE_TRIGGER)
|
|
nC=GetLocalInt(oArea,"nNumTriggers");
|
|
if (nC==0&&nObType==0)
|
|
{ // no objects of that type exist
|
|
if (nObT!=OBJECT_TYPE_PLACEABLE&&GetObjectType(oObB)!=OBJECT_TYPE_PLACEABLE&&GetLocalInt(oArea,"nNumPlaceables")>0)
|
|
{ // try for placeables
|
|
nObT=OBJECT_TYPE_PLACEABLE;
|
|
nC=GetLocalInt(oArea,"nNumPlaceables");
|
|
} // try for placeables
|
|
else if (nObT!=OBJECT_TYPE_WAYPOINT&&GetObjectType(oObB)!=OBJECT_TYPE_WAYPOINT&&GetLocalInt(oArea,"nNumWP")>0)
|
|
{ // try for waypoints
|
|
nObT=OBJECT_TYPE_WAYPOINT;
|
|
nC=GetLocalInt(oArea,"nNumWP");
|
|
} // try for waypoints
|
|
else if (nObT!=OBJECT_TYPE_DOOR&&GetObjectType(oObB)!=OBJECT_TYPE_DOOR&&GetLocalInt(oArea,"nNumDoors")>0)
|
|
{ // try a door
|
|
nObT=OBJECT_TYPE_DOOR;
|
|
nC=GetLocalInt(oArea,"nNumDoors");
|
|
} // try a door
|
|
else if (nObT!=OBJECT_TYPE_TRIGGER&&GetObjectType(oObB)!=OBJECT_TYPE_TRIGGER&&GetLocalInt(oArea,"nNumTriggers")>0)
|
|
{ // try a trigger
|
|
nObT=OBJECT_TYPE_TRIGGER;
|
|
nC=GetLocalInt(oArea,"nNumTriggers");
|
|
} // try a trigger
|
|
} // no objects of that type exist
|
|
if (nC>0&&oObB!=OBJECT_INVALID)
|
|
{ // get the random object
|
|
nR=Random(nC)+1;
|
|
if (nObT==OBJECT_TYPE_PLACEABLE)
|
|
oRet=GetNearestObject(OBJECT_TYPE_PLACEABLE,oObB,nR);
|
|
else if (nObT==OBJECT_TYPE_WAYPOINT)
|
|
oRet=GetNearestObject(OBJECT_TYPE_WAYPOINT,oObB,nR);
|
|
else if (nObT==OBJECT_TYPE_DOOR)
|
|
oRet=GetLocalObject(oArea,"oDoor"+IntToString(nR));
|
|
else if (nObT==OBJECT_TYPE_TRIGGER)
|
|
oRet=GetLocalObject(oArea,"oTrigger"+IntToString(nR));
|
|
} // get the random object
|
|
else if (oObB==OBJECT_INVALID)
|
|
oRet=GetFirstObjectInArea(oArea);
|
|
nLim++;
|
|
fDist=GetDistanceToObject(oRet);
|
|
} // find something not close
|
|
return oRet;
|
|
} // fnRandomObject()
|
|
|
|
object fnRandomTrans(object oArea)
|
|
{ // Returns a random area transition target (trigger or door)
|
|
object oRet=OBJECT_INVALID;
|
|
int nC=GetLocalInt(oArea,"nNumTrans");
|
|
int nR=Random(nC)+1;
|
|
if (nC>0)
|
|
oRet=GetLocalObject(oArea,"oTrans"+IntToString(nR));
|
|
return oRet;
|
|
} // fnRandomTrans()
|
|
|
|
object fnDirectConnection(object oStart,object oEnd)
|
|
{ // find out if there is a direct area transition between the two places
|
|
object oRet=OBJECT_INVALID;
|
|
int nX=GetLocalInt(oStart,"nNumTrans");
|
|
int nC=1;
|
|
object oT=GetLocalObject(oStart,"oTrans"+IntToString(nC));
|
|
object oX;
|
|
oX=GetTransitionTarget(oT);
|
|
while (nC<=nX&&GetArea(oX)!=oEnd)
|
|
{ // look
|
|
oT=GetLocalObject(oStart,"oTrans"+IntToString(nC));
|
|
oX=GetTransitionTarget(oT);
|
|
nC++;
|
|
} // look
|
|
if (GetArea(oX)==oEnd) oRet=oT;
|
|
return oRet;
|
|
} // fnDirectConnection()
|
|
|
|
int fnBuildPath(object oEnd,int nMax=10)
|
|
{ // build a reverse path between this location and me
|
|
int nRet=FALSE;
|
|
object oMe=OBJECT_SELF;
|
|
int nX;
|
|
int nR;
|
|
int nDepth=0;
|
|
object oAt=oEnd;
|
|
object oPrev;
|
|
object oH1;
|
|
object oH2;
|
|
fnPush("Pathing","",0,oEnd); // put end point on stack
|
|
oH1=fnDirectConnection(GetArea(oEnd),GetArea(oMe));
|
|
if (oH1!=OBJECT_INVALID)
|
|
{ // direct connection
|
|
fnPush("Pathing","",0,oH1);
|
|
fnPush("Pathing","",0,fnRandomObject(GetArea(oH1),0,oH1));
|
|
return TRUE;
|
|
} // direct connection
|
|
while(oAt!=oMe&&nDepth<=nMax)
|
|
{ // traverse nodes
|
|
oH1=fnDirectConnection(GetArea(oAt),GetArea(oMe));
|
|
if (oH1!=OBJECT_INVALID)
|
|
{ // direct connection
|
|
oH1=GetTransitionTarget(oH1);
|
|
if(GetTransitionTarget(oH1)!=OBJECT_INVALID)
|
|
{ // !OI-TT
|
|
oH1=fnRandomObject(GetArea(oH1),0,oH1);
|
|
} // !OI-TT
|
|
fnPush("Pathing","",0,oH1);
|
|
return TRUE;
|
|
} // direct connection
|
|
oH1=fnRandomTrans(GetArea(oAt));
|
|
oH1=GetTransitionTarget(oH1);
|
|
nX=0;
|
|
while (GetArea(oH1)==oPrev&&nX<3)
|
|
{ // keep looking
|
|
oH1=fnRandomTrans(GetArea(oAt));
|
|
oH1=GetTransitionTarget(oH1);
|
|
nX++;
|
|
} // keep looking
|
|
if (GetArea(oH1)!=oPrev)
|
|
{ // found a direction to try
|
|
oPrev=GetArea(oAt);
|
|
if(GetTransitionTarget(oH1)!=OBJECT_INVALID)
|
|
{ // !OI-TT
|
|
oH1=fnRandomObject(GetArea(oH1),0,oH1);
|
|
} // !OI-TT
|
|
fnPush("Pathing","",0,oH1);
|
|
oAt=oH1;
|
|
nDepth++;
|
|
} // found a direction to try
|
|
} // traverse nodes
|
|
return nRet;
|
|
} // fnBuildPath()
|
|
|
|
int fnFindPathTo(object oDest,int nMaxNodes=10)
|
|
{ // find a path
|
|
object oMe=OBJECT_SELF;
|
|
object oMyArea=GetArea(oMe);
|
|
object oDestArea=GetArea(oDest);
|
|
int nRet=0;
|
|
int nFoundPath=FALSE;
|
|
int nPathing=GetLocalInt(oMe,"nPathing");
|
|
if (nPathing!=TRUE)
|
|
{ // starting pathing
|
|
fnFlushS("Pathing");
|
|
fnInitializeStack("Pathing",3); // object stack
|
|
if (oMyArea==oDestArea)
|
|
{ // same area - no pathing needed
|
|
//SendMessageToPC(GetFirstPC(),"Pathing: Same Area");
|
|
fnPush("Pathing","",0,oDest);
|
|
nFoundPath=TRUE;
|
|
} // same area - no pathing needed
|
|
else if (fnDirectConnection(oMyArea,oDestArea)!=OBJECT_INVALID)
|
|
{ // there is a direct link - no pathing needed
|
|
fnPush("Pathing","",0,oDest);
|
|
fnPush("Pathing","",0,fnRandomObject(GetArea(oDest),0,oDest));
|
|
//SendMessageToPC(GetFirstPC(),"Pathing: Direct link");
|
|
nFoundPath=TRUE;
|
|
} // there is a direct link - no pathing needed
|
|
else
|
|
{ // build a path
|
|
//SendMessageToPC(GetFirstPC(),"Call fnBuildPath");
|
|
nFoundPath=fnBuildPath(oDest,nMaxNodes);
|
|
} // build a path
|
|
if (nFoundPath==TRUE)
|
|
SetLocalInt(oMe,"nPathing",FALSE);
|
|
} // starting pathing
|
|
else
|
|
{ // still looking
|
|
//SendMessageToPC(GetFirstPC(),"Pathing: Still calling fnBuildPath");
|
|
nFoundPath=fnBuildPath(oDest,nMaxNodes);
|
|
if (nFoundPath==TRUE)
|
|
SetLocalInt(oMe,"nPathing",FALSE);
|
|
} // still looking
|
|
return nRet;
|
|
} // fnFindPathTo()
|
|
|
|
float fnPlanarDistance(location lLoc1,location lLoc2)
|
|
{ // get planar distance
|
|
float fRet=0.0;
|
|
vector vVec=GetPositionFromLocation(lLoc1);
|
|
float fY1=vVec.y;
|
|
float fX1=vVec.x;
|
|
vVec=GetPositionFromLocation(lLoc2);
|
|
float fX2=vVec.x;
|
|
float fY2=vVec.y;
|
|
fY1=(fY1-fY2);
|
|
fX1=(fX1-fX2);
|
|
fRet=(fY1+fX1);
|
|
if (fRet<0.0) fRet=fRet*-1.0;
|
|
fRet=fRet/2.0;
|
|
return fRet;
|
|
} // fnPlanarDistance()
|
|
|
|
int fnMoveToDestination(object oDest,int nAS,float fRange=2.5)
|
|
{ // the actual movement command - handles anti-stuck as well
|
|
if (oDest!=OBJECT_INVALID&&GetIsPC(oDest)!=TRUE&&GetObjectType(oDest)!=OBJECT_TYPE_CREATURE)
|
|
{ // !OI
|
|
int nRet=0;
|
|
object oMe=OBJECT_SELF;
|
|
float fDist=GetDistanceToObject(oDest);
|
|
float fDistR=GetLocalFloat(oMe,"fDistR");
|
|
int nASC=GetLocalInt(oMe,"nASC");
|
|
int nWander=GetLocalInt(oMe,"nWander");
|
|
object oInArea=GetLocalObject(oMe,"oInArea");
|
|
location lMe=GetLocation(oMe);
|
|
location lDest=GetLocation(oDest);
|
|
int nRun=GetLocalInt(oMe,"nRun");
|
|
object oTrans;
|
|
int nC;
|
|
int nErr;
|
|
int nWC=GetLocalInt(oMe,"nWC"); // wander count
|
|
object oPC=GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC,oMe); // PC
|
|
fnDebug(" ["+GetName(oMe)+"]fnMoveToDestination("+GetTag(oDest)+","+IntToString(nAS)+","+FloatToString(fRange)+")");
|
|
if (oDest==OBJECT_INVALID) return -1;
|
|
if (fDist==-1.0)
|
|
{
|
|
if(oInArea==OBJECT_INVALID)
|
|
{
|
|
oInArea=fnRandomObject(GetArea(oMe));
|
|
SetLocalObject(oMe,"oInArea",oInArea);
|
|
}
|
|
}
|
|
else if (fDist!=-1.0&&fDist<=fRange)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (oInArea!=OBJECT_INVALID)
|
|
DeleteLocalObject(oMe,"oInArea");
|
|
if (nWander!=TRUE)
|
|
{ // okay to process movement - not wandering
|
|
SetLocalFloat(oMe,"fDistR",fDist);
|
|
if (fDist==-1.0)
|
|
SetLocalFloat(oMe,"fDistR",GetDistanceToObject(oInArea));
|
|
fnDebug(" fnMoveToDestination:"+GetTag(oDest)+" nASC:"+IntToString(nASC)+" fDist:"+FloatToString(fDist)+" fDistR:"+FloatToString(fDistR)+" MvR:"+IntToString(GetMovementRate(oMe))+" Currently At:"+GetName(GetArea(oMe)));
|
|
if (nAS==TRUE&&((fDist==fDistR&&fDist!=-1.0)||(GetDistanceToObject(oInArea)==fDistR&&fDist==-1.0)))
|
|
{ // Possibly stuck and ANTI-STUCK is enabled
|
|
nASC++;
|
|
SetLocalInt(oMe,"nASC",nASC);
|
|
if (oPC==OBJECT_INVALID)
|
|
{ // no PCs in area - use speed method
|
|
nC=13-GetMovementRate(oMe);
|
|
if (nASC>(nC+2))
|
|
{ // teleport failed - return error
|
|
return -1;
|
|
} // teleport failed - return error
|
|
else if (nASC>nC)
|
|
{ // teleport
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,JumpToObject(oDest));
|
|
} // teleport
|
|
else
|
|
{ // move
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange));
|
|
} // move
|
|
} // no PCs in area - use speed method
|
|
else
|
|
{ // PCs in area - use visible method
|
|
nC=8-GetMovementRate(oMe);
|
|
if (nASC>(nC+7))
|
|
{ // teleport failed - return error
|
|
return -1;
|
|
} // teleport failed - return error
|
|
else if (nASC>(nC+6))
|
|
{ // compare locations (disregard Z)
|
|
if (fnPlanarDistance(lMe,lDest)<fRange)
|
|
return 1;
|
|
} // compare locations (disregard Z)
|
|
else if (nASC>(nC+5))
|
|
{ // Teleport
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,JumpToObject(oDest));
|
|
} // Teleport
|
|
else if (nASC>(nC+3))
|
|
{ // Wander mode
|
|
if (nWC<2)
|
|
{ // have not wandered too much
|
|
oTrans=fnRandomObject(GetArea(oMe));
|
|
SetLocalInt(oMe,"nWander",TRUE);
|
|
DelayCommand(8.0,SetLocalInt(oMe,"nWander",FALSE));
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,ActionMoveToObject(oTrans,nRun,3.0));
|
|
DelayCommand(7.5,AssignCommand(oMe,ClearAllActions()));
|
|
DelayCommand(7.7,AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange)));
|
|
nWC++;
|
|
SetLocalInt(oMe,"nWC",nWC);
|
|
} // have not wandered too much
|
|
} // Wander mode
|
|
else if (nASC>(nC+1))
|
|
{ // active kickstart
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange));
|
|
} // active kickstart
|
|
else
|
|
{ // issue move command
|
|
AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange));
|
|
} // issue move command
|
|
} // PCs in area - use visible method
|
|
} // Possibly stuck and ANTI-STUCK is enabled
|
|
else
|
|
{ // Not STUCK or ANTI-STUCK disabled
|
|
if (fDist<=fRange&&fDist!=-1.0)
|
|
{ // arrived
|
|
SetLocalInt(oMe,"nWC",0);
|
|
SetLocalInt(oMe,"nASC",0);
|
|
SetLocalInt(oMe,"nWander",FALSE);
|
|
return 1;
|
|
} // arrived
|
|
else
|
|
{ // have not arrived
|
|
if (GetTransitionTarget(oDest)!=OBJECT_INVALID)
|
|
{ // transition target
|
|
oTrans=GetTransitionTarget(oDest);
|
|
oTrans=fnRandomObject(GetArea(oTrans),0,oTrans);
|
|
nC=2;
|
|
while((GetTransitionTarget(oTrans)!=OBJECT_INVALID||GetIsPC(oTrans)==TRUE||GetObjectType(oTrans)==OBJECT_TYPE_CREATURE)&&nC<10)
|
|
{
|
|
oTrans=GetNearestObject(OBJECT_TYPE_ALL,oTrans,nC);
|
|
nC++;
|
|
}
|
|
if (GetTransitionTarget(oTrans)==OBJECT_INVALID&&GetIsPC(oTrans)!=TRUE&&GetObjectType(oTrans)!=OBJECT_TYPE_CREATURE)
|
|
{ // go to this object
|
|
nErr=fnMoveToDestination(oTrans,nAS,fRange);
|
|
return nErr;
|
|
} // go to this object
|
|
else
|
|
return -1;
|
|
} // transition target
|
|
else
|
|
{ // in our area
|
|
AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange));
|
|
SetLocalInt(oMe,"nASC",0);
|
|
} // in our area
|
|
} // have not arrived
|
|
} // Not STUCK or ANTI-STUCK disabled
|
|
} // okay to process movement - not wandering
|
|
return nRet;
|
|
} // OI
|
|
else
|
|
return -1; // OBJECT_INVALID
|
|
} // fnMoveToDestination()
|
|
|
|
int fnMovePath(object oDest,int nAS,float fRange=2.5)
|
|
{ // Main movement function
|
|
int nRet=0;
|
|
object oMe=OBJECT_SELF;
|
|
object oDirect;
|
|
object oD2;
|
|
int nErr;
|
|
int nTop=fnSizeS("Pathing");
|
|
float fDist=GetDistanceToObject(oDest);
|
|
if (oDest==OBJECT_INVALID) return -1;
|
|
else fnDebug(" fnMovePath("+GetTag(oDest)+"/"+GetName(oDest)+"/"+GetResRef(oDest)+")");
|
|
if (nTop==0)
|
|
{ // no path build one
|
|
fnDebug(" Initialize Stack & Find Path");
|
|
fnInitializeStack("Pathing",3); // Object stack
|
|
nErr=fnFindPathTo(oDest); // build pathing
|
|
if (nErr==-1)
|
|
return -1;
|
|
} // no path build one
|
|
else if (nTop==1)
|
|
{ // single destination
|
|
oDirect=fnPopObject("Pathing",FALSE);
|
|
fnDebug(" Single Destination Remains:"+GetTag(oDirect));
|
|
if (GetTransitionTarget(oDirect)!=OBJECT_INVALID)
|
|
{ // transition target
|
|
fDist=GetDistanceToObject(oDirect);
|
|
if (fDist!=-1.0&&fDist<20.1)
|
|
{ // close enough
|
|
nErr=fnMoveToDestination(oDirect,nAS,fRange);
|
|
if (nErr==1)
|
|
{ // arrived
|
|
oDirect=fnPopObject("Pathing");
|
|
return 1;
|
|
} // arrived
|
|
else
|
|
{ // seek target close to transition first
|
|
oD2=fnRandomObject(GetArea(oDirect),0,oDirect);
|
|
if (GetTransitionTarget(oD2)!=OBJECT_INVALID)
|
|
oD2=fnRandomObject(GetArea(oDirect),0,oDirect);
|
|
nErr=fnMoveToDestination(oD2,nAS,fRange);
|
|
if (nErr==1)
|
|
{ // arrived at mid-point
|
|
fDist=GetDistanceToObject(oDirect);
|
|
if (fDist!=-1.0&&fDist>20.0)
|
|
{ // still not close enough
|
|
nErr=fnMoveToDestination(oDirect,nAS,fRange);
|
|
if (fDist<30.0)
|
|
DelayCommand(3.0,ClearAllActions());
|
|
else if (fDist<50.0)
|
|
DelayCommand(5.0,ClearAllActions());
|
|
else
|
|
DelayCommand(8.0,ClearAllActions());
|
|
} // still not close enough
|
|
} // arrived at mid-point
|
|
else if (nErr==-1)
|
|
{ // mid-point error
|
|
oD2=fnRandomObject(GetArea(oDirect),0,oDirect);
|
|
nErr=fnMoveToDestination(oD2,nAS,fRange);
|
|
if (nErr==-1)
|
|
{ // mid-point 2 fail
|
|
nErr=fnMoveToDestination(oDirect,nAS,fRange);
|
|
} // mid-point 2 fail
|
|
} // mid-point error
|
|
} // seek target close to transition first
|
|
} // close enough
|
|
} // transition target
|
|
else
|
|
{ // not a transition target
|
|
nErr=fnMoveToDestination(oDirect,nAS,fRange);
|
|
if (nErr==1)
|
|
{ // arrived
|
|
oDirect=fnPopObject("Pathing");
|
|
return 1;
|
|
} // arrived
|
|
else if (nErr==-1)
|
|
{ // error
|
|
fnFlushS("Pathing");
|
|
return -1;
|
|
} // error
|
|
} // not a transition target
|
|
} // single destination
|
|
else
|
|
{ // stack of destinations
|
|
oDirect=fnPopObject("Pathing",FALSE);
|
|
fnDebug(" Stack of destinations Current="+GetTag(oDirect));
|
|
if (GetTransitionTarget(oDirect)!=OBJECT_INVALID)
|
|
{ // transition target -- TRANSITION
|
|
fDist=GetDistanceToObject(oDirect);
|
|
if (fDist!=-1.0&&fDist<20.1)
|
|
{ // close enough
|
|
nErr=fnMoveToDestination(oDirect,nAS,fRange);
|
|
if (nErr==1)
|
|
{ // arrived
|
|
oDirect=fnPopObject("Pathing");
|
|
return 0;
|
|
} // arrived
|
|
} // close enough
|
|
else
|
|
{ // seek target close to transition first
|
|
oD2=fnRandomObject(GetArea(oDirect),0,oDirect);
|
|
if (GetTransitionTarget(oD2)!=OBJECT_INVALID)
|
|
oD2=fnRandomObject(GetArea(oDirect),0,oDirect);
|
|
nErr=fnMoveToDestination(oD2,nAS,fRange);
|
|
if (nErr==1)
|
|
{ // arrived at mid-point
|
|
fDist=GetDistanceToObject(oDirect);
|
|
if (fDist!=-1.0&&fDist>20.0)
|
|
{ // still not close enough
|
|
nErr=fnMoveToDestination(oDirect,nAS,fRange);
|
|
if (fDist<30.0)
|
|
DelayCommand(3.0,ClearAllActions());
|
|
else if (fDist<50.0)
|
|
DelayCommand(5.0,ClearAllActions());
|
|
else
|
|
DelayCommand(8.0,ClearAllActions());
|
|
} // still not close enough
|
|
} // arrived at mid-point
|
|
else if (nErr==-1)
|
|
{ // mid-point error
|
|
oD2=fnRandomObject(GetArea(oDirect),0,oDirect);
|
|
nErr=fnMoveToDestination(oD2,nAS,fRange);
|
|
if (nErr==-1)
|
|
{ // mid-point 2 fail
|
|
nErr=fnMoveToDestination(oDirect,nAS,fRange);
|
|
} // mid-point 2 fail
|
|
} // mid-point error
|
|
} // seek target close to transition first
|
|
} // transition target -- TRANSITION
|
|
else
|
|
{ // not transition target -- NON-TRANSITION
|
|
nErr=fnMoveToDestination(oDirect,nAS,fRange);
|
|
if (nErr==1)
|
|
{ // arrived
|
|
oDirect=fnPopObject("Pathing");
|
|
return 0;
|
|
} // arrived
|
|
else if (nErr==-1)
|
|
{ // error - cannot reach
|
|
fnFlushS("Pathing");
|
|
return -1;
|
|
} // error - cannot reach
|
|
} // not transition target -- NON-TRANSITION
|
|
} // stack of destinations
|
|
return nRet;
|
|
} // fnMovePath()
|
|
|
|
/*
|
|
void fnMakeSureNotImpossible(object oDest,float fRange)
|
|
{
|
|
float fDist=GetDistanceToObject(oDest);
|
|
object oMe=OBJECT_SELF;
|
|
if (fDist>fRange)
|
|
{ // not possible
|
|
SetLocalInt(oMe,"nWanderCount",0);
|
|
SetLocalFloat(oMe,"fDistR",fDist);
|
|
SetLocalInt(oMe,"nASC",1000);
|
|
} // not possible
|
|
} // fnMakeSureNotImpossible() */
|
|
|
|
/* int fnMoveToDestination(object oDest,int nAS=TRUE,float fRange=2.5)
|
|
{ // Move to destination and anti-stuck procedures
|
|
// RETURN VALUES 0 = moving, 1 = appear to have arrived, -1 = unreachable
|
|
int nRet=0;
|
|
object oMe=OBJECT_SELF;
|
|
object oNear=GetLocalObject(oMe,"oNear");
|
|
int nASC=GetLocalInt(oMe,"nASC"); // Anti-Stuck counter
|
|
float fDistR=GetLocalFloat(oMe,"fDistR"); // distance recorded last time
|
|
float fDist=GetDistanceToObject(oDest);
|
|
int nRun=GetLocalInt(oMe,"nRun");
|
|
object oBackup;
|
|
int nSpeedB=0;
|
|
int nWander=GetLocalInt(oMe,"nWander");
|
|
int nR=GetMovementRate(oMe);
|
|
int nWC=GetLocalInt(oMe,"nWanderCount");
|
|
nSpeedB=7-nR;
|
|
if (oNear!=OBJECT_INVALID&&fDist!=-1.0)
|
|
DeleteLocalObject(oMe,"oNear");
|
|
if (nSpeedB<1) nSpeedB=0;
|
|
if (nWander!=TRUE)
|
|
{ // not currently wandering
|
|
SendMessageToPC(GetFirstPC(),"fnMoveToDestination:"+GetTag(oDest)+" nASC:"+IntToString(nASC)+" fDist:"+FloatToString(fDist)+" fDistR:"+FloatToString(fDistR)+" nSpeedB:"+IntToString(nSpeedB));
|
|
if (GetTransitionTarget(oDest)!=OBJECT_INVALID)
|
|
{ // this is a transition object
|
|
oBackup=GetNearestObject(OBJECT_TYPE_ALL,oDest,1);
|
|
if (GetTransitionTarget(oBackup)!=OBJECT_INVALID)
|
|
oBackup=GetNearestObject(OBJECT_TYPE_ALL,oDest,2);
|
|
if (GetTransitionTarget(oBackup)!=OBJECT_INVALID)
|
|
oBackup=GetNearestObject(OBJECT_TYPE_ALL,oDest,3);
|
|
if (oBackup!=OBJECT_INVALID)
|
|
{ // destination
|
|
oDest=oBackup;
|
|
} // destination
|
|
else
|
|
return -1;
|
|
fDist=GetDistanceToObject(oDest);
|
|
} // this is a transition object
|
|
if (fDist!=-1.0&&fDist==fDistR&&nAS==TRUE)
|
|
{ // appears to be stuck
|
|
nASC++;
|
|
if (nASC>5+nSpeedB)
|
|
{ // extreme anti-stuck cannot reach destination
|
|
SetLocalObject(oMe,"oDest",OBJECT_INVALID);
|
|
SetLocalInt(oMe,"nASC",0);
|
|
SetLocalInt(oMe,"nWanderCount",0);
|
|
return -1; // signal failure to reach area
|
|
} // extreme anti-stuck cannot reach destination
|
|
else if (nASC>3+nSpeedB)
|
|
{ // badly stuck - teleport
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,JumpToObject(oDest));
|
|
SetLocalInt(oMe,"nWanderCount",0);
|
|
SetLocalFloat(oMe,"fDistR",GetDistanceToObject(oDest));
|
|
DelayCommand(2.5,fnMakeSureNotImpossible(oDest,fRange));
|
|
} // badly stuck - teleport
|
|
else if (nASC>1+nSpeedB)
|
|
{ // pick a nearby target to move to
|
|
if (nWC<2)
|
|
{ // only 2 wander tries
|
|
if (nWander!=TRUE)
|
|
{ // pick target
|
|
oBackup=fnRandomObject(GetArea(oMe));
|
|
if (oBackup==OBJECT_INVALID)
|
|
{ // pick our own
|
|
oBackup=GetNearestObject(OBJECT_TYPE_ALL,oMe,d6());
|
|
if (GetTransitionTarget(oBackup)!=OBJECT_INVALID)
|
|
oBackup=OBJECT_INVALID;
|
|
} // pick our own
|
|
if (oBackup!=OBJECT_INVALID)
|
|
{ // valid object
|
|
SetLocalInt(oMe,"nWander",TRUE);
|
|
nWC++;
|
|
SetLocalInt(oMe,"nWanderCount",nWC);
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,ActionMoveToObject(oBackup,nRun,3.0));
|
|
nR=4+d4();
|
|
DelayCommand(IntToFloat(nR),AssignCommand(oMe,ClearAllActions()));
|
|
DelayCommand(IntToFloat(nR)+0.1,AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange)));
|
|
DelayCommand(IntToFloat(nR)+4.0,SetLocalInt(oMe,"nWander",FALSE));
|
|
} // valid object
|
|
SetLocalFloat(oMe,"fDistR",fDist);
|
|
} // pick target
|
|
} // only 2 wander tries
|
|
else
|
|
{ // abort - set to teleport time
|
|
SetLocalFloat(oMe,"fDistR",GetDistanceToObject(oDest));
|
|
SetLocalInt(oMe,"nASC",3+nSpeedB);
|
|
} // abort - set to teleport time
|
|
} // pick a nearby target to move to
|
|
else if (nASC==2)
|
|
{ // kick start - move to object
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange));
|
|
SetLocalFloat(oMe,"fDistR",fDist);
|
|
} // kick start - move to object
|
|
else
|
|
{ // move to object
|
|
AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange));
|
|
SetLocalFloat(oMe,"fDistR",fDist);
|
|
} // move to object
|
|
SetLocalInt(oMe,"nASC",nASC);
|
|
} // appears to be stuck
|
|
else if (nAS!=TRUE&&fDist!=-1.0&&fDist==fDistR)
|
|
{ // Might be stuck but, don't use anti-stuck
|
|
AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange));
|
|
} // Might be stuck but, don't use anti-stuck
|
|
else if (fDist==-1.0)
|
|
{ // different area
|
|
if (oNear==OBJECT_INVALID)
|
|
{ // near
|
|
oNear=GetNearestObject(OBJECT_TYPE_ALL,oMe,1);
|
|
fDist=GetDistanceToObject(oNear);
|
|
if (fDist!=fDistR)
|
|
{ // seems to be moving
|
|
AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange));
|
|
SetLocalInt(oMe,"nASC",0);
|
|
SetLocalFloat(oMe,"fDistR",fDist);
|
|
} // seems to be moving
|
|
else
|
|
{ // might be stuck
|
|
nASC++;
|
|
SetLocalInt(oMe,"nASC",nASC);
|
|
if (nASC>3)
|
|
{ // abort this
|
|
return -1;
|
|
} // abort this
|
|
else if (nASC==2)
|
|
{ // kickstart
|
|
AssignCommand(oMe,ClearAllActions());
|
|
AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange));
|
|
} // kickstart
|
|
else
|
|
{ // kickstart
|
|
AssignCommand(oMe,ActionMoveToObject(oDest,nRun,fRange));
|
|
} // kickstart
|
|
} // might be stuck
|
|
} // near
|
|
|
|
} // different area
|
|
else
|
|
{ // not stuck
|
|
SetLocalInt(oMe,"nASC",0);
|
|
SetLocalFloat(oMe,"fDistR",fDist);
|
|
if (fDist!=-1.0&&fDist<(fRange+0.1))
|
|
{ // arrived
|
|
SetLocalInt(oMe,"nWanderCount",0);
|
|
return 1;
|
|
} // arrived
|
|
} // not stuck
|
|
} // not currently wandering
|
|
return nRet;
|
|
} // fnMoveToDestination() */
|
|
|
|
/*
|
|
int fnBuildPath(object oEnd,int nMax=10)
|
|
{ // build a reverse path between this location and me
|
|
int nRet=FALSE;
|
|
object oMe=OBJECT_SELF;
|
|
int nX;
|
|
int nR;
|
|
int nDepth=0;
|
|
object oAt=oEnd;
|
|
object oPrev;
|
|
object oH1;
|
|
object oH2;
|
|
fnPush("Pathing","",0,oEnd); // put end point on stack
|
|
oH1=fnDirectConnection(GetArea(oEnd),GetArea(oMe));
|
|
if (oH1!=OBJECT_INVALID)
|
|
{ // direct connection
|
|
oH2=GetTransitionTarget(oH1);
|
|
fnPush("Pathing","",0,oH2);
|
|
fnPush("Pathing","",0,GetNearestObject(OBJECT_TYPE_ALL,oH1,1));
|
|
return TRUE;
|
|
} // direct connection
|
|
while(oAt!=oMe&&nDepth<=nMax)
|
|
{ // traverse nodes
|
|
oH1=fnDirectConnection(GetArea(oAt),GetArea(oMe));
|
|
if (oH1!=OBJECT_INVALID)
|
|
{ // direct connection
|
|
oH1=GetTransitionTarget(oH1);
|
|
fnPush("Pathing","",0,oH1);
|
|
return TRUE;
|
|
} // direct connection
|
|
oH1=fnRandomTrans(GetArea(oAt));
|
|
oH1=GetTransitionTarget(oH1);
|
|
nX=0;
|
|
while (GetArea(oH1)==oPrev&&nX<3)
|
|
{ // keep looking
|
|
oH1=fnRandomTrans(GetArea(oAt));
|
|
oH1=GetTransitionTarget(oH1);
|
|
nX++;
|
|
} // keep looking
|
|
if (GetArea(oH1)!=oPrev)
|
|
{ // found a direction to try
|
|
oPrev=GetArea(oAt);
|
|
fnPush("Pathing","",0,oH1);
|
|
oAt=oH1;
|
|
nDepth++;
|
|
} // found a direction to try
|
|
} // traverse nodes
|
|
return nRet;
|
|
} // fnBuildPath()
|
|
|
|
void fnFindPathTo(object oDest,int nMaxNodes=10)
|
|
{ // find a path
|
|
object oMe=OBJECT_SELF;
|
|
object oMyArea=GetArea(oMe);
|
|
object oDestArea=GetArea(oDest);
|
|
int nFoundPath=FALSE;
|
|
int nPathing=GetLocalInt(oMe,"nPathing");
|
|
if (nPathing!=TRUE)
|
|
{ // starting pathing
|
|
fnFlushS("Pathing");
|
|
fnInitializeStack("Pathing",3); // object stack
|
|
if (oMyArea==oDestArea)
|
|
{ // same area - no pathing needed
|
|
SendMessageToPC(GetFirstPC(),"Pathing: Same Area");
|
|
fnPush("Pathing","",0,oDest);
|
|
nFoundPath=TRUE;
|
|
} // same area - no pathing needed
|
|
else if (fnDirectConnection(oMyArea,oDestArea)!=OBJECT_INVALID)
|
|
{ // there is a direct link - no pathing needed
|
|
fnPush("Pathing","",0,GetNearestObject(OBJECT_TYPE_ALL,oDest,1));
|
|
fnPush("Pathing","",0,oDest);
|
|
SendMessageToPC(GetFirstPC(),"Pathing: Direct link");
|
|
nFoundPath=TRUE;
|
|
} // there is a direct link - no pathing needed
|
|
else
|
|
{ // build a path
|
|
SendMessageToPC(GetFirstPC(),"Call fnBuildPath");
|
|
nFoundPath=fnBuildPath(oDest,nMaxNodes);
|
|
} // build a path
|
|
if (nFoundPath==TRUE)
|
|
SetLocalInt(oMe,"nPathing",FALSE);
|
|
} // starting pathing
|
|
else
|
|
{ // still looking
|
|
SendMessageToPC(GetFirstPC(),"Pathing: Still calling fnBuildPath");
|
|
nFoundPath=fnBuildPath(oDest,nMaxNodes);
|
|
if (nFoundPath==TRUE)
|
|
SetLocalInt(oMe,"nPathing",FALSE);
|
|
} // still looking
|
|
} // fnFindPathTo()
|
|
|
|
int fnMovePath(object oDest,int nAS,float fRange=2.5)
|
|
{ // move following a path - this is the main movement function
|
|
int nRet=0;
|
|
object oMe=OBJECT_SELF;
|
|
object oDirect;
|
|
int nErr;
|
|
int nTop=fnSizeS("Pathing");
|
|
float fDist=GetDistanceToObject(oDest);
|
|
SendMessageToPC(GetFirstPC(),"fnMovePath("+GetTag(oDest)+",AS:"+IntToString(nAS)+",RANGE:"+FloatToString(fRange)+")");
|
|
if (nTop==0)
|
|
{ // no path
|
|
if (fDist==-1.0||fDist>fRange)
|
|
{ // build a path
|
|
SendMessageToPC(GetFirstPC(),"Building path");
|
|
fnFindPathTo(oDest);
|
|
nErr=fnMovePath(oDest,nAS,fRange);
|
|
if (nErr==-1) return -1;
|
|
} // build a path
|
|
else
|
|
{ // we have arrived
|
|
return 1;
|
|
} // we have arrived
|
|
} // no path
|
|
else if (nTop==1)
|
|
{ // direct link
|
|
oDirect=fnPopObject("Pathing",FALSE);
|
|
fDist=GetDistanceToObject(oDirect);
|
|
if (fDist!=-1.0&&fDist<=fRange)
|
|
{ // arrived
|
|
SendMessageToPC(GetFirstPC(),"We arrived");
|
|
oDirect=fnPopObject("Pathing");
|
|
nRet=fnMovePath(oDest,nAS,fRange);
|
|
} // arrived
|
|
else
|
|
{ // move
|
|
SendMessageToPC(GetFirstPC(),"Call Move");
|
|
nErr=fnMoveToDestination(oDirect,nAS,fRange);
|
|
if (nErr==-1)
|
|
{ // error
|
|
return nErr;
|
|
} // error
|
|
} // move
|
|
} // direct link
|
|
else
|
|
{ // complex path
|
|
SendMessageToPC(GetFirstPC(),"Complex path");
|
|
oDirect=fnPopObject("Pathing",FALSE);
|
|
fDist=GetDistanceToObject(oDirect);
|
|
if (fDist!=-1.0&&fDist<=fRange)
|
|
{ // arrived at that node
|
|
oDirect=fnPopObject("Pathing");
|
|
nRet=fnMovePath(oDest,nAS,fRange);
|
|
} // arrived at that node
|
|
else
|
|
{ // move
|
|
nErr=fnMoveToDestination(oDirect,nAS,fRange);
|
|
if (nErr==-1)
|
|
{ // cannot reach node
|
|
fnFlushS("Pathing");
|
|
nRet=nErr;
|
|
} // cannot reach node
|
|
} // move
|
|
} // complex path
|
|
return nRet;
|
|
} // fnMovePath() */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|