/*
 * Seven Kingdoms: Ancient Adversaries
 *
 * Copyright 1997,1998 Enlight Software Ltd.
 *
 * 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, see <http://www.gnu.org/licenses/>.
 *
 */

//Filename    : OWORLD.CPP
//Description : Object World

#include <OSYS.h>
#include <OGAME.h>
#include <OVGA.h>
#include <OFONT.h>
#include <OMOUSE.h>
#include <OMOUSECR.h>
#include <OFIRMRES.h>
#include <OPLANT.h>
#include <OPOWER.h>
#include <OSITE.h>
#include <OINFO.h>
#include <OTOWN.h>
#include <ONATION.h>
#include <OWEATHER.h>
#include <OTERRAIN.h>
#include <OWORLD.h>
#include <OANLINE.h>
#include <OTORNADO.h>
#include <OU_VEHI.h>
#include <OSERES.h>
#include <OREMOTE.h>
#include <ONEWS.h>


//------------ Define static class variables ------------//

short World::view_top_x, World::view_top_y;
int   World::max_x_loc=200, World::max_y_loc=200;

//----------- Begin of function World::World ----------//

World::World()
{
   memset( this, 0, sizeof(World) );

   //------- initialize matrix objects -------//

   map_matrix  = new MapMatrix;
   zoom_matrix = new ZoomMatrix;
}
//------------- End of function World::World -----------//


//----------- Begin of function World::~World ----------//

World::~World()
{
   if( map_matrix )
	{
      delete map_matrix;
      map_matrix = NULL;
   }

   if( zoom_matrix )
   {
      delete zoom_matrix;
      zoom_matrix = NULL;
   }

   deinit();
}
//------------- End of function World::~World -----------//


//----------- Begin of function World::init ----------//

void World::init()
{
	//----------- initialize vars -------------//

	scan_fire_x = 0;
	scan_fire_y = 0;
	lightning_signal = 0;

	map_matrix->init_para();
	zoom_matrix->init_para();
}
//------------- End of function World::init -----------//


//----------- Begin of function World::deinit ----------//

void World::deinit()
{
   if( loc_matrix )
   {
      mem_del( loc_matrix );
      loc_matrix  = NULL;
   }
}
//------------- End of function World::deinit -----------//


//--------- Begin of function World::assign_map ----------//
//
// After a map is loaded, assign_map() need to be called to
// initial map_matrix and zoom_matrix
//
void World::assign_map()
{
	//------------- assign map -------------//

   map_matrix-> assign_map(loc_matrix, max_x_loc, max_y_loc );
	zoom_matrix->assign_map(loc_matrix, max_x_loc, max_y_loc );

   //-------- set the zoom area box on map matrix ------//

   map_matrix->cur_x_loc = 0;
   map_matrix->cur_y_loc = 0;
   map_matrix->cur_cargo_width  = zoom_matrix->disp_x_loc;
   map_matrix->cur_cargo_height = zoom_matrix->disp_y_loc;
}
//----------- End of function World::assign_map ----------//


//----------- Begin of function World::paint ------------//
//
// Paint world window and scroll bars
//
void World::paint()
{
   map_matrix->paint();
   zoom_matrix->paint();
}
//----------- End of function World::paint ------------//


//----------- Begin of function World::refresh ------------//
//
void World::refresh()
{
   map_matrix->refresh();
   zoom_matrix->refresh();
}
//----------- End of function World::refresh ------------//


//----------- Begin of function World::process ------------//
//
// Called every frame
//
void World::process()
{
	//-------- process wall ----------//

	form_world_wall();

	//-------- process fire -----------//

	// BUGHERE : set Location::flammability for every change in cargo

	world.spread_fire(weather);

	// ------- process visibility --------//
	process_visibility();

	//-------- process lightning ------//
	// ###### begin Gilbert 11/8 ########//
	if(lightning_signal== 0 && weather.is_lightning())
	{
		// ------- create new lightning ----------//
		lightning_signal = 110;
	}
	if( lightning_signal == 106 && config.weather_effect)
	{
		lightning_strike(m.random(MAX_MAP_WIDTH), m.random(MAX_MAP_HEIGHT), 1);
	}
	if(lightning_signal == 100)
		lightning_signal = 5 + m.random(10);
	else if( lightning_signal)
		lightning_signal--;
	// ###### end Gilbert 11/8 ########//

	//---------- process ambient sound ---------//

	if( sys.frame_count%10 == 0 )    // process once per ten frames
		process_ambient_sound();

	// --------- update scan fire x y ----------//
	if(++scan_fire_x >= SCAN_FIRE_DIST)
	{
		scan_fire_x = 0;
		if( ++scan_fire_y >= SCAN_FIRE_DIST)
			scan_fire_y =0;
	}

}
//----------- End of function World::process ------------//


//----------- Begin of function World::next_day ------------//
//
// Called every frame
//
void World::next_day()
{
   plant_ops();

   weather = weather_forecast[0];

	for(int foreDay=0; foreDay < MAX_WEATHER_FORECAST-1; ++foreDay)
   {
      weather_forecast[foreDay] = weather_forecast[foreDay+1];
   }

   weather_forecast[MAX_WEATHER_FORECAST-1].next_day();

	// ####### begin Gilbert 11/7 #########//
	magic_weather.next_day();
	// ####### end Gilbert 11/7 #########//

	if(weather.has_tornado() && config.weather_effect)
	{
		tornado_array.add_tornado(weather.tornado_x_loc(max_x_loc, max_y_loc),
			weather.tornado_y_loc(max_x_loc, max_y_loc), 600);
	}

	// ######## begin Gilbert 31/7 #######//
	if( weather.is_quake() && config.random_event_frequency)
	// ######## end Gilbert 31/7 #######//
	{
		earth_quake();
	}

	//-------- Debug code: BUGHERE ----------//

	#ifdef DEBUG

	Location* locPtr = loc_matrix;

	for( int y=0 ; y<MAX_WORLD_Y_LOC ; y++ )
	{
		for( int x=0 ; x<MAX_WORLD_X_LOC ; x++ )
		{
			if( locPtr->has_unit(UNIT_LAND) )
			{
				err_when( unit_array.is_truly_deleted( locPtr->unit_recno(UNIT_LAND) ) );
			}

			locPtr++;
		}
	}

	#endif
}
//----------- End of function World::next_day ------------//


//----------- Begin of function World::detect ------------//
//
// Detect mouse action from user
//
// Return : 1 - mouse pressed on World area
//          0 - mouse not pressed on World area
//
int World::detect()
{
   if( map_matrix->detect() )
      return 1;

   if( zoom_matrix->detect() )
      return 1;

	if( detect_scroll() )
		return 1;

	// ##### begin Gilbert 16/9 #######//
	// return detect_firm_town();
	return 0;
	// ##### end Gilbert 16/9 #######//
}
//----------- End of function World::detect ------------//


//--------- Begin of function World::detect_scroll ---------//
//
// Detect if the mouse cursor is pushed towards the border
// of the screen to scroll the zoom window.
//
int World::detect_scroll()
{
   if( mouse_cursor.frame_flag )    // if it's now in frame selection mode
      return 0;

   if( next_scroll_time && m.get_time() < next_scroll_time )      // just scrolled not too long ago, wait for a little while before next scroll.
      return 0;

   int rc=0;

   //----- scroll left -----//

   if( mouse.cur_x == mouse.bound_x1 )
   {
      zoom_matrix->scroll(-1,0);
      rc = 1;
   }

   //---- scroll right -----//

   if( mouse.cur_x == mouse.bound_x2 )
   {
      zoom_matrix->scroll(1,0);
      rc = 1;
   }

   //---- scroll top -------//

   if( mouse.cur_y == mouse.bound_y1 )
   {
      zoom_matrix->scroll(0,-1);
      rc = 1;
   }

   //---- scroll bottom ----//

   if( mouse.cur_y == mouse.bound_y2 )
   {
      zoom_matrix->scroll(0,1);
      rc = 1;
   }

   //----- set next scroll time based on scroll_speed -----//
   //
   // slowest scroll speed: 500/1  = 500 milliseconds or 1/2 second
   // fastest scroll speed: 500/10 = 50  milliseconds or 1/20 second
   //
   //------------------------------------------------------//

   if( rc )
   {
      sys.zoom_need_redraw = 1;        // ask the zoom window to refresh next time
      next_scroll_time     = m.get_time() + 500/(config.scroll_speed+1);
   }

   return rc;
}
//----------- End of function World::detect_scroll -----------//


//--------- Begin of function World::go_loc --------//
//
// Go to a specified location.
//
// <int> xLoc, yLoc - location to go to.
// [int] selectFlag - whether should the object on the location if
//							 there is one. (default: 0)
//
void World::go_loc(int xLoc, int yLoc, int selectFlag)
{
	//------- set location ---------//

	zoom_matrix->cur_x_loc = xLoc;
	zoom_matrix->cur_y_loc = yLoc;

	map_matrix->cur_x_loc = xLoc - zoom_matrix->disp_x_loc/2;
	map_matrix->cur_y_loc = yLoc - zoom_matrix->disp_y_loc/2;

	//--------- refresh ------------//

	map_matrix->valid_cur_box();

	zoom_matrix->top_x_loc = map_matrix->cur_x_loc;
	zoom_matrix->top_y_loc = map_matrix->cur_y_loc;

	sys.zoom_need_redraw = 1;

	//---- if should select the object on the location ----//

	if( selectFlag )
	{
		Location* locPtr = world.get_loc(xLoc, yLoc);

		if( locPtr->has_any_unit() )
		{
			int mobileType;
			int unitRecno = locPtr->get_any_unit( mobileType );

			power.reset_selection();

			unit_array[unitRecno]->selected_flag = 1;
			unit_array.selected_recno = unitRecno;
			unit_array.selected_count++;
		}
		else if( locPtr->is_firm() )
		{
			power.reset_selection();
			firm_array.selected_recno = locPtr->firm_recno();
		}
		else if( locPtr->is_town() )
		{
			power.reset_selection();
			town_array.selected_recno = locPtr->town_recno();
		}
		else if( locPtr->has_site() )
		{
			power.reset_selection();
			site_array.selected_recno = locPtr->site_recno();
		}
	}

	//------- refresh the display -------//

	info.disp();
}
//----------- End of function World::go_loc --------//


//-------- Begin of function World::unveil ---------//
//
// Unveil all surrounding areas of the given object.
//
// <int> xLoc1, yLoc1 = the position of the object.
// <int> xLoc2, yLoc2 = the position of the object.
//
void World::unveil(int xLoc1, int yLoc1, int xLoc2, int yLoc2)
{
	if( config.explore_whole_map )
		return;

	xLoc1 = max( 0, xLoc1 - EXPLORE_RANGE);
	yLoc1 = max( 0, yLoc1 - EXPLORE_RANGE);
	xLoc2 = min( MAX_WORLD_X_LOC-1, xLoc2 + EXPLORE_RANGE);
	yLoc2 = min( MAX_WORLD_Y_LOC-1, yLoc2 + EXPLORE_RANGE);

	explore( xLoc1, yLoc1, xLoc2, yLoc2 );
}
//--------- End of function World::unveil ---------//


//-------- Begin of function World::explore ---------//
//
// Explore a specific area. No further exploration around the area.
//
// <int> xLoc1, yLoc1 = the position of the area.
// <int> xLoc2, yLoc2 = the position of the area.
//
void World::explore(int xLoc1, int yLoc1, int xLoc2, int yLoc2)
{
	if( config.explore_whole_map )
		return;

	int 		 xLoc, yLoc;
	Location* locPtr;
	char* 	 imageBuf = map_matrix->save_image_buf + sizeof(short)*2;
	char*     nationColorArray = nation_array.nation_power_color_array;
	char* 	 writePtr;

	int		shadowMapDist = max_x_loc + 1;
	int		tileYOffset;
	Location	*northWestPtr;
	char		tilePixel;

	for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ )
	{
		locPtr = get_loc(xLoc1, yLoc);

		for( xLoc=xLoc1 ; xLoc<=xLoc2 ; xLoc++, locPtr++ )
		{
			if( !locPtr->explored() )
			{
				locPtr->explored_on();

				//-------- draw pixel ----------//

				writePtr = imageBuf+MAP_WIDTH*yLoc+xLoc;

				switch( world.map_matrix->map_mode )
				{
					case MAP_MODE_TERRAIN:
						if( locPtr->fire_str() > 0)
							*writePtr = (char) FIRE_COLOR;

						else if( locPtr->is_plant() )
							*writePtr = plant_res.plant_map_color;

						else
						{
							tileYOffset = (yLoc & TERRAIN_TILE_Y_MASK) * TERRAIN_TILE_WIDTH;

							tilePixel = terrain_res.get_map_tile(locPtr->terrain_id)[tileYOffset + (xLoc & TERRAIN_TILE_X_MASK)];

							if( xLoc == 0 || yLoc == 0)
							{
								*writePtr = tilePixel;
							}
							else
							{
								northWestPtr = locPtr - shadowMapDist;
								if( (terrain_res[locPtr->terrain_id]->average_type >=
									terrain_res[northWestPtr->terrain_id]->average_type) )
								{
									*writePtr = tilePixel;
								}
								else
								{
									*writePtr = (char) VGA_GRAY;
								}
							}
							break;
						}
						break;

					case MAP_MODE_SPOT:
						if( locPtr->sailable() )
							*writePtr = (char) 0x32;

						else if( locPtr->has_hill() )
							*writePtr = (char) V_BROWN;

						else if( locPtr->is_plant() )
							*writePtr = (char) V_DARK_GREEN;

						else
							*writePtr = (char) VGA_GRAY+10;
						break;

					case MAP_MODE_POWER:
						if( locPtr->sailable() )
							*writePtr = (char) 0x32;

						else if( locPtr->has_hill() )
							*writePtr = (char) V_BROWN;

						else if( locPtr->is_plant() )
							*writePtr = (char) V_DARK_GREEN;

						else
							*writePtr = nationColorArray[locPtr->power_nation_recno];
						break;
				}

				//---- if the command base of the opponent revealed, establish contact ----//

				if( locPtr->is_firm() )
				{
					Firm* firmPtr = firm_array[locPtr->firm_recno()];

					if( firmPtr->nation_recno > 0 && nation_array.player_recno )
					{
						NationRelation *relation = (~nation_array)->get_relation(firmPtr->nation_recno);

						if( !relation->has_contact )
						{
							if( !remote.is_enable() )
							{
								(~nation_array)->establish_contact(firmPtr->nation_recno);
							}
							else
							{
								if( !relation->contact_msg_flag )
								{
									// packet structure : <player nation> <explored nation>
									short *shortPtr = (short *)remote.new_send_queue_msg(MSG_NATION_CONTACT, 2*sizeof(short));
									*shortPtr = nation_array.player_recno;
									shortPtr[1] = firmPtr->nation_recno;
									relation->contact_msg_flag = 1;
								}
							}
						}
					}
				}

				if( locPtr->is_town() )
				{
					Town* townPtr = town_array[locPtr->town_recno()];

					if( townPtr->nation_recno > 0 && nation_array.player_recno )
					{
						NationRelation *relation = (~nation_array)->get_relation(townPtr->nation_recno);

						if( !relation->has_contact )
						{
							if( !remote.is_enable() )
							{
								(~nation_array)->establish_contact(townPtr->nation_recno);
							}
							else
							{
								if( !relation->contact_msg_flag )
								{
									// packet structure : <player nation> <explored nation>
									short *shortPtr = (short *)remote.new_send_queue_msg(MSG_NATION_CONTACT, 2*sizeof(short));
									*shortPtr = nation_array.player_recno;
									shortPtr[1] = townPtr->nation_recno;
									relation->contact_msg_flag = 1;
								}
							}
						}
					}
				}
			}
		}
	}
}
//--------- End of function World::explore ---------//


//-------- Begin of function World::is_explored ---------//
//
// Check if the whole area has been explored or not.
//
// <int> xLoc1, yLoc1 = the coordination of the area to explore
// <int> xLoc2, yLoc2 = the coordination of the area to explore
//
int World::is_explored(int xLoc1, int yLoc1, int xLoc2, int yLoc2)
{
	if( config.explore_whole_map )
      return 1;

   int xLoc, yLoc;
   Location* locPtr;

   for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ )
   {
      locPtr = get_loc(xLoc1, yLoc);

      for( xLoc=xLoc1 ; xLoc<=xLoc2 ; xLoc++, locPtr++ )
      {
         if( !locPtr->explored() )
            return 0;
      }
   }

   return 1;
}
//--------- End of function World::is_explored ---------//


//----------- Begin of function World::load_map ------------//
//
// Load a custom map file.
//
void World::load_map(char* fileName)
{
   generate_map();

   return;

   //---------- initialize the map matrix --------//

   max_x_loc = 200;
   max_y_loc = 200;

   loc_matrix = (Location*) mem_resize( loc_matrix  , max_x_loc * max_y_loc * sizeof(Location) );

   memset( loc_matrix, 0, sizeof(Location) * max_x_loc * max_y_loc );

   int baseType = TERRAIN_DARK_DIRT;

   int terrainId    = terrain_res.scan(baseType, MIDDLE_MASK, baseType, MIDDLE_MASK, 
					  		 baseType, MIDDLE_MASK, baseType, MIDDLE_MASK, 1);     // 1-get the first instance

   for( int i=0 ; i<max_x_loc*max_y_loc ; i++ )
   {
      loc_matrix[i].terrain_id = terrainId+m.random(3);
   }

   assign_map();

   return;

   //---------- initialize the map matrix --------//

   max_x_loc = 120;
   max_y_loc = 120;

   loc_matrix = (Location*) mem_resize( loc_matrix  , max_x_loc * max_y_loc * sizeof(Location) );

   //-------------- read in the map -------------//

   File mapFile;

   mapFile.file_open(fileName);

   mapFile.file_read(loc_matrix, sizeof(Location)*MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC );

   mapFile.file_close();

   //----- assign the map to MapMatrix and ZoomMatrix -----//

   assign_map();
}
//----------- End of function World::load_map ------------//


//-------- Begin of function World::check_unit_space ------//
//
// To check whether or not the area bounded by the upper left
// corner (xLoc1, yLoc1) and lower right corner (xLoc2, yLoc2)
// can be built.
//
// <int>  xLoc1          = the upper left x
// <int>  yLoc1          = the upper left y
// <int>  xLoc2          = the lower right x
// <int>  yLoc2          = the lower right y
// [int]  mobileType		 = mobile type (default: UNIT_LAND)
// [int]  buildFlag 		 = whether the located area is for building a firm/town
//									if so, the location must no have any raw site.
//									(default: 0)
//
// return 1 for true. 0 for false
//
inline int World::check_unit_space(int xLoc1, int yLoc1, int xLoc2, int yLoc2, int mobileType, int buildFlag)
{
	if(xLoc1<0 || xLoc1>=MAX_WORLD_X_LOC)
		return 0;
	if(yLoc1<0 || yLoc1>=MAX_WORLD_Y_LOC)
		return 0;
	if(xLoc2<0 || xLoc2>=MAX_WORLD_X_LOC)
		return 0;
	if(yLoc2<0 || yLoc2>=MAX_WORLD_Y_LOC)
		return 0;

	Location* locPtr = world.get_loc(xLoc1, yLoc1);
	int x, y;
	int canBuildFlag = 1;

	for(y=yLoc1; y<=yLoc2; y++)
	{
		locPtr = world.get_loc(xLoc1, y);

		for(x=xLoc1; x<=xLoc2; x++, locPtr++)
		{
			if( !locPtr->can_move(mobileType) ||
				 ( buildFlag && (locPtr->is_power_off() || locPtr->has_site()) ) ) 		// if build a firm/town, there must not be any sites in the area
			{
				canBuildFlag=0;
				break;
			}
		}

		if(canBuildFlag==0)
			break;
	}

	if( canBuildFlag )
		return 1;
	else
		return 0;
}
//-------- End of function World::check_unit_space ------//


//-------- Begin of function World::locate_space ------//
//
// Locate an area in the world map around the firm to place
// the unit
//
// <int&> xLoc1          = the upper left x location of the building, also for returning the result location
// <int&> yLoc1          = the upper left y location of the building
// <int>  xLoc2          = the lower right x location of the building
// <int>  yLoc2          = the lower right y location of the building
// <int>  spaceLocWidth  = the location width of the required space
// <int>  spaceLocHeight = the location height of the required space
// [int]  mobileType		 = mobile type (default: UNIT_LAND)
// [int]  regionId		 = specify the region no. of the location to locate
//									(default: region no. of xLoc1, yLoc1)
// [int]  buildFlag 		 = whether the located area is for building a firm/town
//									if so, the location must no have any raw site.
//									(default: 0)
//
// return : <int> 1 - free space found
//                0 - free space not found
//
int World::locate_space(int& xLoc1, int& yLoc1, int xLoc2, int yLoc2,
								int spaceLocWidth, int spaceLocHeight, int mobileType, int regionId, int buildFlag)
{
	if( !regionId )
		regionId = get_loc(xLoc1, yLoc1)->region_id;

	int isPlateau = get_loc(xLoc1, yLoc1)->is_plateau();

	//-----------------------------------------------------------//
	// xLoc, yLoc is the adjusted upper left corner location of
	// the firm. with the adjustment, it is easier to do the following
	// checking.
	//-----------------------------------------------------------//

	Location* locPtr;
	int xLoc = xLoc1 - spaceLocWidth + 1;
	int yLoc = yLoc1 - spaceLocHeight + 1;

	if(xLoc < 0)
		xLoc = 0;
	if(yLoc < 0)
		yLoc = 0;

	int width   = xLoc2 - xLoc + 1;
	int height  = yLoc2 - yLoc + 1;
	int loopCount=0;

	while(1)
	{
		err_when( ++loopCount > MAX_WORLD_X_LOC * MAX_WORLD_Y_LOC * 4 );

		//-----------------------------------------------------------//
		// step 1
		//-----------------------------------------------------------//
		int xOffset = width/2;
		int yOffset = height;
		int x, y;

		x = xLoc + xOffset;
		y = yLoc + yOffset;

		if(x>=0 && y>=0 && x+spaceLocWidth-1<MAX_WORLD_X_LOC && y+spaceLocHeight-1<MAX_WORLD_Y_LOC)
		{
			if(mobileType==UNIT_LAND || (x%2==0 && y%2==0))
			{
				locPtr = get_loc(x,y);

				if( locPtr->region_id == regionId &&
					 locPtr->is_plateau() == isPlateau && 
					 check_unit_space(x, y, x+spaceLocWidth-1, y+spaceLocHeight-1, mobileType, buildFlag))
				{
					xLoc1 = x;
					yLoc1 = y;
					return 1;
				}
			}
		}

		int sign = -1;
		int i, j, k, limit;

		//-----------------------------------------------------------//
		// step 2
		//-----------------------------------------------------------//
		//y = yLoc + yOffset;
		limit = width + 2;
		for(i=1; i<limit; i++)
		{
			xOffset += sign * i;
			x = xLoc + xOffset;

			if(x>=0 && y>=0 && x+spaceLocWidth-1<MAX_WORLD_X_LOC && y+spaceLocHeight-1<MAX_WORLD_Y_LOC)
			{
				if(mobileType==UNIT_LAND || (x%2==0 && y%2==0))
				{
					locPtr = get_loc(x,y);

					if( locPtr->region_id == regionId &&
						 locPtr->is_plateau() == isPlateau &&
						 check_unit_space(x, y, x+spaceLocWidth-1, y+spaceLocHeight-1, mobileType, buildFlag))
					{
						xLoc1 = x;
						yLoc1 = y;
						return 1;
					}
				}
			}

			sign *= -1;
		}

		//-----------------------------------------------------------//
		// step 3
		//-----------------------------------------------------------//
		i = limit-1;

		limit = (height+1)*2;
		int r = sign*i;
		int lastX = xOffset;
		//int lastY = yOffset;

		for(j=0; j<limit; j++)
		{
			if(j%2)
			{
				//x = xLoc + lastX;
				xOffset = lastX;
				x = xLoc + xOffset;
				//y = yLoc + yOffset;

				if(x>=0 && y>=0 && x+spaceLocWidth-1<MAX_WORLD_X_LOC && y+spaceLocHeight-1<MAX_WORLD_Y_LOC)
				{
					if(mobileType==UNIT_LAND || (x%2==0 && y%2==0))
					{
						locPtr = get_loc(x,y);

						if( locPtr->region_id == regionId &&
							 locPtr->is_plateau() == isPlateau &&
							 check_unit_space(x, y, x+spaceLocWidth-1, y+spaceLocHeight-1, mobileType, buildFlag))
						{
							xLoc1 = x;
							yLoc1 = y;
							return 1;
						}
					}
				}
			}
			else
			{
				xOffset = lastX + r;
				yOffset--;

				x = xLoc + xOffset;
				y = yLoc + yOffset;

				if(x>=0 && y>=0 && x+spaceLocWidth-1<MAX_WORLD_X_LOC && y+spaceLocHeight-1<MAX_WORLD_Y_LOC)
				{
					if(mobileType==UNIT_LAND || (x%2==0 && y%2==0))
					{
						locPtr = get_loc(x,y);

						if( locPtr->region_id == regionId &&
							 locPtr->is_plateau() == isPlateau &&
							 check_unit_space(x, y, x+spaceLocWidth-1, y+spaceLocHeight-1, mobileType, buildFlag))
						{
							xLoc1 = x;
							yLoc1 = y;
							return 1;
						}
					}
				}
			}
		}

		//-----------------------------------------------------------//
		// step 4
		//-----------------------------------------------------------//
		y = yLoc + yOffset;
		for(k=0; k<=width; k++)
		{
			sign *= -1;
			i--;
			r = sign*i;
			xOffset -= r;

			x = xLoc + xOffset;

			if(x>=0 && y>=0 && x+spaceLocWidth-1<MAX_WORLD_X_LOC && y+spaceLocHeight-1<MAX_WORLD_Y_LOC)
			{
				if(mobileType==UNIT_LAND || (x%2==0 && y%2==0))
				{
					locPtr = get_loc(x,y);

					if( locPtr->region_id == regionId &&
						 locPtr->is_plateau() == isPlateau &&
						 check_unit_space(x, y, x+spaceLocWidth-1, y+spaceLocHeight-1, mobileType, buildFlag))
					{
						xLoc1 = x;
						yLoc1 = y;
						return 1;
					}
				}
			}
		}

		//-----------------------------------------------------------//
		// re-init the parameters
		//-----------------------------------------------------------//
		if(xLoc<=0 && yLoc<=0 && width>=MAX_WORLD_X_LOC && height>=MAX_WORLD_Y_LOC)
			break;   // the whole map has been checked

		width += 2;
		height += 2;

		xLoc -= 1;
		yLoc -= 1;
		if(xLoc<0)
		{
			xLoc = 0;
			width--;
		}
		if(yLoc<0)
		{
			yLoc=0;
			height--;
		}

		if(xLoc+width>MAX_WORLD_X_LOC)
			width--;
		if(yLoc+height>MAX_WORLD_Y_LOC)
			height--;

		//if(width==xLoc2-xLoc1+spaceLocWidth && height==yLoc2-yLoc1+spaceLocHeight) // terminate the checking
		// return 0;
	}

	return 0;
}
//-------- End of function World::locate_space ------//


//-------- Begin of function World::locate_space_random ------//
//
// Locate an area of space in the world map randomly. Pick any
// space available in that area without a specific scanning order.
//
// <int&> xLoc1          = the scaning range, also for returning the result location
// <int&> yLoc1          = the scaning range
// <int>  xLoc2          = the scaning range
// <int>  yLoc2          = the scaning range
// <int>  spaceLocWidth  = the location width of the required space
// <int>  spaceLocHeight = the location height of the required space
// <int>  maxTries       = maximum no. of tries
// [int]  regionId		 = if this is specified, the result location will
//									be in this region.
// [int]  buildSite      = whether locating space for building a site
//                         (default: 0)
// [char] teraMask		 = terrain mask (default: 1)
//
// return : <int> 1 - free space found
//                0 - free space found
//
int World::locate_space_random(int& xLoc1, int& yLoc1, int xLoc2, int yLoc2,
								int spaceLocWidth, int spaceLocHeight, int maxTries,
								int regionId, int buildSite, char teraMask)
{
   int       i, x, y, xTemp, xLoc, yLoc, canBuildFlag;
   int       scanWidth  = xLoc2-xLoc1-spaceLocWidth+2; //xLoc2-xLoc1+1-spaceLocWidth+1;
   int       scanHeight = yLoc2-yLoc1-spaceLocHeight+2; //yLoc2-yLoc1+1-spaceLocHeight+1;
	Location* locPtr;

   for( i=0 ; i<maxTries ; i++ )
   {
      xLoc = xLoc1 + m.random(scanWidth);
      yLoc = yLoc1 + m.random(scanHeight);
		canBuildFlag=1;

		//---------- check if the area is all free ----------//

		xTemp = xLoc+spaceLocWidth-1;

		for( y=yLoc+spaceLocHeight-1; y>=yLoc; y-- )
		{
			locPtr = world.get_loc(xTemp, y);

			for(x=xTemp; x>=xLoc; x--, locPtr-- )
			{
				if( ( buildSite ? !locPtr->can_build_site(teraMask) : !locPtr->can_build_firm(teraMask) ) ||
					 locPtr->is_power_off() )
				{
					canBuildFlag=0;
					break;
				}
			}

			if(!canBuildFlag)
				break;
		}

		if( !canBuildFlag )
			continue;

		//------ check region id. ------------//

		locPtr = world.get_loc(xLoc, yLoc);

		if( regionId && locPtr->region_id != regionId )
			continue;

		//------------------------------------//

		xLoc1 = xLoc;
		yLoc1 = yLoc;

		err_when(buildSite && !locPtr->can_build_site(teraMask));//-*** hard codes for mine size 3x3
		return 1;
	}

	return 0;
}
//-------- End of function World::locate_space_random ------//


//-------- Begin of function World::can_build_firm ---------//
//
// Check if it is free to construct a building on the specific area.
//
// <int>   xLoc1, yLoc1 = the coordination of the area to can_build
// <int>   firmId       = id. of the firm
// [short] unitRecno    = the unit recno of the unit to build the firm
//								  if the builder unit stands on the building area, still consider the area as buildable
//								  (default: -1, do not take the builder into account)
//
int World::can_build_firm(int xLoc1, int yLoc1, int firmId, short unitRecno)
{
	if( xLoc1 < 0 || yLoc1 < 0 || xLoc1 > MAX_WORLD_X_LOC || yLoc1 > MAX_WORLD_Y_LOC )	
		return 0;		

	//------------------------------------------//

	FirmInfo* firmInfo = firm_res[firmId];

	int xLoc, yLoc;
	int xLoc2 = xLoc1 + firmInfo->loc_width - 1;
	int yLoc2 = yLoc1 + firmInfo->loc_height - 1;
	if(xLoc2>=max_x_loc || yLoc2>max_y_loc)
		return 0;

	Location* locPtr;
	char teraMask, pierFlag;

	switch(firmInfo->tera_type)
	{
	case 1:		// default : land firm
	case 2:		// sea firm
	case 3:		// land or sea firm
		teraMask = firmInfo->tera_type;
		for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ )
		{
			locPtr = get_loc(xLoc1, yLoc);

			for( xLoc=xLoc1 ; xLoc<=xLoc2 ; xLoc++, locPtr++ )
			{
				// ##### patch begin Gilbert 14/3 ######//
				if(!locPtr->can_build_firm(teraMask) && 
					(!locPtr->has_unit(UNIT_LAND) || locPtr->unit_recno(UNIT_LAND)!=unitRecno))
					return 0;
				// ##### patch end Gilbert 14/3 ######//

				if( firmId != FIRM_MINE && locPtr->has_site() )		// don't allow building any buildings other than mines on a location with a site
					return 0;
			}
		}
		return 1;

	case 4:				// special firm, such as harbor
		// must be 3x3,
		// centre square of one side is land (teraMask=1),
		// two squares on that side can be land or sea (teraMask=3)
		// and other (6 squares) are sea (teraMask=2)
		if( firmInfo->loc_width != 3 ||
			firmInfo->loc_height != 3)
			return 0;

		pierFlag = 1|2|4|8;		// bit0=north, bit1=south, bit2=west, bit3=east
		for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ )
		{
			locPtr = get_loc(xLoc1, yLoc);

			for( xLoc=xLoc1 ; xLoc<=xLoc2 ; xLoc++, locPtr++ )
			{
				if( locPtr->has_site() )		// don't allow building any buildings other than mines on a location with a site
					return 0;

static char northPierTera[3][3] = { {2,2,2},{2,2,2},{3,1,3} };
static char southPierTera[3][3] = { {3,1,3},{2,2,2},{2,2,2} };
static char westPierTera[3][3] = { {2,2,3},{2,2,1},{2,2,3} };
static char eastPierTera[3][3] = { {3,2,2},{1,2,2},{3,2,2} };
				int x = xLoc - xLoc1;
				int y = yLoc - yLoc1;
				if(!locPtr->can_build_harbor(northPierTera[y][x]) )
					pierFlag &= ~1;
				if(!locPtr->can_build_harbor(southPierTera[y][x]) )
					pierFlag &= ~2;
				if(!locPtr->can_build_harbor(westPierTera[y][x]) )
					pierFlag &= ~4;
				if(!locPtr->can_build_harbor(eastPierTera[y][x]) )
					pierFlag &= ~8;
			}
		}
		err_when( pierFlag != 0 && pierFlag != 1 && pierFlag != 2 &&
			pierFlag != 4 && pierFlag != 8 );
		return pierFlag;
		break;

	// other tera_type here

	default:
		err_here();
		return 0;
	}
}
//--------- End of function World::can_build_firm ---------//


//-------- Begin of function World::can_build_town ---------//
//
// <int>   xLoc1, yLoc1 = the coordination of the area to can_build
// [short] unitRecno    = the unit recno of the unit to build the town
//								  if the builder unit stands on the building area, still consider the area as buildable
//								  (default: -1, do not take the builder into account)
//
int World::can_build_town(int xLoc1, int yLoc1, short unitRecno)
{
	int xLoc, yLoc;
	int xLoc2 = xLoc1 + STD_TOWN_LOC_WIDTH  - 1;
	int yLoc2 = yLoc1 + STD_TOWN_LOC_HEIGHT - 1;

	if(xLoc2>=max_x_loc || yLoc2>=max_y_loc)
		return 0;

	Location* locPtr;

	for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ )
	{
		locPtr = get_loc(xLoc1, yLoc);

		for( xLoc=xLoc1 ; xLoc<=xLoc2 ; xLoc++, locPtr++ )
		{
			// ##### patch begin Gilbert 14/3 ######//
			// allow the building unit to stand in the area
			if( !locPtr->can_build_town() && 
				(!locPtr->has_unit(UNIT_LAND) || locPtr->unit_recno(UNIT_LAND)!=unitRecno) )
				return 0;
			// ##### patch end Gilbert 14/3 ######//
		}
	}

	return 1;
}
//--------- End of function World::can_build_town ---------//


//-------- Begin of function World::can_build_wall ---------//
//
// <int> xLoc, yLoc  = the coordination of the area to can_build
// <int> nationRecno = recno of the builder nation.
//
int World::can_build_wall(int xLoc, int yLoc, short nationRecno)
{
	Location* locPtr = get_loc(xLoc, yLoc);

	return locPtr->can_build_wall() && locPtr->power_nation_recno == nationRecno;
}
//--------- End of function World::can_build_wall ---------//


//-------- Begin of function World::can_destruct_wall ---------//
//
// <int> xLoc, yLoc  = the coordination of the area to can_build
// <int> nationRecno = recno of the builder nation.
//
int World::can_destruct_wall(int xLoc, int yLoc, short nationRecno)
{
	Location* locPtr = get_loc(xLoc, yLoc);

	return locPtr->is_wall() && locPtr->power_nation_recno == nationRecno;
}
//--------- End of function World::can_destruct_wall ---------//


//---------- Begin of function World::draw_link_line -----------//
//
// <int> srcFirmId          - id. of the source firm.
//                            0 if the source is a town
// <int> srcTownRecno       - town recno of the source town
//                            0 if the source is a firm
// <int> srcXLoc1, srcYLoc1 - the location of the source area
//       srcXLoc2, srcYLoc2
//
// [int] giveEffectiveDis   - use this value as the effective distance if this is given
//										(default: 0)
//
void World::draw_link_line(int srcFirmId, int srcTownRecno, int srcXLoc1,
									int srcYLoc1, int srcXLoc2, int srcYLoc2, int givenEffectiveDis)
{
	if( srcFirmId == FIRM_INN ) 	// FirmInn's link is only for scan for neighbor inns quickly, the link line is not displayed
		return;

	//--------------------------------------//

	int srcXLoc = (srcXLoc1 + srcXLoc2)/2;
	int srcYLoc = (srcYLoc1 + srcYLoc2)/2;

	int srcX = ( ZOOM_X1 + (srcXLoc1-zoom_matrix->top_x_loc) * ZOOM_LOC_WIDTH
				  + ZOOM_X1 + (srcXLoc2-zoom_matrix->top_x_loc+1) * ZOOM_LOC_WIDTH ) / 2;

	int srcY = ( ZOOM_Y1 + (srcYLoc1-zoom_matrix->top_y_loc) * ZOOM_LOC_HEIGHT
				  + ZOOM_Y1 + (srcYLoc2-zoom_matrix->top_y_loc+1) * ZOOM_LOC_HEIGHT ) / 2;

	//------- draw lines connected to town ---------//

	int   i, townX, townY, effectiveDis;
	Town* townPtr;

	if( givenEffectiveDis )
		effectiveDis = givenEffectiveDis;
	else
	{
		if( srcFirmId )
			effectiveDis = EFFECTIVE_FIRM_TOWN_DISTANCE;
		else
			effectiveDis = EFFECTIVE_TOWN_TOWN_DISTANCE;
	}

	if( !srcFirmId || firm_res[srcFirmId]->is_linkable_to_town )    // don't draw link line to town if it's an inn
	{
		for( i=town_array.size() ; i>0 ; i-- )
		{
			if( town_array.is_deleted(i) )
				continue;

			townPtr = town_array[i];

			if( srcTownRecno && townPtr->town_recno != srcTownRecno )
				continue;

			//-------- check the distance --------//

			if( m.points_distance( townPtr->center_x, townPtr->center_y,
				 srcXLoc, srcYLoc ) > effectiveDis )
			{
				continue;
			}

			//------ check if both are on the same terrain type ------//

			if( (world.get_loc(townPtr->center_x, townPtr->center_y)->is_plateau()==1)
				 != (world.get_loc(srcXLoc, srcYLoc)->is_plateau()==1) )
			{
				continue;
			}

			//---------- draw line now -----------//

			townX = ( ZOOM_X1 + (townPtr->loc_x1-zoom_matrix->top_x_loc) * ZOOM_LOC_WIDTH
					  + ZOOM_X1 + (townPtr->loc_x2-zoom_matrix->top_x_loc+1) * ZOOM_LOC_WIDTH ) / 2;

			townY = ( ZOOM_Y1 + (townPtr->loc_y1-zoom_matrix->top_y_loc) * ZOOM_LOC_HEIGHT
					  + ZOOM_Y1 + (townPtr->loc_y2-zoom_matrix->top_y_loc+1) * ZOOM_LOC_HEIGHT ) / 2;

			anim_line.draw_line(&vga_back, srcX, srcY, townX, townY);
		}
	}

	//------- draw lines connected to firms ---------//

	if( givenEffectiveDis )
		effectiveDis = givenEffectiveDis;
	else
	{
		if( srcFirmId )
			effectiveDis = EFFECTIVE_FIRM_FIRM_DISTANCE;
		else
			effectiveDis = EFFECTIVE_FIRM_TOWN_DISTANCE;
	}

	int   firmX, firmY, linkFlag;
	Firm* firmPtr;

	for( i=firm_array.size() ; i>0 ; i-- )
	{
		if( firm_array.is_deleted(i) )
         continue;

		firmPtr = firm_array[i];

		//------ only link if the firms have relationship -----//

		if( srcFirmId )
			linkFlag = firm_res[firmPtr->firm_id]->is_linkable_to_firm(srcFirmId);
		else
			linkFlag = firm_res[firmPtr->firm_id]->is_linkable_to_town;

		if( !linkFlag )
			continue;

		//-------- check the distance --------//

		if( m.points_distance( firmPtr->center_x, firmPtr->center_y,
			 srcXLoc, srcYLoc ) > effectiveDis )
		{
			continue;
		}

		//------ check if both are on the same terrain type ------//

		if( (world.get_loc(firmPtr->center_x, firmPtr->center_y)->is_plateau()==1)
			 != (world.get_loc(srcXLoc, srcYLoc)->is_plateau()==1) )
		{
			continue;
		}

		//---------- draw line now -----------//

		firmX = ( ZOOM_X1 + (firmPtr->loc_x1-zoom_matrix->top_x_loc) * ZOOM_LOC_WIDTH
				  + ZOOM_X1 + (firmPtr->loc_x2-zoom_matrix->top_x_loc+1) * ZOOM_LOC_WIDTH ) / 2;

		firmY = ( ZOOM_Y1 + (firmPtr->loc_y1-zoom_matrix->top_y_loc) * ZOOM_LOC_HEIGHT
				  + ZOOM_Y1 + (firmPtr->loc_y2-zoom_matrix->top_y_loc+1) * ZOOM_LOC_HEIGHT ) / 2;

		anim_line.draw_line(&vga_back, srcX, srcY, firmX, firmY);
	}
}
//----------- End of function World::draw_link_line ------------//


//-------- Begin of function World::set_surr_power_off ---------//
void World::set_surr_power_off(int xLoc, int yLoc)
{
	if(xLoc>0) // west
		get_loc(xLoc-1, yLoc)->set_power_off();

	if(xLoc<max_x_loc-1)
		get_loc(xLoc+1, yLoc)->set_power_off();
	
	if(yLoc>0) // north
		get_loc(xLoc, yLoc-1)->set_power_off();

	if(yLoc<max_y_loc-1) // south
		get_loc(xLoc, yLoc+1)->set_power_off();
}
//----------- End of function World::set_surr_power_off ------------//


//-------- Begin of function World::set_all_power ---------//
//
void World::set_all_power()
{
	//--------- set town's influence -----------//

	Town* townPtr;
	int i;

	for( i=town_array.size() ; i>0 ; i-- )
	{
		if( town_array.is_deleted(i) )
			continue;

		townPtr = town_array[i];

		if( !townPtr->nation_recno )
			continue;

		//------- set the influence range of this town -----//

		set_power(townPtr->loc_x1, townPtr->loc_y1, townPtr->loc_x2, townPtr->loc_y2, (char)townPtr->nation_recno);
	}

	//--------- set firm's influence -----------//

	Firm* firmPtr;

	for( i=firm_array.size() ; i>0 ; i-- )
	{
		if( firm_array.is_deleted(i) )
			continue;

		firmPtr = firm_array[i];

		if( !firmPtr->nation_recno )
			continue;

		if( !firmPtr->should_set_power )
			continue;

		//------- set the influence range of this firm -----//

		set_power(firmPtr->loc_x1, firmPtr->loc_y1, firmPtr->loc_x2, firmPtr->loc_y2, (char)firmPtr->nation_recno);
	}
}
//--------- End of function World::set_all_power ---------//


//-------- Begin of function World::set_power ---------//
//
// <int> xLoc1, yLoc1, - area on the map which the power should be set
//		   xLoc2, yLoc2
//
// <int> nationRcno - nation recno
//
void World::set_power(int xLoc1, int yLoc1, int xLoc2, int yLoc2, int nationRecno)
{
	//------- reset power_nation_recno first ------//

	int	plateauResult = (get_loc((xLoc1+xLoc2)/2, (yLoc1+yLoc2)/2)->is_plateau()==1);

	int   	 xLoc, yLoc, centerY, t;
	Location* locPtr = loc_matrix;

	xLoc1 = max( 0, xLoc1 - EFFECTIVE_POWER_DISTANCE+1);
	yLoc1 = max( 0, yLoc1 - EFFECTIVE_POWER_DISTANCE+1);
	xLoc2 = min( MAX_WORLD_X_LOC-1, xLoc2 + EFFECTIVE_POWER_DISTANCE-1);
	yLoc2 = min( MAX_WORLD_Y_LOC-1, yLoc2 + EFFECTIVE_POWER_DISTANCE-1);

	centerY = (yLoc1+yLoc2) / 2;

	for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ )
	{
		t=abs(yLoc-centerY)/2;

		for( xLoc=xLoc1+t ; xLoc<=xLoc2-t ; xLoc++, locPtr++ )
		{
			locPtr = get_loc(xLoc, yLoc);

			if(locPtr->sailable())//if(!locPtr->walkable())
				continue;

			if(locPtr->is_power_off())
				continue;

			if((locPtr->is_plateau()==1) != plateauResult)
				continue;

			if(locPtr->power_nation_recno==0)
			{
				locPtr->power_nation_recno = nationRecno;
				sys.map_need_redraw = 1;						// request redrawing the map next time
			}
		}
	}
}
//--------- End of function World::set_power ---------//


//-------- Begin of function World::restore_power ---------//
//
// <int> xLoc1, yLoc1, - area on the map which the power should be restored
//		   xLoc2, yLoc2
//
// <int> townRecno, firmRecno - either one
//
void World::restore_power(int xLoc1, int yLoc1, int xLoc2, int yLoc2, int townRecno, int firmRecno)
{
	int nationRecno;

	if( townRecno )
	{
		nationRecno = town_array[townRecno]->nation_recno;
		town_array[townRecno]->nation_recno = 0;
	}

	if( firmRecno )
	{
		nationRecno = firm_array[firmRecno]->nation_recno;
		firm_array[firmRecno]->nation_recno = 0;
	}

	//------- reset power_nation_recno first ------//

	int   	 xLoc, yLoc, centerY, t;
	Location* locPtr = loc_matrix;

	xLoc1 = max( 0, xLoc1 - EFFECTIVE_POWER_DISTANCE+1);
	yLoc1 = max( 0, yLoc1 - EFFECTIVE_POWER_DISTANCE+1);
	xLoc2 = min( MAX_WORLD_X_LOC-1, xLoc2 + EFFECTIVE_POWER_DISTANCE-1);
	yLoc2 = min( MAX_WORLD_Y_LOC-1, yLoc2 + EFFECTIVE_POWER_DISTANCE-1);

	centerY = (yLoc1+yLoc2) / 2;

	for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ )
	{
		t=abs(yLoc-centerY)/2;

		for( xLoc=xLoc1+t ; xLoc<=xLoc2-t ; xLoc++, locPtr++ )
		{
			locPtr = get_loc(xLoc, yLoc);

			if( locPtr->power_nation_recno==nationRecno )
			{
				locPtr->power_nation_recno = 0;
				sys.map_need_redraw = 1;						// request redrawing the map next time
			}
		}
	}

	//--- if some power areas are freed up, see if neighbor towns/firms should take up these power areas ----//

	if( sys.map_need_redraw )	// when calls set_all_power(), the nation_recno of the calling firm must be reset
		set_all_power();

	//------- restore the nation recno of the calling town/firm -------//

	if( townRecno )
		town_array[townRecno]->nation_recno = nationRecno;

	if( firmRecno )
		firm_array[firmRecno]->nation_recno = nationRecno;
}
//--------- End of function World::restore_power ---------//


//-------- Begin of function World::detect_firm_town ---------//
//
int World::detect_firm_town()
{
	// ##### begin Gilbert 19/9 ########//
	// int rc = mouse.single_click(ZOOM_X1, ZOOM_Y1, ZOOM_X2, ZOOM_Y2, 2);
	if( !mouse.any_click(ZOOM_X1, ZOOM_Y1, ZOOM_X2, ZOOM_Y2, 2) )
		return 0;
	// ##### end Gilbert 19/9 ########//

	//------ detect pressing on link enable/disable sign -----//

	Firm* firmPtr;

	if( firm_array.selected_recno )
	{
		firmPtr = firm_array[firm_array.selected_recno];

		if( firmPtr->should_show_info() &&				// only if should_show_info() is 1, we can detect links from this firm (it is not limited to player firms, as firms with player's workers should be allowed for resigning the player worker from the firm
			 firmPtr->draw_detect_link_line(1) )		// 1-detect action
		{
			return 1;
		}
	}

	Town* townPtr;

	if( town_array.selected_recno )
	{
		townPtr = town_array[town_array.selected_recno];

		if( townPtr->nation_recno==nation_array.player_recno &&
			 townPtr->draw_detect_link_line(1) )		// 1-detect action
		{
			return 1;
		}
	}

	// ####### begin Gilbert 12/9 #########// see Power::detect_select
/*
	//--------------- detect firm ------------------//

	if( rc==1 )			// left click 
	{
		int mouseAbsX = mouse.cur_x - ZOOM_X1 + World::view_top_x;
		int mouseAbsY = mouse.cur_y - ZOOM_Y1 + World::view_top_y;

		int  i;

		for( i=firm_array.size() ; i>0 ; i-- )
		{
			if( firm_array.is_deleted(i) )
				continue;

			firmPtr = firm_array[i];

			if( m.is_touch( mouseAbsX, mouseAbsY, mouseAbsX, mouseAbsY,
				firmPtr->abs_x1, firmPtr->abs_y1, firmPtr->abs_x2, firmPtr->abs_y2 ) )
			{
				power.reset_selection();
				firm_array.selected_recno = i;
				info.disp();

				// -------- sound effect -----------//

				if( firmPtr->nation_recno == nation_array.player_recno && se_res.mark_select_object_time() )
				{
					se_res.sound(firmPtr->center_x, firmPtr->center_y, 1,
						'F', firmPtr->firm_id, firmPtr->under_construction ? "SELU" : "SEL" );
				}
				return 1;
			}
		}

		//----------- detect town section --------------//

		for( i=town_array.size() ; i>0 ; i-- )
		{
			if( town_array.is_deleted(i) )
				continue;

			townPtr = town_array[i];

			if( m.is_touch( mouseAbsX, mouseAbsY, mouseAbsX, mouseAbsY,
				 townPtr->abs_x1, townPtr->abs_y1, townPtr->abs_x2, townPtr->abs_y2 ) )
			{
				power.reset_selection();
				town_array.selected_recno = i;
				info.disp();

				// -------- sound effect -----------//

				if( townPtr->nation_recno == nation_array.player_recno 
					&& se_res.mark_select_object_time() )
				{
					se_res.sound(townPtr->center_x, townPtr->center_y, 1,
						'T', 0, "SEL" );
				}
				return 1;
			}
		}
	}
	*/

	return 0;
}
//-------- End of function World::detect_firm_town ---------//


//-------- Begin of function World::earth_equake ------//
void World::earth_quake()
{
	Location *locPtr;
	int x,y;
	for(y = 0; y < max_y_loc; ++y)
	{
		locPtr = get_loc(0,y);
		for( x = 0; x < max_x_loc; ++x, ++locPtr)
		{
			if(locPtr->is_wall() )
			{
				locPtr->attack_wall( weather.quake_rate(x,y) /2 );
			}
		}
	}

	int firmDamage = 0;
	int firmDie = 0;
	int i;
	for( i=firm_array.size() ; i>0 ; i-- )
	{
		if( firm_array.is_deleted(i) || !firm_res[firm_array[i]->firm_id]->buildable)
			continue;

		Firm *firmPtr = firm_array[i];
		x = firmPtr->center_x;
		y = firmPtr->center_y;
		firmPtr->hit_points -= weather.quake_rate(x,y);
		if( firmPtr->own_firm() )
			firmDamage++;
		if( firmPtr->hit_points <= 0)
		{
			firmPtr->hit_points = (float) 0;
			if( firmPtr->own_firm() )
				firmDie++;
			se_res.sound(firmPtr->center_x, firmPtr->center_y, 1,
				'F', firmPtr->firm_id, "DIE" );
			firm_array.del_firm(i);
		}
	}

	int townDamage = 0;
	for( i=town_array.size() ; i>0 ; i-- )
	{
		if( town_array.is_deleted(i) )
			continue;

		Town *townPtr = town_array[i];
		int ownTown = townPtr->nation_recno == nation_array.player_recno;
		short beforePopulation = townPtr->population;
		short causalty = weather.quake_rate(townPtr->center_x, townPtr->center_y) / 10;
		for( ; causalty > 0 && !town_array.is_deleted(i); --causalty )
		{
			townPtr->kill_town_people(0);
		}
		if( town_array.is_deleted(i) )
			causalty = beforePopulation;
		else
			causalty = beforePopulation - townPtr->population;

		if(ownTown)
			townDamage += causalty;
	}

	int unitDamage = 0;
	int unitDie = 0;
	for( i=unit_array.size(); i > 0; i-- )
	{
		if( unit_array.is_deleted(i))
			continue;

		Unit *unitPtr = unit_array[i];

		// ###### begin Gilbert 30/8 ######//
		// no damage to air unit , sea unit or overseer
		if( !unitPtr->is_visible() || unitPtr->mobile_type == UNIT_AIR
			|| unitPtr->mobile_type == UNIT_SEA)
			continue;
		// ###### end Gilbert 30/8 ######//

		float damage = (float) weather.quake_rate(unitPtr->cur_x_loc(),unitPtr->cur_y_loc() ) *
			unitPtr->max_hit_points / 200;
		if( damage >= unitPtr->hit_points)
			damage = unitPtr->hit_points -1;
		if( damage < (float) 5)
			damage = (float) 5;

		unitPtr->hit_points -= damage;
		if( unitPtr->is_own() )
			unitDamage++;

		if( unitPtr->hit_points <= 0)
		{
			unitPtr->hit_points = (float) 0;
			if( unitPtr->is_own() )
				unitDie++;
		}
		else
		{
			if( unit_res[unitPtr->unit_id]->solider_id &&
				weather.quake_rate(unitPtr->cur_x_loc(),unitPtr->cur_y_loc()) >= 60)
			{
				((UnitVehicle *)unitPtr)->dismount();
			}
		}
	}

	news_array.earthquake_damage(unitDamage-unitDie, unitDie, townDamage, firmDamage-firmDie, firmDie);
}
//-------- End of function World::earth_equake ------//


//-------- Begin of function World::lightning_strike ------//
void World::lightning_strike(short cx, short cy, short radius)
{
	short x, y;
	for( y = cy-radius; y <= cy+radius; ++y)
	{
		if( y < 0 || y >= max_y_loc)
			continue;

		for( x = cx-radius; x <= cx+radius; ++x)
		{
			if( x < 0 || x >= max_x_loc)
				continue;

			Location *locPtr = get_loc(x,y);
			if( locPtr->is_plant() )
			{
				// ---- add a fire on it ------//
				locPtr->set_fire_str(80);
				// ##### begin Gilbert 11/8 #####//
				if( locPtr->can_set_fire() && locPtr->fire_str() < 5 )
					locPtr->set_fire_str(5);
				// ##### end Gilbert 11/8 #####//
			}
		}
	}

	// ------ check hitting units -------//
	int i;
	for( i=unit_array.size(); i > 0; i-- )
	{
		if( unit_array.is_deleted(i))
			continue;

		Unit *unitPtr = unit_array[i];

		// no damage to overseer
		if( !unitPtr->is_visible())
			continue;

		if( unitPtr->cur_x_loc() <= cx+ radius &&
			unitPtr->cur_x_loc() + unitPtr->sprite_info->loc_width > cx-radius &&
			unitPtr->cur_y_loc() <= cy+radius &&
			unitPtr->cur_y_loc() + unitPtr->sprite_info->loc_height > cy-radius )
		{
			unitPtr->hit_points -= (float) unitPtr->sprite_info->lightning_damage / ATTACK_SLOW_DOWN;

			// ---- add news -------//
			if( unitPtr->is_own() )
				news_array.lightning_damage(unitPtr->cur_x_loc(), unitPtr->cur_y_loc(),
					NEWS_LOC_UNIT, i, unitPtr->hit_points <= (float) 0);

			if( unitPtr->hit_points <= 0)
				unitPtr->hit_points = (float) 0;
		}
	}

	for( i=firm_array.size() ; i>0 ; i-- )
	{
		if( firm_array.is_deleted(i) || !firm_res[firm_array[i]->firm_id]->buildable)
			continue;

		Firm *firmPtr = firm_array[i];
		if( firmPtr->loc_x1 <= cx+radius &&
			firmPtr->loc_x2 >= cx-radius &&
			firmPtr->loc_y1 <= cy+radius &&
			firmPtr->loc_y2 >= cy-radius)
		{
			firmPtr->hit_points -= (float) 50 / ATTACK_SLOW_DOWN;

			// ---- add news -------//
			if( firmPtr->own_firm() )
				news_array.lightning_damage(firmPtr->center_x, firmPtr->center_y,
					NEWS_LOC_FIRM, i, firmPtr->hit_points <= (float) 0);

			// ---- add a fire on it ------//
			Location *locPtr = get_loc(firmPtr->center_x, firmPtr->center_y);
			if( locPtr->can_set_fire() && locPtr->fire_str() < 5 )
				locPtr->set_fire_str(5);

			if( firmPtr->hit_points <= 0)
			{
				firmPtr->hit_points = (float) 0;
				se_res.sound(firmPtr->center_x, firmPtr->center_y, 1,
					'F', firmPtr->firm_id, "DIE" );
				firm_array.del_firm(i);
			}
		}
	}

	for( i=town_array.size() ; i>0 ; i-- )
	{
		if( town_array.is_deleted(i))
			continue;

		Town *townPtr = town_array[i];

		if( townPtr->loc_x1 <= cx+radius &&
			townPtr->loc_x2 >= cx-radius &&
			townPtr->loc_y1 <= cy+radius &&
			townPtr->loc_y2 >= cy-radius)
		{
			// ---- add news -------//
			if( townPtr->nation_recno == nation_array.player_recno )
				news_array.lightning_damage(townPtr->center_x, townPtr->center_y,
					NEWS_LOC_TOWN, i, 0);

			// ---- add a fire on it ------//
			// ####### begin Gilbert 11/8 #########//
			Location *locPtr = get_loc(townPtr->center_x, townPtr->center_y);
			if( locPtr->can_set_fire() && locPtr->fire_str() < 5)
				locPtr->set_fire_str(5);
			// ####### end Gilbert 11/8 #########//

			townPtr->kill_town_people(0);
		}
	}
}
//-------- End of function World::lightning_strike -------//


// ------- Begin of function World::visit -----------//
// set the visit_level surrounding unit, town and firm
void World::visit(int xLoc1, int yLoc1, int xLoc2, int yLoc2, int range, int extend)
{
	if(config.fog_of_war)
	{
		int left   = max( 0, xLoc1 - range);
		int top    = max( 0, yLoc1 - range);
		int right  = min( MAX_WORLD_X_LOC-1, xLoc2 + range);
		int bottom = min( MAX_WORLD_Y_LOC-1, yLoc2 + range);

		// ----- mark the visit_level of the square around the unit ------//
		for( int yLoc=top ; yLoc<=bottom ; yLoc++ )
		{
			Location *locPtr = get_loc(left, yLoc);
			for( int xLoc=left ; xLoc<=right ; xLoc++, locPtr++ )
			{
				locPtr->set_visited();
			}
		}

		// ----- visit_level decreasing outside the visible range ------//
		if( extend > 0)
		{
			int visitLevel = FULL_VISIBILITY;
			int levelDrop = (FULL_VISIBILITY - EXPLORED_VISIBILITY) / (extend+1);
			xLoc1 -= range;
			xLoc2 += range;
			yLoc1 -= range;
			yLoc2 += range;
			for( ++range; extend > 0; --extend, ++range)
			{
				xLoc1--;
				xLoc2++;
				yLoc1--;
				yLoc2++;
				visitLevel -= levelDrop;
				visit_shell(xLoc1, yLoc1, xLoc2, yLoc2, visitLevel);
			}
		}
	}
}
// ------- End of function World::visit -----------//


// ------- Begin of function World::visit_shell -----------//
// set specific visit_level on the surrounding unit, town and firm
void World::visit_shell(int xLoc1, int yLoc1, int xLoc2, int yLoc2, int visitLevel)
{
	int left   = max( 0, xLoc1 );
	int top    = max( 0, yLoc1 );
	int right  = min( MAX_WORLD_X_LOC-1, xLoc2);
	int bottom = min( MAX_WORLD_Y_LOC-1, yLoc2);

	// ------- top side ---------//
	if( yLoc1 >= 0)
	{
		Location *locPtr = get_loc( left, yLoc1);
		for( int x = left; x <= right; ++x, ++locPtr)
			locPtr->set_visited(visitLevel);
	}

	// ------- bottom side ---------//
	if( yLoc2 < max_y_loc)
	{
		Location *locPtr = get_loc( left, yLoc2);
		for( int x = left; x <= right; ++x, ++locPtr)
			locPtr->set_visited(visitLevel);
	}

	// ------- left side -----------//
	if( xLoc1 >= 0)
	{
		for( int y = top; y <= bottom; ++y)
		{
			get_loc(xLoc1,y)->set_visited(visitLevel);
		}
	}

	// ------- right side -----------//
	if( xLoc2 < max_x_loc)
	{
		for( int y = top; y <= bottom; ++y)
		{
			get_loc(xLoc2,y)->set_visited(visitLevel);
		}
	}

}
// ------- End of function World::visit_shell -----------//


//------- Begin of function World::process_visibility -----------//

void World::process_visibility()
{
	if( config.fog_of_war )
	{
		// ###### begin Gilbert 13/10 ########//
		//for( int y = 0; y < max_y_loc; ++y)
		//{
		//	Location *locPtr = get_loc(0,y);
		//	for( int x = 0; x < max_x_loc; ++x, ++locPtr)
		//	{
		//		locPtr->dec_visibility();
		//	}
		//}
		
		int count = max_x_loc * max_y_loc;
		const int sizeOfLoc = sizeof(Location);
		unsigned char *locVisitLevel = &get_loc(0,0)->visit_level;
		unsigned char decVisitLevel = EXPLORED_VISIBILITY*2+1;
		/* Original Visual C++ assembly code for reference
		_asm
		{
			mov	ecx, count
			mov	ebx, locVisitLevel
			mov	edx, sizeOfLoc
			mov	ah, decVisitLevel
process_visit_level_1:
			mov	al,[ebx]
			cmp	al,ah			// if(al > EXPLORED_VISIBILITY*2) al--;
			cmc
			sbb	al,0
			mov	[ebx],al
			add	ebx, edx
			loop	process_visit_level_1
		}
		*/

		__asm__ __volatile__ (
			"movb %0, %%ah\n"
		"process_visit_level_1:\n\t"
			"movb (%%ebx), %%al\n\t"
			"cmpb %%ah, %%al\n\t"
			"cmc\n\t"
			"sbbb $0, %%al\n\t"
			"movb %%al, (%%ebx)\n\t"
			"addl %%edx, %%ebx\n\t"
			"loop process_visit_level_1\n\t"
			: 
			: "m"(decVisitLevel), "b"(locVisitLevel), "c"(count), "d"(sizeOfLoc)
			: "%eax"
		);
		// ###### end Gilbert 13/10 ########//
	}
}
//------- End of function World::process_visibility -----------//


//--------- Begin of function World::disp_next --------//
//
// Display the next object of the same type.
//
// <int> seekDir : -1 - display the previous one in the list.
// 					  1 - display the next one in the list.
//
// <int> sameNation - whether display the next object of the same
//							 nation only or of any nation.
//
void World::disp_next(int seekDir, int sameNation)
{
	//--- if the selected one is a unit ----//

	if( unit_array.selected_recno )
	{
		int unitRecno = unit_array.selected_recno;
		Unit* unitPtr = unit_array[unit_array.selected_recno];
		int unitClass = unit_res[unitPtr->unit_id]->unit_class;
		int nationRecno = unitPtr->nation_recno;

		while(1)
		{
			if( seekDir < 0 )
			{
				unitRecno--;

				if( unitRecno < 1 )
					unitRecno = unit_array.size();
			}
			else
			{
				unitRecno++;

				if( unitRecno > unit_array.size() )
					unitRecno = 1;
			}

			if( unit_array.is_deleted(unitRecno) )
				continue;

			unitPtr = unit_array[unitRecno];

			if( !unitPtr->is_visible() )
				continue;

			//--- check if the location of the unit has been explored ---//

			if( !world.get_loc(unitPtr->next_x_loc(), unitPtr->next_y_loc())->explored() )
				continue;

         //-------- if are of the same nation --------//

			if( sameNation && unitPtr->nation_recno != nationRecno )
				continue;

			//---------------------------------//

			if( unit_res[unitPtr->unit_id]->unit_class == unitClass )
			{
				power.reset_selection();
				unitPtr->selected_flag = 1;
				unit_array.selected_recno = unitRecno;
				unit_array.selected_count++;

				world.go_loc( unitPtr->cur_x_loc(), unitPtr->cur_y_loc() );
				return;
			}

			//--- if the recno loops back to the starting one ---//

			if( unitRecno == unit_array.selected_recno )
				break;
		}
	}

	//--- if the selected one is a firm ----//

	if( firm_array.selected_recno )
	{
		int firmRecno = firm_array.selected_recno;
		Firm* firmPtr = firm_array[firm_array.selected_recno];
		int firmId = firmPtr->firm_id;
		int nationRecno = firmPtr->nation_recno;

		while(1)
		{
			if( seekDir < 0 )
			{
				firmRecno--;

				if( firmRecno < 1 )
					firmRecno = firm_array.size();
			}
			else
			{
				firmRecno++;

				if( firmRecno > firm_array.size() )
					firmRecno = 1;
			}

			if( firm_array.is_deleted(firmRecno) )
				continue;

			firmPtr = firm_array[firmRecno];

			//-------- if are of the same nation --------//

			if( sameNation && firmPtr->nation_recno != nationRecno )
				continue;

			//--- check if the location of this firm has been explored ---//

			if( !world.get_loc(firmPtr->center_x, firmPtr->center_y)->explored() )
				continue;

			//---------------------------------//

			if( firmPtr->firm_id == firmId )
			{
				power.reset_selection();
				firm_array.selected_recno = firmRecno;

				world.go_loc( firmPtr->center_x, firmPtr->center_y );
				return;
			}

			//--- if the recno loops back to the starting one ---//

			if( firmRecno == firm_array.selected_recno )
				break;
		}
	}

	//--- if the selected one is a town ----//

	if( town_array.selected_recno )
	{
		int 	townRecno = town_array.selected_recno;
		int   nationRecno = town_array[townRecno]->nation_recno;
		Town* townPtr;

		while(1)
		{
			if( seekDir < 0 )
			{
				townRecno--;

				if( townRecno < 1 )
					townRecno = town_array.size();
			}
			else
			{
				townRecno++;

				if( townRecno > town_array.size() )
					townRecno = 1;
			}

			if( town_array.is_deleted(townRecno) )
				continue;

			townPtr = town_array[townRecno];

			//-------- if are of the same nation --------//

			if( sameNation && townPtr->nation_recno != nationRecno )
				continue;

			//--- check if the location of this town has been explored ---//

			if( !world.get_loc(townPtr->center_x, townPtr->center_y)->explored() )
				continue;

			//---------------------------------//

			power.reset_selection();
			town_array.selected_recno = townRecno;

			world.go_loc( townPtr->center_x, townPtr->center_y );
			return;

			//--- if the recno loops back to the starting one ---//

			if( townRecno == town_array.selected_recno )
				break;
		}
	}

	//--- if the selected one is a natural resource site ----//

	if( site_array.selected_recno )
	{
		int   siteRecno = site_array.selected_recno;
		Site* sitePtr   = site_array[site_array.selected_recno];
		int   siteType  = sitePtr->site_type;

		while(1)
		{
			if( seekDir < 0 )
			{
				siteRecno--;

				if( siteRecno < 1 )
					siteRecno = site_array.size();
			}
			else
			{
				siteRecno++;

				if( siteRecno > site_array.size() )
					siteRecno = 1;
			}

			if( site_array.is_deleted(siteRecno) )
				continue;

			sitePtr = site_array[siteRecno];

			//--- check if the location of this site has been explored ---//

			if( !world.get_loc(sitePtr->map_x_loc, sitePtr->map_y_loc)->explored() )
				continue;

			//---------------------------------//

			if( sitePtr->site_type == siteType )
			{
				power.reset_selection();
				site_array.selected_recno = siteRecno;

				world.go_loc( sitePtr->map_x_loc, sitePtr->map_y_loc );
				return;
			}

			//--- if the recno loops back to the starting one ---//

			if( siteRecno == site_array.selected_recno )
				break;
		}
	}
}
//----------- End of function World::disp_next --------//

#ifdef DEBUG3

//--------- Begin of function World::get_loc --------//
//
Location* World::get_loc(int xLoc, int yLoc)
{
	err_when( xLoc<0 || xLoc>=MAX_WORLD_X_LOC );
	err_when( yLoc<0 || yLoc>=MAX_WORLD_Y_LOC );

	return loc_matrix + MAX_WORLD_X_LOC * yLoc + xLoc;
}
//----------- End of function World::get_loc --------//


//--------- Begin of function World::get_region_id --------//
//
BYTE World::get_region_id(int xLoc, int yLoc)
{
	err_when( xLoc<0 || xLoc>=MAX_WORLD_X_LOC );
	err_when( yLoc<0 || yLoc>=MAX_WORLD_Y_LOC );

	return loc_matrix[MAX_WORLD_X_LOC*yLoc+xLoc].region_id;
}
//----------- End of function World::get_region_id --------//

#endif

// ####### begin Gilbert 25/7 #########//
// return true if any location adjacent to (x,y) is on a particular region
int World::is_adjacent_region(int x, int y, int regionId)
{
	if( y > 0 )
	{
		if( x > 0 )
		{
			if( get_region_id(x-1,y-1) == regionId )
				return 1;
		}
		if( get_region_id(x,y-1) == regionId )
			return 1;
		if( x < max_x_loc-1 )
		{
			if( get_region_id(x+1,y-1) == regionId )
				return 1;
		}
	}
	if( x > 0 )
	{
		if( get_region_id(x-1,y) == regionId )
			return 1;
	}
	if( x < max_x_loc-1 )
	{
		if( get_region_id(x+1,y) == regionId )
			return 1;
	}
	if( y < max_y_loc-1)
	{
		if( x > 0 )
		{
			if( get_region_id(x-1,y+1) == regionId )
				return 1;
		}
		if( get_region_id(x,y+1) == regionId )
			return 1;
		if( x < max_x_loc-1 )
		{
			if( get_region_id(x+1,y+1) == regionId )
				return 1;
		}
	}
	return 0;
}
// ####### end Gilbert 25/7 #########//
