mercenary.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. // Copyright (c) Athena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "mercenary.hpp"
  4. #include <stdlib.h>
  5. #include <math.h>
  6. #include "../common/cbasetypes.h"
  7. #include "../common/malloc.h"
  8. #include "../common/timer.h"
  9. #include "../common/nullpo.h"
  10. #include "../common/mmo.h"
  11. #include "../common/random.h"
  12. #include "../common/showmsg.h"
  13. #include "../common/strlib.h"
  14. #include "../common/utils.h"
  15. #include "log.hpp"
  16. #include "clif.hpp"
  17. #include "intif.hpp"
  18. #include "itemdb.hpp"
  19. #include "pc.hpp"
  20. #include "party.hpp"
  21. #include "trade.hpp"
  22. #include "npc.hpp"
  23. struct s_mercenary_db mercenary_db[MAX_MERCENARY_CLASS]; // Mercenary Database
  24. static uint16 mercenary_count;
  25. /**
  26. * Search Mercenary by class
  27. * @param class_ Class ID of Mercenary
  28. * @return The index of mercenary on mercenary_db, or -1 if not found
  29. **/
  30. static int16 mercenary_search_index(int class_) {
  31. int16 i;
  32. ARR_FIND(0, mercenary_count, i, mercenary_db[i].class_ == class_);
  33. return (i == mercenary_count)?-1:i;
  34. }
  35. /**
  36. * Check if the Class ID is a Mercenary
  37. * @param class_ The Class ID
  38. * @return true if Class ID is a Mercenary, false otherwise
  39. **/
  40. bool mercenary_class(int class_){
  41. return (bool)(mercenary_search_index(class_) > -1);
  42. }
  43. /**
  44. * Get View Data of Mercenary by Class ID
  45. * @param class_ The Class ID
  46. * @return View Data of Mercenary
  47. **/
  48. struct view_data * mercenary_get_viewdata(int class_){
  49. int i = mercenary_search_index(class_);
  50. if( i < 0 )
  51. return 0;
  52. return &mercenary_db[i].vd;
  53. }
  54. /**
  55. * Get mercenary skill index for mercenary skill tree
  56. * @param skill_id
  57. * @return Index in skill_tree or -1
  58. **/
  59. short mercenary_skill_get_index(uint16 skill_id) {
  60. if (!SKILL_CHK_MERC(skill_id))
  61. return -1;
  62. skill_id -= MC_SKILLBASE;
  63. if (skill_id >= MAX_MERCSKILL)
  64. return -1;
  65. return skill_id;
  66. }
  67. /**
  68. * Create a new Mercenary for Player
  69. * @param sd The Player
  70. * @param class_ Mercenary Class
  71. * @param lifetime Contract duration
  72. * @return false if failed, true otherwise
  73. **/
  74. bool mercenary_create(struct map_session_data *sd, int class_, unsigned int lifetime) {
  75. struct s_mercenary merc;
  76. struct s_mercenary_db *db;
  77. int16 i;
  78. nullpo_retr(false,sd);
  79. if( (i = mercenary_search_index(class_)) < 0 )
  80. return false;
  81. db = &mercenary_db[i];
  82. memset(&merc,0,sizeof(struct s_mercenary));
  83. merc.char_id = sd->status.char_id;
  84. merc.class_ = class_;
  85. merc.hp = db->status.max_hp;
  86. merc.sp = db->status.max_sp;
  87. merc.life_time = lifetime;
  88. // Request Char Server to create this mercenary
  89. intif_mercenary_create(&merc);
  90. return true;
  91. }
  92. /**
  93. * Get current Mercenary lifetime
  94. * @param md The Mercenary
  95. * @return The Lifetime
  96. **/
  97. int mercenary_get_lifetime(struct mercenary_data *md) {
  98. const struct TimerData * td;
  99. if( md == NULL || md->contract_timer == INVALID_TIMER )
  100. return 0;
  101. td = get_timer(md->contract_timer);
  102. return (td != NULL) ? DIFF_TICK(td->tick, gettick()) : 0;
  103. }
  104. /**
  105. * Get Guild type of Mercenary
  106. * @param md Mercenary
  107. * @return -1 if not found, 0 - ARCH_MERC_GUILD, 1 - SPEAR_MERC_GUILD, or 2 - SWORD_MERC_GUILD
  108. **/
  109. int mercenary_get_guild(struct mercenary_data *md){
  110. uint16 class_;
  111. if( md == NULL || md->db == NULL )
  112. return -1;
  113. class_ = md->db->class_;
  114. if( class_ >= 6017 && class_ <= 6026 )
  115. return ARCH_MERC_GUILD;
  116. if( class_ >= 6027 && class_ <= 6036 )
  117. return SPEAR_MERC_GUILD;
  118. if( class_ >= 6037 && class_ <= 6046 )
  119. return SWORD_MERC_GUILD;
  120. return -1;
  121. }
  122. /**
  123. * Get Faith value of Mercenary
  124. * @param md Mercenary
  125. * @return the Faith value
  126. **/
  127. int mercenary_get_faith(struct mercenary_data *md) {
  128. struct map_session_data *sd;
  129. uint16 class_;
  130. if( md == NULL || md->db == NULL || (sd = md->master) == NULL )
  131. return 0;
  132. class_ = md->db->class_;
  133. if( class_ >= 6017 && class_ <= 6026 )
  134. return sd->status.arch_faith;
  135. if( class_ >= 6027 && class_ <= 6036 )
  136. return sd->status.spear_faith;
  137. if( class_ >= 6037 && class_ <= 6046 )
  138. return sd->status.sword_faith;
  139. return 0;
  140. }
  141. /**
  142. * Set faith value of Mercenary
  143. * @param md The Mercenary
  144. * @param value Faith Value
  145. **/
  146. void mercenary_set_faith(struct mercenary_data *md, int value) {
  147. struct map_session_data *sd;
  148. uint16 class_;
  149. int *faith;
  150. if( md == NULL || md->db == NULL || (sd = md->master) == NULL )
  151. return;
  152. class_ = md->db->class_;
  153. if( class_ >= 6017 && class_ <= 6026 )
  154. faith = &sd->status.arch_faith;
  155. else if( class_ >= 6027 && class_ <= 6036 )
  156. faith = &sd->status.spear_faith;
  157. else if( class_ >= 6037 && class_ <= 6046 )
  158. faith = &sd->status.sword_faith;
  159. else
  160. return;
  161. *faith += value;
  162. *faith = cap_value(*faith, 0, SHRT_MAX);
  163. clif_mercenary_updatestatus(sd, SP_MERCFAITH);
  164. }
  165. /**
  166. * Get Mercenary's calls
  167. * @param md Mercenary
  168. * @return Number of calls
  169. **/
  170. int mercenary_get_calls(struct mercenary_data *md) {
  171. struct map_session_data *sd;
  172. uint16 class_;
  173. if( md == NULL || md->db == NULL || (sd = md->master) == NULL )
  174. return 0;
  175. class_ = md->db->class_;
  176. if( class_ >= 6017 && class_ <= 6026 )
  177. return sd->status.arch_calls;
  178. if( class_ >= 6027 && class_ <= 6036 )
  179. return sd->status.spear_calls;
  180. if( class_ >= 6037 && class_ <= 6046 )
  181. return sd->status.sword_calls;
  182. return 0;
  183. }
  184. /**
  185. * Set Mercenary's calls
  186. * @param md Mercenary
  187. * @param value
  188. **/
  189. void mercenary_set_calls(struct mercenary_data *md, int value) {
  190. struct map_session_data *sd;
  191. uint16 class_;
  192. int *calls;
  193. if( md == NULL || md->db == NULL || (sd = md->master) == NULL )
  194. return;
  195. class_ = md->db->class_;
  196. if( class_ >= 6017 && class_ <= 6026 )
  197. calls = &sd->status.arch_calls;
  198. else if( class_ >= 6027 && class_ <= 6036 )
  199. calls = &sd->status.spear_calls;
  200. else if( class_ >= 6037 && class_ <= 6046 )
  201. calls = &sd->status.sword_calls;
  202. else
  203. return;
  204. *calls += value;
  205. *calls = cap_value(*calls, 0, INT_MAX);
  206. }
  207. /**
  208. * Save Mercenary data
  209. * @param md Mercenary
  210. **/
  211. void mercenary_save(struct mercenary_data *md) {
  212. md->mercenary.hp = md->battle_status.hp;
  213. md->mercenary.sp = md->battle_status.sp;
  214. md->mercenary.life_time = mercenary_get_lifetime(md);
  215. intif_mercenary_save(&md->mercenary);
  216. }
  217. /**
  218. * Ends contract of Mercenary
  219. **/
  220. static int merc_contract_end(int tid, unsigned int tick, int id, intptr_t data) {
  221. struct map_session_data *sd;
  222. struct mercenary_data *md;
  223. if( (sd = map_id2sd(id)) == NULL )
  224. return 1;
  225. if( (md = sd->md) == NULL )
  226. return 1;
  227. if( md->contract_timer != tid )
  228. {
  229. ShowError("merc_contract_end %d != %d.\n", md->contract_timer, tid);
  230. return 0;
  231. }
  232. md->contract_timer = INVALID_TIMER;
  233. mercenary_delete(md, 0); // Mercenary soldier's duty hour is over.
  234. return 0;
  235. }
  236. /**
  237. * Delete Mercenary
  238. * @param md Mercenary
  239. * @param reply
  240. **/
  241. int mercenary_delete(struct mercenary_data *md, int reply) {
  242. struct map_session_data *sd = md->master;
  243. md->mercenary.life_time = 0;
  244. mercenary_contract_stop(md);
  245. if( !sd )
  246. return unit_free(&md->bl, CLR_OUTSIGHT);
  247. if( md->devotion_flag )
  248. {
  249. md->devotion_flag = 0;
  250. status_change_end(&sd->bl, SC_DEVOTION, INVALID_TIMER);
  251. }
  252. switch( reply )
  253. {
  254. case 0: mercenary_set_faith(md, 1); break; // +1 Loyalty on Contract ends.
  255. case 1: mercenary_set_faith(md, -1); break; // -1 Loyalty on Mercenary killed
  256. }
  257. clif_mercenary_message(sd, reply);
  258. return unit_remove_map(&md->bl, CLR_OUTSIGHT);
  259. }
  260. /**
  261. * Stop contract of Mercenary
  262. * @param md Mercenary
  263. **/
  264. void mercenary_contract_stop(struct mercenary_data *md) {
  265. nullpo_retv(md);
  266. if( md->contract_timer != INVALID_TIMER )
  267. delete_timer(md->contract_timer, merc_contract_end);
  268. md->contract_timer = INVALID_TIMER;
  269. }
  270. /**
  271. * Init contract of Mercenary
  272. * @param md Mercenary
  273. **/
  274. void merc_contract_init(struct mercenary_data *md) {
  275. if( md->contract_timer == INVALID_TIMER )
  276. md->contract_timer = add_timer(gettick() + md->mercenary.life_time, merc_contract_end, md->master->bl.id, 0);
  277. md->regen.state.block = 0;
  278. }
  279. /**
  280. * Received mercenary data from char-serv
  281. * @param merc : mercenary datas
  282. * @param flag : if inter-serv request was sucessfull
  283. * @return false:failure, true:sucess
  284. */
  285. bool mercenary_recv_data(struct s_mercenary *merc, bool flag)
  286. {
  287. struct map_session_data *sd;
  288. struct mercenary_data *md;
  289. struct s_mercenary_db *db;
  290. int i = mercenary_search_index(merc->class_);
  291. if( (sd = map_charid2sd(merc->char_id)) == NULL )
  292. return false;
  293. if( !flag || i < 0 ) { // Not created - loaded - DB info
  294. sd->status.mer_id = 0;
  295. return false;
  296. }
  297. db = &mercenary_db[i];
  298. if( !sd->md ) {
  299. sd->md = md = (struct mercenary_data*)aCalloc(1,sizeof(struct mercenary_data));
  300. md->bl.type = BL_MER;
  301. md->bl.id = npc_get_new_npc_id();
  302. md->devotion_flag = 0;
  303. md->master = sd;
  304. md->db = db;
  305. memcpy(&md->mercenary, merc, sizeof(struct s_mercenary));
  306. status_set_viewdata(&md->bl, md->mercenary.class_);
  307. status_change_init(&md->bl);
  308. unit_dataset(&md->bl);
  309. md->ud.dir = sd->ud.dir;
  310. md->bl.m = sd->bl.m;
  311. md->bl.x = sd->bl.x;
  312. md->bl.y = sd->bl.y;
  313. unit_calc_pos(&md->bl, sd->bl.x, sd->bl.y, sd->ud.dir);
  314. md->bl.x = md->ud.to_x;
  315. md->bl.y = md->ud.to_y;
  316. map_addiddb(&md->bl);
  317. status_calc_mercenary(md, SCO_FIRST);
  318. md->contract_timer = INVALID_TIMER;
  319. md->masterteleport_timer = INVALID_TIMER;
  320. merc_contract_init(md);
  321. } else {
  322. memcpy(&sd->md->mercenary, merc, sizeof(struct s_mercenary));
  323. md = sd->md;
  324. }
  325. if( sd->status.mer_id == 0 )
  326. mercenary_set_calls(md, 1);
  327. sd->status.mer_id = merc->mercenary_id;
  328. if( md && md->bl.prev == NULL && sd->bl.prev != NULL ) {
  329. if(map_addblock(&md->bl))
  330. return false;
  331. clif_spawn(&md->bl);
  332. clif_mercenary_info(sd);
  333. clif_mercenary_skillblock(sd);
  334. }
  335. return true;
  336. }
  337. /**
  338. * Heals Mercenary
  339. * @param md Mercenary
  340. * @param hp HP amount
  341. * @param sp SP amount
  342. **/
  343. void mercenary_heal(struct mercenary_data *md, int hp, int sp) {
  344. if (md->master == NULL)
  345. return;
  346. if( hp )
  347. clif_mercenary_updatestatus(md->master, SP_HP);
  348. if( sp )
  349. clif_mercenary_updatestatus(md->master, SP_SP);
  350. }
  351. /**
  352. * Delete Mercenary
  353. * @param md: Mercenary
  354. * @return false for status_damage
  355. */
  356. bool mercenary_dead(struct mercenary_data *md) {
  357. mercenary_delete(md, 1);
  358. return false;
  359. }
  360. /**
  361. * Gives bonus to Mercenary
  362. * @param md Mercenary
  363. **/
  364. void mercenary_killbonus(struct mercenary_data *md) {
  365. const enum sc_type scs[] = { SC_MERC_FLEEUP, SC_MERC_ATKUP, SC_MERC_HPUP, SC_MERC_SPUP, SC_MERC_HITUP };
  366. uint8 index = rnd() % ARRAYLENGTH(scs);
  367. sc_start(&md->bl,&md->bl, scs[index], 100, rnd() % 5, 600000);
  368. }
  369. /**
  370. * Mercenary does kill
  371. * @param md Mercenary
  372. **/
  373. void mercenary_kills(struct mercenary_data *md){
  374. if(md->mercenary.kill_count <= (INT_MAX-1)) //safe cap to INT_MAX
  375. md->mercenary.kill_count++;
  376. if( (md->mercenary.kill_count % 50) == 0 )
  377. {
  378. mercenary_set_faith(md, 1);
  379. mercenary_killbonus(md);
  380. }
  381. if( md->master )
  382. clif_mercenary_updatestatus(md->master, SP_MERCKILLS);
  383. }
  384. /**
  385. * Check if Mercenary has the skill
  386. * @param md Mercenary
  387. * @param skill_id The skill
  388. * @return Skill Level or 0 if Mercenary doesn't have the skill
  389. **/
  390. int mercenary_checkskill(struct mercenary_data *md, uint16 skill_id) {
  391. short idx = mercenary_skill_get_index(skill_id);
  392. if( !md || !md->db || idx == -1)
  393. return 0;
  394. return md->db->skill[idx].lv;
  395. }
  396. /**
  397. * Read each line of Mercenary's database
  398. **/
  399. static bool mercenary_readdb_sub(char* str[], int columns, int current)
  400. {
  401. int ele;
  402. uint16 i, class_ = atoi(str[0]);
  403. struct s_mercenary_db *db;
  404. struct status_data *status;
  405. //Find the ID, already exist or not in mercenary_db
  406. ARR_FIND(0,mercenary_count,i,mercenary_db[i].class_ == class_);
  407. if (i >= mercenary_count)
  408. db = &mercenary_db[mercenary_count];
  409. else
  410. db = &mercenary_db[i];
  411. db->class_ = class_;
  412. safestrncpy(db->sprite, str[1], NAME_LENGTH);
  413. safestrncpy(db->name, str[2], NAME_LENGTH);
  414. db->lv = atoi(str[3]);
  415. status = &db->status;
  416. db->vd.class_ = db->class_;
  417. status->max_hp = atoi(str[4]);
  418. status->max_sp = atoi(str[5]);
  419. status->rhw.range = atoi(str[6]);
  420. status->rhw.atk = atoi(str[7]);
  421. status->rhw.atk2 = status->rhw.atk + atoi(str[8]);
  422. status->def = atoi(str[9]);
  423. status->mdef = atoi(str[10]);
  424. status->str = atoi(str[11]);
  425. status->agi = atoi(str[12]);
  426. status->vit = atoi(str[13]);
  427. status->int_ = atoi(str[14]);
  428. status->dex = atoi(str[15]);
  429. status->luk = atoi(str[16]);
  430. db->range2 = atoi(str[17]);
  431. db->range3 = atoi(str[18]);
  432. status->size = atoi(str[19]);
  433. status->race = atoi(str[20]);
  434. ele = atoi(str[21]);
  435. status->def_ele = ele%20;
  436. status->ele_lv = (unsigned char)floor(ele/20.);
  437. if( !CHK_ELEMENT(status->def_ele) )
  438. {
  439. ShowWarning("Mercenary %d has invalid element type %d (max element is %d)\n", db->class_, status->def_ele, ELE_ALL - 1);
  440. status->def_ele = ELE_NEUTRAL;
  441. }
  442. if( !CHK_ELEMENT_LEVEL(status->ele_lv) )
  443. {
  444. ShowWarning("Mercenary %d has invalid element level %d (max is %d)\n", db->class_, status->ele_lv, MAX_ELE_LEVEL);
  445. status->ele_lv = 1;
  446. }
  447. status->aspd_rate = 1000;
  448. status->speed = atoi(str[22]);
  449. status->adelay = atoi(str[23]);
  450. status->amotion = atoi(str[24]);
  451. status->dmotion = atoi(str[25]);
  452. if (i >= mercenary_count)
  453. mercenary_count++;
  454. return true;
  455. }
  456. /**
  457. * Load Mercenary's database
  458. **/
  459. void mercenary_readdb(void) {
  460. const char *filename[]={ "mercenary_db.txt",DBIMPORT"/mercenary_db.txt"};
  461. uint8 i;
  462. mercenary_count = 0; //Reset the counter
  463. memset(mercenary_db,0,sizeof(mercenary_db));
  464. for(i = 0; i<ARRAYLENGTH(filename); i++){
  465. sv_readdb(db_path, filename[i], ',', 26, 26, MAX_MERCENARY_CLASS, &mercenary_readdb_sub, i > 0);
  466. }
  467. }
  468. /**
  469. * Read each line of Mercenary's skill
  470. **/
  471. static bool mercenary_read_skilldb_sub(char* str[], int columns, int current)
  472. {// <merc id>,<skill id>,<skill level>
  473. struct s_mercenary_db *db;
  474. uint16 class_, skill_id, skill_lv;
  475. uint8 i = 0;
  476. short idx = -1;
  477. class_ = atoi(str[0]);
  478. ARR_FIND(0, MAX_MERCENARY_CLASS, i, class_ == mercenary_db[i].class_);
  479. if( i == MAX_MERCENARY_CLASS )
  480. {
  481. ShowError("read_mercenary_skilldb : Class %d not found in mercenary_db for skill entry.\n", class_);
  482. return false;
  483. }
  484. skill_id = atoi(str[1]);
  485. if( (idx = mercenary_skill_get_index(skill_id)) == -1 ) {
  486. ShowError("read_mercenary_skilldb: Invalid Mercenary skill '%s'.\n", str[1]);
  487. return false;
  488. }
  489. db = &mercenary_db[i];
  490. skill_lv = atoi(str[2]);
  491. db->skill[idx].id = skill_id;
  492. db->skill[idx].lv = skill_lv;
  493. return true;
  494. }
  495. /**
  496. * Load Mercenary's skill database
  497. **/
  498. void mercenary_read_skilldb(void){
  499. const char *filename[]={ "mercenary_skill_db.txt",DBIMPORT"/mercenary_skill_db.txt"};
  500. uint8 i;
  501. for(i = 0; i<ARRAYLENGTH(filename); i++){
  502. sv_readdb(db_path, filename[i], ',', 3, 3, -1, &mercenary_read_skilldb_sub, i > 0);
  503. }
  504. }
  505. /**
  506. * Init Mercenary datas
  507. **/
  508. void do_init_mercenary(void){
  509. mercenary_readdb();
  510. mercenary_read_skilldb();
  511. //add_timer_func_list(mercenary_contract, "mercenary_contract");
  512. }
  513. /**
  514. * Do Final Mercenary datas
  515. **/
  516. void do_final_mercenary(void){
  517. //Nothing to do yet
  518. }