Sfoglia il codice sorgente

Implements new guild skills (#5192)

* Fixes #4183.
* Adds 3 new guild skills: GD_CHARGESHOUT_FLAG, GD_CHARGESHOUT_BEATING, and GD_EMERGENCY_MOVE
* Changes 5 minute guild global cooldown to be 3 minute individual cooldowns for GD_RESTORE, GD_REGENERATION, GD_BATTLEORDER, GD_EMERGENCYCALL, and GD_ITEMEMERGENCYCALL.
* Reduces GD_RESTORE fixed cast time from 10 seconds to 1.
Aleos 4 anni fa
parent
commit
2d97ece2a2

+ 1 - 0
conf/battle/guild.conf

@@ -18,6 +18,7 @@ guild_max_castles: 0
 
 // Activate guild skills delay by relog?
 // Official setting is 5 minutes (300000 ms), otherwise allow guild leaders to relog to cancel the 5 minute delay.
+// Note: This was changed in renewal in favor of individual skill cooldown.
 guild_skill_relog_delay: 300000
 
 // Melee damage adjustments (non skills) for WoE battles (Guild Vs Guild) (Note 2)

+ 0 - 3
db/pre-re/guild_skill_tree.yml

@@ -118,6 +118,3 @@ Body:
 
   - Id: GD_DEVELOPMENT
     MaxLevel: 1
-
-#  - Id: GD_GUILD_STORAGE
-#    MaxLevel: 5

+ 0 - 6
db/pre-re/skill_db.yml

@@ -33671,9 +33671,3 @@ Body:
       IgnoreKagehumi: true
     CastCancel: true
     Duration2: 300000
-  - Id: 10016
-    Name: GD_GUILD_STORAGE
-    Description: Guild Storage Expansion
-    MaxLevel: 5
-    Flags:
-      IsGuild: true

+ 20 - 0
db/re/guild_skill_tree.yml

@@ -130,3 +130,23 @@ Body:
       Level: 1
     - Id: GD_HAWKEYES
       Level: 1
+
+  - Id: GD_CHARGESHOUT_FLAG
+    MaxLevel: 1
+    Required:
+    - Id: GD_EMERGENCYCALL
+      Level: 1
+
+  - Id: GD_CHARGESHOUT_BEATING
+    MaxLevel: 1
+    Required:
+    - Id: GD_CHARGESHOUT_FLAG
+      Level: 1
+
+  - Id: GD_EMERGENCY_MOVE
+    MaxLevel: 1
+    Required:
+    - Id: GD_LEADERSHIP
+      Level: 1
+    - Id: GD_GLORYWOUNDS
+      Level: 1

+ 1 - 1
db/re/mob_db.txt

@@ -3410,7 +3410,7 @@
 //20266,G_ILL_TEDDY_BEAR_G
 //20267,G_ILL_TEDDY_BEAR_W
 //20268,G_ILL_TEDDY_BEAR_B
-//20269,GUILD_SKILL_FLAG
+20269,GUILD_SKILL_FLAG,Guild Skill Flag,Guild Skill Flag,90,30,0,0,0,1,0,0,0,0,1,17,1,80,126,20,10,12,2,0,20,0x120,300,1288,288,384,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 //20270,ILL_TRI_JOINT
 //20271,ILL_STALACTIC_GOLEM
 //20272,ILL_MEGALITH

+ 44 - 16
db/re/skill_db.yml

@@ -37093,12 +37093,10 @@ Body:
     SplashArea: 15
     CastCancel: true
     Duration1: 180000
-    Duration2: 300000
+    Cooldown: 180000
     CastTimeFlags:
       IgnoreDex: true
       IgnoreStatus: true
-    Requires:
-      SpCost: 1
   - Id: 10011
     Name: GD_REGENERATION
     Description: Regeneration
@@ -37112,12 +37110,10 @@ Body:
     SplashArea: 15
     CastCancel: true
     Duration1: 60000
-    Duration2: 300000
+    Cooldown: 180000
     CastTimeFlags:
       IgnoreDex: true
       IgnoreStatus: true
-    Requires:
-      SpCost: 1
   - Id: 10012
     Name: GD_RESTORE
     Description: Restoration
@@ -37130,13 +37126,11 @@ Body:
       IsGuild: true
     SplashArea: 15
     CastCancel: true
-    Duration2: 300000
-    FixedCastTime: 10000
+    Cooldown: 180000
+    FixedCastTime: 1000
     CastTimeFlags:
       IgnoreStatus: true
       IgnoreItemBonus: true
-    Requires:
-      SpCost: 1
   - Id: 10013
     Name: GD_EMERGENCYCALL
     Description: Urgent Call
@@ -37149,14 +37143,12 @@ Body:
       IsGuild: true
       IgnoreKagehumi: true
     CastCancel: true
-    Duration2: 300000
+    Cooldown: 300000
     FixedCastTime: 5000
     CastTimeFlags:
       IgnoreDex: true
       IgnoreStatus: true
       IgnoreItemBonus: true
-    Requires:
-      SpCost: 1
   - Id: 10014
     Name: GD_DEVELOPMENT
     Description: Permanent Development
@@ -37175,13 +37167,49 @@ Body:
       IsGuild: true
       IgnoreKagehumi: true
     CastCancel: true
-    Duration2: 300000
+    Cooldown: 300000
     FixedCastTime: 5000
-    Requires:
-      SpCost: 1
   - Id: 10016
     Name: GD_GUILD_STORAGE
     Description: Guild Storage Expansion
     MaxLevel: 5
     Flags:
       IsGuild: true
+  - Id: 10017
+    Name: GD_CHARGESHOUT_FLAG
+    Description: Charge Shout Flag
+    MaxLevel: 1
+    TargetType: Self
+    DamageFlags:
+      NoDamage: true
+    Flags:
+      IsGuild: true
+    CastCancel: true
+    Duration1: 300000
+    Cooldown: 60000
+    FixedCastTime: 1000
+  - Id: 10018
+    Name: GD_CHARGESHOUT_BEATING
+    Description: Charge Shout Beating
+    MaxLevel: 1
+    TargetType: Self
+    DamageFlags:
+      NoDamage: true
+    Flags:
+      IsGuild: true
+    CastCancel: true
+    Cooldown: 60000
+    FixedCastTime: 1000
+  - Id: 10019
+    Name: GD_EMERGENCY_MOVE
+    Description: Emergency Move
+    MaxLevel: 1
+    TargetType: Self
+    DamageFlags:
+      NoDamage: true
+      Splash: true
+    Flags:
+      IsGuild: true
+    SplashArea: 15
+    Duration1: 10000
+    Cooldown: 600000

+ 28 - 19
src/common/mmo.hpp

@@ -74,7 +74,11 @@ typedef uint32 t_itemid;
 #define MAX_GUILDPOSITION 20	///Increased max guild positions to accomodate for all members [Valaris] (removed) [PoW]
 #define MAX_GUILDEXPULSION 32 ///Max Guild expulsion
 #define MAX_GUILDALLIANCE 16 ///Max Guild alliance
-#define MAX_GUILDSKILL	17 ///Max Guild skills
+#ifdef RENEWAL
+#define MAX_GUILDSKILL	20 ///Max Guild skills
+#else
+#define MAX_GUILDSKILL	15 ///Max Guild skills
+#endif
 #define MAX_GUILDLEVEL 50 ///Max Guild level
 #define MAX_GUARDIANS 8	///Local max per castle. If this value is increased, need to add more fields on MySQL `guild_castle` table [Skotlex]
 #define MAX_QUEST_OBJECTIVES 3 ///Max quest objectives for a quest
@@ -690,6 +694,8 @@ struct guild {
 
 	/* Used by char-server to save events for guilds */
 	unsigned short save_flag;
+
+	int32 chargeshout_flag_id;
 };
 
 struct guild_castle {
@@ -768,24 +774,27 @@ enum e_guild_member_info { //Change Member Infos
 };
 
 enum e_guild_skill {
-	GD_SKILLBASE=10000,
-	GD_APPROVAL=10000,
-	GD_KAFRACONTRACT=10001,
-	GD_GUARDRESEARCH=10002,
-	GD_GUARDUP=10003,
-	GD_EXTENSION=10004,
-	GD_GLORYGUILD=10005,
-	GD_LEADERSHIP=10006,
-	GD_GLORYWOUNDS=10007,
-	GD_SOULCOLD=10008,
-	GD_HAWKEYES=10009,
-	GD_BATTLEORDER=10010,
-	GD_REGENERATION=10011,
-	GD_RESTORE=10012,
-	GD_EMERGENCYCALL=10013,
-	GD_DEVELOPMENT=10014,
-	GD_ITEMEMERGENCYCALL=10015,
-	GD_GUILD_STORAGE=10016,
+	GD_SKILLBASE = 10000,
+	GD_APPROVAL = 10000,
+	GD_KAFRACONTRACT,
+	GD_GUARDRESEARCH,
+	GD_GUARDUP,
+	GD_EXTENSION,
+	GD_GLORYGUILD,
+	GD_LEADERSHIP,
+	GD_GLORYWOUNDS,
+	GD_SOULCOLD,
+	GD_HAWKEYES,
+	GD_BATTLEORDER,
+	GD_REGENERATION,
+	GD_RESTORE,
+	GD_EMERGENCYCALL,
+	GD_DEVELOPMENT,
+	GD_ITEMEMERGENCYCALL,
+	GD_GUILD_STORAGE,
+	GD_CHARGESHOUT_FLAG,
+	GD_CHARGESHOUT_BEATING,
+	GD_EMERGENCY_MOVE,
 	GD_MAX,
 };
 

+ 14 - 5
src/map/battle.cpp

@@ -1822,7 +1822,7 @@ bool battle_can_hit_gvg_target(struct block_list *src,struct block_list *bl,uint
 
 	if (ud && ud->immune_attack)
 		return false;
-	if(md && md->guardian_data) {
+	if(md && (md->guardian_data || md->special_state.ai == AI_GUILD)) {
 		if ((status_bl_has_mode(bl,MD_SKILL_IMMUNE) || (class_ == MOBID_EMPERIUM && !skill_get_inf2(skill_id, INF2_TARGETEMPERIUM))) && flag&BF_SKILL) //Skill immunity.
 			return false;
 		if( src->type != BL_MOB || mob_is_clone( ((struct mob_data*)src)->mob_id ) ){
@@ -1831,8 +1831,13 @@ bool battle_can_hit_gvg_target(struct block_list *src,struct block_list *bl,uint
 			if (class_ == MOBID_EMPERIUM && (!g || guild_checkskill(g,GD_APPROVAL) <= 0 ))
 				return false;
 
-			if (g && battle_config.guild_max_castles && guild_checkcastles(g)>=battle_config.guild_max_castles)
-				return false; // [MouseJstr]
+			if (g != nullptr) {
+				if (battle_config.guild_max_castles && guild_checkcastles(g)>=battle_config.guild_max_castles)
+					return false; // [MouseJstr]
+
+				if (md->special_state.ai == AI_GUILD && g->guild_id == md->master_id)
+					return false;
+			}
 		}
 	}
 	return true;
@@ -8206,8 +8211,12 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 		case BL_PET:
 			if (t_bl->type != BL_MOB && flag&BCT_ENEMY)
 				return 0; //Pet may not attack non-mobs.
-			if (t_bl->type == BL_MOB && ((TBL_MOB*)t_bl)->guardian_data && flag&BCT_ENEMY)
-				return 0; //pet may not attack Guardians/Emperium
+			if (t_bl->type == BL_MOB && flag & BCT_ENEMY) {
+				mob_data *md = BL_CAST(BL_MOB, t_bl);
+
+				if (md->guardian_data || md->special_state.ai == AI_GUILD)
+					return 0; //pet may not attack Guardians/Emperium
+			}
 			break;
 		case BL_SKILL: {
 				struct skill_unit *su = (struct skill_unit *)src;

+ 2 - 0
src/map/chrif.cpp

@@ -1364,8 +1364,10 @@ int chrif_skillcooldown_save(struct map_session_data *sd) {
 		if (!sd->scd[i])
 			continue;
 
+#ifndef RENEWAL
 		if (!battle_config.guild_skill_relog_delay && (sd->scd[i]->skill_id >= GD_BATTLEORDER && sd->scd[i]->skill_id <= GD_EMERGENCYCALL))
 			continue;
+#endif
 
 		timer = get_timer(sd->scd[i]->timer);
 		if (timer == NULL || timer->func != skill_blockpc_end || DIFF_TICK(timer->tick, tick) < 0)

+ 1 - 1
src/map/clif.cpp

@@ -12630,7 +12630,7 @@ void clif_parse_skill_toid( struct map_session_data* sd, uint16 skill_id, uint16
 	sd->skillitem = sd->skillitemlv = 0;
 
 	if( SKILL_CHK_GUILD(skill_id) ) {
-		if( sd->state.gmaster_flag )
+		if( sd->state.gmaster_flag || skill_id == GD_CHARGESHOUT_BEATING )
 			skill_lv = guild_checkskill(sd->guild, skill_id);
 		else
 			skill_lv = 0;

+ 8 - 0
src/map/guild.cpp

@@ -606,10 +606,12 @@ int guild_recv_info(struct guild *sg) {
 		//Perform the check on the user because the first load
 		guild_check_member(sg);
 		if ((sd = map_nick2sd(sg->master,false)) != NULL) {
+#ifndef RENEWAL
 			//If the guild master is online the first time the guild_info is received,
 			//that means he was the first to join, so apply guild skill blocking here.
 			if( battle_config.guild_skill_relog_delay )
 				guild_block_skill(sd, battle_config.guild_skill_relog_delay);
+#endif
 
 			//Also set the guild master flag.
 			sd->guild = g;
@@ -804,9 +806,11 @@ void guild_member_joined(struct map_session_data *sd) {
 	}
 	if (strcmp(sd->status.name,g->master) == 0) {	// set the Guild Master flag
 		sd->state.gmaster_flag = 1;
+#ifndef RENEWAL
 		// prevent Guild Skills from being used directly after relog
 		if( battle_config.guild_skill_relog_delay )
 			guild_block_skill(sd, battle_config.guild_skill_relog_delay);
+#endif
 	}
 	i = guild_getindex(g, sd->status.account_id, sd->status.char_id);
 	if (i == -1)
@@ -1002,6 +1006,7 @@ int guild_member_withdraw(int guild_id, uint32 account_id, uint32 char_id, int f
 		status_change_end(&sd->bl,SC_GLORYWOUNDS,INVALID_TIMER);
 		status_change_end(&sd->bl,SC_SOULCOLD,INVALID_TIMER);
 		status_change_end(&sd->bl,SC_HAWKEYES,INVALID_TIMER);
+		status_change_end(&sd->bl,SC_EMERGENCY_MOVE,INVALID_TIMER);
 		//@TODO: Send emblem update to self and people around
 	}
 	return 0;
@@ -1882,6 +1887,7 @@ int guild_broken(int guild_id,int flag) {
 			status_change_end(&sd->bl,SC_GLORYWOUNDS,INVALID_TIMER);
 			status_change_end(&sd->bl,SC_SOULCOLD,INVALID_TIMER);
 			status_change_end(&sd->bl,SC_HAWKEYES,INVALID_TIMER);
+			status_change_end(&sd->bl,SC_EMERGENCY_MOVE,INVALID_TIMER);
 		}
 	}
 
@@ -1966,8 +1972,10 @@ int guild_gm_changed(int guild_id, uint32 account_id, uint32 char_id, time_t tim
 		g->member[0].sd->state.gmaster_flag = 1;
 		clif_name_area(&g->member[0].sd->bl);
 		//Block his skills to prevent abuse.
+#ifndef RENEWAL
 		if (battle_config.guild_skill_relog_delay)
 			guild_block_skill(g->member[0].sd, battle_config.guild_skill_relog_delay);
+#endif
 	}
 
 	// announce the change to all guild members

+ 1 - 0
src/map/map.cpp

@@ -2086,6 +2086,7 @@ int map_quit(struct map_session_data *sd) {
 		status_change_end(&sd->bl, SC_GLORYWOUNDS, INVALID_TIMER);
 		status_change_end(&sd->bl, SC_SOULCOLD, INVALID_TIMER);
 		status_change_end(&sd->bl, SC_HAWKEYES, INVALID_TIMER);
+		status_change_end(&sd->bl, SC_EMERGENCY_MOVE, INVALID_TIMER);
 		status_change_end(&sd->bl, SC_CHASEWALK2, INVALID_TIMER);
 		if(sd->sc.data[SC_PROVOKE] && sd->sc.data[SC_PROVOKE]->timer == INVALID_TIMER)
 			status_change_end(&sd->bl, SC_PROVOKE, INVALID_TIMER); //Infinite provoke ends on logout

+ 1 - 0
src/map/map.hpp

@@ -374,6 +374,7 @@ enum mob_ai {
 	AI_ZANZOU,
 	AI_LEGION,
 	AI_FAW,
+	AI_GUILD,
 	AI_MAX
 };
 

+ 1 - 0
src/map/mob.hpp

@@ -76,6 +76,7 @@ enum MOBID {
 	MOBID_S_HORNET			= 2158,
 	MOBID_S_GIANT_HORNET,
 	MOBID_S_LUCIOLA_VESPA,
+	MOBID_GUILD_SKILL_FLAG	= 20269,
 };
 
 ///Mob skill states.

+ 2 - 0
src/map/script_constants.hpp

@@ -1608,6 +1608,7 @@
 	export_constant(SC_EP16_2_BUFF_SS);
 	export_constant(SC_EP16_2_BUFF_SC);
 	export_constant(SC_EP16_2_BUFF_AC);
+	export_constant(SC_EMERGENCY_MOVE);
 #ifdef RENEWAL
 	export_constant(SC_EXTREMITYFIST2);
 #endif
@@ -3774,6 +3775,7 @@
 	export_constant(AI_ZANZOU);
 	export_constant(AI_LEGION);
 	export_constant(AI_FAW);
+	export_constant(AI_GUILD);
 
 	/* battle flags */
 	export_constant(BF_NONE);

+ 44 - 3
src/map/skill.cpp

@@ -2033,6 +2033,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1
 					case SC_ENTRY_QUEUE_APPLY_DELAY:	case SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT:
 					case SC_REUSE_LIMIT_LUXANIMA:	case SC_LUXANIMA:	case SC_SOULENERGY:
 					case SC_EP16_2_BUFF_SS:		case SC_EP16_2_BUFF_SC:	case SC_EP16_2_BUFF_AC:
+					case SC_EMERGENCY_MOVE:
 						continue;
 					case SC_WHISTLE:		case SC_ASSNCROS:		case SC_POEMBRAGI:
 					case SC_APPLEIDUN:		case SC_HUMMING:		case SC_DONTFORGETME:
@@ -8473,6 +8474,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 					case SC_LHZ_DUN_N1:		case SC_LHZ_DUN_N2:			case SC_LHZ_DUN_N3:			case SC_LHZ_DUN_N4:
 					case SC_REUSE_LIMIT_LUXANIMA:	case SC_LUXANIMA:	case SC_SOULENERGY:
 					case SC_EP16_2_BUFF_SS:		case SC_EP16_2_BUFF_SC:	case SC_EP16_2_BUFF_AC:
+					case SC_EMERGENCY_MOVE:
 						continue;
 					case SC_WHISTLE:
 					case SC_ASSNCROS:
@@ -9342,6 +9344,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case GD_BATTLEORDER:
 	case GD_REGENERATION:
 	case GD_RESTORE:
+	case GD_EMERGENCY_MOVE:
 		if(flag&1) {
 			if (status_get_guild_id(src) == status_get_guild_id(bl)) {				
 				if( skill_id == GD_RESTORE )
@@ -9356,7 +9359,11 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 				src,skill_id,skill_lv,tick, flag|BCT_GUILD|1,
 				skill_castend_nodamage_id);
 			if (sd)
-				guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
+#ifdef RENEWAL
+				skill_blockpc_start(sd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+#else
+				guild_block_skill(sd, skill_get_time2(skill_id, skill_lv));
+#endif
 		}
 		break;
 	case GD_EMERGENCYCALL:
@@ -9395,9 +9402,39 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 				}
 			}
 			if (sd)
-				guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
+#ifdef RENEWAL
+				skill_blockpc_start(sd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+#else
+				guild_block_skill(sd, skill_get_time2(skill_id, skill_lv));
+#endif
+		}
+		break;
+	case GD_CHARGESHOUT_FLAG:
+		if (sd && sd->guild && sd->state.gmaster_flag == 1) {
+			mob_data *md = mob_once_spawn_sub(src, src->m, src->x, src->y, sd->guild->name, MOBID_GUILD_SKILL_FLAG, nullptr, SZ_SMALL, AI_GUILD);
+
+			if (md) {
+				sd->guild->chargeshout_flag_id = md->bl.id;
+				md->master_id = src->id;
+
+				if (md->deletetimer != INVALID_TIMER)
+					delete_timer(md->deletetimer, mob_timer_delete);
+				md->deletetimer = add_timer(gettick() + skill_get_time(GD_CHARGESHOUT_FLAG, skill_lv), mob_timer_delete, md->bl.id, 0);
+				mob_spawn(md);
+			}
 		}
 		break;
+	case GD_CHARGESHOUT_BEATING:
+		if (sd && sd->guild && map_blid_exists(sd->guild->chargeshout_flag_id)) {
+			block_list *mob_bl = map_id2bl(sd->guild->chargeshout_flag_id);
+
+			if (pc_setpos(sd, map_id2index(mob_bl->m), mob_bl->x, mob_bl->y, CLR_RESPAWN) != SETPOS_OK)
+				clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+			else
+				clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+		} else
+			clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+		break;
 
 	case SG_FEEL:
 		//AuronX reported you CAN memorize the same map as all three. [Skotlex]
@@ -9967,6 +10004,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 					case SC_ENTRY_QUEUE_APPLY_DELAY:	case SC_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT:
 					case SC_REUSE_LIMIT_LUXANIMA:		case SC_LUXANIMA:	case SC_SOULENERGY:
 					case SC_EP16_2_BUFF_SS:		case SC_EP16_2_BUFF_SC:	case SC_EP16_2_BUFF_AC:
+					case SC_EMERGENCY_MOVE:
 					continue;
 				case SC_ASSUMPTIO:
 					if( bl->type == BL_MOB )
@@ -15894,6 +15932,9 @@ bool skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_i
 		case GD_BATTLEORDER:
 		case GD_REGENERATION:
 		case GD_RESTORE:
+		case GD_CHARGESHOUT_FLAG:
+		case GD_CHARGESHOUT_BEATING:
+		case GD_EMERGENCY_MOVE:
 			if (!map_flag_gvg2(sd->bl.m)) {
 				clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
 				return false;
@@ -15901,7 +15942,7 @@ bool skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_i
 		case GD_EMERGENCYCALL:
 		case GD_ITEMEMERGENCYCALL:
 			// other checks were already done in skill_isNotOk()
-			if (!sd->status.guild_id || !sd->state.gmaster_flag)
+			if (!sd->status.guild_id || (sd->state.gmaster_flag == 0 && skill_id != GD_CHARGESHOUT_BEATING))
 				return false;
 			break;
 

+ 10 - 0
src/map/status.cpp

@@ -802,6 +802,10 @@ void initChangeTables(void)
 	set_sc( GD_BATTLEORDER		, SC_BATTLEORDERS	, EFST_GDSKILL_BATTLEORDER	, SCB_STR|SCB_INT|SCB_DEX );
 	set_sc( GD_REGENERATION		, SC_REGENERATION	, EFST_GDSKILL_REGENERATION	, SCB_REGEN );
 
+#ifdef RENEWAL
+	set_sc( GD_EMERGENCY_MOVE	, SC_EMERGENCY_MOVE	, EFST_INC_AGI	, SCB_SPEED );
+#endif
+
 	/* Rune Knight */
 	set_sc( RK_ENCHANTBLADE		, SC_ENCHANTBLADE	, EFST_ENCHANTBLADE		, SCB_NONE );
 	set_sc( RK_DRAGONHOWLING	, SC_FEAR		, EFST_BLANK			, SCB_FLEE|SCB_HIT );
@@ -7429,6 +7433,8 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha
 			val = max(val, sc->data[SC_DORAM_WALKSPEED]->val1);
 		if (sc->data[SC_RUSHWINDMILL])
 			val = max(val, 25); // !TODO: Confirm bonus movement speed
+		if (sc->data[SC_EMERGENCY_MOVE])
+			val = max(val, sc->data[SC_EMERGENCY_MOVE]->val2);
 
 		// !FIXME: official items use a single bonus for this [ultramage]
 		if( sc->data[SC_SPEEDUP0] ) // Temporary item-based speedup
@@ -12071,6 +12077,9 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			tick_time = 1000;
 			val4 = tick / tick_time;
 			break;
+		case SC_EMERGENCY_MOVE:
+			val2 = 25; // Movement speed increase
+			break;
 
 		case SC_SUNSTANCE:
 			val2 = 2 + val1; // ATK Increase
@@ -14948,6 +14957,7 @@ void status_change_clear_buffs(struct block_list* bl, uint8 type)
 			case SC_GLORYWOUNDS:
 			case SC_SOULCOLD:
 			case SC_HAWKEYES:
+			case SC_EMERGENCY_MOVE:
 			case SC_SAFETYWALL:
 			case SC_PNEUMA:
 			case SC_NOCHAT:

+ 2 - 0
src/map/status.hpp

@@ -939,6 +939,8 @@ enum sc_type : int16 {
 	SC_EP16_2_BUFF_SC,
 	SC_EP16_2_BUFF_AC,
 
+	SC_EMERGENCY_MOVE,
+
 #ifdef RENEWAL
 	SC_EXTREMITYFIST2, //! NOTE: This SC should be right before SC_MAX, so it doesn't disturb if RENEWAL is disabled
 #endif