/* Catacomb Armageddon Source Code * Copyright (C) 1993-2014 Flat Rock Software * * 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 2 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // C3_TRACE.C #include "DEF.H" #pragma hdrstop /* ============================================================================= LOCAL CONSTANTS ============================================================================= */ // // TESTWALLVISABLE will set the global variable wallvisable to 1 or 0 // depending on if tile.x,tile.y,wallon is visable from focal point // #define TESTWALLVISABLE { \ if (tile.yfocal.x) \ voffset += 2; \ wallvisable = visable[voffset][wallon]; } /* ============================================================================= GLOBAL VARIABLES ============================================================================= */ boolean aborttrace; /* ============================================================================= LOCAL VARIABLES ============================================================================= */ unsigned wallvisable,voffset; fixed edgex,edgey; int wallon; int basecolor; walltype *oldwall; // // offsets from upper left corner of a tile to the left and right edges of // a given wall (NORTH-WEST) // fixed point1x[4] = {GLOBAL1,GLOBAL1,0 ,0 }; fixed point1y[4] = {0 ,GLOBAL1,GLOBAL1,0 }; fixed point2x[4] = {0 ,GLOBAL1,GLOBAL1,0 }; fixed point2y[4] = {0 ,0 ,GLOBAL1 ,GLOBAL1}; // // offset from tile.x,tile.y of the tile that shares wallon side // (side is not visable if it is shared) // int sharex[4] = { 0, 1, 0,-1}; int sharey[4] = {-1, 0, 1, 0}; // // amount to move tile.x,tile.y to follow wallon to another tile // int followx[4] = {-1, 0, 1, 0}; int followy[4] = { 0,-1, 0, 1}; // // cornerwall gives the wall on the same tile to start following when the // wall ends at an empty tile (go around an edge on same tile) // turnwall gives the wall on tile.x+sharex,tile.y+sharey to start following // when the wall hits another tile (right angle corner) // int cornerwall[4] = {WEST,NORTH,EAST,SOUTH}; int turnwall[4] = {EAST,SOUTH,WEST,NORTH}; // // wall visabilities in reletive locations // -,- 0,- +,- // -,0 0,0 +,0 // -,+ 0,+ +,+ // int visable[9][4] = { {0,1,1,0}, {0,0,1,0}, {0,0,1,1}, {0,1,0,0}, {0,0,0,0}, {0,0,0,1}, {1,1,0,0}, {1,0,0,0}, {1,0,0,1} }; int startwall[9] = {2,2,3, 1,0,3, 1,0,0}; int backupwall[9] = {3,3,0, 2,0,0, 2,1,1}; int walllength; /* ============================================================================= FUNCTIONS ============================================================================= */ /* ======================== = = FollowTrace = ======================== */ int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max) { int tx,ty,otx,oty; long absdx,absdy,xstep,ystep; tx = tracex>>TILESHIFT; ty = tracey>>TILESHIFT; spotvis[tx][ty] = true; absdx=LABS(deltax); absdy=LABS(deltay); if (absdx>absdy) { ystep = (deltay<<8)/(absdx>>8); if (!ystep) ystep = deltay>0 ? 1 : -1; oty = (tracey+ystep)>>TILESHIFT; if (deltax>0) { //############### // // step x by +1 // //############### do { tx++; spotvis[tx][ty] = true; tracey+=ystep; ty = tracey>>TILESHIFT; if (ty!=oty) { if (tilemap[tx-1][ty]) { tile.x = tx-1; tile.y = ty; return 1; } oty = ty; } if (tilemap[tx][ty]) { tile.x = tx; tile.y = ty; return 1; } } while (--max); return 0; } else { //############### // // step x by -1 // //############### do { tx--; spotvis[tx][ty] = true; tracey+=ystep; ty = tracey>>TILESHIFT; if (ty!=oty) { if (tilemap[tx][oty]) { tile.x = tx; tile.y = oty; return 1; } oty = ty; } if (tilemap[tx][ty]) { tile.x = tx; tile.y = ty; return 1; } } while (--max); return 0; } } else { xstep = (deltax<<8)/(absdy>>8); if (!xstep) xstep = deltax>0 ? 1 : -1; otx = (tracex+xstep)>>TILESHIFT; if (deltay>0) { //############### // // step y by +1 // //############### do { ty++; spotvis[tx][ty] = true; tracex+=xstep; tx = tracex>>TILESHIFT; if (tx!=otx) { if (tilemap[tx][ty-1]) { tile.x = tx; tile.y = ty-1; return 1; } otx = tx; } if (tilemap[tx][ty]) { tile.x = tx; tile.y = ty; return 1; } } while (--max); return 0; } else { //############### // // step y by -1 // //############### do { ty--; spotvis[tx][ty] = true; tracex+=xstep; tx = tracex>>TILESHIFT; if (tx!=otx) { if (tilemap[otx][ty]) { tile.x = otx; tile.y = ty; return 1; } otx = tx; } if (tilemap[tx][ty]) { tile.x = tx; tile.y = ty; return 1; } } while (--max); return 0; } } } //=========================================================================== /* ================= = = BackTrace = = Traces backwards from edgex,edgey to viewx,viewy to see if a closer = tile obscures the given point. If it does, it finishes the wall and = starts a new one. = Returns true if a tile is hit. = Call with a 1 to have it automatically finish the current wall = ================= */ int BackTrace (int finish) { fixed tracex,tracey; long deltax,deltay,absdx,absdy; int steps,otx,oty,testx,testheight,offset,wall; deltax = viewx-edgex; deltay = viewy-edgey; absdx = LABS(deltax); absdy = LABS(deltay); if (absdx>absdy) steps = ABS(focal.x-(edgex>>TILESHIFT))-1; else steps = ABS(focal.y-(edgey>>TILESHIFT))-1; if (steps<=0) return 0; otx = tile.x; oty = tile.y; if (!FollowTrace(edgex,edgey,deltax,deltay,steps)) return 0; // // if the start wall is behind the focal point, the trace went too far back // if (ABS(tile.x-focal.x)<2 && ABS(tile.y-focal.y)<2) // too close { if (tile.x == focal.x && tile.y == focal.y) { tile.x = otx; tile.y = oty; return 0; } if (tile.xx1 = oldwall->x2; // common edge with last wall rightwall->height1 = oldwall->height2; return 0; } // // back up along the intersecting face to find the rightmost wall // if (tile.yfocal.x) offset += 2; wallon = backupwall[offset]; while (tilemap[tile.x][tile.y]) { tile.x += followx[wallon]; tile.y += followy[wallon]; }; tile.x -= followx[wallon]; tile.y -= followy[wallon]; wallon = cornerwall[wallon]; // turn to first visable face edgex = ((long)tile.x<<16); edgey = ((long)tile.y<<16); TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], &rightwall->x1,&rightwall->height1); basecolor = tilemap[tile.x][tile.y]; return 1; } //=========================================================================== /* ================= = = ForwardTrace = = Traces forwards from edgex,edgey along the line from viewx,viewy until = a solid tile is hit. Sets tile.x,tile.y = ================= */ void ForwardTrace (void) { int offset; fixed tracex,tracey; long deltax,deltay; deltax = edgex-viewx; deltay = edgey-viewy; FollowTrace(edgex,edgey,deltax,deltay,0); if (tile.yfocal.x) offset += 2; wallon = startwall[offset]; // // start the new wall // edgex = ((long)tile.x<<16); edgey = ((long)tile.y<<16); // // if entire first wall is invisable, corner // TransformPoint (edgex+point2x[wallon],edgey+point2y[wallon], &rightwall->x2,&rightwall->height2); if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]] || rightwall->x2 < (rightwall-1)->x2 ) wallon = cornerwall [wallon]; // // transform first point // TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], &rightwall->x1,&rightwall->height1); basecolor = tilemap[tile.x][tile.y]; } //=========================================================================== /* ================= = = FinishWall = = Transforms edgex,edgey as the next point of the current wall = and sticks it in the wall list = ================= */ int FinishWall (void) { char num[20]; oldwall = rightwall; rightwall->color = basecolor; TransformPoint (edgex,edgey,&rightwall->x2,&rightwall->height2); if (rightwall->x2 <= (rightwall-1)->x2+2 && rightwall->height2 < (rightwall-1)->height2 ) return 0; rightwall->walllength = walllength; switch (wallon) { case north: case south: rightwall->side = 0; rightwall->planecoord = edgey; break; case west: case east: rightwall->side = 1; rightwall->planecoord = edgex; break; } walllength = 1; rightwall++; return 1; } //=========================================================================== /* ================= = = InsideCorner = ================= */ void InsideCorner (void) { int offset; // // the wall turned -90 degrees, so draw what we have, move to the new tile, // change wallon, change color, and continue following. // FinishWall (); tile.x += sharex[wallon]; tile.y += sharey[wallon]; wallon = turnwall[wallon]; // // if the new wall is visable, continue following it. Otherwise // follow it backwards until it turns // TESTWALLVISABLE; if (wallvisable) { // // just turn to the next wall and continue // rightwall->x1 = oldwall->x2; // common edge with last wall rightwall->height1 = oldwall->height2; basecolor = tilemap[tile.x][tile.y]; return; // continue from here } // // back follow the invisable wall until it turns, then follow that // do { tile.x += followx[wallon]; tile.y += followy[wallon]; } while (tilemap[tile.x][tile.y]); tile.x -= followx[wallon]; tile.y -= followy[wallon]; wallon = cornerwall[wallon]; // turn to first visable face edgex = ((long)tile.x<<16)+point1x[wallon]; edgey = ((long)tile.y<<16)+point1y[wallon]; if (!BackTrace(0)) // backtrace without finishing a wall { TransformPoint (edgex,edgey,&rightwall->x1,&rightwall->height1); basecolor = tilemap[tile.x][tile.y]; } } //=========================================================================== /* ================= = = OutsideCorner = ================= */ void OutsideCorner (void) { int offset; // // edge is the outside edge of a corner, so draw the current wall and // turn the corner (+90 degrees) // FinishWall (); tile.x -= followx[wallon]; // backup to the real tile tile.y -= followy[wallon]; wallon = cornerwall[wallon]; // // if the new wall is visable, continue following it. Otherwise // trace a ray from the corner to find a wall in the distance to // follow // TESTWALLVISABLE; if (wallvisable) { // // the new wall is visable, so just continue on // rightwall->x1 = oldwall->x2; // common edge with last wall rightwall->height1 = oldwall->height2; return; // still on same tile, so color is ok } // // start from a new tile further away // ForwardTrace(); // find the next wall further back } //=========================================================================== /* ================= = = FollowWalls = = Starts a wall edge at the leftmost edge of tile.x,tile.y and follows it = until something else is seen or the entire view area is covered = ================= */ void FollowWalls (void) { int height,newcolor,offset,wall; //#################### // // figure leftmost wall of new tile // //#################### restart: walllength = 1; if (tile.yfocal.x) offset += 2; wallon = startwall[offset]; // // if the start wall is inside a block, skip it by cornering to the second wall // if ( tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]) wallon = cornerwall [wallon]; // // transform first edge to screen coordinates // edgex = ((long)tile.x<<16); edgey = ((long)tile.y<<16); TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon], &rightwall->x1,&rightwall->height1); basecolor = tilemap[tile.x][tile.y]; //################## // // follow the wall as long as possible // //################## advance: do // while ( tile.x != right.x || tile.y != right.y) { // // check for conditions that shouldn't happed... // if (rightwall->x1 > VIEWXH) // somehow missed right tile... return; if (rightwall == &walls[DANGERHIGH]) { // // somethiing got messed up! Correct by thrusting ahead... // // VW_ColorBorder(6); bordertime = 60; Thrust(player->angle,TILEGLOBAL/4); player->angle+=5; if (player->angle>ANGLES) player->angle-=ANGLES; aborttrace = true; return; #if 0 strcpy (str,"Wall list overflow at LE:"); itoa(mapon+1,str2,10); strcat (str,str2); strcat (str," X:"); ltoa(objlist[0].x,str2,10); strcat (str,str2); strcat (str," Y:"); ltoa(objlist[0].y,str2,10); strcat (str,str2); strcat (str," AN:"); itoa(objlist[0].angle,str2,10); strcat (str,str2); Quit (str); #endif } // // proceed along wall // edgex = ((long)tile.x<<16)+point2x[wallon]; edgey = ((long)tile.y<<16)+point2y[wallon]; if (BackTrace(1)) // went behind a closer wall continue; // // advance to next tile along wall // tile.x += followx[wallon]; tile.y += followy[wallon]; if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]) { InsideCorner (); // turn at a corner continue; } newcolor = tilemap[tile.x][tile.y]; if (!newcolor) // turn around an edge { OutsideCorner (); continue; } if (newcolor != basecolor) { // // wall changed color, so draw what we have and continue following // FinishWall (); rightwall->x1 = oldwall->x2; // new wall shares this edge rightwall->height1 = oldwall->height2; basecolor = newcolor; continue; } walllength++; } while (tile.x != right.x || tile.y != right.y); //###################### // // draw the last tile // //###################### edgex = ((long)tile.x<<16)+point2x[wallon]; edgey = ((long)tile.y<<16)+point2y[wallon]; FinishWall(); wallon = cornerwall[wallon]; // // if the corner wall is visable, draw it // TESTWALLVISABLE; if (wallvisable) { rightwall->x1 = oldwall->x2; // common edge with last wall rightwall->height1 = oldwall->height2; edgex = ((long)tile.x<<16)+point2x[wallon]; edgey = ((long)tile.y<<16)+point2y[wallon]; FinishWall(); } } //===========================================================================