Updates stacking for common statuses (#6807)

* Fixes #6798.
* Updates the Fail, End, and EndReturn lists for OPT1 and OPT2 statuses.
* Removes the hardcoded OPT1 overwrite prevention check.
* OPT1 that have RemoveOnDamaged flag should not get applied again in the same attack.
* Fixes Stone status not properly being inflicted by the bAddEff, bAddEff2, bAddEffWhenHit, and bAddEffOnSkill item bonuses.
* Fixes Stone status not properly being inflicted by The Hanged Man from Tarot Card of Fate.
Thanks to @Playtester!
This commit is contained in:
Aleos 2022-04-21 09:31:27 -04:00 committed by GitHub
parent b17b0c7a0b
commit 53bc2376a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 139 additions and 80 deletions

View File

@ -70,7 +70,6 @@ Body:
Stun: true
Sleep: true
Burning: true
#Undead: true
End:
Aeterna: true
EndReturn:
@ -89,7 +88,6 @@ Body:
Stun: true
Sleep: true
Burning: true
#Undead: true
EndReturn:
StoneWait: true
Stone: true
@ -115,8 +113,13 @@ Body:
Inspiration: true
Warmer: true
Gvg_Freez: true
Stone: true
StoneWait: true
Freeze: true
Stun: true
Sleep: true
Burning: true
End:
Dancing: true
Aeterna: true
- Status: Stun
DurationLookup: NPC_STUNATTACK
@ -135,8 +138,7 @@ Body:
Refresh: true
Inspiration: true
Gvg_Stun: true
End:
Dancing: true
Stun: true
- Status: Sleep
DurationLookup: NPC_SLEEPATTACK
States:
@ -155,8 +157,7 @@ Body:
Refresh: true
Inspiration: true
Gvg_Sleep: true
End:
Dancing: true
Sleep: true
- Status: Poison
DurationLookup: NPC_POISON
CalcFlags:
@ -172,6 +173,8 @@ Body:
Fail:
Refresh: true
Inspiration: true
Poison: true
Dpoison: true
- Status: Curse
DurationLookup: NPC_CURSEATTACK
CalcFlags:
@ -189,6 +192,7 @@ Body:
Refresh: true
Inspiration: true
Gvg_Curse: true
Curse: true
- Status: Silence
DurationLookup: NPC_SILENCEATTACK
States:
@ -204,16 +208,16 @@ Body:
Refresh: true
Inspiration: true
Gvg_Silence: true
Silence: true
- Status: Confusion
DurationLookup: NPC_WIDECONFUSE
Flags:
BossResist: true
StopWalking: true
RemoveOnDamaged: true
OverlapFail: true
Fail:
Refresh: true
Inspiration: true
EndReturn:
Confusion: true
- Status: Blind
DurationLookup: NPC_BLINDATTACK
@ -231,6 +235,7 @@ Body:
Inspiration: true
Fear: true
Gvg_Blind: true
Blind: true
- Status: Bleeding
Icon: EFST_BLOODING
DurationLookup: NPC_BLEEDING
@ -243,7 +248,6 @@ Body:
BossResist: true
NoSave: true
NoClearance: true
OverlapFail: true
Fail:
Refresh: true
Inspiration: true
@ -257,7 +261,6 @@ Body:
Flags:
SendOption: true
BossResist: true
OverlapFail: true
Fail:
Refresh: true
Inspiration: true
@ -415,6 +418,8 @@ Body:
Flags:
BossResist: true
TaekwonAngel: true
EndReturn:
Curse: true
- Status: Signumcrucis
Icon: EFST_CRUCIS
DurationLookup: AL_CRUCIS
@ -2840,7 +2845,6 @@ Body:
Flags:
BossResist: true
StopWalking: true
OverlapFail: true
Debuff: true
Fail:
Inspiration: true
@ -2849,6 +2853,8 @@ Body:
- Status: Burning
Icon: EFST_BURNT
DurationLookup: RK_DRAGONBREATH
CalcFlags:
Mdef: true
Opt1: Burning
Flags:
SendOption: true
@ -2861,8 +2867,6 @@ Body:
Fail:
Refresh: true
Inspiration: true
CalcFlags:
Mdef: true
- Status: Freezing
Icon: EFST_FROSTMISTY
DurationLookup: WL_FROSTMISTY
@ -3076,13 +3080,8 @@ Body:
SetStand: true
StopWalking: true
StopAttacking: true
OverlapFail: true
End:
Dancing: true
Burning: true
Freezing: true
Freeze: true
Stone: true
- Status: Marshofabyss
Icon: EFST_MARSHOFABYSS
DurationLookup: WL_MARSHOFABYSS

View File

@ -71,7 +71,6 @@ Body:
Stun: true
Sleep: true
Burning: true
#Undead: true
End:
Aeterna: true
EndReturn:
@ -91,7 +90,6 @@ Body:
Stun: true
Sleep: true
Burning: true
#Undead: true
EndReturn:
StoneWait: true
Stone: true
@ -117,8 +115,14 @@ Body:
Inspiration: true
Warmer: true
Gvg_Freez: true
Whiteimprison: true
Stone: true
StoneWait: true
Freeze: true
Stun: true
Sleep: true
Burning: true
End:
Dancing: true
Aeterna: true
- Status: Stun
DurationLookup: NPC_STUNATTACK
@ -137,8 +141,7 @@ Body:
Refresh: true
Inspiration: true
Gvg_Stun: true
End:
Dancing: true
Stun: true
- Status: Sleep
DurationLookup: NPC_SLEEPATTACK
States:
@ -157,8 +160,7 @@ Body:
Refresh: true
Inspiration: true
Gvg_Sleep: true
End:
Dancing: true
Sleep: true
- Status: Poison
DurationLookup: NPC_POISON
CalcFlags:
@ -175,6 +177,8 @@ Body:
Fail:
Refresh: true
Inspiration: true
Poison: true
Dpoison: true
- Status: Curse
DurationLookup: NPC_WIDECURSE
CalcFlags:
@ -193,6 +197,7 @@ Body:
Refresh: true
Inspiration: true
Gvg_Curse: true
Curse: true
- Status: Silence
DurationLookup: NPC_SILENCEATTACK
States:
@ -209,17 +214,17 @@ Body:
Refresh: true
Inspiration: true
Gvg_Silence: true
Silence: true
- Status: Confusion
DurationLookup: NPC_WIDECONFUSE
Flags:
BossResist: true
StopWalking: true
RemoveOnDamaged: true
OverlapFail: true
SpreadEffect: true
Fail:
Refresh: true
Inspiration: true
EndReturn:
Confusion: true
- Status: Blind
DurationLookup: NPC_BLINDATTACK
@ -238,6 +243,7 @@ Body:
Inspiration: true
Fear: true
Gvg_Blind: true
Blind: true
- Status: Bleeding
Icon: EFST_BLOODING
DurationLookup: NPC_BLEEDING
@ -250,7 +256,6 @@ Body:
BossResist: true
NoSave: true
NoClearance: true
OverlapFail: true
SpreadEffect: true
Fail:
Refresh: true
@ -265,7 +270,6 @@ Body:
Flags:
SendOption: true
BossResist: true
OverlapFail: true
Fail:
Refresh: true
Inspiration: true
@ -2951,7 +2955,6 @@ Body:
Flags:
BossResist: true
StopWalking: true
OverlapFail: true
Debuff: true
Fail:
Inspiration: true
@ -3188,12 +3191,8 @@ Body:
StopWalking: true
StopAttacking: true
StopCasting: true
OverlapFail: true
End:
Burning: true
Freezing: true
Freeze: true
Stone: true
- Status: Marshofabyss
Icon: EFST_MARSHOFABYSS
DurationLookup: WL_MARSHOFABYSS

View File

@ -3,7 +3,7 @@
//===== By: ==================================================
//= rAthena Dev Team
//===== Last Updated: ========================================
//= 20220406
//= 20220414
//===== Description: =========================================
//= Explanation of the status.yml file and structure.
//============================================================
@ -113,6 +113,7 @@ Opt1: Special effect when status is active (Aegis: BODYSTATE_*). This option is
None - No effect (Default)
Stone - Stone curse effect
StoneWait - Stone curse incubation effect
Freeze - Freeze effect
Stun - Stun effect
Sleep - Sleep effect

View File

@ -1302,11 +1302,24 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1
type = it.sc;
time = it.duration;
int32 val1 = 7, val2, val3;
if (type == SC_STONEWAIT) {
val2 = src->id;
val3 = skill_get_time(status_db.getSkill(type), 7);
} else {
val2 = 0;
if (type == SC_BURNING)
val3 = src->id;
else
val3 = 0;
}
if (it.flag&ATF_TARGET)
status_change_start(src,bl,type,rate,7,0,(type == SC_BURNING)?src->id:0,0,time,SCSTART_NONE);
status_change_start(src,bl,type,rate,val1,val2,val3,0,time,SCSTART_NONE);
if (it.flag&ATF_SELF)
status_change_start(src,src,type,rate,7,0,(type == SC_BURNING)?src->id:0,0,time,SCSTART_NONE);
status_change_start(src,src,type,rate,val1,val2,val3,0,time,SCSTART_NONE);
}
}
@ -1321,10 +1334,23 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1
type = it.sc;
time = it.duration;
int32 val1 = 7, val2, val3;
if (type == SC_STONEWAIT) {
val2 = src->id;
val3 = skill_get_time(status_db.getSkill(type), 7);
} else {
val2 = 0;
if (type == SC_BURNING)
val3 = src->id;
else
val3 = 0;
}
if (it.target&ATF_TARGET)
status_change_start(src,bl,type,it.rate,7,0,0,0,time,SCSTART_NONE);
status_change_start(src,bl,type,it.rate,val1,val2,val3,0,time,SCSTART_NONE);
if (it.target&ATF_SELF)
status_change_start(src,src,type,it.rate,7,0,0,0,time,SCSTART_NONE);
status_change_start(src,src,type,it.rate,val1,val2,val3,0,time,SCSTART_NONE);
}
//"While the damage can be blocked by Pneuma, the chance to break armor remains", irowiki. [Cydh]
if (dmg_lv == ATK_BLOCK && skill_id == AM_ACIDTERROR) {
@ -2519,11 +2545,24 @@ int skill_counter_additional_effect (struct block_list* src, struct block_list *
type = it.sc;
time = it.duration;
int32 val1 = 7, val2, val3;
if (type == SC_STONEWAIT) {
val2 = src->id;
val3 = skill_get_time(status_db.getSkill(type), 7);
} else {
val2 = 0;
if (type == SC_BURNING)
val3 = src->id;
else
val3 = 0;
}
if (it.flag&ATF_TARGET && src != bl)
status_change_start(src,src,type,rate,7,0,0,0,time,SCSTART_NONE);
status_change_start(src,src,type,rate,val1,val2,val3,0,time,SCSTART_NONE);
if (it.flag&ATF_SELF && !status_isdead(bl))
status_change_start(src,bl,type,rate,7,0,0,0,time,SCSTART_NONE);
status_change_start(src,bl,type,rate,val1,val2,val3,0,time,SCSTART_NONE);
}
}
@ -4848,10 +4887,14 @@ static int skill_tarotcard(struct block_list* src, struct block_list *target, ui
}
case 8: // THE HANGED MAN - stop, freeze or stoned
{
enum sc_type sc[] = { SC_STOP, SC_FREEZE, SC_STONE };
enum sc_type sc[] = { SC_STOP, SC_FREEZE, SC_STONEWAIT };
uint8 rand_eff = rnd() % 3;
int time = ((rand_eff == 0) ? skill_get_time2(skill_id, skill_lv) : skill_get_time2(status_db.getSkill(sc[rand_eff]), 1));
sc_start(src, target, sc[rand_eff], 100, skill_lv, time);
if (sc[rand_eff] == SC_STONEWAIT)
sc_start4(src, target, SC_STONEWAIT, 100, skill_lv, src->id, skill_get_time(status_db.getSkill(SC_STONEWAIT), 1), 0, time);
else
sc_start(src, target, sc[rand_eff], 100, skill_lv, time);
break;
}
case 9: // DEATH - curse, coma and poison

View File

@ -1000,8 +1000,15 @@ int status_damage(struct block_list *src,struct block_list *target,int64 dhp, in
for (const auto &it : status_db) {
sc_type type = static_cast<sc_type>(it.first);
if (sc->data[type] && it.second->flag[SCF_REMOVEONDAMAGED])
if (sc->data[type] && it.second->flag[SCF_REMOVEONDAMAGED]) {
// A status change that gets broken by damage should still be considered when calculating if a status change can be applied or not (for the same attack).
// !TODO: This is a temporary solution until we refactor the code so that the calculation of an SC is done at the start of an attack rather than after the damage was applied.
if (sc->opt1 > OPT1_NONE && sc->lastEffectTimer == INVALID_TIMER) {
sc->lastEffectTimer = add_timer(gettick() + 10, status_clear_lastEffect_timer, target->id, 0);
sc->lastEffect = type;
}
status_change_end(target, type, INVALID_TIMER);
}
}
if ((sce=sc->data[SC_ENDURE]) && !sce->val4) {
/** [Skotlex]
@ -8640,6 +8647,8 @@ void status_change_init(struct block_list *bl)
struct status_change *sc = status_get_sc(bl);
nullpo_retv(sc);
memset(sc, 0, sizeof (struct status_change));
sc->lastEffect = SC_NONE;
sc->lastEffectTimer = INVALID_TIMER;
}
/*========================================== [Playtester]
@ -9262,7 +9271,8 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
// Check failing SCs from list
if (!scdb->fail.empty()) {
for (const auto &it : scdb->fail) {
if (it && sc->data[it])
// Don't let OPT1 that have RemoveOnDamaged start a new effect in the same attack.
if (sc->data[it] || sc->lastEffect == it)
return 0;
}
}
@ -9624,6 +9634,16 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
break;
}
// Check for OPT1 stacking
if (sc->opt1 > OPT1_NONE && scdb->opt1 > OPT1_NONE) {
for (const auto &status_it : status_db) {
sc_type opt1_type = status_it.second->type;
if (sc->data[opt1_type] && status_it.second->opt1 > OPT1_NONE)
status_change_end(bl, opt1_type, INVALID_TIMER);
}
}
// Before overlapping fail, one must check for status cured.
std::vector<sc_type> endlist;
@ -9652,12 +9672,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
// List of hardcoded status cured.
switch (type) {
case SC_STONE:
if (sc->data[SC_DANCING]) {
unit_stop_walking(bl, 1);
status_change_end(bl, SC_DANCING, INVALID_TIMER);
}
break;
case SC_BLESSING:
// !TODO: Blessing and Agi up should do 1 damage against players on Undead Status, even on PvM
// !but cannot be plagiarized (this requires aegis investigation on packets and official behavior) [Brainstorm]
@ -9698,9 +9712,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
pc_bonus_script_clear(sd, BSF_REM_ON_MADOGEAR);
break;
default:
// If new SC has OPT1 while unit has OPT1, fail it!
if (sc->opt1 && scdb->opt1)
return 0;
break;
}
@ -12012,14 +12023,13 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
unit_stop_attack(bl);
}
break;
case SC_WHITEIMPRISON:
case SC_DEEPSLEEP:
case SC_CRYSTALIZE:
case SC_FREEZE:
case SC_STUN:
case SC_GRAVITYCONTROL:
if (sc->data[SC_DANCING])
case SC_STONE:
if (sc->data[SC_DANCING]) {
unit_stop_walking(bl, 1);
status_change_end(bl, SC_DANCING, INVALID_TIMER);
}
break;
default:
if (!unit_blown_immune(bl,0x1))
@ -12383,26 +12393,6 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
}
if (sce->timer != INVALID_TIMER) // Could be a SC with infinite duration
delete_timer(sce->timer,status_change_timer);
if (sc->opt1)
switch (type) {
// "Ugly workaround" [Skotlex]
// delays status change ending so that a skill that sets opt1 fails to
// trigger when it also removed one
case SC_STONE:
case SC_STONEWAIT:
sce->val4 = -1; // Petrify time
case SC_FREEZE:
case SC_STUN:
case SC_SLEEP:
if (sce->val1) {
// Removing the 'level' shouldn't affect anything in the code
// since these SC are not affected by it, and it lets us know
// if we have already delayed this attack or not.
sce->val1 = 0;
sce->timer = add_timer(gettick()+10, status_change_timer, bl->id, type);
return 1;
}
}
}
(sc->count)--;
@ -14495,6 +14485,29 @@ static TIMER_FUNC(status_natural_heal_timer){
return 0;
}
/**
* Clears the lastEffect value from a target
* @param tid: Timer ID
* @param tick: Current tick (time)
* @param id: Object ID
* @param data: data pushed through timer function
* @return 0
*/
TIMER_FUNC(status_clear_lastEffect_timer) {
block_list *bl = map_id2bl(id);
if (bl != nullptr) {
status_change *sc = status_get_sc(bl);
if (sc != nullptr) {
sc->lastEffect = SC_NONE;
sc->lastEffectTimer = INVALID_TIMER;
}
}
return 0;
}
/**
* Check if status is disabled on a map
* @param type: Status Change data
@ -15184,6 +15197,7 @@ void do_init_status(void) {
add_timer_func_list(status_change_timer,"status_change_timer");
add_timer_func_list(status_natural_heal_timer,"status_natural_heal_timer");
add_timer_func_list(status_clear_lastEffect_timer, "status_clear_lastEffect_timer");
initDummyData();
status_readdb();
natural_heal_prev_tick = gettick();

View File

@ -3055,6 +3055,8 @@ struct status_change {
unsigned short opt1;// body state
unsigned short opt2;// health state (bitfield)
unsigned char count;
sc_type lastEffect; // Used to check for stacking damageable SC on the same attack
int32 lastEffectTimer; // Timer for lastEffect
//! TODO: See if it is possible to implement the following SC's without requiring extra parameters while the SC is inactive.
struct {
uint8 move;
@ -3229,6 +3231,7 @@ int status_change_timer_sub(struct block_list* bl, va_list ap);
int status_change_clear(struct block_list* bl, int type);
void status_change_clear_buffs(struct block_list* bl, uint8 type);
void status_change_clear_onChangeMap(struct block_list *bl, struct status_change *sc);
TIMER_FUNC(status_clear_lastEffect_timer);
#define status_calc_mob(md, opt) status_calc_bl_(&(md)->bl, status_db.getSCB_ALL(), opt)
#define status_calc_pet(pd, opt) status_calc_bl_(&(pd)->bl, status_db.getSCB_ALL(), opt)