/* * 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 -----//