From 652d34f6570b7eb0361ca471f43c68e274962f0a Mon Sep 17 00:00:00 2001 From: Smokexyz Date: Wed, 27 Dec 2017 22:31:00 +0800 Subject: Conversion of exp.txt to libconfig --- src/map/pc.c | 383 ++++++++++++++++++++++++++++++------------------------- src/map/pc.h | 28 +++- src/map/status.c | 48 ++++++- 3 files changed, 274 insertions(+), 185 deletions(-) (limited to 'src/map') diff --git a/src/map/pc.c b/src/map/pc.c index f2bf042eb..0872c85e1 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -79,6 +79,8 @@ struct pc_interface pc_s; struct pc_interface *pc; +struct class_exp_tables exptables; + //Converts a class to its array index for CLASS_COUNT defined arrays. //Note that it does not do a validity check for speed purposes, where parsing //player input make sure to use a pc->db_checkid first! @@ -1921,7 +1923,7 @@ int pc_calc_skilltree_normalize_job(struct map_session_data *sd) skill_point = pc->calc_skillpoint(sd); - novice_skills = pc->max_level[pc->class2idx(JOB_NOVICE)][1] - 1; + novice_skills = pc->dbs->class_exp_table[pc->class2idx(JOB_NOVICE)][CLASS_EXP_TABLE_JOB]->max_level - 1; sd->sktree.second = sd->sktree.third = 0; @@ -1935,7 +1937,7 @@ int pc_calc_skilltree_normalize_job(struct map_session_data *sd) if ((sd->job & JOBL_THIRD) != 0) { // if neither 2nd nor 3rd jobchange levels are known, we have to assume a default for 2nd if (sd->change_level_3rd == 0) { - sd->change_level_2nd = pc->max_level[pc->class2idx(pc->mapid2jobid(sd->job & MAPID_UPPERMASK, sd->status.sex))][1]; + sd->change_level_2nd = pc->dbs->class_exp_table[pc->class2idx(pc->mapid2jobid(sd->job & MAPID_UPPERMASK, sd->status.sex))][CLASS_EXP_TABLE_JOB]->max_level; } else { sd->change_level_2nd = 1 + skill_point + sd->status.skill_point - (sd->status.job_level - 1) @@ -7086,12 +7088,16 @@ bool pc_gainexp(struct map_session_data *sd, struct block_list *src, uint64 base *------------------------------------------*/ int pc_maxbaselv(const struct map_session_data *sd) { - return pc->max_level[pc->class2idx(sd->status.class)][0]; + nullpo_ret(sd); + + return pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_BASE]->max_level; } int pc_maxjoblv(const struct map_session_data *sd) { - return pc->max_level[pc->class2idx(sd->status.class)][1]; + nullpo_ret(sd); + + return pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_JOB]->max_level; } /*========================================== @@ -7101,21 +7107,35 @@ int pc_maxjoblv(const struct map_session_data *sd) //Base exp needed for next level. uint64 pc_nextbaseexp(const struct map_session_data *sd) { + const struct class_exp_group *exp_group = NULL; + nullpo_ret(sd); if (sd->status.base_level >= pc->maxbaselv(sd) || sd->status.base_level <= 0) return 0; - return pc->exp_table[pc->class2idx(sd->status.class)][0][sd->status.base_level-1]; + exp_group = pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_BASE]; + + nullpo_ret(exp_group); + + return VECTOR_INDEX(exp_group->exp, sd->status.base_level >= exp_group->max_level ? 0 : sd->status.base_level - 1); } //Base exp needed for this level. uint64 pc_thisbaseexp(const struct map_session_data *sd) { + const struct class_exp_group *exp_group = NULL; + + nullpo_ret(sd); + if (sd->status.base_level > pc->maxbaselv(sd) || sd->status.base_level <= 1) return 0; - return pc->exp_table[pc->class2idx(sd->status.class)][0][sd->status.base_level-2]; + exp_group = pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_BASE]; + + nullpo_ret(exp_group); + + return VECTOR_INDEX(exp_group->exp, sd->status.base_level - 2); } /*========================================== @@ -7128,19 +7148,33 @@ uint64 pc_thisbaseexp(const struct map_session_data *sd) //Job exp needed for next level. uint64 pc_nextjobexp(const struct map_session_data *sd) { + const struct class_exp_group *exp_group = NULL; + nullpo_ret(sd); if (sd->status.job_level >= pc->maxjoblv(sd) || sd->status.job_level <= 0) return 0; - return pc->exp_table[pc->class2idx(sd->status.class)][1][sd->status.job_level-1]; + + exp_group = pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_JOB]; + + nullpo_ret(exp_group); + + return VECTOR_INDEX(exp_group->exp, sd->status.job_level >= exp_group->max_level ? 0 : sd->status.job_level - 1); } //Job exp needed for this level. uint64 pc_thisjobexp(const struct map_session_data *sd) { + const struct class_exp_group *exp_group = NULL; + + nullpo_ret(sd); + if (sd->status.job_level > pc->maxjoblv(sd) || sd->status.job_level <= 1) return 0; - return pc->exp_table[pc->class2idx(sd->status.class)][1][sd->status.job_level-2]; + + exp_group = pc->dbs->class_exp_table[pc->class2idx(sd->status.class)][CLASS_EXP_TABLE_JOB]; + + return VECTOR_INDEX(exp_group->exp, sd->status.job_level - 2); } /// Returns the value of the specified stat. @@ -11074,94 +11108,6 @@ int pc_level_penalty_mod(int diff, unsigned char race, uint32 mode, int type) return 100; #endif } -int pc_split_str(char *str,char **val,int num) -{ - int i; - - nullpo_ret(val); - for (i=0; i UINT_MAX) { - val[i] = UINT_MAX; - if (!warning) { - warning = 1; - ShowWarning("pc_readdb (exp.txt): Required exp per level is capped to %u\n", UINT_MAX); - } - } else - val[i] = (unsigned int)f; - str = strchr(str,sep); - if (str) - *str++=0; - } - //Zero up the remaining. - for(j=i; j < max; j++) - val[j] = 0; - return i; -} - -int pc_split_atoui64(char* str, uint64* val, char sep, int max) -{ - static int warning=0; - int i,j; - nullpo_ret(val); - for (i=0; i UINT64_MAX) { - val[i] = UINT64_MAX; - if (!warning) { - warning = 1; - ShowWarning("pc_readdb (exp.txt): Required exp per level is capped to %"PRIu64"\n", UINT64_MAX); - } - } else - val[i] = (uint64)f; - str = strchr(str,sep); - if (str) - *str++=0; - } - //Zero up the remaining. - for(j=i; j < max; j++) - val[j] = 0; - return i; -} bool pc_read_skill_job_skip(short skill_id, int job_id) { @@ -11421,6 +11367,115 @@ bool pc_readdb_levelpenalty(char* fields[], int columns, int current) { return true; } +bool pc_read_exp_db_sub_class(struct config_setting_t *t, bool base) +{ + struct class_exp_group entry = { { 0 } }; + struct config_setting_t *exp_t = NULL; + int maxlv = 0; + + nullpo_retr(false, t); + + safestrncpy(entry.name, config_setting_name(t), SCRIPT_VARNAME_LENGTH); + + if (libconfig->setting_lookup_int(t, "MaxLevel", &maxlv) == 0 + || (maxlv <= 0 || maxlv > MAX_LEVEL)) { + ShowError("pc_read_exp_db_sub_class: Invalid max %s level '%d' set for entry '%s'. Defaulting to %d...", base ? "base" : "job", maxlv, entry.name, MAX_LEVEL); + maxlv = MAX_LEVEL; + } + + entry.max_level = maxlv; + + if ((exp_t = libconfig->setting_lookup(t, "Exp")) != NULL && config_setting_is_array(exp_t)) { + int j = 0; + + VECTOR_ENSURE(entry.exp, maxlv - 2, 10); + + if (libconfig->setting_length(exp_t) > maxlv - 1) { + ShowWarning("pc_read_exp_db_sub_class: Exp table length (%d) for %s exp group '%s' exceeds specified max level %d. Skipping remaining entries...\n", libconfig->setting_length(exp_t), base ? "base" : "job", entry.name, maxlv); + } + + while (j < libconfig->setting_length(exp_t) && j <= maxlv - 2) + VECTOR_PUSH(entry.exp, libconfig->setting_get_int64_elem(exp_t, j++)); + + if (j - 1 < maxlv - 2) { + ShowError("pc_read_exp_db_sub_class: Specified max %d for group '%s', but that group's %s exp table only goes up to level %d.\n", maxlv, entry.name, base ? "base" : "job", VECTOR_LENGTH(entry.exp)); + ShowInfo("Filling the missing values with the last exp entry.\n"); + while (j++ <= maxlv - 2) + VECTOR_PUSH(entry.exp, VECTOR_LAST(entry.exp)); + } + } else { + ShowError("pc_read_exp_db_sub_class: Invalid or non-existent 'Exp' field set for %s level entry '%s'. Skipping...\n", entry.name, base ? "base" : "job"); + return false; + } + + VECTOR_ENSURE(pc->class_exp_groups[base ? CLASS_EXP_TABLE_BASE : CLASS_EXP_TABLE_JOB], 1, 1); + VECTOR_PUSH(pc->class_exp_groups[base ? CLASS_EXP_TABLE_BASE : CLASS_EXP_TABLE_JOB], entry); + return true; +} + +/** + * Description: Helper function to read a root configuration in the exp_db.conf file. + * @param[in] t pointer to the root config setting + * @param[in] base boolean switch determining whether to read either base or job exp. + * @return total number of valid entries read from the setting. + */ +int pc_read_exp_db_sub(struct config_setting_t *t, bool base) +{ + int i = 0, entry_count = 0; + struct config_setting_t *tt = NULL; + + nullpo_ret(t); + + while ((tt = libconfig->setting_get_elem(t, i++)) != NULL) { + pc->read_exp_db_sub_class(tt, base); + entry_count++; + } + + return entry_count; +} + +/** + * Description: Initiates reading of the exp_group_db.conf. + * @return true success, false on failure. + */ +bool pc_read_exp_db(void) +{ + struct config_t exp_db_conf; + struct config_setting_t *edb = NULL; + int entry_count = 0; + +#ifdef RENEWAL + const char *config_filename = "db/re/exp_group_db.conf"; +#else + const char *config_filename = "db/pre-re/exp_group_db.conf"; +#endif + + if (!libconfig->load_file(&exp_db_conf, config_filename)) + return false; + + if ((edb = libconfig->setting_lookup(exp_db_conf.root, "base_exp_group_db")) != NULL) { + entry_count += pc->read_exp_db_sub(edb, true); + } else { + ShowError("pc_read_exp_db: Error reading base exp group db in '%s'.\n", config_filename); + libconfig->destroy(&exp_db_conf); + return false; + } + + if ((edb = libconfig->setting_lookup(exp_db_conf.root, "job_exp_group_db")) != NULL) { + entry_count += pc->read_exp_db_sub(edb, false); + } else { + ShowError("pc_read_exp_db: Error reading job exp group db in '%s'.\n", config_filename); + libconfig->destroy(&exp_db_conf); + return false; + } + + libconfig->destroy(&exp_db_conf); + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entry_count, config_filename); + + return true; +} + /*========================================== * pc DB reading. * exp.txt - required experience values @@ -11433,81 +11488,12 @@ int pc_readdb(void) { FILE *fp; char line[24000],*p; - //reset - memset(pc->exp_table,0,sizeof(pc->exp_table)); - memset(pc->max_level,0,sizeof(pc->max_level)); - - sprintf(line, "%s/"DBPATH"exp.txt", map->db_path); - - fp=fopen(line, "r"); - if(fp==NULL){ - ShowError("can't read %s\n", line); - return 1; - } - while(fgets(line, sizeof(line), fp)) { - int jobs[CLASS_COUNT], job_count, job, job_id; - int type; - int maxlv; - char *split[4]; - if(line[0]=='/' && line[1]=='/') - continue; - if (pc_split_str(line,split,4) < 4) - continue; + /** + * Read and load into memory, the exp_group_db.conf file. + */ + pc->clear_exp_groups(); + pc->read_exp_db(); - job_count = pc_split_atoi(split[1],jobs,':',CLASS_COUNT); - if (job_count < 1) - continue; - job_id = jobs[0]; - if (!pc->db_checkid(job_id)) { - ShowError("pc_readdb: Invalid job ID %d.\n", job_id); - continue; - } - type = atoi(split[2]); - if (type < 0 || type > 1) { - ShowError("pc_readdb: Invalid type %d (must be 0 for base levels, 1 for job levels).\n", type); - continue; - } - maxlv = atoi(split[0]); - if (maxlv > MAX_LEVEL) { - ShowWarning("pc_readdb: Specified max level %d for job %d is beyond server's limit (%d).\n ", maxlv, job_id, MAX_LEVEL); - maxlv = MAX_LEVEL; - } - count++; - job = jobs[0] = pc->class2idx(job_id); - //We send one less and then one more because the last entry in the exp array should hold 0. - pc->max_level[job][type] = pc_split_atoui64(split[3], pc->exp_table[job][type], ',', maxlv - 1) + 1; - //Reverse check in case the array has a bunch of trailing zeros... [Skotlex] - //The reasoning behind the -2 is this... if the max level is 5, then the array - //should look like this: - //0: x, 1: x, 2: x: 3: x 4: 0 <- last valid value is at 3. - while ((i = pc->max_level[job][type]) >= 2 && pc->exp_table[job][type][i-2] <= 0) - pc->max_level[job][type]--; - if (pc->max_level[job][type] < maxlv) { - ShowWarning("pc_readdb: Specified max %d for job %d, but that job's exp table only goes up to level %d.\n", maxlv, job_id, pc->max_level[job][type]); - ShowInfo("Filling the missing values with the last exp entry.\n"); - //Fill the requested values with the last entry. - i = (pc->max_level[job][type] <= 2 ? 0: pc->max_level[job][type]-2); - for (; i+2 < maxlv; i++) - pc->exp_table[job][type][i] = pc->exp_table[job][type][i-1]; - pc->max_level[job][type] = maxlv; - } - //ShowDebug("%s - Class %d: %d\n", type?"Job":"Base", job_id, pc->max_level[job][type]); - for (i = 1; i < job_count; i++) { - job_id = jobs[i]; - if (!pc->db_checkid(job_id)) { - ShowError("pc_readdb: Invalid job ID %d.\n", job_id); - continue; - } - job = pc->class2idx(job_id); - memcpy(pc->exp_table[job][type], pc->exp_table[jobs[0]][type], sizeof(pc->exp_table[0][0])); - pc->max_level[job][type] = maxlv; - //ShowDebug("%s - Class %d: %d\n", type?"Job":"Base", job_id, pc->max_level[job][type]); - } - } - fclose(fp); - pc->validate_levels(); - ShowStatus("Done reading '"CL_WHITE"%u"CL_RESET"' entries in '"CL_WHITE"%s/"DBPATH"%s"CL_RESET"'.\n",count,map->db_path,"exp.txt"); - count = 0; // Reset and read skilltree pc->clear_skill_tree(); pc->read_skill_tree(); @@ -11621,18 +11607,31 @@ int pc_readdb(void) { return 0; } +bool pc_job_is_dummy(int job) +{ + if (job == JOB_KNIGHT2 || job == JOB_CRUSADER2 + || job == JOB_WEDDING || job == JOB_XMAS || job == JOB_SUMMER + || job == JOB_LORD_KNIGHT2 || job == JOB_PALADIN2 + || job == JOB_BABY_KNIGHT2 || job == JOB_BABY_CRUSADER2 + || job == JOB_STAR_GLADIATOR2 + || (job >= JOB_RUNE_KNIGHT2 && job <= JOB_MECHANIC_T2) + || (job >= JOB_BABY_RUNE2 && job <= JOB_BABY_MECHANIC2)) + return true; + return false; +} + void pc_validate_levels(void) { int i; int j; for (i = 0; i < JOB_MAX; i++) { if (!pc->db_checkid(i)) continue; - if (i == JOB_WEDDING || i == JOB_XMAS || i == JOB_SUMMER) + if (pc->job_is_dummy(i)) continue; //Classes that do not need exp tables. j = pc->class2idx(i); - if (pc->max_level[j][0] == 0) - ShowWarning("Class %s (%d) does not has a base exp table.\n", pc->job_name(i), i); - if (pc->max_level[j][1] == 0) - ShowWarning("Class %s (%d) does not has a job exp table.\n", pc->job_name(i), i); + if (pc->dbs->class_exp_table[j][CLASS_EXP_TABLE_BASE] == NULL) + ShowWarning("Class %s (%d - %d) does not have a base exp table.\n", pc->job_name(i), i, j); + if (pc->dbs->class_exp_table[j][CLASS_EXP_TABLE_JOB] == NULL) + ShowWarning("Class %s (%d - %d) does not have a job exp table.\n", pc->job_name(i), i, j); } } @@ -12172,7 +12171,29 @@ void pc_update_job_and_level(struct map_session_data *sd) } } -void do_final_pc(void) { +void pc_clear_exp_groups(void) +{ + int i, k, size; + for (k = 0; k < 2; k++) { + size = VECTOR_LENGTH(pc->class_exp_groups[k]); + + for (i = 0; i < size; i++) + VECTOR_CLEAR(VECTOR_INDEX(pc->class_exp_groups[k], i).exp); + VECTOR_CLEAR(pc->class_exp_groups[k]); + } +} + +void pc_init_exp_groups(void) +{ + int i; + for (i = 0; i < 2; i++) { + VECTOR_INIT(pc->class_exp_groups[i]); + } +} + +void do_final_pc(void) +{ + db_destroy(pc->itemcd_db); pc->at_db->destroy(pc->at_db,pc->autotrade_final); @@ -12180,6 +12201,8 @@ void do_final_pc(void) { pc->clear_skill_tree(); + pc->clear_exp_groups(); + ers_destroy(pc->sc_display_ers); ers_destroy(pc->num_reg_ers); ers_destroy(pc->str_reg_ers); @@ -12194,6 +12217,7 @@ void do_init_pc(bool minimal) { pc->itemcd_db = idb_alloc(DB_OPT_RELEASE_DATA); pc->at_db = idb_alloc(DB_OPT_RELEASE_DATA); + pc->init_exp_groups(); pc->readdb(); timer->add_func_list(pc->invincible_timer, "pc_invincible_timer"); @@ -12248,6 +12272,7 @@ void pc_defaults(void) { unsigned int equip_pos[EQI_MAX]={EQP_ACC_L,EQP_ACC_R,EQP_SHOES,EQP_GARMENT,EQP_HEAD_LOW,EQP_HEAD_MID,EQP_HEAD_TOP,EQP_ARMOR,EQP_HAND_L,EQP_HAND_R,EQP_COSTUME_HEAD_TOP,EQP_COSTUME_HEAD_MID,EQP_COSTUME_HEAD_LOW,EQP_COSTUME_GARMENT,EQP_AMMO, EQP_SHADOW_ARMOR, EQP_SHADOW_WEAPON, EQP_SHADOW_SHIELD, EQP_SHADOW_SHOES, EQP_SHADOW_ACC_R, EQP_SHADOW_ACC_L }; pc = &pc_s; + pc->dbs = &exptables; /* vars */ pc->at_db = NULL; @@ -12468,6 +12493,9 @@ void pc_defaults(void) { pc->getmaxspiritball = pc_getmaxspiritball; pc->readdb = pc_readdb; + pc->read_exp_db = pc_read_exp_db; + pc->read_exp_db_sub = pc_read_exp_db_sub; + pc->read_exp_db_sub_class = pc_read_exp_db_sub_class; pc->map_day_timer = map_day_timer; // by [yor] pc->map_night_timer = map_night_timer; // by [yor] // Rental System @@ -12522,6 +12550,9 @@ void pc_defaults(void) { pc->calcweapontype = pc_calcweapontype; pc->removecombo = pc_removecombo; pc->update_job_and_level = pc_update_job_and_level; + pc->clear_exp_groups = pc_clear_exp_groups; + pc->init_exp_groups = pc_init_exp_groups; + pc->job_is_dummy = pc_job_is_dummy; pc->bank_withdraw = pc_bank_withdraw; pc->bank_deposit = pc_bank_deposit; diff --git a/src/map/pc.h b/src/map/pc.h index 695add32f..f998c799d 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -824,6 +824,24 @@ struct autotrade_vending { struct hplugin_data_store *hdata; ///< HPM Plugin Data Store }; +struct class_exp_group { + char name[SCRIPT_VARNAME_LENGTH]; + int max_level; + VECTOR_DECL(uint64) exp; +}; + +/** +* Exp types +*/ +enum class_exp_type { + CLASS_EXP_TABLE_BASE, + CLASS_EXP_TABLE_JOB +}; + +struct class_exp_tables { + struct class_exp_group *class_exp_table[CLASS_COUNT][2]; +}; + /*===================================== * Interface : pc.h * Generated by HerculesInterfaceMaker @@ -841,8 +859,6 @@ struct pc_interface { /* */ BEGIN_ZEROED_BLOCK; /* Everything within this block will be memset to 0 when status_defaults() is executed */ - uint64 exp_table[CLASS_COUNT][2][MAX_LEVEL]; - int max_level[CLASS_COUNT][2]; unsigned int statp[MAX_LEVEL+1]; unsigned int level_penalty[3][RC_MAX][MAX_LEVEL*2+1]; /* */ @@ -852,6 +868,8 @@ BEGIN_ZEROED_BLOCK; /* Everything within this block will be memset to 0 when sta struct fame_list taekwon_fame_list[MAX_FAME_LIST]; END_ZEROED_BLOCK; /* End */ + struct class_exp_tables *dbs; + VECTOR_DECL(struct class_exp_group) class_exp_groups[2]; unsigned int equip_pos[EQI_MAX]; struct sg_data sg_info[MAX_PC_FEELHATE]; /* */ @@ -1062,6 +1080,9 @@ END_ZEROED_BLOCK; /* End */ int (*set_hate_mob) (struct map_session_data *sd, int pos, struct block_list *bl); int (*readdb) (void); + bool (*read_exp_db) (void); + int (*read_exp_db_sub) (struct config_setting_t *conf, bool base); + bool (*read_exp_db_sub_class) (struct config_setting_t *t, bool base); int (*map_day_timer) (int tid, int64 tick, int id, intptr_t data); // by [yor] int (*map_night_timer) (int tid, int64 tick, int id, intptr_t data); // by [yor] // Rental System @@ -1133,6 +1154,9 @@ END_ZEROED_BLOCK; /* End */ void (*validate_levels) (void); void (*update_job_and_level) (struct map_session_data *sd); + void (*clear_exp_groups) (void); + void (*init_exp_groups) (void); + bool (*job_is_dummy) (int job); /** * Autotrade persistency [Ind/Hercules <3] diff --git a/src/map/status.c b/src/map/status.c index 45a8f86bf..bf48d2301 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -13067,6 +13067,7 @@ void status_read_job_db_sub(int idx, const char *name, struct config_setting_t * { struct config_setting_t *temp = NULL; int i32 = 0; + const char *str = NULL; struct { const char *name; @@ -13101,6 +13102,36 @@ void status_read_job_db_sub(int idx, const char *name, struct config_setting_t * #endif }; + /** + * @field BaseExpGroup must be read before any other that deal with exp tables. + */ + if (libconfig->setting_lookup_string(jdb, "BaseExpGroup", &str) != 0) { + int i = 0; + ARR_FIND(0, VECTOR_LENGTH(pc->class_exp_groups[CLASS_EXP_TABLE_BASE]), i, strcmp(str, VECTOR_INDEX(pc->class_exp_groups[CLASS_EXP_TABLE_BASE], i).name) == 0); + if (i == VECTOR_LENGTH(pc->class_exp_groups[CLASS_EXP_TABLE_BASE])) { + ShowError("status_read_job_db: Unknown Base Exp Group '%s' provided for entry '%s'\n", str, name); + } else { + pc->dbs->class_exp_table[idx][CLASS_EXP_TABLE_BASE] = &VECTOR_INDEX(pc->class_exp_groups[CLASS_EXP_TABLE_BASE], i); + } + } else { + ShowError("status_read_job_db: BaseExpGroup setting not found for entry '%s'\n", name); + } + + /** + * @field JobExpGroup must be read before any other that deal with exp tables. + */ + if (libconfig->setting_lookup_string(jdb, "JobExpGroup", &str) != 0) { + int i = 0; + ARR_FIND(0, VECTOR_LENGTH(pc->class_exp_groups[CLASS_EXP_TABLE_JOB]), i, strcmp(str, VECTOR_INDEX(pc->class_exp_groups[CLASS_EXP_TABLE_JOB], i).name) == 0); + if (i == VECTOR_LENGTH(pc->class_exp_groups[CLASS_EXP_TABLE_JOB])) { + ShowError("status_read_job_db: Unknown Job Exp Group '%s' provided for entry '%s'\n", str, name); + } else { + pc->dbs->class_exp_table[idx][CLASS_EXP_TABLE_JOB] = &VECTOR_INDEX(pc->class_exp_groups[CLASS_EXP_TABLE_JOB], i); + } + } else { + ShowError("status_read_job_db: JobExpGroup setting not found for entry '%s'\n", name); + } + if ((temp = libconfig->setting_get_member(jdb, "Inherit"))) { int nidx = 0; const char *iname; @@ -13125,7 +13156,7 @@ void status_read_job_db_sub(int idx, const char *name, struct config_setting_t * } else { avg_increment = 5; } - for ( ; i <= pc->max_level[idx][0]; i++) { + for ( ; i <= pc->dbs->class_exp_table[idx][CLASS_EXP_TABLE_BASE]->max_level; i++) { status->dbs->HP_table[idx][i] = min(base + avg_increment * i, battle_config.max_hp); } @@ -13140,7 +13171,7 @@ void status_read_job_db_sub(int idx, const char *name, struct config_setting_t * } else { avg_increment = 1; } - for ( ; i <= pc->max_level[idx][0]; i++) { + for ( ; i <= pc->dbs->class_exp_table[idx][CLASS_EXP_TABLE_BASE]->max_level; i++) { status->dbs->SP_table[idx][i] = min(base + avg_increment * i, battle_config.max_sp); } } @@ -13166,7 +13197,7 @@ void status_read_job_db_sub(int idx, const char *name, struct config_setting_t * } else { avg_increment = 5; } - for ( ; i <= pc->max_level[idx][0]; i++) { + for ( ; i <= pc->dbs->class_exp_table[idx][CLASS_EXP_TABLE_BASE]->max_level; i++) { status->dbs->HP_table[idx][i] = min(base + avg_increment * i, battle_config.max_hp); } } @@ -13192,7 +13223,7 @@ void status_read_job_db_sub(int idx, const char *name, struct config_setting_t * } else { avg_increment = 1; } - for ( ; i <= pc->max_level[idx][0]; i++) { + for ( ; i <= pc->dbs->class_exp_table[idx][CLASS_EXP_TABLE_BASE]->max_level; i++) { status->dbs->SP_table[idx][i] = min(base + avg_increment * i, battle_config.max_sp); } } @@ -13234,7 +13265,7 @@ void status_read_job_db_sub(int idx, const char *name, struct config_setting_t * } else { avg_increment = 5; } - for (++level; level <= pc->max_level[idx][0]; ++level) { /* limit only to possible maximum level of the given class */ + for (++level; level <= pc->dbs->class_exp_table[idx][CLASS_EXP_TABLE_BASE]->max_level; ++level) { /* limit only to possible maximum level of the given class */ status->dbs->HP_table[idx][level] = min(base + avg_increment * level, battle_config.max_hp); /* some are still empty? then let's use the average increase */ } } @@ -13254,7 +13285,7 @@ void status_read_job_db_sub(int idx, const char *name, struct config_setting_t * } else { avg_increment = 1; } - for (++level; level <= pc->max_level[idx][0]; level++ ) { + for (++level; level <= pc->dbs->class_exp_table[idx][CLASS_EXP_TABLE_BASE]->max_level; level++) { status->dbs->SP_table[idx][level] = min(base + avg_increment * level, battle_config.max_sp); } } @@ -13281,7 +13312,7 @@ void status_read_job_db(void) /* [malufett/Hercules] */ if (!libconfig->load_file(&job_db_conf, config_filename)) return; - while ( (jdb = libconfig->setting_get_elem(job_db_conf.root, i++)) ) { + while ((jdb = libconfig->setting_get_elem(job_db_conf.root, i++))) { int class, idx; const char *name = config_setting_name(jdb); @@ -13293,6 +13324,7 @@ void status_read_job_db(void) /* [malufett/Hercules] */ idx = pc->class2idx(class); status->read_job_db_sub(idx, name, jdb); } + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, config_filename); libconfig->destroy(&job_db_conf); } @@ -13555,6 +13587,8 @@ int status_readdb(void) sv->readdb(map->db_path, "sc_config.txt", ',', 2, 2, SC_MAX, status->readdb_scconfig); status->read_job_db(); + pc->validate_levels(); + return 0; } -- cgit v1.2.3-60-g2f50