OAI_UNIT.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. /*
  2. * Seven Kingdoms: Ancient Adversaries
  3. *
  4. * Copyright 1997,1998 Enlight Software Ltd.
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. //Filename : OAI_UNIT.CPP
  21. //Description: AI - unit related functions
  22. #include <ALL.h>
  23. #include <OUNIT.h>
  24. #include <OF_INN.h>
  25. #include <OTOWN.h>
  26. #include <ONATION.h>
  27. //-------- Begin of function Nation::get_skilled_unit -------//
  28. //
  29. // <int> skillId - the skill the selected unit should have
  30. // <int> raceId - the race the selected unit should have
  31. // (0 for any races)
  32. // <ActionNode*> actionNode - the ActionNode of the action that needs this skilled unit.
  33. //
  34. // return: <Unit*> skilledUnit - pointer to the skilled unit.
  35. //
  36. Unit* Nation::get_skilled_unit(int skillId, int raceId, ActionNode* actionNode)
  37. {
  38. //--------- get a skilled unit --------//
  39. Unit* skilledUnit;
  40. if(actionNode->unit_recno) // a unit has started training previously
  41. {
  42. skilledUnit = unit_array[actionNode->unit_recno];
  43. }
  44. else
  45. {
  46. char resultFlag;
  47. int xLoc, yLoc;
  48. //---- for harbor, we have to get the land region id. instead of the sea region id. ----//
  49. if( actionNode->action_mode==ACTION_AI_BUILD_FIRM &&
  50. actionNode->action_para==FIRM_HARBOR )
  51. {
  52. int rc=0;
  53. for( yLoc=actionNode->action_y_loc ; yLoc<actionNode->action_y_loc+3 ; yLoc++ )
  54. {
  55. for( xLoc=actionNode->action_x_loc ; xLoc<actionNode->action_x_loc+3 ; xLoc++ )
  56. {
  57. if( region_array[ world.get_region_id(xLoc,yLoc) ]->region_type == REGION_LAND )
  58. {
  59. rc=1;
  60. break;
  61. }
  62. }
  63. if( rc )
  64. break;
  65. }
  66. }
  67. else
  68. {
  69. xLoc = actionNode->action_x_loc;
  70. yLoc = actionNode->action_y_loc;
  71. }
  72. //-----------------------------------------//
  73. skilledUnit = find_skilled_unit(skillId, raceId, xLoc, yLoc, resultFlag, actionNode->action_id);
  74. if( !skilledUnit ) // skilled unit not found
  75. return NULL;
  76. }
  77. //------ if the unit is still in training -----//
  78. if( !skilledUnit->is_visible() )
  79. {
  80. actionNode->next_retry_date = info.game_date + TOTAL_TRAIN_DAYS + 1;
  81. return NULL; // continue processing this action after this date, this is used when training a unit for construction
  82. }
  83. err_when( !skilledUnit->race_id );
  84. return skilledUnit;
  85. }
  86. //-------- End of function Nation::get_skilled_unit -------//
  87. //--------- Begin of function Nation::find_skilled_unit --------//
  88. //
  89. // <int> skillId - the skill the selected unit should have
  90. // <int> raceId - the race the selected unit should have
  91. // (0 for any races)
  92. // <short> destX, destY - location the unit move to
  93. // <char&> resultFlag - describle how to find the skilled unit
  94. // 0 - for unable to train unit,
  95. // 1 - for existing skilled unit
  96. // 2 - for unit hired from inn
  97. // 3 - for training unit in town (training is required)
  98. //
  99. // [int] actionId - the action id. of the action which
  100. // the unit should do after it has finished training.
  101. //
  102. // return the unit pointer pointed to the skilled unit
  103. //
  104. Unit* Nation::find_skilled_unit(int skillId, int raceId, short destX, short destY, char& resultFlag, int actionId)
  105. {
  106. //----- try to find an existing unit with the required skill -----//
  107. Unit *skilledUnit = NULL;
  108. Unit *unitPtr;
  109. Firm *firmPtr;
  110. short curDist, minDist=0x1000;
  111. int destRegionId = world.get_region_id(destX, destY);
  112. for(int i=unit_array.size(); i>0; i--)
  113. {
  114. if(unit_array.is_deleted(i))
  115. continue;
  116. unitPtr = unit_array[i];
  117. if( unitPtr->nation_recno!=nation_recno || !unitPtr->race_id )
  118. continue;
  119. if( raceId && unitPtr->race_id != raceId )
  120. continue;
  121. //---- if this unit is on a mission ----//
  122. if( unitPtr->home_camp_firm_recno )
  123. continue;
  124. if( unitPtr->region_id() != destRegionId )
  125. continue;
  126. //----- if this is a mobile unit ------//
  127. if( unitPtr->is_visible() )
  128. {
  129. if( !unitPtr->is_ai_all_stop() )
  130. continue;
  131. if( unitPtr->skill.skill_id==skillId &&
  132. unitPtr->cur_action!=SPRITE_ATTACK && !unitPtr->ai_action_id )
  133. {
  134. curDist = m.points_distance(unitPtr->next_x_loc(), unitPtr->next_y_loc(), destX, destY);
  135. if(curDist < minDist)
  136. {
  137. skilledUnit = unitPtr;
  138. minDist = curDist;
  139. }
  140. }
  141. }
  142. //------- if this is an overseer ------//
  143. else if( skillId==SKILL_LEADING && unitPtr->unit_mode==UNIT_MODE_OVERSEE )
  144. {
  145. firmPtr = firm_array[unitPtr->unit_mode_para];
  146. if( firmPtr->region_id != destRegionId )
  147. continue;
  148. if( firmPtr->firm_id == FIRM_CAMP )
  149. {
  150. //--- if this military camp is going to be closed, use this overseer ---//
  151. if( firmPtr->should_close_flag )
  152. {
  153. firmPtr->mobilize_overseer();
  154. skilledUnit = unitPtr; // pick this overseer
  155. break;
  156. }
  157. }
  158. }
  159. else if( skillId==SKILL_CONSTRUCTION && unitPtr->unit_mode==UNIT_MODE_CONSTRUCT ) // the unit is a residental builder for repairing the firm
  160. {
  161. firmPtr = firm_array[unitPtr->unit_mode_para];
  162. if( !firmPtr->under_construction ) // only if the unit is repairing instead of constructing the firm
  163. {
  164. if( firmPtr->set_builder(0) ) // return 1 if the builder is mobilized successfully, 0 if the builder was killed because of out of space on the map
  165. {
  166. skilledUnit = unitPtr;
  167. break;
  168. }
  169. }
  170. }
  171. }
  172. //---------------------------------------------------//
  173. if(skilledUnit)
  174. {
  175. resultFlag = 1;
  176. }
  177. else
  178. {
  179. //--- if no existing skilled unit found, try to hire one from inns ---//
  180. int unitRecno = hire_unit(skillId, raceId, destX, destY); // this function will try going with hiring units that are better than training your own ones
  181. if( unitRecno )
  182. {
  183. skilledUnit = unit_array[unitRecno];
  184. resultFlag = 2;
  185. }
  186. else //--- if still cannot get a skilled unit, train one ---//
  187. {
  188. int trainTownRecno;
  189. if( train_unit(skillId, raceId, destX, destY, trainTownRecno, actionId) )
  190. resultFlag = 3;
  191. else
  192. resultFlag = 0;
  193. }
  194. }
  195. err_when(skilledUnit && !skilledUnit->is_visible());
  196. err_when(skilledUnit && skilledUnit->rank_id==RANK_KING && (skillId!=SKILL_CONSTRUCTION && skillId!=SKILL_LEADING));
  197. err_when(skilledUnit && (skilledUnit->cur_action==SPRITE_DIE || skilledUnit->action_mode==ACTION_DIE));
  198. return skilledUnit;
  199. }
  200. //---------- End of function Nation::find_skilled_unit --------//
  201. //--------- Begin of function Nation::hire_unit --------//
  202. //
  203. // <int> importantRating - importance of hiring the unit.
  204. //
  205. int Nation::ai_should_hire_unit(int importanceRating)
  206. {
  207. if( !ai_inn_count ) // don't hire any body in the cash is too low
  208. return 0;
  209. return ai_should_spend(importanceRating + pref_hire_unit/5 - 10 ); // -10 to +10 depending on pref_hire_unit
  210. }
  211. //---------- End of function Nation::hire_unit --------//
  212. //--------- Begin of function Nation::hire_unit --------//
  213. //
  214. // <int> skillId - the skill the unit should have
  215. // <int> raceId - the race the selected unit should have
  216. // (0 for any races)
  217. // <short> destX - the x location the unit will move to
  218. // <short> destY - the y location the unit will move to
  219. //
  220. // hire unit with specified skill from an inn
  221. // return the unit pointer pointed to the skilled unit
  222. //
  223. // return: <int> recno of the unit recruited.
  224. //
  225. int Nation::hire_unit(int skillId, int raceId, short destX, short destY)
  226. {
  227. if( !ai_should_hire_unit(20) ) // 20 - importance rating
  228. return 0;
  229. //-------------------------------------------//
  230. FirmInn *firmInnPtr;
  231. Firm *firmPtr;
  232. InnUnit *innUnit;
  233. Skill *innUnitSkill;
  234. int i, j, innUnitCount, curRating, curFirmDist;
  235. int bestRating=0, bestInnRecno=0, bestInnUnitId=0;
  236. int destRegionId = world.get_region_id(destX, destY);
  237. for(i=0; i<ai_inn_count; i++)
  238. {
  239. firmPtr = firm_array[ai_inn_array[i]];
  240. if( firmPtr->region_id != destRegionId )
  241. continue;
  242. firmInnPtr = firmPtr->cast_to_FirmInn();
  243. innUnitCount=firmInnPtr->inn_unit_count;
  244. if( !innUnitCount )
  245. continue;
  246. innUnit = firmInnPtr->inn_unit_array + innUnitCount - 1;
  247. curFirmDist = m.points_distance(firmPtr->center_x, firmPtr->center_y, destX, destY);
  248. //------- check units in the inn ---------//
  249. for(j=innUnitCount; j>0; j--, innUnit--)
  250. {
  251. innUnitSkill = &(innUnit->skill);
  252. if( innUnitSkill->skill_id==skillId &&
  253. (!raceId || unit_res[innUnit->unit_id]->race_id == raceId) &&
  254. cash >= innUnit->hire_cost )
  255. {
  256. //----------------------------------------------//
  257. // evalute a unit on:
  258. // -its race, whether it's the same as the nation's race
  259. // -the inn's distance from the destination
  260. // -the skill level of the unit.
  261. //----------------------------------------------//
  262. curRating = innUnitSkill->skill_level
  263. - (100-100*curFirmDist/MAX_WORLD_X_LOC);
  264. if( unit_res[innUnit->unit_id]->race_id == race_id )
  265. curRating += 50;
  266. if( curRating > bestRating )
  267. {
  268. bestRating = curRating;
  269. bestInnRecno = firmInnPtr->firm_recno;
  270. bestInnUnitId = j;
  271. }
  272. }
  273. }
  274. }
  275. //----------------------------------------------------//
  276. if( bestInnUnitId )
  277. {
  278. firmPtr = firm_array[bestInnRecno];
  279. firmInnPtr = firmPtr->cast_to_FirmInn();
  280. return firmInnPtr->hire(bestInnUnitId);
  281. }
  282. return 0;
  283. }
  284. //---------- End of function Nation::hire_unit --------//
  285. //--------- Begin of function Nation::train_unit --------//
  286. //
  287. // <int> skillId - the skill the unit should have
  288. // <int> raceId - the race the selected unit should have
  289. // (0 for any races)
  290. // <short> destX - the x location the unit will move to
  291. // <short> destY - the y location the unit will move to
  292. //
  293. // <int&> trainTownRecno - the recno of the town where this unit is trained.
  294. //
  295. // [int] actionId - the action id. of the action which
  296. // the unit should do after it has finished training.
  297. //
  298. // return: <int> recno of the unit trained.
  299. //
  300. int Nation::train_unit(int skillId, int raceId, short destX, short destY, int& trainTownRecno, int actionId)
  301. {
  302. //----- locate the best town for training the unit -----//
  303. int i;
  304. Town *townPtr;
  305. int curDist, curRating, bestRating=0;
  306. int destRegionId = world.get_loc(destX, destY)->region_id;
  307. trainTownRecno = 0;
  308. for(i=0; i<ai_town_count; i++)
  309. {
  310. townPtr = town_array[ai_town_array[i]];
  311. if( !townPtr->jobless_population || townPtr->train_unit_recno || // no jobless population or currently a unit is being trained
  312. !townPtr->has_linked_own_camp )
  313. {
  314. continue;
  315. }
  316. if( townPtr->region_id != destRegionId )
  317. continue;
  318. //--------------------------------------//
  319. curDist = m.points_distance(townPtr->center_x, townPtr->center_y, destX, destY);
  320. curRating = 100-100*curDist/MAX_WORLD_X_LOC;
  321. if( curRating > bestRating )
  322. {
  323. bestRating = curRating;
  324. trainTownRecno = townPtr->town_recno;
  325. }
  326. }
  327. if( !trainTownRecno )
  328. return 0;
  329. //---------- train the unit ------------//
  330. townPtr = town_array[trainTownRecno];
  331. if( !raceId )
  332. raceId = townPtr->pick_random_race(1, 1); // 1-pick has job units also, 1-pick spy units
  333. if( !raceId )
  334. return 0;
  335. int unitRecno = townPtr->recruit(skillId, raceId, COMMAND_AI);
  336. if( !unitRecno )
  337. return 0;
  338. townPtr->train_unit_action_id = actionId; // set train_unit_action_id so the unit can immediately execute the action when he has finished training.
  339. return unitRecno;
  340. }
  341. //---------- End of function Nation::train_unit --------//
  342. //--------- Begin of function Nation::recruit_jobless_worker --------//
  343. //
  344. // <Firm*> destFirmPtr - the firm which the workers are recruited for.
  345. // [int] preferedRaceId - the prefered race id.
  346. //
  347. // return: <int> recno of the unit recruited.
  348. //
  349. int Nation::recruit_jobless_worker(Firm* destFirmPtr, int preferedRaceId)
  350. {
  351. #define MIN_AI_TOWN_POP 8
  352. int needSpecificRace, raceId; // the race of the needed unit
  353. if( preferedRaceId )
  354. {
  355. raceId = preferedRaceId;
  356. needSpecificRace = 1;
  357. }
  358. else
  359. {
  360. if( destFirmPtr->firm_id == FIRM_BASE ) // for seat of power, the race must be specific
  361. {
  362. raceId = firm_res.get_build(destFirmPtr->firm_build_id)->race_id;
  363. needSpecificRace = 1;
  364. }
  365. else
  366. {
  367. raceId = destFirmPtr->majority_race();
  368. needSpecificRace = 0;
  369. }
  370. }
  371. if( !raceId )
  372. return 0;
  373. //----- locate the best town for recruiting the unit -----//
  374. Town *townPtr;
  375. int curDist, curRating, bestRating=0, bestTownRecno=0;
  376. for( int i=0; i<ai_town_count; i++ )
  377. {
  378. townPtr = town_array[ai_town_array[i]];
  379. err_when( townPtr->nation_recno != nation_recno );
  380. if( !townPtr->jobless_population ) // no jobless population or currently a unit is being recruited
  381. continue;
  382. if( !townPtr->should_ai_migrate() ) // if the town is going to migrate, disregard the minimum population consideration
  383. {
  384. if( townPtr->population < MIN_AI_TOWN_POP ) // don't recruit workers if the population is low
  385. continue;
  386. }
  387. if( !townPtr->has_linked_own_camp && townPtr->has_linked_enemy_camp ) // cannot recruit from this town if there are enemy camps but no own camps
  388. continue;
  389. if( townPtr->region_id != destFirmPtr->region_id )
  390. continue;
  391. //--- get the distance beteween town & the destination firm ---//
  392. curDist = m.points_distance(townPtr->center_x, townPtr->center_y, destFirmPtr->center_x, destFirmPtr->center_y);
  393. curRating = 100-100*curDist/MAX_WORLD_X_LOC;
  394. //--- recruit units from non-base town first ------//
  395. if( !townPtr->is_base_town )
  396. curRating += 100;
  397. //---- if the town has the race that the firm needs most ----//
  398. if( townPtr->can_recruit(raceId) )
  399. {
  400. curRating += 50 + (int) townPtr->race_loyalty_array[raceId-1];
  401. }
  402. else
  403. {
  404. if( needSpecificRace ) // if the firm must need this race, don't consider the town if it doesn't have the race.
  405. continue;
  406. }
  407. if( curRating > bestRating )
  408. {
  409. bestRating = curRating;
  410. bestTownRecno = townPtr->town_recno;
  411. }
  412. }
  413. if( !bestTownRecno )
  414. return 0;
  415. //---------- recruit the unit ------------//
  416. townPtr = town_array[bestTownRecno];
  417. if( townPtr->recruitable_race_pop(raceId, 1) == 0 )
  418. {
  419. err_when( needSpecificRace );
  420. raceId = townPtr->pick_random_race(0, 1); // 0-pick jobless only, 1-pick spy units
  421. if( !raceId )
  422. return 0;
  423. }
  424. //--- if the chosen race is not recruitable, pick any recruitable race ---//
  425. if( !townPtr->can_recruit(raceId) )
  426. {
  427. //---- if the loyalty is too low to recruit, grant the town people first ---//
  428. if( cash > 0 && townPtr->accumulated_reward_penalty < 10 )
  429. townPtr->reward(COMMAND_AI);
  430. //---- if the loyalty is still too low, return now ----//
  431. if( !townPtr->can_recruit(raceId) )
  432. return 0;
  433. }
  434. return townPtr->recruit(-1, raceId, COMMAND_AI);
  435. }
  436. //---------- End of function Nation::recruit_jobless_worker --------//
  437. //--------- Begin of function Nation::recruit_on_job_worker --------//
  438. //
  439. // Get skilled workers from existing firms who have labor more than
  440. // it really needs.
  441. //
  442. // <Firm*> destFirmPtr - the firm which the workers are recruited for.
  443. // [int] preferedRaceId - the prefered race id.
  444. //
  445. // return: <int> recno of the unit recruited.
  446. //
  447. int Nation::recruit_on_job_worker(Firm* destFirmPtr, int preferedRaceId)
  448. {
  449. err_when( !firm_res[destFirmPtr->firm_id]->need_worker );
  450. err_when( destFirmPtr->firm_id == FIRM_BASE ); // seat of power shouldn't call this function at all, as it doesn't handle the racial issue.
  451. if( !preferedRaceId )
  452. {
  453. preferedRaceId = destFirmPtr->majority_race();
  454. if( !preferedRaceId )
  455. return 0;
  456. }
  457. //--- scan existing firms to see if any of them have excess workers ---//
  458. int aiFirmCount; // reference vars for returning result
  459. short* aiFirmArray = update_ai_firm_array(destFirmPtr->firm_id, 0, 0, aiFirmCount);
  460. Firm* firmPtr, *bestFirmPtr=NULL;
  461. int bestRating=0, curRating, curDistance;
  462. int hasHuman;
  463. Worker* workerPtr;
  464. int i;
  465. for( i=0 ; i<aiFirmCount ; i++ )
  466. {
  467. firmPtr = firm_array[aiFirmArray[i]];
  468. err_when( firmPtr->firm_id != destFirmPtr->firm_id );
  469. if( firmPtr->firm_recno == destFirmPtr->firm_recno )
  470. continue;
  471. if( firmPtr->region_id != destFirmPtr->region_id )
  472. continue;
  473. if( !firmPtr->ai_has_excess_worker() )
  474. continue;
  475. //-----------------------------------//
  476. curDistance = m.points_distance( firmPtr->center_x, firmPtr->center_y,
  477. destFirmPtr->center_x, destFirmPtr->center_y );
  478. curRating = 100 - 100 * curDistance / MAX_WORLD_X_LOC;
  479. workerPtr = firmPtr->worker_array;
  480. hasHuman = 0;
  481. for( int j=0 ; j<firmPtr->worker_count ; j++, workerPtr++ )
  482. {
  483. if( workerPtr->race_id )
  484. hasHuman = 1;
  485. if( workerPtr->race_id == preferedRaceId )
  486. {
  487. //---- can't recruit this unit if he lives in a foreign town ----//
  488. if( workerPtr->town_recno &&
  489. town_array[workerPtr->town_recno]->nation_recno != nation_recno )
  490. {
  491. continue;
  492. }
  493. //--------------------------//
  494. curRating += 100;
  495. break;
  496. }
  497. }
  498. if( hasHuman && curRating > bestRating )
  499. {
  500. bestRating = curRating;
  501. bestFirmPtr = firmPtr;
  502. }
  503. }
  504. if( !bestFirmPtr )
  505. return 0;
  506. //------ mobilize a worker form the selected firm ----//
  507. int workerId=0;
  508. workerPtr = bestFirmPtr->worker_array;
  509. for( i=1 ; i<=bestFirmPtr->worker_count ; i++, workerPtr++ )
  510. {
  511. //---- can't recruit this unit if he lives in a foreign town ----//
  512. if( workerPtr->town_recno &&
  513. town_array[workerPtr->town_recno]->nation_recno != nation_recno )
  514. {
  515. continue;
  516. }
  517. //--------------------------------//
  518. if( workerPtr->race_id ) // if this is a human unit, take it first
  519. workerId = i;
  520. if( workerPtr->race_id == preferedRaceId ) // if we have a better one, take the better one
  521. {
  522. workerId = i;
  523. break;
  524. }
  525. }
  526. if( !workerId ) // this can happen if all the workers are foreign workers.
  527. return 0;
  528. return bestFirmPtr->mobilize_worker( workerId, COMMAND_AI );
  529. }
  530. //---------- End of function Nation::recruit_on_job_worker --------//