Browse Source

Improved Solution for Exact Position (#9134)

- Improved the solution to fetch the exact position of a unit (see 3cb7c9f)
- This makes use of a private struct inside unit_data that remembers the last calculated exact coordinates
- Recalculation only happens when 20 or more milliseconds (1 frame) have passed since the last calculation
- Access to the coordinates is only possible through helper functions that make sure the data is up-to-date

---------

Co-authored-by: Lemongrass3110
Playtester 2 months ago
parent
commit
391850c4ab
3 changed files with 86 additions and 62 deletions
  1. 1 1
      src/map/mob.cpp
  2. 71 58
      src/map/unit.cpp
  3. 14 3
      src/map/unit.hpp

+ 1 - 1
src/map/mob.cpp

@@ -2054,7 +2054,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, t_tick tick)
 		int32 loot_range = 0;
 		if (md->ud.walktimer != INVALID_TIMER) {
 			// Ready to loot
-			if (unit_getx(md->bl, tick) == tbl->x && unit_gety(md->bl, tick) == tbl->y)
+			if (md->ud.getx(tick) == tbl->x && md->ud.gety(tick) == tbl->y)
 				loot_range = 1;
 			// Already moving to target item
 			else if (md->ud.target == tbl->id)

+ 71 - 58
src/map/unit.cpp

@@ -44,6 +44,12 @@ using namespace rathena;
 	#define MAX_SHADOW_SCAR 100 /// Max Shadow Scars
 #endif
 
+// How many milliseconds need to pass before we calculated the exact position of a unit
+// Calculation will only happen on demand and when at least the time defined here has passed
+#ifndef MIN_POS_INTERVAL
+	#define MIN_POS_INTERVAL 20
+#endif
+
 // Directions values
 // 1 0 7
 // 2 . 6
@@ -1488,101 +1494,108 @@ int32 unit_warp(struct block_list *bl,int16 m,int16 x,int16 y,clr_type type)
  * Calculates the exact coordinates of a bl considering the walktimer
  * This is needed because we only update X/Y when finishing movement to the next cell
  * Officially, however, the coordinates update when crossing the border to the next cell
- * @param bl: Object to get the coordinates of
+ * The coordinates are stored in unit_data.pos together with the tick based on which they were calculated
+ * Access to these coordinates is only allowed through corresponding unit_data class functions
+ * This function makes sure calculation only happens when it's needed to save performance
  * @param tick: Tick based on which we calculate the coordinates
- * @param x: Will be set to the exact X value of the bl
- * @param y: Will be set to the exact Y value of the bl
- * @param sx: Will be set to the exact subcell X value of the bl
- * @param sy: Will be set to the exact subcell Y value of the bl
- * @return Whether the coordinates were set (true) or retrieving them failed (false)
  */
-bool unit_pos(block_list& bl, t_tick tick, int16 &x, int16 &y, uint8 &sx, uint8 &sy)
+void unit_data::update_pos(t_tick tick)
 {
-	unit_data* ud = unit_bl2ud(&bl);
+	// Check if coordinates are still up-to-date
+	if (DIFF_TICK(tick, this->pos.tick) < MIN_POS_INTERVAL)
+		return;
 
-	if (ud == nullptr)
-		return false;
+	if (this->bl == nullptr)
+		return;
 
-	if (ud->walkpath.path_pos >= ud->walkpath.path_len)
-		return false;
+	// Set initial coordinates
+	this->pos.x = this->bl->x;
+	this->pos.y = this->bl->y;
+	this->pos.sx = 8;
+	this->pos.sy = 8;
 
-	if (ud->walktimer == INVALID_TIMER)
-		return false;
+	// Remember time at which we did the last calculation
+	this->pos.tick = tick;
 
-	const TimerData* td = get_timer(ud->walktimer);
+	if (this->walkpath.path_pos >= this->walkpath.path_len)
+		return;
 
-	if (td == nullptr)
-		return false;
+	if (this->walktimer == INVALID_TIMER)
+		return;
 
-	// Set initial coordinates
-	x = bl.x;
-	y = bl.y;
-	sx = 8;
-	sy = 8;
+	const TimerData* td = get_timer(this->walktimer);
+
+	if (td == nullptr)
+		return;
 
 	// Get how much percent we traversed on the timer
-	double cell_percent = 1.0 - ((double)DIFF_TICK(td->tick, gettick()) / (double)td->data);
+	double cell_percent = 1.0 - ((double)DIFF_TICK(td->tick, tick) / (double)td->data);
 
 	if (cell_percent > 0.0 && cell_percent < 1.0) {
 		// Set subcell coordinates according to timer
 		// This gives a value between 8 and 39
-		sx = static_cast<uint8>(24.0 + dirx[ud->walkpath.path[ud->walkpath.path_pos]] * 16.0 * cell_percent);
-		sy = static_cast<uint8>(24.0 + diry[ud->walkpath.path[ud->walkpath.path_pos]] * 16.0 * cell_percent);
+		this->pos.sx = static_cast<uint8>(24.0 + dirx[this->walkpath.path[this->walkpath.path_pos]] * 16.0 * cell_percent);
+		this->pos.sy = static_cast<uint8>(24.0 + diry[this->walkpath.path[this->walkpath.path_pos]] * 16.0 * cell_percent);
 		// 16-31 reflect sub position 0-15 on the current cell
 		// 8-15 reflect sub position 8-15 at -1 main coordinate
 		// 32-39 reflect sub position 0-7 at +1 main coordinate
-		if (sx < 16 || sy < 16 || sx > 31 || sy > 31) {
-			if (sx < 16) x--;
-			if (sy < 16) y--;
-			if (sx > 31) x++;
-			if (sy > 31) y++;
+		if (this->pos.sx < 16 || this->pos.sy < 16 || this->pos.sx > 31 || this->pos.sy > 31) {
+			if (this->pos.sx < 16) this->pos.x--;
+			if (this->pos.sy < 16) this->pos.y--;
+			if (this->pos.sx > 31) this->pos.x++;
+			if (this->pos.sy > 31) this->pos.y++;
 		}
-		sx %= 16;
-		sy %= 16;
+		this->pos.sx %= 16;
+		this->pos.sy %= 16;
 	}
 	else if (cell_percent >= 1.0) {
 		// Assume exactly one cell moved
-		x += dirx[ud->walkpath.path[ud->walkpath.path_pos]];
-		y += diry[ud->walkpath.path[ud->walkpath.path_pos]];
+		this->pos.x += dirx[this->walkpath.path[this->walkpath.path_pos]];
+		this->pos.y += diry[this->walkpath.path[this->walkpath.path_pos]];
 	}
-
-	return true;
 }
 
 /**
  * Helper function to get the exact X coordinate
- * @param bl: Object to get the X coordinate of
+ * This ensures that the coordinate is calculated when needed
  * @param tick: Tick based on which we calculate the coordinate
  * @return The exact X coordinate
  */
-int16 unit_getx(block_list& bl, t_tick tick) {
-	int16 x, y;
-	uint8 sx, sy;
-
-	// Get exact coordinates
-	if (unit_pos(bl, tick, x, y, sx, sy))
-		return x;
-
-	// If above failed, object is probably not moving, so just return current X
-	return bl.x;
+int16 unit_data::getx(t_tick tick) {
+	// Make sure exact coordinates are up-to-date
+	this->update_pos(tick);
+	return this->pos.x;
 }
 
 /**
  * Helper function to get the exact Y coordinate
- * @param bl: Object to get the Y coordinate of
+ * This ensures that the coordinate is calculated when needed
  * @param tick: Tick based on which we calculate the coordinate
  * @return The exact Y coordinate
  */
-int16 unit_gety(block_list& bl, t_tick tick) {
-	int16 x, y;
-	uint8 sx, sy;
-
-	// Get exact coordinates
-	if (unit_pos(bl, tick, x, y, sx, sy))
-		return y;
+int16 unit_data::gety(t_tick tick) {
+	// Make sure exact coordinates are up-to-date
+	this->update_pos(tick);
+	return this->pos.y;
+}
 
-	// If above failed, object is probably not moving, so just return current Y
-	return bl.y;
+/**
+ * Helper function to get exact coordinates
+ * This ensures that the coordinates are calculated when needed
+ * @param x: Will be set to the exact X value of the bl
+ * @param y: Will be set to the exact Y value of the bl
+ * @param sx: Will be set to the exact subcell X value of the bl
+ * @param sy: Will be set to the exact subcell Y value of the bl
+ * @param tick: Tick based on which we calculate the coordinate
+ * @return The exact Y coordinate
+ */
+void unit_data::getpos(int16 &x, int16 &y, uint8 &sx, uint8 &sy, t_tick tick) {
+	// Make sure exact coordinates are up-to-date
+	this->update_pos(tick);
+	x = this->pos.x;
+	y = this->pos.y;
+	sx = this->pos.sx;
+	sy = this->pos.sy;
 }
 
 /**
@@ -1608,7 +1621,7 @@ void unit_stop_walking_soon(struct block_list& bl, t_tick tick)
 	}
 	else {
 		// Set coordinates to exact coordinates
-		unit_pos(bl, tick, bl.x, bl.y, ud->sx, ud->sy);
+		ud->getpos(bl.x, bl.y, ud->sx, ud->sy, tick);
 
 		// If x or y already changed, we need to move one more cell
 		if (ox != bl.x || oy != bl.y)

+ 14 - 3
src/map/unit.hpp

@@ -64,6 +64,20 @@ struct unit_data {
 	int32 group_id;
 
 	std::vector<int32> shadow_scar_timer;
+
+	// Functions and struct to calculate and store exact position at a certain tick
+	int16 getx(t_tick tick);
+	int16 gety(t_tick tick);
+	void getpos(int16 &x, int16 &y, uint8 &sx, uint8 &sy, t_tick tick);
+private:
+	void update_pos(t_tick tick);
+	struct {
+		int16 x;
+		int16 y;
+		uint8 sx;
+		uint8 sy;
+		t_tick tick;
+	} pos;
 };
 
 struct view_data {
@@ -116,9 +130,6 @@ int32 unit_calc_pos(struct block_list *bl, int32 tx, int32 ty, uint8 dir);
 TIMER_FUNC(unit_delay_walktoxy_timer);
 TIMER_FUNC(unit_delay_walktobl_timer);
 
-int16 unit_getx(block_list& bl, t_tick tick = gettick());
-int16 unit_gety(block_list& bl, t_tick tick = gettick());
-
 void unit_stop_walking_soon(struct block_list& bl, t_tick tick = gettick());
 // Causes the target object to stop moving.
 bool unit_stop_walking( block_list* bl, int32 type, t_tick canmove_delay = 0 );