//////////////////////////////////////////////////////////////////////////////// // lib_hos2_shpath - Short Pathing AI // By Deva B. Winblood. April 7th, 2008 //////////////////////////////////////////////////////////////////////////////// const int MOVE_RESULT_MOVING = 0; const int MOVE_RESULT_ARRIVED = 1; const int MOVE_RESULT_UNREACHABLE = -1; ////////////////////////////////////// // PROTOTYPES ////////////////////////////////////// // FILE: lib_hos2_shpath FUNCTION: MoveToLocation() // This will move oNPC to the location lLoc and uses a proxy object for distance int MoveToLocation(object oNPC,location lLoc,int bRun=FALSE,float fRange=5.0); // FILE: lib_hos2_shpath FUNCTION: MoveToObject() // This will move oNPC to oTarget and will return -1 if object is unreachable // or 1 if it has made it within range or 0 if it is still working on getting // within range. int MoveToObject(object oNPC,object oTarget,int bRun=FALSE,float fRange=5.0); // FILE: lib_hos2_shpath FUNCTION: MoveTransition() // This will move oNPC to an area near oDestination void MoveTransition(object oNPC,object oDestination); int fnGetTransitionCount(int bAboveGround); object fnGetRandomTransition(int bAboveGround,object oFor=OBJECT_INVALID,int bLightFatal=FALSE); // FILE: lib_hos2_shpath FUNCTION: MoveGetRandomLocation() // Get a Random Destination within nRange of lCenter. nRange is the max // deviation in x and y coordinates allowed. location MoveGetRandomLocation(location lCenter,int nRange=10); // FILE: lib_hos2_shpath FUNCTION: MoveToAndUseTransition() // This will move oNPC to within fRange of oTransition and then will transition. // This function does not return any feedback so, it should only be used by // routines that know when they have reached where they are trying to go. void MoveToAndUseTransition(object oNPC,object oTransition,float fRange=4.0); ////////////////////////////////////// // FUNCTIONS ////////////////////////////////////// location MoveGetRandomLocation(location lCenter,int nRange=10) { // PURPOSE: Return a Random Location location lRet=lCenter; vector vCenter=GetPositionFromLocation(lCenter); int nCX=FloatToInt(vCenter.x); int nCY=FloatToInt(vCenter.y); int nMinX=nCX-nRange; int nMinY=nCY-nRange; int nMaxX=nCX+nRange; int nMaxY=nCY+nRange; object oArea=GetAreaFromLocation(lCenter); int nAreaX=GetAreaSize(AREA_WIDTH,oArea)*10; int nAreaY=GetAreaSize(AREA_HEIGHT,oArea)*10; int nRollX; int nRollY; //SendMessageToPC(GetFirstPC(),"[lib_hos2_shpath]MoveGetRandomLocation("+IntToString(nRange)+") Center X:"+IntToString(nCX)+" Center Y:"+IntToString(nCY)+" nArea(x:"+IntToString(nAreaX)+", y:"+IntToString(nAreaY)+")"); if (nMinX<1) nMinX=1; if (nMinY<1) nMinY=1; if (nMaxX>=nAreaX) nMaxX=nAreaX-1; if (nMaxY>=nAreaY) nMaxY=nAreaY-1; nCX=nMaxX-nMinX; nCY=nMaxY-nMinY; //SendMessageToPC(GetFirstPC()," nMinX:"+IntToString(nMinX)+" nMaxX:"+IntToString(nMaxX)+" nDiffX:"+IntToString(nCX)+" nMinY:"+IntToString(nMinY)+" nMaxY:"+IntToString(nMaxY)+" nDiffY:"+IntToString(nCY)); nRollX=nMinX+Random(nCX); nRollY=nMinY+Random(nCY); //SendMessageToPC(GetFirstPC()," nRollX:"+IntToString(nRollX)+", nRollY:"+IntToString(nRollY)); vCenter.x=IntToFloat(nRollX); vCenter.y=IntToFloat(nRollY); lRet=Location(oArea,vCenter,IntToFloat(Random(360))); return lRet; } // MoveGetRandomLocation() int MoveToLocation(object oNPC,location lLoc,int bRun=FALSE,float fRange=5.0) { // PURPOSE: Move to lLocation and use an object as a proxy since pathing to // objects handles pathing better using the Engine object oShortPathDest=GetLocalObject(oNPC,"oShortPathDest"); float fLast; float fDist=GetDistanceBetween(oNPC,oShortPathDest); location lDest=GetLocalLocation(oNPC,"lShortPathDest"); if (lDest!=lLoc) { // new if (GetIsObjectValid(oShortPathDest)) DestroyObject(oShortPathDest); AssignCommand(oNPC,ClearAllActions()); // stop trying to reach old path } // new if (!GetIsObjectValid(oShortPathDest)||fDist<1.0) { // create pathing object if (GetIsObjectValid(oShortPathDest)) DestroyObject(oShortPathDest); oShortPathDest=CreateObject(OBJECT_TYPE_PLACEABLE,"pathing_object",lLoc); if (!GetIsObjectValid(oShortPathDest)) return -1; SetLocalObject(oShortPathDest,"oOwner",oNPC); SetLocalObject(oNPC,"oShortPathDest",oShortPathDest); fDist=GetDistanceBetween(oNPC,oShortPathDest); SetLocalLocation(oNPC,"lShortPathDest",lLoc); } // create pathing object if (fDist<=fRange) { // arrived DestroyObject(oShortPathDest); DeleteLocalFloat(oNPC,"fLastShortPath"); DeleteLocalInt(oNPC,"nASCShortPath"); return 1; } // arrived fLast=GetLocalFloat(oNPC,"fLastShortPath"); if (fLast!=0.0&&fLast<=fDist) { // stuck int nASC=GetLocalInt(oNPC,"nASCShortPath"); nASC++; //SendMessageToPC(GetFirstPC()," Stuck:"+IntToString(nASC)); if (nASC<2) { // try again SetLocalInt(oNPC,"nASCShortPath",nASC); AssignCommand(oNPC,ClearAllActions(TRUE)); AssignCommand(oNPC,ActionMoveToObject(oShortPathDest,TRUE,fRange-0.5)); } // try again else if (nASC==2) { // try moving away SetLocalInt(oNPC,"nASCShortPath",nASC); AssignCommand(oNPC,ClearAllActions(TRUE)); AssignCommand(oNPC,ActionMoveAwayFromObject(oShortPathDest,TRUE,fDist+2.0)); AssignCommand(oNPC,ActionMoveToObject(oShortPathDest,TRUE,fRange-0.5)); } // try moving away else if (nASC==3) { // move towards one more time SetLocalInt(oNPC,"nASCShortPath",nASC); AssignCommand(oNPC,ClearAllActions(TRUE)); AssignCommand(oNPC,ActionMoveToObject(oShortPathDest,TRUE,fRange-0.5)); } // move towards one more time else { // cannot reach DestroyObject(oShortPathDest); DeleteLocalFloat(oNPC,"fLastShortPath"); DeleteLocalInt(oNPC,"nASCShortPath"); return -1; } // cannot reach } // stuck else { // move AssignCommand(oNPC,ClearAllActions(TRUE)); AssignCommand(oNPC,ActionMoveToObject(oShortPathDest,bRun,fRange-0.5)); SetLocalFloat(oNPC,"fLastShortPath",fDist); DeleteLocalInt(oNPC,"nASCShortPath"); } // move return 0; } // MoveToLocation() void VectorMove(object oNPC,object oMoveAfter,float fRange) { // PURPOSE: Move to a random Vector vector vPos=GetPosition(oNPC); int nRX=Random(601)-300; int nRY=Random(601)-300; float fRX=IntToFloat(nRX)/10.0; float fRY=IntToFloat(nRY)/10.0; vPos.x=vPos.x+fRX; vPos.y=vPos.y+fRY; location lLoc=Location(GetArea(oNPC),vPos,IntToFloat(Random(360))); AssignCommand(oNPC,ActionMoveToLocation(lLoc,TRUE)); AssignCommand(oNPC,ActionMoveToObject(oMoveAfter,TRUE,fRange)); } // VectorMove() int privateMoveToObject(object oNPC,object oTarget,int bRun,float fRange,object oShortPathDest) { // PURPOSE: PATHING float fLast; float fDist; int nN; int nASC; object oMe=oNPC; if (oShortPathDest!=oTarget) { // new destination SetLocalObject(oNPC,"oShortPathDest",oTarget); DeleteLocalFloat(oNPC,"fLastShortPath"); DeleteLocalInt(oNPC,"nASCShortPath"); AssignCommand(oNPC,ClearAllActions()); AssignCommand(oNPC,ActionMoveToObject(oTarget,bRun,fRange)); } // new destination else { // same destination fLast=GetLocalFloat(oNPC,"fLastShortPath"); if (fLast==0.0) fLast=999.0; fDist=GetDistanceBetween(oNPC,oTarget); if (fDist<=fRange) { // arrived DeleteLocalInt(oNPC,"nASCShortPath"); AssignCommand(oNPC,ClearAllActions()); return 1; } // arrived else if (fDist!=fLast) { // not stuck if (GetCurrentAction(oNPC)!=ACTION_MOVETOPOINT) { // move AssignCommand(oNPC,ClearAllActions()); AssignCommand(oNPC,ActionMoveToObject(oTarget,bRun,fRange)); } // move if (fDist0&&bAboveGround) return nCountA; if (nCountB>0&&!bAboveGround) return nCountB; int nN; object oOb; object oTrans; //SendMessageToPC(GetFirstPC(),"fnGetTransitionCount("+IntToString(bAboveGround)+")"); nN=1; oOb=GetNearestObject(OBJECT_TYPE_TRIGGER,oMe,nN); while(GetIsObjectValid(oOb)) { // count triggers oTrans=GetTransitionTarget(oOb); if (GetIsObjectValid(oTrans)) { // transition if(GetIsAreaAboveGround(GetArea(oTrans))&&!GetIsAreaInterior(GetArea(oTrans))) { // above ground nCountA++; //SendMessageToPC(GetFirstPC()," [A"+IntToString(nCountA)+"] "+GetTag(oOb)); SetLocalObject(GetArea(oMe),"oTransitionA"+IntToString(nCountA),oOb); } // above ground else { // below ground nCountB++; //SendMessageToPC(GetFirstPC()," [B"+IntToString(nCountB)+"] "+GetTag(oOb)); SetLocalObject(GetArea(oMe),"oTransitionB"+IntToString(nCountB),oOb); } // below ground } // transition nN++; oOb=GetNearestObject(OBJECT_TYPE_TRIGGER,oMe,nN); } // count triggers nN=1; oOb=GetNearestObject(OBJECT_TYPE_DOOR,oMe,nN); while(GetIsObjectValid(oOb)) { // count doors oTrans=GetTransitionTarget(oOb); if (GetIsObjectValid(oTrans)) { // transition if(GetIsAreaAboveGround(GetArea(oTrans))&&!GetIsAreaInterior(GetArea(oTrans))) { // above ground nCountA++; //SendMessageToPC(GetFirstPC()," [A"+IntToString(nCountA)+"] "+GetTag(oOb)); SetLocalObject(GetArea(oMe),"oTransitionA"+IntToString(nCountA),oOb); } // above ground else { // below ground nCountB++; //SendMessageToPC(GetFirstPC()," [B"+IntToString(nCountB)+"] "+GetTag(oOb)); SetLocalObject(GetArea(oMe),"oTransitionB"+IntToString(nCountB),oOb); } // below ground } // transition nN++; oOb=GetNearestObject(OBJECT_TYPE_DOOR,oMe,nN); } // count doors nN=1; oOb=GetNearestObject(OBJECT_TYPE_PLACEABLE,oMe,nN); while(GetIsObjectValid(oOb)) { // count doors oTrans=GetObjectByTag(GetLocalString(oOb,"sDestTag")); if (GetIsObjectValid(oTrans)) { // transition if(GetIsAreaAboveGround(GetArea(oTrans))&&!GetIsAreaInterior(GetArea(oTrans))) { // above ground nCountA++; //SendMessageToPC(GetFirstPC()," [A"+IntToString(nCountA)+"] "+GetTag(oOb)); SetLocalObject(GetArea(oMe),"oTransitionA"+IntToString(nCountA),oOb); } // above ground else { // below ground nCountB++; //SendMessageToPC(GetFirstPC()," [B"+IntToString(nCountB)+"] "+GetTag(oOb)); SetLocalObject(GetArea(oMe),"oTransitionB"+IntToString(nCountB),oOb); } // below ground } // transition nN++; oOb=GetNearestObject(OBJECT_TYPE_PLACEABLE,oMe,nN); } // count doors //SendMessageToPC(GetFirstPC()," Above Count:"+IntToString(nCountA)+" Below Count:"+IntToString(nCountB)); SetLocalInt(GetArea(oMe),"nTransitionCountA",nCountA); SetLocalInt(GetArea(oMe),"nTransitionCountB",nCountB); if (bAboveGround) return nCountA; return nCountB; } // fnGetTransitionCount() object fnGetRandomTransition(int bAboveGround,object oFor=OBJECT_INVALID,int bLightFatal=FALSE) { // PURPOSE: Get a random transition object oRet; int nCount=fnGetTransitionCount(bAboveGround); //SendMessageToPC(GetFirstPC(),"[Random Transition]: AboveGround:"+IntToString(nCount)); if (nCount>0) { // random int nR=Random(nCount)+1; if (bAboveGround&&(GetIsNight()||!bLightFatal)) oRet=GetLocalObject(GetArea(OBJECT_SELF),"oTransitionA"+IntToString(nR)); else { oRet=GetLocalObject(GetArea(OBJECT_SELF),"oTransitionB"+IntToString(nR)); } //SendMessageToPC(GetFirstPC()," First:("+IntToString(nR)+") Choose:"+GetTag(oRet)); if ((GetArea(GetTransitionTarget(oRet))==oFor||GetLocked(oRet))&&nCount>1) { // try again nR=nR+1; if (nR>nCount) nR=1; if (bAboveGround&&(GetIsNight()||!bLightFatal)) oRet=GetLocalObject(GetArea(OBJECT_SELF),"oTransitionA"+IntToString(nR)); else { oRet=GetLocalObject(GetArea(OBJECT_SELF),"oTransitionB"+IntToString(nR)); } //SendMessageToPC(GetFirstPC()," Second:("+IntToString(nR)+") Choose:"+GetTag(oRet)); } // try again return oRet; } // random return OBJECT_INVALID; } // fnGetRandomTransition() void MoveToAndUseTransition(object oNPC,object oTransition,float fRange=4.0) { // PURPOSE: Move to oTransition and then transition int nN; object oDest; nN=MoveToObject(oNPC,oTransition,TRUE,fRange); if (nN==1) { // arrived if (GetObjectType(oTransition)==OBJECT_TYPE_DOOR) { // door AssignCommand(oNPC,ClearAllActions()); AssignCommand(oNPC,ActionDoCommand(SetFacingPoint(GetPosition(oTransition)))); if (!GetIsOpen(oTransition)) { // open door AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,2.0)); AssignCommand(oTransition,ActionOpenDoor(oTransition)); } // open door oDest=GetTransitionTarget(oTransition); if (GetObjectType(oDest)==OBJECT_TYPE_DOOR&&!GetIsOpen(oDest)) AssignCommand(oNPC,ActionDoCommand(AssignCommand(oDest,ActionOpenDoor(oDest)))); AssignCommand(oNPC,ActionDoCommand(MoveTransition(oNPC,oDest))); DeleteLocalObject(oNPC,"oWasHere"); DeleteLocalObject(oNPC,"oTransition"); } // door else if (GetObjectType(oTransition)==OBJECT_TYPE_PLACEABLE) { // placeable AssignCommand(oNPC,ClearAllActions()); AssignCommand(oNPC,ActionDoCommand(SetFacingPoint(GetPosition(oTransition)))); AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_LOOPING_GET_MID,1.0,2.0)); oDest=GetObjectByTag(GetLocalString(oTransition,"sDestTag")); if (GetObjectType(oDest)==OBJECT_TYPE_DOOR&&!GetIsOpen(oDest)) AssignCommand(oNPC,ActionDoCommand(AssignCommand(oDest,ActionOpenDoor(oDest)))); AssignCommand(oNPC,ActionDoCommand(MoveTransition(oNPC,oDest))); DeleteLocalObject(oNPC,"oWasHere"); DeleteLocalObject(oNPC,"oTransition"); } // placeable else { // trigger AssignCommand(oNPC,ClearAllActions()); oDest=GetTransitionTarget(oTransition); if (GetObjectType(oDest)==OBJECT_TYPE_DOOR&&!GetIsOpen(oDest)) AssignCommand(oNPC,ActionDoCommand(AssignCommand(oDest,ActionOpenDoor(oDest)))); AssignCommand(oNPC,ActionDoCommand(MoveTransition(oNPC,oDest))); DeleteLocalObject(oNPC,"oWasHere"); DeleteLocalObject(oNPC,"oTransition"); } // trigger } // arrived else if (nN==-1) { // error PrintString("[Error: lib_hos2_shpath(MoveToAndUseTransition)] Cannot reach transition '"+GetTag(oTransition)+"' in area '"+GetName(GetArea(oNPC))+"'"); DeleteLocalObject(oNPC,"oTransition"); } // error } // MoveToAndUseTransition() //void main(){}