Преглед изворни кода

- Full implementation of mercenary skills.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@13203 54d463be-8e91-2dee-dedb-b68131a5f0ec
zephyrus пре 16 година
родитељ
комит
2513d01bee
10 измењених фајлова са 622 додато и 322 уклоњено
  1. 51 31
      src/map/battle.c
  2. 133 72
      src/map/clif.c
  3. 1 1
      src/map/clif.h
  4. 12 13
      src/map/mob.c
  5. 2 0
      src/map/pc.c
  6. 250 102
      src/map/skill.c
  7. 3 2
      src/map/skill.h
  8. 133 72
      src/map/status.c
  9. 1 0
      src/map/status.h
  10. 36 29
      src/map/unit.c

+ 51 - 31
src/map/battle.c

@@ -39,7 +39,8 @@ int battle_getcurrentskill(struct block_list *bl)
 {	//Returns the current/last skill in use by this bl.
 	struct unit_data *ud;
 
-	if (bl->type == BL_SKILL) {
+	if( bl->type == BL_SKILL )
+	{
 		struct skill_unit * su = (struct skill_unit*)bl;
 		return su->group?su->group->skill_id:0;
 	}
@@ -322,9 +323,7 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,i
 		if( sc->data[SC_PNEUMA] && (flag&(BF_MAGIC|BF_LONG)) == BF_LONG )
 			return 0;
 
-		if((sce=sc->data[SC_AUTOGUARD]) && flag&BF_WEAPON &&
-			!(skill_get_nk(skill_num)&NK_NO_CARDFIX_ATK) &&
-			rand()%100 < sce->val2)
+		if( (sce=sc->data[SC_AUTOGUARD]) && flag&BF_WEAPON && !(skill_get_nk(skill_num)&NK_NO_CARDFIX_ATK) && rand()%100 < sce->val2 )
 		{
 			int delay;
 			clif_skill_nodamage(bl,bl,CR_AUTOGUARD,sce->val1,1);
@@ -342,11 +341,9 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,i
 			return 0;
 		}
 
-		if((sce=sc->data[SC_PARRYING]) && flag&BF_WEAPON
-			&& skill_num != WS_CARTTERMINATION
-			&& rand()%100 < sce->val2)
-		{// attack blocked by Parrying
-			clif_skill_nodamage(bl,bl,LK_PARRYING,sce->val1,1);
+		if( (sce=sc->data[SC_PARRYING]) && flag&BF_WEAPON && skill_num != WS_CARTTERMINATION && rand()%100 < sce->val2 )
+		{ // attack blocked by Parrying
+			clif_skill_nodamage(bl, bl, LK_PARRYING, sce->val1,1);
 			return 0;
 		}
 		
@@ -948,6 +945,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 				break;
 
 			case KN_PIERCE:
+			case ML_PIERCE:
 				wd.div_= (wd.div_>0?tstatus->size+1:-(tstatus->size+1));
 				break;
 
@@ -959,6 +957,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 			case GS_GROUNDDRIFT:
 			case KN_SPEARSTAB:
 			case KN_BOWLINGBASH:
+			case MS_BOWLINGBASH:
 			case MO_BALKYOUNG:
 			case TK_TURNKICK:
 				wd.blewcount=0;
@@ -1011,7 +1010,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 	if(!flag.cri && sstatus->cri &&
 		(!skill_num ||
 		skill_num == KN_AUTOCOUNTER ||
-		skill_num == SN_SHARPSHOOTING ||
+		skill_num == SN_SHARPSHOOTING || skill_num == MA_SHARPSHOOTING ||
 		skill_num == NJ_KIRIKAGE))
 	{
 		short cri = sstatus->cri;
@@ -1042,6 +1041,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 					cri <<= 1;
 				break;
 			case SN_SHARPSHOOTING:
+			case MA_SHARPSHOOTING:
 				cri += 200;
 				break;
 			case NJ_KIRIKAGE:
@@ -1123,6 +1123,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 			case MS_BASH:
 				hitrate += hitrate * 5 * skill_lv / 100;
 				break;
+			case MS_MAGNUM:
 			case SM_MAGNUM:
 				hitrate += hitrate * 10 * skill_lv / 100;
 				break;
@@ -1141,6 +1142,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 				hitrate += hitrate * 20 / 100;
 				break;
 			case KN_PIERCE:
+			case ML_PIERCE:
 				hitrate += hitrate * 5 * skill_lv / 100;
 				break;
 			case AS_SONICBLOW:
@@ -1194,6 +1196,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 				}
 				break;
 			case LK_SPIRALPIERCE:
+			case ML_SPIRALPIERCE:
 				if (sd) {
 					short index = sd->equip_index[EQI_HAND_R];
 
@@ -1295,18 +1298,15 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 			if(sc->data[SC_BERSERK])
 				skillratio += 100;
 		}
-		if (!skill_num)
-		{
-			// Random chance to deal multiplied damage - Consider it as part of skill-based-damage
-			if(sd &&
-				sd->random_attack_increase_add > 0 &&
-				sd->random_attack_increase_per &&
-				rand()%100 < sd->random_attack_increase_per
-				)
+		if( !skill_num )
+		{ // Random chance to deal multiplied damage - Consider it as part of skill-based-damage
+			if( sd && sd->random_attack_increase_add > 0 && sd->random_attack_increase_per && rand()%100 < sd->random_attack_increase_per )
 				skillratio += sd->random_attack_increase_add;
-		
+
 			ATK_RATE(skillratio);
-		} else {	//Skills
+		}
+		else
+		{
 			switch( skill_num )
 			{
 				case SM_BASH:
@@ -1314,6 +1314,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 					skillratio += 30*skill_lv;
 					break;
 				case SM_MAGNUM:
+				case MS_MAGNUM:
 					skillratio += 20*skill_lv; 
 					break;
 				case MC_MAMMONITE:
@@ -1323,18 +1324,26 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 					skillratio += 5*sstatus->str;
 					break;
 				case AC_DOUBLE:
+				case MA_DOUBLE:
 					skillratio += 10*(skill_lv-1);
 					break;
 				case AC_SHOWER:
+				case MA_SHOWER:
 					skillratio += 5*skill_lv-25;
 					break;
 				case AC_CHARGEARROW:
+				case MA_CHARGEARROW:
 					skillratio += 50;
 					break;
 				case HT_FREEZINGTRAP:
+				case MA_FREEZINGTRAP:
 					skillratio += -50+10*skill_lv;
 					break;
 				case KN_PIERCE:
+				case ML_PIERCE:
+					skillratio += 10*skill_lv;
+					break;
+				case MER_CRASH:
 					skillratio += 10*skill_lv;
 					break;
 				case KN_SPEARSTAB:
@@ -1344,6 +1353,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 					skillratio += 50*skill_lv;
 					break;
 				case KN_BRANDISHSPEAR:
+				case ML_BRANDISH:
 				{
 					int ratio = 100+20*skill_lv;
 					skillratio += ratio-100;
@@ -1356,6 +1366,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 					break;
 				}
 				case KN_BOWLINGBASH:
+				case MS_BOWLINGBASH:
 					skillratio+= 40*skill_lv;
 					break;
 				case AS_GRIMTOOTH:
@@ -1478,6 +1489,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 					skillratio += 40*skill_lv-60;
 					break;
 				case SN_SHARPSHOOTING:
+				case MA_SHARPSHOOTING:
 					skillratio += 100+50*skill_lv;
 					break;
 				case CG_ARROWVULCAN:
@@ -1754,15 +1766,16 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
 		}
 
 		//Post skill/vit reduction damage increases
-		if (sc && skill_num != LK_SPIRALPIERCE)
+		if( sc && skill_num != LK_SPIRALPIERCE && skill_num != ML_SPIRALPIERCE )
 		{	//SC skill damages
 			if(sc->data[SC_AURABLADE]) 
 				ATK_ADD(20*sc->data[SC_AURABLADE]->val1);
 		}
 
 		//Refine bonus
-		if (sd && flag.weapon && skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST) {
-			if (skill_num == MO_FINGEROFFENSIVE) //Counts refine bonus multiple times
+		if( sd && flag.weapon && skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST )
+		{ // Counts refine bonus multiple times
+			if( skill_num == MO_FINGEROFFENSIVE )
 			{
 				ATK_ADD2(wd.div_*sstatus->rhw.atk2, wd.div_*sstatus->lhw.atk2);
 			} else {
@@ -2498,8 +2511,10 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list *
 	//Skill Range Criteria
 	md.flag |= battle_range_type(src, target, skill_num, skill_lv);
 
-	switch(skill_num){
+	switch( skill_num )
+	{
 	case HT_LANDMINE:
+	case MA_LANDMINE:
 		md.damage=skill_lv*(sstatus->dex+75)*(100+sstatus->int_)/100;
 		break;
 	case HT_BLASTMINE:
@@ -2917,9 +2932,11 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
 		battle_consume_ammo(sd, 0, 0);
 
 	damage = wd.damage + wd.damage2;
-	if (damage > 0 && src != target) {
+	if( damage > 0 && src != target )
+	{
 		rdamage = battle_calc_return_damage(target, damage, wd.flag);
-		if (rdamage > 0) {
+		if( rdamage > 0 )
+		{
 			rdelay = clif_damage(src, src, tick, wd.amotion, sstatus->dmotion, rdamage, 1, 4, 0);
 			//Use Reflect Shield to signal this kind of skill trigger. [Skotlex]
 			skill_additional_effect(target,src,CR_REFLECTSHIELD,1,BF_WEAPON|BF_SHORT|BF_NORMAL,tick);
@@ -3094,17 +3111,20 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 		case BL_SKILL:
 		{
 			TBL_SKILL *su = (TBL_SKILL*)target;
-			if (!su->group)
+			if( !su->group )
 				return 0;
-			if (skill_get_inf2(su->group->skill_id)&INF2_TRAP)
-			{	//Only a few skills can target traps...
-				switch (battle_getcurrentskill(src))
+			if( skill_get_inf2(su->group->skill_id)&INF2_TRAP )
+			{ //Only a few skills can target traps...
+				switch( battle_getcurrentskill(src) )
 				{
+					case MA_REMOVETRAP:
 					case HT_REMOVETRAP:
 					case AC_SHOWER:
+					case MA_SHOWER:
 					case WZ_SIGHTRASHER:
 					case WZ_SIGHTBLASTER:
 					case SM_MAGNUM:
+					case MS_MAGNUM:
 						state |= BCT_ENEMY;
 						strip_enemy = 0;
 						break;
@@ -3312,7 +3332,7 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 /*==========================================
  * ŽË’ö”»’è
  *------------------------------------------*/
-bool battle_check_range(struct block_list *src,struct block_list *bl,int range)
+bool battle_check_range(struct block_list *src, struct block_list *bl, int range)
 {
 	int d;
 	nullpo_retr(false, src);

+ 133 - 72
src/map/clif.c

@@ -3313,8 +3313,8 @@ void clif_storageclose(struct map_session_data* sd)
  *------------------------------------------*/
 static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_data* dstsd)
 {
-	struct map_session_data* tmpsd;
 	int gmlvl;
+	struct block_list *d_bl;
 	int i;
 
 	if(dstsd->chatID)
@@ -3338,19 +3338,15 @@ static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_d
 
 	// display link (sd - dstsd) to sd
 	ARR_FIND( 0, 5, i, sd->devotion[i] == dstsd->bl.id );
-	if( i < 5 ) clif_devotion(sd, sd);
+	if( i < 5 ) clif_devotion(&sd->bl, sd);
 	// display links (dstsd - devotees) to sd
 	ARR_FIND( 0, 5, i, dstsd->devotion[i] > 0 );
-	if( i < 5 ) clif_devotion(dstsd, sd);
+	if( i < 5 ) clif_devotion(&dstsd->bl, sd);
 	// display link (dstsd - crusader) to sd
-	if( dstsd->sc.data[SC_DEVOTION] && (tmpsd = map_id2sd(dstsd->sc.data[SC_DEVOTION]->val1)) != NULL )
-		clif_devotion(tmpsd, sd);
-
-	// pvp circle for duel [LuzZza]
-	//if(dstsd->duel_group)
-	//	clif_specialeffect(&dstsd->bl, 159, 4);
-
+	if( dstsd->sc.data[SC_DEVOTION] && (d_bl = map_id2bl(dstsd->sc.data[SC_DEVOTION]->val1)) != NULL )
+		clif_devotion(d_bl, sd);
 }
+
 void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl)
 {
 	uint8 buf[128];
@@ -3381,6 +3377,10 @@ void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl)
 				clif_specialeffect_single(bl,421,sd->fd);
 		}
 		break;
+	case BL_MER: // Devotion Effects
+		if( ((TBL_MER*)bl)->devotion_flag )
+			clif_devotion(bl, sd);
+		break;
 	case BL_NPC:
 		{
 			TBL_NPC* nd = (TBL_NPC*)bl;
@@ -4339,7 +4339,7 @@ int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst)
 	nullpo_retr(0, sd);
 	nullpo_retr(0, dst);
 
-	if(dst->type!=BL_MOB )
+	if( dst->type != BL_MOB )
 		return 0;
 
 	status = status_get_status_data(dst);
@@ -5870,23 +5870,39 @@ int clif_autospell(struct map_session_data *sd,int skilllv)
  * Devotion's visual effect
  * S 01cf <devoter id>.L { <devotee id>.L }[5] <max distance>.W
  *------------------------------------------*/
-void clif_devotion(struct map_session_data *sd, struct map_session_data *tsd)
+void clif_devotion(struct block_list *src, struct map_session_data *tsd)
 {
 	unsigned char buf[56];
 	int i;
-
-	nullpo_retv(sd);
+	
+	nullpo_retv(src);
+	memset(buf,0,packet_len(0x1cf));
 
 	WBUFW(buf,0) = 0x1cf;
-	WBUFL(buf,2) = sd->bl.id;
-	for( i = 0; i < 5; i++ )
-		WBUFL(buf,6+4*i) = sd->devotion[i];
-	WBUFW(buf,26) = skill_get_range2(&sd->bl,CR_DEVOTION,pc_checkskill(sd,CR_DEVOTION)); // ignored
+	WBUFL(buf,2) = src->id;
+	if( src->type == BL_MER )
+	{
+		struct mercenary_data *md = BL_CAST(BL_MER,src);
+		if( md && md->master && md->devotion_flag )
+			WBUFL(buf,6) = md->master->bl.id;
+
+		WBUFW(buf,26) = skill_get_range2(src, ML_DEVOTION, mercenary_checkskill(md, ML_DEVOTION));
+	}
+	else
+	{
+		struct map_session_data *sd = BL_CAST(BL_PC,src);
+		if( sd == NULL )
+			return;
+
+		for( i = 0; i < 5; i++ )
+			WBUFL(buf,6+4*i) = sd->devotion[i];
+		WBUFW(buf,26) = skill_get_range2(src, CR_DEVOTION, pc_checkskill(sd, CR_DEVOTION));
+	}
 
 	if( tsd )
-		clif_send(buf,packet_len(0x1cf),&tsd->bl,SELF);
+		clif_send(buf, packet_len(0x1cf), &tsd->bl, SELF);
 	else
-		clif_send(buf,packet_len(0x1cf),&sd->bl,AREA);
+		clif_send(buf, packet_len(0x1cf), src, AREA);
 }
 
 /*==========================================
@@ -9216,6 +9232,30 @@ static void clif_parse_UseSkillToId_mercenary(struct mercenary_data *md, struct
 		unit_skilluse_id(&md->bl, target_id, skillnum, skilllv);
 }
 
+static void clif_parse_UseSkillToPos_mercenary(struct mercenary_data *md, struct map_session_data *sd, unsigned int tick, short skillnum, short skilllv, short x, short y, int skillmoreinfo)
+{
+	int lv;
+	if( !md )
+		return;
+	if( skillnotok_mercenary(skillnum, md) )
+		return;
+	if( md->ud.skilltimer != INVALID_TIMER )
+		return;
+	if( DIFF_TICK(tick, md->ud.canact_tick) < 0 )
+	{
+		clif_skill_fail(md->master, skillnum, 4, 0);
+		return;
+	}
+
+	if( md->sc.data[SC_BASILICA] )
+		return;
+	lv = mercenary_checkskill(md, skillnum);
+	if( skilllv > lv )
+		skilllv = lv;
+	if( skilllv )
+		unit_skilluse_pos(&md->bl, x, y, skillnum, skilllv);
+}
+
 /*==========================================
  * ƒXƒLƒ‹Žg—p�iIDŽw’è�j
  *------------------------------------------*/
@@ -9229,20 +9269,19 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
 	skillnum = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
 	target_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]);
 
-	if (skilllv < 1) skilllv = 1; //No clue, I have seen the client do this with guild skills :/ [Skotlex]
-
+	if( skilllv < 1 ) skilllv = 1; //No clue, I have seen the client do this with guild skills :/ [Skotlex]
 
 	tmp = skill_get_inf(skillnum);
 	if (tmp&INF_GROUND_SKILL || !tmp)
 		return; //Using a ground/passive skill on a target? WRONG.
 
-	if( skillnum >= HM_SKILLBASE && skillnum < HM_SKILLBASE+MAX_HOMUNSKILL )
+	if( skillnum >= HM_SKILLBASE && skillnum < HM_SKILLBASE + MAX_HOMUNSKILL )
 	{
 		clif_parse_UseSkillToId_homun(sd->hd, sd, tick, skillnum, skilllv, target_id);
 		return;
 	}
 
-	if( skillnum >= MC_SKILLBASE && skillnum < MC_SKILLBASE+MAX_MERCSKILL )
+	if( skillnum >= MC_SKILLBASE && skillnum < MC_SKILLBASE + MAX_MERCSKILL )
 	{
 		clif_parse_UseSkillToId_mercenary(sd->md, sd, tick, skillnum, skilllv, target_id);
 		return;
@@ -9251,21 +9290,24 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
 	// Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
 	sd->idletime = last_tick;
 
-	if (pc_cant_act(sd))
+	if( pc_cant_act(sd) )
 		return;
-	if (pc_issit(sd))
+	if( pc_issit(sd) )
 		return;
 
-	if (skillnotok(skillnum, sd))
+	if( skillnotok(skillnum, sd) )
 		return;
 
-	if (sd->bl.id != target_id && !sd->state.skill_flag && tmp&INF_SELF_SKILL)
+	if( sd->bl.id != target_id && !sd->state.skill_flag && tmp&INF_SELF_SKILL )
 		target_id = sd->bl.id; //What good is it to mess up the target in self skills? Wished I knew... [Skotlex]
 	
-	if (sd->ud.skilltimer != -1) {
-		if (skillnum != SA_CASTCANCEL)
+	if( sd->ud.skilltimer != -1 )
+	{
+		if( skillnum != SA_CASTCANCEL )
 			return;
-	} else if (DIFF_TICK(tick, sd->ud.canact_tick) < 0) {
+	}
+	else if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 )
+	{
 		clif_skill_fail(sd, skillnum, 4, 0);
 		return;
 	}
@@ -9276,70 +9318,81 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
 	if( sd->sc.data[SC_BASILICA] && (skillnum != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
 		return; // On basilica only caster can use Basilica again to stop it.
 	
-	if(target_id<0 && -target_id == sd->bl.id) // for disguises [Valaris]
+	if( target_id < 0 && -target_id == sd->bl.id ) // for disguises [Valaris]
 		target_id = sd->bl.id;
 	
-	if(sd->menuskill_id)
+	if( sd->menuskill_id )
 	{
-		if (sd->menuskill_id == SA_TAMINGMONSTER)
+		if( sd->menuskill_id == SA_TAMINGMONSTER )
 			sd->menuskill_id = sd->menuskill_val = 0; //Cancel pet capture.
-		else
-		if (sd->menuskill_id != SA_AUTOSPELL)
+		else if( sd->menuskill_id != SA_AUTOSPELL )
 			return; //Can't use skills while a menu is open.
 	}
-	if (sd->skillitem == skillnum) {
-		if (skilllv != sd->skillitemlv)
+	if( sd->skillitem == skillnum )
+	{
+		if( skilllv != sd->skillitemlv )
 			skilllv = sd->skillitemlv;
 		unit_skilluse_id(&sd->bl, target_id, skillnum, skilllv);
 		return;
 	}
 
 	sd->skillitem = sd->skillitemlv = 0;
-	if (skillnum == MO_EXTREMITYFIST) {
-		if ((!sd->sc.data[SC_COMBO] ||
+	if( skillnum == MO_EXTREMITYFIST )
+	{
+		if( (!sd->sc.data[SC_COMBO] ||
 			(sd->sc.data[SC_COMBO]->val1 != MO_COMBOFINISH &&
 			sd->sc.data[SC_COMBO]->val1 != CH_TIGERFIST &&
 			sd->sc.data[SC_COMBO]->val1 != CH_CHAINCRUSH))) {
-			if (!sd->state.skill_flag ) {
+			if( !sd->state.skill_flag )
+			{
 				sd->state.skill_flag = 1;
 				clif_skillinfo(sd, MO_EXTREMITYFIST, INF_ATTACK_SKILL, -1);
 				return;
-			} else if (sd->bl.id == target_id) {
+			} else if( sd->bl.id == target_id )
+			{
 				clif_skillinfo(sd, MO_EXTREMITYFIST, INF_ATTACK_SKILL, -1);
 				return;
 			}
 		}
 	}
-	if (skillnum == TK_JUMPKICK) {
-		if (!sd->sc.data[SC_COMBO] || sd->sc.data[SC_COMBO]->val1 != TK_JUMPKICK) {
-			if (!sd->state.skill_flag ) {
+	if( skillnum == TK_JUMPKICK )
+	{
+		if( !sd->sc.data[SC_COMBO] || sd->sc.data[SC_COMBO]->val1 != TK_JUMPKICK )
+		{
+			if( !sd->state.skill_flag )
+			{
 				sd->state.skill_flag = 1;
 				clif_skillinfo(sd, TK_JUMPKICK, INF_ATTACK_SKILL, -1);
 				return;
-			} else if (sd->bl.id == target_id) {
+			}
+			else if( sd->bl.id == target_id )
+			{
 				clif_skillinfo(sd, TK_JUMPKICK, INF_ATTACK_SKILL, -1);
 				return;
 			}
 		}
 	}
 
-	if (skillnum >= GD_SKILLBASE) {
-		if (sd->state.gmaster_flag)
+	if( skillnum >= GD_SKILLBASE )
+	{
+		if( sd->state.gmaster_flag )
 			skilllv = guild_checkskill(sd->state.gmaster_flag, skillnum);
 		else
 			skilllv = 0;
-	} else {
+	}
+	else
+	{
 		tmp = pc_checkskill(sd, skillnum);
-		if (skilllv > tmp)
+		if( skilllv > tmp )
 			skilllv = tmp;
 	}
 
 	pc_delinvincibletimer(sd);
 	
-	if (skilllv)
+	if( skilllv )
 		unit_skilluse_id(&sd->bl, target_id, skillnum, skilllv);
 
-	if (sd->state.skill_flag)
+	if( sd->state.skill_flag )
 		sd->state.skill_flag = 0;
 }
 
@@ -9351,17 +9404,24 @@ void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, short skil
 	int lv;
 	unsigned int tick = gettick();
 
-	//Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
-	sd->idletime = last_tick;
+	if( !(skill_get_inf(skillnum)&INF_GROUND_SKILL) )
+		return; //Using a target skill on the ground? WRONG.
 
-	if (skillnotok(skillnum, sd))
+	if( skillnum >= MC_SKILLBASE && skillnum < MC_SKILLBASE + MAX_MERCSKILL )
+	{
+		clif_parse_UseSkillToPos_mercenary(sd->md, sd, tick, skillnum, skilllv, x, y, skillmoreinfo);
 		return;
+	}
 
-	if (!(skill_get_inf(skillnum)&INF_GROUND_SKILL))
-		return; //Using a target skill on the ground? WRONG.
+	//Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
+	sd->idletime = last_tick;
 
-	if (skillmoreinfo != -1) {
-		if (pc_issit(sd)) {
+	if( skillnotok(skillnum, sd) )
+		return;
+	if( skillmoreinfo != -1 )
+	{
+		if( pc_issit(sd) )
+		{
 			clif_skill_fail(sd, skillnum, 0, 0);
 			return;
 		}
@@ -9369,10 +9429,10 @@ void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, short skil
 		safestrncpy(sd->message, (char*)RFIFOP(fd,skillmoreinfo), MESSAGE_SIZE);
 	}
 
-	if (sd->ud.skilltimer != -1)
+	if( sd->ud.skilltimer != -1 )
 		return;
 
-	if (DIFF_TICK(tick, sd->ud.canact_tick) < 0)
+	if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 )
 	{
 		clif_skill_fail(sd, skillnum, 4, 0);
 		return;
@@ -9384,32 +9444,34 @@ void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, short skil
 	if( sd->sc.data[SC_BASILICA] && (skillnum != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
 		return; // On basilica only caster can use Basilica again to stop it.
 
-	if(sd->menuskill_id)
+	if( sd->menuskill_id )
 	{
-		if (sd->menuskill_id == SA_TAMINGMONSTER)
+		if( sd->menuskill_id == SA_TAMINGMONSTER )
 			sd->menuskill_id = sd->menuskill_val = 0; //Cancel pet capture.
-		else
-		if (sd->menuskill_id != SA_AUTOSPELL)
+		else if( sd->menuskill_id != SA_AUTOSPELL )
 			return; //Can't use skills while a menu is open.
 	}
 
 	pc_delinvincibletimer(sd);
 
-	if (sd->skillitem == skillnum) {
-		if (skilllv != sd->skillitemlv)
+	if( sd->skillitem == skillnum )
+	{
+		if( skilllv != sd->skillitemlv )
 			skilllv = sd->skillitemlv;
 		unit_skilluse_pos(&sd->bl, x, y, skillnum, skilllv);
-	} else {
+	}
+	else
+	{
 		sd->skillitem = sd->skillitemlv = 0;
-		if ((lv = pc_checkskill(sd, skillnum)) > 0) {
-			if (skilllv > lv)
+		if( (lv = pc_checkskill(sd, skillnum)) > 0 )
+		{
+			if( skilllv > lv )
 				skilllv = lv;
 			unit_skilluse_pos(&sd->bl, x, y, skillnum,skilllv);
 		}
 	}
 }
 
-
 void clif_parse_UseSkillToPos(int fd, struct map_session_data *sd)
 {
 	if (pc_cant_act(sd))
@@ -12484,7 +12546,6 @@ void clif_mercenary_info(struct map_session_data *sd)
 	// Mercenary shows ATK as a random value between ATK ~ ATK2
 	atk = rand()%(status->rhw.atk2 - status->rhw.atk + 1) + status->rhw.atk;
 	WFIFOW(fd,6) = cap_value(atk, 0, SHRT_MAX);
-
 	WFIFOW(fd,8) = cap_value(status->matk_max, 0, SHRT_MAX);
 	WFIFOW(fd,10) = status->hit;
 	WFIFOW(fd,12) = status->cri/10;

+ 1 - 1
src/map/clif.h

@@ -233,7 +233,7 @@ void clif_skill_delunit(struct skill_unit *unit);
 void clif_01ac(struct block_list* bl);
 
 int clif_autospell(struct map_session_data *sd,int skilllv);
-void clif_devotion(struct map_session_data *sd, struct map_session_data *tsd);
+void clif_devotion(struct block_list *src, struct map_session_data *tsd);
 int clif_spiritball(struct map_session_data *sd);
 int clif_combo_delay(struct block_list *src,int wait);
 int clif_bladestop(struct block_list *src,struct block_list *dst,int bool_);

+ 12 - 13
src/map/mob.c

@@ -1286,8 +1286,8 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick)
 			      || !mob_can_reach(md, tbl, md->min_chase, MSS_RUSH)
 			    )
 			&&  md->state.attacked_count++ >= RUDE_ATTACKED_COUNT
-			&&  !mobskill_use(md, tick, MSC_RUDEATTACKED) //If can't rude Attack
-			&&  can_move && unit_escape(&md->bl, tbl, rand()%10 +1)) //Attempt escape
+			&&  !mobskill_use(md, tick, MSC_RUDEATTACKED) // If can't rude Attack
+			&&  can_move && unit_escape(&md->bl, tbl, rand()%10 +1)) // Attempt escape
 			{	//Escaped
 				md->attacked_id = 0;
 				return true;
@@ -1297,18 +1297,17 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick)
 		if( (abl = map_id2bl(md->attacked_id)) && (!tbl || mob_can_changetarget(md, abl, mode)) )
 		{
 			if( md->bl.m != abl->m || abl->prev == NULL
-			|| (dist = distance_bl(&md->bl, abl)) >= MAX_MINCHASE
-			|| battle_check_target(&md->bl, abl, BCT_ENEMY) <= 0
-			|| (battle_config.mob_ai&0x2 && !status_check_skilluse(&md->bl, abl, 0, 0)) //Retaliate check
-			|| (!battle_check_range(&md->bl, abl, md->status.rhw.range)
-				&&
-				( //Reach check
-				  (!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0 && (battle_config.mob_ai&0x2 || md->sc.data[SC_SPIDERWEB]))
-				  || !mob_can_reach(md, abl, dist+md->db->range3, MSS_RUSH)
-				  )
+				|| (dist = distance_bl(&md->bl, abl)) >= MAX_MINCHASE // Attacker longer than visual area
+				|| battle_check_target(&md->bl, abl, BCT_ENEMY) <= 0 // Attacker is not enemy of mob
+				|| status_isdead(abl) // Attacker is Dead (Reflecting Damage?)
+				|| (battle_config.mob_ai&0x2 && !status_check_skilluse(&md->bl, abl, 0, 0)) // Cannot normal attack back to Attacker
+				|| (!battle_check_range(&md->bl, abl, md->status.rhw.range) // Not on Melee Range and ...
+				&& ( // Reach check
+					(!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0 && (battle_config.mob_ai&0x2 || md->sc.data[SC_SPIDERWEB]))
+					|| !mob_can_reach(md, abl, dist+md->db->range3, MSS_RUSH)
 				)
-			)
-			{	//Rude attacked
+				) )
+			{ // Rude attacked
 				if (md->state.attacked_count++ >= RUDE_ATTACKED_COUNT
 				&& !mobskill_use(md, tick, MSC_RUDEATTACKED) && can_move
 				&& !tbl && unit_escape(&md->bl, abl, rand()%10 +1))

+ 2 - 0
src/map/pc.c

@@ -3155,6 +3155,8 @@ 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] )
+				return 0;
 			if( nameid == 12242 && sd->md->db->lv < 40 )
 				return 0;
 			if( nameid == 12243 && sd->md->db->lv < 80 )

+ 250 - 102
src/map/skill.c

@@ -215,26 +215,31 @@ int skill_get_casttype (int id)
 int skill_get_range2 (struct block_list *bl, int id, int lv)
 {
 	int range;
-	if(bl->type == BL_MOB && !(battle_config.mob_ai&0x400))
+	if( bl->type == BL_MOB && !(battle_config.mob_ai&0x400) )
 		return 9; //Mobs have a range of 9 regardless of skill used.
 
 	range = skill_get_range(id, lv);
 
-	if(range < 0) {
-		if (battle_config.use_weapon_skill_range&bl->type)
+	if( range < 0 )
+	{
+		if( battle_config.use_weapon_skill_range&bl->type )
 			return status_get_range(bl);
 		range *=-1;
 	}
+
 	//TODO: Find a way better than hardcoding the list of skills affected by AC_VULTURE
-	switch (id) {
-	case AC_SHOWER:
-	case AC_DOUBLE:
+	switch( id )
+	{
+	case AC_SHOWER:			case MA_SHOWER:
+	case AC_DOUBLE:			case MA_DOUBLE:
 	case HT_BLITZBEAT:
 	case AC_CHARGEARROW:
+	case MA_CHARGEARROW:
 	case SN_FALCONASSAULT:
 	case SN_SHARPSHOOTING:
+	case MA_SHARPSHOOTING:
 	case HT_POWER:
-		if (bl->type == BL_PC)
+		if( bl->type == BL_PC )
 			range += pc_checkskill((TBL_PC*)bl, AC_VULTURE);
 		else
 			range += 10; //Assume level 10?
@@ -256,7 +261,7 @@ int skill_get_range2 (struct block_list *bl, int id, int lv)
 		break;
 	}
 
-	if(!range && bl->type != BL_PC)
+	if( !range && bl->type != BL_PC )
 		return 9; // Enable non players to use self skills on others. [Skotlex]
 	return range;
 }
@@ -553,6 +558,10 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int
 		}
 		break;
 
+	case MER_CRASH:
+		sc_start(bl,SC_STUN,(6*skilllv),skilllv,skill_get_time2(skillid,skilllv));
+		break;
+
 	case AS_VENOMKNIFE:
 		if (sd) //Poison chance must be that of Envenom. [Skotlex]
 			skilllv = pc_checkskill(sd, TF_POISON);
@@ -593,6 +602,7 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int
 		break;
 
 	case HT_FREEZINGTRAP:
+	case MA_FREEZINGTRAP:
 		sc_start(bl,SC_FREEZE,(3*skilllv+35),skilllv,skill_get_time2(skillid,skilllv));
 		break;
 
@@ -601,6 +611,7 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int
 		break;
 
 	case HT_LANDMINE:
+	case MA_LANDMINE:
 		sc_start(bl,SC_STUN,(5*skilllv+30),skilllv,skill_get_time2(skillid,skilllv));
 		break;
 
@@ -609,6 +620,7 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int
 		break;
 
 	case HT_SANDMAN:
+	case MA_SANDMAN:
 		sc_start(bl,SC_SLEEP,(10*skilllv+40),skilllv,skill_get_time2(skillid,skilllv));
 		break;
 
@@ -728,6 +740,7 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int
 		break;
 
 	case LK_SPIRALPIERCE:
+	case ML_SPIRALPIERCE:
 		sc_start(bl,SC_STOP,(15+skilllv*5),0,skill_get_time2(skillid,skilllv));
 		break;
 
@@ -852,12 +865,12 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int
 		break;
 	}
 
-	if(sd && attack_type&BF_WEAPON &&
+	if( sd && attack_type&BF_WEAPON &&
 		skillid != WS_CARTTERMINATION &&
 		skillid != AM_DEMONSTRATION &&
-		skillid != CR_REFLECTSHIELD &&
-		skillid != ASC_BREAKER
-	){	//Trigger status effects
+		skillid != CR_REFLECTSHIELD && skillid != MS_REFLECTSHIELD &&
+		skillid != ASC_BREAKER )
+	{ // Trigger status effects
 		enum sc_type type;
 		int i;
 		for(i=0; i < ARRAYLENGTH(sd->addeff) && sd->addeff[i].flag; i++)
@@ -1679,11 +1692,12 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
 			clif_skillinfoblock(tsd);
 		}
 	}
-	if (skillid != WZ_SIGHTRASHER && 
+	if( skillid != WZ_SIGHTRASHER && 
 		skillid != WZ_SIGHTBLASTER && 
-		skillid != AC_SHOWER &&
-		skillid != SM_MAGNUM &&
-		bl->type == BL_SKILL && damage > 0) {
+		skillid != AC_SHOWER && skillid != MA_SHOWER &&
+		skillid != SM_MAGNUM && skillid != MS_MAGNUM &&
+		bl->type == BL_SKILL && damage > 0 )
+	{
 		struct skill_unit* su = (struct skill_unit*)bl;
 		if (su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP)
 			damage = 0; //Sight rasher, blaster, and arrow shower may dmg traps. [Kevin]
@@ -1835,12 +1849,16 @@ static int skill_check_unit_range_sub (struct block_list *bl, va_list ap)
 			break;
 		case AL_WARP:
 		case HT_SKIDTRAP:
+		case MA_SKIDTRAP:
 		case HT_LANDMINE:
+		case MA_LANDMINE:
 		case HT_ANKLESNARE:
 		case HT_SHOCKWAVE:
 		case HT_SANDMAN:
+		case MA_SANDMAN:
 		case HT_FLASHER:
 		case HT_FREEZINGTRAP:
+		case MA_FREEZINGTRAP:
 		case HT_BLASTMINE:
 		case HT_CLAYMORETRAP:
 		case HT_TALKIEBOX:
@@ -2289,17 +2307,21 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 
 	switch(skillid)
 	{
+	case MER_CRASH:
 	case SM_BASH:
 	case MS_BASH:
 	case MC_MAMMONITE:
 	case TF_DOUBLE:
 	case AC_DOUBLE:
+	case MA_DOUBLE:
 	case AS_SONICBLOW:
 	case KN_PIERCE:
+	case ML_PIERCE:
 	case KN_SPEARBOOMERANG:
 	case TF_POISON:
 	case TF_SPRINKLESAND:
 	case AC_CHARGEARROW:
+	case MA_CHARGEARROW:
 	case RG_INTIMIDATE:
 	case AM_ACIDTERROR:
 	case BA_MUSICALSTRIKE:
@@ -2338,6 +2360,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 	case NPC_SLEEPATTACK:
 	case LK_AURABLADE:
 	case LK_SPIRALPIERCE:
+	case ML_SPIRALPIERCE:
 	case LK_HEADCRUSH:
 	case CG_ARROWVULCAN:
 	case HW_MAGICCRASHER:
@@ -2438,6 +2461,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 		break;
 
 	case SN_SHARPSHOOTING:
+	case MA_SHARPSHOOTING:
 	case NJ_KAMAITACHI:
 		//It won't shoot through walls since on castend there has to be a direct
 		//line of sight between caster and target.
@@ -2552,8 +2576,10 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 		flag |= SD_PREAMBLE; // a fake packet will be sent for the first target to be hit
 	case AS_SPLASHER:
 	case SM_MAGNUM:
+	case MS_MAGNUM:
 	case HT_BLITZBEAT:
 	case AC_SHOWER:	
+	case MA_SHOWER:
 	case MG_NAPALMBEAT:
 	case MG_FIREBALL:
 	case RG_RAID:
@@ -2598,15 +2624,19 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 			map_foreachinrange(skill_area_sub, bl, skill_get_splash(skillid, skilllv), splash_target(src), src, skillid, skilllv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
 
 			//FIXME: move this to skill_additional_effect or some such? [ultramage]
-			if (skillid == SM_MAGNUM) {
-				//Initiate 10% of your damage becomes fire element.
+			if( skillid == SM_MAGNUM || skillid == MS_MAGNUM )
+			{ // Initiate 10% of your damage becomes fire element.
 				sc_start4(src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skillid, skilllv));
-				if (sd) skill_blockpc_start (sd, skillid, skill_get_time(skillid, skilllv));
+				if( sd )
+					skill_blockpc_start(sd, skillid, skill_get_time(skillid, skilllv));
+				if( bl->type == BL_MER )
+					skill_blockmerc_start((TBL_MER*)bl, skillid, skill_get_time(skillid, skilllv));
 			}
 		}
 		break;
 
 	case KN_BRANDISHSPEAR:
+	case ML_BRANDISH:
 		//Coded apart for it needs the flag passed to the damage calculation.
 		if (skill_area_temp[1] != bl->id)
 			skill_attack(skill_get_type(skillid), src, src, bl, skillid, skilllv, tick, flag|SD_ANIMATION);
@@ -2615,6 +2645,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
 		break;
 
 	case KN_BOWLINGBASH:
+	case MS_BOWLINGBASH:
 		if(flag&1){
 			if(bl->id==skill_area_temp[1])
 				break;
@@ -2883,14 +2914,14 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
  *------------------------------------------*/
 int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag)
 {
-	struct map_session_data *sd;
+	struct map_session_data *sd, *dstsd;
+	struct mob_data *md, *dstmd;
 	struct homun_data *hd;
-	struct map_session_data *dstsd;
+	struct mercenary_data *mer;
 	struct status_data *sstatus, *tstatus;
 	struct status_change *tsc;
 	struct status_change_entry *tsce;
-	struct mob_data *md;
-	struct mob_data *dstmd;
+	
 	int i;
 	enum sc_type type;
 	
@@ -2905,6 +2936,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 	sd = BL_CAST(BL_PC, src);
 	hd = BL_CAST(BL_HOM, src);
 	md = BL_CAST(BL_MOB, src);
+	mer = BL_CAST(BL_MER, src);
 
 	dstsd = BL_CAST(BL_PC, bl);
 	dstmd = BL_CAST(BL_MOB, bl);
@@ -3080,10 +3112,9 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 		break;
 
 	case AL_DECAGI:
-		clif_skill_nodamage (src, bl, skillid, skilllv,
-			sc_start(bl, type,
-				(40 + skilllv * 2 + (status_get_lv(src) + sstatus->int_)/5),
-				skilllv, skill_get_time(skillid,skilllv)));
+	case MER_DECAGI:
+		clif_skill_nodamage (src, bl, skillid, skilllv, 
+			sc_start(bl, type, (40 + skilllv * 2 + (status_get_lv(src) + sstatus->int_)/5), skilllv, skill_get_time(skillid,skilllv)));
 		break;
 
 	case AL_CRUCIS:
@@ -3097,12 +3128,14 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 		break;
 
 	case PR_LEXDIVINA:
-		if (tsce) {
+	case MER_LEXDIVINA:
+		if( tsce )
+		{
 			status_change_end(bl,type, -1);
 			clif_skill_nodamage (src, bl, skillid, skilllv, 1);
-		} else 
-			clif_skill_nodamage (src, bl, skillid, skilllv, 
-				sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)));
+		}
+		else 
+			clif_skill_nodamage (src, bl, skillid, skilllv, sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)));
 		break;
 
 	case SA_ABRACADABRA:
@@ -3338,6 +3371,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 		break;
 	//Passive Magnum, should had been casted on yourself.
 	case SM_MAGNUM:
+	case MS_MAGNUM:
 		skill_area_temp[1] = 0;
 		map_foreachinrange(skill_area_sub, src, skill_get_splash(skillid, skilllv), BL_SKILL|BL_CHAR,
 			src,skillid,skilllv,tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
@@ -3359,11 +3393,14 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 	case PR_SUFFRAGIUM:
 	case PR_BENEDICTIO:
 	case LK_BERSERK:
+	case MS_BERSERK:
 	case KN_AUTOCOUNTER:
 	case KN_TWOHANDQUICKEN:	
 	case KN_ONEHAND:
+	case MER_QUICKEN:
 	case CR_SPEARQUICKEN:
 	case CR_REFLECTSHIELD:
+	case MS_REFLECTSHIELD:
 	case AS_POISONREACT:
 	case MC_LOUD:
 	case MG_ENERGYCOAT:
@@ -3371,7 +3408,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 	case MO_STEELBODY:
 	case MO_BLADESTOP:
 	case LK_AURABLADE:
-	case LK_PARRYING:	
+	case LK_PARRYING:
+	case MS_PARRYING:
 	case LK_CONCENTRATION:
 	case WS_CARTBOOST:
 	case SN_SIGHT:
@@ -3401,6 +3439,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 			sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)));
 		break;
 	case MG_SIGHT:
+	case MER_SIGHT:
 	case AL_RUWACH:
 	case WZ_SIGHTBLASTER:
 	case NPC_WIDESIGHT:
@@ -3503,74 +3542,90 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 		break;
 
 	case SM_PROVOKE:
-		if((tstatus->mode&MD_BOSS) || battle_check_undead(tstatus->race,tstatus->def_ele)) {
+	case MER_PROVOKE:
+		if( (tstatus->mode&MD_BOSS) || battle_check_undead(tstatus->race,tstatus->def_ele) )
+		{
 			map_freeblock_unlock();
 			return 1;
 		}
 		//TODO: How much does base level affects? Dummy value of 1% per level difference used. [Skotlex]
 		clif_skill_nodamage(src,bl,skillid,skilllv,
-			(i=sc_start(bl,type,
-				50 +3*skilllv +status_get_lv(src) -status_get_lv(bl),
-				skilllv,skill_get_time(skillid,skilllv))));
-		if (!i)
+			(i = sc_start(bl,type, 50 + 3*skilllv + status_get_lv(src) - status_get_lv(bl), skilllv, skill_get_time(skillid,skilllv))));
+		if( !i )
 		{
-			if (sd) 
+			if( sd )
 				clif_skill_fail(sd,skillid,0,0);
 			map_freeblock_unlock();
 			return 0;
 		}
 		unit_skillcastcancel(bl, 2);
 
-		if(tsc && tsc->count){
-			if(tsc->data[SC_FREEZE])
+		if( tsc && tsc->count )
+		{
+			if( tsc->data[SC_FREEZE] )
 				status_change_end(bl,SC_FREEZE,-1);
-			if(tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE)
+			if( tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE )
 				status_change_end(bl,SC_STONE,-1);
-			if(tsc->data[SC_SLEEP])
+			if( tsc->data[SC_SLEEP] )
 				status_change_end(bl,SC_SLEEP,-1);
 		}
 
-		if(dstmd) {
+		if( dstmd )
+		{
 			dstmd->state.provoke_flag = src->id;
-			mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv));
+			mob_target(dstmd, src, skill_get_range2(src,skillid,skilllv));
 		}
 		break;
 
+	case ML_DEVOTION:
 	case CR_DEVOTION:
-		if(sd && dstsd)
 		{
-			int count = min(skilllv, 5);
-			int lv = sd->status.base_level - dstsd->status.base_level;
-			if (lv < 0) lv = -lv;
-			if (lv > battle_config.devotion_level_difference ||
-				(dstsd->sc.data[type] && dstsd->sc.data[type]->val1 != src->id) || //Avoid overriding [Skotlex]
-				(dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) {
-				clif_skill_fail(sd,skillid,0,0);
+			int count, lv;
+			if( !dstsd )
+			{ // Only players can be devoted
+				if( sd )
+					clif_skill_fail(sd, skillid, 0, 0);
+				break;
+			}
+
+			if( (lv = status_get_lv(src) - dstsd->status.base_level) < 0 )
+				lv = -lv;
+			if( lv > battle_config.devotion_level_difference || // Level difference requeriments
+				(dstsd->sc.data[type] && dstsd->sc.data[type]->val1 != src->id) || // Cannot Devote a player devoted from another source
+				(skillid == ML_DEVOTION && (!mer || mer != dstsd->md)) || // Mercenary only can devote owner
+				(dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER ) // Crusader Cannot be devoted
+			{
+				if( sd )
+					clif_skill_fail(sd,skillid,0,0);
 				map_freeblock_unlock();
 				return 1;
 			}
 
-			// check if the char isn't devoted already
-			ARR_FIND( 0, count, i, sd->devotion[i] == bl->id );
-			if( i == count )
-			{// not there, find first empty slot
-				ARR_FIND( 0, count, i, sd->devotion[i] == 0 );
+			i = 0;
+			count = (sd)? min(skilllv,5) : 1; // Mercenary only can Devote owner
+			if( sd )
+			{ // Player Devoting Player
+				ARR_FIND(0, count, i, sd->devotion[i] == bl->id );
 				if( i == count )
-				{// all slots full, fail
-					clif_skill_fail(sd,skillid,0,0);
-					map_freeblock_unlock();
-					return 1;
+				{
+					ARR_FIND(0, count, i, sd->devotion[i] == 0 );
+					if( i == count )
+					{ // No free slots, skill Fail
+						clif_skill_fail(sd, skillid, 0, 0);
+						map_freeblock_unlock();
+						return 1;
+					}
 				}
+
+				sd->devotion[i] = bl->id;
 			}
+			else 
+				mer->devotion_flag = 1; // Mercenary Devoting Owner
 
-			sd->devotion[i] = bl->id;
-			clif_skill_nodamage(src,bl,skillid,skilllv,
-				sc_start4(bl,type,100,src->id,i,skill_get_range2(src,skillid,skilllv),skill_get_time2(skillid, skilllv),1000));
-			clif_devotion(sd,NULL);
+			clif_skill_nodamage(src, bl, skillid, skilllv,
+				sc_start4(bl, type, 100, src->id, i, skill_get_range2(src,skillid,skilllv), skill_get_time2(skillid, skilllv), 1000));
+			clif_devotion(src, NULL);
 		}
-		else
-			if (sd)
-				clif_skill_fail(sd,skillid,0,0);
 		break;
 
 	case MO_CALLSPIRITS:
@@ -3662,6 +3717,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 		break;
 
 	case KN_BRANDISHSPEAR:
+	case ML_BRANDISH:
 		{
 			int c,n=4;
 			int dir = map_calc_dir(src,bl->x,bl->y);
@@ -3749,14 +3805,19 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 	case PR_MAGNIFICAT:
 	case PR_GLORIA:
 	case SN_WINDWALK:
-		if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) {
-			clif_skill_nodamage(bl,bl,skillid,skilllv,
-				sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)));
-		} else if (sd) {
-			party_foreachsamemap (skill_area_sub,
-				sd,skill_get_splash(skillid, skilllv),
-				src,skillid,skilllv,tick, flag|BCT_PARTY|1,
-				skill_castend_nodamage_id);
+		if( sd == NULL || sd->status.party_id == 0 || (flag & 1) )
+			clif_skill_nodamage(bl, bl, skillid, skilllv, sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)));
+		else if( sd )
+			party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skillid, skilllv), src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+		break;
+	case MER_MAGNIFICAT:
+		if( mer != NULL )
+		{
+			clif_skill_nodamage(bl, bl, skillid, skilllv, sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)));
+			if( mer->master && mer->master->status.party_id != 0 && !(flag&1) )
+				party_foreachsamemap(skill_area_sub, mer->master, skill_get_splash(skillid, skilllv), src, skillid, skilllv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+			else if( mer->master && !(flag&1) )
+				clif_skill_nodamage(src, &mer->master->bl, skillid, skilllv, sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)));
 		}
 		break;
 
@@ -3778,7 +3839,9 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 	case BS_MAXIMIZE:
 	case NV_TRICKDEAD:
 	case CR_DEFENDER:
+	case ML_DEFENDER:
 	case CR_AUTOGUARD:
+	case ML_AUTOGUARD:
 	case TK_READYSTORM:
 	case TK_READYDOWN:
 	case TK_READYTURN:
@@ -3814,9 +3877,10 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 		clif_skill_nodamage(src,bl,skillid,skilllv,
 			sc_start(bl,type,100,skilllv,skill_get_time(skillid, skilllv)));
 		break;
-	case SM_AUTOBERSERK:	// Celest
-		if (tsce)
-			i = status_change_end(bl, type, -1);				
+	case SM_AUTOBERSERK:
+	case MER_AUTOBERSERK:
+		if( tsce )
+			i = status_change_end(bl, type, -1);
 		else
 			i = sc_start(bl,type,100,skilllv,60000);
 		clif_skill_nodamage(src,bl,skillid,skilllv,i);
@@ -3996,18 +4060,63 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 			mob_unlocktarget(dstmd,tick);
 		break;
 
+	// Mercenary Supportive Skills
+	case MER_BENEDICTION:
+		status_change_end(bl, SC_CURSE, -1);
+		status_change_end(bl, SC_BLIND, -1);
+		clif_skill_nodamage(src,bl,skillid,skilllv,1);
+		break;
+	case MER_COMPRESS:
+		status_change_end(bl, SC_BLEEDING, -1);
+		clif_skill_nodamage(src,bl,skillid,skilllv,1);
+		break;
+	case MER_MENTALCURE:
+		status_change_end(bl, SC_CONFUSION, -1);
+		clif_skill_nodamage(src,bl,skillid,skilllv,1);
+		break;
+	case MER_RECUPERATE:
+		status_change_end(bl, SC_POISON, -1);
+		status_change_end(bl, SC_SILENCE, -1);
+		clif_skill_nodamage(src,bl,skillid,skilllv,1);
+		break;
+	case MER_REGAIN:
+		status_change_end(bl, SC_SLEEP, -1);
+		status_change_end(bl, SC_STUN, -1);
+		clif_skill_nodamage(src,bl,skillid,skilllv,1);
+		break;
+	case MER_TENDER:
+		status_change_end(bl, SC_FREEZE, -1);
+		status_change_end(bl, SC_STONE, -1);
+		clif_skill_nodamage(src,bl,skillid,skilllv,1);
+		break;
+
+	case MER_SCAPEGOAT:
+		if( mer && mer->master )
+		{
+			status_heal(&mer->master->bl, mer->battle_status.hp, 0, 2);
+			status_damage(src, src, mer->battle_status.max_hp, 0, 0, 1);
+		}
+		break;
+
+	case MER_ESTIMATION:
+		if( !mer )
+			break;
+		sd = mer->master;
 	case WZ_ESTIMATION:
-		if(sd) {
-			if (dstsd) {
-				clif_skill_fail(sd,skillid,0,0);
-				break;
-			}
-			if (dstmd && dstmd->class_ == MOBID_EMPERIUM) {
-				break;
-			}
-			clif_skill_nodamage(src,bl,skillid,skilllv,1);
-			clif_skill_estimation((struct map_session_data *)src,bl);
+		if( sd == NULL )
+			break;
+		if( dstsd )
+		{ // Fail on Players
+			clif_skill_fail(sd,skillid,0,0);
+			break;
 		}
+		if( dstmd && dstmd->class_ == MOBID_EMPERIUM )
+			break; // Cannot be Used on Emperium
+
+		clif_skill_nodamage(src, bl, skillid, skilllv, 1);
+		clif_skill_estimation(sd, bl);
+		if( skillid == MER_ESTIMATION ) 
+			sd = NULL;
 		break;
 
 	case BS_REPAIRWEAPON:
@@ -4676,6 +4785,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 		}
 		break;
 
+	case MA_REMOVETRAP:
 	case HT_REMOVETRAP:
 		//FIXME: I think clif_skill_fail() is supposed to be sent if it fails below [ultramage]
 		clif_skill_nodamage(src, bl, skillid, skilllv, 1);
@@ -4684,13 +4794,12 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 			struct skill_unit_group* sg;
 			su = BL_CAST(BL_SKILL, bl);
 
-			if( (su)
-			&&  (sg = su->group)
-			&&  (sg->src_id == src->id || map_flag_vs(bl->m))
-			&&  (skill_get_inf2(sg->skill_id)&INF2_TRAP) )
-			{	// prevent picking up expired traps
-				if( !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) )
-				{
+			// Mercenaries can remove any trap
+			// Players can only remove their own traps or traps on Vs maps.
+			if( su && (sg = su->group) && (src->type == BL_MER || sg->src_id == src->id || map_flag_vs(bl->m)) && (skill_get_inf2(sg->skill_id)&INF2_TRAP) )
+			{
+				if( sd && !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) )
+				{ // prevent picking up expired traps
 					if( battle_config.skill_removetrap_type )
 					{ // get back all items used to deploy the trap
 						for( i = 0; i < 10; i++ )
@@ -5095,7 +5204,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
 			clif_skill_nodamage(src,bl,skillid,skilllv,1);
 			for(i = 0; i < g->max_member; i++, j++) {
 				if (j>8) j=0;
-				if ((dstsd = g->member[i].sd) != NULL && sd != dstsd) {
+				if ((dstsd = g->member[i].sd) != NULL && sd != dstsd && !pc_isdead(dstsd)) {
 					if (map[dstsd->bl.m].flag.nowarp && !map_flag_gvg2(dstsd->bl.m))
 						continue;
 					if(map_getcell(src->m,src->x+dx[j],src->y+dy[j],CELL_CHKNOREACH))
@@ -5358,7 +5467,7 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr data)
 				break;
 			}
 		}
-		if (ud->skillid == PR_LEXDIVINA)
+		if( ud->skillid == PR_LEXDIVINA || ud->skillid == MER_LEXDIVINA )
 		{
 			sc = status_get_sc(target);
 			if( battle_check_target(src,target, BCT_ENEMY) <= 0 && (!sc || !sc->data[SC_SILENCE]) )
@@ -5723,12 +5832,16 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk
 	case CR_GRANDCROSS:
 	case NPC_GRANDDARKNESS:
 	case HT_SKIDTRAP:
+	case MA_SKIDTRAP:
 	case HT_LANDMINE:
+	case MA_LANDMINE:
 	case HT_ANKLESNARE:
 	case HT_SHOCKWAVE:
 	case HT_SANDMAN:
+	case MA_SANDMAN:
 	case HT_FLASHER:
 	case HT_FREEZINGTRAP:
+	case MA_FREEZINGTRAP:
 	case HT_BLASTMINE:
 	case HT_CLAYMORETRAP:
 	case AS_VENOMDUST:
@@ -5740,6 +5853,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk
 	case WE_CALLPARENT:
 	case WE_CALLBABY:
 	case AC_SHOWER:	//Ground-placed skill implementation.
+	case MA_SHOWER:
 	case SA_VOLCANO:
 	case SA_DELUGE:
 	case SA_VIOLENTGALE:
@@ -6333,12 +6447,16 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli
 	case HT_SHOCKWAVE:
 		val1=skilllv*15+10;
 	case HT_SANDMAN:
+	case MA_SANDMAN:
 	case HT_CLAYMORETRAP:
 	case HT_SKIDTRAP:
+	case MA_SKIDTRAP:
 	case HT_LANDMINE:
+	case MA_LANDMINE:
 	case HT_ANKLESNARE:
 	case HT_FLASHER:
 	case HT_FREEZINGTRAP:
+	case MA_FREEZINGTRAP:
 	case HT_BLASTMINE:
 		if( map_flag_gvg(src->m) )
 			limit *= 4; // longer trap times in WOE [celest]
@@ -6557,13 +6675,17 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli
 			val2 = map_getcell(src->m, ux, uy, CELL_GETTYPE);
 			break;
 		case HT_LANDMINE:
+		case MA_LANDMINE:
 		case HT_ANKLESNARE:
 		case HT_SHOCKWAVE:
 		case HT_SANDMAN:
+		case MA_SANDMAN:
 		case HT_FLASHER:
 		case HT_FREEZINGTRAP:
+		case MA_FREEZINGTRAP:
 		case HT_TALKIEBOX:
 		case HT_SKIDTRAP:
+		case MA_SKIDTRAP:
 			val1 = 3500;
 			break;
 		case GS_DESPERADO:
@@ -7780,7 +7902,9 @@ int skill_check_condition(struct map_session_data* sd, short skill, short lv, in
 	case TF_HIDING:
 	case AS_CLOAKING:
 	case CR_AUTOGUARD:
+	case ML_AUTOGUARD:
 	case CR_DEFENDER:
+	case ML_DEFENDER:
 	case ST_CHASEWALK:
 	case PA_GOSPEL:
 	case CR_SHRINK:
@@ -8411,10 +8535,11 @@ int skill_delayfix (struct block_list *bl, int skill_id, int skill_lv)
 {
 	int delaynodex = skill_get_delaynodex(skill_id, skill_lv);
 	int time = skill_get_delay(skill_id, skill_lv);
-	//struct map_session_data *sd = BL_CAST(BL_PC, bl);
+	struct map_session_data *sd;
 	struct status_change *sc = status_get_sc(bl);
 
 	nullpo_retr(0, bl);
+	sd = BL_CAST(BL_PC, bl);
 
 	if (skill_id == SA_ABRACADABRA)
 		return 0; //Will use picked skill's delay.
@@ -8477,9 +8602,8 @@ int skill_delayfix (struct block_list *bl, int skill_id, int skill_lv)
 		}
 	}
 
-	if (!(delaynodex&4))
-		if (bl->type == BL_PC && ((TBL_PC*)bl)->delayrate != 100)
-			time = time * ((TBL_PC*)bl)->delayrate / 100;
+	if( !(delaynodex&4) && sd && sd->delayrate != 100 )
+		time = time * sd->delayrate / 100;
 
 	if (battle_config.delay_rate != 100)
 		time = time * battle_config.delay_rate / 100;
@@ -10557,6 +10681,30 @@ int skill_blockhomun_start(struct homun_data *hd, int skillid, int tick)	//[orn]
 	return add_timer(gettick() + tick, skill_blockhomun_end, hd->bl.id, skillid);
 }
 
+int skill_blockmerc_end(int tid, unsigned int tick, int id, intptr data)	//[orn]
+{
+	struct mercenary_data *md = (TBL_MER*)map_id2bl(id);
+	if( data <= 0 || data >= MAX_SKILL )
+		return 0;
+	if( md ) md->blockskill[data] = 0;
+
+	return 1;
+}
+
+int skill_blockmerc_start(struct mercenary_data *md, int skillid, int tick)
+{
+	nullpo_retr (-1, md);
+	
+	if( (skillid = skill_get_index(skillid)) == 0 )
+		return -1;
+	if( tick < 1 )
+	{
+		md->blockskill[skillid] = 0;
+		return -1;
+	}
+	md->blockskill[skillid] = 1;
+	return add_timer(gettick() + tick, skill_blockmerc_end, md->bl.id, skillid);
+}
 
 /*
  *

+ 3 - 2
src/map/skill.h

@@ -327,8 +327,9 @@ int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int
 int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
 int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag);
 
-int skill_blockpc_start (struct map_session_data*,int,int);	// [celest]
-int skill_blockhomun_start (struct homun_data*,int,int);	//[orn]
+int skill_blockpc_start (struct map_session_data*,int,int);
+int skill_blockhomun_start (struct homun_data*,int,int);
+int skill_blockmerc_start (struct mercenary_data*,int,int);
 
 // ƒXƒLƒ‹�U?ˆêЇ?—�
 int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc,struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );

+ 133 - 72
src/map/status.c

@@ -403,6 +403,26 @@ void initChangeTables(void)
 	set_sc( HAMI_DEFENCE         , SC_DEFENCE         , SI_BLANK           , SCB_DEF );
 	set_sc( HAMI_BLOODLUST       , SC_BLOODLUST       , SI_BLANK           , SCB_BATK|SCB_WATK );
 
+	add_sc( MER_CRASH            , SC_STUN            );
+	set_sc( MER_PROVOKE          , SC_PROVOKE         , SI_PROVOKE         , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK );
+	add_sc( MS_MAGNUM            , SC_WATK_ELEMENT    );
+	add_sc( MER_SIGHT            , SC_SIGHT           );
+	set_sc( MER_DECAGI           , SC_DECREASEAGI     , SI_DECREASEAGI     , SCB_AGI|SCB_SPEED );
+	set_sc( MER_MAGNIFICAT       , SC_MAGNIFICAT      , SI_MAGNIFICAT      , SCB_REGEN );
+	add_sc( MER_LEXDIVINA        , SC_SILENCE         );
+	add_sc( MA_LANDMINE          , SC_STUN            );
+	add_sc( MA_SANDMAN           , SC_SLEEP           );
+	add_sc( MA_FREEZINGTRAP      , SC_FREEZE          );
+	set_sc( MER_AUTOBERSERK      , SC_AUTOBERSERK     , SI_AUTOBERSERK     , SCB_NONE );
+	set_sc( ML_AUTOGUARD         , SC_AUTOGUARD       , SI_AUTOGUARD       , SCB_NONE );
+	set_sc( MS_REFLECTSHIELD     , SC_REFLECTSHIELD   , SI_REFLECTSHIELD   , SCB_NONE );
+	set_sc( ML_DEFENDER          , SC_DEFENDER        , SI_DEFENDER        , SCB_SPEED|SCB_ASPD );
+	set_sc( MS_PARRYING          , SC_PARRYING        , SI_PARRYING        , SCB_NONE );
+	set_sc( MS_BERSERK           , SC_BERSERK         , SI_BERSERK         , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN );
+	add_sc( ML_SPIRALPIERCE      , SC_STOP            );
+	set_sc( MER_QUICKEN          , SC_MERC_QUICKEN    , SI_BLANK           , SCB_ASPD );
+	add_sc( ML_DEVOTION          , SC_DEVOTION        );
+
 	set_sc( GD_LEADERSHIP        , SC_GUILDAURA       , SI_BLANK           , SCB_STR|SCB_AGI|SCB_VIT|SCB_DEX );
 	set_sc( GD_BATTLEORDER       , SC_BATTLEORDERS    , SI_BLANK           , SCB_STR|SCB_INT|SCB_DEX );
 	set_sc( GD_REGENERATION      , SC_REGENERATION    , SI_BLANK           , SCB_REGEN );
@@ -637,18 +657,23 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s
 	if (sc && !sc->count)
 		sc = NULL;
 
-	if (hp && !(flag&1)) {
-		if (sc) {
+	if( hp && !(flag&1) ) {
+		if( sc ) {
 			struct status_change_entry *sce;
-			if ((sce=sc->data[SC_DEVOTION]) && src && battle_getcurrentskill(src) != PA_PRESSURE)
-			{	//Devotion prevents any of the other ailments from ending.
-				struct map_session_data *sd2 = map_id2sd(sce->val1);
-				if (sd2 && sd2->devotion[sce->val2] == target->id && check_distance_bl(target, &sd2->bl, sce->val3))
+			if( (sce = sc->data[SC_DEVOTION]) && src && battle_getcurrentskill(src) != PA_PRESSURE )
+			{ // Devotion prevents any of the other ailments from ending.
+				struct block_list *d_bl = map_id2bl(sce->val1);
+
+				if( d_bl && (
+					(d_bl->type == BL_MER && ((TBL_MER*)d_bl)->master && ((TBL_MER*)d_bl)->master->bl.id == target->id) ||
+					(d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce->val2] == target->id)
+					) && check_distance_bl(target, d_bl, sce->val3) )
 				{
-					clif_damage(&sd2->bl, &sd2->bl, gettick(), 0, 0, hp, 0, 0, 0);
-					status_fix_damage(NULL, &sd2->bl, hp, 0);
+					clif_damage(d_bl, d_bl, gettick(), 0, 0, hp, 0, 0, 0);
+					status_fix_damage(NULL, d_bl, hp, 0);
 					return 0;
 				}
+
 				status_change_end(target, SC_DEVOTION, -1);
 			}
 			if (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
@@ -2963,11 +2988,6 @@ void status_calc_bl_sub_mer(struct mercenary_data *md, unsigned long flag)
 		status->max_sp = cap_value(status->max_sp, 1, battle_config.max_sp);
 		status->sp = cap_value(status->sp, 0, status->max_sp);
 	}
-	if( flag&SCB_VIT )
-	{
-		flag |= SCB_DEF;
-		status->def += status->vit; // Doddler says Merc DEF = DEF + VIT
-	}
 	if( flag == SCB_ALL )
 		return; // Client Refresh invoked by status_calc_mercenary
 
@@ -3908,6 +3928,10 @@ static short status_calc_aspd_rate(struct block_list *bl, struct status_change *
 			max < sc->data[SC_ONEHAND]->val2)
 			max = sc->data[SC_ONEHAND]->val2;
 
+		if(sc->data[SC_MERC_QUICKEN] &&
+			max < sc->data[SC_MERC_QUICKEN]->val2)
+			max = sc->data[SC_MERC_QUICKEN]->val2;
+
 		if(sc->data[SC_ADRENALINE2] &&
 			max < sc->data[SC_ADRENALINE2]->val3)
 			max = sc->data[SC_ADRENALINE2]->val3;
@@ -4804,6 +4828,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 			return 0;
 	break;
 	case SC_ONEHAND:
+	case SC_MERC_QUICKEN:
 	case SC_TWOHANDQUICKEN:
 		if(sc->data[SC_DECREASEAGI])
 			return 0;
@@ -4974,6 +4999,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 		status_change_end(bl,SC_SPEARQUICKEN,-1);
 		status_change_end(bl,SC_TWOHANDQUICKEN,-1);
 		status_change_end(bl,SC_ONEHAND,-1);
+		status_change_end(bl,SC_MERC_QUICKEN,-1);
 		break;
 	case SC_ONEHAND:
 	  	//Removes the Aspd potion effect, as reported by Vicious. [Skotlex]
@@ -5010,6 +5036,7 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 			status_change_end(bl,SC_CONCENTRATION,-1);
 			status_change_end(bl,SC_PARRYING,-1);
 			status_change_end(bl,SC_AURABLADE,-1);
+			status_change_end(bl,SC_MERC_QUICKEN,-1);
 		}
 		break;
 	case SC_ASSUMPTIO:
@@ -5140,19 +5167,23 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 			break;
 		case SC_ENDURE:
 			val2 = 7; // Hit-count [Celest]
-			if (!(flag&1) && sd && !map_flag_gvg(bl->m) && (type != SC_ENDURE || !val4))
-			{	//See if there are devoted characters, and pass the status to them. [Skotlex]
-				//(but do not pass infinite endure)
+			if( !(flag&1) && (bl->type&(BL_PC|BL_MER)) && !map_flag_gvg(bl->m) && !val4 )
+			{
 				struct map_session_data *tsd;
-				int i;
-				for (i = 0; i < 5; i++)
-				{	
-					if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])))
-						status_change_start(&tsd->bl,type,10000,val1,val2,val3,val4,tick,1);
+				if( sd )
+				{
+					int i;
+					for( i = 0; i < 5; i++ )
+					{
+						if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
+							status_change_start(&tsd->bl, type, 10000, val1, val2, val3, val4, tick, 1);
+					}
 				}
+				else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
+					status_change_start(&tsd->bl, type, 10000, val1, val2, val3, val4, tick, 1);
 			}
 			//val4 signals infinite endure (if val4 == 2 it is infinite endure from Berserk)
-			if(val4)
+			if( val4 )
 				tick = -1;
 			break;
 		case SC_AUTOBERSERK:
@@ -5221,16 +5252,21 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 			val2=val1*5; //Race/Ele resist
 			break;
 		case SC_REFLECTSHIELD:
-			val2=10+val1*3; //% Dmg reflected
-			if (sd && !(flag&1))
-			{	//Pass it to devoted chars.
+			val2=10+val1*3; // %Dmg reflected
+			if( !(flag&1) && (bl->type&(BL_PC|BL_MER)) )
+			{
 				struct map_session_data *tsd;
-				int i;
-				for (i = 0; i < 5; i++)
-				{	//Pass the status to the other affected chars. [Skotlex]
-					if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])))
-						status_change_start(&tsd->bl,type,10000,val1,val2,0,0,tick,1);
+				if( sd )
+				{
+					int i;
+					for( i = 0; i < 5; i++ )
+					{
+						if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
+							status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
+					}
 				}
+				else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
+					status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
 			}
 			break;
 		case SC_STRIPWEAPON:
@@ -5290,6 +5326,10 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 			if (val1 > 10) //For boss casted skills [Skotlex]
 				val2 += 20*(val1-10);
 			break;
+		case SC_MERC_QUICKEN:
+			val2 = 300;
+			break;
+
 		case SC_SPEARQUICKEN:
 			val2 = 200+10*val1;
 			break;
@@ -5458,19 +5498,28 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 			break;
 
 		case SC_AUTOGUARD:
-			if (!(flag&1))
+			if( !(flag&1) )
 			{
 				struct map_session_data *tsd;
 				int i,t;
-				for(i=val2=0;i<val1;i++) {
+				for( i = val2 = 0; i < val1; i++)
+				{
 					t = 5-(i>>1);
 					val2 += (t < 0)? 1:t;
 				}
-				if (sd)
-				for (i = 0; i < 5; i++)
-				{	//Pass the status to the other affected chars. [Skotlex]
-					if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])))
-						status_change_start(&tsd->bl,type,10000,val1,val2,0,0,tick,1);
+
+				if( bl->type&(BL_PC|BL_MER) )
+				{
+					if( sd )
+					{
+						for( i = 0; i < 5; i++ )
+						{
+							if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
+								status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
+						}
+					}
+					else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
+						status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
 				}
 			}
 			break;
@@ -5702,19 +5751,19 @@ int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val
 
 		case SC_DEVOTION:
 		{
-			struct map_session_data *src;
-			if ((src = map_id2sd(val1)) && src->sc.count)
-			{	//Try to inherit the status from the Crusader [Skotlex]
-			//Ideally, we should calculate the remaining time and use that, but we'll trust that
-			//once the Crusader's status changes, it will reflect on the others. 
+			struct block_list *d_bl;
+			struct status_change *d_sc;
+
+			if( (d_bl = map_id2bl(val1)) && (d_sc = status_get_sc(d_bl)) && d_sc->count )
+			{ // Inherits Status From Source
 				const enum sc_type types[] = { SC_AUTOGUARD, SC_DEFENDER, SC_REFLECTSHIELD, SC_ENDURE };
 				enum sc_type type2;
 				int i = map_flag_gvg(bl->m)?2:3;
-				while (i >= 0) {
+				while( i >= 0 )
+				{
 					type2 = types[i];
-					if (src->sc.data[type2])
-						sc_start(bl,type2,100,src->sc.data[type2]->val1,
-							skill_get_time(status_sc2skill(type2),src->sc.data[type2]->val1));
+					if( d_sc->data[type2] )
+						sc_start(bl, type2, 100, d_sc->data[type2]->val1, skill_get_time(status_sc2skill(type2),d_sc->data[type2]->val1));
 					i--;
 				}
 			}
@@ -6476,32 +6525,44 @@ int status_change_end(struct block_list* bl, enum sc_type type, int tid)
 		case SC_DEFENDER:
 		case SC_REFLECTSHIELD:
 		case SC_AUTOGUARD:
-		if (sd) {
-			struct map_session_data *tsd;
-			int i;
-			for (i = 0; i < 5; i++)
-			{	//Clear the status from the others too [Skotlex]
-				if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) && tsd->sc.data[type])
-					status_change_end(&tsd->bl,type,-1);
+			{
+				struct map_session_data *tsd;
+				if( bl->type == BL_PC )
+				{ // Clear Status from others
+					int i;
+					for( i = 0; i < 5; i++ )
+					{
+						if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) && tsd->sc.data[type] )
+							status_change_end(&tsd->bl, type, -1);
+					}
+				}
+				else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag )
+				{ // Clear Status from Master
+					tsd = ((TBL_MER*)bl)->master;
+					if( tsd && tsd->sc.data[type] )
+						status_change_end(&tsd->bl, type, -1);
+				}
 			}
-		}
-		break;
-		case SC_DEVOTION:	
-		{
-			struct map_session_data *md = map_id2sd(sce->val1);
-			//The status could have changed because the Crusader left the game. [Skotlex]
-			if (md)
+			break;
+		case SC_DEVOTION:
 			{
-				md->devotion[sce->val2] = 0;
-				clif_devotion(md,NULL);
+				struct block_list *d_bl = map_id2bl(sce->val1);
+				if( d_bl )
+				{
+					if( d_bl->type == BL_PC )
+						((TBL_PC*)d_bl)->devotion[sce->val2] = 0;
+					else if( d_bl->type == BL_MER )
+						((TBL_MER*)d_bl)->devotion_flag = 0;
+					clif_devotion(d_bl, NULL);
+				}
+
+				status_change_end(bl,SC_AUTOGUARD,-1);
+				status_change_end(bl,SC_DEFENDER,-1);
+				status_change_end(bl,SC_REFLECTSHIELD,-1);
+				status_change_end(bl,SC_ENDURE,-1);
 			}
-			//Remove inherited status [Skotlex]
-			status_change_end(bl,SC_AUTOGUARD,-1);
-			status_change_end(bl,SC_DEFENDER,-1);
-			status_change_end(bl,SC_REFLECTSHIELD,-1);
-			status_change_end(bl,SC_ENDURE,-1);
-		}
-		break;
+			break;
+
 		case SC_BLADESTOP:
 			if(sce->val4)
 			{
@@ -6608,8 +6669,7 @@ int status_change_end(struct block_list* bl, enum sc_type type, int tid)
 				sc->data[SC_ENDURE]->val4 = 0;
 				status_change_end(bl, SC_ENDURE, -1);
 			}
-			sc_start4(bl, SC_REGENERATION, 100, 10,0,0,(RGN_HP|RGN_SP),
-				skill_get_time(LK_BERSERK, sce->val1));
+			sc_start4(bl, SC_REGENERATION, 100, 10,0,0,(RGN_HP|RGN_SP), skill_get_time(LK_BERSERK, sce->val1));
 			break;
 		case SC_GOSPEL:
 			if (sce->val3) { //Clear the group.
@@ -6725,6 +6785,7 @@ int status_change_end(struct block_list* bl, enum sc_type type, int tid)
 	case SC_ONEHAND:
 	case SC_SPEARQUICKEN:
 	case SC_CONCENTRATION:
+	case SC_MERC_QUICKEN:
 		sc->opt3 &= ~0x1;
 		opt_flag = 0;
 		break;
@@ -6813,7 +6874,7 @@ int status_change_end(struct block_list* bl, enum sc_type type, int tid)
 	}
 
 	//On Aegis, when turning off a status change, first goes the sc packet, then the option packet.
-	if( vd && pcdb_checkid(vd->class_) )
+	if( vd && (pcdb_checkid(vd->class_) || bl->type == BL_MER ) )
 		clif_status_change(bl,StatusIconChangeTable[type],0);
 	else if (sd)
 		clif_status_load(bl,StatusIconChangeTable[type],0);

+ 1 - 0
src/map/status.h

@@ -302,6 +302,7 @@ typedef enum sc_type {
 	SC_MERC_HPUP,
 	SC_MERC_SPUP,
 	SC_MERC_HITUP,
+	SC_MERC_QUICKEN,
 
 	SC_MAX, //Automatically updated max, used in for's to check we are within bounds.
 } sc_type;

+ 36 - 29
src/map/unit.c

@@ -945,29 +945,32 @@ int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, sh
 			return 0;
 	}
 	//TODO: Add type-independant skill_check_condition function.
-	if (src->type == BL_MOB) {
-		switch (skill_num) {
+	if( src->type == BL_MOB )
+		switch( skill_num )
+		{
 			case NPC_SUMMONSLAVE:
 			case NPC_SUMMONMONSTER:
 			case AL_TELEPORT:
-				if (((TBL_MOB*)src)->master_id && ((TBL_MOB*)src)->special_state.ai)
+				if( ((TBL_MOB*)src)->master_id && ((TBL_MOB*)src)->special_state.ai )
 					return 0;
 		}
-	}
-
+	
 	//Check range when not using skill on yourself or is a combo-skill during attack
 	//(these are supposed to always have the same range as your attack)
-	if(src->id != target_id && (!temp || ud->attacktimer == -1))
+	if( src->id != target_id && (!temp || ud->attacktimer == -1) )
 	{
-		if (skill_get_state(ud->skillid) == ST_MOVE_ENABLE)
+		if( skill_get_state(ud->skillid) == ST_MOVE_ENABLE )
 		{
-			if (!unit_can_reach_bl(src, target, skill_get_range2(src, skill_num,skill_lv)+1, 1, NULL, NULL))
-				return 0; //Walk-path check failed.
-		} else
-		if	(!battle_check_range(src, target, skill_get_range2(src, skill_num,skill_lv)
-			+(skill_num==RG_CLOSECONFINE?0:1)))
-			//Close confine is exploitable thanks to this extra range "feature" of the client. [Skotlex]
-			return 0; //Arrow-path check failed.
+			if( !unit_can_reach_bl(src, target, skill_get_range2(src, skill_num,skill_lv) + 1, 1, NULL, NULL) )
+				return 0; // Walk-path check failed.
+		}
+		else if( src->type == BL_MER && skill_num == MA_REMOVETRAP )
+		{
+			if( !battle_check_range(battle_get_master(src), target, skill_get_range2(src, skill_num, skill_lv) + 1) )
+				return 0; // Aegis calc remove trap based on Master position, ignoring mercenary O.O
+		}
+		else if( !battle_check_range(src, target, skill_get_range2(src, skill_num,skill_lv) + (skill_num == RG_CLOSECONFINE?0:1)) )
+			return 0; // Arrow-path check failed.
 	}
 
 	if (!temp) //Stop attack on non-combo skills [Skotlex]
@@ -1028,8 +1031,8 @@ int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, sh
 	if (!(skill_get_castnodex(skill_num, skill_lv)&2))
 		casttime = skill_castfix_sc(src, casttime);
 
-	if( casttime>0 || temp){ 
-
+	if( casttime > 0 || temp )
+	{ 
 		clif_skillcasting(src, src->id, target_id, 0,0, skill_num, skill_get_ele(skill_num, skill_lv), casttime);
 
 		if (sd && target->type == BL_MOB)
@@ -1062,8 +1065,8 @@ int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, sh
 		}
 	}
 
-	if( casttime<=0 )
-		ud->state.skillcastcancel=0;
+	if( casttime <= 0 )
+		ud->state.skillcastcancel = 0;
 
 	ud->canact_tick  = tick + casttime + 100;
 	ud->skilltarget  = target_id;
@@ -1072,14 +1075,14 @@ int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, sh
 	ud->skillid      = skill_num;
 	ud->skilllv      = skill_lv;
 
- 	if(sc && sc->data[SC_CLOAKING] &&
-		!(sc->data[SC_CLOAKING]->val4&4) && skill_num != AS_CLOAKING)
+ 	if( sc && sc->data[SC_CLOAKING] && !(sc->data[SC_CLOAKING]->val4&4) && skill_num != AS_CLOAKING )
 	{
 		status_change_end(src,SC_CLOAKING,-1);
 		if (!src->prev) return 0; //Warped away!
 	}
 
-	if(casttime > 0) {
+	if( casttime > 0 )
+	{
 		ud->skilltimer = add_timer( tick+casttime, skill_castend_id, src->id, 0 );
 		if( sd && pc_checkskill(sd,SA_FREECAST) > 0 )
 			status_calc_bl(&sd->bl, SCB_SPEED);
@@ -1147,12 +1150,12 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, sh
 	bl.x = skill_x;
 	bl.y = skill_y;
 
-	if (skill_get_state(ud->skillid) == ST_MOVE_ENABLE)
+	if( skill_get_state(ud->skillid) == ST_MOVE_ENABLE )
 	{
-		if (!unit_can_reach_bl(src, &bl, skill_get_range2(src, skill_num,skill_lv)+1, 1, NULL, NULL))
+		if( !unit_can_reach_bl(src, &bl, skill_get_range2(src, skill_num,skill_lv) + 1, 1, NULL, NULL) )
 			return 0; //Walk-path check failed.
-	} else
-	if	(!battle_check_range(src,&bl,skill_get_range2(src, skill_num,skill_lv)+1))
+	}
+	else if( !battle_check_range(src, &bl, skill_get_range2(src, skill_num,skill_lv) + 1) )
 		return 0; //Arrow-path check failed.
 
 	unit_stop_attack(src);
@@ -1162,10 +1165,12 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, sh
 	if (!(skill_get_castnodex(skill_num, skill_lv)&2))
 		casttime = skill_castfix_sc(src, casttime);
 
-	if( casttime>0 ) {
+	if( casttime > 0 )
+	{
 		unit_stop_walking( src, 1);
 		clif_skillcasting(src, src->id, 0, skill_x, skill_y, skill_num, skill_get_ele(skill_num, skill_lv), casttime);
-	} else
+	}
+	else
 		ud->state.skillcastcancel=0;
 
 	ud->canact_tick  = tick + casttime + 100;
@@ -1181,14 +1186,16 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, sh
 		if (!src->prev) return 0; //Warped away!
 	}
 
-	if(casttime > 0) {
+	if( casttime > 0 )
+	{
 		ud->skilltimer = add_timer( tick+casttime, skill_castend_pos, src->id, 0 );
 		if( sd && pc_checkskill(sd,SA_FREECAST) > 0 )
 			status_calc_bl(&sd->bl, SCB_SPEED);
 		else
 			unit_stop_walking(src,1);
 	}
-	else {
+	else
+	{
 		ud->skilltimer = INVALID_TIMER;
 		skill_castend_pos(ud->skilltimer,tick,src->id,0);
 	}