summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichieru <Michieru@users.noreply.github.com>2014-10-18 17:52:17 +0200
committerMichieru <Michieru@users.noreply.github.com>2014-10-18 17:52:17 +0200
commit6a6e3bb8e69890ee69981467f1e5e3591c0f9356 (patch)
tree552347425d9575b423617326ab3cbdf257479728
parent4ac673941714032ada6d26fb60936ec510bbe496 (diff)
downloadhercules-6a6e3bb8e69890ee69981467f1e5e3591c0f9356.tar.gz
hercules-6a6e3bb8e69890ee69981467f1e5e3591c0f9356.tar.bz2
hercules-6a6e3bb8e69890ee69981467f1e5e3591c0f9356.tar.xz
hercules-6a6e3bb8e69890ee69981467f1e5e3591c0f9356.zip
Fix bug:7454
* 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 - 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 Mega thanks to Playtester (rathena 5540d89cb0e)
-rw-r--r--conf/battle/skill.conf17
-rw-r--r--src/map/battle.c3
-rw-r--r--src/map/battle.h1
-rw-r--r--src/map/map.c25
-rw-r--r--src/map/map.h8
-rw-r--r--src/map/mob.c27
-rw-r--r--src/map/mob.h1
-rw-r--r--src/map/path.c7
-rw-r--r--src/map/skill.c28
-rw-r--r--src/map/skill.h1
-rw-r--r--src/map/unit.c20
11 files changed, 59 insertions, 79 deletions
diff --git a/conf/battle/skill.conf b/conf/battle/skill.conf
index 10cca4af7..1bb58211a 100644
--- a/conf/battle/skill.conf
+++ b/conf/battle/skill.conf
@@ -69,9 +69,9 @@ skill_out_range_consume: no
skillrange_by_distance: 14
// Should the equipped weapon's range override the skill's range defined in the skill_db for most weapon-based skills? (Note 3)
-// NOTE: Skills affected by this option are those whose range in the skill_db are negative. Note that unless monster_ai&0x400 is
-// set, the range of all skills is 9 for monsters.
-skillrange_from_weapon: 30
+// NOTE: Skills affected by this option are those whose range in the skill_db are negative. By default always the skill range is used.
+// Note that if you want all monster skills to have a range of 9 you need to set monster_ai&0x400.
+skillrange_from_weapon: 0
// Should a check on the caster's status be performed in all skill attacks?
// When set to yes, meteors, storm gust and any other ground skills will have
@@ -293,4 +293,13 @@ mob_max_skilllvl: 100
// 1: Gutter line system without demi gutter bug
// 2-20: Area around caster (2 = 5x5, 3 = 7x7, 4 = 9x9, ..., 20 = 41x41)
// Note: If you knock the target out of the area it will only be hit once and won't do splash damage
-bowling_bash_area: 0 \ No newline at end of file
+bowling_bash_area: 0
+
+// 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 \ No newline at end of file
diff --git a/src/map/battle.c b/src/map/battle.c
index 3b50d3507..6844d8bda 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -6830,7 +6830,8 @@ static const struct battle_data {
{ "guild_castle_expulsion", &battle_config.guild_castle_expulsion, 0, 0, 1, },
{ "song_timer_reset", &battle_config.song_timer_reset, 0, 0, 1, },
{ "snap_dodge", &battle_config.snap_dodge, 0, 0, 1, },
- { "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
/**
diff --git a/src/map/battle.h b/src/map/battle.h
index 966fa073e..6ac2df391 100644
--- a/src/map/battle.h
+++ b/src/map/battle.h
@@ -452,6 +452,7 @@ struct Battle_Config {
int mob_size_influence; // Enable modifications on earned experience, drop rates and monster status depending on monster size. [mkbu95]
int bowling_bash_area;
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]
/** Hercules **/
int skill_trap_type;
diff --git a/src/map/map.c b/src/map/map.c
index 14af392b7..5fa8779eb 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -2420,28 +2420,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;
}
diff --git a/src/map/map.h b/src/map/map.h
index dba565cc0..751fef67e 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -658,13 +658,7 @@ struct map_data {
int jexp; // map experience multiplicator
int bexp; // map experience multiplicator
int nocommand; //Blocks @/# commands for non-gms. [Skotlex]
- /**
- * Ice wall reference counter for bugreport:3574
- * - since there are a thousand mobs out there in a lot of maps checking on,
- * - every targeting 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;
diff --git a/src/map/mob.c b/src/map/mob.c
index 122cc91aa..d0a65315a 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -1076,15 +1076,6 @@ 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->list[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;
@@ -1288,8 +1279,7 @@ int mob_unlocktarget(struct mob_data *md, int64 tick) {
md->state.skillstate = MSS_IDLE;
case MSS_IDLE:
// Idle skill.
- if ((md->target_id || !(++md->ud.walk_count%IDLE_SKILL_INTERVAL)) &&
- mob->skill_use(md, tick, -1))
+ if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL) && mob->skill_use(md, tick, -1))
break;
//Random walk.
if (!md->master_id &&
@@ -1457,6 +1447,7 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
|| md->sc.data[SC_WUGBITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNS_TRAP]
|| 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
&& !mob->skill_use(md, tick, MSC_RUDEATTACKED) // If can't rude Attack
@@ -1479,6 +1470,7 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
|| md->sc.data[SC_WUGBITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNS_TRAP]
|| 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
)
)
) {
@@ -1551,7 +1543,7 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
}
}
- //This handles triggering idle walk/skill.
+ //This handles triggering idle/walk skill.
mob->unlocktarget(md, tick);
return true;
}
@@ -1634,12 +1626,11 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 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;
}
diff --git a/src/map/mob.h b/src/map/mob.h
index 4df96d7f8..848bfbb2f 100644
--- a/src/map/mob.h
+++ b/src/map/mob.h
@@ -171,6 +171,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;
diff --git a/src/map/path.c b/src/map/path.c
index 681dfcb06..d8096ad43 100644
--- a/src/map/path.c
+++ b/src/map/path.c
@@ -129,9 +129,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 (md->getcellp(md,x1,y1,cell))
- return false;
-
if (dx > abs(dy)) {
weight = dx;
spd->ry = 1;
@@ -142,8 +139,6 @@ bool path_search_long(struct shootpath_data *spd,int16 m,int16 x0,int16 y0,int16
while (x0 != x1 || y0 != y1)
{
- if (md->getcellp(md,x0,y0,cell))
- return false;
wx += dx;
wy += dy;
if (wx >= weight) {
@@ -163,6 +158,8 @@ bool path_search_long(struct shootpath_data *spd,int16 m,int16 x0,int16 y0,int16
spd->y[spd->len] = y0;
spd->len++;
}
+ if (md->getcellp(md,x0,y0,cell))
+ return false;
}
return true;
diff --git a/src/map/skill.c b/src/map/skill.c
index 51e266559..414ca74d3 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -10782,28 +10782,6 @@ bool skill_dance_switch(struct skill_unit* su, int flag) {
return true;
}
-/**
- * Upon Ice Wall cast it checks all nearby mobs to find any who may be blocked by the IW
- **/
-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,timer->gettick());
- mob_stop_walking(md,1);
- }
-
- return 0;
-}
/*==========================================
* Initializes and sets a ground skill.
* flag&1 is used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active)
@@ -11303,9 +11281,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;
@@ -15599,7 +15574,6 @@ struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int
map->setgatcell(su->bl.m,su->bl.x,su->bl.y,5);
clif->changemapcell(0,su->bl.m,su->bl.x,su->bl.y,5,AREA);
skill->unitsetmapcell(su,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,true);
- map->list[su->bl.m].icewall_num++;
break;
case SA_LANDPROTECTOR:
skill->unitsetmapcell(su,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,true);
@@ -15651,7 +15625,6 @@ int skill_delunit (struct skill_unit* su) {
map->setgatcell(su->bl.m,su->bl.x,su->bl.y,su->val2);
clif->changemapcell(0,su->bl.m,su->bl.x,su->bl.y,su->val2,ALL_SAMEMAP); // hack to avoid clientside cell bug
skill->unitsetmapcell(su,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,false);
- map->list[su->bl.m].icewall_num--;
// AS_CLOAKING in low levels requires a wall to be cast, thus it needs to be
// checked again when a wall disapears! issue:8182 [Panikon]
map->foreachinarea(skill->check_cloaking_end, su->bl.m,
@@ -18803,7 +18776,6 @@ void skill_defaults(void) {
skill->frostjoke_scream = skill_frostjoke_scream;
skill->greed = skill_greed;
skill->destroy_trap = skill_destroy_trap;
- skill->icewall_block = skill_icewall_block;
skill->unitgrouptickset_search = skill_unitgrouptickset_search;
skill->dance_switch = skill_dance_switch;
skill->check_condition_char_sub = skill_check_condition_char_sub;
diff --git a/src/map/skill.h b/src/map/skill.h
index 6373d9275..4ec742bd0 100644
--- a/src/map/skill.h
+++ b/src/map/skill.h
@@ -1984,7 +1984,6 @@ struct skill_interface {
int (*frostjoke_scream) (struct block_list *bl, va_list ap);
int (*greed) (struct block_list *bl, va_list ap);
int (*destroy_trap) ( struct block_list *bl, va_list ap );
- int (*icewall_block) (struct block_list *bl,va_list ap);
struct skill_unit_group_tickset *(*unitgrouptickset_search) (struct block_list *bl, struct skill_unit_group *group, int64 tick);
bool (*dance_switch) (struct skill_unit* su, int flag);
int (*check_condition_char_sub) (struct block_list *bl, va_list ap);
diff --git a/src/map/unit.c b/src/map/unit.c
index f823a3fed..2d8fd530d 100644
--- a/src/map/unit.c
+++ b/src/map/unit.c
@@ -250,6 +250,19 @@ int unit_walktoxy_timer(int tid, int64 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))
+ mob->skill_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);
@@ -300,6 +313,8 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
sd->hd->masterteleport_timer = 0;
}
} else if (md) {
+ //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
} else
@@ -2010,8 +2025,9 @@ int unit_attack_timer_sub(struct block_list* src, int tid, int64 tick) {
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
clif->movetoattack(sd,target);