homunculus.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "homunculus.hpp"
  4. #include <stdlib.h>
  5. #include "../common/cbasetypes.hpp"
  6. #include "../common/malloc.hpp"
  7. #include "../common/mmo.hpp"
  8. #include "../common/nullpo.hpp"
  9. #include "../common/random.hpp"
  10. #include "../common/showmsg.hpp"
  11. #include "../common/strlib.hpp"
  12. #include "../common/timer.hpp"
  13. #include "../common/utils.hpp"
  14. #include "battle.hpp"
  15. #include "clif.hpp"
  16. #include "intif.hpp"
  17. #include "itemdb.hpp"
  18. #include "log.hpp"
  19. #include "npc.hpp"
  20. #include "party.hpp"
  21. #include "pc.hpp"
  22. #include "trade.hpp"
  23. struct s_homunculus_db homunculus_db[MAX_HOMUNCULUS_CLASS]; //[orn]
  24. struct homun_skill_tree_entry hskill_tree[MAX_HOMUNCULUS_CLASS][MAX_HOM_SKILL_TREE];
  25. static TIMER_FUNC(hom_hungry);
  26. static uint16 homunculus_count;
  27. static unsigned int hexptbl[MAX_LEVEL];
  28. //For holding the view data of npc classes. [Skotlex]
  29. static struct view_data hom_viewdb[MAX_HOMUNCULUS_CLASS];
  30. struct s_homun_intimacy_grade {
  31. //const char *grade;
  32. uint32 min_value;
  33. };
  34. /// Intimacy grade, order based on enum e_homun_grade
  35. static struct s_homun_intimacy_grade intimacy_grades[] = {
  36. { /*"Hate with passion",*/ 100 },
  37. { /*"Hate", */ 400 },
  38. { /*"Awkward", */ 1100 },
  39. { /*"Shy", */ 10100 },
  40. { /*"Neutral", */ 25100 },
  41. { /*"Cordial", */ 75100 },
  42. { /*"Loyal", */ 91100 },
  43. };
  44. /**
  45. * Check if the skill is a valid homunculus skill based skill range or availablity in skill db
  46. * @param skill_id
  47. * @return -1 if invalid skill or skill index for homunculus skill in s_homunculus::hskill
  48. */
  49. short hom_skill_get_index(uint16 skill_id) {
  50. if (!SKILL_CHK_HOMUN(skill_id))
  51. return -1;
  52. skill_id -= HM_SKILLBASE;
  53. if (skill_id >= MAX_HOMUNSKILL)
  54. return -1;
  55. return skill_id;
  56. }
  57. /**
  58. * Check homunculus class for array look up
  59. * @param class_
  60. * @return Class index or -1 if invalid class
  61. */
  62. static short hom_class2index(int class_) {
  63. if (homdb_checkid(class_))
  64. return class_ - HM_CLASS_BASE;
  65. return -1;
  66. }
  67. /**
  68. * Get homunculus view data
  69. * @param class_ Homunculus class
  70. * @return vd
  71. */
  72. struct view_data* hom_get_viewdata(int class_)
  73. { //Returns the viewdata for homunculus
  74. if (homdb_checkid(class_))
  75. return &hom_viewdb[class_-HM_CLASS_BASE];
  76. return NULL;
  77. }
  78. /**
  79. * Get homunculus type of specified class_
  80. * @param class_
  81. * @return enum homun_type
  82. */
  83. enum homun_type hom_class2type(int class_) {
  84. int mid = hom_class2mapid(class_);
  85. if((mid&(HOM_REG|HOM_EVO)) == (HOM_REG|HOM_EVO))
  86. return HT_EVO;
  87. else if(mid&(HOM_REG))
  88. return HT_REG;
  89. else if(mid&(HOM_S))
  90. return HT_S;
  91. else
  92. return HT_INVALID;
  93. }
  94. /**
  95. * Get homunculus MAPID from specified class
  96. * @param hom_class
  97. * @return Homunculus MAPID (see enum hom_mapid)
  98. */
  99. int hom_class2mapid(int hom_class)
  100. {
  101. switch(hom_class)
  102. {
  103. // Normal Homunculus
  104. case 6001: case 6005: return MAPID_LIF;
  105. case 6002: case 6006: return MAPID_AMISTR;
  106. case 6003: case 6007: return MAPID_FILIR;
  107. case 6004: case 6008: return MAPID_VANILMIRTH;
  108. // Evolved Homunculus
  109. case 6009: case 6013: return MAPID_LIF_E;
  110. case 6010: case 6014: return MAPID_AMISTR_E;
  111. case 6011: case 6015: return MAPID_FILIR_E;
  112. case 6012: case 6016: return MAPID_VANILMIRTH_E;
  113. // Homunculus S
  114. case 6048: return MAPID_EIRA;
  115. case 6049: return MAPID_BAYERI;
  116. case 6050: return MAPID_SERA;
  117. case 6051: return MAPID_DIETER;
  118. case 6052: return MAPID_ELANOR;
  119. default: return -1;
  120. }
  121. }
  122. /**
  123. * Add homunculus spirit ball
  124. * @param hd
  125. * @param max Maximum number of spirit ball
  126. */
  127. void hom_addspiritball(TBL_HOM *hd, int max) {
  128. nullpo_retv(hd);
  129. if (max > MAX_SPIRITBALL)
  130. max = MAX_SPIRITBALL;
  131. if (hd->homunculus.spiritball < 0)
  132. hd->homunculus.spiritball = 0;
  133. if (hd->homunculus.spiritball && hd->homunculus.spiritball >= max)
  134. hd->homunculus.spiritball = max;
  135. else
  136. hd->homunculus.spiritball++;
  137. clif_spiritball(&hd->bl);
  138. }
  139. /**
  140. * Delete homunculus spirit ball
  141. * @param hd
  142. * @param count Number spirit ball will be deleted
  143. * @param type 1 - Update client
  144. */
  145. void hom_delspiritball(TBL_HOM *hd, int count, int type) {
  146. nullpo_retv(hd);
  147. if (hd->homunculus.spiritball <= 0) {
  148. hd->homunculus.spiritball = 0;
  149. return;
  150. }
  151. if (count <= 0)
  152. return;
  153. if (count > MAX_SPIRITBALL)
  154. count = MAX_SPIRITBALL;
  155. if (count > hd->homunculus.spiritball)
  156. count = hd->homunculus.spiritball;
  157. hd->homunculus.spiritball -= count;
  158. if (!type)
  159. clif_spiritball(&hd->bl);
  160. }
  161. /**
  162. * Update homunculus info to its master after receiving damage
  163. * @param hd
  164. */
  165. void hom_damage(struct homun_data *hd) {
  166. if (hd->master)
  167. clif_hominfo(hd->master,hd,0);
  168. }
  169. /**
  170. * Set homunculus's dead status
  171. * @param hd
  172. * @return flag &1 - Standard dead, &2 - Remove object from map, &4 - Delete object from memory
  173. */
  174. int hom_dead(struct homun_data *hd)
  175. {
  176. //There's no intimacy penalties on death (from Tharis)
  177. struct map_session_data *sd = hd->master;
  178. clif_emotion(&hd->bl, ET_KEK);
  179. //Delete timers when dead.
  180. hom_hungry_timer_delete(hd);
  181. hd->homunculus.hp = 0;
  182. if (!sd) //unit remove map will invoke unit free
  183. return 3;
  184. clif_emotion(&sd->bl, ET_CRY);
  185. //Remove from map (if it has no intimacy, it is auto-removed from memory)
  186. return 3;
  187. }
  188. /**
  189. * Vaporize a character's homunculus
  190. * @param sd
  191. * @param flag 1: then HP needs to be 80% or above. 2: then set to morph state.
  192. */
  193. int hom_vaporize(struct map_session_data *sd, int flag)
  194. {
  195. struct homun_data *hd;
  196. nullpo_ret(sd);
  197. hd = sd->hd;
  198. if (!hd || hd->homunculus.vaporize)
  199. return 0;
  200. if (status_isdead(&hd->bl))
  201. return 0; //Can't vaporize a dead homun.
  202. if (flag == HOM_ST_REST && get_percentage(hd->battle_status.hp, hd->battle_status.max_hp) < 80)
  203. return 0;
  204. hd->regen.state.block = 3; //Block regen while vaporized.
  205. //Delete timers when vaporized.
  206. hom_hungry_timer_delete(hd);
  207. hd->homunculus.vaporize = flag ? flag : HOM_ST_REST;
  208. if (battle_config.hom_setting&HOMSET_RESET_REUSESKILL_VAPORIZED) {
  209. hd->blockskill.clear();
  210. hd->blockskill.shrink_to_fit();
  211. }
  212. clif_hominfo(sd, sd->hd, 0);
  213. hom_save(hd);
  214. return unit_remove_map(&hd->bl, CLR_OUTSIGHT);
  215. }
  216. /**
  217. * Delete a homunculus, completely "killing it".
  218. * Emote is the emotion the master should use, send negative to disable.
  219. * @param hd
  220. * @param emote
  221. */
  222. int hom_delete(struct homun_data *hd, int emote)
  223. {
  224. struct map_session_data *sd;
  225. nullpo_ret(hd);
  226. sd = hd->master;
  227. if (!sd)
  228. return unit_free(&hd->bl,CLR_DEAD);
  229. if (emote >= 0)
  230. clif_emotion(&sd->bl, emote);
  231. //This makes it be deleted right away.
  232. hd->homunculus.intimacy = 0;
  233. // Send homunculus_dead to client
  234. hd->homunculus.hp = 0;
  235. clif_hominfo(sd, hd, 0);
  236. return unit_remove_map(&hd->bl,CLR_OUTSIGHT);
  237. }
  238. /**
  239. * Calculates homunculus skill tree
  240. * @param hd
  241. * @param flag_envolve
  242. */
  243. void hom_calc_skilltree(struct homun_data *hd, bool flag_evolve) {
  244. uint8 i;
  245. short c = 0;
  246. nullpo_retv(hd);
  247. /* load previous homunculus form skills first. */
  248. if (hd->homunculus.prev_class != 0 && (c = hom_class2index(hd->homunculus.prev_class)) >= 0) {
  249. for (i = 0; i < MAX_HOM_SKILL_TREE; i++) {
  250. uint16 skill_id;
  251. short idx = -1;
  252. bool fail = false;
  253. if (!(skill_id = hskill_tree[c][i].id) || (idx = hom_skill_get_index(skill_id)) == -1)
  254. continue;
  255. if (hd->homunculus.hskill[idx].id)
  256. continue; //Skill already known.
  257. if (!battle_config.skillfree) {
  258. uint8 j;
  259. if (hskill_tree[c][i].need_level > hd->homunculus.level)
  260. continue;
  261. for (j = 0; j < MAX_HOM_SKILL_REQUIRE; j++) {
  262. if (hskill_tree[c][i].need[j].id &&
  263. hom_checkskill(hd,hskill_tree[c][i].need[j].id) < hskill_tree[c][i].need[j].lv)
  264. {
  265. fail = true;
  266. break;
  267. }
  268. }
  269. }
  270. if (!fail)
  271. hd->homunculus.hskill[idx].id = skill_id;
  272. }
  273. }
  274. if ((c = hom_class2index(hd->homunculus.class_)) < 0)
  275. return;
  276. for (i = 0; i < MAX_HOM_SKILL_TREE; i++) {
  277. unsigned int intimacy = 0;
  278. uint16 skill_id;
  279. short idx = -1;
  280. bool fail = false;
  281. if (!(skill_id = hskill_tree[c][i].id) || (idx = hom_skill_get_index(skill_id)) == -1)
  282. continue;
  283. if (hd->homunculus.hskill[idx].id)
  284. continue; //Skill already known.
  285. intimacy = (flag_evolve) ? 10 : hd->homunculus.intimacy;
  286. if (intimacy < hskill_tree[c][i].intimacy * 100)
  287. continue;
  288. if (!battle_config.skillfree) {
  289. uint8 j;
  290. if (hskill_tree[c][i].need_level > hd->homunculus.level)
  291. continue;
  292. for (j = 0; j < MAX_HOM_SKILL_REQUIRE; j++) {
  293. if (hskill_tree[c][i].need[j].id &&
  294. hom_checkskill(hd,hskill_tree[c][i].need[j].id) < hskill_tree[c][i].need[j].lv)
  295. {
  296. fail = true;
  297. break;
  298. }
  299. }
  300. }
  301. if (!fail)
  302. hd->homunculus.hskill[idx].id = skill_id;
  303. }
  304. if (hd->master)
  305. clif_homskillinfoblock(hd->master);
  306. }
  307. /**
  308. * Check skill from homunculus
  309. * @param hd
  310. * @param skill_id
  311. * @return Skill Level or 0 if invalid or unlearned skill
  312. */
  313. short hom_checkskill(struct homun_data *hd,uint16 skill_id)
  314. {
  315. short idx = hom_skill_get_index(skill_id);
  316. if (idx < 0) // Invalid skill
  317. return 0;
  318. if (!hd)
  319. return 0;
  320. if (hd->homunculus.hskill[idx].id == skill_id)
  321. return (hd->homunculus.hskill[idx].lv);
  322. return 0;
  323. }
  324. /**
  325. * Get max level for homunculus skill
  326. * @param id Skill ID
  327. * @param b_class
  328. * @return Skill Level
  329. */
  330. int hom_skill_tree_get_max(int skill_id, int b_class){
  331. uint8 i;
  332. if ((b_class = hom_class2index(b_class)) < 0)
  333. return 0;
  334. ARR_FIND(0, MAX_HOM_SKILL_TREE, i, hskill_tree[b_class][i].id == skill_id);
  335. if (i < MAX_HOM_SKILL_TREE)
  336. return hskill_tree[b_class][i].max;
  337. return skill_get_max(skill_id);
  338. }
  339. /**
  340. * Get required minimum level to learn the skill
  341. * @param class_ Homunculus class
  342. * @param skill_id Homunculus skill ID
  343. * @return Level required or 0 if invalid
  344. **/
  345. uint8 hom_skill_get_min_level(int class_, uint16 skill_id) {
  346. short class_idx = hom_class2index(class_), skill_idx = -1;
  347. uint8 i;
  348. if (class_idx == -1 || (skill_idx = hom_skill_get_index(skill_id)) == -1)
  349. return 0;
  350. ARR_FIND(0, MAX_HOM_SKILL_REQUIRE, i, hskill_tree[class_idx][i].id == skill_id);
  351. if (i == MAX_HOM_SKILL_REQUIRE)
  352. return 0;
  353. return hskill_tree[class_idx][i].need_level;
  354. }
  355. /**
  356. * Level up an homunculus skill
  357. * @param hd
  358. * @param skill_id
  359. */
  360. void hom_skillup(struct homun_data *hd, uint16 skill_id)
  361. {
  362. short idx = 0;
  363. nullpo_retv(hd);
  364. if (hd->homunculus.vaporize)
  365. return;
  366. if ((idx = hom_skill_get_index(skill_id)) < 0)
  367. return;
  368. if (hd->homunculus.skillpts > 0 &&
  369. hd->homunculus.hskill[idx].id &&
  370. hd->homunculus.hskill[idx].flag == SKILL_FLAG_PERMANENT && //Don't allow raising while you have granted skills. [Skotlex]
  371. hd->homunculus.level >= hom_skill_get_min_level(hd->homunculus.class_, skill_id) &&
  372. hd->homunculus.hskill[idx].lv < hom_skill_tree_get_max(skill_id, hd->homunculus.class_)
  373. )
  374. {
  375. hd->homunculus.hskill[idx].lv++;
  376. hd->homunculus.skillpts-- ;
  377. status_calc_homunculus(hd, SCO_NONE);
  378. if (hd->master) {
  379. clif_homskillup(hd->master, skill_id);
  380. clif_hominfo(hd->master,hd,0);
  381. clif_homskillinfoblock(hd->master);
  382. }
  383. }
  384. }
  385. /**
  386. * Homunculus leveled up
  387. * @param hd
  388. */
  389. int hom_levelup(struct homun_data *hd)
  390. {
  391. struct s_homunculus *hom;
  392. struct h_stats *min = NULL, *max = NULL;
  393. int growth_str, growth_agi, growth_vit, growth_int, growth_dex, growth_luk ;
  394. int growth_max_hp, growth_max_sp ;
  395. int m_class;
  396. if ((m_class = hom_class2mapid(hd->homunculus.class_)) == -1) {
  397. ShowError("hom_levelup: Invalid class %d. \n", hd->homunculus.class_);
  398. return 0;
  399. }
  400. /// When homunculus is homunculus S, we check to see if we need to apply previous class stats
  401. if(m_class&HOM_S && hd->homunculus.level < battle_config.hom_S_growth_level) {
  402. int i;
  403. if (!hd->homunculus.prev_class) {
  404. /// We also need to be sure that the previous class exists, otherwise give it something to work with
  405. hd->homunculus.prev_class = 6001;
  406. }
  407. // Give the homunculus the level up stats database it needs
  408. i = hom_search(hd->homunculus.prev_class,HOMUNCULUS_CLASS);
  409. if (i < 0) // Nothing should go wrong here, but check anyways
  410. return 0;
  411. max = &homunculus_db[i].gmax;
  412. min = &homunculus_db[i].gmin;
  413. }
  414. if (((m_class&HOM_REG) && hd->homunculus.level >= battle_config.hom_max_level)
  415. || ((m_class&HOM_S) && hd->homunculus.level >= battle_config.hom_S_max_level)
  416. || !hd->exp_next || hd->homunculus.exp < hd->exp_next)
  417. return 0;
  418. hom = &hd->homunculus;
  419. hom->level++ ;
  420. if (!(hom->level % 3))
  421. hom->skillpts++ ; //1 skillpoint each 3 base level
  422. hom->exp -= hd->exp_next ;
  423. hd->exp_next = hexptbl[hom->level - 1] ;
  424. if (!max) {
  425. max = &hd->homunculusDB->gmax;
  426. min = &hd->homunculusDB->gmin;
  427. }
  428. growth_max_hp = rnd_value(min->HP, max->HP);
  429. growth_max_sp = rnd_value(min->SP, max->SP);
  430. growth_str = rnd_value(min->str, max->str);
  431. growth_agi = rnd_value(min->agi, max->agi);
  432. growth_vit = rnd_value(min->vit, max->vit);
  433. growth_dex = rnd_value(min->dex, max->dex);
  434. growth_int = rnd_value(min->int_,max->int_);
  435. growth_luk = rnd_value(min->luk, max->luk);
  436. //Aegis discards the decimals in the stat growth values!
  437. growth_str-=growth_str%10;
  438. growth_agi-=growth_agi%10;
  439. growth_vit-=growth_vit%10;
  440. growth_dex-=growth_dex%10;
  441. growth_int-=growth_int%10;
  442. growth_luk-=growth_luk%10;
  443. hom->max_hp += growth_max_hp;
  444. hom->max_sp += growth_max_sp;
  445. hom->str += growth_str;
  446. hom->agi += growth_agi;
  447. hom->vit += growth_vit;
  448. hom->dex += growth_dex;
  449. hom->int_+= growth_int;
  450. hom->luk += growth_luk;
  451. APPLY_HOMUN_LEVEL_STATWEIGHT();
  452. // Needed to update skill list for mutated homunculus so unlocked skills will appear when the needed level is reached.
  453. status_calc_homunculus(hd,SCO_NONE);
  454. clif_hominfo(hd->master,hd,0);
  455. clif_homskillinfoblock(hd->master);
  456. if ( hd->master && battle_config.homunculus_show_growth ) {
  457. char output[256] ;
  458. sprintf(output,
  459. "Growth: hp:%d sp:%d str(%.2f) agi(%.2f) vit(%.2f) int(%.2f) dex(%.2f) luk(%.2f) ",
  460. growth_max_hp, growth_max_sp,
  461. growth_str/10.0, growth_agi/10.0, growth_vit/10.0,
  462. growth_int/10.0, growth_dex/10.0, growth_luk/10.0);
  463. clif_messagecolor(&hd->master->bl, color_table[COLOR_LIGHT_GREEN], output, false, SELF);
  464. }
  465. return 1;
  466. }
  467. /**
  468. * Changes homunculus class
  469. * @param hd
  470. * @param class_ old class
  471. * @reutrn Fals if the class cannot be changed, True if otherwise
  472. */
  473. static bool hom_change_class(struct homun_data *hd, short class_) {
  474. int i;
  475. i = hom_search(class_,HOMUNCULUS_CLASS);
  476. if (i < 0)
  477. return false;
  478. hd->homunculusDB = &homunculus_db[i];
  479. hd->homunculus.class_ = class_;
  480. status_set_viewdata(&hd->bl, class_);
  481. hom_calc_skilltree(hd, 1);
  482. return true;
  483. }
  484. /**
  485. * Make an homonculus evolve, (changing in evolution class and apply bonus)
  486. * @param hd : homonculus datas
  487. * @return 0:failure, 1:success
  488. */
  489. int hom_evolution(struct homun_data *hd)
  490. {
  491. struct s_homunculus *hom;
  492. struct h_stats *max, *min;
  493. struct map_session_data *sd;
  494. nullpo_ret(hd);
  495. if(!hd->homunculusDB->evo_class || hd->homunculus.class_ == hd->homunculusDB->evo_class) {
  496. clif_emotion(&hd->bl, ET_SWEAT);
  497. return 0 ;
  498. }
  499. sd = hd->master;
  500. if (!sd)
  501. return 0;
  502. if (!hom_change_class(hd, hd->homunculusDB->evo_class)) {
  503. ShowError("hom_evolution: Can't evolve homunc from %d to %d", hd->homunculus.class_, hd->homunculusDB->evo_class);
  504. return 0;
  505. }
  506. //Apply evolution bonuses
  507. hom = &hd->homunculus;
  508. max = &hd->homunculusDB->emax;
  509. min = &hd->homunculusDB->emin;
  510. hom->max_hp += rnd_value(min->HP, max->HP);
  511. hom->max_sp += rnd_value(min->SP, max->SP);
  512. hom->str += 10*rnd_value(min->str, max->str);
  513. hom->agi += 10*rnd_value(min->agi, max->agi);
  514. hom->vit += 10*rnd_value(min->vit, max->vit);
  515. hom->int_+= 10*rnd_value(min->int_,max->int_);
  516. hom->dex += 10*rnd_value(min->dex, max->dex);
  517. hom->luk += 10*rnd_value(min->luk, max->luk);
  518. hom->intimacy = battle_config.homunculus_evo_intimacy_reset;
  519. unit_remove_map(&hd->bl, CLR_OUTSIGHT);
  520. if (map_addblock(&hd->bl))
  521. return 0;
  522. clif_spawn(&hd->bl);
  523. clif_emotion(&sd->bl, ET_BEST);
  524. clif_specialeffect(&hd->bl,EF_HO_UP,AREA);
  525. //status_Calc flag&1 will make current HP/SP be reloaded from hom structure
  526. hom->hp = hd->battle_status.hp;
  527. hom->sp = hd->battle_status.sp;
  528. status_calc_homunculus(hd, SCO_FIRST);
  529. if (!(battle_config.hom_setting&HOMSET_NO_INSTANT_LAND_SKILL))
  530. skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately
  531. return 1 ;
  532. }
  533. /**
  534. * Make an homonculus mutate in renewal homon
  535. * @param hd : homonculus datas
  536. * @param homun_id : id to make it transform into (must be a valid homon class)
  537. * @return 0:failure, 1:sucess
  538. */
  539. int hom_mutate(struct homun_data *hd, int homun_id)
  540. {
  541. struct s_homunculus *hom;
  542. struct map_session_data *sd;
  543. int m_class, m_id, prev_class = 0;
  544. nullpo_ret(hd);
  545. m_class = hom_class2mapid(hd->homunculus.class_);
  546. m_id = hom_class2mapid(homun_id);
  547. if( m_class == -1 || m_id == -1 || !(m_class&HOM_EVO) || !(m_id&HOM_S) ) {
  548. clif_emotion(&hd->bl, ET_SWEAT);
  549. return 0;
  550. }
  551. sd = hd->master;
  552. if (!sd)
  553. return 0;
  554. prev_class = hd->homunculus.class_;
  555. if (!hom_change_class(hd, homun_id)) {
  556. ShowError("hom_mutate: Can't evolve homunc from %d to %d", hd->homunculus.class_, homun_id);
  557. return 0;
  558. }
  559. unit_remove_map(&hd->bl, CLR_OUTSIGHT);
  560. if(map_addblock(&hd->bl))
  561. return 0;
  562. clif_spawn(&hd->bl);
  563. clif_emotion(&sd->bl, ET_BEST);
  564. clif_specialeffect(&hd->bl,EF_HO_UP,AREA);
  565. //status_Calc flag&1 will make current HP/SP be reloaded from hom structure
  566. hom = &hd->homunculus;
  567. hom->hp = hd->battle_status.hp;
  568. hom->sp = hd->battle_status.sp;
  569. hom->prev_class = prev_class;
  570. status_calc_homunculus(hd, SCO_FIRST);
  571. if (!(battle_config.hom_setting&HOMSET_NO_INSTANT_LAND_SKILL))
  572. skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately
  573. return 1;
  574. }
  575. /**
  576. * Add homunculus exp
  577. * @param hd
  578. * @param exp Added EXP
  579. */
  580. void hom_gainexp(struct homun_data *hd,int exp)
  581. {
  582. int m_class;
  583. nullpo_retv(hd);
  584. if(hd->homunculus.vaporize)
  585. return;
  586. if((m_class = hom_class2mapid(hd->homunculus.class_)) == -1) {
  587. ShowError("hom_gainexp: Invalid class %d. \n", hd->homunculus.class_);
  588. return;
  589. }
  590. if( hd->exp_next == 0 ||
  591. ((m_class&HOM_REG) && hd->homunculus.level >= battle_config.hom_max_level) ||
  592. ((m_class&HOM_S) && hd->homunculus.level >= battle_config.hom_S_max_level) )
  593. {
  594. hd->homunculus.exp = 0;
  595. return;
  596. }
  597. hd->homunculus.exp += exp;
  598. if (hd->master && hd->homunculus.exp < hd->exp_next) {
  599. clif_hominfo(hd->master,hd,0);
  600. return;
  601. }
  602. // Do the levelup(s)
  603. while( hd->homunculus.exp > hd->exp_next ){
  604. // Max level reached or error
  605. if( !hom_levelup(hd) ){
  606. break;
  607. }
  608. }
  609. if( hd->exp_next == 0 )
  610. hd->homunculus.exp = 0 ;
  611. clif_specialeffect(&hd->bl,EF_HO_UP,AREA);
  612. status_calc_homunculus(hd, SCO_NONE);
  613. status_percent_heal(&hd->bl, 100, 100);
  614. }
  615. /**
  616. * Increase homunculu sintimacy
  617. * @param hd
  618. * @param value Added intimacy
  619. * @return New intimacy value
  620. */
  621. int hom_increase_intimacy(struct homun_data * hd, unsigned int value)
  622. {
  623. nullpo_ret(hd);
  624. if (battle_config.homunculus_friendly_rate != 100)
  625. value = (value * battle_config.homunculus_friendly_rate) / 100;
  626. if (hd->homunculus.intimacy + value <= 100000)
  627. hd->homunculus.intimacy += value;
  628. else
  629. hd->homunculus.intimacy = 100000;
  630. return hd->homunculus.intimacy;
  631. }
  632. /**
  633. * Decrease homunculu sintimacy
  634. * @param hd
  635. * @param value Reduced intimacy
  636. * @return New intimacy value
  637. */
  638. int hom_decrease_intimacy(struct homun_data * hd, unsigned int value)
  639. {
  640. nullpo_ret(hd);
  641. if (hd->homunculus.intimacy >= value)
  642. hd->homunculus.intimacy -= value;
  643. else
  644. hd->homunculus.intimacy = 0;
  645. return hd->homunculus.intimacy;
  646. }
  647. /**
  648. * Update homunculus info to master after healing
  649. * @param hd
  650. */
  651. void hom_heal(struct homun_data *hd) {
  652. if (hd->master)
  653. clif_hominfo(hd->master,hd,0);
  654. }
  655. /**
  656. * Save homunculus data
  657. * @param hd
  658. */
  659. void hom_save(struct homun_data *hd)
  660. {
  661. // copy data that must be saved in homunculus struct ( hp / sp )
  662. TBL_PC *sd;
  663. nullpo_retv(hd);
  664. sd = hd->master;
  665. //Do not check for max_hp/max_sp caps as current could be higher to max due
  666. //to status changes/skills (they will be capped as needed upon stat
  667. //calculation on login)
  668. hd->homunculus.hp = hd->battle_status.hp;
  669. hd->homunculus.sp = hd->battle_status.sp;
  670. intif_homunculus_requestsave(sd->status.account_id, &hd->homunculus);
  671. }
  672. /**
  673. * Perform requested action from selected homunculus menu
  674. * @param sd
  675. * @param type
  676. */
  677. void hom_menu(struct map_session_data *sd, int type)
  678. {
  679. nullpo_retv(sd);
  680. if (sd->hd == NULL)
  681. return;
  682. switch(type) {
  683. case 0:
  684. break;
  685. case 1:
  686. hom_food(sd, sd->hd);
  687. break;
  688. case 2:
  689. hom_delete(sd->hd, -1);
  690. break;
  691. default:
  692. ShowError("hom_menu : unknown menu choice : %d\n", type);
  693. break;
  694. }
  695. }
  696. /**
  697. * Feed homunculus
  698. * @param sd
  699. * @param hd
  700. */
  701. int hom_food(struct map_session_data *sd, struct homun_data *hd)
  702. {
  703. int i, foodID, emotion;
  704. nullpo_retr(1,sd);
  705. nullpo_retr(1,hd);
  706. if (hd->homunculus.vaporize)
  707. return 1;
  708. foodID = hd->homunculusDB->foodID;
  709. i = pc_search_inventory(sd,foodID);
  710. if (i < 0) {
  711. clif_hom_food(sd,foodID,0);
  712. return 1;
  713. }
  714. pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME);
  715. if ( hd->homunculus.hunger >= 91 ) {
  716. hom_decrease_intimacy(hd, 50);
  717. emotion = ET_KEK;
  718. } else if ( hd->homunculus.hunger >= 76 ) {
  719. hom_decrease_intimacy(hd, 5);
  720. emotion = ET_PROFUSELY_SWEAT;
  721. } else if ( hd->homunculus.hunger >= 26 ) {
  722. hom_increase_intimacy(hd, 75);
  723. emotion = ET_DELIGHT;
  724. } else if ( hd->homunculus.hunger >= 11 ) {
  725. hom_increase_intimacy(hd, 100);
  726. emotion = ET_DELIGHT;
  727. } else {
  728. hom_increase_intimacy(hd, 50);
  729. emotion = ET_DELIGHT;
  730. }
  731. hd->homunculus.hunger += 10; //dunno increase value for each food
  732. if(hd->homunculus.hunger > 100)
  733. hd->homunculus.hunger = 100;
  734. log_feeding(sd, LOG_FEED_HOMUNCULUS, foodID);
  735. clif_emotion(&hd->bl,emotion);
  736. clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
  737. clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
  738. clif_hom_food(sd,foodID,1);
  739. // Too much food :/
  740. if(hd->homunculus.intimacy == 0)
  741. return hom_delete(sd->hd, ET_HUK);
  742. return 0;
  743. }
  744. /**
  745. * Timer to reduce hunger level
  746. */
  747. static TIMER_FUNC(hom_hungry){
  748. struct map_session_data *sd;
  749. struct homun_data *hd;
  750. sd = map_id2sd(id);
  751. if (!sd)
  752. return 1;
  753. if (!sd->status.hom_id || !(hd=sd->hd))
  754. return 1;
  755. if (hd->hungry_timer != tid) {
  756. ShowError("hom_hungry_timer %d != %d\n",hd->hungry_timer,tid);
  757. return 0;
  758. }
  759. hd->hungry_timer = INVALID_TIMER;
  760. hd->homunculus.hunger--;
  761. if(hd->homunculus.hunger <= 10) {
  762. clif_emotion(&hd->bl, ET_FRET);
  763. } else if(hd->homunculus.hunger == 25) {
  764. clif_emotion(&hd->bl, ET_SCRATCH);
  765. } else if(hd->homunculus.hunger == 75) {
  766. clif_emotion(&hd->bl, ET_OK);
  767. }
  768. if( battle_config.feature_homunculus_autofeed && hd->homunculus.autofeed && hd->homunculus.hunger <= battle_config.feature_homunculus_autofeed_rate ){
  769. hom_food( sd, hd );
  770. }
  771. if (hd->homunculus.hunger < 0) {
  772. hd->homunculus.hunger = 0;
  773. // Delete the homunculus if intimacy <= 100
  774. if (!hom_decrease_intimacy(hd, 100))
  775. return hom_delete(hd, ET_HUK);
  776. clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
  777. }
  778. clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
  779. hd->hungry_timer = add_timer(tick+hd->homunculusDB->hungryDelay,hom_hungry,sd->bl.id,0); //simple Fix albator
  780. return 0;
  781. }
  782. /**
  783. * Remove hungry timer from homunculus
  784. * @param hd
  785. */
  786. int hom_hungry_timer_delete(struct homun_data *hd)
  787. {
  788. nullpo_ret(hd);
  789. if (hd->hungry_timer != INVALID_TIMER) {
  790. delete_timer(hd->hungry_timer,hom_hungry);
  791. hd->hungry_timer = INVALID_TIMER;
  792. }
  793. return 1;
  794. }
  795. /**
  796. * Change homunculus name
  797. */
  798. int hom_change_name(struct map_session_data *sd,char *name)
  799. {
  800. int i;
  801. struct homun_data *hd;
  802. nullpo_retr(1, sd);
  803. hd = sd->hd;
  804. if (!hom_is_active(hd))
  805. return 1;
  806. if (hd->homunculus.rename_flag && !battle_config.hom_rename)
  807. return 1;
  808. for (i = 0; i < NAME_LENGTH && name[i];i++) {
  809. if (!(name[i]&0xe0) || name[i] == 0x7f)
  810. return 1;
  811. }
  812. return intif_rename_hom(sd, name);
  813. }
  814. /**
  815. * Acknowledge change name request from inter-server
  816. * @param sd
  817. * @param name
  818. * @param flag
  819. */
  820. void hom_change_name_ack(struct map_session_data *sd, char* name, int flag)
  821. {
  822. struct homun_data *hd = sd->hd;
  823. if (!hom_is_active(hd))
  824. return;
  825. normalize_name(name," ");//bugreport:3032
  826. if (!flag || name[0] == '\0') {
  827. clif_displaymessage(sd->fd, msg_txt(sd,280)); // You cannot use this name
  828. return;
  829. }
  830. safestrncpy(hd->homunculus.name,name,NAME_LENGTH);
  831. clif_name_area(&hd->bl);
  832. hd->homunculus.rename_flag = 1;
  833. clif_hominfo(sd,hd,0);
  834. }
  835. /**
  836. * Search homunculus info (food or next class)
  837. * @param key
  838. * @param type see enum e_hom_search_type
  839. * @return info found
  840. */
  841. int hom_search(int key, int type)
  842. {
  843. int i;
  844. for (i = 0; i < homunculus_count; i++) {
  845. if (homunculus_db[i].base_class <= 0)
  846. continue;
  847. switch (type) {
  848. case HOMUNCULUS_CLASS:
  849. if (homunculus_db[i].base_class == key ||
  850. homunculus_db[i].evo_class == key)
  851. return i;
  852. break;
  853. case HOMUNCULUS_FOOD:
  854. if (homunculus_db[i].foodID == key)
  855. return i;
  856. break;
  857. default:
  858. return -1;
  859. }
  860. }
  861. return -1;
  862. }
  863. /**
  864. * Create homunc structure
  865. * @param sd
  866. * @param hom
  867. */
  868. void hom_alloc(struct map_session_data *sd, struct s_homunculus *hom)
  869. {
  870. struct homun_data *hd;
  871. int i = 0;
  872. nullpo_retv(sd);
  873. Assert((sd->status.hom_id == 0 || sd->hd == 0) || sd->hd->master == sd);
  874. i = hom_search(hom->class_,HOMUNCULUS_CLASS);
  875. if(i < 0) {
  876. ShowError("hom_alloc: unknown class [%d] for homunculus '%s', requesting deletion.\n", hom->class_, hom->name);
  877. sd->status.hom_id = 0;
  878. intif_homunculus_requestdelete(hom->hom_id);
  879. return;
  880. }
  881. sd->hd = hd = (struct homun_data*)aCalloc(1,sizeof(struct homun_data));
  882. hd->bl.type = BL_HOM;
  883. hd->bl.id = npc_get_new_npc_id();
  884. hd->master = sd;
  885. hd->homunculusDB = &homunculus_db[i];
  886. memcpy(&hd->homunculus, hom, sizeof(struct s_homunculus));
  887. hd->exp_next = hexptbl[hd->homunculus.level - 1];
  888. status_set_viewdata(&hd->bl, hd->homunculus.class_);
  889. status_change_init(&hd->bl);
  890. unit_dataset(&hd->bl);
  891. hd->ud.dir = sd->ud.dir;
  892. // Find a random valid pos around the player
  893. hd->bl.m = sd->bl.m;
  894. hd->bl.x = sd->bl.x;
  895. hd->bl.y = sd->bl.y;
  896. unit_calc_pos(&hd->bl, sd->bl.x, sd->bl.y, sd->ud.dir);
  897. hd->bl.x = hd->ud.to_x;
  898. hd->bl.y = hd->ud.to_y;
  899. map_addiddb(&hd->bl);
  900. status_calc_homunculus(hd, SCO_FIRST);
  901. hd->hungry_timer = INVALID_TIMER;
  902. hd->masterteleport_timer = INVALID_TIMER;
  903. }
  904. /**
  905. * Init homunculus timers
  906. * @param hd
  907. */
  908. void hom_init_timers(struct homun_data * hd)
  909. {
  910. if (hd->hungry_timer == INVALID_TIMER)
  911. hd->hungry_timer = add_timer(gettick()+hd->homunculusDB->hungryDelay,hom_hungry,hd->master->bl.id,0);
  912. hd->regen.state.block = 0; //Restore HP/SP block.
  913. hd->masterteleport_timer = INVALID_TIMER;
  914. }
  915. /**
  916. * Make a player spawn a homonculus (call)
  917. * @param sd
  918. * @return False:failure, True:sucess
  919. */
  920. bool hom_call(struct map_session_data *sd)
  921. {
  922. struct homun_data *hd;
  923. if (!sd->status.hom_id) //Create a new homun.
  924. return hom_create_request(sd, HM_CLASS_BASE + rnd_value(0, 7)) ;
  925. // If homunc not yet loaded, load it
  926. if (!sd->hd)
  927. return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id) > 0;
  928. hd = sd->hd;
  929. if (!hd->homunculus.vaporize)
  930. return false; //Can't use this if homun wasn't vaporized.
  931. if (hd->homunculus.vaporize == HOM_ST_MORPH)
  932. return false; // Can't call homunculus (morph state).
  933. hom_init_timers(hd);
  934. hd->homunculus.vaporize = HOM_ST_ACTIVE;
  935. if (hd->bl.prev == NULL)
  936. { //Spawn him
  937. hd->bl.x = sd->bl.x;
  938. hd->bl.y = sd->bl.y;
  939. hd->bl.m = sd->bl.m;
  940. if(map_addblock(&hd->bl))
  941. return false;
  942. clif_spawn(&hd->bl);
  943. clif_send_homdata(sd,SP_ACK,0);
  944. clif_hominfo(sd,hd,1);
  945. clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
  946. clif_homskillinfoblock(sd);
  947. if (battle_config.hom_setting&HOMSET_COPY_SPEED)
  948. status_calc_bl(&hd->bl, SCB_SPEED);
  949. hom_save(hd);
  950. } else
  951. //Warp him to master.
  952. unit_warp(&hd->bl,sd->bl.m, sd->bl.x, sd->bl.y,CLR_OUTSIGHT);
  953. return true;
  954. }
  955. /**
  956. * Receive homunculus data from char server
  957. * @param account_id : owner account_id of the homon
  958. * @param sh : homonculus data from char-serv
  959. * @param flag : does the creation in inter-serv was a success (0:no,1:yes)
  960. * @return 0:failure, 1:sucess
  961. */
  962. int hom_recv_data(uint32 account_id, struct s_homunculus *sh, int flag)
  963. {
  964. struct map_session_data *sd;
  965. struct homun_data *hd;
  966. bool created = false;
  967. sd = map_id2sd(account_id);
  968. if(!sd)
  969. return 0;
  970. if (sd->status.char_id != sh->char_id)
  971. {
  972. if (sd->status.hom_id == sh->hom_id)
  973. sh->char_id = sd->status.char_id; //Correct char id.
  974. else
  975. return 0;
  976. }
  977. if(!flag) { // Failed to load
  978. sd->status.hom_id = 0;
  979. return 0;
  980. }
  981. if (!sd->status.hom_id) { //Hom just created.
  982. sd->status.hom_id = sh->hom_id;
  983. created = true;
  984. }
  985. if (sd->hd) //uh? Overwrite the data.
  986. memcpy(&sd->hd->homunculus, sh, sizeof(struct s_homunculus));
  987. else
  988. hom_alloc(sd, sh);
  989. hd = sd->hd;
  990. if (created)
  991. status_percent_heal(&hd->bl, 100, 100);
  992. if(hd && hd->homunculus.hp && !hd->homunculus.vaporize && hd->bl.prev == NULL && sd->bl.prev != NULL)
  993. {
  994. if(map_addblock(&hd->bl))
  995. return 0;
  996. clif_spawn(&hd->bl);
  997. clif_send_homdata(sd,SP_ACK,0);
  998. clif_hominfo(sd,hd,1);
  999. clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
  1000. clif_homskillinfoblock(sd);
  1001. hom_init_timers(hd);
  1002. }
  1003. return 1;
  1004. }
  1005. /**
  1006. * Ask homunculus creation to char-server
  1007. * @param sd
  1008. * @param class_
  1009. * @return True:Success; False:Failed
  1010. */
  1011. bool hom_create_request(struct map_session_data *sd, int class_)
  1012. {
  1013. struct s_homunculus homun;
  1014. struct h_stats *base;
  1015. int i;
  1016. nullpo_ret(sd);
  1017. i = hom_search(class_,HOMUNCULUS_CLASS);
  1018. if(i < 0)
  1019. return false;
  1020. memset(&homun, 0, sizeof(struct s_homunculus));
  1021. //Initial data
  1022. safestrncpy(homun.name, homunculus_db[i].name, NAME_LENGTH-1);
  1023. homun.class_ = class_;
  1024. homun.level = 1;
  1025. homun.hunger = 32; //32%
  1026. homun.intimacy = 2100; //21/1000
  1027. homun.char_id = sd->status.char_id;
  1028. homun.hp = 10 ;
  1029. base = &homunculus_db[i].base;
  1030. homun.max_hp = base->HP;
  1031. homun.max_sp = base->SP;
  1032. homun.str = base->str *10;
  1033. homun.agi = base->agi *10;
  1034. homun.vit = base->vit *10;
  1035. homun.int_= base->int_*10;
  1036. homun.dex = base->dex *10;
  1037. homun.luk = base->luk *10;
  1038. // Request homunculus creation
  1039. intif_homunculus_create(sd->status.account_id, &homun);
  1040. return true;
  1041. }
  1042. /**
  1043. * Make a player resurect an homon (player must have one)
  1044. * @param sd : player pointer
  1045. * @param per : hp percentage to revive homon
  1046. * @param x : x map coordinate
  1047. * @param y : Y map coordinate
  1048. * @return 0:failure, 1:success
  1049. */
  1050. int hom_ressurect(struct map_session_data* sd, unsigned char per, short x, short y)
  1051. {
  1052. struct homun_data* hd;
  1053. nullpo_ret(sd);
  1054. if (!sd->status.hom_id)
  1055. return 0; // no homunculus
  1056. if (!sd->hd) //Load homun data;
  1057. return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
  1058. hd = sd->hd;
  1059. if (hd->homunculus.vaporize == HOM_ST_REST)
  1060. return 0; // vaporized homunculi need to be 'called'
  1061. if (!status_isdead(&hd->bl))
  1062. return 0; // already alive
  1063. hom_init_timers(hd);
  1064. if (!hd->bl.prev)
  1065. { //Add it back to the map.
  1066. hd->bl.m = sd->bl.m;
  1067. hd->bl.x = x;
  1068. hd->bl.y = y;
  1069. if(map_addblock(&hd->bl))
  1070. return 0;
  1071. clif_spawn(&hd->bl);
  1072. }
  1073. return status_revive(&hd->bl, per, 0);
  1074. }
  1075. /**
  1076. * Revive homunculus
  1077. * @param hd
  1078. * @param hp
  1079. * @param sp
  1080. */
  1081. void hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp)
  1082. {
  1083. struct map_session_data *sd = hd->master;
  1084. hd->homunculus.hp = hd->battle_status.hp;
  1085. if (!sd)
  1086. return;
  1087. clif_send_homdata(sd,SP_ACK,0);
  1088. clif_hominfo(sd,hd,1);
  1089. clif_hominfo(sd,hd,0);
  1090. clif_homskillinfoblock(sd);
  1091. if (hd->homunculus.class_ == 6052) //eleanor
  1092. sc_start(&hd->bl,&hd->bl, SC_STYLE_CHANGE, 100, MH_MD_FIGHTING, INFINITE_TICK);
  1093. }
  1094. /**
  1095. * Reset homunculus status
  1096. * @param hd
  1097. */
  1098. void hom_reset_stats(struct homun_data *hd)
  1099. { //Resets a homunc stats back to zero (but doesn't touches hunger or intimacy)
  1100. struct s_homunculus_db *db;
  1101. struct s_homunculus *hom;
  1102. struct h_stats *base;
  1103. hom = &hd->homunculus;
  1104. db = hd->homunculusDB;
  1105. base = &db->base;
  1106. hom->level = 1;
  1107. hom->hp = 10;
  1108. hom->max_hp = base->HP;
  1109. hom->max_sp = base->SP;
  1110. hom->str = base->str *10;
  1111. hom->agi = base->agi *10;
  1112. hom->vit = base->vit *10;
  1113. hom->int_= base->int_*10;
  1114. hom->dex = base->dex *10;
  1115. hom->luk = base->luk *10;
  1116. hom->exp = 0;
  1117. hd->exp_next = hexptbl[0];
  1118. memset(&hd->homunculus.hskill, 0, sizeof hd->homunculus.hskill);
  1119. hd->homunculus.skillpts = 0;
  1120. }
  1121. /**
  1122. * Shuffle homunculus status
  1123. * @param hd
  1124. */
  1125. int hom_shuffle(struct homun_data *hd)
  1126. {
  1127. struct map_session_data *sd;
  1128. int lv, i, skillpts;
  1129. unsigned int exp;
  1130. struct s_skill b_skill[MAX_HOMUNSKILL];
  1131. if (!hom_is_active(hd))
  1132. return 0;
  1133. sd = hd->master;
  1134. lv = hd->homunculus.level;
  1135. exp = hd->homunculus.exp;
  1136. memcpy(&b_skill, &hd->homunculus.hskill, sizeof(b_skill));
  1137. skillpts = hd->homunculus.skillpts;
  1138. //Reset values to level 1.
  1139. hom_reset_stats(hd);
  1140. //Level it back up
  1141. for (i = 1; i < lv && hd->exp_next; i++){
  1142. hd->homunculus.exp += hd->exp_next;
  1143. // Should never happen, but who knows
  1144. if( !hom_levelup(hd) ) {
  1145. break;
  1146. }
  1147. }
  1148. if(hd->homunculus.class_ == hd->homunculusDB->evo_class) {
  1149. //Evolved bonuses
  1150. struct s_homunculus *hom = &hd->homunculus;
  1151. struct h_stats *max = &hd->homunculusDB->emax, *min = &hd->homunculusDB->emin;
  1152. hom->max_hp += rnd_value(min->HP, max->HP);
  1153. hom->max_sp += rnd_value(min->SP, max->SP);
  1154. hom->str += 10*rnd_value(min->str, max->str);
  1155. hom->agi += 10*rnd_value(min->agi, max->agi);
  1156. hom->vit += 10*rnd_value(min->vit, max->vit);
  1157. hom->int_+= 10*rnd_value(min->int_,max->int_);
  1158. hom->dex += 10*rnd_value(min->dex, max->dex);
  1159. hom->luk += 10*rnd_value(min->luk, max->luk);
  1160. }
  1161. hd->homunculus.exp = exp;
  1162. memcpy(&hd->homunculus.hskill, &b_skill, sizeof(b_skill));
  1163. hd->homunculus.skillpts = skillpts;
  1164. clif_homskillinfoblock(sd);
  1165. status_calc_homunculus(hd, SCO_NONE);
  1166. status_percent_heal(&hd->bl, 100, 100);
  1167. clif_specialeffect(&hd->bl,EF_HO_UP,AREA);
  1168. return 1;
  1169. }
  1170. /**
  1171. * Get minimum intimacy value of specified grade
  1172. * @param grade see enum e_homun_grade
  1173. * @return Intimacy value
  1174. **/
  1175. uint32 hom_intimacy_grade2intimacy(enum e_homun_grade grade) {
  1176. if (grade < HOMGRADE_HATE_WITH_PASSION || grade > HOMGRADE_LOYAL)
  1177. return 0;
  1178. return intimacy_grades[grade].min_value;
  1179. }
  1180. /**
  1181. * Get grade of given intimacy value
  1182. * @param intimacy
  1183. * @return Grade, see enum e_homun_grade
  1184. **/
  1185. enum e_homun_grade hom_intimacy_intimacy2grade(uint32 intimacy) {
  1186. #define CHK_HOMINTIMACY(grade) { if (intimacy >= intimacy_grades[(grade)].min_value) return (grade); }
  1187. CHK_HOMINTIMACY(HOMGRADE_LOYAL)
  1188. CHK_HOMINTIMACY(HOMGRADE_CORDIAL)
  1189. CHK_HOMINTIMACY(HOMGRADE_NEUTRAL)
  1190. CHK_HOMINTIMACY(HOMGRADE_SHY)
  1191. CHK_HOMINTIMACY(HOMGRADE_AWKWARD)
  1192. CHK_HOMINTIMACY(HOMGRADE_HATE)
  1193. #undef CHK_HOMINTIMACY
  1194. return HOMGRADE_HATE_WITH_PASSION;
  1195. }
  1196. /**
  1197. * Get initmacy grade
  1198. * @param hd
  1199. */
  1200. uint8 hom_get_intimacy_grade(struct homun_data *hd) {
  1201. return hom_intimacy_intimacy2grade(hd->homunculus.intimacy);
  1202. }
  1203. /**
  1204. * Read homunculus db
  1205. */
  1206. static bool read_homunculusdb_sub(char* str[], int columns, int current)
  1207. {
  1208. int classid;
  1209. uint16 i;
  1210. struct s_homunculus_db *db;
  1211. //Base Class,Evo Class
  1212. classid = atoi(str[0]);
  1213. if (classid < HM_CLASS_BASE || classid > HM_CLASS_MAX)
  1214. {
  1215. ShowError("read_homunculusdb : Invalid class %d\n", classid);
  1216. return false;
  1217. }
  1218. //Find the ClassID, already exist or not in homunculus_db
  1219. ARR_FIND(0,homunculus_count,i,homunculus_db[i].base_class == classid);
  1220. if (i >= homunculus_count)
  1221. db = &homunculus_db[homunculus_count];
  1222. else
  1223. db = &homunculus_db[i];
  1224. db->base_class = classid;
  1225. classid = atoi(str[1]);
  1226. if (classid < HM_CLASS_BASE || classid > HM_CLASS_MAX)
  1227. {
  1228. db->base_class = 0;
  1229. ShowError("read_homunculusdb : Invalid class %d\n", classid);
  1230. return false;
  1231. }
  1232. db->evo_class = classid;
  1233. //Name, Food, Hungry Delay, Base Size, Evo Size, Race, Element, ASPD
  1234. safestrncpy(db->name,str[2],NAME_LENGTH-1);
  1235. db->foodID = atoi(str[3]);
  1236. db->hungryDelay = atoi(str[4]);
  1237. db->base_size = atoi(str[5]);
  1238. db->evo_size = atoi(str[6]);
  1239. db->race = atoi(str[7]);
  1240. db->element = atoi(str[8]);
  1241. db->baseASPD = atoi(str[9]);
  1242. //base HP, SP, str, agi, vit, int, dex, luk
  1243. db->base.HP = atoi(str[10]);
  1244. db->base.SP = atoi(str[11]);
  1245. db->base.str = atoi(str[12]);
  1246. db->base.agi = atoi(str[13]);
  1247. db->base.vit = atoi(str[14]);
  1248. db->base.int_= atoi(str[15]);
  1249. db->base.dex = atoi(str[16]);
  1250. db->base.luk = atoi(str[17]);
  1251. //Growth Min/Max HP, SP, str, agi, vit, int, dex, luk
  1252. db->gmin.HP = atoi(str[18]);
  1253. db->gmax.HP = atoi(str[19]);
  1254. db->gmin.SP = atoi(str[20]);
  1255. db->gmax.SP = atoi(str[21]);
  1256. db->gmin.str = atoi(str[22]);
  1257. db->gmax.str = atoi(str[23]);
  1258. db->gmin.agi = atoi(str[24]);
  1259. db->gmax.agi = atoi(str[25]);
  1260. db->gmin.vit = atoi(str[26]);
  1261. db->gmax.vit = atoi(str[27]);
  1262. db->gmin.int_= atoi(str[28]);
  1263. db->gmax.int_= atoi(str[29]);
  1264. db->gmin.dex = atoi(str[30]);
  1265. db->gmax.dex = atoi(str[31]);
  1266. db->gmin.luk = atoi(str[32]);
  1267. db->gmax.luk = atoi(str[33]);
  1268. //Evolution Min/Max HP, SP, str, agi, vit, int, dex, luk
  1269. db->emin.HP = atoi(str[34]);
  1270. db->emax.HP = atoi(str[35]);
  1271. db->emin.SP = atoi(str[36]);
  1272. db->emax.SP = atoi(str[37]);
  1273. db->emin.str = atoi(str[38]);
  1274. db->emax.str = atoi(str[39]);
  1275. db->emin.agi = atoi(str[40]);
  1276. db->emax.agi = atoi(str[41]);
  1277. db->emin.vit = atoi(str[42]);
  1278. db->emax.vit = atoi(str[43]);
  1279. db->emin.int_= atoi(str[44]);
  1280. db->emax.int_= atoi(str[45]);
  1281. db->emin.dex = atoi(str[46]);
  1282. db->emax.dex = atoi(str[47]);
  1283. db->emin.luk = atoi(str[48]);
  1284. db->emax.luk = atoi(str[49]);
  1285. //Check that the min/max values really are below the other one.
  1286. if(db->gmin.HP > db->gmax.HP)
  1287. db->gmin.HP = db->gmax.HP;
  1288. if(db->gmin.SP > db->gmax.SP)
  1289. db->gmin.SP = db->gmax.SP;
  1290. if(db->gmin.str > db->gmax.str)
  1291. db->gmin.str = db->gmax.str;
  1292. if(db->gmin.agi > db->gmax.agi)
  1293. db->gmin.agi = db->gmax.agi;
  1294. if(db->gmin.vit > db->gmax.vit)
  1295. db->gmin.vit = db->gmax.vit;
  1296. if(db->gmin.int_> db->gmax.int_)
  1297. db->gmin.int_= db->gmax.int_;
  1298. if(db->gmin.dex > db->gmax.dex)
  1299. db->gmin.dex = db->gmax.dex;
  1300. if(db->gmin.luk > db->gmax.luk)
  1301. db->gmin.luk = db->gmax.luk;
  1302. if(db->emin.HP > db->emax.HP)
  1303. db->emin.HP = db->emax.HP;
  1304. if(db->emin.SP > db->emax.SP)
  1305. db->emin.SP = db->emax.SP;
  1306. if(db->emin.str > db->emax.str)
  1307. db->emin.str = db->emax.str;
  1308. if(db->emin.agi > db->emax.agi)
  1309. db->emin.agi = db->emax.agi;
  1310. if(db->emin.vit > db->emax.vit)
  1311. db->emin.vit = db->emax.vit;
  1312. if(db->emin.int_> db->emax.int_)
  1313. db->emin.int_= db->emax.int_;
  1314. if(db->emin.dex > db->emax.dex)
  1315. db->emin.dex = db->emax.dex;
  1316. if(db->emin.luk > db->emax.luk)
  1317. db->emin.luk = db->emax.luk;
  1318. if (i >= homunculus_count)
  1319. homunculus_count++;
  1320. return true;
  1321. }
  1322. /**
  1323. * Read homunculus db (check the files)
  1324. */
  1325. void read_homunculusdb(void) {
  1326. uint8 i;
  1327. const char *filename[] = {
  1328. DBPATH"homunculus_db.txt",
  1329. DBIMPORT"/homunculus_db.txt",
  1330. };
  1331. homunculus_count = 0;
  1332. memset(homunculus_db,0,sizeof(homunculus_db));
  1333. for(i = 0; i<ARRAYLENGTH(filename); i++){
  1334. sv_readdb(db_path, filename[i], ',', 50, 50, MAX_HOMUNCULUS_CLASS, &read_homunculusdb_sub, i > 0);
  1335. }
  1336. }
  1337. /**
  1338. * Read homunculus skill db
  1339. * <hom class>,<skill id>,<max level>,<need level>,<req id1>,<req lv1>,<req id2>,<req lv2>,<req id3>,<req lv3>,<req id4>,<req lv4>,<req id5>,<req lv5>,<intimacy lv req>
  1340. */
  1341. static bool read_homunculus_skilldb_sub(char* split[], int columns, int current) {
  1342. uint16 skill_id;
  1343. int8 i;
  1344. short class_idx, idx = -1;
  1345. // check for bounds [celest]
  1346. if ((class_idx = hom_class2index(atoi(split[0]))) == -1) {
  1347. ShowWarning("read_homunculus_skilldb: Invalid homunculus class %d.\n", atoi(split[0]));
  1348. return false;
  1349. }
  1350. skill_id = atoi(split[1]);
  1351. if (hom_skill_get_index(skill_id) == -1) {
  1352. ShowError("read_homunculus_skilldb: Invalid Homunculus skill '%s'.\n", split[1]);
  1353. return false;
  1354. }
  1355. // Search an empty line or a line with the same skill_id (stored in idx)
  1356. ARR_FIND(0, MAX_HOM_SKILL_TREE, idx, !hskill_tree[class_idx][idx].id || hskill_tree[class_idx][idx].id == skill_id);
  1357. if (idx == MAX_HOM_SKILL_TREE) {
  1358. ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", skill_id, atoi(split[0]));
  1359. return false;
  1360. }
  1361. hskill_tree[class_idx][idx].id = skill_id;
  1362. hskill_tree[class_idx][idx].max = atoi(split[2]);
  1363. hskill_tree[class_idx][idx].need_level = atoi(split[3]);
  1364. for (i = 0; i < MAX_HOM_SKILL_REQUIRE; i++) {
  1365. hskill_tree[class_idx][idx].need[i].id = atoi(split[4+i*2]);
  1366. hskill_tree[class_idx][idx].need[i].lv = atoi(split[4+i*2+1]);
  1367. }
  1368. hskill_tree[class_idx][idx].intimacy = atoi(split[14]);
  1369. return true;
  1370. }
  1371. /**
  1372. * Read homunculus skill db (check the files)
  1373. */
  1374. static void read_homunculus_skilldb(void) {
  1375. const char *filename[] = { "homun_skill_tree.txt", DBIMPORT"/homun_skill_tree.txt"};
  1376. int i;
  1377. memset(hskill_tree,0,sizeof(hskill_tree));
  1378. for (i = 0; i<ARRAYLENGTH(filename); i++) {
  1379. sv_readdb(db_path, filename[i], ',', 15, 15, -1, &read_homunculus_skilldb_sub, i > 0);
  1380. }
  1381. }
  1382. /**
  1383. * Read homunculus exp db
  1384. */
  1385. void read_homunculus_expdb(void)
  1386. {
  1387. int i;
  1388. const char *filename[]={
  1389. DBPATH"exp_homun.txt",
  1390. DBIMPORT"/exp_homun.txt"
  1391. };
  1392. memset(hexptbl,0,sizeof(hexptbl));
  1393. for (i = 0; i < ARRAYLENGTH(filename); i++) {
  1394. FILE *fp;
  1395. char path[1024];
  1396. char line[1024];
  1397. int j=0;
  1398. sprintf(path, "%s/%s", db_path, filename[i]);
  1399. fp = fopen(path,"r");
  1400. if (fp == NULL) {
  1401. if (i != 0)
  1402. continue;
  1403. ShowError("read_homunculus_expdb: Can't read %s\n",line);
  1404. return;
  1405. }
  1406. while (fgets(line, sizeof(line), fp) && j < MAX_LEVEL) {
  1407. if (line[0] == '/' && line[1] == '/')
  1408. continue;
  1409. hexptbl[j] = strtoul(line, NULL, 10);
  1410. if (!hexptbl[j++])
  1411. break;
  1412. }
  1413. if (hexptbl[MAX_LEVEL - 1]) { // Last permitted level have to be 0!
  1414. ShowWarning("read_homunculus_expdb: Reached max level in %s [%d]. Remaining lines were not read.\n ",path,MAX_LEVEL);
  1415. hexptbl[MAX_LEVEL - 1] = 0;
  1416. }
  1417. fclose(fp);
  1418. ShowStatus("Done reading '" CL_WHITE "%d" CL_RESET "' levels in '" CL_WHITE "%s" CL_RESET "'.\n", j, path);
  1419. }
  1420. }
  1421. void hom_reload(void){
  1422. read_homunculusdb();
  1423. read_homunculus_expdb();
  1424. }
  1425. void hom_reload_skill(void){
  1426. read_homunculus_skilldb();
  1427. }
  1428. void do_init_homunculus(void){
  1429. int class_;
  1430. read_homunculusdb();
  1431. read_homunculus_expdb();
  1432. read_homunculus_skilldb();
  1433. // Add homunc timer function to timer func list [Toms]
  1434. add_timer_func_list(hom_hungry, "hom_hungry");
  1435. //Stock view data for homuncs
  1436. memset(&hom_viewdb, 0, sizeof(hom_viewdb));
  1437. for (class_ = 0; class_ < ARRAYLENGTH(hom_viewdb); class_++)
  1438. hom_viewdb[class_].class_ = HM_CLASS_BASE+class_;
  1439. }
  1440. void do_final_homunculus(void) {
  1441. //Nothing todo yet
  1442. }