From 87476b17df574f4c1cb1ef46c3c8e47419ea5a08 Mon Sep 17 00:00:00 2001 From: Haru Date: Sun, 3 Jan 2016 03:38:34 +0100 Subject: Corrected wrong use of MAX_PC_SKILL_REQUIRE instead of MAX_HOM_SKILL_REQUIRE Signed-off-by: Haru --- src/map/homunculus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/map/homunculus.c b/src/map/homunculus.c index ac1c4052b..d81eeb2da 100644 --- a/src/map/homunculus.c +++ b/src/map/homunculus.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -227,7 +227,7 @@ int homunculus_calc_skilltree(struct homun_data *hd, int flag_evolve) { if( hd->homunculus.hskill[ id - HM_SKILLBASE ].id ) continue; //Skill already known. if(!battle_config.skillfree) { - for( j = 0; j < MAX_PC_SKILL_REQUIRE; j++ ) { + for (j = 0; j < MAX_HOM_SKILL_REQUIRE; j++) { if( homun->dbs->skill_tree[c][i].need[j].id && homun->checkskill(hd,homun->dbs->skill_tree[c][i].need[j].id) < homun->dbs->skill_tree[c][i].need[j].lv ) { f = 0; @@ -252,7 +252,7 @@ int homunculus_calc_skilltree(struct homun_data *hd, int flag_evolve) { if( j < homun->dbs->skill_tree[c][i].intimacylv ) continue; if(!battle_config.skillfree) { - for( j = 0; j < MAX_PC_SKILL_REQUIRE; j++ ) { + for (j = 0; j < MAX_HOM_SKILL_REQUIRE; j++) { if( homun->dbs->skill_tree[c][i].need[j].id && homun->checkskill(hd,homun->dbs->skill_tree[c][i].need[j].id) < homun->dbs->skill_tree[c][i].need[j].lv ) { f = 0; @@ -1234,7 +1234,7 @@ bool homunculus_read_skill_db_sub(char* split[], int columns, int current) { if (minJobLevelPresent) homun->dbs->skill_tree[classid][j].joblv = atoi(split[3]); - for( k = 0; k < MAX_PC_SKILL_REQUIRE; k++ ) { + for (k = 0; k < MAX_HOM_SKILL_REQUIRE; k++) { homun->dbs->skill_tree[classid][j].need[k].id = atoi(split[3+k*2+minJobLevelPresent]); homun->dbs->skill_tree[classid][j].need[k].lv = atoi(split[3+k*2+minJobLevelPresent+1]); } -- cgit v1.2.3-60-g2f50 From 818acc5866707b9e294a87d2dd902aebb670707f Mon Sep 17 00:00:00 2001 From: Haru Date: Sun, 3 Jan 2016 03:45:30 +0100 Subject: Rewritten skill_tree parser in a more robust way - Fixes an issue that prevented skills with more than 4 pre-requisites or more than 3 pre-requisites and a minimum level from being parsed correctly (and without any warning or error messages). - Removes the limit on 5 pre-requisites (replaced a fixed size array with a VECTOR) - Reduces memory usage of skill_tree from 794kB to 440kB (32 bit) or 523kB (64 bit). - Fixes an issue that prevented multiple inheritance from working correctly in rare cases (incorrect definition order), without any warning or error messages. Now a warning is displayed if a job is inherited before being defined. - Fixes an issue that prevented skills inherited from being correctly merged with the skills defined for the current job. - Prevents a job from inheriting itself by accident. - Correctly detects skills defined twice for the same job. Signed-off-by: Haru --- src/map/atcommand.c | 23 ++-- src/map/pc.c | 390 ++++++++++++++++++++++++++++++++-------------------- src/map/pc.h | 16 ++- src/map/skill.c | 24 ++-- 4 files changed, 276 insertions(+), 177 deletions(-) (limited to 'src') diff --git a/src/map/atcommand.c b/src/map/atcommand.c index c26daee54..d98ef8d94 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -5364,12 +5364,13 @@ ACMD(displayskill) { * @skilltree by [MouseJstr] * prints the skill tree for a player required to get to a skill *------------------------------------------*/ -ACMD(skilltree) { +ACMD(skilltree) +{ struct map_session_data *pl_sd = NULL; uint16 skill_id; int meets, j, c=0; char target[NAME_LENGTH]; - struct skill_tree_entry *ent; + struct skill_tree_entry *entry; if(!*message || sscanf(message, "%5hu %23[^\r\n]", &skill_id, target) != 2) { clif->message(fd, msg_fd(fd,1167)); // Usage: @skilltree @@ -5387,21 +5388,19 @@ ACMD(skilltree) { safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1168), pc->job_name(c), pc->checkskill(pl_sd, NV_BASIC)); // Player is using %s skill tree (%d basic points). clif->message(fd, atcmd_output); - ARR_FIND( 0, MAX_SKILL_TREE, j, pc->skill_tree[c][j].id == 0 || pc->skill_tree[c][j].id == skill_id ); - if( j == MAX_SKILL_TREE || pc->skill_tree[c][j].id == 0 ) - { + ARR_FIND(0, MAX_SKILL_TREE, j, pc->skill_tree[c][j].id == 0 || pc->skill_tree[c][j].id == skill_id); + if (j == MAX_SKILL_TREE || pc->skill_tree[c][j].id == 0) { clif->message(fd, msg_fd(fd,1169)); // The player cannot use that skill. return false; } - ent = &pc->skill_tree[c][j]; + entry = &pc->skill_tree[c][j]; meets = 1; - for(j=0;jneed[j].id && pc->checkskill(sd,ent->need[j].id) < ent->need[j].lv) - { - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1170), ent->need[j].lv, skill->dbs->db[ent->need[j].id].desc); // Player requires level %d of skill %s. + for (j = 0; j < VECTOR_LENGTH(entry->need); j++) { + struct skill_tree_requirement *req = &VECTOR_INDEX(entry->need, j); + if (pc->checkskill(sd, req->id) < req->lv) { + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1170), req->lv, skill->dbs->db[req->id].desc); // Player requires level %d of skill %s. clif->message(fd, atcmd_output); meets = 0; } diff --git a/src/map/pc.c b/src/map/pc.c index d697cd9f4..1045a25be 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -1526,43 +1526,42 @@ int pc_calc_skilltree(struct map_session_data *sd) do { flag = 0; - for( i = 0; i < MAX_SKILL_TREE && (id = pc->skill_tree[c][i].id) > 0; i++ ) { - int f, idx = pc->skill_tree[c][i].idx; - if( sd->status.skill[idx].id ) + for (i = 0; i < MAX_SKILL_TREE && (id = pc->skill_tree[c][i].id) > 0; i++) { + int idx = pc->skill_tree[c][i].idx; + bool satisfied = true; + if (sd->status.skill[idx].id > 0) continue; //Skill already known. - f = 1; - if(!battle_config.skillfree) { + if (!battle_config.skillfree) { int j; - for(j = 0; j < MAX_PC_SKILL_REQUIRE; j++) { - int k; - if((k=pc->skill_tree[c][i].need[j].id)) { - int idx2 = pc->skill_tree[c][i].need[j].idx; - if (sd->status.skill[idx2].id == 0 || sd->status.skill[idx2].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[idx2].flag == SKILL_FLAG_PLAGIARIZED) - k = 0; //Not learned. - else if (sd->status.skill[idx2].flag >= SKILL_FLAG_REPLACED_LV_0) //Real lerned level - k = sd->status.skill[idx2].flag - SKILL_FLAG_REPLACED_LV_0; - else - k = pc->checkskill2(sd,idx2); - if (k < pc->skill_tree[c][i].need[j].lv) { - f = 0; - break; - } + for (j = 0; j < VECTOR_LENGTH(pc->skill_tree[c][i].need); j++) { + struct skill_tree_requirement *req = &VECTOR_INDEX(pc->skill_tree[c][i].need, j); + int level; + if (sd->status.skill[req->idx].id == 0 + || sd->status.skill[req->idx].flag == SKILL_FLAG_TEMPORARY + || sd->status.skill[req->idx].flag == SKILL_FLAG_PLAGIARIZED) + level = 0; //Not learned. + else if (sd->status.skill[req->idx].flag >= SKILL_FLAG_REPLACED_LV_0) //Real learned level + level = sd->status.skill[req->idx].flag - SKILL_FLAG_REPLACED_LV_0; + else + level = pc->checkskill2(sd, req->idx); + if (level < req->lv) { + satisfied = false; + break; } } - if ( sd->status.job_level < pc->skill_tree[c][i].joblv ) { - int x = pc->mapid2jobid(sd->class_, sd->status.sex); // need to get its own skilltree - if ( x > -1 ) { - x = pc->class2idx(x); - if ( !pc->skill_tree[x][i].inherited ) - f = 0; // job level requirement wasn't satisfied - } else - f = 0; + if (sd->status.job_level < pc->skill_tree[c][i].joblv) { + int jobid = pc->mapid2jobid(sd->class_, sd->status.sex); // need to get its own skilltree + if (jobid > -1) { + if (!pc->skill_tree[pc->class2idx(jobid)][i].inherited) + satisfied = false; // job level requirement wasn't satisfied + } else { + satisfied = false; + } } } - if( f ) { - int inf2; - inf2 = skill->dbs->db[idx].inf2; + if (satisfied) { + int inf2 = skill->dbs->db[idx].inf2; if(!sd->status.skill[idx].lv && ( (inf2&INF2_QUEST_SKILL && !battle_config.quest_skill_learn) || @@ -1628,39 +1627,40 @@ void pc_check_skilltree(struct map_session_data *sd, int skill_id) c = pc->class2idx(c); do { flag = 0; - for (i = 0; i < MAX_SKILL_TREE && (id=pc->skill_tree[c][i].id)>0; i++) { - int j, f = 1, idx = pc->skill_tree[c][i].idx; + for (i = 0; i < MAX_SKILL_TREE && (id = pc->skill_tree[c][i].id) > 0; i++) { + int j, idx = pc->skill_tree[c][i].idx; + bool satisfied = true; - if( sd->status.skill[idx].id ) //Already learned + if (sd->status.skill[idx].id) //Already learned continue; - for (j = 0; j < MAX_PC_SKILL_REQUIRE; j++) { - int k = pc->skill_tree[c][i].need[j].id; - if (k) { - int idx2 = pc->skill_tree[c][i].need[j].idx; - if (sd->status.skill[idx2].id == 0 || sd->status.skill[idx2].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[idx2].flag == SKILL_FLAG_PLAGIARIZED) - k = 0; //Not learned. - else if (sd->status.skill[idx2].flag >= SKILL_FLAG_REPLACED_LV_0) //Real lerned level - k = sd->status.skill[idx2].flag - SKILL_FLAG_REPLACED_LV_0; - else - k = pc->checkskill2(sd,idx2); - if (k < pc->skill_tree[c][i].need[j].lv) { - f = 0; - break; - } + for (j = 0; j < VECTOR_LENGTH(pc->skill_tree[c][i].need); j++) { + struct skill_tree_requirement *req = &VECTOR_INDEX(pc->skill_tree[c][i].need, j); + int level; + if (sd->status.skill[req->idx].id == 0 + || sd->status.skill[req->idx].flag == SKILL_FLAG_TEMPORARY + || sd->status.skill[req->idx].flag == SKILL_FLAG_PLAGIARIZED) + level = 0; //Not learned. + else if (sd->status.skill[req->idx].flag >= SKILL_FLAG_REPLACED_LV_0) //Real lerned level + level = sd->status.skill[req->idx].flag - SKILL_FLAG_REPLACED_LV_0; + else + level = pc->checkskill2(sd,req->idx); + if (level < req->lv) { + satisfied = false; + break; } } - if( !f ) + if (!satisfied) continue; - if ( sd->status.job_level < pc->skill_tree[c][i].joblv ) { - int x = pc->mapid2jobid(sd->class_, sd->status.sex); // need to get its own skilltree - if ( x > -1 ) { - x = pc->class2idx(x); - if ( !pc->skill_tree[x][i].inherited ) + if (sd->status.job_level < pc->skill_tree[c][i].joblv) { + int jobid = pc->mapid2jobid(sd->class_, sd->status.sex); // need to get its own skilltree + if (jobid > -1) { + if (!pc->skill_tree[pc->class2idx(jobid)][i].inherited) continue; - } else + } else { continue; + } } j = skill->dbs->db[idx].inf2; @@ -10592,128 +10592,203 @@ int pc_split_atoui(char* str, unsigned int* val, char sep, int max) val[j] = 0; return i; } -/* [Ind/Hercules] */ -void pc_read_skill_tree(void) { + +/** + * Parses the skill tree config file. + * + * In order to reclaim the memory allocated by this function + * `pc->clear_skill_tree()` should be used. + * + * @remark + * This function assumes that the skill tree is clear and zeroed. + * If it has been already loaded (ie reloading), it needs to be cleared + * before calling this function again. + * + * @author [Ind/Hercules] + */ +void pc_read_skill_tree(void) +{ config_t skill_tree_conf; - config_setting_t *skt = NULL, *inherit = NULL, *skills = NULL, *sk = NULL; -#ifdef RENEWAL - const char *config_filename = "db/re/skill_tree.conf"; // FIXME hardcoded name -#else - const char *config_filename = "db/pre-re/skill_tree.conf"; // FIXME hardcoded name -#endif + config_setting_t *skt = NULL; + char config_filename[128]; int i = 0; struct s_mapiterator *iter; struct map_session_data *sd; + bool loaded[CLASS_COUNT] = { false }; + safesnprintf(config_filename, sizeof(config_filename), "%s/"DBPATH"skill_tree.conf", map->db_path); if (libconfig->read_file(&skill_tree_conf, config_filename)) { ShowError("can't read %s\n", config_filename); return; } - while ((skt = libconfig->setting_get_elem(skill_tree_conf.root,i++))) { - int k; - const char *name = config_setting_name(skt); + // Foreach job + while ((skt = libconfig->setting_get_elem(skill_tree_conf.root, i++))) { + config_setting_t *t = NULL; + int job_idx; + const char *job_name = config_setting_name(skt); + int job_id = pc->check_job_name(job_name); - if ( (k = pc->check_job_name(name)) == -1 ) { - ShowWarning("pc_read_skill_tree: '%s' unknown job name!\n", name); + if (job_id == -1) { + ShowWarning("pc_read_skill_tree: '%s' unknown job name!\n", job_name); + continue; + } + job_idx = pc->class2idx(job_id); + if (loaded[job_idx]) { + ShowWarning("pc_read_skill_tree: Duplicate entry for job '%s'. Skipping.\n", job_name); continue; } + loaded[job_idx] = true; - if( ( skills = libconfig->setting_get_member(skt,"skills") ) ) { - int c = 0; - int idx = pc->class2idx(k); + if ((t = libconfig->setting_get_member(skt, "inherit")) != NULL) { + int j = 0; + const char *ijob_name = NULL; + // Foreach inherited job + while ((ijob_name = libconfig->setting_get_string_elem(t, j++)) != NULL) { + int k, ijob_idx; + int ijob_id = pc->check_job_name(ijob_name); - while ((sk = libconfig->setting_get_elem(skills,c++))) { - const char *sk_name = config_setting_name(sk); - int skill_id; + if (ijob_id == -1) { + ShowWarning("pc_read_skill_tree: '%s' trying to inherit unknown '%s'!\n", job_name, ijob_name); + continue; + } + ijob_idx = pc->class2idx(ijob_id); + if (ijob_idx == job_idx) { + ShowWarning("pc_read_skill_tree: '%s' trying to inherit itself. Skipping.\n", job_name); + continue; + } + if (!loaded[ijob_idx]) { + ShowWarning("pc_read_skill_tree: '%s' trying to inherit not yet loaded '%s' (wrong order in the tree). Skipping.\n", job_name, ijob_name); + continue; + } - if( ( skill_id = skill->name2id(sk_name) ) ) { - int skidx, offset = 0, h = 0, rlen = 0; + for (k = 0; k < MAX_SKILL_TREE; k++) { + int cur; + struct skill_tree_entry *dst = NULL; + const struct skill_tree_entry *src = &pc->skill_tree[ijob_idx][k]; - ARR_FIND( 0, MAX_SKILL_TREE, skidx, pc->skill_tree[idx][skidx].id == 0 || pc->skill_tree[idx][skidx].id == skill_id ); - if (skidx == MAX_SKILL_TREE) { - ShowWarning("pc_read_skill_tree: Unable to load skill %d (%s) into '%s's tree. Maximum number of skills per class has been reached.\n", skill_id, sk_name, name); - continue; - } else if (pc->skill_tree[idx][skidx].id) { - ShowNotice("pc_read_skill_tree: Overwriting %d for '%s' (%d)\n", skill_id, name, k); - } + if (src->id == 0) + break; // No more skills to copy - pc->skill_tree[idx][skidx].id = skill_id; - pc->skill_tree[idx][skidx].idx = skill->get_index(skill_id); - - if( config_setting_is_group(sk) ) { - int max = 0, jlevel = 0; - libconfig->setting_lookup_int(sk, "MaxLevel", &max); - libconfig->setting_lookup_int(sk, "MinJobLevel", &jlevel); - pc->skill_tree[idx][skidx].max = (unsigned char)max; - pc->skill_tree[idx][skidx].joblv = (unsigned char)jlevel; - rlen = libconfig->setting_length(sk); - offset += jlevel ? 2 : 1; - } else { - pc->skill_tree[idx][skidx].max = (unsigned char)libconfig->setting_get_int(sk); - pc->skill_tree[idx][skidx].joblv = 0; + ARR_FIND(0, MAX_SKILL_TREE, cur, pc->skill_tree[job_idx][cur].id == 0 || pc->skill_tree[job_idx][cur].id == src->id); + if (cur == MAX_SKILL_TREE) { + ShowWarning("pc_read_skill_tree: '%s' can't inherit '%s', skill tree is full!\n", job_name, ijob_name); + break; } - - for (h = offset; h < rlen && h < MAX_PC_SKILL_REQUIRE; h++) { - config_setting_t *rsk = libconfig->setting_get_elem(sk,h); - int rskid; - if (rsk && (rskid = skill->name2id(config_setting_name(rsk))) != 0) { - pc->skill_tree[idx][skidx].need[h].id = rskid; - pc->skill_tree[idx][skidx].need[h].idx = skill->get_index(rskid); - pc->skill_tree[idx][skidx].need[h].lv = (unsigned char)libconfig->setting_get_int(rsk); - } else if( rsk ) { - ShowWarning("pc_read_skill_tree: unknown requirement '%s' for '%s' in '%s'\n",config_setting_name(rsk),sk_name,name); - } else { - ShowWarning("pc_read_skill_tree: error for '%s' in '%s'\n",sk_name,name); + if (src->id == NV_TRICKDEAD && ((pc->jobid2mapid(job_id)&(MAPID_BASEMASK | JOBL_2)) != MAPID_NOVICE)) + continue; // skip trickdead for non-novices + dst = &pc->skill_tree[job_idx][cur]; + dst->inherited = 1; + if (dst->id == 0) { + // Not existing yet, copy + dst->id = src->id; + dst->idx = src->idx; + dst->max = src->max; + dst->joblv = src->joblv; + VECTOR_INIT(dst->need); + if (VECTOR_LENGTH(src->need) > 0) { + VECTOR_ENSURE(dst->need, VECTOR_LENGTH(src->need), 1); + VECTOR_PUSHARRAY(dst->need, VECTOR_DATA(src->need), VECTOR_LENGTH(src->need)); + } + } else { + int l; + // Already existing, merge + if (src->max > dst->max) + dst->max = src->max; + dst->joblv = src->joblv; + for (l = 0; l < VECTOR_LENGTH(src->need); l++) { + int m; + struct skill_tree_requirement *sreq = &VECTOR_INDEX(src->need, l); + ARR_FIND(0, VECTOR_LENGTH(dst->need), m, VECTOR_INDEX(dst->need, m).id == sreq->id); + if (m == VECTOR_LENGTH(dst->need)) { + VECTOR_ENSURE(dst->need, 1, 1); + VECTOR_PUSHCOPY(dst->need, sreq); + } else { + struct skill_tree_requirement *dreq = &VECTOR_INDEX(dst->need, m); + dreq->lv = sreq->lv; + } } } - } else { - ShowWarning("pc_read_skill_tree: unknown skill '%s' in '%s'\n",sk_name,name); } } } - } - - i = 0; - while( (skt = libconfig->setting_get_elem(skill_tree_conf.root,i++)) ) { - int k, idx; - const char *name = config_setting_name(skt); - - if ( (k = pc->check_job_name(name)) == -1 ) { - ShowWarning("pc_read_skill_tree: '%s' unknown job name!\n", name); - continue; - } - - idx = pc->class2idx(k); - - if( ( inherit = libconfig->setting_get_member(skt,"inherit") ) ) { - const char *iname; - int v = 0; - while ( (iname = libconfig->setting_get_string_elem(inherit, v++)) ) { - int b = 0, a, d, f, fidx; + if ((t = libconfig->setting_get_member(skt, "skills")) != NULL) { + int j = 0; + config_setting_t *sk = NULL; + // Foreach skill + while ((sk = libconfig->setting_get_elem(t, j++)) != NULL) { + int skill_id, sk_idx; + config_setting_t *rsk = NULL; + const char *sk_name = config_setting_name(sk); + struct skill_tree_entry *tree_entry = NULL; - if ( (b = pc->check_job_name(iname)) == -1 ) { - ShowWarning("pc_read_skill_tree: '%s' trying to inherit unknown '%s'!\n", name, iname); + if ((skill_id = skill->name2id(sk_name)) == 0) { + ShowWarning("pc_read_skill_tree: unknown skill '%s' in '%s'\n", sk_name, job_name); continue; } - fidx = pc->class2idx(b); - - ARR_FIND(0, MAX_SKILL_TREE, d, pc->skill_tree[fidx][d].id == 0); - - for ( f = 0; f < d; f++ ) { - - ARR_FIND(0, MAX_SKILL_TREE, a, pc->skill_tree[idx][a].id == 0 || pc->skill_tree[idx][a].id == pc->skill_tree[fidx][f].id); + ARR_FIND(0, MAX_SKILL_TREE, sk_idx, pc->skill_tree[job_idx][sk_idx].id == 0 || pc->skill_tree[job_idx][sk_idx].id == skill_id); + if (sk_idx == MAX_SKILL_TREE) { + ShowWarning("pc_read_skill_tree: Unable to load skill %d (%s) into '%s's tree. Maximum number of skills per class has been reached.\n", skill_id, sk_name, job_name); + continue; + } + tree_entry = &pc->skill_tree[job_idx][sk_idx]; - if ( a == MAX_SKILL_TREE ) { - ShowWarning("pc_read_skill_tree: '%s' can't inherit '%s', skill tree is full!\n", name, iname); - break; - } else if ( pc->skill_tree[idx][a].id || (pc->skill_tree[idx][a].id == NV_TRICKDEAD && ((pc->jobid2mapid(k)&(MAPID_BASEMASK | JOBL_2)) != MAPID_NOVICE)) ) /* we skip trickdead for non-novices */ - continue;/* skip */ - memcpy(&pc->skill_tree[idx][a], &pc->skill_tree[fidx][f], sizeof(pc->skill_tree[fidx][f])); - pc->skill_tree[idx][a].inherited = 1; + if (tree_entry->id != 0 && !tree_entry->inherited) { + ShowNotice("pc_read_skill_tree: Duplicate %d for '%s' (%d). Skipping.\n", skill_id, job_name, job_id); + continue; + } + if (config_setting_is_group(sk)) { + int i32 = 0; + if (libconfig->setting_lookup_int(sk, "MaxLevel", &i32) && i32 > 0) { + tree_entry->max = (unsigned char)i32; + } else { + ShowWarning("pc_read_skill_tree: missing MaxLevel for skill %d (%s) class '%s'. Skipping.\n", skill_id, sk_name, job_name); + continue; + } + if (libconfig->setting_lookup_int(sk, "MinJobLevel", &i32) && i32 > 0) { + tree_entry->joblv = (unsigned char)i32; + } else if (!tree_entry->inherited) { + tree_entry->joblv = 0; + } + } else { + tree_entry->max = (unsigned char)libconfig->setting_get_int(sk); + if (!tree_entry->inherited) + tree_entry->joblv = 0; + } + if (!tree_entry->inherited) { + tree_entry->id = skill_id; + tree_entry->idx = skill->get_index(skill_id); + VECTOR_INIT(tree_entry->need); } + if (config_setting_is_group(sk)) { + int k = 0; + // Foreach requirement + while ((rsk = libconfig->setting_get_elem(sk, k++)) != NULL) { + const char *rsk_name = config_setting_name(rsk); + int rsk_id = skill->name2id(rsk_name); + struct skill_tree_requirement *req = NULL; + int l; + + if (rsk_id == 0) { + if (strcmp(rsk_name, "MaxLevel") != 0 && strcmp(rsk_name, "MinJobLevel") != 0) + ShowWarning("pc_read_skill_tree: unknown requirement '%s' for '%s' in '%s'\n", rsk_name, sk_name, job_name); + continue; + } + ARR_FIND(0, VECTOR_LENGTH(tree_entry->need), l, VECTOR_INDEX(tree_entry->need, l).id == rsk_id); + if (l == VECTOR_LENGTH(tree_entry->need)) { + VECTOR_ENSURE(tree_entry->need, 1, 1); + VECTOR_PUSHZEROED(tree_entry->need); + req = &VECTOR_LAST(tree_entry->need); + req->id = rsk_id; + req->idx = skill->get_index(rsk_id); + } else { + req = &VECTOR_INDEX(tree_entry->need, l); + } + req->lv = (unsigned char)libconfig->setting_get_int(rsk); + } + } } } } @@ -10726,6 +10801,24 @@ void pc_read_skill_tree(void) { clif->skillinfoblock(sd); mapit->free(iter); } + +/** + * Clears the skill tree and frees any allocated memory. + */ +void pc_clear_skill_tree(void) +{ + int i; + for (i = 0; i < CLASS_COUNT; i++) { + int j; + for (j = 0; j < MAX_SKILL_TREE; j++) { + if (pc->skill_tree[i][j].id == 0) + continue; + VECTOR_CLEAR(pc->skill_tree[i][j].need); + } + } + memset(pc->skill_tree, 0, sizeof(pc->skill_tree)); +} + bool pc_readdb_levelpenalty(char* fields[], int columns, int current) { #if defined(RENEWAL_DROP) || defined(RENEWAL_EXP) int type, race, diff; @@ -10842,7 +10935,7 @@ int pc_readdb(void) { 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 - memset(pc->skill_tree,0,sizeof(pc->skill_tree)); + pc->clear_skill_tree(); pc->read_skill_tree(); #if defined(RENEWAL_DROP) || defined(RENEWAL_EXP) sv->readdb(map->db_path, "re/level_penalty.txt", ',', 4, 4, -1, pc->readdb_levelpenalty); @@ -11367,6 +11460,8 @@ void do_final_pc(void) { pcg->final(); + pc->clear_skill_tree(); + ers_destroy(pc->sc_display_ers); ers_destroy(pc->num_reg_ers); ers_destroy(pc->str_reg_ers); @@ -11693,6 +11788,7 @@ void pc_defaults(void) { pc->autosave = pc_autosave; pc->follow_timer = pc_follow_timer; pc->read_skill_tree = pc_read_skill_tree; + pc->clear_skill_tree = pc_clear_skill_tree; pc->isUseitem = pc_isUseitem; pc->show_steal = pc_show_steal; pc->checkcombo = pc_checkcombo; diff --git a/src/map/pc.h b/src/map/pc.h index 97f339bf6..23b46a631 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -41,7 +41,6 @@ * Defines **/ #define MAX_PC_BONUS 10 -#define MAX_PC_SKILL_REQUIRE 5 #define MAX_PC_FEELHATE 3 #define MAX_PC_DEVOTION 5 ///< Max amount of devotion targets #define PVP_CALCRANK_INTERVAL 1000 ///< PVP calculation interval @@ -714,17 +713,19 @@ END_ZEROED_BLOCK; #define pc_can_give_items(sd) ( pc_has_permission((sd),PC_PERM_TRADE) ) #define pc_can_give_bound_items(sd) ( pc_has_permission((sd),PC_PERM_TRADE_BOUND) ) +struct skill_tree_requirement { + short id; + unsigned short idx; + unsigned char lv; +}; + struct skill_tree_entry { short id; unsigned short idx; unsigned char max; unsigned char joblv; short inherited; - struct { - short id; - unsigned short idx; - unsigned char lv; - } need[MAX_PC_SKILL_REQUIRE]; + VECTOR_DECL(struct skill_tree_requirement) need; }; // Celest struct sg_data { @@ -1052,6 +1053,7 @@ END_ZEROED_BLOCK; /* End */ int (*autosave) (int tid, int64 tick, int id, intptr_t data); int (*follow_timer) (int tid, int64 tick, int id, intptr_t data); void (*read_skill_tree) (void); + void (*clear_skill_tree) (void); int (*isUseitem) (struct map_session_data *sd,int n); int (*show_steal) (struct block_list *bl,va_list ap); int (*checkcombo) (struct map_session_data *sd, struct item_data *data ); diff --git a/src/map/skill.c b/src/map/skill.c index dc3ebe62e..6795707fd 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team + * Copyright (C) 2012-2016 Hercules Dev Team * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify @@ -19082,22 +19082,24 @@ void skill_readdb(bool minimal) { sv->readdb(map->db_path, "skill_changematerial_db.txt", ',', 4, 4+2*5, MAX_SKILL_PRODUCE_DB, skill->parse_row_changematerialdb); } -void skill_reload (void) { +void skill_reload(void) +{ struct s_mapiterator *iter; struct map_session_data *sd; - int i,c,k; + int i, j, k; skill->read_db(false); //[Ind/Hercules] refresh index cache - for(c = 0; c < CLASS_COUNT; c++) { - for( i = 0; i < MAX_SKILL_TREE; i++ ) { - if( pc->skill_tree[c][i].id ) { - pc->skill_tree[c][i].idx = skill->get_index(pc->skill_tree[c][i].id); - for(k = 0; k < MAX_PC_SKILL_REQUIRE; k++) { - if( pc->skill_tree[c][i].need[k].id ) - pc->skill_tree[c][i].need[k].idx = skill->get_index(pc->skill_tree[c][i].need[k].id); - } + for (j = 0; j < CLASS_COUNT; j++) { + for (i = 0; i < MAX_SKILL_TREE; i++) { + struct skill_tree_entry *entry = &pc->skill_tree[j][i]; + if (entry->id == 0) + continue; + entry->idx = skill->get_index(entry->id); + for (k = 0; k < VECTOR_LENGTH(entry->need); k++) { + struct skill_tree_requirement *req = &VECTOR_INDEX(entry->need, k); + req->idx = skill->get_index(req->id); } } } -- cgit v1.2.3-60-g2f50