Преглед на файлове

Alchemist Marine Spheres (NPC_RANDOMMOVE, NPC_SPEEDUP, etc.) (#9065)

- Implemented NPC_RANDOMMOVE
  * NPC_RANDOMMOVE makes a monster start moving away from its target while showing a fake castbar
  * This fake castbar state is called "trickcasting"
  * Movement can go up to 8 cells and cannot be stopped, but requires the monster to be fast enough
  * This skill and NPC_RUN now make monsters escape in a straight line only
- Implemented NPC_SPEEDUP
  * NPC_SPEEDUP speeds up moving during trickcasting state (250ms less per cell)
  * If close to the end, NPC_SPEEDUP ends the trickasting state and makes the monster stop
- Updated the "Alchemist" monster skill condition
  * The monster needs to have a special AI (such as being summoned by an Alchemist) to apply
  * The monster needs to be wounded
  * The monster cannot be in trickcasting state
- Added new "trickcasting" condition (monster needs to be in trickcasting state to use skill)
- NPC_SELFDESTRUCTION of Alchemist-summoned Marine Spheres can now hit the Alchemist's slaves
  * Moved the BCT_WOS checks into battle_check_target and made it also exclude master
  * Defined BCT_SLAVE as a simplified way to say it should only target slaves of caster/master
- Marine Spheres summoned by Alchemists now keep their base speed of 800
- Marine Spheres now move away from their attacker when hit instead of their master
- Marine Spheres will now use NPC_SPEEDUP every second while in trickcasting state
- Marine Spheres now only explode when still wounded after the trickcasting state ends
- Fixed an issue that caused monsters to stop when casting an Idle skill while moving
- Fixed an issue that caused monsters to sometimes never cast skills while moving
- Fixed the "can_escape" state not actually prevent stopping
- Fixes #8301
Playtester преди 3 месеца
родител
ревизия
8edbda178b
променени са 11 файла, в които са добавени 94 реда и са изтрити 56 реда
  1. 1 0
      conf/battle/monster.conf
  2. 2 0
      db/import-tmpl/mob_skill_db.txt
  3. 5 3
      db/pre-re/mob_skill_db.txt
  4. 5 3
      db/re/mob_skill_db.txt
  5. 4 0
      src/map/battle.cpp
  6. 2 1
      src/map/battle.hpp
  7. 16 10
      src/map/mob.cpp
  8. 3 1
      src/map/mob.hpp
  9. 31 21
      src/map/skill.cpp
  10. 3 3
      src/map/status.cpp
  11. 22 14
      src/map/unit.cpp

+ 1 - 0
conf/battle/monster.conf

@@ -192,6 +192,7 @@ randomize_center_cell: yes
 slaves_inherit_mode: 4
 
 // Do summon slaves have the same walking speed as their master?
+// Does not apply to Marine Spheres summoned by an Alchemist
 // NOTE: The default is 3 for official servers.
 // 0: Never.
 // 1: If the master can walk

+ 2 - 0
db/import-tmpl/mob_skill_db.txt

@@ -46,6 +46,8 @@
 //	mobnearbygt		When monsters in range become greater than specified number.
 //	groundattacked		When mob is hit by ground targeted skill (no condition value).
 //	damagedgt		When single attack deals greater damage than specified number.
+//	alchemist		When mob has a special AI, is not trickcasting, and is wounded (no condition value).
+//	trickcasting		Used once mob started moving through NPC_RANDOMMOVE until disabled (no condition value).
 //
 //	Status abnormalities specified through the statuson/statusoff system:
 //	    anybad (any type of state change) / stone / freeze / stun / sleep /

+ 5 - 3
db/pre-re/mob_skill_db.txt

@@ -47,6 +47,8 @@
 //	mobnearbygt		When monsters in range become greater than specified number.
 //	groundattacked		When mob is hit by ground targeted skill (no condition value).
 //	damagedgt		When single attack deals greater damage than specified number.
+//	alchemist		When mob has a special AI, is not trickcasting, and is wounded (no condition value).
+//	trickcasting		Used once mob started moving through NPC_RANDOMMOVE until disabled (no condition value).
 //
 //	Status abnormalities specified through the statuson/statusoff system:
 //	    anybad (any type of state change) / stone / freeze / stun / sleep /
@@ -717,11 +719,11 @@
 1141,Marina@NPC_CRITICALSLASH,attack,170,1,500,500,5000,no,target,always,0,,,,,,6,
 1141,Marina@NPC_EMOTION,walk,197,1,2000,0,5000,yes,self,always,0,19,,,,,,
 1141,Marina@NPC_WATERATTACK,attack,184,2,500,500,5000,no,target,always,0,,,,,,6,
-1142,Marine Sphere@NPC_RANDOMMOVE,idle,331,1,10000,0,30000,no,master,alchemist,,,,,,,,
-1142,Marine Sphere@NPC_SELFDESTRUCTION,idle,173,1,10000,3000,0,no,self,afterskill,331,,,,,,,
+1142,Marine Sphere@NPC_RANDOMMOVE,idle,331,1,10000,0,30000,no,target,alchemist,,,,,,,,
 1142,Marine Sphere@NPC_SELFDESTRUCTION,idle,173,1,500,2000,5000,no,self,myhpltmaxrate,99,,,,,,,
 1142,Marine Sphere@NPC_SELFDESTRUCTION,idle,173,1,10000,2000,5000,no,self,skillused,173,,,,,,,
-1142,Marine Sphere@NPC_SPEEDUP,idle,332,1,10000,0,700,yes,target,always,,,,,,,,
+1142,Marine Sphere@NPC_SELFDESTRUCTION,idle,173,1,10000,0,0,yes,self,alchemist,,,,,,,,
+1142,Marine Sphere@NPC_SPEEDUP,idle,332,1,10000,0,700,yes,self,trickcasting,,,,,,,,
 1143,Marionette@HT_FREEZINGTRAP,idle,121,5,500,0,300000,yes,around2,always,0,,,,,,29,
 1143,Marionette@MG_FIREWALL,chase,18,5,500,500,5000,yes,target,always,0,,,,,,2,
 1143,Marionette@NPC_TELEKINESISATTACK,attack,191,5,500,0,5000,yes,target,always,0,,,,,,6,

+ 5 - 3
db/re/mob_skill_db.txt

@@ -47,6 +47,8 @@
 //	mobnearbygt		When monsters in range become greater than specified number.
 //	groundattacked		When mob is hit by ground targeted skill (no condition value).
 //	damagedgt		When single attack deals greater damage than specified number.
+//	alchemist		When mob has a special AI, is not trickcasting, and is wounded (no condition value).
+//	trickcasting		Used once mob started moving through NPC_RANDOMMOVE until disabled (no condition value).
 //
 //	Status abnormalities specified through the statuson/statusoff system:
 //	    anybad (any type of state change) / stone / freeze / stun / sleep /
@@ -719,11 +721,11 @@
 1141,Marina@NPC_CRITICALSLASH,attack,170,1,500,500,5000,no,target,always,0,,,,,,6,
 1141,Marina@NPC_EMOTION,walk,197,1,2000,0,5000,yes,self,always,0,19,,,,,,
 1141,Marina@NPC_WATERATTACK,attack,184,2,500,500,5000,no,target,always,0,,,,,,6,
-1142,Marine Sphere@NPC_RANDOMMOVE,idle,331,1,10000,0,30000,no,master,alchemist,,,,,,,,
-1142,Marine Sphere@NPC_SELFDESTRUCTION,idle,173,1,10000,3000,0,no,self,afterskill,331,,,,,,,
+1142,Marine Sphere@NPC_RANDOMMOVE,idle,331,1,10000,0,30000,no,target,alchemist,,,,,,,,
 1142,Marine Sphere@NPC_SELFDESTRUCTION,idle,173,1,500,2000,5000,no,self,myhpltmaxrate,99,,,,,,,
 1142,Marine Sphere@NPC_SELFDESTRUCTION,idle,173,1,10000,2000,5000,no,self,skillused,173,,,,,,,
-1142,Marine Sphere@NPC_SPEEDUP,idle,332,1,10000,0,700,yes,target,always,,,,,,,,
+1142,Marine Sphere@NPC_SELFDESTRUCTION,idle,173,1,10000,0,0,yes,self,alchemist,,,,,,,,
+1142,Marine Sphere@NPC_SPEEDUP,idle,332,1,10000,0,700,yes,self,trickcasting,,,,,,,,
 1143,Marionette@HT_FREEZINGTRAP,idle,121,5,500,0,300000,yes,around2,always,0,,,,,,29,
 1143,Marionette@MG_FIREWALL,chase,18,5,500,500,5000,yes,target,always,0,,,,,,2,
 1143,Marionette@NPC_TELEKINESISATTACK,attack,191,5,500,0,5000,yes,target,always,0,,,,,,6,

+ 4 - 0
src/map/battle.cpp

@@ -10954,6 +10954,10 @@ int32 battle_check_target( struct block_list *src, struct block_list *target,int
 	if( (s_bl = battle_get_master(src)) == nullptr )
 		s_bl = src;
 
+	// Can't hit self and master, but can hit other slaves
+	if (flag&BCT_WOS && (src == target || s_bl == target))
+		return -1;
+
 	if ( s_bl->type == BL_PC ) {
 		switch( t_bl->type ) {
 			case BL_MOB: // Source => PC, Target => MOB

+ 2 - 1
src/map/battle.hpp

@@ -69,7 +69,8 @@ enum e_battle_check_target : uint32 {
 
 	BCT_ALL			= 0x3F0000, ///< All targets
 
-	BCT_WOS			= 0x400000, ///< Except self (currently used for skipping if src == bl in skill_area_sub)
+	BCT_WOS			= 0x400000, ///< Except self and your master
+	BCT_SLAVE		= BCT_SELF|BCT_WOS,				///< Does not hit yourself/master, but hits your/master's slaves
 	BCT_GUILD		= BCT_SAMEGUILD|BCT_GUILDALLY,	///< Guild AND Allies (BCT_SAMEGUILD|BCT_GUILDALLY)
 	BCT_NOGUILD		= BCT_ALL&~BCT_GUILD,			///< Except guildmates
 	BCT_NOPARTY		= BCT_ALL&~BCT_PARTY,			///< Except party members

+ 16 - 10
src/map/mob.cpp

@@ -1196,6 +1196,7 @@ int32 mob_spawn (struct mob_data *md)
 	md->last_pcneartime = 0;
 	md->last_canmove = tick;
 	md->last_skillcheck = 0;
+	md->trickcasting = 0;
 
 	t_tick c = tick - MOB_MAX_DELAY;
 
@@ -1552,7 +1553,11 @@ int32 mob_unlocktarget(struct mob_data *md, t_tick tick)
 		md->state.skillstate = MSS_IDLE;
 		[[fallthrough]];
 	case MSS_IDLE:
-		if( md->ud.walktimer == INVALID_TIMER && md->idle_event[0] && npc_event_do_id( md->idle_event, md->bl.id ) > 0 ){
+		// When walking we want to trigger the idle skills through the walk routine so we can prevent stopping
+		// This situation happens when an immobile monster uses a skill to move
+		if (md->ud.walktimer != INVALID_TIMER)
+			break;
+		if (md->idle_event[0] && npc_event_do_id(md->idle_event, md->bl.id) > 0) {
 			md->idle_event[0] = 0;
 			break;
 		}
@@ -2542,11 +2547,6 @@ void mob_log_damage(mob_data* md, block_list* src, int64 damage, int64 damage_ta
 //Call when a mob has received damage.
 void mob_damage(struct mob_data *md, struct block_list *src, int32 damage)
 {
-	// LOne WOlf explained that ANYONE can trigger the marine countdown skill. [Skotlex]
-	if( src != nullptr && md->special_state.ai == AI_SPHERE && md->dmglog.empty() ){
-		md->state.can_escape = 1;
-	}
-
 	if (src && damage > 0) { //Store total damage...
 		if ((src != &md->bl) && md->state.aggressive) //No longer aggressive, change to retaliate AI.
 			md->state.aggressive = 0;
@@ -3921,7 +3921,7 @@ bool mob_chat_display_message(mob_data &md, uint16 msg_id) {
 /*==========================================
  * Skill use judging
  *------------------------------------------*/
-int32 mobskill_use(struct mob_data *md, t_tick tick, int32 event, int64 damage)
+bool mobskill_use(struct mob_data *md, t_tick tick, int32 event, int64 damage)
 {
 	struct block_list *fbl = nullptr; //Friend bl, which can either be a BL_PC or BL_MOB depending on the situation. [Skotlex]
 	struct block_list *bl;
@@ -4023,9 +4023,11 @@ int32 mobskill_use(struct mob_data *md, t_tick tick, int32 event, int64 damage)
 				case MSC_MASTERATTACKED:
 					flag = (md->master_id > 0 && (fbl=map_id2bl(md->master_id)) && unit_counttargeted(fbl) > 0); break;
 				case MSC_ALCHEMIST:
-					flag = (md->state.can_escape); break;
+					flag = (md->special_state.ai != AI_NONE && md->trickcasting == 0 && md->status.hp < md->status.max_hp); break;
 				case MSC_MOBNEARBYGT:
 					flag = (map_foreachinallrange(mob_count_sub, &md->bl, AREA_SIZE, BL_MOB) > c2 ); break;
+				case MSC_TRICKCASTING:
+					flag = (md->trickcasting > 0); break;
 			}
 		}
 
@@ -4099,6 +4101,9 @@ int32 mobskill_use(struct mob_data *md, t_tick tick, int32 event, int64 damage)
 					break;
 				case MST_TARGET:
 					bl = map_id2bl(md->target_id);
+					// Monsters that cannot attack put their last attacker as target
+					if (bl == nullptr && !status_has_mode(&md->status, MD_CANATTACK))
+						bl = map_id2bl(md->attacked_id);
 					break;
 				case MST_MASTER:
 					bl = &md->bl;
@@ -4150,11 +4155,11 @@ int32 mobskill_use(struct mob_data *md, t_tick tick, int32 event, int64 damage)
 		} else
 			md->skilldelay[i]=tick;
 		map_freeblock_unlock();
-		return 1;
+		return true;
 	}
 	//No skill was used.
 	md->skill_idx = -1;
-	return 0;
+	return false;
 }
 /*==========================================
  * Skill use event processing
@@ -5996,6 +6001,7 @@ static bool mob_parse_row_mobskilldb( char** str, size_t columns, size_t current
 		{ "mobnearbygt",       MSC_MOBNEARBYGT       },
 		{ "groundattacked",    MSC_GROUNDATTACKED    },
 		{ "damagedgt",         MSC_DAMAGEDGT         },
+		{ "trickcasting",      MSC_TRICKCASTING      },
 	}, cond2[] ={
 		{	"anybad",		-1				},
 		{	"stone",		SC_STONE		},

+ 3 - 1
src/map/mob.hpp

@@ -366,6 +366,7 @@ struct mob_data {
 	int32 bg_id; // BattleGround System
 
 	t_tick next_walktime,last_thinktime,last_linktime,last_pcneartime,dmgtick,last_canmove,last_skillcheck;
+	t_tick trickcasting; // Special state where you show a fake castbar while moving
 	int16 move_fail_count;
 	int16 lootitem_count;
 	unsigned char walktoxy_fail_count; //Pathfinding succeeds but the actual walking failed (e.g. Icewall lock)
@@ -467,6 +468,7 @@ enum e_mob_skill_condition {
 	MSC_MOBNEARBYGT,
 	MSC_GROUNDATTACKED,
 	MSC_DAMAGEDGT,
+	MSC_TRICKCASTING,
 };
 
 // The data structures for storing delayed item drops
@@ -535,7 +537,7 @@ int32 mob_warpslave(struct block_list *bl, int32 range);
 int32 mob_linksearch(struct block_list *bl,va_list ap);
 
 bool mob_chat_display_message (mob_data &md, uint16 msg_id);
-int32 mobskill_use(struct mob_data *md,t_tick tick,int32 event, int64 damage = 0);
+bool mobskill_use(struct mob_data *md,t_tick tick,int32 event, int64 damage = 0);
 int32 mobskill_event(struct mob_data *md,struct block_list *src,t_tick tick, int32 flag, int64 damage = 0);
 int32 mob_summonslave(struct mob_data *md2,int32 *value,int32 amount,uint16 skill_id);
 int32 mob_countslave(struct block_list *bl);

+ 31 - 21
src/map/skill.cpp

@@ -4191,9 +4191,6 @@ int32 skill_area_sub(struct block_list *bl, va_list ap)
 	flag = va_arg(ap,int32);
 	func = va_arg(ap,SkillFunc);
 
-	if (flag&BCT_WOS && src == bl)
-		return 0;
-
 	if(battle_check_target(src,bl,flag) > 0) {
 		// several splash skills need this initial dummy packet to display correctly
 		if (flag&SD_PREAMBLE && skill_area_temp[2] == 0)
@@ -7374,9 +7371,6 @@ static int32 skill_apply_songs(struct block_list* target, va_list ap)
 	uint16 skill_lv = static_cast<uint16>(va_arg(ap, int32));
 	t_tick tick = va_arg(ap, t_tick);
 
-	if (flag & BCT_WOS && src == target)
-		return 0;
-
 	if (battle_check_target(src, target, flag) > 0) {
 		switch (skill_id) {
 			// Attack type songs
@@ -9043,9 +9037,10 @@ int32 skill_castend_nodamage_id (struct block_list *src, struct block_list *bl,
 
 	case NPC_SELFDESTRUCTION:
 		//Self Destruction hits everyone in range (allies+enemies)
-		//Except for Summoned Marine spheres on non-versus maps, where it's just enemy.
+		//Except for Summoned Marine spheres on non-versus maps, where it's just enemies and your own slaves.
 		i = ((!md || md->special_state.ai == AI_SPHERE) && !map_flag_vs(src->m))?
-			BCT_ENEMY:BCT_ALL;
+			BCT_ENEMY|BCT_SLAVE:BCT_ALL;
+		clif_skill_nodamage(src, *src, skill_id, skill_lv);
 		map_delblock(src); //Required to prevent chain-self-destructions hitting back.
 		map_foreachinshootrange(skill_area_sub, bl,
 			skill_get_splash(skill_id, skill_lv), BL_CHAR|BL_SKILL,
@@ -10142,22 +10137,37 @@ int32 skill_castend_nodamage_id (struct block_list *src, struct block_list *bl,
 
 	case NPC_RANDOMMOVE:
 		if (md) {
-			md->next_walktime = tick - 1;
-			if (md->special_state.ai == AI_SPHERE)
-				unit_escape(&md->bl, bl, 7, 2);
-			else
-				mob_randomwalk(md,tick);
+			// This skill creates fake casting state where a monster moves while showing a cast bar
+			int32 tricktime = MOB_SKILL_INTERVAL * 3;
+			md->trickcasting = tick + tricktime;
+			clif_skillcasting(src, src->id, src->id, 0, 0, skill_id, skill_lv, ELE_FIRE, tricktime + 500);
+			// Monster cannot be stopped while moving
+			md->state.can_escape = 1;
+			// Move up to 8 cells
+			unit_escape(&md->bl, bl, 8, 3);
 		}
 		break;
 
 	case NPC_SPEEDUP:
-		{
-			// or does it increase casting rate? just a guess xD
-			int32 i_type = SC_ASPDPOTION0 + skill_lv - 1;
-			if (i_type > SC_ASPDPOTION3)
-				i_type = SC_ASPDPOTION3;
-			clif_skill_nodamage(src,*bl,skill_id,skill_lv,
-				sc_start(src,bl,(sc_type)i_type,100,skill_lv,skill_lv * 60000));
+		if (md) {
+			// Officially, trickcasting continues as long as there are more than 700ms left
+			int32 trickstop = (MOB_SKILL_INTERVAL * 7) / 10;
+			if (DIFF_TICK(md->trickcasting, tick) >= trickstop) {
+				// This skill directly modifies a monster's base speed value
+				md->base_status->speed = std::max(md->base_status->speed - 250, MIN_WALK_SPEED);
+				// Need to recalc speed based on new base value
+				status_calc_bl(&md->bl, { SCB_SPEED });
+				// We use skills only on each full cell, to fix the inaccuracy we do this on last move interval
+				if (DIFF_TICK(md->trickcasting, tick) < trickstop + MOB_SKILL_INTERVAL)
+					md->last_skillcheck = tick + 50;
+			}
+			else {
+				// Synchronize skill usage
+				md->last_skillcheck = md->trickcasting;
+				// Causes monster to stop and get ready for next alchemist skill
+				md->trickcasting = 0;
+				md->state.can_escape = 0;
+			}
 		}
 		break;
 
@@ -10181,7 +10191,7 @@ int32 skill_castend_nodamage_id (struct block_list *src, struct block_list *bl,
 				md->state.can_escape = 1;
 				mob_unlocktarget(md, tick);
 				// Official distance is 7, if level > 1, distance = level
-				t_tick time = unit_escape(src, tbl, skill_lv > 1 ? skill_lv : 7, 2);
+				t_tick time = unit_escape(src, tbl, skill_lv > 1 ? skill_lv : 7, 3);
 
 				if (time) {
 					// Need to set state here as it's not set otherwise

+ 3 - 3
src/map/status.cpp

@@ -2784,11 +2784,11 @@ int32 status_calc_mob_(struct mob_data* md, uint8 opt)
 	if (flag&8 && mbl) {
 		struct status_data *mstatus = status_get_base_status(mbl);
 
-		if (mstatus &&
+		if (mstatus && md->special_state.ai != AI_SPHERE &&
 			battle_config.slaves_inherit_speed&(status_has_mode(mstatus,MD_CANMOVE)?1:2))
 			status->speed = mstatus->speed;
-		if( status->speed < 2 ) // Minimum for the unit to function properly
-			status->speed = 2;
+		if (status->speed < MIN_WALK_SPEED)
+			status->speed = MIN_WALK_SPEED;
 	}
 
 	if (flag&32)

+ 22 - 14
src/map/unit.cpp

@@ -638,10 +638,11 @@ static TIMER_FUNC(unit_walktoxy_timer)
 			// To make sure we check one skill per second on average, we substract half the speed as ms
 			if(!ud->state.force_walk && tid != INVALID_TIMER &&
 				DIFF_TICK(tick, md->last_skillcheck) > MOB_SKILL_INTERVAL - md->status.speed / 2 &&
-				DIFF_TICK(tick, md->last_thinktime) > 0 &&
+				//TODO: Full fix requires to move casting of chase skills to nextcell function
+				//DIFF_TICK(tick, md->last_thinktime) > 0 &&
 				map[bl->m].users > 0 &&
 				mobskill_use(md, tick, -1)) {
-				if (!(ud->skill_id == NPC_SELFDESTRUCTION && ud->skilltimer != INVALID_TIMER)
+				if ((ud->skill_id != NPC_SPEEDUP || md->trickcasting == 0) //Stop only when trickcasting expired
 					&& ud->skill_id != NPC_EMOTION && ud->skill_id != NPC_EMOTION_ON //NPC_EMOTION doesn't make the monster stop
 					&& md->state.skillstate != MSS_WALK) //Walk skills are supposed to be used while walking
 				{ // Skill used, abort walking
@@ -1091,19 +1092,28 @@ t_tick unit_get_walkpath_time(struct block_list& bl)
 }
 
 /**
- * Makes unit attempt to run away from target using hard paths
+ * Makes unit attempt to run away from target in a straight line or using hard paths
  * @param bl: Object that is running away from target
  * @param target: Target
  * @param dist: How far bl should run
- * @param flag: unit_walktoxy flag
+ * @param flag: unit_walktoxy flag (&1 = straight line escape)
  * @return The duration the unit will run (0 on fail)
  */
 t_tick unit_escape(struct block_list *bl, struct block_list *target, int16 dist, uint8 flag)
 {
 	uint8 dir = map_calc_dir(target, bl->x, bl->y);
 
-	while( dist > 0 && map_getcell(bl->m, bl->x + dist*dirx[dir], bl->y + dist*diry[dir], CELL_CHKNOREACH) )
-		dist--;
+	if (flag&1) {
+		// Keep moving until we hit an unreachable cell
+		for (int i = 1; i <= dist; i++) {
+			if (map_getcell(bl->m, bl->x + i*dirx[dir], bl->y + i*diry[dir], CELL_CHKNOREACH))
+				dist = i - 1;
+		}
+	} else {
+		// Find the furthest reachable cell (then find a walkpath to it)
+		while( dist > 0 && map_getcell(bl->m, bl->x + dist*dirx[dir], bl->y + dist*diry[dir], CELL_CHKNOREACH) )
+			dist--;
+	}
 
 	if (dist > 0 && unit_walktoxy(bl, bl->x + dist * dirx[dir], bl->y + dist * diry[dir], flag))
 		return unit_get_walkpath_time(*bl);
@@ -1741,18 +1751,16 @@ int32 unit_set_walkdelay(struct block_list *bl, t_tick tick, t_tick delay, int32
 		if (DIFF_TICK(ud->canmove_tick, tick+delay) > 0)
 			return 0;
 	} else {
+		if (bl->type == BL_MOB) {
+			mob_data* md = BL_CAST(BL_MOB, bl);
+			// Mob needs to escape, don't stop it
+			if (md && md->state.can_escape == 1)
+				return 0;
+		}
 		// Don't set walk delays when already trapped.
 		if (!unit_can_move(bl)) {
-			if (bl->type == BL_MOB) {
-				mob_data *md = BL_CAST(BL_MOB, bl);
-
-				if (md && md->state.can_escape == 1) // Mob needs to escape, don't stop it
-					return 0;
-			}
-
 			// Unit might still be moving even though it can't move
 			unit_stop_walking( bl, USW_MOVE_FULL_CELL );
-
 			return 0;
 		}
 		//Immune to being stopped for double the flinch time