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 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 ///////////////////////////////