Bläddra i källkod

- Added some new config settings: homunculus_autoloot, idle_no_autoloot, max_guild_alliance.
- Added a code to activate a Kill Steal protection and the required mapflags.
* (I will explain this later on forums).

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

zephyrus 17 år sedan
förälder
incheckning
69ae4766ce

+ 3 - 0
Changelog-Trunk.txt

@@ -17,6 +17,9 @@ IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
 	* Added some security checks in mail system [Zephyrus]
 	- This supose to fix a bug reported in 622 to limit to MAX_ZENY.
 	- Also add more checks to free space in your inventory to receive items.
+	* Added some new config settings: [Zephyrus]
+	- homunculus_autoloot, idle_no_autoloot, max_guild_alliance.
+	- Added a code to activate a Kill Steal protection and the required mapflags.
 2008/02/11
 	* 'Forget me Not' no longer blocks ASPD bonuses from working or prevents
 	  their re-casting, they are simply dispelled when the effect takes place.

+ 9 - 0
conf/atcommand_athena.conf

@@ -131,6 +131,9 @@ jailtime: 1
 hominfo: 1
 homstats: 1
 
+// Kill Steal Protection
+noks: 1
+
 //---------------------------
 // 10: Super player+ commands
 
@@ -146,6 +149,9 @@ go: 10
 // Enables/disables autolooting from killed mobs.
 autoloot: 10
 
+// Enables/disables autolooting an item.
+alootid: 10
+
 // Allows you continue vending offline.
 autotrade: 10
 at: 10
@@ -278,6 +284,9 @@ gpvpon: 40
 gvgoff: 40
 gpvpoff: 40
 
+// Activate/Deactivate kill steal protection on a map
+allowks: 40
+
 // Modifies your HP/SP.
 heal: 40
 

+ 5 - 0
conf/battle/guild.conf

@@ -62,3 +62,8 @@ gvg_eliminate_time: 7000
 // and does changing emblems require it? (Note 1)
 // P.S: This skill is not implemented on official servers
 require_glory_guild: no
+
+// Limit Guild alliances. Value is 0 to 3.
+// If you want to change this value, clear the guild alliance table.
+// Default is 3
+max_guild_alliance: 3

+ 3 - 0
conf/battle/homunc.conf

@@ -43,3 +43,6 @@ hvan_explosion_intimate: 45000
 
 // Show stat growth to the owner when an Homunculus levels up
 homunculus_show_growth: no
+
+// If a monster is killed only by homunculus, can autoloot works?
+homunculus_autoloot: yes

+ 6 - 0
conf/battle/monster.conf

@@ -199,3 +199,9 @@ mob_clear_delay: 0
 // Type 0: On the player that did the most damage to the mob.
 // NOTE: This affects who gains the Castle when the Emperium is broken. 
 mob_npc_event_type: 1
+
+// Time in milliseconds to actitave protection against Kill Steal
+// Set to 0 to disable it.
+// If this is activated and a player is using @noks, damage from others players (KS) not in the party
+// will be reduced to 0.
+ksprotection: 0

+ 4 - 0
conf/battle/player.conf

@@ -135,3 +135,7 @@ bone_drop: 0
 // 3 = both Normal Classes on Peco have Big Size
 //	and Baby Classes on Peco have Medium Size
 character_size: 0
+
+// Idle characters can receive autoloot?
+// Set to the time in seconds where an idle character will stop receiving items from Autoloot.
+idle_no_autoloot: 120

+ 84 - 1
src/map/atcommand.c

@@ -6085,9 +6085,55 @@ int atcommand_autoloot(const int fd, struct map_session_data* sd, const char* co
 		clif_displaymessage(fd, atcmd_output);
 	}else 
 		clif_displaymessage(fd, "Autoloot is now off.");
+
+	if (sd->state.autoloot && sd->state.autolootid) {
+		// Autolootitem should be turned off
+		sd->state.autolootid = 0;
+		clif_displaymessage(fd, "Autolootitem is now off.");
+	}
+
 	return 0;  
-}   
+}
+/*==========================================
+ * @autolootitem
+ *------------------------------------------*/
+int atcommand_autolootitem(const int fd, struct map_session_data* sd, const char* command, const char* message)
+{
+	struct item_data *item_data = NULL;
+
+	if (!message || !*message) {
+		if (sd->state.autolootid) {
+			sd->state.autolootid = 0;
+			clif_displaymessage(fd, "Autolootitem have been turned OFF.");
+		} else
+			clif_displaymessage(fd, "Please, enter Item name or its ID (usage: @autolootitem <item_name_or_ID>).");
 
+		return -1;
+	}
+
+	if ((item_data = itemdb_exists(atoi(message))) == NULL)
+		item_data = itemdb_searchname(message);
+
+	if (!item_data) {
+		// No items founds in the DB with Id or Name
+		clif_displaymessage(fd, "Item not found.");
+		return -1;
+	}
+
+	sd->state.autolootid = item_data->nameid; // Autoloot Activated
+
+	sprintf(atcmd_output, "Autolooting Item: '%s'/'%s' {%d}",
+		item_data->name, item_data->jname, item_data->nameid);
+	clif_displaymessage(fd, atcmd_output);
+
+	if (sd->state.autolootid && sd->state.autoloot) {
+		// Autoloot should be turned off
+		sd->state.autoloot = 0;
+		clif_displaymessage(fd, "Autoloot is now off (cannot be actitaved together).");
+	}
+
+	return 0;
+}
 
 /*==========================================
  * It is made to rain.
@@ -8115,8 +8161,42 @@ int atcommand_feelreset(const int fd, struct map_session_data* sd, const char* c
 	return 0;
 }
 
+/*==========================================
+ * Kill Steal Protection
+ *------------------------------------------*/
+int atcommand_ksprotection(const int fd, struct map_session_data *sd, const char *command, const char *message)
+{
+	nullpo_retr(-1,sd);
 
+	if( sd->state.noks ) {
+		sd->state.noks = 0;
+		sprintf(atcmd_output, "[ K.S Protection Inactive ]");
+	} else {
+		sprintf(atcmd_output, "[ K.S Protection Active ]");
+		sd->state.noks = 1;
+	}
 
+	clif_displaymessage(fd, atcmd_output);
+	return 0;
+}
+/*==========================================
+ * Map Kill Steal Protection Setting
+ *------------------------------------------*/
+int atcommand_allowks(const int fd, struct map_session_data *sd, const char *command, const char *message)
+{
+	nullpo_retr(-1,sd);
+
+	if( map[sd->bl.m].flag.allowks ) {
+		map[sd->bl.m].flag.allowks = 0;
+		sprintf(atcmd_output, "[ Map K.S Protection Active ]");
+	} else {
+		map[sd->bl.m].flag.allowks = 1;
+		sprintf(atcmd_output, "[ Map K.S Protection Inactive ]");
+	}
+
+	clif_displaymessage(fd, atcmd_output);
+	return 0;
+}
 
 /*==========================================
  * atcommand_info[] structure definition
@@ -8348,6 +8428,7 @@ AtCommandInfo atcommand_info[] = {
 	{ "disguiseall",       99,     atcommand_disguiseall },
 	{ "changelook",        60,     atcommand_changelook },
 	{ "autoloot",          10,     atcommand_autoloot },
+	{ "alootid",           10,     atcommand_autolootitem },
 	{ "mobinfo",            1,     atcommand_mobinfo },
 	{ "monsterinfo",        1,     atcommand_mobinfo },
 	{ "mi",                 1,     atcommand_mobinfo },
@@ -8407,6 +8488,8 @@ AtCommandInfo atcommand_info[] = {
 	{ "showmobs",          10,     atcommand_showmobs },
 	{ "feelreset",         10,     atcommand_feelreset },
 	{ "mail",               1,     atcommand_mail },
+	{ "noks",               0,     atcommand_ksprotection },
+	{ "allowks",            6,     atcommand_allowks },
 };
 
 

+ 22 - 16
src/map/battle.c

@@ -259,6 +259,9 @@ int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,i
 	if (!damage)
 		return 0;
 	
+	if( mob_ksprotected(src, bl) )
+		return 0;
+
 	if (bl->type == BL_PC) {
 		sd=(struct map_session_data *)bl;
 		//Special no damage states
@@ -3139,23 +3142,22 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
 		case BL_PC:
 		{
 			TBL_PC *sd = (TBL_PC*) s_bl;
-			if (sd->state.killer && s_bl != t_bl)
+			if( s_bl != t_bl )
 			{
-				state |= BCT_ENEMY; //Is on a killing rampage :O
-				strip_enemy = 0;
-			} else
-			if (sd->duel_group && t_bl != s_bl && // Duel [LuzZza]
-				!(
-					(!battle_config.duel_allow_pvp && map[m].flag.pvp) ||
-					(!battle_config.duel_allow_gvg && map_flag_gvg(m))
-				))
-		  	{
-				if (t_bl->type == BL_PC &&
-					(sd->duel_group == ((TBL_PC*)t_bl)->duel_group))
-					//Duel targets can ONLY be your enemy, nothing else.
-					return (BCT_ENEMY&flag)?1:-1;
-				else // You can't target anything out of your duel
-					return 0;
+				if( sd->state.killer )
+				{
+					state |= BCT_ENEMY; //Is on a killing rampage :O
+					strip_enemy = 0;
+				}
+				else if( sd->duel_group && !((!battle_config.duel_allow_pvp && map[m].flag.pvp) || (!battle_config.duel_allow_gvg && map_flag_gvg(m))) )
+		  		{
+					if (t_bl->type == BL_PC &&
+						(sd->duel_group == ((TBL_PC*)t_bl)->duel_group))
+						//Duel targets can ONLY be your enemy, nothing else.
+						return (BCT_ENEMY&flag)?1:-1;
+					else // You can't target anything out of your duel
+						return 0;
+				}
 			}
 			if (map_flag_gvg(m) && !sd->status.guild_id &&
 				t_bl->type == BL_MOB && ((TBL_MOB*)t_bl)->guardian_data)
@@ -3650,6 +3652,10 @@ static const struct _battle_data {
 	{ "sg_miracle_skill_duration",          &battle_config.sg_miracle_skill_duration,       3600000, 0,     INT_MAX,        },
 	{ "hvan_explosion_intimate",            &battle_config.hvan_explosion_intimate,         45000,  0,      100000,         },
 	{ "quest_exp_rate",                     &battle_config.quest_exp_rate,                  100,    0,      INT_MAX,        },
+	{ "homunculus_autoloot",                &battle_config.homunculus_autoloot,             0,      0,      1,              },
+	{ "idle_no_autoloot",                   &battle_config.idle_no_autoloot,                0,      0,      INT_MAX,        },
+	{ "max_guild_alliance",                 &battle_config.max_guild_alliance,              3,      1,      3,              },
+	{ "ksprotection",                       &battle_config.ksprotection,                    5000,   0,      INT_MAX,        },
 };
 
 

+ 4 - 0
src/map/battle.h

@@ -438,6 +438,10 @@ extern struct Battle_Config
 	int homunculus_show_growth ;	//[orn]
 	int homunculus_friendly_rate;
 	int quest_exp_rate;
+	int homunculus_autoloot;
+	int idle_no_autoloot;
+	int max_guild_alliance;
+	int ksprotection;
 } battle_config;
 
 void do_init_battle(void);

+ 5 - 5
src/map/guild.c

@@ -1250,11 +1250,11 @@ int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd)
 	if(sd->status.guild_id == tsd->status.guild_id)
 		return 0;
 
-	if( guild_get_alliance_count(g[0],0)>=3 )	{
+	if( guild_get_alliance_count(g[0],0) >= battle_config.max_guild_alliance )	{
 		clif_guild_allianceack(sd,4);
 		return 0;
 	}
-	if( guild_get_alliance_count(g[1],0)>=3 ) {
+	if( guild_get_alliance_count(g[1],0) >= battle_config.max_guild_alliance ) {
 		clif_guild_allianceack(sd,3);
 		return 0;
 	}
@@ -1300,12 +1300,12 @@ int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag)
 		g=guild_search(sd->status.guild_id);
 		tg=guild_search(tsd->status.guild_id);
 		
-		if(g==NULL || guild_get_alliance_count(g,0)>=3){
+		if(g==NULL || guild_get_alliance_count(g,0) >= battle_config.max_guild_alliance){
 			clif_guild_allianceack(sd,4);
 			clif_guild_allianceack(tsd,3);
 			return 0;
 		}
-		if(tg==NULL || guild_get_alliance_count(tg,0)>=3){
+		if(tg==NULL || guild_get_alliance_count(tg,0) >= battle_config.max_guild_alliance){
 			clif_guild_allianceack(sd,3);
 			clif_guild_allianceack(tsd,4);
 			return 0;
@@ -1367,7 +1367,7 @@ int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd)
 	if(sd->status.guild_id == tsd->status.guild_id)
 		return 0;
 
-	if( guild_get_alliance_count(g,1)>=3 )	{
+	if( guild_get_alliance_count(g,1) >= battle_config.max_guild_alliance )	{
 		clif_guild_oppositionack(sd,1);
 		return 0;
 	}

+ 9 - 0
src/map/map.h

@@ -551,6 +551,8 @@ struct map_session_data {
 		unsigned doridori : 1;
 		unsigned ignoreAll : 1;
 		unsigned short autoloot;
+		unsigned short autolootid; // [Zephyrus]
+		unsigned noks : 1; // [Zeph Kill Steal Protection]
 		struct guild *gmaster_flag;
 	} state;
 	struct {
@@ -612,6 +614,7 @@ struct map_session_data {
 	unsigned int canuseitem_tick;	// [Skotlex]
 	unsigned int cantalk_tick;
 	unsigned int cansendmail_tick; // [Mail System Flood Protection]
+	unsigned int ks_floodprotect_tick; // [Kill Steal Protection]
 
 	short weapontype1,weapontype2;
 	short disguise; // [Valaris]
@@ -911,6 +914,11 @@ struct mob_data {
 	unsigned int tdmg; //Stores total damage given to the mob, for exp calculations. [Skotlex]
 	int level;
 	int target_id,attacked_id;
+
+	// Kill Steal Protection
+	int owner_id;
+	unsigned int ks_tick;
+	
 	unsigned int next_walktime,last_thinktime,last_linktime;
 	short move_fail_count;
 	short lootitem_count;
@@ -1168,6 +1176,7 @@ struct map_data {
 	int npc_num;
 	int users;
 	struct map_flag {
+		unsigned allowks : 1; // [Kill Steal Protection]
 		unsigned nomemo : 1;
 		unsigned noteleport : 1;
 		unsigned noreturn : 1;

+ 87 - 7
src/map/mob.c

@@ -10,6 +10,7 @@
 #include "../common/ers.h"
 #include "../common/strlib.h"
 #include "../common/utils.h"
+#include "../common/socket.h"
 
 #include "map.h"
 #include "path.h"
@@ -275,6 +276,80 @@ int mob_get_random_id(int type, int flag, int lv)
 	return class_;
 }
 
+/*==========================================
+ * Kill Steal Protection [Zephyrus]
+ *------------------------------------------*/
+bool mob_ksprotected (struct block_list *src, struct block_list *target)
+{
+	struct block_list *s_bl;
+	struct map_session_data *sd, *pl_sd;
+	struct mob_data *md;
+	unsigned int tick = gettick();
+	char output[128];
+
+	if( !battle_config.ksprotection )
+		return false; // KS Protection Disabled
+
+	if( !(md = BL_CAST(BL_MOB,target)) )
+		return false; // Tarjet is not MOB
+
+	if( (s_bl = battle_get_master(src)) == NULL )
+		s_bl = src;
+
+	if( !(sd = BL_CAST(BL_PC,s_bl)) )
+		return false; // Master is not PC
+
+	do {
+		if( map[md->bl.m].flag.allowks || map[md->bl.m].flag.gvg || map[md->bl.m].flag.pvp )
+			return false; // Ignores GVG, PVP and AllowKS map flags
+
+		if( md->db->mexp || md->master_id )
+			return false; // MVP and Slaves ignores KS
+
+		if( sd->bl.id == md->owner_id )
+			break; // Same player
+
+		if( !md->owner_id || !(pl_sd = map_id2sd(md->owner_id)) )
+			break; // Not owner or owner offline
+
+		if( pl_sd->bl.m != md->bl.m )
+			break; // Owner on different map
+
+		if( DIFF_TICK(md->ks_tick, tick) <= 0 )
+			break; // Protection Time's Out
+
+		if( !pl_sd->state.noks )
+			return false; // No KS Protected, but this is necessary to protect normal players
+
+		if( pl_sd->status.party_id && pl_sd->status.party_id == sd->status.party_id )
+			break; // Same Party Allow KS
+
+		// Message to KS
+		if( DIFF_TICK(sd->ks_floodprotect_tick, tick) <= 0 )
+		{
+			sprintf(output, "[KS Warning!! - Owner : %s]", pl_sd->status.name);
+			clif_disp_onlyself(sd, output, strlen(output));
+
+			sd->ks_floodprotect_tick = tick + 2000;
+		}
+
+		// Message to Owner
+		if( DIFF_TICK(pl_sd->ks_floodprotect_tick, tick) <= 0 )
+		{
+			sprintf(output, "[Warning!! - %s is KS you]", sd->status.name);
+			clif_disp_onlyself(pl_sd, output, strlen(output));
+
+			pl_sd->ks_floodprotect_tick = tick + 2000;
+		}
+
+		return true;
+	} while(0);
+
+	md->owner_id = sd->bl.id;
+	md->ks_tick = tick + battle_config.ksprotection;
+
+	return false;
+}
 
 struct mob_data *mob_once_spawn_sub(struct block_list *bl, int m, short x, short y, const char *mobname, int class_, const char *event)
 {
@@ -1522,8 +1597,9 @@ static int mob_delay_item_drop(int tid, unsigned int tick, int id, int data)
  * Sets the item_drop into the item_drop_list.
  * Also performs logging and autoloot if enabled.
  * rate is the drop-rate of the item, required for autoloot.
+ * flag : Killed only by homunculus?
  *------------------------------------------*/
-static void mob_item_drop(struct mob_data *md, struct item_drop_list *dlist, struct item_drop *ditem, int loot, int drop_rate)
+static void mob_item_drop(struct mob_data *md, struct item_drop_list *dlist, struct item_drop *ditem, int loot, int drop_rate, unsigned short flag)
 {
 	TBL_PC* sd;
 
@@ -1538,7 +1614,7 @@ static void mob_item_drop(struct mob_data *md, struct item_drop_list *dlist, str
 	sd = map_charid2sd(dlist->first_charid);
 	if( sd == NULL ) sd = map_charid2sd(dlist->second_charid);
 	if( sd == NULL ) sd = map_charid2sd(dlist->third_charid);
-	if( sd && drop_rate <= sd->state.autoloot
+	if( sd && (drop_rate <= sd->state.autoloot || ditem->item_data.nameid == sd->state.autolootid) && sd->idletime >= (last_tick - battle_config.idle_no_autoloot) && (battle_config.homunculus_autoloot?1:!flag)
 #ifdef AUTOLOOT_DISTANCE
 		&& check_distance_blxy(&sd->bl, dlist->x, dlist->y, AUTOLOOT_DISTANCE)
 #endif
@@ -1777,6 +1853,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 	} pt[DAMAGELOG_SIZE];
 	int i,temp,count,pnum=0,m=md->bl.m;
 	unsigned int mvp_damage, tick = gettick();
+	unsigned short flaghom = 1; // [Zephyrus] Does the mob only received damage from homunculus?
 
 	if(src && src->type == BL_PC) {
 		sd = (struct map_session_data *)src;
@@ -1851,6 +1928,9 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 		}
 
 		tmpsd[i] = tsd; // record as valid damage-log entry
+
+		if(!md->dmglog[i].flag && flaghom)
+			flaghom = 0; // Damage received from other Types != Homunculus
 	}
 
 	if(!battle_config.exp_calc_type && count > 1)
@@ -2045,13 +2125,13 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 			}
 			// Announce first, or else ditem will be freed. [Lance]
 			// By popular demand, use base drop rate for autoloot code. [Skotlex]
-			mob_item_drop(md, dlist, ditem, 0, md->db->dropitem[i].p);
+			mob_item_drop(md, dlist, ditem, 0, md->db->dropitem[i].p, flaghom);
 		}
 
 		// Ore Discovery [Celest]
 		if (sd == mvp_sd && pc_checkskill(sd,BS_FINDINGORE)>0 && battle_config.finding_ore_rate/10 >= rand()%10000) {
 			ditem = mob_setdropitem(itemdb_searchrandomid(IG_FINDINGORE), 1);
-			mob_item_drop(md, dlist, ditem, 0, battle_config.finding_ore_rate/10);
+			mob_item_drop(md, dlist, ditem, 0, battle_config.finding_ore_rate/10, 0);
 		}
 
 		if(sd) {
@@ -2077,7 +2157,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 					if (rand()%10000 >= drop_rate)
 						continue;
 					itemid = (sd->add_drop[i].id > 0) ? sd->add_drop[i].id : itemdb_searchrandomid(sd->add_drop[i].group);
-					mob_item_drop(md, dlist, mob_setdropitem(itemid,1), 0, drop_rate);
+					mob_item_drop(md, dlist, mob_setdropitem(itemid,1), 0, drop_rate, 0);
 				}
 			}
 			
@@ -2093,7 +2173,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 		// process items looted by the mob
 		if(md->lootitem) {
 			for(i = 0; i < md->lootitem_count; i++)
-				mob_item_drop(md, dlist, mob_setlootitem(&md->lootitem[i]), 1, 10000);
+				mob_item_drop(md, dlist, mob_setlootitem(&md->lootitem[i]), 1, 10000, 0);
 		}
 		if (dlist->item) //There are drop items.
 			add_timer(tick + (!battle_config.delay_battle_damage?500:0), mob_delay_item_drop, (int)dlist, 0);
@@ -2109,7 +2189,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
 		dlist->third_charid = (third_sd ? third_sd->status.char_id : 0);
 		dlist->item = NULL;
 		for(i = 0; i < md->lootitem_count; i++)
-			mob_item_drop(md, dlist, mob_setlootitem(&md->lootitem[i]), 1, 10000);
+			mob_item_drop(md, dlist, mob_setlootitem(&md->lootitem[i]), 1, 10000, 0);
 		add_timer(tick + (!battle_config.delay_battle_damage?500:0), mob_delay_item_drop, (int)dlist, 0);
 	}
 

+ 1 - 0
src/map/mob.h

@@ -157,6 +157,7 @@ struct mob_data *mob_once_spawn_sub(struct block_list *bl, int m,
 int mob_once_spawn(struct map_session_data* sd,int m,short x,short y,const char* mobname,int class_,int amount,const char* event);
 int mob_once_spawn_area(struct map_session_data* sd,int m,int x0,int y0,int x1,int y1,const char* mobname,int class_,int amount,const char* event);
 
+bool mob_ksprotected (struct block_list *src, struct block_list *target);
 int mob_spawn_guardian(const char* mapname, short x, short y, const char* mobname, int class_, const char* event, int guardian);	// Spawning Guardians [Valaris]
 int mob_guardian_guildchange(struct block_list *bl,va_list ap); //Change Guardian's ownership. [Skotlex]
 

+ 2 - 0
src/map/npc.c

@@ -2318,6 +2318,8 @@ static const char* npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4, con
 		}
 		map[m].flag.nosave = state;
 	}
+	else if (!strcmpi(w3,"allowks"))
+		map[m].flag.allowks=state; // [Kill Steal Protection]
 	else if (!strcmpi(w3,"nomemo"))
 		map[m].flag.nomemo=state;
 	else if (!strcmpi(w3,"noteleport"))

+ 2 - 0
src/map/unit.c

@@ -807,6 +807,8 @@ int unit_skilluse_id2(struct block_list *src, int target_id, short skill_num, sh
 		if(skillnotok(skill_num, sd)) // [MouseJstr]
 			return 0;
 
+		mob_ksprotected(src, map_id2bl(target_id));
+
 		switch(skill_num)
 		{	//Check for skills that auto-select target
 		case MO_CHAINCOMBO: