diff --git a/conf/battle/monster.conf b/conf/battle/monster.conf index bc877acfe20..805d0f85e41 100644 --- a/conf/battle/monster.conf +++ b/conf/battle/monster.conf @@ -52,14 +52,14 @@ monster_ai: 0 // How often should a monster rethink its chase? // 0: Every 100ms (MIN_MOBTHINKTIME) -// 1: Every cell moved (official) +// 1: Every cell moved // 2: Every 2 cells moved -// 3: Every 3 cells moved (previous setting) +// 3: Every 3 cells moved (official) // x: Every x cells moved // Regardless of this setting, a monster will always rethink its chase if it has // reached its target. Increase this value if you want to make monsters continue // moving after they lost their target (hide, loot picked, etc.). -monster_chase_refresh: 1 +monster_chase_refresh: 3 // Should mobs be able to be warped (add as needed)? // 0: Disable. diff --git a/conf/battle/skill.conf b/conf/battle/skill.conf index 735b3d6243f..1a5a491bf8a 100644 --- a/conf/battle/skill.conf +++ b/conf/battle/skill.conf @@ -279,9 +279,15 @@ invincible.nodamage: no // Default: yes dancing_weaponswitch_fix: yes -// Skill Trap Type (GvG) -// 0: (official) Traps in GvG only make player stop moving after its walk path is complete, and it activates other traps on the way. -// 1: Traps in GvG make player stop moving right when stepping over it. +// Skill Trap Type +// On official servers if a unit is completely immune to knockback, it will still walk to the last target tile before +// stopping when inflicted by a stopping status effect (including traps like Ankle Snare and Spiderweb). All traps on +// the way will be activated. +// This does NOT include being immune to knock back from equip. This bonus only helps against knockback skills. +// 0: (official) +// 1: Stop effects in GvG/WoE make units stop immediately. +// 2: Stop effects make monsters immune to knockback / bosses stop immediately. +// 3: 1+2 skill_trap_type: 0 // Area of Bowling Bash chain reaction diff --git a/db/pre-re/skill_cast_db.txt b/db/pre-re/skill_cast_db.txt index 56b85d4f9ab..5d661622c82 100644 --- a/db/pre-re/skill_cast_db.txt +++ b/db/pre-re/skill_cast_db.txt @@ -199,7 +199,7 @@ //===== Hunter ============================= //-- HT_SKIDTRAP -115,0,0,0,300000:240000:180000:120000:60000,0,0 +115,0,0,0,300000:240000:180000:120000:60000,3000,0 //-- HT_LANDMINE 116,0,0,0,200000:160000:120000:80000:40000,5000,0 //-- HT_ANKLESNARE diff --git a/db/re/skill_cast_db.txt b/db/re/skill_cast_db.txt index dc85de35de3..0140631a655 100644 --- a/db/re/skill_cast_db.txt +++ b/db/re/skill_cast_db.txt @@ -200,7 +200,7 @@ //===== Hunter ============================= //-- HT_SKIDTRAP -115,0,0,0,300000:240000:180000:120000:60000,0,0,0 +115,0,0,0,300000:240000:180000:120000:60000,3000,0,0 //-- HT_LANDMINE 116,0,1000,0,200000:160000:120000:80000:40000,5000,0,1000 //-- HT_ANKLESNARE diff --git a/src/map/battle.c b/src/map/battle.c index 7c4da14e4a4..840a0e6477e 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -7844,7 +7844,7 @@ static const struct _battle_data { { "homunculus_max_level", &battle_config.hom_max_level, 99, 0, MAX_LEVEL, }, { "homunculus_S_max_level", &battle_config.hom_S_max_level, 150, 0, MAX_LEVEL, }, { "mob_size_influence", &battle_config.mob_size_influence, 0, 0, 1, }, - { "skill_trap_type", &battle_config.skill_trap_type, 0, 0, 1, }, + { "skill_trap_type", &battle_config.skill_trap_type, 0, 0, 3, }, { "allow_consume_restricted_item", &battle_config.allow_consume_restricted_item, 1, 0, 1, }, { "allow_equip_restricted_item", &battle_config.allow_equip_restricted_item, 1, 0, 1, }, { "max_walk_path", &battle_config.max_walk_path, 17, 1, MAX_WALKPATH, }, diff --git a/src/map/map.c b/src/map/map.c index 5fc96a1209a..ff94e52d4a5 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -2573,41 +2573,53 @@ int map_check_dir(int s_dir,int t_dir) uint8 map_calc_dir(struct block_list* src, int16 x, int16 y) { uint8 dir = 0; - int dx, dy; nullpo_ret(src); - dx = x-src->x; - dy = y-src->y; + dir = map_calc_dir_xy(src->x, src->y, x, y, unit_getdir(src)); + + return dir; +} + +/*========================================== + * Returns the direction of the given cell, relative to source cell + * Use this if you don't have a block list available to check against + *------------------------------------------*/ +uint8 map_calc_dir_xy(int16 srcx, int16 srcy, int16 x, int16 y, uint8 srcdir) { + uint8 dir = 0; + int dx, dy; + + dx = x-srcx; + dy = y-srcy; if( dx == 0 && dy == 0 ) { // both are standing on the same spot // aegis-style, makes knockback default to the left // athena-style, makes knockback default to behind 'src' - dir = (battle_config.knockback_left ? 6 : unit_getdir(src)); + dir = (battle_config.knockback_left ? 6 : srcdir); } else if( dx >= 0 && dy >=0 ) { // upper-right - if( dx*2 < dy || dx == 0 ) dir = 0; // up - else if( dx > dy*2+1 || dy == 0 ) dir = 6; // right - else dir = 7; // up-right + if( dx >= dy*3 ) dir = 6; // right + else if( dx*3 < dy ) dir = 0; // up + else dir = 7; // up-right } else if( dx >= 0 && dy <= 0 ) { // lower-right - if( dx*2 < -dy || dx == 0 ) dir = 4; // down - else if( dx > -dy*2+1 || dy == 0 ) dir = 6; // right - else dir = 5; // down-right + if( dx >= -dy*3 ) dir = 6; // right + else if( dx*3 < -dy ) dir = 4; // down + else dir = 5; // down-right } else if( dx <= 0 && dy <= 0 ) { // lower-left - if( dx*2 > dy || dx == 0 ) dir = 4; // down - else if( dx < dy*2-1 || dy == 0 ) dir = 2; // left - else dir = 3; // down-left + if( dx*3 >= dy ) dir = 4; // down + else if( dx < dy*3 ) dir = 2; // left + else dir = 3; // down-left } else { // upper-left - if( -dx*2 < dy || dx == 0 ) dir = 0; // up - else if( -dx > dy*2+1 || dy == 0) dir = 2; // left - else dir = 1; // up-left + if( -dx*3 <= dy ) dir = 0; // up + else if( -dx > dy*3 ) dir = 2; // left + else dir = 1; // up-left } return dir; } diff --git a/src/map/map.h b/src/map/map.h index 0101db96d65..a26c36226a5 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -856,7 +856,8 @@ bool mapit_exists(struct s_mapiterator* mapit); #define mapit_geteachiddb() mapit_alloc(MAPIT_NORMAL,BL_ALL) int map_check_dir(int s_dir,int t_dir); -uint8 map_calc_dir( struct block_list *src,int16 x,int16 y); +uint8 map_calc_dir(struct block_list *src,int16 x,int16 y); +uint8 map_calc_dir_xy(int16 srcx, int16 srcy, int16 x, int16 y, uint8 srcdir); int map_random_dir(struct block_list *bl, short *x, short *y); // [Skotlex] int cleanup_sub(struct block_list *bl, va_list ap); diff --git a/src/map/mob.c b/src/map/mob.c index 541fae76c9d..3d5b3a8e669 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -1446,7 +1446,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick) )) { //No valid target if (mob_warpchase(md, tbl)) return true; //Chasing this target. - if(md->ud.walktimer != INVALID_TIMER && md->ud.walkpath.path_pos <= battle_config.mob_chase_refresh) + if(md->ud.walktimer != INVALID_TIMER && (!can_move || md->ud.walkpath.path_pos <= battle_config.mob_chase_refresh)) return true; //Walk at least "mob_chase_refresh" cells before dropping the target mob_unlocktarget(md, tick); //Unlock target tbl = NULL; @@ -1661,6 +1661,10 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick) if(battle_check_range(&md->bl, tbl, md->status.rhw.range)) return true; + //Only update target cell / drop target after having moved at least "mob_chase_refresh" cells + if(md->ud.walktimer != INVALID_TIMER && (!can_move || md->ud.walkpath.path_pos <= battle_config.mob_chase_refresh)) + return true; + //Out of range... if (!(mode&MD_CANMOVE) || (!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0)) { //Can't chase. Immobile and trapped mobs should unlock target and use an idle skill. @@ -1679,10 +1683,6 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick) )) //Current target tile is still within attack range. return true; - //Only update target cell after having moved at least "mob_chase_refresh" cells - if(md->ud.walktimer != INVALID_TIMER && md->ud.walkpath.path_pos <= battle_config.mob_chase_refresh) - return true; - //Follow up if possible. //Hint: Chase skills are handled in the walktobl routine if(!mob_can_reach(md, tbl, md->min_chase, MSS_RUSH) || diff --git a/src/map/skill.c b/src/map/skill.c index 2e86a7c0432..02d59e4a942 100755 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -2381,6 +2381,7 @@ static int skill_area_temp[8]; short skill_blown(struct block_list* src, struct block_list* target, char count, int8 dir, unsigned char flag) { int dx = 0, dy = 0; + int reason = 0, checkflag = 0; nullpo_ret(src); nullpo_ret(target); @@ -2388,34 +2389,23 @@ short skill_blown(struct block_list* src, struct block_list* target, char count, if (!count) return count; // Actual knockback distance is 0. - if (src != target && (map_flag_gvg(target->m) || map[target->m].flag.battleground)) - return ((flag&0x04) ? count : 0); // No knocking back in WoE - - switch (target->type) { - case BL_MOB: { - struct mob_data* md = BL_CAST(BL_MOB, target); - if( md->mob_id == MOBID_EMPERIUM ) - return count; - // Bosses or imune can't be knocked-back - if(src != target && status_get_mode(target)&(MD_KNOCKBACK_IMMUNE|MD_BOSS)) - return ((flag&0x08) ? count : 0); - } - break; - case BL_PC: { - struct map_session_data *sd = BL_CAST(BL_PC, target); - if( sd->sc.data[SC_BASILICA] && sd->sc.data[SC_BASILICA]->val4 == sd->bl.id && !is_boss(src)) - return ((flag&0x20) ? count : 0); // Basilica caster can't be knocked-back by normal monsters. - if( !(flag&0x2) && src != target && sd->special_state.no_knockback ) - return ((flag&0x10) ? count : 0); - } - break; - case BL_SKILL: { - struct skill_unit* su = NULL; - su = (struct skill_unit *)target; - if (su && su->group && skill_get_unit_flag(su->group->skill_id)&UF_NOKNOCKBACK) - return count; // Cannot be knocked back - } - break; + // Create flag needed in unit_blown_immune + if(src != target) + checkflag |= 0x1; // Offensive + if(!(flag&0x2)) + checkflag |= 0x2; // Knockback type + if(is_boss(src)) + checkflag |= 0x4; // Boss attack + + // Get reason and check for flags + reason = unit_blown_immune(target, checkflag); + switch(reason) { + case 1: return ((flag&0x04) ? count : 0); // No knocking back in WoE / BG + case 2: return count; // Emperium can't be knocked back + case 3: return ((flag&0x08) ? count : 0); // Bosses or immune can't be knocked back + case 4: return ((flag&0x20) ? count : 0); // Basilica caster can't be knocked-back by normal monsters. + case 5: return ((flag&0x10) ? count : 0); // Target has special_state.no_knockback (equip) + case 6: return count; // Trap cannot be knocked back } if (dir == -1) // : do the computation here instead of outside @@ -2429,7 +2419,6 @@ short skill_blown(struct block_list* src, struct block_list* target, char count, return unit_blown(target, dx, dy, count, flag); // Send over the proper flag } - // Checks if 'bl' should reflect back a spell cast by 'src'. // type is the type of magic attack: 0: indirect (aoe), 1: direct (targetted) // In case of success returns type of reflection, otherwise 0 @@ -11955,13 +11944,14 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_ break; case HT_ANKLESNARE: if( flag&2 ) val3 = SC_ESCAPE; + case HT_SKIDTRAP: + case MA_SKIDTRAP: + //Save position of caster + val1 = ((src->x)<<16)|(src->y); case HT_SHOCKWAVE: - val1=skill_lv*15+10; case HT_SANDMAN: case MA_SANDMAN: case HT_CLAYMORETRAP: - case HT_SKIDTRAP: - case MA_SKIDTRAP: case HT_LANDMINE: case MA_LANDMINE: case HT_FLASHER: @@ -12824,10 +12814,14 @@ int skill_unit_onplace_timer(struct skill_unit *unit, struct block_list *bl, uns break; case UNT_SKIDTRAP: { - skill_blown(&unit->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),unit_getdir(bl),0); + //Knockback away from position of user during placement [Playtester] + skill_blown(&unit->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv), + (map_calc_dir_xy(sg->val1>>16,sg->val1&0xFFFF,bl->x,bl->y,6)+4)%8,0); sg->unit_id = UNT_USED_TRAPS; clif_changetraplook(&unit->bl, UNT_USED_TRAPS); sg->limit=DIFF_TICK(tick,sg->tick)+1500; + //Target will be stopped for 3 seconds + sc_start(ss,bl,SC_STOP,100,0,skill_get_time2(sg->skill_id,sg->skill_lv)); } break; @@ -12841,7 +12835,7 @@ int skill_unit_onplace_timer(struct skill_unit *unit, struct block_list *bl, uns if( td ) sec = DIFF_TICK(td->tick, tick); - if( sg->unit_id == UNT_MANHOLE || battle_config.skill_trap_type || !map_flag_gvg(unit->bl.m) ) { + if( !unit_blown_immune(bl,0x1) ) { unit_movepos(bl, unit->bl.x, unit->bl.y, 0, 0); clif_fixpos(bl); } diff --git a/src/map/status.c b/src/map/status.c index 629016795ac..141d50d42d1 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -10042,10 +10042,9 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty case SC_KYOUGAKU: case SC_PARALYSIS: case SC_MAGNETICFIELD: - unit_stop_walking(bl,1); - break; case SC_ANKLE: - if( battle_config.skill_trap_type || !map_flag_gvg(bl->m) ) + case SC_VACUUM_EXTREME: + if (!unit_blown_immune(bl,0x1)) unit_stop_walking(bl,1); break; case SC_HIDING: @@ -10068,10 +10067,6 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty if (battle_config.sc_castcancel&bl->type) unit_skillcastcancel(bl, 0); break; - case SC_VACUUM_EXTREME: - if (!map_flag_gvg(bl->m)) - unit_stop_walking(bl, 1); - break; case SC_ITEMSCRIPT: // Shows Buff Icons if (sd && val2 != SI_BLANK) clif_status_change(bl, (enum si_type)val2, 1, tick, 0, 0, 0); diff --git a/src/map/unit.c b/src/map/unit.c index e4921bfe82c..a08bce23a84 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -100,11 +100,11 @@ int unit_walktoxy_sub(struct block_list *bl) uint8 dir; // Trim the last part of the path to account for range, // but always move at least one cell when requested to move. - for (i = ud->chaserange*10; i > 0 && ud->walkpath.path_len>1;) { + for (i = (ud->chaserange*10)-10; i > 0 && ud->walkpath.path_len>1;) { ud->walkpath.path_len--; dir = ud->walkpath.path[ud->walkpath.path_len]; if(dir&1) - i -= MOVE_DIAGONAL_COST; + i -= MOVE_COST*20; //When chasing, units will target a diamond-shaped area in range [Playtester] else i -= MOVE_COST; ud->to_x -= dirx[dir]; @@ -1061,6 +1061,64 @@ int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag) return count; // Return amount of knocked back cells } +/** + * Checks if unit can be knocked back / stopped by skills. + * @param bl: Object to check + * @param flag + * 0x1 - Offensive (not set: self skill, e.g. Backslide) + * 0x2 - Knockback type (not set: Stop type, e.g. Ankle Snare) + * 0x4 - Boss attack + * @return reason for immunity + * 0 - can be knocked back / stopped + * 1 - at WOE/BG map; + * 2 - target is emperium + * 3 - target is MD_KNOCKBACK_IMMUNE|MD_BOSS; + * 4 - target is in Basilica area; + * 5 - target has 'special_state.no_knockback'; + * 6 - target is trap that cannot be knocked back + */ +int unit_blown_immune(struct block_list* bl, int flag) +{ + if ((flag&0x1) && (map_flag_gvg(bl->m) || map[bl->m].flag.battleground) + && ((flag&0x2) || !(battle_config.skill_trap_type&0x1))) + return 1; // No knocking back in WoE / BG + + switch (bl->type) { + case BL_MOB: { + struct mob_data* md = BL_CAST(BL_MOB, bl); + // Emperium can't be knocked back + if( md->mob_id == MOBID_EMPERIUM ) + return 2; + // Bosses or immune can't be knocked back + if((flag&0x1) && status_get_mode(bl)&(MD_KNOCKBACK_IMMUNE|MD_BOSS) + && ((flag&0x2) || !(battle_config.skill_trap_type&0x2))) + return 3; + } + break; + case BL_PC: { + struct map_session_data *sd = BL_CAST(BL_PC, bl); + // Basilica caster can't be knocked-back by normal monsters. + if( sd->sc.data[SC_BASILICA] && sd->sc.data[SC_BASILICA]->val4 == sd->bl.id && !(flag&0x4)) + return 4; + // Target has special_state.no_knockback (equip) + if( (flag&0x1) && (flag&0x2) && sd->special_state.no_knockback ) + return 5; + } + break; + case BL_SKILL: { + struct skill_unit* su = NULL; + su = (struct skill_unit *)bl; + // Trap cannot be knocked back + if (su && su->group && skill_get_unit_flag(su->group->skill_id)&UF_NOKNOCKBACK) + return 6; + } + break; + } + + //Object can be knocked back / stopped + return 0; +} + /** * Warps a unit to a map/position * pc_setpos is used for player warping diff --git a/src/map/unit.h b/src/map/unit.h index 45a593c3835..38f6ae3fac6 100644 --- a/src/map/unit.h +++ b/src/map/unit.h @@ -99,6 +99,7 @@ int unit_warp(struct block_list *bl, short map, short x, short y, clr_type type) int unit_setdir(struct block_list *bl, unsigned char dir); uint8 unit_getdir(struct block_list *bl); int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag); +int unit_blown_immune(struct block_list* bl, int flag); // Can-reach checks bool unit_can_reach_pos(struct block_list *bl,int x,int y,int easy);