homunculus.cpp 52 KB

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