6 * @brief 2点間の距離をニュートン・ラプソン法で算出する / Distance between two points via Newton-Raphson technique
13 POSITION distance(POSITION y1, POSITION x1, POSITION y2, POSITION x2)
15 POSITION dy = (y1 > y2) ? (y1 - y2) : (y2 - y1);
16 POSITION dx = (x1 > x2) ? (x1 - x2) : (x2 - x1);
18 /* Squared distance */
19 POSITION target = (dy * dy) + (dx * dx);
21 /* Approximate distance: hypot(dy,dx) = max(dy,dx) + min(dy,dx) / 2 */
22 POSITION d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
27 if (!dy || !dx) return d;
31 /* Approximate error */
32 err = (target - d * d) / (2 * d);
34 /* No error - we are done */
45 * @brief プレイヤーから指定の座標がどの方角にあるかを返す /
46 * Convert an adjacent location to a direction.
51 DIRECTION coords_to_dir(POSITION y, POSITION x)
53 DIRECTION d[3][3] = { {7, 4, 1}, {8, 5, 2}, {9, 6, 3} };
58 if (ABS(dx) > 1 || ABS(dy) > 1) return (0);
60 return d[dx + 1][dy + 1];
64 * @brief 始点から終点への直線経路を返す /
65 * Determine the path taken by a projection.
66 * @param gp 経路座標リストを返す参照ポインタ
76 * The projection will always start from the grid (y1,x1), and will travel
77 * towards the grid (y2,x2), touching one grid per unit of distance along
78 * the major axis, and stopping when it enters the destination grid or a
79 * wall grid, or has travelled the maximum legal distance of "range".
81 * Note that "distance" in this function (as in the "update_view()" code)
82 * is defined as "MAX(dy,dx) + MIN(dy,dx)/2", which means that the player
83 * actually has an "octagon of projection" not a "circle of projection".
85 * The path grids are saved into the grid array pointed to by "gp", and
86 * there should be room for at least "range" grids in "gp". Note that
87 * due to the way in which distance is calculated, this function normally
88 * uses fewer than "range" grids for the projection path, so the result
89 * of this function should never be compared directly to "range". Note
90 * that the initial grid (y1,x1) is never saved into the grid array, not
91 * even if the initial grid is also the final grid.
93 * The "flg" flags can be used to modify the behavior of this function.
95 * In particular, the "PROJECT_STOP" and "PROJECT_THRU" flags have the same
96 * semantics as they do for the "project" function, namely, that the path
97 * will stop as soon as it hits a monster, or that the path will continue
98 * through the destination grid, respectively.
100 * The "PROJECT_JUMP" flag, which for the "project()" function means to
101 * start at a special grid (which makes no sense in this function), means
102 * that the path should be "angled" slightly if needed to avoid any wall
103 * grids, allowing the player to "target" any grid which is in "view".
104 * This flag is non-trivial and has not yet been implemented, but could
105 * perhaps make use of the "vinfo" array (above).
107 * This function returns the number of grids (if any) in the path. This
108 * function will return zero if and only if (y1,x1) and (y2,x2) are equal.
110 * This algorithm is similar to, but slightly different from, the one used
111 * by "update_view_los()", and very different from the one used by "los()".
114 sint project_path(u16b *gp, POSITION range, POSITION y1, POSITION x1, POSITION y2, POSITION x2, BIT_FLAGS flg)
136 /* No path necessary (or allowed) */
137 if ((x1 == x2) && (y1 == y2)) return (0);
165 /* Number of "units" in one "half" grid */
168 /* Number of "units" in one "full" grid */
174 /* Let m = ((dx/dy) * full) = (dx * dx * 2) */
185 /* Advance (X) part 2 */
188 /* Advance (X) part 3 */
195 /* Create the projection path */
199 gp[n++] = GRID(y, x);
201 /* Hack -- Check maximum range */
202 if ((n + (k >> 1)) >= range) break;
204 /* Sometimes stop at destination grid */
205 if (!(flg & (PROJECT_THRU)))
207 if ((x == x2) && (y == y2)) break;
210 if (flg & (PROJECT_DISI))
212 if ((n > 0) && cave_stop_disintegration(y, x)) break;
214 else if (flg & (PROJECT_LOS))
216 if ((n > 0) && !cave_los_bold(y, x)) break;
218 else if (!(flg & (PROJECT_PATH)))
220 /* Always stop at non-initial wall grids */
221 if ((n > 0) && !cave_have_flag_bold(y, x, FF_PROJECT)) break;
224 /* Sometimes stop at non-initial monsters/players */
225 if (flg & (PROJECT_STOP))
228 (player_bold(y, x) || current_floor_ptr->grid_array[y][x].m_idx != 0))
232 if (!in_bounds(y, x)) break;
237 /* Advance (X) part 1 */
240 /* Horizontal change */
243 /* Advance (X) part 2 */
246 /* Advance (X) part 3 */
262 /* Let m = ((dy/dx) * full) = (dy * dy * 2) */
271 /* Vertical change */
274 /* Advance (Y) part 2 */
277 /* Advance (Y) part 3 */
284 /* Create the projection path */
288 gp[n++] = GRID(y, x);
290 /* Hack -- Check maximum range */
291 if ((n + (k >> 1)) >= range) break;
293 /* Sometimes stop at destination grid */
294 if (!(flg & (PROJECT_THRU)))
296 if ((x == x2) && (y == y2)) break;
299 if (flg & (PROJECT_DISI))
301 if ((n > 0) && cave_stop_disintegration(y, x)) break;
303 else if (flg & (PROJECT_LOS))
305 if ((n > 0) && !cave_los_bold(y, x)) break;
307 else if (!(flg & (PROJECT_PATH)))
309 /* Always stop at non-initial wall grids */
310 if ((n > 0) && !cave_have_flag_bold(y, x, FF_PROJECT)) break;
313 /* Sometimes stop at non-initial monsters/players */
314 if (flg & (PROJECT_STOP))
317 (player_bold(y, x) || current_floor_ptr->grid_array[y][x].m_idx != 0))
321 if (!in_bounds(y, x)) break;
326 /* Advance (Y) part 1 */
329 /* Vertical change */
332 /* Advance (Y) part 2 */
335 /* Advance (Y) part 3 */
355 /* Create the projection path */
359 gp[n++] = GRID(y, x);
361 /* Hack -- Check maximum range */
362 if ((n + (n >> 1)) >= range) break;
364 /* Sometimes stop at destination grid */
365 if (!(flg & (PROJECT_THRU)))
367 if ((x == x2) && (y == y2)) break;
370 if (flg & (PROJECT_DISI))
372 if ((n > 0) && cave_stop_disintegration(y, x)) break;
374 else if (flg & (PROJECT_LOS))
376 if ((n > 0) && !cave_los_bold(y, x)) break;
378 else if (!(flg & (PROJECT_PATH)))
380 /* Always stop at non-initial wall grids */
381 if ((n > 0) && !cave_have_flag_bold(y, x, FF_PROJECT)) break;
384 /* Sometimes stop at non-initial monsters/players */
385 if (flg & (PROJECT_STOP))
388 (player_bold(y, x) || current_floor_ptr->grid_array[y][x].m_idx != 0))
392 if (!in_bounds(y, x)) break;
407 * @brief LOS(Line Of Sight / 視線が通っているか)の判定を行う。
412 * @return LOSが通っているならTRUEを返す。
414 * A simple, fast, integer-based line-of-sight algorithm. By Joseph Hall,\n
415 * 4116 Brewster Drive, Raleigh NC 27606. Email to jnh@ecemwl.ncsu.edu.\n
417 * Returns TRUE if a line of sight can be traced from (x1,y1) to (x2,y2).\n
419 * The LOS begins at the center of the tile (x1,y1) and ends at the center of\n
420 * the tile (x2,y2). If los() is to return TRUE, all of the tiles this line\n
421 * passes through must be floor tiles, except for (x1,y1) and (x2,y2).\n
423 * We assume that the "mathematical corner" of a non-floor tile does not\n
424 * block line of sight.\n
426 * Because this function uses (short) ints for all calculations, overflow may\n
427 * occur if dx and dy exceed 90.\n
429 * Once all the degenerate cases are eliminated, the values "qx", "qy", and\n
430 * "m" are multiplied by a scale factor "f1 = abs(dx * dy * 2)", so that\n
431 * we can use integer arithmetic.\n
433 * We travel from start to finish along the longer axis, starting at the border\n
434 * between the first and second tiles, where the y offset = .5 * slope, taking\n
435 * into account the scale factor. See below.\n
437 * Also note that this function and the "move towards target" code do NOT\n
438 * share the same properties. Thus, you can see someone, target them, and\n
439 * then fire a bolt at them, but the bolt may hit a wall, not them. However\n,
440 * by clever choice of target locations, you can sometimes throw a "curve".\n
442 * Note that "line of sight" is not "reflexive" in all cases.\n
444 * Use the "projectable()" routine to test "spell/missile line of sight".\n
446 * Use the "update_view()" function to determine player line-of-sight.\n
448 bool los(POSITION y1, POSITION x1, POSITION y2, POSITION x2)
468 /* Slope, or 1/Slope, of LOS */
472 /* Extract the offset */
476 /* Extract the absolute offset */
481 /* Handle adjacent (or identical) grids */
482 if ((ax < 2) && (ay < 2)) return TRUE;
485 /* Paranoia -- require "safe" origin */
486 /* if (!in_bounds(y1, x1)) return FALSE; */
487 /* if (!in_bounds(y2, x2)) return FALSE; */
490 /* Directly South/North */
493 /* South -- check for walls */
496 for (ty = y1 + 1; ty < y2; ty++)
498 if (!cave_los_bold(ty, x1)) return FALSE;
502 /* North -- check for walls */
505 for (ty = y1 - 1; ty > y2; ty--)
507 if (!cave_los_bold(ty, x1)) return FALSE;
515 /* Directly East/West */
518 /* East -- check for walls */
521 for (tx = x1 + 1; tx < x2; tx++)
523 if (!cave_los_bold(y1, tx)) return FALSE;
527 /* West -- check for walls */
530 for (tx = x1 - 1; tx > x2; tx--)
532 if (!cave_los_bold(y1, tx)) return FALSE;
541 /* Extract some signs */
542 sx = (dx < 0) ? -1 : 1;
543 sy = (dy < 0) ? -1 : 1;
546 /* Vertical "knights" */
551 if (cave_los_bold(y1 + sy, x1)) return TRUE;
555 /* Horizontal "knights" */
560 if (cave_los_bold(y1, x1 + sx)) return TRUE;
565 /* Calculate scale factor div 2 */
568 /* Calculate scale factor */
572 /* Travel horizontally */
575 /* Let m = dy / dx * 2 * (dy * dx) = 2 * dy * dy */
581 /* Consider the special case where slope == 1. */
592 /* Note (below) the case (qy == f2), where */
593 /* the LOS exactly meets the corner of a tile. */
596 if (!cave_los_bold(ty, tx)) return FALSE;
607 if (!cave_los_bold(ty, tx)) return FALSE;
620 /* Travel vertically */
623 /* Let m = dx / dy * 2 * (dx * dy) = 2 * dx * dx */
639 /* Note (below) the case (qx == f2), where */
640 /* the LOS exactly meets the corner of a tile. */
643 if (!cave_los_bold(ty, tx)) return FALSE;
654 if (!cave_los_bold(ty, tx)) return FALSE;
672 * Determine if a bolt spell cast from (y1,x1) to (y2,x2) will arrive
673 * at the final destination, assuming no monster gets in the way.
675 * This is slightly (but significantly) different from "los(y1,x1,y2,x2)".
677 bool projectable(POSITION y1, POSITION x1, POSITION y2, POSITION x2)
684 /* Check the projection path */
685 grid_n = project_path(grid_g, (project_length ? project_length : MAX_RANGE), y1, x1, y2, x2, 0);
688 if (!grid_n) return TRUE;
691 y = GRID_Y(grid_g[grid_n - 1]);
692 x = GRID_X(grid_g[grid_n - 1]);
694 /* May not end in an unrequested grid */
695 if ((y != y2) || (x != x2)) return (FALSE);
704 * Standard "find me a location" function
706 * Obtains a legal location within the given distance of the initial
707 * location, and with "los()" from the source to destination location.
709 * This function is often called from inside a loop which searches for
710 * locations while increasing the "d" distance.
712 * Currently the "m" parameter is unused.
714 void scatter(POSITION *yp, POSITION *xp, POSITION y, POSITION x, POSITION d, BIT_FLAGS mode)
718 /* Pick a location */
721 /* Pick a new location */
722 ny = rand_spread(y, d);
723 nx = rand_spread(x, d);
725 /* Ignore annoying locations */
726 if (!in_bounds(ny, nx)) continue;
728 /* Ignore "excessively distant" locations */
729 if ((d > 1) && (distance(y, x, ny, nx) > d)) continue;
731 if (mode & PROJECT_LOS)
733 if (los(y, x, ny, nx)) break;
737 if (projectable(y, x, ny, nx)) break;
742 /* Save the location */
748 * Calculate "incremental motion". Used by project() and shoot().
749 * Assumes that (*y,*x) lies on the path from (y1,x1) to (y2,x2).
751 void mmove2(POSITION *y, POSITION *x, POSITION y1, POSITION x1, POSITION y2, POSITION x2)
753 POSITION dy, dx, dist, shift;
755 /* Extract the distance travelled */
756 dy = (*y < y1) ? y1 - *y : *y - y1;
757 dx = (*x < x1) ? x1 - *x : *x - x1;
759 /* Number of steps */
760 dist = (dy > dx) ? dy : dx;
762 /* We are calculating the next location */
766 /* Calculate the total distance along each axis */
767 dy = (y2 < y1) ? (y1 - y2) : (y2 - y1);
768 dx = (x2 < x1) ? (x1 - x2) : (x2 - x1);
770 /* Paranoia -- Hack -- no motion */
771 if (!dy && !dx) return;
774 /* Move mostly vertically */
777 /* Extract a shift factor */
778 shift = (dist * dx + (dy - 1) / 2) / dy;
780 /* Sometimes move along the minor axis */
781 (*x) = (x2 < x1) ? (x1 - shift) : (x1 + shift);
783 /* Always move along major axis */
784 (*y) = (y2 < y1) ? (y1 - dist) : (y1 + dist);
787 /* Move mostly horizontally */
790 /* Extract a shift factor */
791 shift = (dist * dy + (dx - 1) / 2) / dx;
793 /* Sometimes move along the minor axis */
794 (*y) = (y2 < y1) ? (y1 - shift) : (y1 + shift);
796 /* Always move along major axis */
797 (*x) = (x2 < x1) ? (x1 - dist) : (x1 + dist);