Explorar o código

Poison React now activates when character is damaged only - Fixes bugreport:109
Cloaking fixed to official behaviour (Thanks helvetica for the hour of testing) - Concerning bugreport:171
Fixed instancing long map names causing crashes
- Cannot save or memo on instance maps (Lemongrass)
- Map channels are no longer activated on instance maps (Lemongrass)
- Fixed a character save issue with instance maps (Lemongrass)
- Map local channels are disabled by default, this is custom (Lemongrass)
- Just thank Lemongrass overall, I gave him a hard time with this issue

~ Hercules Merge ~
Update Bloody Lust and Maelstrom to official behaviour (Credits: malufett)

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

akinari1087 %!s(int64=12) %!d(string=hai) anos
pai
achega
81e3b03ace
Modificáronse 17 ficheiros con 195 adicións e 123 borrados
  1. 1 1
      conf/channels.conf
  2. 3 1
      conf/msg_conf/map_msg.conf
  3. 1 1
      db/re/skill_db.txt
  4. 6 2
      src/map/atcommand.c
  5. 6 2
      src/map/battle.c
  6. 16 1
      src/map/chrif.c
  7. 22 5
      src/map/clif.c
  8. 1 1
      src/map/clif.h
  9. 11 4
      src/map/instance.c
  10. 11 3
      src/map/map.c
  11. 15 13
      src/map/pc.c
  12. 0 1
      src/map/pc.h
  13. 3 8
      src/map/script.c
  14. 63 38
      src/map/skill.c
  15. 1 0
      src/map/skill.h
  16. 29 40
      src/map/status.c
  17. 6 2
      src/map/unit.c

+ 1 - 1
conf/channels.conf

@@ -30,7 +30,7 @@ chsys: (
 	allow_user_channel_creation: true
 
 	/* "map_local_channel" is an instanced channel unique to each map. */
-	map_local_channel: true
+	map_local_channel: false
 	map_local_channel_name: "map"
 	map_local_channel_color: "Yellow"
 	map_local_channel_autojoin: true /* Disable autojoin in specific maps through mapflag 'nomapchannelautojoin'. */

+ 3 - 1
conf/msg_conf/map_msg.conf

@@ -399,7 +399,9 @@
 380: Item Failed. [%s] is cooling down. Wait %d seconds.
 381: Skill Failed. [%s] requires %dx %s.
 382: You're too close to a stone or emperium to use this skill.
-//383-389 free
+383: You cannot create a savepoint in an instance.
+384: You cannot create a memo in an instance.
+//385-389 free
 //NoAsk
 390: Autorejecting is activated.
 391: Autorejecting is deactivated.

+ 1 - 1
db/re/skill_db.txt

@@ -874,7 +874,7 @@
 2300,7,6,2,0,0x1,0,3,1,yes,0,0,1,none,0,	SC_DIMENSIONDOOR,Dimension Door
 2301,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0,	SC_CHAOSPANIC,Chaos Panic
 2302,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0,	SC_MAELSTROM,Maelstrom
-2303,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0,	SC_BLOODYLUST,Bloody Lust
+2303,7,6,2,0,0x1,3,3,1,yes,0,0,1,none,0,	SC_BLOODYLUST,Bloody Lust
 2304,0,6,4,-1,0,0,3,1,no,0,0,0,weapon,0,	SC_FEINTBOMB,Feint Bomb
 
 //****

+ 6 - 2
src/map/atcommand.c

@@ -729,6 +729,11 @@ ACMD_FUNC(save)
 {
 	nullpo_retr(-1, sd);
 
+	if( map[sd->bl.m].instance_id ) {
+		clif_displaymessage(fd, msg_txt(sd,383)); // You cannot create a savepoint in an instance.
+		return 1;
+	}
+
 	pc_setsavepoint(sd, sd->mapindex, sd->bl.x, sd->bl.y);
 	if (sd->status.pet_id > 0 && sd->pd)
 		intif_save_petdata(sd->status.account_id, &sd->pd->pet);
@@ -2221,8 +2226,7 @@ ACMD_FUNC(memo)
 		return -1;
 	}
 
-	pc_memo(sd, position);
-	return 0;
+	return !pc_memo( sd, position );
 }
 
 /*==========================================

+ 6 - 2
src/map/battle.c

@@ -745,6 +745,10 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,struct Damag
 	if( battle_config.ksprotection && mob_ksprotected(src, bl) )
 		return 0;
 
+	if( map_getcell(bl->m, bl->x, bl->y, CELL_CHKMAELSTROM) && skill_get_type(skill_id) != BF_MISC  
+		&& skill_get_casttype(skill_id) == CAST_GROUND ) 
+		return 0;
+
 	if (bl->type == BL_PC) {
 		sd=(struct map_session_data *)bl;
 		//Special no damage states
@@ -2739,7 +2743,7 @@ static int battle_calc_attack_skill_ratio(struct Damage wd, struct block_list *s
 			skillratio += sc->data[SC_OVERTHRUST]->val3;
 		if(sc->data[SC_MAXOVERTHRUST])
 			skillratio += sc->data[SC_MAXOVERTHRUST]->val2;
-		if (sc->data[SC_BERSERK] || sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC__BLOODYLUST])
+		if (sc->data[SC_BERSERK] || sc->data[SC_SATURDAYNIGHTFEVER])
 			skillratio += 100;
 		if(sc->data[SC_ZENKAI] && sstatus->rhw.ele == sc->data[SC_ZENKAI]->val2 )
 			skillratio += sc->data[SC_ZENKAI]->val1 * 2;
@@ -6008,7 +6012,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
 	}
 
 	if (tsc) {
-		if (tsc->data[SC_POISONREACT] &&
+		if (damage > 0 && tsc->data[SC_POISONREACT] &&
 			(rnd()%100 < tsc->data[SC_POISONREACT]->val3
 			|| sstatus->def_ele == ELE_POISON) &&
 //			check_distance_bl(src, target, tstatus->rhw.range+1) && Doesn't checks range! o.O;

+ 16 - 1
src/map/chrif.c

@@ -304,7 +304,22 @@ int chrif_save(struct map_session_data *sd, int flag) {
 	WFIFOL(char_fd,4) = sd->status.account_id;
 	WFIFOL(char_fd,8) = sd->status.char_id;
 	WFIFOB(char_fd,12) = (flag==1)?1:0; //Flag to tell char-server this character is quitting.
-	memcpy(WFIFOP(char_fd,13), &sd->status, sizeof(sd->status));
+
+	// If the user is on a instance map, we have to fake his current position
+	if( map[sd->bl.m].instance_id ){
+		struct mmo_charstatus status;
+
+		// Copy the whole status
+		memcpy( &status, &sd->status, sizeof( struct mmo_charstatus ) );
+		// Change his current position to his savepoint
+		memcpy( &status.last_point, &status.save_point, sizeof( struct point ) );
+		// Copy the copied status into the packet
+		memcpy( WFIFOP( char_fd, 13 ), &status, sizeof( struct mmo_charstatus ) );
+	} else {
+		// Copy the whole status into the packet
+		memcpy( WFIFOP( char_fd, 13 ), &sd->status, sizeof( struct mmo_charstatus ) );
+	}
+
 	WFIFOSET(char_fd, WFIFOW(char_fd,2));
 
 	if( sd->status.pet_id > 0 && sd->pd )

+ 22 - 5
src/map/clif.c

@@ -1660,7 +1660,7 @@ void clif_quitsave(int fd,struct map_session_data *sd) {
 
 /// Notifies the client of a position change to coordinates on given map (ZC_NPCACK_MAPMOVE).
 /// 0091 <map name>.16B <x>.W <y>.W
-void clif_changemap(struct map_session_data *sd, short map, int x, int y)
+void clif_changemap(struct map_session_data *sd, short m, int x, int y)
 {
 	int fd;
 	nullpo_retv(sd);
@@ -1668,7 +1668,18 @@ void clif_changemap(struct map_session_data *sd, short map, int x, int y)
 
 	WFIFOHEAD(fd,packet_len(0x91));
 	WFIFOW(fd,0) = 0x91;
-	mapindex_getmapname_ext(mapindex_id2name(map), (char*)WFIFOP(fd,2));
+	if(map[m].instance_id) { // Instance map check to send client source map name so we don't crash player
+		struct instance_data *im = &instance_data[map[m].instance_id];
+		int i;
+		if(!im) // This shouldn't happen but if it does give them the map we intended to give
+			mapindex_getmapname_ext(map[m].name, (char*)WFIFOP(fd,2));
+		for(i = 0; i < MAX_MAP_PER_INSTANCE; i++) // Loop to find the src map we want
+			if(im->map[i].m == m) {
+				mapindex_getmapname_ext(map[im->map[i].src_m].name, (char*)WFIFOP(fd,2));
+				break;
+			}
+	} else
+		mapindex_getmapname_ext(map[m].name, (char*)WFIFOP(fd,2));
 	WFIFOW(fd,18) = x;
 	WFIFOW(fd,20) = y;
 	WFIFOSET(fd,packet_len(0x91));
@@ -8286,7 +8297,7 @@ void clif_refresh(struct map_session_data *sd)
 	int i;
 	nullpo_retv(sd);
 
-	clif_changemap(sd,sd->mapindex,sd->bl.x,sd->bl.y);
+	clif_changemap(sd,sd->bl.m,sd->bl.x,sd->bl.y);
 	clif_inventorylist(sd);
 	if(pc_iscarton(sd)) {
 		clif_cartlist(sd);
@@ -9139,7 +9150,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 
 	if (sd->state.rewarp) { //Rewarp player.
 		sd->state.rewarp = 0;
-		clif_changemap(sd, sd->mapindex, sd->bl.x, sd->bl.y);
+		clif_changemap(sd, sd->bl.m, sd->bl.x, sd->bl.y);
 		return;
 	}
 
@@ -9379,7 +9390,8 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
 		status_calc_pc(sd, false); // Some conditions are map-dependent so we must recalculate
 		sd->state.changemap = false;
 
-		if( Channel_Config.map_enable && Channel_Config.map_autojoin && !map[sd->bl.m].flag.chmautojoin) {
+		// Instances do not need their own channels
+		if( Channel_Config.map_enable && Channel_Config.map_autojoin && !map[sd->bl.m].flag.chmautojoin && !map[sd->bl.m].instance_id ) {
 			channel_mjoin(sd); //join new map
 		}
 	}
@@ -9567,6 +9579,11 @@ void clif_parse_WalkToXY(int fd, struct map_session_data *sd)
 	if(sd->sc.data[SC_RUN] || sd->sc.data[SC_WUGDASH])
 		return;
 
+	// Cloaking wall check is actually updated when you click to process next movement
+	// not when you move each cell.  This is official behaviour.
+	if (sd->sc.data[SC_CLOAKING])
+		skill_check_cloaking(&sd->bl, sd->sc.data[SC_CLOAKING]);
+
 	pc_delinvincibletimer(sd);
 
 	RFIFOPOS(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0], &x, &y, NULL);

+ 1 - 1
src/map/clif.h

@@ -345,7 +345,7 @@ void clif_clearunit_delayed(struct block_list* bl, clr_type type, unsigned int t
 int clif_spawn(struct block_list *bl);	//area
 void clif_walkok(struct map_session_data *sd);	// self
 void clif_move(struct unit_data *ud); //area
-void clif_changemap(struct map_session_data *sd, short map, int x, int y);	//self
+void clif_changemap(struct map_session_data *sd, short m, int x, int y);	//self
 void clif_changemapserver(struct map_session_data* sd, unsigned short map_index, int x, int y, uint32 ip, uint16 port);	//self
 void clif_blown(struct block_list *bl); // area
 void clif_slide(struct block_list *bl, int x, int y); // area

+ 11 - 4
src/map/instance.c

@@ -369,7 +369,7 @@ int instance_mapname2mapid(const char *name, short instance_id)
 {
 	struct instance_data *im;
 	int m = map_mapname2mapid(name);
-	char iname[12];
+	char iname[MAP_NAME_LENGTH];
 	int i;
 
 	if(m < 0) {
@@ -377,6 +377,8 @@ int instance_mapname2mapid(const char *name, short instance_id)
 		return m;
 	}
 
+	strcpy(iname,name);
+
 	if(instance_id <= 0 || instance_id > MAX_INSTANCE_DATA)
 		return m;
 
@@ -386,8 +388,13 @@ int instance_mapname2mapid(const char *name, short instance_id)
 
 	for(i = 0; i < MAX_MAP_PER_INSTANCE; i++)
 		if(im->map[i].src_m == m) {
-			snprintf(iname, sizeof(iname), ((strchr(name,'@') == NULL)?"%.3d#%s":"%.3d%s"), instance_id, name);
-			return mapindex_name2id(iname);
+			char alt_name[MAP_NAME_LENGTH];
+			if((strchr(iname,'@') == NULL) && strlen(iname) > 8) {
+				memmove(iname, iname+(strlen(iname)-9), strlen(iname));
+				snprintf(alt_name, sizeof(alt_name),"%d#%s", instance_id, iname);
+			} else
+				snprintf(alt_name, sizeof(alt_name),"%.3d%s", instance_id, iname);
+			return map_mapname2mapid(alt_name);
 		}
 
 	return m;
@@ -515,7 +522,7 @@ int instance_enter(struct map_session_data *sd, const char *name)
 	if((m = instance_mapname2mapid(db->enter.mapname, p->instance_id)) < 0)
 		return 3;
 
-	if(pc_setpos(sd, m, db->enter.x, db->enter.y, 0))
+	if(pc_setpos(sd, map_id2index(m), db->enter.x, db->enter.y, 0))
 		return 3;
 
 	// If there was an idle timer, let's stop it

+ 11 - 3
src/map/map.c

@@ -425,8 +425,6 @@ int map_moveblock(struct block_list *bl, int x1, int y1, unsigned int tick)
 			if (sc->data[SC_DANCING])
 				skill_unit_move_unit_group(skill_id2group(sc->data[SC_DANCING]->val2), bl->m, x1-x0, y1-y0);
 			else {
-				if (sc->data[SC_CLOAKING])
-					skill_check_cloaking(bl, sc->data[SC_CLOAKING]);
 				if (sc->data[SC_WARM])
 					skill_unit_move_unit_group(skill_id2group(sc->data[SC_WARM]->val4), bl->m, x1-x0, y1-y0);
 				if (sc->data[SC_BANDING])
@@ -2181,6 +2179,7 @@ int map_addinstancemap(const char *name, int id)
 {
 	int src_m = map_mapname2mapid(name);
 	int dst_m = -1, i;
+	char iname[MAP_NAME_LENGTH];
 	size_t size;
 
 	if(src_m < 0)
@@ -2209,8 +2208,16 @@ int map_addinstancemap(const char *name, int id)
 	// Copy the map
 	memcpy(&map[dst_m], &map[src_m], sizeof(struct map_data));
 
+	strcpy(iname,name);
+
 	// Alter the name
-	snprintf(map[dst_m].name, sizeof(map[dst_m].name), ((strchr(name,'@') == NULL)?"%.3d#%s":"%.3d%s"), id, name);
+	// Due to this being custom we only worry about preserving as many characters as necessary for accurate map distinguishing
+	// This also allows us to maintain complete independence with main map functions
+	if((strchr(iname,'@') == NULL) && strlen(iname) > 8) {
+		memmove(iname, iname+(strlen(iname)-9), strlen(iname));
+		snprintf(map[dst_m].name, sizeof(map[dst_m].name),"%d#%s", id, iname);
+	} else
+		snprintf(map[dst_m].name, sizeof(map[dst_m].name),"%.3d%s", id, iname);
 	map[dst_m].name[MAP_NAME_LENGTH-1] = '\0';
 
 	map[dst_m].m = dst_m;
@@ -2225,6 +2232,7 @@ int map_addinstancemap(const char *name, int id)
 	map[dst_m].block_mob = (struct block_list **)aCalloc(1,size);
 
 	map[dst_m].index = mapindex_addmap(-1, map[dst_m].name);
+	map[dst_m].channel = NULL;
 
 	map_addmap2db(&map[dst_m]);
 

+ 15 - 13
src/map/pc.c

@@ -1091,7 +1091,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim
 		/**
 		 * Fixes login-without-aura glitch (the screen won't blink at this point, don't worry :P)
 		 **/
-		clif_changemap(sd,sd->mapindex,sd->bl.x,sd->bl.y);
+		clif_changemap(sd,sd->bl.m,sd->bl.x,sd->bl.y);
 	}
 
 	/**
@@ -4211,7 +4211,7 @@ int pc_isUseitem(struct map_session_data *sd,int n)
 		case 12243: // Mercenary's Berserk Potion
 			if( sd->md == NULL || sd->md->db == NULL )
 				return 0;
-			if (sd->md->sc.data[SC_BERSERK] || sd->md->sc.data[SC_SATURDAYNIGHTFEVER] || sd->md->sc.data[SC__BLOODYLUST])
+			if (sd->md->sc.data[SC_BERSERK] || sd->md->sc.data[SC_SATURDAYNIGHTFEVER])
 				return 0;
 			if( nameid == 12242 && sd->md->db->lv < 40 )
 				return 0;
@@ -4308,7 +4308,7 @@ int pc_useitem(struct map_session_data *sd,int n)
 		return 0;
 
 	if (sd->sc.count && (
-		sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || sd->sc.data[SC_SATURDAYNIGHTFEVER] ||
+		sd->sc.data[SC_BERSERK] || sd->sc.data[SC_SATURDAYNIGHTFEVER] ||
 		(sd->sc.data[SC_GRAVITATION] && sd->sc.data[SC_GRAVITATION]->val3 == BCT_SELF) ||
 		sd->sc.data[SC_TRICKDEAD] ||
 		sd->sc.data[SC_HIDING] ||
@@ -4879,7 +4879,7 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y
 
 	if(sd->bl.prev != NULL){
 		unit_remove_map_pc(sd,clrtype);
-		clif_changemap(sd,map[m].index,x,y); // [MouseJstr]
+		clif_changemap(sd,m,x,y); // [MouseJstr]
 	} else if(sd->state.active) //Tag player for rewarping after map-loading is done. [Skotlex]
 		sd->state.rewarp = 1;
 
@@ -4992,6 +4992,11 @@ int pc_memo(struct map_session_data* sd, int pos)
 		pos = 0;
 	}
 
+	if( map[sd->bl.m].instance_id ) {
+		clif_displaymessage( sd->fd, msg_txt(sd,384) ); // You cannot create a memo in an instance.
+		return 0;
+	}
+
 	sd->status.memo_point[pos].map = map_id2index(sd->bl.m);
 	sd->status.memo_point[pos].x = sd->bl.x;
 	sd->status.memo_point[pos].y = sd->bl.y;
@@ -8553,8 +8558,7 @@ int pc_equipitem(struct map_session_data *sd,int n,int req_pos)
 		return 0;
 	}
 
-	if( DIFF_TICK(sd->canequip_tick,gettick()) > 0 )
-	{
+	if( DIFF_TICK(sd->canequip_tick,gettick()) > 0 ) {
 		clif_equipitemack(sd,n,0,0);
 		return 0;
 	}
@@ -8570,8 +8574,7 @@ int pc_equipitem(struct map_session_data *sd,int n,int req_pos)
 		return 0;
 	}
 
-	if (sd->sc.data[SC_BERSERK] || sd->sc.data[SC_SATURDAYNIGHTFEVER] || sd->sc.data[SC__BLOODYLUST])
-	{
+	if (sd->sc.data[SC_BERSERK] || sd->sc.data[SC_SATURDAYNIGHTFEVER]) {
 		clif_equipitemack(sd,n,0,0);	// fail
 		return 0;
 	}
@@ -8582,15 +8585,14 @@ int pc_equipitem(struct map_session_data *sd,int n,int req_pos)
 			pos = sd->equip_index[EQI_ACC_R] >= 0 ? EQP_ACC_L : EQP_ACC_R;
 	}
 
-	if(pos == EQP_ARMS && id->equip == EQP_HAND_R)
-	{	//Dual wield capable weapon.
+	if(pos == EQP_ARMS && id->equip == EQP_HAND_R) { //Dual wield capable weapon.
 		pos = (req_pos&EQP_ARMS);
 		if (pos == EQP_ARMS) //User specified both slots, pick one for them.
 			pos = sd->equip_index[EQI_HAND_R] >= 0 ? EQP_HAND_L : EQP_HAND_R;
 	}
 
-	if (pos&EQP_HAND_R && battle_config.use_weapon_skill_range&BL_PC)
-	{	//Update skill-block range database when weapon range changes. [Skotlex]
+	if (pos&EQP_HAND_R && battle_config.use_weapon_skill_range&BL_PC) {
+		//Update skill-block range database when weapon range changes. [Skotlex]
 		i = sd->equip_index[EQI_HAND_R];
 		if (i < 0 || !sd->inventory_data[i]) //No data, or no weapon equipped
 			flag = 1;
@@ -8607,7 +8609,7 @@ int pc_equipitem(struct map_session_data *sd,int n,int req_pos)
 		}
 	}
 
-	if(pos==EQP_AMMO){
+	if(pos==EQP_AMMO) {
 		clif_arrowequip(sd,n);
 		clif_arrow_fail(sd,3);
 	}

+ 0 - 1
src/map/pc.h

@@ -250,7 +250,6 @@ struct map_session_data {
 	unsigned int canskill_tick; // used to prevent abuse from no-delay ACT files
 	unsigned int cansendmail_tick; // [Mail System Flood Protection]
 	unsigned int ks_floodprotect_tick; // [Kill Steal Protection]
-	unsigned int bloodylust_tick; // bloodylust player timer [out/in re full-heal protection]
 
 	struct {
 		short nameid;

+ 3 - 8
src/map/script.c

@@ -16561,21 +16561,18 @@ BUILDIN_FUNC(instance_npcname)
 BUILDIN_FUNC(instance_mapname)
 {
  	const char *str;
-	char iname[12];
 	int16 m;
 	short instance_id = 0;
 
  	str = script_getstr(st,2);
+
 	if( script_hasdata(st,3) )
 		instance_id = script_getnum(st,3);
 	else
 		instance_id = script_instancegetid(st);
 
-	// Build the instance mapname
-	snprintf(iname, sizeof(iname), ((strchr(str,'@') == NULL)?"%.3d#%s":"%.3d%s"), instance_id, str);
-
 	// Check that instance mapname is a valid map
-	if( !instance_id || (m = map_mapname2mapid(iname)) < 0 )
+	if(!instance_id || (m = instance_mapname2mapid(str,instance_id)) < 0)
 		script_pushconststr(st, "");
 	else
 		script_pushconststr(st, map[m].name);
@@ -16616,7 +16613,6 @@ BUILDIN_FUNC(instance_warpall)
 	short instance_id;
 	const char *mapn;
 	int x, y;
-	unsigned short mapindex;
 
 	mapn = script_getstr(st,2);
 	x    = script_getnum(st,3);
@@ -16632,9 +16628,8 @@ BUILDIN_FUNC(instance_warpall)
 	if( !(p = party_search(instance_data[instance_id].party_id)) )
 		return 0;
 
-	mapindex = map_id2index(m);
 	for( i = 0; i < MAX_PARTY; i++ )
-		if( (pl_sd = p->data[i].sd) && map[pl_sd->bl.m].instance_id == instance_id ) pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT);
+		if( (pl_sd = p->data[i].sd) && map[pl_sd->bl.m].instance_id == instance_id ) pc_setpos(pl_sd,map_id2index(m),x,y,CLR_TELEPORT);
 
 	return 0;
 }

+ 63 - 38
src/map/skill.c

@@ -5028,7 +5028,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 						dstsd = sd;
 					}
 				}
-				else if (tsc->data[SC_BERSERK] || tsc->data[SC_SATURDAYNIGHTFEVER] || tsc->data[SC__BLOODYLUST])
+				else if (tsc->data[SC_BERSERK] || tsc->data[SC_SATURDAYNIGHTFEVER])
 					heal = 0; //Needed so that it actually displays 0 when healing.
 			}
 			clif_skill_nodamage (src, bl, skill_id, heal, 1);
@@ -10081,6 +10081,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
 	case SC_DIMENSIONDOOR:
 	case SC_CHAOSPANIC:
 	case SC_MAELSTROM:
+	case SC_BLOODYLUST:
 	case WM_REVERBERATION:
 	case WM_SEVERE_RAINSTORM:
 	case WM_POEMOFNETHERWORLD:
@@ -10565,11 +10566,6 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
 			sc_start2(src,src, type, 100, skill_id, skill_lv, skill_get_time(skill_id, skill_lv)));
 		break;
 
-	case SC_BLOODYLUST: //set in another group so instance will move if recasted
-            flag |= 33;
-            skill_unitsetting(src, skill_id, skill_lv, x, y, 0);
-            break;
-
 	case KO_MAKIBISHI:
 		for( i = 0; i < (skill_lv+2); i++ ) {
 			x = src->x - 1 + rnd()%3;
@@ -10629,7 +10625,7 @@ int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char
 		sd->sc.data[SC_AUTOCOUNTER] ||
 		sd->sc.data[SC_STEELBODY] ||
 		(sd->sc.data[SC_DANCING] && skill_id < RK_ENCHANTBLADE && !pc_checkskill(sd, WM_LESSON)) ||
-		sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] ||
+		sd->sc.data[SC_BERSERK] ||
 		sd->sc.data[SC_BASILICA] ||
 		sd->sc.data[SC_MARIONETTE] ||
 		sd->sc.data[SC_WHITEIMPRISON] ||
@@ -11163,9 +11159,6 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill
 	case SO_VACUUM_EXTREME:
 		range++;
 
-		break;
-	case SC_BLOODYLUST:
-		skill_clear_group(src, 32);
 		break;
 	case GN_WALLOFTHORN:
 		if( flag&1 )
@@ -11299,6 +11292,9 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill
 		if (unit_flag&UF_RANGEDSINGLEUNIT && i == (layout->count / 2))
 			val2 |= UF_RANGEDSINGLEUNIT; // center.
 
+		if( sd && map_getcell(src->m, ux, uy, CELL_CHKMAELSTROM) ) //Does not recover SP from monster skills 
+			map_foreachincell(skill_maelstrom_suction,src->m,ux,uy,BL_SKILL,skill_id,skill_lv);
+
 		if( range <= 0 )
 			map_foreachincell(skill_cell_overlap,src->m,ux,uy,BL_SKILL,skill_id, &alive, src);
 		if( !alive )
@@ -11360,7 +11356,8 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un
 	nullpo_ret(sg=src->group);
 	nullpo_ret(ss=map_id2bl(sg->src_id));
 
-	if( skill_get_type(sg->skill_id) == BF_MAGIC && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) && sg->skill_id != SA_LANDPROTECTOR )
+	if( (skill_get_type(sg->skill_id) == BF_MAGIC && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) && sg->skill_id != SA_LANDPROTECTOR) ||
+		map_getcell(bl->m, bl->x, bl->y, CELL_CHKMAELSTROM) )
 		return 0; //AoE skills are ineffective. [Skotlex]
 
 	if( skill_get_inf2(sg->skill_id)&(INF2_SONG_DANCE|INF2_ENSEMBLE_SKILL) && map_getcell(bl->m, bl->x, bl->y, CELL_CHKBASILICA) )
@@ -11400,27 +11397,18 @@ static int skill_unit_onplace (struct skill_unit *src, struct block_list *bl, un
 				sc_start4(ss, bl,type,100,sg->skill_lv,sg->skill_id,sg->group_id,0,sg->limit);
 			break;
 
+		case UNT_BLOODYLUST:
+			if (sg->src_id == bl->id)
+				break; //Does not affect the caster.
+			if( !sce && sc_start4(ss, bl,type,100,sg->skill_lv,0,SC__BLOODYLUST,0,sg->limit) )
+				sc_start(ss, bl,SC__BLOODYLUST,100,sg->skill_lv,sg->limit);
+			break;
+
 		case UNT_PNEUMA:
 		case UNT_CHAOSPANIC:
-		case UNT_MAELSTROM:
 			if (!sce)
 				sc_start4(ss, bl,type,100,sg->skill_lv,sg->group_id,0,0,sg->limit);
 			break;
-		case UNT_BLOODYLUST:
-			if (sg->src_id == bl->id)
-				break; //Does not affect the caster.
-			if (!sce) {
-				TBL_PC *sd = BL_CAST(BL_PC, bl); //prevent fullheal exploit
-				if (sd && sd->bloodylust_tick && DIFF_TICK(gettick(), sd->bloodylust_tick) < skill_get_time2(SC_BLOODYLUST, 1))
-					clif_skill_nodamage(&src->bl,bl,sg->skill_id,sg->skill_lv,
-						sc_start4(ss, bl, type, 100, sg->skill_lv, 1, 0, 0, skill_get_time(LK_BERSERK, sg->skill_lv)));
-				else {
-					if (sd) sd->bloodylust_tick = gettick();
-						clif_skill_nodamage(&src->bl,bl,sg->skill_id,sg->skill_lv,
-							sc_start4(ss, bl, type, 100, sg->skill_lv, 0, 0, 0, skill_get_time(LK_BERSERK, sg->skill_lv)));
-				}
-			}
-			break;
 
 		case UNT_WARP_WAITING: {
 			int working = sg->val1&0xffff;
@@ -12413,14 +12401,9 @@ int skill_unit_onleft (uint16 skill_id, struct block_list *bl, unsigned int tick
 		case SO_WATER_INSIGNIA:
 		case SO_WIND_INSIGNIA:
 		case SO_EARTH_INSIGNIA:
-			if (sce)
-				status_change_end(bl, type, INVALID_TIMER);
-			break;
 		case SC_BLOODYLUST:
-			if (sce) {
+			if (sce)
 				status_change_end(bl, type, INVALID_TIMER);
-				status_set_sp(bl, 0, 0); //set sp to 0 when quitting zone
-			}
 			break;
 		case BA_DISSONANCE:
 		case DC_UGLYDANCE: //Used for updating song timers in overlap instances
@@ -12908,6 +12891,22 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id
 				return 0;
 			}
 			break;
+		case AS_CLOAKING:
+		{
+			static int dx[] = { 0, 1, 0, -1, -1,  1, 1, -1};
+			static int dy[] = {-1, 0, 1,  0, -1, -1, 1,  1};
+
+			if( (sd->bl.type == BL_PC && battle_config.pc_cloak_check_type&1)
+			||	(sd->bl.type != BL_PC && battle_config.monster_cloak_check_type&1) ) { //Check for walls.
+				int i;
+				ARR_FIND( 0, 8, i, map_getcell(sd->bl.m, sd->bl.x+dx[i], sd->bl.y+dy[i], CELL_CHKNOPASS) != 0 );
+				if( i == 8 ) {
+					clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+					return 0;
+				}
+			}
+			break;
+		}
 		case AL_WARP:
 			if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza]
 				char output[128]; sprintf(output, msg_txt(sd,365), skill_get_name(AL_WARP));
@@ -15002,10 +15001,6 @@ int skill_clear_group (struct block_list *bl, int flag)
 				if( flag&8 )
 					group[count++]= ud->skillunit[i];
 				break;
-			case SC_BLOODYLUST:
-				if (flag & 32)
-					group[count++] = ud->skillunit[i];
-				break;
 			default:
 				if (flag&2 && skill_get_inf2(ud->skillunit[i]->skill_id)&INF2_TRAP)
 					group[count++]= ud->skillunit[i];
@@ -15036,7 +15031,6 @@ struct skill_unit_group *skill_locate_element_field(struct block_list *bl)
 			case SA_LANDPROTECTOR:
 			case NJ_SUITON:
 			case SO_WARMER:
-			case SC_BLOODYLUST:
 				return ud->skillunit[i];
 		}
 	}
@@ -15317,6 +15311,34 @@ static int skill_trap_splash (struct block_list *bl, va_list ap)
 	return 1;
 }
 
+int skill_maelstrom_suction(struct block_list *bl, va_list ap)
+{
+	uint16 skill_id, skill_lv;
+	struct skill_unit *unit;
+
+	skill_id = va_arg(ap,int);
+	skill_lv = va_arg(ap,int);
+	unit = (struct skill_unit *)bl;
+
+	if( unit == NULL || unit->group == NULL )
+		return 0;
+
+	if( skill_get_inf2(skill_id)&INF2_TRAP )
+		return 0;
+
+	if( unit->group->skill_id == SC_MAELSTROM ){
+		struct block_list *src;
+		if( (src = map_id2bl(unit->group->src_id)) ){
+			int sp = unit->group->skill_lv * skill_lv;
+			if( src->type == BL_PC )
+				sp += ((TBL_PC*)src)->status.job_level / 5;
+			status_heal(src, 0, sp/2, 1);
+		}
+	}
+
+	return 0;
+}
+
 /*==========================================
  *
  *------------------------------------------*/
@@ -15405,6 +15427,9 @@ struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int
 	nullpo_retr(NULL, group->unit); // crash-protection against poor coding
 	nullpo_retr(NULL, unit=&group->unit[idx]);
 
+	if( map_getcell(map_id2bl(group->src_id)->m, x, y, CELL_CHKMAELSTROM) )
+		return unit;
+
 	if(!unit->alive)
 		group->alive_count++;
 

+ 1 - 0
src/map/skill.h

@@ -1865,6 +1865,7 @@ struct s_skill_magicmushroom_db {
 	uint16 skill_id;
 };
 extern struct s_skill_magicmushroom_db skill_magicmushroom_db[MAX_SKILL_MAGICMUSHROOM_DB];
+int skill_maelstrom_suction(struct block_list *bl, va_list ap);
 /**
  * Ranger
  **/

+ 29 - 40
src/map/status.c

@@ -496,10 +496,10 @@ void initChangeTables(void) {
 	set_sc(MH_PAIN_KILLER		, SC_PAIN_KILLER	, SI_PAIN_KILLER	, SCB_ASPD );
 
 	add_sc(MH_STYLE_CHANGE		, SC_STYLE_CHANGE	);
-	set_sc(MH_TINDER_BREAKER	, SC_TINDER_BREAKER2	, SI_TINDER_BREAKER, SCB_FLEE );
-	set_sc(MH_TINDER_BREAKER	, SC_TINDER_BREAKER	, SI_TINDER_BREAKER_POSTDELAY, SCB_FLEE );
-	set_sc(MH_CBC			, SC_CBC, SI_CBC	, SCB_FLEE );
-	set_sc(MH_EQC			, SC_EQC, SI_EQC	, SCB_DEF2|SCB_BATK|SCB_MAXHP );
+	set_sc(MH_TINDER_BREAKER	, SC_TINDER_BREAKER2	, SI_TINDER_BREAKER		, SCB_FLEE );
+	set_sc(MH_TINDER_BREAKER	, SC_TINDER_BREAKER	, SI_TINDER_BREAKER_POSTDELAY	, SCB_FLEE );
+	set_sc(MH_CBC			, SC_CBC		, SI_CBC			, SCB_FLEE );
+	set_sc(MH_EQC			, SC_EQC		, SI_EQC			, SCB_DEF2|SCB_BATK|SCB_MAXHP );
 
 	add_sc( MER_CRASH		, SC_STUN		);
 	set_sc( MER_PROVOKE		, SC_PROVOKE		, SI_PROVOKE		, SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK );
@@ -635,7 +635,7 @@ void initChangeTables(void) {
 	set_sc( SC_STRIPACCESSARY	, SC__STRIPACCESSORY	, SI_STRIPACCESSARY	, SCB_DEX|SCB_INT|SCB_LUK );
 	set_sc_with_vfx( SC_MANHOLE	, SC__MANHOLE		, SI_MANHOLE		, SCB_NONE );
 	add_sc( SC_CHAOSPANIC		, SC_CONFUSION		);
-	set_sc_with_vfx( SC_BLOODYLUST	, SC__BLOODYLUST	, SI_BERSERK		, SCB_DEF | SCB_DEF2 | SCB_MDEF | SCB_MDEF2 | SCB_FLEE | SCB_SPEED | SCB_ASPD | SCB_MAXHP | SCB_REGEN );
+	add_sc( SC_BLOODYLUST		, SC_BERSERK		);
 	/**
 	 * Sura
 	 **/
@@ -1048,7 +1048,6 @@ void initChangeTables(void) {
 	StatusChangeStateTable[SC_BERSERK]		|= SCS_NOCHAT;
 	StatusChangeStateTable[SC_SATURDAYNIGHTFEVER]	|= SCS_NOCHAT;
 	StatusChangeStateTable[SC_DEEPSLEEP]		|= SCS_NOCHAT;
-	StatusChangeStateTable[SC__BLOODYLUST]		|= SCS_NOCHAT;
 	StatusChangeStateTable[SC_NOCHAT]		|= SCS_NOCHAT|SCS_NOCHATCOND;
 
 }
@@ -1233,8 +1232,6 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s
 			status_change_end(target, SC_RAISINGDRAGON, INVALID_TIMER);
 		if (sc->data[SC_SATURDAYNIGHTFEVER] && status->hp <= 100)
 			status_change_end(target, SC_SATURDAYNIGHTFEVER, INVALID_TIMER);
-		if (sc->data[SC__BLOODYLUST] && status->hp <= 100)
-			status_change_end(target, SC__BLOODYLUST, INVALID_TIMER);
 	}
 
 	switch (target->type) {
@@ -1367,7 +1364,7 @@ int status_heal(struct block_list *bl,int hp,int sp, int flag)
 	}
 
 	if(hp) {
-		if( sc && (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) ) {
+		if( sc && (sc->data[SC_BERSERK]) ) {
 			if( flag&1 )
 				flag &= ~2;
 			else
@@ -3488,7 +3485,7 @@ void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, str
 	if (
 		(sc->data[SC_POISON] && !sc->data[SC_SLOWPOISON])
 		|| (sc->data[SC_DPOISON] && !sc->data[SC_SLOWPOISON])
-		|| sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]
+		|| sc->data[SC_BERSERK]
 		|| sc->data[SC_TRICKDEAD]
 		|| sc->data[SC_BLEEDING]
 		|| sc->data[SC_MAGICMUSHROOM]
@@ -4910,7 +4907,7 @@ static signed short status_calc_flee(struct block_list *bl, struct status_change
 		flee += flee * sc->data[SC_INCFLEERATE]->val1/100;
 	if(sc->data[SC_SPIDERWEB] && sc->data[SC_SPIDERWEB]->val1)
 		flee -= flee * 50/100;
-	if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+	if(sc->data[SC_BERSERK])
 		flee -= flee * 50/100;
 	if(sc->data[SC_BLIND])
 		flee -= flee * 25/100;
@@ -4957,7 +4954,7 @@ static defType status_calc_def(struct block_list *bl, struct status_change *sc,
 	if(!sc || !sc->count)
 		return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX);
 
-	if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+	if(sc->data[SC_BERSERK])
 		return 0;
 	if(sc->data[SC_SKA])
 		return sc->data[SC_SKA]->val3;
@@ -5037,7 +5034,7 @@ static signed short status_calc_def2(struct block_list *bl, struct status_change
 		return (short)cap_value(def2,1,SHRT_MAX);
 #endif
 
-	if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+	if(sc->data[SC_BERSERK])
 		return 0;
 	if(sc->data[SC_ETERNALCHAOS])
 		return 0;
@@ -5095,7 +5092,7 @@ static defType status_calc_mdef(struct block_list *bl, struct status_change *sc,
 	if(!sc || !sc->count)
 		return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX);
 
-	if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+	if(sc->data[SC_BERSERK])
 		return 0;
 	if(sc->data[SC_BARRIER])
 		return 100;
@@ -5145,7 +5142,7 @@ static signed short status_calc_mdef2(struct block_list *bl, struct status_chang
 #endif
 
 
-	if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+	if(sc->data[SC_BERSERK])
 		return 0;
 	if(sc->data[SC_SKA])
 		return 90;
@@ -5290,7 +5287,7 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha
 				val = max( val, 1 * pc_checkskill(sd,TF_MISS) );
 			if( sc->data[SC_CLOAKING] && (sc->data[SC_CLOAKING]->val4&1) == 1 )
 				val = max( val, sc->data[SC_CLOAKING]->val1 >= 10 ? 25 : 3 * sc->data[SC_CLOAKING]->val1 - 3 );
-			if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+			if( sc->data[SC_BERSERK] )
 				val = max( val, 25 );
 			if( sc->data[SC_RUN] )
 				val = max( val, 55 );
@@ -5405,7 +5402,7 @@ static short status_calc_aspd(struct block_list *bl, struct status_change *sc, s
 		}
 	}
 
-	if((sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) &&	skills1 < 15)
+	if(sc->data[SC_BERSERK] && skills1 < 15)
 		skills1 = 15;
 	else if(sc->data[SC_MADNESSCANCEL] && skills1 < 15) // needs more info
 		skills1 = 15;
@@ -5550,7 +5547,7 @@ static short status_calc_aspd_rate(struct block_list *bl, struct status_change *
 		}
 		aspd_rate -= max;
 
-		if((sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]))
+		if(sc->data[SC_BERSERK])
 			aspd_rate -= 300;
 		else if(sc->data[SC_MADNESSCANCEL])
 			aspd_rate -= 200;
@@ -5652,7 +5649,7 @@ static unsigned int status_calc_maxhp(struct block_list *bl, struct status_chang
 		maxhp += maxhp * sc->data[SC_APPLEIDUN]->val2/100;
 	if(sc->data[SC_DELUGE])
 		maxhp += maxhp * sc->data[SC_DELUGE]->val2/100;
-	if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+	if(sc->data[SC_BERSERK])
 		maxhp += maxhp * 2;
 	if(sc->data[SC_MARIONETTE])
 		maxhp -= 1000;
@@ -6310,7 +6307,6 @@ int status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_typ
 			case SC__LAZINESS:
 			case SC__UNLUCKY:
 			case SC__WEAKNESS:
-			case SC__BLOODYLUST:
 				return 0;
 		}
 
@@ -6690,12 +6686,8 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 	break;
 
 	//There all like berserk, do not everlap each other
-	case SC__BLOODYLUST:
-		if(!sd) return 0; //should only affect player
 	case SC_BERSERK:
-		if (((type == SC_BERSERK) && (sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC__BLOODYLUST]))
-		|| ((type == SC__BLOODYLUST) && (sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC_BERSERK]))
-		)
+		if(sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC__BLOODYLUST])
 			return 0;
 		break;
 
@@ -6946,7 +6938,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 		}
 	break;
 	case SC_SATURDAYNIGHTFEVER:
-		if (sc->data[SC_BERSERK] || sc->data[SC_INSPIRATION] || sc->data[SC__BLOODYLUST])
+		if (sc->data[SC_BERSERK] || sc->data[SC_INSPIRATION])
 			return 0;
 		break;
 	}
@@ -7067,8 +7059,9 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 		status_change_end(bl, SC_CLOSECONFINE, INVALID_TIMER);
 		status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER);
 		break;
-	case SC__BLOODYLUST:
 	case SC_BERSERK:
+		if( val3 == SC__BLOODYLUST )
+			break;
 		if(battle_config.berserk_cancels_buffs) {
 			status_change_end(bl, SC_ONEHAND, INVALID_TIMER);
 			status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER);
@@ -7324,7 +7317,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 				val4 = sce->val4;
 				break;
 			case SC_LERADSDEW:
-				if (sc && (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]))
+				if (sc && sc->data[SC_BERSERK])
 					return 0;
 			case SC_SHAPESHIFT:
 			case SC_PROPERTYWALK:
@@ -7785,9 +7778,10 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			break;
 
 		case SC_BERSERK:
-			if (!sc->data[SC_ENDURE] || !sc->data[SC_ENDURE]->val4)
+			if( val3 == SC__BLOODYLUST )
+				sc_start(src,bl,(sc_type)val3,100,val1,tick);
+			if (!val3 && !(sc->data[SC_ENDURE] && sc->data[SC_ENDURE]->val4))
 				sc_start4(src,bl, SC_ENDURE, 100,10,0,0,2, tick);
-		case SC__BLOODYLUST:
 			//HP healing is performing after the calc_status call.
 			//Val2 holds HP penalty
 			if (!val4) val4 = skill_get_time2(status_sc2skill(type),val1);
@@ -8962,7 +8956,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 			break;
 		case SC_BERSERK:
 			opt_flag = 0;
-//		case SC__BLOODYLUST:
 			sc->opt3 |= OPT3_BERSERK;
 			break;
 //		case ???: // doesn't seem to do anything
@@ -9101,7 +9094,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 		pet_sc_check(sd, type); //Skotlex: Pet Status Effect Healing
 
     switch (type) {
-		case SC__BLOODYLUST:
 		case SC_BERSERK:
 			if (!(sce->val2)) { //don't heal if already set
 				status_heal(bl, status->max_hp, 0, 1); //Do not use percent_heal as this healing must override BERSERK's block.
@@ -9580,15 +9572,15 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 
 		case SC_BERSERK:
 		case SC_SATURDAYNIGHTFEVER:
-			//If val2 is removed, no HP penalty (dispelled?) [Skotlex]
-			if (status->hp > 100 && sce->val2)
+			if(status->hp > 200 && sc && sc->data[SC__BLOODYLUST]) {
+				status_percent_heal(bl, 100, 0);
+				status_change_end(bl, SC__BLOODYLUST, INVALID_TIMER);
+			} else if (status->hp > 100 && sce->val2) //If val2 is removed, no HP penalty (dispelled?) [Skotlex]
 				status_set_hp(bl, 100, 0);
-			if(sc->data[SC_ENDURE] && sc->data[SC_ENDURE]->val4 == 2)
-			{
+			if(sc->data[SC_ENDURE] && sc->data[SC_ENDURE]->val4 == 2) {
 				sc->data[SC_ENDURE]->val4 = 0;
 				status_change_end(bl, SC_ENDURE, INVALID_TIMER);
 			}
-		case SC__BLOODYLUST:
 			sc_start4(bl, bl, SC_REGENERATION, 100, 10,0,0,(RGN_HP|RGN_SP), skill_get_time(LK_BERSERK, sce->val1));
 			if( type == SC_SATURDAYNIGHTFEVER ) //Sit down force of Saturday Night Fever has the duration of only 3 seconds.
 				sc_start(bl, bl,SC_SITDOWN_FORCE,100,sce->val1,skill_get_time2(WM_SATURDAY_NIGHT_FEVER,sce->val1));
@@ -9875,7 +9867,6 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 		break;
 	case SC_BERSERK:
 		opt_flag = 0;
-//	case SC__BLOODYLUST:
 		sc->opt3 &= ~OPT3_BERSERK;
 		break;
 //	case ???: // doesn't seem to do anything
@@ -10257,7 +10248,6 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
 			return 0;
 		}
 		break;
-	case SC__BLOODYLUST:
 	case SC_BERSERK:
 		// 5% every 10 seconds [DracoRPG]
 		if( --( sce->val3 ) > 0 && status_charge(bl, sce->val2, 0) && status->hp > 100 )
@@ -11015,7 +11005,6 @@ int status_change_clear_buffs (struct block_list* bl, int type)
 					continue;
 				break;
 				//The rest are buffs that can be removed.
-			case SC__BLOODYLUST:
 			case SC_BERSERK:
 			case SC_SATURDAYNIGHTFEVER:
 				if (!(type&1))

+ 6 - 2
src/map/unit.c

@@ -987,8 +987,7 @@ int unit_can_move(struct block_list *bl) {
 				(sc->data[SC_DANCING]->val1&0xFFFF) == CG_MOONLIT ||
 				(sc->data[SC_DANCING]->val1&0xFFFF) == CG_HERMODE
 				) )
-			|| (sc->data[SC_CLOAKING] && //Need wall at level 1-2
-				sc->data[SC_CLOAKING]->val1 < 3 && !(sc->data[SC_CLOAKING]->val4&1))
+			|| (sc->data[SC_CLOAKING] && sc->data[SC_CLOAKING]->val1 < 3) // Can't move at level less than 3
 			)
 			return 0;
 
@@ -1475,6 +1474,11 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
 		}
 	}
 
+	if( (skill_id >= SC_MANHOLE && skill_id <= SC_FEINTBOMB) && map_getcell(src->m, skill_x, skill_y, CELL_CHKMAELSTROM) ) {
+		clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+		return 0;
+	}
+
 	if (!status_check_skilluse(src, NULL, skill_id, 0))
 		return 0;