summaryrefslogtreecommitdiff
path: root/src/map/pc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/pc.c')
-rw-r--r--src/map/pc.c517
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;