|
@@ -1858,7 +1858,14 @@ static bool mob_ai_sub_hard(struct mob_data *md, t_tick tick)
|
|
|
|
|
|
if ((mode&MD_AGGRESSIVE && (!tbl || slave_lost_target)) || md->state.skillstate == MSS_FOLLOW)
|
|
|
{
|
|
|
+ int32 prev_id = md->target_id;
|
|
|
map_foreachinallrange (mob_ai_sub_hard_activesearch, &md->bl, view_range, DEFAULT_ENEMY_TYPE(md), md, &tbl, mode);
|
|
|
+ // If a monster finds a target through search that is already in attack range it immediately switches to berserk mode
|
|
|
+ // This behavior overrides even angry mode and other mode-specific behavior
|
|
|
+ if (tbl != nullptr && prev_id != md->target_id && battle_check_range(&md->bl, tbl, md->status.rhw.range)) {
|
|
|
+ md->state.aggressive = 0;
|
|
|
+ md->state.skillstate = MSS_BERSERK;
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
if (mode&MD_CHANGECHASE && (md->state.skillstate == MSS_RUSH || md->state.skillstate == MSS_FOLLOW))
|
|
@@ -1961,24 +1968,39 @@ static bool mob_ai_sub_hard(struct mob_data *md, t_tick tick)
|
|
|
}
|
|
|
|
|
|
// Normal attack / berserk skill is only used when target is in range
|
|
|
- if (battle_check_range(&md->bl, tbl, md->status.rhw.range) && !(md->sc.option&OPTION_HIDE))
|
|
|
- { //Target within range and able to use normal attack, engage
|
|
|
- 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_getenemy(&md->bl, DEFAULT_ENEMY_TYPE(md), search_size))) {
|
|
|
- md->target_id = tbl->id;
|
|
|
- md->min_chase = md->db->range3;
|
|
|
+ if (battle_check_range(&md->bl, tbl, md->status.rhw.range))
|
|
|
+ {
|
|
|
+ // Hiding is a special case because it prevents normal attacks but allows skill usage
|
|
|
+ // TODO: Some other states also have this behavior and should be investigated (e.g. NPC_SR_CURSEDCIRCLE)
|
|
|
+ if (!(md->sc.option&OPTION_HIDE)) {
|
|
|
+ // Target within range and potentially able to use normal attack, engage
|
|
|
+ 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);
|
|
|
+ tbl = battle_getenemy(&md->bl, DEFAULT_ENEMY_TYPE(md), search_size);
|
|
|
+ if (tbl != nullptr) {
|
|
|
+ md->target_id = tbl->id;
|
|
|
+ md->min_chase = md->db->range3;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ else if (md->state.skillstate == MSS_BERSERK && DIFF_TICK(md->ud.attackabletime, tick) <= 0) {
|
|
|
+ // Target within range and no attack delay, but unable to use normal attack, check for skill
|
|
|
+ // On official servers this check happens every 20ms
|
|
|
+ // If you want fully official behavior you will need to set MIN_MOBTHINKTIME to 20
|
|
|
+ if (mobskill_use(md, tick, -1)) {
|
|
|
+ // When a skill was used, the attack delay is applied
|
|
|
+ md->ud.attackabletime = tick + md->status.adelay;
|
|
|
+ }
|
|
|
+ }
|
|
|
return true;
|
|
|
}
|
|
|
|