/*

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/>.

*/
/*
 * FrTerr.c
 *
 * $Source: r:/prj/cit/src/RCS/frterr.c $
 * $Revision: 1.27 $
 * $Author: jaemz $
 * $Date: 1994/09/06 05:03:37 $
 *
 * Citadel Renderer
 *  terrain parsing, internal and external faces, facelet
 *  physics interaction?
 */

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

#include "map.h"
#include "mapflags.h"
#include "frintern.h"
#include "frtables.h"
#include "tilename.h"
#include "frparams.h"
#include "frflags.h"
#include "frquad.h"
#include "frsubclp.h"
#include "rendtool.h"
#include "textmaps.h" // pain, sadness

#define FLIP_BITS

// for the game system to grab and deparse for distance and all
int _game_fr_tmap;

// these should go somewhere else...
#define MAX_HGT HGT_STEPS

#define FDT_TMPPTCNT 8

#define T_LEFT 0
#define T_RIGHT 1

// really need oct_low and oct_high mirrors for fhgt_list so we can +/- 33 as oct_parm

// globals
int _fdt_dist;                         // distance in hv squares to us
fix _fr_fhgt_step;                     // single hgt step
fix _fr_fhgt_list[MAX_HGT + 1];        // all hgt steps fix mapping
sfix _fr_sfuv_list[(2 * MAX_HGT) + 1]; // all hgt steps uv mapping
fix slope_norm[MAX_HGT][3];            // table for sloped floors

// these example values are for a 32wide map, they get set in frpipe when map is resized
int wall_adds[] = {DEF_MAP_SZ, 1, -DEF_MAP_SZ, -1};       // how to get from one tile to next ptr math
int csp_trans_add[] = {0, DEF_MAP_SZ, DEF_MAP_SZ + 1, 1}; // alternate mapping of same deltas

int _fdt_x, _fdt_y, _fdt_mask; // implicit parameters to draw tile
MapElem *_fdt_mptr;            // more implicit parameters

// texture mapping function pointers.
void (*_fr_lit_floor_func)(int, g3s_phandle *, grs_bitmap *);
void (*_fr_floor_func)(int, g3s_phandle *, grs_bitmap *);
void (*_fr_lit_wall_func)(int, g3s_phandle *, grs_bitmap *);
void (*_fr_wall_func)(int, g3s_phandle *, grs_bitmap *);
void (*_fr_lit_per_func)(int, g3s_phandle *, grs_bitmap *);
void (*_fr_per_func)(int, g3s_phandle *, grs_bitmap *);

#if _fr_defdbg(CURSOR)
int _fr_cursorx, _fr_cursory;
#endif

#if _fr_defdbg(ALTCAM)
int _fr_altcamx, _fr_altcamy;
#endif

// various nested locals, read static globals in C
static uchar _fdt_tt;           // local storage of our current tile's tiletype
static TilesToWalls _fdt_ttw;   // secret tiles to walls code
static TilesToFloors *_fdt_ttf; // pointer to current floor spew
static uchar _fdt_hgts[5];      // parm, flr, ceil, fprm, cprm
static fix _fdt_fix_parm;       // fix rep for parameter
static fix _fdt_cur_parm;       // dont pass around, as it is constant per tile
static char _fdt_icnt;          // internal walls count - can be made a local again, i think
static uchar *_fdt_fo;          // current tile's fo data
static fix _fdt_hgt_val;        // last point hgt world coordinate
static uchar _fdt_whichpt;      //  fhgt_list_entry 0-MAX_HGT for last point looked up height w/parm adjust
static uchar _fdt_hgt_pt;       // actual fdt_hgts value for this whichpt, since we use it 4-8 times for lit pts
g3s_phandle _fdt_tmppts[8];     // these are used for all temporary point sets
static int _fdt_wallid;         // current external wall id
static g3s_phandle *_fdt_lcore; // left core for external walls
static g3s_phandle *_fdt_rcore; // right core for external walls
static int _fdt_rbase;          // value of pbase at right of external walls, we use pbase itself for left
static int _fdt_me_flags;       // hold store the current map flags
static int _fdt_wmap;           // current tmap family to use
static sfix _fdt_slock;         // sfix value for vlock in square

int _fdt_pbase; // where pbase is, for corner for idx lookup - sadly global for object lighting

static uchar last_csp_fr = 0; // last frame annoyance
static uchar _fdt_flip;       // tile flippitude of cur tile
static bool _fdt_terr;        // are we doing terrain or physics

// indirections for low level render functions
static void (*_fr_terr_int_wall)(int wall_id);
static void (*_fr_terr_ext_wall)(fix pt_list[4][2]);
static void (*_fr_terr_flr)(void);
static void (*_fr_terr_ceil)(void);
// how to render a composite inner wall
static void (*_fr_render_walls)(int which, int cnt);
// how to deal w/the object chain at a square
static void (*_fr_parse_obj)(void);

// in rendtool.c
#define TIM_WERE_AWAKE
#ifdef TIM_WERE_AWAKE
#define IsTpropNotStars() (textprops[_game_fr_tmap].force_dir == 0)
#else
#define IsTpropNotStars() (_game_fr_tmap >= 4)
#endif
#define quik_draw_tmap_p(ptcnt) ((IsTpropNotStars()) || (draw_tmap_p(ptcnt)))
//#define quik_draw_tmap_p(ptcnt) (TRUE)

// lookups into arrays
#define FDT_LK_FLR 0
#define FDT_LK_CEIL 1

// points in hgt structures
#define FDT_PT_PARM 0
#define FDT_PT_FLR 1
#define FDT_PT_CEIL 2
#define FDT_PT_FPRM 3
#define FDT_PT_CPRM 4

int _fr_terr_prim = 1;

// Internal prototypes (for Mac version)
void _fr_figure_pt(g3s_phandle tmp, int pt_code);
fix get_light(fix dist_to);
int _fr_do_light_val(int which, fix dist_val);
int _obj_do_light(int which, fix dist);
void _fr_do_cspace(g3s_phandle wrk, int which);
void _fr_draw_wire_cpoly_4(void);
void _fr_draw_wire_cpoly_3or4(int cnt);
void flip_setup(int wall_code);
int fr_get_ext_gap(fix *face_l, uchar in_fo, uchar *hgts, uchar *mmptr);
int _fr_get_anti_gap(fix *face_l, uchar in_fo, uchar *hgts, uchar *mmptr);
void _render_3d_walls(int which, int cnt);
int merge_walls(fix *dst_wall, fix *i_wall, int i_cnt, fix *o_wall, int o_cnt);
void parse_clip_tile();
void fr_terr_cspace_pick(uchar do_wall);
int _fr_tfunc_flr_normal(fix *vec, int *lflg, int fnorm_entry);
int _fr_tfunc_ceil_normal(fix *vec, int *lflg, int fnorm_entry);
int do_fc_trans(fix *xy, fix *sc, fix *vals);
void do_floor_element(int nrm_mask);
void do_ceil_element(int nrm_mask);
void fr_tfunc_grab_start(void);
void fr_tfunc_grab_fast(int mask);

///// AAHRRHRHGGHGHG switch to secondary data cache!!!

// probably should have 3 of these, one for cspace, one for unlit, one for lantern, so on, swap in pointers,
// and for asm have a table of calls to this which we switch if necessary at start of frame

// oh oh oh baby oh baby oh self modify height delta step baby now baby please
// really, this wants to become the threaded nightmare assembler point coder from hell
// and to use the 4 respts per map pt idea so we can reuse too
// 3 tests,
//   either succeed jump out and reuse
//   or fail all 3 (no jumps taken) and optimally build new one

// deal with optimality of data structures when rewriting in asm

// SPEED

// the phone is ringing its 4 am
// if it's your friends then i dont want to hear from them
// please leave a number and a message at the tone
// or you can just go on and leave me alone
// and i...  dont want to know if you are lonely

// hmm.. have to deal with halve points and such
void _fr_figure_pt(g3s_phandle tmp, int pt_code) {
    g3s_phandle *core;
    pt_mods *ptm;

    ptm = &pt_deref[pt_code & FRPTSPTOFF];
    switch (_fdt_pbase = ptm->base) { // bad bad bad - hmmm... arrays?
    case 0:
        core = _fr_ptbase;
        break;
    case 1:
        core = _fr_ptnext;
        break;
    case 2:
        core = _fr_ptnext + 1;
        break;
    case 3:
        core = _fr_ptbase + 1;
        break;
        //#ifndef SHIP
        //   default: Warning(("_fr_terr_poly_wall: bad point base code %x gives %d\n",pt_code,ptm->base)); break;
        //#endif
    }
    core += _fdt_x;
    _fdt_whichpt = ((pt_code & FRPTSZMASK) >> FRPTSZSHF);
    _fdt_hgt_pt = _fdt_hgts[_fdt_whichpt + 1];
    _fdt_hgt_val = _fr_fhgt_list[_fdt_hgt_pt];
    g3_replace_add_delta_y(*core, tmp, -_fdt_hgt_val);
    switch (ptm->modcnt) { // 1 function table
    case FRMODYAXIS:
        g3_add_delta_z(tmp, ptm->arg);
        break;
    case FRMODXAXIS:
        g3_add_delta_x(tmp, ptm->arg);
        break;
    case FRMODNONE:
        break;
    }
}

fix get_light(fix dist_to) // , fix dot_prod)
{
    //   if (fr_normal_lights)
    //	   dist_to+=fix_mul(dist_to>>fr_normal_shf,fix_make(1,0)-dot_prod);      // 1/(1<<norm_shf) *
    //dist_to*(anti_dot_prod)
    //   Spew(DSRC_TESTING_Test1,("v%s",fix_sprint(ft,dist_to)));
    // wow, fix this up, make fixes in frp, so on
    if (dist_to < fix_make((int)_frp.lighting.rad[0], 0))
        return fix_make((int)_frp.lighting.base[0], 0);
    if (dist_to > fix_make((int)_frp.lighting.rad[1], 0))
        return fix_make((int)_frp.lighting.base[1], 0);
    return fix_mul(dist_to, _frp.lighting.slope) + _frp.lighting.yint;
}

#define get_light_t(targ, dist_to)                             \
    if (dist_to < fix_make((int)_frp.lighting.rad[0], 0))      \
        targ = ((int)_frp.lighting.base[0]) << 8;              \
    else if (dist_to > fix_make((int)_frp.lighting.rad[1], 0)) \
        targ = ((int)_frp.lighting.base[1]) << 8;              \
    else                                                       \
        targ = (fix_mul(dist_to, _frp.lighting.slope) + _frp.lighting.yint) >> 8

#ifndef NEW_WAY
#define set_terr_light(our_mp, which)                                    \
    if (which == FRPTSZCEIL_DN)                                          \
        i = (int)me_light_ceil(our_mp) - (int)me_templight_ceil(our_mp); \
    else                                                                 \
        i = (int)me_light_flr(our_mp) - (int)me_templight_flr(our_mp);   \
    if (i < 0)                                                           \
    i = 0
#else
#define set_terr_light(our_mp, which)           \
    {                                           \
        int j;                                  \
        if (which == FRPTSZCEIL_DN) {           \
            i = (int)me_light_ceil(our_mp);     \
            j = (int)me_templight_ceil(our_mp); \
        } else {                                \
            i = (int)me_light_flr(our_mp);      \
            j = (int)me_templight_flr(our_mp);  \
        }                                       \
        i = min(i, j);                          \
    }
#endif

// should redo these to setup sfix with punch in not shift
// for now hack with which to get stuff running
// later we want an interpolator
// rewrite this in assembler
int _fr_do_light_val(int which, fix dist_val) {
    MapElem *our_mp = _fdt_mptr;
    int i;

    our_mp += csp_trans_add[_fdt_pbase];
    set_terr_light(our_mp, which);
    i <<= 8;

    // speed this up, add fast dist check to trivial reject it.. ick, is this correct
    // secret sacred secret sacred secret gnosis... neat, eh?
    if ((_fr_curflags & FR_HACKCAM_MASK) == 0) {
        if (_frp_light_bits_cam())
            if ((dist_val >> 16) <= _frp.lighting.rad[1] + 1) {
                fix light_res;
                get_light_t(light_res, dist_val);
                if (light_res < i)
                    i -= light_res;
                else
                    i = 0;
            }
    }

    if (_frp.lighting.global_mod) {
        i -= _frp.lighting.global_mod;
        if (i < 0)
            i = 0;
        else if (i > (15 << 8))
            i = (15 << 8);
    }

    if (me_bits_rend3_x(our_mp)) {
        i = 0;
        //		i-=(6<<8);     // ah-yep....
        //	   if (i<0) i=0;
    }

    // printf("Lighting! %x\n", i);

    return i;

    //   if (_frp_light_bits_norm())
    //   wrk->i=i;
    //   *((uchar *)&wrk->i)=i;
    //   return wrk->i;
}

int _fr_do_light(g3s_phandle wrk, int which) {
    //   fix dval=abs(wrk->x)+abs(wrk->y)+abs(wrk->z));
    //   fix dval=fix_fast_pyth_dist(fix_fast_pyth_dist(wrk->x,wrk->y),wrk->z);
    // really, we want the original, not transformed, points
    // how bout this mess, eh?
    static int x_mod[] = {0, 0, 1, 1}, y_mod[] = {0, 1, 1, 0};
    int _lgt_x, _lgt_y;
    fix dval;
    _lgt_x = _fdt_x + x_mod[_fdt_pbase];
    _lgt_y = _fdt_y + y_mod[_fdt_pbase];
    dval =
        fix_fast_pyth_dist(fix_fast_pyth_dist((_lgt_x << 16) - fr_camera_last[0], (_lgt_y << 16) - fr_camera_last[1]),
                           fr_camera_last[2] - _fdt_hgt_val);
    wrk->i = _fr_do_light_val(which, dval);
    return wrk->i;
}

int _obj_do_light(int which, fix dist) { return _fr_do_light_val(which, dist); }

void _fr_do_cspace(g3s_phandle wrk, int which) {
    int col, dst, ncval;
    MapElem *our_mp = _fdt_mptr + csp_trans_add[_fdt_pbase];
    if (which == FRPTSZCEIL_DN)
        col = me_cybcolor_ceil(our_mp);
    else
        col = me_cybcolor_flr(our_mp);
    // light table based on radius here, somehow....
    dst = _fdt_dist - 6; // dimming code. i guess
    if (dst < 0)
        dst = 0;
    else if (dst > 10)
        dst = 10;
    dst <<= 8;
    ncval = *((_fr_clut_list[0]) + dst + col);
    wrk->rgb = grd_bpal[ncval]; // col
    wrk->p3_flags |= PF_RGB;
}

#define dlC FRPTSZCEIL_DN
#define dlF FRPTSZFLR_DN
#define _fr_pt_light(phnd) _fr_do_light(phnd, _fdt_whichpt &FRPTSZPICK_DN)
#define _fr_pt_cspace(phnd) _fr_do_cspace((phnd), _fdt_whichpt &FRPTSZPICK_DN)

void _fr_draw_wire_cpoly_4(void) {
    g3_draw_cline(_fdt_tmppts[0], _fdt_tmppts[1]);
    g3_draw_cline(_fdt_tmppts[1], _fdt_tmppts[2]);
    g3_draw_cline(_fdt_tmppts[2], _fdt_tmppts[3]);
    g3_draw_cline(_fdt_tmppts[3], _fdt_tmppts[0]);
}

void _fr_draw_wire_cpoly_3or4(int cnt) {
    g3_draw_cline(_fdt_tmppts[0], _fdt_tmppts[1]);
    g3_draw_cline(_fdt_tmppts[1], _fdt_tmppts[2]);
    if (cnt == 3)
        g3_draw_cline(_fdt_tmppts[2], _fdt_tmppts[0]);
    else {
        g3_draw_cline(_fdt_tmppts[2], _fdt_tmppts[3]);
        g3_draw_cline(_fdt_tmppts[3], _fdt_tmppts[0]);
    }
}

void flip_setup(int wall_code) {
    int lflags = _fdt_me_flags;
    if (lflags & MAP_FLIP_FNCY_MASK) {
        lflags &= ~MAP_FLIP_FNCY_MASK;                                        /* clear fancy bit */
        lflags += (((_fdt_x + _fdt_y + wall_code) & 1) << MAP_FLIP_PRTY_SHF); /* add in current parity */
        lflags &= ~MAP_FLIP_FNCY_MASK;                                        /* mod 1 within the bit field */
    }
    _fdt_flip = ((lflags & MAP_FLIP_MASK) >> MAP_FLIP_SHF);
#ifdef FLIP_SPEW
    if (_fdt_me_flags & MAP_FLIP_MASK)
        mprintf("Flip now set to %x from %x,%x at %x %x t %x\n", _fdt_flip, lflags, _fdt_me_flags, _fdt_x, _fdt_y,
                wall_code);
    else if (_fdt_flip)
        mprintf("Hey, flip set, huh? %x %x %x\n", _fdt_x, _fdt_y, wall_code);
#endif
}

#ifdef FLIP_BITS
#define WALL_L_SET(pt) pt->uv.u = (_fdt_flip) << 8
#define WALL_R_SET(pt) pt->uv.u = ((1 + _fdt_flip) & 1) << 8
#else
#define WALL_L_SET(pt) pt->uv.u = 0 << 8
#define WALL_R_SET(pt) pt->uv.u = 1 << 8
#endif

// uv for a wall
/// ARRRGH, sfix points suck.. make this a look up through _fdt_hgts+vhold?
#define wall_uv_l(tmp)                                   \
    tmp->uv.v = _fr_sfuv_list[_fdt_hgt_pt] + _fdt_slock; \
    tmp->p3_flags |= PF_U | PF_V;                        \
    WALL_L_SET(tmp)

#define wall_uv_r(tmp)                                   \
    tmp->uv.v = _fr_sfuv_list[_fdt_hgt_pt] + _fdt_slock; \
    tmp->p3_flags |= PF_U | PF_V;                        \
    WALL_R_SET(tmp)

#define wall_uv_l_i(tmp)                                 \
    tmp->uv.v = _fr_sfuv_list[_fdt_hgt_pt] + _fdt_slock; \
    tmp->p3_flags |= PF_U | PF_V | PF_I;                 \
    WALL_L_SET(tmp)

#define wall_uv_r_i(tmp)                                 \
    tmp->uv.v = _fr_sfuv_list[_fdt_hgt_pt] + _fdt_slock; \
    tmp->p3_flags |= PF_U | PF_V | PF_I;                 \
    WALL_R_SET(tmp)

    // if ((pt_num+1)&2) tmp->u=0; else tmp->u=1<<8;

#ifndef PARTIAL_TILES
#define EXT_WALL_L_SET(pt) WALL_L_SET(pt)
#define EXT_WALL_R_SET(pt) WALL_R_SET(pt)
#else
#ifdef FLIP_BITS
#define EXT_WALL_L_SET(pt) pt->uv.u = (fix_flip + (flip_sign)*hgt_data[0]) >> 8
#define EXT_WALL_R_SET(pt) pt->uv.u = (fix_flip + (flip_sign)*hgt_data[0]) >> 8
#else
#define EXT_WALL_L_SET(pt) pt->uv.u = hgt_data[0] >> 8
#define EXT_WALL_R_SET(pt) pt->uv.u = hgt_data[0] >> 8
#endif
#endif

// really, these could use 0 and 1<<8 for u coordinates, since why not without partial tiles
#define ext_wall_uv_l(tmp, hgt_data)                                \
    tmp->uv.v = ((fix_make(4, 0) - hgt_data[1]) >> 8) + _fdt_slock; \
    tmp->p3_flags |= PF_U | PF_V;                                   \
    EXT_WALL_L_SET(tmp)

#define ext_wall_uv_r(tmp, hgt_data)                                \
    tmp->uv.v = ((fix_make(4, 0) - hgt_data[1]) >> 8) + _fdt_slock; \
    tmp->p3_flags |= PF_U | PF_V;                                   \
    EXT_WALL_R_SET(tmp)

#define ext_wall_uv_l_i(tmp, hgt_data)                              \
    tmp->uv.v = ((fix_make(4, 0) - hgt_data[1]) >> 8) + _fdt_slock; \
    tmp->p3_flags |= PF_U | PF_V | PF_I;                            \
    EXT_WALL_L_SET(tmp)

#define ext_wall_uv_r_i(tmp, hgt_data)                              \
    tmp->uv.v = ((fix_make(4, 0) - hgt_data[1]) >> 8) + _fdt_slock; \
    tmp->p3_flags |= PF_U | PF_V | PF_I;                            \
    EXT_WALL_R_SET(tmp)

// i can feel it in my bones
// im gonna spend my whole life alone

// tabs_int_wall
//#pragma disable_message(202)
static void _fr_null_int_wall(int wall_id) {}
//#pragma enable_message(202)

static void _fr_flat_int_wall(int wall_id) {
    WallsToPts *wpt = &wall_pts[wall_id];
    // need to have face_code set and be ready with flip and hold and all that jazz
    _fr_figure_pt(_fdt_tmppts[0], wpt->ul);
    _fr_figure_pt(_fdt_tmppts[1], wpt->ur);
    _fr_figure_pt(_fdt_tmppts[2], wpt->lr);
    _fr_figure_pt(_fdt_tmppts[3], wpt->ll);
    _fr_ndbg(NO_REND, g3_draw_poly((*fr_get_idx)(), 4, _fdt_tmppts));
    _fr_sdbg(STATS, _frp.stats.int_wall++);
}

#ifdef FLAT_SUPPORT
static void _fr_flat_lit_int_wall(int wall_id) {
    WallsToPts *wpt = &wall_pts[wall_id];
    // need to have face_code set and be ready with flip and hold and all that jazz
    _fr_figure_pt(_fdt_tmppts[0], wpt->ul);
    _fr_pt_light(_fdt_tmppts[0]);
    _fdt_tmppts[0]->p3_flags |= PF_I;
    _fr_figure_pt(_fdt_tmppts[1], wpt->ur);
    _fr_pt_light(_fdt_tmppts[1]);
    _fdt_tmppts[1]->p3_flags |= PF_I;
    _fr_figure_pt(_fdt_tmppts[2], wpt->lr);
    _fr_pt_light(_fdt_tmppts[2]);
    _fdt_tmppts[2]->p3_flags |= PF_I;
    _fr_figure_pt(_fdt_tmppts[3], wpt->ll);
    _fr_pt_light(_fdt_tmppts[3]);
    _fdt_tmppts[3]->p3_flags |= PF_I;
    gr_set_fcolor((*fr_get_idx)());
    _fr_ndbg(NO_REND, g3_draw_spoly(4, _fdt_tmppts));
    _fr_sdbg(STATS, _frp.stats.int_wall++);
}
#else
#define _fr_flat_lit_int_wall _fr_tmap_lit_int_wall
#endif

static void _fr_tmap_int_wall(int wall_id) {
    WallsToPts *wpt = &wall_pts[wall_id];
    // need to have face_code set and be ready with flip and hold and all that jazz
    _fr_figure_pt(_fdt_tmppts[0], wpt->ul);
    wall_uv_l(_fdt_tmppts[0]);
    _fr_figure_pt(_fdt_tmppts[1], wpt->ur);
    wall_uv_r(_fdt_tmppts[1]);
    _fr_figure_pt(_fdt_tmppts[2], wpt->lr);
    wall_uv_r(_fdt_tmppts[2]);
    _fr_figure_pt(_fdt_tmppts[3], wpt->ll);
    wall_uv_l(_fdt_tmppts[3]);
    if (quik_draw_tmap_p(4)) {
        _fr_ndbg(NO_REND, _fr_wall_func(4, _fdt_tmppts, (*fr_get_tmap)()));
    }
    _fr_sdbg(STATS, _frp.stats.int_wall++);
}

static void _fr_tmap_lit_int_wall(int wall_id) {
    WallsToPts *wpt = &wall_pts[wall_id];
    // need to have face_code set and be ready with flip and hold and all that jazz
    _fr_figure_pt(_fdt_tmppts[0], wpt->ul);
    wall_uv_l_i(_fdt_tmppts[0]);
    _fr_pt_light(_fdt_tmppts[0]);
    _fr_figure_pt(_fdt_tmppts[1], wpt->ur);
    wall_uv_r_i(_fdt_tmppts[1]);
    _fr_pt_light(_fdt_tmppts[1]);
    _fr_figure_pt(_fdt_tmppts[2], wpt->lr);
    wall_uv_r_i(_fdt_tmppts[2]);
    _fr_pt_light(_fdt_tmppts[2]);
    _fr_figure_pt(_fdt_tmppts[3], wpt->ll);
    wall_uv_l_i(_fdt_tmppts[3]);
    _fr_pt_light(_fdt_tmppts[3]);
    if (quik_draw_tmap_p(4)) {
        _fr_ndbg(NO_REND, _fr_lit_wall_func(4, _fdt_tmppts, (*fr_get_tmap)()));
    }
    _fr_sdbg(STATS, _frp.stats.int_wall++);
}

static void _fr_cspace_wire_int_wall(int wall_id) {
    WallsToPts *wpt = &wall_pts[wall_id];
    // need to have face_code set and be ready with flip and hold and all that jazz
    _fr_figure_pt(_fdt_tmppts[0], wpt->ul);
    _fr_pt_cspace(_fdt_tmppts[0]);
    _fr_figure_pt(_fdt_tmppts[1], wpt->ur);
    _fr_pt_cspace(_fdt_tmppts[1]);
    _fr_figure_pt(_fdt_tmppts[2], wpt->lr);
    _fr_pt_cspace(_fdt_tmppts[2]);
    _fr_figure_pt(_fdt_tmppts[3], wpt->ll);
    _fr_pt_cspace(_fdt_tmppts[3]);
    _fr_ndbg(NO_REND, _fr_draw_wire_cpoly_4());
    _fr_sdbg(STATS, _frp.stats.int_wall++);
}

static void _fr_cspace_full_int_wall(int wall_id) {
    WallsToPts *wpt = &wall_pts[wall_id];
    // need to have face_code set and be ready with flip and hold and all that jazz
    _fr_figure_pt(_fdt_tmppts[0], wpt->ul);
    _fr_pt_cspace(_fdt_tmppts[0]);
    _fr_figure_pt(_fdt_tmppts[1], wpt->ur);
    _fr_pt_cspace(_fdt_tmppts[1]);
    _fr_figure_pt(_fdt_tmppts[2], wpt->lr);
    _fr_pt_cspace(_fdt_tmppts[2]);
    _fr_figure_pt(_fdt_tmppts[3], wpt->ll);
    _fr_pt_cspace(_fdt_tmppts[3]);
    _fr_ndbg(NO_REND, g3_draw_cpoly(4, _fdt_tmppts));
    _fr_sdbg(STATS, _frp.stats.int_wall++);
}

// tabs_ext_wall

// this takes a wacky hgt data set
// uses _fdt_wallid, _fdt_lcore, _fdt_rcore
//#pragma disable_message(202)
static void _fr_null_ext_wall(fix pt_list[4][2]) {}
//#pragma enable_message(202)

static void _fr_flat_ext_wall(
    fix pt_list[4][2]) { // note we do this out of order so we can have left left right right, ie. note their indicies
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]);
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]);
    _fdt_pbase = _fdt_rbase;
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]);
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]);
    _fr_ndbg(NO_REND, g3_draw_poly((*fr_get_idx)(), 4, _fdt_tmppts));
    _fr_sdbg(STATS, _frp.stats.ext_wall++);
}

#ifdef FLAT_SUPPORT
static void _fr_flat_lit_ext_wall(fix pt_list[4][2]) {
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]);
    _fdt_hgt_val = -pt_list[0][1];
    _fr_do_light(_fdt_tmppts[0], dlC);
    _fdt_tmppts[0]->p3_flags |= PF_I;
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]);
    _fdt_hgt_val = -pt_list[3][1];
    _fr_do_light(_fdt_tmppts[3], dlF);
    _fdt_tmppts[3]->p3_flags |= PF_I;
    _fdt_pbase = _fdt_rbase;
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]);
    _fdt_hgt_val = -pt_list[1][1];
    _fr_do_light(_fdt_tmppts[1], dlC);
    _fdt_tmppts[1]->p3_flags |= PF_I;
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]);
    _fdt_hgt_val = -pt_list[2][1];
    _fr_do_light(_fdt_tmppts[2], dlF);
    _fdt_tmppts[2]->p3_flags |= PF_I;
    gr_set_fcolor((*fr_get_idx)());
    _fr_ndbg(NO_REND, g3_draw_spoly(4, _fdt_tmppts));
    _fr_sdbg(STATS, _frp.stats.ext_wall++);
}
#else
#define _fr_flat_lit_ext_wall _fr_tmap_lit_ext_wall
#endif

static void _fr_tmap_ext_wall(fix pt_list[4][2]) {
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]);
    ext_wall_uv_l(_fdt_tmppts[0], pt_list[0]);
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]);
    ext_wall_uv_l(_fdt_tmppts[3], pt_list[3]);
    _fdt_pbase = _fdt_rbase;
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]);
    ext_wall_uv_r(_fdt_tmppts[1], pt_list[1]);
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]);
    ext_wall_uv_r(_fdt_tmppts[2], pt_list[2]);
    if (quik_draw_tmap_p(4)) {
        _fr_ndbg(NO_REND, _fr_wall_func(4, _fdt_tmppts, (*fr_get_tmap)()));
    }
    _fr_sdbg(STATS, _frp.stats.ext_wall++);
}

static void _fr_tmap_lit_ext_wall(fix pt_list[4][2]) {
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]);
    ext_wall_uv_l_i(_fdt_tmppts[0], pt_list[0]);
    _fdt_hgt_val = pt_list[0][1];
    _fr_do_light(_fdt_tmppts[0], dlC);
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]);
    ext_wall_uv_l_i(_fdt_tmppts[3], pt_list[3]);
    _fdt_hgt_val = pt_list[3][1];
    _fr_do_light(_fdt_tmppts[3], dlF);
    _fdt_pbase = _fdt_rbase;
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]);
    ext_wall_uv_r_i(_fdt_tmppts[1], pt_list[1]);
    _fdt_hgt_val = pt_list[1][1];
    _fr_do_light(_fdt_tmppts[1], dlC);
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]);
    ext_wall_uv_r_i(_fdt_tmppts[2], pt_list[2]);
    _fdt_hgt_val = pt_list[2][1];
    _fr_do_light(_fdt_tmppts[2], dlF);
    if (quik_draw_tmap_p(4)) {
        _fr_ndbg(NO_REND, _fr_lit_wall_func(4, _fdt_tmppts, (*fr_get_tmap)()));
    }
    _fr_sdbg(STATS, _frp.stats.ext_wall++);
}

static void _fr_cspace_wire_ext_wall(fix pt_list[4][2]) {
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]);
    _fr_do_cspace(_fdt_tmppts[0], dlC);
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]);
    _fr_do_cspace(_fdt_tmppts[3], dlF);
    _fdt_pbase = _fdt_rbase;
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]);
    _fr_do_cspace(_fdt_tmppts[1], dlC);
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]);
    _fr_do_cspace(_fdt_tmppts[2], dlF);
    _fr_ndbg(NO_REND, _fr_draw_wire_cpoly_4());
    _fr_sdbg(STATS, _frp.stats.ext_wall++);
}

static void _fr_cspace_full_ext_wall(fix pt_list[4][2]) {
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[0], -pt_list[0][1]);
    _fr_do_cspace(_fdt_tmppts[0], dlC);
    g3_replace_add_delta_y(*_fdt_lcore, _fdt_tmppts[3], -pt_list[3][1]);
    _fr_do_cspace(_fdt_tmppts[3], dlF);
    _fdt_pbase = _fdt_rbase;
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[1], -pt_list[1][1]);
    _fr_do_cspace(_fdt_tmppts[1], dlC);
    g3_replace_add_delta_y(*_fdt_rcore, _fdt_tmppts[2], -pt_list[2][1]);
    _fr_do_cspace(_fdt_tmppts[2], dlF);
    _fr_ndbg(NO_REND, g3_draw_cpoly(4, _fdt_tmppts));
    _fr_sdbg(STATS, _frp.stats.ext_wall++);
}

// tabs_flr

// should store pt_code, and look up in there....
#define floor_uv(pt, pn)                   \
    {                                      \
        (pt)->uv.u = pt_uv[pn][pt_rot][0]; \
        (pt)->uv.v = pt_uv[pn][pt_rot][1]; \
        (pt)->p3_flags |= PF_U | PF_V;     \
    }
#define ceil_uv(pt, pn)                    \
    {                                      \
        (pt)->uv.u = pt_uv[pn][pt_rot][0]; \
        (pt)->uv.v = pt_uv[pn][pt_rot][1]; \
        (pt)->p3_flags |= PF_U | PF_V;     \
    }

#define floor_uv_i(pt, pn)                    \
    {                                         \
        (pt)->uv.u = pt_uv[pn][pt_rot][0];    \
        (pt)->uv.v = pt_uv[pn][pt_rot][1];    \
        (pt)->p3_flags |= PF_U | PF_V | PF_I; \
    }
#define ceil_uv_i(pt, pn)                     \
    {                                         \
        (pt)->uv.u = pt_uv[pn][pt_rot][0];    \
        (pt)->uv.v = pt_uv[pn][pt_rot][1];    \
        (pt)->p3_flags |= PF_U | PF_V | PF_I; \
    }

static void _fr_null_flrceil(void) {}

// lets start a war, jack up the dow jones
static void _fr_flat_flr(void) {
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;
    int i, pt_code, loopcnt;
    g3s_phandle *pb;

    loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR];
    do {
        for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb++, pt_code);
        }
        //Â¥Â¥ bug??      _fr_ndbg(NO_REND,g3_draw_poly((*fr_get_idx)(),_fdt_ttf->ptsper,&_fdt_tmppts));
        _fr_ndbg(NO_REND, g3_draw_poly((*fr_get_idx)(), _fdt_ttf->ptsper, _fdt_tmppts));
    } while (loopcnt-- > 0);
    _fr_sdbg(STATS, _frp.stats.flr++);
}

#ifdef FLAT_SUPPORT
static void _fr_flat_lit_flr(void) {
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;
    int i, pt_code, loopcnt;
    g3s_phandle *pb;

    loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR];
    gr_set_fcolor((*fr_get_idx)());
    do {
        for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb, pt_code);
            _fr_pt_light(*pb);
            (*pb++)->p3_flags |= PF_I;
        }
        _fr_ndbg(NO_REND, g3_draw_spoly(_fdt_ttf->ptsper, &_fdt_tmppts));
    } while (loopcnt-- > 0);
    _fr_sdbg(STATS, _frp.stats.flr++);
}
#else
#define _fr_flat_lit_flr _fr_tmap_lit_flr
#endif

static void _fr_tmap_flr(void) {
    uchar nrm_mask = fr_fnorm_list[_fdt_tt];
    int i, pt_code, loopcnt;
    g3s_phandle *pb;
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;
    int pt_rot = me_rotflr(_fdt_mptr);

    loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR];
    do {
        for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb, pt_code);
            pt_code &= FRPTSPTOFF;
            floor_uv(*pb, pt_code);
            pb++;
        }
        if (quik_draw_tmap_p(_fdt_ttf->ptsper)) {
            if ((nrm_mask & 0xf) == FRFNORM_VFULL) {
                _fr_ndbg(NO_REND, _fr_floor_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)()));
            } else {
                _fr_ndbg(NO_REND, _fr_per_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)()));
            }
        }
    } while (loopcnt-- > 0);
    _fr_sdbg(STATS, _frp.stats.flr++);
}

static void _fr_tmap_lit_flr(void) {
    uchar nrm_mask = fr_fnorm_list[_fdt_tt];
    int i, pt_code, loopcnt;
    g3s_phandle *pb;
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;
    int pt_rot = me_rotflr(_fdt_mptr);

    loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR];
    do {
        for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb, pt_code);
            pt_code &= FRPTSPTOFF;
            floor_uv_i(*pb, pt_code);
            _fr_pt_light(*pb++);
        }
        if (quik_draw_tmap_p(_fdt_ttf->ptsper)) {
            if ((nrm_mask & 0xf) == FRFNORM_VFULL) {
                _fr_ndbg(NO_REND, _fr_lit_floor_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)()));
            } else {
                _fr_ndbg(NO_REND, _fr_lit_per_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)()));
            }
        }
    } while (loopcnt-- > 0);
    _fr_sdbg(STATS, _frp.stats.flr++);
}

static void _fr_cspace_wire_flr(void) {
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;
    int i, pt_code, loopcnt;
    g3s_phandle *pb;

    loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR];
    gr_set_fcolor((*fr_get_idx)());
    do {
        for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb, pt_code);
            _fr_pt_cspace(*pb);
            pb++;
        }
        _fr_ndbg(NO_REND, _fr_draw_wire_cpoly_3or4(_fdt_ttf->ptsper));
    } while (loopcnt-- > 0);
    _fr_sdbg(STATS, _frp.stats.flr++);
}

static void _fr_cspace_full_flr(void) {
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;
    int i, pt_code, loopcnt;
    g3s_phandle *pb;

    loopcnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_FLR];
    gr_set_fcolor((*fr_get_idx)());
    do {
        for (pb = &_fdt_tmppts[0], i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb, pt_code);
            _fr_pt_cspace(*pb);
            pb++;
        }
        _fr_ndbg(NO_REND, g3_draw_cpoly(_fdt_ttf->ptsper, _fdt_tmppts));
    } while (loopcnt-- > 0);
    _fr_sdbg(STATS, _frp.stats.flr++);
}

// tabs_ceil
static void _fr_flat_ceil(void) {
    int i, pt_code, loopcnt = 1;
    g3s_phandle *pb;
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;

    if (_fdt_ttf->flags & FRFLRFLG_NOTOP)
        return;
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL];
    do {
        for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb--, pt_code);
        }
        _fr_ndbg(NO_REND, g3_draw_poly((*fr_get_idx)(), _fdt_ttf->ptsper, _fdt_tmppts));
    } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0));
    _fr_sdbg(STATS, _frp.stats.ceil++);
}

#ifdef FLAT_SUPPORT
static void _fr_flat_lit_ceil(void) {
    int i, pt_code, loopcnt = 1;
    g3s_phandle *pb;
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;

    if (_fdt_ttf->flags & FRFLRFLG_NOTOP)
        return;
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL];
    do {
        for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb, pt_code);
            _fr_pt_light(*pb);
            (*pb--)->p3_flags |= PF_I;
        }
        gr_set_fcolor((*fr_get_idx)());
        _fr_ndbg(NO_REND, g3_draw_spoly(_fdt_ttf->ptsper, _fdt_tmppts));
    } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0));
    _fr_sdbg(STATS, _frp.stats.ceil++);
}
#else
#define _fr_flat_lit_ceil _fr_tmap_lit_ceil
#endif

static void _fr_tmap_ceil(void) {
    uchar nrm_mask = fr_fnorm_list[_fdt_tt];
    int i, pt_code, loopcnt = 1;
    g3s_phandle *pb;
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;
    int pt_rot = me_rotceil(_fdt_mptr);

    if (_fdt_ttf->flags & FRFLRFLG_NOTOP)
        return;
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL];
    do {
        for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb, pt_code);
            pt_code &= FRPTSPTOFF;
            ceil_uv(*pb, pt_code);
            pb--;
        }
        if (quik_draw_tmap_p(_fdt_ttf->ptsper)) {
            if ((nrm_mask & 0xf) == FRFNORM_VFULL) {
                _fr_ndbg(NO_REND, _fr_floor_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)()));
            } else {
                _fr_ndbg(NO_REND, _fr_per_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)()));
            }
        }
    } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0));
    _fr_sdbg(STATS, _frp.stats.ceil++);
}

static void _fr_tmap_lit_ceil(void) {
    uchar nrm_mask = fr_fnorm_list[_fdt_tt];
    int i, pt_code, loopcnt = 1;
    g3s_phandle *pb;
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;
    int pt_rot = me_rotceil(_fdt_mptr);

    if (_fdt_ttf->flags & FRFLRFLG_NOTOP)
        return;
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL];
    do {
        for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb, pt_code);
            pt_code &= FRPTSPTOFF;
            ceil_uv_i(*pb, pt_code);
            _fr_pt_light(*pb--);
        }
        if (quik_draw_tmap_p(_fdt_ttf->ptsper)) {
            if ((nrm_mask & 0xf) == FRFNORM_VFULL) {
                _fr_ndbg(NO_REND, _fr_lit_floor_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)()));
            } else {
                _fr_ndbg(NO_REND, _fr_lit_per_func(_fdt_ttf->ptsper, _fdt_tmppts, (*fr_get_tmap)()));
            }
        }
    } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0));
    _fr_sdbg(STATS, _frp.stats.ceil++);
}

//#pragma disable_message(202)
static void _fr_cspace_wire_ceil(void) {
    int i, pt_code, loopcnt = 1;
    g3s_phandle *pb;
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;

    if (_fdt_ttf->flags & FRFLRFLG_NOTOP)
        return;
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL];
    do {
        for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb, pt_code);
            _fr_pt_cspace(*pb);
            pb--;
        }
        _fr_ndbg(NO_REND, _fr_draw_wire_cpoly_3or4(_fdt_ttf->ptsper));
    } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0));
    _fr_sdbg(STATS, _frp.stats.ceil++);
}

static void _fr_cspace_full_ceil(void) {
    int i, pt_code, loopcnt = 1;
    g3s_phandle *pb;
    uchar *ptdat = _fdt_ttf->data, *pt_merge_mask;

    if (_fdt_ttf->flags & FRFLRFLG_NOTOP)
        return;
    pt_merge_mask = merge_masks[me_bits_mirror(_fdt_mptr)][FDT_LK_CEIL];
    do {
        for (pb = (&_fdt_tmppts[0]) + _fdt_ttf->ptsper - 1, i = 0; i < _fdt_ttf->ptsper; i++) {
            pt_code = *ptdat++;
            pt_code = (pt_code ^ pt_merge_mask[0]) & pt_merge_mask[1];
            _fr_figure_pt(*pb, pt_code);
            _fr_pt_cspace(*pb);
            pb--;
        }
        _fr_ndbg(NO_REND, g3_draw_cpoly(_fdt_ttf->ptsper, _fdt_tmppts));
    } while ((_fdt_ttf->flags & FRFLRFLG_2ELEM) && (loopcnt-- > 0));
    _fr_sdbg(STATS, _frp.stats.ceil++);
}
//#pragma enable_message(202)

// reoptimize these to _NOT_ do all the boneheaded stuff

// i ask for nothing, for myself
// for i am dead, for i am dead
int fr_get_ext_gap(fix *face_l, uchar in_fo, uchar *hgts, uchar *mmptr) {
    int fo_t, fo_b;
    if (in_fo == 0xff)
        return 0;
    //   if (in_fo&FO_HT_MSK)
    fo_t = (in_fo ^ mmptr[2]) & mmptr[3];
    fo_b = (in_fo ^ mmptr[0]) & mmptr[1];
    //   if (_fdt_ttf->flags&FRFLRFLG_USEPR)
    if ((fo_t | fo_b) & FO_HT_MSK) {
        //	   uchar cur_fo;
        //    cur_fo=(in_fo^mmptr[2])&mmptr[3];
        *(face_l + 0) = fo_unpack[fo_t & FO_LR_MSK][T_LEFT];              // set x for left ceil
        *(face_l + 2) = fo_unpack[fo_t & FO_LR_MSK][T_RIGHT];             // set x for right ceil
        *(face_l + 1) = *(face_l + 3) = _fr_fhgt_list[hgts[FDT_PT_CEIL]]; // set y's
        if (fo_t & FO_L_PARM)
            *(face_l + 1) -= _fr_fhgt_list[hgts[FDT_PT_PARM]];
        if (fo_t & FO_R_PARM)
            *(face_l + 3) -= _fr_fhgt_list[hgts[FDT_PT_PARM]];
        //      cur_fo=(in_fo^mmptr[0])&mmptr[1];
        *(face_l + 4) = fo_unpack[fo_b & FO_LR_MSK][T_RIGHT];            // set x for right flr
        *(face_l + 6) = fo_unpack[fo_b & FO_LR_MSK][T_LEFT];             // set x for left flr
        *(face_l + 5) = *(face_l + 7) = _fr_fhgt_list[hgts[FDT_PT_FLR]]; // set y's
        if (fo_b & FO_R_PARM)
            *(face_l + 5) += _fr_fhgt_list[hgts[FDT_PT_PARM]];
        if (fo_b & FO_L_PARM)
            *(face_l + 7) += _fr_fhgt_list[hgts[FDT_PT_PARM]];
        if ((*(face_l + 3) == *(face_l + 5)) && (*(face_l + 1) == *(face_l + 7)))
            return 0;
    } else {
        *(face_l + 0) = fo_unpack[in_fo & FO_LR_MSK][T_LEFT];             // set x for left ceil
        *(face_l + 2) = fo_unpack[in_fo & FO_LR_MSK][T_RIGHT];            // set x for right ceil
        *(face_l + 1) = *(face_l + 3) = _fr_fhgt_list[hgts[FDT_PT_CEIL]]; // set y's
        *(face_l + 4) = fo_unpack[in_fo & FO_LR_MSK][T_RIGHT];            // set x for right flr
        *(face_l + 6) = fo_unpack[in_fo & FO_LR_MSK][T_LEFT];             // set x for left flr
        *(face_l + 5) = *(face_l + 7) = _fr_fhgt_list[hgts[FDT_PT_FLR]];  // set y's
        if (*(face_l + 1) == *(face_l + 5))
            return 0;
    }
    return 1;
}

#define ANTI_MASK_COUNT 0x0fff
#define ANTI_MASK_PUNTTOP 0x1000
#define ANTI_MASK_PUNTBOT 0x2000
// or in 1, since there is still the top face, though it is to be punted
#define ANTI_RET_PUNTALL (ANTI_MASK_PUNTTOP | ANTI_MASK_PUNTBOT | 1)

// connect the god damn dots
// connect the god damn dots
// connect the god damn dots
// who am i trying to impress
// who could care less
int _fr_get_anti_gap(fix *face_l, uchar in_fo, uchar *hgts, uchar *mmptr) {
    int fo_t, fo_b;
    int facecnt = 1;
    // more importantly, we think face_l is starting 4 in, we fill normally which works
    // also note that if no height changes, it could mean we have half walls
    // so we check in that case, and if so, add it in place
    _fr_sdbg(ANAL_CHK, if (in_fo == 0xff) mprintf("_fr_get_anti_gap: hey in_fo is 0xff\n"));
    fo_t = (in_fo ^ mmptr[2]) & mmptr[3];
    fo_b = (in_fo ^ mmptr[0]) & mmptr[1];
    if ((fo_t | fo_b) & FO_HT_MSK) { // these cant be at top, since they have parameter
        //	   uchar cur_fo;                                              // unless of course it masks out.... hmmm
        //      cur_fo=(in_fo^mmptr[2])&mmptr[3];
        if (((fo_t & FO_HT_MSK) == 0) && (hgts[FDT_PT_CEIL] == MAX_HGT)) // we are fully at top
            facecnt |= ANTI_MASK_PUNTTOP;
        else {
            *(face_l + 5) = *(face_l + 7) = _fr_fhgt_list[hgts[FDT_PT_CEIL]]; // set y's
            if (fo_t & FO_L_PARM)
                *(face_l + 5) -= _fr_fhgt_list[hgts[FDT_PT_PARM]];
            if (fo_t & FO_R_PARM)
                *(face_l + 7) -= _fr_fhgt_list[hgts[FDT_PT_PARM]];
        }
        face_l += 8;
        //      cur_fo=(in_fo^mmptr[0])&mmptr[1];
        if (((fo_b & FO_HT_MSK) == 0) && (hgts[FDT_PT_FLR] == 0)) // we are fully at bottom
            facecnt |= ANTI_MASK_PUNTBOT;
        else {
            *(face_l + 1) = *(face_l + 3) = _fr_fhgt_list[hgts[FDT_PT_FLR]]; // set y's
            if (fo_b & FO_R_PARM)
                *(face_l + 1) += _fr_fhgt_list[hgts[FDT_PT_PARM]];
            if (fo_b & FO_L_PARM)
                *(face_l + 3) += _fr_fhgt_list[hgts[FDT_PT_PARM]];
        }
    } else {
        if (hgts[FDT_PT_CEIL] == MAX_HGT) // already at top
            facecnt |= ANTI_MASK_PUNTTOP;
        else // correct x's for this have been set already in _fr_init_slopes in frpipe
            *(face_l + 5) = *(face_l + 7) = _fr_fhgt_list[hgts[FDT_PT_CEIL]]; // set y's
        face_l += 8;                     // skip to 2nd entry, as we have finshed or punted the first
        if ((in_fo & FO_LR_MSK) != 0x06) // are we not fully free, tm? (ie. foClr)
        {                                // add the anti of the current here...
            facecnt++;
            // im sure this is wrong
            *(face_l + 0) = fo_unpack[in_fo & FO_LR_MSK][T_LEFT]; // flip since this is real order
            *(face_l + 2) = fo_unpack[in_fo & FO_LR_MSK][T_RIGHT];
            *(face_l + 1) = *(face_l + 3) = _fr_fhgt_list[hgts[FDT_PT_CEIL]]; // set y's
            *(face_l + 4) = fo_unpack[in_fo & FO_LR_MSK][T_RIGHT];            // har har, same flip
            *(face_l + 6) = fo_unpack[in_fo & FO_LR_MSK][T_LEFT];
            *(face_l + 7) = *(face_l + 5) = _fr_fhgt_list[hgts[FDT_PT_FLR]]; // set y's
            face_l += 8;                                                     // skip to 3rd, as we have just done middle
        }
        if (hgts[FDT_PT_FLR] == 0)
            facecnt |= ANTI_MASK_PUNTBOT;
        else
            *(face_l + 1) = *(face_l + 3) = _fr_fhgt_list[hgts[FDT_PT_FLR]]; // set y's
    }
    return facecnt;
}

// amazes me the will of instinct

// keep these in place so the compiler can deal
// these are of wall_id vs. point_id vs. x and y (as in u,v)
static fix inner_wall[2][4][2], final_wall[4][4][2];
fix outer_wall[3][4][2];    // base is set up in frpipe.c
static fix *use_outer_wall; // pointer to the outer wall area to really use

fix tf_diag_walls[4][2] = {{0, fix_make(4, 0)}, {fix_make(1, 0), fix_make(4, 0)}, {fix_make(1, 0), 0}, {0, 0}};
static fix diag_norms[3] = {0, 0, 0};

#ifdef HACK_SHOW
void hack_show(fix edward[4][2]) {
    mprintf(" %8.8x %8.8x   %8.8x %8.8x\n", edward[0][0], edward[0][1], edward[1][0], edward[1][1]);
    mprintf(" %8.8x %8.8x   %8.8x %8.8x\n", edward[3][0], edward[3][1], edward[2][0], edward[2][1]);
}
#endif

// renders icnt of final_walls
void _render_3d_walls(int which, int cnt) {
    WallsToPts *wpt = &wall_pts[FROUTERWALLS + which];
    _fdt_wallid = which;
    flip_setup(which);

    switch (_fdt_pbase = pt_deref[(wpt->ul & FRPTSPTOFF)].base) {
    case 0:
        _fdt_lcore = _fr_ptbase;
        break;
    case 1:
        _fdt_lcore = _fr_ptnext;
        break;
    case 2:
        _fdt_lcore = _fr_ptnext + 1;
        break;
    case 3:
        _fdt_lcore = _fr_ptbase + 1;
        break;
    }
    switch (_fdt_rbase = pt_deref[(wpt->ur & FRPTSPTOFF)].base) {
    case 0:
        _fdt_rcore = _fr_ptbase;
        break;
    case 1:
        _fdt_rcore = _fr_ptnext;
        break;
    case 2:
        _fdt_rcore = _fr_ptnext + 1;
        break;
    case 3:
        _fdt_rcore = _fr_ptbase + 1;
        break;
    }
    _fdt_lcore += _fdt_x;
    _fdt_rcore += _fdt_x;
    switch (cnt) {
    case 4:
        _fr_terr_ext_wall(final_wall[3]);
    case 3:
        _fr_terr_ext_wall(final_wall[2]);
    case 2:
        _fr_terr_ext_wall(final_wall[1]);
    case 1:
        _fr_terr_ext_wall(final_wall[0]);
    case 0:
        break;
    }
}

    // currently always assumes full width... ie. is really broken

#define sgn(x, y) (x > y ? 1 : (x == y ? 0 : -1))

#define io_wall_cmp(iwp, cmp, owp) ((*(i_wall + (iwp << 1) + 1)) cmp(*(o_wall + (owp << 1) + 1)))
#define io_wall_sgn(iwp, owp) (sgn((*(i_wall + (iwp << 1) + 1)), (*(o_wall + (owp << 1) + 1))))
#define dst_from_i(dwp, iwp)                                           \
    {                                                                  \
        (*(dst_wall + (dwp << 1) + 1)) = (*(i_wall + (iwp << 1) + 1)); \
        (*(dst_wall + (dwp << 1))) = (*(i_wall + (iwp << 1)));         \
    }
#define dst_from_o(dwp, owp)                                           \
    {                                                                  \
        (*(dst_wall + (dwp << 1) + 1)) = (*(o_wall + (owp << 1) + 1)); \
        (*(dst_wall + (dwp << 1))) = (*(o_wall + (owp << 1)));         \
    }

int f_is_i;

/*
 * currently broken when the following is true:             40000 40000
 *     /| as the outside gap, and |  |  inside         ie.  10000 20000          20000 20000
 *    / |                         |  |              o_wall  10000 10000  i_wall  10000 10000
 *                                                          00000 00000
 * the problem is 40 40 > 10 20, and 10 <= 10 but 20 ! <= 10 so it thinks bottom crossing failure
 */
// a hack is now in to fix this, should be rewritten in assembler as well
int merge_walls(fix *dst_wall, fix *i_wall, int i_cnt, fix *o_wall, int o_cnt) {
    int cur_i = 0, cur_o = 0, f_cnt = 0, tmp1, tmp2;
    int sgn0, sgn1, sgn2, sgn3;

    f_is_i = TRUE;
    while (cur_i < i_cnt) {
        while (io_wall_cmp(0, <=, 3) && io_wall_cmp(1, <=, 2)) // skip to next outer wall, this one is above our gap
        {
            o_wall += 8;
            if (++cur_o >= o_cnt)
                return f_cnt;
        }
        if (io_wall_cmp(3, >=, 0) && io_wall_cmp(2, >=, 1)) // if gap bottom above current outer wall, skip to next gap
        {
            i_wall += 8;
            f_is_i = FALSE;
            if (++cur_i >= i_cnt)
                return f_cnt;
            else
                continue;
        }
        f_cnt++;
        if ((sgn0 = io_wall_sgn(0, 0)) != (sgn1 = io_wall_sgn(1, 1))) // tmp is gap top below outer wall top
        {
            if (sgn0 == 0)
                tmp1 = (sgn1 <= 0);
            else if (sgn1 == 0)
                tmp1 = (sgn0 <= 0);
            else {
                //            Warning(("top crossing case\n")); // crossing case sucks rocks
                tmp1 = 0;
            }
        } else
            tmp1 = (sgn0 <= 0);
        if (tmp1) {
            dst_from_i(0, 0);
            dst_from_i(1, 1);
        } else {
            dst_from_o(0, 0);
            dst_from_o(1, 1);
        }
        if ((sgn3 = io_wall_sgn(3, 3)) != (sgn2 = io_wall_sgn(2, 2))) // tmp is gap bottom above outer wall bottom
        {
            if (sgn2 == 0)
                tmp2 = (sgn3 >= 0);
            else if (sgn3 == 0)
                tmp2 = (sgn2 >= 0);
            else {
                //		      Warning(("bottom crossing case\n"));
                tmp2 = 0;
            }
        } else
            tmp2 = (sgn2 >= 0);
        f_is_i = (f_is_i && tmp1 && tmp2);
        if (tmp2) {
            dst_from_i(2, 2);
            dst_from_i(3, 3);
            i_wall += 8;
            if (++cur_i >= i_cnt)
                break;
        } else {
            dst_from_o(2, 2);
            dst_from_o(3, 3);
            o_wall += 8;
            if (++cur_o >= o_cnt)
                break;
        }
        dst_wall += 8;
    }
    return f_cnt;
}

uchar solid_chk[4] = {FMK_INT_NW, FMK_INT_EW, FMK_INT_SW, FMK_INT_WW};
uchar clear_chk[4] = {FMK_NW, FMK_EW, FMK_SW, FMK_WW};

// local statics dont actually work, since i define static null in debug, so this is here
static int last_ocnt = -1;

// look around at all my playthings
// eighteen percent a year, for nothing
static void _fr_parse_wall(int which) {
    int icnt, fcnt, ocnt, useocnt, are_solid;
    MapElem *oth_mptr;
    uchar oth_hgts[3], oth_fo;

    oth_mptr = _fdt_mptr + wall_adds[which];

    if (_fdt_me_flags & MAP_FRIEND_FULL_MASK)
        _game_fr_tmap = me_tmap_wall(oth_mptr);
    else
        _game_fr_tmap = _fdt_wmap;

    if (me_clearsolid(_fdt_mptr) & solid_chk[which]) {
        _fr_sdbg(STATS, _frp.stats.hitsolid++);
        are_solid = 2;
    } else {
        oth_fo = face_obstruct[me_tiletype(oth_mptr)][(which + 2) & 3];
        are_solid = (oth_fo == 0xff);
    }

    if (are_solid) // totally solid on other side, just do us, baby baby
    {
        _fr_sdbg(STATS, if ((me_clearsolid(_fdt_mptr) & solid_chk[which]) == 0) _frp.stats.setsolid++);
        _me_clearsolid(_fdt_mptr) |= solid_chk[which];
        // int walls doesnt know about parameters, so we lost
        //      if (_fdt_ttf->flags&FRFLRFLG_USEPR)
        //      {
        fcnt = fr_get_ext_gap(&final_wall[0][0][0], _fdt_fo[which], _fdt_hgts,
                              (uchar *)mmask_facelet[me_bits_mirror(_fdt_mptr)]);
        if (fcnt == 0) {
            _me_clearsolid(_fdt_mptr) |= clear_chk[which];
            _fr_sdbg(STATS, _frp.stats.setclear++);
        } else
            (*_fr_render_walls)(which, fcnt);
        //      }
        //      else
        //	      _fr_terr_int_wall(FROUTERWALLS+which);
        return;
    }
    _fr_sdbg(NO_RTF, return );
    // otherwise, grind away
    oth_hgts[FDT_PT_FLR] = me_height_flr(oth_mptr);
    oth_hgts[FDT_PT_CEIL] = MAX_HGT - me_height_ceil(oth_mptr);
    oth_hgts[FDT_PT_PARM] = me_param(oth_mptr);
    ocnt = _fr_get_anti_gap(&outer_wall[0][0][0], oth_fo, oth_hgts, (uchar *)mmask_facelet[me_bits_mirror(oth_mptr)]);
    if (ocnt != ANTI_RET_PUNTALL) {
        useocnt = (ocnt & ANTI_MASK_COUNT);
        if ((ocnt & ANTI_MASK_PUNTBOT) == 0) { // touch up the bottom edge
            if (useocnt != last_ocnt) {        // we want to store the ocnt we actually filled, not ended with
                last_ocnt = useocnt;
                outer_wall[useocnt][1][0] = outer_wall[useocnt][2][0] = fix_make(1, 0);
                outer_wall[useocnt][0][0] = outer_wall[useocnt][2][1] = outer_wall[useocnt][3][0] =
                    outer_wall[useocnt][3][1] = 0;
            }
            useocnt++;
        }
        if ((ocnt & ANTI_MASK_PUNTTOP) == 0)
            use_outer_wall = &outer_wall[0][0][0];
        else {
            use_outer_wall = &outer_wall[1][0][0];
            useocnt--;
        }
        _fr_sdbg(SANITY, if (useocnt == 0) mprintf("_fr_parse_wall: have 0 usecnt, but didnt punt yet\n"));
        _fr_sdbg(SANITY,
                 if (_fdt_fo[which] == 0xff) mprint("_fr_parse_wall: local fo==0xff, wallbits should have been 0"));
        icnt = fr_get_ext_gap(&inner_wall[0][0][0], _fdt_fo[which], _fdt_hgts,
                              (uchar *)mmask_facelet[me_bits_mirror(_fdt_mptr)]);

        fcnt = merge_walls(&final_wall[0][0][0], &inner_wall[0][0][0], icnt, use_outer_wall, useocnt);
        if ((f_is_i) && (fcnt == icnt)) {
            _fr_sdbg(STATS, _frp.stats.fisisolid++);
            _me_clearsolid(_fdt_mptr) |= solid_chk[which];
        }
        (*_fr_render_walls)(which, fcnt);

#ifdef HACK_SHOW
        {
            int i;
            mprintf("%d outer (%d) are:\n", useocnt, me_tiletype(oth_mptr));
            for (i = 0; i < useocnt; i++)
                hack_show(use_outer_wall + (i * 8));
            mprintf("%d inner (%d) are:\n", icnt, me_tiletype(_fdt_mptr));
            for (i = 0; i < icnt; i++)
                hack_show(inner_wall[i]);
            mprintf("%d final walls be:\n", fcnt);
            for (i = 0; i < fcnt; i++)
                hack_show(final_wall[i]);
        }
#endif

    } else
        fcnt = 0;
    if (fcnt == 0) {
        _me_clearsolid(_fdt_mptr) |= clear_chk[which]; // or just shift here? who can tell
        _fr_sdbg(STATS, _frp.stats.setclear++);
    }
}

// take the mptr, decode it's clip vectors, and set initial wall masks for the tile
void parse_clip_tile() {}

    // so, ahh, should have wall bits for floor and ceiling, so we can start clipping

#if _fr_defdbg(STATS)
#define wall_check(wmask1, wmask2, w_id)                      \
    if ((_fdt_mask & wmask1) && (_fdt_ttw.wallbits & wmask2)) \
        if ((me_clearsolid(_fdt_mptr) & wmask1) != 0) {       \
            _fr_sdbg(STATS, _frp.stats.hitclear++);           \
        } else if ((me_subclip(_fdt_mptr) & wmask1) != 0) {   \
            _fr_sdbg(STATS, _frp.stats.hitsc++);              \
        } else                                                \
            _fr_parse_wall(w_id)
#else // do wacky shit to make this work
#define wall_check(wmask1, wmask2, w_id)                                                                      \
    if ((_fdt_mask & wmask1) && (_fdt_ttw.wallbits & wmask2) && ((me_clearsolid(_fdt_mptr) & wmask1) == 0) && \
        ((me_subclip(_fdt_mptr) & wmask1) == 0))                                                              \
    _fr_parse_wall(w_id)
#endif

#include "tilename.h"

// if you're so special, why aren't you dead
// implicit parameters are _fdt_x,_fdt_y,_fdt_mptr,_fdt_mask
void fr_draw_tile(void) {
    if ((_fdt_tt = me_tiletype(_fdt_mptr)) == TILE_SOLID) // really, clip should deal
    {
        me_subclip_set(_fdt_mptr, SUBCLIP_OUT_OF_CONE); // sure, deal with it...
        return;                                         // so make this a warn later
    }
    if (me_subclip(_fdt_mptr) == SUBCLIP_OUT_OF_CONE) {
        return;
    }

    _fr_ndbg(NO_SUB_CLIP, parse_clip_tile());
    _fr_sdbg(SHOW_BASE, if (_fr_terr_prim) _fr_terr_base(0); else _fr_terr_dumb_base(0));
    _fdt_ttw = tile_walls[_fdt_tt];
    _fdt_ttf = &tile_floors[_fdt_tt];
    _fdt_hgts[FDT_PT_FLR] = me_height_flr(_fdt_mptr);
    _fdt_hgts[FDT_PT_CEIL] = MAX_HGT - me_height_ceil(_fdt_mptr);
    _fdt_hgts[FDT_PT_PARM] = me_param(_fdt_mptr);
    _fdt_hgts[FDT_PT_FPRM] = me_height_flr(_fdt_mptr) + me_param(_fdt_mptr);
    _fdt_hgts[FDT_PT_CPRM] = MAX_HGT - me_height_ceil(_fdt_mptr) - me_param(_fdt_mptr);
    _fdt_cur_parm = _fdt_fix_parm = _fr_fhgt_list[_fdt_hgts[FDT_PT_PARM]];
    _fdt_fo = face_obstruct[_fdt_tt];
    _fdt_wmap = me_tmap_wall(_fdt_mptr);
    _fdt_me_flags = me_flags(_fdt_mptr);
    _fdt_slock = _fr_fhgt_list[_fdt_me_flags & MAP_VLOCK_MASK] >> 8;

    if (_frp.faces.cyber && _fdt_terr) {
        int tmp = me_bits_flip(_fdt_mptr);
        void fr_terr_cspace_pick(uchar do_w);

        if (tmp != last_csp_fr)
            fr_terr_cspace_pick(last_csp_fr = tmp);
    }

    // how about external walls, eh?
    wall_check(FMK_NW, FRWALLNORTH, 0);
    wall_check(FMK_EW, FRWALLEAST, 1);
    wall_check(FMK_SW, FRWALLSOUTH, 2);
    wall_check(FMK_WW, FRWALLWEST, 3);

    _game_fr_tmap = _fdt_wmap;
    _fdt_icnt = (_fdt_ttw.wallbits & FRWALLINT);
    // set real flip for internal faces now
    if (_fdt_icnt) {
        flip_setup(4); // 4 is secret internal face code
        switch (_fdt_icnt) {
#ifdef USE_OCT
        case 6:
            _fdt_cur_parm = FROCTNUM; // recompute wacky prm stuff
            _fdt_hgts[FDT_PT_FPRM] = me_height_flr(_fdt_mptr) + me_param(_fdt_mptr);
            // this cant be done yet
            // _fdt_hgts[FDT_PT_CPRM]=MAX_HGT-me_height_ceil(_fdt_mptr)-me_param(_fdt_mptr);
            // _fdt_hgts[FDT_PT_PARM]=me_param(_fdt_mptr);
            _fr_terr_int_wall(_fdt_ttw.wallbase + 5); // or hmm.. a temp
        case 5:
            _fr_terr_int_wall(_fdt_ttw.wallbase + 4); //  variable and then
        case 4:
            _fr_terr_int_wall(_fdt_ttw.wallbase + 3); //  increment it in
        case 3:
            _fr_terr_int_wall(_fdt_ttw.wallbase + 2); //  each case??
#endif
        case 2:
            _fr_terr_int_wall(_fdt_ttw.wallbase + 1);
        case 1:
            _fr_terr_int_wall(_fdt_ttw.wallbase + 0);
        case 0:
            break;
        }
    }
#ifdef USE_OCT // in case it was overloaded by FROCTNUM, not needed if no oct tiles
    _fdt_cur_parm = _fdt_fix_parm;
#endif
    _game_fr_tmap = me_tmap_flr(_fdt_mptr);
    _fr_terr_flr();
    _game_fr_tmap = me_tmap_ceil(_fdt_mptr);
    _fr_terr_ceil();
    // now check object cache

    // FIXME HAX HAX HAX why does this segfault?
    _fr_parse_obj();

#ifdef CLEAR_AS_WE_GO
    me_subclip_set(_fdt_mptr, SUBCLIP_OUT_OF_CONE); // sure, deal with it...
#endif
    //   mprintf("-");
}

    // when all the leaves have fallen and turned to dust
    // will we remain entrenched within our ways
    // indifference, the plague that moves throughout this land

#define FRT_PTS_POLY 1
#define FRT_PTS_TMAP 2
#define FRT_PTS_LITE 4
#define FRT_PTS_CYB  8

#define FRT_FLAT   0
#define FRT_LIT    1
#define FRT_TMAP   2
#define FRT_CSPACE 4
#define FRT_NULL   6
#define FRT_MAX_F  7

static void (*_fr_tabs_int_wall[FRT_MAX_F])(int wall_id) = {
    _fr_flat_int_wall,        _fr_flat_lit_int_wall,    _fr_tmap_int_wall, _fr_tmap_lit_int_wall,
    _fr_cspace_wire_int_wall, _fr_cspace_full_int_wall, _fr_null_int_wall};
static void (*_fr_tabs_ext_wall[FRT_MAX_F])(fix pt_list[4][2]) = {
    _fr_flat_ext_wall,        _fr_flat_lit_ext_wall,    _fr_tmap_ext_wall, _fr_tmap_lit_ext_wall,
    _fr_cspace_wire_ext_wall, _fr_cspace_full_ext_wall, _fr_null_ext_wall};
static void (*_fr_tabs_flr[FRT_MAX_F])(void) = {_fr_flat_flr,     _fr_flat_lit_flr,    _fr_tmap_flr,
                                                _fr_tmap_lit_flr, _fr_cspace_wire_flr, _fr_cspace_full_flr,
                                                _fr_null_flrceil};
static void (*_fr_tabs_ceil[FRT_MAX_F])(void) = {_fr_flat_ceil,     _fr_flat_lit_ceil,    _fr_tmap_ceil,
                                                 _fr_tmap_lit_ceil, _fr_cspace_wire_ceil, _fr_cspace_full_ceil,
                                                 _fr_null_flrceil};

#define CSPACE_WALLS

void fr_terr_cspace_pick(uchar do_wall) {
    int to_do, wall_do;

    if (do_wall) {
        wall_do = to_do = FRT_CSPACE + 1; // FRT_FLAT
        if (do_wall >= 2) {
            if (_fr_curflags & FR_PICKUPM_MASK)
                wall_do = FRT_NULL;
            else
                wall_do--;
	}
    } else {
        if (_fr_curflags & FR_PICKUPM_MASK)
            wall_do = to_do = FRT_NULL;
        else
            wall_do = to_do = FRT_CSPACE;
    }

    _fr_terr_int_wall = _fr_tabs_int_wall[wall_do];
    _fr_terr_ext_wall = _fr_tabs_ext_wall[wall_do];
    _fr_terr_ceil = _fr_tabs_ceil[to_do];
    _fr_terr_flr = _fr_tabs_flr[to_do];
}

// setup the point revector
void fr_terr_frame_start(void) {
    int wall_do, ceil_do, flr_do;

    // cspace
    if (_fr_curflags & FR_PICKUPM_MASK)
        if (_frp.faces.cyber)
            wall_do = ceil_do = flr_do = FRT_NULL;
        else
            wall_do = ceil_do = flr_do = FRT_FLAT;
    else if (_frp.faces.cyber)
        wall_do = ceil_do = flr_do = _frp.faces.cyber_full + FRT_CSPACE;
    else { // realspace
        int lmod = _frp_light_bits_any() ? 1 : 0;
        wall_do = lmod + (((_frp.faces.main) && (_frp.faces.wall)) ? FRT_TMAP : 0);
        ceil_do = lmod + (((_frp.faces.main) && (_frp.faces.ceiling)) ? FRT_TMAP : 0);
        flr_do = lmod + (((_frp.faces.main) && (_frp.faces.floor)) ? FRT_TMAP : 0);
    }

    _fr_terr_int_wall = _fr_tabs_int_wall[wall_do];
    _fr_terr_ext_wall = _fr_tabs_ext_wall[wall_do];
    _fr_terr_ceil = _fr_tabs_ceil[ceil_do];
    _fr_terr_flr = _fr_tabs_flr[flr_do];

    _fr_parse_obj = render_parse_obj;

    _fr_render_walls = _render_3d_walls;

    // setup the tmp points for this frame
    g3_alloc_list(FDT_TMPPTCNT, _fdt_tmppts);

    last_csp_fr = 0; // initially no filled mode
    _fdt_terr = TRUE;
}

void fr_terr_frame_end(void) {
    void fr_tfunc_grab_start(void);
    fr_tfunc_grab_start(); // set up the physics facelet indirections...
    _fdt_terr = FALSE;
    g3_free_list(FDT_TMPPTCNT, _fdt_tmppts);
}

#if _fr_defdbg(CURSOR)
// wrapped within the walkman with a halo of distortion
// aural contraceptive aborting pregnant conversation
void fr_set_cursor(int x, int y) {
    _fr_cursorx = x;
    _fr_cursory = y;
}
#endif

#define TF_DIRECT

#ifdef TF_DIRECT
#include "tfdirect.h"
#include "ss_flet.h"
// this is a direct render <-> tfunc connection
// ie. we dont accumulate and then send facelets, we just send as we go
//  by calling the tfutil stuff, namely facelet_solve and so on
//  thus we never build 3d surfaces, just distances and normals
// hopefully, this will cause things to, gasp, speed up

//#define AddTmapFlags(fl) if (_game_fr_tmap<10) fl|=SS_BCD_MISC_CLIMB
#define AddTmapFlags(fl)                         \
    if (textprops[_game_fr_tmap].friction_climb) \
    fl |= SS_BCD_MISC_CLIMB

uchar wall_pc[] = {SS_BCD_PRIM_NEG_Y | SS_BCD_TYPE_WALL, SS_BCD_PRIM_NEG_X | SS_BCD_TYPE_WALL,
                   SS_BCD_PRIM_YAXIS | SS_BCD_TYPE_WALL, SS_BCD_PRIM_XAXIS | SS_BCD_TYPE_WALL,
                   SS_BCD_PRIM_MULTI | SS_BCD_TYPE_WALL};

// secret gnosis becomes the order of the day
// we set up primary and transform the center point into our space
// then we just pass it on to facelet_solve, none the worse for wear...
static void _render_tfunc_walls(int which, int cnt) {
    // fuck it, ext_walls are always ext, so punt generality, lets go
    fix pt[3];
    int pc = wall_pc[which];
    switch (which) // set up localized point set
    {              // NESW
    case 0:
        pt[2] = fix_1 - tf_loc_pt[1];
        pt[0] = tf_loc_pt[0];
        break;
    case 1:
        pt[2] = fix_1 - tf_loc_pt[0];
        pt[0] = fix_1 - tf_loc_pt[1];
        break;
    case 2:
        pt[2] = tf_loc_pt[1];
        pt[0] = fix_1 - tf_loc_pt[0];
        break;
    case 3:
        pt[2] = tf_loc_pt[0];
        pt[0] = tf_loc_pt[1];
        break;
    }
    pt[1] = tf_loc_pt[2];
    AddTmapFlags(pc);
    //   if (pc&SS_BCD_MISC_CLIMB) mprintf("Set climbable... %x\n",pc);
    switch (cnt) {
    case 4:
        tf_solve_aligned_face(pt, final_wall[3], pc, NULL);
    case 3:
        tf_solve_aligned_face(pt, final_wall[2], pc, NULL);
    case 2:
        tf_solve_aligned_face(pt, final_wall[1], pc, NULL);
    case 1:
        tf_solve_aligned_face(pt, final_wall[0], pc, NULL);
    case 0:
        break;
    }
}

static void _fr_tfunc_diag_wall(int wall_id) {
    fix pt[3], C;
    int pfl;
    switch (wall_id) {
    case 0: // nw - se, normal to upper ne
        diag_norms[0] = fix_inv_root2;
        diag_norms[1] = fix_inv_root2;
        C = tf_loc_pt[0] - tf_loc_pt[1];
        pt[0] = (fix_1 - C) / 2;
        pt[2] = tf_loc_pt[1] - pt[0];
        break;
    case 1: // sw - ne, normal to lower se
        diag_norms[0] = fix_inv_root2;
        diag_norms[1] = -fix_inv_root2;
        C = tf_loc_pt[0] + tf_loc_pt[1];
        pt[0] = C / 2;
        pt[2] = pt[0] - tf_loc_pt[1];
        break;
    case 2: // ne - sw, normal to upper nw
        diag_norms[0] = -fix_inv_root2;
        diag_norms[1] = fix_inv_root2;
        C = tf_loc_pt[0] + tf_loc_pt[1];
        pt[0] = fix_1 - (C / 2);
        pt[2] = tf_loc_pt[1] - (C / 2);
        break;
    case 3: // nw - se, normal to lower sw
        diag_norms[0] = -fix_inv_root2;
        diag_norms[1] = -fix_inv_root2;
        C = tf_loc_pt[0] - tf_loc_pt[1];
        pt[0] = (fix_1 - C) / 2;
        pt[2] = pt[0] - tf_loc_pt[1];
        break;
    }
    pt[1] = tf_loc_pt[2];
    pfl = wall_pc[4] | TF_FLG_BOX_FULL;
    AddTmapFlags(pfl);
    tf_solve_remetriced_face(pt, tf_diag_walls, pfl, diag_norms, fix_inv_root2);
}

// hack this for now for diagonals
static void _fr_tfunc_int_wall(int wall_id) {
    if ((_fdt_mask & FACELET_MASK_I) == 0)
        return;
    _fr_tfunc_diag_wall(wall_id);
}

int _fr_tfunc_flr_normal(fix *vec, int *lflg, int fnorm_entry) {
    int rv;
    if (me_bits_mirror_x(_fdt_mptr) == (MAP_FFLAT << MAP_MIRROR_SHF))
        fnorm_entry = FRFNORM_VFULL;
    else
        fnorm_entry &= 0xf;
    switch (fnorm_entry) {
    case FRFNORM_VZERO:
    case FRFNORM_VFULL:
        *lflg |= SS_BCD_PRIM_ZAXIS | SS_BCD_TYPE_FLOOR;
        return 0;

    case FRFNORM_SLPS:
        vec[0] = 0;
        vec[1] = slope_norm[_fdt_hgts[FDT_PT_PARM]][2];
        rv = 1;
        break;
    case FRFNORM_SLPW:
        vec[0] = slope_norm[_fdt_hgts[FDT_PT_PARM]][2];
        vec[1] = 0;
        rv = 2;
        break;
    case FRFNORM_SLPN:
        vec[0] = 0;
        vec[1] = slope_norm[_fdt_hgts[FDT_PT_PARM]][0];
        rv = 3;
        break;
    case FRFNORM_SLPE:
        vec[0] = slope_norm[_fdt_hgts[FDT_PT_PARM]][0];
        vec[1] = 0;
        rv = 4;
        break;
    }
    vec[2] = slope_norm[_fdt_hgts[FDT_PT_PARM]][1];
    *lflg |= SS_BCD_PRIM_MULTI | SS_BCD_TYPE_FLOOR;
    return rv;
}

int _fr_tfunc_ceil_normal(fix *vec, int *lflg, int fnorm_entry) {
    int tmp = me_bits_mirror(_fdt_mptr), rv;
    switch (tmp) {
    case MAP_CFLAT:
        fnorm_entry = FRFNORM_VFULL;
        break;
    case MAP_MIRROR:
        fnorm_entry = fnorm_entry ^ 0x2; // fall through to the mask
    case MAP_FFLAT:
    case MAP_MATCH:
        fnorm_entry &= 0xf;
    }
    switch (fnorm_entry) {
    case FRFNORM_VZERO:
    case FRFNORM_VZ_MIR:
    case FRFNORM_VF_MIR:
    case FRFNORM_VFULL:
        *lflg |= SS_BCD_PRIM_NEG_Z | SS_BCD_TYPE_CEIL;
        return 0;

    case FRFNORM_SLPS:
        vec[0] = 0;
        vec[1] = slope_norm[_fdt_hgts[FDT_PT_PARM]][0];
        rv = 1;
        break;
    case FRFNORM_SLPW:
        vec[0] = slope_norm[_fdt_hgts[FDT_PT_PARM]][0];
        vec[1] = 0;
        rv = 2;
        break;
    case FRFNORM_SLPN:
        vec[0] = 0;
        vec[1] = slope_norm[_fdt_hgts[FDT_PT_PARM]][2];
        rv = 3;
        break;
    case FRFNORM_SLPE:
        vec[0] = slope_norm[_fdt_hgts[FDT_PT_PARM]][2];
        vec[1] = 0;
        rv = 4;
        break;
    }
    vec[2] = -slope_norm[_fdt_hgts[FDT_PT_PARM]][1];
    *lflg |= SS_BCD_PRIM_MULTI | SS_BCD_TYPE_CEIL;
    return rv;
}

static fix _tfunc_real_floor[4][2] = {{0, fix_1}, {fix_1, fix_1}, {fix_1, 0}, {0, 0}};

static fix _tfunc_nrm[3], _tfunc_rpts[3];
static int _tfunc_flg;

int do_fc_trans(fix *xy, fix *sc, fix *vals) {
    int rm = fix_div(fix_1, sc[0]);
    vals[0] = fix_mul(xy[0], sc[0]) + fix_mul(xy[1], -sc[1]);
    vals[1] = fix_mul(xy[0], sc[1]) + fix_mul(xy[1], sc[0]);
    //   mprintf("dft: from %x %x and %x %x stores %x %x, rm %x\n",xy[0],xy[1],sc[0],sc[1],vals[0],vals[1],rm);
    return rm;
}

void do_floor_element(int nrm_mask) {
    int code;
    fix sc[2], xy[2];

    _tfunc_flg = 0;
    code = _fr_tfunc_flr_normal(_tfunc_nrm, &_tfunc_flg, nrm_mask & 0xf);
    xy[1] = tf_loc_pt[2] - _fr_fhgt_list[_fdt_hgts[FDT_PT_FLR]];
    switch (code) {
    case 0:

        *(g3s_vector *)_tfunc_rpts =
            *(g3s_vector *)tf_loc_pt; // sub faster version?      _memcpy32l(_tfunc_rpts,tf_loc_pt,3);
        _tfunc_rpts[2] = xy[1];
        _tfunc_real_floor[0][1] = _tfunc_real_floor[1][1] = fix_1;
        tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_BOX_FULL, NULL);
        return; // flat floor
    case 1:
        xy[0] = tf_loc_pt[1];
        _tfunc_rpts[0] = tf_loc_pt[0];
        sc[1] = _tfunc_nrm[1];
        break; // N up (slpS) slope
    case 2:
        xy[0] = tf_loc_pt[0];
        _tfunc_rpts[0] = fix_1 - tf_loc_pt[1];
        sc[1] = _tfunc_nrm[0];
        break; // E up slope
    case 3:
        xy[0] = fix_1 - tf_loc_pt[1];
        _tfunc_rpts[0] = fix_1 - tf_loc_pt[0];
        sc[1] = -_tfunc_nrm[1];
        break; // S up slope
    case 4:
        xy[0] = fix_1 - tf_loc_pt[0];
        _tfunc_rpts[0] = tf_loc_pt[1];
        sc[1] = -_tfunc_nrm[0];
        break; // W up slope
    }
    sc[0] = _tfunc_nrm[2];
    _tfunc_real_floor[0][1] = _tfunc_real_floor[1][1] = do_fc_trans(xy, sc, &_tfunc_rpts[1]);
    if (nrm_mask <= 0xf)
        tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_BOX_FULL, _tfunc_nrm);
}

void do_ceil_element(int nrm_mask) {
    int code;
    fix sc[2], xy[2];

    _tfunc_flg = 0;
    code = _fr_tfunc_ceil_normal(_tfunc_nrm, &_tfunc_flg, nrm_mask & 0xf);
    xy[1] = _fr_fhgt_list[_fdt_hgts[FDT_PT_CEIL]] - tf_loc_pt[2];
    switch (code) {
    case 0:
        _tfunc_rpts[0] = tf_loc_pt[0];
        _tfunc_rpts[1] = fix_1 - tf_loc_pt[1];
        _tfunc_rpts[2] = xy[1];
        _tfunc_real_floor[0][1] = _tfunc_real_floor[1][1] = fix_1;
        tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_BOX_FULL, NULL);
        return; // flat ceil
    case 1:
        xy[0] = fix_1 - tf_loc_pt[1];
        _tfunc_rpts[0] = tf_loc_pt[0];
        sc[1] = -_tfunc_nrm[1];
        break; // N up (slpS) slope
    case 2:
        xy[0] = fix_1 - tf_loc_pt[0];
        _tfunc_rpts[0] = fix_1 - tf_loc_pt[1];
        sc[1] = -_tfunc_nrm[0];
        break; // E up slope
    case 3:
        xy[0] = tf_loc_pt[1];
        _tfunc_rpts[0] = fix_1 - tf_loc_pt[0];
        sc[1] = _tfunc_nrm[1];
        break; // S up slope
    case 4:
        xy[0] = tf_loc_pt[0];
        _tfunc_rpts[0] = tf_loc_pt[1];
        sc[1] = _tfunc_nrm[0];
        break; // W up slope
    }
    sc[0] = -_tfunc_nrm[2];
    _tfunc_real_floor[0][1] = _tfunc_real_floor[1][1] = do_fc_trans(xy, sc, &_tfunc_rpts[1]);
    if (nrm_mask <= 0xf)
        tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_BOX_FULL, _tfunc_nrm);
}

static void _fr_tfunc_flr(void) {
    uchar nrm_mask = fr_fnorm_list[_fdt_tt];
    int fcecnt;

    if ((_fdt_mask & FACELET_MASK_F) == 0)
        return;
    fcecnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1
                                                //   if ((_fdt_tt!=TILE_OPEN)&&(_fdt_tt!=TILE_SOLID))
                                                //      mprintf("BonusF %d (%d)..",nrm_mask,_fdt_tt);
    if (fcecnt == 0)
        do_floor_element(nrm_mask & 0xf);
    else {
        int tmp = me_bits_mirror(_fdt_mptr);
        uchar icky = (_fdt_tt >= TILE_SLOPECV_NW);
        if (tmp == MAP_FFLAT)
            do_floor_element(FRFNORM_VFULL);
        else if (icky) {
            do_floor_element(0xf0 | (nrm_mask & 0xf)); // now make it 3pt...
            _tfunc_real_floor[1][0] = 0;
            tf_solve_aligned_face(_tfunc_rpts, &_tfunc_real_floor[1], _tfunc_flg | TF_FLG_3PNT_MASK, _tfunc_nrm);
            _tfunc_real_floor[1][0] = fix_1;
            do_floor_element(0xf0 | (nrm_mask >> 4)); // and so on...
            tf_solve_aligned_face(_tfunc_rpts, &_tfunc_real_floor[1], _tfunc_flg | TF_FLG_3PNT_MASK, _tfunc_nrm);
        } else {
            do_floor_element(nrm_mask & 0xf);
            do_floor_element(nrm_mask >> 4);
        }
    }
}

static void _fr_tfunc_ceil(void) {
    uchar nrm_mask = fr_fnorm_list[_fdt_tt];
    int fcecnt;

    if ((_fdt_mask & FACELET_MASK_C) == 0)
        return;
    if (_fdt_ttf->flags & FRFLRFLG_NOTOP)
        return;
    fcecnt = _fdt_ttf->flags >> FRFLRSHF_2ELEM; // 0 or 1
                                                //   if ((_fdt_tt!=TILE_OPEN)&&(_fdt_tt!=TILE_SOLID))
                                                //      mprintf("BonusC %d (%d)..",nrm_mask,_fdt_tt);
    if (fcecnt == 0)
        do_ceil_element(nrm_mask);
    else {
        int tmp = me_bits_mirror(_fdt_mptr);
        uchar icky = (_fdt_tt < TILE_SLOPECV_NW), hard;
        if (tmp == MAP_CFLAT)
            hard = 0;
        else if (tmp == MAP_MIRROR)
            hard = !icky;
        else
            hard = icky;
        if (hard) { // broken, depends on icky
            do_ceil_element(0xf0 | (nrm_mask & 0xf));
            tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_3PNT_MASK, _tfunc_nrm);
            do_ceil_element(0xf0 | (nrm_mask >> 4));
            _tfunc_real_floor[2][0] = 0;
            tf_solve_aligned_face(_tfunc_rpts, _tfunc_real_floor, _tfunc_flg | TF_FLG_3PNT_MASK, _tfunc_nrm);
            _tfunc_real_floor[2][0] = fix_1;
        } else {
            do_ceil_element(nrm_mask & 0xf);
            do_ceil_element(nrm_mask >> 4);
        }
    }
}

// check for terrain objects
static void _fr_tfunc_obj(void) { facelet_parse_obj(); }

void fr_tfunc_grab_start(void) {
    _fr_terr_int_wall = _fr_tfunc_int_wall;
    _fr_terr_ceil = _fr_tfunc_ceil;
    _fr_terr_flr = _fr_tfunc_flr;

    _fr_parse_obj = _fr_tfunc_obj;

    _fr_render_walls = _render_tfunc_walls;
}

void fr_tfunc_grab_fast(int mask) {
    me_subclip_set(_fdt_mptr, SUBCLIP_FULL_TILE);
    _fdt_mask = mask;
    fr_draw_tile();
    //   me_subclip_set(_fdt_mptr,SUBCLIP_OUT_OF_CONE);
}
#endif // TFUNC_SUPPORT

#ifdef WHOSE_MUMP

// add to everything.... arrrrgghgh
#ifndef SHIP
#define CheckRendWallBt(btid) ((me_bits_rend4(c_t) & btid) == 0)
#define CheckRendOthBt(btid) ((me_bits_rend3(c_t) & btid) == 0)
#else
#define CheckRendWallBt(btid) (TRUE)
#define CheckRendOthBt(btid) (TRUE)
#endif

#endif

//==============================================================================
//  Edge-finding routines.
//==============================================================================
uchar edge_get_fandc(MapElem *mp, int c_edge, char *e_list);

#define EDGE_GET

#include "fredge.h"
#ifdef EDGE_GET

uchar edge_get_fandc(MapElem *mp, int c_edge, char *e_list) {
    uchar *mmptr, fo, p = me_param(mp), in_fo;
    in_fo = face_obstruct[me_tiletype(mp)][c_edge];
    if ((me_tiletype(mp) == TILE_SOLID) || (in_fo == 0xff)) {
        e_list[0] = e_list[1] = MAX_HGT;
        e_list[2] = e_list[3] = MAX_HGT;
        return FALSE;
    } else if (p == 0) {
        e_list[0] = e_list[1] = me_height_flr(mp);
        e_list[2] = e_list[3] = MAX_HGT - me_height_ceil(mp);
    } else {
        mmptr = (uchar *)mmask_facelet[me_bits_mirror(mp)];
        e_list[0] = e_list[1] = me_height_flr(mp);
        fo = (in_fo ^ mmptr[0]) & mmptr[1];
        if (fo & FO_L_PARM)
            e_list[0] += p;
        if (fo & FO_R_PARM)
            e_list[1] += p;
        e_list[2] = e_list[3] = MAX_HGT - me_height_ceil(mp);
        fo = (in_fo ^ mmptr[2]) & mmptr[3];
        if (fo & FO_L_PARM)
            e_list[2] -= p;
        if (fo & FO_R_PARM)
            e_list[3] -= p;
    }
    return TRUE;
}

char edge_vals[3];
// returns left and right heights for a map edge
// ceil_p is 1 if it is the ceiling you care about
char *map_get_edge(void *omp, int edge, int ceil_p) {
    uchar *mmptr, fo, p, in_fo;
    MapElem *mp = (MapElem *)omp;

    p = me_param(mp);
    in_fo = face_obstruct[me_tiletype(mp)][edge];

    if (p == 0) {
        if (ceil_p)
            edge_vals[0] = edge_vals[1] = MAX_HGT - me_height_ceil(mp);
        else
            edge_vals[0] = edge_vals[1] = me_height_flr(mp);
    } else {
        mmptr = (uchar *)mmask_facelet[me_bits_mirror(mp)];
        if (ceil_p) {
            edge_vals[0] = edge_vals[1] = MAX_HGT - me_height_ceil(mp);
            fo = (in_fo ^ mmptr[2]) & mmptr[3];
            p = -p;
        } else {
            edge_vals[0] = edge_vals[1] = me_height_flr(mp);
            fo = (in_fo ^ mmptr[0]) & mmptr[1];
        }
        if (fo & FO_L_PARM)
            edge_vals[0] += p;
        if (fo & FO_R_PARM)
            edge_vals[1] += p;
    }
    return edge_vals;
}

// 0 and 1 will be ceiling, 2 and 3 will be floor for internal, +4 for other

// this is a really dumb way to a do this, really
// returns 0 if no edge space, 1 for flat, 2 for mini-step, 3 for step
//   4 for ledge, 5 for cliff
int get_edge_code(void *omp, int edge) {
    char edge_list[8], d_list[4], x_diff[4];
    char el, el2, max_df[2], min_dc[2], gap[2];
    MapElem *mp = (MapElem *)omp, *oth_mp = mp + wall_adds[edge];

    // get the real data, if no internal edge we go home and punt...
    if (!edge_get_fandc(mp, edge, &edge_list[0])) {
        edge_vals[0] = edge_vals[1] = edge_vals[2] = 0;
        return MEDGE_NO_TILE;
    }
    edge_get_fandc(oth_mp, (edge + 2) & 3, &edge_list[4]);

    for (el = el2 = 0; el < 4; el += 2, el2 += 4) {
        x_diff[el + 0] = edge_list[el + 5] - edge_list[el + 0];   // diff from us to it
        x_diff[el + 1] = edge_list[el + 4] - edge_list[el + 1];   //  fl,fr,cl,cr
        d_list[el + 0] = edge_list[el2 + 2] - edge_list[el2 + 0]; // height diffs in square
        d_list[el + 1] = edge_list[el2 + 3] - edge_list[el2 + 1]; //  ourl,ourr,othl,othr
    }
    max_df[0] = lg_max(edge_list[5], edge_list[0]);
    max_df[1] = lg_max(edge_list[4], edge_list[1]);
    min_dc[0] = lg_min(edge_list[7], edge_list[2]);
    min_dc[1] = lg_min(edge_list[6], edge_list[3]);
    edge_vals[0] = gap[0] = min_dc[0] - max_df[0];
    edge_vals[1] = gap[1] = min_dc[1] - max_df[1];
    edge_vals[2] = (gap[0] + gap[1]) >> 1; // sure, average, not min or max

    if ((gap[0] <= 0) && (gap[1] <= 0))
        return MEDGE_NO_EGRESS;
    else if ((x_diff[0] | x_diff[1]) == 0)
        return MEDGE_FLAT_CASE; // should scale these based on zshf
    else if ((abs(x_diff[0]) > 8) || (abs(x_diff[1]) > 8))
        return MEDGE_CLIFF_THING;
    else if ((abs(x_diff[0]) > 4) || (abs(x_diff[1]) > 4))
        return MEDGE_LARGE_STEP;
    else if ((abs(x_diff[0]) > 1) || (abs(x_diff[1]) > 1))
        return MEDGE_SMALL_STEP;
    return MEDGE_NO_TILE;
}
#endif
