/* * 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_CAPT.CPP //Description: AI - capturing independent towns #include #include #include #include #include #include #include //------- define struct CaptureTown -------// struct CaptureTown { short town_recno; short min_resistance; }; //------ Declare static functions --------// static int sort_capture_town_function( const void *a, const void *b ); //--------- Begin of function Nation::think_capture --------// // int Nation::think_capture() { if( ai_camp_count==0 ) // this can happen when a new nation has just emerged return 0; //--- don't capture if the AI is using growth and capture strategy (as opposite to build mine strategy) ---// if( ai_mine_count==0 && total_population < 25 ) return 0; //-----------------------------------------// if( think_capture_independent() ) return 1; return 0; } //---------- End of function Nation::think_capture ---------// //--------- Begin of function Nation::think_capture_independent --------// // // Think about capturing independent towns. // int Nation::think_capture_independent() { //------- Capture target choices -------// #define MAX_CAPTURE_TOWN 30 CaptureTown capture_town_array[MAX_CAPTURE_TOWN]; short capture_town_count=0; //--- find the town that makes most sense to capture ---// int townRecno; Town* townPtr; for(townRecno=town_array.size(); townRecno>0; townRecno--) { if(town_array.is_deleted(townRecno)) continue; townPtr = town_array[townRecno]; if( townPtr->nation_recno ) // only capture independent towns continue; if( townPtr->no_neighbor_space ) // if there is no space in the neighbor area for building a new firm. continue; if( townPtr->rebel_recno ) // towns controlled by rebels will not drop in resistance even if a command base is present continue; //------ only if we have a presence/a base town in this region -----// if( !has_base_town_in_region(townPtr->region_id) ) continue; //---- check if there are already camps linked to this town ----// int i; for( i=townPtr->linked_firm_count-1 ; i>=0 ; i-- ) { Firm* firmPtr = firm_array[ townPtr->linked_firm_array[i] ]; if( firmPtr->firm_id != FIRM_CAMP ) continue; //------ if we already have a camp linked to this town -----// if( firmPtr->nation_recno == nation_recno ) break; //--- if there is an overseer with high leadership and right race in the opponent's camp, do bother to compete with him ---// if( firmPtr->overseer_recno ) { Unit* unitPtr = unit_array[firmPtr->overseer_recno]; if( unitPtr->skill.skill_level >= 70 && unitPtr->race_id == townPtr->majority_race() ) { break; } } } if( i>=0 ) // there is already a camp linked to this town and we don't want to get involved with its capturing plan continue; //-- if the town has linked military camps of the same nation --// int targetResistance = capture_expected_resistance(townRecno); int averageResistance = townPtr->average_resistance(nation_recno); int minResistance = min( averageResistance, targetResistance ); if( minResistance < 50 - pref_peacefulness/5 ) // 30 to 50 depending on { capture_town_array[capture_town_count].town_recno = townRecno; capture_town_array[capture_town_count].min_resistance = minResistance; capture_town_count++; } } //------ sort the capture target choices by min_resistance ----// qsort( &capture_town_array, capture_town_count, sizeof(capture_town_array[0]), sort_capture_town_function ); //------- try to capture the town in their resistance order ----// for( int i=0 ; icenter_x, targetTown->center_y, ownTown->center_x, ownTown->center_y); if( info.game_date-info.game_start_date > townDistance * (5-config.ai_aggressiveness) / 5 ) // 3 to 5 / 5 { break; } } if( j==ai_town_count ) continue; } if( start_capture( capture_town_array[i].town_recno ) ) return 1; } return 0; } //---------- End of function Nation::think_capture_independent ---------// //--------- Begin of function Nation::should_use_cash_to_capture --------// // int Nation::should_use_cash_to_capture() { //--- if we have plenty of cash, use cash to decrease the resistance of the villagers ---// return military_rank_rating() < 50+pref_peacefulness/5 && // 50 to 70 ai_should_spend(pref_loyalty_concern/4); } //---------- End of function Nation::should_use_cash_to_capture ---------// //--------- Begin of function Nation::capture_expected_resistance --------// // // The lowest resistance can be expected if we are going to capture the // town. // int Nation::capture_expected_resistance(int townRecno) { //--- we have plenty of cash, use cash to decrease the resistance of the villagers ---// if( should_use_cash_to_capture() ) return 0; // return zero resistance //----- the average resistance determines the captureRating ------// int captureRating = 0; Town* townPtr = town_array[townRecno]; int averageResistance; if( townPtr->nation_recno ) averageResistance = townPtr->average_loyalty(); else averageResistance = townPtr->average_resistance(nation_recno); //----- get the id. of the most populated races in the town -----// int majorityRace = townPtr->majority_race(); err_when( !majorityRace ); // this should not happen //---- see if there are general available for capturing this town ---// int targetResistance=0; if( !find_best_capturer(townRecno, majorityRace, targetResistance) ) return 100; int resultResistance = ( targetResistance * townPtr->race_pop_array[majorityRace-1] + averageResistance * (townPtr->population - townPtr->race_pop_array[majorityRace-1]) ) / townPtr->population; return resultResistance; } //---------- End of function Nation::capture_expected_resistance ---------// //--------- Begin of function Nation::start_capture --------// // int Nation::start_capture(int townRecno) { //--- find the two races with most population in the town ---// Town* townPtr = town_array[townRecno]; int majorityRace=0; //--- if it's an independent town, the race of the commander must match with the race of the town ---// if( townPtr->nation_recno == 0 ) { majorityRace = townPtr->majority_race(); err_when( !majorityRace ); // this shouldn't happen } //---- see if we have generals in the most populated race, if so build a camp next to the town ----// return capture_build_camp(townRecno, majorityRace); } //---------- End of function Nation::start_capture ---------// //--------- Begin of function Nation::capture_build_camp --------// // int Nation::capture_build_camp(int townRecno, int raceId) { Town* captureTown = town_array[townRecno]; //---- find the best available general for the capturing action ---// int targetResistance; int unitRecno = find_best_capturer(townRecno, raceId, targetResistance); if( !unitRecno ) unitRecno = hire_best_capturer(townRecno, raceId); if( !unitRecno ) { //--- if we have plenty of cash and can use cash to decrease the resistance of the independent villagers ---// if( should_use_cash_to_capture() ) { char resultFlag; Unit* skilledUnit = find_skilled_unit(SKILL_LEADING, raceId, captureTown->center_x, captureTown->center_y, resultFlag); if( skilledUnit ) unitRecno = skilledUnit->sprite_recno; } if( !unitRecno ) return 0; } //------- locate a place to build the camp -------// short buildXLoc, buildYLoc; if( !find_best_firm_loc(FIRM_CAMP, captureTown->loc_x1, captureTown->loc_y1, buildXLoc, buildYLoc) ) { captureTown->no_neighbor_space = 1; return 0; } //--- if the picked unit is an overseer of an existng camp ---// if( !mobilize_capturer(unitRecno) ) return 0; //---------- add the action to the queue now ----------// err_when( captureTown->nation_recno==0 && unit_array[unitRecno]->race_id != captureTown->majority_race() ); int actionRecno = add_action( buildXLoc, buildYLoc, captureTown->loc_x1, captureTown->loc_y1, ACTION_AI_BUILD_FIRM, FIRM_CAMP, 1, unitRecno ); if( actionRecno ) process_action(actionRecno); return 1; } //---------- End of function Nation::capture_build_camp ---------// //-------- Begin of function Nation::find_best_capturer ------// // // Find an existing unit as the capturer of the town. // // townRecno - recno of the town to capture // raceId - race id. of the capturer. 0 if any races. // bestTargetResistance - a reference var for returning the target resistance if the returned unit is assigned as the overseer // // return: the recno of the unit found. // int Nation::find_best_capturer(int townRecno, int raceId, int& bestTargetResistance) { #define MIN_CAPTURE_RESISTANCE_DEC 20 // if we assign a unit as the commander, the minimum expected resistance decrease should be 20, otherwise we don't do it. Unit* unitPtr; Town* targetTown = town_array[townRecno]; Firm* firmPtr; int targetResistance; int bestUnitRecno=0; bestTargetResistance = 100; for( int i=ai_general_count-1 ; i>=0 ; i-- ) { unitPtr = unit_array[ ai_general_array[i] ]; if( raceId && unitPtr->race_id != raceId ) continue; err_when( unitPtr->nation_recno != nation_recno ); err_when( unitPtr->rank_id != RANK_KING && unitPtr->rank_id != RANK_GENERAL ); if( unitPtr->nation_recno != nation_recno ) continue; //---- if this unit is on a mission ----// if( unitPtr->home_camp_firm_recno ) continue; //---- don't use the king to build camps next to capture enemy towns, only next to independent towns ----// if( unitPtr->rank_id == RANK_KING && targetTown->nation_recno ) continue; //----- if this unit is in a camp -------// if( unitPtr->unit_mode == UNIT_MODE_OVERSEE ) { firmPtr = firm_array[unitPtr->unit_mode_para]; //--- check if the unit currently in a command base trying to take over an independent town ---// int j; for( j=firmPtr->linked_town_count-1 ; j>=0 ; j-- ) { Town* townPtr = town_array[ firmPtr->linked_town_array[j] ]; //--- if the unit is trying to capture an independent town and he is still influencing the town to decrease resistance ---// if( townPtr->nation_recno==0 && townPtr->average_target_resistance(nation_recno) < townPtr->average_resistance(nation_recno) ) { break; // then don't use this unit } } if( j>=0 ) // if so, don't use this unit continue; } //--- if this unit is idle and the region ids are matched ---// if( unitPtr->action_mode != ACTION_STOP || unitPtr->region_id() != targetTown->region_id ) { continue; } //------- get the unit's influence index --------// err_when( unitPtr->skill.skill_id != SKILL_LEADING ); targetResistance = 100-targetTown->camp_influence(unitPtr->sprite_recno); // influence of this unit if he is assigned as a commander of a military camp //-- see if this unit's rating is higher than the current best --// if( targetResistance < bestTargetResistance ) { bestTargetResistance = targetResistance; bestUnitRecno = unitPtr->sprite_recno; } } return bestUnitRecno; } //-------- End of function Nation::find_best_capturer -------// //-------- Begin of function Nation::mobilize_capturer ------// // // Mobilize the capturer unit if he isn't mobilized yet. // int Nation::mobilize_capturer(int unitRecno) { //--- if the picked unit is an overseer of an existng camp ---// Unit* unitPtr = unit_array[unitRecno]; if( unitPtr->unit_mode == UNIT_MODE_OVERSEE ) { Firm* firmPtr = firm_array[unitPtr->unit_mode_para]; Town* townPtr; //-- can recruit from either a command base or seat of power --// //-- train a villager with leadership to replace current overseer --// int i; for( i=0 ; ilinked_town_count ; i++ ) { townPtr = town_array[ firmPtr->linked_town_array[i] ]; if( townPtr->nation_recno != nation_recno ) continue; //--- first try to train a unit who is racially homogenous to the commander ---// int unitRecno = townPtr->recruit( SKILL_LEADING, firmPtr->majority_race(), COMMAND_AI ); //--- if unsucessful, then try to train a unit whose race is the same as the majority of the town ---// if( !unitRecno ) unitRecno = townPtr->recruit( SKILL_LEADING, townPtr->majority_race(), COMMAND_AI ); if( unitRecno ) { add_action(townPtr->loc_x1, townPtr->loc_y1, -1, -1, ACTION_AI_ASSIGN_OVERSEER, FIRM_CAMP); break; } } if( i==firmPtr->linked_town_count ) // unsuccessful return 0; //------- mobilize the current overseer --------// firmPtr->mobilize_overseer(); } return 1; } //-------- End of function Nation::mobilize_capturer -------// //-------- Begin of function Nation::hire_best_capturer ------// // // Hire the best unit you can find in one of the existing inns. // // townRecno - recno of the town to capture // raceId - race id. of the unit to hire // // return: the recno of the unit hired. // int Nation::hire_best_capturer(int townRecno, int raceId) { if( !ai_should_hire_unit(30) ) // 30 - importance rating return 0; FirmInn *firmInn; Firm *firmPtr; InnUnit *innUnit; Skill *innUnitSkill; int i, j, innUnitCount, curRating; int bestRating=0, bestInnRecno=0, bestInnUnitId=0; Town* townPtr = town_array[townRecno]; int destRegionId = world.get_region_id(townPtr->loc_x1, townPtr->loc_y1); for(i=0; ifirm_id != FIRM_INN ); if( firmPtr->region_id != destRegionId ) continue; firmInn = firmPtr->cast_to_FirmInn(); innUnitCount=firmInn->inn_unit_count; if( !innUnitCount ) continue; innUnit = firmInn->inn_unit_array + innUnitCount - 1; //------- check units in the inn ---------// for(j=innUnitCount; j>0; j--, innUnit--) { innUnitSkill = &(innUnit->skill); if( innUnitSkill->skill_id==SKILL_LEADING && unit_res[innUnit->unit_id]->race_id == raceId && cash >= innUnit->hire_cost ) { //----------------------------------------------// // evalute a unit on: // -its race, whether it's the same as the nation's race // -the inn's distance from the destination // -the skill level of the unit. //----------------------------------------------// curRating = innUnitSkill->skill_level; if( curRating > bestRating ) { bestRating = curRating; bestInnRecno = firmInn->firm_recno; bestInnUnitId = j; } } } } if( !bestInnUnitId ) return 0; //----------------------------------------------------// firmInn = (FirmInn*) firm_array[bestInnRecno]; int unitRecno = firmInn->hire(bestInnUnitId); if( !unitRecno ) return 0; unit_array[unitRecno]->set_rank(RANK_GENERAL); return unitRecno; } //-------- End of function Nation::hire_best_capturer -------// //------ Begin of function sort_capture_town_function ------// // static int sort_capture_town_function( const void *a, const void *b ) { return ((CaptureTown*)a)->min_resistance - ((CaptureTown*)b)->min_resistance; } //------- End of function sort_capture_town_function ------//