1 /* Catacomb Armageddon Source Code
\r
2 * Copyright (C) 1993-2014 Flat Rock Software
\r
4 * This program is free software; you can redistribute it and/or modify
\r
5 * it under the terms of the GNU General Public License as published by
\r
6 * the Free Software Foundation; either version 2 of the License, or
\r
7 * (at your option) any later version.
\r
9 * This program is distributed in the hope that it will be useful,
\r
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
12 * GNU General Public License for more details.
\r
14 * You should have received a copy of the GNU General Public License along
\r
15 * with this program; if not, write to the Free Software Foundation, Inc.,
\r
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
25 =============================================================================
\r
29 =============================================================================
\r
34 // TESTWALLVISABLE will set the global variable wallvisable to 1 or 0
\r
35 // depending on if tile.x,tile.y,wallon is visable from focal point
\r
37 #define TESTWALLVISABLE { \
\r
38 if (tile.y<focal.y) \
\r
40 else if (tile.y==focal.y) \
\r
44 if (tile.x==focal.x) \
\r
46 else if (tile.x>focal.x) \
\r
48 wallvisable = visable[voffset][wallon]; }
\r
52 =============================================================================
\r
56 =============================================================================
\r
62 =============================================================================
\r
66 =============================================================================
\r
69 unsigned wallvisable,voffset;
\r
80 // offsets from upper left corner of a tile to the left and right edges of
\r
81 // a given wall (NORTH-WEST)
\r
83 fixed point1x[4] = {GLOBAL1,GLOBAL1,0 ,0 };
\r
84 fixed point1y[4] = {0 ,GLOBAL1,GLOBAL1,0 };
\r
86 fixed point2x[4] = {0 ,GLOBAL1,GLOBAL1,0 };
\r
87 fixed point2y[4] = {0 ,0 ,GLOBAL1 ,GLOBAL1};
\r
91 // offset from tile.x,tile.y of the tile that shares wallon side
\r
92 // (side is not visable if it is shared)
\r
94 int sharex[4] = { 0, 1, 0,-1};
\r
95 int sharey[4] = {-1, 0, 1, 0};
\r
98 // amount to move tile.x,tile.y to follow wallon to another tile
\r
100 int followx[4] = {-1, 0, 1, 0};
\r
101 int followy[4] = { 0,-1, 0, 1};
\r
104 // cornerwall gives the wall on the same tile to start following when the
\r
105 // wall ends at an empty tile (go around an edge on same tile)
\r
106 // turnwall gives the wall on tile.x+sharex,tile.y+sharey to start following
\r
107 // when the wall hits another tile (right angle corner)
\r
109 int cornerwall[4] = {WEST,NORTH,EAST,SOUTH};
\r
110 int turnwall[4] = {EAST,SOUTH,WEST,NORTH};
\r
113 // wall visabilities in reletive locations
\r
118 int visable[9][4] =
\r
120 {0,1,1,0}, {0,0,1,0}, {0,0,1,1},
\r
121 {0,1,0,0}, {0,0,0,0}, {0,0,0,1},
\r
122 {1,1,0,0}, {1,0,0,0}, {1,0,0,1}
\r
125 int startwall[9] = {2,2,3, 1,0,3, 1,0,0};
\r
126 int backupwall[9] = {3,3,0, 2,0,0, 2,1,1};
\r
132 =============================================================================
\r
136 =============================================================================
\r
140 ========================
\r
144 ========================
\r
147 int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max)
\r
150 long absdx,absdy,xstep,ystep;
\r
152 tx = tracex>>TILESHIFT;
\r
153 ty = tracey>>TILESHIFT;
\r
155 spotvis[tx][ty] = true;
\r
157 absdx=LABS(deltax);
\r
158 absdy=LABS(deltay);
\r
162 ystep = (deltay<<8)/(absdx>>8);
\r
165 ystep = deltay>0 ? 1 : -1;
\r
167 oty = (tracey+ystep)>>TILESHIFT;
\r
178 spotvis[tx][ty] = true;
\r
180 ty = tracey>>TILESHIFT;
\r
184 if (tilemap[tx-1][ty])
\r
192 if (tilemap[tx][ty])
\r
211 spotvis[tx][ty] = true;
\r
213 ty = tracey>>TILESHIFT;
\r
217 if (tilemap[tx][oty])
\r
225 if (tilemap[tx][ty])
\r
238 xstep = (deltax<<8)/(absdy>>8);
\r
240 xstep = deltax>0 ? 1 : -1;
\r
243 otx = (tracex+xstep)>>TILESHIFT;
\r
254 spotvis[tx][ty] = true;
\r
256 tx = tracex>>TILESHIFT;
\r
260 if (tilemap[tx][ty-1])
\r
268 if (tilemap[tx][ty])
\r
287 spotvis[tx][ty] = true;
\r
289 tx = tracex>>TILESHIFT;
\r
293 if (tilemap[otx][ty])
\r
301 if (tilemap[tx][ty])
\r
316 //===========================================================================
\r
324 = Traces backwards from edgex,edgey to viewx,viewy to see if a closer
\r
325 = tile obscures the given point. If it does, it finishes the wall and
\r
326 = starts a new one.
\r
327 = Returns true if a tile is hit.
\r
328 = Call with a 1 to have it automatically finish the current wall
\r
333 int BackTrace (int finish)
\r
335 fixed tracex,tracey;
\r
336 long deltax,deltay,absdx,absdy;
\r
337 int steps,otx,oty,testx,testheight,offset,wall;
\r
339 deltax = viewx-edgex;
\r
340 deltay = viewy-edgey;
\r
342 absdx = LABS(deltax);
\r
343 absdy = LABS(deltay);
\r
346 steps = ABS(focal.x-(edgex>>TILESHIFT))-1;
\r
348 steps = ABS(focal.y-(edgey>>TILESHIFT))-1;
\r
355 if (!FollowTrace(edgex,edgey,deltax,deltay,steps))
\r
359 // if the start wall is behind the focal point, the trace went too far back
\r
361 if (ABS(tile.x-focal.x)<2 && ABS(tile.y-focal.y)<2) // too close
\r
363 if (tile.x == focal.x && tile.y == focal.y)
\r
370 if (tile.x<focal.x)
\r
372 if (tile.y<focal.y)
\r
377 else if (tile.x==focal.x)
\r
379 if (tile.y<focal.y)
\r
386 if (tile.y<=focal.y)
\r
393 // rotate the X value to see if it is behind the view plane
\r
395 if (TransformX (((long)tile.x<<16)+point1x[wall],
\r
396 ((long)tile.y<<16)+point1y[wall]) < FOCALLENGTH)
\r
405 // if the old wall is still behind a closer wall, ignore the back trace
\r
406 // and continue on (dealing with limited precision...)
\r
408 if (finish && !FinishWall ()) // the wall is still behind a forward wall
\r
412 rightwall->x1 = oldwall->x2; // common edge with last wall
\r
413 rightwall->height1 = oldwall->height2;
\r
419 // back up along the intersecting face to find the rightmost wall
\r
422 if (tile.y<focal.y)
\r
424 else if (tile.y==focal.y)
\r
428 if (tile.x==focal.x)
\r
430 else if (tile.x>focal.x)
\r
433 wallon = backupwall[offset];
\r
435 while (tilemap[tile.x][tile.y])
\r
437 tile.x += followx[wallon];
\r
438 tile.y += followy[wallon];
\r
441 tile.x -= followx[wallon];
\r
442 tile.y -= followy[wallon];
\r
444 wallon = cornerwall[wallon]; // turn to first visable face
\r
446 edgex = ((long)tile.x<<16);
\r
447 edgey = ((long)tile.y<<16);
\r
449 TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],
\r
450 &rightwall->x1,&rightwall->height1);
\r
452 basecolor = tilemap[tile.x][tile.y];
\r
457 //===========================================================================
\r
465 = Traces forwards from edgex,edgey along the line from viewx,viewy until
\r
466 = a solid tile is hit. Sets tile.x,tile.y
\r
471 void ForwardTrace (void)
\r
474 fixed tracex,tracey;
\r
475 long deltax,deltay;
\r
477 deltax = edgex-viewx;
\r
478 deltay = edgey-viewy;
\r
480 FollowTrace(edgex,edgey,deltax,deltay,0);
\r
482 if (tile.y<focal.y)
\r
484 else if (tile.y==focal.y)
\r
488 if (tile.x==focal.x)
\r
490 else if (tile.x>focal.x)
\r
493 wallon = startwall[offset];
\r
496 // start the new wall
\r
498 edgex = ((long)tile.x<<16);
\r
499 edgey = ((long)tile.y<<16);
\r
502 // if entire first wall is invisable, corner
\r
504 TransformPoint (edgex+point2x[wallon],edgey+point2y[wallon],
\r
505 &rightwall->x2,&rightwall->height2);
\r
507 if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]
\r
508 || rightwall->x2 < (rightwall-1)->x2 )
\r
509 wallon = cornerwall [wallon];
\r
512 // transform first point
\r
515 TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],
\r
516 &rightwall->x1,&rightwall->height1);
\r
518 basecolor = tilemap[tile.x][tile.y];
\r
522 //===========================================================================
\r
530 = Transforms edgex,edgey as the next point of the current wall
\r
531 = and sticks it in the wall list
\r
536 int FinishWall (void)
\r
540 oldwall = rightwall;
\r
542 rightwall->color = basecolor;
\r
544 TransformPoint (edgex,edgey,&rightwall->x2,&rightwall->height2);
\r
546 if (rightwall->x2 <= (rightwall-1)->x2+2
\r
547 && rightwall->height2 < (rightwall-1)->height2 )
\r
550 rightwall->walllength = walllength;
\r
556 rightwall->side = 0;
\r
557 rightwall->planecoord = edgey;
\r
562 rightwall->side = 1;
\r
563 rightwall->planecoord = edgex;
\r
574 //===========================================================================
\r
585 void InsideCorner (void)
\r
590 // the wall turned -90 degrees, so draw what we have, move to the new tile,
\r
591 // change wallon, change color, and continue following.
\r
595 tile.x += sharex[wallon];
\r
596 tile.y += sharey[wallon];
\r
598 wallon = turnwall[wallon];
\r
601 // if the new wall is visable, continue following it. Otherwise
\r
602 // follow it backwards until it turns
\r
609 // just turn to the next wall and continue
\r
611 rightwall->x1 = oldwall->x2; // common edge with last wall
\r
612 rightwall->height1 = oldwall->height2;
\r
613 basecolor = tilemap[tile.x][tile.y];
\r
614 return; // continue from here
\r
618 // back follow the invisable wall until it turns, then follow that
\r
622 tile.x += followx[wallon];
\r
623 tile.y += followy[wallon];
\r
624 } while (tilemap[tile.x][tile.y]);
\r
626 tile.x -= followx[wallon];
\r
627 tile.y -= followy[wallon];
\r
629 wallon = cornerwall[wallon]; // turn to first visable face
\r
631 edgex = ((long)tile.x<<16)+point1x[wallon];
\r
632 edgey = ((long)tile.y<<16)+point1y[wallon];
\r
634 if (!BackTrace(0)) // backtrace without finishing a wall
\r
636 TransformPoint (edgex,edgey,&rightwall->x1,&rightwall->height1);
\r
637 basecolor = tilemap[tile.x][tile.y];
\r
641 //===========================================================================
\r
652 void OutsideCorner (void)
\r
657 // edge is the outside edge of a corner, so draw the current wall and
\r
658 // turn the corner (+90 degrees)
\r
662 tile.x -= followx[wallon]; // backup to the real tile
\r
663 tile.y -= followy[wallon];
\r
664 wallon = cornerwall[wallon];
\r
667 // if the new wall is visable, continue following it. Otherwise
\r
668 // trace a ray from the corner to find a wall in the distance to
\r
676 // the new wall is visable, so just continue on
\r
678 rightwall->x1 = oldwall->x2; // common edge with last wall
\r
679 rightwall->height1 = oldwall->height2;
\r
680 return; // still on same tile, so color is ok
\r
684 // start from a new tile further away
\r
686 ForwardTrace(); // find the next wall further back
\r
691 //===========================================================================
\r
699 = Starts a wall edge at the leftmost edge of tile.x,tile.y and follows it
\r
700 = until something else is seen or the entire view area is covered
\r
705 void FollowWalls (void)
\r
707 int height,newcolor,offset,wall;
\r
709 //####################
\r
711 // figure leftmost wall of new tile
\r
713 //####################
\r
719 if (tile.y<focal.y)
\r
721 else if (tile.y==focal.y)
\r
725 if (tile.x==focal.x)
\r
727 else if (tile.x>focal.x)
\r
730 wallon = startwall[offset];
\r
733 // if the start wall is inside a block, skip it by cornering to the second wall
\r
735 if ( tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]])
\r
736 wallon = cornerwall [wallon];
\r
739 // transform first edge to screen coordinates
\r
741 edgex = ((long)tile.x<<16);
\r
742 edgey = ((long)tile.y<<16);
\r
744 TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],
\r
745 &rightwall->x1,&rightwall->height1);
\r
747 basecolor = tilemap[tile.x][tile.y];
\r
749 //##################
\r
751 // follow the wall as long as possible
\r
753 //##################
\r
757 do // while ( tile.x != right.x || tile.y != right.y)
\r
760 // check for conditions that shouldn't happed...
\r
762 if (rightwall->x1 > VIEWXH) // somehow missed right tile...
\r
765 if (rightwall == &walls[DANGERHIGH])
\r
768 // somethiing got messed up! Correct by thrusting ahead...
\r
770 // VW_ColorBorder(6);
\r
772 Thrust(player->angle,TILEGLOBAL/4);
\r
774 if (player->angle>ANGLES)
\r
775 player->angle-=ANGLES;
\r
780 strcpy (str,"Wall list overflow at LE:");
\r
781 itoa(mapon+1,str2,10);
\r
783 strcat (str," X:");
\r
784 ltoa(objlist[0].x,str2,10);
\r
786 strcat (str," Y:");
\r
787 ltoa(objlist[0].y,str2,10);
\r
789 strcat (str," AN:");
\r
790 itoa(objlist[0].angle,str2,10);
\r
798 // proceed along wall
\r
801 edgex = ((long)tile.x<<16)+point2x[wallon];
\r
802 edgey = ((long)tile.y<<16)+point2y[wallon];
\r
804 if (BackTrace(1)) // went behind a closer wall
\r
808 // advance to next tile along wall
\r
810 tile.x += followx[wallon];
\r
811 tile.y += followy[wallon];
\r
813 if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]])
\r
815 InsideCorner (); // turn at a corner
\r
819 newcolor = tilemap[tile.x][tile.y];
\r
821 if (!newcolor) // turn around an edge
\r
827 if (newcolor != basecolor)
\r
830 // wall changed color, so draw what we have and continue following
\r
833 rightwall->x1 = oldwall->x2; // new wall shares this edge
\r
834 rightwall->height1 = oldwall->height2;
\r
835 basecolor = newcolor;
\r
840 } while (tile.x != right.x || tile.y != right.y);
\r
844 //######################
\r
846 // draw the last tile
\r
848 //######################
\r
850 edgex = ((long)tile.x<<16)+point2x[wallon];
\r
851 edgey = ((long)tile.y<<16)+point2y[wallon];
\r
854 wallon = cornerwall[wallon];
\r
857 // if the corner wall is visable, draw it
\r
863 rightwall->x1 = oldwall->x2; // common edge with last wall
\r
864 rightwall->height1 = oldwall->height2;
\r
865 edgex = ((long)tile.x<<16)+point2x[wallon];
\r
866 edgey = ((long)tile.y<<16)+point2y[wallon];
\r
872 //===========================================================================
\r