Pārlūkot izejas kodu

> Hercules merges:
* 5b37805:
Items are no longer unequipped when a card within them is blacklisted (e.g. item_unequip.txt), instead the card's effect is nullified.

* cc8e005:
item_noequip overhaul.
Items are no longer unequipped when disabled by item_noequip.txt, instead their effects are nullified -- however cards in them, unless also disabled, wont have its effect nullified.
Consumables will be consumed even while disabled, unless you modify the new config item_restricted_consumption_type.

* 7192b10:
SECURE_NPCTIMEOUT should be fully functional now, fixed remaining known issues. (bugreport:5343)

* 63145d6:
Where casting MO_EXTREMITY to untargetable targets causes the caster to move in fixed location/coordinate.

* 17f2e6a:
Fixed clif.c::clif_parse_NpcSelectMenu. SECURE_NPCTIMEOUT is always defined; the check is wrong.

* 125b758:
Added New Char-Server Rename Packet.

* d6d49c4:
Updated Safety Wall Renewal behavior, when taking damage higher than the health of the safety wall, excess damage will no longer be passed to the player.
Fixes bugreport:6243, bugreport:6697.

* 7626b14:
Fixed wrong 'if' condition in checking CELL_CHKNOPASS for the OFFICIAL_WALKPATH where clear path is considered to be limited to 14 cells it should only be with obstacles. (follow-up r17091, bugreport:7239)

* 0dbbe22:
Fixed magical reflection behavior, user reflecting now visually casts the skill back at the caster, instead of caster casting it on itself.

* 4671a39:
Monsters in stone curse pre-stone state no longer are able to attack.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@17119 54d463be-8e91-2dee-dedb-b68131a5f0ec

euphyy 12 gadi atpakaļ
vecāks
revīzija
84d500ea2c
11 mainītis faili ar 150 papildinājumiem un 125 dzēšanām
  1. 3 0
      conf/battle/items.conf
  2. 29 0
      src/char/char.c
  3. 4 7
      src/map/battle.c
  4. 2 1
      src/map/battle.h
  5. 11 5
      src/map/clif.c
  6. 3 0
      src/map/npc.c
  7. 40 73
      src/map/pc.c
  8. 15 19
      src/map/script.c
  9. 10 1
      src/map/skill.c
  10. 30 17
      src/map/status.c
  11. 3 2
      src/map/unit.c

+ 3 - 0
conf/battle/items.conf

@@ -75,3 +75,6 @@ gtb_sc_immunity: 50
 // NOTE: Different cards that grant the same skill will both 
 // always work independently of each other regardless of setting.
 autospell_stacking: no
+
+// Will disabled consumables (disabled by item_noequip.txt) be consumed when trying to use them? (Note 1)
+item_restricted_consumption_type: yes

+ 29 - 0
src/char/char.c

@@ -3992,6 +3992,35 @@ int parse_char(int fd)
 				return 0;
 			RFIFOSKIP(fd,6);
 		break;
+		// char rename request
+		// R 08fc <char ID>.l <new name>.24B
+		case 0x8fc:
+			FIFOSD_CHECK(30);
+			{
+				int i, cid =RFIFOL(fd,2);
+				char name[NAME_LENGTH];
+				char esc_name[NAME_LENGTH*2+1];
+				safestrncpy(name, (char *)RFIFOP(fd,6), NAME_LENGTH);
+				RFIFOSKIP(fd,30);
+				
+				ARR_FIND( 0, MAX_CHARS, i, sd->found_char[i] == cid );
+				if( i == MAX_CHARS )
+					break;
+				
+				normalize_name(name,TRIM_CHARS);
+				Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
+				if( !check_char_name(name,esc_name) ) {
+					i = 1;
+					safestrncpy(sd->new_name, name, NAME_LENGTH);
+				} else
+					i = 0;
+				
+				WFIFOHEAD(fd, 4);
+				WFIFOW(fd,0) = 0x28e;
+				WFIFOW(fd,2) = i;
+				WFIFOSET(fd,4);
+			}
+			break;
 
 		// char rename request
 		// R 028d <account ID>.l <char ID>.l <new name>.24B

+ 4 - 7
src/map/battle.c

@@ -811,13 +811,12 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag
 				 * in RE, SW possesses a lifetime equal to 3 times the caster's health
 				 **/
 			#ifdef RENEWAL
+				d->dmg_lv = ATK_BLOCK;
 				if ( ( group->val2 - damage) > 0 ) {
 					group->val2 -= damage;
-					d->dmg_lv = ATK_BLOCK;
-					return 0;
 				} else
-					damage -= group->val2;
-				skill_delunitgroup(group);
+					skill_delunitgroup(group);
+				return 0;
 			#else
 				if (--group->val2<=0)
 					skill_delunitgroup(group);
@@ -5875,10 +5874,8 @@ static const struct _battle_data {
 	{ "homunculus_max_level",               &battle_config.hom_max_level,                   99,     0,      MAX_LEVEL,      },
 	{ "homunculus_S_max_level",             &battle_config.hom_S_max_level,                 150,    0,      MAX_LEVEL,      },
 	{ "mob_size_influence",					&battle_config.mob_size_influence,				0,		0,		1,				},
-	/**
-	 * Hercules
-	 **/
 	{ "skill_trap_type",                    &battle_config.skill_trap_type,                 0,      0,      1,              },
+	{ "item_restricted_consumption_type",   &battle_config.item_restricted_consumption_type,1,      0,      1,              },
 };
 #ifndef STATS_OPT_OUT
 /**

+ 2 - 1
src/map/battle.h

@@ -479,11 +479,12 @@ extern struct Battle_Config
 	int mvp_tomb_enabled;
 
 	int atcommand_suggestions_enabled;
-    int min_npc_vending_distance;
+	int min_npc_vending_distance;
 	int atcommand_mobinfo_type;
 
 	int mob_size_influence; // Enable modifications on earned experience, drop rates and monster status depending on monster size. [mkbu95]
 	int skill_trap_type;
+	int item_restricted_consumption_type;
 	} battle_config;
 
 void do_init_battle(void);

+ 11 - 5
src/map/clif.c

@@ -9359,6 +9359,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 		}
 
 		map_iwall_get(sd); // Updates Walls Info on this Map to Client
+		status_calc_pc(sd, false); // Some conditions are map-dependent so we must recalculate
 		sd->state.changemap = false;
 	}
 
@@ -11112,11 +11113,16 @@ void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd)
 	int npc_id = RFIFOL(fd,2);
 	uint8 select = RFIFOB(fd,6);
 
-	if( (select > sd->npc_menu && select != 0xff) || select == 0 )
-	{
-		TBL_NPC* nd = map_id2nd(npc_id);
-		ShowWarning("Invalid menu selection on npc %d:'%s' - got %d, valid range is [%d..%d] (player AID:%d, CID:%d, name:'%s')!\n", npc_id, (nd)?nd->name:"invalid npc id", select, 1, sd->npc_menu, sd->bl.id, sd->status.char_id, sd->status.name);
-		clif_GM_kick(NULL,sd);
+	if( (select > sd->npc_menu && select != 0xff) || select == 0 ) {
+#if SECURE_NPCTIMEOUT
+		if( sd->npc_idle_timer != INVALID_TIMER ) {
+#endif
+			TBL_NPC* nd = map_id2nd(npc_id);
+			ShowWarning("Invalid menu selection on npc %d:'%s' - got %d, valid range is [%d..%d] (player AID:%d, CID:%d, name:'%s')!\n", npc_id, (nd)?nd->name:"invalid npc id", select, 1, sd->npc_menu, sd->bl.id, sd->status.char_id, sd->status.name);
+			clif_GM_kick(NULL,sd);
+#if SECURE_NPCTIMEOUT
+		}
+#endif
 		return;
 	}
 

+ 3 - 0
src/map/npc.c

@@ -231,6 +231,9 @@ int npc_rr_secure_timeout_timer(int tid, unsigned int tick, int id, intptr_t dat
 		 **/
 		if( sd->st )
 			sd->st->state = END;
+		sd->state.menu_or_input = 0;
+		sd->npc_menu = 0;
+
 		/**
 		 * This guy's been idle for longer than allowed, close him.
 		 **/

+ 40 - 73
src/map/pc.c

@@ -703,23 +703,23 @@ int pc_setequipindex(struct map_session_data *sd)
 	return 0;
 }
 
-static int pc_isAllowedCardOn(struct map_session_data *sd,int s,int eqindex,int flag)
-{
-	int i;
-	struct item *item = &sd->status.inventory[eqindex];
-	struct item_data *data;
-
-	//Crafted/made/hatched items.
-	if (itemdb_isspecial(item->card[0]))
-		return 1;
-
-	/* scan for enchant armor gems */
-	if( item->card[MAX_SLOTS - 1] && s < MAX_SLOTS - 1 )
-		s = MAX_SLOTS - 1;
-
-	ARR_FIND( 0, s, i, item->card[i] && (data = itemdb_exists(item->card[i])) != NULL && data->flag.no_equip&flag );
-	return( i < s ) ? 0 : 1;
-}
+//static int pc_isAllowedCardOn(struct map_session_data *sd,int s,int eqindex,int flag)
+//{
+//	int i;
+//	struct item *item = &sd->status.inventory[eqindex];
+//	struct item_data *data;
+//
+//	//Crafted/made/hatched items.
+//	if (itemdb_isspecial(item->card[0]))
+//		return 1;
+//
+//	/* scan for enchant armor gems */
+//	if( item->card[MAX_SLOTS - 1] && s < MAX_SLOTS - 1 )
+//		s = MAX_SLOTS - 1;
+//
+//	ARR_FIND( 0, s, i, item->card[i] && (data = itemdb_exists(item->card[i])) != NULL && data->flag.no_equip&flag );
+//	return( i < s ) ? 0 : 1;
+//}
 
 bool pc_isequipped(struct map_session_data *sd, int nameid)
 {
@@ -862,20 +862,6 @@ int pc_isequip(struct map_session_data *sd,int n)
 #endif
 	if(item->sex != 2 && sd->status.sex != item->sex)
 		return 0;
-	if(!map_flag_vs(sd->bl.m) && ((item->flag.no_equip&1) || !pc_isAllowedCardOn(sd,item->slot,n,1)))
-		return 0;
-	if(map[sd->bl.m].flag.pvp && ((item->flag.no_equip&2) || !pc_isAllowedCardOn(sd,item->slot,n,2)))
-		return 0;
-	if(map_flag_gvg(sd->bl.m) && ((item->flag.no_equip&4) || !pc_isAllowedCardOn(sd,item->slot,n,4)))
-		return 0;
-	if(map[sd->bl.m].flag.battleground && ((item->flag.no_equip&8) || !pc_isAllowedCardOn(sd,item->slot,n,8)))
-		return 0;
-	if(map[sd->bl.m].flag.restricted)
-	{
-		int flag =8*map[sd->bl.m].zone;
-		if (item->flag.no_equip&flag || !pc_isAllowedCardOn(sd,item->slot,n,flag))
-			return 0;
-	}
 
 	if (sd->sc.count) {
 
@@ -4135,16 +4121,6 @@ int pc_isUseitem(struct map_session_data *sd,int n)
 	else if( itemdb_is_poison(nameid) && (sd->class_&MAPID_THIRDMASK) != MAPID_GUILLOTINE_CROSS )
 		return 0;
 
-	//added item_noequip.txt items check by Maya&[Lupus]
-	if (
-		(!map_flag_vs(sd->bl.m) && item->flag.no_equip&1) || // Normal
-		(map[sd->bl.m].flag.pvp && item->flag.no_equip&2) || // PVP
-		(map_flag_gvg(sd->bl.m) && item->flag.no_equip&4) || // GVG
-		(map[sd->bl.m].flag.battleground && item->flag.no_equip&8) || // Battleground
-		(map[sd->bl.m].flag.restricted && item->flag.no_equip&(8*map[sd->bl.m].zone)) // Zone restriction
-	)
-		return 0;
-
 	//Gender check
 	if(item->sex != 2 && sd->status.sex != item->sex)
 		return 0;
@@ -4274,6 +4250,21 @@ int pc_useitem(struct map_session_data *sd,int n)
 		}
 	}
 
+	/* on restricted maps the item is consumed but the effect is not used */
+	if (
+		(!map_flag_vs(sd->bl.m) && sd->inventory_data[n]->flag.no_equip&1) || // Normal
+		(map[sd->bl.m].flag.pvp && sd->inventory_data[n]->flag.no_equip&2) || // PVP
+		(map_flag_gvg(sd->bl.m) && sd->inventory_data[n]->flag.no_equip&4) || // GVG
+		(map[sd->bl.m].flag.battleground && sd->inventory_data[n]->flag.no_equip&8) || // Battleground
+		(map[sd->bl.m].flag.restricted && sd->inventory_data[n]->flag.no_equip&(8*map[sd->bl.m].zone)) // Zone restriction
+		) {
+		if( battle_config.item_restricted_consumption_type ) {
+			clif_useitemack(sd,n,sd->status.inventory[n].amount-1,true);
+			pc_delitem(sd,n,1,1,0,LOG_TYPE_CONSUME);
+		}
+		return 0;/* regardless, effect is not run */
+	}
+
 	sd->itemid = sd->status.inventory[n].nameid;
 	sd->itemindex = n;
 	if(sd->catch_target_class != -1) //Abort pet catching.
@@ -8617,41 +8608,33 @@ int pc_unequipitem(struct map_session_data *sd,int n,int flag) {
 int pc_checkitem(struct map_session_data *sd)
 {
 	int i,id,calc_flag = 0;
-	struct item_data *it=NULL;
 
 	nullpo_ret(sd);
 
 	if( sd->state.vending ) //Avoid reorganizing items when we are vending, as that leads to exploits (pointed out by End of Exam)
 		return 0;
 
-	if( battle_config.item_check )
-	{// check for invalid(ated) items
-		for( i = 0; i < MAX_INVENTORY; i++ )
-		{
+	if( battle_config.item_check ) {// check for invalid(ated) items
+		for( i = 0; i < MAX_INVENTORY; i++ ) {
 			id = sd->status.inventory[i].nameid;
 
-			if( id && !itemdb_available(id) )
-			{
+			if( id && !itemdb_available(id) ) {
 				ShowWarning("Removed invalid/disabled item id %d from inventory (amount=%d, char_id=%d).\n", id, sd->status.inventory[i].amount, sd->status.char_id);
 				pc_delitem(sd, i, sd->status.inventory[i].amount, 0, 0, LOG_TYPE_OTHER);
 			}
 		}
 
-		for( i = 0; i < MAX_CART; i++ )
-		{
+		for( i = 0; i < MAX_CART; i++ ) {
 			id = sd->status.cart[i].nameid;
 
-			if( id && !itemdb_available(id) )
-			{
+			if( id && !itemdb_available(id) ) {
 				ShowWarning("Removed invalid/disabled item id %d from cart (amount=%d, char_id=%d).\n", id, sd->status.cart[i].amount, sd->status.char_id);
 				pc_cart_delitem(sd, i, sd->status.cart[i].amount, 0, LOG_TYPE_OTHER);
 			}
 		}
 	}
 
-	for( i = 0; i < MAX_INVENTORY; i++)
-	{
-		it = sd->inventory_data[i];
+	for( i = 0; i < MAX_INVENTORY; i++) {
 
 		if( sd->status.inventory[i].nameid == 0 )
 			continue;
@@ -8659,31 +8642,15 @@ int pc_checkitem(struct map_session_data *sd)
 		if( !sd->status.inventory[i].equip )
 			continue;
 
-		if( sd->status.inventory[i].equip&~pc_equippoint(sd,i) )
-		{
+		if( sd->status.inventory[i].equip&~pc_equippoint(sd,i) ) {
 			pc_unequipitem(sd, i, 2);
 			calc_flag = 1;
 			continue;
 		}
 
-		if( it )
-		{ // check for forbiden items.
-			int flag =
-					(map[sd->bl.m].flag.restricted?(8*map[sd->bl.m].zone):0)
-					| (!map_flag_vs(sd->bl.m)?1:0)
-					| (map[sd->bl.m].flag.pvp?2:0)
-					| (map_flag_gvg(sd->bl.m)?4:0)
-					| (map[sd->bl.m].flag.battleground?8:0);
-			if( flag && (it->flag.no_equip&flag || !pc_isAllowedCardOn(sd,it->slot,i,flag)) )
-			{
-				pc_unequipitem(sd, i, 2);
-				calc_flag = 1;
-			}
-		}
 	}
 
-	if( calc_flag && sd->state.active )
-	{
+	if( calc_flag && sd->state.active ) {
 		pc_checkallowskill(sd);
 		status_calc_pc(sd,0);
 	}

+ 15 - 19
src/map/script.c

@@ -3590,30 +3590,26 @@ static void script_detach_state(struct script_state* st, bool dequeue_event)
 {
 	struct map_session_data* sd;
 
-	if(st->rid && (sd = map_id2sd(st->rid))!=NULL)
-	{
+	if(st->rid && (sd = map_id2sd(st->rid))!=NULL) {
 		sd->st = st->bk_st;
 		sd->npc_id = st->bk_npcid;
-		/**
-		 * For the Secure NPC Timeout option (check config/Secure.h) [RR]
-		 **/
-	#if SECURE_NPCTIMEOUT
-		/**
-		 * We're done with this NPC session, so we cancel the timer (if existent) and move on
-		 **/
-		if( sd->npc_idle_timer != INVALID_TIMER ) {
-			delete_timer(sd->npc_idle_timer,npc_rr_secure_timeout_timer);
-			sd->npc_idle_timer = INVALID_TIMER;
-		}
-	#endif
-		if(st->bk_st)
-		{
+		if(st->bk_st) {
 			//Remove tag for removal.
 			st->bk_st = NULL;
 			st->bk_npcid = 0;
-		}
-		else if(dequeue_event)
-		{
+		} else if(dequeue_event) {
+			/**
+			 * For the Secure NPC Timeout option (check config/Secure.h) [RR]
+			 **/
+#if SECURE_NPCTIMEOUT
+			/**
+			 * We're done with this NPC session, so we cancel the timer (if existent) and move on
+			 **/
+			if( sd->npc_idle_timer != INVALID_TIMER ) {
+				delete_timer(sd->npc_idle_timer,npc_rr_secure_timeout_timer);
+				sd->npc_idle_timer = INVALID_TIMER;
+			}
+#endif
 			npc_event_dequeue(sd);
 		}
 	}

+ 10 - 1
src/map/skill.c

@@ -2251,6 +2251,7 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
 			rmdamage = 1;
 			bl = src;
 			src = tbl;
+			dsrc = tbl;
 			sd = BL_CAST(BL_PC, src);
 			tsd = BL_CAST(BL_PC, bl);
 			sc = status_get_sc(bl);
@@ -9373,7 +9374,15 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
 		}
 		if (target && target->m == src->m)
 		{	//Move character to target anyway.
-			if (unit_movepos(src, src->x+3, src->y+3, 1, 1))
+			int dir, x, y;
+			dir = map_calc_dir(src,target->x,target->y);
+			if( dir > 0 && dir < 4) x = -2;
+			else if( dir > 4 ) x = 2;
+			else x = 0;
+			if( dir > 2 && dir < 6 ) y = -2;
+			else if( dir == 7 || dir < 2 ) y = 2;
+			else y = 0;
+			if (unit_movepos(src, src->x+x, src->y+y, 1, 1))
 			{	//Display movement + animation.
 				clif_slide(src,src->x,src->y);
 				clif_skill_damage(src,target,tick,sd->battle_status.amotion,0,0,1,ud->skill_id, ud->skill_lv, 5);

+ 30 - 17
src/map/status.c

@@ -1584,7 +1584,7 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, uin
 
 	if( sc && sc->count ) {
 
-		if (skill_id != RK_REFRESH && sc->opt1 >0 && (sc->opt1 != OPT1_CRYSTALIZE && src->type != BL_MOB) && sc->opt1 != OPT1_BURNING && skill_id != SR_GENTLETOUCH_CURE) { //Stuned/Frozen/etc
+		if (skill_id != RK_REFRESH && sc->opt1 >0 && !(sc->opt1 == OPT1_CRYSTALIZE && src->type == BL_MOB) && sc->opt1 != OPT1_BURNING && skill_id != SR_GENTLETOUCH_CURE) { //Stuned/Frozen/etc
 			if (flag != 1) //Can't cast, casted stuff can't damage.
 				return 0;
 			if (!(skill_get_inf(skill_id)&INF_GROUND_SKILL))
@@ -1677,22 +1677,22 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, uin
 		}
 	}
 
-	if (sc && sc->option)
-	{
-		if (sc->option&OPTION_HIDE)
-		switch (skill_id) { //Usable skills while hiding.
-			case TF_HIDING:
-			case AS_GRIMTOOTH:
-			case RG_BACKSTAP:
-			case RG_RAID:
-			case NJ_SHADOWJUMP:
-			case NJ_KIRIKAGE:
-			case KO_YAMIKUMO:
-				break;
-			default:
-				//Non players can use all skills while hidden.
-				if (!skill_id || src->type == BL_PC)
-					return 0;
+	if (sc && sc->option) {
+		if (sc->option&OPTION_HIDE) {
+			switch (skill_id) { //Usable skills while hiding.
+				case TF_HIDING:
+				case AS_GRIMTOOTH:
+				case RG_BACKSTAP:
+				case RG_RAID:
+				case NJ_SHADOWJUMP:
+				case NJ_KIRIKAGE:
+				case KO_YAMIKUMO:
+					break;
+				default:
+					//Non players can use all skills while hidden.
+					if (!skill_id || src->type == BL_PC)
+						return 0;
+			}
 		}
 		if (sc->option&OPTION_CHASEWALK && skill_id != ST_CHASEWALK)
 			return 0;
@@ -2442,6 +2442,19 @@ int status_calc_pc_(struct map_session_data* sd, bool first)
 		if(!sd->inventory_data[index])
 			continue;
 
+		if(sd->inventory_data[index]->flag.no_equip) { // Items may be equipped, their effects however are nullified.
+			if(map[sd->bl.m].flag.restricted && sd->inventory_data[index]->flag.no_equip&(8*map[sd->bl.m].zone))
+				continue;
+			if(!map_flag_vs(sd->bl.m) && sd->inventory_data[index]->flag.no_equip&1)
+				continue;
+			if(map[sd->bl.m].flag.pvp && sd->inventory_data[index]->flag.no_equip&2)
+				continue;
+			if(map_flag_gvg(sd->bl.m) && sd->inventory_data[index]->flag.no_equip&4)
+				continue;
+			if(map[sd->bl.m].flag.battleground && sd->inventory_data[index]->flag.no_equip&8)
+				continue;
+		}
+
 		status->def += sd->inventory_data[index]->def;
 
 		if(first && sd->inventory_data[index]->equip_script)

+ 3 - 2
src/map/unit.c

@@ -325,7 +325,8 @@ int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
 	if( ud == NULL) return 0;
 
 #ifdef OFFICIAL_WALKPATH
-	if( path_search(&wpd, bl->m, bl->x, bl->y, x, y, flag&1, CELL_CHKNOPASS) // Check if there is an obstacle between
+	path_search(&wpd, bl->m, bl->x, bl->y, x, y, flag&1, CELL_CHKNOPASS); // Count walk path cells
+	if( !path_search_long(NULL, bl->m, bl->x, bl->y, x, y, CELL_CHKNOPASS) // Check if there is an obstacle between
 		&& wpd.path_len > 14 ) // Official number of walkable cells is 14 if and only if there is an obstacle between. [malufett]
 		return 0;
 #endif
@@ -938,7 +939,7 @@ int unit_can_move(struct block_list *bl) {
 		if( sc->data[SC_ANKLE] && ( battle_config.skill_trap_type || !unit_is_walking(bl) ) ) // Ankle only stops you after you're done moving
 			return 0;
 			
-		if (sc->opt1 > 0 && sc->opt1 != OPT1_STONEWAIT && sc->opt1 != OPT1_BURNING && (sc->opt1 != OPT1_CRYSTALIZE && bl->type != BL_MOB))
+		if (sc->opt1 > 0 && sc->opt1 != OPT1_STONEWAIT && sc->opt1 != OPT1_BURNING && !(sc->opt1 == OPT1_CRYSTALIZE && bl->type == BL_MOB))
 			return 0;
 
 		if ((sc->option & OPTION_HIDE) && (!sd || pc_checkskill(sd, RG_TUNNELDRIVE) <= 0))