Updates behavior of statuses that reduce damage (#3129)

* Fixes #2671.
* Restructured battle_calc_damage to utilize battle_status_block_damage for statuses that block damage.
* Renewal Safety Wall has a hit count tracker, same as pre-renewal.
* Updated renewal Safety Wall HP formula.
* Safety Wall will now check for Land Protector before attempting to consume a Blue Gemstone.
* Moves Kyrie and other similar type status changes that may or may not block damage to the top of the list of priority.
* Adds a short delay to Parrying for the next attack when damage is successfully blocked.
* Fixes Millennium Shields remaining shields getting destroyed when damage was over 1000.
* Fixes Millennium Shields disappearing on map change.
Thanks to @Paoly28 and @mrjnumber1, @cydh, @ecdarreola, @InusualZ, and @Badarosk0!
This commit is contained in:
Aleos 2020-07-31 08:34:04 -04:00 committed by GitHub
parent 1c6270f96a
commit 9991b09837
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 313 additions and 279 deletions

View File

@ -9404,6 +9404,7 @@ Body:
Time: 55000
- Level: 10
Time: 60000
Duration2: 1000
Requires:
SpCost: 50
Weapon:

View File

@ -9711,6 +9711,7 @@ Body:
Time: 55000
- Level: 10
Time: 60000
Duration2: 1000
Requires:
SpCost: 50
Weapon:

View File

@ -1033,54 +1033,209 @@ static void battle_absorb_damage(struct block_list *bl, struct Damage *d) {
}
/**
* Check Safety Wall and Pneuma effect.
* Maybe expand this to move checks the target's SC from battle_calc_damage?
* @param src Attacker
* @param target Target of attack
* @param sc STatus Change
* @param d Damage data
* @param damage Damage received
* @param skill_id
* @param skill_lv
* @return True:Damage inflicted, False:Missed
* Check for active statuses that block damage
* @param src: Attacker
* @param target: Target of attack
* @param sc: Status Change data
* @param d: Damage data
* @param damage: Damage received
* @param skill_id: Skill ID
* @param skill_lv: Skill level
* @return True: Damage inflicted, False: Missed
**/
bool battle_check_sc(struct block_list *src, struct block_list *target, struct status_change *sc, struct Damage *d, int64 damage, uint16 skill_id, uint16 skill_lv) {
if (!sc)
bool battle_status_block_damage(struct block_list *src, struct block_list *target, struct status_change *sc, struct Damage *d, int64 damage, uint16 skill_id, uint16 skill_lv) {
if (!src || !target || !sc || !d)
return true;
if (sc->data[SC_SAFETYWALL] && (d->flag&(BF_SHORT|BF_MAGIC)) == BF_SHORT) {
struct skill_unit_group* group = skill_id2group(sc->data[SC_SAFETYWALL]->val3);
uint16 skill_id_val = sc->data[SC_SAFETYWALL]->val2;
status_change_entry *sce;
int flag = d->flag;
// SC Types that must be first because they may or may not block damage
if ((sce = sc->data[SC_KYRIE]) && damage > 0) {
sce->val2 -= static_cast<int>(cap_value(damage, INT_MIN, INT_MAX));
if (flag & BF_WEAPON || skill_id == TF_THROWSTONE) {
if (sce->val2 >= 0)
damage = 0;
else
damage = -sce->val2;
}
if ((--sce->val3) <= 0 || (sce->val2 <= 0) || skill_id == AL_HOLYLIGHT)
status_change_end(target, SC_KYRIE, INVALID_TIMER);
}
if ((sce = sc->data[SC_P_ALTER]) && damage > 0) {
clif_specialeffect(target, EF_GUARD, AREA);
sce->val3 -= static_cast<int>(cap_value(damage, INT_MIN, INT_MAX));
if (sce->val3 >= 0)
damage = 0;
else
damage = -sce->val3;
if (sce->val3 <= 0)
status_change_end(target, SC_P_ALTER, INVALID_TIMER);
}
if ((sce = sc->data[SC_TUNAPARTY]) && damage > 0) {
sce->val2 -= static_cast<int>(cap_value(damage, INT_MIN, INT_MAX));
if (sce->val2 >= 0)
damage = 0;
else
damage = -sce->val2;
if (sce->val2 <= 0)
status_change_end(target, SC_TUNAPARTY, INVALID_TIMER);
}
if ((sce = sc->data[SC_DIMENSION1]) && damage > 0) {
sce->val2 -= static_cast<int>(cap_value(damage, INT_MIN, INT_MAX));
if (sce->val2 >= 0)
damage = 0;
else
damage = -sce->val2;
if (sce->val2 <= 0)
status_change_end(target, SC_DIMENSION1, INVALID_TIMER);
}
if ((sce = sc->data[SC_DIMENSION2]) && damage > 0) {
sce->val2 -= static_cast<int>(cap_value(damage, INT_MIN, INT_MAX));
if (sce->val2 >= 0)
damage = 0;
else
damage = -sce->val2;
if (sce->val2 <= 0)
status_change_end(target, SC_DIMENSION2, INVALID_TIMER);
}
if (damage == 0)
return false;
// ATK_BLOCK Type
if ((sce = sc->data[SC_SAFETYWALL]) && (flag&(BF_SHORT | BF_MAGIC)) == BF_SHORT) {
skill_unit_group *group = skill_id2group(sce->val3);
if (group) {
if (skill_id_val == MH_STEINWAND) {
if (--group->val2 <= 0)
d->dmg_lv = ATK_BLOCK;
switch (sce->val2) {
case MG_SAFETYWALL:
if (--group->val2 <= 0) {
skill_delunitgroup(group);
d->dmg_lv = ATK_BLOCK;
if( (group->val3 - damage) > 0 )
group->val3 -= (int)cap_value(damage, INT_MIN, INT_MAX);
break;
}
#ifdef RENEWAL
if (group->val3 - damage > 0)
group->val3 -= static_cast<int>(cap_value(damage, INT_MIN, INT_MAX));
else
skill_delunitgroup(group);
return false;
}
//in RE, SW possesses a lifetime equal to group val2, (3x caster hp, or homon formula)
d->dmg_lv = ATK_BLOCK;
#ifdef RENEWAL
if ( ( group->val2 - damage) > 0 ) {
group->val2 -= (int)cap_value(damage, INT_MIN, INT_MAX);
} else
skill_delunitgroup(group);
return false;
#else
if (--group->val2 <= 0)
skill_delunitgroup(group);
return false;
#endif
break;
case MH_STEINWAND:
if (--group->val2 <= 0) {
skill_delunitgroup(group);
break;
}
if (group->val3 - damage > 0)
group->val3 -= static_cast<int>(cap_value(damage, INT_MIN, INT_MAX));
else
skill_delunitgroup(group);
break;
}
return false;
}
status_change_end(target, SC_SAFETYWALL, INVALID_TIMER);
}
if (sc->data[SC_NEUTRALBARRIER] && ((d->flag&(BF_LONG|BF_MAGIC)) == BF_LONG
if ((sc->data[SC_PNEUMA] && (flag&(BF_MAGIC | BF_LONG)) == BF_LONG) ||
#ifdef RENEWAL
(sc->data[SC_BASILICA_CELL]
#else
(sc->data[SC_BASILICA]
#endif
&& !status_bl_has_mode(src, MD_STATUS_IMMUNE) && skill_id != SP_SOULEXPLOSION) ||
(sc->data[SC_ZEPHYR] && !(flag&BF_MAGIC && skill_id) && !(skill_get_inf(skill_id)&(INF_GROUND_SKILL | INF_SELF_SKILL))) ||
sc->data[SC__MANHOLE] ||
sc->data[SC_KINGS_GRACE] ||
sc->data[SC_GRAVITYCONTROL]
)
{
d->dmg_lv = ATK_BLOCK;
return false;
}
if (sc->data[SC_WHITEIMPRISON]) { // Gravitation and Pressure do damage without removing the effect
if (skill_id == MG_NAPALMBEAT ||
skill_id == MG_SOULSTRIKE ||
skill_id == WL_SOULEXPANSION ||
(skill_id && skill_get_ele(skill_id, skill_lv) == ELE_GHOST) ||
(skill_id == 0 && (status_get_status_data(src))->rhw.ele == ELE_GHOST))
{
if (skill_id == WL_SOULEXPANSION)
damage <<= 1; // If used against a player in White Imprison, the skill deals double damage.
status_change_end(target, SC_WHITEIMPRISON, INVALID_TIMER); // Those skills do damage and removes effect
} else {
d->dmg_lv = ATK_BLOCK;
return false;
}
}
if ((sce = sc->data[SC_WEAPONBLOCKING]) && flag&(BF_SHORT | BF_WEAPON) && rnd() % 100 < sce->val2) {
clif_skill_nodamage(target, src, GC_WEAPONBLOCKING, sce->val1, 1);
sc_start(src, target, SC_WEAPONBLOCK_ON, 100, src->id, skill_get_time2(GC_WEAPONBLOCKING, sce->val1));
d->dmg_lv = ATK_BLOCK;
return false;
}
if ((sce = sc->data[SC_MILLENNIUMSHIELD]) && sce->val2 > 0 && damage > 0) {
sce->val3 -= static_cast<int>(cap_value(damage, INT_MIN, INT_MAX)); // absorb damage
d->dmg_lv = ATK_BLOCK;
if (sce->val3 <= 0) { // Shield Down
sce->val2--;
if (sce->val2 > 0) {
clif_millenniumshield(target, sce->val2);
sce->val3 = 1000; // Next shield
} else
status_change_end(target, SC_MILLENNIUMSHIELD, INVALID_TIMER); // All shields down
status_change_start(src, target, SC_STUN, 10000, 0, 0, 0, 0, 1000, SCSTART_NOTICKDEF);
}
return false;
}
// ATK_MISS Type
if ((sce = sc->data[SC_AUTOGUARD]) && flag&BF_WEAPON && rnd() % 100 < sce->val2 && !skill_get_inf2(skill_id, INF2_IGNOREAUTOGUARD)) {
status_change_entry *sce_d = sc->data[SC_DEVOTION];
block_list *d_bl;
int delay;
// different delay depending on skill level [celest]
if (sce->val1 <= 5)
delay = 300;
else if (sce->val1 > 5 && sce->val1 <= 9)
delay = 200;
else
delay = 100;
map_session_data *sd = map_id2sd(target->id);
if (sd && pc_issit(sd))
pc_setstand(sd, true);
if (sce_d && (d_bl = map_id2bl(sce_d->val1)) &&
((d_bl->type == BL_MER && ((TBL_MER*)d_bl)->master && ((TBL_MER*)d_bl)->master->bl.id == target->id) ||
(d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce_d->val2] == target->id)) &&
check_distance_bl(target, d_bl, sce_d->val3))
{ //If player is target of devotion, show guard effect on the devotion caster rather than the target
clif_skill_nodamage(d_bl, d_bl, CR_AUTOGUARD, sce->val1, 1);
unit_set_walkdelay(d_bl, gettick(), delay, 1);
d->dmg_lv = ATK_MISS;
return false;
} else {
clif_skill_nodamage(target, target, CR_AUTOGUARD, sce->val1, 1);
unit_set_walkdelay(target, gettick(), delay, 1);
if (sc->data[SC_SHRINK] && rnd() % 100 < 5 * sce->val1)
skill_blown(target, src, skill_get_blewcount(CR_SHRINK, 1), -1, BLOWN_NONE);
d->dmg_lv = ATK_MISS;
return false;
}
}
if (sc->data[SC_NEUTRALBARRIER] && ((flag&(BF_LONG|BF_MAGIC)) == BF_LONG
#ifndef RENEWAL
|| skill_id == CR_ACIDDEMONSTRATION
#endif
@ -1089,10 +1244,79 @@ bool battle_check_sc(struct block_list *src, struct block_list *target, struct s
return false;
}
if( sc->data[SC_PNEUMA] && (d->flag&(BF_MAGIC|BF_LONG)) == BF_LONG ) {
d->dmg_lv = ATK_BLOCK;
// ATK_DEF Type
if ((sce = sc->data[SC_LIGHTNINGWALK]) && flag&BF_LONG && rnd() % 100 < sce->val1) {
const int dx[8] = { 0,-1,-1,-1,0,1,1,1 };
const int dy[8] = { 1,1,0,-1,-1,-1,0,1 };
uint8 dir = map_calc_dir(target, src->x, src->y);
if (unit_movepos(target, src->x - dx[dir], src->y - dy[dir], 1, 1)) {
clif_blown(target);
unit_setdir(target, dir);
}
d->dmg_lv = ATK_DEF;
status_change_end(target, SC_LIGHTNINGWALK, INVALID_TIMER);
return false;
}
// Other
if ((sc->data[SC_HERMODE] && flag&BF_MAGIC) ||
(sc->data[SC_TATAMIGAESHI] && (flag&(BF_MAGIC | BF_LONG)) == BF_LONG) ||
(sc->data[SC_MEIKYOUSISUI] && rnd() % 100 < 40)) // custom value
return false;
if ((sce = sc->data[SC_PARRYING]) && flag&BF_WEAPON && skill_id != WS_CARTTERMINATION && rnd() % 100 < sce->val2) {
clif_skill_nodamage(target, target, LK_PARRYING, sce->val1, 1);
unit_data *ud = unit_bl2ud(target);
if (ud) // Delay the next attack
ud->attackabletime = gettick() + skill_get_time2(LK_PARRYING, sce->val1);
return false;
}
if (sc->data[SC_DODGE] && (flag&BF_LONG || sc->data[SC_SPURT]) && rnd() % 100 < 20) {
map_session_data *sd = map_id2sd(target->id);
if (sd && pc_issit(sd))
pc_setstand(sd, true); //Stand it to dodge.
clif_skill_nodamage(target, target, TK_DODGE, 1, 1);
sc_start4(src, target, SC_COMBO, 100, TK_JUMPKICK, src->id, 1, 0, 2000);
return false;
}
if ((sce = sc->data[SC_KAUPE]) && rnd() % 100 < sce->val2) { //Kaupe blocks damage (skill or otherwise) from players, mobs, homuns, mercenaries.
clif_specialeffect(target, EF_STORMKICK4, AREA);
//Shouldn't end until Breaker's non-weapon part connects.
#ifndef RENEWAL
if (skill_id != ASC_BREAKER || !(flag&BF_WEAPON))
#endif
if (--sce->val3 <= 0) //We make it work like Safety Wall, even though it only blocks 1 time.
status_change_end(target, SC_KAUPE, INVALID_TIMER);
return false;
}
if (flag&BF_MAGIC && (sce = sc->data[SC_PRESTIGE]) && rnd() % 100 < sce->val2) {
clif_specialeffect(target, EF_STORMKICK4, AREA); // Still need confirm it.
return false;
}
if (((sce = sc->data[SC_UTSUSEMI]) || sc->data[SC_BUNSINJYUTSU]) && flag&BF_WEAPON && !skill_get_inf2(skill_id, INF2_IGNORECICADA)) {
skill_additional_effect(src, target, skill_id, skill_lv, flag, ATK_BLOCK, gettick());
if (!status_isdead(src))
skill_counter_additional_effect(src, target, skill_id, skill_lv, flag, gettick());
if (sce) {
clif_specialeffect(target, EF_STORMKICK4, AREA);
skill_blown(src, target, sce->val3, -1, BLOWN_NONE);
}
//Both need to be consumed if they are active.
if (sce && --sce->val2 <= 0)
status_change_end(target, SC_UTSUSEMI, INVALID_TIMER);
if ((sce = sc->data[SC_BUNSINJYUTSU]) && --sce->val2 <= 0)
status_change_end(target, SC_BUNSINJYUTSU, INVALID_TIMER);
return false;
}
return true;
}
@ -1111,7 +1335,7 @@ bool battle_check_sc(struct block_list *src, struct block_list *target, struct s
int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damage *d,int64 damage,uint16 skill_id,uint16 skill_lv)
{
struct map_session_data *sd = NULL;
struct status_change *sc, *tsc;
struct status_change *sc;
struct status_change_entry *sce;
int div_ = d->div_, flag = d->flag;
@ -1164,162 +1388,16 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam
if (skill_id == SJ_NOVAEXPLOSING && !(sc && (sc->data[SC_SAFETYWALL] || sc->data[SC_MILLENNIUMSHIELD])))
return damage;
if( sc && sc->count ) { // SC_* that reduce damage to 0.
#ifdef RENEWAL
if (sc->data[SC_BASILICA_CELL]
#else
if (sc->data[SC_BASILICA]
#endif
&& !status_bl_has_mode(src,MD_STATUS_IMMUNE) && skill_id != SP_SOULEXPLOSION ) {
d->dmg_lv = ATK_BLOCK;
return 0;
}
if( sc->data[SC_WHITEIMPRISON] ) { // Pre-Renewal: Gravitation and Pressure do damage without removing the effect
if( skill_id == MG_NAPALMBEAT ||
skill_id == MG_SOULSTRIKE ||
skill_id == WL_SOULEXPANSION ||
(skill_id && skill_get_ele(skill_id, skill_lv) == ELE_GHOST) ||
(!skill_id && (status_get_status_data(src))->rhw.ele == ELE_GHOST) )
{
if( skill_id == WL_SOULEXPANSION )
damage <<= 1; // If used against a player in White Imprison, the skill deals double damage.
status_change_end(bl,SC_WHITEIMPRISON,INVALID_TIMER); // Those skills do damage and removes effect
} else {
d->dmg_lv = ATK_BLOCK;
return 0;
}
}
if( sc->data[SC_ZEPHYR] && !(flag&BF_MAGIC && skill_id) && !(skill_get_inf(skill_id)&(INF_GROUND_SKILL|INF_SELF_SKILL)) ) {
d->dmg_lv = ATK_BLOCK;
return 0;
}
if (!battle_check_sc(src, bl, sc, d, damage, skill_id, skill_lv))
if( sc && sc->count ) {
if (!battle_status_block_damage(src, bl, sc, d, damage, skill_id, skill_lv)) // Statuses that reduce damage to 0.
return 0;
if (sc->data[SC__MANHOLE] || (src->type == BL_PC && sc->data[SC_KINGS_GRACE]) || sc->data[SC_GRAVITYCONTROL]) {
d->dmg_lv = ATK_BLOCK;
return 0;
}
if( sc->data[SC_WEAPONBLOCKING] && flag&(BF_SHORT|BF_WEAPON) && rnd()%100 < sc->data[SC_WEAPONBLOCKING]->val2 ) {
clif_skill_nodamage(bl,src,GC_WEAPONBLOCKING,sc->data[SC_WEAPONBLOCKING]->val1,1);
sc_start(src, bl, SC_WEAPONBLOCK_ON, 100, src->id, skill_get_time2(GC_WEAPONBLOCKING, 1));
d->dmg_lv = ATK_BLOCK;
return 0;
}
if( (sce = sc->data[SC_AUTOGUARD]) && flag&BF_WEAPON && !skill_get_inf2(skill_id, INF2_IGNOREAUTOGUARD) && rnd()%100 < sce->val2) {
int delay;
struct status_change_entry *sce_d = sc->data[SC_DEVOTION];
struct block_list *d_bl = NULL;
// different delay depending on skill level [celest]
if (sce->val1 <= 5)
delay = 300;
else if (sce->val1 > 5 && sce->val1 <= 9)
delay = 200;
else
delay = 100;
if (sd && pc_issit(sd))
pc_setstand(sd, true);
if( sce_d && (d_bl = map_id2bl(sce_d->val1)) &&
((d_bl->type == BL_MER && ((TBL_MER*)d_bl)->master && ((TBL_MER*)d_bl)->master->bl.id == bl->id) ||
(d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce_d->val2] == bl->id)) &&
check_distance_bl(bl,d_bl,sce_d->val3) )
{ //If player is target of devotion, show guard effect on the devotion caster rather than the target
clif_skill_nodamage(d_bl,d_bl,CR_AUTOGUARD,sce->val1,1);
unit_set_walkdelay(d_bl,gettick(),delay,1);
d->dmg_lv = ATK_MISS;
return 0;
} else {
clif_skill_nodamage(bl,bl,CR_AUTOGUARD,sce->val1,1);
unit_set_walkdelay(bl,gettick(),delay,1);
if( sc->data[SC_SHRINK] && rnd()%100 < 5 * sce->val1 )
skill_blown(bl,src,skill_get_blewcount(CR_SHRINK,1),-1,BLOWN_NONE);
d->dmg_lv = ATK_MISS;
return 0;
}
}
if( (sce = sc->data[SC_MILLENNIUMSHIELD]) && sce->val2 > 0 && damage > 0 ) {
sce->val3 -= (int)cap_value(damage,INT_MIN,INT_MAX); // absorb damage
d->dmg_lv = ATK_BLOCK;
if( sce->val3 <= 0 ) { // Shield Down
sce->val2--;
if( sce->val2 >= 0 ) {
clif_millenniumshield(bl,sce->val2);
if( !sce->val2 )
status_change_end(bl,SC_MILLENNIUMSHIELD,INVALID_TIMER); // All shields down
else
sce->val3 = 1000; // Next shield
}
status_change_start(src,bl,SC_STUN,10000,0,0,0,0,1000,SCSTART_NOTICKDEF);
}
return 0;
}
// attack blocked by Parrying
if( (sce = sc->data[SC_PARRYING]) && flag&BF_WEAPON && skill_id != WS_CARTTERMINATION && rnd()%100 < sce->val2 ) {
clif_skill_nodamage(bl, bl, LK_PARRYING, sce->val1,1);
return 0;
}
if (sc->data[SC_DODGE] && (flag&BF_LONG || sc->data[SC_SPURT]) && rnd() % 100 < 20) {
if (sd && pc_issit(sd))
pc_setstand(sd, true); //Stand it to dodge.
clif_skill_nodamage(bl, bl, TK_DODGE, 1, 1);
sc_start4(src, bl, SC_COMBO, 100, TK_JUMPKICK, src->id, 1, 0, 2000);
return 0;
}
if(sc->data[SC_HERMODE] && flag&BF_MAGIC)
return 0;
if(sc->data[SC_TATAMIGAESHI] && (flag&(BF_MAGIC|BF_LONG)) == BF_LONG)
return 0;
//Kaupe blocks damage (skill or otherwise) from players, mobs, homuns, mercenaries.
if ((sce = sc->data[SC_KAUPE]) && rnd()%100 < sce->val2) {
clif_specialeffect(bl, EF_STORMKICK4, AREA);
//Shouldn't end until Breaker's non-weapon part connects.
#ifndef RENEWAL
if (skill_id != ASC_BREAKER || !(flag&BF_WEAPON))
#endif
if (--(sce->val3) <= 0) //We make it work like Safety Wall, even though it only blocks 1 time.
status_change_end(bl, SC_KAUPE, INVALID_TIMER);
return 0;
}
// Damage increasing effects
#ifdef RENEWAL // Flat +400% damage from melee
if (sc->data[SC_KAITE] && (flag&(BF_SHORT|BF_MAGIC)) == BF_SHORT)
damage <<= 2;
#endif
if( flag&BF_MAGIC && (sce=sc->data[SC_PRESTIGE]) && rnd()%100 < sce->val2) {
clif_specialeffect(bl, EF_STORMKICK4, AREA); // Still need confirm it.
return 0;
}
if (((sce = sc->data[SC_UTSUSEMI]) || sc->data[SC_BUNSINJYUTSU]) && flag&BF_WEAPON && !skill_get_inf2(skill_id, INF2_IGNORECICADA)) {
skill_additional_effect (src, bl, skill_id, skill_lv, flag, ATK_BLOCK, gettick() );
if (!status_isdead(src))
skill_counter_additional_effect( src, bl, skill_id, skill_lv, flag, gettick() );
if (sce) {
clif_specialeffect(bl, EF_STORMKICK4, AREA);
skill_blown(src,bl,sce->val3,-1,BLOWN_NONE);
}
//Both need to be consumed if they are active.
if (sce && --(sce->val2) <= 0)
status_change_end(bl, SC_UTSUSEMI, INVALID_TIMER);
if ((sce = sc->data[SC_BUNSINJYUTSU]) && --(sce->val2) <= 0)
status_change_end(bl, SC_BUNSINJYUTSU, INVALID_TIMER);
return 0;
}
//Now damage increasing effects
if (sc->data[SC_AETERNA] && skill_id != PF_SOULBURN) {
if (src->type != BL_MER || !skill_id)
damage <<= 1; // Lex Aeterna only doubles damage of regular attacks from mercenaries
@ -1504,80 +1582,9 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam
status_change_end(bl, SC_ELECTRICSHOCKER, INVALID_TIMER);
}
//Finally Kyrie because it may, or not, reduce damage to 0.
if((sce = sc->data[SC_KYRIE]) && damage > 0){
sce->val2 -= (int)cap_value(damage,INT_MIN,INT_MAX);
if(flag&BF_WEAPON || skill_id == TF_THROWSTONE){
if(sce->val2>=0)
damage=0;
else
damage=-sce->val2;
}
if((--sce->val3)<=0 || (sce->val2<=0) || skill_id == AL_HOLYLIGHT)
status_change_end(bl, SC_KYRIE, INVALID_TIMER);
}
if ((sce = sc->data[SC_P_ALTER]) && damage > 0) {
clif_specialeffect(bl, EF_GUARD, AREA);
sce->val3 -= (int)cap_value(damage, INT_MIN, INT_MAX);
if (sce->val3 >= 0)
damage = 0;
else
damage = -sce->val3;
if (sce->val3 <= 0)
status_change_end(bl, SC_P_ALTER, INVALID_TIMER);
}
if ((sce = sc->data[SC_TUNAPARTY]) && damage > 0) {
sce->val2 -= (int)cap_value(damage, INT_MIN, INT_MAX);
if (sce->val2 >= 0)
damage = 0;
else
damage = -sce->val2;
if (sce->val2 <= 0)
status_change_end(bl, SC_TUNAPARTY, INVALID_TIMER);
}
if ((sce = sc->data[SC_DIMENSION1]) && damage > 0) {
sce->val2 -= (int)cap_value(damage, INT_MIN, INT_MAX);
if (sce->val2 >= 0)
damage = 0;
else
damage = -sce->val2;
if (sce->val2 <= 0)
status_change_end(bl, SC_DIMENSION1, INVALID_TIMER);
}
if ((sce = sc->data[SC_DIMENSION2]) && damage > 0) {
sce->val2 -= (int)cap_value(damage, INT_MIN, INT_MAX);
if (sce->val2 >= 0)
damage = 0;
else
damage = -sce->val2;
if (sce->val2 <= 0)
status_change_end(bl, SC_DIMENSION2, INVALID_TIMER);
}
if( sc->data[SC_MEIKYOUSISUI] && rnd()%100 < 40 ) // custom value
damage = 0;
if (!damage)
return 0;
if( (sce = sc->data[SC_LIGHTNINGWALK]) && flag&BF_LONG && rnd()%100 < sce->val1 ) {
int dx[8] = { 0,-1,-1,-1,0,1,1,1 };
int dy[8] = { 1,1,0,-1,-1,-1,0,1 };
uint8 dir = map_calc_dir(bl, src->x, src->y);
if( unit_movepos(bl, src->x-dx[dir], src->y-dy[dir], 1, 1) ) {
clif_blown(bl);
unit_setdir(bl, dir);
}
d->dmg_lv = ATK_DEF;
status_change_end(bl, SC_LIGHTNINGWALK, INVALID_TIMER);
return 0;
}
if( sd && (sce = sc->data[SC_FORCEOFVANGUARD]) && flag&BF_WEAPON && rnd()%100 < sce->val2 )
pc_addspiritball(sd,skill_get_time(LG_FORCEOFVANGUARD,sce->val1),sce->val3);
@ -1604,7 +1611,6 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam
//SC effects from caster side.
sc = status_get_sc(src);
tsc = status_get_sc(src);
if (sc && sc->count) {
if( sc->data[SC_INVINCIBLE] && !sc->data[SC_INVINCIBLEOFF] )
@ -1665,20 +1671,16 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam
damage += damage * sc->data[SC_DANCEWITHWUG]->val1 / 100;
if (sc->data[SC_UNLIMITEDHUMMINGVOICE] && flag&BF_MAGIC)
damage += damage * sc->data[SC_UNLIMITEDHUMMINGVOICE]->val3 / 100;
} //End of caster SC_ check
if (tsc && tsc->count) {
struct map_session_data *tsd = (struct map_session_data *)src;
map_session_data *tsd = (map_session_data *)src;
if (tsc->data[SC_INVINCIBLE] && !tsc->data[SC_INVINCIBLEOFF])
damage += damage * 75 / 100;
if (tsd && (sce = tsc->data[SC_SOULREAPER])) {
if (tsd && (sce = sc->data[SC_SOULREAPER])) {
if (rnd()%100 < sce->val2 && tsd->soulball < MAX_SOUL_BALL) {
clif_specialeffect(src, 1208, AREA);
pc_addsoulball(tsd, 5 + 3 * pc_checkskill(tsd, SP_SOULENERGY));
}
}
}
} //End of caster SC_ check
//PK damage rates
if (battle_config.pk_mode && sd && bl->type == BL_PC && damage && map_getmapflag(bl->m, MF_PVP)) {
@ -4970,6 +4972,10 @@ static void battle_attack_sc_bonus(struct Damage* wd, struct block_list *src, st
RE_ALLATK_ADDRATE(wd, sc->data[SC_GVG_GIANT]->val3);
}
if (skill_id == 0 && sc->data[SC_EXEEDBREAK]) {
ATK_ADDRATE(wd->damage, wd->damage2, sc->data[SC_EXEEDBREAK]->val2);
RE_ALLATK_ADDRATE(wd, sc->data[SC_EXEEDBREAK]->val2);
}
if (sc->data[SC_PYREXIA] && sc->data[SC_PYREXIA]->val3 == 0) {
ATK_ADDRATE(wd->damage, wd->damage2, sc->data[SC_PYREXIA]->val2);
RE_ALLATK_ADDRATE(wd, sc->data[SC_PYREXIA]->val2);
@ -5235,9 +5241,6 @@ static void battle_calc_attack_plant(struct Damage* wd, struct block_list *src,s
{
struct status_data *tstatus = status_get_status_data(target);
bool attack_hits = is_attack_hitting(wd, src, target, skill_id, skill_lv, false);
int right_element = battle_get_weapon_element(wd, src, target, skill_id, skill_lv, EQI_HAND_R, false);
int left_element = battle_get_weapon_element(wd, src, target, skill_id, skill_lv, EQI_HAND_L, false);
short class_ = status_get_class(target);
if (skill_id != SN_SHARPSHOOTING && skill_id != RA_ARROWSTORM)
status_change_end(src, SC_CAMOUFLAGE, INVALID_TIMER);
@ -5257,17 +5260,21 @@ static void battle_calc_attack_plant(struct Damage* wd, struct block_list *src,s
if (attack_hits && target->type == BL_MOB) {
struct status_change *sc = status_get_sc(target);
if (sc && !battle_check_sc(src, target, sc, wd, 1, skill_id, skill_lv)) {
if (sc && !battle_status_block_damage(src, target, sc, wd, 1, skill_id, skill_lv)) { // Statuses that reduce damage to 0.
wd->damage = wd->damage2 = 0;
return;
}
}
if( attack_hits && class_ == MOBID_EMPERIUM ) {
if( attack_hits && status_get_class(target) == MOBID_EMPERIUM ) {
if(target && !battle_can_hit_gvg_target(src,target,skill_id,(skill_id)?BF_SKILL:0) && map_flag_gvg2(target->m)) {
wd->damage = wd->damage2 = 0;
return;
}
const int right_element = battle_get_weapon_element(wd, src, target, skill_id, skill_lv, EQI_HAND_R, false);
const int left_element = battle_get_weapon_element(wd, src, target, skill_id, skill_lv, EQI_HAND_L, false);
if (wd->damage > 0) {
wd->damage = battle_attr_fix(src, target, wd->damage, right_element, tstatus->def_ele, tstatus->ele_lv);
wd->damage = battle_calc_gvg_damage(src, target, wd->damage, skill_id, wd->flag);
@ -7729,11 +7736,8 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
vellum_damage = true;
if( sc && sc->count ) {
if (sc->data[SC_EXEEDBREAK]) {
if (!is_infinite_defense(target, wd.flag) && !vellum_damage)
wd.damage *= sc->data[SC_EXEEDBREAK]->val2 / 100;
if (sc->data[SC_EXEEDBREAK])
status_change_end(src, SC_EXEEDBREAK, INVALID_TIMER);
}
if( sc->data[SC_SPELLFIST] ) {
if( --(sc->data[SC_SPELLFIST]->val1) >= 0 && !vellum_damage ){
if (!is_infinite_defense(target, wd.flag)) {

View File

@ -1472,6 +1472,22 @@ static void clif_spiritball_single(int fd, struct map_session_data *sd)
WFIFOSET(fd, packet_len(0x1e1));
}
/// Notifies the client of an object's Millenium Shields.
static void clif_millenniumshield_single(int fd, map_session_data *sd)
{
nullpo_retv(sd);
if (sd->sc.data[SC_MILLENNIUMSHIELD] == nullptr)
return;
WFIFOHEAD(fd, packet_len(0x440));
WFIFOW(fd, 0) = 0x440;
WFIFOL(fd, 2) = sd->bl.id;
WFIFOW(fd, 6) = sd->sc.data[SC_MILLENNIUMSHIELD]->val2;
WFIFOW(fd, 8) = 0;
WFIFOSET(fd, packet_len(0x440));
}
/*==========================================
* Kagerou/Oboro amulet spirit
*------------------------------------------*/
@ -1613,6 +1629,8 @@ int clif_spawn( struct block_list *bl, bool walking ){
if (sd->spiritball > 0)
clif_spiritball(&sd->bl);
if (sd->sc.data[SC_MILLENNIUMSHIELD])
clif_millenniumshield(&sd->bl, sd->sc.data[SC_MILLENNIUMSHIELD]->val2);
if (sd->soulball > 0)
clif_soulball(sd);
if(sd->state.size==SZ_BIG) // tiny/big players [Valaris]
@ -4700,6 +4718,8 @@ static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_d
if(dstsd->spiritball > 0)
clif_spiritball_single(sd->fd, dstsd);
if (dstsd->sc.data[SC_MILLENNIUMSHIELD])
clif_millenniumshield_single(sd->fd, dstsd);
if (dstsd->spiritcharm_type != CHARM_TYPE_NONE && dstsd->spiritcharm > 0)
clif_spiritcharm_single(sd->fd, dstsd);
if (dstsd->soulball > 0)
@ -9601,6 +9621,8 @@ void clif_refresh(struct map_session_data *sd)
clif_updatestatus(sd,SP_LUK);
if (sd->spiritball)
clif_spiritball_single(sd->fd, sd);
if (sd->sc.data[SC_MILLENNIUMSHIELD])
clif_millenniumshield_single(sd->fd, sd);
if (sd->spiritcharm_type != CHARM_TYPE_NONE && sd->spiritcharm > 0)
clif_spiritcharm_single(sd->fd, sd);
if (sd->soulball)

View File

@ -12314,7 +12314,14 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
}
// Skill Unit Setting
case MG_SAFETYWALL:
case MG_SAFETYWALL: {
int dummy = 1;
if (map_foreachincell(skill_cell_overlap, src->m, x, y, BL_SKILL, skill_id, &dummy, src)) {
skill_unitsetting(src, skill_id, skill_lv, x, y, 0);
return 0; // Don't consume gems if cast on Land Protector
}
}
case MG_FIREWALL:
case MG_THUNDERSTORM:
case AL_PNEUMA:
@ -13334,10 +13341,9 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_
val3 = 300 * skill_lv + 65 * ( status->int_ + status_get_lv(src) ) + status->max_sp; //nb hp
break;
case MG_SAFETYWALL:
val2 = skill_lv + 1;
#ifdef RENEWAL
val2 = status_get_max_hp(src) * 3;
#else
val2 = skill_lv+1;
val3 = 300 * skill_lv + 65 * (status->int_ + status_get_lv(src)) + status->max_sp;
#endif
break;
case MG_FIREWALL: