/*
 * 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    : OF_CAMP.CPP
//Description : Firm Military Camp

#include <OINFO.h>
#include <OVGA.h>
#include <ODATE.h>
#include <OIMGRES.h>
#include <OHELP.h>
#include <OSYS.h>
#include <OSTR.h>
#include <OFONT.h>
#include <OMOUSE.h>
#include <OBUTTON.h>
#include <OBUTT3D.h>
#include <OPOWER.h>
#include <OUNIT.h>
#include <OINFO.h>
#include <OGAME.h>
#include <OSPY.h>
#include <ONATION.h>
#include <ORACERES.h>
#include <OTERRAIN.h>
#include <OWORLD.h>
#include <OF_CAMP.h>
#include <OREMOTE.h>
#include <OSERES.h>
#include <OSE.h>


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

static Button3D button_patrol, button_reward, button_defense;
static Firm*	 cur_firm_ptr;

//--------- Declare static functions ---------//

static int  sort_soldier_function( const void *a, const void *b );
static int  sort_soldier_id_function( const void *a, const void *b );

static void disp_debug_info(FirmCamp* firmPtr, int refreshFlag);

//--------- Begin of function FirmCamp::FirmCamp ---------//
//
FirmCamp::FirmCamp()
{
	firm_skill_id = SKILL_LEADING;

	employ_new_worker = 1;

	memset(defense_array, 0, sizeof(DefenseUnit)*(MAX_WORKER+1));

	defend_target_recno = 0;
	defense_flag = 1;

	is_attack_camp = 0;
}
//----------- End of function FirmCamp::FirmCamp -----------//


//--------- Begin of function FirmCamp::deinit ---------//
//
void FirmCamp::deinit()
{
	int firmRecno = firm_recno;		// save the firm_recno first for reset_unit_home_camp()

	Firm::deinit();

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

	int saveOverseerRecno = overseer_recno;

	overseer_recno = 0;     // set overseer_recno to 0 when calling update_influence(), so this base is disregarded.

	update_influence();

	overseer_recno = saveOverseerRecno;

	clear_defense_mode();

	//---- reset all units whose home_camp_firm_recno is this firm ----//

	reset_unit_home_camp(firmRecno);		// this must be called at last as Firm::deinit() will create new units.

	//--- if this camp is in the Nation::attack_camp_array[], remove it now ---//

	reset_attack_camp(firmRecno);
}
//----------- End of function FirmCamp::deinit -----------//


//--------- Begin of function FirmCamp::reset_unit_home_camp ---------//
//
void FirmCamp::reset_unit_home_camp(int firmRecno)
{
	//---- reset all units whose home_camp_firm_recno is this firm ----//

	Unit* unitPtr;

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

		unitPtr = unit_array[i];

		if( unitPtr->home_camp_firm_recno == firmRecno )
			unitPtr->home_camp_firm_recno = 0;
	}
}
//----------- End of function FirmCamp::reset_unit_home_camp -----------//


//--------- Begin of function FirmCamp::reset_attack_camp ---------//
//
void FirmCamp::reset_attack_camp(int firmRecno)
{
	//--- if this camp is in the Nation::attack_camp_array[], remove it now ---//

	if( firm_ai )
	{
		Nation* nationPtr = nation_array[nation_recno];

		for( int i=0 ; i<nationPtr->attack_camp_count ; i++ )
		{
			if( nationPtr->attack_camp_array[i].firm_recno == firmRecno )
			{
				m.del_array_rec(nationPtr->attack_camp_array, nationPtr->attack_camp_count, sizeof(AttackCamp), i+1 );
				nationPtr->attack_camp_count--;
				break;
			}
		}
	}
}
//----------- End of function FirmCamp::reset_attack_camp -----------//


//--------- Begin of function FirmCamp::clear_defense_mode ---------//
void FirmCamp::clear_defense_mode()
{
	//------------------------------------------------------------------//
	// change defense unit's to non-defense mode
	//------------------------------------------------------------------//
	Unit *unitPtr;
	for(int i=unit_array.size(); i>=1; --i)
	{
		if(unit_array.is_deleted(i))
			continue;

		unitPtr = unit_array[i];
		if(!unitPtr)
			continue;

		err_when(unitPtr->cur_action==SPRITE_DIE || unitPtr->action_mode==ACTION_DIE || unitPtr->hit_points<=0);
		if(unitPtr->in_auto_defense_mode() && unitPtr->action_misc==ACTION_MISC_DEFENSE_CAMP_RECNO &&
			unitPtr->action_misc_para==firm_recno)
			unitPtr->clear_unit_defense_mode();
	}

	memset(defense_array, 0, sizeof(DefenseUnit)*(MAX_WORKER+1));
}
//----------- End of function FirmCamp::clear_defense_mode -----------//


//--------- Begin of function FirmCamp::assign_unit ---------//
//
void FirmCamp::assign_unit(int unitRecno)
{
	Unit* unitPtr = unit_array[unitRecno];

	//------- if this is a construction worker -------//

	if( unitPtr->skill.skill_id == SKILL_CONSTRUCTION )
	{
		set_builder(unitRecno);
		return;
	}

	//-------- assign the unit ----------//

	int rankId = unit_array[unitRecno]->rank_id;

	if( rankId == RANK_GENERAL || rankId==RANK_KING )
	{
		assign_overseer(unitRecno);
	}
	else
	{
		assign_worker(unitRecno);
	}
}
//----------- End of function FirmCamp::assign_unit -----------//


//--------- Begin of function FirmCamp::assign_overseer ---------//
//
void FirmCamp::assign_overseer(int overseerRecno)
{
	//---- reset the team member count of the general ----//

	if( overseerRecno )
	{
		Unit* unitPtr = unit_array[overseerRecno];

		err_when( !unitPtr->race_id );
		err_when( unitPtr->rank_id != RANK_GENERAL && unitPtr->rank_id != RANK_KING );
		err_when( !unitPtr->team_info );

		unitPtr->team_info->member_count = 0;
		unitPtr->home_camp_firm_recno 	= 0;
	}

	//----- assign the overseer now -------//

	Firm::assign_overseer(overseerRecno);

	//------------- update influence -----------//

	update_influence();
}
//----------- End of function FirmCamp::assign_overseer -----------//


//--------- Begin of function FirmCamp::assign_worker --------//
//
// Increase armed unit count of the race of the worker assigned,
// as when a unit is assigned to a camp, Unit::deinit() will decrease
// the counter, so we need to increase it back here.
//
void FirmCamp::assign_worker(int workerUnitRecno)
{
	Firm::assign_worker(workerUnitRecno);

	//--- remove the unit from patrol_unit_array when it returns to the base ---//

	validate_patrol_unit();

	//-------- sort soldiers ---------//

	sort_soldier();
}
//----------- End of function FirmCamp::assign_worker --------//


//------- Begin of function FirmCamp::validate_patrol_unit ---------//
//
void FirmCamp::validate_patrol_unit()
{
	int unitRecno;
	Unit* unitPtr;

	err_when( patrol_unit_count > 9 );

	for( int i=patrol_unit_count ; i>0 ; i-- )
	{
		unitRecno = patrol_unit_array[i-1];

		if( unit_array.is_deleted(unitRecno) ||
			 (unitPtr=unit_array[unitRecno])->is_visible()==0 ||
			 unitPtr->nation_recno != nation_recno )
		{
			err_when( patrol_unit_count > 9 );

			m.del_array_rec( patrol_unit_array, patrol_unit_count, sizeof(patrol_unit_array[0]), i );

			err_when( patrol_unit_count==0 );		// it's already 0
			patrol_unit_count--;

			err_when( patrol_unit_count < 0 );
			err_when( patrol_unit_count > 9 );
		}
	}
}
//-------- End of function FirmCamp::validate_patrol_unit ---------//


//------- Begin of function FirmCamp::change_nation ---------//
//
void FirmCamp::change_nation(int newNationRecno)
{
	//--- update the UnitInfo vars of the workers in this firm ---//

	for( int i=0 ; i<worker_count ; i++ )
		unit_res[ worker_array[i].unit_id ]->unit_change_nation(newNationRecno, nation_recno, worker_array[i].rank_id );

	//----- reset unit's home camp to this firm -----//

	reset_unit_home_camp(firm_recno);

	//--- if this camp is in the Nation::attack_camp_array[], remove it now ---//

	reset_attack_camp(firm_recno);

	//---- reset AI vars --------//

	ai_capture_town_recno = 0;
	ai_recruiting_soldier = 0;

	//-------- change the nation of this firm now ----------//

	Firm::change_nation(newNationRecno);
}
//-------- End of function FirmCamp::change_nation ---------//


//--------- Begin of function FirmCamp::update_influence ---------//
//
// Update this camp's influence on neighbor towns.
//
void FirmCamp::update_influence()
{
	int   i;
	Town* townPtr;

	for( i=0 ; i<linked_town_count ; i++ )
	{
		if(town_array.is_deleted(linked_town_array[i]))
			continue;

		townPtr = town_array[linked_town_array[i]];

		if( linked_town_enable_array[i] == LINK_EE )
		{
			if( townPtr->nation_recno )
				townPtr->update_target_loyalty();
			else
				townPtr->update_target_resistance();
		}
	}
}
//----------- End of function FirmCamp::update_influence -----------//


//--------- Begin of function FirmCamp::put_info ---------//
//
void FirmCamp::put_info(int refreshFlag)
{
	disp_basic_info(INFO_Y1, refreshFlag);

	if( !should_show_info() )
		return;

	disp_camp_info(INFO_Y1+54, refreshFlag);
	disp_worker_list(INFO_Y1+104, refreshFlag);
	disp_worker_info(INFO_Y1+168, refreshFlag);

	//------ display button -------//

	int x;

	if( own_firm() )
	{
		if( refreshFlag==INFO_REPAINT )
		{
			button_patrol.paint( INFO_X1, INFO_Y1+242, 'A', "PATROL" );
			button_reward.paint( INFO_X1+BUTTON_ACTION_WIDTH, INFO_Y1+242, 'A', "REWARDCB" );
			button_defense.paint( INFO_X2-BUTTON_ACTION_WIDTH, INFO_Y1+242, 'A', defense_flag ? (char*)"DEFENSE1" : (char*)"DEFENSE0" );
		}

		if( overseer_recno || worker_count )
			button_patrol.enable();
		else
			button_patrol.disable();

		if( nation_array[nation_recno]->cash >= REWARD_COST &&
			 ( (overseer_recno && unit_array[overseer_recno]->rank_id != RANK_KING)
			  || selected_worker_id ) )
		{
			button_reward.enable();
		}
		else
		{
			button_reward.disable();
		}

		x=INFO_X1+BUTTON_ACTION_WIDTH*2;
	}
	else
		x=INFO_X1;

	disp_spy_button(x, INFO_Y1+242, refreshFlag);

	#ifdef DEBUG
		if( sys.testing_session || sys.debug_session )
			disp_debug_info(this, refreshFlag);
	#endif
}
//----------- End of function FirmCamp::put_info -----------//


//--------- Begin of function FirmCamp::detect_info ---------//
//
void FirmCamp::detect_info()
{
	if( detect_basic_info() )
		return;

	if( !should_show_info() )
		return;

	//------ detect the overseer button -----//

	int rc = mouse.single_click(INFO_X1+6, INFO_Y1+58,
				INFO_X1+5+UNIT_LARGE_ICON_WIDTH, INFO_Y1+57+UNIT_LARGE_ICON_HEIGHT, 2 );

	if( rc==1 )		// display this overseer's info
	{
		selected_worker_id = 0;
		disp_camp_info(INFO_Y1+54, INFO_UPDATE);
		disp_worker_list(INFO_Y1+104, INFO_UPDATE);
		disp_worker_info(INFO_Y1+168, INFO_UPDATE);
	}

	//--------- detect soldier info ---------//

	if( detect_worker_list() )
	{
		disp_camp_info(INFO_Y1+54, INFO_UPDATE);
		disp_worker_list(INFO_Y1+104, INFO_UPDATE);
		disp_worker_info(INFO_Y1+168, INFO_UPDATE);
	}

	//---------- detect spy button ----------//

	detect_spy_button();

	if( !own_firm() )
		return;

	//------ detect the overseer button -----//

	if( rc==2 )
	{
		if(remote.is_enable())
		{
			// packet structure : <firm recno>
			short *shortPtr=(short *)remote.new_send_queue_msg(MSG_FIRM_MOBL_OVERSEER, sizeof(short));
			shortPtr[0] = firm_recno;
		}
		else
		{
			assign_overseer(0);		// the overseer quits the camp
		}
	}

	//----------- detect patrol -----------//

	if( button_patrol.detect() )
	{
		if(remote.is_enable())
		{
			// packet structure : <firm recno>
			short *shortPtr=(short *)remote.new_send_queue_msg(MSG_F_CAMP_PATROL, sizeof(short));
			shortPtr[0] = firm_recno;
		}
		else
		{
			patrol();
		}
	}

	//----------- detect reward -----------//

	if( button_reward.detect() )
	{
		reward(selected_worker_id, COMMAND_PLAYER);
		// ##### begin Gilbert 25/9 ######//
		se_ctrl.immediate_sound("TURN_ON");
		// ##### end Gilbert 25/9 ######//
	}

	//----- detect defense mode button -------//

	if( button_defense.detect() )
	{
		// ##### begin Gilbert 25/9 ######//
		se_ctrl.immediate_sound( !defense_flag?(char*)"TURN_ON":(char*)"TURN_OFF");
		// ##### end Gilbert 25/9 ######//

		if( !remote.is_enable() )
		{
			// update RemoteMsg::toggle_camp_patrol()
			defense_flag = !defense_flag;
		}
		else
		{
			// packet structure : <firm recno> <defense_flag>
			short *shortPtr=(short *)remote.new_send_queue_msg(MSG_F_CAMP_TOGGLE_PATROL, 2*sizeof(short));
			shortPtr[0] = firm_recno;
			shortPtr[1] = !defense_flag;
		}

		button_defense.update_bitmap( defense_flag ? (char*)"DEFENSE1" : (char*)"DEFENSE0" );
	}
}
//----------- End of function FirmCamp::detect_info -----------//


//--------- Begin of function FirmCamp::disp_camp_info ---------//
//
void FirmCamp::disp_camp_info(int dispY1, int refreshFlag)
{
	//---------------- paint the panel --------------//

	if( refreshFlag == INFO_REPAINT )
		vga.d3_panel_up( INFO_X1, dispY1, INFO_X2, dispY1+46);

	if( !overseer_recno )
		return;

	//------------ display overseer info -------------//

	Unit* overseerUnit = unit_array[overseer_recno];

	int x=INFO_X1+6, y=dispY1+4, x1=x+UNIT_LARGE_ICON_WIDTH+8;

	if( selected_worker_id == 0 )
	{
		vga_front.rect( x-2, y-2, x+UNIT_LARGE_ICON_WIDTH+1, y+UNIT_LARGE_ICON_HEIGHT+1, 2, V_YELLOW );
	}
	else
	{
		vga.blt_buf( x-2, y-2, x+UNIT_LARGE_ICON_WIDTH+1, y-1, 0 );
		vga.blt_buf( x-2, y+UNIT_LARGE_ICON_HEIGHT+1, x+UNIT_LARGE_ICON_WIDTH+1, y+UNIT_LARGE_ICON_HEIGHT+2, 0 );
		vga.blt_buf( x-2, y-2, x-1, y+UNIT_LARGE_ICON_HEIGHT+2, 0 );
		vga.blt_buf( x+UNIT_LARGE_ICON_WIDTH, y-2, x+UNIT_LARGE_ICON_WIDTH+1, y+UNIT_LARGE_ICON_HEIGHT+2, 0 );
	}

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

	if( refreshFlag == INFO_REPAINT )
	{
		vga_front.put_bitmap(x, y, unit_res[overseerUnit->unit_id]->get_large_icon_ptr(overseerUnit->rank_id) );
	}

	//-------- set help parameters --------//

	if( mouse.in_area(x, y, x+UNIT_LARGE_ICON_WIDTH+3, y+UNIT_LARGE_ICON_HEIGHT+3) )
		help.set_unit_help( overseerUnit->unit_id, overseerUnit->rank_id, x, y, x+UNIT_LARGE_ICON_WIDTH+3, y+UNIT_LARGE_ICON_HEIGHT+3);

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

	if( overseerUnit->rank_id == RANK_KING )
	{
		if( refreshFlag == INFO_REPAINT )
			font_san.put( x1, y, "King" );

		y+=14;
	}

	if( refreshFlag == INFO_REPAINT )
		font_san.put( x1, y, overseerUnit->unit_name(0), 0, INFO_X2-2 );		// 0-ask unit_name() not to return the title of the unit

	y+=14;

	//------- display leadership -------//

	String str;

	str  = translate.process("Leadership");
	str += ": ";
	str += overseerUnit->skill.get_skill(SKILL_LEADING);

	font_san.disp( x1, y, str, INFO_X2-10 );
	y+=14;

	//--------- display loyalty ----------//

	if( overseerUnit->rank_id != RANK_KING )
	{
		x1 = font_san.put( x1, y, "Loyalty:" );

		int x2 = info.disp_loyalty( x1, y-1, x1, overseerUnit->loyalty, overseerUnit->target_loyalty, nation_recno, refreshFlag );

		if( overseerUnit->spy_recno )
		{
			//------ if this is the player's spy -------//

			if( overseerUnit->true_nation_recno() == nation_array.player_recno )
			{
				vga_front.put_bitmap( x2+5, y+1, image_icon.get_ptr("U_SPY") );
				x2 += 15;
			}
		}

		vga.blt_buf( x2, y-1, INFO_X2-2, dispY1+44, 0 );
	}
}
//----------- End of function FirmCamp::disp_camp_info -----------//


//--------- Begin of function FirmCamp::next_day ---------//
//
void FirmCamp::next_day()
{
	//----- call next_day() of the base class -----//

	Firm::next_day();

	//----- update the patrol_unit_array -----//

	validate_patrol_unit();

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

	if( info.game_date%15 == firm_recno%15 )			// once a week
	{
		train_unit();
		recover_hit_point();
	}

	//--- if there are weapons in the firm, pay for their expenses ---//

	pay_weapon_expense();
}
//----------- End of function FirmCamp::next_day -----------//


//------- Begin of function FirmCamp::train_unit -------//
//
// Increase the leadership and ocmbat level of the general and the soldiers.
//
void FirmCamp::train_unit()
{
	if( !overseer_recno )
		return;

	Unit* overseerUnit = unit_array[overseer_recno];

	if( overseerUnit->skill.skill_id != SKILL_LEADING )
		return;

	int 		 overseerSkill = overseerUnit->skill.skill_level;
	RaceInfo* overseerRaceInfo = race_res[overseerUnit->race_id];
	int		 incValue;

	//------- increase the commander's leadership ---------//

	if( worker_count > 0 && overseerUnit->skill.skill_level < 100 )
	{
		//-- the more soldiers this commander has, the higher the leadership will increase ---//

		incValue = 5 * worker_count
					  * (int) overseerUnit->hit_points / overseerUnit->max_hit_points
					  * (100+overseerUnit->skill.skill_potential*2) / 100;

		overseerUnit->skill.skill_level_minor += incValue;

		if( overseerUnit->skill.skill_level_minor >= 100 )
		{
			overseerUnit->skill.skill_level_minor -= 100;
			overseerUnit->skill.skill_level++;
		}
	}

	//------- increase the commander's combat level ---------//

	if( overseerUnit->skill.combat_level < 100 )
	{
		incValue = 20 * (int) overseerUnit->hit_points / overseerUnit->max_hit_points
					  * (100+overseerUnit->skill.skill_potential*2) / 100;

		overseerUnit->skill.combat_level_minor += incValue;

		if( overseerUnit->skill.combat_level_minor >= 100 )
		{
			overseerUnit->skill.combat_level_minor -= 100;

			overseerUnit->set_combat_level(overseerUnit->skill.combat_level+1);
		}
	}

	//------- increase the solider's combat level -------//

	int	  levelMinor;
	Worker* workerPtr = worker_array;

	for( int i=0 ; i<worker_count ; i++, workerPtr++ )
	{
		if( workerPtr->skill_id != SKILL_LEADING )
			continue;

		//------- increase worker skill -----------//

		if( workerPtr->combat_level < overseerSkill )
		{
			incValue = max(20, overseerSkill-workerPtr->combat_level)
						  * workerPtr->hit_points / workerPtr->max_hit_points()
						  * (100+workerPtr->skill_potential*2) / 100;

			levelMinor = workerPtr->combat_level_minor + incValue;		// with random factors, resulting in 75% to 125% of the original

			while( levelMinor >= 100 )
			{
				levelMinor -= 100;
				workerPtr->combat_level++;
			}

			workerPtr->combat_level_minor = levelMinor;
		}

		//-- if the soldier has leadership potential, he learns leadership --//

		if( workerPtr->skill_potential > 0 && workerPtr->skill_level < 100 )
		{
			incValue = (int) max(50, overseerUnit->skill.skill_level-workerPtr->skill_level)
						  * workerPtr->hit_points / workerPtr->max_hit_points()
						  * workerPtr->skill_potential*2 / 100;

			workerPtr->skill_level_minor += incValue;

			err_when( workerPtr->skill_level >= 100 );

			if( workerPtr->skill_level_minor > 100 )
			{
				workerPtr->skill_level++;
				workerPtr->skill_level_minor -= 100;
			}
		}
	}

	sort_soldier();
}
//-------- End of function FirmCamp::train_unit --------//


//------- Begin of function FirmCamp::recover_hit_point -------//
//
// Soldiers recover their hit points.
//
// No need to recover the hit points of the general here as
// this is taken care in the Unit class function of the general.
//
void FirmCamp::recover_hit_point()
{
	Worker* workerPtr = worker_array;

	for( int i=0 ; i<worker_count ; i++, workerPtr++ )
	{
		//------- increase worker hit points --------//

		if( workerPtr->hit_points < workerPtr->max_hit_points() )
			workerPtr->hit_points++;
	}
}
//------- End of function FirmCamp::recover_hit_point -------//


//------- Begin of function FirmCamp::pay_weapon_expense -------//
//
void FirmCamp::pay_weapon_expense()
{
	Worker* workerPtr = worker_array;
	Nation* nationPtr = nation_array[nation_recno];

	for( int i=1 ; i<=worker_count ; i++, workerPtr++ )
	{
		if( workerPtr->unit_id &&
			 unit_res[workerPtr->unit_id]->unit_class == UNIT_CLASS_WEAPON )
		{
			if( nationPtr->cash > 0 )
			{
				nationPtr->add_expense( EXPENSE_WEAPON, (float) unit_res[workerPtr->unit_id]->year_cost / 365, 1 );
			}
			else     // decrease hit points if the nation cannot pay the unit
			{
				if( workerPtr->hit_points > 0 )
					workerPtr->hit_points--;

				if( workerPtr->hit_points == 0 )
					kill_worker(i);		// if its hit points is zero, delete it

				err_when( workerPtr->hit_points < 0 );
			}
		}
	}
}
//------- End of function FirmCamp::pay_weapon_expense -------//



//--------- Begin of function FirmCamp::patrol ---------//
//
void FirmCamp::patrol()
{
	err_when( !overseer_recno && !worker_count );
	err_when( firm_ai && ai_capture_town_recno );		// ai_capture_town_recno is set after patrol() is called

	if( nation_recno == nation_array.player_recno )
		power.reset_selection();

	//------------------------------------------------------------//
	// If the commander in this camp has units under his lead
	// outside and he is now going to lead a new team, then
	// the old team members should be reset.
	//------------------------------------------------------------//

	if(overseer_recno)
	{
		TeamInfo* teamInfo = unit_array[overseer_recno]->team_info;

		if( worker_count>0 && teamInfo->member_count>0 )
		{
			int unitRecno;

			for( int i=0 ; i<teamInfo->member_count ; i++ )
			{
				unitRecno = teamInfo->member_unit_array[i];

				if( unit_array.is_deleted(unitRecno) )
					continue;

				unit_array[unitRecno]->leader_unit_recno = 0;
			}
		}
	}

	//------------------------------------------------------------//
	// mobilize workers first, then the overseer.
	//------------------------------------------------------------//

	short overseerRecno = overseer_recno;

	if(patrol_all_soldier() && overseer_recno)
	{
		Unit* unitPtr = unit_array[overseer_recno];

		err_when(unitPtr->rank_id!=RANK_GENERAL && unitPtr->rank_id!=RANK_KING);
		unitPtr->team_id = unit_array.cur_team_id-1;   // set it to the same team as the soldiers which are defined in mobilize_all_worker()

		if( nation_recno == nation_array.player_recno )
		{
			unitPtr->selected_flag = 1;
			unit_array.selected_recno = overseer_recno;
			unit_array.selected_count++;
		}
	}

	assign_overseer(0);

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

	if(overseerRecno && !overseer_recno) // has overseer and the overseer is mobilized
	{
		Unit* overseerUnit = unit_array[overseerRecno];

		if(overseerUnit->is_own() )
		{
			se_res.sound( overseerUnit->cur_x_loc(), overseerUnit->cur_y_loc(), 1,
				'S', overseerUnit->sprite_id, "SEL");
		}

		err_when( patrol_unit_count > MAX_WORKER );

		//--- add the overseer into the patrol_unit_array[] of this camp ---//

		patrol_unit_array[patrol_unit_count++] = overseerRecno;

		err_when( patrol_unit_count > 9 );

		//------- set the team_info of the overseer -------//

		err_when( !overseerUnit->team_info );

		for( int i=0 ; i<patrol_unit_count ; i++ )
			overseerUnit->team_info->member_unit_array[i] = patrol_unit_array[i];

		overseerUnit->team_info->member_count = patrol_unit_count;
	}

	//-------- display info --------//

	if( nation_recno == nation_array.player_recno )		// for player's camp, patrol() can only be called when the player presses the button.
		info.disp();

	err_when( patrol_unit_count < 0 );
	err_when( patrol_unit_count > 9 );
}
//----------- End of function FirmCamp::patrol -----------//


//--------- Begin of function FirmCamp::patrol_all_soldier ---------//
//
// return 1 if there is enough space for patroling all soldiers
// return 0 otherwise
//
int FirmCamp::patrol_all_soldier()
{
	err_when(!worker_array);    // this function shouldn't be called if this firm does not need worker

	//------- detect buttons on hiring firm workers -------//

	err_when(worker_count>MAX_WORKER);
	
	#ifdef DEBUG
		int loopCount=0;
	#endif

	short unitRecno;
	int mobileWorkerId = 1;

	patrol_unit_count = 0;		// reset it, it will be increased later

	while(worker_count>0 && mobileWorkerId<=worker_count)
	{
		err_when(++loopCount > 100);

		if(worker_array[mobileWorkerId-1].skill_id==SKILL_LEADING)
		{
			unitRecno = mobilize_worker(mobileWorkerId, COMMAND_AUTO);

			patrol_unit_array[patrol_unit_count++] = unitRecno;
			err_when(patrol_unit_count>MAX_WORKER);
		}
		else
		{
			mobileWorkerId++;
			continue;
		}

		if(!unitRecno)
			return 0; // keep the rest workers as there is no space for creating the unit

		if(overseer_recno)
		{
			Unit* unitPtr = unit_array[unitRecno];
			unitPtr->team_id = unit_array.cur_team_id;   // define it as a team
			unitPtr->leader_unit_recno = overseer_recno;
			unitPtr->update_loyalty();							// the unit is just assigned to a new leader, set its target loyalty

			err_when( unit_array[overseer_recno]->rank_id != RANK_KING &&
					  unit_array[overseer_recno]->rank_id != RANK_GENERAL );

			if( nation_recno == nation_array.player_recno )
			{
				unitPtr->selected_flag = 1;
				unit_array.selected_count++;
			}
		}
	}

	unit_array.cur_team_id++;
	return 1;
}
//----------- End of function FirmCamp::patrol_all_soldier -----------//


//--------- Begin of function FirmCamp::mobilize_overseer --------//
//
int FirmCamp::mobilize_overseer()
{
	int unitRecno = Firm::mobilize_overseer();

	//--- set the home camp firm recno of the unit for later return ---//

	if( unitRecno )
	{
		unit_array[unitRecno]->home_camp_firm_recno = firm_recno;
		return unitRecno;
	}
	else
		return 0;
}
//----------- End of function FirmCamp::mobilize_overseer --------//


//--------- Begin of function FirmCamp::mobilize_worker ---------//
//
int FirmCamp::mobilize_worker(int workerId, char remoteAction)
{
	int unitRecno = Firm::mobilize_worker(workerId, remoteAction);

	//--- set the home camp firm recno of the unit for later return ---//

	if( unitRecno )
	{
		unit_array[unitRecno]->home_camp_firm_recno = firm_recno;
		return unitRecno;
	}
	else
		return 0;
}
//----------- End of function FirmCamp::mobilize_worker -----------//


//--------- Begin of function FirmCamp::defense ---------//
//### begin alex 15/10 ###//
//void FirmCamp::defense(short targetRecno)
void FirmCamp::defense(short targetRecno, int useRangeAttack)
//#### end alex 15/10 ####//
{
	//### begin alex 15/10 ###//
	//--******* BUGHERE , please provide a reasonable condition to set useRangeAttack to 1
	if(unit_array[targetRecno]->mobile_type!=UNIT_LAND)
		useRangeAttack = 1;
	else
		useRangeAttack = 0;
	//#### end alex 15/10 ####//

	if( !defense_flag )
		return;

	//--------------- define parameters ------------------//

	DefenseUnit *defPtr, *defPtr2;
	Unit *unitPtr;
	short unitRecno;
	int numOfUnitInside = worker_count + (overseer_recno>0);
	int i, j;

	if(employ_new_worker)
	{
		//---------- reset unit's parameters in the previous defense -----------//
		defPtr = defense_array;
		for(int i=0; i<=MAX_WORKER; i++, defPtr++)
		{
			if(defPtr->status==OUTSIDE_CAMP && defPtr->unit_recno && !unit_array.is_deleted(defPtr->unit_recno))
			{
				unitPtr = unit_array[defPtr->unit_recno];
				if(unitPtr->nation_recno==nation_recno && unitPtr->action_misc==ACTION_MISC_DEFENSE_CAMP_RECNO &&
					unitPtr->action_misc_para==firm_recno)
				{
					unitPtr->clear_unit_defense_mode();
					err_when(unitPtr->in_auto_defense_mode());
				}
			}
		}
		memset(defense_array, 0, sizeof(DefenseUnit)*(MAX_WORKER+1));
	}

	//------------------------------------------------------------------//
	// check all the exist(not dead) units outside the camp and arrange
	// them in the front part of the array.
	//------------------------------------------------------------------//
	j = 0;
	defPtr2 = defense_array;

	if(!employ_new_worker)	// some soliders may be outside the camp
	{
		for(i=0, defPtr=defense_array; i<=MAX_WORKER; i++, defPtr++)
		{
			if(!defPtr->unit_recno)
				continue;	// a free slot

			if(unit_array.is_deleted(defPtr->unit_recno))
			{
				defPtr->unit_recno = 0;
				continue;	// unit is dead
			}

			//----------------------------------------------------------------//
			// arrange the recno in the array front part
			//----------------------------------------------------------------//
			if(defPtr->status==OUTSIDE_CAMP)
			{
				unitPtr = unit_array[defPtr->unit_recno];

				//-------------- ignore this unit if it is dead --------------------//
				if(unitPtr->is_unit_dead())
					continue;

				if(!unitPtr->in_auto_defense_mode())
					continue; // the unit is ordered by the player to do other thing, so cannot control it afterwards

				//--------------- the unit is in defense mode ----------------//
				defPtr2->unit_recno = defPtr->unit_recno;
				defPtr2->status = OUTSIDE_CAMP;
				j++;
				defPtr2++;
			}
		}

		err_when(defPtr2 + (MAX_WORKER-j+1) > defense_array + MAX_WORKER + 1);
		memset(defPtr2, 0, sizeof(DefenseUnit)*(MAX_WORKER-j+1));
	}
	
	set_employ_worker(0);

	//------------------------------------------------------------------//
	// the unit outside the camp should be in defense mode and ready to
	// attack new target
	//------------------------------------------------------------------//
	for(i=0, defPtr=defense_array; i<j; i++, defPtr++)
	{
		//------------------------------------------------------------------//
		// order those unit outside the camp to attack the target
		//------------------------------------------------------------------//
		unitPtr = unit_array[defPtr->unit_recno];
		defense_outside_camp(defPtr->unit_recno, targetRecno);
		unitPtr->action_misc = ACTION_MISC_DEFENSE_CAMP_RECNO;
		unitPtr->action_misc_para = firm_recno;
	}

	int mobilizePos = 0;
	//### begin alex 13/10 ###//
	//for(; i<MAX_WORKER && worker_count; i++, defPtr++)
	for(; i<MAX_WORKER && mobilizePos<worker_count; i++, defPtr++)
	//#### end alex 13/10 ####//
	{
		err_when(mobilizePos >= worker_count);

		//------------------------------------------------------------------//
		// order those soldier inside the firm to move to target for attacking
		// keep those unable to attack inside the firm
		//------------------------------------------------------------------//
		//### begin alex 13/10 ###//
		//if(worker_array[mobilizePos].unit_id==UNIT_EXPLOSIVE_CART)
		if(worker_array[mobilizePos].unit_id==UNIT_EXPLOSIVE_CART ||
			(useRangeAttack && worker_array[mobilizePos].max_attack_range()==1))
		//#### end alex 13/10 ####//
		{
			mobilizePos++;
			continue;
		}
				
		unitRecno = mobilize_worker(mobilizePos+1, COMMAND_AUTO);
		//unitRecno = mobilize_worker(1, COMMAND_AUTO);        // always record 1 as the workers info are moved forward from the back to the front
		if(!unitRecno)
			break;
		
		Unit* unitPtr = unit_array[unitRecno];
		unitPtr->team_id = unit_array.cur_team_id;   // define it as a team
		unitPtr->action_misc = ACTION_MISC_DEFENSE_CAMP_RECNO;
		unitPtr->action_misc_para = firm_recno; // store the firm_recno for going back camp

		defense_inside_camp(unitRecno, targetRecno);
		defPtr->unit_recno = unitRecno;
		defPtr->status = OUTSIDE_CAMP;
	}

	/*if(overseer_recno>0)
	{
		//------------------------------------------------------------------//
		// order those overseer inside the firm to move to target for attacking
		//------------------------------------------------------------------//
		unitPtr = unit_array[overseer_recno];
		assign_overseer(0);
		unitPtr->team_id = unit_array.cur_team_id;   // define it as a team
		unitPtr->action_misc = ACTION_MISC_DEFENSE_CAMP_RECNO;
		unitPtr->action_misc_para = firm_recno; // store the firm_recno for going back camp

		defense_inside_camp(unitPtr->sprite_recno, targetRecno);
		defPtr->unit_recno = unitPtr->sprite_recno;
		defPtr->status = OUTSIDE_CAMP;
	}*/

	unit_array.cur_team_id++;
}
//----------- End of function FirmCamp::defense -----------//


//--------- Begin of function FirmCamp::defense_inside_camp ---------//
void FirmCamp::defense_inside_camp(short unitRecno, short targetRecno)
{
	Unit *unitPtr = unit_array[unitRecno];
	unitPtr->defense_attack_unit(targetRecno);
	//	err_when(unitPtr->action_mode2!=ACTION_AUTO_DEFENSE_ATTACK_TARGET);
	
	if(unitPtr->action_mode==ACTION_STOP && !unitPtr->action_para && unitPtr->action_x_loc==-1 && unitPtr->action_y_loc==-1)
		unitPtr->defense_detect_target();
}
//----------- End of function FirmCamp::defense_inside_camp -----------//


//--------- Begin of function FirmCamp::defense_outside_camp ---------//
void FirmCamp::defense_outside_camp(short unitRecno, short targetRecno)
{
	Unit *unitPtr = unit_array[unitRecno];
	
	if(unitPtr->action_mode2==ACTION_AUTO_DEFENSE_DETECT_TARGET || unitPtr->action_mode2==ACTION_AUTO_DEFENSE_BACK_CAMP ||
		(unitPtr->action_mode2==ACTION_AUTO_DEFENSE_ATTACK_TARGET && unitPtr->cur_action==SPRITE_IDLE))
	{
		//----------------- attack new target now -------------------//
		unitPtr->defense_attack_unit(targetRecno);
		err_when(unitPtr->action_mode2!=ACTION_AUTO_DEFENSE_ATTACK_TARGET && unitPtr->action_mode2!=ACTION_AUTO_DEFENSE_DETECT_TARGET);

		if(unitPtr->action_mode==ACTION_STOP && !unitPtr->action_para && unitPtr->action_x_loc==-1 && unitPtr->action_y_loc==-1)
			unitPtr->defense_detect_target();
	}
}
//----------- End of function FirmCamp::defense_outside_camp -----------//


//--------- Begin of function FirmCamp::set_employ_worker ---------//
void FirmCamp::set_employ_worker(char flag)
{
	employ_new_worker = flag;
	
	if(!flag)
		ai_status = CAMP_IN_DEFENSE;
	else
		ai_status = FIRM_WITHOUT_ACTION;
/*
	//------- a button should exist for accept new worker or not ---------//
	//-*********** codes here **********-//
	Town *townPtr;
	for(int i=0; i<linked_town_count; i++)
	{
		err_when(!linked_town_array[i] || town_array.is_deleted(linked_town_array[i]));
		townPtr = town_array[linked_town_array[i]];

		if(nation_recno == townPtr->nation_recno)
			toggle_town_link(i+1, employ_new_worker, COMMAND_AUTO); // enable links if employ_new_worker is true, otherwise disable
	}
*/
}
//----------- End of function FirmCamp::set_employ_worker -----------//


//--------- Begin of function FirmCamp::update_defense_unit ---------//
void FirmCamp::update_defense_unit(short unitRecno)
{
	DefenseUnit *defPtr = defense_array;
	int allInCamp = 1;
	int found=0;

	for(int i=0; i<=MAX_WORKER; i++, defPtr++)
	{
		if(!defPtr->unit_recno)
			continue; // empty slot

		if(unit_array.is_deleted(defPtr->unit_recno))
		{
			defPtr->unit_recno = 0;
			defPtr->status = INSIDE_CAMP;
			continue;
		}

		if(defPtr->unit_recno==unitRecno)
		{
			defPtr->unit_recno = 0;
			defPtr->status = INSIDE_CAMP;
			Unit *unitPtr = unit_array[unitRecno];
			unitPtr->stop2();
			unitPtr->reset_action_misc_para();
			err_when(unitPtr->in_auto_defense_mode());
			found++;
		}
		else
			allInCamp = 0; // some units are still outside camp
	}

	if(allInCamp)
	{
		set_employ_worker(1);
		memset(defense_array, 0, sizeof(DefenseUnit)*(MAX_WORKER+1));
	}

//	err_when(!found);				//**BUGHERE
}
//----------- End of function FirmCamp::update_defense_unit -----------//


//-------- Begin of function FirmCamp::is_worker_full ------//
//
int FirmCamp::is_worker_full()
{
	return worker_count + patrol_unit_count + coming_unit_count >= MAX_WORKER;
}
//----------- End of function FirmCamp::is_worker_full ---------//


//--------- Begin of function FirmCamp::sort_soldier ---------//
//
// Sort units in camp by their leaderhip.
//
void FirmCamp::sort_soldier()
{
	if( worker_count > 1 )
	{
		//--- prepare worker_id_array[] for later preserving the currently worker selected ---//

		short worker_id_array[MAX_WORKER];

		int i;
		for( i=0 ; i<worker_count ; i++ )
			worker_id_array[i] = i+1;

		cur_firm_ptr = this;

		err_when( selected_worker_id < 0 );
		err_when( selected_worker_id > worker_count );

		qsort( worker_id_array, worker_count, sizeof(short), sort_soldier_id_function );
		qsort( worker_array, worker_count, sizeof(Worker), sort_soldier_function );

		//---- scan worker_id_array[] for preserving the currently worker selected ---//

		if( selected_worker_id )
		{
			for( i=0 ; i<worker_count ; i++ )
			{
				if( worker_id_array[i] == selected_worker_id )
				{
					selected_worker_id = i+1;
					break;
				}
			}

			err_when( i==worker_count );
		}
	}
}
//----------- End of function FirmCamp::sort_soldier -----------//


//--------- Begin of function sort_soldier_function ---------//
//
static int sort_soldier_function( const void *a, const void *b )
{
	return ((Worker*)b)->skill_level - ((Worker*)a)->skill_level;
}
//----------- End of function sort_soldier_function -----------//


//--------- Begin of function sort_soldier_id_function ---------//
//
static int sort_soldier_id_function( const void *a, const void *b )
{
	int workerId1 = *((short*)a);
	int workerId2 = *((short*)b);

	return cur_firm_ptr->worker_array[workerId2-1].skill_level -
			 cur_firm_ptr->worker_array[workerId1-1].skill_level;
}
//----------- End of function sort_soldier_id_function -----------//


#ifdef DEBUG

//----------- Begin of static function disp_debug_info -----------//

static void disp_debug_info(FirmCamp* firmPtr, int refreshFlag)
{
	if( refreshFlag == INFO_REPAINT )
		vga.d3_panel_up( INFO_X1, INFO_Y2-40, INFO_X2, INFO_Y2 );

	int x=INFO_X1+3, y=INFO_Y2-37, x2=x+120;

	font_san.field( x, y   , "patrol unit count", x2, firmPtr->patrol_unit_count, 1, INFO_X2, refreshFlag);
	font_san.field( x, y+16, "coming unit count", x2, firmPtr->coming_unit_count, 1, INFO_X2, refreshFlag);

	font_san.put( x+180, y, firmPtr->firm_recno );
}
//----------- End of static function disp_debug_info -----------//

#endif
