Explorar el Código

Knockback, stop effects and Skid Trap reworked, monster chase and direction updates, code optimizations
- Created a new function unit_blown_immune that will now serve as the central function to determine if an object can be knocked back or stopped (bugreport:7637)
* Moved the check code from skill_blown to unit_blown_immune
* Several stopping effects and traps will now use unit_blown_immune to check if the object can be stopped, if not, the object will always move to its target cell before stopping
* Bosses and monsters immune to knockback will now no longer be stopped by such traps
* Expanded the configuration skill_trap_type and moved its checks into unit_blown_immune, so it's possible to switch the "no stop" behavior off for GVG/BG and monsters individually
* Long-term we should make all skills use this function to check for knockback immunity, it will make the checks a lot cleaner and more centralized
- Monster chase range updates (bugreport:7637)
* Updated monster_chase_range in monster.conf from 1 to 3; I originally thought official value is 1, but doing some in-depth tests myself I realized it's 3 for the most important situations
* When a monster cannot issue new "move" commands because it was affected by a status change, but is still moving due to knockback immunity, it will no longer unlock its target and stop
* Fixed a bug that always caused the chase path monsters calculated to be 1 cell too short causing them to recalculate their path one cell before their goal every single time
- Fixed the direction calculation once again and optimized it at the same time (bugreport:9373)
* Now the calculated direction is 100% official, really truly, checked it myself with every single cell and various skills
* Added a new function map_calc_dir_xy that allows to check for a direction between two cells without the need of a block_list
* map_calc_dir will now just use map_calc_dir_xy to avoid duplicate code
- Implemented Skid Trap properly (bugreport:9373)
* The direction of the knockback will now be "away from position of the caster during cast" rather than "away from trap"
* Skid Trap will now stop the target for 3 seconds; this works even in GVG/BG and on bosses, even though the actual knockback doesn't happen

Playtester hace 10 años
padre
commit
902c920b73

+ 3 - 3
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.

+ 9 - 3
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

+ 1 - 1
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

+ 1 - 1
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

+ 1 - 1
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,   },

+ 28 - 16
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;
 }

+ 2 - 1
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);

+ 5 - 5
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) ||

+ 28 - 34
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) // <optimized>: 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);
 					}

+ 2 - 7
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);

+ 60 - 2
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

+ 1 - 0
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);