Browse Source

Fixes script commands unitwalkto/unitwalk (#7007)

* Fixes #5340.
* Resolves an issue where monsters may tend to lose their target if they are too slow and the destination is too far when using script commands unitwalkto/unitwalk.
* Allow script command unitstopwalk to remove the state tracking for script commands unitwalk/unitwalkto.
* Print a warning to the console if a unit is forced to walk again and hasn't yet reached its initial destination.
Thanks to @NeutralDev and @Lemongrass3110!
Aleos 2 năm trước cách đây
mục cha
commit
aad3a70bbc
5 tập tin đã thay đổi với 44 bổ sung7 xóa
  1. 2 0
      doc/script_commands.txt
  2. 8 0
      src/map/mob.cpp
  3. 29 5
      src/map/script.cpp
  4. 4 2
      src/map/unit.cpp
  5. 1 0
      src/map/unit.hpp

+ 2 - 0
doc/script_commands.txt

@@ -8270,6 +8270,8 @@ values (can be combined using the pipe operator):
 	USW_MOVE_FULL_CELL = Enable moving to the next cell when unit was already half-way there (may cause on-touch/place side-effects, such as a scripted map change).
 	USW_FORCE_STOP = Force stop moving.
 
+This command will also remove the state tracking used for 'unitwalk' and 'unitwalkto'.
+
 ---------------------------------------
 
 *unittalk <GID>,"<text>"{,flag};

+ 8 - 0
src/map/mob.cpp

@@ -1687,6 +1687,10 @@ static bool mob_ai_sub_hard(struct mob_data *md, t_tick tick)
 	if(md->bl.prev == nullptr || md->status.hp == 0)
 		return false;
 
+	// Monsters force-walked by script commands should not be searching for targets.
+	if (md->ud.state.force_walk)
+		return false;
+
 	if (DIFF_TICK(tick, md->last_thinktime) < MIN_MOBTHINKTIME)
 		return false;
 
@@ -2005,6 +2009,10 @@ static int mob_ai_sub_lazy(struct mob_data *md, va_list args)
 	if(md->bl.prev == NULL)
 		return 0;
 
+	// Monsters force-walked by script commands should not be searching for targets.
+	if (md->ud.state.force_walk)
+		return false;
+
 	t_tick tick = va_arg(args,t_tick);
 
 	if (battle_config.mob_ai&0x20 && map_getmapdata(md->bl.m)->users>0)

+ 29 - 5
src/map/script.cpp

@@ -19310,6 +19310,13 @@ BUILDIN_FUNC(unitwalk)
 
 	ud = unit_bl2ud(bl);
 
+	// Unit was already forced to walk.
+	if (ud != nullptr && ud->state.force_walk) {
+		script_pushint(st, 0);
+		ShowWarning("buildin_%s: Unit has already been forced to walk and not reached it's destination yet.\n", cmd);
+		return SCRIPT_CMD_FAILURE;
+	}
+
 	if (bl->type == BL_NPC) {
 		if (!((TBL_NPC*)bl)->status.hp)
 			status_calc_npc(((TBL_NPC*)bl), SCO_FIRST);
@@ -19321,8 +19328,11 @@ BUILDIN_FUNC(unitwalk)
 		int x = script_getnum(st,3);
 		int y = script_getnum(st,4);
 
-		if (script_pushint(st, unit_can_reach_pos(bl,x,y,0)))
+		if (script_pushint(st, unit_can_reach_pos(bl,x,y,0))) {
+			if (ud != nullptr)
+				ud->state.force_walk = true;
 			add_timer(gettick()+50, unit_delay_walktoxy_timer, bl->id, (x<<16)|(y&0xFFFF)); // Need timer to avoid mismatches
+		}
 	} else {
 		struct block_list* tbl = map_id2bl(script_getnum(st,3));
 
@@ -19330,8 +19340,11 @@ BUILDIN_FUNC(unitwalk)
 			ShowError("buildin_unitwalk: Bad target destination.\n");
 			script_pushint(st, 0);
 			return SCRIPT_CMD_FAILURE;
-		} else if (script_pushint(st, unit_can_reach_bl(bl, tbl, distance_bl(bl, tbl)+1, 0, NULL, NULL)))
+		} else if (script_pushint(st, unit_can_reach_bl(bl, tbl, distance_bl(bl, tbl)+1, 0, NULL, NULL))) {
+			if (ud != nullptr)
+				ud->state.force_walk = true;
 			add_timer(gettick()+50, unit_delay_walktobl_timer, bl->id, tbl->id); // Need timer to avoid mismatches
+		}
 		off = 4;
 	}
 
@@ -19477,10 +19490,21 @@ BUILDIN_FUNC(unitstopwalk)
 	if (script_hasdata(st, 3))
 		flag = script_getnum(st, 3);
 
-	if(script_rid2bl(2,bl))
-		unit_stop_walking(bl, flag);
+	if(script_rid2bl(2,bl)) {
+		unit_data *ud = unit_bl2ud(bl);
 
-	return SCRIPT_CMD_SUCCESS;
+		if (ud != nullptr)
+			ud->state.force_walk = false;
+
+		if (unit_stop_walking(bl, flag) == 0 && flag != USW_FORCE_STOP) {
+			ShowWarning("buildin_unitstopwalk: Unable to find unit or unit is not walking.\n");
+			return SCRIPT_CMD_FAILURE;
+		}
+
+		return SCRIPT_CMD_SUCCESS;
+	} else {
+		return SCRIPT_CMD_FAILURE;
+	}
 }
 
 /**

+ 4 - 2
src/map/unit.cpp

@@ -435,7 +435,7 @@ static TIMER_FUNC(unit_walktoxy_timer)
 
 	//Monsters can only leave icewalls to the west and south
 	//But if movement fails more than icewall_walk_block times, they can ignore this rule
-	if(md && md->walktoxy_fail_count < icewall_walk_block && map_getcell(bl->m,x,y,CELL_CHKICEWALL) && (dx > 0 || dy > 0)) {
+	if(md && !ud->state.force_walk && md->walktoxy_fail_count < icewall_walk_block && map_getcell(bl->m,x,y,CELL_CHKICEWALL) && (dx > 0 || dy > 0)) {
 		//Needs to be done here so that rudeattack skills are invoked
 		md->walktoxy_fail_count++;
 		clif_fixpos(bl);
@@ -472,6 +472,8 @@ static TIMER_FUNC(unit_walktoxy_timer)
 		}
 #endif
 
+		ud->state.force_walk = false;
+
 		if (ud->walk_done_event[0]){
 			char walk_done_event[EVENT_NAME_LENGTH];
 
@@ -526,7 +528,7 @@ static TIMER_FUNC(unit_walktoxy_timer)
 				md->min_chase--;
 			// Walk skills are triggered regardless of target due to the idle-walk mob state.
 			// But avoid triggering on stop-walk calls.
-			if(tid != INVALID_TIMER &&
+			if(!ud->state.force_walk && tid != INVALID_TIMER &&
 				!(ud->walk_count%WALK_SKILL_INTERVAL) &&
 				map[bl->m].users > 0 &&
 				mobskill_use(md, tick, -1)) {

+ 1 - 0
src/map/unit.hpp

@@ -58,6 +58,7 @@ struct unit_data {
 		unsigned blockedmove : 1;
 		unsigned blockedskill : 1;
 		unsigned ignore_cell_stack_limit : 1;
+		bool force_walk; ///< Used with script commands unitwalk/unitwalkto. Disables monster idle and random walk.
 	} state;
 	char walk_done_event[EVENT_NAME_LENGTH];
 	char title[NAME_LENGTH];