/*
 *  MapWindow.cpp
 *  Nuvie
 *
 *  Created by Eric Fry on Fri Mar 21 2003.
 *  Copyright (c) 2003. All rights reserved.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
#include <cassert>
#include "nuvieDefs.h"

#include "Configuration.h"
#include "U6misc.h"
#include "U6LList.h"
#include "Actor.h"
#include "ActorManager.h"
#include "ViewManager.h"
#include "MapWindow.h"
#include "Event.h"
#include "MsgScroll.h"
#include "MsgScrollNewUI.h"
#include "Effect.h" /* for initial fade-in */

#include "SoundManager.h"

#include "GUI.h"
#include "GameClock.h"
#include "GamePalette.h"
#include "Party.h"
#include "Weather.h"
#include "Script.h"
#include "U6objects.h"
#include "CommandBar.h"
#include "ActorView.h"
#include "InventoryView.h"
#include "Background.h"
#include "Keys.h"

#define USE_BUTTON 1 /* FIXME: put this in a common location */
#define WALK_BUTTON 3
#define ACTION_BUTTON 3
#define DRAG_BUTTON 1

#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.x = offset_x;
 area.y = 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.w = win_width * 16;
 area.h = 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.x = area.x+8;
 clip_rect.w = (win_width - 1) * 16;
 clip_rect.h = (win_height - 1) * 16;

 if(game_type == NUVIE_GAME_U6)
   clip_rect.y = area.y+8;
 else
   {
    clip_rect.y = area.y+16;
    clip_rect.h -= 16;
   }
}
else
{
	 clip_rect.x = game->get_game_x_offset();
	 clip_rect.y = game->get_game_y_offset();
	if(game->is_original_plus_cutoff_map())
		 clip_rect.w = game->get_game_width() - border_width - 1;
	else
		 clip_rect.w = game->get_game_width();
	 clip_rect.h = 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 = (SDL_BUTTON(USE_BUTTON) | SDL_BUTTON(WALK_BUTTON));
	else
		walk_button_mask = SDL_BUTTON(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();
    Event *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(SDL_GetMouseState(NULL, NULL) & 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;
    }
#ifdef HAVE_JOYSTICK_SUPPORT // repeat axis or hat when walking or wizard_eye_mode
	KeyBinder *keybinder = game->get_keybinder();
	if(keybinder->get_joystick() && keybinder->is_joy_repeat_enabled() && (event->get_mode() == MOVE_MODE || is_wizard_eye_mode())
	   && keybinder->get_next_joy_repeat_time() < clock->get_ticks())
// !game->user_paused(), !game->get_view_manager()->gumps_are_active() - I don't think these are needed but may need them later
	{
		SDL_Keycode key;
		if(keybinder->is_hat_repeating())
			key = keybinder->get_key_from_joy_hat_button(SDL_JoystickGetHat(keybinder->get_joystick(), 0));
		else
			key = keybinder->get_key_from_joy_walk_axes();
		if(key != SDLK_UNKNOWN)
		{
			SDL_Event sdl_event;
			sdl_event.type = SDL_KEYDOWN;
			sdl_event.key.keysym.sym = key;
			sdl_event.key.keysym.mod = KMOD_NONE;
			if(GUI::get_gui()->HandleEvent(&sdl_event) == GUI_PASS)
				event->handleEvent(&sdl_event);
		}
	}
#endif
}

// 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;
 //unsigned char *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.x + (j*16), draw_y = area.y + (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,(unsigned char *)tile->data,8,16,16,16,tile->transparent,&clip_rect);
           }

         tile = tile_manager->get_tile(map_ptr[j]);
         screen->blit(draw_x,draw_y,(unsigned char *)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.x+cursor_x*16,area.y+cursor_y*16,(unsigned char *)cursor_tile->data,8,16,16,16,true,&clip_rect);
  }

 if(show_use_cursor)
  {
   screen->blit(area.x+cursor_x*16,area.y+cursor_y*16,(unsigned char *)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.x, area.y, &clip_rect);

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

 if(overlay && overlay_level == MAP_OVERLAY_DEFAULT)
   screen->blit(area.x, area.y, (unsigned char *)(overlay->pixels), overlay->format->BitsPerPixel, 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.x;
	 if(game->is_original_plus_full_map())
		 we_x -= ((map_center_xoff + 1)/2)*16;
	screen->blit(we_x,mousecenter_y*16+area.y,(unsigned char *)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.x, area.y, (unsigned char *)(overlay->pixels), overlay->format->BitsPerPixel, overlay->w, overlay->h, overlay->pitch, true, &clip_rect);

// ptr = (unsigned char *)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.x+8,area.y+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.x=x;
    ti.y=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.x+(x*16)-cur_x_add,area.y+(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.x+(x*16)-cur_x_add,area.y+(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);

    SDL_Rect src, dst;
    src.w = 16;
    src.h = 16;
    dst.w = 16;
    dst.h = 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.x = area.x + (j*16);
	      dst.y = area.y + (i*16);
	      dst.x -= cur_x_add;
	      dst.y -= cur_y_add;

	       src.x = (roof_map_ptr[j] % MAPWINDOW_ROOFTILES_IMG_W) * 16;
	       src.y = (roof_map_ptr[j] / MAPWINDOW_ROOFTILES_IMG_W) * 16;

	       if(orig_style)
	       {
	    	   src.w = 16;
	    	   src.h = 16;
	    	   dst.w = 16;
	    	   dst.h = 16;

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

	    	   if(j==0)
	    	   {
	    		   src.x += 8;
	    		   src.w = 8;
	    		   dst.x += 8;
	    		   dst.w = 8;
	    	   } else if(j == win_width-1)
	    	   {
	    		   src.w = 8;
	    		   dst.w = 8;
	    	   }
	    	   SDL_BlitSurface(roof_tiles, &src, surface, &dst);
	       }
			else {
				unsigned char *ptr = (unsigned char *)roof_tiles->pixels;
				ptr += src.x + src.y*80;
				screen->blit(dst.x, dst.y, 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.x + NUVIE_RAND()%((win_width-1)*16-2) + 8;
			y = area.y + 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.x + (j*16) - cur_x_add, area.y + (i*16) - cur_y_add, (unsigned char *)grid_tile.data, 8, 16, 16, 16, true);
	     }
	  }
}

void MapWindow::generateTmpMap()
{
 unsigned char *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(unsigned char *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 map_width;

 x -= area.x;
 y -= area.y;

 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;
    }
    map_width = map->get_width(cur_level);
    x = (cur_x + x) % map_width;
    y = (cur_y + y) % map_width;

    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");
    Event *event = game->get_event();
    uint16 map_width = map->get_width(cur_level);

    x -= area.x;
    y -= area.y;

    if(message == GUI_DRAG_OBJ)
    {
        x = (cur_x + x / 16) % map_width;
        y = (cur_y + y / 16) % map_width;
        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, int 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, int button)
{
    Event *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, int button)
{
    looking = false;
    if(walk_with_left_button)
        set_walking(true);
    return(GUI_PASS);
}

// double-click
GUI_status MapWindow::MouseDouble(int x, int y, int button)
{
    Event *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 *game = Game::get_game();

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

GUI_status MapWindow::MouseDown (int x, int y, int button)
{
	//DEBUG(0,LEVEL_DEBUGGING,"MapWindow::MouseDown, button = %i\n", button);
	Event *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, int 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)
{
//	Event *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(SDL_Keysym 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.x;
	int y = my - area.y;

    int	map_width = map->get_width(cur_level);

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

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)
{
    Event *event = game->get_event();
    int wx, wy;
    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.x)/16;
    my = (my - area.y)/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);
}

unsigned char *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()
{
 SDL_Rect src_rect;

 src_rect.w = MAPWINDOW_THUMBNAIL_SIZE * MAPWINDOW_THUMBNAIL_SCALE;
 src_rect.h = src_rect.w;

 src_rect.x = area.x + win_width * 8 - (src_rect.w/2);  // area.x + (win_width * 16) / 2 - 120 / 2
 src_rect.y = area.y + win_height * 8 - (src_rect.h/2); // area.y + (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. */
SDL_Surface *MapWindow::get_sdl_surface()
{
 return(get_sdl_surface(0, 0, area.w, area.h));
}

SDL_Surface *MapWindow::get_sdl_surface(uint16 x, uint16 y, uint16 w, uint16 h)
{
 SDL_Surface *new_surface = NULL;
 unsigned char *screen_area;
 SDL_Rect copy_area = { (Sint16)(area.x + x), (Sint16)(area.y + y), w, h };

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

 new_surface = screen->create_sdl_surface_8(screen_area, copy_area.w, copy_area.h);
// 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. */
SDL_Surface *MapWindow::get_overlay()
{
    if(!overlay)
        overlay = SDL_CreateRGBSurface(SDL_SWSURFACE, area.w, area.h,
                                       8, 0, 0, 0, 0);
    return(overlay);
}

/* Set the overlay surface. The current overlay is deleted if necessary. */
void MapWindow::set_overlay(SDL_Surface *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);
}
