homunculus.cpp 43 KB

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