diff --git a/conf/battle/monster.conf b/conf/battle/monster.conf index 28aea5f737..69262c26ea 100644 --- a/conf/battle/monster.conf +++ b/conf/battle/monster.conf @@ -26,8 +26,8 @@ monster_max_aspd: 199 // are attacked and they can't attack back regardless of how they were // attacked (eg: GrimTooth), otherwise, their rude attack" is only activated // if they can't melee reach the target (eg: sniping) -// 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 +// 0x004: If not set, mobs that can change target only do so when attacked within a +// distance of [attack range+1], otherwise mobs may change target and chase // ranged attackers. This flag also overrides the 'provoke' target. // 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 @@ -38,6 +38,9 @@ monster_max_aspd: 199 // of players. // 0x040: When set, when the mob's target changes map, the mob will walk towards // any npc-warps in it's sight of view (use with mob_warp below) +// 0x080: If not set, mobs on attack state will only change targets when attacked +// by normal attacks. Set this if you want mobs to also switch targets when +// hit by skills. // 0x100: When set, a mob will pick a random skill from it's list and start from // that instead of checking skills in orders (when unset, if a mob has too // many skills, the ones near the end will rarely get selected) diff --git a/src/map/battle.c b/src/map/battle.c index fd7905795b..834c72975a 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -242,6 +242,36 @@ struct block_list* battle_getenemyarea(struct block_list *src, int x, int y, int return bl_list[rnd()%c]; } +/*========================================== [Playtester] +* Deals damage without delay, applies additional effects and triggers monster events +* This function is called from battle_delay_damage or battle_delay_damage_sub +* @param src: Source of damage +* @param target: Target of damage +* @param damage: Damage to be dealt +* @param delay: Damage delay +* @param skill_lv: Level of skill used +* @param skill_id: ID o skill used +* @param dmg_lv: State of the attack (miss, etc.) +* @param attack_type: Damage delay +* @param additional_effects: Whether additional effect should be applied +* @param tick: Current tick +*------------------------------------------*/ +void battle_damage(struct block_list *src, struct block_list *target, int64 damage, int delay, uint16 skill_lv, uint16 skill_id, enum damage_lv dmg_lv, unsigned short attack_type, bool additional_effects, unsigned int tick) { + map_freeblock_lock(); + status_fix_damage(src, target, damage, delay); // We have to separate here between reflect damage and others [icescope] + if (attack_type && !status_isdead(target) && additional_effects) + skill_additional_effect(src, target, skill_id, skill_lv, attack_type, dmg_lv, tick); + if (dmg_lv > ATK_BLOCK && attack_type) + skill_counter_additional_effect(src, target, skill_id, skill_lv, attack_type, tick); + // This is the last place where we have access to the actual damage type, so any monster events depending on type must be placed here + if (target->type == BL_MOB && damage && (attack_type&BF_NORMAL)) { + // Monsters differentiate whether they have been attacked by a skill or a normal attack + struct mob_data* md = BL_CAST(BL_MOB, target); + md->norm_attacked_id = md->attacked_id; + } + map_freeblock_unlock(); +} + /// Damage Delayed Structure struct delay_damage { int src_id; @@ -281,13 +311,8 @@ int battle_delay_damage_sub(int tid, unsigned int tick, int id, intptr_t data) (target->type != BL_PC || ((TBL_PC*)target)->invincible_timer == INVALID_TIMER) && check_distance_bl(src, target, dat->distance) ) //Check to see if you haven't teleported. [Skotlex] { - map_freeblock_lock(); - status_fix_damage(src, target, dat->damage, dat->delay); - if( dat->attack_type && !status_isdead(target) && dat->additional_effects ) - skill_additional_effect(src,target,dat->skill_id,dat->skill_lv,dat->attack_type,dat->dmg_lv,tick); - if( dat->dmg_lv > ATK_BLOCK && dat->attack_type ) - skill_counter_additional_effect(src,target,dat->skill_id,dat->skill_lv,dat->attack_type,tick); - map_freeblock_unlock(); + //Deal damage + battle_damage(src, target, dat->damage, dat->delay, dat->skill_lv, dat->skill_id, dat->dmg_lv, dat->attack_type, dat->additional_effects, tick); } else if( !src && dat->skill_id == CR_REFLECTSHIELD ) { // it was monster reflected damage, and the monster died, we pass the damage to the character as expected map_freeblock_lock(); status_fix_damage(target, target, dat->damage, dat->delay); @@ -327,13 +352,8 @@ int battle_delay_damage(unsigned int tick, int amotion, struct block_list *src, damage = 0; if ( !battle_config.delay_battle_damage || amotion <= 1 ) { - map_freeblock_lock(); - status_fix_damage(src, target, damage, ddelay); // We have to separate here between reflect damage and others [icescope] - if( attack_type && !status_isdead(target) && additional_effects ) - skill_additional_effect(src, target, skill_id, skill_lv, attack_type, dmg_lv, gettick()); - if( dmg_lv > ATK_BLOCK && attack_type ) - skill_counter_additional_effect(src, target, skill_id, skill_lv, attack_type, gettick()); - map_freeblock_unlock(); + //Deal damage + battle_damage(src, target, damage, ddelay, skill_lv, skill_id, dmg_lv, attack_type, additional_effects, gettick()); return 0; } dat = ers_alloc(delay_damage_ers, struct delay_damage); @@ -8010,7 +8030,7 @@ static const struct _battle_data { { "ignore_items_gender", &battle_config.ignore_items_gender, 1, 0, 1, }, { "berserk_cancels_buffs", &battle_config.berserk_cancels_buffs, 0, 0, 1, }, { "debuff_on_logout", &battle_config.debuff_on_logout, 1|2, 0, 1|2, }, - { "monster_ai", &battle_config.mob_ai, 0x000, 0x000, 0x77F, }, + { "monster_ai", &battle_config.mob_ai, 0x000, 0x000, 0x7FF, }, { "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 bd83c0606f..a2e007888a 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -96,6 +96,7 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam int64 battle_calc_gvg_damage(struct block_list *src,struct block_list *bl,int64 damage,uint16 skill_id,int flag); int64 battle_calc_bg_damage(struct block_list *src,struct block_list *bl,int64 damage,uint16 skill_id,int flag); +void battle_damage(struct block_list *src, struct block_list *target, int64 damage, int delay, uint16 skill_lv, uint16 skill_id, enum damage_lv dmg_lv, unsigned short attack_type, bool additional_effects, unsigned int tick); int battle_delay_damage (unsigned int tick, int amotion, struct block_list *src, struct block_list *target, int attack_type, uint16 skill_id, uint16 skill_lv, int64 damage, enum damage_lv dmg_lv, int ddelay, bool additional_effects); // Summary normal attack treatment (basic attack) diff --git a/src/map/mob.c b/src/map/mob.c index d32b864b40..1940d46084 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -981,6 +981,7 @@ int mob_spawn (struct mob_data *md) memset(&md->state, 0, sizeof(md->state)); status_calc_mob(md, SCO_FIRST); md->attacked_id = 0; + md->norm_attacked_id = 0; md->target_id = 0; md->move_fail_count = 0; md->ud.state.attack_continue = 0; @@ -1047,7 +1048,9 @@ static int mob_can_changetarget(struct mob_data* md, struct block_list* target, case MSS_BERSERK: if (!(mode&MD_CHANGETARGET_MELEE)) return 0; - return (battle_config.mob_ai&0x4 || check_distance_bl(&md->bl, target, 3)); + if (!(battle_config.mob_ai&0x80) && md->norm_attacked_id != target->id) + return 0; + return (battle_config.mob_ai&0x4 || check_distance_bl(&md->bl, target, md->status.rhw.range+1)); case MSS_RUSH: return (mode&MD_CHANGETARGET_CHASE); case MSS_FOLLOW: @@ -1476,7 +1479,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick) // 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_BLADESTOP] || md->sc.data[SC__MANHOLE] || md->sc.data[SC_CURSEDCIRCLE_TARGET]) {//Should reset targets. - md->target_id = md->attacked_id = 0; + md->target_id = md->attacked_id = md->norm_attacked_id = 0; return false; } @@ -1527,7 +1530,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick) && !mobskill_use(md, tick, MSC_RUDEATTACKED) // If can't rude Attack && can_move && unit_escape(&md->bl, tbl, rnd()%10 +1)) // Attempt escape { //Escaped - md->attacked_id = 0; + md->attacked_id = md->norm_attacked_id = 0; return true; } } @@ -1555,7 +1558,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick) && !tbl && unit_escape(&md->bl, abl, rnd()%10 +1)) { //Escaped. //TODO: Maybe it shouldn't attempt to run if it has another, valid target? - md->attacked_id = 0; + md->attacked_id = md->norm_attacked_id = 0; return true; } } @@ -1566,23 +1569,19 @@ static bool mob_ai_sub_hard(struct mob_data *md, unsigned int tick) } else { //Attackable - if (!tbl || dist < md->status.rhw.range || !check_distance_bl(&md->bl, tbl, dist) - || battle_gettarget(tbl) != md->bl.id) - { //Change if the new target is closer than the actual one - //or if the previous target is not attacking the mob. [Skotlex] - md->target_id = md->attacked_id; // set target - if (md->state.attacked_count) - md->state.attacked_count--; //Should we reset rude attack count? - md->min_chase = dist+md->db->range3; - if(md->min_chase>MAX_MINCHASE) - md->min_chase=MAX_MINCHASE; - tbl = abl; //Set the new target - } + //If a monster can change the target to the attacker, it will change the target + md->target_id = md->attacked_id; // set target + if (md->state.attacked_count) + md->state.attacked_count--; //Should we reset rude attack count? + md->min_chase = dist+md->db->range3; + if(md->min_chase>MAX_MINCHASE) + md->min_chase=MAX_MINCHASE; + tbl = abl; //Set the new target } } //Clear it since it's been checked for already. - md->attacked_id = 0; + md->attacked_id = md->norm_attacked_id = 0; } // Processing of slave monster @@ -2155,13 +2154,8 @@ void mob_damage(struct mob_data *md, struct block_list *src, int damage) damage = (int)(UINT_MAX - md->tdmg); md->tdmg = UINT_MAX; } - if (md->state.aggressive) { //No longer aggressive, change to retaliate AI. + if (md->state.aggressive) //No longer aggressive, change to retaliate AI. md->state.aggressive = 0; - if(md->state.skillstate== MSS_ANGRY) - md->state.skillstate = MSS_BERSERK; - if(md->state.skillstate== MSS_FOLLOW) - md->state.skillstate = MSS_RUSH; - } //Log damage if (src) mob_log_damage(md, src, damage); @@ -2941,7 +2935,7 @@ int mob_class_change (struct mob_data *md, int mob_id) md->lootitems = (struct s_mob_lootitem *)aCalloc(LOOTITEM_SIZE,sizeof(struct s_mob_lootitem)); //Targets should be cleared no morph - md->target_id = md->attacked_id = 0; + md->target_id = md->attacked_id = md->norm_attacked_id = 0; //Need to update name display. clif_charnameack(0, &md->bl); diff --git a/src/map/mob.h b/src/map/mob.h index eca26094ac..3f3c3cb262 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -171,7 +171,7 @@ struct mob_data { short mob_id; unsigned int tdmg; //Stores total damage given to the mob, for exp calculations. [Skotlex] int level; - int target_id,attacked_id; + int target_id,attacked_id,norm_attacked_id; int areanpc_id; //Required in OnTouchNPC (to avoid multiple area touchs) unsigned int bg_id; // BattleGround System diff --git a/src/map/unit.c b/src/map/unit.c index 6f5e5aeab1..f5eae613c1 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -2576,10 +2576,9 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t if(md->state.skillstate == MSS_ANGRY || md->state.skillstate == MSS_BERSERK) { if (mobskill_use(md,tick,-1)) return 1; - } else { - // Set mob's ANGRY/BERSERK states. - md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK; } + // Set mob's ANGRY/BERSERK states. + md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK; if (sstatus->mode&MD_ASSIST && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME) { // Link monsters nearby [Skotlex]