homunculus.cpp 53 KB

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