/*
 * 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    : OU_CARA.CPP
//Description : Unit Caravan

#include <OVGA.h>
#include <OSTR.h>
#include <OHELP.h>
#include <OFONT.h>
#include <OINFO.h>
#include <OCONFIG.h>
#include <OIMGRES.h>
#include <OMOUSE.h>
#include <OPOWER.h>
#include <OBUTTON.h>
#include <OBUTT3D.h>
#include <ONATION.h>
#include <OU_CARA.h>
#include <OREMOTE.h>
#include <OF_MINE.h>
#include <OF_FACT.h>
#include <OBUTTCUS.h>
#include <OSE.h>

//------------- Define static vars ------------//

static Button 			button_set_stop[MAX_STOP_FOR_CARAVAN];
static Button 			button_go_stop[MAX_STOP_FOR_CARAVAN];
static Button 			button_cancel_stop[MAX_STOP_FOR_CARAVAN];
static ButtonCustom	button_select_array[MAX_STOP_FOR_CARAVAN][MAX_GOODS_SELECT_BUTTON];
static char				button_select_enable_flag[MAX_STOP_FOR_CARAVAN][MAX_GOODS_SELECT_BUTTON];

static void				i_disp_caravan_select_button(ButtonCustom *button, int repaintBody);
static char				goods_num[MAX_STOP_FOR_CARAVAN];

static char				dummyCaravanEnableFlag[MAX_STOP_FOR_CARAVAN][MAX_GOODS_SELECT_BUTTON];
static char				dummyCaravanGoodsNum[MAX_STOP_FOR_CARAVAN];
//------------- define static function ------------//
static void update_caravan_stop_and_goods_to_dummy(UnitCaravan *caravanPtr)
{
	CaravanStop *stopPtr = caravanPtr->stop_array;
	for(int i=0; i<MAX_STOP_FOR_CARAVAN; i++, stopPtr++)
	{
		if(!stopPtr->firm_recno)
			continue;

		err_when(firm_array.is_deleted(stopPtr->firm_recno));
		dummyCaravanGoodsNum[i] = stopPtr->update_pick_up(dummyCaravanEnableFlag[i]);
	}
}

//--------- Begin of function UnitCaravan::UnitCaravan ---------//
//
UnitCaravan::UnitCaravan()
{
	memset( stop_array, 0, MAX_STOP_FOR_CARAVAN * sizeof(CaravanStop) );

	journey_status		= ON_WAY_TO_FIRM;
	dest_stop_id		= 1;
	stop_defined_num	= 0;
	wait_count			= 0;
	stop_x_loc			= 0;
	stop_y_loc			= 0;

	memset(raw_qty_array, 0, sizeof(short)*MAX_RAW);
	memset(product_raw_qty_array, 0, sizeof(short)*MAX_PRODUCT);
}
//---------- End of function UnitCaravan::UnitCaravan ----------//


//--------- Begin of function UnitCaravan::disp_info ---------//
//
void UnitCaravan::disp_info(int refreshFlag)
{
	//----- for multiplayer game, skip displaying information for the first frame --------//
	if(remote.is_enable())
	{
		if(unit_array.mp_first_frame_to_select_caravan && // first frame
			unit_array.mp_pre_selected_caravan_recno==sprite_recno) // is selected
			return;
	}

	disp_basic_info(INFO_Y1, refreshFlag);

	if( !config.show_ai_info && !is_own() )
		return;

	disp_stop(INFO_Y1+54, refreshFlag);

	disp_goods(INFO_Y1+235, refreshFlag);
}
//---------- End of function UnitCaravan::disp_info ----------//


//--------- Begin of function UnitCaravan::detect_info ---------//
//
void UnitCaravan::detect_info()
{
	if(!is_visible())
		return;

	if( detect_basic_info() )
		return;

	if( !is_own() && !config.show_ai_info )
		return;

	detect_stop();
}
//---------- End of function UnitCaravan::detect_info ----------//


//--------- Begin of function UnitCaravan::disp_stop ---------//
//
void UnitCaravan::disp_stop(int dispY1, int refreshFlag)
{
//###### begin trevor 15/9 #######//

	if(refreshFlag!=INFO_REPAINT && refreshFlag!=INFO_UPDATE)
		return;

	int 	i, x=INFO_X1, y=dispY1, needRefresh;
	Firm	*firmPtr;
	static short last_firm_recno_array[MAX_STOP_FOR_CARAVAN];

	for(i=0 ; i<MAX_STOP_FOR_CARAVAN ; i++, y+=60)
	{
		//---- compare with the previous display and see if an update is needed ----//

		if( refreshFlag==INFO_REPAINT )
		{
			needRefresh = 1;
		}
		else if( last_firm_recno_array[i] != stop_array[i].firm_recno )
		{
			needRefresh = 1;
		}

		last_firm_recno_array[i] = stop_array[i].firm_recno;

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

		if( !stop_array[i].firm_recno ||
			 firm_array.is_deleted(stop_array[i].firm_recno) )
		{
			if( refreshFlag == INFO_REPAINT )
			{
				vga.d3_panel_up(x, y, INFO_X2, y+58);
#if(defined(FRENCH))
				button_set_stop[i].paint_text( x+4, y+37, x+86, y+56, "Set Stop" );
#else
				button_set_stop[i].paint_text( x+4, y+37, x+80, y+56, "Set Stop" );
#endif
				button_set_stop[i].set_help_code( "CSETSTOP" );
			}
		}
		else
		{
			if( refreshFlag == INFO_REPAINT )
			{
				vga.d3_panel_up(x, y, INFO_X2, y+58);

				//-------- display name of the stop --------//

				firmPtr = firm_array[ stop_array[i].firm_recno ];
				nation_array[firmPtr->nation_recno]->disp_nation_color(x+4, y+4);
				font_san.put(x+20, y+4, firmPtr->firm_name());
				font_san.put(x+4, y+19, "Pick up: ");

#if(defined(FRENCH))
				button_set_stop[i].paint_text( x+4, y+37, x+86, y+56, "Set Stop" );
#else
				button_set_stop[i].paint_text( x+4, y+37, x+80, y+56, "Set Stop" );
#endif

				button_set_stop[i].set_help_code( "CSETSTOP" );

#if(defined(FRENCH))
				button_go_stop[i].paint_text( x+90, y+37, x+180, y+56, "View Stop" );
#else
				button_go_stop[i].paint_text( x+84, y+37, x+180, y+56, "View Stop" );
#endif
				button_go_stop[i].set_help_code( "CGOSTOP" );

				button_cancel_stop[i].paint_text( x+184, y+37, INFO_X2-4, y+56, "X" );
				button_cancel_stop[i].set_help_code( "CDELSTOP" );
			}

			disp_goods_select_button(i, y+1, refreshFlag);
		}
	}
//###### end trevor 15/9 #######//

}
//---------- End of function UnitCaravan::disp_stop ----------//


//--------- Begin of function UnitCaravan::detect_stop ---------//
//
void UnitCaravan::detect_stop()
{
	int i, x=INFO_X1;

	for( i=0 ; i<MAX_STOP_FOR_CARAVAN ; i++ )
	{
		// ###### begin Gilbert 14/8 #########//
		if( button_set_stop[i].detect() && is_own() )
			power.issue_command( COMMAND_SET_CARAVAN_STOP, sprite_recno, i+1 );		// i+1 - stop id., passed as a parameter of the command
		// ###### end Gilbert 14/8 #########//

		if(i>=stop_defined_num)
			continue;

		if(button_cancel_stop[i].detect())
		{
			if(is_visible())
			{
				del_stop(i+1, COMMAND_PLAYER);
				// ###### begin Gilbert 26/9 ######//
				se_ctrl.immediate_sound("TURN_OFF");
				// ###### end Gilbert 26/9 ######//
			}
		}

		for(int b=0; b<MAX_GOODS_SELECT_BUTTON; ++b)
		{
			if(!button_select_enable_flag[i][b])
				continue;

			if(button_select_array[i][b].detect())
			{
				// ###### begin Gilbert 26/9 ######//
				se_ctrl.immediate_sound(
					button_select_array[i][b].elastic_flag || button_select_array[i][b].pushed_flag ? 
					(char*)"TURN_ON" : (char*)"TURN_OFF");
				// ###### end Gilbert 26/9 ######//

				set_stop_pick_up(i+1, b, COMMAND_PLAYER); // b = 1 - MAX_PICK_UP_GOODS
			}
		}

		if( button_go_stop[i].detect() )
		{
			Firm* firmPtr = firm_array[ stop_array[i].firm_recno ];
			world.go_loc( firmPtr->center_x, firmPtr->center_y );
		}
	}
}
//---------- End of function UnitCaravan::detect_stop ----------//


//--------- Begin of function UnitCaravan::disp_goods_select_button ---------//

void UnitCaravan::disp_goods_select_button(int stopNum, int dispY1, int refreshFlag)
{
	if(refreshFlag!=INFO_REPAINT && refreshFlag!=INFO_UPDATE)
		return;

	#define SHIFT_X_OFFSET	60
	#define SELECT_BUTTON_WIDTH	16
	#define SELECT_BUTTON_HEIGHT	16

	CaravanStop	*stopPtr = &stop_array[stopNum];

	if( !goods_num[stopNum] )
		return;

	int  x=INFO_X1+SHIFT_X_OFFSET, y=dispY1+17, x1;
	char *pickUpArray = stopPtr->pick_up_array;
	char isPush;

	//-------------- draw the buttons for the cargo -------------//

//###### begin trevor 13/9 #######//

	for(int i=1 ;i<=MAX_PICK_UP_GOODS; ++i, pickUpArray++)
	{
		x1 = x + i*SELECT_BUTTON_WIDTH;

		if(button_select_enable_flag[stopNum][i])
		{
			isPush = stopPtr->pick_up_array[i-1];
			err_when(isPush && (stopPtr->pick_up_type==AUTO_PICK_UP || stopPtr->pick_up_type==NO_PICK_UP));

			button_select_array[stopNum][i].paint(x1, y, x1+SELECT_BUTTON_WIDTH,
				y+SELECT_BUTTON_HEIGHT, i_disp_caravan_select_button, ButtonCustomPara(this, i),
				0, isPush); // 0 for inelastic
		}
		else
		{
			vga.blt_buf( x1, y, x1+SELECT_BUTTON_WIDTH, y+SELECT_BUTTON_HEIGHT, 0 );
		}
	}

	//---------------- draw the buttons for auto_pick_up and no_pick_up -------------//

	if(goods_num[stopNum]>1)
	{
		x1 = x;
		isPush = (stopPtr->pick_up_type==AUTO_PICK_UP);
		button_select_enable_flag[stopNum][AUTO_PICK_UP] = 1;
		button_select_array[stopNum][AUTO_PICK_UP].paint(x1, y, x1+SELECT_BUTTON_WIDTH,
			y+SELECT_BUTTON_HEIGHT, i_disp_caravan_select_button, ButtonCustomPara(this, AUTO_PICK_UP),
			0, isPush); // 0 for inelastic

		x1 = x+SELECT_BUTTON_WIDTH*NO_PICK_UP;
		button_select_enable_flag[stopNum][NO_PICK_UP] = 1;
		button_select_array[stopNum][NO_PICK_UP].paint(x1, y, x1+SELECT_BUTTON_WIDTH,
			y+SELECT_BUTTON_HEIGHT, i_disp_caravan_select_button, ButtonCustomPara(this, NO_PICK_UP));
	}
	else
	{
		x1 = x;
		vga.blt_buf( x1, y, x1+SELECT_BUTTON_WIDTH, y+SELECT_BUTTON_HEIGHT, 0 );

		x1 = x+SELECT_BUTTON_WIDTH*NO_PICK_UP;
		vga.blt_buf( x1, y, x1+SELECT_BUTTON_WIDTH, y+SELECT_BUTTON_HEIGHT, 0 );
	}

	//###### end trevor 13/9 #######//
}
//---------- End of function UnitCaravan::disp_goods_select_button ----------//


//--------- Begin of function UnitCaravan::set_stop_pick_up ---------//
//
// Set the pickup type of a specific stop of this caravan.
//
// <int> stopId		  - id. of the stop. (1 - MAX_STOP_FOR_CARAVAN)
// <int> newPickUpType - set the pickup type of the specific stop. (0 - MAX_GOODS_SELECT_BUTTON-1)
// <int> remoteActoin  - remote action type
//
void UnitCaravan::set_stop_pick_up(int stopId, int newPickUpType, int remoteAction)
{
	int remoteEnable = 0;
	if(remote.is_enable())
	{
		if(!remoteAction)
		{
			// packet structure : <unit recno> <stop id> <new pick_up_type>
			short *shortPtr = (short *)remote.new_send_queue_msg(MSG_U_CARA_CHANGE_GOODS, 3*sizeof(short));
			*shortPtr = sprite_recno;

			shortPtr[1] = stopId;
			shortPtr[2] = newPickUpType;
			return;
		}
		else //-------- validate remote message ----------//
		{
			//-*******************************************************-//
			/*char mess[255];
			sprintf(mess, "Change Seed !!!! \r\n");
			OutputDebugString(mess);*/
			
			/*Firm *firmPtr = firm_array[stop_array[stopId-1].firm_recno];
			
			switch(firmPtr->firm_id)
			{
				case FIRM_MINE:
						//firmPtr->sell_firm(COMMAND_AUTO);
						//firm_array[stop_array[0].firm_recno]->sell_firm(COMMAND_AUTO);
						break;
				case FIRM_FACTORY:
						break;
				case FIRM_MARKET:
						break;
			}

			update_stop_list();
			if(unit_array.selected_recno == sprite_recno)
			{
				if(!remote.is_enable() || nation_renco==nation_array.player_recno || config.show_ai_info)
					disp_stop(INFO_Y1+54, INFO_UPDATE);
			}*/
			//-*******************************************************-//

			err_when(!is_visible()); // no action if the unit is invisible
			if(firm_array.is_deleted(stop_array[stopId-1].firm_recno))
				return; // firm is deleted

			if(stop_defined_num<stopId)
				return; // stop_list is updated, stop exists no more

			#ifdef DEBUG
			//-*******************************************************-//
			/*//char mess[255];
			sprintf(mess, "Change Seed : %d %d %d\r\n", stopId, newPickUpType, sprite_recno);
			OutputDebugString(mess);*/
			//-*******************************************************-//

			m.set_random_seed(stopId + newPickUpType*(m.random(4)+1)*10 + sprite_recno*100*m.random(100) +
									m.get_random_seed());

			//-*******************************************************-//
			/*//char mess[255];
			sprintf(mess, "Change Seed : %d\r\n", m.random_seed);
			OutputDebugString(mess);*/
			//-*******************************************************-//
			#endif
		}

		remoteEnable = 1;
	}

	switch(newPickUpType)
	{
		case AUTO_PICK_UP:
				if(remoteEnable)
				{
					update_caravan_stop_and_goods_to_dummy(this);
					stop_array[stopId-1].mp_pick_up_set_auto(dummyCaravanEnableFlag[stopId-1]);
				}
				else
					stop_array[stopId-1].pick_up_set_auto();
				break;

		case NO_PICK_UP:
				// if(remoteEnable)
				if(remoteEnable)
				{
					update_caravan_stop_and_goods_to_dummy(this);
					stop_array[stopId-1].mp_pick_up_set_none(dummyCaravanEnableFlag[stopId-1]);
				}
				else
					stop_array[stopId-1].pick_up_set_none();
				break;

		default:
				err_when(newPickUpType<PICK_UP_RAW_FIRST || newPickUpType>PICK_UP_PRODUCT_LAST);
				if(remoteEnable)
					stop_array[stopId-1].mp_pick_up_toggle(newPickUpType);
				else
					stop_array[stopId-1].pick_up_toggle(newPickUpType);
				break;
	}

	if(unit_array.selected_recno==sprite_recno)
	{
		if(!remote.is_enable() || nation_recno==nation_array.player_recno || config.show_ai_info)
			disp_stop(INFO_Y1+54, INFO_UPDATE);
	}
}
//---------- End of function UnitCaravan::set_stop_pick_up ----------//


//--------- Begin of function UnitCaravan::disp_goods ---------//
//
void UnitCaravan::disp_goods(int dispY1, int refreshFlag)
{
	if( refreshFlag == INFO_REPAINT )
		vga.d3_panel_up( INFO_X1, dispY1, INFO_X2, dispY1+42 );

	int	x=INFO_X1+20, y=dispY1+5;
	String str;

	int i;
	for(i=0; i<MAX_RAW; i++, x+=60)
	{
		vga_front.d3_panel_up( x, y, x+RAW_SMALL_ICON_WIDTH+5, y+RAW_SMALL_ICON_HEIGHT+5 );

		raw_res.put_small_raw_icon( x+3, y+3, i+1 );

		font_san.disp( x+25, y+2, raw_qty_array[i], 1, x+59 );
	}

	x =INFO_X1+20;
	y+=19;

	for( i=0; i<MAX_PRODUCT; i++, x+=60)
	{
		vga_front.d3_panel_up( x, y, x+RAW_SMALL_ICON_WIDTH+5, y+RAW_SMALL_ICON_HEIGHT+5 );

		raw_res.put_small_product_icon( x+3, y+3, i+1 );

		font_san.disp( x+25, y+2, product_raw_qty_array[i], 1, x+59 );
	}
}
//---------- End of function UnitCaravan::disp_goods ----------//


//--------- Begin of function UnitCaravan::set_stop ---------//
//
// <int> stopId 				 - the id. of the stop
// <int> stopXLoc, stopYLoc - the location of the stop
//
void UnitCaravan::set_stop(int stopId, int stopXLoc, int stopYLoc, char remoteAction)
{
	//-------------------------------------------------------//
	// check if there is a station in the given location
	//-------------------------------------------------------//
	Location *locPtr = world.get_loc(stopXLoc, stopYLoc);
	if(!locPtr->is_firm())
		return;

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

	if( !can_set_stop(firmPtr->firm_recno) )
		return;

	//-------------------------------------------------------//
	// return if the market stop is in another territory
	//-------------------------------------------------------//
	if(world.get_loc(next_x_loc(), next_y_loc())->region_id!=locPtr->region_id)
		return;

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

	if(!remoteAction && remote.is_enable())
	{
		// packet structure : <unit recno> <stop id> <stop x> <stop y>
		short *shortPtr = (short *) remote.new_send_queue_msg(MSG_U_CARA_SET_STOP, 4*sizeof(short));
		*shortPtr = sprite_recno;
		shortPtr[1] = stopId;
		shortPtr[2] = stopXLoc;
		shortPtr[3] = stopYLoc;
		return;
	}

	if(!stop_array[stopId-1].firm_recno)
	{
		stop_defined_num++;	// no plus one if the recno is defined originally

		err_when( stop_defined_num > MAX_STOP_FOR_CARAVAN );
	}

	//-------------------------------------------------------//
	// set the station recno of the stop
	//-------------------------------------------------------//
	CaravanStop *stopPtr = stop_array+stopId-1;
	if(stopPtr->firm_recno == firmPtr->firm_recno)
	{
		err_when(stopPtr->firm_loc_x1!=firmPtr->loc_x1 || stopPtr->firm_loc_y1!=firmPtr->loc_y1 ||
					stopPtr->firm_id!=firmPtr->firm_id);
		return; // same stop as before
	}

	//-------------- reset ignore_power_nation -------------//
	ignore_power_nation = 0;

	memset(button_select_enable_flag[stopId-1], 0, sizeof(char)*MAX_GOODS_SELECT_BUTTON);
	goods_num[stopId-1] = 0;

	short oldStopFirmRecno = dest_stop_id ? stop_array[dest_stop_id-1].firm_recno : 0;
	short newStopFirmRecno;
	memset(stopPtr->pick_up_array, 0, sizeof(char)*MAX_PICK_UP_GOODS);
	stopPtr->firm_recno		= firmPtr->firm_recno;
	stopPtr->firm_id			= firmPtr->firm_id;
	stopPtr->firm_loc_x1		= firmPtr->loc_x1;
	stopPtr->firm_loc_y1		= firmPtr->loc_y1;

	//------------------------------------------------------------------------------------//
	// codes for setting pick_up_type
	//------------------------------------------------------------------------------------//
	MarketGoods *goodsPtr;
	int i, goodsId, goodsNum;
	switch(firmPtr->firm_id)
	{
		case FIRM_MINE:
				goodsId = ((FirmMine*)firmPtr)->raw_id;
				if(goodsId)
					stopPtr->pick_up_toggle(goodsId); // enable
				else
					stopPtr->pick_up_set_none();
				break;

		case FIRM_FACTORY:
				goodsId = ((FirmFactory*)firmPtr)->product_raw_id+MAX_RAW;
				if(goodsId)
					stopPtr->pick_up_toggle(goodsId); // enable
				else
					stopPtr->pick_up_set_none();
				break;

		case FIRM_MARKET:
				goodsPtr = ((FirmMarket*) firmPtr)->market_goods_array;
				goodsNum = 0;
				for(i=0; i<MAX_MARKET_GOODS; ++i, goodsPtr++)
				{
					if(goodsPtr->raw_id)
					{
						if(goodsNum==0)
							goodsId = goodsPtr->raw_id;

						goodsNum++;
					}
					else if(goodsPtr->product_raw_id)
					{
						if(goodsNum==0)
							goodsId = goodsPtr->product_raw_id+MAX_RAW;

						goodsNum++;
					}
				}

				if(goodsNum==1)
					stopPtr->pick_up_toggle(goodsId); // cancel auto_pick_up
				else if(goodsNum==0)
					stopPtr->pick_up_set_none();
				else
					stopPtr->pick_up_set_auto();
				break;

		default: err_here();
					break;
	}

	last_set_stop_date = info.game_date;

	//-------------------------------------------------------//
	// remove duplicate stop or stop change nation
	//-------------------------------------------------------//
	update_stop_list();

	if(dest_stop_id)
	{
		if((newStopFirmRecno=stop_array[dest_stop_id-1].firm_recno) != oldStopFirmRecno)
		{
			firmPtr = firm_array[newStopFirmRecno];
			err_when(firmPtr->firm_id!=FIRM_MARKET && firmPtr->firm_id!=FIRM_MINE && firmPtr->firm_id!=FIRM_FACTORY);
			move_to_firm_surround(firmPtr->loc_x1, firmPtr->loc_y1, sprite_info->loc_width, sprite_info->loc_height, stop_array[dest_stop_id-1].firm_id);
			journey_status = ON_WAY_TO_FIRM;
		}
	}
	else
		stop2();

	if( unit_array.selected_recno == sprite_recno )
	{
		if(!remote.is_enable() || nation_recno==nation_array.player_recno || config.show_ai_info)
			info.disp();
	}
}
//---------- End of function UnitCaravan::set_stop ----------//


//--------- Begin of function UnitCaravan::del_stop ---------//
void UnitCaravan::del_stop(int stopId, char remoteAction)
{
	err_when(action_para || action_para2);

	if(!remoteAction && remote.is_enable())
	{
		// packet structure : <unit recno> <stop id>
		short *shortPtr = (short *) remote.new_send_queue_msg(MSG_U_CARA_DEL_STOP, 2*sizeof(short));
		*shortPtr = sprite_recno;
		shortPtr[1] = stopId;
		return;
	}

	//------ stop is deleted before receiving this message, thus, ignore invalid message -----//
	if(remote.is_enable() && stop_array[stopId-1].firm_recno==0)
		return;

	stop_array[stopId-1].firm_recno = 0;
	stop_defined_num--;
	err_when( stop_defined_num < 0 );

	update_stop_list();

	if( unit_array.selected_recno == sprite_recno )
	{
		if(!remote.is_enable() || nation_recno==nation_array.player_recno || config.show_ai_info)
			info.disp();
	}
}
//---------- End of function UnitCaravan::del_stop ----------//


//--------- Begin of function UnitCaravan::update_stop_list ---------//
void UnitCaravan::update_stop_list()
{
	err_when(stop_defined_num<0 || stop_defined_num>MAX_STOP_FOR_CARAVAN);

	//------------- used to debug for multiplayer game ------------------//
	#ifdef DEBUG
		m.random(100);
	#endif

	//-------------------------------------------------------//
	// backup original destination stop firm recno
	//-------------------------------------------------------//
	short nextStopRecno = stop_array[dest_stop_id-1].firm_recno;

	//----------------------------------------------------------------------//
	// check stop existence and the relationship between firm's nation
	//----------------------------------------------------------------------//
	CaravanStop *nodePtr = stop_array;
	Firm			*firmPtr;
	int i;
	for(i=0; i<MAX_STOP_FOR_CARAVAN; i++, nodePtr++)
	{
		if(!nodePtr->firm_recno)
			continue;

		if(firm_array.is_deleted(nodePtr->firm_recno))
		{
			nodePtr->firm_recno = 0;	// clear the recno
			stop_defined_num--;
			err_when( stop_defined_num < 0 );

			continue;
		}

		firmPtr = firm_array[nodePtr->firm_recno];

		if( !can_set_stop(nodePtr->firm_recno) ||
			 firmPtr->loc_x1!=nodePtr->firm_loc_x1 ||
			 firmPtr->loc_y1!=nodePtr->firm_loc_y1 )
		{
			nodePtr->firm_recno = 0;
			stop_defined_num--;
			err_when( stop_defined_num < 0 );

			continue;
		}
	}

	//-------------------------------------------------------//
	// remove duplicate node
	//-------------------------------------------------------//
	CaravanStop *insertNodePtr = stop_array;

	if(stop_defined_num<1)
	{
		memset(stop_array, 0, sizeof(CaravanStop)*MAX_STOP_FOR_CARAVAN);
		dest_stop_id = 0;
		return;	// no stop
	}

	//-------------------------------------------------------//
	// pack the firm_recno to the beginning part of the array
	//-------------------------------------------------------//
	short compareRecno;
	for(i=0, nodePtr=stop_array; i<MAX_STOP_FOR_CARAVAN; i++, nodePtr++)
	{
		if(nodePtr->firm_recno)
		{
			compareRecno = nodePtr->firm_recno;
			break;
		}
	}

	if(i++) // else, the first record is already in the beginning of the array
		memcpy(insertNodePtr, nodePtr, sizeof(CaravanStop));

	if(stop_defined_num==1)
	{
		memset(insertNodePtr+1, 0, sizeof(CaravanStop)*(MAX_STOP_FOR_CARAVAN-1));
		dest_stop_id = 1;
		return;
	}

	short unprocessed = stop_defined_num-1;
	err_when(i==MAX_STOP_FOR_CARAVAN); // error if only one record
	err_when(!unprocessed);
	insertNodePtr++;
	nodePtr++;

	for(; i<MAX_STOP_FOR_CARAVAN && unprocessed; i++, nodePtr++)
	{
		if(!nodePtr->firm_recno)
			continue; // empty

		err_when(!nodePtr->firm_recno);
		if(nodePtr->firm_recno==compareRecno)
		{
			nodePtr->firm_recno = 0;
			stop_defined_num--;
			err_when( stop_defined_num < 0 );
		}
		else
		{
			compareRecno = nodePtr->firm_recno;

			if(insertNodePtr!=nodePtr)
				memcpy(insertNodePtr++, nodePtr, sizeof(CaravanStop));
			else
				insertNodePtr++;
		}
		unprocessed--;
	}

	if(stop_defined_num>2)
	{
		//-------- compare the first and the end record -------//
		nodePtr = stop_array + stop_defined_num - 1; // point to the end
		if(nodePtr->firm_recno == stop_array[0].firm_recno)
		{
			nodePtr->firm_recno = 0;	// remove the end record
			stop_defined_num--;
			err_when( stop_defined_num < 0 );
		}
	}

	if(stop_defined_num<MAX_STOP_FOR_CARAVAN)
		memset(stop_array+stop_defined_num, 0, sizeof(CaravanStop)*(MAX_STOP_FOR_CARAVAN-stop_defined_num));

	#ifdef DEBUG
		int debugCount;
		for(debugCount=0; debugCount<stop_defined_num; debugCount++)
			err_when(!stop_array[debugCount].firm_recno);

		for(; debugCount<MAX_STOP_FOR_CARAVAN; debugCount++)
			err_when(stop_array[debugCount].firm_recno);

		for(debugCount=0; debugCount<stop_defined_num; debugCount++)
			err_when(stop_array[debugCount].firm_recno &&
						stop_array[debugCount].firm_recno==stop_array[(debugCount+1)%MAX_STOP_FOR_CARAVAN].firm_recno);
	#endif

	//-----------------------------------------------------------------------------------------//
	// There should be at least one stop in the list.  Otherwise, clear all the stops
	//-----------------------------------------------------------------------------------------//
	int ourFirmExist = 0;
	for(i=0, nodePtr=stop_array; i<stop_defined_num; i++, nodePtr++)
	{
		err_when(firm_array.is_deleted(nodePtr->firm_recno));
		firmPtr = firm_array[nodePtr->firm_recno];
		if(firmPtr->nation_recno==nation_recno)
		{
			ourFirmExist++;
			break;
		}
	}

	if(!ourFirmExist) // none of the markets belong to our nation
	{
		memset(stop_array, 0, MAX_STOP_FOR_CARAVAN * sizeof(CaravanStop));
		journey_status		= ON_WAY_TO_FIRM;
		dest_stop_id		= 0;
		stop_defined_num	= 0;
		return;
	}

	//-----------------------------------------------------------------------------------------//
	// reset dest_stop_id since the order of the stop may be changed
	//-----------------------------------------------------------------------------------------//
	int xLoc = next_x_loc();
	int yLoc = next_y_loc();
	int dist, minDist=0x7FFF;

	for(i=0, dest_stop_id=0, nodePtr=stop_array; i<stop_defined_num; i++, nodePtr++)
	{
		if(nodePtr->firm_recno==nextStopRecno)
		{
			dest_stop_id = i+1;
			break;
		}
		else
		{
			firmPtr = firm_array[nodePtr->firm_recno];
			dist = m.points_distance(xLoc, yLoc, firmPtr->center_x, firmPtr->center_y);

			if(dist<minDist)
			{
				dist = minDist;
				dest_stop_id = i+1;
			}
		}
	}

	err_when(dest_stop_id<0 || dest_stop_id>MAX_STOP_FOR_CARAVAN);
}
//----------- End of function UnitCaravan::update_stop_list -----------//


//--------- Begin of function UnitCaravan::update_stop_and_goods_info ---------//
void UnitCaravan::update_stop_and_goods_info()
{
	update_stop_list();
	update_caravan_stop_and_goods_to_dummy(this);

	if(sprite_recno==unit_array.selected_recno && ((nation_array.player_recno && nation_recno==nation_array.player_recno) ||
		config.show_ai_info))
	{
		memcpy(goods_num, dummyCaravanGoodsNum, sizeof(char)*MAX_STOP_FOR_CARAVAN);
		memcpy(button_select_enable_flag, dummyCaravanEnableFlag, sizeof(char)*MAX_STOP_FOR_CARAVAN*MAX_GOODS_SELECT_BUTTON);
	}
}
//----------- End of function UnitCaravan::update_stop_and_goods_info -----------//


//--------- Begin of function UnitCaravan::can_set_stop ---------//
//
// Whether can set a caravan's stop on the given firm.
//
int UnitCaravan::can_set_stop(int firmRecno)
{
	Firm* firmPtr = firm_array[firmRecno];

	if( firmPtr->under_construction )
		return 0;

	switch(firmPtr->firm_id)
	{
	case FIRM_MARKET:
		return nation_array[nation_recno]->get_relation(firmPtr->nation_recno)->trade_treaty;

	case FIRM_MINE:
	case FIRM_FACTORY:
		return nation_recno == firmPtr->nation_recno;

	default:
		return 0;
	}
}
//----------- End of function UnitCaravan::can_set_stop -----------//


//--------- Begin of function UnitCaravan::get_next_stop_id ---------//
//
// Get the id. of the next defined stop.
//
// [int] curStopId - the id. of the current stop.
//							if it is MAX_STOP_FOR_CARAVAN, this function will return
//							the id. of the first valid stop.
//
//      					(default: MAX_STOP_FOR_CARAVAN)
// return :	0 ~ MAX_STOP_FOR_CARAVAN, where 0 for no valid stop
//
int UnitCaravan::get_next_stop_id(int curStopId)
{
	int nextStopId = (curStopId>=stop_defined_num) ? 1 : curStopId+1;

	CaravanStop *stopPtr = stop_array+nextStopId-1;

	int needUpdate = 0;

	if(firm_array.is_deleted(stopPtr->firm_recno))
	{
		needUpdate++;
	}
	else
	{
		Firm *firmPtr = firm_array[stopPtr->firm_recno];

		if( !can_set_stop( stopPtr->firm_recno ) ||
			 firmPtr->loc_x1 != stopPtr->firm_loc_x1 ||
			 firmPtr->loc_y1 != stopPtr->firm_loc_y1 )
		{
			needUpdate++;
		}
	}

	//### begin alex 24/10 ###//
	if(needUpdate)
	{
		short preStopRecno = stop_array[curStopId-1].firm_recno;

		update_stop_list();

		if(!stop_defined_num)
			return 0;	// no stop is valid

		int i;
		for(i=1, stopPtr=stop_array; i<=stop_defined_num; i++, stopPtr++)
		{
			if(stopPtr->firm_recno==preStopRecno)
				return (i>=stop_defined_num) ? 1 : i+1;
		}

		return 1;
	}
	else
		return nextStopId;
	//#### end alex 24/10 ####//
}
//----------- End of function UnitCaravan::get_next_stop_id -----------//


//--------- Begin of function UnitCaravan::pre_process ---------//
//
void UnitCaravan::pre_process()
{
	Unit::pre_process();

	if(cur_x == -1) // can't use !is_visible(), keep process if cur_x < -1
		return;

	#define SURROUND_FIRM_WAIT_FACTOR	10

	//-----------------------------------------------------------------------------//
	// if all the hit points are lost, die now
	//-----------------------------------------------------------------------------//
	if(hit_points <= 0)
	{
		if(action_mode != ACTION_DIE)
			set_die();

		return;
	}

	err_when(action_mode==ACTION_DIE || cur_action==SPRITE_DIE || hit_points<=0);

	//-----------------------------------------------------------------------------//
	// stop action if no stop is defined
	//-----------------------------------------------------------------------------//
	if(!stop_defined_num)
	{
		err_when(dest_stop_id!=0);
		if(journey_status!=NO_STOP_DEFINED)
			stop();	// stop if no valid stop is defined

		journey_status = NO_STOP_DEFINED;
		return;
	}

	//-----------------------------------------------------------------------------//
	// wait in the surrounding of the stop if stop_defined_num==1 (only one stop)
	//-----------------------------------------------------------------------------//
	if(stop_defined_num==1)
	{
		CaravanStop *stopPtr = &stop_array[0];
		err_when(!stopPtr->firm_recno);

		if(firm_array.is_deleted(stopPtr->firm_recno))
		{
			update_stop_list();
			return;
		}

		Firm *firmPtr = firm_array[stopPtr->firm_recno];
		int firmXLoc1 = firmPtr->loc_x1;
		int firmYLoc1 = firmPtr->loc_y1;
		int firmXLoc2 = firmPtr->loc_x2;
		int firmYLoc2 = firmPtr->loc_y2;
		int firmId = firmPtr->firm_id;
		if(firmXLoc1!=stopPtr->firm_loc_x1 || firmYLoc1!=stopPtr->firm_loc_y1 ||
			(firmId!=FIRM_MINE && firmId!=FIRM_FACTORY && firmId!=FIRM_MARKET))
		{
			update_stop_list();
			return;
		}

		int curXLoc = next_x_loc();
		int curYLoc = next_y_loc();

		if(curXLoc<firmXLoc1-1 || curXLoc>firmXLoc2+1 || curYLoc<firmYLoc1-1 || curYLoc>firmYLoc2+1)
		{
			if(cur_action==SPRITE_IDLE)
				move_to_firm_surround(firmXLoc1, firmYLoc1, sprite_info->loc_width, sprite_info->loc_height, firmId);
			else
				journey_status = ON_WAY_TO_FIRM;
		}
		else
		{
			journey_status = SURROUND_FIRM;
			//if(firmPtr->nation_recno==nation_recno)
			if(nation_array[nation_recno]->get_relation(firmPtr->nation_recno)->trade_treaty)
			{
				if(wait_count<=0)
				{
					//---------- unloading goods -------------//
					switch(stopPtr->firm_id)
					{
						case FIRM_MINE:
								break; // no goods unload to mine

						case FIRM_FACTORY:
								factory_unload_goods();
								break;

						case FIRM_MARKET:
								market_unload_goods();
								break;

						default: err_here();
									break;
					}

					wait_count = MAX_CARAVAN_WAIT_TERM*SURROUND_FIRM_WAIT_FACTOR;
				}
				else
					wait_count--;
			}
		}
		return;
	}

	//-----------------------------------------------------------------------------//
	// at least 2 stops for the caravan to move between
	//-----------------------------------------------------------------------------//
	err_when(stop_defined_num<=1);

	if(journey_status==INSIDE_FIRM)
		caravan_in_firm();
	else
		caravan_on_way();
}
//----------- End of function UnitCaravan::pre_process -----------//


//--------- Begin of function UnitCaravan::caravan_in_firm ---------//
// journey_status : INSIDE_FIRM -->	ON_WAY_TO_FIRM
//												NO_STOP_DEFINED if no valid stop
//												SURROUND_FIRM if only one stop
//
void UnitCaravan::caravan_in_firm()
{
	//-----------------------------------------------------------------------------//
	// the market is deleted while the caravan is in market
	//-----------------------------------------------------------------------------//
	if(firm_array.is_deleted(action_para))
	{
		hit_points = (float) 0;	// caravan also die if the market is deleted
		unit_array.disappear_in_firm(sprite_recno); // caravan also die if the market is deleted
		return;
	}

	//-----------------------------------------------------------------------------//
	// waiting (time to upload/download cargo)
	//-----------------------------------------------------------------------------//
	if(wait_count>0)
	{
		wait_count--;
		return;
	}

	//-----------------------------------------------------------------------------//
	// leave the market and go to another market if possible
	//-----------------------------------------------------------------------------//
	CaravanStop *stopPtr = stop_array + dest_stop_id - 1;
	int xLoc = stop_x_loc;
	int yLoc = stop_y_loc;
	Location *locPtr = world.get_loc(xLoc, yLoc);
	Firm		*firmPtr;

	if(locPtr->can_move(mobile_type))
		init_sprite(xLoc, yLoc); // appear in the location the unit disappeared before
	else
	{
		//---- the entering location is blocked, select another location to leave ----//
		err_when(action_para==0);
		firmPtr = firm_array[action_para];

		if(appear_in_firm_surround(xLoc, yLoc, firmPtr))
		{
			init_sprite(xLoc, yLoc);
			stop();
			err_when(action_para);
		}
		else
		{
			wait_count = MAX_CARAVAN_WAIT_TERM*10; //********* BUGHERE, continue to wait or ....
			return;
		}
	}

	//-------------- get next stop id. ----------------//
	int nextStopId = get_next_stop_id(dest_stop_id);
	if(!nextStopId || dest_stop_id==nextStopId)
	{
		dest_stop_id = nextStopId;
		journey_status = (!nextStopId) ? NO_STOP_DEFINED : SURROUND_FIRM;
		return;	// no stop or only one stop is valid
	}

	dest_stop_id = nextStopId;
	firmPtr = firm_array[stop_array[dest_stop_id-1].firm_recno];

	action_para = 0; // since action_para is used to store the current market recno, reset before searching
	move_to_firm_surround(firmPtr->loc_x1, firmPtr->loc_y1, sprite_info->loc_width, sprite_info->loc_height, firmPtr->firm_id);
	
	journey_status = ON_WAY_TO_FIRM;
}
//----------- End of function UnitCaravan::caravan_in_firm -----------//


//--------- Begin of function UnitCaravan::caravan_on_way ---------//
// journey_status : ON_WAY_TO_FIRM --> SURROUND_FIRM
//						  SURROUND_FIRM  --> INSIDE_FIRM
//
void UnitCaravan::caravan_on_way()
{
	CaravanStop *stopPtr = stop_array + dest_stop_id - 1;

	if(cur_action==SPRITE_IDLE && journey_status!=SURROUND_FIRM)
	{
		if(!firm_array.is_deleted(stopPtr->firm_recno))
		{
			Firm *firmPtr = firm_array[stopPtr->firm_recno];
			move_to_firm_surround(firmPtr->loc_x1, firmPtr->loc_y1, sprite_info->loc_width, sprite_info->loc_height, firmPtr->firm_id);
			int nextXLoc = next_x_loc();
			int nextYLoc = next_y_loc();

			if(nextXLoc>=firmPtr->loc_x1-1 && nextXLoc<=firmPtr->loc_x2+1 &&
				nextYLoc>=firmPtr->loc_y1-1 && nextYLoc<=firmPtr->loc_y2+1) // hard code 1 for carvan size 1x1
				journey_status = SURROUND_FIRM;

			if(nextXLoc==move_to_x_loc && nextYLoc==move_to_y_loc && !ignore_power_nation)
				ignore_power_nation = 1;

			return;
		}
	}

	short unitRecno = sprite_recno;

	err_when(cur_action==SPRITE_ATTACK || action_mode==ACTION_ATTACK_UNIT || action_mode==ACTION_ATTACK_FIRM ||
				action_mode==ACTION_ATTACK_TOWN || action_mode==ACTION_ATTACK_WALL);

	if(unit_array.is_deleted(unitRecno))
		return; //-***************** BUGHERE ***************//

	if(firm_array.is_deleted(stopPtr->firm_recno))
	{
		update_stop_list();

		if(stop_defined_num) // move to next stop
		{
			Firm *firmPtr = firm_array[stop_array[stop_defined_num-1].firm_recno];
			move_to_firm_surround(firmPtr->loc_x1, firmPtr->loc_y1, sprite_info->loc_width, sprite_info->loc_height, firmPtr->firm_id);
		}
		return;
	}

	//CaravanStop *stopPtr = stop_array + dest_stop_id - 1;
	Firm	*firmPtr = firm_array[stopPtr->firm_recno];

	int nextXLoc = next_x_loc();
	int nextYLoc = next_y_loc();

	if(journey_status==SURROUND_FIRM ||
		( nextXLoc==move_to_x_loc && nextYLoc==move_to_y_loc && cur_x==next_x && cur_y==next_y && // move in a tile exactly
		  (nextXLoc>=firmPtr->loc_x1-1 && nextXLoc<=firmPtr->loc_x2+1 &&
			nextYLoc>=firmPtr->loc_y1-1 && nextYLoc<=firmPtr->loc_y2+1) )) // in the surrounding of the firm
	{
		//-*************************************************************-//
		/*if(nation_recno==nation_array.player_recno)
		{
			OutputDebugString("surround firm\r\n");
			for(int type = 0; type<MAX_GOODS_SELECT_BUTTON; type++)
			{
				if(button_select_enable_flag[0][type])
					break;
			}

			stop_array[0].pick_up_array[type] ^= 1;
			set_stop_pick_up(1, type, COMMAND_PLAYER);
		}*/
		//-*************************************************************-//

		//-------------------- update pick_up_array --------------------//
		stopPtr->update_pick_up();

		//-------------------------------------------------------//
		// load/unload goods
		//-------------------------------------------------------//
		if(nation_array[nation_recno]->get_relation(firmPtr->nation_recno)->trade_treaty)
		{
			switch(firmPtr->firm_id)
			{
				case FIRM_MINE:
						mine_load_goods(stopPtr->pick_up_type);
						break;

				case FIRM_FACTORY:
						factory_unload_goods();
						factory_load_goods(stopPtr->pick_up_type);
						break;

				case FIRM_MARKET:
						market_unload_goods();

						if(stopPtr->pick_up_type == AUTO_PICK_UP)
							market_auto_load_goods();
						else if(stopPtr->pick_up_type!=NO_PICK_UP)
							market_load_goods();
						break;

				default: err_here();
							break;
			}
		}

		//-------------------------------------------------------//
		// action_para is used to store the firm_recno of the market
		// where the caravan move in.
		//-------------------------------------------------------//
		action_para = stopPtr->firm_recno;

		stop_x_loc = move_to_x_loc; // store entering location
		stop_y_loc = move_to_y_loc;
		wait_count = MAX_CARAVAN_WAIT_TERM;		// set waiting term

		reset_path();
		deinit_sprite(1);	// the caravan enters the market now. 1-keep it selected if it is currently selected

		err_when(cur_x!=-1);
		cur_x--;	// set cur_x to -2, such that invisible but still process pre_process()

		journey_status = INSIDE_FIRM;
	}
	else
	{
		if(cur_action!=SPRITE_MOVE)
		{
			//----------------------------------------------------//
			// blocked by something, go to the destination again
			// note: if return value is 0, cannot reach the firm.		//*********BUGHERE
			//----------------------------------------------------//
			move_to_firm_surround(firmPtr->loc_x1, firmPtr->loc_y1, sprite_info->loc_width, sprite_info->loc_height, firmPtr->firm_id);
			journey_status = ON_WAY_TO_FIRM;
		}
	}
}
//----------- End of function UnitCaravan::caravan_on_way -----------//


//--------- Begin of function UnitCaravan::appear_in_firm_surround ---------//
//
// This function return 1 if a suitable location is found, that means the
// caravan will leave the firm there. Otherwise, return 0.
//
// xLoc, yLoc are reference variables for returning the location found.
//
int UnitCaravan::appear_in_firm_surround(int& xLoc, int& yLoc, Firm* firmPtr)
{
	int upperLeftBoundX	= firmPtr->loc_x1 - 1;	// the surrounding coordinates of the firm
	int upperLeftBoundY	= firmPtr->loc_y1 - 1;
	int lowerRightBoundX = firmPtr->loc_x2 + 1;
	int lowerRightBoundY = firmPtr->loc_y2 + 1;

	int count = 1, inside = 1, found = 0, i;
	int testXLoc = xLoc;
	int testYLoc = yLoc;
	int limit;
	Location *locPtr;

	//---------------------------------------------------------//
	//		9  10  11  12		the location is tested in the order
	//		8   1   2  13		shown, if the location is the surrounding
	//		7   x   3  14		of the firm and non-blocked, break
	//		6   5   4  ...		the test
	//---------------------------------------------------------//

	while(inside)
	{
		inside = 0;
		limit = count<<1;
		err_when(limit!=count*2);

		//------------ upper --------------//
		testXLoc = xLoc - count + 1;
		testYLoc = yLoc - count;
		for(i=0; i<limit; i++)
		{
			if(testXLoc<0 || testYLoc>=MAX_WORLD_X_LOC || testYLoc<0 || testYLoc>=MAX_WORLD_Y_LOC)
				continue;

			if(testXLoc<upperLeftBoundX || testXLoc>lowerRightBoundX || testYLoc<upperLeftBoundY || testYLoc>lowerRightBoundY)
				continue;

			locPtr = world.get_loc(testXLoc, testYLoc);
			if(locPtr->can_move(mobile_type))
			{
				found++;
				break;
			}
			else
				xLoc++;

			inside++;
		}

		if(found)
			break;

		//------------ right --------------//
		testXLoc = xLoc + count;
		testYLoc = yLoc - count + 1;
		for(i=0; i<limit; i++)
		{
			if(testXLoc<0 || testYLoc>=MAX_WORLD_X_LOC || testYLoc<0 || testYLoc>=MAX_WORLD_Y_LOC)
				continue;

			if(testXLoc<upperLeftBoundX || testXLoc>lowerRightBoundX || testYLoc<upperLeftBoundY || testYLoc>lowerRightBoundY)
				continue;

			locPtr = world.get_loc(testXLoc, testYLoc);
			if(locPtr->can_move(mobile_type))
			{
				found++;
				break;
			}
			else
				yLoc++;

			inside++;
		}

		if(found)
			break;

		//------------- down --------------//
		testXLoc = xLoc + count - 1;
		testYLoc = yLoc + count;
		for(i=0; i<limit; i++)
		{
			if(testXLoc<0 || testYLoc>=MAX_WORLD_X_LOC || testYLoc<0 || testYLoc>=MAX_WORLD_Y_LOC)
				continue;

			if(testXLoc<upperLeftBoundX || testXLoc>lowerRightBoundX || testYLoc<upperLeftBoundY || testYLoc>lowerRightBoundY)
				continue;

			locPtr = world.get_loc(testXLoc, testYLoc);
			if(locPtr->can_move(mobile_type))
			{
				found++;
				break;
			}
			else
				xLoc--;

			inside++;
		}

		if(found)
			break;

		//------------- left --------------//
		testXLoc = xLoc - count;
		testYLoc = yLoc + count - 1;
		for(i=0; i<limit; i++)
		{
			if(testXLoc<0 || testYLoc>=MAX_WORLD_X_LOC || testYLoc<0 || testYLoc>=MAX_WORLD_Y_LOC)
				continue;

			if(testXLoc<upperLeftBoundX || testXLoc>lowerRightBoundX || testYLoc<upperLeftBoundY || testYLoc>lowerRightBoundY)
				continue;

			locPtr = world.get_loc(testXLoc, testYLoc);
			if(locPtr->can_move(mobile_type))
			{
				found++;
				break;
			}
			else
				yLoc--;

			inside++;
		}

		if(found)
			break;

		//---------------------------------------------//
		count++;
	}

	if(found)
	{
		xLoc = testXLoc;
		yLoc = testYLoc;
		return 1;
	}

	return 0;
}
//----------- End of function UnitCaravan::appear_in_firm_surround -----------//


//---------- begin static function i_disp_caravan_select_button ---------------//
static void i_disp_caravan_select_button(ButtonCustom *button, int repaintBody)
{
	int x1 = button->x1;
	int y1 = button->y1;
	int x2 = button->x2;
	int y2 = button->y2;
	int shift;

	//------------- modify x1,y1, x2,y2 to the button body --------------//
	if(button->pushed_flag)
	{
		int colorDown = Vga::active_buf->color_down;		// change the color of the body area to yellow to highlight the change
		Vga::active_buf->color_down = (char) V_YELLOW;

		Vga::active_buf->d3_panel_down(x1, y1, x2, y2);

		Vga::active_buf->color_down = (char) colorDown;

		x1++;
		y1++;
		shift = 2;
	}
	else
	{
		Vga::active_buf->d3_panel_up(x1, y1, x2, y2);
		x2--;
		y2--;
		shift = 3;
	}

	//-------------- put goods icon ---------------//

	int id = button->custom_para.value;
	char *iconName=NULL;

	int x = x1+shift;
	int y = y1+shift;

	if(id==AUTO_PICK_UP)
	{
		iconName = "AUTOPICK";
	}
	else if(id==NO_PICK_UP)
	{
		iconName = "NOPICK";
	}
	else if(id>=PICK_UP_RAW_FIRST && id<=PICK_UP_RAW_LAST)
	{
		raw_res.put_small_raw_icon( x, y, id-PICK_UP_RAW_FIRST+1 );
	}
	else if(id>=PICK_UP_PRODUCT_FIRST && id<=PICK_UP_PRODUCT_LAST)
	{
		raw_res.put_small_product_icon( x, y, id-PICK_UP_PRODUCT_FIRST+1 );
	}
	else
		err_here();

	if( iconName )
	{
		help.set_help( x, y, x+9, y+9, iconName );
		Vga::active_buf->put_bitmap_trans( x, y, image_icon.get_ptr(iconName) );
	}
}
//---------- end static function i_disp_caravan_select_button ---------------//


//--------- Begin of function UnitCaravan::has_pick_up_type ---------//
//
// Return whether the specific stop has the specific pick up types.
// 
int UnitCaravan::has_pick_up_type(int stopId, int pickUpType)
{
	return stop_array[stopId-1].pick_up_array[pickUpType-1];
}
//---------- End of function UnitCaravan::has_pick_up_type ----------//


//--------- Begin of function UnitCaravan::carrying_qty ---------//
//
// Return whether the qty of the specific product/raw type that
// this caravan is currently carrying.
//
int UnitCaravan::carrying_qty(int pickUpType)
{
	if( pickUpType >= PICK_UP_RAW_FIRST &&
		 pickUpType <= PICK_UP_RAW_LAST )
	{
		return raw_qty_array[pickUpType-PICK_UP_RAW_FIRST];
	}
	else if( pickUpType >= PICK_UP_PRODUCT_FIRST &&
				pickUpType <= PICK_UP_PRODUCT_LAST )
	{
		return product_raw_qty_array[pickUpType-PICK_UP_PRODUCT_FIRST];
	}
	else
	{
		err_here();
		return 0;
	}
}
//---------- End of function UnitCaravan::carrying_qty ----------//


