diff options
-rw-r--r-- | conf/battle/skill.conf | 8 | ||||
-rw-r--r-- | src/map/battle.c | 5 | ||||
-rw-r--r-- | src/map/battle.h | 1 | ||||
-rw-r--r-- | src/map/pc.c | 1 | ||||
-rw-r--r-- | src/map/pc.h | 1 | ||||
-rw-r--r-- | src/map/skill.c | 43 |
6 files changed, 49 insertions, 10 deletions
diff --git a/conf/battle/skill.conf b/conf/battle/skill.conf index 960960692..657e3c41f 100644 --- a/conf/battle/skill.conf +++ b/conf/battle/skill.conf @@ -52,6 +52,14 @@ no_skill_delay: 2 // At what dex does the cast time become zero (instacast)? castrate_dex_scale: 150 +// What level of leniency should the skill system give for skills when +// accounting attack motion (ASPD) for casting skills (Note 2, between 0 and 100) +// +// NOTE: Setting this to 100% may cause some issues with valid skills not being cast. +// The time difference between client and server varies so allowing 90% leniency +// should be enough to forgive very small margins of error. +skill_amotion_leniency: 90 + // Will normal attacks be able to ignore the delay after skills? (Note 1) skill_delay_attack_enable: yes diff --git a/src/map/battle.c b/src/map/battle.c index 0df68a572..b1232b488 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -4590,8 +4590,9 @@ static const struct _battle_data { /** * rAthena **/ - { "max_third_parameter", &battle_config.max_third_parameter, 20, 0, INT_MAX, }, - { "atcommand_max_stat_bypass", &battle_config.atcommand_max_stat_bypass, 0, 0, 100, }, + { "max_third_parameter", &battle_config.max_third_parameter, 20, 0, INT_MAX, }, + { "atcommand_max_stat_bypass", &battle_config.atcommand_max_stat_bypass, 0, 0, 100, }, + { "skill_amotion_leniency", &battle_config.skill_amotion_leniency, 90, 0, 100 }, }; diff --git a/src/map/battle.h b/src/map/battle.h index 41ff70850..b53b794c3 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -116,6 +116,7 @@ extern struct Battle_Config int left_cardfix_to_right; int skill_add_range; int skill_out_range_consume; + int skill_amotion_leniency; int skillrange_by_distance; //[Skotlex] int use_weapon_skill_range; //[Skotlex] int pc_damage_delay_rate; diff --git a/src/map/pc.c b/src/map/pc.c index 214cf81a7..2d27f194e 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -844,6 +844,7 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim sd->canusecashfood_tick = tick; sd->canequip_tick = tick; sd->cantalk_tick = tick; + sd->canskill_tick = tick; sd->cansendmail_tick = tick; for(i = 0; i < MAX_SKILL_LEVEL; i++) diff --git a/src/map/pc.h b/src/map/pc.h index 39e38bc34..af749534d 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -208,6 +208,7 @@ struct map_session_data { unsigned int canusecashfood_tick; unsigned int canequip_tick; // [Inkfish] unsigned int cantalk_tick; + unsigned int canskill_tick; // used to prevent abuse from no-delay ACT files unsigned int cansendmail_tick; // [Mail System Flood Protection] unsigned int ks_floodprotect_tick; // [Kill Steal Protection] diff --git a/src/map/skill.c b/src/map/skill.c index 973e38bf7..137af795f 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -433,6 +433,15 @@ int skillnotok (int skillid, struct map_session_data *sd) if( skillid == AL_TELEPORT && sd->skillitem == skillid && sd->skillitemlv > 2 ) return 0; // 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->canskill_tick && DIFF_TICK(gettick(), sd->canskill_tick) < (sd->battle_status.amotion * (100 + battle_config.skill_amotion_leniency) / 100) ) + {// attempted to cast a skill before the attack motion has finished + return 1; + } + if (sd->blockskill[i] > 0) return 1; @@ -3832,15 +3841,21 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int } map_freeblock_unlock(); - + if( sd && !(flag&1) ) - { - if( sd->state.arrow_atk ) //Consume arrow on last invocation to this skill. + {// ensure that the skill last-cast tick is recorded + sd->canskill_tick = gettick(); + + if( sd->state.arrow_atk ) + {// consume arrow on last invocation to this skill. battle_consume_ammo(sd, skillid, skilllv); + } + + // perform auto-cast routines and skill requirement consumption skill_onskillusage(sd, bl, skillid, tick); skill_consume_requirement(sd,skillid,skilllv,2); } - + return 0; } @@ -7090,9 +7105,15 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in } if( sd && !(flag&1) ) - { - if( sd->state.arrow_atk ) //Consume arrow on last invocation to this skill. + {// ensure that the skill last-cast tick is recorded + sd->canskill_tick = gettick(); + + if( sd->state.arrow_atk ) + {// consume arrow on last invocation to this skill. battle_consume_ammo(sd, skillid, skilllv); + } + + // perform auto-cast routines and skill requirement consumption skill_onskillusage(sd, bl, skillid, tick); skill_consume_requirement(sd,skillid,skilllv,2); } @@ -8078,9 +8099,15 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk status_change_end(src, SC_MAGICPOWER, INVALID_TIMER); if( sd ) - { - if( sd->state.arrow_atk && !(flag&1) ) //Consume arrow if a ground skill was not invoked. [Skotlex] + {// ensure that the skill last-cast tick is recorded + sd->canskill_tick = gettick(); + + if( sd->state.arrow_atk && !(flag&1) ) + {// consume arrow if this is a ground skill battle_consume_ammo(sd, skillid, skilllv); + } + + // perform auto-cast routines and skill requirement consumption skill_onskillusage(sd, NULL, skillid, tick); skill_consume_requirement(sd,skillid,skilllv,2); } |