summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichieru <Michieru@users.noreply.github.com>2014-10-02 12:07:50 +0200
committerMichieru <Michieru@users.noreply.github.com>2014-10-02 12:07:50 +0200
commitc813ffe0ba08d3330397da4116b25bfe374a6116 (patch)
treeaed8adf2182e457961c986ed03d5755ff8515bfe
parentd8c84637efc7513503c4248e5f95bac6ba378d5c (diff)
downloadhercules-c813ffe0ba08d3330397da4116b25bfe374a6116.tar.gz
hercules-c813ffe0ba08d3330397da4116b25bfe374a6116.tar.bz2
hercules-c813ffe0ba08d3330397da4116b25bfe374a6116.tar.xz
hercules-c813ffe0ba08d3330397da4116b25bfe374a6116.zip
* Rewrote the hard monster AI. Monsters will now behave a lot closer to official servers:
* Monsters will now attack immediately when they are chasing a target and it comes into attack range (bugreport:7370) * Monsters will now chase their target during their aDelay, but they still have to wait for aMotion to be able to move again (bugreport:9269) * Monsters will now rethink their chase in a configurable interval (see monster_chase_refresh in monster.conf), official value is once per cell, previously it was once per 3 cells * Monsters will now stop when they rethink their chase and their target is gone (player hides or target loot was picked), regardless of the monster_ai setting (note: if you want the old, stupid behavior, just increase monster_chase_refresh instead) Thanks to Playtester (rathena: cfef8a0088c3)
-rw-r--r--conf/battle/monster.conf15
-rw-r--r--src/map/battle.c1
-rw-r--r--src/map/battle.h1
-rw-r--r--src/map/mob.c66
4 files changed, 50 insertions, 33 deletions
diff --git a/conf/battle/monster.conf b/conf/battle/monster.conf
index 50d661087..9ca8df825 100644
--- a/conf/battle/monster.conf
+++ b/conf/battle/monster.conf
@@ -29,9 +29,7 @@ monster_max_aspd: 199
// 0x004: If not set, mobs that can change target only do so when melee attacked
// (distance player/mob < 3), otherwise mobs may change target and chase
// ranged attackers. This flag also overrides the 'provoke' target.
-// 0x008: If set, when a mob loses track of their target, they stop walking
-// immediately. Otherwise, they continue to their last target tile. When
-// set mobs also scatter as soon as they lose their target. Use this mode
+// 0x008: When set, mobs scatter as soon as they lose their target. Use this mode
// to make it much harder to mob-train by hiding and collecting them on a
// single spot (ie: GrimTooth training)
// 0x010: If set, mob skills defined for friends will also trigger on themselves.
@@ -52,6 +50,17 @@ monster_max_aspd: 199
// Example: 0x140 -> Chase players through warps + use skills in random order.
monster_ai: 0
+// How often should a monster rethink its chase?
+// 0: Every 100ms (MIN_MOBTHINKTIME)
+// 1: Every cell moved (official)
+// 2: Every 2 cells moved
+// 3: Every 3 cells moved (previous setting)
+// 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
+
// Should mobs be able to be warped (add as needed)?
// 0: Disable.
// 1: Enable mob-warping when standing on NPC-warps
diff --git a/src/map/battle.c b/src/map/battle.c
index bdd1b9bc9..5eabc719d 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -6715,6 +6715,7 @@ static const struct battle_data {
{ "copyskill_restrict", &battle_config.copyskill_restrict, 2, 0, 2, },
{ "berserk_cancels_buffs", &battle_config.berserk_cancels_buffs, 0, 0, 1, },
{ "monster_ai", &battle_config.mob_ai, 0x000, 0x000, 0x77F, },
+ { "monster_chase_refresh", &battle_config.mob_chase_refresh, 1, 0, 30, },
{ "hom_setting", &battle_config.hom_setting, 0xFFFF, 0x0000, 0xFFFF, },
{ "dynamic_mobs", &battle_config.dynamic_mobs, 1, 0, 1, },
{ "mob_remove_damaged", &battle_config.mob_remove_damaged, 1, 0, 1, },
diff --git a/src/map/battle.h b/src/map/battle.h
index 734a6187d..eeb9897fa 100644
--- a/src/map/battle.h
+++ b/src/map/battle.h
@@ -353,6 +353,7 @@ struct Battle_Config {
int copyskill_restrict; // [Aru]
int berserk_cancels_buffs; // [Aru]
int mob_ai; //Configures various mob_ai settings to make them smarter or dumber(official). [Skotlex]
+ int mob_chase_refresh; //How often a monster should refresh its chase [Playtester]
int hom_setting; //Configures various homunc settings which make them behave unlike normal characters.. [Skotlex]
int dynamic_mobs; // Dynamic Mobs [Wizputer] - battle.conf flag implemented by [random]
int mob_remove_damaged; // Dynamic Mobs - Remove mobs even if damaged [Wizputer]
diff --git a/src/map/mob.c b/src/map/mob.c
index ffab804a6..cdd842075 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -1298,8 +1298,7 @@ int mob_unlocktarget(struct mob_data *md, int64 tick) {
break;
default:
mob_stop_attack(md);
- if (battle_config.mob_ai&0x8)
- mob_stop_walking(md,1); //Immediately stop chasing.
+ mob_stop_walking(md,1); //Stop chasing.
md->state.skillstate = MSS_IDLE;
md->next_walktime=tick+rnd()%3000+3000;
break;
@@ -1404,9 +1403,6 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
if (md->ud.skilltimer != INVALID_TIMER)
return false;
- if(md->ud.walktimer != INVALID_TIMER && md->ud.walkpath.path_pos <= 3)
- return false;
-
// Abnormalities
if(( md->sc.opt1 > 0 && md->sc.opt1 != OPT1_STONEWAIT && md->sc.opt1 != OPT1_BURNING && md->sc.opt1 != OPT1_CRYSTALIZE )
|| md->sc.data[SC_DEEP_SLEEP] || md->sc.data[SC_BLADESTOP] || md->sc.data[SC__MANHOLE] || md->sc.data[SC_CURSEDCIRCLE_TARGET]) {
@@ -1434,10 +1430,12 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
|| ((TBL_PC*)tbl)->invincible_timer != INVALID_TIMER)
)
) {
- //Unlock current target.
+ //No valid target
if (mob->warpchase(md, tbl))
return true; //Chasing this target.
- mob->unlocktarget(md, tick-(battle_config.mob_ai&0x8?3000:0)); //Immediately do random walk.
+ if(md->ud.walktimer != INVALID_TIMER && 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-(battle_config.mob_ai&0x8?3000:0)); //Immediately do random walk.
tbl = NULL;
}
}
@@ -1604,27 +1602,25 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
mob->unlocktarget (md,tick);
return true;
}
+
//Attempt to attack.
//At this point we know the target is attackable, we just gotta check if the range matches.
- if (md->ud.target == tbl->id && md->ud.attacktimer != INVALID_TIMER) //Already locked.
- return true;
-
if (battle->check_range (&md->bl, tbl, md->status.rhw.range))
{ //Target within range, engage
-
- if(tbl->type == BL_PC)
- mob->log_damage(md, tbl, 0); //Log interaction (counts as 'attacker' for the exp bonus)
-
- if(!(mode&MD_RANDOMTARGET))
- unit->attack(&md->bl,tbl->id,1);
- else { // Attack once and find new random target
- int search_size = (view_range < md->status.rhw.range) ? view_range : md->status.rhw.range;
- unit->attack(&md->bl,tbl->id,0);
- tbl = battle->get_enemy(&md->bl, DEFAULT_ENEMY_TYPE(md), search_size);
- // If no target was found, keep atacking the old one
- if( tbl ) {
- md->target_id = tbl->id;
- md->min_chase = md->db->range3;
+ if (md->ud.target != tbl->id || md->ud.attacktimer == INVALID_TIMER)
+ { //Only attack if no more attack delay left
+ if(tbl->type == BL_PC)
+ mob->log_damage(md, tbl, 0); //Log interaction (counts as 'attacker' for the exp bonus)
+
+ if( !(mode&MD_RANDOMTARGET) )
+ unit->attack(&md->bl,tbl->id,1);
+ else { // Attack once and find a new random target
+ int search_size = (view_range < md->status.rhw.range) ? view_range : md->status.rhw.range;
+ unit->attack(&md->bl,tbl->id, 0);
+ if ((tbl = battle->get_enemy(&md->bl, DEFAULT_ENEMY_TYPE(md), search_size))) {
+ md->target_id = tbl->id;
+ md->min_chase = md->db->range3;
+ }
}
}
return true;
@@ -1633,17 +1629,23 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
//Out of range...
if (!(mode&MD_CANMOVE))
{ //Can't chase. Attempt an idle skill before unlocking.
- md->state.skillstate = MSS_IDLE;
- if (!mob->skill_use(md, tick, -1))
- mob->unlocktarget(md,tick);
+ if (md->ud.target != tbl->id || md->ud.attacktimer == INVALID_TIMER)
+ { //Only use skill if no more attack delay left
+ md->state.skillstate = MSS_IDLE;
+ if (!mob->skill_use(md, tick, -1))
+ mob->unlocktarget(md,tick);
+ }
return true;
}
if (!can_move)
{ //Stuck. Attempt an idle skill
- md->state.skillstate = MSS_IDLE;
- if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL))
- mob->skill_use(md, tick, -1);
+ if (md->ud.target != tbl->id || md->ud.attacktimer == INVALID_TIMER)
+ { //Only use skill if no more attack delay left
+ md->state.skillstate = MSS_IDLE;
+ if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL))
+ mob->skill_use(md, tick, -1);
+ }
return true;
}
@@ -1654,6 +1656,10 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 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.
if(!mob->can_reach(md, tbl, md->min_chase, MSS_RUSH) ||
!unit->walktobl(&md->bl, tbl, md->status.rhw.range, 2))