/*
* 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 : OAI_MARI.CPP
//Description: AI functions on sea exploration, trading
#include
#include
#include
#include
#include
#include
#include
#include
//--------- Begin of function Nation::think_marine --------//
//
void Nation::think_marine()
{
if( pref_use_marine < 50 ) // don't use marine at all
return;
if( !ai_should_spend(20+pref_use_marine/2) ) // 20 to 70 importance rating
return;
//--- think over building harbor network ---//
think_build_harbor_network();
if( ai_harbor_count == 0 )
return;
//------ think about sea attack enemies -------//
if( m.random(3)==0 ) // 33% chance
{
if( think_sea_attack_enemy() )
return;
}
//---- check if it is safe for sea travel now ----//
if( !ai_is_sea_travel_safe() )
return;
//----- think over moving between regions -----//
think_move_between_region();
// think_move_to_region_with_mine();
}
//---------- End of function Nation::think_marine --------//
//----- Begin of function Nation::think_build_harbor_network ----//
//
// Think about thinking a harbor network so that we have harbors
// from every region to any other regions.
//
int Nation::think_build_harbor_network()
{
//--- only build one harbor at a time, to avoid double building ---//
if( is_action_exist( ACTION_AI_BUILD_FIRM, FIRM_HARBOR ) )
return 0;
//--------------------------------------------//
RegionStat* regionStat = region_array.region_stat_array;
RegionPath* regionPath;
for( int i=0 ; ibase_town_nation_count_array[nation_recno-1] )
continue;
if( regionStat->harbor_nation_count_array[nation_recno-1] > 0 ) // if we already have a harbor in this region
continue;
err_when( regionStat->harbor_nation_count_array[nation_recno-1] > 1 ); // this shouldn't happen if the AI works properly
//-----------------------------------------------------------------------//
//
// Scan thru all regions which this region can be connected to thru sea.
// If one of them is worth our landing, then builld a harbor in this
// region so we can sail to that region.
//
//-----------------------------------------------------------------------//
regionPath = regionStat->reachable_region_array;
for( int j=0 ; jreachable_region_count ; j++, regionPath++ )
{
err_when( regionPath->land_region_stat_id == i+1 ); // pointing back to its own
//--------------------------------------//
if( ai_harbor_count == 0 && // if we have already built one harbor, then we should continue to build others asa single harbor isn't useful
ai_should_sail_to_rating(regionPath->land_region_stat_id) <= 0 )
{
continue;
}
//--------- build a harbor now ---------//
if( ai_build_harbor( regionStat->region_id, regionPath->sea_region_id ) )
return 1;
}
}
return 0;
}
//----- End of function Nation::think_build_harbor_network ----//
//----- Begin of function Nation::ai_should_sail_to_rating ----//
//
int Nation::ai_should_sail_to_rating(int regionStatId)
{
RegionStat* regionStat = region_array.get_region_stat2(regionStatId);
int curRating;
curRating = regionStat->raw_count * 100
+ regionStat->independent_town_count * 20
+ regionStat->nation_presence_count * 30;
/*
- (regionStat->total_town_count - regionStat->town_nation_count_array[nation_recno-1] ) * 10 // towns of other nations
- (regionStat->total_firm_count - regionStat->firm_nation_count_array[nation_recno-1] ) * 5 // firms of other nations
- (regionStat->total_unit_count - regionStat->unit_nation_count_array[nation_recno-1] ) * 2 // units of other nations
- regionStat->independent_unit_count * 2; // monsters or rebel units
*/
return curRating > 0;
}
//----- End of function Nation::ai_should_sail_to_rating ----//
//--------- Begin of function Nation::ai_build_harbor --------//
//
// Build a harbor across the given land and sea region id.
//
// landRegionId - the land region id.
// seaRegionId - the sea region id.
//
// return: 1 - a suitable location is found and the
// building action has been queued.
// 0 - not suitable location is found.
//
int Nation::ai_build_harbor(int landRegionId, int seaRegionId)
{
#define ADEQUATE_ENEMY_HARBOR_DISTANCE 10
//---- randomly pick a base town of this nation ----//
Town* townPtr;
int townSeq = m.random(ai_town_count);
int i;
for( i=0 ; i= ai_town_count )
townSeq=0;
townPtr = town_array[ ai_town_array[townSeq] ];
if( townPtr->is_base_town && landRegionId==townPtr->region_id )
break;
}
if( i==ai_town_count ) // not found
return 0;
int homeXLoc = townPtr->center_x;
int homeYLoc = townPtr->center_y;
//---- scan out from the town and find the nearest suitable location to build the harbor ----//
int xOffset, yOffset;
int xLoc, yLoc, bestXLoc= -1, bestYLoc= -1, maxEnemyDistance=0;
Location* locPtr;
for( i=2 ; ican_build_whole_harbor() )
continue;
if( !world.is_adjacent_region(xLoc, yLoc, seaRegionId) )
continue;
if( !world.can_build_firm(xLoc, yLoc, FIRM_HARBOR) )
continue;
//--------------------------------------//
int enemyDistance = closest_enemy_firm_distance(FIRM_HARBOR, xLoc, yLoc);
if( enemyDistance > maxEnemyDistance )
{
maxEnemyDistance = enemyDistance;
bestXLoc = xLoc;
bestYLoc = yLoc;
if( enemyDistance >= ADEQUATE_ENEMY_HARBOR_DISTANCE )
break;
}
}
//--------------------------------//
if( bestXLoc >= 0 )
{
add_action(xLoc, yLoc, homeXLoc, homeYLoc, ACTION_AI_BUILD_FIRM, FIRM_HARBOR);
return 1;
}
return 0;
}
//---------- End of function Nation::ai_build_harbor --------//
//--------- Begin of function Nation::closest_enemy_firm_distance --------//
//
// Return how close is the cloeset enemy harbor to the given location.
//
// firmId - firm id.
// xLoc, yLoc - the given location
//
int Nation::closest_enemy_firm_distance(int firmId, int xLoc, int yLoc)
{
int curDistance, minDistance=0x7FFF;
for( int i=firm_array.size() ; i>0 ; i-- )
{
if( firm_array.is_deleted(i) )
continue;
Firm* firmPtr = firm_array[i];
if( firmPtr->firm_id != firmId ||
firmPtr->nation_recno == nation_recno ) // belonging to own nation, not enemy nation
{
continue;
}
curDistance = m.points_distance(firmPtr->center_x, firmPtr->center_y, xLoc, yLoc);
if( curDistance < minDistance )
minDistance = curDistance;
}
return minDistance;
}
//---------- End of function Nation::closest_enemy_firm_distance --------//
//------ Begin of function Nation::think_move_between_region ------//
//
int Nation::think_move_between_region()
{
if( think_move_people_between_region() )
return 1;
if( think_move_troop_between_region() )
return 1;
return 0;
}
//------ End of function Nation::think_move_between_region -------//
//------ Begin of function Nation::think_move_troop_between_region ------//
//
// Thing about moving units between regions
//
int Nation::think_move_troop_between_region()
{
//----- find the region with the least population -----//
int campCount, maxCampCount=0, minCampCount=0x1000;
int maxRegionId=0, minRegionId=0;
RegionStat* regionStat = region_array.region_stat_array;
int curRating, minRegionRating=0;
int i;
for( i=0 ; ination_presence_count==0 &&
regionStat->independent_town_count==0 &&
regionStat->raw_count==0 )
{
continue;
}
campCount = regionStat->camp_nation_count_array[nation_recno-1];
if( campCount > maxCampCount )
{
maxCampCount = campCount;
maxRegionId = regionStat->region_id;
}
if( campCount <= minCampCount )
{
curRating = ai_should_sail_to_rating(i+1);
if( campCount < minCampCount || curRating >= minRegionRating )
{
minCampCount = campCount;
minRegionId = regionStat->region_id;
minRegionRating = curRating;
}
}
}
if( !maxRegionId || !minRegionId || maxRegionId==minRegionId )
return 0;
//----- only move if the difference is big enough ------//
int minJoblessPop = region_array.get_region_stat(minRegionId)->nation_jobless_population_array[nation_recno-1];
int maxJoblessPop = region_array.get_region_stat(maxRegionId)->nation_jobless_population_array[nation_recno-1];
if( pref_use_marine < 90 ) // if > 90, it will ignore all these and move anyway
{
if( minCampCount==0 )
{
if( maxJoblessPop - minJoblessPop < 200 - pref_use_marine ) // 150 to 200 (pref_use_marine is always >= 50, if it is < 50, marine functions are not called at all
return 0;
}
else
{
if( maxJoblessPop - minJoblessPop < 150 - pref_use_marine ) // 100 to 150 (pref_use_marine is always >= 50, if it is < 50, marine functions are not called at all
return 0;
}
}
else
{
if( maxJoblessPop < 20 ) // don't move if we only have a few jobless people
return 0;
}
//------------ see if we have any camps in the region -----------//
int destRegionId = minRegionId;
Firm* firmPtr;
for( i=ai_camp_count-1 ; i>=0 ; i-- )
{
firmPtr = firm_array[ai_camp_array[i]];
if( firmPtr->region_id == destRegionId &&
!firmPtr->under_construction ) // if it's under construction there may be unit waiting outside of the camp
{
//--- if there is one, must move the troop close to it ---//
return ai_patrol_to_region(firmPtr->center_x, firmPtr->center_y, SEA_ACTION_NONE);
}
}
//----- if we don't have any camps in the region, build one ----//
int xLoc=0, yLoc=0;
FirmInfo* firmInfo = firm_res[FIRM_CAMP];
if(world.locate_space_random(xLoc, yLoc, MAX_WORLD_X_LOC-1,
MAX_WORLD_Y_LOC-1, firmInfo->loc_width, firmInfo->loc_height,
MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC, destRegionId, 1))
{
return ai_patrol_to_region(xLoc, yLoc, SEA_ACTION_BUILD_CAMP);
}
return 0;
}
//------ End of function Nation::think_move_troop_between_region -------//
//------ Begin of function Nation::think_move_people_between_region ------//
//
// Thing about moving units between regions
//
int Nation::think_move_people_between_region()
{
//----- find the region with the least population -----//
int joblessPop, maxJoblessPop=0, minJoblessPop=0x1000;
int maxRegionId=0, minRegionId=0;
RegionStat* regionStat = region_array.region_stat_array;
int i;
for( i=0 ; icamp_nation_count_array[nation_recno-1] == 0 )
continue;
joblessPop = regionStat->nation_jobless_population_array[nation_recno-1];
if( joblessPop > maxJoblessPop )
{
maxJoblessPop = joblessPop;
maxRegionId = regionStat->region_id;
}
if( joblessPop < minJoblessPop )
{
minJoblessPop = joblessPop;
minRegionId = regionStat->region_id;
}
}
if( !maxRegionId || !minRegionId || maxRegionId==minRegionId )
return 0;
//----- only move if the difference is big enough ------//
if( pref_use_marine < 90 ) // if > 90, it will ignore all these and move anyway
{
if( maxJoblessPop - minJoblessPop < 150 - pref_use_marine ) // 100 to 150 (pref_use_marine is always >= 50, if it is < 50, marine functions are not called at all
return 0;
}
else
{
if( maxJoblessPop < 20 ) // don't move if we only have a few jobless people
return 0;
}
//------------ see if we have any towns in the region -----------//
int destRegionId = minRegionId;
Town* townPtr;
for( i=ai_town_count-1 ; i>=0 ; i-- )
{
townPtr = town_array[ai_town_array[i]];
if( townPtr->region_id == destRegionId )
{
//--- if there is one, must move the people to it ---//
return ai_settle_to_region(townPtr->center_x, townPtr->center_y, SEA_ACTION_NONE);
}
}
//----- if we don't have any towns in the region, settle one ----//
int xLoc=0, yLoc=0;
if(world.locate_space_random(xLoc, yLoc, MAX_WORLD_X_LOC-1,
MAX_WORLD_Y_LOC-1, STD_TOWN_LOC_WIDTH, STD_TOWN_LOC_HEIGHT,
MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC, destRegionId, 1))
{
return ai_settle_to_region(xLoc, yLoc, SEA_ACTION_SETTLE);
}
return 0;
}
//------ End of function Nation::think_move_people_between_region -------//
//------ Begin of function Nation::ai_is_sea_travel_safe ------//
//
// return: 1 - it's safe for sea travel
// 0 - it's not safe for sea travel
//
int Nation::ai_is_sea_travel_safe()
{
//--- count the no. of battle ships owned by each nation ---//
Unit* unitPtr;
short nationShipCountArray[MAX_NATION];
memset( nationShipCountArray, 0, sizeof(nationShipCountArray) );
int i;
for( i=unit_array.size() ; i>0 ; i-- )
{
if( unit_array.is_deleted(i) )
continue;
unitPtr = unit_array[i];
if( unitPtr->unit_id != UNIT_CARAVEL &&
unitPtr->unit_id != UNIT_GALLEON )
{
continue;
}
err_when( unitPtr->nation_recno < 1 || unitPtr->nation_recno > MAX_NATION );
nationShipCountArray[unitPtr->nation_recno-1]++;
}
//--- compare the no. of ships of ours and those of the human players ---//
int ourBattleShipCount = nationShipCountArray[nation_recno-1];
int nationRecno = m.random(nation_array.size())+1;
for( i=nation_array.size() ; i>0 ; i-- )
{
if( ++nationRecno > nation_array.size() )
nationRecno = 1;
if( nation_array.is_deleted(nationRecno) )
continue;
if( get_relation(nationRecno)->status != NATION_HOSTILE ) // only check enemies
continue;
if( nation_array[nationRecno]->is_ai() ) // only check human players
continue;
//-- if enemy has battle ships, it is not safe for sea travel, destroy them first ---//
if( nationShipCountArray[nationRecno-1] > 0 )
{
//--- if enemy ships significantly outnumber ours, don't do any sea travel ---//
if( nationShipCountArray[nationRecno-1] - ourBattleShipCount >
pref_military_courage/3 ) // 0 to 3
{
return 0;
}
}
}
return 1;
}
//----- End of function Nation::ai_is_sea_travel_safe -----//
//------ Begin of function Nation::max_human_battle_ship_count ------//
//
// return: the number of ships owned by the human player who
// is strongest on sea power.
//
int Nation::max_human_battle_ship_count()
{
//--- count the no. of battle ships owned by each nation ---//
Unit* unitPtr;
short nationShipCountArray[MAX_NATION];
memset( nationShipCountArray, 0, sizeof(nationShipCountArray) );
int i;
for( i=unit_array.size() ; i>0 ; i-- )
{
if( unit_array.is_deleted(i) )
continue;
unitPtr = unit_array[i];
if( unitPtr->unit_id != UNIT_CARAVEL &&
unitPtr->unit_id != UNIT_GALLEON )
{
continue;
}
err_when( unitPtr->nation_recno < 1 || unitPtr->nation_recno > MAX_NATION );
nationShipCountArray[unitPtr->nation_recno-1]++;
}
//--- compare the no. of ships of ours and those of the human players ---//
int maxShipCount=0;
for( i=nation_array.size() ; i>0 ; i-- )
{
if( nation_array.is_deleted(i) )
continue;
if( nation_array[i]->is_ai() ) // only check human players
continue;
//-- if enemy has battle ships, it is not safe for sea travel, destroy them first ---//
if( nationShipCountArray[i-1] > maxShipCount )
{
maxShipCount = nationShipCountArray[i-1];
}
}
return maxShipCount;
}
//----- End of function Nation::max_human_battle_ship_count -----//
//------ Begin of function Nation::think_sea_attack_enemy ------//
//
// Think about attacking enemy harbors and ships.
//
int Nation::think_sea_attack_enemy()
{
if( total_ship_combat_level < 700 - (pref_military_courage + pref_use_marine)*2 ) // 300 to 700
return 0;
//-----------------------------------------//
int totalFirm = firm_array.size();
int firmRecno = m.random(totalFirm)+1;
Firm* firmPtr;
for( int i=0 ; i totalFirm )
firmRecno = 1;
if( firm_array.is_deleted(firmRecno) )
continue;
firmPtr = firm_array[firmRecno];
if( firmPtr->firm_id != FIRM_HARBOR )
continue;
if( get_relation_status(firmPtr->nation_recno) != NATION_HOSTILE )
continue;
//--- if the AI has more powerful fleets than the enemy ---//
if( total_ship_combat_level >
nation_array[firmPtr->nation_recno]->total_ship_combat_level )
{
ai_sea_attack_target(firmPtr->center_x, firmPtr->center_y);
return 1;
}
}
return 0;
}
//----- End of function Nation::think_sea_attack_enemy -----//