123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665 |
- /*
- * Seven Kingdoms: Ancient Adversaries
- *
- * Copyright 1997,1998 Enlight Software Ltd.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- //Filename : OAI_MARI.CPP
- //Description: AI functions on sea exploration, trading
- #include <OTOWN.h>
- #include <OREGIONS.h>
- #include <OU_MARI.h>
- #include <OUNITRES.h>
- #include <OSITE.h>
- #include <OF_HARB.h>
- #include <OF_CAMP.h>
- #include <ONATION.h>
- //--------- 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 ; i<region_array.region_stat_count ; i++, regionStat++ )
- {
- //--- only build on those regions that this nation has base towns ---//
- if( !regionStat->base_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 ; j<regionStat->reachable_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.
- //
- // <int> landRegionId - the land region id.
- // <int> seaRegionId - the sea region id.
- //
- // return: <int> 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 ; i++ )
- {
- if( ++townSeq >= 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 ; i<MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC ; i++ )
- {
- m.cal_move_around_a_point(i, MAX_WORLD_X_LOC, MAX_WORLD_Y_LOC, xOffset, yOffset);
- xLoc = homeXLoc + xOffset;
- yLoc = homeYLoc + yOffset;
- xLoc = max(0, xLoc);
- xLoc = min(MAX_WORLD_X_LOC-1, xLoc);
- yLoc = max(0, yLoc);
- yLoc = min(MAX_WORLD_Y_LOC-1, yLoc);
- locPtr = world.get_loc(xLoc, yLoc);
- if( !locPtr->can_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.
- //
- // <int> firmId - firm id.
- // <int> 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 ; i<region_array.region_stat_count ; i++, regionStat++ )
- {
- if( regionStat->nation_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 ; i<region_array.region_stat_count ; i++, regionStat++ )
- {
- //--- only move to regions in which we have camps ---//
- if( regionStat->camp_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: <int> 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: <int> 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 ; i++ )
- {
- if( ++firmRecno > 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 -----//
|