/////////////////////////////////////////////////////////////////////////////////// // Real Time Strategy - NWN - Complex Path Finding Made Easy //================================================================================ // By Deva Bryson Winblood. 03/09/2003 /////////////////////////////////////////////////////////////////////////////////// // required: each area have a waypoint with tag FIND_NODE // NAME must be setup as follows x.y.z.UP.DOWN.NORTH.EAST.SOUTH.WEST // where x,y,z is a number 0 and up. The directions are replaced by the tag // of the area that leads to. This waypoint will be used by units for // pathing. int MAX_SEARCH = 15; int DIR_UP = 1; int DIR_DOWN = 2; int DIR_NORTH = 3; int DIR_EAST = 4; int DIR_SOUTH = 5; int DIR_WEST = 6; void fnError(string sE) { // quick error message SendMessageToPC(GetFirstPC(),sE); PrintString(sE); } // fnError() string fnParsePeriod(string sIn) { // Parse period string sH=sIn; string sR=""; while (GetStringLength(sH)>0&&GetStringLeft(sH,1)!=".") { // build string sR=sR+GetStringLeft(sH,1); sH=GetStringRight(sH,GetStringLength(sH)-1); } // build string return sR; } // fnParsePeriod() string fnRemParsed(string sO, string sP) { // Remove parsed portion from sO string sR=""; if (GetStringLength(sO)>=GetStringLength(sP)) sR=GetStringRight(sO,GetStringLength(sO)-GetStringLength(sP)); if (GetStringLeft(sR,1)==".") sR=GetStringRight(sR,GetStringLength(sR)-1); return sR; } // fnRemParsed() string fnCanGoDirection(object oArea,int nDir) { // is their a direction for this returns NA if false string sMNDown; // down destination string sMNUp; // up destination string sMNNorth; // North destination string sMNSouth; // South destination string sMNEast; // East destination string sMNWest; // West destination string x,y,z; // coordinates object oNode=GetNearestObjectByTag("FIND_NODE",oArea); string sName=GetName(oNode); x=fnParsePeriod(sName); sName=fnRemParsed(sName,x); y=fnParsePeriod(sName); sName=fnRemParsed(sName,y); z=fnParsePeriod(sName); sName=fnRemParsed(sName,z); sMNUp=fnParsePeriod(sName); sName=fnRemParsed(sName,sMNUp); sMNDown=fnParsePeriod(sName); sName=fnRemParsed(sName,sMNDown); sMNNorth=fnParsePeriod(sName); sName=fnRemParsed(sName,sMNNorth); sMNEast=fnParsePeriod(sName); sName=fnRemParsed(sName,sMNEast); sMNSouth=fnParsePeriod(sName); sName=fnRemParsed(sName,sMNSouth); sMNWest=fnParsePeriod(sName); sName=fnRemParsed(sName,sMNWest); if (nDir==DIR_UP&&sMNUp!="NA"&&GetStringLength(sMNUp)>1) return sMNUp; else if (nDir==DIR_DOWN&&sMNDown!="NA"&&GetStringLength(sMNDown)>1) return sMNDown; else if (nDir==DIR_NORTH&&sMNNorth!="NA"&&GetStringLength(sMNNorth)>1) return sMNNorth; else if (nDir==DIR_EAST&&sMNEast!="NA"&&GetStringLength(sMNEast)>1) return sMNEast; else if (nDir==DIR_SOUTH&&sMNSouth!="NA"&&GetStringLength(sMNSouth)>1) return sMNSouth; else if (nDir==DIR_WEST&&sMNWest!="NA"&&GetStringLength(sMNWest)>1) return sMNWest; return "NA"; } // fnCanGoDirection() int fnAreaWhichDirection(object oArea,object oDest) { // returns the direction to be taken to the area specified // return of 0 = already there int nRet=0; string x,y,z; string dx,dy,dz; string sName=GetName(oArea); int nx,ny,nz; int ndx,ndy,ndz; object oHandle; x=fnParsePeriod(sName); sName=fnRemParsed(sName,x); y=fnParsePeriod(sName); sName=fnRemParsed(sName,y); z=fnParsePeriod(sName); sName=GetName(oDest); dx=fnParsePeriod(sName); sName=fnRemParsed(sName,dx); dy=fnParsePeriod(sName); sName=fnRemParsed(sName,dy); dz=fnParsePeriod(sName); nx=StringToInt(x); ny=StringToInt(y); nz=StringToInt(z); ndx=StringToInt(dx); ndy=StringToInt(dy); ndz=StringToInt(dz); if (oArea!=oDest) { // separate areas if (nz!=ndz) { // deal with Z axis first if (ndz>nz) { // need to go up if (fnCanGoDirection(oArea,DIR_UP)!="NA") return DIR_UP; else { // find nearby up location sName=fnCanGoDirection(oArea,DIR_NORTH); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_UP)!="NA") return DIR_NORTH; } // test this direction sName=fnCanGoDirection(oArea,DIR_SOUTH); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_UP)!="NA") return DIR_SOUTH; } // test this direction sName=fnCanGoDirection(oArea,DIR_EAST); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_UP)!="NA") return DIR_EAST; } // test this direction sName=fnCanGoDirection(oArea,DIR_WEST); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_UP)!="NA") return DIR_WEST; } // test this direction return d6(); // random direction } // find nearby up location } // need to go up else { // need to go down if (fnCanGoDirection(oArea,DIR_DOWN)!="NA") return DIR_DOWN; else { // find nearby down location sName=fnCanGoDirection(oArea,DIR_NORTH); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_DOWN)!="NA") return DIR_NORTH; } // test this direction sName=fnCanGoDirection(oArea,DIR_SOUTH); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_DOWN)!="NA") return DIR_SOUTH; } // test this direction sName=fnCanGoDirection(oArea,DIR_EAST); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_DOWN)!="NA") return DIR_EAST; } // test this direction sName=fnCanGoDirection(oArea,DIR_WEST); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_DOWN)!="NA") return DIR_WEST; } // test this direction return d6(); // random direction } // find nearby down location } // need to go down } // deal with Z axis first else if (nx!=ndx) { // deal with X axis if (ndx>nx) { // need to go east if (fnCanGoDirection(oArea,DIR_EAST)!="NA") return DIR_EAST; else { // find nearby east location sName=fnCanGoDirection(oArea,DIR_NORTH); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_EAST)!="NA") return DIR_NORTH; } // test this direction sName=fnCanGoDirection(oArea,DIR_SOUTH); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_EAST)!="NA") return DIR_SOUTH; } // test this direction sName=fnCanGoDirection(oArea,DIR_UP); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_EAST)!="NA") return DIR_UP; } // test this direction sName=fnCanGoDirection(oArea,DIR_DOWN); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_EAST)!="NA") return DIR_DOWN; } // test this direction return d6(); // random direction } // find nearby east location } // need to go east else { // need to go west if (fnCanGoDirection(oArea,DIR_WEST)!="NA") return DIR_WEST; else { // find nearby west location sName=fnCanGoDirection(oArea,DIR_NORTH); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_WEST)!="NA") return DIR_NORTH; } // test this direction sName=fnCanGoDirection(oArea,DIR_SOUTH); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_WEST)!="NA") return DIR_SOUTH; } // test this direction sName=fnCanGoDirection(oArea,DIR_UP); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_WEST)!="NA") return DIR_UP; } // test this direction sName=fnCanGoDirection(oArea,DIR_DOWN); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_WEST)!="NA") return DIR_DOWN; } // test this direction return d6(); // random direction } // find nearby west location } // need to go west } // deal with X axis else if (ny!=ndy) { // deal with Y axis if (ndy>ny) { // need to go south if (fnCanGoDirection(oArea,DIR_SOUTH)!="NA") return DIR_SOUTH; else { // find nearby south location sName=fnCanGoDirection(oArea,DIR_EAST); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_SOUTH)!="NA") return DIR_EAST; } // test this direction sName=fnCanGoDirection(oArea,DIR_WEST); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_SOUTH)!="NA") return DIR_WEST; } // test this direction sName=fnCanGoDirection(oArea,DIR_UP); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_SOUTH)!="NA") return DIR_UP; } // test this direction sName=fnCanGoDirection(oArea,DIR_DOWN); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_SOUTH)!="NA") return DIR_DOWN; } // test this direction return d6(); // random direction } // find nearby south location } // need to go south else { // need to go north if (fnCanGoDirection(oArea,DIR_NORTH)!="NA") return DIR_NORTH; else { // find nearby north location sName=fnCanGoDirection(oArea,DIR_EAST); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_NORTH)!="NA") return DIR_EAST; } // test this direction sName=fnCanGoDirection(oArea,DIR_WEST); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_NORTH)!="NA") return DIR_WEST; } // test this direction sName=fnCanGoDirection(oArea,DIR_UP); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_NORTH)!="NA") return DIR_UP; } // test this direction sName=fnCanGoDirection(oArea,DIR_DOWN); oHandle=GetObjectByTag(sName); if (oHandle!=OBJECT_INVALID) { // test this direction if(fnCanGoDirection(oHandle,DIR_NORTH)!="NA") return DIR_DOWN; } // test this direction return d6(); // random direction } // find nearby north location } // need to go north } // deal with Y axis else return -1; // error } // separate areas return nRet; } // fnAreaWhichDirection() int fnDirectPathBetween(object oStart,object oEnd) { // find out if there is a direct route between oStart and oEnd int nRet=FALSE; object oDoor; object oTrans; int nC=1; oDoor=GetNearestObject(OBJECT_TYPE_DOOR,oStart,nC); while(oDoor!=OBJECT_INVALID&&nRet==FALSE) { // check doors nC++; oTrans=GetTransitionTarget(oDoor); if (oTrans!=OBJECT_INVALID) { if (GetArea(oTrans)==oEnd) nRet=TRUE; } oDoor=GetNearestObject(OBJECT_TYPE_DOOR,oStart,nC); } // check doors nC=1; oDoor=GetNearestObject(OBJECT_TYPE_TRIGGER,oStart,nC); while(oDoor!=OBJECT_INVALID&&nRet==FALSE) { // check doors nC++; oTrans=GetTransitionTarget(oDoor); if (oTrans!=OBJECT_INVALID) { if (GetArea(oTrans)==oEnd) nRet=TRUE; } oDoor=GetNearestObject(OBJECT_TYPE_TRIGGER,oStart,nC); } // check doors return nRet; } // fnDirectPathBetween() void fnSneak() { // sneaky method } // fnSneak() //====================================================== MAIN ================== void main() { object oMe=OBJECT_SELF; object oMDest=GetLocalObject(oMe,"oMainDest"); // main destination object oDest=GetLocalObject(oMe,"oDestWP"); // current destination int nMM=GetLocalInt(oMe,"nMoveMethod"); // 0 = whatever RUN is set at 1= sneak int nRun=GetLocalInt(oMe,"nRun"); // FALSE = WALK, TRUE = RUN int nWalk=GetLocalInt(oMe,"nWalkDir"); // move in specific direction object oCMNode=GetNearestObjectByTag("FIND_NODE",GetArea(oMe)); // local map node object oDMNode=GetNearestObjectByTag("FIND_NODE",GetArea(oMDest)); int nDir; string cx,cy,cz; // current X,Y,Z coordinates string dx,dy,dz; // destination X,Y,Z coordinates string sMNDown; // down destination string sMNUp; // up destination string sMNNorth; // North destination string sMNSouth; // South destination string sMNEast; // East destination string sMNWest; // West destination string sName; // name stored for FIND_NODES string sParse; // used for parsing float fDist=GetDistanceBetween(oDest,oMe); float fAS=GetLocalFloat(oMe,"fAS"); int nASC=GetLocalInt(oMe,"nASC"); sName=GetName(oCMNode); ////// Get X,Y,Z coordinates cx=fnParsePeriod(sName); sName=fnRemParsed(sName,cx); cy=fnParsePeriod(sName); sName=fnRemParsed(sName,cy); cz=fnParsePeriod(sName); ////// End X,Y,Z get if (nMM==1) fnSneak(); if (GetArea(oDest)==GetArea(oMe)||fnDirectPathBetween(GetArea(oDest),GetArea(oMe))) { // same area if (fDist<3.0&&fDist!=0.0) { // we are there if (oDest==oMDest) { // final destination reached SetLocalObject(oMe,"oMainDest",OBJECT_INVALID); } // final destination reached else { // set new destination SetLocalObject(oMe,"oDestWP",oMDest); } // set new destination } // we are there else { // check for anti-stuck if (fAS==fDist&&fDist!=0.0) { // possibly stuck nASC++; if (nASC<5) { // gentle prod AssignCommand(oMe,ActionMoveToObject(oDest,nRun,2.0)); } // gentle prod else if (nASC==5) { // simple anti-stuck oDest=GetNearestObject(OBJECT_TYPE_WAYPOINT,oMe,d4()); AssignCommand(oMe,ClearAllActions()); AssignCommand(oMe,ActionMoveToObject(oDest,nRun,3.0)); DelayCommand(10.0,AssignCommand(oMe,ClearAllActions())); } // simple anti-stuck else if (nASC==10) { // really stuck TELEPORT AssignCommand(oMe,ClearAllActions()); AssignCommand(oMe,JumpToObject(oDest)); } // really stuck TELEPORT else if (nASC>12) { // get unstuck - just do it oDest=GetNearestObject(OBJECT_TYPE_WAYPOINT,oMe,d8()); AssignCommand(oMe,ClearAllActions()); AssignCommand(oMe,JumpToObject(oDest)); } // get unstuck - just do it SetLocalInt(oMe,"nASC",nASC); } // possibly stuck else { // set distance for future AS and set AS counter to 0 SetLocalInt(oMe,"nASC",0); SetLocalFloat(oMe,"fAS",fDist); AssignCommand(oMe,ActionMoveToObject(oDest,nRun,2.0)); } // set distance for future AS and set AS counter to 0 } // check for anti-stuck } // same area else { // different area fDist=GetDistanceBetween(oMe,oCMNode); if (fDist==fAS) { // possibly stuck nASC++; if (nASC==5) { // simple anti-stuck oDest=GetNearestObject(OBJECT_TYPE_WAYPOINT,oMe,d4()); AssignCommand(oMe,ClearAllActions()); AssignCommand(oMe,ActionMoveToObject(oDest,nRun,3.0)); DelayCommand(10.0,AssignCommand(oMe,ClearAllActions())); } // simple anti-stuck else if (nASC==10) { // really stuck TELEPORT AssignCommand(oMe,ClearAllActions()); AssignCommand(oMe,JumpToObject(oDest)); } // really stuck TELEPORT else if (nASC>12) { // get unstuck - just do it oDest=GetNearestObject(OBJECT_TYPE_WAYPOINT,oMe,d8()); AssignCommand(oMe,ClearAllActions()); AssignCommand(oMe,JumpToObject(oDest)); } // get unstuck - just do it SetLocalInt(oMe,"nASC",nASC); } // possibly stuck else { // not stuck SetLocalInt(oMe,"nASC",0); SetLocalFloat(oMe,"fAS",fDist); nDir=fnAreaWhichDirection(oCMNode,oDMNode); if (nDir==0) { // should be there SetLocalObject(oMe,"oDestWP",oMDest); } // should be there else if (nDir==-1) { // error } // error else { // go that way sName=fnCanGoDirection(GetArea(oMe),nDir); oDest=GetObjectByTag(sName); if (oDest!=OBJECT_INVALID) { // go to that area oMDest=GetNearestObject(OBJECT_TYPE_WAYPOINT,oDest,d8()); while(oMDest==OBJECT_INVALID) oMDest=GetNearestObject(OBJECT_TYPE_WAYPOINT,oDest,d8()); SetLocalObject(oMe,"oDestWP",oMDest); } // go to that area else { fnError(GetName(GetArea(oMe))+" pathing error direction: "+IntToString(nDir)); } } // go that way } // not stuck } // different area }