Ver Fonte

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

Playtester há 9 anos atrás
pai
commit
4fdcb2ea61
6 ficheiros alterados com 62 adições e 45 exclusões
  1. 5 2
      conf/battle/monster.conf
  2. 35 15
      src/map/battle.c
  3. 1 0
      src/map/battle.h
  4. 18 24
      src/map/mob.c
  5. 1 1
      src/map/mob.h
  6. 2 3
      src/map/unit.c

+ 5 - 2
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)

+ 35 - 15
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,              },

+ 1 - 0
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)

+ 18 - 24
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);

+ 1 - 1
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
 

+ 2 - 3
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]