/* * 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 : OUNITAI.CPP //Description : Object Unit AI #include #include #include #include #include #include #include #include #ifdef NO_DEBUG_UNIT #undef err_when #undef err_here #undef err_if #undef err_else #undef err_now #define err_when(cond) #define err_here() #define err_if(cond) #define err_else #define err_now(msg) #undef DEBUG #endif //--------- Begin of function Unit::process_ai --------// // // [int] forceExecute - whether force execute all AI functions // without checking day interavals. // (default: 0) // void Unit::process_ai() { err_when( !nation_recno ); //-*********** simulate aat ************-// #ifdef DEBUG if(debug_sim_game_type) return; #endif //-*********** simulate aat ************-// //------ the aggressive_mode of AI units is always 1 ------// aggressive_mode = 1; //------- handle Seek Path failures ------// if( ai_handle_seek_path_fail() ) return; //--- if it's a spy from other nation, don't control it ---// if( spy_recno && true_nation_recno() != nation_recno ) { //--- a random chance of the AI catching the spy and resign it ---// if( is_visible() && m.random(365 * FRAMES_PER_DAY)==0 ) // if the unit stay outside for one year, it will get caught { stop2(); resign(COMMAND_AI); return; } if( !spy_array[spy_recno]->notify_cloaked_nation_flag ) // if notify_cloaked_nation_flag is 1, the nation will take it as its own spies return; } //----- think about rewarding this unit -----// if( race_id && rank_id != RANK_KING && info.game_date%5 == sprite_recno%5 ) { think_reward(); } //-----------------------------------------// if( !is_visible() ) return; //--- if the unit has stopped, but ai_action_id hasn't been reset ---// if( cur_action==SPRITE_IDLE && action_mode==ACTION_STOP && action_mode2==ACTION_STOP && ai_action_id ) { nation_array[nation_recno]->action_failure(ai_action_id, sprite_recno); err_when( ai_action_id ); // it should have been reset } //---- King flees under attack or surrounded by enemy ---// if( race_id && rank_id==RANK_KING ) { if( think_king_flee() ) return; } //---- General flees under attack or surrounded by enemy ---// if( race_id && rank_id==RANK_GENERAL && info.game_date%7 == sprite_recno%7 ) { if( think_general_flee() ) return; } //-- let Unit::next_day() process it process original_action_mode --// if( original_action_mode ) return; //------ if the unit is not stop right now ------// if( !is_ai_all_stop() ) { think_stop_chase(); return; } //-----------------------------------------// if( mobile_type==UNIT_LAND ) { if( ai_escape_fire() ) return; } //---------- if this is your spy --------// if( spy_recno && true_nation_recno()==nation_recno ) think_spy_action(); //------ if this unit is from a camp --------// if( home_camp_firm_recno ) { Firm* firmCamp = firm_array[home_camp_firm_recno]; int rc; if( rank_id == RANK_SOLDIER ) rc = firmCamp->worker_count < MAX_WORKER; else rc = !firmCamp->overseer_recno; if( rc ) { if( return_camp() ) return; } home_camp_firm_recno = 0; // the camp is already occupied by somebody } //----------------------------------------// if( race_id && rank_id==RANK_KING ) { think_king_action(); } else if( race_id && rank_id==RANK_GENERAL ) { think_general_action(); } else { if( unit_res[unit_id]->unit_class == UNIT_CLASS_WEAPON ) { if( info.game_date%15 == sprite_recno%15 ) // don't call too often as the action may fail and it takes a while to call the function each time { think_weapon_action(); //-- ships AI are called in UnitMarine --// } } else if( race_id ) { //--- if previous attempts for new action failed, don't call think_normal_human_action() so frequently then ---// if( ai_no_suitable_action ) { if( info.game_date%15 != sprite_recno%15 ) // don't call too often as the action may fail and it takes a while to call the function each time return; } //---------------------------------// if( !think_normal_human_action() ) { ai_no_suitable_action = 1; // set this flag so think_normal_human_action() won't be called continously if( !leader_unit_recno ) // only when the unit is not led by a commander { resign(COMMAND_AI); } else { ai_move_to_nearby_town(); } } } } } //---------- End of function Unit::process_ai --------// //--------- Begin of function Unit::think_stop_chase --------// int Unit::think_stop_chase() { //-----------------------------------------------------// // // Stop the chase if the target is being far away from // its original attacking location. // //-----------------------------------------------------// if( !(action_mode==ACTION_ATTACK_UNIT && ai_original_target_x_loc>=0) ) return 0; if( unit_array.is_deleted(action_para) ) { stop2(); return 1; } Unit* targetUnit = unit_array[action_para]; if( !targetUnit->is_visible() ) { stop2(); return 1; } //----------------------------------------// int aiChaseDistance = 10 + nation_array[nation_recno]->pref_military_courage/20; // chase distance: 10 to 15 int curDistance = m.points_distance( targetUnit->next_x_loc(), targetUnit->next_y_loc(), ai_original_target_x_loc, ai_original_target_y_loc ); if( curDistance <= aiChaseDistance ) return 0; //--------- stop the unit ----------------// stop2(); //--- if this unit leads a troop, stop the action of all troop members as well ---// int leaderUnitRecno; if( leader_unit_recno ) leaderUnitRecno = leader_unit_recno; else leaderUnitRecno = sprite_recno; TeamInfo* teamInfo = unit_array[leaderUnitRecno]->team_info; if( teamInfo ) { for( int i=teamInfo->member_count-1 ; i>=0 ; i-- ) { int unitRecno = teamInfo->member_unit_array[i]; if( unit_array.is_deleted(unitRecno) ) continue; unit_array[unitRecno]->stop2(); } } return 1; } //---------- End of function Unit::think_stop_chase --------// //--------- Begin of function Unit::is_ai_all_stop --------// int Unit::is_ai_all_stop() { return cur_action==SPRITE_IDLE && action_mode==ACTION_STOP && action_mode2==ACTION_STOP && !ai_action_id; } //---------- End of function Unit::is_ai_all_stop --------// //--------- Begin of function Unit::ai_move_to_nearby_town --------// void Unit::ai_move_to_nearby_town() { //---- look for towns to assign to -----// Nation* ownNation = nation_array[nation_recno]; Town *townPtr, *bestTown=NULL; int regionId = world.get_region_id( next_x_loc(), next_y_loc() ); int curDistance, curRating, bestRating=0; int curXLoc = next_x_loc(), curYLoc = next_y_loc(); for( int i=town_array.size() ; i>0 ; i-- ) // can't use ai_town_array[] because this function will be called by Unit::betray() when a unit defected to the player's kingdom { if( town_array.is_deleted(i) ) continue; townPtr = town_array[i]; if( townPtr->nation_recno != nation_recno ) continue; if( townPtr->region_id != regionId ) continue; //-------------------------------------// curDistance = m.points_distance(curXLoc, curYLoc, townPtr->center_x, townPtr->center_y ); if( curDistance < 10 ) // no need to move if the unit is already close enough to the town. return; curRating = 100 - 100 * curDistance / MAX_WORLD_X_LOC; curRating += townPtr->population; //-------------------------------------// if( curRating > bestRating ) { bestRating = curRating; bestTown = townPtr; } } if( bestTown ) move_to_town_surround(bestTown->loc_x1, bestTown->loc_y1, sprite_info->loc_width, sprite_info->loc_height); } //---------- End of function Unit::ai_move_to_nearby_town --------// //--------- Begin of function Unit::ai_escape_fire --------// // // Move away if the unit currently stands on a burning ground. // int Unit::ai_escape_fire() { if(cur_action!=SPRITE_IDLE) return 0; if(mobile_type!=UNIT_LAND) return 0; Location *locPtr = world.get_loc(next_x_loc(), next_y_loc()); if( !locPtr->fire_str() ) return 0; //--------------------------------------------// int checkLimit = 400; // checking for 400 location int xShift, yShift, checkXLoc, checkYLoc; int curXLoc = next_x_loc(); int curYLoc = next_y_loc(); for(int i=2; i=MAX_WORLD_X_LOC || checkYLoc<0 || checkYLoc>=MAX_WORLD_Y_LOC) continue; if(!locPtr->can_move(mobile_type)) continue; locPtr = world.get_loc(checkXLoc, checkYLoc); if( locPtr->fire_str()==0 ) // move to a safe place now { move_to(checkXLoc, checkYLoc); return 1; } } return 0; } //---------- End of function Unit::ai_escape_fire --------// //--------- Begin of function Unit::think_spy_action --------// void Unit::think_spy_action() { ai_move_to_nearby_town(); // just move it to one of our towns } //---------- End of function Unit::think_spy_action --------// //--------- Begin of function Unit::think_king_action --------// int Unit::think_king_action() { return think_leader_action(); } //---------- End of function Unit::think_king_action --------// //--------- Begin of function Unit::think_general_action --------// int Unit::think_general_action() { if( think_leader_action() ) return 1; //--- if the general is not assigned to a camp due to its low competency ----// Nation* ownNation = nation_array[nation_recno]; int rc = 0; if( team_info->member_count <= 1 ) { rc = 1; } //--- if the skill of the general and the number of soldiers he commands is not large enough to justify building a new camp ---// else if( skill.skill_level + team_info->member_count*4 < 40 + ownNation->pref_keep_general/5 ) // 40 to 60 { rc = 1; } //-- think about splitting the team and assign them into other forts --// else if( ownNation->ai_has_too_many_camp() ) { rc = 1; } //--------- demote the general to soldier and disband the troop -------// if( rc ) { set_rank(RANK_SOLDIER); return think_normal_human_action(); } return 0; } //---------- End of function Unit::think_general_action --------// //--------- Begin of function Unit::think_leader_action --------// // // Think about the action of a leader (either a general or a king). // int Unit::think_leader_action() { Nation* nationPtr = nation_array[nation_recno]; FirmCamp *firmCamp, *bestCamp=NULL; int curRating, bestRating=10, delActionRecno=0; int curXLoc = next_x_loc(), curYLoc = next_y_loc(); int curRegionId = world.get_region_id( curXLoc, curYLoc ); if( rank_id == RANK_KING ) // if this unit is the king, always assign it to a camp regardless of whether the king is a better commander than the existing one bestRating = -1000; err_when( skill.skill_id != SKILL_LEADING ); //----- think about which camp to move to -----// for( int i=nationPtr->ai_camp_count-1 ; i>=0 ; i-- ) { firmCamp = (FirmCamp*) firm_array[ nationPtr->ai_camp_array[i] ]; if( firmCamp->region_id != curRegionId ) continue; //--- if the commander of this camp is the king, never replace him ---// if( firmCamp->overseer_recno == nationPtr->king_unit_recno ) continue; //-------------------------------------// int curLeadership = firmCamp->cur_commander_leadership(); int newLeadership = firmCamp->new_commander_leadership(race_id, skill.skill_level); curRating = newLeadership - curLeadership; //-------------------------------------// if( curRating > bestRating ) { //--- if there is already somebody being assigned to it ---// int actionRecno=0; if( rank_id != RANK_KING ) // don't check this if the current unit is the king { actionRecno = nationPtr->is_action_exist(firmCamp->loc_x1, firmCamp->loc_y1, -1, -1, ACTION_AI_ASSIGN_OVERSEER, FIRM_CAMP); if( actionRecno && nationPtr->get_action(actionRecno)->processing_instance_count ) continue; } bestRating = curRating; bestCamp = firmCamp; delActionRecno = actionRecno; } } if( !bestCamp ) return 0; //----- delete an unprocessed queued action if there is any ----// if( delActionRecno ) nationPtr->del_action(delActionRecno); //--------- move to the camp now ---------// //-- if there is room in the camp to host all soldiers led by this general --// if( team_info->member_count-1 <= MAX_WORKER-bestCamp->worker_count ) { validate_team(); unit_array.assign( bestCamp->loc_x1, bestCamp->loc_y1, 0, COMMAND_AI, team_info->member_unit_array, team_info->member_count ); return 1; } else //--- otherwise assign the general only ---// { return nationPtr->add_action(bestCamp->loc_x1, bestCamp->loc_y1, -1, -1, ACTION_AI_ASSIGN_OVERSEER, FIRM_CAMP, 1, sprite_recno); } return 0; } //---------- End of function Unit::think_leader_action --------// //--------- Begin of function Unit::think_normal_human_action --------// int Unit::think_normal_human_action() { if( home_camp_firm_recno ) return 0; if( leader_unit_recno && unit_array[leader_unit_recno]->is_visible() ) // if the unit is led by a commander, let the commander makes the decision. If the leader has been assigned to a firm, don't consider it as a leader anymore { return 0; } err_when( !race_id ); err_when( !nation_recno ); //---- think about assign the unit to a firm that needs workers ----// Nation* ownNation = nation_array[nation_recno]; Firm *firmPtr, *bestFirm=NULL; int regionId = world.get_region_id( next_x_loc(), next_y_loc() ); int skillId = skill.skill_id; int skillLevel = skill.skill_level; int i, curRating, bestRating=0; int curXLoc = next_x_loc(), curYLoc = next_y_loc(); if( skill.skill_id ) { for( i=firm_array.size() ; i>0 ; i-- ) { if( firm_array.is_deleted(i) ) continue; firmPtr = firm_array[i]; if( firmPtr->nation_recno != nation_recno ) continue; if( firmPtr->region_id != regionId ) continue; curRating = 0; if( skill.skill_id == SKILL_CONSTRUCTION ) // if this is a construction worker { if( firmPtr->builder_recno ) // assign the construction worker to this firm as an residental builder continue; } else { if( !firmPtr->worker_array || firmPtr->firm_skill_id != skillId ) { continue; } //----- if the firm is full of worker ------// if( firmPtr->is_worker_full() ) { //---- get the lowest skill worker of the firm -----// Worker* workerPtr = firmPtr->worker_array; int minSkill=100; for( int j=0 ; jworker_count ; j++, workerPtr++ ) { if( workerPtr->skill_level < minSkill ) minSkill = workerPtr->skill_level; } //------------------------------// if( firmPtr->majority_race() == race_id ) { if( skill.skill_level < minSkill+10 ) continue; } else //-- for different race, only assign if the skill is significantly higher than the existing ones --// { if( skill.skill_level < minSkill+30 ) continue; } } else { curRating += 300; // if the firm is not full, rating + 300 } } //-------- calculate the rating ---------// curRating += world.distance_rating( curXLoc, curYLoc, firmPtr->center_x, firmPtr->center_y ); if( firmPtr->majority_race() == race_id ) curRating += 70; curRating += (MAX_WORKER - firmPtr->worker_count) * 10; //-------------------------------------// if( curRating > bestRating ) { bestRating = curRating; bestFirm = firmPtr; } } if( bestFirm ) { assign(bestFirm->loc_x1, bestFirm->loc_y1); return 1; } } //---- look for towns to assign to -----// bestRating = 0; int hasTownInRegion=0; Town *townPtr, *bestTown=NULL; for( i=town_array.size() ; i>0 ; i-- ) // can't use ai_town_array[] because this function will be called by Unit::betray() when a unit defected to the player's kingdom { if( town_array.is_deleted(i) ) continue; townPtr = town_array[i]; if( townPtr->nation_recno != nation_recno ) continue; if( townPtr->region_id != regionId ) continue; hasTownInRegion = 1; if( townPtr->population >= MAX_TOWN_POPULATION || !townPtr->is_base_town ) continue; //--- only assign to towns of the same race ---// if( ownNation->pref_town_harmony > 50 ) { if( townPtr->majority_race() != race_id ) continue; } //-------- calculate the rating ---------// curRating = world.distance_rating(curXLoc, curYLoc, townPtr->center_x, townPtr->center_y ); curRating += 300 * townPtr->race_pop_array[race_id-1] / townPtr->population; // racial homogenous bonus curRating += MAX_TOWN_POPULATION - townPtr->population; //-------------------------------------// if( curRating > bestRating ) { bestRating = curRating; bestTown = townPtr; } } if( bestTown ) { assign(bestTown->loc_x1, bestTown->loc_y1); return 1; } //----- if we don't have any existing towns in this region ----// if( !hasTownInRegion ) { //#ifdef AMPLUS // --- if region is too small don't consider this area, stay in the island forever --// if( region_array[regionId]->region_stat_id == 0 ) return 0; //#endif //-- if we also don't have any existing camps in this region --// if( region_array.get_region_stat(regionId)->camp_nation_count_array[nation_recno-1]==0 ) { //---- try to build one if this unit can ----// if( ownNation->cash > firm_res[FIRM_CAMP]->setup_cost && firm_res[FIRM_CAMP]->can_build(sprite_recno) && !leader_unit_recno ) // if this unit is commanded by a leader, let the leader build the camp { ai_build_camp(); } } else // if there is already a camp in this region, try to settle a new town next to the camp { ai_settle_new_town(); } return 1; // if we don't have any town in this region, return 1, so this unit won't be resigned and so it can wait for other units to set up camps and villages later ---// } return 0; } //---------- End of function Unit::think_normal_human_action --------// //--------- Begin of function Unit::think_weapon_action --------// int Unit::think_weapon_action() { //---- first try to assign the weapon to an existing camp ----// if( think_assign_weapon_to_camp() ) return 1; //----- if no camp to assign, build a new one ------// if( think_build_camp() ) return 1; return 0; } //---------- End of function Unit::think_weapon_action --------// //--------- Begin of function Unit::think_assign_weapon_to_camp --------// int Unit::think_assign_weapon_to_camp() { Nation *nationPtr = nation_array[nation_recno]; FirmCamp *firmCamp, *bestCamp=NULL; int curRating=0, bestRating=0; int regionId = world.get_region_id( next_x_loc(), next_y_loc() ); int curXLoc = next_x_loc(), curYLoc = next_y_loc(); for( int i=0 ; iai_camp_count ; i++ ) { firmCamp = (FirmCamp*) firm_array[ nationPtr->ai_camp_array[i] ]; if( firmCamp->region_id != regionId || firmCamp->is_worker_full() ) continue; //-------- calculate the rating ---------// curRating = world.distance_rating(curXLoc, curYLoc, firmCamp->center_x, firmCamp->center_y ); curRating += (MAX_WORKER - firmCamp->worker_count) * 10; //-------------------------------------// if( curRating > bestRating ) { bestRating = curRating; bestCamp = firmCamp; } } //-----------------------------------// if( bestCamp ) { assign(bestCamp->loc_x1, bestCamp->loc_y1); return 1; } return 0; } //---------- End of function Unit::think_assign_weapon_to_camp --------// //--------- Begin of function Unit::think_build_camp --------// // // Think about building a camp next to the town which is // closest to the unit. // int Unit::think_build_camp() { //---- select a town to build the camp ---// Nation* ownNation = nation_array[nation_recno]; Town *townPtr, *bestTown=NULL; int curRating=0, bestRating=0; int regionId = world.get_region_id( next_x_loc(), next_y_loc() ); int curXLoc = next_x_loc(), curYLoc = next_y_loc(); for( int i=ownNation->ai_town_count-1 ; i>=0 ; i-- ) { townPtr = town_array[ ownNation->ai_town_array[i] ]; if( townPtr->region_id != regionId ) continue; if( !townPtr->is_base_town || townPtr->no_neighbor_space ) continue; curRating = world.distance_rating(curXLoc, curYLoc, townPtr->center_x, townPtr->center_y ); if( curRating > bestRating ) { bestRating = curRating; bestTown = townPtr; } } if( bestTown ) return bestTown->ai_build_neighbor_firm(FIRM_CAMP); return 0; } //---------- End of function Unit::think_build_camp --------// //--------- Begin of function Unit::think_reward --------// int Unit::think_reward() { Nation* ownNation = nation_array[nation_recno]; //----------------------------------------------------------// // The need to secure high loyalty on this unit is based on: // -its skill // -its combat level // -soldiers commanded by this unit //----------------------------------------------------------// if( spy_recno && true_nation_recno() == nation_recno ) // if this is a spy of ours { return 0; // Spy::think_reward() will handle this. } int curLoyalty = loyalty; int neededLoyalty; //----- if this unit is on a mission ------/ if( ai_action_id ) { neededLoyalty = UNIT_BETRAY_LOYALTY+10; } //----- otherwise only reward soldiers and generals ------// else if( skill.skill_id == SKILL_LEADING ) { //----- calculate the needed loyalty --------// neededLoyalty = commanded_soldier_count()*5 + skill.skill_level; if( unit_mode == UNIT_MODE_OVERSEE ) // if this unit is an overseer { if( loyalty < UNIT_BETRAY_LOYALTY ) // if this unit's loyalty is < betrayel level, reward immediately { reward(nation_recno); // reward it immediatley if it's an overseer, don't check ai_should_spend() return 1; } neededLoyalty += 30; } neededLoyalty = max( UNIT_BETRAY_LOYALTY+10, neededLoyalty ); // 10 points above the betray loyalty level to prevent betrayal neededLoyalty = min( 100, neededLoyalty ); } else { return 0; } //------- if the loyalty is already high enough ------// if( curLoyalty >= neededLoyalty ) return 0; //---------- see how many cash & profit we have now ---------// int rewardNeedRating = neededLoyalty - curLoyalty; if( curLoyalty < UNIT_BETRAY_LOYALTY+5 ) rewardNeedRating += 50; if( ownNation->ai_should_spend(rewardNeedRating) ) { reward(nation_recno); return 1; } return 0; } //---------- End of function Unit::think_reward --------// //--------- Begin of function Unit::ai_leader_being_attacked --------// // // This function is called when the king is under attack. // // attackerUnitRecno - recno of the attacker unit. // void Unit::ai_leader_being_attacked(int attackerUnitRecno) { err_when( !team_info ); if( unit_array[attackerUnitRecno]->nation_recno == nation_recno ) // this can happen when the unit has just changed nation return; //------------------------------------// int rc=0, callIntervalDays; if( rank_id == RANK_KING ) { rc = 1; callIntervalDays = 7; } else if( rank_id == RANK_GENERAL ) { rc = skill.skill_level >= 30 + (100-nation_array[nation_recno]->pref_keep_general)/2; // 30 to 80 callIntervalDays = 15; // don't call too freqently } if( rc ) { if( info.game_date > team_info->ai_last_request_defense_date + callIntervalDays ) { team_info->ai_last_request_defense_date = info.game_date; nation_array[nation_recno]->ai_defend(attackerUnitRecno); } } } //---------- End of function Unit::ai_leader_being_attacked --------// //--------- Begin of function Unit::think_king_flee --------// // // Note: we still need to keep think_king_action() because // between these two functions, a number of things // may be done, like returning home camp. We only // carry out actions in this function if the king // is in danger and urgently need to flee. // int Unit::think_king_flee() { if( force_move_flag && cur_action != SPRITE_IDLE ) // the king is already fleeing now return 1; //------- if the king is alone --------// Nation* ownNation = nation_array[nation_recno]; //------------------------------------------------// // When the king is alone and there is no assigned action OR // when the king is injured, the king will flee // back to its camp. //------------------------------------------------// if( ( team_info->member_count==0 && !ai_action_id ) || hit_points < 125-ownNation->pref_military_courage/4 ) { //------------------------------------------// // // If the king is currently under attack, flee // to the nearest camp with the maximum protection. // //------------------------------------------// Firm *firmCamp, *bestCamp=NULL; int curRating, bestRating=0; int curXLoc = next_x_loc(), curYLoc = next_y_loc(); int curRegionId = world.get_region_id( curXLoc, curYLoc ); if( cur_action == SPRITE_ATTACK ) { for( int i=ownNation->ai_camp_count-1 ; i>=0 ; i-- ) { firmCamp = (FirmCamp*) firm_array[ ownNation->ai_camp_array[i] ]; if( firmCamp->region_id != curRegionId ) continue; if( firmCamp->overseer_recno && rank_id!=RANK_KING ) // if there is already a commander in this camp. However if this is the king, than ingore this continue; curRating = world.distance_rating( curXLoc, curYLoc, firmCamp->center_x, firmCamp->center_y ); if( curRating > bestRating ) { bestRating = curRating; bestCamp = firmCamp; } } } else if( home_camp_firm_recno ) // if there is a home for the king { bestCamp = firm_array[home_camp_firm_recno]; } //------------------------------------// if( bestCamp ) { if( config.ai_aggressiveness > OPTION_LOW ) force_move_flag = 1; assign( bestCamp->loc_x1, bestCamp->loc_y1 ); } else // if the king is neither under attack or has a home camp, then call the standard think_leader_action() { think_leader_action(); } return cur_action != SPRITE_IDLE; } return 0; } //---------- End of function Unit::think_king_flee --------// //--------- Begin of function Unit::think_general_flee --------// int Unit::think_general_flee() { if( force_move_flag && cur_action != SPRITE_IDLE ) // the general is already fleeing now return 1; //------- if the general is alone --------// Nation* ownNation = nation_array[nation_recno]; //------------------------------------------------// // When the general is alone and there is no assigned action OR // when the general is injured, the general will flee // back to its camp. //------------------------------------------------// if( ( team_info->member_count==0 && !ai_action_id ) || hit_points < max_hit_points * (75+ownNation->pref_military_courage/2) / 200 ) // 75 to 125 / 200 { //------------------------------------------// // // If the general is currently under attack, flee // to the nearest camp with the maximum protection. // //------------------------------------------// Firm *firmCamp, *bestCamp=NULL; int curRating, bestRating=0; int curXLoc = next_x_loc(), curYLoc = next_y_loc(); int curRegionId = world.get_region_id( curXLoc, curYLoc ); if( cur_action == SPRITE_ATTACK ) { for( int i=ownNation->ai_camp_count-1 ; i>=0 ; i-- ) { firmCamp = (FirmCamp*) firm_array[ ownNation->ai_camp_array[i] ]; if( firmCamp->region_id != curRegionId ) continue; curRating = world.distance_rating( curXLoc, curYLoc, firmCamp->center_x, firmCamp->center_y ); if( curRating > bestRating ) { bestRating = curRating; bestCamp = firmCamp; } } } else if( home_camp_firm_recno ) // if there is a home for the general { bestCamp = firm_array[home_camp_firm_recno]; } //------------------------------------// if( bestCamp ) { if( bestCamp->overseer_recno ) // if there is already an overseer there, just move close to the camp for protection { if( config.ai_aggressiveness > OPTION_LOW ) force_move_flag = 1; move_to( bestCamp->loc_x1, bestCamp->loc_y1 ); } else assign( bestCamp->loc_x1, bestCamp->loc_y1 ); } else // if the general is neither under attack or has a home camp, then call the standard think_leader_action() { think_leader_action(); } return cur_action != SPRITE_IDLE; } return 0; } //---------- End of function Unit::think_general_flee --------// //--------- Begin of function Unit::ai_build_camp --------// // // Order this unit to build a camp in its region. // int Unit::ai_build_camp() { //--- to prevent building more than one camp at the same time ---// int curRegionId = region_id(); Nation* ownNation = nation_array[nation_recno]; if( ownNation->is_action_exist( ACTION_AI_BUILD_FIRM, FIRM_CAMP, curRegionId ) ) return 0; //------- locate a place for the camp --------// FirmInfo* firmInfo = firm_res[FIRM_CAMP]; int xLoc=0, yLoc=0; char teraMask = UnitRes::mobile_type_to_mask(UNIT_LAND); if( world.locate_space_random(xLoc, yLoc, MAX_WORLD_X_LOC-1, MAX_WORLD_Y_LOC-1, firmInfo->loc_width+2, firmInfo->loc_height+2, // leave at least one location space around the building MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC, curRegionId, 1, teraMask) ) { return ownNation->add_action( xLoc, yLoc, -1, -1, ACTION_AI_BUILD_FIRM, FIRM_CAMP, 1, sprite_recno ); } return 0; } //---------- End of function Unit::ai_build_camp --------// //--------- Begin of function Unit::ai_settle_new_town --------// // // Settle a new village next to one of the camps in this region. // int Unit::ai_settle_new_town() { //----- locate a suitable camp for the new town to settle next to ----// Nation* ownNation = nation_array[nation_recno]; FirmCamp* firmCamp, *bestCamp=NULL; int curRegionId = region_id(); int curRating, bestRating=0; for( int i=ownNation->ai_camp_count-1 ; i>=0 ; i-- ) { firmCamp = (FirmCamp*) firm_array[ ownNation->ai_camp_array[i] ]; if( firmCamp->region_id != curRegionId ) continue; curRating = firmCamp->total_combat_level(); if( curRating > bestRating ) { bestRating = curRating; bestCamp = firmCamp; } } if( !bestCamp ) return 0; //--------- settle a new town now ---------// int xLoc=bestCamp->loc_x1; int yLoc=bestCamp->loc_y1; if( world.locate_space(xLoc, yLoc, bestCamp->loc_x2, bestCamp->loc_y2, STD_TOWN_LOC_WIDTH, STD_TOWN_LOC_HEIGHT, UNIT_LAND, curRegionId, 1 ) ) // 1-build flag { settle( xLoc, yLoc ); return 1; } return 0; } //---------- End of function Unit::ai_settle_new_town --------// //--------- Begin of function Unit::ai_handle_seek_path_fail --------// // // This function is used for handling cases when AI units are not // able to seek a path successfully. // int Unit::ai_handle_seek_path_fail() { if( seek_path_fail_count < 5 ) // wait unit it has failed many times return 0; //----- try to move to a new location -----// if( seek_path_fail_count==5 ) { stop2(); // stop the unit and think for new action return 0; } //--- if the seek path has failed too many times, resign the unit ---// int resignFlag = 0; if( rank_id == RANK_SOLDIER && !leader_unit_recno ) { if( seek_path_fail_count>=7 ) resignFlag = 1; } else if( rank_id == RANK_GENERAL ) { if( seek_path_fail_count >= 7+skill.skill_level/10 ) resignFlag = 1; } if( resignFlag && is_visible() ) { resign(COMMAND_AI); return 1; } else return 0; } //---------- End of function Unit::ai_handle_seek_path_fail --------//