diff --git a/db/re/item_db.txt b/db/re/item_db.txt index 4b8a0d0bba..ae24c5bc08 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -6270,9 +6270,9 @@ 12256,PRO_Gift_Box,PRO Gift Box,2,0,,10,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{} 12257,Cold_Medicine,Cold Medicine,0,20,,100,,,,,0xFFFFFFFF,63,2,,,50,,,{ percentheal 25,25; },{},{} 12258,Bombring_Box,Bomb Poring Box,2,0,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ if(strcharinfo(3)=="job3_rang02") { monster "this",-1,-1,"--ja--",1904,1,""; } },{},{} -12259,Miracle_Medicine,Miracle Tonic,2,0,,0,,,,,0xFFFFFFFF,63,2,,,,,,{ BaseExp += 3000000; JobExp += 1500000; },{},{} +12259,Miracle_Medicine,Miracle Tonic,2,0,,0,,,,,0xFFFFFFFF,63,2,,,,,,{ getexp2(3000000,1500000); },{},{} 12260,Cool_Summer_Outfit,Cool Summer Outfit,2,0,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_SUMMER,600000,0; },{},{} -12261,Secret_Medicine,Leap of Fantasy,2,0,,0,,,,,0xFFFFFFFF,63,2,,,,,,{ BaseExp += 2000000; JobExp += 1000000; },{},{} +12261,Secret_Medicine,Leap of Fantasy,2,0,,0,,,,,0xFFFFFFFF,63,2,,,,,,{ getexp2(2000000,1000000); },{},{} 12262,Inspector_Certificate_,Authoritative Badge,2,0,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_SPEEDUP0,540000,25; },{},{} 12263,Comp_Battle_Manual,Field Manual,2,2,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_EXPBOOST,1800000,50; },{},{} 12264,Comp_Bubble_Gum,Bubble Gum,2,2,,10,,,,,0xFFFFFFFF,63,2,,,,,,{ sc_start SC_ITEMBOOST,1800000,200; },{},{} diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 415a3dadce..73ee3f8ba6 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -4185,25 +4185,26 @@ the character will also have their skills reset upon 'changecharsex'. --------------------------------------- -*getexp ,{,}; +*getexp ,{,}; This command will give the invoking character a specified number of base and job -experience points. Can be used as a quest reward. Negative values won't work. +experience points. Used for a quest reward. Negative values won't work. + +The EXP values are adjustted by 'quest_exp_rate' config value, VIP bonus, Guild +Tax and EXP boost items such Battle Manual, Bubble Gum, or items that have +SC_EXPBOOST or SC_ITEMBOOST. getexp 10000,5000; -You can also use the "set" command with the constants defined in 'db/const.txt': +--------------------------------------- - // These 2 combined has the same effect as the above command - set BaseExp,BaseExp+10000; - set JobExp,JobExp+5000; +*getexp2 ,{,}; -You can also reduce the amount of experience points: +This command is safety version of 'set' command for BaseExp and JobExp. If using +'set' while the BaseExp or JobExp value is more than 2,147,483,647 (INT_MAX) will +causing overflow error. - set BaseExp,BaseExp-10000; - -Note that 'getexp' is now subject to the 'quest_exp_rate' config option, which -adjusts the gained value. If you want to bypass this, use the 'set' method. +Unlike 'getexp', this command ignores the adjustment factors! --------------------------------------- diff --git a/src/map/mob.c b/src/map/mob.c index c8b42d14c2..a65fd193a5 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -2410,7 +2410,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) job_exp = (unsigned int)cap_value(apply_rate(job_exp, rate), 1, UINT_MAX); } #endif - pc_gainexp(tmpsd[i], &md->bl, base_exp, job_exp, false); + pc_gainexp(tmpsd[i], &md->bl, base_exp, job_exp, 0); } } if(zeny) // zeny from mobs [Valaris] @@ -2606,7 +2606,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type) clif_mvp_effect(mvp_sd); clif_mvp_exp(mvp_sd,mexp); - pc_gainexp(mvp_sd, &md->bl, mexp,0, false); + pc_gainexp(mvp_sd, &md->bl, mexp,0, 0); log_mvp[1] = mexp; if( !(map[m].flag.nomvploot || type&1) ) { diff --git a/src/map/npc.c b/src/map/npc.c index f9c902b532..7f9f224799 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -1817,7 +1817,7 @@ uint8 npc_buylist(struct map_session_data* sd, uint16 n, struct s_npc_buy_list * z = z * (double)skill * (double)battle_config.shop_exp/10000.; if( z < 1 ) z = 1; - pc_gainexp(sd,NULL,0,(int)z, false); + pc_gainexp(sd,NULL,0,(int)z, 0); } } @@ -1974,7 +1974,7 @@ uint8 npc_selllist(struct map_session_data* sd, int n, unsigned short *item_list z = z * (double)skill * (double)battle_config.shop_exp/10000.; if( z < 1 ) z = 1; - pc_gainexp(sd, NULL, 0, (int)z, false); + pc_gainexp(sd, NULL, 0, (int)z, 0); } } diff --git a/src/map/party.c b/src/map/party.c index 88f91ad154..36aae67fa2 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -1077,7 +1077,7 @@ int party_exp_share(struct party_data* p, struct block_list* src, unsigned int b } #endif - pc_gainexp(sd[i], src, base_exp, job_exp, false); + pc_gainexp(sd[i], src, base_exp, job_exp, 0); if (zeny) // zeny from mobs [Valaris] pc_getzeny(sd[i],zeny,LOG_TYPE_PICKDROP_MONSTER,NULL); diff --git a/src/map/pc.c b/src/map/pc.c index 13c07d6a97..4f0d53a6e5 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -6448,31 +6448,35 @@ void pc_gainexp_disp(struct map_session_data *sd, unsigned int base_exp, unsigne * @param src EXP source * @param base_exp Base EXP gained * @param base_exp Job EXP gained - * @param quest True if EXP from quest, false otherwise. + * @param exp_flag 1: Quest EXP; 2: Param Exp (Ignore Guild EXP tax, EXP adjustments) * @return **/ -int pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int base_exp, unsigned int job_exp, bool quest) +void pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int base_exp, unsigned int job_exp, uint8 exp_flag) { unsigned int nextb = 0, nextj = 0; uint8 flag = 0; ///< 1: Base EXP given, 2: Job EXP given, 4: Max Base level, 8: Max Job Level - nullpo_ret(sd); + nullpo_retv(sd); if(sd->bl.prev == NULL || pc_isdead(sd)) - return 0; + return; - if(!battle_config.pvp_exp && map[sd->bl.m].flag.pvp) // [MouseJstr] - return 0; // no exp on pvp maps + if (!(exp_flag&2)) { + + if (!battle_config.pvp_exp && map[sd->bl.m].flag.pvp) // [MouseJstr] + return; // no exp on pvp maps - if(sd->status.guild_id>0) - base_exp-=guild_payexp(sd,base_exp); + if (sd->status.guild_id>0) + base_exp -= guild_payexp(sd,base_exp); + } flag = ((base_exp) ? 1 : 0) | ((job_exp) ? 2 : 0) | ((pc_is_maxbaselv(sd)) ? 4 : 0) | ((pc_is_maxjoblv(sd)) ? 8 : 0); - pc_calcexp(sd, &base_exp, &job_exp, src); + if (!(exp_flag&2)) + pc_calcexp(sd, &base_exp, &job_exp, src); nextb = pc_nextbaseexp(sd); nextj = pc_nextjobexp(sd); @@ -6490,7 +6494,7 @@ int pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int job_exp = MAX_LEVEL_JOB_EXP - sd->status.job_exp; } - if (battle_config.max_exp_gain_rate && (base_exp || job_exp)) { + if (!(exp_flag&2) && battle_config.max_exp_gain_rate && (base_exp || job_exp)) { //Note that this value should never be greater than the original //therefore no overflow checks are needed. [Skotlex] if (nextb > 0) { @@ -6526,14 +6530,40 @@ int pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int } if (flag&1) - clif_displayexp(sd, (flag&4) ? 0 : base_exp, SP_BASEEXP, quest, false); + clif_displayexp(sd, (flag&4) ? 0 : base_exp, SP_BASEEXP, exp_flag&1, false); if (flag&2) - clif_displayexp(sd, (flag&8) ? 0 : job_exp, SP_JOBEXP, quest, false); + clif_displayexp(sd, (flag&8) ? 0 : job_exp, SP_JOBEXP, exp_flag&1, false); if (sd->state.showexp) pc_gainexp_disp(sd, base_exp, nextb, job_exp, nextj, false); +} - return 1; +/** + * Lost Base/Job EXP from a player + * @param sd Player + * @param base_exp Base EXP lost + * @param job_exp Job EXP lost + **/ +void pc_lostexp(struct map_session_data *sd, unsigned int base_exp, unsigned int job_exp) { + + nullpo_retv(sd); + + if (base_exp) { + base_exp = u32min(sd->status.base_exp, base_exp); + sd->status.base_exp -= base_exp; + clif_displayexp(sd, base_exp, SP_BASEEXP, false, true); + clif_updatestatus(sd, SP_BASEEXP); + } + + if (job_exp) { + job_exp = u32min(sd->status.job_exp, job_exp); + sd->status.job_exp -= job_exp; + clif_displayexp(sd, job_exp, SP_JOBEXP, false, true); + clif_updatestatus(sd, SP_JOBEXP); + } + + if (sd->state.showexp) + pc_gainexp_disp(sd, base_exp, pc_nextbaseexp(sd), job_exp, pc_nextjobexp(sd), true); } /** @@ -7586,9 +7616,6 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) if (battle_config.pk_mode && src && src->type==BL_PC) base_penalty *= 2; base_penalty = u32min(sd->status.base_exp, base_penalty); - sd->status.base_exp -= base_penalty; - clif_displayexp(sd, base_penalty, SP_BASEEXP, false, true); - clif_updatestatus(sd,SP_BASEEXP); } } else @@ -7603,16 +7630,13 @@ int pc_dead(struct map_session_data *sd,struct block_list *src) if (battle_config.pk_mode && src && src->type==BL_PC) job_penalty *= 2; job_penalty = u32min(sd->status.job_exp, job_penalty); - sd->status.job_exp -= job_penalty; - clif_displayexp(sd, job_penalty, SP_JOBEXP, false, true); - clif_updatestatus(sd,SP_JOBEXP); } } else job_penalty = 0; - if (sd->state.showexp && (base_penalty || job_penalty)) - pc_gainexp_disp(sd, base_penalty, pc_nextbaseexp(sd), job_penalty, pc_nextjobexp(sd), true); + if (base_penalty || job_penalty) + pc_lostexp(sd, base_penalty, job_penalty); if( zeny_penalty > 0 && !map[sd->bl.m].flag.nozenypenalty) { zeny_penalty = (uint32)( sd->status.zeny * ( zeny_penalty / 10000. ) ); @@ -7932,56 +7956,22 @@ bool pc_setparam(struct map_session_data *sd,int type,int val) break; case SP_BASEEXP: { - unsigned int exp = sd->status.base_exp; - unsigned int next = pc_nextbaseexp(sd); - bool isLost = false; - bool isMax = false; - val = cap_value(val, 0, INT_MAX); - sd->status.base_exp = val; - - if ((unsigned int)val < exp) { // Lost - exp -= val; - isLost = true; - } - else { // Gained - if ((isMax = pc_is_maxbaselv(sd)) && sd->status.base_exp >= MAX_LEVEL_BASE_EXP) - exp = 0; - else - exp = val-exp; - pc_checkbaselevelup(sd); - } - clif_displayexp(sd, isMax ? 0 : exp, SP_BASEEXP, false, isLost); - if (sd->state.showexp) - pc_gainexp_disp(sd, exp, next, 0, pc_nextjobexp(sd), isLost); + if ((unsigned int)val < sd->status.base_exp) // Lost + pc_lostexp(sd, sd->status.base_exp - val, 0); + else // Gained + pc_gainexp(sd, NULL, val - sd->status.base_exp, 0, 2); } - break; + return true; case SP_JOBEXP: { - unsigned int exp = sd->status.job_exp; - unsigned int next = pc_nextjobexp(sd); - bool isLost = false; - bool isMax = false; - val = cap_value(val, 0, INT_MAX); - sd->status.job_exp = val; - - if ((unsigned int)val < exp) { // Lost - exp -= val; - isLost = true; - } - else { // Gained - if ((isMax = pc_is_maxjoblv(sd)) && sd->status.job_exp >= MAX_LEVEL_JOB_EXP) - exp = 0; - else - exp = val-exp; - pc_checkjoblevelup(sd); - } - clif_displayexp(sd, isMax ? 0 : exp, SP_JOBEXP, false, isLost); - if (sd->state.showexp) - pc_gainexp_disp(sd, 0, pc_nextbaseexp(sd), exp, next, isLost); + if ((unsigned int)val < sd->status.job_exp) // Lost + pc_lostexp(sd, 0, sd->status.job_exp - val); + else // Gained + pc_gainexp(sd, NULL, 0, val - sd->status.job_exp, 2); } - break; + return true; case SP_SEX: sd->status.sex = val ? SEX_MALE : SEX_FEMALE; break; diff --git a/src/map/pc.h b/src/map/pc.h index d145fa1584..be3b6cecd6 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -1008,8 +1008,9 @@ bool pc_is_maxbaselv(struct map_session_data *sd); bool pc_is_maxjoblv(struct map_session_data *sd); int pc_checkbaselevelup(struct map_session_data *sd); int pc_checkjoblevelup(struct map_session_data *sd); -int pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int base_exp, unsigned int job_exp, bool quest); +void pc_gainexp(struct map_session_data *sd, struct block_list *src, unsigned int base_exp, unsigned int job_exp, uint8 exp_flag); void pc_gainexp_disp(struct map_session_data *sd, unsigned int base_exp, unsigned int next_base_exp, unsigned int job_exp, unsigned int next_job_exp, bool lost); +void pc_lostexp(struct map_session_data *sd, unsigned int base_exp, unsigned int job_exp); unsigned int pc_nextbaseexp(struct map_session_data *sd); unsigned int pc_nextjobexp(struct map_session_data *sd); int pc_gets_status_point(int); diff --git a/src/map/script.c b/src/map/script.c index 1944dfa33c..b1737c1bf4 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -9641,7 +9641,7 @@ BUILDIN_FUNC(getexp) if (job) job = (int) cap_value(job * bonus, 0, INT_MAX); - pc_gainexp(sd, NULL, base, job, true); + pc_gainexp(sd, NULL, base, job, 1); return SCRIPT_CMD_SUCCESS; } @@ -21082,6 +21082,35 @@ BUILDIN_FUNC(navigateto){ #endif } +/** + * Safety Base/Job EXP addition than using `set BaseExp,n;` or `set JobExp,n;` + * Unlike `getexp` that affected by some adjustments. + * getexp2 ,{,}; + * @author [Cydh] + **/ +BUILDIN_FUNC(getexp2) { + TBL_PC *sd = NULL; + int base_exp = script_getnum(st, 2); + int job_exp = script_getnum(st, 3); + + if (!script_charid2sd(4, sd)) + return SCRIPT_CMD_FAILURE; + + if (base_exp == 0 && job_exp == 0) + return SCRIPT_CMD_SUCCESS; + + if (base_exp > 0) + pc_gainexp(sd, NULL, base_exp, 0, 2); + else if (base_exp < 0) + pc_lostexp(sd, base_exp * -1, 0); + + if (job_exp > 0) + pc_gainexp(sd, NULL, 0, job_exp, 2); + else if (job_exp < 0) + pc_lostexp(sd, 0, job_exp * -1); + return SCRIPT_CMD_SUCCESS; +} + #include "../custom/script.inc" // declarations that were supposed to be exported from npc_chat.c @@ -21649,6 +21678,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(setquestinfo_job,"ii*"), BUILDIN_DEF(opendressroom,"i?"), BUILDIN_DEF(navigateto,"s???????"), + BUILDIN_DEF(getexp2,"ii?"), #include "../custom/script_def.inc" diff --git a/src/map/skill.c b/src/map/skill.c index 616df0cb90..f63391fabd 100755 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -5908,7 +5908,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui heal_get_jobexp = heal_get_jobexp * battle_config.heal_exp / 100; if (heal_get_jobexp <= 0) heal_get_jobexp = 1; - pc_gainexp (sd, bl, 0, heal_get_jobexp, false); + pc_gainexp (sd, bl, 0, heal_get_jobexp, 0); } } break; @@ -5931,12 +5931,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui skill_area_temp[0] = battle_config.exp_cost_redemptio_limit - skill_area_temp[0]; // The actual penalty... if (skill_area_temp[0] > 0 && !map[src->m].flag.noexppenalty && battle_config.exp_cost_redemptio) { //Apply penalty //If total penalty is 1% => reduced 0.2% penalty per each revived player - unsigned int base_penalty = u32min(sd->status.base_exp, (pc_nextbaseexp(sd) * skill_area_temp[0] * battle_config.exp_cost_redemptio / battle_config.exp_cost_redemptio_limit) / 100); - sd->status.base_exp -= base_penalty; - clif_displayexp(sd, base_penalty, SP_BASEEXP, false, true); - clif_updatestatus(sd,SP_BASEEXP); - if (sd->state.showexp) - pc_gainexp_disp(sd, base_penalty, pc_nextbaseexp(sd), 0, pc_nextjobexp(sd), true); + pc_lostexp(sd, u32min(sd->status.base_exp, (pc_nextbaseexp(sd) * skill_area_temp[0] * battle_config.exp_cost_redemptio / battle_config.exp_cost_redemptio_limit) / 100), 0); } status_set_hp(src, 1, 0); status_set_sp(src, 0, 0); @@ -5987,7 +5982,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui if (jexp < 1) jexp = 1; } if(exp > 0 || jexp > 0) - pc_gainexp (sd, bl, exp, jexp, false); + pc_gainexp (sd, bl, exp, jexp, 0); } } } @@ -6106,7 +6101,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui break; case SA_LEVELUP: clif_skill_nodamage(src,bl,skill_id,skill_lv,1); - if (sd && pc_nextbaseexp(sd)) pc_gainexp(sd, NULL, pc_nextbaseexp(sd) * 10 / 100, 0, false); + if (sd && pc_nextbaseexp(sd)) + pc_gainexp(sd, NULL, pc_nextbaseexp(sd) * 10 / 100, 0, 0); break; case SA_INSTANTDEATH: clif_skill_nodamage(src,bl,skill_id,skill_lv,1); @@ -9648,14 +9644,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui break; case LG_INSPIRATION: - if( sd && !map[sd->bl.m].flag.noexppenalty && battle_config.exp_cost_inspiration ) { - unsigned int base_penalty = u32min(sd->status.base_exp, pc_nextbaseexp(sd) * battle_config.exp_cost_inspiration / 100); // 1% penalty. - sd->status.base_exp -= base_penalty; - clif_displayexp(sd, base_penalty, SP_BASEEXP, false, true); - clif_updatestatus(sd,SP_BASEEXP); - if (sd->state.showexp) - pc_gainexp_disp(sd, base_penalty, pc_nextbaseexp(sd), 0, pc_nextjobexp(sd), true); - } + if( sd && !map[sd->bl.m].flag.noexppenalty && battle_config.exp_cost_inspiration ) + pc_lostexp(sd, u32min(sd->status.base_exp, pc_nextbaseexp(sd) * battle_config.exp_cost_inspiration / 100), 0); // 1% penalty. clif_skill_nodamage(bl,src,skill_id,skill_lv, sc_start(src,bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv))); break; case SR_CURSEDCIRCLE: