فهرست منبع

Implements SC_STONEWAIT (#6794)

* Fixes #6748.
* Implements SC_STONEWAIT to be used with OPT1_STONEWAIT.
* Removes a lot of hard coded OPT1_STONE checks now that the two states are split to their own statuses.
* Fixes SC_STONE not ending when the target receives damage.
* Fixes SC_STONE getting overwritten by other statuses the have OPT1 states.
* SC_STONE should damage the target by 1% of MaxHP, not CurrentHP.
* All skills that give StoneWait aside from Stone Curse are 100ms.
* Confirmed that Sienna Execrate is 8 + 2 * SkillLv seconds of Stone.
* Moves Provoke and Mind Breaker status clearings to the status database.
* Converts EndReturn in the status database from a boolean to a list of statuses. Statuses defined in this list will be ended and then the status making the call will not take affect.
Thanks to @Singe-Horizontal, @Playtester, and @Lemongrass3110!
Aleos 3 سال پیش
والد
کامیت
cee161e677
11فایلهای تغییر یافته به همراه233 افزوده شده و 181 حذف شده
  1. 2 2
      db/import-tmpl/status.yml
  2. 9 7
      db/pre-re/skill_db.yml
  3. 43 10
      db/pre-re/status.yml
  4. 12 8
      db/re/skill_db.yml
  5. 45 10
      db/re/status.yml
  6. 2 2
      db/status.yml
  7. 4 2
      doc/status.txt
  8. 2 1
      src/map/script_constants.hpp
  9. 10 39
      src/map/skill.cpp
  10. 99 96
      src/map/status.cpp
  11. 5 4
      src/map/status.hpp

+ 2 - 2
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

+ 9 - 7
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

+ 43 - 10
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

+ 12 - 8
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:

+ 45 - 10
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

+ 2 - 2
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:

+ 4 - 2
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.
 
 ---------------------------------------
 

+ 2 - 1
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);

+ 10 - 39
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));

+ 99 - 96
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<sc_type>(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<uint16>(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<sc_type> 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<sc_type> 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<s_status_change_db> 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<sc_type>(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<sc_type>(constant));
+			else
+				util::vector_erase_if_exists(status->endreturn, static_cast<sc_type>(constant));
+		}
 	}
 
 	if (!exists) {

+ 5 - 4
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<sc_type> end;		///< List of SC that will be ended when this SC is activated
 	std::vector<sc_type> 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<sc_type> 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<uint16, s_status_change_db> {
 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);