/* * 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_SPY.CPP //Description: AI - Spy activities #include #include #include #include #include #include //--------- Begin of function Nation::think_spy --------// void Nation::think_spy() { } //---------- End of function Nation::think_spy --------// //--------- Begin of function Nation::ai_assign_spy_to_town --------// // // Think about sending spies to the specific town. // // townRecno - recno of the town // [int] raceId - race id. of the spy // (default: majority_race() of the tonw) // // return: 1 - a spy is assigned successfully. // 0 - failure. // int Nation::ai_assign_spy_to_town(int townRecno, int raceId) { Town* townPtr = town_array[townRecno]; if( townPtr->population >= MAX_TOWN_POPULATION ) return 0; if( !raceId ) raceId = townPtr->majority_race(); int mobileOnly = townPtr->nation_recno == nation_recno; // if assign to own towns/firms, only get mobile spies, don't get spies from existing towns/firms as that will result in a loop effect return ai_assign_spy( townPtr->loc_x1, townPtr->loc_y1, raceId, mobileOnly ); } //---------- End of function Nation::ai_assign_spy_to_town --------// //--------- Begin of function Nation::ai_assign_spy_to_firm --------// // // Think about sending spies to the specific firm. // // return: 1 - a spy is assigned successfully. // 0 - failure. // int Nation::ai_assign_spy_to_firm(int firmRecno) { Firm* firmPtr = firm_array[firmRecno]; err_when( !firmPtr->worker_array ); //---- check if the firm is full or not -----// if( firmPtr->nation_recno == nation_recno ) // if it's our own firm { if( firmPtr->is_worker_full() ) // use is_worker_full() for own firms as it take into account of units patrolling outside return 0; } else { if( firmPtr->worker_count == MAX_WORKER ) return 0; } //------ look for an existing spy -------// int raceId = firmPtr->majority_race(); int mobileOnly = firmPtr->nation_recno == nation_recno; // if assign to own firms/firms, only get mobile spies, don't get spies from existing firms/firms as that will result in a loop effect return ai_assign_spy( firmPtr->loc_x1, firmPtr->loc_y1, raceId, mobileOnly ); } //---------- End of function Nation::ai_assign_spy_to_firm --------// //--------- Begin of function Nation::ai_assign_spy --------// // // Try to locate an existing spy for use. // // targetXLoc, targetYLoc - the target location // [int] spyRaceId - if specified, only spies of this race // will be located. (default:0) // [int] mobileOnly - get mobile spies only. (default:0) // int Nation::ai_assign_spy(int targetXLoc, int targetYLoc, int spyRaceId, int mobileOnly) { int unitRecno=0; //---- try to find an existing spy ----// Spy* spyPtr = ai_find_spy( targetXLoc, targetYLoc, spyRaceId, mobileOnly ); if( spyPtr ) unitRecno = spyPtr->mobilize_spy(); //--- if not successful, then try to hire one ---// if( !unitRecno ) unitRecno = hire_unit(SKILL_SPYING, spyRaceId, targetXLoc, targetYLoc); //--- if cannot hire one, try to train one ----// int trainTownRecno=0; if( !unitRecno ) unitRecno = train_unit(SKILL_SPYING, spyRaceId, targetXLoc, targetYLoc, trainTownRecno); if( !unitRecno ) return 0; //------ get the spy object of the unit ------// Unit* unitPtr = unit_array[unitRecno]; err_when( !unitPtr->spy_recno ); spyPtr = spy_array[unitPtr->spy_recno]; //------- get the nation of the assign destination -----// Location* locPtr = world.get_loc(targetXLoc, targetYLoc); int cloakedNationRecno; if( locPtr->is_firm() ) { Firm* firmPtr = firm_array[locPtr->firm_recno()]; err_when( firmPtr->nation_recno==0 ); // cannot assign to a monster firm cloakedNationRecno = firmPtr->nation_recno; } else if( locPtr->is_town() ) { Town* townPtr = town_array[locPtr->town_recno()]; cloakedNationRecno = townPtr->nation_recno; } else { return 0; // the original firm or town has been destroyed or sold } //------- Add the assign spy action --------// int actionRecno = add_action( targetXLoc, targetYLoc, -1, -1, ACTION_AI_ASSIGN_SPY, cloakedNationRecno, 1, unitRecno ); if( !actionRecno ) return 0; //------ if the unit is under training ------// if( trainTownRecno ) town_array[trainTownRecno]->train_unit_action_id = get_action(actionRecno)->action_id; return 1; } //---------- End of function Nation::ai_assign_spy --------// //--------- Begin of function Nation::ai_find_spy --------// // // Try to locate an existing spy for use. // // targetXLoc, targetYLoc - the target location // [int] spyRaceId - if specified, only spies of this race // will be located. (default:0) // [int] mobileOnly - get mobile spies only. (default:0) // Spy* Nation::ai_find_spy(int targetXLoc, int targetYLoc, int spyRaceId, int mobileOnly) { //--- first check if we have an existing spy ready for the mission ---// Spy *spyPtr, *bestSpy=NULL; int curRating, bestRating=0; int spyXLoc, spyYLoc; int targetRegionId = world.get_region_id(targetXLoc, targetYLoc); for(int i=spy_array.size(); i>0; i--) { if(spy_array.is_deleted(i)) continue; spyPtr = spy_array[i]; if( !spyPtr->true_nation_recno != nation_recno ) continue; if( spyRaceId && spyRaceId != race_id ) continue; if( spyPtr->spy_place == SPY_MOBILE ) { Unit* unitPtr = unit_array[spyPtr->spy_place_para]; if( !unitPtr->is_ai_all_stop() ) continue; spyXLoc = unitPtr->next_x_loc(); spyYLoc = unitPtr->next_y_loc(); } else { if( mobileOnly ) continue; if( spyPtr->spy_place == SPY_FIRM ) { Firm* firmPtr = firm_array[spyPtr->spy_place_para]; if( firmPtr->nation_recno != nation_recno ) // only get spies from our own firms continue; spyXLoc = firmPtr->center_x; spyYLoc = firmPtr->center_y; } else if( spyPtr->spy_place == SPY_TOWN ) { Town* townPtr = town_array[spyPtr->spy_place_para]; if( townPtr->nation_recno != nation_recno ) // only get spies from our own towns continue; spyXLoc = townPtr->center_x; spyYLoc = townPtr->center_y; } else continue; // in ships or undefined } //--- check if the region ids are matched ---// if( world.get_region_id(spyXLoc, spyYLoc) != targetRegionId ) continue; //----------------------------------------// curRating = world.distance_rating(targetXLoc, targetYLoc, spyXLoc, spyYLoc); curRating += spyPtr->spy_skill + spyPtr->spy_loyalty/2; if( curRating > bestRating ) { bestRating = curRating; bestSpy = spyPtr; } } return bestSpy; } //---------- End of function Nation::ai_find_spy --------// //----- Begin of function Nation::ai_assign_spy -----// // // action_x_loc, action_y_loc - location of the target firm or town // ref_x_loc, ref_y_loc - not used // unit_recno - unit recno of the spy // action_para - the cloak nation recno the spy should set to. // int Nation::ai_assign_spy(ActionNode* actionNode) { if(!seek_path.total_node_avail) return 0; if( unit_array.is_deleted(actionNode->unit_recno) ) return -1; Unit* spyUnit = unit_array[actionNode->unit_recno]; if( !spyUnit->is_visible() ) // it's still under training, not available yet return -1; if( !spyUnit->spy_recno || spyUnit->true_nation_recno() != nation_recno ) return -1; //------ change the cloak of the spy ------// int newFlag; Spy* spyPtr = spy_array[spyUnit->spy_recno]; if( reputation < 0 ) // if the nation's reputation is negative, use sneak mode to avoid chance of being uncovered and further damage the reputation newFlag = m.random( 2+(-(int)reputation)/5 )==0; // 2 to 22 else newFlag = m.random(2)==0; // 50% chance of being 1 spyPtr->notify_cloaked_nation_flag = newFlag; if( !spyUnit->can_spy_change_nation() ) // if the spy can't change nation recno now { int destXLoc = spyUnit->next_x_loc() + m.random(20) - 10; int destYLoc = spyUnit->next_y_loc() + m.random(20) - 10; destXLoc = max(0, destXLoc); destXLoc = min(MAX_WORLD_X_LOC-1, destXLoc); destYLoc = max(0, destYLoc); destYLoc = min(MAX_WORLD_Y_LOC-1, destXLoc); spyUnit->move_to( destXLoc, destYLoc ); actionNode->retry_count++; // never give up return 0; // return now and try again later } spyUnit->spy_change_nation(actionNode->action_para,COMMAND_AI); //------- assign the spy to the target -------// spyUnit->assign(actionNode->action_x_loc, actionNode->action_y_loc); //----------------------------------------------------------------// // Since the spy has already changed its cloaked nation recno // we cannot set the ai_action_id of the unit as when it needs // to call action_finished() or action_failure() it will // use the cloaked nation recno, which is incorrect. // So we just return -1, noting that the action has been completed. //----------------------------------------------------------------// return -1; } //----- End of function Nation::ai_assign_spy -----// //-------- Begin of function Nation::think_assign_spy_target_camp --------// // // Think about planting spies into independent towns and enemy towns. // int Nation::think_assign_spy_target_camp(int raceId, int regionId) { Firm *firmPtr; int curRating, bestRating=0, bestFirmRecno=0; for( int firmRecno=firm_array.size() ; firmRecno>0 ; firmRecno-- ) { if( firm_array.is_deleted(firmRecno) ) continue; firmPtr = firm_array[firmRecno]; if( firmPtr->nation_recno == nation_recno ) // don't assign to own firm continue; if( firmPtr->region_id != regionId ) continue; if( firmPtr->overseer_recno == 0 || firmPtr->worker_count == MAX_WORKER ) { continue; } if( firmPtr->majority_race() != raceId ) continue; //---------------------------------// Unit* overseerUnit = unit_array[firmPtr->overseer_recno]; if( overseerUnit->spy_recno ) // if the overseer is already a spy continue; curRating = 100 - overseerUnit->loyalty; if( curRating > bestRating ) { bestRating = curRating; bestFirmRecno = firmRecno; } } return bestFirmRecno; } //-------- End of function Nation::think_assign_spy_target_camp --------// //-------- Begin of function Nation::think_assign_spy_target_town --------// // // Think about planting spies into independent towns and enemy towns. // int Nation::think_assign_spy_target_town(int raceId, int regionId) { Town *townPtr; int townCount = town_array.size(); int townRecno = m.random(townCount)+1; for( int i=town_array.size() ; i>0 ; i-- ) { if( ++townRecno > townCount ) townRecno = 1; if( town_array.is_deleted(townRecno) ) continue; townPtr = town_array[townRecno]; if( townPtr->nation_recno == nation_recno ) // don't assign to own firm continue; if( townPtr->region_id != regionId ) continue; if( townPtr->population > MAX_TOWN_POPULATION-5 ) // -5 so that even if we assign too many spies to a town at the same time, there will still room for them continue; //---- for player towns, don't assign too frequently ----// if( !townPtr->ai_town ) { if( m.random(3) != 0 ) continue; } //----------------------------------------// if( townPtr->nation_recno ) { if( townPtr->race_loyalty_array[raceId-1] < MIN_NATION_DEFEND_LOYALTY ) // no need to assign spies to these towns as they are already very low continue; } else { if( townPtr->race_resistance_array[raceId-1][nation_recno-1] < MIN_INDEPENDENT_DEFEND_LOYALTY ) // no need to assign spies to these towns as they are already very low continue; } if( townPtr->majority_race() != raceId ) continue; return townRecno; } return 0; } //-------- End of function Nation::think_assign_spy_target_town --------// //-------- Begin of function Nation::think_assign_spy_own_town --------// // // Think about planting spies into independent towns and enemy towns. // int Nation::think_assign_spy_own_town(int raceId, int regionId) { Town *townPtr; int townCount = town_array.size(); int townRecno = m.random(townCount)+1; int spyCount; for( int i=town_array.size() ; i>0 ; i-- ) { if( ++townRecno > townCount ) townRecno = 1; if( town_array.is_deleted(townRecno) ) continue; townPtr = town_array[townRecno]; if( townPtr->nation_recno != nation_recno ) // only assign to own firm continue; if( townPtr->region_id != regionId ) continue; if( townPtr->population > MAX_TOWN_POPULATION-5 ) continue; if( townPtr->majority_race() != raceId ) continue; int curSpyLevel = spy_array.total_spy_skill_level( SPY_TOWN, townRecno, nation_recno, spyCount ); int neededSpyLevel = townPtr->needed_anti_spy_level(); if( neededSpyLevel > curSpyLevel + 30 ) return townRecno; } return 0; } //-------- End of function Nation::think_assign_spy_own_town --------//