/*
* 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 .
*
*/
//Filename : OUNITAI.CPP
//Description : Object Unit AI
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef NO_DEBUG_UNIT
#undef err_when
#undef err_here
#undef err_if
#undef err_else
#undef err_now
#define err_when(cond)
#define err_here()
#define err_if(cond)
#define err_else
#define err_now(msg)
#undef DEBUG
#endif
//--------- Begin of function Unit::process_ai --------//
//
// [int] forceExecute - whether force execute all AI functions
// without checking day interavals.
// (default: 0)
//
void Unit::process_ai()
{
err_when( !nation_recno );
//-*********** simulate aat ************-//
#ifdef DEBUG
if(debug_sim_game_type)
return;
#endif
//-*********** simulate aat ************-//
//------ the aggressive_mode of AI units is always 1 ------//
aggressive_mode = 1;
//------- handle Seek Path failures ------//
if( ai_handle_seek_path_fail() )
return;
//--- if it's a spy from other nation, don't control it ---//
if( spy_recno && true_nation_recno() != nation_recno )
{
//--- a random chance of the AI catching the spy and resign it ---//
if( is_visible() && m.random(365 * FRAMES_PER_DAY)==0 ) // if the unit stay outside for one year, it will get caught
{
stop2();
resign(COMMAND_AI);
return;
}
if( !spy_array[spy_recno]->notify_cloaked_nation_flag ) // if notify_cloaked_nation_flag is 1, the nation will take it as its own spies
return;
}
//----- think about rewarding this unit -----//
if( race_id && rank_id != RANK_KING &&
info.game_date%5 == sprite_recno%5 )
{
think_reward();
}
//-----------------------------------------//
if( !is_visible() )
return;
//--- if the unit has stopped, but ai_action_id hasn't been reset ---//
if( cur_action==SPRITE_IDLE && action_mode==ACTION_STOP &&
action_mode2==ACTION_STOP && ai_action_id )
{
nation_array[nation_recno]->action_failure(ai_action_id, sprite_recno);
err_when( ai_action_id ); // it should have been reset
}
//---- King flees under attack or surrounded by enemy ---//
if( race_id && rank_id==RANK_KING )
{
if( think_king_flee() )
return;
}
//---- General flees under attack or surrounded by enemy ---//
if( race_id && rank_id==RANK_GENERAL &&
info.game_date%7 == sprite_recno%7 )
{
if( think_general_flee() )
return;
}
//-- let Unit::next_day() process it process original_action_mode --//
if( original_action_mode )
return;
//------ if the unit is not stop right now ------//
if( !is_ai_all_stop() )
{
think_stop_chase();
return;
}
//-----------------------------------------//
if( mobile_type==UNIT_LAND )
{
if( ai_escape_fire() )
return;
}
//---------- if this is your spy --------//
if( spy_recno && true_nation_recno()==nation_recno )
think_spy_action();
//------ if this unit is from a camp --------//
if( home_camp_firm_recno )
{
Firm* firmCamp = firm_array[home_camp_firm_recno];
int rc;
if( rank_id == RANK_SOLDIER )
rc = firmCamp->worker_count < MAX_WORKER;
else
rc = !firmCamp->overseer_recno;
if( rc )
{
if( return_camp() )
return;
}
home_camp_firm_recno = 0; // the camp is already occupied by somebody
}
//----------------------------------------//
if( race_id && rank_id==RANK_KING )
{
think_king_action();
}
else if( race_id && rank_id==RANK_GENERAL )
{
think_general_action();
}
else
{
if( unit_res[unit_id]->unit_class == UNIT_CLASS_WEAPON )
{
if( info.game_date%15 == sprite_recno%15 ) // don't call too often as the action may fail and it takes a while to call the function each time
{
think_weapon_action(); //-- ships AI are called in UnitMarine --//
}
}
else if( race_id )
{
//--- if previous attempts for new action failed, don't call think_normal_human_action() so frequently then ---//
if( ai_no_suitable_action )
{
if( info.game_date%15 != sprite_recno%15 ) // don't call too often as the action may fail and it takes a while to call the function each time
return;
}
//---------------------------------//
if( !think_normal_human_action() )
{
ai_no_suitable_action = 1; // set this flag so think_normal_human_action() won't be called continously
if( !leader_unit_recno ) // only when the unit is not led by a commander
{
resign(COMMAND_AI);
}
else
{
ai_move_to_nearby_town();
}
}
}
}
}
//---------- End of function Unit::process_ai --------//
//--------- Begin of function Unit::think_stop_chase --------//
int Unit::think_stop_chase()
{
//-----------------------------------------------------//
//
// Stop the chase if the target is being far away from
// its original attacking location.
//
//-----------------------------------------------------//
if( !(action_mode==ACTION_ATTACK_UNIT && ai_original_target_x_loc>=0) )
return 0;
if( unit_array.is_deleted(action_para) )
{
stop2();
return 1;
}
Unit* targetUnit = unit_array[action_para];
if( !targetUnit->is_visible() )
{
stop2();
return 1;
}
//----------------------------------------//
int aiChaseDistance = 10 + nation_array[nation_recno]->pref_military_courage/20; // chase distance: 10 to 15
int curDistance = m.points_distance( targetUnit->next_x_loc(), targetUnit->next_y_loc(),
ai_original_target_x_loc, ai_original_target_y_loc );
if( curDistance <= aiChaseDistance )
return 0;
//--------- stop the unit ----------------//
stop2();
//--- if this unit leads a troop, stop the action of all troop members as well ---//
int leaderUnitRecno;
if( leader_unit_recno )
leaderUnitRecno = leader_unit_recno;
else
leaderUnitRecno = sprite_recno;
TeamInfo* teamInfo = unit_array[leaderUnitRecno]->team_info;
if( teamInfo )
{
for( int i=teamInfo->member_count-1 ; i>=0 ; i-- )
{
int unitRecno = teamInfo->member_unit_array[i];
if( unit_array.is_deleted(unitRecno) )
continue;
unit_array[unitRecno]->stop2();
}
}
return 1;
}
//---------- End of function Unit::think_stop_chase --------//
//--------- Begin of function Unit::is_ai_all_stop --------//
int Unit::is_ai_all_stop()
{
return cur_action==SPRITE_IDLE && action_mode==ACTION_STOP &&
action_mode2==ACTION_STOP && !ai_action_id;
}
//---------- End of function Unit::is_ai_all_stop --------//
//--------- Begin of function Unit::ai_move_to_nearby_town --------//
void Unit::ai_move_to_nearby_town()
{
//---- look for towns to assign to -----//
Nation* ownNation = nation_array[nation_recno];
Town *townPtr, *bestTown=NULL;
int regionId = world.get_region_id( next_x_loc(), next_y_loc() );
int curDistance, curRating, bestRating=0;
int curXLoc = next_x_loc(), curYLoc = next_y_loc();
for( int i=town_array.size() ; i>0 ; i-- ) // can't use ai_town_array[] because this function will be called by Unit::betray() when a unit defected to the player's kingdom
{
if( town_array.is_deleted(i) )
continue;
townPtr = town_array[i];
if( townPtr->nation_recno != nation_recno )
continue;
if( townPtr->region_id != regionId )
continue;
//-------------------------------------//
curDistance = m.points_distance(curXLoc, curYLoc, townPtr->center_x, townPtr->center_y );
if( curDistance < 10 ) // no need to move if the unit is already close enough to the town.
return;
curRating = 100 - 100 * curDistance / MAX_WORLD_X_LOC;
curRating += townPtr->population;
//-------------------------------------//
if( curRating > bestRating )
{
bestRating = curRating;
bestTown = townPtr;
}
}
if( bestTown )
move_to_town_surround(bestTown->loc_x1, bestTown->loc_y1, sprite_info->loc_width, sprite_info->loc_height);
}
//---------- End of function Unit::ai_move_to_nearby_town --------//
//--------- Begin of function Unit::ai_escape_fire --------//
//
// Move away if the unit currently stands on a burning ground.
//
int Unit::ai_escape_fire()
{
if(cur_action!=SPRITE_IDLE)
return 0;
if(mobile_type!=UNIT_LAND)
return 0;
Location *locPtr = world.get_loc(next_x_loc(), next_y_loc());
if( !locPtr->fire_str() )
return 0;
//--------------------------------------------//
int checkLimit = 400; // checking for 400 location
int xShift, yShift, checkXLoc, checkYLoc;
int curXLoc = next_x_loc();
int curYLoc = next_y_loc();
for(int i=2; i=MAX_WORLD_X_LOC || checkYLoc<0 || checkYLoc>=MAX_WORLD_Y_LOC)
continue;
if(!locPtr->can_move(mobile_type))
continue;
locPtr = world.get_loc(checkXLoc, checkYLoc);
if( locPtr->fire_str()==0 ) // move to a safe place now
{
move_to(checkXLoc, checkYLoc);
return 1;
}
}
return 0;
}
//---------- End of function Unit::ai_escape_fire --------//
//--------- Begin of function Unit::think_spy_action --------//
void Unit::think_spy_action()
{
ai_move_to_nearby_town(); // just move it to one of our towns
}
//---------- End of function Unit::think_spy_action --------//
//--------- Begin of function Unit::think_king_action --------//
int Unit::think_king_action()
{
return think_leader_action();
}
//---------- End of function Unit::think_king_action --------//
//--------- Begin of function Unit::think_general_action --------//
int Unit::think_general_action()
{
if( think_leader_action() )
return 1;
//--- if the general is not assigned to a camp due to its low competency ----//
Nation* ownNation = nation_array[nation_recno];
int rc = 0;
if( team_info->member_count <= 1 )
{
rc = 1;
}
//--- if the skill of the general and the number of soldiers he commands is not large enough to justify building a new camp ---//
else if( skill.skill_level + team_info->member_count*4
< 40 + ownNation->pref_keep_general/5 ) // 40 to 60
{
rc = 1;
}
//-- think about splitting the team and assign them into other forts --//
else if( ownNation->ai_has_too_many_camp() )
{
rc = 1;
}
//--------- demote the general to soldier and disband the troop -------//
if( rc )
{
set_rank(RANK_SOLDIER);
return think_normal_human_action();
}
return 0;
}
//---------- End of function Unit::think_general_action --------//
//--------- Begin of function Unit::think_leader_action --------//
//
// Think about the action of a leader (either a general or a king).
//
int Unit::think_leader_action()
{
Nation* nationPtr = nation_array[nation_recno];
FirmCamp *firmCamp, *bestCamp=NULL;
int curRating, bestRating=10, delActionRecno=0;
int curXLoc = next_x_loc(), curYLoc = next_y_loc();
int curRegionId = world.get_region_id( curXLoc, curYLoc );
if( rank_id == RANK_KING ) // if this unit is the king, always assign it to a camp regardless of whether the king is a better commander than the existing one
bestRating = -1000;
err_when( skill.skill_id != SKILL_LEADING );
//----- think about which camp to move to -----//
for( int i=nationPtr->ai_camp_count-1 ; i>=0 ; i-- )
{
firmCamp = (FirmCamp*) firm_array[ nationPtr->ai_camp_array[i] ];
if( firmCamp->region_id != curRegionId )
continue;
//--- if the commander of this camp is the king, never replace him ---//
if( firmCamp->overseer_recno == nationPtr->king_unit_recno )
continue;
//-------------------------------------//
int curLeadership = firmCamp->cur_commander_leadership();
int newLeadership = firmCamp->new_commander_leadership(race_id, skill.skill_level);
curRating = newLeadership - curLeadership;
//-------------------------------------//
if( curRating > bestRating )
{
//--- if there is already somebody being assigned to it ---//
int actionRecno=0;
if( rank_id != RANK_KING ) // don't check this if the current unit is the king
{
actionRecno = nationPtr->is_action_exist(firmCamp->loc_x1, firmCamp->loc_y1,
-1, -1, ACTION_AI_ASSIGN_OVERSEER, FIRM_CAMP);
if( actionRecno && nationPtr->get_action(actionRecno)->processing_instance_count )
continue;
}
bestRating = curRating;
bestCamp = firmCamp;
delActionRecno = actionRecno;
}
}
if( !bestCamp )
return 0;
//----- delete an unprocessed queued action if there is any ----//
if( delActionRecno )
nationPtr->del_action(delActionRecno);
//--------- move to the camp now ---------//
//-- if there is room in the camp to host all soldiers led by this general --//
if( team_info->member_count-1 <= MAX_WORKER-bestCamp->worker_count )
{
validate_team();
unit_array.assign( bestCamp->loc_x1, bestCamp->loc_y1, 0, COMMAND_AI,
team_info->member_unit_array, team_info->member_count );
return 1;
}
else //--- otherwise assign the general only ---//
{
return nationPtr->add_action(bestCamp->loc_x1, bestCamp->loc_y1, -1, -1, ACTION_AI_ASSIGN_OVERSEER, FIRM_CAMP, 1, sprite_recno);
}
return 0;
}
//---------- End of function Unit::think_leader_action --------//
//--------- Begin of function Unit::think_normal_human_action --------//
int Unit::think_normal_human_action()
{
if( home_camp_firm_recno )
return 0;
if( leader_unit_recno &&
unit_array[leader_unit_recno]->is_visible() ) // if the unit is led by a commander, let the commander makes the decision. If the leader has been assigned to a firm, don't consider it as a leader anymore
{
return 0;
}
err_when( !race_id );
err_when( !nation_recno );
//---- think about assign the unit to a firm that needs workers ----//
Nation* ownNation = nation_array[nation_recno];
Firm *firmPtr, *bestFirm=NULL;
int regionId = world.get_region_id( next_x_loc(), next_y_loc() );
int skillId = skill.skill_id;
int skillLevel = skill.skill_level;
int i, curRating, bestRating=0;
int curXLoc = next_x_loc(), curYLoc = next_y_loc();
if( skill.skill_id )
{
for( i=firm_array.size() ; i>0 ; i-- )
{
if( firm_array.is_deleted(i) )
continue;
firmPtr = firm_array[i];
if( firmPtr->nation_recno != nation_recno )
continue;
if( firmPtr->region_id != regionId )
continue;
curRating = 0;
if( skill.skill_id == SKILL_CONSTRUCTION ) // if this is a construction worker
{
if( firmPtr->builder_recno ) // assign the construction worker to this firm as an residental builder
continue;
}
else
{
if( !firmPtr->worker_array ||
firmPtr->firm_skill_id != skillId )
{
continue;
}
//----- if the firm is full of worker ------//
if( firmPtr->is_worker_full() )
{
//---- get the lowest skill worker of the firm -----//
Worker* workerPtr = firmPtr->worker_array;
int minSkill=100;
for( int j=0 ; jworker_count ; j++, workerPtr++ )
{
if( workerPtr->skill_level < minSkill )
minSkill = workerPtr->skill_level;
}
//------------------------------//
if( firmPtr->majority_race() == race_id )
{
if( skill.skill_level < minSkill+10 )
continue;
}
else //-- for different race, only assign if the skill is significantly higher than the existing ones --//
{
if( skill.skill_level < minSkill+30 )
continue;
}
}
else
{
curRating += 300; // if the firm is not full, rating + 300
}
}
//-------- calculate the rating ---------//
curRating += world.distance_rating( curXLoc, curYLoc, firmPtr->center_x, firmPtr->center_y );
if( firmPtr->majority_race() == race_id )
curRating += 70;
curRating += (MAX_WORKER - firmPtr->worker_count) * 10;
//-------------------------------------//
if( curRating > bestRating )
{
bestRating = curRating;
bestFirm = firmPtr;
}
}
if( bestFirm )
{
assign(bestFirm->loc_x1, bestFirm->loc_y1);
return 1;
}
}
//---- look for towns to assign to -----//
bestRating = 0;
int hasTownInRegion=0;
Town *townPtr, *bestTown=NULL;
for( i=town_array.size() ; i>0 ; i-- ) // can't use ai_town_array[] because this function will be called by Unit::betray() when a unit defected to the player's kingdom
{
if( town_array.is_deleted(i) )
continue;
townPtr = town_array[i];
if( townPtr->nation_recno != nation_recno )
continue;
if( townPtr->region_id != regionId )
continue;
hasTownInRegion = 1;
if( townPtr->population >= MAX_TOWN_POPULATION || !townPtr->is_base_town )
continue;
//--- only assign to towns of the same race ---//
if( ownNation->pref_town_harmony > 50 )
{
if( townPtr->majority_race() != race_id )
continue;
}
//-------- calculate the rating ---------//
curRating = world.distance_rating(curXLoc, curYLoc, townPtr->center_x, townPtr->center_y );
curRating += 300 * townPtr->race_pop_array[race_id-1] / townPtr->population; // racial homogenous bonus
curRating += MAX_TOWN_POPULATION - townPtr->population;
//-------------------------------------//
if( curRating > bestRating )
{
bestRating = curRating;
bestTown = townPtr;
}
}
if( bestTown )
{
assign(bestTown->loc_x1, bestTown->loc_y1);
return 1;
}
//----- if we don't have any existing towns in this region ----//
if( !hasTownInRegion )
{
//#ifdef AMPLUS
// --- if region is too small don't consider this area, stay in the island forever --//
if( region_array[regionId]->region_stat_id == 0 )
return 0;
//#endif
//-- if we also don't have any existing camps in this region --//
if( region_array.get_region_stat(regionId)->camp_nation_count_array[nation_recno-1]==0 )
{
//---- try to build one if this unit can ----//
if( ownNation->cash > firm_res[FIRM_CAMP]->setup_cost &&
firm_res[FIRM_CAMP]->can_build(sprite_recno) &&
!leader_unit_recno ) // if this unit is commanded by a leader, let the leader build the camp
{
ai_build_camp();
}
}
else // if there is already a camp in this region, try to settle a new town next to the camp
{
ai_settle_new_town();
}
return 1; // if we don't have any town in this region, return 1, so this unit won't be resigned and so it can wait for other units to set up camps and villages later ---//
}
return 0;
}
//---------- End of function Unit::think_normal_human_action --------//
//--------- Begin of function Unit::think_weapon_action --------//
int Unit::think_weapon_action()
{
//---- first try to assign the weapon to an existing camp ----//
if( think_assign_weapon_to_camp() )
return 1;
//----- if no camp to assign, build a new one ------//
if( think_build_camp() )
return 1;
return 0;
}
//---------- End of function Unit::think_weapon_action --------//
//--------- Begin of function Unit::think_assign_weapon_to_camp --------//
int Unit::think_assign_weapon_to_camp()
{
Nation *nationPtr = nation_array[nation_recno];
FirmCamp *firmCamp, *bestCamp=NULL;
int curRating=0, bestRating=0;
int regionId = world.get_region_id( next_x_loc(), next_y_loc() );
int curXLoc = next_x_loc(), curYLoc = next_y_loc();
for( int i=0 ; iai_camp_count ; i++ )
{
firmCamp = (FirmCamp*) firm_array[ nationPtr->ai_camp_array[i] ];
if( firmCamp->region_id != regionId || firmCamp->is_worker_full() )
continue;
//-------- calculate the rating ---------//
curRating = world.distance_rating(curXLoc, curYLoc, firmCamp->center_x, firmCamp->center_y );
curRating += (MAX_WORKER - firmCamp->worker_count) * 10;
//-------------------------------------//
if( curRating > bestRating )
{
bestRating = curRating;
bestCamp = firmCamp;
}
}
//-----------------------------------//
if( bestCamp )
{
assign(bestCamp->loc_x1, bestCamp->loc_y1);
return 1;
}
return 0;
}
//---------- End of function Unit::think_assign_weapon_to_camp --------//
//--------- Begin of function Unit::think_build_camp --------//
//
// Think about building a camp next to the town which is
// closest to the unit.
//
int Unit::think_build_camp()
{
//---- select a town to build the camp ---//
Nation* ownNation = nation_array[nation_recno];
Town *townPtr, *bestTown=NULL;
int curRating=0, bestRating=0;
int regionId = world.get_region_id( next_x_loc(), next_y_loc() );
int curXLoc = next_x_loc(), curYLoc = next_y_loc();
for( int i=ownNation->ai_town_count-1 ; i>=0 ; i-- )
{
townPtr = town_array[ ownNation->ai_town_array[i] ];
if( townPtr->region_id != regionId )
continue;
if( !townPtr->is_base_town || townPtr->no_neighbor_space )
continue;
curRating = world.distance_rating(curXLoc, curYLoc, townPtr->center_x, townPtr->center_y );
if( curRating > bestRating )
{
bestRating = curRating;
bestTown = townPtr;
}
}
if( bestTown )
return bestTown->ai_build_neighbor_firm(FIRM_CAMP);
return 0;
}
//---------- End of function Unit::think_build_camp --------//
//--------- Begin of function Unit::think_reward --------//
int Unit::think_reward()
{
Nation* ownNation = nation_array[nation_recno];
//----------------------------------------------------------//
// The need to secure high loyalty on this unit is based on:
// -its skill
// -its combat level
// -soldiers commanded by this unit
//----------------------------------------------------------//
if( spy_recno && true_nation_recno() == nation_recno ) // if this is a spy of ours
{
return 0; // Spy::think_reward() will handle this.
}
int curLoyalty = loyalty;
int neededLoyalty;
//----- if this unit is on a mission ------/
if( ai_action_id )
{
neededLoyalty = UNIT_BETRAY_LOYALTY+10;
}
//----- otherwise only reward soldiers and generals ------//
else if( skill.skill_id == SKILL_LEADING )
{
//----- calculate the needed loyalty --------//
neededLoyalty = commanded_soldier_count()*5 + skill.skill_level;
if( unit_mode == UNIT_MODE_OVERSEE ) // if this unit is an overseer
{
if( loyalty < UNIT_BETRAY_LOYALTY ) // if this unit's loyalty is < betrayel level, reward immediately
{
reward(nation_recno); // reward it immediatley if it's an overseer, don't check ai_should_spend()
return 1;
}
neededLoyalty += 30;
}
neededLoyalty = max( UNIT_BETRAY_LOYALTY+10, neededLoyalty ); // 10 points above the betray loyalty level to prevent betrayal
neededLoyalty = min( 100, neededLoyalty );
}
else
{
return 0;
}
//------- if the loyalty is already high enough ------//
if( curLoyalty >= neededLoyalty )
return 0;
//---------- see how many cash & profit we have now ---------//
int rewardNeedRating = neededLoyalty - curLoyalty;
if( curLoyalty < UNIT_BETRAY_LOYALTY+5 )
rewardNeedRating += 50;
if( ownNation->ai_should_spend(rewardNeedRating) )
{
reward(nation_recno);
return 1;
}
return 0;
}
//---------- End of function Unit::think_reward --------//
//--------- Begin of function Unit::ai_leader_being_attacked --------//
//
// This function is called when the king is under attack.
//
// attackerUnitRecno - recno of the attacker unit.
//
void Unit::ai_leader_being_attacked(int attackerUnitRecno)
{
err_when( !team_info );
if( unit_array[attackerUnitRecno]->nation_recno == nation_recno ) // this can happen when the unit has just changed nation
return;
//------------------------------------//
int rc=0, callIntervalDays;
if( rank_id == RANK_KING )
{
rc = 1;
callIntervalDays = 7;
}
else if( rank_id == RANK_GENERAL )
{
rc = skill.skill_level >= 30 + (100-nation_array[nation_recno]->pref_keep_general)/2; // 30 to 80
callIntervalDays = 15; // don't call too freqently
}
if( rc )
{
if( info.game_date > team_info->ai_last_request_defense_date + callIntervalDays )
{
team_info->ai_last_request_defense_date = info.game_date;
nation_array[nation_recno]->ai_defend(attackerUnitRecno);
}
}
}
//---------- End of function Unit::ai_leader_being_attacked --------//
//--------- Begin of function Unit::think_king_flee --------//
//
// Note: we still need to keep think_king_action() because
// between these two functions, a number of things
// may be done, like returning home camp. We only
// carry out actions in this function if the king
// is in danger and urgently need to flee.
//
int Unit::think_king_flee()
{
if( force_move_flag && cur_action != SPRITE_IDLE ) // the king is already fleeing now
return 1;
//------- if the king is alone --------//
Nation* ownNation = nation_array[nation_recno];
//------------------------------------------------//
// When the king is alone and there is no assigned action OR
// when the king is injured, the king will flee
// back to its camp.
//------------------------------------------------//
if( ( team_info->member_count==0 && !ai_action_id ) ||
hit_points < 125-ownNation->pref_military_courage/4 )
{
//------------------------------------------//
//
// If the king is currently under attack, flee
// to the nearest camp with the maximum protection.
//
//------------------------------------------//
Firm *firmCamp, *bestCamp=NULL;
int curRating, bestRating=0;
int curXLoc = next_x_loc(), curYLoc = next_y_loc();
int curRegionId = world.get_region_id( curXLoc, curYLoc );
if( cur_action == SPRITE_ATTACK )
{
for( int i=ownNation->ai_camp_count-1 ; i>=0 ; i-- )
{
firmCamp = (FirmCamp*) firm_array[ ownNation->ai_camp_array[i] ];
if( firmCamp->region_id != curRegionId )
continue;
if( firmCamp->overseer_recno && rank_id!=RANK_KING ) // if there is already a commander in this camp. However if this is the king, than ingore this
continue;
curRating = world.distance_rating( curXLoc, curYLoc,
firmCamp->center_x, firmCamp->center_y );
if( curRating > bestRating )
{
bestRating = curRating;
bestCamp = firmCamp;
}
}
}
else if( home_camp_firm_recno ) // if there is a home for the king
{
bestCamp = firm_array[home_camp_firm_recno];
}
//------------------------------------//
if( bestCamp )
{
if( config.ai_aggressiveness > OPTION_LOW )
force_move_flag = 1;
assign( bestCamp->loc_x1, bestCamp->loc_y1 );
}
else // if the king is neither under attack or has a home camp, then call the standard think_leader_action()
{
think_leader_action();
}
return cur_action != SPRITE_IDLE;
}
return 0;
}
//---------- End of function Unit::think_king_flee --------//
//--------- Begin of function Unit::think_general_flee --------//
int Unit::think_general_flee()
{
if( force_move_flag && cur_action != SPRITE_IDLE ) // the general is already fleeing now
return 1;
//------- if the general is alone --------//
Nation* ownNation = nation_array[nation_recno];
//------------------------------------------------//
// When the general is alone and there is no assigned action OR
// when the general is injured, the general will flee
// back to its camp.
//------------------------------------------------//
if( ( team_info->member_count==0 && !ai_action_id ) ||
hit_points < max_hit_points * (75+ownNation->pref_military_courage/2) / 200 ) // 75 to 125 / 200
{
//------------------------------------------//
//
// If the general is currently under attack, flee
// to the nearest camp with the maximum protection.
//
//------------------------------------------//
Firm *firmCamp, *bestCamp=NULL;
int curRating, bestRating=0;
int curXLoc = next_x_loc(), curYLoc = next_y_loc();
int curRegionId = world.get_region_id( curXLoc, curYLoc );
if( cur_action == SPRITE_ATTACK )
{
for( int i=ownNation->ai_camp_count-1 ; i>=0 ; i-- )
{
firmCamp = (FirmCamp*) firm_array[ ownNation->ai_camp_array[i] ];
if( firmCamp->region_id != curRegionId )
continue;
curRating = world.distance_rating( curXLoc, curYLoc,
firmCamp->center_x, firmCamp->center_y );
if( curRating > bestRating )
{
bestRating = curRating;
bestCamp = firmCamp;
}
}
}
else if( home_camp_firm_recno ) // if there is a home for the general
{
bestCamp = firm_array[home_camp_firm_recno];
}
//------------------------------------//
if( bestCamp )
{
if( bestCamp->overseer_recno ) // if there is already an overseer there, just move close to the camp for protection
{
if( config.ai_aggressiveness > OPTION_LOW )
force_move_flag = 1;
move_to( bestCamp->loc_x1, bestCamp->loc_y1 );
}
else
assign( bestCamp->loc_x1, bestCamp->loc_y1 );
}
else // if the general is neither under attack or has a home camp, then call the standard think_leader_action()
{
think_leader_action();
}
return cur_action != SPRITE_IDLE;
}
return 0;
}
//---------- End of function Unit::think_general_flee --------//
//--------- Begin of function Unit::ai_build_camp --------//
//
// Order this unit to build a camp in its region.
//
int Unit::ai_build_camp()
{
//--- to prevent building more than one camp at the same time ---//
int curRegionId = region_id();
Nation* ownNation = nation_array[nation_recno];
if( ownNation->is_action_exist( ACTION_AI_BUILD_FIRM, FIRM_CAMP, curRegionId ) )
return 0;
//------- locate a place for the camp --------//
FirmInfo* firmInfo = firm_res[FIRM_CAMP];
int xLoc=0, yLoc=0;
char teraMask = UnitRes::mobile_type_to_mask(UNIT_LAND);
if( world.locate_space_random(xLoc, yLoc, MAX_WORLD_X_LOC-1,
MAX_WORLD_Y_LOC-1, firmInfo->loc_width+2, firmInfo->loc_height+2, // leave at least one location space around the building
MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC, curRegionId, 1, teraMask) )
{
return ownNation->add_action( xLoc, yLoc, -1, -1,
ACTION_AI_BUILD_FIRM, FIRM_CAMP, 1, sprite_recno );
}
return 0;
}
//---------- End of function Unit::ai_build_camp --------//
//--------- Begin of function Unit::ai_settle_new_town --------//
//
// Settle a new village next to one of the camps in this region.
//
int Unit::ai_settle_new_town()
{
//----- locate a suitable camp for the new town to settle next to ----//
Nation* ownNation = nation_array[nation_recno];
FirmCamp* firmCamp, *bestCamp=NULL;
int curRegionId = region_id();
int curRating, bestRating=0;
for( int i=ownNation->ai_camp_count-1 ; i>=0 ; i-- )
{
firmCamp = (FirmCamp*) firm_array[ ownNation->ai_camp_array[i] ];
if( firmCamp->region_id != curRegionId )
continue;
curRating = firmCamp->total_combat_level();
if( curRating > bestRating )
{
bestRating = curRating;
bestCamp = firmCamp;
}
}
if( !bestCamp )
return 0;
//--------- settle a new town now ---------//
int xLoc=bestCamp->loc_x1;
int yLoc=bestCamp->loc_y1;
if( world.locate_space(xLoc, yLoc, bestCamp->loc_x2, bestCamp->loc_y2,
STD_TOWN_LOC_WIDTH, STD_TOWN_LOC_HEIGHT,
UNIT_LAND, curRegionId, 1 ) ) // 1-build flag
{
settle( xLoc, yLoc );
return 1;
}
return 0;
}
//---------- End of function Unit::ai_settle_new_town --------//
//--------- Begin of function Unit::ai_handle_seek_path_fail --------//
//
// This function is used for handling cases when AI units are not
// able to seek a path successfully.
//
int Unit::ai_handle_seek_path_fail()
{
if( seek_path_fail_count < 5 ) // wait unit it has failed many times
return 0;
//----- try to move to a new location -----//
if( seek_path_fail_count==5 )
{
stop2(); // stop the unit and think for new action
return 0;
}
//--- if the seek path has failed too many times, resign the unit ---//
int resignFlag = 0;
if( rank_id == RANK_SOLDIER && !leader_unit_recno )
{
if( seek_path_fail_count>=7 )
resignFlag = 1;
}
else if( rank_id == RANK_GENERAL )
{
if( seek_path_fail_count >= 7+skill.skill_level/10 )
resignFlag = 1;
}
if( resignFlag && is_visible() )
{
resign(COMMAND_AI);
return 1;
}
else
return 0;
}
//---------- End of function Unit::ai_handle_seek_path_fail --------//