Files
scummvm-cursorfix/engines/ultima/ultima8/misc/direction_util.h
2026-02-02 04:50:13 +01:00

277 lines
8.5 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_MISC_DIRECTIONUTIL_H
#define ULTIMA8_MISC_DIRECTIONUTIL_H
#include "ultima/ultima8/misc/direction.h"
#include "ultima/ultima8/ultima8.h"
#include "math/utils.h"
namespace Ultima {
namespace Ultima8 {
/*
* Tables to map a Direction to x/y deltas
*/
inline int Direction_XFactor(Direction dir) {
static const int _x_fact[] = { 0, +1, +1, +1, 0, -1, -1, -1 };
static const int _x_fact16[] = { 0, +1, +1, +2, +1, +2, +1, +1, 0, -1, -1, -2, -1, -2, -1, -1, 0 };
assert((int)dir >= 0 && (int)dir < 16);
if (GAME_IS_U8)
return _x_fact[(int)dir / 2];
else
return _x_fact16[(int)dir];
}
inline int Direction_YFactor(Direction dir) {
static const int _y_fact[] = { -1, -1, 0, +1, +1, +1, 0, -1 };
static const int _y_fact16[] = { -1, -2, -1, -1, 0, +1, +1, +2, +1, +2, +1, +1, 0, -1, -1, -2, 0 };
assert((int)dir >= 0 && (int)dir < 16);
if (GAME_IS_U8)
return _y_fact[(int)dir / 2];
else
return _y_fact16[(int)dir];
}
//! Convert a direction to hundreths of degrees (rotated by 90 degrees)
inline int32 Direction_ToCentidegrees(Direction dir) {
return static_cast<int>(dir) * 2250;
}
//! Convert from centidegrees to a direction.
inline Direction Direction_FromCentidegrees(int32 cds) {
return static_cast<Direction>(((cds + 1125) / 2250) % 16);
}
/**
* Return the direction for a given slope (0-7).
* NOTE: Assumes cartesian coords, NOT screen coords. (which have y
* growing downwards).
*
* NOTE: The returned direction is rotated 45 degrees clockwise! This is
* how U8 things should be.
*/
inline Direction Direction_Get(int deltay, int deltax, DirectionMode dirmode) {
if (deltax == 0)
return deltay > 0 ? dir_northwest : dir_southeast;
if (dirmode == dirmode_8dirs) {
int dydx = (1024 * deltay) / deltax; // Figure 1024*tan.
if (dydx >= 0)
if (deltax > 0) // Top-right
return dydx <= 424 ? dir_northeast : dydx <= 2472 ? dir_north
: dir_northwest;
else // Bottom-left.
return dydx <= 424 ? dir_southwest : dydx <= 2472 ? dir_south
: dir_southeast;
else if (deltax > 0) // Bottom-right.
return dydx >= -424 ? dir_northeast : dydx >= -2472 ? dir_east
: dir_southeast;
else // Top-left
return dydx >= -424 ? dir_southwest : dydx >= -2472 ? dir_west
: dir_northwest;
} else {
double angle = Math::rad2deg(atan2(deltay, deltax));
if (angle < -168.75) return dir_southwest;
else if (angle < -146.25) return dir_ssw;
else if (angle < -123.75) return dir_south;
else if (angle < -101.25) return dir_sse;
else if (angle < -78.75) return dir_southeast;
else if (angle < -56.25) return dir_ese;
else if (angle < -33.75) return dir_east;
else if (angle < -11.25) return dir_ene;
else if (angle < 11.25) return dir_northeast;
else if (angle < 33.75) return dir_nne;
else if (angle < 56.25) return dir_north;
else if (angle < 78.75) return dir_nnw;
else if (angle < 101.25) return dir_northwest;
else if (angle < 123.75) return dir_wnw;
else if (angle < 146.25) return dir_west;
else if (angle < 168.75) return dir_wsw;
return dir_southwest;
}
}
// Note that for WorldDir, Y goes down, so a positive Y points south.
inline Direction Direction_GetWorldDir(int deltay, int deltax, DirectionMode dirmode) {
if (deltax == 0) {
if (deltay == 0) return dir_northeast; // for better compatibility with U8
return deltay > 0 ? dir_south : dir_north;
}
if (dirmode == dirmode_8dirs) {
int dydx = (1024 * deltay) / deltax;
if (dydx >= 0)
if (deltax > 0) // south-east
return dydx <= 424 ? dir_east : dydx <= 2472 ? dir_southeast : dir_south;
else // north-west
return dydx <= 424 ? dir_west : dydx <= 2472 ? dir_northwest : dir_north;
else if (deltax > 0) // north-east
return dydx >= -424 ? dir_east : dydx >= -2472 ? dir_northeast : dir_north;
else // south-west
return dydx >= -424 ? dir_west : dydx >= -2472 ? dir_southwest : dir_south;
} else {
double angle = Math::rad2deg(atan2(deltay, deltax));
if (angle < -168.75) return dir_west;
else if (angle < -146.25) return dir_wnw;
else if (angle < -123.75) return dir_northwest;
else if (angle < -101.25) return dir_nnw;
else if (angle < -78.75) return dir_north;
else if (angle < -56.25) return dir_nne;
else if (angle < -33.75) return dir_northeast;
else if (angle < -11.25) return dir_ene;
else if (angle < 11.25) return dir_east;
else if (angle < 33.75) return dir_ese;
else if (angle < 56.25) return dir_southeast;
else if (angle < 78.75) return dir_sse;
else if (angle < 101.25) return dir_south;
else if (angle < 123.75) return dir_ssw;
else if (angle < 146.25) return dir_southwest;
else if (angle < 168.75) return dir_wsw;
return dir_west;
}
}
inline Direction Direction_GetWorldDirInRange(int deltay, int deltax, DirectionMode dirmode, Direction mindir, Direction maxdir) {
int ndirs = (dirmode == dirmode_8dirs ? 8 : 16);
Direction dir = Direction_GetWorldDir(deltay, deltax, dirmode);
if ((dir < mindir) || (dir > maxdir)) {
int32 dmin1 = dir - mindir;
int32 dmin2 = mindir - dir;
if (dmin1 < 0) {
dmin1 = dmin1 + ndirs;
}
if (dmin2 < 0) {
dmin2 = dmin2 + ndirs;
}
int32 dist_to_min = MIN(dmin1, dmin2);
int dmax1 = dir - maxdir;
int dmax2 = maxdir - dir;
if (dmax1 < 0) {
dmax1 = dmax1 + ndirs;
}
if (dmax2 < 0) {
dmax2 = dmax2 + ndirs;
}
int32 dist_to_max = MIN(dmax1, dmax2);
if (dist_to_min < dist_to_max) {
return mindir;
} else {
return maxdir;
}
}
return dir;
}
inline Direction Direction_Invert(Direction dir) {
assert(dir != dir_current);
switch (dir) {
case dir_north: return dir_south;
case dir_nne: return dir_ssw;
case dir_northeast: return dir_southwest;
case dir_ene: return dir_wsw;
case dir_east: return dir_west;
case dir_ese: return dir_wnw;
case dir_southeast: return dir_northwest;
case dir_sse: return dir_nnw;
case dir_south: return dir_north;
case dir_ssw: return dir_nne;
case dir_southwest: return dir_northeast;
case dir_wsw: return dir_ene;
case dir_west: return dir_east;
case dir_wnw: return dir_ese;
case dir_northwest: return dir_southeast;
case dir_nnw: return dir_sse;
default: return dir_north;
}
}
//! Return the direction one left (aka counter-clockwise) of the input
inline Direction Direction_OneLeft(Direction dir, DirectionMode mode) {
if (mode == dirmode_8dirs)
return static_cast<Direction>((static_cast<int>(dir) + 14) % 16);
else
return static_cast<Direction>((static_cast<int>(dir) + 15) % 16);
}
//! Return the direction one right (aka clockwise) of the input
inline Direction Direction_OneRight(Direction dir, DirectionMode mode) {
if (mode == dirmode_8dirs)
return static_cast<Direction>((static_cast<int>(dir) + 2) % 16);
else
return static_cast<Direction>((static_cast<int>(dir) + 1) % 16);
}
inline Direction Direction_TurnByDelta(Direction dir, int delta, DirectionMode mode) {
if (delta > 0) {
for (int i = 0; i < delta; i++)
dir = Direction_OneRight(dir, mode);
} else if (delta < 0) {
for (int i = 0; i < -delta; i++)
dir = Direction_OneLeft(dir, mode);
}
return dir;
}
//! Get a turn delta (-1 for left, +1 for right) to turn the fastest
//! way from one direction to another
inline int Direction_GetShorterTurnDelta(Direction from, Direction to) {
if ((from - to + 16) % 16 < 8)
return -1;
return 1;
}
inline uint32 Direction_ToUsecodeDir(Direction dir) {
if (GAME_IS_U8) {
return static_cast<int32>(dir / 2);
} else {
return static_cast<int32>(dir);
}
}
inline Direction Direction_FromUsecodeDir(uint32 dir) {
if (GAME_IS_U8) {
return static_cast<Direction>(dir * 2);
} else {
return static_cast<Direction>(dir);
}
}
} // End of namespace Ultima8
} // End of namespace Ultima
#endif