Official Icewall implementation and other fixes

- Reverted all the icewall-related changes done in SVN r15777 and following as testing shows they aren't official and are actually pretty exploitable (bugreport:7412)
- Instead implemented the official icewall characteristic that monsters can only leave an icewall cell to the west or south, the changes include:
  * The "sight" path check no longer checks for the current cell so standing on an icewall allows you to see/attack into any direction
  * The path finding will still ignore the current cell as before but the walk routine will not allow to walk east or north while standing on an icewall cell
  * This leads monsters in the situation where they go through an AI loop not allowing them to escape the icewall (if their target is north or east of them)
  * Monster in this situation will use idle skills and if they get attacked will use their rudeattacked skills if available, similar to traps like Spiderweb
  * Added a configuration icewall_walk_block that allows to configure how long a monster should go through the AI loop before the server allows it any movement, this "safety" system is official and seems to equal about 75 AI loops; if you want to disable the whole icewall system so that monsters don't get stuck in icewall at all, just set this to 0
  * Here are videos from jRO showing how this system works: http://ragdo.blog56.fc2.com/blog-entry-763.html
- Implemented the official calculation for "direction"; now you will be considered horizontal/vertical/diagonally aligned with a target cell in the exact same way as on official servers, this is for example used to determine whether an icewall or a firewall should be horizontal, vertical or diagonal; the only thing that is still unofficial is the default direction (officially always "west"); effectively now there are more situations considered diagonal than before
- Fixed a compiler warning (converting double to float)
- Further cleanups on the idle skill use code for immobile monsters and monsters near a player but without a target (now skill using will always go via mob_unlocktarget)
  * This also fixes that monsters switched to idle mode and start to use idle skills one second too late
This commit is contained in:
Playtester 2014-10-18 16:52:55 +02:00
parent 5110ddbaf4
commit 5540d89cb0
11 changed files with 58 additions and 78 deletions

View File

@ -313,3 +313,12 @@ cart_revo_knockback: yes
// On official servers, Arrow Shower blow direction always rely on skill placed location to target instead of caster to target
arrow_shower_knockback: yes
// How many attempts should a monster need until it can escape from an icewall casted directly on it?
// On official servers, monsters can only leave an icewall to the west and south. If their target is north or east of them
// they will continously try to chase it but fail doing so. This brings them into a loop during which they will cast idle
// and rudeattacked skills (if attacked). Official servers have a safety system that eventually allows monsters to escape
// when their walk routine failed many times in row so they won't stay on the loop endlessly. The time for this seems to be
// around 15 seconds for fast monsters and 35 seconds for slow monsters, this equals about 75 attempts.
// Set this to 0 if you don't want monsters to be stuck in icewalls at all.
icewall_walk_block: 75

View File

@ -7057,7 +7057,7 @@ ACMD_FUNC(mobinfo)
if (mob->mvpitem[i].nameid <= 0 || (item_data = itemdb_exists(mob->mvpitem[i].nameid)) == NULL)
continue;
//Because if there are 3 MVP drops at 50%, the first has a chance of 50%, the second 25% and the third 12.5%
mvppercent = (float)mob->mvpitem[i].p * mvpremain / 10000.0;
mvppercent = (float)mob->mvpitem[i].p * mvpremain / 10000.0f;
if(battle_config.item_drop_mvp_mode == 0) {
mvpremain -= mvppercent;
}

View File

@ -7900,7 +7900,8 @@ static const struct _battle_data {
{ "arrow_shower_knockback", &battle_config.arrow_shower_knockback, 1, 0, 1, },
{ "devotion_rdamage_skill_only", &battle_config.devotion_rdamage_skill_only, 1, 0, 1, },
{ "max_extended_aspd", &battle_config.max_extended_aspd, 193, 100, 199, },
{ "monster_chase_refresh", &battle_config.mob_chase_refresh, 1, 0, 30, }
{ "monster_chase_refresh", &battle_config.mob_chase_refresh, 1, 0, 30, },
{ "icewall_walk_block", &battle_config.icewall_walk_block, 75, 0, 255, }
};
#ifndef STATS_OPT_OUT

View File

@ -572,6 +572,7 @@ extern struct Battle_Config
int devotion_rdamage_skill_only;
int max_extended_aspd;
int mob_chase_refresh; //How often a monster should refresh its chase [Playtester]
int icewall_walk_block; //How long a monster should stay trapped in icewall [Playtester]
} battle_config;
void do_init_battle(void);

View File

@ -2585,28 +2585,27 @@ uint8 map_calc_dir(struct block_list* src, int16 x, int16 y)
}
else if( dx >= 0 && dy >=0 )
{ // upper-right
if( dx*2 <= dy ) dir = 0; // up
else if( dx > dy*2 ) dir = 6; // right
else dir = 7; // up-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
}
else if( dx >= 0 && dy <= 0 )
{ // lower-right
if( dx*2 <= -dy ) dir = 4; // down
else if( dx > -dy*2 ) dir = 6; // right
else dir = 5; // down-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
}
else if( dx <= 0 && dy <= 0 )
{ // lower-left
if( dx*2 >= dy ) dir = 4; // down
else if( dx < dy*2 ) dir = 2; // left
else dir = 3; // down-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
}
else
{ // upper-left
if( -dx*2 <= dy ) dir = 0; // up
else if( -dx > dy*2 ) dir = 2; // left
else dir = 1; // up-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
}
return dir;
}

View File

@ -703,13 +703,6 @@ struct map_data {
#ifdef ADJUST_SKILL_DAMAGE
struct s_skill_damage skill_damage[MAX_MAP_SKILL_MODIFIER];
#endif
/**
* Ice wall reference counter for bugreport:3574
* - since there are a thounsand mobs out there in a lot of maps checking on,
* - every targetting for icewall on attack path would just be a waste, so,
* - this counter allows icewall checking be only run when there is a actual ice wall on the map
**/
int icewall_num;
// Instance Variables
int instance_id;
int instance_src_map;

View File

@ -1092,15 +1092,6 @@ static int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap)
((*target) == NULL || !check_distance_bl(&md->bl, *target, dist)) &&
battle_check_range(&md->bl,bl,md->db->range2)
) { //Pick closest target?
if( map[bl->m].icewall_num &&
!path_search_long(NULL,bl->m,md->bl.x,md->bl.y,bl->x,bl->y,CELL_CHKICEWALL) ) {
if( !check_distance_bl(&md->bl, bl, status_get_range(&md->bl) ) )
return 0;
}
(*target) = bl;
md->target_id=bl->id;
md->min_chase= dist + md->db->range3;
@ -1306,8 +1297,7 @@ int mob_unlocktarget(struct mob_data *md, unsigned int tick)
md->state.skillstate = MSS_IDLE;
case MSS_IDLE:
// Idle skill.
if ((md->target_id || !(++md->ud.walk_count%IDLE_SKILL_INTERVAL)) &&
mobskill_use(md, tick, -1))
if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL) && mobskill_use(md, tick, -1))
break;
//Random walk.
if (!md->master_id &&
@ -1474,6 +1464,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick)
|| md->sc.data[SC_BITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNSTRAP]
|| md->sc.data[SC__MANHOLE])) // Not yet confirmed if boss will teleport once it can't reach target.
|| !mob_can_reach(md, tbl, md->min_chase, MSS_RUSH)
|| md->walktoxy_fail_count > 0
)
&& md->state.attacked_count++ >= RUDE_ATTACKED_COUNT
&& !mobskill_use(md, tick, MSC_RUDEATTACKED) // If can't rude Attack
@ -1497,6 +1488,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick)
|| md->sc.data[SC_BITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNSTRAP]
|| md->sc.data[SC__MANHOLE])) // Not yet confirmed if boss will teleport once it can't reach target.
|| !mob_can_reach(md, abl, dist+md->db->range3, MSS_RUSH)
|| md->walktoxy_fail_count > 0
)
) )
{ // Rude attacked
@ -1573,7 +1565,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick)
}
}
//This handles triggering idle walk/skill.
//This handles triggering idle/walk skill.
mob_unlocktarget(md, tick);
return true;
}
@ -1586,14 +1578,14 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick)
return true; //Already locked.
if (md->lootitem == NULL)
{ //Can't loot...
mob_unlocktarget (md, tick);
mob_unlocktarget(md, tick);
return true;
}
if (!check_distance_bl(&md->bl, tbl, 1))
{ //Still not within loot range.
if (!(mode&MD_CANMOVE))
{ //A looter that can't move? Real smart.
mob_unlocktarget(md,tick);
mob_unlocktarget(md, tick);
return true;
}
if (!can_move) //Stuck. Wait before walking.
@ -1626,8 +1618,8 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick)
unit_set_walkdelay(&md->bl, tick, md->status.amotion, 1);
}
//Clear item.
map_clearflooritem (tbl);
mob_unlocktarget (md,tick);
map_clearflooritem(tbl);
mob_unlocktarget(md, tick);
return true;
}
@ -1656,12 +1648,11 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick)
//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 on next interval.
if ((md->ud.target != tbl->id || md->ud.attacktimer == INVALID_TIMER))
{ //Only unlock target to use idle skill if no more attack left
md->ud.walk_count = (md->ud.walk_count+1)%250;
if (!(md->ud.walk_count%IDLE_SKILL_INTERVAL))
mob_unlocktarget(md,tick);
{ //Can't chase. Immobile and trapped mobs should unlock target and use an idle skill.
if (md->ud.attacktimer == INVALID_TIMER)
{ //Only unlock target if no more attack delay left
//This handles triggering idle/walk skill.
mob_unlocktarget(md,tick);
}
return true;
}

View File

@ -168,6 +168,7 @@ struct mob_data {
short move_fail_count;
short lootitem_count;
short min_chase;
unsigned char walktoxy_fail_count; //Pathfinding succeeds but the actual walking failed (e.g. Icewall lock)
int deletetimer;
int master_id,master_dist;

View File

@ -143,9 +143,6 @@ bool path_search_long(struct shootpath_data *spd,int16 m,int16 x0,int16 y0,int16
spd->x[0] = x0;
spd->y[0] = y0;
if (map_getcellp(md,x1,y1,cell))
return false;
if (dx > abs(dy)) {
weight = dx;
spd->ry = 1;
@ -156,8 +153,6 @@ bool path_search_long(struct shootpath_data *spd,int16 m,int16 x0,int16 y0,int16
while (x0 != x1 || y0 != y1)
{
if (map_getcellp(md,x0,y0,cell))
return false;
wx += dx;
wy += dy;
if (wx >= weight) {
@ -177,6 +172,8 @@ bool path_search_long(struct shootpath_data *spd,int16 m,int16 x0,int16 y0,int16
spd->y[spd->len] = y0;
spd->len++;
}
if (map_getcellp(md,x0,y0,cell))
return false;
}
return true;

View File

@ -11852,28 +11852,6 @@ static bool skill_dance_switch(struct skill_unit* unit, int flag)
return true;
}
/**
* Upon Ice Wall cast it checks all nearby mobs to find any who may be blocked by the IW
*/
static int skill_icewall_block(struct block_list *bl,va_list ap) {
struct block_list *target = NULL;
struct mob_data *md = ((TBL_MOB*)bl);
nullpo_ret(bl);
nullpo_ret(md);
if( !md->target_id || ( target = map_id2bl(md->target_id) ) == NULL )
return 0;
if( path_search_long(NULL,bl->m,bl->x,bl->y,target->x,target->y,CELL_CHKICEWALL) )
return 0;
if( !check_distance_bl(bl, target, status_get_range(bl) ) ) {
mob_unlocktarget(md,gettick());
mob_stop_walking(md,1);
}
return 0;
}
/**
* Initializes and sets a ground skill / skill unit. Usually called after skill_casted_pos() or skill_castend_map()
@ -12365,9 +12343,6 @@ struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 skill_
//success, unit created.
switch( skill_id ) {
case WZ_ICEWALL:
map_foreachinrange(skill_icewall_block, src, AREA_SIZE, BL_MOB);
break;
case NJ_TATAMIGAESHI: //Store number of tiles.
group->val1 = group->alive_count;
break;
@ -16894,7 +16869,6 @@ struct skill_unit *skill_initunit(struct skill_unit_group *group, int idx, int x
map_setgatcell(unit->bl.m,unit->bl.x,unit->bl.y,5);
clif_changemapcell(0,unit->bl.m,unit->bl.x,unit->bl.y,5,AREA);
skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,true);
map[unit->bl.m].icewall_num++;
break;
case SA_LANDPROTECTOR:
skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,true);
@ -16952,7 +16926,6 @@ int skill_delunit(struct skill_unit* unit)
map_setgatcell(unit->bl.m,unit->bl.x,unit->bl.y,unit->val2);
clif_changemapcell(0,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2,ALL_SAMEMAP); // hack to avoid clientside cell bug
skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,false);
map[unit->bl.m].icewall_num--;
break;
case SA_LANDPROTECTOR:
skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,false);

View File

@ -369,6 +369,19 @@ static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data
if(map_getcell(bl->m,x+dx,y+dy,CELL_CHKNOPASS))
return unit_walktoxy_sub(bl);
//Monsters can only leave icewalls to the west and south
//But if movement fails more than icewall_walk_block times, they can ignore this rule
if(md && md->walktoxy_fail_count < battle_config.icewall_walk_block && map_getcell(bl->m,x,y,CELL_CHKICEWALL) && (dx > 0 || dy > 0)) {
//Needs to be done here so that rudeattack skills are invoked
md->walktoxy_fail_count++;
clif_fixpos(bl);
mob_unlocktarget(md, tick);
//Use idle skill at this point
if (!(++ud->walk_count%WALK_SKILL_INTERVAL))
mobskill_use(md, tick, -1);
return 0;
}
// Refresh view for all those we lose sight
map_foreachinmovearea(clif_outsight, bl, AREA_SIZE, dx, dy, sd?BL_ALL:BL_PC, bl);
@ -398,6 +411,8 @@ static int unit_walktoxy_timer(int tid, unsigned int tick, int id, intptr_t data
pc_cell_basilica(sd);
break;
case BL_MOB:
//Movement was successful, reset walktoxy_fail_count
md->walktoxy_fail_count = 0;
if( map_getcell(bl->m,x,y,CELL_CHKNPC) ) {
if( npc_touch_areanpc2(md) )
return 0; // Warped
@ -2463,8 +2478,8 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t
sstatus = status_get_status_data(src);
range = sstatus->rhw.range;
if( unit_is_walking(target) )
range++; // Extra range when chasing
if( unit_is_walking(target) && (target->type == BL_PC || !map_getcell(target->m,target->x,target->y,CELL_CHKICEWALL)) )
range++; // Extra range when chasing (does not apply to mobs locked in an icewall)
if(sd && !check_distance_client_bl(src,target,range)) {
// Player tries to attack but target is too far, notify client