/* * 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_CAP2.CPP //Description: AI - capturing AI towns #include #include #include #include #include #include #include #include //--------- Begin of function Nation::think_capture_new_enemy_town --------// // // capturerTown - our town to capture enemy towns. // [int] useAllCamp - whether use troops in all camps to attack the enemy town // (default: 0) // int Nation::think_capture_new_enemy_town(Town* capturerTown, int useAllCamp) { if( ai_camp_count==0 ) // this can happen when a new nation has just emerged return 0; if( ai_capture_enemy_town_recno ) // no new action if we are still trying to capture a town return 0; //---- only attack when we have enough money to support the war ----// if( cash < 1000 + 2000 * pref_cash_reserve / 100 ) // if the cash is really too low now return 0; //--------------------------------------// Town* targetTown = think_capture_enemy_town_target(capturerTown); if( !targetTown ) return 0; //---- attack enemy's defending forces on the target town ----// int rc = attack_enemy_town_defense(targetTown, useAllCamp); if( rc==0 ) // 0 means we don't have enough troop to attack the enemy return 0; else if( rc == 1 ) // 1 means a troop has been sent to attack the town { ai_capture_enemy_town_recno = targetTown->town_recno; // this nation is currently trying to capture this town ai_capture_enemy_town_plan_date = info.game_date; ai_capture_enemy_town_start_attack_date = 0; ai_capture_enemy_town_use_all_camp = useAllCamp; return 1; } else if( rc == -1 ) // -1 means no defense on the target town, no attacking is needed. { return start_capture( targetTown->town_recno ); // call AI functions in OAI_CAPT.CPP to capture the town } return 0; } //---------- End of function Nation::think_capture_new_enemy_town --------// //--------- Begin of function Nation::think_capturing_enemy_town --------// void Nation::think_capturing_enemy_town() { if( !ai_capture_enemy_town_recno ) return; if( town_array.is_deleted(ai_capture_enemy_town_recno) || town_array[ai_capture_enemy_town_recno]->nation_recno == nation_recno ) // this town has been captured already { ai_capture_enemy_town_recno = 0; return; } //--- check the enemy's mobile defense combat level around the town ---// Town* targetTown = town_array[ai_capture_enemy_town_recno]; int hasWar; int mobileCombatLevel = mobile_defense_combat_level(targetTown->center_x, targetTown->center_y, targetTown->nation_recno, 0, hasWar); // 0-don't return immediately even if there is war around this town //---- if we haven't started attacking the town yet -----// if( !ai_capture_enemy_town_start_attack_date ) { if( hasWar==2 ) // we are at war with the nation now ai_capture_enemy_town_start_attack_date = info.game_date; if( info.game_date > ai_capture_enemy_town_plan_date + 90 ) // when 3 months have gone and there still hasn't been any attack on the town, there must be something bad happened to our troop, cancel the entire action ai_capture_enemy_town_recno = 0; return; // do nothing if the attack hasn't started yet } //--------- check if we need reinforcement --------// //-----------------------------------------------------------// // Check how long we have started attacking because only // when the it has been started for a while, our force // will reach the target and the offensive and defensive force // total can be calculated accurately. //-----------------------------------------------------------// if( info.game_date - ai_capture_enemy_town_start_attack_date >= 15 ) { //-------- check if we need any reinforcement --------// if( mobileCombatLevel > 0 && hasWar==2 ) // we are still in war with the enemy { ai_attack_target(targetTown->center_x, targetTown->center_y, mobileCombatLevel, 0, 1 ); // 1-just all move there and wait for the units to attack the enemies automatically return; } } //----- there is currently no war at the town -----// // // - either we are defeated or we have destroyed their command base. // //--------------------------------------------------// if( hasWar != 2 ) { //---- attack enemy's defending forces on the target town ----// int rc = attack_enemy_town_defense(targetTown, ai_capture_enemy_town_use_all_camp); if( rc == 1 ) // 1 means a troop has been sent to attack the town { ai_capture_enemy_town_start_attack_date = 0; return; } //---------- reset the vars --------// ai_capture_enemy_town_recno = 0; ai_capture_enemy_town_start_attack_date = 0; //--------- other situations --------// if( rc == -1 ) // -1 means no defense on the target town, no attacking is needed. { start_capture( targetTown->town_recno ); // call AI functions in OAI_CAPT.CPP to capture the town } // 0 means we don't have enough troop to attack the enemy } } //---------- End of function Nation::think_capturing_enemy_town --------// //--------- Begin of function Nation::attack_enemy_town_defense --------// // // Attack enemy's defending forces on the target town. // // targetTown - the pointer to the target town. // [int] useAllCamp - whether use troops in all camps to attack the enemy town // (default: 0) // // return: 1 - a troop has been sent to attack the target. // 0 - we don't have sufficient troops for attacking the target. // -1 - no defense on the target town, no attacking is needed. // int Nation::attack_enemy_town_defense(Town* targetTown, int useAllCamp) { err_when( targetTown->nation_recno == nation_recno ); // cannot attack itself if( targetTown->nation_recno == 0 ) return -1; //--- if there are any command bases linked to the town, attack them first ---// int campCombatLevel, maxCampCombatLevel= -1; Firm *firmPtr, *bestTargetFirm=NULL; for( int i=targetTown->linked_firm_count-1 ; i>=0 ; i-- ) { firmPtr = firm_array[ targetTown->linked_firm_array[i] ]; if( firmPtr->nation_recno == targetTown->nation_recno && firmPtr->firm_id == FIRM_CAMP ) { campCombatLevel = ((FirmCamp*)firmPtr)->total_combat_level(); if( campCombatLevel > maxCampCombatLevel ) { maxCampCombatLevel = campCombatLevel; bestTargetFirm = firmPtr; } } } //----- get the defense combat level of the mobile units around the town ----// int hasWar; int townMobileCombatLevel = mobile_defense_combat_level(targetTown->center_x, targetTown->center_y, targetTown->nation_recno, 0, hasWar); int totalDefenseCombatLevel = maxCampCombatLevel + townMobileCombatLevel; //----------------------------------------// if( bestTargetFirm ) { Nation* targetNation = nation_array[bestTargetFirm->nation_recno]; if( targetNation->is_at_war() ) // use all camps force if the nation is at war useAllCamp = 1; return ai_attack_target(bestTargetFirm->loc_x1, bestTargetFirm->loc_y1, totalDefenseCombatLevel, 0, 0, 0, 0, useAllCamp ); } else { //--- if there are any mobile defense force around the town ----// if( townMobileCombatLevel > 0 ) return ai_attack_target(targetTown->center_x, targetTown->center_y, totalDefenseCombatLevel, 0, 1 ); // 1-just all move there and wait for the units to attack the enemies automatically } return -1; } //---------- End of function Nation::attack_enemy_town_defense --------// //--------- Begin of function Nation::think_capture_enemy_town_target --------// // // capturerTown - our town to capture enemy towns. // // Motives for attacking another nation: // // 1. Capture towns // 2. Conquer land // 3. Defeat enemies // Town* Nation::think_capture_enemy_town_target(Town* capturerTown) { int townRecno, curRating; Town* targetTown, *bestTown=NULL; Firm* firmPtr; int ourMilitary = military_rank_rating(); Nation* ownNation = nation_array[nation_recno]; int bestRating = -1000; int hasWar; int neededCombatLevel=0; for( townRecno=town_array.size() ; townRecno>0 ; townRecno-- ) { if( town_array.is_deleted(townRecno) ) continue; targetTown = town_array[townRecno]; if( targetTown->nation_recno == 0 || targetTown->nation_recno == nation_recno ) { continue; } if( targetTown->region_id != capturerTown->region_id ) continue; //----- if we have already built a camp next to this town -----// if( targetTown->has_linked_camp(nation_recno, 0) ) //0-count both camps with or without overseers continue; //--------- only attack enemies -----------// NationRelation* nationRelation = get_relation(targetTown->nation_recno); int rc=0; if( nationRelation->status == NATION_HOSTILE ) rc = 1; else if( nationRelation->ai_relation_level < 10 ) // even if the relation is not hostile, if the ai_relation_level is < 10, attack anyway rc = 1; else if( nationRelation->status <= NATION_NEUTRAL && targetTown->nation_recno == nation_array.max_overall_nation_recno && // if this is our biggest enemy nationRelation->ai_relation_level < 30 ) { rc = 1; } if( !rc ) continue; //----- if this town does not have any linked camps, capture this town immediately -----// if( targetTown->has_linked_camp(targetTown->nation_recno, 0) ) //0-count both camps with or without overseers return targetTown; //--- if the enemy is very powerful overall, don't attack it yet ---// if( nation_array[targetTown->nation_recno]->military_rank_rating() > ourMilitary * (80+pref_military_courage/2) / 100 ) { continue; } //------ only attack if we have enough money to support the war ----// if( !ai_should_spend_war( nation_array[targetTown->nation_recno]->military_rank_rating() ) ) continue; //-------------------------------------------------------// int townCombatLevel = enemy_town_combat_level(targetTown, 1, hasWar); // 1-return a rating if there is war with the town if( townCombatLevel == -1 ) // do not attack this town because a battle is already going on continue; //------- calculate the rating --------------// curRating = world.distance_rating(capturerTown->center_x, capturerTown->center_y, targetTown->center_x, targetTown->center_y); curRating -= townCombatLevel/10; curRating -= targetTown->average_loyalty(); curRating += targetTown->population; // put a preference on capturing villages with large population //----- the power of between the nation also affect the rating ----// curRating += 2 * (ourMilitary - nation_array[targetTown->nation_recno]->military_rank_rating()); //-- AI Aggressive is set above Low, than the AI will try to capture the player's town first ---// if( !targetTown->ai_town ) { if( game.game_mode == GAME_TUTORIAL ) // next attack the player in a tutorial game { continue; } else { switch( config.ai_aggressiveness ) { case OPTION_MODERATE: curRating += 100; break; case OPTION_HIGH: curRating += 300; break; case OPTION_VERY_HIGH: curRating += 500; break; } } } //--- if there are mines linked to this town, increase its rating ---// for( int i=targetTown->linked_firm_count-1 ; i>=0 ; i-- ) { firmPtr = firm_array[ targetTown->linked_firm_array[i] ]; if( firmPtr->nation_recno != targetTown->nation_recno ) continue; if( firmPtr->firm_id == FIRM_MINE ) { //--- if this mine's raw materials is one that we don't have --// if( raw_count_array[ ((FirmMine*)firmPtr)->raw_id-1 ]==0 ) curRating += 150 * (int) ((FirmMine*)firmPtr)->reserve_qty / MAX_RAW_RESERVE_QTY; } } //--- more linked towns increase the attractiveness rating ---// curRating += targetTown->linked_firm_count*5; //-------- compare with the current best rating ---------// if( curRating > bestRating ) { bestRating = curRating; bestTown = targetTown; neededCombatLevel = townCombatLevel; } } return bestTown; } //-------- End of function Nation::think_capture_enemy_town_target ------// //--------- Begin of function Nation::enemy_town_combat_level --------// // // targetTown - the target town // returnIfWar - return -1 if there is any war around the town // hasWar - a reference var for returning whether there is any war // // return: the enemy's total defense combat level minus the player's // combat level. /// return -1 if there is war and returnIfWar is 1 // int Nation::enemy_town_combat_level(Town* targetTown, int returnIfWar, int hasWar) { int enemyCombatLevel = mobile_defense_combat_level(targetTown->center_x, targetTown->center_y, targetTown->nation_recno, returnIfWar, hasWar); //0-don't return even there are wars around the town if( enemyCombatLevel < 0 ) // there is a war going on return -1; //---- calculate the attack rating of this target town ----// // enemyCombatLevel += targetTown->jobless_population * 5; //**BUGHERE //--- calculate the combat level of enemy camps linked to this town ---// Firm* firmPtr; for( int i=targetTown->linked_firm_count-1 ; i>=0 ; i-- ) { firmPtr = firm_array[ targetTown->linked_firm_array[i] ]; if( firmPtr->nation_recno == targetTown->nation_recno && firmPtr->firm_id == FIRM_CAMP ) { enemyCombatLevel += ((FirmCamp*)firmPtr)->total_combat_level(); } } /* //----- add this and neighbor town's needed combat level ----// Town* townPtr; for( i=targetTown->linked_town_count-1 ; i>=0 ; i-- ) { townPtr = town_array[ targetTown->linked_town_array[i] ]; if( townPtr->nation_recno == targetTown->nation_recno ) //**BUGHERE enemyCombatLevel += townPtr->jobless_population * 5; } */ return enemyCombatLevel; } //-------- End of function Nation::enemy_town_combat_level ------// //--------- Begin of function Nation::enemy_firm_combat_level --------// // // targetFirm - the target firm // returnIfWar - return -1 if there is any war around the town // hasWar - a reference var for returning whether there is any war // // return: the enemy's total defense combat level minus the player's // combat level. /// return -1 if there is war and returnIfWar is 1 // int Nation::enemy_firm_combat_level(Firm* targetFirm, int returnIfWar, int hasWar) { int enemyCombatLevel = mobile_defense_combat_level(targetFirm->center_x, targetFirm->center_y, targetFirm->nation_recno, returnIfWar, hasWar); //0-don't return even there are wars around the town if( enemyCombatLevel < 0 ) // there is a war going on return -1; //--- calculate the combat level of enemy camps linked to this towns that are linked to this mine ---// Town* linkedTown; Firm* firmPtr; int targetNationRecno = targetFirm->nation_recno; //---- scan towns linked to this mine -----// for( int i=targetFirm->linked_town_count-1 ; i>=0 ; i-- ) { linkedTown = town_array[ targetFirm->linked_town_array[i] ]; if( linkedTown->nation_recno != targetNationRecno ) continue; //------ scan firms linked to this town -------// for( int j=linkedTown->linked_firm_count-1 ; j>=0 ; j-- ) { firmPtr = firm_array[ linkedTown->linked_firm_array[j] ]; if( firmPtr->nation_recno == targetNationRecno && firmPtr->firm_id == FIRM_CAMP ) { enemyCombatLevel += ((FirmCamp*)firmPtr)->total_combat_level(); } } } return enemyCombatLevel; } //-------- End of function Nation::enemy_firm_combat_level ------// //------- Begin of function Nation::mobile_defense_combat_level ------// // // Take into account of the mobile units around this target location // when considering attacking it. // // targetXLoc, targetYLoc - the target location // targetNationRecno - nation recno of the target // returnIfWar - whether return -1 if there is war // around the given area. // hasWar - a var for returning whether there is war // around the given area. // 1 - if there is war // 2 - if our nation is involved in the war // // return : >= 0 the defense rating of this location, the rating can be < 0, // if we have our own units there. // // -1 don't attack this town because a battle is already // going on. // int Nation::mobile_defense_combat_level(int targetXLoc, int targetYLoc, int targetNationRecno, int returnIfWar, int& hasWar) { //--- the scanning distance is determined by the AI aggressiveness setting ---// int scanRangeX = 5 + config.ai_aggressiveness * 2; int scanRangeY = scanRangeX; int xLoc1 = targetXLoc - scanRangeX; int yLoc1 = targetYLoc - scanRangeY; int xLoc2 = targetXLoc + scanRangeX; int yLoc2 = targetYLoc + scanRangeY; xLoc1 = max( xLoc1, 0 ); yLoc1 = max( yLoc1, 0 ); xLoc2 = min( xLoc2, MAX_WORLD_X_LOC-1 ); yLoc2 = min( yLoc2, MAX_WORLD_Y_LOC-1 ); //------------------------------------------// float totalCombatLevel=(float)0; // the higher the rating, the easier we can attack the target town. int xLoc, yLoc; Unit* unitPtr; Location* locPtr; hasWar = 0; for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ ) { locPtr = world.get_loc(xLoc1, yLoc); for( xLoc=xLoc1 ; xLoc<=xLoc2 ; xLoc++, locPtr++ ) { if( !locPtr->has_unit(UNIT_LAND) ) continue; unitPtr = unit_array[ locPtr->unit_recno(UNIT_LAND) ]; //--------------------------------------------------// // If there is already a battle going on in this town, // do not attack this town. //--------------------------------------------------// if( unitPtr->cur_action == SPRITE_ATTACK ) { if( returnIfWar ) return -1; else { if( unitPtr->nation_recno == nation_recno ) hasWar = 2; else hasWar = 1; } } //---- if this unit is guarding the town -----// if( unitPtr->nation_recno == targetNationRecno ) { totalCombatLevel += unitPtr->unit_power(); } //------- if this is our own unit ------// else if( unitPtr->nation_recno == nation_recno ) { if( unitPtr->cur_action == SPRITE_ATTACK || // only units that are currently attacking or idle are counted, moving units may just be passing by unitPtr->cur_action == SPRITE_IDLE ) { totalCombatLevel -= unitPtr->unit_power(); } } } } if( totalCombatLevel == -1 ) // -1 is reserved for returning don't attack return 0; else return (int) totalCombatLevel; } //-------- End of function Nation::mobile_defense_combat_level ------//