1
2 #include "slope.h"
3
4 #include "Utils/Logger.h"
5 #include "game.h"
6 #include "graphics/Renderer.h"
7 #include "map.h"
8 #include "nx.h"
9
10 using namespace NXE::Graphics;
11
12 //#define DEBUG_SLOPE
13 static SlopeTable slopetable[SLOPE_LAST + 1];
14
15 // creates the slope tables
initslopetable(void)16 bool initslopetable(void)
17 {
18 int x, y, ya, mx;
19 int curtable, opposing_table;
20 int inverttable, invertfliptable;
21 int flipmx, flipy;
22
23 LOG_DEBUG("initslopetable: generating slopetables.");
24 memset(slopetable, 0, sizeof(slopetable));
25
26 ya = TILE_H - 1;
27 for (x = 0; x < TILE_W * 2; x++)
28 {
29 if (x < TILE_W)
30 {
31 mx = x;
32 curtable = SLOPE_FWD1;
33 opposing_table = SLOPE_BACK2;
34 inverttable = SLOPE_CEIL_BACK1;
35 invertfliptable = SLOPE_CEIL_FWD2;
36 }
37 else
38 {
39 mx = x - TILE_W;
40 curtable = SLOPE_FWD2;
41 opposing_table = SLOPE_BACK1;
42 inverttable = SLOPE_CEIL_BACK2;
43 invertfliptable = SLOPE_CEIL_FWD1;
44 }
45
46 for (y = ya; y < TILE_H; y++)
47 {
48 flipmx = TILE_W - 1 - mx;
49 flipy = TILE_H - 1 - y;
50
51 slopetable[curtable].table[mx][y] = 1;
52 slopetable[opposing_table].table[flipmx][y] = 1;
53 slopetable[inverttable].table[mx][flipy] = 1;
54 slopetable[invertfliptable].table[flipmx][flipy] = 1;
55 }
56
57 if (x & 1)
58 ya--;
59 }
60
61 return 0;
62 }
63
64 /*
65 void c------------------------------() {}
66 */
67
68 // X and Y are non-CSFd pixel coordinates relative to the upper-left of the map.
69 // if the given pixel is inside of a slope, returns the slope type 1-8. else, returns 0.
ReadSlopeTable(int x,int y)70 uint8_t ReadSlopeTable(int x, int y)
71 {
72 int mx, my;
73 int slopetype;
74 uint8_t t;
75
76 #ifdef DEBUG_SLOPE
77 DrawSlopeTablesOnTiles();
78 #endif
79
80 // convert coordinates into a tile and check if the tile is a slope tile
81 mx = (x / TILE_W);
82 my = (y / TILE_H);
83
84 if (mx < 0 || my < 0 || mx >= map.xsize || my >= map.ysize)
85 return 0;
86
87 t = map.tiles[mx][my];
88
89 if (tileattr[t] & TA_SLOPE)
90 {
91 slopetype = (tilecode[t] & 0x07) + 1; // extract slope type from tile code
92
93 // get offset from the tile
94 x %= TILE_W;
95 y %= TILE_H;
96
97 if (slopetable[slopetype].table[x][y])
98 return slopetype;
99 }
100
101 return 0;
102 }
103
104 // returns true if any of the points in the given point list
105 // are on the solid portion of a slope tile.
IsSlopeAtPointList(Object * o,SIFPointList * points)106 bool IsSlopeAtPointList(Object *o, SIFPointList *points)
107 {
108 int x, y, i;
109
110 for (i = 0; i < points->count; i++)
111 {
112 x = (o->x / CSFI) + points->point[i].x;
113 y = (o->y / CSFI) + points->point[i].y;
114 if (ReadSlopeTable(x, y))
115 return 1;
116 }
117
118 return 0;
119 }
120
121 /*
122 void c------------------------------() {}
123 */
124
125 // returns nonzero (the slope type) if the object is standing on a slope.
CheckStandOnSlope(Object * o)126 int CheckStandOnSlope(Object *o)
127 {
128 int x, y, st;
129
130 y = (o->y / CSFI) + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.y2 + 1;
131 x = (o->x / CSFI);
132
133 if ((st = ReadSlopeTable(x + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.x1, y)))
134 return st;
135 if ((st = ReadSlopeTable(x + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.x2, y)))
136 return st;
137
138 return 0;
139 }
140
141 // returns nonzero (the slope type) if the objects blocku should be set
142 // because of a ceiling slope.
CheckBoppedHeadOnSlope(Object * o)143 int CheckBoppedHeadOnSlope(Object *o)
144 {
145 int x, y, st;
146
147 y = (o->y / CSFI) + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.y1 - 1;
148 x = (o->x / CSFI);
149
150 // without this, you get stuck in the save area below Gum Door in Grasstown
151 // if (o == player) y += 4;
152
153 if ((st = ReadSlopeTable(x + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.x1, y)))
154 return st;
155 if ((st = ReadSlopeTable(x + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.x2, y)))
156 return st;
157
158 return 0;
159 }
160
161 // move an object laterally, and have it climb slopes as it approaches them.
162 // We also have to move the object down as it goes down the slope.
163 // Otherwise, it would "skip" down the slope ungracefully.
164 // returns 1 if the object was blocked by a wall.
movehandleslope(Object * o,int xinertia)165 bool movehandleslope(Object *o, int xinertia)
166 {
167 int xoff, opposing_x;
168 int newx, newy, oldy;
169 char blocked_wall;
170
171 if (!xinertia)
172 return 0;
173
174 // for objects which don't follow slope, just treat the slope as a blockl/r
175 if (!(o->nxflags & NXFLAG_FOLLOW_SLOPE))
176 {
177 if (xinertia > 0)
178 {
179 if (o->blockr)
180 return 1;
181 }
182 else
183 {
184 if (o->blockl)
185 return 1;
186 }
187
188 o->x += xinertia;
189 return 0;
190 }
191
192 newx = o->x;
193 newy = o->y;
194
195 // determine which side of the bounding box to use based on which way
196 // we're traveling
197 if (xinertia > 0)
198 { // moving right (right side of slopebox hits slopes first)
199 opposing_x = Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.x1;
200 xoff = Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.x2;
201 }
202 else
203 { // move left (left side of slopebox hits slopes first)
204 opposing_x = Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.x2;
205 xoff = Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.x1;
206 }
207
208 // check the opposing side at y+1 to see if we were standing on a slope before the move.
209 uint8_t old_floor_slope, old_ceil_slope;
210 old_floor_slope = ReadSlopeTable((newx / CSFI) + opposing_x, (newy / CSFI) + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.y2 + 1);
211
212 old_ceil_slope = ReadSlopeTable((newx / CSFI) + opposing_x, (newy / CSFI) + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.y1 - 1);
213
214 // move the object
215 newx += xinertia;
216
217 // check the opposing side again and if now we're not standing any more,
218 // we moved down the slope, so add +1 to the object's Y coordinate.
219 if (old_floor_slope
220 && !ReadSlopeTable((newx / CSFI) + opposing_x, (newy / CSFI) + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.y2 + 1))
221 {
222 bool walking_down = false;
223
224 // only trigger if it's the correct slope type so that we would be walking down it if
225 // we were going in the direction we're going. prevents being shoved down 1px when
226 // exiting the top of a slope.
227 if (xinertia < 0)
228 {
229 if (old_floor_slope == SLOPE_FWD1 || old_floor_slope == SLOPE_FWD2)
230 {
231 walking_down = true;
232 }
233 }
234 else if (xinertia > 0)
235 {
236 if (old_floor_slope == SLOPE_BACK1 || old_floor_slope == SLOPE_BACK2)
237 {
238 walking_down = true;
239 }
240 }
241
242 if (walking_down)
243 {
244 newy += (1 * CSFI);
245 }
246 }
247
248 // the same for ceiling slopes
249 if (old_ceil_slope && !ReadSlopeTable((newx / CSFI) + opposing_x, (newy / CSFI) + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.y1 - 1))
250 {
251 bool moveme = false;
252
253 if (xinertia < 0)
254 {
255 if (old_ceil_slope == SLOPE_CEIL_BACK1 || old_ceil_slope == SLOPE_CEIL_BACK2)
256 {
257 moveme = true;
258 }
259 }
260 else if (xinertia > 0)
261 {
262 if (old_ceil_slope == SLOPE_CEIL_FWD1 || old_ceil_slope == SLOPE_CEIL_FWD2)
263 {
264 moveme = true;
265 }
266 }
267
268 if (moveme)
269 { // moving down (actually up) the "descending" (closer to real ceil) portion
270 // of a ceiling slope tile. Reverse of floor slope thingy above.
271 newy -= (1 * CSFI);
272 }
273 }
274
275 // check the coordinate and see if it's inside a slope tile.
276 // if so, move the object up 1 Y pixel.
277 uint8_t moved_into_ceil_slope = ReadSlopeTable((newx / CSFI) + xoff, (newy / CSFI) + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.y1);
278 if (moved_into_ceil_slope)
279 {
280 newy += (1 * CSFI);
281 }
282
283 uint8_t moved_into_floor_slope = ReadSlopeTable((newx / CSFI) + xoff, (newy / CSFI) + Renderer::getInstance()->sprites.sprites[o->sprite].slopebox.y2);
284 if (moved_into_floor_slope)
285 {
286 newy -= (1 * CSFI);
287 }
288
289 // can't move if blocked by a wall. but if we've moved up or down 1px, be sure and update
290 // the blockr/l state before declaring we can't move--otherwise we can get stuck at the
291 // top of a slope with the bottom blockr/l stuck at the top px of the adjacent solid tile.
292 oldy = o->y;
293 o->y = newy;
294
295 if (xinertia > 0)
296 {
297 if (oldy != newy)
298 o->UpdateBlockStates(RIGHTMASK);
299
300 blocked_wall = o->blockr;
301 }
302 else
303 {
304 if (oldy != newy)
305 o->UpdateBlockStates(LEFTMASK);
306
307 blocked_wall = o->blockl;
308 }
309
310 if (blocked_wall)
311 { // we can't actually move...so reset Y position
312 o->y = oldy;
313 }
314 else
315 { // can move...complete the move by setting the X position too
316 o->x = newx;
317 }
318
319 return blocked_wall;
320 }
321
322 /*
323 void c------------------------------() {}
324 */
325
326 #ifdef DEBUG_SLOPE
327 // debug crap
328
DrawSlopeTablesOnTiles()329 void DrawSlopeTablesOnTiles()
330 {
331 static int lastmap = -1;
332
333 if (game.curmap != lastmap)
334 {
335 lastmap = game.curmap;
336 for (int i = 0; i < 256; i++)
337 {
338 if (tileattr[i] & TA_SLOPE)
339 {
340 DrawSlopeTableOnTile((tilecode[i] & 7) + 1, i);
341 }
342 }
343 }
344 }
345
DrawSlopeTableOnTile(int table,int tile)346 void DrawSlopeTableOnTile(int table, int tile)
347 {
348 SDL_Rect dstrect;
349 int x, y;
350 extern SDL_Surface *tileset;
351
352 for (y = 0; y < TILE_H; y++)
353 {
354 for (x = 0; x < TILE_W; x++)
355 {
356 dstrect.x = (tile & 15) << 5;
357 dstrect.y = (tile >> 4) << 5;
358 dstrect.w = 2;
359 dstrect.h = 2;
360
361 dstrect.x += x * 2;
362 dstrect.y += y * 2;
363
364 if (slopetable[table].table[x][y])
365 {
366 if (table > 4) // floor slopes
367 SDL_FillRect(tileset, &dstrect, SDL_MapRGB(tileset->format, 0, 255, 0));
368 else
369 SDL_FillRect(tileset, &dstrect, SDL_MapRGB(tileset->format, 255, 0, 0));
370 }
371 }
372 }
373 }
374
dumpslopetable(int t)375 void dumpslopetable(int t)
376 {
377 int x, y;
378 char buffer[80];
379
380 LOG_TRACE("\nDumping slope table {}:", t);
381 for (y = 0; y < TILE_H; y++)
382 {
383 for (x = 0; x < TILE_W; x++)
384 {
385 buffer[x] = slopetable[t].table[x][y] + '0';
386 }
387 buffer[x] = 0;
388 LOG_TRACE("{}", buffer);
389 }
390 }
391 #endif
392