From 659cc5757490aa705ae3ee5f8284044d665f62e8 Mon Sep 17 00:00:00 2001 From: Cahyadi Ramadhan Togihon Date: Thu, 26 Sep 2013 17:39:41 +0700 Subject: [PATCH] * 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 --- db/pre-re/skill_db.txt | 39 ++-- db/pre-re/skill_unit_db.txt | 26 +-- db/re/skill_db.txt | 5 +- db/re/skill_unit_db.txt | 26 +-- db/skill_nonearnpc_db.txt | 24 +++ src/map/clif.c | 12 +- src/map/elemental.c | 2 +- src/map/npc.c | 17 +- src/map/npc.h | 1 + src/map/skill.c | 332 +++++++++++++++++++--------------- src/map/skill.h | 12 +- src/map/unit.c | 348 ++++++++++++++++++------------------ 12 files changed, 470 insertions(+), 374 deletions(-) create mode 100644 db/skill_nonearnpc_db.txt diff --git a/db/pre-re/skill_db.txt b/db/pre-re/skill_db.txt index f43ff51d5c..b124ea36fb 100644 --- a/db/pre-re/skill_db.txt +++ b/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 diff --git a/db/pre-re/skill_unit_db.txt b/db/pre-re/skill_unit_db.txt index b71dde9a72..5f75ed930e 100644 --- a/db/pre-re/skill_unit_db.txt +++ b/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: diff --git a/db/re/skill_db.txt b/db/re/skill_db.txt index 36fbaeb7e0..5d00943f91 100644 --- a/db/re/skill_db.txt +++ b/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 diff --git a/db/re/skill_unit_db.txt b/db/re/skill_unit_db.txt index ccf5c0baae..97ea0f3f4a 100644 --- a/db/re/skill_unit_db.txt +++ b/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: diff --git a/db/skill_nonearnpc_db.txt b/db/skill_nonearnpc_db.txt new file mode 100644 index 0000000000..3c10b5285a --- /dev/null +++ b/db/skill_nonearnpc_db.txt @@ -0,0 +1,24 @@ +// Database of Additional Range and NPC Type that used by INF2_NO_NEARNPC +// ,{,} +// ==================================================== +// 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 diff --git a/src/map/clif.c b/src/map/clif.c index dcc391aac3..2597b61785 100644 --- a/src/map/clif.c +++ b/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) ) { diff --git a/src/map/elemental.c b/src/map/elemental.c index 99af929d3a..33950d7892 100644 --- a/src/map/elemental.c +++ b/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){ diff --git a/src/map/npc.c b/src/map/npc.c index cf0ea92ec6..9a009a0044 100644 --- a/src/map/npc.c +++ b/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; diff --git a/src/map/npc.h b/src/map/npc.h index 323ce3fdba..ab9d084899 100644 --- a/src/map/npc.h +++ b/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); diff --git a/src/map/skill.c b/src/map/skill.c index c26884176c..986bc17a5f 100644 --- a/src/map/skill.c +++ b/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 skillnotok(skill_id, md->master); + 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; + + 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; - else - type = BL_PC; + //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; - return map_foreachinarea(skill_check_unit_range2_sub, bl->m, - x - range, y - range, x + range, y + range, - type, skill_id); + 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_NPC; + + 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 } diff --git a/src/map/skill.h b/src/map/skill.h index b9acbe1b93..8b7d195ad5 100644 --- a/src/map/skill.h +++ b/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); diff --git a/src/map/unit.c b/src/map/unit.c index 4afdb51d18..5323996058 100644 --- a/src/map/unit.c +++ b/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)) { - 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; + 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 RA_WUGDASH: - if (sc && sc->data[SC_WUGDASH]) - casttime = -1; + case MO_FINGEROFFENSIVE: + if(sd) + casttime += casttime * min(skill_lv, sd->spiritball); 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 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; - sd->skill_id_old = 0; - } - } + 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; + 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) )