Selaa lähdekoodia

Walk Delay on Attack/Skill Cleanup/Fix (#9184)

- Replaced the special "walkdelay by inactive AI on attack" solution with a separate monster_ai setting
- Cast end and cast cancel delay events will now also go through unit_set_attackdelay
- Monster-specific delays are now handled by a new mob_set_delay function called from unit_set_attackdelay
  * It handles setting the AI inactive on attack and cast end
  * And setting monster skill delays on cast end and cast cancel
- Attack delays at cast end for monsters is now handled by unit_set_attackdelay instead of mobskill_end
  * Renamed mobskill_end to mobskill_delay as this function now only handles setting monster skill delays
- Disabled attack_walk_delay by default and improved the documentation of this setting
  * This has no visible change as the client will still send movement requests delayed by amotion
  * Feel free to re-enable for "PC" if you want to prevent client edits allowing to move earlier
Playtester 1 kuukausi sitten
vanhempi
commit
13651d57e1
7 muutettua tiedostoa jossa 43 lisäystä ja 42 poistoa
  1. 7 6
      conf/battle/battle.conf
  2. 2 0
      conf/battle/monster.conf
  3. 2 2
      src/map/battle.cpp
  4. 19 8
      src/map/mob.cpp
  5. 2 1
      src/map/mob.hpp
  6. 2 14
      src/map/skill.cpp
  7. 9 11
      src/map/unit.cpp

+ 7 - 6
conf/battle/battle.conf

@@ -24,12 +24,13 @@ enable_critical: 17
 mob_critical_rate: 100
 critical_rate: 100
 
-// Should normal attacks give you a walk delay? (Note 3)
-// If no, characters can move as soon as they start an attack (attack animation
-// or walk animation may be omitted client-side, causing cropped attacks or
-// monsters that teleport to you)
-// Otherwise, the delay is equal to the 'attack animation' (amotion)
-attack_walk_delay: 15
+// Which unit types should get a walk delay from normal attacks? (Note 3)
+// The delay is equal to the 'attack animation' (amotion). The client already blocks
+// sending movement requests during this time, so for client-controlled characters 
+// this setting is mainly a safety mechanism against client edits.
+// Monsters are usually unaffected by this as their AI is inactive during their attack
+// animation unless you customize their AI (see monster_ai 0x2000 in monster.conf)
+attack_walk_delay: 0
 
 // Move-delay adjustment after being hit. (Note 2)
 // The 'can't walk' delay after being hit is calculated as a percentage of the damage animation duration.

+ 2 - 0
conf/battle/monster.conf

@@ -56,6 +56,8 @@ monster_hp_rate: 100
 //         This makes e.g. Greatest General use "Earth Spike" at range 8-9 whereas
 //         officially it would only use it after already having used "Blind Attack"
 //         at range 0-7.
+// 0x2000: When set, monsters will move right after they attacked or used a skill.
+//         Usually a monster's AI is inactive for its attack motion.
 // Example: 0x140 -> Chase players through warps + use skills in random order.
 monster_ai: 0
 

+ 2 - 2
src/map/battle.cpp

@@ -11785,7 +11785,7 @@ static const struct _battle_data {
 	{ "amotion_min_skill_delay",            &battle_config.amotion_min_skill_delay,         0,      0,      1,              },
 	{ "default_walk_delay",                 &battle_config.default_walk_delay,              300,    0,      INT_MAX,        },
 	{ "no_skill_delay",                     &battle_config.no_skill_delay,                  BL_MOB, BL_NUL, BL_ALL,         },
-	{ "attack_walk_delay",                  &battle_config.attack_walk_delay,               BL_ALL, BL_NUL, BL_ALL,         },
+	{ "attack_walk_delay",                  &battle_config.attack_walk_delay,               BL_NUL, BL_NUL, BL_ALL,         },
 	{ "require_glory_guild",                &battle_config.require_glory_guild,             0,      0,      1,              },
 	{ "idle_no_share",                      &battle_config.idle_no_share,                   0,      0,      INT_MAX,        },
 	{ "party_even_share_bonus",             &battle_config.party_even_share_bonus,          0,      0,      INT_MAX,        },
@@ -11795,7 +11795,7 @@ static const struct _battle_data {
 	{ "display_hallucination",              &battle_config.display_hallucination,           1,      0,      1,              },
 	{ "use_statpoint_table",                &battle_config.use_statpoint_table,             1,      0,      1,              },
 	{ "debuff_on_logout",                   &battle_config.debuff_on_logout,                0,      0,      1|2,            },
-	{ "monster_ai",                         &battle_config.mob_ai,                          0x0000, 0x0000, 0x1FFF,         },
+	{ "monster_ai",                         &battle_config.mob_ai,                          0x0000, 0x0000, 0x3FFF,         },
 	{ "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,              },

+ 19 - 8
src/map/mob.cpp

@@ -4050,19 +4050,13 @@ bool mob_chat_display_message(mob_data &md, uint16 msg_id) {
 	return false;
 }
 
-
 /**
- * This function handles actions that should be done at the end of a skill
+ * This function sets the monster skill delays
  * This needs to happen whether the skill was cast successfully or cast-cancelled
- * It handles setting the skill delays and the normal attack wait time
  * @param bl: Mob data
  */
-void mobskill_end(mob_data& md, t_tick tick)
+void mobskill_delay(mob_data& md, t_tick tick)
 {
-	// After a skill a monster cannot attack for its attack delay
-	// We make sure to not reduce it in case it was set by a skill for another purpose
-	md.ud.attackabletime = i64max(tick + md.status.adelay, md.ud.attackabletime);
-
 	// If skill was used by a script, do not apply any skill delay
 	if (md.skill_idx < 0)
 		return;
@@ -4368,6 +4362,23 @@ int32 mobskill_event(struct mob_data *md, struct block_list *src, t_tick tick, i
 	return res;
 }
 
+/**
+ * This function sets the monster-specific delays based on the delay event
+ * @param md Monster to apply delays to
+ * @param tick Current tick
+ * @param event The event that resulted in calling this function
+ */
+void mob_set_delay(mob_data& md, t_tick tick, e_delay_event event)
+{
+	// A monster's AI is inactive for its attack motion after attacking or finish casting a skill
+	if (!(battle_config.mob_ai&0x2000) && (event == DELAY_EVENT_ATTACK || event == DELAY_EVENT_CASTEND))
+		md.next_thinktime = i64max(tick + md.status.amotion, md.next_thinktime);
+
+	// On cast end and cast cancel, the monster skill delays are set
+	if (event == DELAY_EVENT_CASTEND || event == DELAY_EVENT_CASTCANCEL)
+		mobskill_delay(md, tick);
+}
+
 // Player cloned mobs. [Valaris]
 int32 mob_is_clone(int32 mob_id)
 {

+ 2 - 1
src/map/mob.hpp

@@ -547,9 +547,10 @@ int32 mob_warpslave(struct block_list *bl, int32 range);
 int32 mob_linksearch(struct block_list *bl,va_list ap);
 
 bool mob_chat_display_message (mob_data &md, uint16 msg_id);
-void mobskill_end(mob_data& md, t_tick tick);
+void mobskill_delay(mob_data& md, t_tick tick);
 bool mobskill_use(struct mob_data *md,t_tick tick,int32 event, int64 damage = 0);
 int32 mobskill_event(struct mob_data *md,struct block_list *src,t_tick tick, int32 flag, int64 damage = 0);
+void mob_set_delay(mob_data& md, t_tick tick, e_delay_event event);
 int32 mob_summonslave(struct mob_data *md2,int32 *value,int32 amount,uint16 skill_id);
 int32 mob_countslave(struct block_list *bl);
 int32 mob_count_sub(struct block_list *bl, va_list ap);

+ 2 - 14
src/map/skill.cpp

@@ -13934,16 +13934,10 @@ TIMER_FUNC(skill_castend_id){
 			break;
 
 		// These actions happen even if the skill fails except when the caster is already dead
+		unit_set_attackdelay(*src, tick, DELAY_EVENT_CASTEND);
 		if (md != nullptr) {
-			// When a monster uses a skill, its AI will be inactive for its attack motion
-			// This is also the reason why it doesn't move during this time
-			md->next_thinktime = tick + status_get_amotion(src);
-
 			if (md->skill_idx >= 0 && md->db->skill[md->skill_idx]->emotion >= ET_SURPRISE && md->db->skill[md->skill_idx]->emotion < ET_MAX)
 				clif_emotion(*src, static_cast<emotion_type>(md->db->skill[md->skill_idx]->emotion));
-
-			// Sets cooldowns and attack delay
-			mobskill_end(*md, tick);
 		}
 
 		if (!target || target->prev == nullptr)
@@ -14312,16 +14306,10 @@ TIMER_FUNC(skill_castend_pos){
 			break;
 
 		// These actions happen even if the skill fails except when the caster is already dead
+		unit_set_attackdelay(*src, tick, DELAY_EVENT_CASTEND);
 		if (md != nullptr) {
-			// When a monster uses a skill, its AI will be inactive for its attack motion
-			// This is also the reason why it doesn't move during this time
-			md->next_thinktime = tick + status_get_amotion(src);
-
 			if (md->skill_idx >= 0 && md->db->skill[md->skill_idx]->emotion >= ET_SURPRISE && md->db->skill[md->skill_idx]->emotion < ET_MAX)
 				clif_emotion(*src, static_cast<emotion_type>(md->db->skill[md->skill_idx]->emotion));
-
-			// Sets cooldowns and attack delay
-			mobskill_end(*md, tick);
 		}
 
 		if (!skill_pos_maxcount_check(src, ud->skillx, ud->skilly, ud->skill_id, ud->skill_lv, src->type, true))

+ 9 - 11
src/map/unit.cpp

@@ -1835,7 +1835,7 @@ TIMER_FUNC(unit_resume_running){
 /**
  * Sets the delays that prevent attacks and skill usage considering the bl type
  * Makes sure that delays are not decreased in case they are already higher
- * Currently this function only handles normal attacks, cast begin and parry events
+ * Will also invoke bl type specific delay functions when required
  * @param bl Object to apply attack delay to
  * @param tick Current tick
  * @param event The event that resulted in calling this function
@@ -1874,6 +1874,8 @@ void unit_set_attackdelay(block_list& bl, t_tick tick, e_delay_event event)
 		case BL_MOB:
 			switch (event) {
 				case DELAY_EVENT_ATTACK:
+				case DELAY_EVENT_CASTEND:
+				case DELAY_EVENT_CASTCANCEL:
 					// This represents setting of attack delay (recharge time) that happens for non-PCs
 					attack_delay = status_get_adelay(&bl);
 					break;
@@ -1882,6 +1884,8 @@ void unit_set_attackdelay(block_list& bl, t_tick tick, e_delay_event event)
 					// When monsters use skills, they only get delays on cast end and cast cancel
 					break;
 			}
+			// Set monster-specific delays (inactive AI time, monster skill delays)
+			mob_set_delay(reinterpret_cast<mob_data&>(bl), tick, event);
 			break;
 		case BL_HOM:
 			switch (event) {
@@ -3260,13 +3264,8 @@ static int32 unit_attack_timer_sub(struct block_list* src, int32 tid, t_tick tic
 			ud->skill_id = 0;
 
 		// You can't move during your attack motion
-		if (src->type&battle_config.attack_walk_delay) {
-			// Monsters set their AI inactive instead of having a walkdelay
-			if (md != nullptr)
-				md->next_thinktime = tick + status_get_amotion(src);
-			else
-				unit_set_walkdelay(src, tick, sstatus->amotion, 1);
-		}
+		if (src->type&battle_config.attack_walk_delay)
+			unit_set_walkdelay(src, tick, sstatus->amotion, 1);
 	}
 
 	if(ud->state.attack_continue) {
@@ -3392,11 +3391,10 @@ int32 unit_skillcastcancel(struct block_list *bl, char type)
 		}
 	}
 
+	unit_set_attackdelay(*bl, tick, DELAY_EVENT_CASTCANCEL);
+
 	if (bl->type == BL_MOB) {
 		mob_data& md = reinterpret_cast<mob_data&>(*bl);
-		// Sets cooldowns and attack delay
-		// This needs to happen even if the cast was cancelled
-		mobskill_end(md, tick);
 		md.skill_idx = -1;
 	}