/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "ultima/nuvie/core/nuvie_defs.h"
#include "ultima/nuvie/conf/configuration.h"
#include "ultima/nuvie/misc/u6_misc.h"
#include "ultima/nuvie/misc/u6_llist.h"
#include "ultima/nuvie/actors/actor.h"
#include "ultima/nuvie/actors/actor_manager.h"
#include "ultima/nuvie/views/view_manager.h"
#include "ultima/nuvie/gui/widgets/map_window.h"
#include "ultima/nuvie/core/events.h"
#include "ultima/nuvie/gui/widgets/msg_scroll.h"
#include "ultima/nuvie/gui/widgets/msg_scroll_new_ui.h"
#include "ultima/nuvie/core/effect.h" /* for initial fade-in */
#include "ultima/nuvie/core/tile_manager.h"
#include "ultima/nuvie/sound/sound_manager.h"
#include "ultima/nuvie/gui/gui.h"
#include "ultima/nuvie/core/game_clock.h"
#include "ultima/nuvie/screen/game_palette.h"
#include "ultima/nuvie/core/party.h"
#include "ultima/nuvie/core/weather.h"
#include "ultima/nuvie/script/script.h"
#include "ultima/nuvie/core/u6_objects.h"
#include "ultima/nuvie/gui/widgets/command_bar.h"
#include "ultima/nuvie/views/actor_view.h"
#include "ultima/nuvie/views/inventory_view.h"
#include "ultima/nuvie/gui/widgets/background.h"
#include "ultima/nuvie/keybinding/keys.h"

namespace Ultima {
namespace Nuvie {

#define TMP_MAP_BORDER 3

#define WRAP_VIEWP(p,p1,s) ((p1-p) < 0 ? (p1-p) + s : p1-p)

// This should make the mouse-cursor hovering identical to that in U6.
static const uint8 movement_array[9 * 9] = {
	9, 9, 2, 2, 2, 2, 2, 3, 3,
	9, 9, 9, 2, 2, 2, 3, 3, 3,
	8, 9, 9, 2, 2, 2, 3, 3, 4,
	8, 8, 8, 9, 2, 3, 4, 4, 4,
	8, 8, 8, 8, 1, 4, 4, 4, 4,
	8, 8, 8, 7, 6, 5, 4, 4, 4,
	8, 7, 7, 6, 6, 6, 5, 5, 4,
	7, 7, 7, 6, 6, 6, 5, 5, 5,
	7, 7, 6, 6, 6, 6, 6, 5, 5
};

static const Tile grid_tile = {
	0,
	false,
	false,
	false,
	false,
	false,
	true,
	false,
	false,
	0,
	//uint8 qty;
	//uint8 flags;

	0,
	0,
	0,

	{
		54, 255, 255, 255, 58, 255, 255, 255, 58, 255, 255, 255, 58, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		58, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		58, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		58, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
		255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255

	}
};

MapWindow::MapWindow(Configuration *cfg, Map *m): GUI_Widget(NULL, 0, 0, 0, 0) {

	config = cfg;
	config->value("config/GameType", game_type);

	uint16 x_off = Game::get_game()->get_game_x_offset();
	uint16 y_off = Game::get_game()->get_game_y_offset();

	GUI_Widget::Init(NULL, x_off, y_off, 0, 0);

	map = m;

	screen = NULL;
//surface = NULL;
	anim_manager = NULL;

	cur_x = 0;
	mousecenter_x = 0;
	cur_y = 0;
	mousecenter_y = 0;
	cur_x_add = cur_y_add = 0;
	vel_x = vel_y = 0;
	last_boundary_fill_x = last_boundary_fill_y = 0;

	cursor_x = 0;
	cursor_y = 0;
	show_cursor = false;
	show_use_cursor = false;
	show_grid = false;
	x_ray_view = X_RAY_OFF;
	freeze_blacking_location = false;
	enable_blacking = true;

	new_thumbnail = false;
	thumbnail = NULL;
	overlay = NULL;
	overlay_level = MAP_OVERLAY_DEFAULT;

	cur_level = 0;
	map_width = map->get_width(cur_level);

	tmp_map_buf = NULL;

	selected_obj = NULL;
	look_obj = NULL;
	look_actor = NULL;
	walking = false;
	looking = false;
	config->value(config_get_game_key(config) + "/map_tile_lighting", using_map_tile_lighting, game_type == NUVIE_GAME_MD ? false : true);
	config->value("config/input/enable_doubleclick", enable_doubleclick, true);
	config->value("config/input/look_on_left_click", look_on_left_click, true);
	set_use_left_clicks();
	config->value("config/input/walk_with_left_button", walk_with_left_button, true);
	set_walk_button_mask();
	config->value("config/cheats/min_brightness", min_brightness, 0);
	original_obj_loc = MapCoord(0, 0, 0);

	roof_mode = Game::get_game()->is_roof_mode();
	roof_tiles = NULL;

	draw_brit_lens_anim = false;
	draw_garg_lens_anim = false;

	window_updated = true;
	roof_display = ROOF_DISPLAY_NORMAL;

	lighting_update_required = true;

	set_interface();
}

MapWindow::~MapWindow() {
	set_overlay(NULL); // free
	free(tmp_map_buf);
	delete anim_manager;
	if (roof_tiles) {
		SDL_FreeSurface(roof_tiles);
	}
}

bool MapWindow::init(TileManager *tm, ObjManager *om, ActorManager *am) {
// int game_type; Why is this local, and retrieved again here? --SB-X

	game = Game::get_game();

	tile_manager = tm;
	obj_manager = om;
	actor_manager = am;
	uint16 map_w = 11, map_h = 11;
	border_width = game->get_background()->get_border_width();
	if (!game->is_orig_style()) {
		uint16 game_width = game->get_game_width();
		uint16 game_height = game->get_game_height();

		if (game->is_original_plus_cutoff_map()) {
			map_center_xoff = 0;
			game_width -= border_width; // don't go over border
		} else if (game->is_original_plus_full_map()) {
			map_center_xoff = (border_width / 16) % 16;
		} else { // new style
			map_center_xoff = 0;
		}
		map_w = game_width / 16;
		map_h = game_height / 16;
		if (game_width % 16 != 0 || map_w % 2 == 0) { // not just the right size
			map_w += 1;
			if (map_w % 2 == 0) // need odd number of tiles to center properly
				map_w += 1;
		}
		if (game_height % 16 != 0 || map_h % 2 == 0) { // not just the right size
			map_h += 1;
			if (map_h % 2 == 0) // need odd number of tiles to center properly
				map_h += 1;
		}
		offset_x -= (map_w * 16 - game_width) / 2;
		offset_y -= (map_h * 16 - game_height) / 2;
	} else
		map_center_xoff = 0;
	anim_manager = new AnimManager(offset_x, offset_y);

	cursor_tile = tile_manager->get_cursor_tile();
	use_tile = tile_manager->get_use_tile();

	area.left = offset_x;
	area.top = offset_y;

	set_windowSize(map_w, map_h);

// hide the window until game is fully loaded and does fade-in
	get_overlay(); // this allocates `overlay`
	overlay_level = MAP_OVERLAY_ONTOP;
	assert(SDL_FillRect(overlay, NULL, game->get_palette()->get_bg_color()) == 0);

	wizard_eye_info.eye_tile = tile_manager->get_tile(TILE_U6_WIZARD_EYE);
	wizard_eye_info.moves_left = 0;
	wizard_eye_info.caller = NULL;

	if (roof_mode)
		loadRoofTiles();

	return true;
}

void MapWindow::set_use_left_clicks() {
	if (enable_doubleclick || look_on_left_click)
		set_accept_mouseclick(true, USE_BUTTON); // allow left clicks
	else
		set_accept_mouseclick(false, USE_BUTTON); // disallow left clicks
}

bool MapWindow::set_windowSize(uint16 width, uint16 height) {
	win_width = width;
	win_height = height;
	area.setWidth(win_width * 16);
	area.setHeight(win_height * 16);

// We make the temp map +1 bigger on the top and left edges
// and +2 bigger on the bottom and right edges

// The +1 is for the boundary fill function

// The additional +1 on the right/bottom edges is needed
// to hide objects on boundarys when wall is in darkness

	tmp_map_width = win_width + (TMP_MAP_BORDER * 2);// + 1;
	tmp_map_height = win_height + (TMP_MAP_BORDER * 2);// + 1;

	tmp_map_buf = (uint16 *)nuvie_realloc(tmp_map_buf, tmp_map_width * tmp_map_height * sizeof(uint16));
	if (tmp_map_buf == NULL)
		return false;

// if(surface != NULL)
//   delete surface;
// surface = new Surface;

// if(surface->init(win_width*16,win_height*16) == false)
//   return false;
	if (game->is_orig_style()) {
		clip_rect.left = area.left + 8;
		clip_rect.setWidth((win_width - 1) * 16);
		clip_rect.setHeight((win_height - 1) * 16);

		if (game_type == NUVIE_GAME_U6)
			clip_rect.top = area.top + 8;
		else {
			clip_rect.top = area.top + 16;
			clip_rect.bottom -= 16;
		}
	} else {
		clip_rect.left = game->get_game_x_offset();
		clip_rect.top = game->get_game_y_offset();
		if (game->is_original_plus_cutoff_map())
			clip_rect.setWidth(game->get_game_width() - border_width - 1);
		else
			clip_rect.setWidth(game->get_game_width());
		clip_rect.setHeight(game->get_game_height());
	}
	anim_manager->set_area(clip_rect);

	reset_mousecenter();

	updateBlacking();

	return true;
}

void MapWindow::set_walk_button_mask() {
	if (walk_with_left_button)
		walk_button_mask = (BUTTON_MASK(USE_BUTTON) | BUTTON_MASK(WALK_BUTTON));
	else
		walk_button_mask = BUTTON_MASK(WALK_BUTTON);
}

void MapWindow::set_show_cursor(bool state) {
	ActorView *actor_view = game->get_view_manager()->get_actor_view();
	InventoryView *inventory_view = game->get_view_manager()->get_inventory_view();
	if (actor_view)
		actor_view->set_show_cursor(false);
	if (inventory_view)
		inventory_view->set_show_cursor(false);

	show_cursor = state;
}

void MapWindow::set_show_use_cursor(bool state) {
	ActorView *actor_view = game->get_view_manager()->get_actor_view();
	InventoryView *inventory_view = game->get_view_manager()->get_inventory_view();
	if (actor_view)
		actor_view->set_show_cursor(false);
	if (inventory_view)
		inventory_view->set_show_cursor(false);
	show_use_cursor = state;
}

void MapWindow::set_show_grid(bool state) {
	show_grid = state;
}

/**
 * cheat_off is needed to turn the X-ray cheat off (set to X_RAY_OFF)
 * Convoluted logic is so we don't slow down boundaryFill with an extra check
 */
void MapWindow::set_x_ray_view(X_RayType state, bool cheat_off) {
	if (x_ray_view == X_RAY_CHEAT_ON) {
		if (state == X_RAY_ON) // X_RAY_CHEAT_ON takes precedence to preserve X-ray cheat
			return;
		else if (state == X_RAY_OFF) {
			if (!cheat_off) { // not turning X-ray cheat off
				if (game->are_cheats_enabled()) // don't turn off when cheats are enabled
					return;
				else  // need to preserve X-ray cheat setting when cheats are off
					state = X_RAY_CHEAT_OFF;
			}
		}
	} else if (x_ray_view == X_RAY_CHEAT_OFF) {
		if (state == X_RAY_OFF) // X_RAY_CHEAT_OFF takes precedence to preserve X-ray cheat
			return;
		else if (state == X_RAY_ON) // need to preserve X-ray cheat setting when cheats are off
			state = X_RAY_CHEAT_ON;
	}
	x_ray_view = state;
	updateBlacking();
}

void MapWindow::set_freeze_blacking_location(bool state) {
	freeze_blacking_location = state;
}

void MapWindow::set_enable_blacking(bool state) {
	enable_blacking = state;
	updateBlacking();
}

void MapWindow::set_walking(bool state) {
	if (state && game->get_view_manager()->gumps_are_active()) //we don't allow walking while gumps are active.
		return;

	walking = state;
}

void MapWindow::moveLevel(uint8 new_level) {
	cur_level = new_level;

	updateBlacking();
}

void MapWindow::moveMap(sint16 new_x, sint16 new_y, sint8 new_level, uint8 new_x_add, uint8 new_y_add) {

	map_width = map->get_width(new_level);

	if (new_x < 0) {
		new_x = map_width + new_x;
	} else {
		new_x %= map_width;
	}

//printf("cur_x = %d\n",new_x);
	cur_x = new_x;
	cur_y = new_y;
	cur_level = new_level;
	cur_x_add = new_x_add;
	cur_y_add = new_y_add;
	updateBlacking();

}

void MapWindow::moveMapRelative(sint16 rel_x, sint16 rel_y) {
	moveMap(cur_x + rel_x, cur_y + rel_y, cur_level);
}

/* Move map by relative pixel amount.
 */
void MapWindow::shiftMapRelative(sint16 rel_x, sint16 rel_y) {
	uint8 tile_pitch = 16;
	uint32 total_px = (cur_x * tile_pitch) + cur_x_add,
	       total_py = (cur_y * tile_pitch) + cur_y_add;
	total_px += rel_x;
	total_py += rel_y;
	moveMap(total_px / tile_pitch, total_py / tile_pitch, cur_level,
	        total_px % tile_pitch, total_py % tile_pitch);
}

/* Center MapWindow on a location.
 */
void MapWindow::centerMap(uint16 x, uint16 y, uint8 z) {
	moveMap(x - ((win_width - 1 - map_center_xoff) / 2) , y - ((win_height - 1) / 2), z);
}

void MapWindow::centerMapOnActor(Actor *actor) {
	uint16 x;
	uint16 y;
	uint8 z;

	actor->get_location(&x, &y, &z);

	centerMap(x, y, z);

	return;
}

void MapWindow::centerCursor() {

	cursor_x = (win_width - 1 - map_center_xoff) / 2;
	cursor_y = (win_height - 1) / 2;

	return;
}

void MapWindow::moveCursor(sint16 new_x, sint16 new_y) {
	if (new_x < 0 || new_x >= win_width)
		return;

	if (new_y < 0 || new_y >= win_height)
		return;

	cursor_x = new_x;
	cursor_y = new_y;

	return;
}

void MapWindow::moveCursorRelative(sint16 rel_x, sint16 rel_y) {
	moveCursor(cursor_x + rel_x, cursor_y + rel_y);
}

bool MapWindow::is_on_screen(uint16 x, uint16 y, uint8 z) {
	if (z == cur_level && WRAP_VIEWP(cur_x, x, map_width) < win_width && y >= cur_y && y < cur_y + win_height) {
		if (tile_is_black(x, y) == false) {
			return true;
		}
	}

	return false;
}

/**
 * Can we display an object at this location on the tmp map buffer.
 *
 * @param x coord on the tmp buf
 * @param y coord on the tmp buf
 * @param obj object to display
 * @return
 */
bool MapWindow::can_display_obj(uint16 x, uint16 y, Obj *obj) {
	uint16 tile_num = tmp_map_buf[y * tmp_map_width + x];
	if (tile_num == 0) //don't draw object if area is in darkness.
		return false;
	else {
		if (x >= tmp_map_width - 1 || y >= tmp_map_height - 1)
			return false;

		// We don't show objects on walls if the area to the right or bottom of the wall is in darkness
		if (tmp_map_buf[y * tmp_map_width + (x + 1)] == 0 || tmp_map_buf[(y + 1)*tmp_map_width + x] == 0) {
			Tile *tile = tile_manager->get_tile(tile_num);
			if (((tile->flags1 & TILEFLAG_WALL) || (game_type == NUVIE_GAME_U6 && obj->obj_n == OBJ_U6_BARS)))
				return false;
		}
	}

	return true;
}

bool MapWindow::tile_is_black(uint16 x, uint16 y, Obj *obj) {
	if (game->using_hackmove())
		return false;
	if (!MapCoord(x, y, cur_level).is_visible()) // tmpBufTileIsBlack will crash if called (doesn't happen in gdb)
		return true;
	uint16 wrapped_x = WRAP_VIEWP(cur_x, x, map_width);
	if (tmpBufTileIsBlack(wrapped_x + TMP_MAP_BORDER, y - cur_y + TMP_MAP_BORDER))
		return true;
	else if (obj) {
		Tile *tile = tile_manager->get_original_tile(obj_manager->get_obj_tile_num(obj->obj_n) + obj->frame_n);
		if (!tile || (tmpBufTileIsBlack(wrapped_x + TMP_MAP_BORDER + 1, y - cur_y + TMP_MAP_BORDER)  && !(tile->flags1 & TILEFLAG_WALL))
		        || (tmpBufTileIsBlack(wrapped_x + TMP_MAP_BORDER, y - cur_y + TMP_MAP_BORDER + 1) && !(tile->flags1 & TILEFLAG_WALL)))
			return true;
	}

	return false;
}

const char *MapWindow::look(uint16 x, uint16 y, bool show_prefix) {
	Actor *actor;

	if (tmp_map_buf[(y + TMP_MAP_BORDER) * tmp_map_width + (x + TMP_MAP_BORDER)] == 0) //black area
		return "darkness."; // nothing to see here. ;)

	uint16 wrapped_x = WRAPPED_COORD(cur_x + x, cur_level);
	actor = actor_manager->get_actor(wrapped_x, cur_y + y, cur_level);
	if (actor != NULL && actor->is_visible())
		return actor_manager->look_actor(actor, show_prefix);

	return map->look(wrapped_x, cur_y + y, cur_level);
}


Obj *MapWindow::get_objAtCursor(bool for_use /* = false */) {
	MapCoord coord = get_cursorCoord();
	return get_objAtCoord(coord, OBJ_SEARCH_TOP, OBJ_EXCLUDE_IGNORED, for_use);
}

Obj *MapWindow::get_objAtCoord(MapCoord coord, bool top_obj, bool include_ignored_objects, bool for_use /* = false */) {
	if (tile_is_black(coord.x, coord.y))
		return NULL; // nothing to see here. ;)

	Obj *obj = obj_manager->get_obj(coord.x, coord.y, coord.z, top_obj, include_ignored_objects);
	// Savage Empire Create Object from Tile
	if (for_use && game_type == NUVIE_GAME_SE && obj == NULL) {
		Script *script = game->get_script();
		uint16 map_win_x = WRAP_VIEWP(cur_x, coord.x, map_width);
		uint16 map_win_y = coord.y - cur_y;
		// Check that x,y is in tmp_map_buf
		if (is_on_screen(coord.x, coord.y, coord.z)) {
			uint16 tile_n = tmp_map_buf[(map_win_y + TMP_MAP_BORDER) * tmp_map_width + (map_win_x + TMP_MAP_BORDER)];
			uint16 obj_n = script->call_get_tile_to_object_mapping(tile_n);
			if (obj_n != 0) {
				obj = obj_manager->get_tile_obj(obj_n);
				obj->x = coord.x;
				obj->y = coord.y;
				obj->z = coord.z;
			}
		}
	}
	return obj;
}

Actor *MapWindow::get_actorAtCursor() {
//Actor *actor;

	if (tmp_map_buf[(cursor_y + TMP_MAP_BORDER) * tmp_map_width + (cursor_x + TMP_MAP_BORDER)] == 0) //black area
		return NULL; // nothing to see here. ;)

	return actor_manager->get_actor(WRAPPED_COORD(cur_x + cursor_x, cur_level), cur_y + cursor_y, cur_level);
}

MapCoord MapWindow::get_cursorCoord() {
	return (MapCoord(WRAPPED_COORD(cur_x + cursor_x, cur_level), cur_y + cursor_y, cur_level));
}

void MapWindow::get_level(uint8 *level) {
	*level = cur_level;
}

void MapWindow::get_pos(uint16 *x, uint16 *y, uint8 *px, uint8 *py) {
	*x = cur_x;
	*y = cur_y;
	if (px)
		*px = cur_x_add;
	if (py)
		*py = cur_y_add;
}

void MapWindow::get_windowSize(uint16 *width, uint16 *height) {
	*width = win_width;
	*height = win_height;
}

/* Returns true if the location at the coordinates is visible on the map window.
 */
bool MapWindow::in_window(uint16 x, uint16 y, uint8 z) {
	return ((z == cur_level && WRAP_VIEWP(cur_x, x, map_width) < win_width
	         && y >= cur_y && y <= (cur_y + win_height)));
}


/* Update player position if walking to mouse cursor. Update map position.
 */
void MapWindow::update() {
	GameClock *clock = game->get_clock();
	Events *event = game->get_event();
	static bool game_started = false; // set to true on the first update()
	static uint32 last_update_time = clock->get_ticks();
	uint32 update_time = clock->get_ticks();

	// do fade-in on the first update (game has loaded now)
	if (game_started == false) {
//        new FadeEffect(FADE_PIXELATED_ONTOP, FADE_IN, 0x31);
		new GameFadeInEffect(game->get_palette()->get_bg_color());
		game_started = true;
	}

	anim_manager->update(); // update animations

	if (vel_x || vel_y) { // this slides the map
		if ((update_time - last_update_time) >= 100) { // only move every 10th sec
			sint32 sx = vel_x / 10, sy = vel_y / 10;
			if (vel_x && !sx) // move even if vel_x/vel_y was < 10
				sx = (vel_x < 0) ? -1 : 1;
			if (vel_y && !sy)
				sy = (vel_y < 0) ? -1 : 1;

			shiftMapRelative(sx, sy);
			last_update_time = update_time;
		}
	}

	if (walking) {
		if (Events::get()->getButtonState() & walk_button_mask) {
			if (game->user_paused())
				return;

			int mx, my; // bit-AND buttons with mouse state to test
			screen->get_mouse_location(&mx, &my);

			if (is_wizard_eye_mode()) {
//              int wx, wy;
//              mouseToWorldCoords(mx, my, wx, wy);
				sint16 rx, ry;
				get_movement_direction((uint16)mx, (uint16)my, rx, ry);
				moveMapRelative((rx == 0) ? 0 : rx < 0 ? -1 : 1,
				                (ry == 0) ? 0 : ry < 0 ? -1 : 1);
				wizard_eye_update();
			} else {
				//DEBUG(0, LEVEL_DEBUGGING, "MOUSE WALKING...\n");
				event->walk_to_mouse_cursor((uint32)mx,
				                            (uint32)my);
			}
		} else
			walking = false;
	}

	KeyBinder *keybinder = game->get_keybinder();
	if (keybinder->is_joy_repeat_enabled() && (event->get_mode() == MOVE_MODE || is_wizard_eye_mode())
	        && keybinder->get_next_joy_repeat_time() < clock->get_ticks()) {
		Common::KeyCode key = keybinder->get_key_from_joy_walk_axes();
		if (key != Common::KEYCODE_INVALID) {
			Common::Event sdl_event;
			sdl_event.type = Common::EVENT_KEYDOWN;
			sdl_event.kbd.keycode = key;
			sdl_event.kbd.flags = 0;
			if (GUI::get_gui()->HandleEvent(&sdl_event) == GUI_PASS)
				event->handleEvent(&sdl_event);
		}
	}
}

// moved from updateBlacking() so you don't have to update all blacking (SB-X)
void MapWindow::updateAmbience() {
	lighting_update_required = true;
}

// moved from updateBlacking() so you don't have to update all blacking (SB-X)
void MapWindow::createLightOverlay() {
	//Dusk starts at 19:00
	//It's completely dark by 20:00
	//Dawn starts at 5:00
	//It's completely bright by 6:00
	//Dusk and dawn operate by changing the ambient light, not by changing the radius of the avatar's light globe

	if (!screen)
		return;
	bool dawn_or_dusk = false;
	uint8 cur_min_brightness;
	if (game->are_cheats_enabled())
		cur_min_brightness = min_brightness;
	else
		cur_min_brightness = 0;

	GameClock *clock = game->get_clock();
	Weather *weather = game->get_weather();

	int h = clock->get_hour();
	int a;
	if (x_ray_view >= X_RAY_ON)
		a = 255;
	else if (in_dungeon_level())
		a = cur_min_brightness;
	else if (weather->is_eclipse())  //solar eclipse
		a = cur_min_brightness;
	else if (h == 19) { //Dusk -- Smooth transition between 255 and min_brightness during first 59 minutes
		if (screen->get_lighting_style() == LIGHTING_STYLE_SMOOTH) {
			dawn_or_dusk = true;
			a = 255 - (uint8)((255.0f - cur_min_brightness) * (float)clock->get_minute() / 59.0f);
		} else {
			a = 20 * (6 - clock->get_minute() / 10);
			if (a < cur_min_brightness)
				a = cur_min_brightness;
		}
	} else if (h == 5) { //Dawn -- Smooth transition between min_brightness and 255 during first 59 minutes
		if (screen->get_lighting_style() == LIGHTING_STYLE_SMOOTH) {
			dawn_or_dusk = true;
			a = cur_min_brightness + (255.0f - cur_min_brightness) * (float)clock->get_minute() / 59.0f;
		} else {
			a = 20 * (1 +  clock->get_minute() / 10);
			if (a < cur_min_brightness)
				a = cur_min_brightness;
		}
	} else if (h > 5 && h < 19) //Day
		a = 255;
	else //Night
		a = cur_min_brightness;

	if (a > 255)
		a = 255;
	bool party_light_source;
	// smooth seems to need an enormous range in order to have smooth transitions
	if (a < (screen->get_lighting_style() == LIGHTING_STYLE_SMOOTH ? 248 : 81) &&
	        (game->get_party()->has_light_source() || clock->get_timer(GAMECLOCK_TIMER_U6_LIGHT) != 0)) { //FIXME U6 specific
		party_light_source = true;
		if (screen->get_lighting_style() == LIGHTING_STYLE_SMOOTH) {
			if (!dawn_or_dusk) // preserve a when dusk or dawn so we have the correct opacity
				a = cur_min_brightness;
		} else
			a = 80;
	} else
		party_light_source = false;
	screen->set_ambient(a);

	//Clear the opacity map
	screen->clearalphamap8(0, 0, win_width, win_height, screen->get_ambient(), party_light_source);

	updateLighting();

	lighting_update_required = false;
}

void MapWindow::updateLighting() {
	uint16 x, y;
	if (using_map_tile_lighting) {
		uint16 *ptr = tmp_map_buf;
		for (y = 0; y < tmp_map_height; y++) {
			for (x = 0; x < tmp_map_width; x++) {
				if (tmp_map_buf[x + y * tmp_map_width] != 0) {
					Tile *tile = tile_manager->get_tile(*ptr);
					if (GET_TILE_LIGHT_LEVEL(tile) > 0)
						screen->drawalphamap8globe(x - TMP_MAP_BORDER, y - TMP_MAP_BORDER, GET_TILE_LIGHT_LEVEL(tile));

					U6LList *obj_list = obj_manager->get_obj_list(cur_x - TMP_MAP_BORDER + x, cur_y - TMP_MAP_BORDER + y, cur_level); //FIXME wrapped coords.
					if (obj_list) {
						for (U6Link *link = obj_list->start(); link != NULL; link = link->next) {
							Obj *obj = (Obj *)link->data;
							tile = tile_manager->get_tile(obj_manager->get_obj_tile_num(obj) + obj->frame_n); //FIXME do we need to check the light for each tile in a multi-tile object.
							if (GET_TILE_LIGHT_LEVEL(tile) > 0 && can_display_obj(x, y, obj))
								screen->drawalphamap8globe(x - TMP_MAP_BORDER, y - TMP_MAP_BORDER, GET_TILE_LIGHT_LEVEL(tile));
						}
					}
				}
				ptr++;
			}
		}

		for (Std::vector<TileInfo>::iterator ti = m_ViewableMapTiles.begin();
		        ti != m_ViewableMapTiles.end(); ti++) {
			if (GET_TILE_LIGHT_LEVEL((*ti).t) > 0)
				screen->drawalphamap8globe((*ti).x, (*ti).y, GET_TILE_LIGHT_LEVEL((*ti).t));
		}
	}

	/* draw light coming from the actor
	   Wisps can change the light level depending on their current tile so we can't use actor->light for an actor's innate lighting.
	*/
	Actor *actor;

	for (uint16 i = 0; i < 256; i++) {
		actor = actor_manager->get_actor(i);

		if (actor->z == cur_level) {
			if (actor->x >= cur_x - TMP_MAP_BORDER && actor->x < cur_x + win_width + TMP_MAP_BORDER) {
				if (actor->y >= cur_y - TMP_MAP_BORDER && actor->y < cur_y + win_height + TMP_MAP_BORDER) {
					if (tmp_map_buf[(actor->y - cur_y + TMP_MAP_BORDER) * tmp_map_width + (actor->x - cur_x + TMP_MAP_BORDER)] != 0) {
						uint8 light = actor->get_light_level();
						if (light > 0) {
							screen->drawalphamap8globe(actor->x - cur_x, actor->y - cur_y, light);
						}
					}
				}
			}
		}
	}
}

void MapWindow::updateBlacking() {
	generateTmpMap();

	updateAmbience();

	m_ViewableObjects.clear();
/// m_ViewableObjTiles.clear();

	draw_brit_lens_anim = false;
	draw_garg_lens_anim = false;

	window_updated = true;
}

void MapWindow::Display(bool full_redraw) {
	uint16 i, j;
	uint16 *map_ptr;
// uint16 map_width;
	Tile *tile;
//byte *ptr;

	if (lighting_update_required) {
		createLightOverlay();
	}

//map_ptr = map->get_map_data(cur_level);
// map_width = map->get_width(cur_level);

//map_ptr += cur_y * map_width + cur_x;
	map_ptr = tmp_map_buf;
	map_ptr += (TMP_MAP_BORDER * tmp_map_width + TMP_MAP_BORDER);// * sizeof(uint16); //remember our tmp map is TMP_MAP_BORDER bigger all around.

	for (i = 0; i < win_height; i++) {
		for (j = 0; j < win_width; j++) {
			sint16 draw_x = area.left + (j * 16), draw_y = area.top + (i * 16);
			//draw_x -= (cur_x_add <= draw_x) ? cur_x_add : draw_x;
			//draw_y -= (cur_y_add <= draw_y) ? cur_y_add : draw_y;
			draw_x -= cur_x_add;
			draw_y -= cur_y_add;
			if (map_ptr[j] == 0)
				screen->clear(draw_x, draw_y, 16, 16, &clip_rect); //blackout tile.
			else {
				if (map_ptr[j] >= 16 && map_ptr[j] < 48) { //lay down the base tile for shoreline tiles
					tile = tile_manager->get_anim_base_tile(map_ptr[j]);
					screen->blit(draw_x, draw_y, (byte *)tile->data, 8, 16, 16, 16, tile->transparent, &clip_rect);
				}

				tile = tile_manager->get_tile(map_ptr[j]);
				screen->blit(draw_x, draw_y, (byte *)tile->data, 8, 16, 16, 16, tile->transparent, &clip_rect);

			}

		}
		//map_ptr += map_width;
		map_ptr += tmp_map_width ;//* sizeof(uint16);
	}

	drawObjs();

//drawAnims();

	if (roof_mode && roof_display != ROOF_DISPLAY_OFF) {
		drawRoofs();
	}

	if (game->get_clock()->get_timer(GAMECLOCK_TIMER_U6_STORM) != 0) //FIXME u6 specific.
		drawRain();

	if (show_grid) {
		drawGrid();
	}

	if (show_cursor) {
		screen->blit(area.left + cursor_x * 16, area.top + cursor_y * 16, (byte *)cursor_tile->data, 8, 16, 16, 16, true, &clip_rect);
	}

	if (show_use_cursor) {
		screen->blit(area.left + cursor_x * 16, area.top + cursor_y * 16, (byte *)use_tile->data, 8, 16, 16, 16, true, &clip_rect);
	}

// screen->fill(0,8,8,win_height*16-16,win_height*16-16);

	screen->blitalphamap8(area.left, area.top, &clip_rect);

	if (game->get_clock()->get_timer(GAMECLOCK_TIMER_U6_INFRAVISION) != 0)
		drawActors();

	if (overlay && overlay_level == MAP_OVERLAY_DEFAULT)
		screen->blit(area.left, area.top, (byte *)(overlay->getPixels()), overlay->format.bpp(), overlay->w, overlay->h, overlay->pitch, true, &clip_rect);

	drawAnims(true);

	if (new_thumbnail)
		create_thumbnail();

	if (is_wizard_eye_mode()) {
		uint16 we_x = mousecenter_x * 16 + area.left;
		if (game->is_original_plus_full_map())
			we_x -= ((map_center_xoff + 1) / 2) * 16;
		screen->blit(we_x, mousecenter_y * 16 + area.top, (byte *)wizard_eye_info.eye_tile->data, 8, 16, 16, 16, true, &clip_rect);
	}

	if (game->is_orig_style())
		drawBorder();

	if (overlay && overlay_level == MAP_OVERLAY_ONTOP)
		screen->blit(area.left, area.top, (byte *)(overlay->getPixels()), overlay->format.bpp(), overlay->w, overlay->h, overlay->pitch, true, &clip_rect);

// ptr = (byte *)screen->get_pixels();
// ptr += 8 * screen->get_pitch() + 8;

// screen->blit(8,8,ptr,8,(win_width-1) * 16,(win_height-1) * 16, win_width * 16, false);

	if (game->is_orig_style())
		screen->update(area.left + 8, area.top + 8, win_width * 16 - 16, win_height * 16 - 16);
	else if (game->is_original_plus_cutoff_map())
		screen->update(Game::get_game()->get_game_x_offset(), Game::get_game()->get_game_y_offset(), game->get_game_width() - border_width - 1, game->get_game_height());
	else
		screen->update(Game::get_game()->get_game_x_offset(), Game::get_game()->get_game_y_offset(), game->get_game_width(), game->get_game_height());

	if (window_updated) {
		window_updated = false;
		game->get_sound_manager()->update_map_sfx();
	}

}

void MapWindow::drawActors() {
	uint16 i;
	Actor *actor;

	for (i = 0; i < 256; i++) {
		actor = actor_manager->get_actor(i);

		if (actor->z == cur_level) {
			uint8 x = WRAP_VIEWP(cur_x, actor->x, map_width);
			if (x < win_width) { //actor->x >= cur_x && actor->x < cur_x + win_width)
				if (actor->y >= cur_y && actor->y < cur_y + win_height) {
					if (tmp_map_buf[(actor->y - cur_y + TMP_MAP_BORDER) * tmp_map_width + (x + TMP_MAP_BORDER)] != 0) {
						drawActor(actor);
					}
				}
			}
		}
	}
}

//FIX need a function for multi-tile actors.
inline void MapWindow::drawActor(Actor *actor) {
	if (actor->is_visible() /* && actor->obj_n != 0*/
	        && (!(actor->obj_flags & OBJ_STATUS_INVISIBLE) || actor->is_in_party() || actor == actor_manager->get_player())
	        && actor->get_corpser_flag() == false) {
		Tile *tile = tile_manager->get_tile(actor->get_tile_num() + actor->frame_n);
		Tile *rtile = 0;

		if (actor->obj_flags & OBJ_STATUS_INVISIBLE) {
			rtile = new Tile(*tile);
			for (int x = 0; x < 256; x++)
				if (rtile->data[x] != 0x00)
					rtile->data[x] = 0xFF;
				else
					rtile->data[x] = 0x0B;
		} else if (actor->status_flags & ACTOR_STATUS_PROTECTED) { // actually this doesn't appear when using a protection ring
			rtile = new Tile(*tile);
			for (int x = 0; x < 256; x++)
				if (rtile->data[x] == 0x00)
					rtile->data[x] = 0x0C;
		} else if (actor->is_cursed()) {
			rtile = new Tile(*tile);
			for (int x = 0; x < 256; x++)
				if (rtile->data[x] == 0x00)
					rtile->data[x] = 0x9;
		}
		uint16 wrapped_x = WRAP_VIEWP(cur_x, actor->x, map_width);
		if (rtile != 0) {
			drawNewTile(rtile, wrapped_x, actor->y - cur_y, false);
			drawNewTile(rtile, wrapped_x, actor->y - cur_y, true);
			delete rtile;
		} else {
			drawTile(tile, wrapped_x, actor->y - cur_y, false);
			drawTile(tile, wrapped_x, actor->y - cur_y, true);
			if (game->get_clock()->get_timer(GAMECLOCK_TIMER_U6_INFRAVISION) != 0) {
				Std::list<Obj *> *surrounding_objs = actor->get_surrounding_obj_list();

				if (surrounding_objs) {
					Std::list<Obj *>::iterator obj_iter;
					for (obj_iter = surrounding_objs->begin(); obj_iter != surrounding_objs->end(); obj_iter++) {
						Obj *obj = *obj_iter;
						Tile *t = tile_manager->get_original_tile(obj_manager->get_obj_tile_num(obj->obj_n) + obj->frame_n);
						uint16 wrapped_obj_x = WRAP_VIEWP(cur_x, obj->x, map_width);
						drawTile(t, wrapped_obj_x, obj->y - cur_y, false);
						drawTile(t, wrapped_obj_x, obj->y - cur_y, true); // doesn't seem needed but will do it anyway (for now)
					}
				}
			}
		}
	}
}

void MapWindow::drawObjs() {
//FIX we need to make this more efficent.

	drawObjSuperBlock(true, false); //draw force lower objects
	drawObjSuperBlock(false, false); //draw lower objects

	drawActors();

	drawAnims(false);

	drawObjSuperBlock(false, true); //draw top objects

	drawLensAnim();
	return;
}

inline void MapWindow::drawLensAnim() {
	if (draw_brit_lens_anim) {
		if (cur_x < 0x399)
			drawTile(tile_manager->get_tile(TILE_U6_BRITANNIAN_LENS_ANIM_2), 0x398 - cur_x, 0x353 - cur_y, true);
		if (cur_x + win_width > 0x39a)
			drawTile(tile_manager->get_tile(TILE_U6_BRITANNIAN_LENS_ANIM_1), 0x39a - cur_x, 0x353 - cur_y, true);
	}

	if (draw_garg_lens_anim) {
		if (cur_x < 0x39d)
			drawTile(tile_manager->get_tile(TILE_U6_GARGOYLE_LENS_ANIM_2), 0x39c - cur_x, 0x353 - cur_y, true);
		if (cur_x + win_width > 0x39e)
			drawTile(tile_manager->get_tile(TILE_U6_GARGOYLE_LENS_ANIM_1), 0x39e - cur_x, 0x353 - cur_y, true);
	}
}

void MapWindow::drawObjSuperBlock(bool draw_lowertiles, bool toptile) {
	U6Link *link;
	U6LList *obj_list;
	Obj *obj;
	sint16 x, y;
	uint16 stop_x, stop_y;

	if (cur_x < 0)
		stop_x = 0;
	else
		stop_x = cur_x;

	if (cur_y < 0)
		stop_y = 0;
	else
		stop_y = cur_y;

	for (y = cur_y + win_height; y >= stop_y; y--) {
		for (x = cur_x + win_width; x >= stop_x; x--) {
			obj_list = obj_manager->get_obj_list(x, y, cur_level);
			if (obj_list) {
				for (link = obj_list->start(); link != NULL; link = link->next) {
					obj = (Obj *)link->data;
					drawObj(obj, draw_lowertiles, toptile);
				}
			}
		}
	}

}

inline void MapWindow::drawObj(Obj *obj, bool draw_lowertiles, bool toptile) {
	sint16 x, y;
	Tile *tile;

	y = obj->y - cur_y;
	x = WRAP_VIEWP(cur_x, obj->x, map_width);

	if (x < 0 || y < 0)
		return;

	if (window_updated) {
		m_ViewableObjects.push_back(obj);

		if (game_type == NUVIE_GAME_U6 && cur_level == 0 && obj->y == 0x353 && tmp_map_buf[(y + TMP_MAP_BORDER)*tmp_map_width + (x + TMP_MAP_BORDER)] != 0) {
			if (obj->obj_n == 394 && obj->x == 0x399) {
				draw_brit_lens_anim = true;
			} else if (obj->obj_n == 396 && obj->x == 0x39d) {
				draw_garg_lens_anim = true;
			}
		}
	}

//don't show invisible objects.
	if (obj->status & OBJ_STATUS_INVISIBLE)
		return;

	tile = tile_manager->get_original_tile(obj_manager->get_obj_tile_num(obj) + obj->frame_n);

	if (draw_lowertiles == false && (tile->flags3 & 0x4) && toptile == false) //don't display force lower tiles.
		return;

	if (draw_lowertiles == true && !(tile->flags3 & 0x4))
		return;

	if (tmp_map_buf[(y + TMP_MAP_BORDER)*tmp_map_width + (x + TMP_MAP_BORDER)] == 0) //don't draw object if area is in darkness.
		return;
	else {
		// We don't show objects on walls if the area to the right or bottom of the wall is in darkness
		if (tmp_map_buf[(y + TMP_MAP_BORDER)*tmp_map_width + (x + TMP_MAP_BORDER + 1)] == 0 || tmp_map_buf[(y + TMP_MAP_BORDER + 1)*tmp_map_width + (x + TMP_MAP_BORDER)] == 0) {
			if ((!(tile->flags1 & TILEFLAG_WALL) || (game_type == NUVIE_GAME_U6 && obj->obj_n == OBJ_U6_BARS)))
				return;
		}
	}

	drawTile(tile, x, obj->y - cur_y, toptile);

}

/* The pixeldata in the passed Tile pointer will be used if use_tile_data is
 * true, otherwise the current tile is derived from tile_num. This can't be
 * used with animated tiles. It only applies to the base tile in multi-tiles.
 */
inline void MapWindow::drawTile(Tile *tile, uint16 x, uint16 y, bool toptile,
                                bool use_tile_data) {
	bool dbl_width, dbl_height;
	uint16 tile_num;

	tile_num = tile->tile_num;

	//don't show special marker tiles in MD unless "show eggs" is turned on.
	if (game_type == NUVIE_GAME_MD
	        && tile_num > 2040 && tile_num < 2048
	        && !obj_manager->is_showing_eggs())
		return;

	/* shouldn't be needed for in_town check
	 if(window_updated)
	   {
	    TileInfo ti;
	    ti.t=tile;
	    ti.left=x;
	    ti.top=y;
	    m_ViewableObjTiles.push_back(ti);
	   }
	*/
	dbl_width = tile->dbl_width;
	dbl_height = tile->dbl_height;

	if (x < win_width && y < win_height)
		drawTopTile(use_tile_data ? tile : tile_manager->get_tile(tile_num), x, y, toptile);

	if (dbl_width) {
		tile_num--;
		if (x > 0 && y < win_height) {
			tile = tile_manager->get_tile(tile_num);
			drawTopTile(tile, x - 1, y, toptile);
		}
	}

	if (dbl_height) {
		tile_num--;
		if (y > 0 && x < win_width) {
			tile = tile_manager->get_tile(tile_num);
			drawTopTile(tile, x, y - 1, toptile);
		}
	}

	if (x > 0 && dbl_width && y > 0 && dbl_height) {
		tile_num--;
		tile = tile_manager->get_tile(tile_num);
		drawTopTile(tile, x - 1, y - 1, toptile);
	}

}

inline void MapWindow::drawNewTile(Tile *tile, uint16 x, uint16 y, bool toptile) {
	drawTile(tile, x, y, toptile, true);
}

inline void MapWindow::drawTopTile(Tile *tile, uint16 x, uint16 y, bool toptile) {



// if(tile->boundary)
//  {
//    screen->blit(cursor_tile->data,8,x*16,y*16,16,16,false);
//   }
// FIXME: Don't use pixel offset (x_add,y_add) here, pass it via params?
	if (toptile) {
		if (tile->toptile)
//        screen->blit(x*16,y*16,tile->data,8,16,16,16,tile->transparent,&clip_rect);
			screen->blit(area.left + (x * 16) - cur_x_add, area.top + (y * 16) - cur_y_add, tile->data, 8, 16, 16, 16, tile->transparent, &clip_rect);
	} else {
		if (!tile->toptile)
//        screen->blit(x*16,y*16,tile->data,8,16,16,16,tile->transparent,&clip_rect);
			screen->blit(area.left + (x * 16) - cur_x_add, area.top + (y * 16) - cur_y_add, tile->data, 8, 16, 16, 16, tile->transparent, &clip_rect);
	}
}

void MapWindow::drawBorder() {
	Tile *tile;
	Tile *tile1;
	uint16 i;

	if (game_type != NUVIE_GAME_U6)
		return;
	uint16 orig_win_w = 11;
	uint16 orig_win_h = 11;
	uint16 x_off = Game::get_game()->get_game_x_offset();
	uint16 y_off = Game::get_game()->get_game_y_offset();

	tile = tile_manager->get_tile(432);
	screen->blit(x_off, y_off, tile->data, 8, 16, 16, 16, true, &clip_rect); // upper left corner

	tile = tile_manager->get_tile(434);
	screen->blit(x_off + (orig_win_w - 1) * 16, y_off, tile->data, 8, 16, 16, 16, true); // upper right corner (got rid of &clip_rect for original+)

	tile = tile_manager->get_tile(435);
	screen->blit(x_off, y_off + (orig_win_h - 1) * 16, tile->data, 8, 16, 16, 16, true, &clip_rect); // lower left corner

	tile = tile_manager->get_tile(437);
	screen->blit(x_off + (orig_win_w - 1) * 16, y_off + (orig_win_h - 1) * 16, tile->data, 8, 16, 16, 16, true); // lower right corner (got rid of &clip_rect for original+)

	tile = tile_manager->get_tile(433);
	tile1 = tile_manager->get_tile(436);

	for (i = 1; i < orig_win_w - 1; i++) {
		screen->blit(x_off + i * 16, y_off, tile->data, 8, 16, 16, 16, true, &clip_rect); // top row
		screen->blit(x_off + i * 16, y_off + (orig_win_h - 1) * 16, tile1->data, 8, 16, 16, 16, true, &clip_rect); // bottom row
	}

	tile = tile_manager->get_tile(438);
	tile1 = tile_manager->get_tile(439);

	for (i = 1; i < orig_win_h - 1; i++) {
		screen->blit(x_off, y_off + i * 16, tile->data, 8, 16, 16, 16, true, &clip_rect); // left column
		screen->blit(x_off + (orig_win_w - 1) * 16, y_off + i * 16, tile1->data, 8, 16, 16, 16, true); // right column (got rid of &clip_rect for original+)
	}
}

//FIXME this won't work for wrapped maps like MD.
void MapWindow::drawRoofs() {
	if (cur_y < 1 || cur_y > 760) // FIXME We need to handle this properly
		return;
	if (roof_display == ROOF_DISPLAY_NORMAL && map->has_roof(WRAPPED_COORD(cur_x + (win_width - 1 - map_center_xoff) / 2, cur_level), cur_y + (win_height - 1) / 2, cur_level)) //Don't draw roof tiles if player is underneath.
		return;
	if (x_ray_view >= X_RAY_ON)
		return;

	bool orig_style = game->is_orig_style();

	uint16 *roof_map_ptr = map->get_roof_data(cur_level);

	Common::Rect src(16, 16), dst(16, 16);

	if (roof_map_ptr) {
		roof_map_ptr += cur_y * 1024 + cur_x;
		for (uint16 i = 0; i < win_height; i++) {
			for (uint16 j = 0; j < win_width; j++) {
				if (roof_map_ptr[j] != 0) {
					dst.left = area.left + (j * 16);
					dst.top = area.top + (i * 16);
					dst.left -= cur_x_add;
					dst.top -= cur_y_add;

					src.left = (roof_map_ptr[j] % MAPWINDOW_ROOFTILES_IMG_W) * 16;
					src.top = (roof_map_ptr[j] / MAPWINDOW_ROOFTILES_IMG_W) * 16;

					if (orig_style) {
						src.setWidth(16);
						src.setHeight(16);
						dst.setWidth(16);
						dst.setHeight(16);

						if (i == 0) {
							src.top += 8;
							src.setHeight(8);
							dst.top += 8;
							dst.setHeight(8);
						} else if (i == win_height - 1) {
							src.setHeight(8);
							dst.setHeight(8);
						}

						if (j == 0) {
							src.left += 8;
							src.setWidth(8);
							dst.left += 8;
							dst.setWidth(8);
						} else if (j == win_width - 1) {
							src.setWidth(8);
							dst.setWidth(8);
						}
						SDL_BlitSurface(roof_tiles, &src, surface, &dst);
					} else {
						byte *ptr = (byte *)roof_tiles->getPixels();
						ptr += src.left + src.top * 80;
						screen->blit(dst.left, dst.top, ptr, 8, 16, 16, 80, true, &clip_rect);
					}
				}
			}
			roof_map_ptr += 1024;
		}
	}
}

void MapWindow::drawRain() {
	int c;
	if (game->is_orig_style())
		c = win_width * win_height;
	else if (game->is_original_plus_cutoff_map())
		c = ((game->get_game_width() - border_width) * game->get_game_height()) / 256;
	else
		c = (game->get_game_width() * game->get_game_height()) / 256;
	for (int i = 0; i < c; i++) {
		uint16 x;
		uint16 y;
		if (game->is_orig_style()) {
			x = area.left + NUVIE_RAND() % ((win_width - 1) * 16 - 2) + 8;
			y = area.top + NUVIE_RAND() % ((win_height - 1) * 16 - 2) + 8;
		} else {
			if (game->is_original_plus_cutoff_map())
				x = game->get_game_x_offset() + NUVIE_RAND() % (game->get_game_width() - border_width - 2);
			else
				x = game->get_game_x_offset() + NUVIE_RAND() % (game->get_game_width() - 2);
			y = game->get_game_y_offset() + NUVIE_RAND() % (game->get_game_height() - 2);
		}

		//FIXME the original does something with the palette if a pixel is black then draw gray etc.
		//We can't do this easily here because we don't have the original 8 bit display surface.
		screen->put_pixel(118, x, y);
		screen->put_pixel(118, x + 1, y + 1);
		screen->put_pixel(0, x + 2, y + 2);
	}
}

void MapWindow::AddMapTileToVisibleList(uint16 tile_num, uint16 x, uint16 y) {
	if (x >= TMP_MAP_BORDER &&
	        y >= TMP_MAP_BORDER &&
	        x < tmp_map_width - TMP_MAP_BORDER &&
	        y < tmp_map_height - TMP_MAP_BORDER) {
		TileInfo ti;
		ti.t = tile_manager->get_tile(tile_num);
		ti.x = (uint16)(x - TMP_MAP_BORDER);
		ti.y = (uint16)(y - TMP_MAP_BORDER);
		m_ViewableMapTiles.push_back(ti);
	}
}

void MapWindow::drawGrid() {
	for (uint16 i = 0; i < win_height; i++) {
		for (uint16 j = 0; j < win_width; j++) {
			screen->blit(area.left + (j * 16) - cur_x_add, area.top + (i * 16) - cur_y_add,
				(const byte *)grid_tile.data, 8, 16, 16, 16, true);
		}
	}
}

void MapWindow::generateTmpMap() {
	byte *map_ptr;
	uint16 pitch;
	uint16 x, y;
	Tile *tile;

	m_ViewableMapTiles.clear();

	map_ptr = map->get_map_data(cur_level);
	pitch = map->get_width(cur_level);

	if (enable_blacking == false) {
		uint16 *ptr = tmp_map_buf;
		for (y = 0; y < tmp_map_height; y++) {
			for (x = 0; x < tmp_map_width; x++) {
				uint16 x1 = cur_x + x - TMP_MAP_BORDER;
				uint16 y1 = cur_y + y - TMP_MAP_BORDER;
				WRAP_COORD(x1, cur_level);
				WRAP_COORD(y1, cur_level);
				*ptr = map_ptr[y1 * pitch + x1];
				AddMapTileToVisibleList(*ptr, x, y);

				ptr++;
			}
		}
		return;
	}

	roof_display = ROOF_DISPLAY_NORMAL;

	memset(tmp_map_buf, 0, tmp_map_width * tmp_map_height * sizeof(uint16));

	if (freeze_blacking_location == false) {
		x = cur_x + ((win_width - 1 - map_center_xoff) / 2);
		y = cur_y + ((win_height - 1) / 2);
	} else { // SB-X
		x = last_boundary_fill_x;
		y = last_boundary_fill_y;
	}

	WRAP_COORD(x, cur_level);
	WRAP_COORD(y, cur_level);

//This is for U6. Sherry needs to pass through walls
//We shift the boundary fill start location off the wall tile so it flood
//fills correctly. We move east for vertical wall tiles and south for
//horizontal wall tiles.
	if (game_type == NUVIE_GAME_U6 && obj_manager->is_boundary(x, y, cur_level)) {
		tile = obj_manager->get_obj_tile(x, y, cur_level, false);
		if ((tile->flags1 & TILEFLAG_WALL_MASK) == (TILEFLAG_WALL_NORTH | TILEFLAG_WALL_SOUTH))
			x = WRAPPED_COORD(x + 1, cur_level);
		else
			y = WRAPPED_COORD(y + 1, cur_level);
	}
	last_boundary_fill_x = x;
	last_boundary_fill_y = y;
	boundaryFill(map_ptr, pitch, x, y);

	reshapeBoundary();

	if (roof_mode && floorTilesVisible())
		roof_display = ROOF_DISPLAY_OFF; // hide roof if a building's floor is showing.
}

void MapWindow::boundaryFill(byte *map_ptr, uint16 pitch, uint16 x, uint16 y) {
	unsigned char current;
	uint16 *ptr;
	uint16 pos;
	uint16 tmp_x, tmp_y;
	uint16 p_cur_x, p_cur_y; //wrapped cur_x - 1 and wrapped cur_y - 1

	p_cur_x = WRAPPED_COORD(cur_x - TMP_MAP_BORDER, cur_level);
	p_cur_y = WRAPPED_COORD(cur_y - TMP_MAP_BORDER, cur_level);

	if (x == WRAPPED_COORD(p_cur_x - 1, cur_level) || x == WRAPPED_COORD(p_cur_x + tmp_map_width, cur_level))
		return;

	if (y == WRAPPED_COORD(p_cur_y - 1, cur_level) || y == WRAPPED_COORD(p_cur_y + tmp_map_height, cur_level))
		return;

	if (p_cur_y > y)
		tmp_y = pitch - p_cur_y + y;
	else
		tmp_y = y - p_cur_y;

	pos = tmp_y * tmp_map_width;

	if (p_cur_x > x)
		tmp_x = pitch - p_cur_x + x;
	else
		tmp_x = x - p_cur_x;

	pos += tmp_x;

	ptr = &tmp_map_buf[pos];

	if (*ptr != 0)
		return;

	current = map_ptr[y * pitch + x];

	*ptr = (uint16)current;

	AddMapTileToVisibleList(current, tmp_x, tmp_y);

	if (x_ray_view <= X_RAY_OFF && map->is_boundary(x, y, cur_level)) { //hit the boundary wall tiles
		if (boundaryLookThroughWindow(*ptr, x, y) == false)
			return;
		else
			roof_display = ROOF_DISPLAY_OFF; //hide roof tiles if player is looking through window.
	}


	uint16 xp1, xm1;
	uint16 yp1, ym1;

	xp1 = WRAPPED_COORD(x + 1, cur_level);
	xm1 = WRAPPED_COORD(x - 1, cur_level);

	yp1 = WRAPPED_COORD(y + 1, cur_level);
	ym1 = WRAPPED_COORD(y - 1, cur_level);

	boundaryFill(map_ptr, pitch, xp1,   y);
	boundaryFill(map_ptr, pitch,   x, yp1);
	boundaryFill(map_ptr, pitch, xp1, yp1);
	boundaryFill(map_ptr, pitch, xm1, ym1);
	boundaryFill(map_ptr, pitch, xm1,   y);
	boundaryFill(map_ptr, pitch,   x, ym1);
	boundaryFill(map_ptr, pitch, xp1, ym1);
	boundaryFill(map_ptr, pitch, xm1, yp1);


	return;
}

bool MapWindow::floorTilesVisible() {
	Actor *actor;
	uint16 a_x, a_y;
	uint8 a_z;
	actor = actor_manager->get_player();
	if (!actor)
		return false;
	actor->get_location(&a_x, &a_y, &a_z);
	uint16 cX = WRAPPED_COORD(a_x - 1, cur_level), eX = WRAPPED_COORD(a_x + 2, cur_level);
	uint16 cY = WRAPPED_COORD(a_y - 1, cur_level), eY = WRAPPED_COORD(a_y + 2, cur_level);

	for (; cY != eY;) {
		for (; cX != eX;) {
			if (map->has_roof(cX, cY, cur_level) && !map->is_boundary(cX, cY, cur_level)) {
				Tile *t = obj_manager->get_obj_tile(cX, cY, cur_level, false);
				if (t && (t->flags1 & TILEFLAG_WALL))
					return true;
			}
			cX = WRAPPED_COORD(cX + 1, cur_level);
		}
		cX = WRAPPED_COORD(a_x - 1, cur_level);
		cY = WRAPPED_COORD(cY + 1, cur_level);
	}

	return false;
}

bool MapWindow::boundaryLookThroughWindow(uint16 tile_num, uint16 x, uint16 y) {
	Tile *tile;
	Actor *actor;
	uint16 a_x, a_y;
	uint8 a_z;
	Obj  *obj;

	tile = tile_manager->get_tile(tile_num);
	if (!(tile->flags2 & TILEFLAG_WINDOW)) {
		obj = obj_manager->get_objBasedAt(x, y, cur_level, true);
		if (obj) { //check for a windowed object.
			tile = tile_manager->get_tile(obj_manager->get_obj_tile_num(obj->obj_n) + obj->frame_n);
			if (!(tile->flags2 & TILEFLAG_WINDOW))
				return false;
		} else
			return false;
	}

	actor = actor_manager->get_player();
	actor->get_location(&a_x, &a_y, &a_z);

	if (a_x == x) {
		if (a_y == WRAPPED_COORD(y - 1, cur_level) || a_y == WRAPPED_COORD(y + 1, cur_level))
			return true;
	}

	if (a_y == y) {
		if (a_x == WRAPPED_COORD(x - 1, cur_level) || a_x == WRAPPED_COORD(x + 1, cur_level))
			return true;
	}

	return false;
}

//reshape walls based on new blacked out areas.

void MapWindow::reshapeBoundary() {
	uint16 x, y;
	uint8 flag, original_flag;
	Tile *tile;

	for (y = 1; y < tmp_map_height - 1; y++) {
		for (x = 1; x < tmp_map_width - 1; x++) {
			if (tmpBufTileIsBoundary(x, y)) {
				tile = tile_manager->get_tile(tmp_map_buf[y * tmp_map_width + x]);

				if ((tile->tile_num >= 140 && tile->tile_num <= 187)) { //main U6 wall tiles FIX for WOU games
					flag = 0;
					original_flag = tile->flags1 & TILEFLAG_WALL_MASK;
				} else
					continue;

				//generate the required wall flags
				if (tmpBufTileIsWall(x, y - 1, NUVIE_DIR_N))
					flag |= TILEFLAG_WALL_NORTH;
				if (tmpBufTileIsWall(x + 1, y, NUVIE_DIR_E))
					flag |= TILEFLAG_WALL_EAST;
				if (tmpBufTileIsWall(x, y + 1, NUVIE_DIR_S))
					flag |= TILEFLAG_WALL_SOUTH;
				if (tmpBufTileIsWall(x - 1, y, NUVIE_DIR_W))
					flag |= TILEFLAG_WALL_WEST;

				//we want to keep existing tile if it is pointing to non-wall tiles which are not blacked
				//this is used to support cookfire walls which aren't considered walls in tileflags.

				if (tmpBufTileIsBlack(x, y - 1) == false && (original_flag & TILEFLAG_WALL_NORTH))
					flag |= TILEFLAG_WALL_NORTH;
				if (tmpBufTileIsBlack(x + 1, y) == false && (original_flag & TILEFLAG_WALL_EAST))
					flag |= TILEFLAG_WALL_EAST;
				if (tmpBufTileIsBlack(x, y + 1) == false && (original_flag & TILEFLAG_WALL_SOUTH))
					flag |= TILEFLAG_WALL_SOUTH;
				if (tmpBufTileIsBlack(x - 1, y) == false && (original_flag & TILEFLAG_WALL_WEST))
					flag |= TILEFLAG_WALL_WEST;

				if (flag == 0) //isolated border tiles
					continue;

				if (flag == 48) { // 0011 top right corner
					if (tmpBufTileIsBlack(x, y - 1) && tmpBufTileIsBlack(x + 1, y)) { //replace with blacked corner tile
						//Oh dear! this is evil. FIX
						tmp_map_buf[y * tmp_map_width + x] = 266 + 2 * (((tile->tile_num - tile->tile_num % 16) - 140) / 16);
						continue;
					}
				}

				if (flag == 192) { // 1100 bottom left corner
					if (tmpBufTileIsBlack(x, y + 1) && tmpBufTileIsBlack(x - 1, y)) { //replace with blacked corner tile
						//Oh dear! this is evil. FIX
						tmp_map_buf[y * tmp_map_width + x] = 266 + 1 + 2 * (((tile->tile_num - tile->tile_num % 16) - 140) / 16);
						continue;
					}
				}

				if ((tile->flags1 & TILEFLAG_WALL_MASK) == flag) // complete match no work needed
					continue;

				// Look for a suitable tile to transform into

				// ERIC 05/03/05 flag |= TILEFLAG_WALL_NORTH | TILEFLAG_WALL_WEST;

				if (((tile->flags1) & TILEFLAG_WALL_MASK) > flag && flag != 144) { // 1001 _| corner
					//flag |= TILEFLAG_WALL_NORTH | TILEFLAG_WALL_WEST;
					for (; ((tile->flags1) & TILEFLAG_WALL_MASK) != flag && (tile->flags1 & TILEFLAG_WALL_MASK);)
						tile = tile_manager->get_tile(tile->tile_num - 1);
				} else {
					//flag |= TILEFLAG_WALL_NORTH | TILEFLAG_WALL_WEST;
					for (; ((tile->flags1) & TILEFLAG_WALL_MASK) != flag && (tile->flags1 & TILEFLAG_WALL_MASK);)
						tile = tile_manager->get_tile(tile->tile_num + 1);
				}

				if ((tile->flags1 & TILEFLAG_WALL_MASK) == flag)
					tmp_map_buf[y * tmp_map_width + x] = tile->tile_num;
			}
		}
	}
}

inline bool MapWindow::tmpBufTileIsBlack(uint16 x, uint16 y) {
	if (tmp_map_buf[y * tmp_map_width + x] == 0)
		return true;

	return false;
}

bool MapWindow::tmpBufTileIsBoundary(uint16 x, uint16 y) {
	uint16 tile_num;
	Tile *tile;

	tile_num = tmp_map_buf[y * tmp_map_width + x];

	if (tile_num == 0)
		return false;

	tile = tile_manager->get_tile(tile_num);

	if (tile->boundary)
		return true;

	if (obj_manager->is_boundary(WRAPPED_COORD(cur_x - TMP_MAP_BORDER + x, cur_level), WRAPPED_COORD(cur_y - TMP_MAP_BORDER + y, cur_level), cur_level))
		return true;

	return false;
}

bool MapWindow::tmpBufTileIsWall(uint16 x, uint16 y, uint8 direction) {
	uint16 tile_num;
	Tile *tile;
	uint8 mask = 0;

	tile_num = tmp_map_buf[y * tmp_map_width + x];

	if (tile_num == 0)
		return false;

	switch (direction) {
	case NUVIE_DIR_N :
		mask = TILEFLAG_WALL_SOUTH;
		break;
	case NUVIE_DIR_S :
		mask = TILEFLAG_WALL_NORTH;
		break;
	case NUVIE_DIR_E :
		mask = TILEFLAG_WALL_WEST;
		break;
	case NUVIE_DIR_W :
		mask = TILEFLAG_WALL_EAST;
		break;
	}

	tile = tile_manager->get_tile(tile_num);

	if (tile->flags1 & TILEFLAG_WALL) {
		if (tile->flags1 & mask)
			return true;
	}

// if(obj_manager->is_boundary(cur_x-1+x, cur_y-1+y, cur_level))
//  return true;

	tile = obj_manager->get_obj_tile(WRAPPED_COORD(cur_x - TMP_MAP_BORDER + x, cur_level), WRAPPED_COORD(cur_y - TMP_MAP_BORDER + y, cur_level), cur_level, false);
	if (tile != NULL) {
		if (tile->flags2 & TILEFLAG_BOUNDARY) {
			if (tile->flags1 & mask)
				return true;
		}
	}

	return false;
}

/* Returns MSG_SUCCESS if the obj can be dropped or moved by the actor at world coordinates x,y or an error msg.
 * There must be a direct path between the actor to the obj and also between the obj and destination.
 * Objs that can't be picked up will be blocked actors and unpassable areas. (z is always cur_level)
 */
CanDropOrMoveMsg MapWindow::can_drop_or_move_obj(uint16 x, uint16 y, Actor *actor, Obj *obj) {
	bool in_inventory = obj->is_in_inventory();
	if (!in_inventory && obj->x == x && obj->y == y)
		return MSG_NOT_POSSIBLE;
	if (game->using_hackmove())
		return MSG_SUCCESS;

	if (tile_is_black(x, y, obj)) {
		if (tile_is_black(x, y))
			return MSG_NOT_POSSIBLE;
		else
			return MSG_BLOCKED;
	}
	MapCoord actor_loc = actor->get_location();
	if (actor_manager->get_actor(x, y, actor_loc.z))
		return MSG_NOT_POSSIBLE;

	Obj *dest_obj = NULL;
	if (game_type == NUVIE_GAME_U6) {
		dest_obj = obj_manager->get_obj(x, y, actor_loc.z); //FIXME this might not be right. We might want to exclude obj.
	} else {
		dest_obj = obj_manager->get_obj(x, y, actor_loc.z, OBJ_SEARCH_TOP, OBJ_EXCLUDE_IGNORED, obj);
	}

	bool can_go_in_water = (game_type == NUVIE_GAME_U6
	                        && (obj->obj_n == OBJ_U6_SKIFF || obj->obj_n == OBJ_U6_RAFT));
	if (can_go_in_water && dest_obj) // it is drawn underneath so only allow on hackmove
		return MSG_NOT_POSSIBLE;

	LineTestResult lt;
	MapCoord target_loc(x, y, actor_loc.z);
	MapCoord obj_loc(obj->x, obj->y, actor_loc.z);

	if (in_inventory && !obj->get_actor_holding_obj()->is_onscreen()
	        && obj->get_actor_holding_obj()->get_location().distance(target_loc) > 5)
		return MSG_OUT_OF_RANGE;

	if (get_interface() == INTERFACE_IGNORE_BLOCK && map->can_put_obj(x, y, cur_level))
		return MSG_SUCCESS;

	if (actor_loc.distance(target_loc) > 5 && get_interface() == INTERFACE_NORMAL)
		return MSG_OUT_OF_RANGE;

	uint8 lt_flags;
	lt_flags = (game_type == NUVIE_GAME_U6) ? LT_HitMissileBoundary : 0; //FIXME this probably isn't quite right for MD/SE
	if (map->lineTest(actor_loc.x, actor_loc.y, x, y, actor_loc.z, lt_flags, lt, 0, obj)) {
		MapCoord hit_loc = MapCoord(lt.hit_x, lt.hit_y, lt.hit_level);
		if (obj_loc.distance(target_loc) != 1 || hit_loc.distance(target_loc) != 1) {
			if (lt.hitObj && target_loc == hit_loc) {
				if (obj_manager->can_store_obj(lt.hitObj, obj)) //if we are moving onto a container.
					return MSG_SUCCESS;
			}
			return MSG_BLOCKED;
		}
		// trying to push object one tile away from actor
		if (map->lineTest(obj->x, obj->y, x, y, actor_loc.z, lt_flags, lt, 0, obj)) {
			if (lt.hitObj) {
				if (obj_manager->can_store_obj(lt.hitObj, obj)) //if we are moving onto a container.
					return MSG_SUCCESS;
				/*                else // I don't think these are needed
				                {
				                    // We can place an object on a bench or table. Or on any other object if
				                    // the object is passable and not on a boundary.

				                    Tile *obj_tile = obj_manager->get_obj_tile(lt.hitObj->obj_n, lt.hitObj->frame_n);
				                    if(!obj_tile) // shouldn't happen
				                        return MSG_NO_TILE;
				                    if((obj_tile->flags3 & TILEFLAG_CAN_PLACE_ONTOP)
				                       || (obj_tile->passable && !map->is_boundary(lt.hit_x, lt.hit_y, lt.hit_level)) )
				                        return MSG_SUCCESS;
				                }*/
			}
			return MSG_BLOCKED;
		}
	}
	Tile *tile;
	if (dest_obj)
		tile = obj_manager->get_obj_tile(dest_obj->obj_n, dest_obj->frame_n);
	else
		tile = map->get_tile(x, y, actor_loc.z);

	if (!tile) // shouldn't happen
		return MSG_NO_TILE;

	if ((can_go_in_water || !map->is_water(x, y, actor_loc.z))
	        && ((tile->flags3 & TILEFLAG_CAN_PLACE_ONTOP)
	            || (tile->passable && !map->is_boundary(x, y, actor_loc.z))))
		return MSG_SUCCESS;

	return MSG_NOT_POSSIBLE;
}

void MapWindow::display_can_drop_or_move_msg(CanDropOrMoveMsg msg, string msg_text) {
	if (msg == MSG_NOT_POSSIBLE)
		msg_text += "Not possible\n";
	else if (msg == MSG_BLOCKED)
		msg_text += "Blocked\n";
	else if (msg == MSG_OUT_OF_RANGE)
		msg_text += "Out of range\n";
	/*  else if(msg == MSG_NO_TILE) // shouldn't be needed now that blacked out areas are checked first
	        msg_text += "ERROR: No tile. Report me\n";*/
	game->get_scroll()->display_string(msg_text);
}

bool MapWindow::can_get_obj(Actor *actor, Obj *obj) {
	if (!obj)
		return false;
	if (get_interface() == INTERFACE_IGNORE_BLOCK)
		return true;
	if (obj->is_in_inventory())
		return false;
	if (obj->is_in_container())
		obj = obj->get_container_obj(true);

	if (actor->get_z() != obj->z)
		return false;

	LineTestResult lt;
	if (map->lineTest(actor->get_x(), actor->get_y(), obj->x, obj->y, obj->z, LT_HitUnpassable, lt, 0, obj)) {
		// Skip Check for SE Tile Objects - We are actually using the blocking item/tree
		Script *script = game->get_script();
		if (game_type != NUVIE_GAME_SE || !script->call_is_tile_object(obj->obj_n)) {
			return false;
		}
	}

	if (game_type == NUVIE_GAME_U6 && obj->obj_n == OBJ_U6_SECRET_DOOR)
		return true;
	return !blocked_by_wall(actor, obj);
}

/*
 * Check to make sure the obj isn't on the other side of a wall or trying to push through it.
 * The original engine didn't bother to check.
 */
bool MapWindow::blocked_by_wall(Actor *actor, Obj *obj) {
	if (game_type == NUVIE_GAME_U6 && obj->x == 282 && obj->y == 438 && cur_level == 0) // HACK for buggy location
		return false;
	Tile *tile = map->get_tile(obj->x, obj->y, cur_level);
	if (((tile->flags1 & TILEFLAG_WALL) && !game->get_usecode()->is_door(obj))
	        && (((tile->flags1 & TILEFLAG_WALL_MASK) == 208 && actor->get_y() < obj->y) // can't get items that are south
	            || ((tile->flags1 & TILEFLAG_WALL_MASK) == 176 && actor->get_x() < obj->x) // can't get items that are east
	            || ((tile->flags1 & TILEFLAG_WALL_MASK) == 240 // northwest corner - used in SE (not sure if used in other games)
	                && (actor->get_y() < obj->y || actor->get_x() < obj->x))))
		return true;

	return false;
}

bool MapWindow::drag_accept_drop(int x, int y, int message, void *data) {
	DEBUG(0, LEVEL_DEBUGGING, "MapWindow::drag_accept_drop()\n");
	uint16 mapWidth;

	x -= area.left;
	y -= area.top;

	x /= 16;
	y /= 16;

	GUI::get_gui()->force_full_redraw();

	if (message == GUI_DRAG_OBJ) {
		if (game->get_player()->is_in_vehicle() && !game->using_hackmove()) {
			game->get_event()->display_not_aboard_vehicle();
			return false;
		}
		mapWidth = map->get_width(cur_level);
		x = (cur_x + x) % mapWidth;
		y = (cur_y + y) % mapWidth;

		Obj *obj = (Obj *)data;
		Actor *p = actor_manager->get_player();
		Actor *target_actor = map->get_actor(x, y, cur_level);

		if (obj->is_in_inventory() == false) { //obj on map.
			if (can_get_obj(p, obj)) { //make sure there is a clear line from player to object
				if (target_actor) {
					game->get_event()->display_move_text(target_actor, obj);

					if (target_actor == p || (target_actor->is_in_party())) {
						if (obj_manager->obj_is_damaging(obj, p)) {
							game->get_player()->subtract_movement_points(3);
							return false;
						}
						if ((!game->get_usecode()->has_getcode(obj) || game->get_usecode()->get_obj(obj, target_actor))
						        && game->get_event()->can_move_obj_between_actors(obj, p, target_actor))
							return true;
						else {
							game->get_scroll()->message("\n\n");
							return false;
						}
					} else {
						game->get_scroll()->display_string("\n\nOnly within the party!");
						game->get_scroll()->message("\n\n");
						return false;
					}
				} else
					return true;
			}
		} else {
			if (game->get_usecode()->cannot_unready(obj)) {
				game->get_event()->unready(obj);
				return false;
			}
			if (target_actor) {
				Actor *owner = obj->get_actor_holding_obj();
				game->get_event()->display_move_text(target_actor, obj);

				if (game->get_event()->can_move_obj_between_actors(obj, owner, target_actor) == false) {
					game->get_scroll()->message("\n\n");
					return false;
				} else
					return true;
			} else
				return true; //throw on ground
		}
		game->get_scroll()->display_string("Move-");
		game->get_scroll()->display_string(obj_manager->look_obj(obj)); // getting obj name
		game->get_scroll()->display_string("\nto ");
		game->get_scroll()->display_string(get_direction_name(x - obj->x ,  y - obj->y));
		game->get_scroll()->message(".\n\nCan't reach it\n\n");
	}

	return false;
}

void MapWindow::drag_perform_drop(int x, int y, int message, void *data) {
	DEBUG(0, LEVEL_DEBUGGING, "MapWindow::drag_perform_drop()\n");
	Events *event = game->get_event();
	uint16 mapWidth = map->get_width(cur_level);

	x -= area.left;
	y -= area.top;

	if (message == GUI_DRAG_OBJ) {
		x = (cur_x + x / 16) % mapWidth;
		y = (cur_y + y / 16) % mapWidth;
		Obj *obj = (Obj *)data;

		if (obj->obj_n == OBJ_U6_LOCK_PICK && game_type == NUVIE_GAME_U6)
			game->get_usecode()->search_container(obj, false);
		Actor *a = map->get_actor(x, y, cur_level);
		if (a && (a->is_in_party() || a == actor_manager->get_player())) {
			if (a == actor_manager->get_player()) // get
				game->get_player()->subtract_movement_points(3);
			else // get plus move
				game->get_player()->subtract_movement_points(8);
			obj_manager->moveto_inventory(obj, a);
			game->get_scroll()->message("\n\n");
		} else {
			if (!obj->is_in_inventory() && !obj->is_in_container()) { // !need is_in_container to exclude container gumps
				move_on_drop(obj); // no longer determines whether to drop or move but can call usecode
				event->newAction(PUSH_MODE);
				event->select_obj(obj);
				event->pushTo(x - obj->x, y - obj->y, PUSH_FROM_OBJECT);
				event->endAction();
				return;
			}
			CanDropOrMoveMsg can_drop; // so we can skip quantity prompt
			if ((can_drop = can_drop_or_move_obj(x, y, actor_manager->get_player(), obj)) != MSG_SUCCESS) {
				game->get_scroll()->display_string("Drop-");
				game->get_scroll()->display_string(obj_manager->look_obj(obj));
				game->get_scroll()->display_string("\n\nlocation:\n\n");
				display_can_drop_or_move_msg(can_drop, "");
				game->get_scroll()->message("\n");
				return;
			}
			// drop on ground or into a container
			event->newAction(DROP_MODE);
			event->select_obj(obj);
			if (obj->qty <= 1 || !obj_manager->is_stackable(obj))
				event->select_target(x, y);
			else
				event->set_drop_target(x, y); // pre-select target
		}
	}

}
// performs some usecode but used to decide whether to move or drop too
bool MapWindow::move_on_drop(Obj *obj) {
	bool move = (get_interface() == INTERFACE_NORMAL);
	/*  if(drop_with_move && move)
	        return true;

	    if(obj_manager->obj_is_damaging(obj))
	        return move;
	*/
	if (game->get_usecode()->has_getcode(obj) && obj->is_in_inventory() == false) {
		if (game_type == NUVIE_GAME_U6) {
			switch (obj->obj_n) {
			case OBJ_U6_CHEST:
			case OBJ_U6_LOCK_PICK:
			case OBJ_U6_MOONSTONE:
				game->get_usecode()->get_obj(obj, actor_manager->get_player());
				return false;
			case OBJ_U6_SKIFF:
				return false;
			case OBJ_U6_TORCH:
				if (obj->frame_n == 0)
					return false;
				break;
			default :
				break;
			}
		}

		return move;
	}

	return false;
}

void MapWindow::set_interface() {
	Std::string interface_str;
	config->value("config/input/interface", interface_str, "normal");
	if (interface_str == "ignore_block" ||  Game::get_game()->using_hackmove()) // game variable is not initialized
		interface = INTERFACE_IGNORE_BLOCK;
	else if (interface_str == "fullscreen")
		interface = INTERFACE_FULLSCREEN;
	else
		interface = INTERFACE_NORMAL;
}

InterfaceType MapWindow::get_interface() {
	// check is easily exploited but would be annoying if checking for nearby enemies
	if (interface == INTERFACE_FULLSCREEN && game->get_party()->is_in_combat_mode())
		return INTERFACE_NORMAL;
	return interface;
}

bool MapWindow::is_interface_fullscreen_in_combat() {
	if (interface == INTERFACE_FULLSCREEN && game->get_party()->is_in_combat_mode())
		return true;
	return false;
}

GUI_status MapWindow::Idle(void) {
	return (GUI_Widget::Idle());
}


// single-click (press and release button)
GUI_status MapWindow::MouseClick(int x, int y, Shared::MouseButton button) {
	if (button == USE_BUTTON && look_on_left_click) {
		wait_for_mouseclick(button); // see MouseDelayed
	}
	return (MouseUp(x, y, button)); // do MouseUp so selected_obj is cleared
}

// single-click; waited for double-click
GUI_status MapWindow::MouseDelayed(int x, int y, Shared::MouseButton button) {
	Events *event = game->get_event();
	if (!looking || game->user_paused() || event->cursor_mode
	        || (event->get_mode() != MOVE_MODE && event->get_mode() != EQUIP_MODE)) {
		look_obj = NULL;
		look_actor = NULL;
		return (GUI_PASS);
	}
	game->get_scroll()->display_string("Look-");
	event->set_mode(LOOK_MODE);
	event->lookAtCursor(true, original_obj_loc.x, original_obj_loc.y, original_obj_loc.z, look_obj, look_actor);
	look_obj = NULL;
	look_actor = NULL;

	return (MouseUp(x, y, button)); // do MouseUp so selected_obj is cleared
}

// MouseDown; waited for MouseUp
GUI_status MapWindow::MouseHeld(int x, int y, Shared::MouseButton button) {
	looking = false;
	if (walk_with_left_button)
		set_walking(true);
	return (GUI_PASS);
}

// double-click
GUI_status MapWindow::MouseDouble(int x, int y, Shared::MouseButton button) {
	Events *event = game->get_event();

	// only USE if not doing anything in event
	if (enable_doubleclick && event->get_mode() == MOVE_MODE && !is_wizard_eye_mode()) {
		int wx, wy;
		mouseToWorldCoords(x, y, wx, wy);
		event->multiuse((uint16)wx, (uint16)wy);
	}
	looking = false;
	return (MouseUp(x, y, button)); // do MouseUp so selected_obj is cleared
}

GUI_status MapWindow::MouseWheel(sint32 x, sint32 y) {
	Game *g = Game::get_game();

	if (g->is_new_style()) {
		if (y > 0)
			g->get_scroll()->move_scroll_up();
		if (y < 0)
			g->get_scroll()->move_scroll_down();
	} else {
		if (y > 0)
			g->get_scroll()->page_up();
		if (y < 0)
			g->get_scroll()->page_down();
	}
	return GUI_YUM;
}

GUI_status MapWindow::MouseDown(int x, int y, Shared::MouseButton button) {
	//DEBUG(0,LEVEL_DEBUGGING,"MapWindow::MouseDown, button = %i\n", button);
	Events *event = game->get_event();
	Actor *player = actor_manager->get_player();
	Obj *obj = get_objAtMousePos(x, y);

	if (is_wizard_eye_mode()) {
		set_walking(true);
		return GUI_YUM;
	}
	if (event->is_looking_at_spellbook()) {
		event->cancelAction();
		return GUI_YUM;
	}

	if (game->is_original_plus() && y <= Game::get_game()->get_game_y_offset() + 200
	        && x >= Game::get_game()->get_game_x_offset() + game->get_game_width() - border_width) {
		looking = false;
		return GUI_PASS;
	}
	if (event->get_mode() == MOVE_MODE || event->get_mode() == EQUIP_MODE) {
		int wx, wy;
		mouseToWorldCoords(x, y, wx, wy);

		if (button == WALK_BUTTON && game->get_command_bar()->get_selected_action() != -1) {
			if (game->get_command_bar()->try_selected_action() == false) // start new action
				return GUI_PASS; // false if new event doesn't need target
		} else if (wx == player->x && wy == player->y // PASS if Avatar is hit
		           && (button == WALK_BUTTON || !enable_doubleclick)) {
			event->cancelAction(); // MOVE_MODE, so this should work
			return GUI_PASS;
		} else if (button == WALK_BUTTON
		           || (!enable_doubleclick && button == USE_BUTTON
		               && !game->is_dragging_enabled() && !look_on_left_click)) {
			set_walking(true);
		} else if (button == USE_BUTTON) { // you can also walk by holding the USE button
			if (look_on_left_click && !event->cursor_mode) { // need to preserve location because of click delay
				looking = true;
				original_obj_loc = MapCoord(wx, wy , cur_level);
				look_actor = actor_manager->get_actor(wx , wy, cur_level);
				look_obj = obj_manager->get_obj(wx , wy, cur_level);
				moveCursor(WRAP_VIEWP(cur_x, wx, map_width), wy - cur_y);
			}
			wait_for_mousedown(button);
		}
	}

	if (event->get_mode() == INPUT_MODE || event->get_mode() == ATTACK_MODE) { // finish whatever action is being done, with mouse coordinates
		if (button != USE_BUTTON && button != WALK_BUTTON)
			return GUI_PASS;
		looking = false;
		select_target(x, y);
		return  GUI_PASS;
	} else if (event->get_mode() != MOVE_MODE && event->get_mode() != EQUIP_MODE) {
		return GUI_PASS;
	}

	if (!obj || button != DRAG_BUTTON)
		return  GUI_PASS;

	original_obj_loc = MapCoord(obj->x, obj->y, obj->z);
	int distance = player->get_location().distance(original_obj_loc);
	float weight = obj_manager->get_obj_weight(obj, OBJ_WEIGHT_EXCLUDE_CONTAINER_ITEMS);

	if ((weight == 0 || player->get_actor_num() == 0
	        || tile_is_black(obj->x, obj->y, obj)) && !game->using_hackmove())
		return  GUI_PASS;

	// checking interface directly to allow dragging in INTERFACE_FULLSCREEN when in combat
	if (distance > 1 && interface == INTERFACE_NORMAL)
		return  GUI_PASS;

	if (button == DRAG_BUTTON && game->is_dragging_enabled())
		selected_obj = obj;

	return  GUI_PASS;
}

GUI_status MapWindow::MouseUp(int x, int y, Shared::MouseButton button) {
	// cancel dragging and movement no matter what button is released
	if (selected_obj) {
		selected_obj = NULL;
	}
	walking = false;
	dragging = false;

	return  GUI_PASS;
}

GUI_status  MapWindow::MouseMotion(int x, int y, uint8 state) {
//	Events *event = game->get_event();
	Tile    *tile;

	update_mouse_cursor((uint32)x, (uint32)y);

	//  DEBUG(0,LEVEL_DEBUGGING,"MapWindow::MouseMotion\n");

//	if(selected_obj) // We don't want to walk if we are selecting an object to move.
//		walking = false;
	if (walking) { // No, we don't want to select an object to move if we are walking.
		selected_obj = NULL;
		dragging = false;
	}

	if (selected_obj && !dragging) {
		int wx, wy;
		// ensure that the player can reach the selected object before
		// letting them drag it
		//mouseToWorldCoords(x, y, wx, wy);
		wx = selected_obj->x;
		wy = selected_obj->y;
		LineTestResult result;
		Actor *player = actor_manager->get_player();

		if (map->lineTest(player->x, player->y, wx, wy, cur_level, LT_HitUnpassable, result)
		        && !(result.hitObj && result.hitObj->x == wx && result.hitObj->y == wy)
		        && get_interface() == INTERFACE_NORMAL)
			// something was in the way, so don't allow a drag
			return GUI_PASS;
		dragging = true;
		set_mousedown(0, DRAG_BUTTON); // cancel MouseHeld
		game->set_mouse_pointer(0); // arrow
		tile = tile_manager->get_tile(obj_manager->get_obj_tile_num(selected_obj->obj_n) + selected_obj->frame_n);
		bool out_of_range;
		if (is_interface_fullscreen_in_combat() && player->get_location().distance(original_obj_loc) > 1)
			out_of_range = true;
		else
			out_of_range = false;
		return gui_drag_manager->start_drag(this, GUI_DRAG_OBJ, selected_obj, tile->data, 16, 16, 8, out_of_range);
	}

	return  GUI_PASS;
}

void    MapWindow::drag_drop_success(int x, int y, int message, void *data) {
	//DEBUG(0,LEVEL_DEBUGGING,"MapWindow::drag_drop_success\n");
	dragging = false;

// handled by drop target
//	if (selected_obj)
//		obj_manager->remove_obj (selected_obj);

	selected_obj = NULL;
	Redraw();
}

void    MapWindow::drag_drop_failed(int x, int y, int message, void *data) {
	DEBUG(0, LEVEL_DEBUGGING, "MapWindow::drag_drop_failed\n");
	dragging = false;
	selected_obj = NULL;
}

// this does nothing
GUI_status MapWindow::KeyDown(const Common::KeyState &key) {
	if (is_wizard_eye_mode()) {
		KeyBinder *keybinder = Game::get_game()->get_keybinder();
		ActionType a = keybinder->get_ActionType(key);
		switch (keybinder->GetActionKeyType(a)) {
		case WEST_KEY:
			moveMapRelative(-1, 0);
			break;
		case EAST_KEY:
			moveMapRelative(1, 0);
			break;
		case SOUTH_KEY:
			moveMapRelative(0, 1);
			break;
		case NORTH_KEY:
			moveMapRelative(0, -1);
			break;
		case NORTH_EAST_KEY:
			moveMapRelative(1, -1);
			break;
		case SOUTH_EAST_KEY:
			moveMapRelative(1, 1);
			break;
		case NORTH_WEST_KEY:
			moveMapRelative(-1, -1);
			break;
		case SOUTH_WEST_KEY:
			moveMapRelative(-1, 1);
			break;
		case CANCEL_ACTION_KEY:
			wizard_eye_stop();
			break;
		default:
			keybinder->handle_always_available_keys(a);
			return GUI_YUM;
		}
		if (keybinder->GetActionKeyType(a) <= SOUTH_WEST_KEY)
			wizard_eye_update();
		return GUI_YUM;
	}
	return GUI_PASS;
}

Obj *MapWindow::get_objAtMousePos(int mx, int my) {
	int wx, wy;
	mouseToWorldCoords(mx, my, wx, wy);

	return  obj_manager->get_obj(wx, wy, cur_level);
}


Actor *MapWindow::get_actorAtMousePos(int mx, int my) {
	int wx, wy;
	mouseToWorldCoords(mx, my, wx, wy);

	return  actor_manager->get_actor(wx, wy, cur_level);
}

void MapWindow::teleport_to_cursor() {
	int mx, my, wx, wy;
	screen->get_mouse_location(&mx, &my);

	mouseToWorldCoords(mx, my, wx, wy);
	game->get_player()->move(wx, wy, cur_level, true);
}

void MapWindow::select_target(int x, int y) {
	int wx, wy;
	mouseToWorldCoords(x, y, wx, wy);
	moveCursor(WRAP_VIEWP(cur_x, wx, map_width), wy - cur_y);
	game->get_event()->select_target(uint16(wx), uint16(wy), cur_level);
}

void MapWindow::mouseToWorldCoords(int mx, int my, int &wx, int &wy) {
	int x = mx - area.left;
	int y = my - area.top;

	int mapWidth = map->get_width(cur_level);

	wx = (cur_x + x / 16) % mapWidth;
	wy = (cur_y + y / 16) % mapWidth;
}

void MapWindow::drag_draw(int x, int y, int message, void *data) {
	Tile *tile;

	if (!selected_obj)
		return;

	tile = tile_manager->get_tile(obj_manager->get_obj_tile_num(selected_obj) + selected_obj->frame_n);

	int nx = x - 8;
	int ny = y - 8;

	if (nx + 16 >= screen->get_width())
		nx = screen->get_width() - 17;
	else if (nx < 0)
		nx = 0;

	if (ny + 16 >= screen->get_height())
		ny = screen->get_height() - 17;
	else if (ny < 0)
		ny = 0;

	screen->blit(nx, ny, tile->data, 8, 16, 16, 16, true);
	screen->update(nx, ny, 16, 16);
}


/* Display MapWindow animations. */
void MapWindow::drawAnims(bool top_anims) {
	if (!screen) // screen should be set early on
		return;
	else if (!anim_manager->get_surface()) // screen must be assigned to AnimManager
		anim_manager->set_surface(screen);
	anim_manager->display(top_anims);
}


/* Set mouse pointer to a movement-arrow for walking, or a crosshair. */
void MapWindow::update_mouse_cursor(uint32 mx, uint32 my) {
	Events *event = game->get_event();
	int wx = 0, wy = 0;
	sint16 rel_x, rel_y;
	uint8 mptr; // mouse-pointer is set here in get_movement_direction()

	if (event->get_mode() != MOVE_MODE && event->get_mode() != INPUT_MODE)
		return;

	// MousePos->WorldCoord->Direction&MousePointer
	if (game->is_orig_style())
		mouseToWorldCoords((int)mx, (int)my, wx, wy);
	get_movement_direction((uint16)mx, (uint16)my, rel_x, rel_y, &mptr);
	if (event->get_mode() == INPUT_MODE && mousecenter_x == (win_width / 2) && mousecenter_y == (win_height / 2)
	        && !event->dont_show_target_cursor())
		game->set_mouse_pointer(1); // crosshairs
	else if (dragging || (game->is_orig_style() && (wx == cur_x || wy == cur_y || wx == WRAP_VIEWP(cur_x, win_width - 1, map_width) || wy == (cur_y + win_height - 1)))
	         || (game->is_original_plus() && (my <= (uint32)Game::get_game()->get_game_y_offset() + 200 || game->is_original_plus_cutoff_map())
	             && mx >= (uint32)Game::get_game()->get_game_x_offset() + game->get_game_width() - border_width))
		game->set_mouse_pointer(0); // arrow
	else
		game->set_mouse_pointer(mptr); // 1=crosshairs, 2to9=arrows
}

/* Get relative movement direction from the MouseCenter coordinates to the
 * mouse coordinates mx,my, for walking with the mouse, etc. The mouse-pointer
 * number that should be used for that direction will be set to mptr.
 */
void MapWindow::get_movement_direction(uint16 mx, uint16 my, sint16 &rel_x, sint16 &rel_y, uint8 *mptr) {
	uint16 cent_x = mousecenter_x,
	       cent_y = mousecenter_y;
	if (game->is_original_plus_full_map() && game->get_event()->get_mode() != INPUT_MODE)
		cent_x -= (map_center_xoff + 1) / 2; // player is off center

	mx = (mx - area.left) / 16;
	my = (my - area.top) / 16;
	uint16 dist_x = abs(mx - cent_x), dist_y = abs(my - cent_y);

	rel_x = rel_y = 0;
	if (dist_x <= 4 && dist_y <= 4) {
		// use mapwindow coords (4,4 is center of mapwindow)
		uint8 cursor_num = movement_array[(9 * (4 + (my - cent_y))) + (4 + (mx - cent_x))];
		if (mptr) // set mouse-pointer number
			*mptr = cursor_num;
		if (cursor_num == 1) // nowhere
			return;
		if (cursor_num == 2) // up
			rel_y = -1;
		else if (cursor_num == 6) // down
			rel_y = 1;
		else if (cursor_num == 8) // left
			rel_x = -1;
		else if (cursor_num == 4) // right
			rel_x = 1;
		else if (cursor_num == 3) { // up-right
			rel_x = 1;
			rel_y = -1;
		} else if (cursor_num == 5) { // down-right
			rel_x = 1;
			rel_y = 1;
		} else if (cursor_num == 7) { // down-left
			rel_x = -1;
			rel_y = 1;
		} else if (cursor_num == 9) { // up-left
			rel_x = -1;
			rel_y = -1;
		}
	} else { // mapwindow is larger than the array; use 4 squares around center array
		if (dist_x <= 4 && my < cent_y) { // up
			rel_y = -1;
			if (mptr) *mptr = 2;
		} else if (dist_x <= 4 && my > cent_y) { // down
			rel_y = 1;
			if (mptr) *mptr = 6;
		} else if (mx < cent_x && dist_y <= 4) { // left
			rel_x = -1;
			if (mptr) *mptr = 8;
		} else if (mx > cent_x && dist_y <= 4) { // right
			rel_x = 1;
			if (mptr) *mptr = 4;
		} else if (mx > cent_x && my < cent_y) { // up-right
			rel_x = 1;
			rel_y = -1;
			if (mptr) *mptr = 3;
		} else if (mx > cent_x && my > cent_y) { // down-right
			rel_x = 1;
			rel_y = 1;
			if (mptr) *mptr = 5;
		} else if (mx < cent_x && my > cent_y) { // down-left
			rel_x = -1;
			rel_y = 1;
			if (mptr) *mptr = 7;
		} else if (mx < cent_x && my < cent_y) { // up-left
			rel_x = -1;
			rel_y = -1;
			if (mptr) *mptr = 9;
		}
	}
}


/* Revert mouse cursor to normal arrow. Stop walking. */
GUI_status MapWindow::MouseLeave(uint8 state) {
	if (game_type == NUVIE_GAME_MD) // magnifying glass - pointer 0 should be used too for some areas
		game->set_mouse_pointer(1);
	else
		game->set_mouse_pointer(0);
	walking = false;
	dragging = false;
	// NOTE: Don't clear selected_obj here! It's used to remove the object after
	// dragging.
	return (GUI_PASS);
}

byte *MapWindow::make_thumbnail() {
	if (thumbnail)
		return NULL;

	new_thumbnail = true;

	GUI::get_gui()->Display(); // this calls MapWindow::display() which in turn calls create_thumbnail(). :-)

	return thumbnail;
}

void MapWindow::create_thumbnail() {
	Common::Rect src_rect;

	src_rect.setWidth(MAPWINDOW_THUMBNAIL_SIZE * MAPWINDOW_THUMBNAIL_SCALE);
	src_rect.setHeight(src_rect.width());

	src_rect.left = area.left + win_width * 8 - (src_rect.width() / 2); // area.left + (win_width * 16) / 2 - 120 / 2
	src_rect.top = area.top + win_height * 8 - (src_rect.height() / 2); // area.top + (win_height * 16) / 2 - 120 / 2

	thumbnail = screen->copy_area(&src_rect, MAPWINDOW_THUMBNAIL_SCALE); //scale down x3

	new_thumbnail = false;
}

void MapWindow::free_thumbnail() {
	if (thumbnail) {
		delete[] thumbnail;
		thumbnail = NULL;
	}

	return;
}


/* Returns a new 8bit copy of the mapwindow as displayed. Caller must free it. */
Graphics::ManagedSurface *MapWindow::get_sdl_surface() {
	return (get_sdl_surface(0, 0, area.width(), area.height()));
}

Graphics::ManagedSurface *MapWindow::get_sdl_surface(uint16 x, uint16 y, uint16 w, uint16 h) {
	Graphics::ManagedSurface *new_surface = NULL;
	byte *screen_area;
	Common::Rect copy_area(area.left + x, area.top + y, area.left + x + w, area.top + y + h);

	GUI::get_gui()->Display();
	screen_area = screen->copy_area(&copy_area);

	new_surface = screen->create_sdl_surface_8(screen_area, copy_area.width(), copy_area.height());
// new_surface = screen->create_sdl_surface_from(screen_area, screen->get_bpp(),
//                                               copy_area.w, copy_area.h,
//                                               copy_area.w);
	free(screen_area);
	return (new_surface);
}

/* Returns the overlay surface. A new 8bit overlay is created if necessary. */
Graphics::ManagedSurface *MapWindow::get_overlay() {
	if (!overlay)
		overlay = new Graphics::ManagedSurface(area.width(), area.height(),
			Graphics::PixelFormat::createFormatCLUT8());

	return (overlay);
}

/* Set the overlay surface. The current overlay is deleted if necessary. */
void MapWindow::set_overlay(Graphics::ManagedSurface *surfpt) {
	if (overlay && (overlay != surfpt))
		SDL_FreeSurface(overlay);
	overlay = surfpt;
}

/* Returns true if town tiles are within 5 tiles of the player */
bool MapWindow::in_town() {
	MapCoord player_loc = actor_manager->get_player()->get_location();

	for (Std::vector<TileInfo>::iterator ti = m_ViewableMapTiles.begin();
	        ti != m_ViewableMapTiles.end(); ti++)
		if (MapCoord((*ti).x + cur_x, (*ti).y + cur_y, cur_level).distance(player_loc) <= 5 && // make sure tile is close enough
		        ((*ti).t->flags1 & TILEFLAG_WALL) && ((*ti).t->flags1 & TILEFLAG_WALL_MASK)) { //only wall tiles with wall direction bits set.
			return true;
		}
	return false;
}

void MapWindow::wizard_eye_start(MapCoord location, uint16 duration, CallBack *caller) {
	wizard_eye_info.moves_left = duration;
	wizard_eye_info.caller = caller;

	wizard_eye_info.prev_x = cur_x;
	wizard_eye_info.prev_y = cur_y;

	set_x_ray_view(X_RAY_ON);
	sint16 map_x = location.x - (win_width / 2);
	if (game->is_original_plus_full_map())
		map_x += ((map_center_xoff + 1) / 2);
	moveMap(map_x, location.y - (win_height / 2) , cur_level); // FIXME - map should already be centered on the caster so why are we doing this?
	grab_focus();
}

void MapWindow::wizard_eye_stop() {
	if (wizard_eye_info.moves_left > 0) {
		wizard_eye_info.moves_left = 0;
		wizard_eye_update();
	}
}

void MapWindow::wizard_eye_update() {
	if (wizard_eye_info.moves_left > 0)
		wizard_eye_info.moves_left--;

	if (wizard_eye_info.moves_left == 0) {
		set_x_ray_view(X_RAY_OFF);
		moveMap(wizard_eye_info.prev_x, wizard_eye_info.prev_y, cur_level);
		wizard_eye_info.caller->callback(EFFECT_CB_COMPLETE, (CallBack *)this, NULL);
		release_focus();
	}
}

void MapWindow::set_roof_mode(bool roofs) {
	roof_mode = roofs;
	if (roof_mode) {
		if (roof_tiles)
			return;
		else
			loadRoofTiles();
	} else {
		if (roof_tiles) {
			SDL_FreeSurface(roof_tiles);
			roof_tiles = NULL;
		}
	}
}

void MapWindow::loadRoofTiles() {
	Std::string imagefile = map->getRoofTilesetFilename();
	roof_tiles = SDL_LoadBMP(imagefile.c_str());
	if (roof_tiles && game->is_orig_style()) {
		SDL_SetColorKey(roof_tiles, SDL_TRUE, SDL_MapRGB(roof_tiles->format, 0, 0x70, 0xfc));
	}
}

bool MapWindow::in_dungeon_level() {
	if (game_type == NUVIE_GAME_MD) {
		return (cur_level == 1 || cur_level > 3); //FIXME this should probably be moved into script.
	}
	return (cur_level != 0 && cur_level != 5);
}

} // End of namespace Nuvie
} // End of namespace Ultima
