achievement.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "achievement.hpp"
  4. #include <array>
  5. #include <setjmp.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include "../common/cbasetypes.hpp"
  10. #include "../common/database.hpp"
  11. #include "../common/malloc.hpp"
  12. #include "../common/nullpo.hpp"
  13. #include "../common/showmsg.hpp"
  14. #include "../common/strlib.hpp"
  15. #include "../common/utilities.hpp"
  16. #include "../common/utils.hpp"
  17. #include "battle.hpp"
  18. #include "chrif.hpp"
  19. #include "clif.hpp"
  20. #include "intif.hpp"
  21. #include "itemdb.hpp"
  22. #include "map.hpp"
  23. #include "mob.hpp"
  24. #include "npc.hpp"
  25. #include "pc.hpp"
  26. #include "script.hpp"
  27. #include "status.hpp"
  28. using namespace rathena;
  29. void AchievementDatabase::clear(){
  30. TypesafeYamlDatabase::clear();
  31. this->achievement_mobs.clear();
  32. }
  33. const std::string AchievementDatabase::getDefaultLocation(){
  34. return std::string(db_path) + "/achievement_db.yml";
  35. }
  36. /**
  37. * Reads and parses an entry from the achievement_db.
  38. * @param node: YAML node containing the entry.
  39. * @return count of successfully parsed rows
  40. */
  41. uint64 AchievementDatabase::parseBodyNode(const ryml::NodeRef& node){
  42. uint32 achievement_id;
  43. if( !this->asUInt32( node, "Id", achievement_id ) ){
  44. return 0;
  45. }
  46. std::shared_ptr<s_achievement_db> achievement = this->find( achievement_id );
  47. bool exists = achievement != nullptr;
  48. if( !exists ){
  49. if( !this->nodesExist( node, { "Name" } ) ){
  50. return 0;
  51. }
  52. achievement = std::make_shared<s_achievement_db>();
  53. achievement->achievement_id = achievement_id;
  54. }
  55. if( this->nodeExists( node, "Group" ) ){
  56. std::string group_name;
  57. if( !this->asString( node, "Group", group_name ) ){
  58. return 0;
  59. }
  60. std::string group_name_constant = "AG_" + group_name;
  61. int64 constant;
  62. if( !script_get_constant( group_name_constant.c_str(), &constant ) ){
  63. this->invalidWarning( node, "Invalid Group %s for achievement %d, skipping.\n", group_name.c_str(), achievement_id );
  64. return 0;
  65. }
  66. achievement->group = (e_achievement_group)constant;
  67. } else {
  68. if (!exists)
  69. achievement->group = AG_NONE;
  70. }
  71. if( this->nodeExists( node, "Name" ) ){
  72. std::string name;
  73. if( !this->asString( node, "Name", name ) ){
  74. return 0;
  75. }
  76. achievement->name = name;
  77. }
  78. if( this->nodeExists( node, "Targets" ) ){
  79. const auto& targets = node["Targets"];
  80. for( const auto& targetNode : targets ){
  81. uint16 targetId;
  82. if( !this->asUInt16( targetNode, "Id", targetId ) ){
  83. continue;
  84. }
  85. if( targetId >= MAX_ACHIEVEMENT_OBJECTIVES ){
  86. this->invalidWarning( targetNode["Id"], "Target Id is out of valid range [0,%d], skipping.\n", MAX_ACHIEVEMENT_OBJECTIVES );
  87. continue;
  88. }
  89. std::shared_ptr<achievement_target> target = rathena::util::map_find( achievement->targets, targetId );
  90. bool targetExists = target != nullptr;
  91. if( !targetExists ){
  92. if( !this->nodeExists( targetNode, "Count" ) && !this->nodeExists( targetNode, "Mob" ) ){
  93. this->invalidWarning( targetNode, "Target has no data specified, skipping.\n" );
  94. return 0;
  95. }
  96. target = std::make_shared<achievement_target>();
  97. }
  98. if( this->nodeExists( targetNode, "Count" ) ){
  99. uint32 count;
  100. if( !this->asUInt32( targetNode, "Count", count ) ){
  101. return 0;
  102. }
  103. if( count == 0 ){
  104. if( targetExists ){
  105. achievement->targets.erase( targetId );
  106. continue;
  107. }else{
  108. this->invalidWarning( targetNode["Count"], "Target count has to be > 0, skipping.\n" );
  109. return 0;
  110. }
  111. }
  112. target->count = count;
  113. }else{
  114. if( !targetExists ){
  115. target->count = 1;
  116. }
  117. }
  118. if( this->nodeExists( targetNode, "Mob" ) ){
  119. if( achievement->group != AG_BATTLE && achievement->group != AG_TAMING ){
  120. this->invalidWarning( targets, "Target Mob is only supported for targets in group AG_BATTLE or AG_TAMING, skipping.\n" );
  121. continue;
  122. }
  123. std::string mob_name;
  124. if( !this->asString( targetNode, "Mob", mob_name ) ){
  125. return 0;
  126. }
  127. std::shared_ptr<s_mob_db> mob = mobdb_search_aegisname( mob_name.c_str() );
  128. if (mob == nullptr) {
  129. this->invalidWarning(targetNode["Mob"], "Target Mob %s does not exist, skipping.\n", mob_name.c_str());
  130. return 0;
  131. }
  132. uint32 mob_id = mob->id;
  133. if( !this->mobexists( mob_id ) ){
  134. this->achievement_mobs.push_back( mob_id );
  135. }
  136. target->mob = mob_id;
  137. }else{
  138. if( !targetExists ){
  139. target->mob = 0;
  140. }
  141. }
  142. achievement->targets[targetId] = target;
  143. }
  144. }
  145. if( this->nodeExists( node, "Condition" ) ){
  146. std::string condition;
  147. if( !this->asString( node, "Condition", condition ) ){
  148. return 0;
  149. }
  150. if( condition.find( "achievement_condition" ) == std::string::npos ){
  151. condition = "achievement_condition( " + condition + " );";
  152. }
  153. if( achievement->condition ){
  154. script_free_code( achievement->condition );
  155. achievement->condition = nullptr;
  156. }
  157. achievement->condition = parse_script( condition.c_str(), this->getCurrentFile().c_str(), this->getLineNumber(node["Condition"]), SCRIPT_IGNORE_EXTERNAL_BRACKETS );
  158. }else{
  159. if (!exists)
  160. achievement->condition = nullptr;
  161. }
  162. if( this->nodeExists( node, "Map" ) ){
  163. if( achievement->group != AG_CHATTING ){
  164. this->invalidWarning( node, "Map can only be used with the group AG_CHATTING, skipping.\n" );
  165. return 0;
  166. }
  167. std::string mapname;
  168. if( !this->asString( node, "Map", mapname ) ){
  169. return 0;
  170. }
  171. achievement->mapindex = map_mapname2mapid( mapname.c_str() );
  172. if( achievement->mapindex == -1 ){
  173. this->invalidWarning( node["Map"], "Map %s does not exist, skipping.\n", mapname.c_str() );
  174. return 0;
  175. }
  176. }else{
  177. if( !exists ){
  178. achievement->mapindex = -1;
  179. }
  180. }
  181. if( this->nodeExists( node, "Dependents" ) ){
  182. const auto& dependentNode = node["Dependents"];
  183. for( const auto& it : dependentNode ){
  184. auto id_str = it.key();
  185. uint32 dependent_achievement_id;
  186. c4::atou<uint32>(id_str, &dependent_achievement_id);
  187. bool active;
  188. if (!this->asBool(dependentNode, std::to_string(dependent_achievement_id), active))
  189. return 0;
  190. if (active) {
  191. if (std::find(achievement->dependent_ids.begin(), achievement->dependent_ids.end(), dependent_achievement_id) != achievement->dependent_ids.end()) {
  192. this->invalidWarning(dependentNode, "Dependent achievement %d is already part of the list, skipping.\n", dependent_achievement_id);
  193. continue;
  194. }
  195. if (achievement->dependent_ids.size() >= MAX_ACHIEVEMENT_DEPENDENTS) {
  196. this->invalidWarning(dependentNode, "Maximum amount (%d) of dependent achievements reached, skipping.\n", MAX_ACHIEVEMENT_DEPENDENTS);
  197. break;
  198. }
  199. achievement->dependent_ids.push_back(dependent_achievement_id);
  200. } else
  201. util::vector_erase_if_exists(achievement->dependent_ids, dependent_achievement_id);
  202. }
  203. }
  204. if( this->nodeExists( node, "Rewards" ) ){
  205. const auto& rewardNode = node["Rewards"];
  206. if( this->nodeExists( rewardNode, "Item" ) ){
  207. std::string item_name;
  208. if( !this->asString( rewardNode, "Item", item_name ) ){
  209. return 0;
  210. }
  211. std::shared_ptr<item_data> item = item_db.search_aegisname(item_name.c_str());
  212. if (item == nullptr) {
  213. this->invalidWarning(rewardNode["Item"], "Reward Item %s does not exist, skipping.\n", item_name.c_str());
  214. return 0;
  215. }
  216. achievement->rewards.nameid = item->nameid;
  217. }
  218. if( this->nodeExists( rewardNode, "Amount" ) ){
  219. uint16 amount;
  220. if( !this->asUInt16( rewardNode, "Amount", amount ) ){
  221. return 0;
  222. }
  223. achievement->rewards.amount = amount;
  224. } else {
  225. if (!exists)
  226. achievement->rewards.amount = 1;
  227. }
  228. if( this->nodeExists( rewardNode, "Script" ) ){
  229. std::string script;
  230. if( !this->asString( rewardNode, "Script", script ) ){
  231. return 0;
  232. }
  233. if( achievement->rewards.script ){
  234. script_free_code( achievement->rewards.script );
  235. achievement->rewards.script = nullptr;
  236. }
  237. achievement->rewards.script = parse_script( script.c_str(), this->getCurrentFile().c_str(), this->getLineNumber(rewardNode["Script"]), SCRIPT_IGNORE_EXTERNAL_BRACKETS );
  238. }else{
  239. if (!exists)
  240. achievement->rewards.script = nullptr;
  241. }
  242. if( this->nodeExists( rewardNode, "TitleId" ) ){
  243. uint32 title;
  244. if( !this->asUInt32( rewardNode, "TitleId", title ) ){
  245. return 0;
  246. }
  247. if (title < TITLE_BASE || title > TITLE_MAX) {
  248. this->invalidWarning(rewardNode["TitleId"], "Reward Title ID %u does not exist (%hu~%hu), skipping.\n", title, TITLE_BASE, TITLE_MAX);
  249. return 0;
  250. }
  251. achievement->rewards.title_id = title;
  252. } else {
  253. if (!exists)
  254. achievement->rewards.title_id = 0;
  255. }
  256. }
  257. if( this->nodeExists( node, "Score" ) ){
  258. uint32 score;
  259. if( !this->asUInt32( node, "Score", score ) ){
  260. return 0;
  261. }
  262. achievement->score = score;
  263. } else {
  264. if (!exists)
  265. achievement->score = 0;
  266. }
  267. if( !exists ){
  268. this->put( achievement_id, achievement );
  269. }
  270. return 1;
  271. }
  272. void AchievementDatabase::loadingFinished(){
  273. for (const auto &achit : *this) {
  274. const std::shared_ptr<s_achievement_db> ach = achit.second;
  275. for (auto dep = ach->dependent_ids.begin(); dep != ach->dependent_ids.end(); dep++) {
  276. if (!this->exists(*dep)) {
  277. ShowWarning("achievement_read_db: An invalid Dependent ID %d was given for Achievement %d. Removing from list.\n", *dep, ach->achievement_id);
  278. dep = ach->dependent_ids.erase(dep);
  279. if (dep == ach->dependent_ids.end()) {
  280. break;
  281. }
  282. }
  283. }
  284. ach->dependent_ids.shrink_to_fit();
  285. }
  286. TypesafeYamlDatabase::loadingFinished();
  287. }
  288. AchievementDatabase achievement_db;
  289. /**
  290. * Searches for an achievement by monster ID
  291. * @param mob_id: Monster ID to lookup
  292. * @return True on success, false on failure
  293. */
  294. bool AchievementDatabase::mobexists( uint32 mob_id ){
  295. if (!battle_config.feature_achievement)
  296. return false;
  297. auto it = std::find(this->achievement_mobs.begin(), this->achievement_mobs.end(), mob_id);
  298. return (it != this->achievement_mobs.end()) ? true : false;
  299. }
  300. const std::string AchievementLevelDatabase::getDefaultLocation(){
  301. return std::string(db_path) + "/achievement_level_db.yml";
  302. }
  303. uint64 AchievementLevelDatabase::parseBodyNode( const ryml::NodeRef& node ){
  304. if( !this->nodesExist( node, { "Level", "Points" } ) ){
  305. return 0;
  306. }
  307. uint16 level;
  308. if( !this->asUInt16( node, "Level", level ) ){
  309. return 0;
  310. }
  311. if( level == 0 ){
  312. this->invalidWarning( node, "Invalid achievement level %hu (minimum value: 1), skipping.\n", level );
  313. return 0;
  314. }
  315. // Make it zero based
  316. level -= 1;
  317. std::shared_ptr<s_achievement_level> ptr = this->find( level );
  318. bool exists = ptr != nullptr;
  319. if( !exists ){
  320. ptr = std::make_shared<s_achievement_level>();
  321. ptr->level = level;
  322. }
  323. uint16 points;
  324. if (!this->asUInt16(node, "Points", points)) {
  325. return 0;
  326. }
  327. ptr->points = points;
  328. if( !exists ){
  329. this->put( level, ptr );
  330. }
  331. return 1;
  332. }
  333. AchievementLevelDatabase achievement_level_db;
  334. /**
  335. * Add an achievement to the player's log
  336. * @param sd: Player data
  337. * @param achievement_id: Achievement to add
  338. * @return NULL on failure, achievement data on success
  339. */
  340. struct achievement *achievement_add(map_session_data *sd, int achievement_id)
  341. {
  342. int i, index;
  343. nullpo_retr(NULL, sd);
  344. std::shared_ptr<s_achievement_db> adb = achievement_db.find( achievement_id );
  345. if( adb == nullptr ){
  346. ShowError( "achievement_add: Achievement %d not found in DB.\n", achievement_id );
  347. return nullptr;
  348. }
  349. ARR_FIND(0, sd->achievement_data.count, i, sd->achievement_data.achievements[i].achievement_id == achievement_id);
  350. if (i < sd->achievement_data.count) {
  351. ShowError("achievement_add: Character %d already has achievement %d.\n", sd->status.char_id, achievement_id);
  352. return NULL;
  353. }
  354. index = sd->achievement_data.incompleteCount;
  355. sd->achievement_data.count++;
  356. sd->achievement_data.incompleteCount++;
  357. RECREATE(sd->achievement_data.achievements, struct achievement, sd->achievement_data.count);
  358. // The character has some completed achievements, make room before them so that they will stay at the end of the array
  359. if (sd->achievement_data.incompleteCount != sd->achievement_data.count)
  360. memmove(&sd->achievement_data.achievements[index + 1], &sd->achievement_data.achievements[index], sizeof(struct achievement) * (sd->achievement_data.count - sd->achievement_data.incompleteCount));
  361. memset(&sd->achievement_data.achievements[index], 0, sizeof(struct achievement));
  362. sd->achievement_data.achievements[index].achievement_id = achievement_id;
  363. sd->achievement_data.achievements[index].score = adb->score;
  364. sd->achievement_data.save = true;
  365. clif_achievement_update(sd, &sd->achievement_data.achievements[index], sd->achievement_data.count - sd->achievement_data.incompleteCount);
  366. return &sd->achievement_data.achievements[index];
  367. }
  368. /**
  369. * Removes an achievement from a player's log
  370. * @param sd: Player's data
  371. * @param achievement_id: Achievement to remove
  372. * @return True on success, false on failure
  373. */
  374. bool achievement_remove(map_session_data *sd, int achievement_id)
  375. {
  376. struct achievement dummy;
  377. int i;
  378. nullpo_retr(false, sd);
  379. if (!achievement_db.exists(achievement_id)) {
  380. ShowError("achievement_delete: Achievement %d not found in DB.\n", achievement_id);
  381. return false;
  382. }
  383. ARR_FIND(0, sd->achievement_data.count, i, sd->achievement_data.achievements[i].achievement_id == achievement_id);
  384. if (i == sd->achievement_data.count) {
  385. ShowError("achievement_delete: Character %d doesn't have achievement %d.\n", sd->status.char_id, achievement_id);
  386. return false;
  387. }
  388. if (!sd->achievement_data.achievements[i].completed)
  389. sd->achievement_data.incompleteCount--;
  390. if (i != sd->achievement_data.count - 1)
  391. memmove(&sd->achievement_data.achievements[i], &sd->achievement_data.achievements[i + 1], sizeof(struct achievement) * (sd->achievement_data.count - 1 - i));
  392. sd->achievement_data.count--;
  393. if( sd->achievement_data.count == 0 ){
  394. aFree(sd->achievement_data.achievements);
  395. sd->achievement_data.achievements = NULL;
  396. }else{
  397. RECREATE(sd->achievement_data.achievements, struct achievement, sd->achievement_data.count);
  398. }
  399. sd->achievement_data.save = true;
  400. // Send a removed fake achievement
  401. memset(&dummy, 0, sizeof(struct achievement));
  402. dummy.achievement_id = achievement_id;
  403. clif_achievement_update(sd, &dummy, sd->achievement_data.count - sd->achievement_data.incompleteCount);
  404. return true;
  405. }
  406. /**
  407. * Lambda function that checks for completed achievements
  408. * @param sd: Player data
  409. * @param achievement_id: Achievement to check if it's complete
  410. * @return True on completed, false if not
  411. */
  412. static bool achievement_done(map_session_data *sd, int achievement_id) {
  413. for (int i = 0; i < sd->achievement_data.count; i++) {
  414. if (sd->achievement_data.achievements[i].achievement_id == achievement_id && sd->achievement_data.achievements[i].completed > 0)
  415. return true;
  416. }
  417. return false;
  418. }
  419. /**
  420. * Checks to see if an achievement has a dependent, and if so, checks if that dependent is complete
  421. * @param sd: Player data
  422. * @param achievement_id: Achievement to check if it has a dependent
  423. * @return False on failure or not complete, true on complete or no dependents
  424. */
  425. bool achievement_check_dependent(map_session_data *sd, int achievement_id)
  426. {
  427. nullpo_retr(false, sd);
  428. std::shared_ptr<s_achievement_db> adb = achievement_db.find( achievement_id );
  429. if( adb == nullptr ){
  430. return false;
  431. }
  432. // Check if the achievement has a dependent
  433. // If so, then do a check on all dependents to see if they're complete
  434. for (const auto &depit : adb->dependent_ids) {
  435. if (!achievement_done(sd, depit))
  436. return false; // One of the dependent is not complete!
  437. }
  438. return true;
  439. }
  440. /**
  441. * Check achievements that only have dependents and no other requirements
  442. * @param sd: Player to update
  443. * @param sd: Achievement to compare for completed dependents
  444. * @return True if successful, false if not
  445. */
  446. static int achievement_check_groups(map_session_data *sd, struct s_achievement_db *ad)
  447. {
  448. int i;
  449. if (ad == NULL || sd == NULL)
  450. return 0;
  451. if (ad->group != AG_BATTLE && ad->group != AG_TAMING && ad->group != AG_ADVENTURE)
  452. return 0;
  453. if (ad->dependent_ids.empty() || ad->condition)
  454. return 0;
  455. ARR_FIND(0, sd->achievement_data.count, i, sd->achievement_data.achievements[i].achievement_id == ad->achievement_id);
  456. if (i == sd->achievement_data.count) { // Achievement isn't in player's log
  457. if (achievement_check_dependent(sd, ad->achievement_id) == true) {
  458. achievement_add(sd, ad->achievement_id);
  459. achievement_update_achievement(sd, ad->achievement_id, true);
  460. }
  461. }
  462. return 1;
  463. }
  464. /**
  465. * Update an achievement
  466. * @param sd: Player to update
  467. * @param achievement_id: Achievement ID of the achievement to update
  468. * @param complete: Complete state of an achievement
  469. * @return True if successful, false if not
  470. */
  471. bool achievement_update_achievement(map_session_data *sd, int achievement_id, bool complete)
  472. {
  473. int i;
  474. nullpo_retr(false, sd);
  475. std::shared_ptr<s_achievement_db> adb = achievement_db.find( achievement_id );
  476. if( adb == nullptr ){
  477. return false;
  478. }
  479. ARR_FIND(0, sd->achievement_data.incompleteCount, i, sd->achievement_data.achievements[i].achievement_id == achievement_id);
  480. if (i == sd->achievement_data.incompleteCount)
  481. return false;
  482. if (sd->achievement_data.achievements[i].completed > 0)
  483. return false;
  484. // Finally we send the updated achievement to the client
  485. if (complete) {
  486. if (!adb->targets.empty()) { // Make sure all the objective targets are at their respective total requirement
  487. for (const auto &it : adb->targets)
  488. sd->achievement_data.achievements[i].count[it.first] = it.second->count;
  489. }
  490. sd->achievement_data.achievements[i].completed = time(NULL);
  491. if (i < (--sd->achievement_data.incompleteCount)) { // The achievement needs to be moved to the completed achievements block at the end of the array
  492. struct achievement tmp_ach;
  493. memcpy(&tmp_ach, &sd->achievement_data.achievements[i], sizeof(struct achievement));
  494. memcpy(&sd->achievement_data.achievements[i], &sd->achievement_data.achievements[sd->achievement_data.incompleteCount], sizeof(struct achievement));
  495. memcpy(&sd->achievement_data.achievements[sd->achievement_data.incompleteCount], &tmp_ach, sizeof(struct achievement));
  496. }
  497. achievement_level(sd, true); // Re-calculate achievement level
  498. // Check dependents
  499. for (auto &ach : achievement_db)
  500. achievement_check_groups(sd, ach.second.get());
  501. ARR_FIND(sd->achievement_data.incompleteCount, sd->achievement_data.count, i, sd->achievement_data.achievements[i].achievement_id == achievement_id); // Look for the index again, the position most likely changed
  502. }
  503. clif_achievement_update(sd, &sd->achievement_data.achievements[i], sd->achievement_data.count - sd->achievement_data.incompleteCount);
  504. sd->achievement_data.save = true; // Flag to save with the autosave interval
  505. return true;
  506. }
  507. /**
  508. * Get the reward of an achievement
  509. * @param sd: Player getting the reward
  510. * @param achievement_id: Achievement to get reward data
  511. */
  512. void achievement_get_reward(map_session_data *sd, int achievement_id, time_t rewarded)
  513. {
  514. int i;
  515. nullpo_retv(sd);
  516. std::shared_ptr<s_achievement_db> adb = achievement_db.find( achievement_id );
  517. if( adb == nullptr ){
  518. ShowError( "achievement_reward: Inter server sent a reward claim for achievement %d not found in DB.\n", achievement_id );
  519. return;
  520. }
  521. if (rewarded == 0) {
  522. clif_achievement_reward_ack(sd->fd, 0, achievement_id);
  523. return;
  524. }
  525. ARR_FIND(0, sd->achievement_data.count, i, sd->achievement_data.achievements[i].achievement_id == achievement_id);
  526. if (i == sd->achievement_data.count)
  527. return;
  528. // Only update in the cache, db was updated already
  529. sd->achievement_data.achievements[i].rewarded = rewarded;
  530. sd->achievement_data.save = true;
  531. run_script(adb->rewards.script, 0, sd->bl.id, fake_nd->bl.id);
  532. if (adb->rewards.title_id) {
  533. sd->titles.push_back(adb->rewards.title_id);
  534. clif_achievement_list_all(sd);
  535. }else{
  536. clif_achievement_reward_ack(sd->fd, 1, achievement_id);
  537. clif_achievement_update(sd, &sd->achievement_data.achievements[i], sd->achievement_data.count - sd->achievement_data.incompleteCount);
  538. }
  539. }
  540. /**
  541. * Check if player has recieved an achievement's reward
  542. * @param sd: Player to get reward
  543. * @param achievement_id: Achievement to get reward data
  544. */
  545. void achievement_check_reward(map_session_data *sd, int achievement_id)
  546. {
  547. int i;
  548. nullpo_retv(sd);
  549. std::shared_ptr<s_achievement_db> adb = achievement_db.find( achievement_id );
  550. if( adb == nullptr ){
  551. ShowError( "achievement_reward: Trying to reward achievement %d not found in DB.\n", achievement_id );
  552. clif_achievement_reward_ack( sd->fd, 0, achievement_id );
  553. return;
  554. }
  555. ARR_FIND(0, sd->achievement_data.count, i, sd->achievement_data.achievements[i].achievement_id == achievement_id);
  556. if (i == sd->achievement_data.count) {
  557. clif_achievement_reward_ack(sd->fd, 0, achievement_id);
  558. return;
  559. }
  560. if (sd->achievement_data.achievements[i].rewarded > 0 || sd->achievement_data.achievements[i].completed == 0) {
  561. clif_achievement_reward_ack(sd->fd, 0, achievement_id);
  562. return;
  563. }
  564. if (!intif_achievement_reward(sd, adb.get())) {
  565. clif_achievement_reward_ack(sd->fd, 0, achievement_id);
  566. }
  567. }
  568. /**
  569. * Return all titles to a player based on completed achievements
  570. * @param char_id: Character ID requesting
  571. */
  572. void achievement_get_titles(uint32 char_id)
  573. {
  574. map_session_data *sd = map_charid2sd(char_id);
  575. if (sd) {
  576. sd->titles.clear();
  577. if (sd->achievement_data.count) {
  578. for (int i = 0; i < sd->achievement_data.count; i++) {
  579. std::shared_ptr<s_achievement_db> adb = achievement_db.find( sd->achievement_data.achievements[i].achievement_id );
  580. // If the achievement has a title and is complete, give it to the player
  581. if( adb != nullptr && adb->rewards.title_id && sd->achievement_data.achievements[i].completed > 0 ){
  582. sd->titles.push_back( adb->rewards.title_id );
  583. }
  584. }
  585. }
  586. }
  587. }
  588. /**
  589. * Frees the player's data for achievements
  590. * @param sd: Player's session
  591. */
  592. void achievement_free(map_session_data *sd)
  593. {
  594. nullpo_retv(sd);
  595. if (sd->achievement_data.count) {
  596. aFree(sd->achievement_data.achievements);
  597. sd->achievement_data.achievements = NULL;
  598. sd->achievement_data.count = sd->achievement_data.incompleteCount = 0;
  599. }
  600. }
  601. /**
  602. * Get an achievement's progress information
  603. * @param sd: Player to check achievement progress
  604. * @param achievement_id: Achievement progress to check
  605. * @param type: Type to return
  606. * @return The type's data, -1 if player doesn't have achievement, -2 on failure/incorrect type
  607. */
  608. int achievement_check_progress(map_session_data *sd, int achievement_id, int type)
  609. {
  610. int i;
  611. nullpo_retr(-2, sd);
  612. // Achievement ID is not needed so skip the lookup
  613. if (type == ACHIEVEINFO_LEVEL)
  614. return sd->achievement_data.level;
  615. else if (type == ACHIEVEINFO_SCORE)
  616. return sd->achievement_data.total_score;
  617. ARR_FIND(0, sd->achievement_data.count, i, sd->achievement_data.achievements[i].achievement_id == achievement_id);
  618. if (i == sd->achievement_data.count)
  619. return -1;
  620. if (type >= ACHIEVEINFO_COUNT1 && type <= ACHIEVEINFO_COUNT10)
  621. return sd->achievement_data.achievements[i].count[type - 1];
  622. else if (type == ACHIEVEINFO_COMPLETE)
  623. return sd->achievement_data.achievements[i].completed > 0;
  624. else if (type == ACHIEVEINFO_COMPLETEDATE)
  625. return (int)sd->achievement_data.achievements[i].completed;
  626. else if (type == ACHIEVEINFO_GOTREWARD)
  627. return sd->achievement_data.achievements[i].rewarded > 0;
  628. return -2;
  629. }
  630. /**
  631. * Calculate a player's achievement level
  632. * @param sd: Player to check achievement level
  633. * @param flag: If the call should attempt to give the AG_GOAL_ACHIEVE achievement
  634. * @return Rollover and TNL EXP or 0 on failure
  635. */
  636. int *achievement_level(map_session_data *sd, bool flag)
  637. {
  638. nullpo_retr(nullptr, sd);
  639. sd->achievement_data.total_score = 0;
  640. for (int i = 0; i < sd->achievement_data.count; i++) { // Recount total score
  641. if (sd->achievement_data.achievements[i].completed > 0)
  642. sd->achievement_data.total_score += sd->achievement_data.achievements[i].score;
  643. }
  644. int left_score, right_score, old_level = sd->achievement_data.level;
  645. for( sd->achievement_data.level = 0; /* Break condition's inside the loop */; sd->achievement_data.level++ ){
  646. std::shared_ptr<s_achievement_level> level = achievement_level_db.find( sd->achievement_data.level );
  647. if( level != nullptr && sd->achievement_data.total_score > level->points ){
  648. std::shared_ptr<s_achievement_level> next_level = achievement_level_db.find( sd->achievement_data.level + 1 );
  649. // Check if there is another level
  650. if( next_level == nullptr ){
  651. std::shared_ptr<s_achievement_level> level = achievement_level_db.find( sd->achievement_data.level );
  652. left_score = sd->achievement_data.total_score - level->points;
  653. right_score = 0;
  654. // Increase the level for client side display
  655. sd->achievement_data.level++;
  656. break;
  657. }else{
  658. // Enough points for this level, check the next one
  659. continue;
  660. }
  661. }
  662. if( sd->achievement_data.level == 0 ){
  663. left_score = sd->achievement_data.total_score;
  664. if( level == nullptr ){
  665. right_score = 0;
  666. }else{
  667. right_score = level->points;
  668. }
  669. break;
  670. }else{
  671. std::shared_ptr<s_achievement_level> previous_level = achievement_level_db.find( sd->achievement_data.level - 1 );
  672. left_score = sd->achievement_data.total_score - previous_level->points;
  673. right_score = level->points - previous_level->points;
  674. break;
  675. }
  676. }
  677. static int info[2];
  678. info[0] = left_score; // Left number
  679. info[1] = right_score; // Right number
  680. if (flag && old_level != sd->achievement_data.level) { // Give AG_GOAL_ACHIEVE
  681. achievement_update_objective( sd, AG_GOAL_ACHIEVE, 0 );
  682. }
  683. return info;
  684. }
  685. bool achievement_check_condition( struct script_code* condition, map_session_data* sd ){
  686. // Save the old script the player was attached to
  687. struct script_state* previous_st = sd->st;
  688. // Only if there was an old script
  689. if( previous_st != nullptr ){
  690. // Detach the player from the current script
  691. script_detach_rid(previous_st);
  692. }
  693. run_script( condition, 0, sd->bl.id, fake_nd->bl.id );
  694. struct script_state* st = sd->st;
  695. int value = 0;
  696. if( st != nullptr ){
  697. value = script_getnum( st, 2 );
  698. script_free_state(st);
  699. }
  700. // If an old script is present
  701. if( previous_st != nullptr ){
  702. // Because of detach the RID will be removed, so we need to restore it
  703. previous_st->rid = sd->bl.id;
  704. // Reattach the player to it, so that the limitations of that script kick back in
  705. script_attach_state( previous_st );
  706. }
  707. return value != 0;
  708. }
  709. /**
  710. * Check to see if an achievement's target count is complete
  711. * @param ad: Achievement data
  712. * @param current_count: Current target data
  713. * @return True if all target values meet the requirements or false otherwise
  714. */
  715. static bool achievement_target_complete(std::shared_ptr<s_achievement_db> ad, std::array<int, MAX_ACHIEVEMENT_OBJECTIVES> current_count) {
  716. return std::find_if(ad->targets.begin(), ad->targets.end(),
  717. [current_count](const std::pair<uint16, std::shared_ptr<achievement_target>> &target) -> bool {
  718. return current_count[target.first] < target.second->count;
  719. }) == ad->targets.end();
  720. }
  721. /**
  722. * Update achievement objectives.
  723. * @param sd: Player to update
  724. * @param ad: Achievement data to compare for completion
  725. * @param group: Achievement group to update
  726. * @param update_count: Objective values from event
  727. * @return 1 on success and false on failure
  728. */
  729. static bool achievement_update_objectives(map_session_data *sd, std::shared_ptr<struct s_achievement_db> ad, enum e_achievement_group group, const std::array<int, MAX_ACHIEVEMENT_OBJECTIVES> &update_count)
  730. {
  731. if (!ad || !sd)
  732. return false;
  733. if (group <= AG_NONE || group >= AG_MAX)
  734. return false;
  735. if (group != ad->group)
  736. return false;
  737. struct achievement *entry = NULL;
  738. bool isNew = false, changed = false, complete = false;
  739. std::array<int, MAX_ACHIEVEMENT_OBJECTIVES> current_count = {}; // Player's current objective values
  740. int i;
  741. ARR_FIND(0, sd->achievement_data.count, i, sd->achievement_data.achievements[i].achievement_id == ad->achievement_id);
  742. if (i == sd->achievement_data.count) { // Achievement isn't in player's log
  743. if (!achievement_check_dependent(sd, ad->achievement_id)) // Check to see if dependents are complete before adding to player's log
  744. return false;
  745. isNew = true;
  746. } else {
  747. entry = &sd->achievement_data.achievements[i];
  748. if (entry->completed > 0) // Player has completed the achievement
  749. return false;
  750. memcpy(current_count.data(), entry->count, sizeof(current_count));
  751. }
  752. switch (group) {
  753. case AG_ADD_FRIEND:
  754. case AG_BABY:
  755. case AG_CHATTING_COUNT:
  756. case AG_CHATTING_CREATE:
  757. case AG_CHATTING_DYING:
  758. case AG_GET_ITEM:
  759. case AG_GET_ZENY:
  760. case AG_GOAL_LEVEL:
  761. case AG_GOAL_STATUS:
  762. case AG_JOB_CHANGE:
  763. case AG_MARRY:
  764. case AG_PARTY:
  765. case AG_ENCHANT_FAIL:
  766. case AG_ENCHANT_SUCCESS:
  767. if (!ad->condition)
  768. return false;
  769. if (!achievement_check_condition(ad->condition, sd)) // Parameters weren't met
  770. return false;
  771. changed = true;
  772. complete = true;
  773. break;
  774. case AG_SPEND_ZENY:
  775. if (ad->targets.empty() || !ad->condition)
  776. return false;
  777. for (const auto &it : ad->targets) {
  778. if (current_count[it.first] < it.second->count)
  779. current_count[it.first] += update_count[it.first];
  780. }
  781. if (!achievement_check_condition(ad->condition, sd)) // Parameters weren't met
  782. return false;
  783. changed = true;
  784. if (achievement_target_complete(ad, current_count))
  785. complete = true;
  786. break;
  787. case AG_BATTLE:
  788. case AG_TAMING:
  789. if (ad->targets.empty())
  790. return false;
  791. for (const auto &it : ad->targets) {
  792. if (it.second->mob == update_count[0] && current_count[it.first] < it.second->count) { // Here update_count[0] contains the killed/tamed monster ID
  793. current_count[it.first]++;
  794. changed = true;
  795. }
  796. }
  797. if (!changed)
  798. return false;
  799. if (achievement_target_complete(ad, current_count))
  800. complete = true;
  801. break;
  802. case AG_GOAL_ACHIEVE:
  803. if (!achievement_check_condition(ad->condition, sd)) // Parameters weren't met
  804. return false;
  805. changed = true;
  806. complete = true;
  807. break;
  808. /*
  809. case AG_CHATTING:
  810. if (ad->targets.empty())
  811. return false;
  812. if (ad->mapindex > -1 && sd->bl.m != ad->mapindex)
  813. return false;
  814. for (const auto &it : ad->targets) {
  815. if (current_count[it.first] < it.second->count) {
  816. current_count[it.first]++;
  817. changed = true;
  818. }
  819. }
  820. if (!changed)
  821. return false;
  822. if (achievement_target_complete(ad, current_count))
  823. complete = true;
  824. break;
  825. */
  826. }
  827. if( isNew ){
  828. // Always add the achievement if it was completed
  829. bool hasCounter = complete;
  830. // If it was not completed
  831. if( !hasCounter ){
  832. // Check if it has a counter
  833. for( int counter : current_count ){
  834. if( counter != 0 ){
  835. hasCounter = true;
  836. break;
  837. }
  838. }
  839. }
  840. if( hasCounter ){
  841. if( !( entry = achievement_add( sd, ad->achievement_id ) ) ){
  842. return false; // Failed to add achievement
  843. }
  844. }else{
  845. changed = false;
  846. }
  847. }
  848. if (changed) {
  849. memcpy(entry->count, current_count.data(), sizeof(current_count));
  850. achievement_update_achievement(sd, ad->achievement_id, complete);
  851. }
  852. return true;
  853. }
  854. /**
  855. * Update achievement objective count.
  856. * @param sd: Player data
  857. * @param group: Achievement enum type
  858. * @param sp_value: SP parameter value
  859. * @param arg_count: va_arg count
  860. */
  861. void achievement_update_objective(map_session_data *sd, enum e_achievement_group group, uint8 arg_count, ...)
  862. {
  863. if (!battle_config.feature_achievement)
  864. return;
  865. if (sd) {
  866. va_list ap;
  867. std::array<int, MAX_ACHIEVEMENT_OBJECTIVES> count = {};
  868. va_start(ap, arg_count);
  869. for (int i = 0; i < arg_count; i++){
  870. std::string name = "ARG" + std::to_string(i);
  871. count[i] = va_arg(ap, int);
  872. pc_setglobalreg( sd, add_str( name.c_str() ), (int)count[i] );
  873. }
  874. va_end(ap);
  875. for (auto &ach : achievement_db)
  876. achievement_update_objectives(sd, ach.second, group, count);
  877. // Remove variables that might have been set
  878. for (int i = 0; i < arg_count; i++){
  879. std::string name = "ARG" + std::to_string(i);
  880. pc_setglobalreg( sd, add_str( name.c_str() ), 0 );
  881. }
  882. }
  883. }
  884. /**
  885. * Map iterator subroutine to update achievement objectives for a party after killing a monster.
  886. * @see map_foreachinrange
  887. * @param ap: Argument list, expecting:
  888. * int Party ID
  889. * int Mob ID
  890. */
  891. int achievement_update_objective_sub(block_list *bl, va_list ap)
  892. {
  893. map_session_data *sd;
  894. int mob_id, party_id;
  895. nullpo_ret(bl);
  896. nullpo_ret(sd = (map_session_data *)bl);
  897. party_id = va_arg(ap, int);
  898. mob_id = va_arg(ap, int);
  899. if (sd->achievement_data.achievements == nullptr)
  900. return 0;
  901. if (sd->status.party_id != party_id)
  902. return 0;
  903. achievement_update_objective(sd, AG_BATTLE, 1, mob_id);
  904. return 1;
  905. }
  906. /**
  907. * Reloads the achievement database
  908. */
  909. void achievement_db_reload(void)
  910. {
  911. do_final_achievement();
  912. do_init_achievement();
  913. }
  914. /**
  915. * Initializes the achievement database
  916. */
  917. void do_init_achievement(void)
  918. {
  919. if (!battle_config.feature_achievement)
  920. return;
  921. achievement_db.load();
  922. achievement_level_db.load();
  923. }
  924. /**
  925. * Finalizes the achievement database
  926. */
  927. void do_final_achievement(void){
  928. achievement_db.clear();
  929. achievement_level_db.clear();
  930. }
  931. /**
  932. * Achievement constructor
  933. */
  934. s_achievement_db::s_achievement_db()
  935. : achievement_id(0)
  936. , name("")
  937. , group()
  938. , targets()
  939. , dependent_ids()
  940. , condition(nullptr)
  941. , mapindex(-1)
  942. , rewards()
  943. , score(0)
  944. , has_dependent(0)
  945. {}
  946. /**
  947. * Achievement deconstructor
  948. */
  949. s_achievement_db::~s_achievement_db()
  950. {
  951. if (condition)
  952. script_free_code(condition);
  953. }
  954. /**
  955. * Achievement reward constructor
  956. */
  957. s_achievement_db::ach_reward::ach_reward()
  958. : nameid(0)
  959. , amount(0)
  960. , script(nullptr)
  961. , title_id(0)
  962. {}
  963. /**
  964. * Achievement reward deconstructor
  965. */
  966. s_achievement_db::ach_reward::~ach_reward()
  967. {
  968. if (script)
  969. script_free_code(script);
  970. }