// Emacs style mode select   -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id: hw_trick.c,v 1.3 2003/08/18 18:26:06 hurdler Exp $
//
// Copyright (C) 1998-2001 by DooM Legacy Team.
//
// 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.
//
//
// $Log: hw_trick.c,v $
// Revision 1.3  2003/08/18 18:26:06  hurdler
// stable 1.41
//
// Revision 1.10  2002/07/26 15:22:44  hurdler
// near RC release
//
// Revision 1.9  2001/12/26 17:24:47  hurdler
// Update Linux version
//
// Revision 1.8  2001/05/14 19:02:58  metzgermeister
//   * Fixed floor not moving up with player on E3M1
//   * Fixed crash due to oversized string in screen message ... bad bug!
//   * Corrected some typos
//   * fixed sound bug in SDL
//
// Revision 1.7  2001/05/07 15:55:05  hurdler
// temporary "fix" for heretic
//
// Revision 1.6  2001/04/11 21:14:11  metzgermeister
// *** empty log message ***
//
// Revision 1.5  2001/04/10 18:39:39  metzgermeister
// fixed a (possible?) crash bug
//
// Revision 1.4  2001/04/09 23:26:06  hurdler
// clean up
//
// Revision 1.3  2001/04/09 20:23:12  metzgermeister
// more conservative trick treatment
//
// Revision 1.2  2001/03/26 19:47:56  metzgermeister
// more tricky things
//
// Revision 1.1  2001/03/25 18:11:24  metzgermeister
//   * SDL sound bug with swapped stereo channels fixed
//   * separate hw_trick.c now for HW_correctSWTrick(.)
//
//
//
// DESCRIPTION:
//      special trick routines to make some SW tricks look OK with
//      HW rendering. This includes:
//      - deepwatereffect (e.g. tnt/map02)
//      - invisible staircase (e.g. eternal/map02)
//      - floating ceilings (e.g. eternal/map03)
//      
//      It is not guaranteed that it looks identical to the SW mode,
//      but it looks in most of the cases far better than having
//      holes in the architecture, HOM, etc.
//      
//      It fixes as well missing textures, which are replaced by either
//      a default texture or the midtexture.
//      
//      words of notice:
//      pseudosectors, as mentioned in this file, are sectors where both
//      sidedefs point to the same sector. This expression is also used
//      for sectors which are enclosed by another sector but have no
//      correct sidedefs at all
//      
//      if a vertex is inside a poly is determined by the angles between
//      this vertex and all angles on the linedefs (imagine walking along
//      a circle always facing a certain point inside/outside the circle;
//      if inside, angle have taken all values [0..\pi), otherwise the
//      range was < \pi/2
//      
//
//-----------------------------------------------------------------------------

#include <math.h>
#include "hw_glob.h"
#include "../r_local.h"
#include "../i_system.h"

//
// add a line to a sectors list of lines
//
static void addLineToChain(sector_t * sector, line_t * line)
{
    linechain_t *thisElem, *nextElem;

    if (NULL == sector)
        return;

    thisElem = NULL;
    nextElem = sector->sectorLines;

    while (NULL != nextElem)    // walk through chain
    {
        thisElem = nextElem;
        nextElem = thisElem->next;
    }

    // add a new element into the chain
    if (thisElem)
    {
        thisElem->next = malloc(sizeof(linechain_t));
        if (thisElem->next)
        {
            thisElem->next->line = line;
            thisElem->next->next = NULL;
        }
        else
        {
            I_Error("Out of memory in addLineToChain(.)\n");
        }
    }
    else        // first element in chain
    {
        sector->sectorLines = malloc(sizeof(linechain_t));
        if (sector->sectorLines)
        {
            sector->sectorLines->line = line;
            sector->sectorLines->next = NULL;
        }
        else
        {
            I_Error("Out of memory in addLineToChain(.)\n");
        }
    }
}

//
// We donīt want a memory hole, do we? ;-)
//
static void releaseLineChains(void)
{
    linechain_t *thisElem, *nextElem;
    sector_t *sector;
    int i;

    for (i = 0; i < numsectors; i++)
    {
        sector = &sectors[i];
        nextElem = sector->sectorLines;

        while (NULL != nextElem)
        {
            thisElem = nextElem;
            nextElem = thisElem->next;
            free(thisElem);
        }

        sector->sectorLines = NULL;
    }
}

//
// check if a pseudo sector is valid by checking all itīs linedefs
//
static boolean isPSectorValid(sector_t * sector)
{
    linechain_t *thisElem, *nextElem;

    if (!sector->pseudoSector)  // check only pseudosectors, others donīt care
    {
#ifdef PARANOIA
        CONS_Printf("Alert! non-pseudosector fed to isPSectorClosed()\n");
#endif
        return false;
    }

    nextElem = sector->sectorLines;

    while (NULL != nextElem)
    {
        thisElem = nextElem;
        nextElem = thisElem->next;
        if (thisElem->line->frontsector != thisElem->line->backsector)
            return false;
    }
    return true;
}

//
// angles are always phiMax-phiMin [0...2\pi)
//
static double phiDiff(double phiMin, double phiMax)
{
    double result;

    result = phiMax - phiMin;

    if (result < 0.0)
        result += 2.0 * PI;

    return result;
}

//
// sort phi's so that enclosed angle < \pi
//
static void sortPhi(double phi1, double phi2, double *phiMin, double *phiMax)
{
    if (phiDiff(phi1, phi2) < PI)
    {
        *phiMin = phi1;
        *phiMax = phi2;
    }
    else
    {
        *phiMin = phi2;
        *phiMax = phi1;
    }
}

//
// return if angle(phi1, phi2) is bigger than \pi
// if so, the vertex lies inside the poly
//
static boolean biggerThanPi(double phi1, double phi2)
{
    if (phiDiff(phi1, phi2) > PI)
        return true;

    return false;
}

#define DELTAPHI (PI/100.0)     // some small phi << \pi

//
// calculate bounds for minimum angle
//
void phiBounds(double phi1, double phi2, double *phiMin, double *phiMax)
{
    double phi1Tmp, phi2Tmp;
    double psi1, psi2, psi3, psi4, psi5, psi6, psi7;    // for optimization

    sortPhi(phi1, phi2, &phi1Tmp, &phi2Tmp);
    phi1 = phi1Tmp;
    phi2 = phi2Tmp;

    // check start condition
    if (*phiMin > PI || *phiMax > PI)
    {
        *phiMin = phi1;
        *phiMax = phi2;
        return;
    }

    // 6 cases:
    // new angles inbetween phiMin, phiMax -> forget it
    // new angles enclose phiMin -> set phiMin
    // new angles enclose phiMax -> set phiMax
    // new angles completely outside phiMin, phiMax -> leave largest area free
    // new angles close the range completely!
    // new angles enlarges range on both sides

    psi1 = phiDiff(*phiMin, phi1);
    psi2 = phiDiff(*phiMin, phi2);
    psi3 = phiDiff(*phiMax, phi1);
    psi4 = phiDiff(*phiMax, phi2);
    psi5 = phiDiff(*phiMin, *phiMax);
    psi6 = 2.0 * PI - psi5;     // phiDiff(*phiMax, *phiMin);
    psi7 = 2.0 * PI - psi2;     // phiDiff(phi2, *phiMin);

    // case 1 & 5!
    if ((psi1 <= psi5) && (psi2 <= psi5))
    {
        if (psi1 <= psi2)       // case 1
        {
            return;
        }
        else    // case 5
        {
            // create some artificial interval here not to get into numerical trouble
            // in fact we know now the sector is completely enclosed -> base for computational optimization
            *phiMax = 0.0;
            *phiMin = DELTAPHI;
            return;
        }
    }

    // case 2
    if ((psi1 >= psi5) && (psi2 <= psi5))
    {
        *phiMin = phi1;
        return;
    }

    // case 3
    if ((psi3 >= psi6) && (psi4 <= psi6))
    {
        *phiMax = phi2;
        return;
    }

    // case 4 & 6
#ifdef PARANOIA
    if ((psi3 <= psi6) && (psi4 <= psi6))       // FIXME: isn't this case implicitly true anyway??
#endif
    {
        if (psi3 <= psi4)       //case 4
        {
            if (psi3 >= psi7)
            {
                *phiMin = phi1;
                return;
            }
            else
            {
                *phiMax = phi2;
                return;
            }
        }
        else    // case 6
        {
            *phiMin = phi1;
            *phiMax = phi2;
            return;
        }
    }

    I_OutputMsg("phiMin = %f, phiMax = %f, phi1 = %f, phi2 = %f\n", *phiMin, *phiMax, phi1, phi2);
    I_Error("Holy shit, phiBounds() freaked out\n");
}

//
// Check if a vertex lies inside a sector
// This works for "well-behaved" convex polygons
// If we need it mathematically correct, we need to sort the 
// linedefs first so we have them in a row, then walk along the linedefs,
// but this is a bit overdone
// 
boolean isVertexInside(vertex_t * vertex, sector_t * sector)
{
    double xa, ya, xe, ye;
    linechain_t *chain;
    double phiMin, phiMax;
    double phi1, phi2;

    chain = sector->sectorLines;
    phiMin = phiMax = 10.0 * PI;        // some value > \pi

    while (chain)
    {
        // start and end vertex
        xa = (double) chain->line->v1->x - (double) vertex->x;
        ya = (double) chain->line->v1->y - (double) vertex->y;
        xe = (double) chain->line->v2->x - (double) vertex->x;
        ye = (double) chain->line->v2->y - (double) vertex->y;

        // angle phi of connection between the vertices and the x-axis
        phi1 = atan2(ya, xa);
        phi2 = atan2(ye, xe);

        // if we have just started, we can have to create start bounds for phi

        phiBounds(phi1, phi2, &phiMin, &phiMax);
        chain = chain->next;
    }

    return biggerThanPi(phiMin, phiMax);
}

#define MAXSTACK 256    // Not more than 256 polys in each other?
//
// generate a list of sectors which enclose the given sector
//
static void generateStacklist(sector_t * thisSector)
{
    int i;
    int stackCnt;

    sector_t *locStacklist[MAXSTACK];
    sector_t *checkSector;

    stackCnt = 0;

    for (i = 0; i < numsectors; i++)
    {
        checkSector = &sectors[i];

        if (checkSector == thisSector)  // donīt check self
            continue;

        // buggy sector?
        if (NULL == thisSector->sectorLines)
            continue;

        // check if an arbitrary vertex of thisSector lies inside the checkSector
        if (isVertexInside(thisSector->sectorLines->line->v1, checkSector))
        {
            // if so, the thisSector lies inside the checkSector
            locStacklist[stackCnt] = checkSector;
            stackCnt++;

            if (MAXSTACK - 1 == stackCnt)       // beware of the SIGSEGV! and consider terminating NULL!
                break;
        }
    }

    thisSector->stackList = malloc(sizeof(sector_t *) * (stackCnt + 1));
    if (NULL == thisSector->stackList)
    {
        I_Error("Out of memory error in generateStacklist()");
    }

    locStacklist[stackCnt] = NULL;      // terminating NULL

    memcpy(thisSector->stackList, locStacklist, sizeof(sector_t *) * (stackCnt + 1));
}

//
// Bubble sort the stacklist with rising lineoutlengths
//
static void sortStacklist(sector_t * sector)
{
    sector_t **list;
    sector_t *sec1, *sec2;
    boolean finished;
    int i;

    list = sector->stackList;
    finished = false;

    if (NULL == *list)
        return; // nothing to sort

    while (!finished)
    {
        i = 0;
        finished = true;

        while (NULL != *(list + i + 1))
        {
            sec1 = *(list + i);
            sec2 = *(list + i + 1);

            if (sec1->lineoutLength > sec2->lineoutLength)
            {
                *(list + i) = sec2;
                *(list + i + 1) = sec1;
                finished = false;
            }
            i++;
        }
    }
}

//
// length of a line in euclidian sense
//
static double lineLength(line_t * line)
{
    double dx, dy, length;

    dx = (double) line->v1->x - (double) line->v2->x;
    dy = (double) line->v1->y - (double) line->v2->y;

    length = sqrt(dx * dx + dy * dy);

    return length;
}

//
// length of the sector lineout
//
static double calcLineoutLength(sector_t * sector)
{
    linechain_t *chain;
    double length;

    length = 0.0;
    chain = sector->sectorLines;

    while (NULL != chain)       // sum up lengths of all lines
    {
        length += lineLength(chain->line);
        chain = chain->next;
    }
    return length;
}

//
// Calculate length of the sectors lineout
//
static void calcLineouts(sector_t * sector)
{
    sector_t *encSector;
    int secCount;

    secCount = 0;
    encSector = *(sector->stackList);

    while (NULL != encSector)
    {
        if (encSector->lineoutLength < 0.0)     // if length has not yet been calculated
        {
            encSector->lineoutLength = calcLineoutLength(encSector);
        }

        secCount++;
        encSector = *((sector->stackList) + secCount);
    }
}

//
// Free Stacklists of all sectors
//
static void freeStacklists(void)
{
    int i;

    for (i = 0; i < numsectors; i++)
    {
        if (sectors[i].stackList)
        {
            free(sectors[i].stackList);
            sectors[i].stackList = NULL;
        }
    }
}

//
// if more than half of the toptextures are missing
//
static boolean areToptexturesMissing(sector_t * thisSector)
{
    linechain_t *thisElem, *nextElem;
    sector_t *frontSector, *backSector;
    int nomiss;
    side_t *sdl, *sdr;

    thisElem = NULL;
    nextElem = thisSector->sectorLines;
    nomiss = 0;

    while (NULL != nextElem)    // walk through chain
    {
        thisElem = nextElem;
        nextElem = thisElem->next;

        frontSector = thisElem->line->frontsector;
        backSector = thisElem->line->backsector;

        if (frontSector == backSector)  // skip damn renderer tricks here
        {
            continue;
        }

        if (frontSector == NULL || backSector == NULL)
        {
            continue;
        }

        sdr = &sides[thisElem->line->sidenum[0]];
        sdl = &sides[thisElem->line->sidenum[1]];

        if (backSector->ceilingheight < frontSector->ceilingheight)
        {
            if (sdr->toptexture != 0)
            {
                nomiss++;
                break;  // we can stop here if decision criterium is ==0
            }
        }

        else if (backSector->ceilingheight > frontSector->ceilingheight)
        {
            if (sdl->toptexture != 0)
            {
                nomiss++;
                break;  // we can stop here if decision criterium is ==0
            }
        }
    }

    return nomiss == 0;
}

//
// are more textures missing than present?
//
static boolean areBottomtexturesMissing(sector_t * thisSector)
{
    linechain_t *thisElem, *nextElem;
    sector_t *frontSector, *backSector;
    int nomiss;
    side_t *sdl, *sdr;

    thisElem = NULL;
    nextElem = thisSector->sectorLines;
    nomiss = 0;

    while (NULL != nextElem)    // walk through chain
    {
        thisElem = nextElem;
        nextElem = thisElem->next;

        frontSector = thisElem->line->frontsector;
        backSector = thisElem->line->backsector;

        if (frontSector == backSector)  // skip damn renderer tricks here
        {
            continue;
        }

        if (frontSector == NULL || backSector == NULL)
        {
            continue;
        }
        sdr = &sides[thisElem->line->sidenum[0]];
        sdl = &sides[thisElem->line->sidenum[1]];

        if (backSector->floorheight > frontSector->floorheight)
        {
            if (sdr->bottomtexture != 0)
            {
                nomiss++;
                break;  // we can stop here if decision criterium is ==0
            }
        }

        else if (backSector->floorheight < frontSector->floorheight)
        {
            if (sdl->bottomtexture != 0)
            {
                nomiss++;
                break;  // we can stop here if decision criterium is ==0
            }
        }
    }

    //    return missing >= nomiss;
    return nomiss == 0;
}

//
// check if no adjacent sector has same ceiling height
//
static boolean isCeilingFloating(sector_t * thisSector)
{
    sector_t *adjSector, *refSector, *frontSector, *backSector;
    boolean floating = true;
    linechain_t *thisElem, *nextElem;

    if (NULL == thisSector)
        return false;

    refSector = NULL;
    thisElem = NULL;
    nextElem = thisSector->sectorLines;

    while (NULL != nextElem)    // walk through chain
    {
        thisElem = nextElem;
        nextElem = thisElem->next;

        frontSector = thisElem->line->frontsector;
        backSector = thisElem->line->backsector;

        if (frontSector == thisSector)
            adjSector = backSector;
        else
            adjSector = frontSector;

        if (NULL == adjSector)  // assume floating sectors have surrounding sectors
        {
            floating = false;
            break;
        }

        if (NULL == refSector)
        {
            refSector = adjSector;
            continue;
        }

        // if adjacent sector has same height or more than one adjacent sector exists -> stop
        if (thisSector->ceilingheight == adjSector->ceilingheight || refSector != adjSector)
        {
            floating = false;
            break;
        }
    }

    // now check for walltextures
    if (floating)
    {
        if (!areToptexturesMissing(thisSector))
        {
            floating = false;
        }
    }
    return floating;
}

//
// check if no adjacent sector has same ceiling height
// FIXME: throw that together with isCeilingFloating??
//
static boolean isFloorFloating(sector_t * thisSector)
{
    sector_t *adjSector, *refSector, *frontSector, *backSector;
    boolean floating = true;
    linechain_t *thisElem, *nextElem;

    if (NULL == thisSector)
        return false;

    refSector = NULL;
    thisElem = NULL;
    nextElem = thisSector->sectorLines;

    while (NULL != nextElem)    // walk through chain
    {
        thisElem = nextElem;
        nextElem = thisElem->next;

        frontSector = thisElem->line->frontsector;
        backSector = thisElem->line->backsector;

        if (frontSector == thisSector)
            adjSector = backSector;
        else
            adjSector = frontSector;

        if (NULL == adjSector)  // assume floating sectors have surrounding sectors
        {
            floating = false;
            break;
        }

        if (NULL == refSector)
        {
            refSector = adjSector;
            continue;
        }

        // if adjacent sector has same height or more than one adjacent sector exists -> stop
        if (thisSector->floorheight == adjSector->floorheight || refSector != adjSector)
        {
            floating = false;
            break;
        }
    }

    // now check for walltextures
    if (floating)
    {
        if (!areBottomtexturesMissing(thisSector))
        {
            floating = false;
        }
    }
    return floating;
}

//
// estimate ceilingheight according to height of adjacent sector
//
static fixed_t estimateCeilHeight(sector_t * thisSector)
{
    sector_t *adjSector;

    if (NULL == thisSector || NULL == thisSector->sectorLines || NULL == thisSector->sectorLines->line)
        return 0;

    adjSector = thisSector->sectorLines->line->frontsector;
    if (adjSector == thisSector)
        adjSector = thisSector->sectorLines->line->backsector;

    if (NULL == adjSector)
        return 0;

    return adjSector->ceilingheight;
}

//
// estimate ceilingheight according to height of adjacent sector
//
static fixed_t estimateFloorHeight(sector_t * thisSector)
{
    sector_t *adjSector;

    if (NULL == thisSector || NULL == thisSector->sectorLines || NULL == thisSector->sectorLines->line)
        return 0;

    adjSector = thisSector->sectorLines->line->frontsector;
    if (adjSector == thisSector)
        adjSector = thisSector->sectorLines->line->backsector;

    if (NULL == adjSector)
        return 0;

    return adjSector->floorheight;
}

#define CORRECT_FLOAT_EXPERIMENTAL

extern boolean raven;           // true with heretic and hexen

// --------------------------------------------------------------------------
// Some levels have missing sidedefs, which produces HOM, so letīs try to compensate for that
// and some levels have deep water trick, invisible staircases etc.
// --------------------------------------------------------------------------
// FIXME: put some nice default texture in legacy.dat and use it
void HWR_CorrectSWTricks(void)
{
    int i, k;
    line_t *ld;
    side_t *sdl = NULL, *sdr;
    sector_t *secl, *secr;
    sector_t **sectorList;
    sector_t *outSector;

    if ((0 == cv_grcorrecttricks.value) || raven)
        return;

    // determine lines for sectors
    for (i = 0; i < numlines; i++)
    {
        ld = &lines[i];
        secr = ld->frontsector;
        secl = ld->backsector;

        if (secr == secl)
        {
            secr->pseudoSector = true;  // special renderer trick?
            addLineToChain(secr, ld);
        }
        else
        {
            addLineToChain(secr, ld);
            addLineToChain(secl, ld);
        }
    }

    // preprocessing
    for (i = 0; i < numsectors; i++)
    {
        sector_t *checkSector;

        checkSector = &sectors[i];

        // identify real pseudosectors first
        if (checkSector->pseudoSector)
        {
            if (!isPSectorValid(checkSector))   // drop invalid pseudo sectors
            {
                checkSector->pseudoSector = false;
            }
        }

        // determine enclosing sectors for pseudosectors ... used later
        if (checkSector->pseudoSector)
        {
            generateStacklist(checkSector);
            calcLineouts(checkSector);
            sortStacklist(checkSector);
        }
    }

    // set virtual floor heights for pseudo sectors
    // required for deep water effect e.g.
    for (i = 0; i < numsectors; i++)
    {
        if (sectors[i].pseudoSector)
        {
            sectorList = sectors[i].stackList;
            k = 0;
            while (*(sectorList + k))
            {
                outSector = *(sectorList + k);
                if (!outSector->pseudoSector)
                {
                    sectors[i].virtualFloorheight = outSector->floorheight;
                    sectors[i].virtualCeilingheight = outSector->ceilingheight;
                    break;
                }
                k++;
            }
            if (*(sectorList + k) == NULL)      // sorry, did not work :(
            {
                sectors[i].virtualFloorheight = sectors[i].floorheight;
                sectors[i].virtualCeilingheight = sectors[i].ceilingheight;
            }
        }
    }
#ifdef CORRECT_FLOAT_EXPERIMENTAL
    // correct ceiling/floor heights of totally floating sectors
    for (i = 0; i < numsectors; i++)
    {
        sector_t *floatSector;

        floatSector = &sectors[i];

        // correct height of floating sectors
        if (isCeilingFloating(floatSector))
        {
            fixed_t corrheight;

            corrheight = estimateCeilHeight(floatSector);
            floatSector->virtualCeilingheight = corrheight;
            floatSector->virtualCeiling = true;
        }
        if (isFloorFloating(floatSector))
        {
            fixed_t corrheight;

            corrheight = estimateFloorHeight(floatSector);
            floatSector->virtualFloorheight = corrheight;
            floatSector->virtualFloor = true;
        }
    }
#endif

    // now for the missing textures
    for (i = 0; i < numlines; i++)
    {
        ld = &lines[i];
        sdr = &sides[ld->sidenum[0]];
        if (ld->sidenum[1] >= 0)
        {
            sdl = &sides[ld->sidenum[1]];
        }

        secr = ld->frontsector;
        secl = ld->backsector;

        if (secr == secl)       // special renderer trick 
            continue;   // we canīt correct missing textures here

        if (NULL != secl)       // only if there is a backsector 
        {
            if (secr->pseudoSector || secl->pseudoSector)
                continue;
            if (!secr->virtualFloor && !secl->virtualFloor)
            {
                if (secl->floorheight > secr->floorheight)
                {
                    // now check if r-sidedef is correct
                    if (sdr->bottomtexture == 0)
                    {
                        if (sdr->midtexture == 0)
                            sdr->bottomtexture = R_TextureNumForName("STONE2");
                        else
                            sdr->bottomtexture = sdr->midtexture;
                    }
                }
                else if (secl->floorheight < secr->floorheight)
                {
                    // now check if l-sidedef is correct
                    if (sdl->bottomtexture == 0)
                    {
                        if (sdl->midtexture == 0)
                            sdl->bottomtexture = R_TextureNumForName("STONE2");
                        else
                            sdl->bottomtexture = sdl->midtexture;
                    }
                }
            }

            if (!secr->virtualCeiling && !secl->virtualCeiling)
            {
                if (secl->ceilingheight < secr->ceilingheight)
                {
                    // now check if r-sidedef is correct
                    if (sdr->toptexture == 0)
                    {
                        if (sdr->midtexture == 0)
                            sdr->toptexture = R_TextureNumForName("STONE2");
                        else
                            sdr->toptexture = sdr->midtexture;
                    }
                }
                else if (secl->ceilingheight > secr->ceilingheight)
                {
                    // now check if l-sidedef is correct
                    if (sdl->toptexture == 0)
                    {
                        if (sdl->midtexture == 0)
                            sdl->toptexture = R_TextureNumForName("STONE2");
                        else
                            sdl->toptexture = sdl->midtexture;

                    }
                }
            }
        }       // if(NULL != secl)
    }   // for(i=0; i<numlines; i++)

    // release all linechains
    releaseLineChains();
    freeStacklists();
}
