diff options
Diffstat (limited to 'src/map/pc.c')
-rw-r--r-- | src/map/pc.c | 517 |
1 files changed, 319 insertions, 198 deletions
diff --git a/src/map/pc.c b/src/map/pc.c index d697cd9f4..2dfd9519b 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 @@ -123,10 +123,11 @@ bool pc_should_log_commands(struct map_session_data *sd) return pcg->should_log_commands(sd->group); } -int pc_invincible_timer(int tid, int64 tick, int id, intptr_t data) { - struct map_session_data *sd; +int pc_invincible_timer(int tid, int64 tick, int id, intptr_t data) +{ + struct map_session_data *sd = map->id2sd(id); - if( (sd=(struct map_session_data *)map->id2sd(id)) == NULL || sd->bl.type!=BL_PC ) + if (sd == NULL) return 1; if(sd->invincible_timer != tid){ @@ -162,10 +163,10 @@ void pc_delinvincibletimer(struct map_session_data* sd) } int pc_spiritball_timer(int tid, int64 tick, int id, intptr_t data) { - struct map_session_data *sd; + struct map_session_data *sd = map->id2sd(id); int i; - if( (sd=(struct map_session_data *)map->id2sd(id)) == NULL || sd->bl.type!=BL_PC ) + if (sd == NULL) return 1; if( sd->spiritball <= 0 ) @@ -286,14 +287,17 @@ int pc_delspiritball(struct map_session_data *sd,int count,int type) } return 0; } -int pc_check_banding( struct block_list *bl, va_list ap ) { +int pc_check_banding(struct block_list *bl, va_list ap) +{ int *c, *b_sd; struct block_list *src; - struct map_session_data *tsd; + const struct map_session_data *tsd; struct status_change *sc; nullpo_ret(bl); - nullpo_ret(tsd = (struct map_session_data*)bl); + Assert_ret(bl->type == BL_PC); + tsd = BL_UCCAST(BL_PC, bl); + nullpo_ret(src = va_arg(ap,struct block_list *)); c = va_arg(ap,int *); b_sd = va_arg(ap, int *); @@ -596,6 +600,10 @@ int pc_makesavestatus(struct map_session_data *sd) if(!battle_config.save_clothcolor) sd->status.clothes_color=0; + if (!battle_config.save_body_style) + sd->status.body = 0; + + //Only copy the Cart/Peco/Falcon options, the rest are handled via //status change load/saving. [Skotlex] #ifdef NEW_CARTS @@ -1067,6 +1075,9 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim if( sd->status.clothes_color < MIN_CLOTH_COLOR || sd->status.clothes_color > MAX_CLOTH_COLOR ) { sd->status.clothes_color = MIN_CLOTH_COLOR; } + if (sd->status.body < MIN_BODY_STYLE || sd->status.body > MAX_BODY_STYLE) { + sd->status.body = MIN_BODY_STYLE; + } //Initializations to null/0 unneeded since map_session_data was filled with 0 upon allocation. if(!sd->status.hp) pc_setdead(sd); @@ -1526,43 +1537,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 +1638,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; @@ -1851,9 +1862,9 @@ int pc_disguise(struct map_session_data *sd, int class_) { clif->updatestatus(sd,SP_CARTINFO); } if (sd->chatID) { - struct chat_data* cd; + struct chat_data *cd = map->id2cd(sd->chatID); - if( (cd = (struct chat_data*)map->id2bl(sd->chatID)) ) + if (cd != NULL) clif->dispchat(cd,0); } } @@ -3969,7 +3980,7 @@ int pc_bonus5(struct map_session_data *sd,int type,int type2,int type3,int type4 * Grants a player a given skill. * Flag values: @see enum pc_skill_flag *------------------------------------------*/ -int pc_skill(TBL_PC* sd, int id, int level, int flag) +int pc_skill(struct map_session_data *sd, int id, int level, int flag) { uint16 index = 0; nullpo_ret(sd); @@ -5217,7 +5228,7 @@ void pc_bound_clear(struct map_session_data *sd, enum e_item_bound_type type) { *------------------------------------------*/ int pc_show_steal(struct block_list *bl,va_list ap) { - struct map_session_data *sd; + struct map_session_data *sd = NULL, *tsd = NULL; int itemid; struct item_data *item=NULL; @@ -5226,11 +5237,16 @@ int pc_show_steal(struct block_list *bl,va_list ap) sd=va_arg(ap,struct map_session_data *); itemid=va_arg(ap,int); + nullpo_ret(bl); + Assert_ret(bl->type == BL_PC); + tsd = BL_UCAST(BL_PC, bl); + nullpo_ret(sd); + if((item=itemdb->exists(itemid))==NULL) sprintf(output,"%s stole an Unknown Item (id: %i).",sd->status.name, itemid); else sprintf(output,"%s stole %s.",sd->status.name,item->jname); - clif->message( ((struct map_session_data *)bl)->fd, output); + clif->message(tsd->fd, output); return 0; } @@ -5245,15 +5261,13 @@ int pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 skil int i,itemid,flag; double rate; struct status_data *sd_status, *md_status; - struct mob_data *md; + struct mob_data *md = BL_CAST(BL_MOB, bl); struct item tmp_item; struct item_data *data = NULL; - if(!sd || !bl || bl->type!=BL_MOB) + if (sd == NULL || md == NULL) return 0; - md = (TBL_MOB *)bl; - if(md->state.steal_flag == UCHAR_MAX || ( md->sc.opt1 && md->sc.opt1 != OPT1_BURNING && md->sc.opt1 != OPT1_CRYSTALIZE ) ) //already stolen from / status change check return 0; @@ -5325,12 +5339,11 @@ int pc_steal_item(struct map_session_data *sd,struct block_list *bl, uint16 skil **/ int pc_steal_coin(struct map_session_data *sd, struct block_list *target) { int rate, skill_lv; - struct mob_data *md; + struct mob_data *md = BL_CAST(BL_MOB, target); - if (!sd || !target || target->type != BL_MOB) + if (sd == NULL || md == NULL) return 0; - md = (TBL_MOB*)target; if (md->state.steal_coin_flag || md->sc.data[SC_STONE] || md->sc.data[SC_FREEZE] || md->status.mode&MD_BOSS) return 0; @@ -7590,11 +7603,11 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { if (sd->charm_type != CHARM_TYPE_NONE && sd->charm_count > 0) pc->del_charm(sd, sd->charm_count, sd->charm_type); - if (src) { + if (src != NULL) { switch (src->type) { case BL_MOB: { - struct mob_data *md=(struct mob_data *)src; + struct mob_data *md = BL_UCAST(BL_MOB, src); if (md->target_id==sd->bl.id) mob->unlocktarget(md,tick); if (battle_config.mobs_level_up && md->status.hp @@ -7616,19 +7629,19 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { } break; case BL_PET: //Pass on to master... - src = &((TBL_PET*)src)->msd->bl; + src = &BL_UCAST(BL_PET, src)->msd->bl; break; case BL_HOM: - src = &((TBL_HOM*)src)->master->bl; + src = &BL_UCAST(BL_HOM, src)->master->bl; break; case BL_MER: - src = &((TBL_MER*)src)->master->bl; + src = &BL_UCAST(BL_MER, src)->master->bl; break; } } - if (src && src->type == BL_PC) { - struct map_session_data *ssd = (struct map_session_data *)src; + if (src != NULL && src->type == BL_PC) { + struct map_session_data *ssd = BL_UCAST(BL_PC, src); pc->setparam(ssd, SP_KILLEDRID, sd->bl.id); npc->script_event(ssd, NPCE_KILLPC); @@ -7814,9 +7827,8 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) { if( map->list[sd->bl.m].flag.pvp && !battle_config.pk_mode && !map->list[sd->bl.m].flag.pvp_nocalcrank ) { sd->pvp_point -= 5; sd->pvp_lost++; - if( src && src->type == BL_PC ) - { - struct map_session_data *ssd = (struct map_session_data *)src; + if (src != NULL && src->type == BL_PC) { + struct map_session_data *ssd = BL_UCAST(BL_PC, src); ssd->pvp_point++; ssd->pvp_won++; } @@ -8316,11 +8328,12 @@ int pc_percentheal(struct map_session_data *sd,int hp,int sp) int jobchange_killclone(struct block_list *bl, va_list ap) { - struct mob_data *md; - int flag; - md = (struct mob_data *)bl; - nullpo_ret(md); - flag = va_arg(ap, int); + struct mob_data *md = NULL; + int flag = va_arg(ap, int); + + nullpo_ret(bl); + Assert_ret(bl->type == BL_MOB); + md = BL_UCAST(BL_MOB, bl); if (md->master_id && md->special_state.clone && md->master_id == flag) status_kill(&md->bl); @@ -8450,6 +8463,8 @@ int pc_jobchange(struct map_session_data *sd,int job, int upper) clif->changelook(&sd->bl,LOOK_BASE,sd->vd.class_); // move sprite update to prevent client crashes with incompatible equipment [Valaris] if(sd->vd.cloth_color) clif->changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color); + if (sd->vd.body_style) + clif->changelook(&sd->bl,LOOK_BODY2,sd->vd.body_style); //Update skill tree. pc->calc_skilltree(sd); @@ -8546,6 +8561,8 @@ int pc_changelook(struct map_session_data *sd,int type,int val) clif->changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); if (sd->vd.cloth_color) clif->changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color); + if (sd->vd.body_style) + clif->changelook(&sd->bl,LOOK_BODY2,sd->vd.body_style); clif->skillinfoblock(sd); return 0; break; @@ -8594,6 +8611,10 @@ int pc_changelook(struct map_session_data *sd,int type,int val) case LOOK_ROBE: sd->status.robe = val; break; + case LOOK_BODY2: + val = cap_value(val, MIN_BODY_STYLE, MAX_BODY_STYLE); + sd->status.body=val; + break; } clif->changelook(&sd->bl,type,val); return 0; @@ -8690,6 +8711,8 @@ int pc_setoption(struct map_session_data *sd,int type) clif->changelook(&sd->bl,LOOK_BASE,new_look); if (sd->vd.cloth_color) clif->changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color); + if( sd->vd.body_style ) + clif->changelook(&sd->bl,LOOK_BODY2,sd->vd.body_style); clif->skillinfoblock(sd); // Skill list needs to be updated after base change. return 0; @@ -8761,7 +8784,7 @@ int pc_setcart(struct map_session_data *sd,int type) { * @param sd Target player. * @param flag New state. **/ -void pc_setfalcon(TBL_PC* sd, bool flag) +void pc_setfalcon(struct map_session_data *sd, bool flag) { if (flag) { if (pc->checkskill(sd,HT_FALCON) > 0) // add falcon if he have the skill @@ -8779,7 +8802,7 @@ void pc_setfalcon(TBL_PC* sd, bool flag) * @param sd Target player. * @param flag New state. **/ -void pc_setridingpeco(TBL_PC* sd, bool flag) +void pc_setridingpeco(struct map_session_data *sd, bool flag) { if (flag) { if (pc->checkskill(sd, KN_RIDING)) @@ -8815,7 +8838,7 @@ void pc_setmadogear(struct map_session_data *sd, bool flag) * @param sd Target player. * @param type New state. This must be a valid OPTION_DRAGON* or 0. **/ -void pc_setridingdragon(TBL_PC* sd, unsigned int type) +void pc_setridingdragon(struct map_session_data *sd, unsigned int type) { if (type&OPTION_DRAGON) { // Ensure only one dragon is set at a time. @@ -8847,7 +8870,7 @@ void pc_setridingdragon(TBL_PC* sd, unsigned int type) * @param sd Target player. * @param flag New state. **/ -void pc_setridingwug(TBL_PC* sd, bool flag) +void pc_setridingwug(struct map_session_data *sd, bool flag) { if (flag) { if (pc->checkskill(sd, RA_WUGRIDER) > 0) @@ -9982,12 +10005,15 @@ int pc_checkitem(struct map_session_data *sd) /*========================================== * Update PVP rank for sd1 in cmp to sd2 *------------------------------------------*/ -int pc_calc_pvprank_sub(struct block_list *bl,va_list ap) +int pc_calc_pvprank_sub(struct block_list *bl, va_list ap) { - struct map_session_data *sd1,*sd2; + struct map_session_data *sd1 = NULL; + struct map_session_data *sd2 = va_arg(ap,struct map_session_data *); - sd1=(struct map_session_data *)bl; - sd2=va_arg(ap,struct map_session_data *); + nullpo_ret(bl); + Assert_ret(bl->type == BL_PC); + sd1 = BL_UCAST(BL_PC, bl); + nullpo_ret(sd2); if (pc_isinvisible(sd1) ||pc_isinvisible(sd2)) { // cannot register pvp rank for hidden GMs @@ -10242,8 +10268,7 @@ int pc_autosave(int tid, int64 tick, int id, intptr_t data) { save_flag = 1; //Noone was saved, so save first found char. iter = mapit_getallusers(); - for( sd = (TBL_PC*)mapit->first(iter); mapit->exists(iter); sd = (TBL_PC*)mapit->next(iter) ) - { + for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { if(sd->bl.id == last_save_id && save_flag != 1) { save_flag = 1; continue; @@ -10386,10 +10411,10 @@ bool pc_can_use_command(struct map_session_data *sd, const char *command) { */ int pc_charm_timer(int tid, int64 tick, int id, intptr_t data) { - struct map_session_data *sd; + struct map_session_data *sd = map->id2sd(id); int i; - if( (sd=(struct map_session_data *)map->id2sd(id)) == NULL || sd->bl.type!=BL_PC ) + if (sd == NULL) return 1; if (sd->charm_count <= 0) { @@ -10592,128 +10617,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); + } + } } } } @@ -10722,10 +10822,28 @@ void pc_read_skill_tree(void) { /* lets update all players skill tree */ iter = mapit_getallusers(); - for( sd = (TBL_PC*)mapit->first(iter); mapit->exists(iter); sd = (TBL_PC*)mapit->next(iter) ) + for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) 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 +10960,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); @@ -11087,7 +11205,7 @@ int pc_global_expiration_timer(int tid, int64 tick, int id, intptr_t data) { struct map_session_data* sd; iter = mapit_getallusers(); - for( sd = (TBL_PC*)mapit->first(iter); mapit->exists(iter); sd = (TBL_PC*)mapit->next(iter) ) { + for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { if( sd->expiration_time ) pc->expire_check(sd); } @@ -11133,7 +11251,7 @@ void pc_autotrade_load(void) SQL->GetData(map->mysql_handle, 2, &data, NULL); sex = atoi(data); SQL->GetData(map->mysql_handle, 3, &data, NULL); safestrncpy(title, data, sizeof(title)); - CREATE(sd, TBL_PC, 1); + CREATE(sd, struct map_session_data, 1); pc->setnewpc(sd, account_id, char_id, 0, 0, sex, 0); @@ -11279,7 +11397,7 @@ void pc_autotrade_prepare(struct map_session_data *sd) { map->quit(sd); chrif->auth_delete(account_id, char_id, ST_LOGOUT); - CREATE(sd, TBL_PC, 1); + CREATE(sd, struct map_session_data, 1); pc->setnewpc(sd, account_id, char_id, 0, 0, sex, 0); @@ -11367,6 +11485,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 +11813,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; |