1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294 |
- /*
- * 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 : OUNITAI.CPP
- //Description : Object Unit AI
- #include <OSYS.h>
- #include <OSPY.h>
- #include <OREBEL.h>
- #include <OUNIT.h>
- #include <OCONFIG.h>
- #include <OREGIONS.h>
- #include <OF_CAMP.h>
- #include <ONATION.h>
- #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<checkLimit; i++)
- {
- m.cal_move_around_a_point(i, 20, 20, xShift, yShift);
- checkXLoc = curXLoc + xShift;
- checkYLoc = curYLoc + yShift;
- if(checkXLoc<0 || checkXLoc>=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 ; j<firmPtr->worker_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 ; i<nationPtr->ai_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.
- //
- // <int> 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 --------//
|