const string MATCHBOX = "matchbox";
const string WALL = "shelfwall_";
const string RAY_SEGMENT_FILE = "sdiv";
void OnStart()
{
//#1: Create the detectors & init vats
CreateDetectors(MATCHBOX,RAY_SEGMENT_FILE,0.5f,10);
//#2: Set the initial direction
SetCurrentDir(EAST);
//#3: Wait until the crate hits the floor (2 seconds)
AddTimer("TMR_CRATE_MOVE",2.0f,"tmrCrateMotion");
}
///////////////////////////////
//+ DETECTOR INIT
///////////////////////////////
//Create a set of wall detectors around the matchbox
void CreateDetectors(string &in asEntity, string &in asFileBase, float afStepSize, int maxDetectors)
{
string filename = asFileBase + afStepSize + ".ent";
//Create the beam of detectors
for(int i = 0; i < maxDetectors; i++)
{
float pos = afStepSize * i;
//East detectors (X+)
CreateWallDetector(pos,filename,asEntity,EAST, i);
//West detectors (X-)
CreateWallDetector(pos,filename,asEntity,WEST, i);
//North detectors (Z+)
CreateWallDetector(pos,filename,asEntity,NORTH, i);
//South detectors (Z-)
CreateWallDetector(pos,filename,asEntity,SOUTH, i);
}
//Calculate the min detectors
@RayCastCondition = @rccMatchboxInit;
SetMinDir(NORTH); SetMinDir(SOUTH); SetMinDir(EAST); SetMinDir(WEST);
@RayCastCondition = @rccWall;
}
//Create a single wall detector
void CreateWallDetector(float afAmt, string &in asFileName, string &in asAttachBase, Direction dir, int alIndex)
{
string dname = DirectionPrefix(dir) + alIndex;
AttachProp(asAttachBase,dname,asFileName, Xcomp(dir) * afAmt, Ycomp(dir) * afAmt, Zcomp(dir) * afAmt);
}
//Fixed variant of AddAttachedPropToProp
void AttachProp(string &in asPName,string &in asAName, string &in aFile, float afX, float afY, float afZ) { AddAttachedPropToProp(asPName,asAName,aFile,afX,afY,0,afZ,90.0f,afZ); }
///////////////////////////////
//- DETECTOR INIT
///////////////////////////////
///////////////////////////////
//+ CRATE MOTION
///////////////////////////////
float TIMESTEP = 1.0f / 5.0f;
void tmrCrateMotion(string &in asTimer)
{
Direction dir = GetCurrentDir();
//Move the crate forward
float impulse = 1.0f;
AddPropImpulse(MATCHBOX, impulse * Xcomp(dir), impulse * Ycomp(dir), impulse * Zcomp(dir), "world");
//Raycast forward -> Are we at the wall?
@RayCastCondition = @rccWall;
AddDebugMessage("RCV: " + RayCast(dir),false);
if(RayCast(dir) <= GetMinDir(dir))
{
AddDebugMessage("At end of path. Picking new direction.",false);
SetCurrentDir(SelectBestDirection(dir));
}
AddTimer(asTimer,TIMESTEP,"tmrCrateMotion");
}
//Select the best direction by seeing which way has the longest distance box can move (excluding where box came from)
Direction SelectBestDirection(Direction oldDirection)
{
//Get a collection of directions the box can move in
Direction[] newDirections = GetNewDirections(oldDirection);
//Assume first defined direction is best for now
Direction bestDirection = newDirections[0];
int bestValue = RayCast(newDirections[0]);
//Look through all remaining possible directions to pick best path
for(uint i = 1; i<newDirections.length(); i++)
{
int newValue = RayCast(newDirections[i]);
if(newValue > bestValue)
{
//Longer path than before - this is now the best direction to move
bestValue = newValue;
bestDirection = newDirections[i];
}
}
AddDebugMessage(DirectionPrefix(oldDirection) + " -> " + DirectionPrefix(bestDirection) + " (" + bestValue + ")",false);
return bestDirection;
}
//Get all the directions the box can move excluding where the box came from
Direction[] GetNewDirections(Direction oldDirection)
{
switch(oldDirection)
{
case NORTH: return DS03;
case SOUTH: return DS04;
case EAST: return DS01;
case WEST: return DS02;
}
return DS00;
}
Direction[] DS00 = {NORTH, SOUTH, EAST, WEST};
Direction[] DS01 = {NORTH, SOUTH, EAST};
Direction[] DS02 = {NORTH, SOUTH, WEST};
Direction[] DS03 = {NORTH, EAST, WEST};
Direction[] DS04 = {SOUTH, EAST, WEST};
///////////////////////////////
//- CRATE MOTION
///////////////////////////////
///////////////////////////////
//+ RAY CASTING
///////////////////////////////
//Returns how many ray segments in the given direction there are until a collision occurrs
int RayCast(Direction dir)
{
string pfx = DirectionPrefix(dir);
int i =0;
for(; GetEntityExists(pfx+i); i++)
if(RayCastCondition(pfx+i)) return i;
return i;
}
//used to determine if the raytrace should continue
funcdef bool RAYCASTCONDITION(string &in);
RAYCASTCONDITION @RayCastCondition = @rccWall;
//returns true if ray segment collides with a wall (used for detector)
bool rccWall(string &in asRaySegment)
{
//i<100 to get around missing wall peices.
for(int i = 1; i < 100; i++)
if(GetEntitiesCollide(WALL+i,asRaySegment)) return true;
return false;
}
//returns true if ray segment doesn't collide with the matchbox (used in init)
bool rccMatchboxInit(string &in asRaySegment)
{ return !GetEntitiesCollide(MATCHBOX,asRaySegment); }
///////////////////////////////
//- RayCasting
///////////////////////////////
///////////////////////////////
//+ DIRECTION ENUM
///////////////////////////////
enum Direction
{
NORTH,
SOUTH,
EAST,
WEST
}
//Prefix of a given direction
string DirectionPrefix(Direction dir)
{
switch(dir)
{
case NORTH: return "N";
case SOUTH: return "S";
case EAST: return "E";
case WEST: return "W";
}
AddDebugMessage("Error: Unknown direction error",false);
return "?";
}
//Get/Set the min value for a direction
void SetMinDir(Direction dir)
{
string vn = "MIN_" + DirectionPrefix(dir);
SetLocalVarInt(vn,RayCast(dir));
AddDebugMessage(vn + ":" + GetLocalVarInt(vn),false);
}
int GetMinDir(Direction dir)
{
return GetLocalVarInt("MIN_" + DirectionPrefix(dir));
}
//Get/Set current direction
void SetCurrentDir(Direction dir)
{
switch(dir)
{
case NORTH: SetLocalVarInt("CDIR",1); break;
case SOUTH: SetLocalVarInt("CDIR",2); break;
case EAST: SetLocalVarInt("CDIR",3); break;
case WEST: SetLocalVarInt("CDIR",4); break;
}
}
Direction GetCurrentDir()
{
switch(GetLocalVarInt("CDIR"))
{
case 1: return NORTH;
case 2: return SOUTH;
case 3: return EAST;
case 4: return WEST;
}
return NORTH;
}
//Components of direction
float Xcomp(Direction dir) { switch(dir) { case EAST: return 1.0f; case WEST: return -1.0f; } return 0.0f; }
float Ycomp(Direction dir) { return 0.0f; }
float Zcomp(Direction dir) { switch(dir) { case NORTH: return 1.0f; case SOUTH: return -1.0f; } return 0.0f; }
///////////////////////////////
//- DIRECTION ENUM
///////////////////////////////