Browse Source

Monster Activity on Teleport, Assist/Slave Behavior, NPC_EMOTION crash fix (#9013)

- When monsters teleport or are otherwise removed from the map they now become inactive immediately (fixes #8964)
  * This for example means that when Mistress and Lady Tanee teleport, they won't teleport again
  * They become active again when a player is within 18 cells of them
  * If you want them to stay active, please set mob_active_time and boss_active_time to the desired values
- When slaves are recalled, they now also become inactive, even if their master is still active
  * Added a new option slave_active_with_master if you still want them to be active with their master
  * All slave-related options are now in one place, to keep a better overview
- When you enable slave_stick_with_master, slaves now drop their target if outside the visible area
  * This fixes them looping from their master back out of allowed range
- Fixed an issue that caused change-chase monsters to keep target_to on their old target when attacking
  * This also caused slaves to attack the wrong target
- Assist range is now 11
  * This refers to the range between two assist monsters to trigger assist aggro
  * The value is now configurable in monster.conf
- Assist no longer has a range limit between the assist mob and the target
  * But it might drop target again immediately if walkpath fails due to being too long
- Reduced MIN_MOBLINKTIME to 300ms by default
  * This causes assist and slave monsters to be more reactive
  * If you want fully official behavior both MIN_MOBTHINKTIME and MIN_MOBLINKTIME need to be set to 20
  * If this takes too many resources on your server, just increase this back to 1000ms
- Fixed NPC_EMOTION recursively calling mobskill_use, potentially causing a crash (fixes #8933)
Playtester 4 months ago
parent
commit
f272ca6b58
7 changed files with 77 additions and 30 deletions
  1. 24 10
      conf/battle/monster.conf
  2. 2 0
      src/map/battle.cpp
  3. 2 0
      src/map/battle.hpp
  4. 19 12
      src/map/mob.cpp
  5. 1 1
      src/map/mob.hpp
  6. 4 2
      src/map/skill.cpp
  7. 25 5
      src/map/unit.cpp

+ 24 - 10
conf/battle/monster.conf

@@ -82,8 +82,11 @@ mob_warp: 0
 
 // Defines the time (in ms) during which monsters will have their AI active
 // after all players have left their vicinity.
-mob_active_time: 5000
-boss_active_time: 5000
+// Even after this time they will still walk randomly and use idle skills until
+// all players that saw them logged out or until they get teleported, recalled
+// or otherwise removed from the map.
+mob_active_time: 0
+boss_active_time: 0
 
 // Mobs and Pets view-range adjustment (range2 column in the mob_db) (Note 2)
 view_range_rate: 100
@@ -99,9 +102,16 @@ chase_range_rate: 100
 // be increased by that number.
 monster_eye_range_bonus: 0
 
-// Range in which looters search for loot (eAthena - 10, official - 12, max - 32)
+// Range in which looters search for loot (max 32)
+// Official: 12
+// Legacy Athena: 10
 loot_range: 12
 
+// Range in which assist mobs search for allies to assist (max 32)
+// Official: 11
+// Legacy Athena: 10
+assist_range: 11
+
 // Allow monsters to be aggresive and attack first? (Note 1)
 monster_active_enable: yes
 
@@ -193,6 +203,17 @@ slaves_inherit_mode: 4
 // 3: Always
 slaves_inherit_speed: 3
 
+// Should MVP slaves retain their target when summoned back to their master? (Note 1)
+mob_slave_keep_target: yes
+
+// Should slaves teleport back to their master if they get too far during chase? (Note 1)
+// Officially they can be moved as far away from their master as you want.
+slave_stick_with_master: no
+
+// Should slaves always be active when their master is active? (Note 1)
+// Officially it can be that the master is active but the slaves are not.
+slave_active_with_master: no
+
 // Will summoned monsters (alchemists, or @summon'ed monsters) attack cause a
 // chance of triggering the master's autospell cards? (Note 1)
 summons_trigger_autospells: yes
@@ -248,9 +269,6 @@ mob_npc_event_type: 1
 // will be reduced to 0.
 ksprotection: 0
 
-// Should MVP slaves retain their target when summoned back to their master?
-mob_slave_keep_target: yes
-
 // Whether or not to spawn the mvp tomb.
 // See http://irowiki.org/wiki/MVP#Gravestone
 mvp_tomb_enabled: yes
@@ -307,10 +325,6 @@ boss_nopc_move_rate: 100
 // Area is limited to area_size battle config.
 achievement_mob_share: no
 
-// Should slaves teleport back to their master if they get too far during chase? (Note 1)
-// Default (Official): no
-slave_stick_with_master: no
-
 // Absolute minimum respawn time in milliseconds of a monster.
 // Also used in delaying the spawning of guardians when a guild is not loaded.
 // Default (Official): 1000

+ 2 - 0
src/map/battle.cpp

@@ -11880,6 +11880,7 @@ static const struct _battle_data {
 	{ "show_skill_scale",                   &battle_config.show_skill_scale,                1,      0,      1,              },
 	{ "achievement_mob_share",              &battle_config.achievement_mob_share,           0,      0,      1,              },
 	{ "slave_stick_with_master",            &battle_config.slave_stick_with_master,         0,      0,      1,              },
+	{ "slave_active_with_master",           &battle_config.slave_active_with_master,        0,      0,      1,              },
 	{ "at_logout_event",                    &battle_config.at_logout_event,                 1,      0,      1,              },
 	{ "homunculus_starving_rate",           &battle_config.homunculus_starving_rate,        10,     0,      100,            },
 	{ "homunculus_starving_delay",          &battle_config.homunculus_starving_delay,       20000,  0,      INT_MAX,        },
@@ -11952,6 +11953,7 @@ static const struct _battle_data {
 	{ "hom_delay_reset_warp",               &battle_config.hom_delay_reset_warp,            1,      0,      1,              },
 #endif
 	{ "loot_range",                         &battle_config.loot_range,                      12,     1,      MAX_WALKPATH,   },
+	{ "assist_range",                       &battle_config.assist_range,                    11,     1,      MAX_WALKPATH,   },
 
 #include <custom/battle_config_init.inc>
 };

+ 2 - 0
src/map/battle.hpp

@@ -710,6 +710,7 @@ struct Battle_Config
 	int32 show_skill_scale;
 	int32 achievement_mob_share;
 	int32 slave_stick_with_master;
+	int32 slave_active_with_master;
 	int32 at_logout_event;
 	int32 homunculus_starving_rate;
 	int32 homunculus_starving_delay;
@@ -767,6 +768,7 @@ struct Battle_Config
 	int32 hom_delay_reset_vaporize;
 	int32 hom_delay_reset_warp;
 	int32 loot_range;
+	int32 assist_range;
 
 #include <custom/battle_config_struct.inc>
 };

+ 19 - 12
src/map/mob.cpp

@@ -1014,25 +1014,27 @@ int32 mob_can_reach(struct mob_data *md,struct block_list *bl,int32 range)
  *------------------------------------------*/
 int32 mob_linksearch(struct block_list *bl,va_list ap)
 {
-	struct mob_data *md;
+	mob_data *md;
 	int32 mob_id;
-	struct block_list *target;
+	int32 target_id;
 	t_tick tick;
 
 	nullpo_ret(bl);
-	md=(struct mob_data *)bl;
+	md = reinterpret_cast<mob_data*>(bl);
 	mob_id = va_arg(ap, int32);
-	target = va_arg(ap, struct block_list *);
+	target_id = va_arg(ap, int32);
 	tick=va_arg(ap, t_tick);
 
-	if (md->mob_id == mob_id && status_has_mode(&md->status,MD_ASSIST) && DIFF_TICK(tick, md->last_linktime) >= MIN_MOBLINKTIME
-		&& !md->target_id)
+	// Check if mob qualifies for assistance
+	// Line of sight to the ally is already checked at this point
+	// No valid path to the target is required
+	if (md->mob_id == mob_id && status_has_mode(&md->status,MD_ASSIST)
+		&& DIFF_TICK(tick, md->last_linktime) >= MIN_MOBLINKTIME
+		&& md->target_id == 0)
 	{
 		md->last_linktime = tick;
-		if( mob_can_reach(md,target,md->db->range2) ){	// Reachability judging
-			md->target_id = target->id;
-			return 1;
-		}
+		md->target_id = target_id;
+		return 1;
 	}
 
 	return 0;
@@ -1974,6 +1976,9 @@ 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))
 	{
+		// Make sure there is no chase target when already in attack range
+		md->ud.target_to = 0;
+
 		// 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)) {
@@ -2085,9 +2090,9 @@ static int32 mob_ai_sub_hard_timer(struct block_list *bl,va_list ap)
 	struct mob_data *md = (struct mob_data*)bl;
 	uint32 char_id = va_arg(ap, uint32);
 	t_tick tick = va_arg(ap, t_tick);
+	mob_add_spotted(md, char_id);
 	if (mob_ai_sub_hard(md, tick))
 	{	//Hard AI triggered.
-		mob_add_spotted(md, char_id);
 		md->last_pcneartime = tick;
 	}
 	return 0;
@@ -2156,10 +2161,12 @@ static int32 mob_ai_sub_lazy(struct mob_data *md, va_list args)
 
 	if (md->master_id) {
 		if (!mob_is_spotted(md)) {
+			if (battle_config.slave_active_with_master == 0)
+				return 0;
 			// Get mob data of master
 			mob_data* mmd = map_id2md(md->master_id);
 			// If neither master nor slave have been spotted we don't have to execute the slave AI
-			if (mmd && !mob_is_spotted(mmd))
+			if (mmd != nullptr && !mob_is_spotted(mmd))
 				return 0;
 		}
 		mob_ai_sub_hard_slavemob (md,tick);

+ 1 - 1
src/map/mob.hpp

@@ -33,7 +33,7 @@ struct guardian_data;
 //Min time between AI executions
 const t_tick MIN_MOBTHINKTIME = 100;
 //Min time before mobs do a check to call nearby friends for help (or for slaves to support their master)
-const t_tick MIN_MOBLINKTIME = 1000;
+const t_tick MIN_MOBLINKTIME = 300;
 //Min time between random walks
 const t_tick MIN_RANDOMWALKTIME = 4000;
 

+ 4 - 2
src/map/skill.cpp

@@ -10198,8 +10198,10 @@ int32 skill_castend_nodamage_id (struct block_list *src, struct block_list *bl,
 				status_change_end(bl, type);
 
 			//If mode gets set by NPC_EMOTION then the target should be reset [Playtester]
-			if(!battle_config.npc_emotion_behavior && skill_id == NPC_EMOTION && md->db->skill[md->skill_idx]->val[1])
-				mob_unlocktarget(md,tick);
+			if (!battle_config.npc_emotion_behavior && skill_id == NPC_EMOTION
+				&& md->state.skillstate != MSS_IDLE && md->state.skillstate != MSS_WALK
+				&& md->db->skill[md->skill_idx]->val[1])
+				mob_unlocktarget(md, tick);
 
 			if(md->db->skill[md->skill_idx]->val[1] || md->db->skill[md->skill_idx]->val[2])
 				sc_start4(src,src, type, 100, skill_lv,

+ 25 - 5
src/map/unit.cpp

@@ -1440,10 +1440,26 @@ int32 unit_warp(struct block_list *bl,int16 m,int16 x,int16 y,clr_type type)
 	bl->y = ud->to_y = y;
 	bl->m = m;
 
-	if (bl->type == BL_NPC) {
-		TBL_NPC *nd = (TBL_NPC*)bl;
-		map_addnpc(m, nd);
-		npc_setcells(nd);
+	switch (bl->type) {
+		case BL_NPC:
+		{
+			TBL_NPC* nd = reinterpret_cast<npc_data*>(bl);
+			map_addnpc(m, nd);
+			npc_setcells(nd);
+			break;
+		}
+		case BL_MOB:
+		{
+			TBL_MOB* md = reinterpret_cast<mob_data*>(bl);
+			// If slaves are set to stick with their master they should drop target if recalled at range
+			if (battle_config.slave_stick_with_master && md->target_id != 0) {
+				block_list* tbl = map_id2bl(md->target_id);
+				if (tbl == nullptr || !check_distance_bl(bl, tbl, AREA_SIZE)) {
+					md->target_id = 0;
+				}
+			}
+			break;
+		}
 	}
 
 	if(map_addblock(bl))
@@ -2968,7 +2984,7 @@ static int32 unit_attack_timer_sub(struct block_list* src, int32 tid, t_tick tic
 			if (status_has_mode(sstatus,MD_ASSIST) && DIFF_TICK(tick, md->last_linktime) >= MIN_MOBLINKTIME) { 
 				// Link monsters nearby [Skotlex]
 				md->last_linktime = tick;
-				map_foreachinrange(mob_linksearch, src, md->db->range2, BL_MOB, md->mob_id, target, tick);
+				map_foreachinshootrange(mob_linksearch, src, battle_config.assist_range, BL_MOB, md->mob_id, target->id, tick);
 			}
 		}
 
@@ -3412,6 +3428,10 @@ int32 unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file
 			if (!battle_config.mob_slave_keep_target)
 				md->target_id=0;
 
+			// When a monster is removed from map, its spotted log is cleared
+			for (int32 i = 0; i < DAMAGELOG_SIZE; i++)
+				md->spotted_log[i] = 0;
+
 			md->attacked_id=0;
 			md->state.skillstate= MSS_IDLE;
 			break;