/////////////////////////////////////////////////////////////////////////////////// // 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)(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() */