/*

Copyright (C) 2015-2018 Night Dive Studios, LLC.

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 3 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, see <http://www.gnu.org/licenses/>.

*/
/*
 * FrClip.c
 *
 * $Source: r:/prj/cit/src/RCS/frclip.c $
 * $Revision: 1.12 $
 * $Author: dc $
 * $Date: 1994/09/10 00:30:09 $
 *
 * Citadel Renderer
 *  various clippers for terrain, including basic cone clip and the later day
 *   tile based clipper
 *
 * $Log: frclip.c $
 * Revision 1.12  1994/09/10  00:30:09  dc
 * this time i really think i fixed the malloc's
 *
 * Revision 1.11  1994/09/06  03:14:58  dc
 * do cspace a little more correctly, eh?
 *
 * Revision 1.10  1994/09/05  08:33:35  dc
 * hey how about not forgetting about cyberspace this time, eh?
 *
 * Revision 1.9  1994/09/05  06:43:26  dc
 * span parse fixes, clear as we go
 *
 * Revision 1.8  1994/08/30  05:51:01  dc
 * fix vanish if home square not drawn bug, othe rstuff
 *
 * Revision 1.7  1994/08/21  03:10:06  dc
 * duh.
 *
 * Revision 1.6  1994/08/21  03:06:55  dc
 * parameterize spawn_check, save a bit of code space
 *
 * Revision 1.5  1994/07/28  05:53:42  dc
 * protection from span clip nightmare, what is cone doing...
 *
 * Revision 1.4  1994/03/13  17:18:04  dc
 * doors take 38, still doesnt do minimal inclusion clips right
 *
 * Revision 1.3  1994/03/10  00:11:03  dc
 * better door stuff, still need some stuff, though....
 *
 * Revision 1.2  1994/01/22  18:57:39  dc
 * fix objclip to set correct bits of subclip, next switch it to flick_qclip
 */

#define __FRCLIP_SRC

#include <stdlib.h>
#include <string.h>

#include "frintern.h"
#include "frspans.h"
#include "frtables.h"
#include "fr3d.h"
#include "frquad.h"
#include "frflags.h"
#include "frparams.h"
#include "frsubclp.h"
#include "frshipm.h"

#include "cone.h"

#include "map.h"
#include "tilename.h"

// pre prototyping
static void _fr_rebuild_nVecWork(void);
static void _fr_init_vecwork(void);
static void clear_clip_bits(void);

// for the subtile clipper
ushort sc_reg[NUM_SUBCLIP][SC_VEC_COUNT];
ushort *cur_sc_ptr;
uint cur_sc_reg;

// Prototypes
void _fr_sclip_line(MapElem *sp_base, int len, int val);
void _fr_sclip_line_check_solid(MapElem *sp_base, int len, int val);
void fr_span_parse(void);
void span_fixup(void);
int fr_clip_show_all(void);
// uchar _fr_move_ccv_x(struct _nVecWork *nvp);
void _fr_move_along_dcode(int dircode);

#ifndef MAP_RESIZING
static uchar real_x_spans[(1 << DEFAULT_YSHF) * SPAN_MEM];
static uchar real_cone_spans[(1 << DEFAULT_YSHF) * 2];
#endif

int fr_clip_freemem(void) {
    if (x_span_lists != NULL)
        free(x_span_lists);
    _fr_ret;
}

int fr_clip_resize(int x, int y) // x, y
{
    int i;
#ifdef MAP_RESIZING
    if (x_span_lists != NULL)
        free(x_span_lists);
    if (cone_span_list != NULL)
        free(cone_span_list);
    x_span_lists = (uchar *)malloc(y * SPAN_MEM * sizeof(uchar));
    cone_span_list = (uchar *)malloc(y * 2 * sizeof(uchar));
#else
    x_span_lists = &real_x_spans[0];
    cone_span_list = &real_cone_spans[0];
#endif
    _fr_rebuild_nVecWork();
    _fr_init_vecwork();
    _fr_dbg(if (x_span_lists == NULL) _fr_ret_val(FR_NOMEM));
    _fr_dbg(if (cone_span_list == NULL) _fr_ret_val(FR_NOMEM));
    for (i = 0; i < fr_map_y; i++)
        span_count(i) = 0;
    _fr_ret;
}

int fr_clip_frame_start(void) {
    // setup real span lists
    // setup real obj stack
    //   _fr_init_vecwork();
    // hmm... is this really necessary????
    LG_memset(cone_span_list, 0xff, fr_map_y * 2 * sizeof(uchar));
    _fr_sdbg(SANITY, _fr_init_vecwork()); // hey, why not?
    _fr_ret;
}

int fr_clip_frame_end(void) {
#ifndef CLEAR_AS_WE_GO
    clear_clip_bits();
#endif
    _fr_ret;
}

// cradle every word the falls
// into the arms of failure
void store_x_span(int y, int lx, int rx) {
    int c_span;

    c_span = span_count(y)++;
    if (c_span >= MAX_SPANS) {
        //      Warning(("HEY too many spans!!! y %d from %d to %d\n",y,lx,rx));  // should probably solve the problem
        span_count(y)--;
        c_span--;
        span_right(y, c_span) = rx; // just add a new right edge
    } else {
        span_left(y, c_span) = lx;
        span_right(y, c_span) = rx;
    }
    _fr_sdbg(VECSPEW, mprintf("Put %d->%d at span %d of %d\n", lx, rx, c_span, y));

#ifdef _FR_TILEMAP
    if (fr_highlights) {
        LGPoint p;
        for (p.x = lx, p.y = y; p.x <= rx; p.x++)
            TileMapSetHighlight(NULL, p, 0, TRUE);
    }
#endif // _FR_TILEMAP
}

void _fr_sclip_line(MapElem *sp_base, int len, int val) {
    _fr_sdbg(SPAN_PARSE, mprintf("yep, at %x, len %x, val %x\n", sp_base, len, val));
    for (; len > 0; len--, sp_base++)
        me_subclip(sp_base) = val;
}

void _fr_sclip_line_check_solid(MapElem *sp_base, int len, int val) {
    _fr_sdbg(SPAN_PARSE, mprintf("yep, at %x, len %x, val %x\n", sp_base, len, val));
    for (; len > 0; len--, sp_base++)
        if (me_tiletype(sp_base) == TILE_SOLID)
            me_subclip(sp_base) = SUBCLIP_OUT_OF_CONE;
        else
            me_subclip(sp_base) = val;
}

// compute bounding box as well
extern MapElem *fr_map_base;
ushort frpipe_dist;
void fr_span_parse(void) {
    int y, dist;
    MapElem *cur_span = fr_map_base;
    uchar *cur_span_cnt = &(span_count(0)), *cur_cone_span = &cone_span_list[0];

    frpipe_dist = 0;
    for (y = 0; y < fr_map_y; y++, cur_span += fr_map_x, cur_span_cnt += (1 << SPAN_SHIFT), cur_cone_span += 2)
        if (*cur_span_cnt) {
            if ((*cur_span_cnt) == 1) {
                _fr_sdbg(SPAN_PARSE, mprintf("sc 1 at %x, going from %x to %x and %x to %x\n", y, *cur_cone_span,
                                             span_left(y, 0), span_right(y, 0) + 1, *(cur_cone_span + 1)));
                _fr_sclip_line(cur_span + (*cur_cone_span), span_left(y, 0) - *cur_cone_span, SUBCLIP_OUT_OF_CONE);
                _fr_sclip_line(cur_span + span_right(y, 0) + 1, (*(cur_cone_span + 1)) - (span_right(y, 0) + 1) + 1,
                               SUBCLIP_OUT_OF_CONE);
            } else { // first merge wacky scenes....
                int cur_l, cur_r, span_id = 0, off_l;
                _fr_sdbg(SPAN_PARSE, for (cur_l = 0; cur_l < *cur_span_cnt; cur_l++)
                                         mprintf("at %x MultiSpan %d from %x to %x\n", y, cur_l, span_left(y, cur_l),
                                                 span_right(y, cur_l)));
                cur_l = span_left(y, 0);
                cur_r = span_right(y, 0);
                off_l = *cur_cone_span;
                while (++span_id < (*cur_span_cnt)) {
                    if (span_left(y, span_id) <=
                        cur_r) { // merge the spans into 1, but sclip clean any overlap pass first
                        _fr_sdbg(SPAN_PARSE, mprintf("at %x Cleanup from %x to %x\n", y, span_left(y, span_id), cur_r));
                        _fr_sclip_line(cur_span + span_left(y, span_id), cur_r - span_left(y, span_id) + 1,
                                       SUBCLIP_FULL_TILE); // set them to no subclip
                        if (cur_r < span_right(y, span_id)) {
                            _fr_sdbg(SPAN_PARSE,
                                     mprintf("span merge right to %x from %x\n", span_right(y, span_id), cur_r));
                            cur_r = span_right(y, span_id);
                        }
                    } else {
                        _fr_sdbg(SPAN_PARSE, mprintf("sent old span %x from %x to %x\n", y, off_l, cur_l));
                        _fr_sclip_line(cur_span + off_l, cur_l - off_l, SUBCLIP_OUT_OF_CONE);
                        off_l = cur_r + 1;
                        cur_l = span_left(y, span_id);
                        cur_r = span_right(y, span_id);
                    }
                }
                _fr_sdbg(SPAN_PARSE, mprintf("loop done at %x, now sending %x -> %x and %x -> %x\n", y, off_l, cur_l,
                                             cur_r + 1, *(cur_cone_span + 1)));
                _fr_sclip_line(cur_span + off_l, cur_l - off_l, SUBCLIP_OUT_OF_CONE);
                _fr_sclip_line(cur_span + cur_r + 1, (*(cur_cone_span + 1)) - (cur_r + 1) + 1, SUBCLIP_OUT_OF_CONE);
            }
            dist = abs(y - _fr_y_cen);
            if ((dist + abs(span_left(y, 0) - _fr_x_cen)) > frpipe_dist)
                frpipe_dist = dist + abs(span_left(y, 0) - _fr_x_cen);
            if ((dist + abs(span_right(y, (*cur_span_cnt) - 1) - _fr_x_cen)) > frpipe_dist)
                frpipe_dist = dist + abs(span_right(y, (*cur_span_cnt) - 1) - _fr_x_cen);
#ifdef CLEAR_AS_WE_GO
            *cur_span_cnt = 0;
#endif
        } else if (*cur_cone_span != 0xff) {
            _fr_sdbg(SPAN_PARSE, mprintf("Cleaning up %x from %x to %x\n", y, *cur_cone_span, *(cur_cone_span + 1)));
            _fr_sclip_line(cur_span + (*cur_cone_span), (*(cur_cone_span + 1)) - (*(cur_cone_span)) + 1,
                           SUBCLIP_OUT_OF_CONE);
        } // note the ()'s are right, it is (right edge) - (left edge) + 1
}

void span_fixup(void) {
    _fr_sdbg(VECSPEW,
             mprintf("Center was %d spans, %d -> %d and %d -> %d\n", span_count(_fr_y_cen), span_left(_fr_y_cen, 0),
                     span_right(_fr_y_cen, 0), span_left(_fr_y_cen, 1), span_right(_fr_y_cen, 1)));
    if (span_count(_fr_y_cen) > 1) {
        span_count(_fr_y_cen)--;
        span_left(_fr_y_cen, 0) = lg_min(span_left(_fr_y_cen, 0), span_left(_fr_y_cen, 1));
        span_right(_fr_y_cen, 0) = lg_max(span_right(_fr_y_cen, 0), span_right(_fr_y_cen, 1));
    } else
        WARN("%s: Only one span at y center", __FUNCTION__);
    fr_span_parse();
#if _fr_defdbg(VECSPEW)
    if (_fr_dbgflg_chk(VECSPEW)) {
        int i, j, lc;
        mprintf("At %d %d\n", _fr_x_cen, _fr_y_cen);
        for (i = 0, lc = -1; i < fr_map_y; i++) {
            if (span_count(i) == 0) {
                if (lc == -1)
                    lc = i;
            } else {
                if (lc != -1) {
                    mprintf("y %d-%d have none\n", lc, i);
                    lc = -1;
                }
                mprintf("y%d c%d: ", i, span_count(i));
                for (j = 0; j < span_count(i); j++)
                    mprintf(" %d->%d", span_left(i, j), span_right(i, j));
                mprintf("\n");
            }
        }
        if (lc != -1)
            mprintf("y %d-%d have none\n", lc, i - 1);
    }
#endif
}

#define STAY_ON_MAP
void cone_span_set(int y, int l, int r) {
    cone_span_list[y + y] = l;
    cone_span_list[y + y + 1] = r;
}

#ifndef CLEAR_AS_WE_GO
static void clear_clip_bits(void) {
    int i, j;
    MapElem *mbptr = MAP_MAP, *mptr;
    for (i = 0; i < fr_map_y; i++, mbptr += fr_map_x)
        if (cone_span_left(i) != 0xff)
            for (j = cone_span_left(i), mptr = mbptr + j; j <= cone_span_right(i); j++, mptr++)
                _me_subclip(mptr) = SUBCLIP_OUT_OF_CONE;
}
#endif

static void set_clip_bits(void) {
    int i;
    MapElem *mbptr = MAP_MAP, *mptr, *rptr;
    for (i = 0; i < fr_map_y; i++, mbptr += fr_map_x)
        if (cone_span_left(i) != 0xff)
            for (mptr = mbptr + cone_span_left(i), rptr = mbptr + cone_span_right(i); mptr <= rptr; mptr++)
                _me_subclip(mptr) = SUBCLIP_FULL_TILE;
}

#if _fr_defdbg(NO_CONE)
void set_full_cone(void) {}
#endif

// satan got her tongue
// now, it's undone
int fr_clip_cone(void) {
    simple_cone_clip_pass();
    //   _fr_ndbg(NO_CONE,simple_cone_clip_pass());
    //   _fr_sdbg(NO_CONE,set_full_cone());
    set_clip_bits();

#if _fr_defdbg(VECSPEW)
    if (_fr_dbgflg_chk(VECSPEW)) {
        int i, lc;
        mprintf("Cone from %d %d\n", _fr_x_cen, _fr_y_cen);
        for (i = 0, lc = -1; i < fr_map_y; i++) {
            if (cone_span_left(i) == 0xff) {
                if (lc == -1)
                    lc = i;
            } else {
                if (lc != -1) {
                    mprintf("y %d-%d have none\n", lc, i);
                    lc = -1;
                }
                mprintf("y%d: %d->%d\n", i, cone_span_left(i), cone_span_right(i));
            }
        }
        if (lc != -1)
            mprintf("y %d-%d have none\n", lc, i - 1);
        mprintf("left (%x,%x), right (%x,%x)\n", span_intersect[0], span_intersect[1], span_intersect[2],
                span_intersect[3]);
        mprintf("vec tl (%x,%x), vec tr (%x,%x)\n", span_lines[2], span_lines[3], span_lines[4], span_lines[5]);
        mprintf("vec bl (%x,%x), vec br (%x,%x)\n", span_lines[0], span_lines[1], span_lines[6], span_lines[7]);
        mprintf("Note eye @ %x %x, ang %x %x %x\n", coor(EYE_X), coor(EYE_Y), coor(EYE_H), coor(EYE_P), coor(EYE_B));
    }
#endif
    _fr_ret;
}

// these two are a little unstoked at the moment
int fr_clip_show_all(void) {
    MapElem *cur_span = fr_map_base;
#ifndef REALLY_ALL
    uchar *cur_cone_span = &cone_span_left(0);
    int i, dist;
    for (i = 0; i < fr_map_y; i++, cur_span += fr_map_x, cur_cone_span += 2)
        if ((*cur_cone_span) != 0xff) {
#ifndef CLEAR_AS_WE_GO
            store_x_span(i, *cur_cone_span, *(cur_cone_span + 1));
#endif
            _fr_sclip_line_check_solid(cur_span + (*cur_cone_span), *(cur_cone_span + 1) - *(cur_cone_span) + 1,
                                       SUBCLIP_FULL_TILE);
            dist = abs(i - _fr_y_cen);
            if ((dist + abs(*cur_cone_span - _fr_x_cen)) > frpipe_dist)
                frpipe_dist = dist + abs(*cur_cone_span - _fr_x_cen);
            if ((dist + abs(*(cur_cone_span + 1) - _fr_x_cen)) > frpipe_dist)
                frpipe_dist = dist + abs(*(cur_cone_span - 1) - _fr_x_cen);
        }
#else
    int y;
    for (y = 0; y < fr_map_y; y++) {
#ifndef CLEAR_AS_WE_GO
        store_x_span(y, 0, fr_map_x - 1);
#endif
        _fr_sclip_line_check_solid(cur_span + (*cur_cone_span), *(cur_cone_span + 1) - *(cur_cone_span) + 1,
                                   SUBCLIP_FULL_TILE);
    }
    if (_fr_x_cen < (fr_map_x >> 1))
        dist = fr_map_x - _fr_x_cen;
    else
        dist = _fr_x_cen - fr_map_x;
    if (_fr_y_cen < (fr_map_y >> 1))
        dist += fr_map_y - _fr_y_cen;
    else
        dist = _fr_y_cen - fr_map_y;
#endif
    _fr_ret;
}

// when i hear the word security, i reach for my shotgun
typedef struct {
    fix loc[3];    // current location of vector
    fix deltas[3]; // current steps for vector
    MapElem *mptr; // current map pointer
    uchar flags;   // inuse, LR, pointer to self
    uchar nxtv;    // for now, points to next
    fix oldx;      // x at last y crossing
    fix len;       // current vector length
} FrClipVec;

struct _nVecWork {
    fix remx;         // how far to go in x
    short mapstep[2]; // how to move in map when going by x, y
    uchar inface[2];  // inface for x and y
    fix stepy;        // how far to go in y for a remx
    uchar move_x;     // do we move in x?
    uchar dircode;    // so we can get back out
};

#define nVW_XDIR 2
#define nVW_YDIR 1

#define nVW_NXNY 0
#define nVW_NXPY 1
#define nVW_PXNY 2
#define nVW_PXPY 3

#define _fixp1 (fix_make(1, 0))
#define _fixn1 (-fix_make(1, 0))
static fix locstep[4][2] = {{_fixn1, _fixn1}, {_fixn1, _fixp1}, {_fixp1, _fixn1}, {_fixp1, _fixp1}};
static fix edgestep[4][2] = {{0, 0}, {0, _fixp1 - 1}, {_fixp1 - 1, 0}, {_fixp1 - 1, _fixp1 - 1}};
static char rmmod[4] = {-1, -1, 1, 1};       /* mod to rem to get to next full square */
static short new_del_mapstep[2] = {-32, 32}; /* wants to be wall_adds[2] and then wall_adds[0] */
static MapElem *eye_mptr;

#define FRVECSELF 0x3F
#define FRVECMASK 0x3F
#define FRVECUSE  0x80
#define FRVECL    0x40
#define FRVECR    0x00
#define FRVECDIR  0x40

#define MS_X 0
#define MS_Y 1

#define MAX_CLIP_VEC 16
static FrClipVec allclipv[MAX_CLIP_VEC];
static FrClipVec *ccv;
static char vechead = 0, ffreevec = 0, lastvec = -1, endvec = MAX_CLIP_VEC - 1;

struct _nVecWork _nVP[4] = {{0xdeadbeef, -1, -64, 3, 2, 0xdeadbeef, 0xff, nVW_NXNY},
                            {0xdeadbeef, -1, 64, 3, 0, 0xdeadbeef, 0xff, nVW_NXPY},
                            {0xdeadbeef, 1, -64, 1, 2, 0xdeadbeef, 0xff, nVW_PXNY},
                            {0xdeadbeef, 1, 64, 1, 0, 0xdeadbeef, 0xff, nVW_PXPY}};

// this is a gross way to do this...
#define _fr_build_clip_vec(cv, org, ray, aflags)               \
    (cv)->len = 0;                                             \
    (cv)->flags = ((cv)->flags & FRVECMASK) + aflags;          \
    (cv)->mptr = MAP_GET_XY(fix_int(org[0]), fix_int(org[1])); \
    (cv)->loc[0] = org[0];                                     \
    (cv)->loc[1] = org[1];                                     \
    (cv)->loc[2] = org[2];                                     \
    cv->oldx = org[0];                                         \
    (cv)->deltas[0] = ray[0];                                  \
    (cv)->deltas[1] = ray[1];                                  \
    (cv)->deltas[2] = ray[2]

// More prototypes
uchar _fr_skip_solid_right_n_back(FrClipVec *cv, fix max_loc, int y_map_step);
uchar _fr_skip_space_right_n_back(FrClipVec *cv, fix max_loc, int y_map_step);
uchar _fr_skip_solid_left_n_back(FrClipVec *cv, fix min_loc, int y_map_step);
void _fr_del_compute(FrClipVec *v1, FrClipVec *v2);
void _fr_spawn_check_one(FrClipVec *lv, FrClipVec *rv, uchar northward);
uchar _fr_move_new_dels(FrClipVec *lv, FrClipVec *rv, uchar northward);
void _fr_kill_pair(FrClipVec *lv, FrClipVec *rv);
uchar _fr_setup_first_pair(uchar headnorth);

// these dont really work, the *org, *ray doesnt move all 3
// (*((cv)->loc))=*org; (*((cv)->deltas))=*ray;
// this gets a missing lvalue stupidity
// (cv)->loc=org; (cv)->deltas=ray;

static void _fr_rebuild_nVecWork(void) {
    _nVP[nVW_NXNY].mapstep[MS_X] = _nVP[nVW_NXPY].mapstep[MS_X] = wall_adds[3];
    _nVP[nVW_PXNY].mapstep[MS_X] = _nVP[nVW_PXPY].mapstep[MS_X] = wall_adds[1];
    _nVP[nVW_NXNY].mapstep[MS_Y] = _nVP[nVW_PXNY].mapstep[MS_Y] = wall_adds[2];
    _nVP[nVW_NXPY].mapstep[MS_Y] = _nVP[nVW_PXPY].mapstep[MS_Y] = wall_adds[0];
    new_del_mapstep[0] = wall_adds[2];
    new_del_mapstep[1] = wall_adds[0];
}

static void _fr_init_vecwork(void) {
    int i;
    for (i = 0; i < MAX_CLIP_VEC; i++) // point at self and next
    {
        allclipv[i].flags = i;
        allclipv[i].nxtv = (i + 1) & (MAX_CLIP_VEC - 1);
    }
    vechead = 0;
    ffreevec = 0;
    lastvec = -1;
    endvec = MAX_CLIP_VEC - 1;
}

#if _fr_defdbg(VECSPEW)
void print_nvp(struct _nVecWork *nvp) {
    static char *nvm_str[4] = {"nxny", "nxpy", "pxny", "pxpy"};
    static char ifc_str[4] = {'n', 'e', 's', 'w'};
    mprintf(" _nVP xrem %x stepy %x. move_x %d std %s %c%c %d %d\n", nvp->remx, nvp->stepy, nvp->move_x,
            nvm_str[nvp->dircode], ifc_str[nvp->inface[0]], ifc_str[nvp->inface[1]], nvp->mapstep[0], nvp->mapstep[1]);
}

void print_fcv(FrClipVec *_ccv, int dim) {
    if (dim == 3)
        mprintf(" _ccv @(%x,%x,%x),d(%x,%x,%x)..", _ccv->loc[0], _ccv->loc[1], _ccv->loc[2], _ccv->deltas[0],
                _ccv->deltas[1], _ccv->deltas[2]);
    else
        mprintf(" _ccv @(%x,%x),d(%x,%x)..", _ccv->loc[0], _ccv->loc[1], _ccv->deltas[0], _ccv->deltas[1]);
    mprintf("ox%x mp%x fl%xnx%x\n", _ccv->oldx, _ccv->mptr, _ccv->flags, _ccv->nxtv);
}
#else
#define print_nvp(a)
#define print_fcv(a, b)
#endif

static uchar *_face_curedge, *_face_nxtedge;
static uchar _face_curmask, _face_nxtmask;
static uchar _face_topmask, _face_botmask;

//#define out_of_cone (me_bits_seen_p(mp)==0)
#define out_of_cone(mp) (me_subclip(mp) == SUBCLIP_OUT_OF_CONE)

// indexed by (vecside)+(dircode<<2)+xdir/ydir, with do nothings for second at Y
static uchar _sclip_major_mask[9][2] = {{FMK_SW, FMK_EW}, {FMK_SW, FMK_WW}, {FMK_NW, FMK_EW},
                                        {FMK_NW, FMK_WW}, {FMK_NW, FMK_WW}, {FMK_NW, FMK_EW},
                                        {FMK_SW, FMK_WW}, {FMK_SW, FMK_EW}, {0, 0}};

static uchar _sclip_door_mask[9][2] = {{FMK_INT_NW, FMK_INT_EW}, {FMK_INT_NW, FMK_INT_WW}, {FMK_INT_SW, FMK_INT_EW},
                                       {FMK_INT_SW, FMK_INT_WW}, {FMK_INT_SW, FMK_INT_WW}, {FMK_INT_SW, FMK_INT_EW},
                                       {FMK_INT_NW, FMK_INT_WW}, {FMK_INT_NW, FMK_INT_EW}, {0, 0}};

static uchar *_sclip_mask, *_sclip_door;

// assumes ccv is us
// moves until next y span is hit, i guess
// perhaps will have to learn about it's partner vector?
// strip the gloss from a beauty queen
uchar _fr_move_ccv_x(struct _nVecWork *nvp) {
    int tt, oflow;
    uchar move_in_y = TRUE, move_in_x = TRUE;

    // should be saving off texture cuts some day!!
    ccv->loc[1] += nvp->stepy;
    tt = me_tiletype(ccv->mptr);
    if (fr_obj_block(ccv->mptr, _sclip_door, (int *)ccv->loc) ||
        ((_face_curedge[tt << 2] == 0xff) ||
         (me_clearsolid(ccv->mptr) &
          _face_curmask))) { // these really have to get wacky and learn about partial obscuration
        move_in_x = move_in_y = FALSE;
        _fr_sdbg(VECSPEW, mprintf("move_x(top): hit our tile\n"));
    } else {
        _fr_sdbg(VECSPEW, mprintf("move_x(top): sub_clip or %x, old %x\n", _sclip_mask[0], me_subclip(ccv->mptr)));
        _me_subclip(ccv->mptr) |= _sclip_mask[0];
        ccv->mptr += nvp->mapstep[0];
        tt = me_tiletype(ccv->mptr);
        if ((_face_nxtedge[tt << 2] == 0xff) || (me_clearsolid(ccv->mptr) & _face_nxtmask) ||
            out_of_cone(ccv->mptr)) { // these really have to get wacky and learn about partial obscuration
            move_in_x = move_in_y = FALSE;
            ccv->mptr -= nvp->mapstep[0];
            _fr_sdbg(VECSPEW, mprintf("move_x top: hit other tile\n"));
        } else { // correct for new setup
            ccv->loc[0] += nvp->remx + rmmod[nvp->dircode];
            nvp->remx = locstep[nvp->dircode][0];
            nvp->stepy = fix_mul_div(ccv->deltas[1], nvp->remx, ccv->deltas[0]);
            _fr_sdbg(VECSPEW, mprintf("move_x top: move and recompute step\n"));
        }
    }
    _fr_sdbg(VECSPEW, {
        mprintf("move_x top:\n");
        print_fcv(ccv, 2);
        print_nvp(nvp);
    });

    oflow = ccv->loc[1] & 0xffff;
    while (move_in_x) {
        oflow += nvp->stepy;
        if (oflow & 0xffff0000) { // perhaps should get more elegant, but i mean really, how could it?
            move_in_x = FALSE;
            move_in_y = TRUE;
            _fr_sdbg(VECSPEW, mprintf("move_x(while): reached y\n"));
        } else {
            ccv->loc[1] += nvp->stepy;
            // can we leave the square there?
            tt = me_tiletype(ccv->mptr);
            // no matter what, we can get out of ourselves?
            if (fr_obj_block(ccv->mptr, _sclip_door, (int *)ccv->loc) ||
                ((_face_curedge[tt << 2] == 0xff) ||
                 (me_clearsolid(ccv->mptr) &
                  _face_curmask))) { // these really have to get wacky and learn about partial obscuration
                move_in_x = move_in_y = FALSE;
                _fr_sdbg(VECSPEW, mprintf("move_x(while): hit our tile\n"));
            } else {
                _fr_sdbg(VECSPEW,
                         mprintf("move_x(while): sub_clip or %x, old %x\n", _sclip_mask[0], me_subclip(ccv->mptr)));
                _me_subclip(ccv->mptr) |= _sclip_mask[0];
                ccv->mptr += nvp->mapstep[0];
                tt = me_tiletype(ccv->mptr);
                if ((_face_nxtedge[tt << 2] == 0xff) || (me_clearsolid(ccv->mptr) & _face_nxtmask) ||
                    out_of_cone(ccv->mptr)) { // these really have to get wacky and learn about partial obscuration
                    move_in_x = move_in_y = FALSE;
                    ccv->mptr -= nvp->mapstep[0];
                    _fr_sdbg(VECSPEW, mprintf("move_x(while): hit other tile\n"));
                } else {
                    ccv->loc[0] += locstep[nvp->dircode][0];
                    _fr_sdbg(VECSPEW, mprintf("move_x: moving ccv\n"));
                }
            }
        }
        _fr_sdbg(VECSPEW, {
            mprintf("move_x(while):\n");
            print_fcv(ccv, 2);
            print_nvp(nvp);
        });
    }

    // should go back to this, it should add to oflow, non add to loc[1] in loop, then do loc[1]|=(oflow-stepy)
    //   if (move_in_y) ccv->loc[1]-=nvp->stepy;      // undo the step that went too far(tm)

    return move_in_y;
    // compute intersection
    // if not clear internally, migrate to clear point, recompute
    //   if now clear internally, move mptr and vec to next square
    //   if not, backup pointers, return FALSE
    // check crossin clear, if clear return TRUE
    // if not clear, migrate to next clear point, recompute
    //   if now clear, return TRUE
    //   if not, backup pointers if appropriate, return FALSE
}

void _fr_move_along_dcode(int dircode) {
    uchar move_in_y = TRUE;
    struct _nVecWork *nvp = &_nVP[dircode];

    if (dircode & nVW_XDIR)
        nvp->remx = _fixp1 - 1 - fix_frac(ccv->loc[0]);
    else
        nvp->remx = -fix_frac(ccv->loc[0]);

    if (ccv->deltas[1] == 0) // no y delta
    {
        nvp->move_x = TRUE;
        nvp->stepy = 0;
    }                             // lets do the flat line thing
    else if (ccv->deltas[0] == 0) // just do a single y step
        nvp->move_x = FALSE;      // no need to set step or anything
    else                          // replace with wacky table neg?
    {                             // get the signs right, as it were
        if (nvp->remx)
            switch (dircode) { // these just arent right
            case nVW_NXNY:
                nvp->move_x = (-nvp->remx * ccv->deltas[1] > (fix_frac(ccv->loc[1])) * (ccv->deltas[0]));
                break; // flip sign for --
            case nVW_NXPY:
                nvp->move_x = (nvp->remx * ccv->deltas[1] > (_fixp1 - 1 - fix_frac(ccv->loc[1])) * (ccv->deltas[0]));
                break; // flip xd, sign
            case nVW_PXNY:
                nvp->move_x = (-nvp->remx * ccv->deltas[1] < (fix_frac(ccv->loc[1])) * ccv->deltas[0]);
                break; // flip yd, sign
            case nVW_PXPY:
                nvp->move_x = (nvp->remx * ccv->deltas[1] < (_fixp1 - 1 - fix_frac(ccv->loc[1])) * ccv->deltas[0]);
                break; // all things good
            }
        else
            nvp->move_x = TRUE;
        if (nvp->move_x)
            nvp->stepy = fix_mul_div(nvp->remx, ccv->deltas[1], ccv->deltas[0]);
    }

    // this is dumb, we want the second to be based on primary
    if (ccv->mptr == eye_mptr) // ( () &&((nvp->dircode&nVW_YDIR)==0))
    {
        _sclip_mask = &_sclip_major_mask[8][0];
        //      _sclip_door=&_sclip_door_mask[8][0];
    } else {
        _sclip_mask = (&_sclip_major_mask[0][0]) + ((ccv->flags & FRVECDIR) >> 3) + (dircode + dircode);
        //      _sclip_door=(&_sclip_door_mask[0][0])+((ccv->flags&FRVECDIR)>>3)+(dircode+dircode);
    }
    _sclip_door = (&_sclip_door_mask[0][0]) + ((ccv->flags & FRVECDIR) >> 3) + (dircode + dircode);

    _fr_sdbg(VECSPEW, {
        mprintf("move_along(new_vec): sclip offs %d d%d (%d,%d)\n", _sclip_mask - _sclip_major_mask, dircode,
                *_sclip_mask, *(_sclip_mask + 1));
        print_fcv(ccv, 2);
        print_nvp(nvp);
    });

    if (nvp->move_x) {
        if (dircode & nVW_XDIR) {
            _face_curedge = &face_obstruct[0][1];
            _face_nxtedge = &face_obstruct[0][3];
            _face_curmask = FMK_INT_EW;
            _face_nxtmask = FMK_INT_WW;
        } else {
            _face_curedge = &face_obstruct[0][3];
            _face_nxtedge = &face_obstruct[0][1];
            _face_curmask = FMK_INT_WW;
            _face_nxtmask = FMK_INT_EW;
        }
        move_in_y = _fr_move_ccv_x(nvp);
    }

    // the more he likes me, the more i drink
    // i think the more i drink the more he likes me
    if (move_in_y) { // actually move in y
        fix nstp;
        if (dircode & nVW_YDIR)
            nstp = _fixp1 - 1 - fix_frac(ccv->loc[1]);
        else
            nstp = -fix_frac(ccv->loc[1]);
        nstp = fix_mul_div(nstp, ccv->deltas[0], ccv->deltas[1]);
        ccv->loc[0] += nstp;
        ccv->loc[1] &= 0xffff0000; // this will go away in asm, as all we need to do is set the bottom
        ccv->loc[1] += edgestep[dircode][1];
        _fr_sdbg(VECSPEW, mprintf("move_y(if): sub_clip or %x, old %x\n", _sclip_mask[1], me_subclip(ccv->mptr)));
        _me_subclip(ccv->mptr) |= _sclip_mask[1];
        _fr_sdbg(VECSPEW, {
            mprintf("move_y(if): nstp %x\n", nstp);
            print_fcv(ccv, 2);
        });
    } else { // pop up to top of square, for now, no f_o usage
        // wow this is a slow dumb way to do this, eh?
        ccv->loc[1] &= 0xffff0000; // the ands go away in assembler, as we need only set the bottom
        ccv->loc[0] &= 0xffff0000;
        ccv->loc[0] += edgestep[dircode][0];
        ccv->loc[1] += edgestep[dircode][1];
#ifdef SWITCH_IS_FASTER_MAYBE
        switch (dircode) {
        case nVW_NXNY:
            ccv->loc[0] &= 0xffff0000;
            ccv->loc[1] &= 0xffff0000;
            break;
        case nVW_NXPY:
            ccv->loc[0] &= 0xffff0000;
            ccv->loc[1] |= 0x0000ffff;
            break;
        case nVW_PXNY:
            ccv->loc[0] |= 0x0000ffff;
            ccv->loc[1] &= 0xffff0000;
            break;
        case nVW_PXPY:
            ccv->loc[0] |= 0x0000ffff;
            ccv->loc[1] |= 0x0000ffff;
            break;
        }
#endif
#ifdef ANOTHER_WACKY_WAY
        if (dircode & nVW_XDIR)
            ccv->loc[0] |= 0x0000ffff;
        else
            ccv->loc[0] &= 0xffff0000;
        if (dircode & nVW_YDIR)
            ccv->loc[1] |= 0x0000ffff;
        else
            ccv->loc[1] &= 0xffff0000;
#endif
        _fr_sdbg(VECSPEW, mprintf("move_y(else): sub_clip or %x, old %x\n", _sclip_mask[0], me_subclip(ccv->mptr)));
        _me_subclip(ccv->mptr) |= _sclip_mask[0];
        _fr_sdbg(VECSPEW, {
            mprintf("move_y(else):\n");
            print_fcv(ccv, 2);
        });
    }
    _fr_sdbg(VECSPEW, {
        mprintf("move_along(end):\n");
        print_fcv(ccv, 2);
        print_nvp(nvp);
    });
}

#define _fr_move_along_hn_p(x) _fr_move_along_dcode(((ccv->deltas[0] > 0) ? nVW_XDIR : 0) + x)

static uchar *_face_topedge, *_face_botedge;

// partial tiles? who knows when

// note fullwise, at the moment the subclip&face_topmask in middle gets doors, whereas the
//  subclip==SUBCLIP_OUT_OF_CONE gets itself, though in reality and face_botmask would do same thing, i think

// wow is this a total mess, goddamn
#define is_solid()                                                                                                     \
    ((_face_topedge[(me_tiletype(cv->mptr) << 2)] == 0xff) || (me_clearsolid(cv->mptr) & _face_topmask) ||             \
     (me_subclip(cv->mptr) & _face_topmask) || (_face_botedge[(me_tiletype((cv->mptr + y_map_step)) << 2)] == 0xff) || \
     (me_clearsolid((cv->mptr + y_map_step)) & _face_botmask) ||                                                       \
     (me_subclip(cv->mptr + y_map_step) == SUBCLIP_OUT_OF_CONE))

#define is_space()                                                                                                \
    ((_face_topedge[(me_tiletype(cv->mptr) << 2)] != 0xff) && ((me_clearsolid(cv->mptr) & _face_topmask) == 0) && \
     ((me_subclip(cv->mptr) & _face_topmask) == 0) &&                                                             \
     (_face_botedge[(me_tiletype((cv->mptr + y_map_step)) << 2)] != 0xff) &&                                      \
     ((me_clearsolid((cv->mptr + y_map_step)) & _face_botmask) == 0))

// note how pretty this looks till you look at the is_solid macro
uchar _fr_skip_solid_right_n_back(FrClipVec *cv, fix max_loc, int y_map_step) {
    if (is_solid()) {
        cv->loc[0] &= 0xffff0000;
        cv->loc[0] += _fixp1;
        cv->mptr += 1;
        while (is_solid() && (cv->loc[0] < max_loc)) {
            cv->loc[0] += _fixp1;
            cv->mptr += 1;
        }
    }
    return (cv->loc[0] >= max_loc);
}

uchar _fr_skip_space_right_n_back(FrClipVec *cv, fix max_loc, int y_map_step) {
    if (is_space()) {
        cv->loc[0] &= 0xffff0000;
        cv->loc[0] += _fixp1;
        cv->mptr += 1;
        while (is_space() && (cv->loc[0] < max_loc)) {
            cv->loc[0] += _fixp1;
            cv->mptr += 1;
        }
    }
    return (cv->loc[0] >= max_loc);
}

uchar _fr_skip_solid_left_n_back(FrClipVec *cv, fix min_loc, int y_map_step) {
    if (is_solid()) {
        cv->loc[0] |= 0x0000ffff;
        cv->loc[0] += _fixn1;
        cv->mptr += -1;
        while (is_solid() && (min_loc < cv->loc[0])) {
            cv->loc[0] += _fixn1;
            cv->mptr += -1;
        }
    }
    return (cv->loc[0] <= min_loc);
}

#define DT_SHFT (8)
#define DT_FAKE ((1 << DT_SHFT) - 1)
#define fixup_delta(dlta)                     \
    if ((dlta) > 0)                           \
        dlta = ((dlta) + DT_FAKE) >> DT_SHFT; \
    else                                      \
        (dlta) = ((dlta)-DT_FAKE) >> DT_SHFT;

void _fr_del_compute(FrClipVec *v1, FrClipVec *v2) {
    v1->deltas[0] = v1->loc[0] - coor(EYE_X);
    v1->deltas[1] = v1->loc[1] - coor(EYE_Y);
    fixup_delta(v1->deltas[0]);
    fixup_delta(v1->deltas[1]);
    v2->deltas[0] = v2->loc[0] - coor(EYE_X);
    v2->deltas[1] = v2->loc[1] - coor(EYE_Y);
    fixup_delta(v2->deltas[0]);
    fixup_delta(v2->deltas[1]);
    v1->oldx = v1->loc[0];
    v2->oldx = v2->loc[0];
}

void _fr_spawn_check_one(FrClipVec *lv, FrClipVec *rv, uchar northward) {
    FrClipVec *tmpr, *tmpl;
    short cur_mapstep = new_del_mapstep[northward];
    int nxts;

    while (1) // really, run till we find the right edge
    {
        tmpr = allclipv + ffreevec;
        nxts = tmpr->nxtv;
        *tmpr = *lv; // perhaps should back up a mapstep and check for other half tiles first
        tmpr->nxtv = nxts;
        tmpr->flags = ffreevec;                                          // fixup next and self
        if (!_fr_skip_space_right_n_back(tmpr, rv->loc[0], cur_mapstep)) // really should do render bit setup here...
        { // ok lv->rv->?, and tmpr->tmpl->vecx, ffreevec->tmpr... we want lv->tmpr->tmpl->rv, ffreevec->vecx
            _fr_sdbg(VECSPEW, {
                mprintf("new_dels(spawn): found ");
                print_fcv(tmpr, 2);
            });
            tmpl = allclipv + nxts; // tmpl is next free vec, as nxts is tmpr->nxtv and tmpr was ffreevec
            ffreevec = tmpl->nxtv;  // point ffreevec at tmpl's old next, as tmpl and tmpr are from free list
            *tmpl = *tmpr;
            tmpl->nxtv = lv->nxtv;  // point tmpl->rv, lv's old friend
            lv->nxtv = tmpr->flags; // we havent or'red in real flag data, so flags is currently just self for tmpr
            tmpl->flags =
                tmpr->nxtv; // tmpr->nxtv never changes, it is always pointing at tmpl, so tmpl can self set with it
#if _fr_defdbg(SANITY)
            if (_fr_skip_solid_right_n_back(tmpl, rv->loc[0], cur_mapstep)) // this is true
                mprintf("new_dels(spawn) ERR: found middle with no right edge\n");
#else
            _fr_skip_solid_right_n_back(tmpl, rv->loc[0], cur_mapstep);
#endif
            tmpr->loc[0]--;
            tmpr->mptr--; // note that tmpl is a-ok
            if (northward) {
                tmpr->flags |= FRVECUSE | FRVECL;
                tmpl->flags |= FRVECUSE | FRVECR;
            } else {
                tmpl->flags |= FRVECUSE | FRVECL;
                tmpr->flags |= FRVECUSE | FRVECR;
            }
            _fr_sdbg(VECSPEW, {
                mprintf("new_dels(spawn): generated\n");
                print_fcv(tmpr, 2);
                print_fcv(tmpl, 2);
            });
            (allclipv + lastvec)->nxtv = ffreevec;
            _fr_del_compute(tmpl, tmpr);
            lv = tmpl; // move scan start over appropriately
        } else
            return;
    }
}

// returns FALSE if there are no vectors left in this branch, else true
// ok. northward is done super grossly at the moment
// really, this has to spawn new vectors as well

// i want no part of their death culture
// i just want to go the beach
uchar _fr_move_new_dels(FrClipVec *lv, FrClipVec *rv, uchar northward) {
    int lm, rm;

    lm = lg_min(lv->oldx, lv->loc[0]);
    rm = lg_max(rv->oldx, rv->loc[0]);
    store_x_span(fix_int(lv->loc[1]), fix_int(lm), fix_int(rm));
    _fr_sdbg(VECSPEW, mprintf("new_dels: setting span %d to %x,%x - o: %x %x c: x %x %x y %x %x m %x\n",
                              span_count(fix_int(lv->loc[1])), lm, rm, lv->oldx, rv->oldx, lv->loc[0], rv->loc[0],
                              lv->loc[1], rv->loc[1], lv->mptr));

    if (_fr_skip_solid_right_n_back(lv, rv->loc[0], new_del_mapstep[northward]))
        return FALSE;
    if (_fr_skip_solid_left_n_back(rv, lv->loc[0], new_del_mapstep[northward])) {
        _fr_sdbg(SANITY, mprintf("new_dels ERR: closure from right\n"));
        return FALSE;
    }

    lv->loc[1] += rmmod[northward + 1]; // if we are keeping the vectors, move their y coordinates appropriately
    rv->loc[1] += rmmod[northward + 1];
    _fr_del_compute(lv, rv);

    //   (*_fr_spawn_check)(lv,rv);
    _fr_spawn_check_one(lv, rv, northward);

    lm = lv->flags & FRVECSELF; // now move everyones map coordinates
    rm = rv->nxtv;
    for (; lm != rm; lm = (allclipv + lm)->nxtv)
        (allclipv + lm)->mptr += new_del_mapstep[northward];
    return TRUE;
}

// remembering you fallen into my arms
// crying for the death of your heart
// you were stone white so delicate lost in the cold
// you were always so lost in the dark
void _fr_kill_pair(FrClipVec *lv, FrClipVec *rv) {
    int lft_self = lv->flags & FRVECSELF, lft_pt = vechead;
    // should sanity check for too many vectors here and in spawn code
    if (vechead == lft_self) {
        if (rv->nxtv != ffreevec) {
            vechead = rv->nxtv; // if not, leave vechead at base for simplicities sake, i guess?
            allclipv[endvec].nxtv = vechead;
        } else
            lastvec = -1;
    } else {
        while ((lft_pt != ffreevec) && (allclipv[lft_pt].nxtv != lft_self))
            lft_pt = allclipv[lft_pt].nxtv; // go find who points at lv
        _fr_sdbg(SANITY, if (lft_pt == ffreevec) mprintf("kill_pair(lft_pt) ERR: no lft pt\n"));
        allclipv[lft_pt].nxtv = rv->nxtv;
        if (lastvec == lv->nxtv)
            lastvec = lft_pt;
    }
    rv->nxtv = ffreevec;
    ffreevec = lv->flags & FRVECMASK;
    rv->flags &= FRVECSELF;
    lv->flags &= FRVECSELF;
    if (lastvec != -1)
        (allclipv + lastvec)->nxtv = ffreevec;
}

// it's a wonderful world, with a lot of strange men
// who are standing around, and they're all wearing towels
uchar _fr_setup_first_pair(uchar headnorth) {
    fix org[3], ray[3];
    int flags;

    _fr_sdbg(VECSPEW, mprintf("setup_first_pair: note vh %d ff %d\n", vechead, ffreevec));
#define FULL_360_VECTORS
#ifdef FULL_360_VECTORS
    org[0] = coor(EYE_X);
    org[1] = coor(EYE_Y);
    org[2] = coor(EYE_Z);
    ray[1] = fix_make(0, 0);
    ray[2] = fix_make(0, 0);
    //   ray[0]=fix_make((headnorth)?-1:1,0);
    ray[0] = fix_make(-1, 0);
    ccv = allclipv + ffreevec;
    flags = FRVECUSE;
    flags |= headnorth ? FRVECR : FRVECL;
    _fr_build_clip_vec(ccv, org, ray, flags);
    ccv = allclipv + ccv->nxtv;
    ray[0] = fix_make(1, 0);
    //   ray[0]=fix_make((headnorth)?1:-1,0);
    flags = FRVECUSE;
    flags |= headnorth ? FRVECL : FRVECR;
    _fr_build_clip_vec(ccv, org, ray, flags);
    ffreevec = ccv->nxtv;
    lastvec = ccv->flags & FRVECSELF;
#else
    if (headnorth) {
        if (span_lines[3] < 0)
            return FALSE;
        ray[0] = span_lines[2];
        ray[1] = span_lines[3];
    } else {
        if (span_lines[1] > 0)
            return FALSE;
        ray[0] = span_lines[0];
        ray[1] = span_lines[1];
    }
    org[2] = coor(EYE_Z); // these are constant
    ray[2] = fix_make(0, 0);
    org[0] = span_intersect[0]; // these are true for both north and south left vecs
    org[1] = span_intersect[1];

    ccv = allclipv + ffreevec;
    flags = FRVECUSE;
    flags |= headnorth ? FRVECR : FRVECL;
    _fr_build_clip_vec(ccv, org, ray, flags);
    ccv = allclipv + ccv->nxtv;

    if (headnorth) {
        ray[0] = span_lines[4];
        ray[1] = span_lines[5];
    } else {
        ray[0] = span_lines[6];
        ray[1] = span_lines[7];
    }

    org[0] = span_intersect[2];
    org[1] = span_intersect[3];

    flags = FRVECUSE;
    flags |= headnorth ? FRVECL : FRVECR;
    _fr_build_clip_vec(ccv, org, ray, flags);
    ffreevec = ccv->nxtv;
    lastvec = ccv->flags & FRVECSELF;
#endif

    // setup various revectorings
    if (headnorth) {
        _face_topedge = &face_obstruct[0][0];
        _face_botedge = &face_obstruct[0][2];
        _face_topmask = FMK_INT_NW;
        _face_botmask = FMK_INT_SW;
    } else {
        _face_topedge = &face_obstruct[0][2];
        _face_botedge = &face_obstruct[0][0];
        _face_topmask = FMK_INT_SW;
        _face_botmask = FMK_INT_NW;
    }
    fr_clip_start(headnorth);
    return TRUE;
}

#if _fr_defdbg(VECTRACK)
void _fr_show_veclist(void) {
    int i, cv;
    mprintf("Vec(ff%d): ", ffreevec);
    for (i = 0, cv = vechead; i < MAX_CLIP_VEC; i++, cv = allclipv[cv].nxtv)
        mprintf("%1.1X%c%c ", cv, (allclipv[cv].flags & FRVECUSE) ? 'U' : 'x',
                (allclipv[cv].flags & FRVECL) ? 'L' : 'R');
    if (cv != vechead)
        mprintf("ERROR %d!%d\n", cv, vechead);
    else
        mprintf("\n");
}
#else
#define _fr_show_veclist()
#endif

// stains on the carpet and stains on the memory
// and both of us know, how the end always is
int fr_clip_tile(void) {
    FrClipVec *_v1, *_v2;
    int northward, nxtvec;
    // also have to do exact correct reverse order, so obj_stack works, so go north first, then south
    // sadly, new render order invalidates this

    // Just draw everything if physics is disabled
    if (global_fullmap->cyber) {
        fr_clip_show_all();
        _fr_ret;
    }

    // next, do each direction
    if (_fr_curflags & FR_SHOWALL_MASK) {
        fr_clip_show_all();
        _fr_ret;
    } // fill in all things
    _fr_sdbg(VECSPEW, mprintf("Frame start at %x %x\n", coor(EYE_X), coor(EYE_Y)));
    eye_mptr = MAP_GET_XY(_fr_x_cen, _fr_y_cen);

    for (northward = 1; northward >= 0; northward--) {
        // set up the initial vectors and list
        if (!_fr_setup_first_pair(northward))
            continue;
        _fr_sdbg(VECSPEW, mprintf("clip_tile(for): heading %d\n", northward));
        // for each line
        do {
            _v1 = ccv = allclipv + vechead;
            while (ccv->flags & FRVECUSE) {
                // move out left vector
                _fr_move_along_hn_p(northward);
                // at each square, code objects
                // keep a right edge for internal? ick!

                // move out right vector
                _v2 = ccv = allclipv + ccv->nxtv;
                nxtvec = ccv->nxtv; // so if we new_dels more vecs, or kill our vec, we have the next ptr ready, eh?
                _fr_move_along_hn_p(northward); // do same things, but in reverse

                if (!_fr_move_new_dels(_v1, _v2, northward)) // kill off the vectors
                {                                            // wow, can we do multiple here... i guess so
                    _fr_sdbg(VECSPEW, {
                        mprintf("clip_tile(while): killing vector pair\n");
                        print_fcv(_v1, 2);
                        print_fcv(_v2, 2);
                    });
                    _fr_kill_pair(_v1, _v2);
                } else {
                    nxtvec = _v2->nxtv; // could have changed
                    _fr_sdbg(VECSPEW, {
                        mprintf("clip_tile(while): moving on to %d after pair\n", nxtvec);
                        print_fcv(_v1, 2);
                        print_fcv(_v2, 2);
                    });
                }
#if _fr_defdbg(VECTRACK)
                _fr_sdbg(VECTRACK, _fr_show_veclist());
#endif
                // store off new base span
                // spawn/collect vectors
                // move to next span line
                _v1 = ccv = allclipv + nxtvec;
            }
        } while (ffreevec != vechead);
    }
    // hit the fucking road
    span_fixup();
    _fr_ret;
}

// can you see?
// see into the back of a long black car
// pulling away from a funeral of flowers
// with my hand, between your legs
// melting

// fills dst with a wall hit by a ray cast from orig along ray
// a len!=0 is stopped at, 0 goes forever or until page fault
// each fix* is assumed to be a 3 element array
#ifdef WE_WERE_COOL
fix *fr_ray_cast(fix *org, fix *ray, fix *dst, fix len) {
    MapElem *cur_us;

    _fr_build_clip_vec(&scratchvec, org, ray, len);
}
#endif
