<?php
//$Id: mapapi.module,v 1.1 2007/04/30 03:41:38 dmitrig01 Exp $
/*
* @file Creates and loads maps.
*/
/*
* Loads maps maps. Use this instead of _mapapi_load.
* @param $maps
* An array of maps
* A sample maps array would be
* @code
* $maps['multi_tile'] = array(
* 'mid' => 1,
* 'x' => 5, // Try negative values... you will see them adjust and always return
* 'y' => 5, // The correct box bounds (defined by length_x and length_y)
* 'length_x' => 10,
* 'length_y' => 9, // Doesn't have to be exactly square!
* 'tile_content' => TRUE // True/false on whether to load tile content or not.
* );
* $maps['single_tile'] = array(
* 'mid' => 1,
* 'x' => 5,
* 'y' => 5,
* 'length_x' => 10,
* 'length_y' => 9,
* 'tile_content' => TRUE,
* );
* @endcode
*
* @todo
* cache the maps loaded, since it can be expensive
*/
function mapapi_load($maps) {
// This is the array in which maps are stored.
// The keys are the same as those of $maps
// But the content is the tiles.
$mapapi_array = array();
// Loop through each map, because $maps
// is an array of maps, not a single map
foreach($maps as $mapapi_name => $map) {
// Load the map, and insert it into $mapapi_array
// with the same key as $maps
$mapapi_array[$mapapi_name] = _mapapi_load($map);
}
// Return the array of maps
return $mapapi_array;
}
/*
* Loads a single map.
* Internal usage only. Use mapapi_load instead.
* This function does all limit / boundary
* calculation, as well as fetching the tiles.
*/
function _mapapi_load($map) {
// We need to get the limits of the map
// to make sure that the map array passed in's
// area is not more than itself's area.
// This needs to be done to eleminate errors
// and such.
if ($mapapi_limits_query = db_query("SELECT mid, name, limit_x, limit_y FROM {mapapi_map_definition} WHERE mid = %d", $map['mid'])) {
// The upper limits of the map
$mapapi_limits = db_fetch_array($mapapi_limits_query);
// Build the dimentions of the map, respecting the limits
// of the map defnition.
$limits = _mapapi_make_dimentions($map, $mapapi_limits);
}
// If that query failed, we need to silently return null
// and not execute the rest of the function, because
// it would be pointless.
else {
return NULL;
}
$check_query_args = array();
$query = "SELECT tid, coord_x, coord_y, description FROM {mapapi_tile_definition} WHERE ";
// We need to respect the upper limits of the map
// in both x and y.
// If we don't do this, the query can go wacko
$query .= "coord_y <= %d AND "; $check_query_args[] = $mapapi_limits['limit_y'];
$query .= "coord_x <= %d AND "; $check_query_args[] = $mapapi_limits['limit_x'];
// We need to (quite obviously) follow the upper limits
// of the map requested.
// We need to follow both X and Y
$query .= "coord_y <= %d AND "; $check_query_args[] = $limits['y']['high'];
$query .= "coord_x <= %d AND "; $check_query_args[] = $limits['x']['high'];
// Ditto, but for lower limits this time
$query .= "coord_y >= %d AND "; $check_query_args[] = $limits['y']['low'];
$query .= "coord_x >= %d AND "; $check_query_args[] = $limit['x']['low'];
// Make sure we select from the proper map (!!!)
$query .= "mid = %d"; $check_query_args[] = $map['mid'];
// Execute the query
if ($result = db_query($query, $check_query_args)) {
// Return some basic information about the map,
// might be helpful in some circumstances
$selection = array('map_info' => $mapapi_limits, 'dimentions' => $limits);
$tiles = array();
while($row = db_fetch_array($result)){
// Eventually, this will be the tile content, but for now, it is not
$row['content'] = array();
// Since the description can be an array, we serialize it
$row['description'] = unserialize($row['description']);
// We need the coordinates of the tile, to know where in the array to put them
$coord_y = $row['coord_y'];
$coord_x = $row['coord_x'];
// However, the array of tiles does not need them, because it's keys are already it's coordinates.
unset($row['coord_y']);
unset($row['coord_x']);
// Insert the row into the tiles array
$tiles[$coord_x][$coord_y] = $row;
}
$selection['map_tiles'] = $tiles;
return $selection;
}
// If no tiles were found or the query failed, then return an empty array.
else {
return array();
}
}
/**
* Figure out dimentions of a map, around the x and y.
*/
function _mapapi_make_dimentions($map, $limits) {
// Determine number of tiles to go to
// each direction away from the center.
// Up and down should be equal length
// as well as left and right
$x_spacer = floor($map['length_x'] / 2);
$y_spacer = floor($map['length_y'] / 2);
// Determine X boundaries
$x_low = $map['x'] - $x_spacer;
$x_high = $map['x'] + $x_spacer;
// Determine Y boundaries
$y_low = $map['y'] - $y_spacer;
$y_high = $map['y'] + $y_spacer;
// If the map's left boundary
// is below 0, then something
// needs to be done!
// The whole map needs to move
// right, so the width stays the
// same.
if ($x_low < 0) {
// Increase $x_high by the
// amount below 0 $x_low is
// So as an example
// if $x_low is -3
// and $x_high is 5
// then the height of the
// map is 8.
// So $x_low becomes 0
// and $x_high becomes 8.
// We also need to compensate
// for if the height of the
// new $x_high is higher than
// the limit of the map.
$x_high = min($x_high - $x_low, $limits['limit_x']);
$x_low = 0;
}
// Even if the boundaries are above 0
// we still need to make sure that the
// upper boundaries don't exceed the
// upper boundaries of the map.
if ($x_high > $limits['limit_x']) {
// This still needs to be above 0.
// That is why the max function is used
$x_low = max($x_low - ($x_high - $limits['limit_x']), 0);
$x_high = $limits['limit_x'];
}
// If the map's left boundary
// is below 0, then something
// needs to be done!
// The whole map needs to move
// right, so the width stays the
// same.
if ($y_low < 0) {
// Increase $y_high by the
// amount below 0 $y_low is
// So as an example
// if $y_low is -3
// and $y_high is 5
// then the height of the
// map is 8.
// So $y_low becomes 0
// and $y_high becomes 8.
// We also need to compensate
// for if the height of the
// new $y_high is higher than
// the limit of the map.
$y_high = min($y_high - $y_low, $limits['limit_y']);
$y_low = 0;
}
// Even if the boundaries are above 0
// we still need to make sure that the
// upper boundaries don't exceed the
// upper boundaries of the map.
if ($y_high > $limits['limit_y']) {
// This still needs to be above 0.
// That is why the may function is used
$y_low = max($y_low - ($y_high - $limits['limit_y']), 0);
$y_high = $limits['limit_y'];
}
$dimentions = array();
$dimentions['x'] = array('low' => $x_low , 'high' => $x_high);
$dimentions['y'] = array('low' => $y_low , 'high' => $y_high);
return $dimentions;
}
/*
* Wrapper function
* Call mapapi_save('tile', $data)
* to save a tile, mapapi_save('map', $data),
* etc.
* Use this function to save stuff, don't
* call the mapapi_*_save function individually.
* However, that is where the documentation for
* the format is is.
* It returns an array of the status.
* The elements are $status['function']
* which refers to the function name,
* $status['overall'], which is either true
* or false, depending on the success of the function
* and $status['message'], which can give information
* about different errors.
* Functions can also define additional elements
* to add to the array, like the success of
* different elements.
*/
function mapapi_save($type, $data) {
if(function_exists("mapapi_$type"."_save")) {
return call_user_func("mapapi_$type"."_save", $data);
}
else {
return false;
}
}
/**
* Internal usage only. Use mapapi_save('tile', $data);
* This function saves tiles. $args has no practical use now, it is reserved for later addons.
*
* Usage
* @code
* $save_format = array(
* 'mid' => 1,
* 'tid' => 1,
* 'coord_x' => '5',
* 'coord_y' => '7',
* 'description' => array(),
* 'content' => array(),
* );
* @endcode
*
* @param $data
* @param $args
* @return mixed
*/
function mapapi_tile_save($data, $args = array()) {
// We return this at the end of the function execution.
// It must be an array because we are saving tiles and then content,
// and the coder may want a status marker on both of those.
// There is also the overall value, to check if both succeded.
// this may be easier to test for, however, the individual
// ones may be easier for debugging. The overall does not
// include if there is a message.
// There is also the message. This is for things like
// "mid is not valid" or "not all parameters were passed in."
// This is also useful for deveopers.
// The last one is the function, which notes what function
// the error came from (if there is an error)
// Default false values will be changed to true later on,
// if the function succeds.
$status['tile'] = FALSE;
$status['content'] = FALSE;
$status['message'] = "";
$status['function'] = "mapapi_tile_save";
$status['overall'] = FALSE;
// Check if we have all required data
// We need to check the map ID, the x
// coordinate, and the y coordinate
if (!isset($data['mid']) || !isset($data['coord_x']) || !isset($data['coord_y'])) {
// The status is still all false, so return it.
// We need to set the message to "not enough parameters,"
// indicating not enough data was passed in.
$status['message'] = "Not enough parameters. Parameters sent in were <pre>". $data ."</pre> ";
$status['message'] .= "You need to pass in the mid of the map to be saved to, ";
$status['message'] .= "and the x and y coordinates of the tile to save.";
return $status;
}
// If the x or y coordinates are less than 0, which
// is not allowed, throw an error
else if ($data['coord_x'] < 0 || $data['coord_y'] < 0) {
// Set a very descriptive status message.
// We need to alert the user on if both
// or one, and we need to include which one,
// of the parameters were lower than 0
if ($data['coord_x'] < 0 && $data['coord_y'] < 0) {
$status['message'] = "Both the x and y coordinates passed into the function were";
$status['message'] .= "less than 0, which is not allowed.";
}
else if ($data['coord_x'] < 0) {
$status['message'] = "The x coordinate passed into the function were";
$status['message'] .= "less than 0, which is not allowed.";
}
else if ($data['coord_y'] < 0) {
$status['message'] = "The y coordinate passed into the function were";
$status['message'] .= "less than 0, which is not allowed.";
}
return $status;
}
// Now we must test if mid is valid.
$test_mid_query = "SELECT mid FROM mapapi_map_definition WHERE mid = %d";
if ($result = db_query($test_mid_query, $data['mid'])){
if (db_num_rows($result) > 0){
// The map is valid so we don't do anything
}
else {
$status['message'] = "The mid is invalid";
// Not valid map! We musn't save anything
return $status;
}
}
// Check if tile exists
$query = "SELECT tid FROM {mapapi_tile_definition} WHERE "; $query_args = array();
$query .= "coord_x = %d "; $query_args[] = $data['coord_x'];
$query .= "AND coord_y = %d "; $query_args[] = $data['coord_y'];
$query .= "AND mid = %d "; $query_args[] = $data['mid'];
// If it exists, we update the tile instead of
// saving it.
if ($result = db_query($query, $query_args)) {
if (db_num_rows($result) > 0) {
$status['message'] = 'Tile found. Updating it. ';
$tid = db_fetch_array($result);
// Maybe tid was already passed, but we dont care beacuse $tid is guaranteed to always be more correct
$data['tid'] = $tid['tid'];
// No need to see if data definition or data content are set! That check is done inside the tile and content functions
// Update the tile
_mapapi_tile_update($data, $status);
// The tile content must also be saved
mapapi_content_save($data, $status);
}
else {
$status['message'] = 'Tile not found. Adding a new one. ';
// If this is a new tile then tid should not be set!
// In the admin UI there will be no way to set tid.
$data['tid'] = db_next_id("mapapi_tile_tid");
// No need to see if data definition or data content are set! That check is done inside the tile and content functions
// Insert the tile
// If it succeeds, save the content as well.
if(_mapapi_tile_insert($data, $status)){
mapapi_content_save($data, $status);
}
}
}
else {
$status['message'] = "The query to check of the tile is updated failed!";
}
return $status;
}
function _mapapi_tile_insert($data, &$status){
// We check if the tid and description are set.
// The other items are checked by mapapi_tile_save
if (!isset($data['tid']) || !isset($data['description'])) {
// We don't need to alter $status, because
// it is set to false by default.
// However, we do need to return false,
// so the if statement in mapapi_tile_save
// won't go through
return FALSE;
}
// This is the big query.
$query = "INSERT INTO {mapapi_tile_definition} (mid, tid, coord_x, coord_y, description) VALUES (%d, %d, %d, %d, '%s')";
$query_args = array();
$query_args[] = $data['mid'];
$query_args[] = $data['tid'];
$query_args[] = $data['coord_x'];
$query_args[] = $data['coord_y'];
// The description is serialized, because
// it can contain lots of information,
// like the terrain.
$query_args[] = serialize($data['description']);
if (db_query($query, $query_args)) {
$status['tile'] = TRUE;
return TRUE;
}
// The status does not need to be set to false,
// because it is false by default.
return FALSE;
}
// Updates tile description only! (because to update X or Y means that we just insert a new tile and should use _mapapi_tile_insert
function _mapapi_tile_update($data, &$status) {
// Check if the description is set.
// The other items are checked by mapapi_tile_save
if (!isset($data['description'])) {
// We don't need to alter $status['tile'],
// because it is set to false by default.
// $status['message'] does need to be
// set to something more descriptive.
// At this point, $status['message']
// needs to be appended to, because
// it already has some information
// in it.
// $status['function'] needs to be
// set to _mapapi_tile_update to
// alert the user that the error
// occered here.
// We need to return false,
// so the if statement in mapapi_tile_save
// won't go through.
$status['message'] .= "Not enough parameters. Parameters sent in were <pre>". print_r($data, 1) ."</pre> ";
$status['message'] .= "You need to pass in the mid of the map to be saved to, ";
$status['message'] .= "and the x and y coordinates of the tile to save.";
$status['function'] = '_mapapi_tile_update';
return FALSE;
}
// The query itself...
$update_query = "UPDATE {mapapi_tile_definition} SET description = '%s' WHERE tid = %d";
if (db_query($update_query, serialize($data['description']), $data['tid'])) {
$status['tile'] = TRUE;
return TRUE;
}
else {
return FALSE;
}
}
function mapapi_tile_delete($tid) {
// The status vairable is an array,
// indecating the different status
// items.
// It is a keyed array containing
// information about the success
// of the query, any debug messages,
// this function name
$status['query'] = FALSE;
$status['message'] = "";
$status['function'] = "mapapi_tile_save";
$status['overall'] = FALSE;
$del_tile_content_query = "DELETE FROM {mapapi_tile_content} WHERE tid = %d";
if(!db_query($del_tile_content_query, $tid)){
//do something because deleting tile failed!
$query_status = false;
}
$del_tile_definition_query = "DELETE FROM {mapapi_tile_definition} WHERE tid = %d";
if(!db_query($del_tile_definition_query, $tid)){
//handle failed query
$query_status = false;
}
return $query_status;
}
/**
* inserts tile content
*
* @param $data
* @param $status
*
* $content_format = array(
* 'tid' => 1,
* 'data' => array()
* );
* @return bool
*/
function mapapi_content_save($data, &$status){
// Check if the content is set.
// The other items are checked by mapapi_tile_save
if(!isset($data['content'])) {
return FALSE;
}
$content_query = "REPLACE INTO {mapapi_tile_content} (tid, mid, content) VALUES (%d, %d, '%s')";
$content_args[] = $data['tid'];
$content_args[] = $data['mid'];
$content_args[] = serialize($data['content']);
if(db_query($content_query, $content_args)){
return true;
}
return false;
}
function mapapi_content_delete($tid){
$content_query = "DELETE FROM {mapapi_tile_content} WHERE tid = %d";
if(db_query($content_query,$tid)){
return true;
}
return false;
}
/**
* Saves map data. $args has no practical use now, reserved for later addons
* @example
* $map_format = array(
* 'mid' => 1, //optional
* 'limit_x' => '6',
* 'limit_y' => '7',
* 'name' => 'New map name', //optional
* );
*
* @param array $data
* @param array $args
*/
function mapapi_map_save($data,$args = array()){
if(!isset($data['mid']) && isset($data['name'])){
//find the mid by map name
$query = "SELECT mid FROM {mapapi_map_definition} WHERE name LIKE ('%s')"; $query_arg[] = db_escape_string($data['name']);
$success = false;
if($result = db_query($query, $query_arg)){
$success = true;
}
if($success && db_num_rows($result) > 0){
$mid = db_fetch_array($result);
$data['mid'] = $mid['mid'];
} else {
$data['mid'] = db_next_id("mapapi_map_definition");
}
} else {
$data['mid'] = db_next_id("mapapi_map_definition");
}
return _mapapi_map_save($data,$args);
}
function _mapapi_map_save($data,$args = array()) {
//make sure $data is of correct format
if(!isset($data['mid']) || !isset($data['limit_x']) || !isset($data['limit_y']) || !isset($data['name'])){
return false;
}
$insert_query = "REPLACE INTO {mapapi_map_definition} (mid, limit_x, limit_y, name) VALUES (%d, %d, %d, '%s')";
$insert_query_args = array();
$insert_query_args[] = $data['mid'];
$insert_query_args[] = $data['limit_x'];
$insert_query_args[] = $data['limit_y'];
$insert_query_args[] = db_escape_string($data['name']);
if(db_query($insert_query,$insert_query_args)){
return true; //successful insert
} else {
return false; // unsuccessful insert
}
}
function mapapi_map_delete($mid){
$query_status = true; //return at the end of the function, This is not very descriptive at which stage this was set to false
//delete tile contents
$content_query = "DELETE FROM {mapapi_tile_content} WHERE {mapapi_tile_content}.mid = %d";
if(!db_query($content_query,$mid)){
$query_status = false;
}
// delete tiles
$tiles_query = "DELETE FROM {mapapi_tile_definition} WHERE mid = %d";
if(!db_query($tiles_query,$mid)){
$query_status = false;
}
//delete map
$map_query = "DELETE FROM {mapapi_map_definition} WHERE mid = %d";
if(!db_query($map_query,$mid)){
$query_status = false;
}
return $query_status; //all data from this particular $mid was deleted
}