Merged redundant hak files. Moved hak scripts into module. Updated gitignore. Full Compile. Added release folder & archive.
457 lines
15 KiB
Plaintext
457 lines
15 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_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()
|
|
|
|
|
|
|
|
|
|
|
|
|