/*
* 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 : OF_CAMP2.CPP
//Description : Firm Military Camp - AI functions
#include
#include
#include
#include
#include
#include
#include
//--------- Begin of function FirmCamp::init_derived ---------//
void FirmCamp::init_derived()
{
patrol_unit_count = 0;
coming_unit_count = 0;
ai_capture_town_recno = 0;
ai_recruiting_soldier = 1;
}
//----------- End of function FirmCamp::init_derived -----------//
//--------- Begin of function FirmCamp::process_ai ---------//
void FirmCamp::process_ai()
{
if( info.game_date%15==firm_recno%15 ) // do not call too often as the penalty of accumulation is 10 days
think_use_cash_to_capture();
//------- assign overseer and workers -------//
if( info.game_date%15==firm_recno%15 ) // do not call too often because when an AI action is queued, it will take a while to carry it out
think_recruit();
//---- if this firm is currently trying to capture a town ----//
if( ai_capture_town_recno )
{
should_close_flag = 0; // disable should_close_flag when trying to capture a firm, should_close_flag is set in process_common_ai()
if( nation_array[nation_recno]->attack_camp_count==0 && // only when attack_camp_count==0, the attack mission is complete
patrol_unit_count==0 )
{
ai_capture_town_recno = 0;
defense_flag = 1; // turn it on again after the capturing plan is finished
return;
}
// process_ai_capturing();
return;
}
//--- if the firm is empty and should be closed, sell/destruct it now ---//
if( should_close_flag )
{
if( worker_count==0 )
{
ai_del_firm();
return;
}
}
//----- think about assigning a better commander -----//
if( info.game_date%30==firm_recno%30 )
think_assign_better_commander();
//----- think about changing links to foreign town -----//
if( info.game_date%30==firm_recno%30 )
think_change_town_link();
//------ think about attacking enemies nearby -------//
int checkInterval = 13 - nation_array[nation_recno]->pref_military_development/10;
if( info.game_date%checkInterval == firm_recno%checkInterval )
think_attack_nearby_enemy();
//------ think about capturing independent town -------//
static short interval_days_array[] = { 60, 30, 20, 10 };
int intervalDays = interval_days_array[config.ai_aggressiveness-1];
if( info.game_date%intervalDays == firm_recno%intervalDays ) // do not call too often because when an AI action is queued, it will take a while to carry it out
think_capture();
//---------------------------------------//
if( info.game_date%30 == firm_recno%30 )
{
ai_reset_defense_mode(); // reset defense mode if all soldiers are dead
}
}
//----------- End of function FirmCamp::process_ai -----------//
//--------- Begin of function FirmCamp::ai_reset_defense_mode ---------//
void FirmCamp::ai_reset_defense_mode()
{
if(ai_status!=CAMP_IN_DEFENSE)
return;
//------------------------------------------------------------//
// reset defense mode if all soldiers are dead
//------------------------------------------------------------//
DefenseUnit *defPtr = defense_array;
Unit *unitPtr;
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))
continue;
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) // is a soldier of this camp
found++;
}
if(!found)
{
set_employ_worker(1); // all soldiers have died, reset defense mode to employ new workers
memset(defense_array, 0, sizeof(DefenseUnit)*(MAX_WORKER+1));
}
}
//----------- End of function FirmCamp::ai_reset_defense_mode -----------//
//--------- Begin of function FirmCamp::process_ai_capturing ---------//
//
// This function is called when the AI is in the process of
// attacking and capturing the independent town this camp is
// linked to.
//
void FirmCamp::process_ai_capturing()
{
err_when( patrol_unit_count <= 0 ); // this function shouldn't be called at all if patrol_unit_count==0
err_when( patrol_unit_count>9 );
if( !ai_capture_town_recno )
return;
if( think_capture_return() ) // if the capturing units should now return to their base.
return;
//--- there are still town defender out there, order idle units to attack them ---//
Unit* unitPtr;
Town* townPtr = town_array[ai_capture_town_recno];
if( townPtr->town_defender_count > 0 )
{
for( int i=patrol_unit_count ; i>0 ; i-- )
{
unitPtr = unit_array[ patrol_unit_array[i-1] ];
if( unitPtr->cur_action == SPRITE_IDLE )
ai_attack_town_defender(unitPtr);
}
}
//--- if the town is still not captured but all mobile town defenders are destroyed, attack the town again ---//
if( townPtr->town_defender_count == 0 )
{
//----- have one of the units attack the town ----//
short unitArray[1];
err_when( patrol_unit_count<=0 );
unitArray[0] = patrol_unit_array[ m.random(patrol_unit_count) ];
err_when( unit_array.is_deleted(unitArray[0]) );
if( townPtr->nation_recno )
nation_array[nation_recno]->set_relation_should_attack( townPtr->nation_recno, 1, COMMAND_AI );
// ##### patch begin Gilbert 5/8 ######//
unit_array.attack(townPtr->loc_x1, townPtr->loc_y1, 0, unitArray, 1, COMMAND_AI, 0);
// ##### patch end Gilbert 5/8 ######//
}
}
//----------- End of function FirmCamp::process_ai_capturing -----------//
//------ Begin of function FirmCamp::ai_attack_town_defender ------//
//
void FirmCamp::ai_attack_town_defender(Unit* attackerUnit)
{
int shouldAttackUnit=0;
if( attackerUnit->cur_action == SPRITE_IDLE )
shouldAttackUnit = 1;
else if( attackerUnit->cur_action == SPRITE_ATTACK )
{
//--- if this unit is currently attacking the town, ask it to attack a defender unit ---//
Town* townPtr = town_array[ai_capture_town_recno];
if( attackerUnit->action_x_loc==townPtr->loc_x1 && attackerUnit->action_y_loc==townPtr->loc_y1 )
shouldAttackUnit = 1;
}
if( !shouldAttackUnit )
return;
//---- if there are still town defenders out there ---//
int unitCount = unit_array.size();
int unitRecno = m.random(unitCount)+1; // scan randomly
short unitArray[1];
Unit* targetUnit;
for( int i=0 ; i unitCount )
unitRecno = 1;
if( unit_array.is_deleted(unitRecno) )
continue;
targetUnit = unit_array[unitRecno];
if( targetUnit->unit_mode == UNIT_MODE_DEFEND_TOWN &&
targetUnit->unit_mode_para == ai_capture_town_recno )
{
unitArray[0] = attackerUnit->sprite_recno;
if( targetUnit->nation_recno )
nation_array[nation_recno]->set_relation_should_attack( targetUnit->nation_recno, 1, COMMAND_AI );
// ##### patch begin Gilbert 5/8 ######//
unit_array.attack( targetUnit->next_x_loc(), targetUnit->next_y_loc(),
0, unitArray, 1, COMMAND_AI, targetUnit->sprite_recno );
// ##### patch end Gilbert 5/8 ######//
break;
}
}
}
//-------- End of function FirmCamp::ai_attack_town_defender ------//
//--------- Begin of function FirmCamp::think_recruit ---------//
//
// Think about recruiting an overseer and soliders to this base.
//
void FirmCamp::think_recruit()
{
if( patrol_unit_count ) // if there are units of this camp patrolling outside
return;
Nation* nationPtr = nation_array[nation_recno];
ai_recruiting_soldier = 1; // the AI is currently trying to recruit soldiers
//---- if there are currently units coming to this firm ----//
if( coming_unit_count > 0 )
{
Unit* unitPtr;
for( int i=0 ; ination_recno == nation_recno &&
unitPtr->action_mode == ACTION_ASSIGN_TO_FIRM )
{
//--- if so, do not do anything unit they are all done ---//
return;
}
}
coming_unit_count = 0;
}
//-- if this camp is empty, think about move a whole troop from a useless camp (should_ai_close()==1)
if( !overseer_recno && worker_count==0 &&
nationPtr->firm_should_close_array[FIRM_CAMP-1] > 0 )
{
FirmCamp *firmCamp, *bestCampPtr=NULL;
int bestRating=0, curRating;
//--- see if there are any useless camps around and pick the most suitable one ---//
for( int i=nationPtr->ai_camp_count-1 ; i>=0 ; i-- )
{
firmCamp = (FirmCamp*) firm_array[ nationPtr->ai_camp_array[i] ];
err_when( firmCamp->firm_id != FIRM_CAMP );
if( firmCamp->region_id != region_id )
continue;
if( firmCamp->should_close_flag &&
(firmCamp->overseer_recno || firmCamp->worker_count>0) )
{
curRating = 100 - m.points_distance( center_x, center_y,
firmCamp->center_x, firmCamp->center_y );
if( curRating > bestRating )
{
bestRating = curRating;
bestCampPtr = firmCamp;
}
}
}
//--------- start the move now -------//
if( bestCampPtr )
{
bestCampPtr->patrol();
if( bestCampPtr->patrol_unit_count==0 ) // there could be chances that there are no some for mobilizing the units
return;
//--- set coming_unit_count for later checking ---//
err_when( sizeof(coming_unit_array) != sizeof(patrol_unit_array) );
memcpy( coming_unit_array, bestCampPtr->patrol_unit_array, sizeof(coming_unit_array) );
coming_unit_count = bestCampPtr->patrol_unit_count;
//---- order the unit to move to this camp now ---//
unit_array.assign(loc_x1, loc_y1, 0, COMMAND_AI, bestCampPtr->patrol_unit_array, bestCampPtr->patrol_unit_count);
//------ delete the camp as it no longer has any use ----//
bestCampPtr->ai_del_firm();
return;
}
}
//------- get an overseer if there isn't any right now -----//
if( !overseer_recno )
nationPtr->add_action(loc_x1, loc_y1, -1, -1, ACTION_AI_ASSIGN_OVERSEER, FIRM_CAMP);
//---- think about the no. of workers needed for this base ----//
int combatDiff;
if( overseer_recno == nationPtr->king_unit_recno ) // recruit as many soldiers as possible if the commander is the king
{
combatDiff = 1000;
}
else if( nationPtr->total_jobless_population > 20 + (100-nationPtr->pref_military_development) / 3 ) // 20 to 53
{
combatDiff = 1000; // recruit as many as possible
}
else
{
int combatLevelNeeded = ai_combat_level_needed();
combatDiff = combatLevelNeeded - total_combat_level();
}
if( combatDiff > 0 )
{
ai_recruit(combatDiff);
}
else
{
if( overseer_recno )
ai_recruiting_soldier = 0; // this firm has enough soldiers already
}
}
//----------- End of function FirmCamp::think_recruit ----------//
//--------- Begin of function FirmCamp::ai_recruit ---------//
//
// recruitCombatLevel - the combat level needed to be added.
//
// return: 1-succeeded, 0-failed.
//
int FirmCamp::ai_recruit(int recruitCombatLevel)
{
if( worker_count == MAX_WORKER || !overseer_recno )
return 0;
int recruitCount = max( 1, recruitCombatLevel / 20 );
recruitCount = min( recruitCount, MAX_WORKER-worker_count );
//--- first try to recruit soldiers directly from a linked village ---//
int majorityRace = majority_race();
int raceId;
int loopCount=0;
Town* townPtr;
for( int i=0 ; ination_recno != nation_recno || !townPtr->jobless_population )
continue;
//-- recruit majority race first, but will also consider other races --//
raceId = max( 1, majorityRace );
for( int j=0 ; j MAX_RACE )
raceId = 1;
//--- if the loyalty is too low, reward before recruiting ---//
if( townPtr->jobless_race_pop_array[raceId-1] > 0 &&
townPtr->race_loyalty_array[raceId-1] < 40 )
{
if( townPtr->accumulated_reward_penalty > 30 ) // if the reward penalty is too high, do reward
break;
townPtr->reward(COMMAND_AI);
}
//---- recruit the soldiers we needed ----//
while( townPtr->can_recruit(raceId) )
{
err_when( ++loopCount > 1000 );
pull_town_people(townPtr->town_recno, COMMAND_AI, raceId, 1); // last 1-force pulling people from the town to the firm
if( --recruitCount == 0 )
return 1;
}
}
}
//------ next, try to recruit from remote villages -----//
if( recruitCount > 0 )
nation_array[nation_recno]->add_action(loc_x1, loc_y1, -1, -1, ACTION_AI_ASSIGN_WORKER, FIRM_CAMP, recruitCount);
return 1;
}
//----------- End of function FirmCamp::ai_recruit ----------//
//--------- Begin of function FirmCamp::ai_combat_level_needed ---------//
//
// Think about the no. of soldiers needed by this base.
//
int FirmCamp::ai_combat_level_needed()
{
Town* townPtr;
int combatNeeded=0;
Nation* nationPtr = nation_array[nation_recno];
//------- scan for linked towns ---------//
for( int i=0 ; ination_recno == nation_recno )
{
if( townPtr->should_ai_migrate() ) // no need for this if this town is going to migrate
continue;
combatNeeded += townPtr->population * 10; // 30 people need 300 combat levels
if( townPtr->is_base_town )
combatNeeded += townPtr->population * 10; // double the combat level need for base town
}
}
//--- if the overseer is the king, increase its combat level needed ---//
if( overseer_recno && unit_array[overseer_recno]->rank_id == RANK_KING )
combatNeeded = max(400, combatNeeded);
//---------------------------------------//
return combatNeeded;
}
//----------- End of function FirmCamp::ai_combat_level_needed ----------//
//--------- Begin of function FirmCamp::total_combat_level ---------//
//
// Total combat level of all soldiers and commander in the base.
// The leadership of the general also applies to the final combat level.
//
// return: the total combat level - it is the sum of hit points
// of all the units in the camp.
//
int FirmCamp::total_combat_level()
{
int totalCombatLevel=0;
Worker* workerPtr = worker_array;
for( int i=0 ; ihit_points; // use it points instead of combat level because hit_points represent both combat level and hit points left
err_when( totalCombatLevel < 0 );
//---- the combat level of weapons are higher ------//
UnitInfo* unitInfo = unit_res[workerPtr->unit_id];
if( unitInfo->unit_class == UNIT_CLASS_WEAPON )
totalCombatLevel += (unitInfo->weapon_power + workerPtr->extra_para - 1) * 30; // extra_para keeps the weapon version
err_when( totalCombatLevel < 0 );
}
if( overseer_recno )
{
Unit* unitPtr = unit_array[overseer_recno];
//--- the commander's leadership effects over the soldiers ---//
totalCombatLevel += totalCombatLevel * unitPtr->skill.skill_level / 150; // divided by 150 instead of 100 because while the attacking ability of the unit is affected by the general, the hit points isn't, so we shouldn't do a direct multiplication.
//------ the leader's own hit points ------//
totalCombatLevel += (int) unitPtr->hit_points;
err_when( totalCombatLevel < 0 );
}
return totalCombatLevel;
}
//----------- End of function FirmCamp::total_combat_level ----------//
//--------- Begin of function FirmCamp::average_combat_level ---------//
//
int FirmCamp::average_combat_level()
{
int personCount = worker_count + (overseer_recno>0);
if( personCount > 0 )
return total_combat_level() / personCount;
else
return 0;
}
//----------- End of function FirmCamp::average_combat_level ----------//
//--------- Begin of function FirmCamp::ai_should_close ---------//
//
// Whether this AI firm should be closed or not.
//
int FirmCamp::ai_should_close()
{
return linked_town_count==0 && linked_firm_count==0;
}
//----------- End of function FirmCamp::ai_should_close ----------//
//--------- Begin of function FirmCamp::think_capture ---------//
//
// Think about capturing towns.
//
void FirmCamp::think_capture()
{
if( is_attack_camp ) // if this camp has been assigned to an attack mission already
return;
int targetTownRecno = think_capture_target_town();
if( !targetTownRecno )
return;
//----- if the target town is a nation town -----//
Town* targetTown = town_array[targetTownRecno];
Nation* ownNation = nation_array[nation_recno];
if( targetTown->nation_recno )
{
//--- if there are any defenses (camps and mobile units) on the target town, destroy them all first -----//
if( ownNation->attack_enemy_town_defense(targetTown) != -1 ) // only proceed further when the result is -1, which means no defense on the target town, no attacking is needed.
return;
}
//------ check if the town people will go out to defend -----//
float thisResistance, resistanceDiff;
int defenseCombatLevel=0;
for( int i=0 ; irace_pop_array[i] < 5 ) // if the pop count is lower than 5, ingore it
continue;
if( targetTown->nation_recno )
{
thisResistance = targetTown->race_loyalty_array[i];
resistanceDiff = thisResistance - MIN_NATION_DEFEND_LOYALTY;
}
else
{
thisResistance = targetTown->race_resistance_array[i][nation_recno-1];
resistanceDiff = thisResistance - MIN_INDEPENDENT_DEFEND_LOYALTY;
}
if( resistanceDiff >= 0 )
{
float resistanceDecPerDefender = thisResistance/targetTown->race_pop_array[i]; // resistance decrease per new defender
int defenderCount = int(resistanceDiff / resistanceDecPerDefender)+1; // no. of defenders will go out if you attack this town
defenseCombatLevel += targetTown->town_combat_level * 2 * defenderCount; // *2 is defenseCombatLevel is actually the sum of hit points, not combat level
}
}
//--- try using spies if there are defense forces and the nation likes to use spies ---//
if( defenseCombatLevel > 0 ) // && ownNation->pref_spy >= 50 && m.random(3)==0 ) // 1/3 chance of using spies here, otherwise only use spies when we are not strong enough to take over the village by force
{
if( targetTown->nation_recno==0 ) // if the camp is trying to capture an independent town, the leadership and race id. of the overseer matters.
{
if( think_assign_better_overseer(targetTown) ) // a better general is being assigned to this firm, wait for it
return;
if( think_capture_use_spy(targetTown) )
return;
if( defenseCombatLevel > 100+ownNation->pref_military_courage &&
resistanceDiff > (100-ownNation->pref_peacefulness)/5 ) // depending on the peacefulness, the nation won't attack if resistance > (0-20)
{
return;
}
}
else
{
//--- don't attack if the target nation's military rating is higher than ours ---//
if( nation_array[targetTown->nation_recno]->military_rank_rating()
> ownNation->military_rank_rating() )
{
return;
}
}
}
//------ send out troops to capture the target town now ----//
int rc;
if( targetTown->nation_recno )
rc = ai_capture_enemy_town(targetTown, defenseCombatLevel);
else
rc = ai_capture_independent_town(targetTown, defenseCombatLevel);
//-- use the same approach to capture both enemy and independent towns --//
if( rc )
{
ai_capture_town_recno = targetTownRecno;
defense_flag = 0; // turn off the defense flag during capturing so the general is staying in the base to influence the town
//--- as the current commander has been out to attack the town by ai_attack_target(), we need to assign him back to the camp for influencing the town and eventually capture it ---//
if( !overseer_recno && targetTown->nation_recno && patrol_unit_count>0 )
unit_array[ patrol_unit_array[0] ]->assign(loc_x1, loc_y1);
}
}
//----------- End of function FirmCamp::think_capture -----------//
//--------- Begin of function FirmCamp::think_capture_target_town ---------//
//
// Think about which town to capture.
//
// Return: recno of the target town.
//
int FirmCamp::think_capture_target_town()
{
if( !linked_town_count || !overseer_recno )
return 0;
//--- if there are any units currently being assigned to this camp ---//
if( nation_array[nation_recno]->is_action_exist( loc_x1, loc_y1, -1, -1, ACTION_AI_ASSIGN_WORKER, FIRM_CAMP ) )
return 0;
//-- decide which town to attack (only when the camp is linked to more than one town ---//
int curResistance, curTargetResistance, resistanceDec;
int i, minResistance=100, bestTownRecno=0;
Town* townPtr;
int prefPeacefulness = nation_array[nation_recno]->pref_peacefulness;
Nation* ownNation = nation_array[nation_recno];
int overseerRaceId = unit_array[overseer_recno]->race_id;
for( i=0 ; ination_recno == nation_recno )
continue;
//------- if it's an independent town -------//
if( !townPtr->nation_recno ) // only capture independent town
{
curResistance = townPtr->average_resistance(nation_recno);
curTargetResistance = townPtr->average_target_resistance(nation_recno);
resistanceDec = curResistance - curTargetResistance;
//------- if the resistance is decreasing ---------//
if( resistanceDec > 0 &&
curResistance > 25-25*ownNation->pref_peacefulness/100 && // for nation that has a high peacefulness preference they will wait for the loyalty to fall and try not to attack the town unless necessary
townPtr->race_pop_array[overseerRaceId-1] >= 5 ) // if it's less than 5, don't count it, as that it will be easy to attack
{
continue; // let it decrease until it can no longer decrease
}
}
else //-------- if it's a nation town ---------//
{
NationRelation* nationRelation = ownNation->get_relation(townPtr->nation_recno);
if( nationRelation->status != NATION_HOSTILE )
continue;
curResistance = townPtr->average_loyalty();
}
//--------------------------------------//
if( curResistance < minResistance )
{
minResistance = curResistance;
bestTownRecno = townPtr->town_recno;
}
}
return bestTownRecno;
}
//--------- End of function FirmCamp::think_capture_target_town --------//
//--------- Begin of function FirmCamp::ai_capture_independent_town -------//
//
// Try to capture the given independent town.
//
int FirmCamp::ai_capture_independent_town(Town* targetTown, int defenseCombatLevel)
{
//---- see if the force is strong enough to attack the town ----//
Nation* nationPtr = nation_array[nation_recno];
int curCombatLevel = total_combat_level(); // total combat level
int combatDiff = defenseCombatLevel * (150+nationPtr->pref_force_projection/2) / 100
- curCombatLevel; // try to recruit soldiers based on the force projection perference
if( combatDiff > 0 )
{
if( ai_recruit(combatDiff) ) // try to recruit new soldiers to increase the combat ability of the troop
return 0;
}
combatDiff = defenseCombatLevel * (200-nationPtr->pref_military_courage/2) / 100
- curCombatLevel;
//---------- attack the target town now ----------//
if( nation_array[nation_recno]->ai_attack_target(targetTown->loc_x1, targetTown->loc_y1, defenseCombatLevel, 0, 0, 0, firm_recno) )
return 1;
return 0;
}
//--------- End of function FirmCamp::ai_capture_independent_town --------//
//--------- Begin of function FirmCamp::think_capture_return ---------//
//
// Think about if the capturing units should now return to their base.
//
int FirmCamp::think_capture_return()
{
//----- if the target town is destroyed ------//
int returnCamp=0;
if( town_array.is_deleted(ai_capture_town_recno) )
{
returnCamp = 1;
}
else //---- check whether the town has been captured ----//
{
Town* townPtr = town_array[ai_capture_town_recno];
if( townPtr->nation_recno == nation_recno ) // the town has been captured
returnCamp = 1;
}
//-------- if should return to the camp now ---------//
if( returnCamp )
{
Unit* unitPtr;
for( int i=0; iis_visible() )
unitPtr->assign(loc_x1, loc_y1);
}
ai_capture_town_recno = 0; // turn it on again after the capturing plan is finished
defense_flag = 1;
return 1;
}
return 0;
}
//----------- End of function FirmCamp::think_capture_return -----------//
//--------- Begin of function FirmCamp::think_assign_better_overseer -------//
//
int FirmCamp::think_assign_better_overseer(Town* targetTown)
{
//----- check if there is already a queued action -----//
Nation* ownNation = nation_array[nation_recno];
if( ownNation->is_action_exist( loc_x1, loc_y1, -1, -1,
ACTION_AI_ASSIGN_OVERSEER, FIRM_CAMP ) )
{
return 1; // there is a queued action being processed already
}
//------ get the two most populated races of the town ----//
int mostRaceId1, mostRaceId2;
targetTown->get_most_populated_race(mostRaceId1, mostRaceId2);
//-- if the resistance of the majority race has already dropped to its lowest possible --//
if( targetTown->race_resistance_array[mostRaceId1-1][nation_recno-1] <=
(float) (targetTown->race_target_resistance_array[mostRaceId1-1][nation_recno-1]+1) )
{
if( targetTown->race_resistance_array[mostRaceId1-1][nation_recno-1] > 30 )
{
if( think_assign_better_overseer2(targetTown->town_recno, mostRaceId1) )
return 1;
}
}
//-- if the resistance of the 2nd majority race has already dropped to its lowest possible --//
if( targetTown->race_resistance_array[mostRaceId2-1][nation_recno-1] <=
(float) (targetTown->race_target_resistance_array[mostRaceId2-1][nation_recno-1]+1) )
{
if( targetTown->race_resistance_array[mostRaceId2-1][nation_recno-1] > 30 )
{
if( think_assign_better_overseer2(targetTown->town_recno, mostRaceId2) )
return 1;
}
}
return 0;
}
//--------- End of function FirmCamp::think_assign_better_overseer --------//
//--------- Begin of function FirmCamp::think_assign_better_overseer2 -------//
//
int FirmCamp::think_assign_better_overseer2(int targetTownRecno, int raceId)
{
int reduceResistance;
Nation* ownNation = nation_array[nation_recno];
int bestUnitRecno = ownNation->find_best_capturer(targetTownRecno, raceId, reduceResistance);
if( !bestUnitRecno || bestUnitRecno==overseer_recno ) // if we already got the best one here
return 0;
//---- only assign new overseer if the new one's leadership is significantly higher than the current one ----//
if( overseer_recno &&
unit_array[bestUnitRecno]->skill.skill_level < unit_array[overseer_recno]->skill.skill_level + 15 )
{
return 0;
}
//------ check what the best unit is -------//
if( !ownNation->mobilize_capturer(bestUnitRecno) )
return 0;
//---------- add the action to the queue now ----------//
int actionRecno = ownNation->add_action( loc_x1, loc_y1, -1, -1,
ACTION_AI_ASSIGN_OVERSEER, FIRM_CAMP, 1, bestUnitRecno );
if( actionRecno )
ownNation->process_action(actionRecno);
return 1;
}
//--------- End of function FirmCamp::think_assign_better_overseer2 --------//
//--------- Begin of function FirmCamp::ai_capture_enemy_town -------//
//
// When capturing an enemy town, the commander should stay in the
// command base to influence the village and reinforcement should be
// sent instead of using the troop in the base for attacking the enemies.
//
int FirmCamp::ai_capture_enemy_town(Town* targetTown, int defenseCombatLevel)
{
int useAllCamp;
Nation* ownNation = nation_array[nation_recno];
Nation* targetNation = nation_array[targetTown->nation_recno];
int ourMilitary = ownNation->military_rank_rating();
int enemyMilitary = targetNation->military_rank_rating();
//--- use all camps to attack if we have money and we are stronger than the enemy ---//
if( ourMilitary - enemyMilitary > 30 && ownNation->ai_should_spend(ownNation->pref_military_courage/2) )
useAllCamp = 1;
//---- use all camps to attack the enemy if the enemy is a human player
else if( config.ai_aggressiveness >= OPTION_MEDIUM &&
!targetNation->is_ai() && ourMilitary > enemyMilitary )
{
if( config.ai_aggressiveness >= OPTION_HIGH ||
ownNation->pref_peacefulness < 50 )
{
useAllCamp = 1;
}
}
return nation_array[nation_recno]->ai_attack_target(targetTown->loc_x1, targetTown->loc_y1,
defenseCombatLevel, 0, 0, 0, firm_recno, useAllCamp );
}
//--------- End of function FirmCamp::ai_capture_enemy_town --------//
//--------- Begin of function FirmCamp::think_capture_use_spy ---------//
//
// Think about using spies for capturing the target town.
//
int FirmCamp::think_capture_use_spy(Town* targetTown)
{
Nation* ownNation = nation_array[nation_recno];
//------ get the two most populated races of the town ----//
int mostRaceId1, mostRaceId2;
targetTown->get_most_populated_race(mostRaceId1, mostRaceId2);
//-- get the current number of our spies in this town and see if we need more --//
int spyCount;
int curSpyLevel = spy_array.total_spy_skill_level( SPY_TOWN, targetTown->town_recno, nation_recno, spyCount );
//--------------------------------------------//
int rc=0;
if( think_capture_use_spy2(targetTown, mostRaceId1, curSpyLevel ) )
rc = 1;
if( mostRaceId2 )
{
if( think_capture_use_spy2(targetTown, mostRaceId2, curSpyLevel ) )
rc = 1;
}
return rc;
}
//----------- End of function FirmCamp::think_capture_use_spy -----------//
//--------- Begin of function FirmCamp::think_capture_use_spy2 ---------//
//
// Think about using spies for capturing the target town.
//
int FirmCamp::think_capture_use_spy2(Town* targetTown, int raceId, int curSpyLevel)
{
Nation* ownNation = nation_array[nation_recno];
int curResistance, targetResistance;
if( targetTown->nation_recno )
{
curResistance = (int) targetTown->race_loyalty_array[raceId-1];
targetResistance = targetTown->race_target_loyalty_array[raceId-1];
}
else
{
curResistance = (int) targetTown->race_resistance_array[raceId-1][nation_recno-1];
targetResistance = targetTown->race_target_resistance_array[raceId-1][nation_recno-1];
}
int minResistance = min(curResistance, targetResistance);
//----- if the resistance is low enough, don't have to use spies -----//
if( targetTown->nation_recno )
{
if( minResistance < MIN_NATION_DEFEND_LOYALTY )
return 0;
}
else
{
if( minResistance < MIN_INDEPENDENT_DEFEND_LOYALTY )
return 0;
}
//----- if the needed spy level > current spy level, assign more spies ----//
int neededSpyLevel = minResistance * (50+ownNation->pref_spy) / 50;
if( neededSpyLevel > curSpyLevel )
return ownNation->ai_assign_spy_to_town(targetTown->town_recno, raceId);
return 0;
}
//----------- End of function FirmCamp::think_capture_use_spy2 -----------//
//--------- Begin of function FirmCamp::ai_update_link_status ---------//
//
// Updating link status of this firm with towns.
//
// It's a overloaded function of Firm::ai_update_link_status().
//
void FirmCamp::ai_update_link_status()
{
Nation* ownNation = nation_array[nation_recno];
//------ always enable links to all towns -----//
for( int i=0 ; ination_recno &&
ownNation->get_relation_status(townPtr->nation_recno) <= NATION_TENSE ) // hostile or tense
{
continue;
}
toggle_town_link( i+1, 1, COMMAND_AI );
//------------------------------------------------------------------//
// Here we only update this camp's link to the town.
// The town's link to this firm is updated in Town::update_target_loyalty().
//------------------------------------------------------------------//
}
}
//----------- End of function FirmCamp::ai_update_link_status ----------//
//------- Begin of function FirmCamp::ai_has_excess_worker -------//
//
// Return whether this firm has any excessive soldiers or not.
//
int FirmCamp::ai_has_excess_worker()
{
if( linked_town_count==0 )
return 1;
if( ai_capture_town_recno ) // no if the camp is trying to capture an independent town
return 0;
if( is_attack_camp ) // no if the camp is trying to capture an independent town
return 0;
if( should_close_flag )
return 1;
return 0;
// return total_combat_level() > ai_combat_level_needed()+100;
}
//--------- End of function FirmCamp::ai_has_excess_worker -------//
//------- Begin of function FirmCamp::think_use_cash_to_capture -------//
//
// Think about using money to decrease the resistance of the
// independent villagers.
//
int FirmCamp::think_use_cash_to_capture()
{
if( !nation_array[nation_recno]->should_use_cash_to_capture() )
return 0;
//-------------------------------------//
Town* townPtr;
for( int i=0 ; ination_recno == nation_recno )
continue;
if( townPtr->accumulated_enemy_grant_penalty > 0 )
continue;
if( townPtr->can_grant_to_non_own_town(nation_recno) )
townPtr->grant_to_non_own_town(nation_recno, COMMAND_AI);
}
return 1;
}
//--------- End of function FirmCamp::think_use_cash_to_capture -------//
//------- Begin of function FirmCamp::think_linked_town_change_nation ------//
//
// This function is called by Town::set_nation() when a town linked
// to this firm has changed nation.
//
// linkedTownRecno - the recno of the town that has changed nation.
// oldNationRecno - the old nation recno of the town
// newNationRecno - the new nation recno of the town
//
void FirmCamp::think_linked_town_change_nation(int linkedTownRecno, int oldNationRecno, int newNationRecno)
{
//-----------------------------------------------//
//
// If we are trying to capture an independent town and our
// enemies have managed to capture it first.
//
//-----------------------------------------------//
Nation* ownNation = nation_array[nation_recno];
if( oldNationRecno==0 && newNationRecno>0 && newNationRecno != nation_recno )
{
Town* townPtr = town_array[linkedTownRecno];
//--- if the town does not have any protection, then don't remove this camp ---//
if( townPtr->protection_available()==0 )
return;
should_close_flag = 1;
ownNation->firm_should_close_array[firm_id-1]++;
}
}
//-------- End of function FirmCamp::think_linked_town_change_nation ------//
//------- Begin of function FirmCamp::think_attack_nearby_enemy -------//
//
// Think about attacking enemies near this cmap.
//
int FirmCamp::think_attack_nearby_enemy()
{
//------------------------------------------//
Nation* nationPtr = nation_array[nation_recno];
int scanRange = 6 + nationPtr->pref_military_courage/20; // 6 to 11
int xLoc1 = loc_x1 - scanRange;
int yLoc1 = loc_y1 - scanRange;
int xLoc2 = loc_x2 + scanRange;
int yLoc2 = loc_y2 + scanRange;
xLoc1 = max( xLoc1, 0 );
yLoc1 = max( yLoc1, 0 );
xLoc2 = min( xLoc2, MAX_WORLD_X_LOC-1 );
yLoc2 = min( yLoc2, MAX_WORLD_Y_LOC-1 );
//------------------------------------------//
int enemyCombatLevel=0; // the higher the rating, the easier we can attack the target town.
int xLoc, yLoc;
int enemyXLoc= -1, enemyYLoc;
Unit* unitPtr;
Location* locPtr;
for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ )
{
locPtr = world.get_loc(xLoc1, yLoc);
for( xLoc=xLoc1 ; xLoc<=xLoc2 ; xLoc++, locPtr++ )
{
//--- if there is an enemy unit here ---//
if( locPtr->has_unit(UNIT_LAND) )
{
unitPtr = unit_array[ locPtr->unit_recno(UNIT_LAND) ];
if( !unitPtr->nation_recno )
continue;
//--- if the unit is idle and he is our enemy ---//
if( unitPtr->cur_action == SPRITE_ATTACK &&
nationPtr->get_relation_status(unitPtr->nation_recno) == NATION_HOSTILE )
{
err_when( unitPtr->nation_recno == nation_recno );
enemyCombatLevel += (int) unitPtr->hit_points;
if( enemyXLoc == -1 || m.random(5)==0 )
{
enemyXLoc = xLoc;
enemyYLoc = yLoc;
}
}
}
}
}
if( enemyCombatLevel==0 )
return 0;
err_when( enemyXLoc < 0 );
//--------- attack the target now -----------//
if( worker_count > 0 )
{
patrol_all_soldier();
if( patrol_unit_count > 0 )
{
// ###### patch begin Gilbert 5/8 ########//
unit_array.attack(enemyXLoc, enemyYLoc, 0, patrol_unit_array, patrol_unit_count, COMMAND_AI,
world.get_loc(enemyXLoc, enemyYLoc)->unit_recno(UNIT_LAND));
// ###### patch end Gilbert 5/8 ########//
return 1;
}
}
return 0;
}
//-------- End of function FirmCamp::think_attack_nearby_enemy -------//
//------- Begin of function FirmCamp::think_change_town_link -------//
//
// Think about changing links to foreign towns.
//
void FirmCamp::think_change_town_link()
{
Nation* ownNation = nation_array[nation_recno];
for( int i=linked_town_count-1 ; i>=0 ; i-- )
{
Town* townPtr = town_array[ linked_town_array[i] ];
//--- only change links to foreign towns, links to own towns are always on ---//
if( townPtr->nation_recno == nation_recno )
continue;
//---- only enable links to non-friendly towns ----//
int enableFlag = townPtr->nation_recno==0 ||
ownNation->get_relation(townPtr->nation_recno)->status == NATION_HOSTILE;
toggle_town_link( i+1, enableFlag, COMMAND_AI );
}
}
//--------- End of function FirmCamp::think_change_town_link -------//
//------- Begin of function FirmCamp::think_assign_better_commander -------//
//
// Assign a better commander to this camp.
//
int FirmCamp::think_assign_better_commander()
{
//----- if there is already an overseer being assigned to the camp ---//
Nation* ownNation = nation_array[nation_recno];
int actionRecno = ownNation->is_action_exist(loc_x1, loc_y1, -1, -1,
ACTION_AI_ASSIGN_OVERSEER, FIRM_CAMP);
//--- if there is already one existing ---//
if( actionRecno )
{
//--- if the action still is already being processed, don't bother with it ---//
if( ownNation->get_action(actionRecno)->processing_instance_count > 0 )
return 0;
//--- however, if the action hasn't been processed, we still try to use the approach here ---//
}
//-------------------------------------------------//
Firm* firmPtr;
Worker* workerPtr;
int bestRaceId = best_commander_race();
int bestFirmRecno=0, bestWorkerId;
int bestLeadership=0;
if( overseer_recno )
{
bestLeadership = cur_commander_leadership(bestRaceId)
+ 10 + ownNation->pref_loyalty_concern/10; // nations that have higher loyalty concern will not switch commander too frequently
}
//--- locate for a soldier who has a higher leadership ---//
for( int i=ownNation->ai_camp_count-1 ; i>=0 ; i-- )
{
firmPtr = firm_array[ ownNation->ai_camp_array[i] ];
if( firmPtr->region_id != region_id )
continue;
workerPtr = firmPtr->worker_array;
for( int j=1 ; j<=firmPtr->worker_count ; j++, workerPtr++ )
{
if( !workerPtr->race_id )
continue;
int workerLeadership = workerPtr->skill_level;
if( workerPtr->race_id != bestRaceId )
workerLeadership /= 2;
if( workerLeadership > bestLeadership )
{
bestLeadership = workerLeadership;
bestFirmRecno = firmPtr->firm_recno;
bestWorkerId = j;
}
}
}
if( bestFirmRecno == 0 )
return 0;
//-------- assign the overseer now -------//
int unitRecno = firm_array[bestFirmRecno]->mobilize_worker(bestWorkerId, COMMAND_AI);
if( !unitRecno )
return 0;
Unit* unitPtr = unit_array[unitRecno];
unitPtr->set_rank(RANK_GENERAL);
//---- if there is already an existing but unprocessed one, delete it first ---//
if( actionRecno )
ownNation->del_action(actionRecno);
return ownNation->add_action(loc_x1, loc_y1, -1, -1, ACTION_AI_ASSIGN_OVERSEER, FIRM_CAMP, 1, unitRecno);
}
//-------- End of function FirmCamp::think_assign_better_commander ---------//
//------- Begin of function FirmCamp::cur_commander_leadership -------//
//
// [int] bestRaceId - if this is given, it will be used, otherwise
// it will use best_commander_race().
//
// Return the commander leadership
//
int FirmCamp::cur_commander_leadership(int bestRaceId)
{
int commanderLeadership=0;
//--- get the current leadership of the commander ----//
if( overseer_recno )
{
if( !bestRaceId )
bestRaceId = best_commander_race();
Unit* unitCommander = unit_array[overseer_recno];
if( unitCommander->race_id == bestRaceId )
commanderLeadership = unitCommander->skill.skill_level;
else
commanderLeadership = unitCommander->skill.skill_level / 2; // divided by 2 if the race doesn't match
}
return commanderLeadership;
}
//-------- End of function FirmCamp::cur_commander_leadership ---------//
//------- Begin of function FirmCamp::new_commander_leadership -------//
//
// newRaceId - the race id. of the would-be commander.
// newSkillLevel - the skill level of the would-be commander.
//
// Return the commander leadership if the unit is assigned to this camp.
//
int FirmCamp::new_commander_leadership(int newRaceId, int newSkillLevel)
{
int commanderLeadership=0;
int bestRaceId = best_commander_race();
//--- get the current leadership of the commander ----//
if( overseer_recno )
{
if( newRaceId == bestRaceId )
commanderLeadership = newSkillLevel;
else
commanderLeadership = newSkillLevel / 2; // divided by 2 if the race doesn't match
}
return commanderLeadership;
}
//-------- End of function FirmCamp::new_commander_leadership ---------//
//------- Begin of function FirmCamp::best_commander_race -------//
//
// Return what race the commander should be for this camp.
//
int FirmCamp::best_commander_race()
{
//---------------------------------------------//
//
// If this camp is the commanding camp of a town,
// then return the majority race of the town.
//
// A camp is the commanding camp of a town when
// it is the closest camp to the town.
//
//---------------------------------------------//
for( int i=linked_town_count-1 ; i>=0 ; i-- )
{
Town* townPtr = town_array[ linked_town_array[i] ];
if( townPtr->closest_own_camp() == firm_recno )
return townPtr->majority_race();
}
//----- check if this camp is trying to capture an independent town ---//
int targetTownRecno = think_capture_target_town();
if( targetTownRecno && town_array[targetTownRecno]->nation_recno==0 )
return town_array[targetTownRecno]->majority_race();
//----- Otherwise return the majority race of this camp -----//
return majority_race();
}
//-------- End of function FirmCamp::best_commander_race ---------//