1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #ifndef ULTIMA8_MISC_DIRECTIONUTIL_H
24 #define ULTIMA8_MISC_DIRECTIONUTIL_H
25
26 #include "ultima/ultima8/misc/direction.h"
27 #include "ultima/ultima8/ultima8.h"
28 #include "common/math.h"
29
30 namespace Ultima {
31 namespace Ultima8 {
32
33 /*
34 * Tables to map a Direction to x/y deltas
35 */
36
37
Direction_XFactor(Direction dir)38 inline int Direction_XFactor(Direction dir) {
39 static const int _x_fact[] = { 0, +1, +1, +1, 0, -1, -1, -1 };
40 static const int _x_fact16[] = { 0, +1, +1, +2, +1, +2, +1, +1, 0, -1, -1, -2, -1, -2, -1, -1, 0 };
41
42 assert((int)dir >= 0 && (int)dir < 16);
43
44 if (GAME_IS_U8)
45 return _x_fact[(int)dir / 2];
46 else
47 return _x_fact16[(int)dir];
48 }
49
Direction_YFactor(Direction dir)50 inline int Direction_YFactor(Direction dir) {
51 static const int _y_fact[] = { -1, -1, 0, +1, +1, +1, 0, -1 };
52 static const int _y_fact16[] = { -1, -2, -1, -1, 0, +1, +1, +2, +1, +2, +1, +1, 0, -1, -1, -2, 0 };
53
54 assert((int)dir >= 0 && (int)dir < 16);
55
56 if (GAME_IS_U8)
57 return _y_fact[(int)dir / 2];
58 else
59 return _y_fact16[(int)dir];
60 }
61
62 //! Convert a direction to hundreths of degrees (rotated by 90 degrees)
Direction_ToCentidegrees(Direction dir)63 inline int32 Direction_ToCentidegrees(Direction dir) {
64 return static_cast<int>(dir) * 2250;
65 }
66
67 //! Convert from centidegrees to a direction.
Direction_FromCentidegrees(int32 cds)68 inline Direction Direction_FromCentidegrees(int32 cds) {
69 return static_cast<Direction>(((cds + 1125) / 2250) % 16);
70 }
71
72
73 /**
74 * Return the direction for a given slope (0-7).
75 * NOTE: Assumes cartesian coords, NOT screen coords. (which have y
76 * growing downwards).
77 *
78 * NOTE: The returned direction is rotated 45 degrees clockwise! This is
79 * how U8 things should be.
80 */
Direction_Get(int deltay,int deltax,DirectionMode dirmode)81 inline Direction Direction_Get(int deltay, int deltax, DirectionMode dirmode) {
82 if (deltax == 0)
83 return deltay > 0 ? dir_northwest : dir_southeast;
84
85 if (dirmode == dirmode_8dirs) {
86 int dydx = (1024 * deltay) / deltax; // Figure 1024*tan.
87 if (dydx >= 0)
88 if (deltax > 0) // Top-right
89 return dydx <= 424 ? dir_northeast : dydx <= 2472 ? dir_north
90 : dir_northwest;
91 else // Bottom-left.
92 return dydx <= 424 ? dir_southwest : dydx <= 2472 ? dir_south
93 : dir_southeast;
94 else if (deltax > 0) // Bottom-right.
95 return dydx >= -424 ? dir_northeast : dydx >= -2472 ? dir_east
96 : dir_southeast;
97 else // Top-left
98 return dydx >= -424 ? dir_southwest : dydx >= -2472 ? dir_west
99 : dir_northwest;
100 } else {
101 double angle = Common::rad2deg(atan2(deltay, deltax));
102 if (angle < -168.75) return dir_southwest;
103 else if (angle < -146.25) return dir_ssw;
104 else if (angle < -123.75) return dir_south;
105 else if (angle < -101.25) return dir_sse;
106 else if (angle < -78.75) return dir_southeast;
107 else if (angle < -56.25) return dir_ese;
108 else if (angle < -33.75) return dir_east;
109 else if (angle < -11.25) return dir_ene;
110 else if (angle < 11.25) return dir_northeast;
111 else if (angle < 33.75) return dir_nne;
112 else if (angle < 56.25) return dir_north;
113 else if (angle < 78.75) return dir_nnw;
114 else if (angle < 101.25) return dir_northwest;
115 else if (angle < 123.75) return dir_wnw;
116 else if (angle < 146.25) return dir_west;
117 else if (angle < 168.75) return dir_wsw;
118 return dir_southwest;
119 }
120 }
121
122 // Note that for WorldDir, Y goes down, so a positive Y points south.
Direction_GetWorldDir(int deltay,int deltax,DirectionMode dirmode)123 inline Direction Direction_GetWorldDir(int deltay, int deltax, DirectionMode dirmode) {
124 if (deltax == 0) {
125 if (deltay == 0) return dir_northeast; // for better compatibility with U8
126 return deltay > 0 ? dir_south : dir_north;
127 }
128
129 if (dirmode == dirmode_8dirs) {
130 int dydx = (1024 * deltay) / deltax;
131
132 if (dydx >= 0)
133 if (deltax > 0) // south-east
134 return dydx <= 424 ? dir_east : dydx <= 2472 ? dir_southeast : dir_south;
135 else // north-west
136 return dydx <= 424 ? dir_west : dydx <= 2472 ? dir_northwest : dir_north;
137 else if (deltax > 0) // north-east
138 return dydx >= -424 ? dir_east : dydx >= -2472 ? dir_northeast : dir_north;
139 else // south-west
140 return dydx >= -424 ? dir_west : dydx >= -2472 ? dir_southwest : dir_south;
141 } else {
142 double angle = Common::rad2deg(atan2(deltay, deltax));
143 if (angle < -168.75) return dir_west;
144 else if (angle < -146.25) return dir_wnw;
145 else if (angle < -123.75) return dir_northwest;
146 else if (angle < -101.25) return dir_nnw;
147 else if (angle < -78.75) return dir_north;
148 else if (angle < -56.25) return dir_nne;
149 else if (angle < -33.75) return dir_northeast;
150 else if (angle < -11.25) return dir_ene;
151 else if (angle < 11.25) return dir_east;
152 else if (angle < 33.75) return dir_ese;
153 else if (angle < 56.25) return dir_southeast;
154 else if (angle < 78.75) return dir_sse;
155 else if (angle < 101.25) return dir_south;
156 else if (angle < 123.75) return dir_ssw;
157 else if (angle < 146.25) return dir_southwest;
158 else if (angle < 168.75) return dir_wsw;
159 return dir_west;
160 }
161 }
162
163
164
Direction_GetWorldDirInRange(int deltay,int deltax,DirectionMode dirmode,Direction mindir,Direction maxdir)165 inline Direction Direction_GetWorldDirInRange(int deltay, int deltax, DirectionMode dirmode, Direction mindir, Direction maxdir) {
166 int ndirs = (dirmode == dirmode_8dirs ? 8 : 16);
167 Direction dir = Direction_GetWorldDir(deltay, deltax, dirmode);
168
169 if ((dir < mindir) || (dir > maxdir)) {
170 int32 dmin1 = dir - mindir;
171 int32 dmin2 = mindir - dir;
172 if (dmin1 < 0) {
173 dmin1 = dmin1 + ndirs;
174 }
175 if (dmin2 < 0) {
176 dmin2 = dmin2 + ndirs;
177 }
178 int32 dist_to_min = MIN(dmin1, dmin2);
179
180 int dmax1 = dir - maxdir;
181 int dmax2 = maxdir - dir;
182 if (dmax1 < 0) {
183 dmax1 = dmax1 + ndirs;
184 }
185 if (dmax2 < 0) {
186 dmax2 = dmax2 + ndirs;
187 }
188 int32 dist_to_max = MIN(dmax1, dmax2);
189
190 if (dist_to_min < dist_to_max) {
191 return mindir;
192 } else {
193 return maxdir;
194 }
195 }
196
197 return dir;
198 }
199
Direction_Invert(Direction dir)200 inline Direction Direction_Invert(Direction dir) {
201 assert(dir != dir_current);
202 switch (dir) {
203 case dir_north: return dir_south;
204 case dir_nne: return dir_ssw;
205 case dir_northeast: return dir_southwest;
206 case dir_ene: return dir_wsw;
207 case dir_east: return dir_west;
208 case dir_ese: return dir_wnw;
209 case dir_southeast: return dir_northwest;
210 case dir_sse: return dir_nnw;
211 case dir_south: return dir_north;
212 case dir_ssw: return dir_nne;
213 case dir_southwest: return dir_northeast;
214 case dir_wsw: return dir_ene;
215 case dir_west: return dir_east;
216 case dir_wnw: return dir_ese;
217 case dir_northwest: return dir_southeast;
218 case dir_nnw: return dir_sse;
219 default: return dir_north;
220 }
221 }
222
223 //! Return the direction one left (aka counter-clockwise) of the input
Direction_OneLeft(Direction dir,DirectionMode mode)224 inline Direction Direction_OneLeft(Direction dir, DirectionMode mode) {
225 if (mode == dirmode_8dirs)
226 return static_cast<Direction>((static_cast<int>(dir) + 14) % 16);
227 else
228 return static_cast<Direction>((static_cast<int>(dir) + 15) % 16);
229 }
230
231 //! Return the direction one right (aka clockwise) of the input
Direction_OneRight(Direction dir,DirectionMode mode)232 inline Direction Direction_OneRight(Direction dir, DirectionMode mode) {
233 if (mode == dirmode_8dirs)
234 return static_cast<Direction>((static_cast<int>(dir) + 2) % 16);
235 else
236 return static_cast<Direction>((static_cast<int>(dir) + 1) % 16);
237 }
238
Direction_TurnByDelta(Direction dir,int delta,DirectionMode mode)239 inline Direction Direction_TurnByDelta(Direction dir, int delta, DirectionMode mode) {
240 if (delta > 0) {
241 for (int i = 0; i < delta; i++)
242 dir = Direction_OneRight(dir, mode);
243 } else if (delta < 0) {
244 for (int i = 0; i < -delta; i++)
245 dir = Direction_OneLeft(dir, mode);
246 }
247 return dir;
248 }
249
250 //! Get a turn delta (-1 for left, +1 for right) to turn the fastest
251 //! way from one direction to another
Direction_GetShorterTurnDelta(Direction from,Direction to)252 inline int Direction_GetShorterTurnDelta(Direction from, Direction to) {
253 if ((from - to + 16) % 16 < 8)
254 return -1;
255 return 1;
256 }
257
Direction_ToUsecodeDir(Direction dir)258 inline uint32 Direction_ToUsecodeDir(Direction dir) {
259 if (GAME_IS_U8) {
260 return static_cast<int32>(dir / 2);
261 } else {
262 return static_cast<int32>(dir);
263 }
264 }
265
Direction_FromUsecodeDir(uint32 dir)266 inline Direction Direction_FromUsecodeDir(uint32 dir) {
267 if (GAME_IS_U8) {
268 return static_cast<Direction>(dir * 2);
269 } else {
270 return static_cast<Direction>(dir);
271 }
272 }
273
274 } // End of namespace Ultima8
275 } // End of namespace Ultima
276
277 #endif
278