277 lines
8.5 KiB
C++
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
|