/////////////////////////////////////////////////////////////////////////////////// // 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_headers" ///////////////////////////////////////// // FUNCTIONS ///////////////////////////////////////// object fnRandomObject(object oArea,int nObType=0) { // 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); SendMessageToPC(GetFirstPC(),"fnRandomObject("+GetName(oArea)+","+IntToString(nObType)+")"); SendMessageToPC(GetFirstPC()," 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; } SendMessageToPC(GetFirstPC()," Object type chosen:"+IntToString(nObT)); nC=0; 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 SendMessageToPC(GetFirstPC()," 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() 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() 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 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()