浏览代码

* INF2_NO_NEARNPC (skill_db.txt updates):
-- Added a new option info for skill's 'inf2', INF2_NO_NEARNPC, that used for checking if the skill is castable if caster/ground/target is near with NPC (by specified range option)
-- Corrected Shadow Chaser skills, SC_CHAOSPANIC and SC_MAELSTROM, that cannot be placed near warp portal (according to iRO Skill Balance Patch)
-- Added 'db/skill_nonearnpc_db.txt' for more option of INF2_NO_NEARNPC (additional range beside splash area, unit range, or layout range calculaiton and type of NPC)
* Follow up 5e6626e

Cahyadi Ramadhan Togihon 11 年之前
父节点
当前提交
659cc57574
共有 12 个文件被更改,包括 467 次插入371 次删除
  1. 21 18
      db/pre-re/skill_db.txt
  2. 13 13
      db/pre-re/skill_unit_db.txt
  3. 3 2
      db/re/skill_db.txt
  4. 13 13
      db/re/skill_unit_db.txt
  5. 24 0
      db/skill_nonearnpc_db.txt
  6. 6 6
      src/map/clif.c
  7. 1 1
      src/map/elemental.c
  8. 15 2
      src/map/npc.c
  9. 1 0
      src/map/npc.h
  10. 188 142
      src/map/skill.c
  11. 8 4
      src/map/skill.h
  12. 174 170
      src/map/unit.c

+ 21 - 18
db/pre-re/skill_db.txt

@@ -23,27 +23,30 @@
 // 10 Cast interrupted when hit?
 // 11 defense-reduction rate during cast.
 // 12 inf2 (skill information 2):
-//    0x0001- quest skill
-//    0x0002- npc skill
-//    0x0004- wedding skill
-//    0x0008- spirit skill
-//    0x0010- guild skill
-//    0x0020- song/dance
-//    0x0040- ensemble skill
-//    0x0080- trap
-//    0x0100- skill that damages/targets yourself
-//    0x0200- cannot be casted on self (if inf = 4, auto-select target skill)
-//    0x0400- usable only on party-members (and enemies if skill is offensive)
-//    0x0800- usable only on guild-mates (and enemies if skill is offensive)
-//    0x1000- disable usage on enemies (for non-offensive skills).
-//    0x2000- free
-//    0x4000- chorus skill
+//    0x00001- quest skill
+//    0x00002- npc skill
+//    0x00004- wedding skill
+//    0x00008- spirit skill
+//    0x00010- guild skill
+//    0x00020- song/dance
+//    0x00040- ensemble skill
+//    0x00080- trap
+//    0x00100- skill that damages/targets yourself
+//    0x00200- cannot be casted on self (if inf = 4, auto-select target skill)
+//    0x00400- usable only on party-members (and enemies if skill is offensive)
+//    0x00800- usable only on guild-mates (and enemies if skill is offensive)
+//    0x01000- disable usage on enemies (for non-offensive skills).
+//    0x02000- free
+//    0x04000- chorus skill
+//    0x08000- spell that ignore bg reduction
+//    0x10000- spell that ignore gvg reduction
+//    0x20000- makes 'self'/'place' skill cannot be casted/placed when near NPC (see 'db/skill_nonearnpc_db.txt' for more options)
 // 13 maxcount: max amount of skill instances to place on the ground when
 //    player_land_skill_limit/monster_land_skill_limit is enabled. For skills
 //    that attack using a path, this is the path length to be used.
 // 14 attack type (none, weapon, magic, misc)
 // 15 Blowcount (amount of tiles skill knockbacks)
-// 16 inf3 (skill option)
+// 16 inf3 (skill information 3):
 //    0x0001- skill ignores land protector (e.g. arrow shower)
 //    0x0002- spell that doesn't end camouflage
 //    0x0004- usable skills while hiding
@@ -1036,8 +1039,8 @@
 2298,3,6,1,0,0x1,0,5,1,yes,0,0,0,weapon,0,0x20,	SC_STRIPACCESSARY,Strip Accessory //CHECK Is weapon attack type needed?
 2299,7,6,2,0,0x1,0,3,1,yes,0,0,3,none,0,0x0,	SC_MANHOLE,Man Hole
 2300,7,6,2,0,0x1,0,3,1,yes,0,0,1,none,0,0x0,	SC_DIMENSIONDOOR,Dimension Door
-2301,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0,0x0,	SC_CHAOSPANIC,Chaos Panic
-2302,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0,0x0,	SC_MAELSTROM,Maelstrom
+2301,7,6,2,0,0x1,0,3,1,yes,0,0x20000,0,none,0,0x0,	SC_CHAOSPANIC,Chaos Panic
+2302,7,6,2,0,0x1,0,3,1,yes,0,0x20000,0,none,0,0x0,	SC_MAELSTROM,Maelstrom
 2303,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0,0x0,	SC_BLOODYLUST,Bloody Lust
 2304,0,6,4,-1,0,0,3,1,no,0,0,0,weapon,0,0x0,	SC_FEINTBOMB,Feint Bomb
 

+ 13 - 13
db/pre-re/skill_unit_db.txt

@@ -3,19 +3,19 @@
 // layout = -1:special, 0:1*1, 1:3*3, 2:5*5, up to 5:11*11
 // target = friend (party +guildmates +neutral players) / party / guild
 //          ally (party +guildmates) / all / enemy
-// flag 0x001(UF_DEFNOTENEMY)		If 'defunit_not_enemy' is set, the target is changed to 'friend'
-//      0x002(UF_NOREITERRATION)	Spell cannot be stacked
-//      0x004(UF_NOFOOTSET)		Spell cannot be cast near/on targets
-//      0x008(UF_NOOVERLAP)		Spell effects do not overlap
-//      0x010(UF_PATHCHECK)	Only cells with a shootable path will be placed
-//      0x020(UF_NOPC)		Spell cannot affect players.
-//      0x040(UF_NOMOB)		Spell cannot affect mobs.
-//      0x080(UF_SKILL)		Spell CAN affect skills.
-//      0x100(UF_DANCE)		Dance skill
-//      0x200(UF_ENSEMBLE)	Ensemble skill
-//      0x400(UF_SONG)		Song skill
-//      0x800(UF_DUALMODE)	Spell has effects both at an interval and when you step in/out
-//	0x2000(UF_RANGEDSINGLEUNIT)	Layout hack, use layout range propriety but only display center.
+// flag 0x0001(UF_DEFNOTENEMY)		If 'defunit_not_enemy' is set, the target is changed to 'friend'
+//      0x0002(UF_NOREITERRATION)	Spell cannot be stacked
+//      0x0004(UF_NOFOOTSET)		Spell cannot be cast near/on targets
+//      0x0008(UF_NOOVERLAP)		Spell effects do not overlap
+//      0x0010(UF_PATHCHECK)		Only cells with a shootable path will be placed
+//      0x0020(UF_NOPC)				Spell cannot affect players.
+//      0x0040(UF_NOMOB)			Spell cannot affect mobs.
+//      0x0080(UF_SKILL)			Spell CAN affect skills.
+//      0x0100(UF_DANCE)			Dance skill
+//      0x0200(UF_ENSEMBLE)			Ensemble skill
+//      0x0400(UF_SONG)				Song skill
+//      0x0800(UF_DUALMODE)			Spell has effects both at an interval and when you step in/out
+//      0x2000(UF_RANGEDSINGLEUNIT)	Layout hack, use layout range propriety but only display center.
 // 	Example: 0x006 = 0x002+0x004 -> Cannot be stacked nor cast near targets
 //
 // Notes:

+ 3 - 2
db/re/skill_db.txt

@@ -40,6 +40,7 @@
 //    0x04000- chorus skill
 //    0x08000- spell that ignore bg reduction
 //    0x10000- spell that ignore gvg reduction
+//    0x20000- makes 'self'/'place' skill cannot be casted/placed when near NPC (see 'db/skill_nonearnpc_db.txt' for more options)
 // 13 maxcount: max amount of skill instances to place on the ground when
 //    player_land_skill_limit/monster_land_skill_limit is enabled. For skills
 //    that attack using a path, this is the path length to be used.
@@ -1049,8 +1050,8 @@
 2298,3,6,1,0,0x1,0,5,1,yes,0,0,0,weapon,0,0x20,	SC_STRIPACCESSARY,Strip Accessory //CHECK Is weapon attack type needed?
 2299,7,6,2,0,0x1,0,3,1,yes,0,0,3,none,0,0x0,	SC_MANHOLE,Man Hole
 2300,7,6,2,0,0x1,0,3,1,yes,0,0,1,none,0,0x0,	SC_DIMENSIONDOOR,Dimension Door
-2301,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0,0x0,	SC_CHAOSPANIC,Chaos Panic
-2302,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0,0x0,	SC_MAELSTROM,Maelstrom
+2301,7,6,2,0,0x1,0,3,1,yes,0,0x20000,0,none,0,0x0,	SC_CHAOSPANIC,Chaos Panic
+2302,7,6,2,0,0x1,0,3,1,yes,0,0x20000,0,none,0,0x0,	SC_MAELSTROM,Maelstrom
 2303,7,6,2,0,0x1,3,3,1,yes,0,0,1,none,0,0x0,	SC_BLOODYLUST,Bloody Lust
 2304,0,6,4,-1,0,0,3,1,no,0,0,0,weapon,0,0x0,	SC_FEINTBOMB,Feint Bomb
 

+ 13 - 13
db/re/skill_unit_db.txt

@@ -3,19 +3,19 @@
 // layout = -1:special, 0:1*1, 1:3*3, 2:5*5, up to 5:11*11
 // target = friend (party +guildmates +neutral players) / party / guild
 //          ally (party +guildmates) / all / enemy
-// flag 0x001(UF_DEFNOTENEMY)		If 'defunit_not_enemy' is set, the target is changed to 'friend'
-//      0x002(UF_NOREITERRATION)	Spell cannot be stacked
-//      0x004(UF_NOFOOTSET)		Spell cannot be cast near/on targets
-//      0x008(UF_NOOVERLAP)		Spell effects do not overlap
-//      0x010(UF_PATHCHECK)	Only cells with a shootable path will be placed
-//      0x020(UF_NOPC)		Spell cannot affect players.
-//      0x040(UF_NOMOB)		Spell cannot affect mobs.
-//      0x080(UF_SKILL)		Spell CAN affect skills.
-//      0x100(UF_DANCE)		Dance skill
-//      0x200(UF_ENSEMBLE)	Ensemble skill
-//      0x400(UF_SONG)		Song skill
-//      0x800(UF_DUALMODE)	Spell has effects both at an interval and when you step in/out
-//	0x2000(UF_RANGEDSINGLEUNIT)	Layout hack, use layout range propriety but only display center.
+// flag 0x0001(UF_DEFNOTENEMY)		If 'defunit_not_enemy' is set, the target is changed to 'friend'
+//      0x0002(UF_NOREITERRATION)	Spell cannot be stacked
+//      0x0004(UF_NOFOOTSET)		Spell cannot be cast near/on targets
+//      0x0008(UF_NOOVERLAP)		Spell effects do not overlap
+//      0x0010(UF_PATHCHECK)		Only cells with a shootable path will be placed
+//      0x0020(UF_NOPC)				Spell cannot affect players.
+//      0x0040(UF_NOMOB)			Spell cannot affect mobs.
+//      0x0080(UF_SKILL)			Spell CAN affect skills.
+//      0x0100(UF_DANCE)			Dance skill
+//      0x0200(UF_ENSEMBLE)			Ensemble skill
+//      0x0400(UF_SONG)				Song skill
+//      0x0800(UF_DUALMODE)			Spell has effects both at an interval and when you step in/out
+//      0x2000(UF_RANGEDSINGLEUNIT)	Layout hack, use layout range propriety but only display center.
 // 	Example: 0x006 = 0x002+0x004 -> Cannot be stacked nor cast near targets
 //
 // Notes:

+ 24 - 0
db/skill_nonearnpc_db.txt

@@ -0,0 +1,24 @@
+// Database of Additional Range and NPC Type that used by INF2_NO_NEARNPC
+// <skill_name>,<additional_range>{,<npc_type>}
+// ====================================================
+// additional_range: If this value is 0, splash range value will be used from skill_db,
+//      or if it is 0, range+layout's range from skill_unit_db. Otherwise, the range
+//      will be added.
+// npc_type (bitmask): 1 = warp portal, 2 = shop NPC, 4 = normal NPC script, 8 = tomb
+// ====================================================
+// Example:
+//MG_SAFETYWALL,2
+// MG_SAFETYWALL can't be placed if the ground's target is near from NPC by 2 cells
+// (MG_SAFETYWALL doesn't have splash, layout range, and range value, so must add the
+// 'additional_range', or it will be pointless)
+//
+//GS_DESPERADO,2
+// GS_DESPERADO can't be casted if the caster is standing near from NPC within range
+// 5 cells. (Why? GS_DESPERADO has 3 cells of splash range +2 'additional_range' here)
+//
+//SC_CHAOSPANIC,0,1
+// SC_CHAOSPANIC can't be placed on the ground that near the warp portal with range 2
+// cells. (Because SC_CHAOSPANIC doens't have splash range, it uses layout range)
+
+SC_CHAOSPANIC,0,1
+SC_MAELSTROM,0,1

+ 6 - 6
src/map/clif.c

@@ -10783,7 +10783,7 @@ static void clif_parse_UseSkillToId_homun(struct homun_data *hd, struct map_sess
 
 	if( !hd )
 		return;
-	if( skillnotok_hom(skill_id, hd) )
+	if( skill_isNotOk_hom(skill_id, hd) )
 		return;
 	if( hd->bl.id != target_id && skill_get_inf(skill_id)&INF_SELF_SKILL )
 		target_id = hd->bl.id;
@@ -10806,7 +10806,7 @@ static void clif_parse_UseSkillToPos_homun(struct homun_data *hd, struct map_ses
 	int lv;
 	if( !hd )
 		return;
-	if( skillnotok_hom(skill_id, hd) )
+	if( skill_isNotOk_hom(skill_id, hd) )
 		return;
 	if( hd->ud.skilltimer != INVALID_TIMER ) {
 		if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return;
@@ -10828,7 +10828,7 @@ static void clif_parse_UseSkillToId_mercenary(struct mercenary_data *md, struct
 
 	if( !md )
 		return;
-	if( skillnotok_mercenary(skill_id, md) )
+	if( skill_isNotOk_mercenary(skill_id, md) )
 		return;
 	if( md->bl.id != target_id && skill_get_inf(skill_id)&INF_SELF_SKILL )
 		target_id = md->bl.id;
@@ -10851,7 +10851,7 @@ static void clif_parse_UseSkillToPos_mercenary(struct mercenary_data *md, struct
 	int lv;
 	if( !md )
 		return;
-	if( skillnotok_mercenary(skill_id, md) )
+	if( skill_isNotOk_mercenary(skill_id, md) )
 		return;
 	if( md->ud.skilltimer != INVALID_TIMER )
 		return;
@@ -10920,7 +10920,7 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
 	if( pc_issit(sd) )
 		return;
 
-	if( skillnotok(skill_id, sd) )
+	if( skill_isNotOk(skill_id, sd) )
 		return;
 
 	if( sd->bl.id != target_id && tmp&INF_SELF_SKILL )
@@ -11002,7 +11002,7 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uin
 	//Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
 	sd->idletime = last_tick;
 
-	if( skillnotok(skill_id, sd) )
+	if( skill_isNotOk(skill_id, sd) )
 		return;
 	if( skillmoreinfo != -1 ) {
 		if( pc_issit(sd) ) {

+ 1 - 1
src/map/elemental.c

@@ -562,7 +562,7 @@ int elemental_skillnotok(uint16 skill_id, struct elemental_data *ed) {
 	if (idx == 0)
 		return 1; // invalid skill id
 
-	return skillnotok(skill_id, ed->master);
+	return skill_isNotOk(skill_id,ed->master);
 }
 
 struct skill_condition elemental_skill_get_requirements(uint16 skill_id, uint16 skill_lv){

+ 15 - 2
src/map/npc.c

@@ -118,8 +118,21 @@ struct view_data* npc_get_viewdata(int class_)
 	return NULL;
 }
 
-static int npc_isnear_sub(struct block_list* bl, va_list args) {
+int npc_isnear_sub(struct block_list* bl, va_list args) {
     struct npc_data *nd = (struct npc_data*)bl;
+	int skill_id = va_arg(args, int);
+	uint16 idx = -1;
+
+	//Check the NPC type if is used by INF2_NO_NEARNPC or UF_NONEARNPC [Cydh]
+	if (skill_id && (idx = skill_get_index(skill_id)) && skill_db[idx].unit_nonearnpc_type) {
+		while (1) {
+			if (skill_db[idx].unit_nonearnpc_type&1 && nd->subtype == WARP) break;
+			if (skill_db[idx].unit_nonearnpc_type&2 && nd->subtype == SHOP) break;
+			if (skill_db[idx].unit_nonearnpc_type&4 && nd->subtype == SCRIPT) break;
+			if (skill_db[idx].unit_nonearnpc_type&8 && nd->subtype == TOMB) break;
+				return 0;
+		}
+	}
 
     if( nd->sc.option & (OPTION_HIDE|OPTION_INVISIBLE) )
         return 0;
@@ -130,7 +143,7 @@ static int npc_isnear_sub(struct block_list* bl, va_list args) {
 bool npc_isnear(struct block_list * bl) {
 
     if( battle_config.min_npc_vendchat_distance > 0 &&
-            map_foreachinrange(npc_isnear_sub,bl, battle_config.min_npc_vendchat_distance, BL_NPC) )
+            map_foreachinrange(npc_isnear_sub,bl, battle_config.min_npc_vendchat_distance, BL_NPC, 0) )
         return true;
 
     return false;

+ 1 - 0
src/map/npc.h

@@ -136,6 +136,7 @@ int npc_enable(const char* name, int flag);
 void npc_setdisplayname(struct npc_data* nd, const char* newname);
 void npc_setclass(struct npc_data* nd, short class_);
 struct npc_data* npc_name2id(const char* name);
+int npc_isnear_sub(struct block_list* bl, va_list args);
 bool npc_isnear(struct block_list * bl);
 
 int npc_get_new_npc_id(void);

+ 188 - 142
src/map/skill.c

@@ -121,7 +121,7 @@ int earthstrain_unit_pos;
 //early declaration
 int skill_block_check(struct block_list *bl, enum sc_type type, uint16 skill_id);
 static int skill_check_unit_range (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv);
-static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv);
+static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv, bool isNearNPC);
 static int skill_destroy_trap( struct block_list *bl, va_list ap );
 static int skill_check_condition_mob_master_sub (struct block_list *bl, va_list ap);
 //Since only mob-casted splash skills can hit ice-walls
@@ -483,42 +483,43 @@ static short skill_isCopyable (struct map_session_data *sd, uint16 skill_id, str
 
 // [MouseJstr] - skill ok to cast? and when?
 //done before check_condition_begin, requirement
-int skillnotok (uint16 skill_id, struct map_session_data *sd)
+bool skill_isNotOk(uint16 skill_id, struct map_session_data *sd)
 {
 	int16 idx,m;
-	nullpo_retr (1, sd);
+	nullpo_retr(1,sd);
 	m = sd->bl.m;
 	idx = skill_get_index(skill_id);
 
 	if (idx == 0)
-		return 1; // invalid skill id
+		return true; // invalid skill id
 
-	if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL))
-		return 0; // can do any damn thing they want
+	if (pc_has_permission(sd,PC_PERM_SKILL_UNCONDITIONAL))
+		return false; // can do any damn thing they want
 
-	if( skill_id == AL_TELEPORT && sd->skillitem == skill_id && sd->skillitemlv > 2 )
-		return 0; // Teleport lv 3 bypasses this check.[Inkfish]
+	if (skill_id == AL_TELEPORT && sd->skillitem == skill_id && sd->skillitemlv > 2)
+		return false; // Teleport lv 3 bypasses this check.[Inkfish]
 
 	// Epoque:
 	// This code will compare the player's attack motion value which is influenced by ASPD before
 	// allowing a skill to be cast. This is to prevent no-delay ACT files from spamming skills such as
 	// AC_DOUBLE which do not have a skill delay and are not regarded in terms of attack motion.
-	if( !sd->state.autocast && sd->skillitem != skill_id && sd->canskill_tick &&
-		DIFF_TICK(gettick(), sd->canskill_tick) < (sd->battle_status.amotion * (battle_config.skill_amotion_leniency) / 100) )
+	if (!sd->state.autocast && sd->skillitem != skill_id && sd->canskill_tick &&
+		DIFF_TICK(gettick(),sd->canskill_tick) < (sd->battle_status.amotion * (battle_config.skill_amotion_leniency) / 100))
 	{// attempted to cast a skill before the attack motion has finished
-		return 1;
+		return true;
 	}
 
-	if (sd->blockskill[idx] > 0){
-		clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
-		return 1;
+	if (sd->blockskill[idx] > 0) {
+		clif_skill_fail(sd,skill_id,USESKILL_FAIL_SKILLINTERVAL,0);
+		return true;
 	}
+
 	/**
 	 * It has been confirmed on a official server (thanks to Yommy) that item-cast skills bypass all the restrictions above
 	 * Also, without this check, an exploit where an item casting + healing (or any other kind buff) isn't deleted after used on a restricted map
 	 **/
 	if( sd->skillitem == skill_id )
-		return 0;
+		return false;
 	// Check skill restrictions [Celest]
 	if( (!map_flag_vs(m) && skill_get_nocast (skill_id) & 1) ||
 		(map[m].flag.pvp && skill_get_nocast (skill_id) & 2) ||
@@ -526,11 +527,11 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd)
 		(map[m].flag.battleground && skill_get_nocast (skill_id) & 8) ||
 		(map[m].flag.restricted && map[m].zone && skill_get_nocast (skill_id) & (8*map[m].zone)) ){
 			clif_msg(sd, 0x536); // This skill cannot be used within this area
-			return 1;
+			return true;
 	}
 
 	if( sd->sc.option&OPTION_MOUNTING )
-		return 1;//You can't use skills while in the new mounts (The client doesn't let you, this is to make cheat-safe)
+		return true;//You can't use skills while in the new mounts (The client doesn't let you, this is to make cheat-safe)
 
 	switch (skill_id) {
 		case AL_WARP:
@@ -539,24 +540,24 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd)
 		case ECLAGE_RECALL:
 			if(map[m].flag.nowarp) {
 				clif_skill_teleportmessage(sd,0);
-				return 1;
+				return true;
 			}
-			return 0;
+			return false;
 		case AL_TELEPORT:
 		case SC_FATALMENACE:
 		case SC_DIMENSIONDOOR:
 		case ALL_ODINS_RECALL:
 			if(map[m].flag.noteleport) {
 				clif_skill_teleportmessage(sd,0);
-				return 1;
+				return true;
 			}
-			return 0; // gonna be checked in 'skill_castend_nodamage_id'
+			return false; // gonna be checked in 'skill_castend_nodamage_id'
 		case WE_CALLPARTNER:
 		case WE_CALLPARENT:
 		case WE_CALLBABY:
 			if (map[m].flag.nomemo) {
 				clif_skill_teleportmessage(sd,1);
-				return 1;
+				return true;
 			}
 			break;
 		case MC_VENDING:
@@ -564,12 +565,12 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd)
 			if( map[sd->bl.m].flag.novending ) {
 				clif_displaymessage (sd->fd, msg_txt(sd,276)); // "You can't open a shop on this map"
 				clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
-				return 1;
+				return true;
 			}
 			if( map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNOVENDING) ) {
 				clif_displaymessage (sd->fd, msg_txt(sd,204)); // "You can't open a shop on this cell."
 				clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
-				return 1;
+				return true;
 			}
 			if( npc_isnear(&sd->bl) ) {
 				// uncomment to send msg_txt.
@@ -577,21 +578,21 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd)
 				//sprintf(output, msg_txt(662), battle_config.min_npc_vendchat_distance);
 				//clif_displaymessage(sd->fd, output);
 				clif_skill_fail(sd,skill_id,USESKILL_FAIL_THERE_ARE_NPC_AROUND,0);
-				return 1;
+				return true;
 			}
 		case MC_IDENTIFY:
-			return 0; // always allowed
+			return false; // always allowed
 		case WZ_ICEWALL:
 			// noicewall flag [Valaris]
 			if (map[m].flag.noicewall) {
 				clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
-				return 1;
+				return true;
 			}
 			break;
 		case GC_DARKILLUSION:
 			if( map_flag_gvg(m) ) {
 				clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
-				return 1;
+				return true;
 			}
 			break;
 		case GD_EMERGENCYCALL:
@@ -602,7 +603,7 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd)
 				(battle_config.emergency_call&16 && map[m].flag.nowarpto && !map[m].flag.gvg_castle)
 			)	{
 				clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
-				return 1;
+				return true;
 			}
 			break;
 		case BS_GREED:
@@ -619,7 +620,7 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd)
 			 **/
 			if( pc_ismadogear(sd) ) {
 				clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
-				return 1;
+				return true;
 			}
 			break;
 
@@ -630,7 +631,7 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd)
 		case WM_SATURDAY_NIGHT_FEVER:
 			if( !map_flag_vs(m) ) {
 				clif_skill_teleportmessage(sd,2); // This skill uses this msg instead of skill fails.
-				return 1;
+				return true;
 			}
 			break;
 
@@ -638,64 +639,91 @@ int skillnotok (uint16 skill_id, struct map_session_data *sd)
 	return (map[m].flag.noskill);
 }
 
-int skillnotok_hom(uint16 skill_id, struct homun_data *hd)
+bool skill_isNotOk_hom(uint16 skill_id, struct homun_data *hd)
 {
 	uint16 idx = skill_get_index(skill_id);
 	nullpo_retr(1,hd);
 
 	if (idx == 0)
-		return 1; // invalid skill id
+		return true; // invalid skill id
 
 	if (hd->blockskill[idx] > 0)
-		return 1;
-	switch(skill_id){
-	case MH_LIGHT_OF_REGENE: //must be cordial
-		if(hd->homunculus.intimacy <= 750) return 1;
-		break;
-	case MH_OVERED_BOOST: //if we starving
-		if(hd->homunculus.hunger <= 1) return 1;
-		break;
-	case MH_GOLDENE_FERSE: //cant be used with angriff
-		if(hd->sc.data[SC_ANGRIFFS_MODUS]) return 1;
-		break;
-	case MH_ANGRIFFS_MODUS:
-		if(hd->sc.data[SC_GOLDENE_FERSE]) return 1;
-		break;
-	case MH_TINDER_BREAKER: //must be in grappling mode
-		if(!(hd->sc.data[SC_STYLE_CHANGE] && hd->sc.data[SC_STYLE_CHANGE]->val1 == MH_MD_GRAPPLING)) return 1;
-		break;
-	case MH_SONIC_CRAW: //must be in fighting mode
-		if(!(hd->sc.data[SC_STYLE_CHANGE] && hd->sc.data[SC_STYLE_CHANGE]->val1 == MH_MD_FIGHTING)) return 1;
-		break;
-	case MH_SILVERVEIN_RUSH:
-		if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_SONIC_CRAW)) return 1;
-		break;
-	case MH_MIDNIGHT_FRENZY:
-		if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_SILVERVEIN_RUSH)) return 1;
-		break;
-	case MH_CBC:
-		if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_TINDER_BREAKER)) return 1;
-		break;
-	case MH_EQC:
-		if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_CBC)) return 1;
-		break;
+		return true;
+
+	switch(skill_id) {
+		case MH_LIGHT_OF_REGENE: //must be cordial
+			if(hd->homunculus.intimacy <= 750) return true;
+			break;
+		case MH_OVERED_BOOST: //if we starving
+			if(hd->homunculus.hunger <= 1) return true;
+			break;
+		case MH_GOLDENE_FERSE: //cant be used with angriff
+			if(hd->sc.data[SC_ANGRIFFS_MODUS]) return true;
+			break;
+		case MH_ANGRIFFS_MODUS:
+			if(hd->sc.data[SC_GOLDENE_FERSE]) return true;
+			break;
+		case MH_TINDER_BREAKER: //must be in grappling mode
+			if(!(hd->sc.data[SC_STYLE_CHANGE] && hd->sc.data[SC_STYLE_CHANGE]->val1 == MH_MD_GRAPPLING)) return true;
+			break;
+		case MH_SONIC_CRAW: //must be in fighting mode
+			if(!(hd->sc.data[SC_STYLE_CHANGE] && hd->sc.data[SC_STYLE_CHANGE]->val1 == MH_MD_FIGHTING)) return true;
+			break;
+		case MH_SILVERVEIN_RUSH:
+			if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_SONIC_CRAW)) return true;
+			break;
+		case MH_MIDNIGHT_FRENZY:
+			if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_SILVERVEIN_RUSH)) return true;
+			break;
+		case MH_CBC:
+			if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_TINDER_BREAKER)) return true;
+			break;
+		case MH_EQC:
+			if(!(hd->sc.data[SC_COMBO] && hd->sc.data[SC_COMBO]->val1 == MH_CBC)) return true;
+			break;
 	}
 
 	//Use master's criteria.
-	return skillnotok(skill_id, hd->master);
+	return skill_isNotOk(skill_id, hd->master);
 }
 
-int skillnotok_mercenary(uint16 skill_id, struct mercenary_data *md)
+bool skill_isNotOk_mercenary(uint16 skill_id, struct mercenary_data *md)
 {
 	uint16 idx = skill_get_index(skill_id);
 	nullpo_retr(1,md);
 
 	if( idx == 0 )
-		return 1; // Invalid Skill ID
+		return true; // Invalid Skill ID
 	if( md->blockskill[idx] > 0 )
-		return 1;
+		return true;
+
+	return skill_isNotOk(skill_id, md->master);
+}
+
+/// Check if the skill can be casted near NPC or not [Cydh]
+/// NOTE: 'target' may be NULL if the skill is targetting ground/area
+bool skill_isNotOk_npcRange(struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, int pos_x, int pos_y) {
+	int inf;
 
-	return skillnotok(skill_id, md->master);
+	if (!src || skill_get_index(skill_id) < 0)
+		return false;
+
+	if (src->type == BL_PC && pc_has_permission(BL_CAST(BL_PC,src),PC_PERM_SKILL_UNCONDITIONAL))
+		return false;
+	
+	inf = skill_get_inf(skill_id);
+	//if self skill
+	if (inf&INF_SELF_SKILL) {
+		pos_x = src->x;
+		pos_y = src->y;
+	}
+
+	if (pos_x <= 0 || pos_y <= 0) {
+		pos_x = src->x;
+		pos_y = src->y;
+	}
+
+	return skill_check_unit_range2(src,pos_x,pos_y,skill_id,skill_lv,true);
 }
 
 struct s_skill_unit_layout* skill_get_unit_layout (uint16 skill_id, uint16 skill_lv, struct block_list* src, int x, int y) {
@@ -1524,7 +1552,7 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, uint
 	{
 		struct block_list *tbl;
 		struct unit_data *ud;
-		int i, skill_lv, type, notok;
+		int i, skill_lv, type;
 
 		for (i = 0; i < ARRAYLENGTH(sd->autospell) && sd->autospell[i].id; i++) {
 
@@ -1536,15 +1564,14 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, uint
 			skill = (sd->autospell[i].id > 0) ? sd->autospell[i].id : -sd->autospell[i].id;
 
 			sd->state.autocast = 1;
-			notok = skillnotok(skill, sd);
 			sd->state.autocast = 0;
 
-			if ( notok )
-				continue;
-
 			skill_lv = sd->autospell[i].lv?sd->autospell[i].lv:1;
 			if (skill_lv < 0) skill_lv = 1+rnd()%(-skill_lv);
 
+			if ( skill_isNotOk(skill, sd) )
+				continue;
+
 			rate = (!sd->state.arrow_atk) ? sd->autospell[i].rate : sd->autospell[i].rate / 2;
 
 			if (rnd()%1000 >= rate)
@@ -1557,15 +1584,13 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, uint
 				if( !(BL_PC&battle_config.skill_reiteration) &&
 					skill_get_unit_flag(skill)&UF_NOREITERATION &&
 					skill_check_unit_range(src,tbl->x,tbl->y,skill,skill_lv)
-				  ) {
+				  )
 					continue;
-				}
 				if( BL_PC&battle_config.skill_nofootset &&
 					skill_get_unit_flag(skill)&UF_NOFOOTSET &&
-					skill_check_unit_range2(src,tbl->x,tbl->y,skill,skill_lv)
-				  ) {
+					skill_check_unit_range2(src,tbl->x,tbl->y,skill,skill_lv,false)
+				  )
 					continue;
-				}
 				if( BL_PC&battle_config.land_skill_limit &&
 					(maxcount = skill_get_maxcount(skill, skill_lv)) > 0
 				  ) {
@@ -1574,9 +1599,8 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, uint
 						if(sd->ud.skillunit[v]->skill_id == skill)
 							maxcount--;
 					}
-					if( maxcount == 0 ) {
+					if( maxcount == 0 )
 						continue;
-					}
 				}
 			}
 			if( battle_config.autospell_check_range &&
@@ -1660,7 +1684,7 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, uint
 }
 
 int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint16 skill_id, unsigned int tick) {
-	int skill, skill_lv, i, type, notok;
+	int skill, skill_lv, i, type;
 	struct block_list *tbl;
 
 	if( sd == NULL || !skill_id )
@@ -1676,12 +1700,11 @@ int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint1
 		skill = (sd->autospell3[i].id > 0) ? sd->autospell3[i].id : -sd->autospell3[i].id;
 
 		sd->state.autocast = 1;
-		notok = skillnotok(skill, sd);
 		sd->state.autocast = 0;
 
-		if ( notok )
+		if ( skill_isNotOk(skill, sd) )
 			continue;
-
+		
 		skill_lv = sd->autospell3[i].lv ? sd->autospell3[i].lv : 1;
 		if( skill_lv < 0 ) skill_lv = 1 + rnd()%(-skill_lv);
 
@@ -1689,23 +1712,20 @@ int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint1
 			continue; // No target
 		if( rnd()%1000 >= sd->autospell3[i].rate )
 			continue;
-
+		
 		tbl = (sd->autospell3[i].id < 0) ? &sd->bl : bl;
-
 		if( (type = skill_get_casttype(skill)) == CAST_GROUND ) {
 			int maxcount = 0;
 			if( !(BL_PC&battle_config.skill_reiteration) &&
 				skill_get_unit_flag(skill)&UF_NOREITERATION &&
 				skill_check_unit_range(&sd->bl,tbl->x,tbl->y,skill,skill_lv)
-			  ) {
+			  )
 				continue;
-			}
 			if( BL_PC&battle_config.skill_nofootset &&
 				skill_get_unit_flag(skill)&UF_NOFOOTSET &&
-				skill_check_unit_range2(&sd->bl,tbl->x,tbl->y,skill,skill_lv)
-			  ) {
+				skill_check_unit_range2(&sd->bl,tbl->x,tbl->y,skill,skill_lv,false)
+			  )
 				continue;
-			}
 			if( BL_PC&battle_config.land_skill_limit &&
 				(maxcount = skill_get_maxcount(skill, skill_lv)) > 0
 			  ) {
@@ -1714,9 +1734,8 @@ int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint1
 					if(sd->ud.skillunit[v]->skill_id == skill)
 						maxcount--;
 				}
-				if( maxcount == 0 ) {
+				if( maxcount == 0 )
 					continue;
-				}
 			}
 		}
 		if( battle_config.autospell_check_range &&
@@ -1868,7 +1887,7 @@ int skill_counter_additional_effect (struct block_list* src, struct block_list *
 	{
 		struct block_list *tbl;
 		struct unit_data *ud;
-		int i, skill_id, skill_lv, rate, type, notok;
+		int i, skill_id, skill_lv, rate, type;
 
 		for (i = 0; i < ARRAYLENGTH(dstsd->autospell2) && dstsd->autospell2[i].id; i++) {
 
@@ -1886,31 +1905,27 @@ int skill_counter_additional_effect (struct block_list* src, struct block_list *
 				 rate>>=1;
 
 			dstsd->state.autocast = 1;
-			notok = skillnotok(skill_id, dstsd);
 			dstsd->state.autocast = 0;
 
-			if ( notok )
+			if ( skill_isNotOk(skill_id, dstsd) )
 				continue;
 
 			if (rnd()%1000 >= rate)
 				continue;
 
 			tbl = (dstsd->autospell2[i].id < 0) ? bl : src;
-
 			if( (type = skill_get_casttype(skill_id)) == CAST_GROUND ) {
 				int maxcount = 0;
 				if( !(BL_PC&battle_config.skill_reiteration) &&
 					skill_get_unit_flag(skill_id)&UF_NOREITERATION &&
 					skill_check_unit_range(bl,tbl->x,tbl->y,skill_id,skill_lv)
-				  ) {
+				  )
 					continue;
-				}
 				if( BL_PC&battle_config.skill_nofootset &&
 					skill_get_unit_flag(skill_id)&UF_NOFOOTSET &&
-					skill_check_unit_range2(bl,tbl->x,tbl->y,skill_id,skill_lv)
-				  ) {
+					skill_check_unit_range2(bl,tbl->x,tbl->y,skill_id,skill_lv,false)
+				  )
 					continue;
-				}
 				if( BL_PC&battle_config.land_skill_limit &&
 					(maxcount = skill_get_maxcount(skill_id, skill_lv)) > 0
 				  ) {
@@ -3027,36 +3042,53 @@ static int skill_check_unit_range2_sub (struct block_list *bl, va_list ap)
 	return 1;
 }
 
-static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv)
+//NOTE: 'isNearNPC' is used to check is the skill near NPC or not, if yes will use npc_isnear and range calculation [Cydh]
+static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv, bool isNearNPC)
 {
-	int range, type;
+	int range = 0, type;
 
-	switch (skill_id) {	// to be expanded later
-	case WZ_ICEWALL:
-		range = 2;
-		break;
-	default:
-		{
-			int layout_type = skill_get_unit_layout_type(skill_id,skill_lv);
-			if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) {
-				ShowError("skill_check_unit_range2: unsupported layout type %d for skill %d\n",layout_type,skill_id);
-				return 0;
-			}
-			range = skill_get_unit_range(skill_id,skill_lv) + layout_type;
+	//Range for INF2_NO_NEARNPC is using skill splash value [Cydh]
+	if (isNearNPC)
+		range = skill_get_splash(skill_id,skill_lv);
+
+	//While checking INF2_NO_NEARNPC and the range from splash is 0, get the range from skill_unit range and layout. [Cydh]
+	if (!isNearNPC || !range) {
+		switch (skill_id) {	// to be expanded later
+			case WZ_ICEWALL:
+				range = 2;
+				break;
+			default:
+				{
+					int layout_type = skill_get_unit_layout_type(skill_id,skill_lv);
+					if (layout_type == -1 || layout_type > MAX_SQUARE_LAYOUT) {
+						ShowError("skill_check_unit_range2: unsupported layout type %d for skill %d\n",layout_type,skill_id);
+						return 0;
+					}
+					range = skill_get_unit_range(skill_id,skill_lv) + layout_type;
+				}
+				break;
 		}
-		break;
 	}
 
-	// if the caster is a monster/NPC, only check for players
-	// otherwise just check characters
-	if (bl->type == BL_PC)
-		type = BL_CHAR;
+	//Check the additional range [Cydh]
+	if (isNearNPC && skill_db[skill_get_index(skill_id)].unit_nonearnpc_range)
+		range += skill_db[skill_get_index(skill_id)].unit_nonearnpc_range;
+
+	if (!isNearNPC) { //Doesn't check the NPC range
+		//If the caster is a monster/NPC, only check for players. Otherwise just check characters
+		if (bl->type == BL_PC)
+			type = BL_CHAR;
+		else
+			type = BL_PC;
+	}
 	else
-		type = BL_PC;
+		type = BL_NPC;
 
-	return map_foreachinarea(skill_check_unit_range2_sub, bl->m,
-		x - range, y - range, x + range, y + range,
-		type, skill_id);
+	return (!isNearNPC) ?
+		//!isNearNPC is used for UF_NOFOOTSET, regardless the NPC position, only check the BL_CHAR or BL_PC
+		map_foreachinarea(skill_check_unit_range2_sub,bl->m,x - range,y - range,x + range,y + range,type,skill_id):
+		//isNearNPC is used to check range from NPC
+		map_foreachinarea(npc_isnear_sub,bl->m,x - range,y - range,x + range,y + range,type,skill_id);
 }
 
 int skill_guildaura_sub (struct map_session_data* sd, int id, int strvit, int agidex)
@@ -9778,7 +9810,7 @@ int skill_castend_pos(int tid, unsigned int tick, int id, intptr_t data)
 		}
 		if( src->type&battle_config.skill_nofootset &&
 			skill_get_unit_flag(ud->skill_id)&UF_NOFOOTSET &&
-			skill_check_unit_range2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv)
+			skill_check_unit_range2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv,false)
 		  )
 		{
 			if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
@@ -13129,7 +13161,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id
 			}
 		case GD_EMERGENCYCALL:
 		case GD_ITEMEMERGENCYCALL:
-			// other checks were already done in skillnotok()
+			// other checks were already done in skill_isNotOk()
 			if (!sd->status.guild_id || !sd->state.gmaster_flag)
 				return 0;
 			break;
@@ -18252,16 +18284,16 @@ static bool skill_parse_row_magicmushroomdb(char* split[], int column, int curre
 	return true;
 }
 
-static bool skill_parse_row_reproducedb(char* split[], int column, int current) {
+static bool skill_parse_row_copyabledb(char* split[], int column, int current) {
 	uint16 skill_id = skill_name2id(split[0]), idx;
 	uint8 option;
 
 	if (!skill_get_index(skill_id)) {
-		ShowError("skill_parse_row_reproducedb: Invalid skill %s\n",split[0]);
+		ShowError("skill_parse_row_copyabledb: Invalid skill '%s'\n",split[0]);
 		return false;
 	}
 	if (!(option = atoi(split[1]))) {
-		ShowError("skill_parse_row_reproducedb: Invalid option %d\n",option);
+		ShowError("skill_parse_row_copyabledb: Invalid option %d\n",option);
 		return false;
 	}
 	idx = skill_get_index(skill_id);
@@ -18277,6 +18309,19 @@ static bool skill_parse_row_reproducedb(char* split[], int column, int current)
 	return true;
 }
 
+/// Reads additional range [Cydh]
+static bool skill_parse_row_nonearnpcrangedb(char* split[], int column, int current) {
+	uint16 skill_id = skill_name2id(split[0]), idx;
+	if ((idx = skill_get_index(skill_id)) < 0) { // invalid skill id
+		ShowError("skill_parse_row_nonearnpcrangedb: Invalid skill '%s'\n",split[0]);
+		return false;
+	}
+
+	skill_db[idx].unit_nonearnpc_range = max(atoi(split[1]),0);
+	skill_db[idx].unit_nonearnpc_type = (atoi(split[2])) ? cap_value(atoi(split[2]),1,15) : 15;
+	return true;
+}
+
 
 static bool skill_parse_row_abradb(char* split[], int columns, int current)
 {// skill_id,DummyName,RatePerLvl
@@ -18387,9 +18432,9 @@ static void skill_readdb(void)
 	sv_readdb(db_path, DBPATH"skill_db.txt"          , ',',  18, 18, MAX_SKILL_DB, skill_parse_row_skilldb);
 	sv_readdb(db_path, DBPATH"skill_require_db.txt"  , ',',  33, 33, MAX_SKILL_DB, skill_parse_row_requiredb);
 #ifdef RENEWAL_CAST
-	sv_readdb(db_path, "re/skill_cast_db.txt"     , ',',   8,  8, MAX_SKILL_DB, skill_parse_row_castdb);
+	sv_readdb(db_path, "re/skill_cast_db.txt"        , ',',   8,  8, MAX_SKILL_DB, skill_parse_row_castdb);
 #else
-	sv_readdb(db_path, "pre-re/skill_cast_db.txt"     , ',',   7,  7, MAX_SKILL_DB, skill_parse_row_castdb);
+	sv_readdb(db_path, "pre-re/skill_cast_db.txt"    , ',',   7,  7, MAX_SKILL_DB, skill_parse_row_castdb);
 #endif
 	sv_readdb(db_path, DBPATH"skill_castnodex_db.txt", ',',   2,  3, MAX_SKILL_DB, skill_parse_row_castnodexdb);
 	sv_readdb(db_path, DBPATH"skill_unit_db.txt"     , ',',   8,  8, MAX_SKILL_DB, skill_parse_row_unitdb);
@@ -18397,18 +18442,19 @@ static void skill_readdb(void)
 	sv_readdb(db_path, DBPATH"skill_nocast_db.txt"   , ',',   2,  2, MAX_SKILL_DB, skill_parse_row_nocastdb);
 
 	skill_init_unit_layout();
-	sv_readdb(db_path, "produce_db.txt"        , ',',   4,  4+2*MAX_PRODUCE_RESOURCE, MAX_SKILL_PRODUCE_DB, skill_parse_row_producedb);
-	sv_readdb(db_path, "create_arrow_db.txt"   , ',', 1+2,  1+2*MAX_ARROW_RESOURCE, MAX_SKILL_ARROW_DB, skill_parse_row_createarrowdb);
-	sv_readdb(db_path, "abra_db.txt"           , ',',   3,  3, MAX_SKILL_ABRA_DB, skill_parse_row_abradb);
+	sv_readdb(db_path, "produce_db.txt"              , ',',   4,  4+2*MAX_PRODUCE_RESOURCE, MAX_SKILL_PRODUCE_DB, skill_parse_row_producedb);
+	sv_readdb(db_path, "create_arrow_db.txt"         , ',', 1+2,  1+2*MAX_ARROW_RESOURCE, MAX_SKILL_ARROW_DB, skill_parse_row_createarrowdb);
+	sv_readdb(db_path, "abra_db.txt"                 , ',',   3,  3, MAX_SKILL_ABRA_DB, skill_parse_row_abradb);
 	//Warlock
-	sv_readdb(db_path, "spellbook_db.txt"      , ',',   3,  3, MAX_SKILL_SPELLBOOK_DB, skill_parse_row_spellbookdb);
+	sv_readdb(db_path, "spellbook_db.txt"            , ',',   3,  3, MAX_SKILL_SPELLBOOK_DB, skill_parse_row_spellbookdb);
 	//Guillotine Cross
-	sv_readdb(db_path, "magicmushroom_db.txt"  , ',',   1,  1, MAX_SKILL_MAGICMUSHROOM_DB, skill_parse_row_magicmushroomdb);
-	sv_readdb(db_path, "skill_copyable_db.txt", ',',    2,  4, MAX_SKILL_DB, skill_parse_row_reproducedb);
+	sv_readdb(db_path, "magicmushroom_db.txt"        , ',',   1,  1, MAX_SKILL_MAGICMUSHROOM_DB, skill_parse_row_magicmushroomdb);
+	sv_readdb(db_path, "skill_copyable_db.txt"       , ',',    2,  4, MAX_SKILL_DB, skill_parse_row_copyabledb);
 	sv_readdb(db_path, "skill_improvise_db.txt"      , ',',   2,  2, MAX_SKILL_IMPROVISE_DB, skill_parse_row_improvisedb);
-	sv_readdb(db_path, "skill_changematerial_db.txt"      , ',',   4,  4+2*5, MAX_SKILL_PRODUCE_DB, skill_parse_row_changematerialdb);
+	sv_readdb(db_path, "skill_changematerial_db.txt" , ',',   4,  4+2*5, MAX_SKILL_PRODUCE_DB, skill_parse_row_changematerialdb);
+	sv_readdb(db_path, "skill_nonearnpc_db.txt"      , ',',   2,  3, MAX_SKILL_DB, skill_parse_row_nonearnpcrangedb);
 #ifdef ADJUST_SKILL_DAMAGE
-	sv_readdb(db_path, "skill_damage_db.txt"      , ',',   4,  7, MAX_SKILL_DB, skill_parse_row_skilldamage);
+	sv_readdb(db_path, "skill_damage_db.txt"         , ',',   4,  7, MAX_SKILL_DB, skill_parse_row_skilldamage);
 #endif
 }
 

+ 8 - 4
src/map/skill.h

@@ -69,6 +69,7 @@ enum e_skill_inf2 {
 	INF2_CHORUS_SKILL	= 0x04000, // Chorus skill
 	INF2_NO_BG_DMG		= 0x08000, // spell that ignore bg reduction
 	INF2_NO_GVG_DMG		= 0x10000, // spell that ignore gvg reduction
+	INF2_NO_NEARNPC     = 0x20000, // disable cast skill if near with NPC [Cydh]
 };
 
 /// Skill info type 3
@@ -163,6 +164,8 @@ struct s_skill_db {
 	int unit_interval;
 	int unit_target;
 	int unit_flag;
+	uint8 unit_nonearnpc_range;	//additional range for UF_NONEARNPC or INF2_NO_NEARNPC [Cydh]
+	uint8 unit_nonearnpc_type;	//type of NPC [Cydh]
 #ifdef ADJUST_SKILL_DAMAGE
 	struct s_skill_damage damage;
 #endif
@@ -361,7 +364,6 @@ int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int li
 int skill_clear_unitgroup(struct block_list *src);
 int skill_clear_group(struct block_list *bl, int flag);
 void ext_skill_unit_onplace(struct skill_unit *src, struct block_list *bl, unsigned int tick);
-
 int64 skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl,int64 damage,unsigned int tick);
 
 int skill_castfix( struct block_list *bl, uint16 skill_id, uint16 skill_lv);
@@ -408,9 +410,11 @@ bool skill_check_cloaking(struct block_list *bl, struct status_change_entry *sce
 
 // Abnormal status
 int skill_enchant_elemental_end(struct block_list *bl, int type);
-int skillnotok(uint16 skill_id, struct map_session_data *sd);
-int skillnotok_hom(uint16 skill_id, struct homun_data *hd);
-int skillnotok_mercenary(uint16 skill_id, struct mercenary_data *md);
+bool skill_isNotOk(uint16 skill_id, struct map_session_data *sd);
+bool skill_isNotOk_hom(uint16 skill_id, struct homun_data *hd);
+bool skill_isNotOk_mercenary(uint16 skill_id, struct mercenary_data *md);
+
+bool skill_isNotOk_npcRange(struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, int pos_x, int pos_y);
 
 int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap);
 

+ 174 - 170
src/map/unit.c

@@ -1100,7 +1100,8 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 
 	//temp: used to signal combo-skills right now.
 	if (sc && sc->data[SC_COMBO] && (sc->data[SC_COMBO]->val1 == skill_id ||
-		(sd?skill_check_condition_castbegin(sd,skill_id,skill_lv):0) )) {
+		(sd?skill_check_condition_castbegin(sd,skill_id,skill_lv):0) ))
+	{
 		if (sc->data[SC_COMBO]->val2)
 			target_id = sc->data[SC_COMBO]->val2;
 		else
@@ -1109,8 +1110,8 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 		if( skill_get_inf(skill_id)&INF_SELF_SKILL && skill_get_nk(skill_id)&NK_NO_DAMAGE )// exploit fix
 			target_id = src->id;
 		combo = 1;
-	} else
-	if ( target_id == src->id &&
+	}
+	else if ( target_id == src->id &&
 		skill_get_inf(skill_id)&INF_SELF_SKILL &&
 		skill_get_inf2(skill_id)&INF2_NO_TARGET_SELF )
 	{
@@ -1120,53 +1121,51 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 
 	if (sd) {
 		//Target_id checking.
-		if(skillnotok(skill_id, sd)) // [MouseJstr]
+		if(skill_isNotOk(skill_id, sd)) // [MouseJstr]
 			return 0;
 
-		switch(skill_id)
-		{	//Check for skills that auto-select target
-		case MO_CHAINCOMBO:
-			if (sc && sc->data[SC_BLADESTOP]){
-				if ((target=map_id2bl(sc->data[SC_BLADESTOP]->val4)) == NULL)
+		switch(skill_id) {	//Check for skills that auto-select target
+			case MO_CHAINCOMBO:
+				if (sc && sc->data[SC_BLADESTOP]) {
+					if ((target=map_id2bl(sc->data[SC_BLADESTOP]->val4)) == NULL)
+						return 0;
+				}
+				break;
+			case WE_MALE:
+			case WE_FEMALE:
+				if (!sd->status.partner_id)
 					return 0;
-			}
-			break;
-		case WE_MALE:
-		case WE_FEMALE:
-			if (!sd->status.partner_id)
-				return 0;
-			target = (struct block_list*)map_charid2sd(sd->status.partner_id);
-			if (!target) {
-				clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
-				return 0;
-			}
-			break;
+				target = (struct block_list*)map_charid2sd(sd->status.partner_id);
+				if (!target) {
+					clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+					return 0;
+				}
+				break;
 		}
 		if (target)
 			target_id = target->id;
 	}
 	else if (src->type==BL_HOM)
-	switch(skill_id)
-	{ //Homun-auto-target skills.
-		case HLIF_HEAL:
-		case HLIF_AVOID:
-		case HAMI_DEFENCE:
-		case HAMI_CASTLE:
-			target = battle_get_master(src);
-			if (!target) return 0;
-			target_id = target->id;
-			break;
-		case MH_SONIC_CRAW:
-		case MH_TINDER_BREAKER: {
-			int skill_id2 = ((skill_id==MH_SONIC_CRAW)?MH_MIDNIGHT_FRENZY:MH_EQC);
-			if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id2){ //it,s a combo
-				target_id = sc->data[SC_COMBO]->val2;
-				combo = 1;
-				casttime = -1;
+		switch(skill_id) { //Homun-auto-target skills.
+			case HLIF_HEAL:
+			case HLIF_AVOID:
+			case HAMI_DEFENCE:
+			case HAMI_CASTLE:
+				target = battle_get_master(src);
+				if (!target) return 0;
+				target_id = target->id;
+				break;
+			case MH_SONIC_CRAW:
+			case MH_TINDER_BREAKER: {
+				int skill_id2 = ((skill_id==MH_SONIC_CRAW)?MH_MIDNIGHT_FRENZY:MH_EQC);
+				if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id2){ //it,s a combo
+					target_id = sc->data[SC_COMBO]->val2;
+					combo = 1;
+					casttime = -1;
+				}
+				break;
 			}
-			break;
 		}
-	}
 
 	if( !target ) // choose default target
 		target = map_id2bl(target_id);
@@ -1187,44 +1186,50 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 	if(!status_check_skilluse(src, target, skill_id, 0))
 		return 0;
 
+	//fail if the targetted skill is near NPC [Cydh]
+	if(skill_get_inf2(skill_id)&INF2_NO_NEARNPC && skill_isNotOk_npcRange(src,target,skill_id,skill_lv,target->x,target->y)) {
+		if (sd)
+			clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+		return 0;
+	}
+
 	tstatus = status_get_status_data(target);
 	// Record the status of the previous skill)
 	if(sd) {
-		switch(skill_id){
-		case SA_CASTCANCEL:
-			if(ud->skill_id != skill_id){
-				sd->skill_id_old = ud->skill_id;
-				sd->skill_lv_old = ud->skill_lv;
-			}
-			break;
-		case BD_ENCORE:
-			//Prevent using the dance skill if you no longer have the skill in your tree.
-			if(!sd->skill_id_dance || pc_checkskill(sd,sd->skill_id_dance)<=0){
-				clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
-				return 0;
-			}
-			sd->skill_id_old = skill_id;
-			break;
-		case WL_WHITEIMPRISON:
-			if( battle_check_target(src,target,BCT_SELF|BCT_ENEMY) < 0 ) {
-				clif_skill_fail(sd,skill_id,USESKILL_FAIL_TOTARGET,0);
-				return 0;
-			}
-			break;
-		case MG_FIREBOLT:
-		case MG_LIGHTNINGBOLT:
-		case MG_COLDBOLT:
-			sd->skill_id_old = skill_id;
-			sd->skill_lv_old = skill_lv;
-			break;
+		switch(skill_id) {
+			case SA_CASTCANCEL:
+				if(ud->skill_id != skill_id) {
+					sd->skill_id_old = ud->skill_id;
+					sd->skill_lv_old = ud->skill_lv;
+				}
+				break;
+			case BD_ENCORE:
+				//Prevent using the dance skill if you no longer have the skill in your tree.
+				if(!sd->skill_id_dance || pc_checkskill(sd,sd->skill_id_dance)<=0) {
+					clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+					return 0;
+				}
+				sd->skill_id_old = skill_id;
+				break;
+			case WL_WHITEIMPRISON:
+				if( battle_check_target(src,target,BCT_SELF|BCT_ENEMY) < 0 ) {
+					clif_skill_fail(sd,skill_id,USESKILL_FAIL_TOTARGET,0);
+					return 0;
+				}
+				break;
+			case MG_FIREBOLT:
+			case MG_LIGHTNINGBOLT:
+			case MG_COLDBOLT:
+				sd->skill_id_old = skill_id;
+				sd->skill_lv_old = skill_lv;
+				break;
 		}
 		if (!skill_check_condition_castbegin(sd, skill_id, skill_lv))
 			return 0;
 	}
 
 	if( src->type == BL_MOB )
-		switch( skill_id )
-		{
+		switch( skill_id ) {
 			case NPC_SUMMONSLAVE:
 			case NPC_SUMMONMONSTER:
 			case AL_TELEPORT:
@@ -1261,79 +1266,79 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 	//temp: Used to signal force cast now.
 	combo = 0;
 
-	switch(skill_id){
-	case ALL_RESURRECTION:
-		if(battle_check_undead(tstatus->race,tstatus->def_ele)) {
+	switch(skill_id) {
+		case ALL_RESURRECTION:
+			if(battle_check_undead(tstatus->race,tstatus->def_ele)) {
+				combo = 1;
+			} else if (!status_isdead(target))
+				return 0; //Can't cast on non-dead characters.
+		break;
+		case MO_FINGEROFFENSIVE:
+			if(sd)
+				casttime += casttime * min(skill_lv, sd->spiritball);
+		break;
+		case MO_EXTREMITYFIST:
+			if (sc && sc->data[SC_COMBO] &&
+			   (sc->data[SC_COMBO]->val1 == MO_COMBOFINISH ||
+				sc->data[SC_COMBO]->val1 == CH_TIGERFIST ||
+				sc->data[SC_COMBO]->val1 == CH_CHAINCRUSH))
+				casttime = -1;
+			combo = 1;
+		break;
+		case SR_GATEOFHELL:
+		case SR_TIGERCANNON:
+			if (sc && sc->data[SC_COMBO] &&
+			   sc->data[SC_COMBO]->val1 == SR_FALLENEMPIRE)
+				casttime = -1;
+			combo = 1;
+		break;
+		case SA_SPELLBREAKER:
 			combo = 1;
-		} else if (!status_isdead(target))
-			return 0; //Can't cast on non-dead characters.
-	break;
-	case MO_FINGEROFFENSIVE:
-		if(sd)
-			casttime += casttime * min(skill_lv, sd->spiritball);
-	break;
-	case MO_EXTREMITYFIST:
-		if (sc && sc->data[SC_COMBO] &&
-		   (sc->data[SC_COMBO]->val1 == MO_COMBOFINISH ||
-			sc->data[SC_COMBO]->val1 == CH_TIGERFIST ||
-			sc->data[SC_COMBO]->val1 == CH_CHAINCRUSH))
-			casttime = -1;
-		combo = 1;
-	break;
-	case SR_GATEOFHELL:
-	case SR_TIGERCANNON:
-		if (sc && sc->data[SC_COMBO] &&
-		   sc->data[SC_COMBO]->val1 == SR_FALLENEMPIRE)
-			casttime = -1;
-		combo = 1;
-	break;
-	case SA_SPELLBREAKER:
-		combo = 1;
-	break;
-	case ST_CHASEWALK:
-		if (sc && sc->data[SC_CHASEWALK])
-			casttime = -1;
-	break;
-	case TK_RUN:
-		if (sc && sc->data[SC_RUN])
-			casttime = -1;
-	break;
-	case HP_BASILICA:
-		if( sc && sc->data[SC_BASILICA] )
-			casttime = -1; // No Casting time on basilica cancel
-	break;
-	case KN_CHARGEATK:
-		{
-		unsigned int k = (distance_bl(src,target)-1)/3; //+100% every 3 cells of distance
-		if( k > 2 ) k = 2; // ...but hard-limited to 300%.
-		casttime += casttime * k;
-		}
-	break;
-	case GD_EMERGENCYCALL: //Emergency Call double cast when the user has learned Leap [Daegaladh]
-		if( sd && pc_checkskill(sd,TK_HIGHJUMP) )
-			casttime *= 2;
 		break;
-	case RA_WUGDASH:
-		if (sc && sc->data[SC_WUGDASH])
-			casttime = -1;
+		case ST_CHASEWALK:
+			if (sc && sc->data[SC_CHASEWALK])
+				casttime = -1;
 		break;
-	case EL_WIND_SLASH:
-	case EL_HURRICANE:
-	case EL_TYPOON_MIS:
-	case EL_STONE_HAMMER:
-	case EL_ROCK_CRUSHER:
-	case EL_STONE_RAIN:
-	case EL_ICE_NEEDLE:
-	case EL_WATER_SCREW:
-	case EL_TIDAL_WEAPON:
-		if( src->type == BL_ELEM ){
-			sd = BL_CAST(BL_PC, battle_get_master(src));
-			if( sd && sd->skill_id_old == SO_EL_ACTION ){
+		case TK_RUN:
+			if (sc && sc->data[SC_RUN])
 				casttime = -1;
-				sd->skill_id_old = 0;
+		break;
+		case HP_BASILICA:
+			if( sc && sc->data[SC_BASILICA] )
+				casttime = -1; // No Casting time on basilica cancel
+		break;
+		case KN_CHARGEATK:
+			{
+			unsigned int k = (distance_bl(src,target)-1)/3; //+100% every 3 cells of distance
+			if( k > 2 ) k = 2; // ...but hard-limited to 300%.
+			casttime += casttime * k;
 			}
-		}
 		break;
+		case GD_EMERGENCYCALL: //Emergency Call double cast when the user has learned Leap [Daegaladh]
+			if( sd && pc_checkskill(sd,TK_HIGHJUMP) )
+				casttime *= 2;
+			break;
+		case RA_WUGDASH:
+			if (sc && sc->data[SC_WUGDASH])
+				casttime = -1;
+			break;
+		case EL_WIND_SLASH:
+		case EL_HURRICANE:
+		case EL_TYPOON_MIS:
+		case EL_STONE_HAMMER:
+		case EL_ROCK_CRUSHER:
+		case EL_STONE_RAIN:
+		case EL_ICE_NEEDLE:
+		case EL_WATER_SCREW:
+		case EL_TIDAL_WEAPON:
+			if( src->type == BL_ELEM ){
+				sd = BL_CAST(BL_PC, battle_get_master(src));
+				if( sd && sd->skill_id_old == SO_EL_ACTION ){
+					casttime = -1;
+					sd->skill_id_old = 0;
+				}
+			}
+			break;
 	}
 
 	// moved here to prevent Suffragium from ending if skill fails
@@ -1344,40 +1349,37 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 	casttime = skill_vfcastfix(src, casttime, skill_id, skill_lv);
 #endif
 
-	if (src->type == BL_NPC) { // NPC-objects do not have cast time
+	if (src->type == BL_NPC) // NPC-objects do not have cast time
 		casttime = 0;
-	}
 
 	if(!ud->state.running) //need TK_RUN or WUGDASH handler to be done before that, see bugreport:6026
 		unit_stop_walking(src,1);// eventhough this is not how official works but this will do the trick. bugreport:6829
 	// in official this is triggered even if no cast time.
 	clif_skillcasting(src, src->id, target_id, 0,0, skill_id, skill_get_ele(skill_id, skill_lv), casttime);
-	if( casttime > 0 || combo )
-	{
-		if (sd && target->type == BL_MOB)
-		{
+	if( casttime > 0 || combo ) {
+		if (sd && target->type == BL_MOB) {
 			TBL_MOB *md = (TBL_MOB*)target;
 			mobskill_event(md, src, tick, -1); //Cast targetted skill event.
 			if (tstatus->mode&(MD_CASTSENSOR_IDLE|MD_CASTSENSOR_CHASE) &&
 				battle_check_target(target, src, BCT_ENEMY) > 0)
 			{
 				switch (md->state.skillstate) {
-				case MSS_RUSH:
-				case MSS_FOLLOW:
-					if (!(tstatus->mode&MD_CASTSENSOR_CHASE))
+					case MSS_RUSH:
+					case MSS_FOLLOW:
+						if (!(tstatus->mode&MD_CASTSENSOR_CHASE))
+							break;
+						md->target_id = src->id;
+						md->state.aggressive = (tstatus->mode&MD_ANGRY)?1:0;
+						md->min_chase = md->db->range3;
 						break;
-					md->target_id = src->id;
-					md->state.aggressive = (tstatus->mode&MD_ANGRY)?1:0;
-					md->min_chase = md->db->range3;
-					break;
-				case MSS_IDLE:
-				case MSS_WALK:
-					if (!(tstatus->mode&MD_CASTSENSOR_IDLE))
+					case MSS_IDLE:
+					case MSS_WALK:
+						if (!(tstatus->mode&MD_CASTSENSOR_IDLE))
+							break;
+						md->target_id = src->id;
+						md->state.aggressive = (tstatus->mode&MD_ANGRY)?1:0;
+						md->min_chase = md->db->range3;
 						break;
-					md->target_id = src->id;
-					md->state.aggressive = (tstatus->mode&MD_ANGRY)?1:0;
-					md->min_chase = md->db->range3;
-					break;
 				}
 			}
 		}
@@ -1388,13 +1390,11 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 
 	if( !sd || sd->skillitem != skill_id || skill_get_cast(skill_id,skill_lv) )
 		ud->canact_tick = tick + casttime + 100;
-	if( sd )
-	{
-		switch( skill_id )
-		{
-		case CG_ARROWVULCAN:
-			sd->canequip_tick = tick + casttime;
-			break;
+	if( sd ) {
+		switch( skill_id ) {
+			case CG_ARROWVULCAN:
+				sd->canequip_tick = tick + casttime;
+				break;
 		}
 	}
 	ud->skilltarget  = target_id;
@@ -1464,9 +1464,8 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
 	if (sc && !sc->count)
 		sc = NULL;
 
-	if( sd )
-	{
-		if( skillnotok(skill_id, sd) || !skill_check_condition_castbegin(sd, skill_id, skill_lv) )
+	if( sd ) {
+		if( skill_isNotOk(skill_id, sd) || !skill_check_condition_castbegin(sd, skill_id, skill_lv) )
 			return 0;
 		/**
 		 * "WHY IS IT HEREE": pneuma cannot be cancelled past this point, the client displays the animation even,
@@ -1486,8 +1485,14 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
 	if (!status_check_skilluse(src, NULL, skill_id, 0))
 		return 0;
 
-	if( map_getcell(src->m, skill_x, skill_y, CELL_CHKWALL) )
-	{// can't cast ground targeted spells on wall cells
+	//fail if the targetted skill is near NPC [Cydh]
+	if(skill_get_inf2(skill_id)&INF2_NO_NEARNPC && skill_isNotOk_npcRange(src,NULL,skill_id,skill_lv,skill_x,skill_y)) {
+		if (sd)
+			clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+		return 0;
+	}
+
+	if( map_getcell(src->m, skill_x, skill_y, CELL_CHKWALL) ) {// can't cast ground targeted spells on wall cells
 		if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
 		return 0;
 	}
@@ -1520,9 +1525,8 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
 	casttime = skill_vfcastfix(src, casttime, skill_id, skill_lv );
 #endif
 
-	if (src->type == BL_NPC) { // NPC-objects do not have cast time
+	if (src->type == BL_NPC) // NPC-objects do not have cast time
 		casttime = 0;
-	}
 
 	ud->state.skillcastcancel = castcancel&&casttime>0?1:0;
 	if( !sd || sd->skillitem != skill_id || skill_get_cast(skill_id,skill_lv) )