Join the forums now, and start posting to receive access to our Scripts Vault!
<?php /********************************************************************************************************/ /* __construct /* __destruct /* ClearObject /* CompareCost /* getPath /* getStep /* FindPath /* AddNode /* MakePath /* LineOfSight /********************************************************************************************************/ class Path { var $strErrorMsg = ""; var $strDebugInfo = ""; var $arrTiles = array(); var $arrQueue = array(); var $arrPath = array(); var $blnFound = FALSE; var $intSourceX; var $intSourceY; var $intTargetX; var $intTargetY; var $intTimeTotal = 0; var $intMax = 200; var $intCount = 0; /********************************************************************************************************/ /* Name : __construct() /* Type : Public /* Parameters : $arrTiles, Array, a collection of tiles that make up the map. /* Return Type : None /* Description : Class initialisation /********************************************************************************************************/ public function __construct( $arrTiles ) { try { $this->intTimeTotal = microtime(true); $this->arrTiles = $arrTiles; } catch( Exception $e ) { $this->strErrorMsg = $e->getMessage(); } } /********************************************************************************************************/ /* Name : __destruct() /* Type : Public /* Parameters : None /* Return Type : None /* Description : Class destructor. /********************************************************************************************************/ public function __destruct() { //echo "Path: Execution time: " . round( microtime( true ) - $this->intTimeTotal, 3 ) . "s<br>\n"; } /********************************************************************************************************/ /* Name : __destruct() /* Type : Public /* Parameters : None /* Return Type : None /* Description : Class destructor. /********************************************************************************************************/ public function ClearObject() { try { $intReturn = 1; $this->intSourceX = NULL; $this->intSourceY = NULL; $this->intTargetX = NULL; $this->intTargetY = NULL; unset( $this->arrQueue ); unset( $this->arrPath ); $this->arrQueue = array(); $this->arrPath = array(); $this->blnFound = FALSE; $this->intCount = 0; } catch( Exception $e ) { $this->strErrorMsg = $e->getMessage(); $intReturn = -1; } return $intReturn; } /********************************************************************************************************/ /* Name : CompareCost() /* Type : Public /* Parameters : $x, Array, the array with primary tile information. /* Return Type : $y, Array, the array with secondary tile information /* Description : This compares the cost (distance to target) of the two tiles and sees which one is /* : lower in order to use it to sort the queue. /********************************************************************************************************/ private function CompareCost( $x, $y ) { if ( $x["cost"] == $y["cost"] ) { return 0; } elseif ( $x["cost"] < $y["cost"] ) { return -1; } else { return 1; } } /********************************************************************************************************/ /* Name : getPath() /* Type : Public /* Parameters : $intSourceX, Integer, the x-coordinate for the source tile on the map. /* : $intSourceY, Integer, the y-coordinate for the source tile on the map. /* : $intTargetX, Integer, the y-coordinate for the target tile on the map. /* : $intTargetY, Integer, the y-coordinate for the target tile on the map. /* Return Type : Integer. /* Description : This is the main method of this class, which is called and determines the path from /* : source to target. /********************************************************************************************************/ public function getPath( $intSourceX, $intSourceY, $intTargetX, $intTargetY ) { try { $intReturn = 1; //$strOutput = ""; $this->intSourceX = $intSourceX; $this->intSourceY = $intSourceY; $this->intTargetX = $intTargetX; $this->intTargetY = $intTargetY; if ( $this->AddNode( NULL, NULL, $intSourceX, $intSourceY ) < 1 ) { throw new Exception( $this->strErrorMsg ); } if ( $this->FindPath( $intSourceX, $intSourceY ) < 1 ) { throw new Exception( $this->strErrorMsg ); } if ( $this->blnFound ) { //$strOutput .= "Found!<br>\n"; if ( $this->MakePath() < 1 ) { //$strOutput .= $this->strErrorMsg; throw new Exception( $this->strErrorMsg ); } } else { //$strOutput .= "Not found!<br>\n"; } } catch( Exception $e ) { $this->strErrorMsg = $e->getMessage(); //$strOutput = $this->strErrorMsg; $intReturn = -1; } //return $strOutput; return $intReturn; } /********************************************************************************************************/ /* Name : getStep() /* Type : Public /* Parameters : $intX, Integer (byRef) /* : $intY, Integer (byRef) /* Return Type : Integer. /* Description : /********************************************************************************************************/ public function getStep( &$intX, &$intY ) { try { $intReturn = 1; if ( count( $this->arrPath ) < 1 ) { throw new Warning( "No elements in path." ); } //$arrStep = array_pop( $this->arrPath ); $arrStep = array_pop( $this->arrPath ); $intX = $arrStep["x"]; $intY = $arrStep["y"]; } catch( Warning $e ) { $this->strErrorMsg = $e->getMessage(); $intReturn = 0; } catch( Exception $e ) { $this->strErrorMsg = $e->getMessage(); $intReturn = -1; } return $intReturn; } /********************************************************************************************************/ /* Name : FindPath() /* Type : Private /* Parameters : $intX, Integer /* : $intY, Integer /* Return Type : Integer. /* Description : /********************************************************************************************************/ private function FindPath( $intX, $intY ) { try { // Initialise variables. $intReturn = 1; if ( $this->AddNode( $intX, $intY, $intX - 1, $intY - 1 ) < 1 ) { throw new Exception( $this->strErrorMsg ); } if ( $this->AddNode( $intX, $intY, $intX + 0, $intY - 1 ) < 1 ) { throw new Exception( $this->strErrorMsg ); } if ( $this->AddNode( $intX, $intY, $intX + 1, $intY - 1 ) < 1 ) { throw new Exception( $this->strErrorMsg ); } if ( $this->AddNode( $intX, $intY, $intX - 1, $intY + 0 ) < 1 ) { throw new Exception( $this->strErrorMsg ); } if ( $this->AddNode( $intX, $intY, $intX + 1, $intY + 0 ) < 1 ) { throw new Exception( $this->strErrorMsg ); } if ( $this->AddNode( $intX, $intY, $intX - 1, $intY + 1 ) < 1 ) { throw new Exception( $this->strErrorMsg ); } if ( $this->AddNode( $intX, $intY, $intX + 0, $intY + 1 ) < 1 ) { throw new Exception( $this->strErrorMsg ); } if ( $this->AddNode( $intX, $intY, $intX + 1, $intY + 1 ) < 1 ) { throw new Exception( $this->strErrorMsg ); } // Sort uasort( $this->arrQueue, array( $this, "CompareCost" ) ); foreach( $this->arrQueue as $arrTileCheck ) { $this->intCount++; if ( $this->intCount > $this->intMax ) { break; } if ( !$this->blnFound ) { if ( !$arrTileCheck["checked"] ) { if ( $arrTileCheck["x"] == $this->intTargetX && $arrTileCheck["y"] == $this->intTargetY ) { $this->blnFound = TRUE; break; } else { $this->arrQueue[$arrTileCheck["x"] . "x" . $arrTileCheck["y"]]["checked"] = TRUE; if ( $this->FindPath( $arrTileCheck["x"], $arrTileCheck["y"] ) < 1 ) { throw new Exception( $this->strErrorMsg ); } } } } } } catch( Exception $e ) { $this->strErrorMsg = $e->getMessage(); $intReturn = -1; } return $intReturn; } /********************************************************************************************************/ /* Name : AddNode() /* Type : Private /* Parameters : $intParX, Integer /* : $intParX, Integer /* : $intX, Integer /* : $intY, Integer /* Return Type : Integer. /* Description : /********************************************************************************************************/ private function AddNode( $intParX, $intParY, $intX, $intY ) { try { // Initialise variables. $intReturn = 1; $strKey = $intX . "x" . $intY; if ( $intX > 0 && $intY > 0 ) { if ( !array_key_exists( $strKey, $this->arrQueue ) ) { if ( $this->arrTiles[$strKey]["block"] == 0 ) { $this->arrQueue[$strKey] = array( "parx" => $intParX, "pary" => $intParY, "x" => $intX, "y" => $intY, "checked" => FALSE, "cost" => abs( $this->intTargetX - $intX ) + abs( $this->intTargetY - $intY ) ); } } } } catch( Exception $e ) { $this->strErrorMsg = $e->getMessage(); $intReturn = -1; } return $intReturn; } /********************************************************************************************************/ /* Name : MakePath() /* Type : Private /* Parameters : None. /* Return Type : Integer. /* Description : /********************************************************************************************************/ private function MakePath() { try { // Initialise variables. $intReturn = 1; $arrTile = $this->arrQueue[$this->intTargetX . "x" . $this->intTargetY]; while( !is_null( $arrTile["x"] ) && !is_null( $arrTile["y"] ) ) { $this->arrPath[$arrTile["x"] . "x" . $arrTile["y"]] = array( "x" => $arrTile["x"], "y" => $arrTile["y"] ); $arrTile = $this->arrQueue[$arrTile["parx"] . "x" . $arrTile["pary"]]; } } catch( Exception $e ) { $this->strErrorMsg = $e->getMessage(); $intReturn = -1; } return $intReturn; } /********************************************************************************************************/ /* Name : LineOfSight() /* Type : Public /* Parameters : $intSourceX, Integer, the x-coordinate for the source tile on the map. /* : $intSourceY, Integer, the y-coordinate for the source tile on the map. /* : $intTargetX, Integer, the y-coordinate for the target tile on the map. /* : $intTargetY, Integer, the y-coordinate for the target tile on the map. /* : $blnLineOfSight, Boolean, By Reference, whether the target tile can be seen (and /* : targeted) from the source tile. /* Return Type : Integer. /* Description : /* Date edited : 2011-01-05 /* Reason : In the case of a slope ( so the else statement ) then the increment should always be /* : 0.25, and it shouldn't matter if the intDiffY == 0, since that can never happen there. /* Date edited : 2011-01-06 /* Reason : It turns out in case of a slope, we always followed it along the x-axis, which can cause /* : huge jumps along the y-axis if delta-x is smaller than delta-y, skipping potentially /* : blocking tiles. Now we first check if delta-x is bigger than delta-y and then deciding /* : to follow the path along the x-axis or the y-axis. This is much more accurate. /********************************************************************************************************/ public function LineOfSight( $intSourceX, $intSourceY, $intTargetX, $intTargetY, &$blnLineOfSight ) { try { // Initialise variables. $intReturn = 1; $blnFound = false; // Determine equation. $intDiffX = $intTargetX - $intSourceX; $intDiffY = $intTargetY - $intSourceY; if ( $intDiffX == 0 ) { $intTileStart = ( $intDiffY < 0 ) ? $intTargetY : $intSourceY; $intTileEnd = ( $intDiffY < 0 ) ? $intSourceY : $intTargetY; for ( $intCount = $intTileStart; $intCount <= $intTileEnd; $intCount++ ) { if ( !array_key_exists( $intSourceX . "x" . $intCount, $this->arrTiles ) ) { $blnLineOfSight = false; break; } if ( $this->arrTiles[$intSourceX . "x" . $intCount]["block"] == 1 ) { $blnLineOfSight = false; break; } } } elseif ( $intDiffY == 0 ) { $intTileStart = ( $intDiffX < 0 ) ? $intTargetX : $intSourceX; $intTileEnd = ( $intDiffX < 0 ) ? $intSourceX : $intTargetX; for ( $intCount = $intTileStart; $intCount <= $intTileEnd; $intCount++ ) { if ( !array_key_exists( $intCount . "x" . $intSourceY, $this->arrTiles ) ) { $blnLineOfSight = false; break; } if ( $this->arrTiles[$intCount . "x" . $intSourceY]["block"] == 1 ) { $blnLineOfSight = false; break; } } } else { if ( abs( $intDiffX ) > abs( $intDiffY ) ) { $dblSlope = $intDiffY / $intDiffX; $dblIntercept = $intSourceY - ( $dblSlope * $intSourceX ); $dblIncrement = 0.25; $intTileStart = ( $intDiffX < 0 ) ? $intTargetX : $intSourceX; $intTileEnd = ( $intDiffX < 0 ) ? $intSourceX : $intTargetX; for ( $dblCount = $intTileStart; $dblCount <= $intTileEnd; $dblCount += $dblIncrement ) { $intTileX = floor( $dblCount ); $intTileY = floor( ( $dblCount * $dblSlope ) + $dblIntercept ); //$this->strDebugInfo .= "Count: " . $dblCount . ", Slope: " . $dblSlope . ", Intercept: " . $dblIntercept . ", [" . $intTileX . "x" . $intTileY . "]: " . $this->arrTiles[$intTileX . "x" . $intTileY]["block"] . "<br>"; if ( !array_key_exists( $intTileX . "x" . $intTileY, $this->arrTiles ) ) { $blnLineOfSight = false; break; } if ( $this->arrTiles[$intTileX . "x" . $intTileY]["block"] == 1 ) { $blnLineOfSight = false; break; } } } else { $dblSlope = $intDiffX / $intDiffY; $dblIntercept = $intSourceX - ( $dblSlope * $intSourceY ); $dblIncrement = 0.25; $intTileStart = ( $intDiffY < 0 ) ? $intTargetY : $intSourceY; $intTileEnd = ( $intDiffY < 0 ) ? $intSourceY : $intTargetY; for ( $dblCount = $intTileStart; $dblCount <= $intTileEnd; $dblCount += $dblIncrement ) { $intTileY = floor( $dblCount ); $intTileX = floor( ( $dblCount * $dblSlope ) + $dblIntercept ); //$this->strDebugInfo .= "Count: " . $dblCount . ", Slope: " . $dblSlope . ", Intercept: " . $dblIntercept . ", [" . $intTileX . "x" . $intTileY . "]: " . $this->arrTiles[$intTileX . "x" . $intTileY]["block"] . "<br>"; if ( !array_key_exists( $intTileX . "x" . $intTileY, $this->arrTiles ) ) { $blnLineOfSight = false; break; } if ( $this->arrTiles[$intTileX . "x" . $intTileY]["block"] == 1 ) { $blnLineOfSight = false; break; } } } } } catch( Exception $e ) { $this->strErrorMsg = $e->getMessage(); $intReturn = -1; } return $intReturn; } }?>
<?php $this->arrTiles["0x1"] = array( "block" => 1, "y" => 1, "x" => 0, "name" => "wall_vertical_3" ); $this->arrTiles["1x1"] = array( "block" => 0, "y" => 1, "x" => 1, "name" => "ground_dark_2" ); $this->arrTiles["2x1"] = array( "block" => 0, "y" => 1, "x" => 2, "name" => "ground_dark_2" ); $this->arrTiles["3x1"] = array( "block" => 0, "y" => 1, "x" => 3, "name" => "ground_dark_5" ); $this->arrTiles["4x1"] = array( "block" => 0, "y" => 1, "x" => 4, "name" => "ground_dark_5" ); $this->arrTiles["5x1"] = array( "block" => 0, "y" => 1, "x" => 5, "name" => "ground_dark_4" ); $this->arrTiles["6x1"] = array( "block" => 1, "y" => 1, "x" => 6, "name" => "wall_vertical_4" ); $this->arrTiles["7x1"] = array( "block" => 1, "y" => 1, "x" => 7, "name" => "wall_vertical_7" ); $this->arrTiles["8x1"] = array( "block" => 0, "y" => 1, "x" => 8, "name" => "ground_dark_4" ); $this->arrTiles["9x1"] = array( "block" => 0, "y" => 1, "x" => 9, "name" => "ground_dark_3" );?>