diff --git a/db/constants.conf b/db/constants.conf
index 5938f9fb5a7..c159ed707ba 100644
--- a/db/constants.conf
+++ b/db/constants.conf
@@ -475,6 +475,8 @@ constants_db: {
bSubDefEle: 2063
bMagicSubDefEle: 2064
bStateNoRecoverRace: 2065
+ bSubSkill: 2066
+ bDropAddRace: 2067
comment__: "Equip index"
/* reference to script.c::script_defaults():equip[] array used for easy-conversion */
diff --git a/doc/item_bonus.md b/doc/item_bonus.md
index 4eba4e3069e..da81f77f859 100644
--- a/doc/item_bonus.md
+++ b/doc/item_bonus.md
@@ -225,6 +225,7 @@ bonus bCritAtkRate,`n`; | Increase critical damage by +`n`%
bonus bNoWeaponDamage,`n`; | Prevents from receiving `n`% physical damage
bonus bNoMagicDamage,`n`; | Prevents from receiving `n`% magical effect (Attack, Healing, Support spells are all blocked)
bonus bNoMiscDamage,`n`; | Adds `n`% reduction to received misc damage
+bonus2 bSubSkill,`sk`,`n`; | Reduce damage received from `sk` skill by `n`%
Heal | Description
:-------------------------------- | :-------------------------
@@ -419,6 +420,7 @@ bonus bAddMonsterDropChainItem,`ic`; | Able to get Item of chain `ic` when
bonus2 bAddMonsterDropChainItem,`ic`,`r`; | Able to get item of chain `ic` when you kill a monster of race `r`
bonus2 bGetZenyNum,`x`,`n`; | When killing a monster, there is a `n`% chance of gaining 1~x zeny (only the highest among all is applied).
bonus2 bAddGetZenyNum,`x`,`n`; | When killing a monster, there is a `n`% chance of gaining 1~x zeny (Stackable)
x:
< 0: Max Zeny gain is `(-x*monster_level)`
+bonus2 bDropAddRace,`r`,`n`; | Increase item drop rate by `n`% when you kill a monster of race `r`
Misc effects | Description
:------------------------------------- | :-------------------------
diff --git a/src/map/battle.c b/src/map/battle.c
index 0b3d877f373..dd761ce37f6 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -3757,6 +3757,7 @@ static struct Damage battle_calc_magic_attack(struct block_list *src, struct blo
flag.imdef = (nk&NK_IGNORE_DEF)? 1 : 0;
sd = BL_CAST(BL_PC, src);
+ struct map_session_data *tsd = BL_CAST(BL_PC, target);
sc = status->get_sc(src);
@@ -3989,6 +3990,8 @@ static struct Damage battle_calc_magic_attack(struct block_list *src, struct blo
))
flag.imdef = 1;
}
+ if (tsd && (i = pc->sub_skillatk_bonus(tsd, skill_id)))
+ ad.damage -= ad.damage * i / 100;
ad.damage = battle->calc_defense(BF_MAGIC, src, target, skill_id, skill_lv, ad.damage, flag.imdef, 0);
@@ -4436,6 +4439,8 @@ static struct Damage battle_calc_misc_attack(struct block_list *src, struct bloc
}
if (sd && (i = pc->skillatk_bonus(sd, rskill)) != 0)
md.damage += md.damage*i/100;
+ if (tsd && (i = pc->sub_skillatk_bonus(tsd, rskill)) != 0)
+ md.damage -= md.damage * i / 100;
}
if( (i = battle->adjust_skill_damage(src->m,skill_id)) )
md.damage = md.damage * i / 100;
@@ -5577,6 +5582,9 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl
}
}
+ if (tsd && skill_id && (i = pc->sub_skillatk_bonus(tsd, skill_id)))
+ ATK_ADDRATE(-i);
+
if((!flag.idef || !flag.idef2)
#ifdef RENEWAL
&& (!flag.distinct || flag.tdef)
diff --git a/src/map/map.h b/src/map/map.h
index 2851cfa19aa..3212b100384 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -773,6 +773,8 @@ enum status_point_types { //we better clean up this enum and change it name [Hem
SP_SUB_DEF_ELE = 2063,
SP_MAGIC_SUB_DEF_ELE = 2064,
SP_STATE_NO_RECOVER_RACE = 2065,
+ SP_SUB_SKILL = 2066,
+ SP_ADD_DROP_RACE = 2067,
#ifndef SP_LAST_KNOWN
SP_LAST_KNOWN
diff --git a/src/map/mob.c b/src/map/mob.c
index ab4caa6b7e8..6bef7c19309 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -2723,51 +2723,64 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type)
if ( !(it = itemdb->exists(md->db->dropitem[i].nameid)) )
continue;
drop_rate = md->db->dropitem[i].p;
- if (drop_rate <= 0) {
- if (battle_config.drop_rate0item)
- continue;
- drop_rate = 1;
- }
// change drops depending on monsters size [Valaris]
- if (battle_config.mob_size_influence)
- {
+ if (battle_config.mob_size_influence) {
if (md->special_state.size == SZ_MEDIUM && drop_rate >= 2)
drop_rate /= 2;
else if( md->special_state.size == SZ_BIG)
drop_rate *= 2;
}
- if (src) {
+ if (src != NULL) {
//Drops affected by luk as a fixed increase [Valaris]
if (battle_config.drops_by_luk)
- drop_rate += status_get_luk(src)*battle_config.drops_by_luk/100;
+ drop_rate += status_get_luk(src) * battle_config.drops_by_luk / 100;
+
//Drops affected by luk as a % increase [Skotlex]
if (battle_config.drops_by_luk2)
- drop_rate += (int)(0.5+drop_rate*status_get_luk(src)*battle_config.drops_by_luk2/10000.);
+ drop_rate += (int)(0.5 + drop_rate * status_get_luk(src) * battle_config.drops_by_luk2 / 10000.);
+
+ if (sd != NULL) {
+ int drop_rate_bonus = 100;
+
+ // When PK Mode is enabled, increase item drop rate bonus of each items by 25% when there is a 20 level difference between the player and the monster.[KeiKun]
+ if (battle_config.pk_mode && (md->level - sd->status.base_level >= 20))
+ drop_rate_bonus += 25; // flat 25% bonus
+
+ drop_rate_bonus += sd->dropaddrace[md->status.race] + (is_boss(src) ? sd->dropaddrace[RC_BOSS] : sd->dropaddrace[RC_NONBOSS]); // bonus2 bDropAddRace[KeiKun]
+
+ if (sd->sc.data[SC_CASH_RECEIVEITEM] != NULL) // Increase drop rate if user has SC_CASH_RECEIVEITEM
+ drop_rate_bonus += sd->sc.data[SC_CASH_RECEIVEITEM]->val1;
+
+ if (sd->sc.data[SC_OVERLAPEXPUP] != NULL)
+ drop_rate_bonus += sd->sc.data[SC_OVERLAPEXPUP]->val2;
+
+ drop_rate = (int)(0.5 + drop_rate * drop_rate_bonus / 100.);
+
+ // Limit drop rate, default: 90%
+ drop_rate = max(drop_rate, 90000);
+ }
}
- if (sd && battle_config.pk_mode &&
- md->level - sd->status.base_level >= 20)
- drop_rate = (int)(drop_rate*1.25); // pk_mode increase drops if 20 level difference [Valaris]
-
- // Increase drop rate if user has SC_CASH_RECEIVEITEM
- if (sd && sd->sc.data[SC_CASH_RECEIVEITEM]) // now rig the drop rate to never be over 90% unless it is originally >90%.
- drop_rate = max(drop_rate, cap_value((int)(0.5 + drop_rate * (sd->sc.data[SC_CASH_RECEIVEITEM]->val1) / 100.), 0, 9000));
- if (sd && sd->sc.data[SC_OVERLAPEXPUP])
- drop_rate = max(drop_rate, cap_value((int)(0.5 + drop_rate * (sd->sc.data[SC_OVERLAPEXPUP]->val2) / 100.), 0, 9000));
+
#ifdef RENEWAL_DROP
- if( drop_modifier != 100 ) {
+ if (drop_modifier != 100) {
drop_rate = drop_rate * drop_modifier / 100;
- if( drop_rate < 1 )
+ if (drop_rate < 1)
drop_rate = 1;
}
#endif
- if( sd && sd->status.mod_drop != 100 ) {
+ if (sd != NULL && sd->status.mod_drop != 100) {
drop_rate = drop_rate * sd->status.mod_drop / 100;
- if( drop_rate < 1 )
+ if (drop_rate < 1)
drop_rate = 1;
}
+ if (battle_config.drop_rate0item)
+ drop_rate = max(drop_rate, 0);
+ else
+ drop_rate = max(drop_rate, 1);
+
// attempt to drop the item
if (rnd() % 10000 >= drop_rate)
continue;
diff --git a/src/map/pc.c b/src/map/pc.c
index 627467bb6b8..cde4520b510 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -3839,6 +3839,35 @@ static int pc_bonus2(struct map_session_data *sd, int type, int type2, int val)
}
break;
#endif
+ case SP_SUB_SKILL:
+ if (sd->state.lr_flag == 2)
+ break;
+ ARR_FIND(0, ARRAYLENGTH(sd->subskill), i, sd->subskill[i].id == 0 || sd->subskill[i].id == type2);
+ if (i == ARRAYLENGTH(sd->subskill)) {
+ ShowDebug("script->run: bonus2 bSubSkill reached it's limit (%d skills per character), bonus skill %d (+%d%%) lost.\n",
+ ARRAYLENGTH(sd->subskill), type2, val);
+ break;
+ }
+ if (sd->subskill[i].id == type2) {
+ sd->subskill[i].val += val;
+ } else {
+ sd->subskill[i].id = type2;
+ sd->subskill[i].val = val;
+ }
+ break;
+ case SP_ADD_DROP_RACE:
+ {
+ uint32 race_mask = map->race_id2mask(type2);
+ if (race_mask == RCMASK_NONE) {
+ ShowWarning("pc_bonus2: SP_ADD_DROP_RACE: Invalid Race (%d)\n", type2);
+ break;
+ }
+ if (sd->state.lr_flag == 2)
+ break;
+ BONUS_FOREACH_RCARRAY_FROMMASK(i, race_mask)
+ sd->dropaddrace[i] += val;
+ }
+ break;
default:
ShowWarning("pc_bonus2: unknown type %d %d %d!\n",type,type2,val);
Assert_report(0);
@@ -7890,6 +7919,19 @@ static int pc_skillatk_bonus(struct map_session_data *sd, uint16 skill_id)
return bonus;
}
+static int pc_sub_skillatk_bonus(struct map_session_data *sd, uint16 skill_id)
+{
+ int i, bonus = 0;
+ nullpo_ret(sd);
+
+ ARR_FIND(0, ARRAYLENGTH(sd->subskill), i, sd->subskill[i].id == skill_id);
+
+ if (i < ARRAYLENGTH(sd->subskill))
+ bonus = sd->subskill[i].val;
+
+ return bonus;
+}
+
static int pc_skillheal_bonus(struct map_session_data *sd, uint16 skill_id)
{
int i, bonus = sd->bonus.add_heal_rate;
@@ -12867,6 +12909,7 @@ void pc_defaults(void)
pc->autocast_remove = pc_autocast_remove;
pc->skillatk_bonus = pc_skillatk_bonus;
+ pc->sub_skillatk_bonus = pc_sub_skillatk_bonus;
pc->skillheal_bonus = pc_skillheal_bonus;
pc->skillheal2_bonus = pc_skillheal2_bonus;
diff --git a/src/map/pc.h b/src/map/pc.h
index e65d4146110..197b406695b 100644
--- a/src/map/pc.h
+++ b/src/map/pc.h
@@ -368,13 +368,14 @@ BEGIN_ZEROED_BLOCK; // this block will be globally zeroed at the beginning of st
#ifdef RENEWAL
int race_tolerance[RC_MAX];
#endif
+ int dropaddrace[RC_MAX];
struct s_autospell autospell[15], autospell2[15], autospell3[15];
struct s_addeffect addeff[MAX_PC_BONUS], addeff2[MAX_PC_BONUS];
struct s_addeffectonskill addeff3[MAX_PC_BONUS];
struct { //skillatk raises bonus dmg% of skills, skillheal increases heal%, skillblown increases bonus blewcount for some skills.
unsigned int id;
int val;
- } skillatk[MAX_PC_BONUS], skillusesprate[MAX_PC_BONUS], skillusesp[MAX_PC_BONUS], skillheal[5], skillheal2[5], skillblown[MAX_PC_BONUS], skillcast[MAX_PC_BONUS], skillcooldown[MAX_PC_BONUS], skillfixcast[MAX_PC_BONUS], skillvarcast[MAX_PC_BONUS], skillfixcastrate[MAX_PC_BONUS];
+ } skillatk[MAX_PC_BONUS], skillusesprate[MAX_PC_BONUS], skillusesp[MAX_PC_BONUS], skillheal[5], skillheal2[5], skillblown[MAX_PC_BONUS], skillcast[MAX_PC_BONUS], skillcooldown[MAX_PC_BONUS], skillfixcast[MAX_PC_BONUS], skillvarcast[MAX_PC_BONUS], skillfixcastrate[MAX_PC_BONUS], subskill[MAX_PC_BONUS];
struct {
int value;
int rate;
@@ -1063,6 +1064,7 @@ END_ZEROED_BLOCK; /* End */
void (*autocast_remove) (struct map_session_data *sd, enum autocast_type type, int skill_id, int skill_lv);
int (*skillatk_bonus) (struct map_session_data *sd, uint16 skill_id);
+ int (*sub_skillatk_bonus) (struct map_session_data *sd, uint16 skill_id);
int (*skillheal_bonus) (struct map_session_data *sd, uint16 skill_id);
int (*skillheal2_bonus) (struct map_session_data *sd, uint16 skill_id);
diff --git a/src/map/script.c b/src/map/script.c
index 568278e9d9d..28a378e6e3e 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -10554,6 +10554,7 @@ static BUILDIN(bonus)
case SP_VARCASTRATE:
case SP_FIXCASTRATE:
case SP_SKILL_USE_SP:
+ case SP_SUB_SKILL:
// these bonuses support skill names
if (script_isstringtype(st, 3)) {
val1 = skill->name2id(script_getstr(st, 3));
diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc
index 089120c3946..f45a85d3d85 100644
--- a/src/plugins/HPMHooking/HPMHooking.Defs.inc
+++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc
@@ -6268,6 +6268,8 @@ typedef void (*HPMHOOK_pre_pc_autocast_remove) (struct map_session_data **sd, en
typedef void (*HPMHOOK_post_pc_autocast_remove) (struct map_session_data *sd, enum autocast_type type, int skill_id, int skill_lv);
typedef int (*HPMHOOK_pre_pc_skillatk_bonus) (struct map_session_data **sd, uint16 *skill_id);
typedef int (*HPMHOOK_post_pc_skillatk_bonus) (int retVal___, struct map_session_data *sd, uint16 skill_id);
+typedef int (*HPMHOOK_pre_pc_sub_skillatk_bonus) (struct map_session_data **sd, uint16 *skill_id);
+typedef int (*HPMHOOK_post_pc_sub_skillatk_bonus) (int retVal___, struct map_session_data *sd, uint16 skill_id);
typedef int (*HPMHOOK_pre_pc_skillheal_bonus) (struct map_session_data **sd, uint16 *skill_id);
typedef int (*HPMHOOK_post_pc_skillheal_bonus) (int retVal___, struct map_session_data *sd, uint16 skill_id);
typedef int (*HPMHOOK_pre_pc_skillheal2_bonus) (struct map_session_data **sd, uint16 *skill_id);
diff --git a/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc b/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc
index 4faa49fc8e3..7b17eb3c722 100644
--- a/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc
+++ b/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc
@@ -4846,6 +4846,8 @@ struct {
struct HPMHookPoint *HP_pc_autocast_remove_post;
struct HPMHookPoint *HP_pc_skillatk_bonus_pre;
struct HPMHookPoint *HP_pc_skillatk_bonus_post;
+ struct HPMHookPoint *HP_pc_sub_skillatk_bonus_pre;
+ struct HPMHookPoint *HP_pc_sub_skillatk_bonus_post;
struct HPMHookPoint *HP_pc_skillheal_bonus_pre;
struct HPMHookPoint *HP_pc_skillheal_bonus_post;
struct HPMHookPoint *HP_pc_skillheal2_bonus_pre;
@@ -11963,6 +11965,8 @@ struct {
int HP_pc_autocast_remove_post;
int HP_pc_skillatk_bonus_pre;
int HP_pc_skillatk_bonus_post;
+ int HP_pc_sub_skillatk_bonus_pre;
+ int HP_pc_sub_skillatk_bonus_post;
int HP_pc_skillheal_bonus_pre;
int HP_pc_skillheal_bonus_post;
int HP_pc_skillheal2_bonus_pre;
diff --git a/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc b/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc
index 281ac9096cd..94ba681032b 100644
--- a/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc
+++ b/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc
@@ -2481,6 +2481,7 @@ struct HookingPointData HookingPoints[] = {
{ HP_POP(pc->autocast_set_current, HP_pc_autocast_set_current) },
{ HP_POP(pc->autocast_remove, HP_pc_autocast_remove) },
{ HP_POP(pc->skillatk_bonus, HP_pc_skillatk_bonus) },
+ { HP_POP(pc->sub_skillatk_bonus, HP_pc_sub_skillatk_bonus) },
{ HP_POP(pc->skillheal_bonus, HP_pc_skillheal_bonus) },
{ HP_POP(pc->skillheal2_bonus, HP_pc_skillheal2_bonus) },
{ HP_POP(pc->damage, HP_pc_damage) },
diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc
index 5a8347df494..2a447844807 100644
--- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc
+++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc
@@ -64456,6 +64456,33 @@ int HP_pc_skillatk_bonus(struct map_session_data *sd, uint16 skill_id) {
}
return retVal___;
}
+int HP_pc_sub_skillatk_bonus(struct map_session_data *sd, uint16 skill_id) {
+ int hIndex = 0;
+ int retVal___ = 0;
+ if (HPMHooks.count.HP_pc_sub_skillatk_bonus_pre > 0) {
+ int (*preHookFunc) (struct map_session_data **sd, uint16 *skill_id);
+ *HPMforce_return = false;
+ for (hIndex = 0; hIndex < HPMHooks.count.HP_pc_sub_skillatk_bonus_pre; hIndex++) {
+ preHookFunc = HPMHooks.list.HP_pc_sub_skillatk_bonus_pre[hIndex].func;
+ retVal___ = preHookFunc(&sd, &skill_id);
+ }
+ if (*HPMforce_return) {
+ *HPMforce_return = false;
+ return retVal___;
+ }
+ }
+ {
+ retVal___ = HPMHooks.source.pc.sub_skillatk_bonus(sd, skill_id);
+ }
+ if (HPMHooks.count.HP_pc_sub_skillatk_bonus_post > 0) {
+ int (*postHookFunc) (int retVal___, struct map_session_data *sd, uint16 skill_id);
+ for (hIndex = 0; hIndex < HPMHooks.count.HP_pc_sub_skillatk_bonus_post; hIndex++) {
+ postHookFunc = HPMHooks.list.HP_pc_sub_skillatk_bonus_post[hIndex].func;
+ retVal___ = postHookFunc(retVal___, sd, skill_id);
+ }
+ }
+ return retVal___;
+}
int HP_pc_skillheal_bonus(struct map_session_data *sd, uint16 skill_id) {
int hIndex = 0;
int retVal___ = 0;