diff --git a/db/import-tmpl/status.yml b/db/import-tmpl/status.yml index b83eed9de6d..afcb6721a0d 100644 --- a/db/import-tmpl/status.yml +++ b/db/import-tmpl/status.yml @@ -36,9 +36,9 @@ # MinDuration Minimum duration in milliseconds after status change reduction. (Default: 1) # Fail: List of Status Changes that causes the status to fail to activate. (Optional) # End: List of Status Changes that will end when the status activates. (Optional) -# EndReturn If the status has an End list and succeeds to remove these status changes, it won't give its effect. (Default: false) +# EndReturn: List of Status Changes that will end when the status activates and won't give its effect. (Optional) ########################################################################### Header: Type: STATUS_DB - Version: 1 + Version: 2 diff --git a/db/pre-re/skill_db.yml b/db/pre-re/skill_db.yml index 803217ab7f8..4e10453e8fd 100644 --- a/db/pre-re/skill_db.yml +++ b/db/pre-re/skill_db.yml @@ -796,8 +796,8 @@ Body: Element: Earth CastCancel: true CastTime: 1000 - Duration1: 5000 - Duration2: 20000 + Duration1: 20000 + Duration2: 5000 Requires: SpCost: - Level: 1 @@ -823,7 +823,7 @@ Body: ItemCost: - Item: Red_Gemstone Amount: 1 - Status: Stone + Status: StoneWait - Id: 17 Name: MG_FIREBALL Description: Fire Ball @@ -5663,8 +5663,9 @@ Body: Hit: Single HitCount: 1 Element: Weapon - Duration2: 20000 - Status: Stone + Duration1: 20000 + Duration2: 100 + Status: StoneWait - Id: 181 Name: NPC_CURSEATTACK Description: Curse Attack @@ -15911,8 +15912,9 @@ Body: Area: 11 - Level: 5 Area: 14 - Duration2: 20000 - Status: Stone + Duration1: 20000 + Duration2: 100 + Status: StoneWait - Id: 667 Name: NPC_WIDECONFUSE Description: Wide Confusion diff --git a/db/pre-re/status.yml b/db/pre-re/status.yml index b7b2b0d68c2..d98c5038ad9 100644 --- a/db/pre-re/status.yml +++ b/db/pre-re/status.yml @@ -36,37 +36,63 @@ # MinDuration Minimum duration in milliseconds after status change reduction. (Default: 1) # Fail: List of Status Changes that causes the status to fail to activate. (Optional) # End: List of Status Changes that will end when the status activates. (Optional) -# EndReturn If the status has an End list and succeeds to remove these status changes, it won't give its effect. (Default: false) +# EndReturn: List of Status Changes that will end when the status activates and won't give its effect. (Optional) ########################################################################### Header: Type: STATUS_DB - Version: 1 + Version: 2 Body: - Status: Stone DurationLookup: NPC_PETRIFYATTACK States: NoMove: true - NoMoveCond: true NoCast: true NoAttack: true CalcFlags: Def_Ele: true Def: true Mdef: true + Opt1: Stone Flags: SendOption: true BossResist: true StopAttacking: true StopCasting: true + RemoveOnDamaged: true Fail: Refresh: true Inspiration: true Power_Of_Gaia: true Gvg_Stone: true + Freeze: true + Stun: true + Sleep: true + Burning: true + #Undead: true End: - Dancing: true + Aeterna: true + EndReturn: + StoneWait: true + Stone: true + - Status: StoneWait + DurationLookup: NPC_PETRIFYATTACK + States: + NoCast: true + Opt1: StoneWait + Flags: + SendOption: true + StopAttacking: true + Fail: + Freeze: true + Stun: true + Sleep: true + Burning: true + #Undead: true + EndReturn: + StoneWait: true + Stone: true - Status: Freeze DurationLookup: NPC_WIDEFREEZE States: @@ -247,6 +273,11 @@ Body: BossResist: true Debuff: true NoSaveInfinite: true + End: + Freeze: true + Stone: true + Sleep: true + TrickDead: true - Status: Endure Icon: EFST_ENDURE DurationLookup: SM_ENDURE @@ -505,6 +536,7 @@ Body: Flags: NoSave: true Fail: + Stone: true Freeze: true - Status: Adrenaline Icon: EFST_ADRENALINE @@ -1229,9 +1261,8 @@ Body: Fail: Quagmire: true Dontforgetme: true - End: + EndReturn: Decreaseagi: true - EndReturn: true - Status: Chasewalk Icon: EFST_CHASEWALK DurationLookup: ST_CHASEWALK @@ -1317,6 +1348,10 @@ Body: Flags: NoSave: true Debuff: true + End: + Freeze: true + Stone: true + Sleep: true - Status: Memorize Icon: EFST_MEMORIZE DurationLookup: PF_MEMORIZE @@ -4179,9 +4214,8 @@ Body: Fail: Quagmire: true Dontforgetme: true - End: + EndReturn: Decreaseagi: true - EndReturn: true - Status: Thornstrap Icon: EFST_THORNS_TRAP DurationLookup: GN_THORNS_TRAP @@ -5698,9 +5732,8 @@ Body: NoBanishingBuster: true NoClearance: true NoForcedEnd: true - End: + EndReturn: All_Riding: true - EndReturn: true - Status: Teargas_Sob Flags: BossResist: true diff --git a/db/re/skill_db.yml b/db/re/skill_db.yml index 892075f427d..2fabc229fe0 100644 --- a/db/re/skill_db.yml +++ b/db/re/skill_db.yml @@ -780,8 +780,8 @@ Body: Element: Earth CastCancel: true CastTime: 800 - Duration1: 5000 - Duration2: 17000 + Duration1: 17000 + Duration2: 5000 FixedCastTime: 200 Requires: SpCost: @@ -808,7 +808,7 @@ Body: ItemCost: - Item: Red_Gemstone Amount: 1 - Status: Stone + Status: StoneWait - Id: 17 Name: MG_FIREBALL Description: Fire Ball @@ -5933,8 +5933,9 @@ Body: Hit: Single HitCount: 1 Element: Weapon - Duration2: 17000 - Status: Stone + Duration1: 17000 + Duration2: 100 + Status: StoneWait - Id: 181 Name: NPC_CURSEATTACK Description: Curse Attack @@ -16310,8 +16311,9 @@ Body: Area: 11 - Level: 5 Area: 14 - Duration2: 17000 - Status: Stone + Duration1: 17000 + Duration2: 100 + Status: StoneWait - Id: 667 Name: NPC_WIDECONFUSE Description: Wide Confusion @@ -19241,6 +19243,7 @@ Body: Time: 13000 - Level: 5 Time: 15000 + Duration2: 100 Requires: SpCost: - Level: 1 @@ -19256,7 +19259,7 @@ Body: ItemCost: - Item: Red_Gemstone Amount: 2 - Status: Stone + Status: StoneWait - Id: 2208 Name: WL_RADIUS Description: Radius @@ -32010,6 +32013,7 @@ Body: CastCancel: true AfterCastActDelay: 1000 Duration1: 10000 + Duration2: 5000 Requires: SpCost: 30 Unit: diff --git a/db/re/status.yml b/db/re/status.yml index 31ce4ef2700..1da2ef2a1f1 100644 --- a/db/re/status.yml +++ b/db/re/status.yml @@ -36,37 +36,65 @@ # MinDuration Minimum duration in milliseconds after status change reduction. (Default: 1) # Fail: List of Status Changes that causes the status to fail to activate. (Optional) # End: List of Status Changes that will end when the status activates. (Optional) -# EndReturn If the status has an End list and succeeds to remove these status changes, it won't give its effect. (Default: false) +# EndReturn: List of Status Changes that will end when the status activates and won't give its effect. (Optional) ########################################################################### Header: Type: STATUS_DB - Version: 1 + Version: 2 Body: - Status: Stone DurationLookup: NPC_PETRIFYATTACK States: NoMove: true - NoMoveCond: true NoCast: true NoAttack: true CalcFlags: Def_Ele: true Def: true Mdef: true + Opt1: Stone Flags: SendOption: true BossResist: true StopAttacking: true StopCasting: true + RemoveOnDamaged: true Fail: Refresh: true Inspiration: true Power_Of_Gaia: true Gvg_Stone: true + Whiteimprison: true + Freeze: true + Stun: true + Sleep: true + Burning: true + #Undead: true End: - Dancing: true + Aeterna: true + EndReturn: + StoneWait: true + Stone: true + - Status: StoneWait + DurationLookup: NPC_PETRIFYATTACK + States: + NoCast: true + Opt1: StoneWait + Flags: + SendOption: true + StopAttacking: true + Fail: + Whiteimprison: true + Freeze: true + Stun: true + Sleep: true + Burning: true + #Undead: true + EndReturn: + StoneWait: true + Stone: true - Status: Freeze DurationLookup: NPC_WIDEFREEZE States: @@ -253,6 +281,11 @@ Body: BossResist: true Debuff: true NoSaveInfinite: true + End: + Freeze: true + Stone: true + Sleep: true + TrickDead: true - Status: Endure Icon: EFST_ENDURE DurationLookup: SM_ENDURE @@ -515,6 +548,7 @@ Body: Flags: NoSave: true Fail: + Stone: true Freeze: true - Status: Adrenaline Icon: EFST_ADRENALINE @@ -1242,9 +1276,8 @@ Body: NoDispell: true Fail: Quagmire: true - End: + EndReturn: Decreaseagi: true - EndReturn: true - Status: Chasewalk Icon: EFST_CHASEWALK DurationLookup: ST_CHASEWALK @@ -1330,6 +1363,10 @@ Body: Flags: NoSave: true Debuff: true + End: + Freeze: true + Stone: true + Sleep: true - Status: Memorize Icon: EFST_MEMORIZE DurationLookup: PF_MEMORIZE @@ -4323,9 +4360,8 @@ Body: Fail: Quagmire: true Dontforgetme: true - End: + EndReturn: Decreaseagi: true - EndReturn: true - Status: Thornstrap Icon: EFST_THORNS_TRAP DurationLookup: GN_THORNS_TRAP @@ -5931,9 +5967,8 @@ Body: NoBanishingBuster: true NoClearance: true NoForcedEnd: true - End: + EndReturn: All_Riding: true - EndReturn: true - Status: Teargas_Sob Flags: BossResist: true diff --git a/db/status.yml b/db/status.yml index 6e01c143666..dc2ca167156 100644 --- a/db/status.yml +++ b/db/status.yml @@ -36,12 +36,12 @@ # MinDuration Minimum duration in milliseconds after status change reduction. (Default: 1) # Fail: List of Status Changes that causes the status to fail to activate. (Optional) # End: List of Status Changes that will end when the status activates. (Optional) -# EndReturn If the status has an End list and succeeds to remove these status changes, it won't give its effect. (Default: false) +# EndReturn: List of Status Changes that will end when the status activates and won't give its effect. (Optional) ########################################################################### Header: Type: STATUS_DB - Version: 1 + Version: 2 Footer: Imports: diff --git a/doc/status.txt b/doc/status.txt index 1fe26ed7a5b..0ac48e99aab 100644 --- a/doc/status.txt +++ b/doc/status.txt @@ -3,7 +3,7 @@ //===== By: ================================================== //= rAthena Dev Team //===== Last Updated: ======================================== -//= 20220222 +//= 20220406 //===== Description: ========================================= //= Explanation of the status.yml file and structure. //============================================================ @@ -270,7 +270,9 @@ End: List of status that will end if the status activates. --------------------------------------- -EndReturn: If the status has any 'End' and succeeds to end other status in the list, it won't give its effect. +EndReturn: List of status that will end if the status activates and it won't give its effect. + The statuses checked in this list are done at the beginning of status_change_start(). If at least 1 status from this list + is removed then it will return back and not check anything else. --------------------------------------- diff --git a/src/map/script_constants.hpp b/src/map/script_constants.hpp index fd0933751e0..edbe4f32e1d 100644 --- a/src/map/script_constants.hpp +++ b/src/map/script_constants.hpp @@ -940,7 +940,7 @@ export_constant2("VAR_SHOES",LOOK_SHOES); /* status changes */ - export_constant2("Eff_Stone",SC_STONE); + export_constant2("Eff_Stone",SC_STONEWAIT); export_constant2("Eff_Freeze",SC_FREEZE); export_constant2("Eff_Stun",SC_STUN); export_constant2("Eff_Sleep",SC_SLEEP); @@ -972,6 +972,7 @@ export_constant(SC_BLIND); export_constant(SC_BLEEDING); export_constant(SC_DPOISON); + export_constant(SC_STONEWAIT); export_constant(SC_PROVOKE); export_constant(SC_ENDURE); export_constant(SC_TWOHANDQUICKEN); diff --git a/src/map/skill.cpp b/src/map/skill.cpp index b3eefd5b205..bd5c0bd238c 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -1622,7 +1622,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1 break; case NPC_PETRIFYATTACK: - sc_start4(src,bl,SC_STONE,(20*skill_lv),skill_lv,0,0,skill_get_time(skill_id,skill_lv),skill_get_time2(skill_id,skill_lv)); + sc_start4(src,bl,SC_STONEWAIT,(20*skill_lv),skill_lv,src->id,skill_get_time(skill_id,skill_lv),0,skill_get_time2(skill_id,skill_lv)); break; case NPC_CURSEATTACK: sc_start(src,bl,SC_CURSE,(20*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)); @@ -7945,15 +7945,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui clif_skill_nodamage(src, bl, skill_id == SM_SELFPROVOKE ? SM_PROVOKE : skill_id, skill_lv, i); unit_skillcastcancel(bl, 2); - if( tsc && tsc->count ) - { - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - if( tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE ) - status_change_end(bl, SC_STONE, INVALID_TIMER); - status_change_end(bl, SC_SLEEP, INVALID_TIMER); - status_change_end(bl, SC_TRICKDEAD, INVALID_TIMER); - } - if( dstmd ) { dstmd->state.provoke_flag = src->id; @@ -8714,22 +8705,19 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui case MG_STONECURSE: { - int brate = 0; if (status_has_mode(tstatus,MD_STATUSIMMUNE)) { - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); + if (sd) + clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); break; } if(status_isimmune(bl) || !tsc) break; + int32 brate = 0; + if (sd && sd->sc.data[SC_PETROLOGY_OPTION]) brate = sd->sc.data[SC_PETROLOGY_OPTION]->val3; - if (tsc && tsc->data[type]) { - status_change_end(bl, type, INVALID_TIMER); - if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); - break; - } if (sc_start4(src,bl,type,(skill_lv*4+20)+brate, skill_lv, src->id, skill_get_time(skill_id, skill_lv), 0, skill_get_time2(skill_id,skill_lv))) @@ -9844,13 +9832,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui unit_skillcastcancel(bl,0); - if(tsc && tsc->count){ - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - if(tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE) - status_change_end(bl, SC_STONE, INVALID_TIMER); - status_change_end(bl, SC_SLEEP, INVALID_TIMER); - } - if (dstmd) mob_target(dstmd, src, skill_get_range2(src, skill_id, skill_lv, true)); } @@ -10333,8 +10314,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui case SC_BURNING: sc_start4(src,bl,type,100,skill_lv,1000,src->id,0,skill_get_time2(skill_id,skill_lv)); break; - case SC_VOICEOFSIREN: - sc_start2(src,bl,type,100,skill_lv,src->id,skill_get_time2(skill_id,skill_lv)); + case SC_STONEWAIT: + sc_start4(src,bl,type,100,skill_lv,src->id,skill_get_time(skill_id, skill_lv), 0, skill_get_time2(skill_id,skill_lv)); break; default: sc_start2(src,bl,type,100,skill_lv,src->id,skill_get_time2(skill_id,skill_lv)); @@ -10784,22 +10765,12 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui if( bl->id == skill_area_temp[1] ) break; // Already work on this target - if( tsc && tsc->data[type] ) - status_change_end(bl,type,INVALID_TIMER); - else - status_change_start(src,bl,type,10000,skill_lv,src->id,0,0,skill_get_time(skill_id, skill_lv),SCSTART_NOTICKDEF); + status_change_start(src,bl,type,10000,skill_lv,src->id,skill_get_time(skill_id, skill_lv),0,skill_get_time2(skill_id,skill_lv), SCSTART_NOTICKDEF); } else { int rate = 45 + 5 * skill_lv + ( sd? sd->status.job_level : 50 ) / 4; // IroWiki says Rate should be reduced by target stats, but currently unknown if( rnd()%100 < rate ) { // Success on First Target - if( !tsc->data[type] ) - rate = status_change_start(src,bl,type,10000,skill_lv,src->id,0,0,skill_get_time(skill_id, skill_lv),SCSTART_NOTICKDEF); - else { - rate = 1; - status_change_end(bl,type,INVALID_TIMER); - } - - if( rate ) { + if( status_change_start(src,bl,type,10000,skill_lv,src->id,skill_get_time(skill_id, skill_lv),0,skill_get_time2(skill_id,skill_lv), SCSTART_NOTICKDEF) ) { clif_skill_nodamage(src,bl,skill_id,skill_lv,1); skill_area_temp[1] = bl->id; map_foreachinallrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_nodamage_id); @@ -15908,7 +15879,7 @@ int skill_unit_onplace_timer(struct skill_unit *unit, struct block_list *bl, t_t case UNT_ZENKAI_LAND: switch (rnd()%2 + 1) { case 1: - sc_start2(ss, bl, SC_STONE, sg->val1*5, sg->skill_lv, ss->id, skill_get_time(sg->skill_id, sg->skill_lv)); + sc_start4(ss, bl, SC_STONEWAIT, sg->val1*5, sg->skill_lv, ss->id, skill_get_time(sg->skill_id, sg->skill_lv), 0, skill_get_time2(sg->skill_id, sg->skill_lv)); break; case 2: sc_start2(ss, bl, SC_POISON, sg->val1*5, sg->skill_lv, ss->id, skill_get_time(sg->skill_id, sg->skill_lv)); diff --git a/src/map/status.cpp b/src/map/status.cpp index 7c7d59ff90c..66c8450521f 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -1000,10 +1000,8 @@ 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(it.first); - if (it.second->flag[SCF_REMOVEONDAMAGED]) { - if (type != SC_STONE || (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)) - status_change_end(target, type, INVALID_TIMER); - } + if (sc->data[type] && it.second->flag[SCF_REMOVEONDAMAGED]) + status_change_end(target, type, INVALID_TIMER); } if ((sce=sc->data[SC_ENDURE]) && !sce->val4) { /** [Skotlex] @@ -1616,7 +1614,7 @@ bool status_check_skilluse(struct block_list *src, struct block_list *target, ui if((skill_id == WZ_STORMGUST || skill_id == WZ_FROSTNOVA || skill_id == NJ_HYOUSYOURAKU || skill_id == NPC_STORMGUST2) && tsc->data[SC_FREEZE]) return false; - if(skill_id == PR_LEXAETERNA && (tsc->data[SC_FREEZE] || (tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE))) + if(skill_id == PR_LEXAETERNA && (tsc->data[SC_FREEZE] || tsc->data[SC_STONE])) return false; if (tsc->data[SC__MANHOLE] && !skill_get_inf2(skill_id, INF2_TARGETMANHOLE)) return false; @@ -4797,7 +4795,6 @@ void status_calc_state( struct block_list *bl, struct status_change *sc, std::bi (sc->data[SC_DANCING]->val1&0xFFFF) == CG_HERMODE )) || (sc->data[SC_CRYSTALIZE] && bl->type != BL_MOB) - || (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) ) sc->cant.move += (start ? 1 : ((sc->cant.move) ? -1 : 0)); } @@ -6949,7 +6946,7 @@ static defType status_calc_def(struct block_list *bl, struct status_change *sc, def -= 20 + 10 * sc->data[SC_ANGRIFFS_MODUS]->val1; if(sc->data[SC_STONEHARDSKIN]) def += sc->data[SC_STONEHARDSKIN]->val1; - if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) + if(sc->data[SC_STONE]) def >>=1; if(sc->data[SC_FREEZE]) def >>=1; @@ -7113,7 +7110,7 @@ static defType status_calc_mdef(struct block_list *bl, struct status_change *sc, mdef += (sc->data[SC_ENDURE]->val4 == 0) ? sc->data[SC_ENDURE]->val1 : 1; if(sc->data[SC_STONEHARDSKIN]) mdef += sc->data[SC_STONEHARDSKIN]->val1; - if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) + if(sc->data[SC_STONE]) mdef += 25 * mdef / 100; if(sc->data[SC_FREEZE]) mdef += 25 * mdef / 100; @@ -7943,7 +7940,7 @@ static unsigned char status_calc_element(struct block_list *bl, struct status_ch if(sc->data[SC_FREEZE] || sc->data[SC_CRYSTAL_ARMOR_OPTION]) return ELE_WATER; - if((sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) || sc->data[SC_STRONG_PROTECTION_OPTION]) + if(sc->data[SC_STONE] || sc->data[SC_STRONG_PROTECTION_OPTION]) return ELE_EARTH; if(sc->data[SC_FLAMEARMOR_OPTION]) return ELE_FIRE; @@ -7977,7 +7974,7 @@ static unsigned char status_calc_element_lv(struct block_list *bl, struct status if(sc->data[SC_FREEZE]) return 1; - if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) + if(sc->data[SC_STONE]) return 1; if(sc->data[SC_BENEDICTIO]) return 1; @@ -8730,7 +8727,7 @@ t_tick status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_ sc = NULL; #ifdef RENEWAL - uint16 levelAdv = (pow(max(0, status_get_lv(src) - status_get_lv(bl)), 2) / 5) * 100; + uint16 levelAdv = (static_cast(pow(max(0, status_get_lv(src) - status_get_lv(bl)), 2)) / 5) * 100; #endif switch (type) { @@ -9241,6 +9238,24 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty if(status->mode&MD_MVP && !(flag&SCSTART_NOAVOID) && scdb->flag[SCF_MVPRESIST]) return 0; + // End the SCs from the list and immediately return + // If anything in this list is removed, the rest is ignored. + if (!scdb->endreturn.empty()) { + bool isRemoved = false; + + for (const auto &it : scdb->endreturn) { + sc_type rem_sc = it; + + if (sc->data[rem_sc]) { + status_change_end(bl, rem_sc, INVALID_TIMER); + isRemoved = true; + } + } + + if (isRemoved) // Something was removed, don't give the status + return 1; // Return 1 so that sc_start can be checked as success + } + // Check failing SCs from list if (!scdb->fail.empty()) { for (const auto &it : scdb->fail) { @@ -9288,10 +9303,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty if((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) return 0; break; - case SC_AETERNA: - if( sc->data[SC_STONE] && sc->opt1 == OPT1_STONE ) - return 0; - break; case SC_KYRIE: case SC_TUNAPARTY: if (bl->type == BL_MOB) @@ -9611,14 +9622,45 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty } // Before overlapping fail, one must check for status cured. + std::vector endlist; + + if (type == SC_BERSERK && val3 == SC__BLOODYLUST) //There is some reasons that using SC_BERSERK first before SC__BLOODYLUST itself on Akinari's fix + endlist = status_db.getEnd(SC__BLOODYLUST); + else + endlist = scdb->end; + + // End the SCs from the list + if (!endlist.empty()) { + for (const auto &it : endlist) { + sc_type rem_sc = it; + + if (sc->data[rem_sc]) { + switch (rem_sc) { + case SC_BERSERK: + case SC_SATURDAYNIGHTFEVER: + sc->data[rem_sc]->val2 = 0; // Mark to not lose hp + default: + status_change_end(bl, rem_sc, INVALID_TIMER); + break; + } + } + } + } + + // 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] if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) { status_change_end(bl, SC_CURSE, INVALID_TIMER); - if (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) - status_change_end(bl, SC_STONE, INVALID_TIMER); + status_change_end(bl, SC_STONE, INVALID_TIMER); if (sc->data[SC_CURSE]) { status_change_end(bl, SC_CURSE, INVALID_TIMER); return 1; // End Curse and do not give stat boost @@ -9659,36 +9701,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty break; } - std::vector list; - - if (type == SC_BERSERK && val3 == SC__BLOODYLUST) //There is some reasons that using SC_BERSERK first before SC__BLOODYLUST itself on Akinari's fix - list = status_db.getEnd(SC__BLOODYLUST); - else - list = scdb->end; - - // End the SCs from the list - if (!list.empty()) { - bool isRemoving = false; - - for (const auto &it : list) { - sc_type rem_sc = it; - - if (sc->data[rem_sc]) { - switch (rem_sc) { - case SC_BERSERK: - case SC_SATURDAYNIGHTFEVER: - sc->data[rem_sc]->val2 = 0; // Mark to not lose hp - default: - status_change_end(bl, rem_sc, INVALID_TIMER); - isRemoving = true; - break; - } - } - } - if (isRemoving && scdb->end_return) - return 0; - } - // Check for overlapping fails if( (sce = sc->data[type]) ) { if (scdb->flag[SCF_OVERLAPFAIL]) @@ -10098,11 +10110,8 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty } break; - case SC_STONE: - val3 = max(val3, 100); // Incubation time - val4 = max(tick-val3, 100); // Petrify time - tick = val3; - calc_flag.reset(); // Actual status changes take effect on petrified state. + case SC_STONEWAIT: + val3 -= tick; // Petrify time - Incubation time break; case SC_DPOISON: @@ -10114,6 +10123,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty status_zap(bl, diff, 0); } // Fall through + case SC_STONE: case SC_POISON: case SC_BLEEDING: case SC_BURNING: @@ -11887,7 +11897,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty break; default: - if (calc_flag.none() && scdb->skill_id == 0 && scdb->icon == EFST_BLANK && scdb->opt1 == OPT1_NONE && scdb->opt2 == OPT2_NONE && scdb->state.none() && scdb->flag.none() && scdb->end.empty() && scdb->fail.empty()) { + if (calc_flag.none() && scdb->skill_id == 0 && scdb->icon == EFST_BLANK && scdb->opt1 == OPT1_NONE && scdb->opt2 == OPT2_NONE && scdb->state.none() && scdb->flag.none() && scdb->end.empty() && scdb->endreturn.empty() && scdb->fail.empty()) { // Status change with no calc, no icon, and no skill associated...? ShowWarning("status_change_start: Status %s (%d) is bare. Add the NoWarning flag to suppress this message.\n", script_get_constant_str("SC_", type), type); return 0; @@ -11909,9 +11919,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty clif_changelook(bl,LOOK_BODY2,0); break; case SC_STONE: - if (val3 > 0) - break; //Incubation time still active - //Fall through + case SC_STONEWAIT: case SC_POISON: case SC_DPOISON: case SC_BLEEDING: @@ -12030,12 +12038,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty uint16 disable_opt_flag = false; switch(type) { - case SC_STONE: - if (val3 > 0) - sc->opt1 = OPT1_STONEWAIT; - else - sc->opt1 = OPT1_STONE; - break; case SC_DANCING: if ((val1&0xFFFF) != CG_MOONLIT) sc->opt3 |= OPT3_MOONLIT; @@ -12129,8 +12131,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty } // Non-zero - // Calc state for SC_STONE when OPT1_STONE in the timer - if (sc_isnew && scdb->state.any() && type != SC_STONE) + if (sc_isnew && scdb->state.any()) status_calc_state(bl, sc, scdb->state, true); if (sd) { @@ -12385,6 +12386,7 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const // 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: @@ -12952,9 +12954,6 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const bool disable_opt_flag = false; switch (type) { - case SC_STONE: - sc->opt1 = OPT1_NONE; - break; case SC_DANCING: if ((sce->val1&0xFFFF) == CG_MOONLIT) sc->opt3 &= ~OPT3_MOONLIT; @@ -13031,6 +13030,10 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const if(opt_flag[SCF_ONTOUCH] && sd && !sd->state.warping && map_getcell(bl->m,bl->x,bl->y,CELL_CHKNPC)) npc_touch_area_allnpc(sd,bl->m,bl->x,bl->y); // Trigger on-touch event. + // Needed to be here to make sure OPT1_STONEWAIT has been cleared from the target + if (type == SC_STONEWAIT && sce->val4 > -1) + sc_start2(bl, bl, SC_STONE, 100, sce->val1, sce->val2, sce->val3); + ers_free(sc_data_ers, sce); return 1; } @@ -13140,28 +13143,8 @@ TIMER_FUNC(status_change_timer){ break; case SC_STONE: - if (sc->opt1 == OPT1_STONEWAIT && sce->val4) { - sce->val3 = 0; //Incubation time used up - unit_stop_attack(bl); - if (sc->data[SC_DANCING]) { - unit_stop_walking(bl, 1); - status_change_end(bl, SC_DANCING, INVALID_TIMER); - } - status_change_end(bl, SC_AETERNA, INVALID_TIMER); - sc->opt1 = OPT1_STONE; - - std::shared_ptr scdb = status_db.find(type); - - status_calc_state(bl,sc,scdb->state,1); - clif_changeoption(bl); - sc_timer_next(min(sce->val4, interval) + tick); - sce->val4 -= interval; //Remaining time - status_calc_bl_(bl, scdb->calc_flag); - return 0; - } - if (sce->val4 >= 0 && !(sce->val3) && status->hp > status->max_hp / 4) { - status_percent_damage(NULL, bl, 1, 0, false); - } + if (sce->val4 >= 0 && status->hp > status->max_hp / 4) + status_percent_damage(nullptr, bl, -1, 0, false); break; case SC_POISON: @@ -15080,15 +15063,35 @@ uint64 StatusDatabase::parseBodyNode(const ryml::NodeRef& node) { } if (this->nodeExists(node, "EndReturn")) { - bool end; + const ryml::NodeRef &endNode = node["EndReturn"]; - if (!this->asBool(node, "EndReturn", end)) - return 0; + for (const auto &it : endNode) { + std::string end; + c4::from_chars(it.key(), &end); - status->end_return = end; - } else { - if (!exists) - status->end_return = false; + std::string end_constant = "SC_" + end; + int64 constant; + + if (!script_get_constant(end_constant.c_str(), &constant)) { + this->invalidWarning(endNode, "EndReturn status %s is invalid.\n", end.c_str()); + return 0; + } + + if (!this->validateStatus(static_cast(constant))) { + this->invalidWarning(endNode, "EndReturn status %s is out of bounds.\n", end.c_str()); + return 0; + } + + bool active; + + if (!this->asBool(endNode, end, active)) + return 0; + + if (active) + status->endreturn.push_back(static_cast(constant)); + else + util::vector_erase_if_exists(status->endreturn, static_cast(constant)); + } } if (!exists) { diff --git a/src/map/status.hpp b/src/map/status.hpp index ff1dd4e5b1d..79989de7ab0 100644 --- a/src/map/status.hpp +++ b/src/map/status.hpp @@ -150,7 +150,7 @@ enum sc_type : int16 { //First we enumerate common status ailments which are often used around. SC_STONE = 0, - SC_COMMON_MIN = 0, // begin + SC_COMMON_MIN = SC_STONE, // begin SC_FREEZE, SC_STUN, SC_SLEEP, @@ -161,7 +161,8 @@ enum sc_type : int16 { SC_BLIND, SC_BLEEDING, SC_DPOISON, //10 - SC_COMMON_MAX = 10, // end + SC_STONEWAIT, + SC_COMMON_MAX = SC_STONEWAIT, // end //Next up, we continue on 20, to leave enough room for additional "common" ailments in the future. SC_PROVOKE = 20, @@ -2870,14 +2871,14 @@ struct s_status_change_db { uint16 skill_id; ///< Associated skill for (addeff) duration lookups std::vector end; ///< List of SC that will be ended when this SC is activated std::vector fail; ///< List of SC that causing this SC cannot be activated - bool end_return; ///< After SC ends the SC from end list, it does nothing + std::vector endreturn; ///< List of SC that will be ended when this SC is activated and then immediately return t_tick min_duration; ///< Minimum duration effect (after all status reduction) uint16 min_rate; ///< Minimum rate to be applied (after all status reduction) }; class StatusDatabase : public TypesafeCachedYamlDatabase { public: - StatusDatabase() : TypesafeCachedYamlDatabase("STATUS_DB", 1) { + StatusDatabase() : TypesafeCachedYamlDatabase("STATUS_DB", 2) { // All except BASE and extra flags. SCB_BATTLE.set(); SCB_BATTLE.reset(SCB_BASE);