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