aMotion delay is now set at cast begin (fixes #944)
* When casting a spell with cast time shorter than aMotion, the delay will now be set to aMotion or min_skill_delay_limit * aMotion and min_skill_delay_limit are no longer applied at cast end nor by auto spells * Aftercast delay can only increase the delay and never decrease it * When there is no aftercast delay, the client will no longer show a non-existent 10 second delay * Removed "double cast" code of Jupitel and Waterball, it's no longer needed as it fits into the official delay system * Added a constant SECURITY_CASTTIME, which is added at cast begin and subtracted at cast end to prevent hackers to send in skill packets before the timer triggers cast end * Set skill_amotion_leniency to 0 by default as it applies delay at cast end and would consequently block the official system from working; you can set it back to 90 or even 140 for increased security
This commit is contained in:
parent
f264885411
commit
a63b031958
@ -20,7 +20,7 @@ delay_rate: 100
|
||||
delay_dependon_dex: no
|
||||
delay_dependon_agi: no
|
||||
|
||||
// Minimum allowed delay for ANY skills after casting (in milliseconds) (Note 1)
|
||||
// Minimum allowed delay for ANY skills after castbegin (in milliseconds) (Note 1)
|
||||
// Note: Setting this to anything above 0 can stop speedhacks.
|
||||
min_skill_delay_limit: 100
|
||||
|
||||
@ -30,8 +30,8 @@ min_skill_delay_limit: 100
|
||||
// appear to "teleport" afterwards.
|
||||
default_walk_delay: 300
|
||||
|
||||
//Completely disable skill delay of the following types (Note 3)
|
||||
//NOTE: By default mobs don't have the skill delay as specified in the skill
|
||||
// Completely disable skill delay of the following types (Note 3)
|
||||
// NOTE: By default mobs don't have the skill delay as specified in the skill
|
||||
// database, but follow their own 'reuse' skill delay which is specified on
|
||||
// the mob skill db. When set, the delay for all skills become
|
||||
// min_skill_delay_limit.
|
||||
@ -43,13 +43,12 @@ castrate_dex_scale: 150
|
||||
// How much (dex*2+int) does variable cast turns zero?
|
||||
vcast_stat_scale: 530
|
||||
|
||||
// What level of leniency should the skill system give for skills when
|
||||
// accounting attack motion (ASPD) for casting skills (Note 2, between 0 and 300)
|
||||
//
|
||||
// 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
|
||||
// On official servers, amotion delay is applied at castbegin. There is no amotion delay applied
|
||||
// at castend. Set this to anything above 0 to also apply amotion delay at castend. (Note 2)
|
||||
// NOTE: Setting this will break chaining of skills with cast time but no aftercast delay.
|
||||
// The client-sided delays are different from skill to skill and usually range from 140 to 180.
|
||||
// If you want to be secure, a value between 90 and 140 is recommended.
|
||||
skill_amotion_leniency: 0
|
||||
|
||||
// Will normal attacks be able to ignore the delay after skills? (Note 1)
|
||||
skill_delay_attack_enable: yes
|
||||
|
@ -6991,7 +6991,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
|
||||
}
|
||||
if (rnd()%100 < triple_rate) {
|
||||
//Need to apply canact_tick here because it doesn't go through skill_castend_id
|
||||
sd->ud.canact_tick = tick + skill_delayfix(src, MO_TRIPLEATTACK, skillv);
|
||||
sd->ud.canact_tick = max(tick + skill_delayfix(src, MO_TRIPLEATTACK, skillv), sd->ud.canact_tick);
|
||||
if( skill_attack(BF_WEAPON,src,src,target,MO_TRIPLEATTACK,skillv,tick,0) )
|
||||
return ATK_DEF;
|
||||
return ATK_MISS;
|
||||
@ -7189,7 +7189,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
|
||||
int autospell_tick = skill_delayfix(src, skill_id, skill_lv);
|
||||
|
||||
if (DIFF_TICK(ud->canact_tick, tick + autospell_tick) < 0) {
|
||||
ud->canact_tick = tick + autospell_tick;
|
||||
ud->canact_tick = max(tick + autospell_tick, ud->canact_tick);
|
||||
if (battle_config.display_status_timers && sd)
|
||||
clif_status_change(src, SI_ACTIONDELAY, 1, autospell_tick, 0, 0, 0);
|
||||
}
|
||||
@ -7253,7 +7253,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
|
||||
}
|
||||
sd->state.autocast = 0;
|
||||
|
||||
sd->ud.canact_tick = tick + skill_delayfix(src, r_skill, r_lv);
|
||||
sd->ud.canact_tick = max(tick + skill_delayfix(src, r_skill, r_lv), sd->ud.canact_tick);
|
||||
clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, r_skill, r_lv), 0, 0, 1);
|
||||
}
|
||||
}
|
||||
@ -8098,7 +8098,7 @@ static const struct _battle_data {
|
||||
{ "max_trans_parameter", &battle_config.max_trans_parameter, 99, 10, SHRT_MAX, },
|
||||
{ "max_third_trans_parameter", &battle_config.max_third_trans_parameter, 135, 10, SHRT_MAX, },
|
||||
{ "max_extended_parameter", &battle_config.max_extended_parameter, 125, 10, SHRT_MAX, },
|
||||
{ "skill_amotion_leniency", &battle_config.skill_amotion_leniency, 90, 0, 300 },
|
||||
{ "skill_amotion_leniency", &battle_config.skill_amotion_leniency, 0, 0, 300 },
|
||||
{ "mvp_tomb_enabled", &battle_config.mvp_tomb_enabled, 1, 0, 1 },
|
||||
{ "feature.atcommand_suggestions", &battle_config.atcommand_suggestions_enabled, 0, 0, 1 },
|
||||
{ "min_npc_vendchat_distance", &battle_config.min_npc_vendchat_distance, 3, 0, 100 },
|
||||
|
@ -5868,6 +5868,9 @@ void clif_status_change(struct block_list *bl,int type,int flag,int tick,int val
|
||||
if (type == SI_BLANK) //It shows nothing on the client...
|
||||
return;
|
||||
|
||||
if (type == SI_ACTIONDELAY && tick == 0)
|
||||
return;
|
||||
|
||||
nullpo_retv(bl);
|
||||
|
||||
sd = BL_CAST(BL_PC, bl);
|
||||
|
@ -1920,7 +1920,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1
|
||||
if (ud) {
|
||||
rate = skill_delayfix(src, skill, skill_lv);
|
||||
if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){
|
||||
ud->canact_tick = tick+rate;
|
||||
ud->canact_tick = max(tick + rate, ud->canact_tick);
|
||||
if ( battle_config.display_status_timers )
|
||||
clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0);
|
||||
}
|
||||
@ -2014,7 +2014,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1
|
||||
if (ud) {
|
||||
rate = skill_delayfix(src, skill, autospl_skill_lv);
|
||||
if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){
|
||||
ud->canact_tick = tick+rate;
|
||||
ud->canact_tick = max(tick + rate, ud->canact_tick);
|
||||
if ( battle_config.display_status_timers && sd )
|
||||
clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0);
|
||||
}
|
||||
@ -2348,7 +2348,7 @@ int skill_counter_additional_effect (struct block_list* src, struct block_list *
|
||||
if (ud) {
|
||||
autospl_rate = skill_delayfix(bl, autospl_skill_id, autospl_skill_lv);
|
||||
if (DIFF_TICK(ud->canact_tick, tick + autospl_rate) < 0){
|
||||
ud->canact_tick = tick+autospl_rate;
|
||||
ud->canact_tick = max(tick + autospl_rate, ud->canact_tick);
|
||||
if ( battle_config.display_status_timers && dstsd )
|
||||
clif_status_change(bl, SI_ACTIONDELAY, 1, autospl_rate, 0, 0, 0);
|
||||
}
|
||||
@ -3930,7 +3930,7 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data)
|
||||
// Official behaviour is to hit as long as there is a line of sight, regardless of distance
|
||||
if (skl->type > 0 && !status_isdead(target) && path_search_long(NULL,src->m,src->x,src->y,target->x,target->y,CELL_CHKWALL)) {
|
||||
// Apply canact delay here to prevent hacks (unlimited casting)
|
||||
ud->canact_tick = tick + skill_delayfix(src, skl->skill_id, skl->skill_lv);
|
||||
ud->canact_tick = max(tick + status_get_amotion(src), ud->canact_tick);
|
||||
skill_attack(BF_MAGIC, src, src, target, skl->skill_id, skl->skill_lv, tick, skl->flag);
|
||||
}
|
||||
if (unit && !status_isdead(target) && !status_isdead(src)) {
|
||||
@ -5231,7 +5231,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
|
||||
break;
|
||||
}
|
||||
|
||||
sd->ud.canact_tick = tick + skill_delayfix(src, pres_skill_id, pres_skill_lv);
|
||||
sd->ud.canact_tick = max(tick + skill_delayfix(src, pres_skill_id, pres_skill_lv), sd->ud.canact_tick);
|
||||
clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, pres_skill_id, pres_skill_lv), 0, 0, 0);
|
||||
|
||||
cooldown = pc_get_skillcooldown(sd,pres_skill_id, pres_skill_lv);
|
||||
@ -5721,22 +5721,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
|
||||
|
||||
if( sd && !(flag&1) )
|
||||
{// ensure that the skill last-cast tick is recorded
|
||||
tick = gettick();
|
||||
switch (skill_id) {
|
||||
//These skill don't call skill_attack right away and allow to cast a second spell before the first skill deals damage
|
||||
case WZ_JUPITEL:
|
||||
case WZ_WATERBALL:
|
||||
//Only allow the double-cast trick every 2000ms to prevent hacks
|
||||
if (DIFF_TICK(tick, sd->canskill_tick) > 2000) {
|
||||
sd->ud.canact_tick = tick;
|
||||
sd->canskill_tick = tick-2000+TIMERSKILL_INTERVAL;
|
||||
break;
|
||||
}
|
||||
//Fall through
|
||||
default:
|
||||
sd->canskill_tick = tick;
|
||||
break;
|
||||
}
|
||||
sd->canskill_tick = gettick();
|
||||
|
||||
if( sd->state.arrow_atk )
|
||||
{// consume arrow on last invocation to this skill.
|
||||
@ -10999,8 +10984,8 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
|
||||
if (ud->walktimer != INVALID_TIMER && ud->skill_id != TK_RUN && ud->skill_id != RA_WUGDASH)
|
||||
unit_stop_walking(src,1);
|
||||
|
||||
if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
|
||||
ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv); //Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish]
|
||||
if (!sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id, ud->skill_lv))
|
||||
ud->canact_tick = max(tick + skill_delayfix(src, ud->skill_id, ud->skill_lv), ud->canact_tick - SECURITY_CASTTIME);
|
||||
if (sd) { //Cooldown application
|
||||
int cooldown = pc_get_skillcooldown(sd,ud->skill_id, ud->skill_lv); // Increases/Decreases cooldown of a skill by item/card bonuses.
|
||||
if(cooldown) skill_blockpc_start(sd, ud->skill_id, cooldown);
|
||||
@ -11228,8 +11213,8 @@ int skill_castend_pos(int tid, unsigned int tick, int id, intptr_t data)
|
||||
if (ud->walktimer != INVALID_TIMER)
|
||||
unit_stop_walking(src,1);
|
||||
|
||||
if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
|
||||
ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv);
|
||||
if (!sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id, ud->skill_lv))
|
||||
ud->canact_tick = max(tick + skill_delayfix(src, ud->skill_id, ud->skill_lv), ud->canact_tick - SECURITY_CASTTIME);
|
||||
if (sd) { //Cooldown application
|
||||
int cooldown = pc_get_skillcooldown(sd,ud->skill_id, ud->skill_lv);
|
||||
if(cooldown) skill_blockpc_start(sd, ud->skill_id, cooldown);
|
||||
@ -16233,9 +16218,6 @@ int skill_delayfix(struct block_list *bl, uint16 skill_id, uint16 skill_lv)
|
||||
if (battle_config.delay_rate != 100)
|
||||
time = time * battle_config.delay_rate / 100;
|
||||
|
||||
//min delay
|
||||
time = max(time, status_get_amotion(bl)); // Delay can never be below amotion [Playtester]
|
||||
time = max(time, battle_config.min_skill_delay_limit);
|
||||
//ShowInfo("Delay delayfix = %d\n",time);
|
||||
|
||||
return time;
|
||||
|
@ -104,6 +104,10 @@ enum e_skill_inf3 {
|
||||
/// If you change this, make sure it's an odd value (for icewall block behavior).
|
||||
#define WALK_SKILL_INTERVAL 5
|
||||
|
||||
/// Time that's added to canact delay on castbegin and substracted on castend
|
||||
/// This is to prevent hackers from sending a skill packet after cast but before a timer triggers castend
|
||||
#define SECURITY_CASTTIME 100
|
||||
|
||||
/// Flags passed to skill_attack/skill_area_sub
|
||||
enum e_skill_display {
|
||||
SD_LEVEL = 0x1000, // skill_attack will send -1 instead of skill level (affects display of some skills)
|
||||
|
@ -1858,8 +1858,8 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
|
||||
if( casttime <= 0 )
|
||||
ud->state.skillcastcancel = 0;
|
||||
|
||||
if( !sd || sd->skillitem != skill_id || skill_get_cast(skill_id,skill_lv) )
|
||||
ud->canact_tick = tick + casttime + 100;
|
||||
if (!sd || sd->skillitem != skill_id || skill_get_cast(skill_id, skill_lv))
|
||||
ud->canact_tick = tick + max(casttime, max(status_get_amotion(src), battle_config.min_skill_delay_limit)) + SECURITY_CASTTIME;
|
||||
|
||||
if( sd ) {
|
||||
switch( skill_id ) {
|
||||
@ -2037,8 +2037,8 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
|
||||
casttime = 0;
|
||||
|
||||
ud->state.skillcastcancel = castcancel&&casttime>0?1:0;
|
||||
if( !sd || sd->skillitem != skill_id || skill_get_cast(skill_id,skill_lv) )
|
||||
ud->canact_tick = tick + casttime + 100;
|
||||
if (!sd || sd->skillitem != skill_id || skill_get_cast(skill_id, skill_lv))
|
||||
ud->canact_tick = tick + max(casttime, max(status_get_amotion(src), battle_config.min_skill_delay_limit)) + SECURITY_CASTTIME;
|
||||
|
||||
// if( sd )
|
||||
// {
|
||||
|
Loading…
x
Reference in New Issue
Block a user