Official monster/MVP target selection (fixes #926)
* Monsters with mode MD_CHANGETARGET_MELEE will now only change targets on "attack" state if they are attacked by a normal attack * Monsters will change targets if the target is within "attack range+1" distance instead of a static distance of 3 * When a monster gets attacked, it will now switch to the attacker even if the attacker is farther away than its current target and the current target is auto-attacking it * Angry mode monsters will now always switch target to the first person that attacked them
This commit is contained in:
parent
418dc47751
commit
4fdcb2ea61
@ -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)
|
||||
|
@ -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, },
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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]
|
||||
|
Loading…
x
Reference in New Issue
Block a user