Refactored achievements to utilize C++ features (#2607)

* Refactored achievements to utilize C++ features
* Cleaned up the YAML parser.
* Moved achievements from DBMap to an unordered_map.
* Moved achievement targets from DBMap to a vector.
* Changed all struct arrays into vectors.
* Changed all char arrays to strings.
* Changed all int arrays to std::arrays.
* Removed achievement_dummy as it's no longer needed.
* Achievements now use smart pointer to ensure proper construction and deconstruction of objects.
Thanks to @lighta!
This commit is contained in:
Aleos 2017-11-20 21:34:16 -05:00 committed by GitHub
parent ca1b980ede
commit 1c66035761
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 442 additions and 434 deletions

View File

@ -967,63 +967,63 @@ Achievements:
Name: "Prontera Contribution"
Map: "prontera"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127002
Group: "AG_CHATTING"
Name: "Geffen Contribution"
Map: "geffen"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127003
Group: "AG_CHATTING"
Name: "Morocc Contribution"
Map: "morocc"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127004
Group: "AG_CHATTING"
Name: "Payon Contribution"
Map: "payon"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127005
Group: "AG_CHATTING"
Name: "Yuno Contribution"
Map: "yuno"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127006
Group: "AG_CHATTING"
Name: "Lighthalzen Contribution"
Map: "lighthalzen"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127007
Group: "AG_CHATTING"
Name: "Einbroch Contribution"
Map: "einbroch"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127008
Group: "AG_CHATTING"
Name: "Rachel Contribution"
Map: "rachel"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127009
Group: "AG_CHATTING"
Name: "Veins Contribution"
Map: "veins"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 128000
Group: "AG_BATTLE"
@ -2046,35 +2046,35 @@ Achievements:
Name: "Activating the market economy (1)"
Condition: " ARG0 >= 10000 "
Target:
Count: 10000
- Count: 10000
Score: 10
- ID: 220010
Group: "AG_SPEND_ZENY"
Name: "Activating the market economy (2)"
Condition: " ARG0 >= 100000 "
Target:
Count: 100000
- Count: 100000
Score: 15
- ID: 220011
Group: "AG_SPEND_ZENY"
Name: "Activating the market economy (3)"
Condition: " ARG0 >= 500000 "
Target:
Count: 500000
- Count: 500000
Score: 20
- ID: 220012
Group: "AG_SPEND_ZENY"
Name: "Activating the market economy (4)"
Condition: " ARG0 >= 1000000 "
Target:
Count: 1000000
- Count: 1000000
Score: 30
- ID: 220013
Group: "AG_SPEND_ZENY"
Name: "Activating the market economy (5)"
Condition: " ARG0 >= 5000000 "
Target:
Count: 5000000
- Count: 5000000
Score: 50
- ID: 220014
Group: "AG_ENCHANT_SUCCESS"

View File

@ -967,63 +967,63 @@ Achievements:
Name: "Prontera Contribution"
Map: "prontera"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127002
Group: "AG_CHATTING"
Name: "Geffen Contribution"
Map: "geffen"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127003
Group: "AG_CHATTING"
Name: "Morocc Contribution"
Map: "morocc"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127004
Group: "AG_CHATTING"
Name: "Payon Contribution"
Map: "payon"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127005
Group: "AG_CHATTING"
Name: "Yuno Contribution"
Map: "yuno"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127006
Group: "AG_CHATTING"
Name: "Lighthalzen Contribution"
Map: "lighthalzen"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127007
Group: "AG_CHATTING"
Name: "Einbroch Contribution"
Map: "einbroch"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127008
Group: "AG_CHATTING"
Name: "Rachel Contribution"
Map: "rachel"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 127009
Group: "AG_CHATTING"
Name: "Veins Contribution"
Map: "veins"
Target:
Count: 100000
- Count: 100000
Score: 10
- ID: 128000
Group: "AG_BATTLE"
@ -2046,35 +2046,35 @@ Achievements:
Name: "Activating the market economy (1)"
Condition: " ARG0 >= 10000 "
Target:
Count: 10000
- Count: 10000
Score: 10
- ID: 220010
Group: "AG_SPEND_ZENY"
Name: "Activating the market economy (2)"
Condition: " ARG0 >= 100000 "
Target:
Count: 100000
- Count: 100000
Score: 15
- ID: 220011
Group: "AG_SPEND_ZENY"
Name: "Activating the market economy (3)"
Condition: " ARG0 >= 500000 "
Target:
Count: 500000
- Count: 500000
Score: 20
- ID: 220012
Group: "AG_SPEND_ZENY"
Name: "Activating the market economy (4)"
Condition: " ARG0 >= 1000000 "
Target:
Count: 1000000
- Count: 1000000
Score: 30
- ID: 220013
Group: "AG_SPEND_ZENY"
Name: "Activating the market economy (5)"
Condition: " ARG0 >= 5000000 "
Target:
Count: 5000000
- Count: 5000000
Score: 50
- ID: 220014
Group: "AG_ENCHANT_SUCCESS"

View File

@ -64,7 +64,7 @@ Example 2:
// IE: In the achievement_list.lub file, UI_Type 0 is displayed as non-incremental while 1 shows a progress bar of completion for the achievement.
Condition: " ARG0 >= 100 "
Target:
Count: 100
- Count: 100
---------------------------------------

View File

@ -143,8 +143,8 @@
//Achievement System
#define MAX_ACHIEVEMENT_RANK 20 /// Maximum achievement level
#define MAX_ACHIEVEMENT_OBJECTIVES 10 /// Maximum different objectives in achievement_db.conf
#define MAX_ACHIEVEMENT_DEPENDENTS 20 /// Maximum different dependents in achievement_db.conf
#define MAX_ACHIEVEMENT_OBJECTIVES 10 /// Maximum different objectives in achievement_db.yml
#define MAX_ACHIEVEMENT_DEPENDENTS 20 /// Maximum different dependents in achievement_db.yml
#define ACHIEVEMENT_NAME_LENGTH 50 /// Max Achievement Name length
enum item_types {

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,18 @@
#ifndef _ACHIEVEMENT_HPP_
#define _ACHIEVEMENT_HPP_
#include <algorithm>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include "../common/mmo.h"
#include "../common/db.h"
struct map_session_data;
struct block_list;
enum e_achievement_group {
AG_NONE = 0,
AG_ADD_FRIEND,
@ -54,51 +63,43 @@ enum e_achievement_info {
ACHIEVEINFO_MAX,
};
struct achievement_mob {
int mod_id;
};
struct achievement_target {
int mob;
int count;
};
struct achievement_dependent {
int achievement_id;
};
struct av_condition {
int op;
struct av_condition *left;
struct av_condition *right;
std::shared_ptr<struct av_condition> left;
std::shared_ptr<struct av_condition> right;
long long value;
av_condition() : op(0), left(nullptr), right(nullptr), value(0) {}
};
struct achievement_db {
struct s_achievement_db {
int achievement_id;
char name[ACHIEVEMENT_NAME_LENGTH];
std::string name;
enum e_achievement_group group;
uint8 target_count;
struct achievement_target *targets;
uint8 dependent_count;
struct achievement_dependent *dependents;
struct av_condition *condition;
std::vector <achievement_target> targets;
std::vector <int> dependent_ids;
std::shared_ptr<struct av_condition> condition;
int16 mapindex;
struct ach_reward {
unsigned short nameid, amount;
struct script_code *script;
int title_id;
ach_reward();
~ach_reward();
} rewards;
int score;
int has_dependent; // Used for quick updating of achievements that depend on others - this is their ID
s_achievement_db();
};
struct map_session_data;
struct block_list;
extern struct achievement_db achievement_dummy; ///< Dummy entry for invalid achievement lookups
struct achievement_db *achievement_search(int achievement_id);
bool achievement_exists(int achievement_id);
std::shared_ptr<s_achievement_db>& achievement_get(int achievement_id);
bool achievement_mobexists(int mob_id);
void achievement_get_reward(struct map_session_data *sd, int achievement_id, time_t rewarded);
struct achievement *achievement_add(struct map_session_data *sd, int achievement_id);
@ -117,9 +118,9 @@ void do_init_achievement(void);
void do_final_achievement(void);
// Parser
const char *av_parse_subexpr(const char *p,int limit, struct av_condition *parent);
const char *av_parse_simpleexpr(const char *p, struct av_condition *parent);
long long achievement_check_condition(struct av_condition *condition, struct map_session_data *sd, int *count);
void achievement_script_free(struct av_condition *condition);
const char *av_parse_subexpr(const char *p,int limit, std::shared_ptr<struct av_condition> parent);
const char *av_parse_simpleexpr(const char *p, std::shared_ptr<struct av_condition> parent);
long long achievement_check_condition(std::shared_ptr<struct av_condition> condition, struct map_session_data *sd, const int *count);
void achievement_script_free(std::shared_ptr<struct av_condition> condition);
#endif /* _ACHIEVEMENT_HPP_ */

View File

@ -19020,7 +19020,7 @@ void clif_change_title_ack(struct map_session_data *sd, unsigned char result, un
*/
void clif_parse_change_title(int fd, struct map_session_data *sd)
{
int title_id, i;
int title_id;
nullpo_retv(sd);
@ -19032,8 +19032,7 @@ void clif_parse_change_title(int fd, struct map_session_data *sd)
}else if( title_id <= 0 ){
sd->status.title_id = 0;
}else{
ARR_FIND(0, sd->titleCount, i, sd->titles[i] == title_id);
if( i == sd->titleCount ){
if (std::find(sd->titles.begin(), sd->titles.end(), title_id) != sd->titles.end()) {
clif_change_title_ack(sd, 1, title_id);
return;
}

View File

@ -2130,13 +2130,14 @@ void intif_parse_achievements(int fd)
CREATE(sd->achievement_data.achievements, struct achievement, num_received);
for (i = 0; i < num_received; i++) {
struct achievement_db *adb = achievement_search(received[i].achievement_id);
if (!adb) {
if (!achievement_exists(received[i].achievement_id)) {
ShowError("intif_parse_achievementlog: Achievement %d not found in DB.\n", received[i].achievement_id);
continue;
}
auto &adb = achievement_get(received[i].achievement_id);
received[i].score = adb->score;
if (received[i].completed == 0) // Insert at the beginning
@ -2220,7 +2221,7 @@ void intif_parse_achievementreward(int fd){
/**
* Request the achievement rewards from the inter server.
*/
int intif_achievement_reward(struct map_session_data *sd, struct achievement_db *adb){
int intif_achievement_reward(struct map_session_data *sd, struct s_achievement_db *adb){
if( CheckForCharServer() ){
return 0;
}
@ -2232,7 +2233,7 @@ int intif_achievement_reward(struct map_session_data *sd, struct achievement_db
WFIFOW(inter_fd, 10) = adb->rewards.nameid;
WFIFOL(inter_fd, 12) = adb->rewards.amount;
safestrncpy(WFIFOCP(inter_fd, 16), sd->status.name, NAME_LENGTH);
safestrncpy(WFIFOCP(inter_fd, 16+NAME_LENGTH), adb->name, ACHIEVEMENT_NAME_LENGTH);
safestrncpy(WFIFOCP(inter_fd, 16+NAME_LENGTH), adb->name.c_str(), ACHIEVEMENT_NAME_LENGTH);
WFIFOSET(inter_fd, 16+NAME_LENGTH+ACHIEVEMENT_NAME_LENGTH);
return 1;

View File

@ -16,7 +16,7 @@ struct s_mercenary;
struct s_elemental;
struct mail_message;
struct auction_data;
struct achievement_db;
struct s_achievement_db;
struct map_session_data;
int intif_parse(int fd);
@ -119,7 +119,7 @@ int intif_clan_member_left( int clan_id );
// ACHIEVEMENT SYSTEM
void intif_request_achievements(uint32 char_id);
int intif_achievement_save(struct map_session_data *sd);
int intif_achievement_reward(struct map_session_data *sd, struct achievement_db *adb);
int intif_achievement_reward(struct map_session_data *sd, struct s_achievement_db *adb);
int intif_request_accinfo(int u_fd, int aid, int group_lv, char* query, char type);

View File

@ -4,6 +4,8 @@
#ifndef _PC_HPP_
#define _PC_HPP_
#include <vector>
#include "../common/mmo.h" // JOB_*, MAX_FAME_LIST, struct fame_list, struct mmo_charstatus
#include "../common/strlib.h"// StringBuf
@ -612,8 +614,7 @@ struct map_session_data {
} achievement_data;
// Title system
int *titles;
uint8 titleCount;
std::vector<int> titles;
/* ShowEvent Data Cache flags from map */
bool *qi_display;

View File

@ -23374,7 +23374,7 @@ BUILDIN_FUNC(achievementadd) {
return SCRIPT_CMD_FAILURE;
}
if (achievement_search(achievement_id) == &achievement_dummy) {
if (achievement_exists(achievement_id) == false) {
ShowWarning("buildin_achievementadd: Achievement '%d' doesn't exist.\n", achievement_id);
script_pushint(st, false);
return SCRIPT_CMD_FAILURE;
@ -23411,7 +23411,7 @@ BUILDIN_FUNC(achievementremove) {
return SCRIPT_CMD_FAILURE;
}
if (achievement_search(achievement_id) == &achievement_dummy) {
if (achievement_exists(achievement_id) == false) {
ShowWarning("buildin_achievementremove: Achievement '%d' doesn't exist.\n", achievement_id);
script_pushint(st, false);
return SCRIPT_CMD_SUCCESS;
@ -23447,7 +23447,7 @@ BUILDIN_FUNC(achievementinfo) {
return SCRIPT_CMD_FAILURE;
}
if (achievement_search(achievement_id) == &achievement_dummy) {
if (achievement_exists(achievement_id) == false) {
ShowWarning("buildin_achievementinfo: Achievement '%d' doesn't exist.\n", achievement_id);
script_pushint(st, false);
return SCRIPT_CMD_FAILURE;
@ -23481,7 +23481,7 @@ BUILDIN_FUNC(achievementcomplete) {
return SCRIPT_CMD_FAILURE;
}
if (achievement_search(achievement_id) == &achievement_dummy) {
if (achievement_exists(achievement_id) == false) {
ShowWarning("buildin_achievementcomplete: Achievement '%d' doesn't exist.\n", achievement_id);
script_pushint(st, false);
return SCRIPT_CMD_FAILURE;
@ -23518,7 +23518,7 @@ BUILDIN_FUNC(achievementexists) {
return SCRIPT_CMD_FAILURE;
}
if (achievement_search(achievement_id) == &achievement_dummy) {
if (achievement_exists(achievement_id) == false) {
ShowWarning("buildin_achievementexists: Achievement '%d' doesn't exist.\n", achievement_id);
script_pushint(st, false);
return SCRIPT_CMD_SUCCESS;
@ -23557,7 +23557,7 @@ BUILDIN_FUNC(achievementupdate) {
return SCRIPT_CMD_FAILURE;
}
if (achievement_search(achievement_id) == &achievement_dummy) {
if (achievement_exists(achievement_id) == false) {
ShowWarning("buildin_achievementupdate: Achievement '%d' doesn't exist.\n", achievement_id);
script_pushint(st, false);
return SCRIPT_CMD_FAILURE;