code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT

This file is part of NBlood.

NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------
#pragma once

#include "compat.h"
#include "common_game.h"
#include "blood.h"
#include "inifile.h"
#include "resource.h"
#include "qav.h"

#define M_MOUSETIMEOUT 210

#define kMaxGameMenuItems 128
#define kMaxGameCycleItems 128
#define kMaxPicCycleItems 128
#define kMaxTitleLength 32

// alpha increments of 3 --> 255 / 3 = 85 --> round up to power of 2 --> 128 --> divide by 2 --> 64 alphatabs required
// use 16 anyway :P
#define MOUSEUSEALPHA (videoGetRenderMode() != REND_CLASSIC || numalphatabs >= 15)
#define MOUSEALPHA (MOUSEUSEALPHA ? clamp(((int)totalclock - gGameMenuMgr.m_mouselastactivity - 90)*3, 0, 255) : 0)
#define CURSORALPHA (MOUSEUSEALPHA ? clamp(((int)totalclock - gGameMenuMgr.m_mouselastactivity - 90)*2 + (255/3), (255/3), 255) : 255/3)
#define MOUSEACTIVECONDITION ((int)totalclock - gGameMenuMgr.m_mouselastactivity < M_MOUSETIMEOUT)
#define MOUSEACTIVECONDITIONAL(condition) (MOUSEACTIVECONDITION && (condition))
#define MOUSEINACTIVECONDITIONAL(condition) (!MOUSEACTIVECONDITION && (condition))
#define MOUSEWATCHPOINTCONDITIONAL(condition) ((condition) || gGameMenuMgr.m_mousewake_watchpoint || gGameMenuMgr.m_menuchange_watchpoint == 3)

enum {
    kMenuEventNone = 0,
    kMenuEventKey = 1,
    kMenuEventUp = 2,
    kMenuEventDown = 3,
    kMenuEventLeft = 4,
    kMenuEventRight = 5,
    kMenuEventEnter = 6,
    kMenuEventEscape = 7,
    kMenuEventSpace = 8,
    kMenuEventBackSpace = 9,
    kMenuEventDelete = 10,
    kMenuEventScrollUp = 11,
    kMenuEventScrollDown = 12,

    kMenuEventInit = 0x8000,
    kMenuEventDeInit = 0x8001
};

enum {
    kMenuSliderNone = 0,
    kMenuSliderValue,
    kMenuSliderPercent,
    kMenuSliderQ16
};

struct CGameMenuEvent {
    unsigned short at0;
    char at2;
};

// NUKE-TODO:
#ifdef DrawText
#undef DrawText
#endif

class CMenuTextMgr
{
public:
    int at0;
    CMenuTextMgr();
    void DrawText(const char *pString, int nFont, int x, int y, int nShade, int nPalette, bool shadow );
    void GetFontInfo(int nFont, const char *pString, int *pXSize, int *pYSize);
};

class CGameMenu;

class CGameMenuItem {
public:
    CGameMenu *pMenu;
    const char* m_pzText;
    int m_nFont;
    int m_nX;
    int m_nY;
    int m_nWidth;
    void (*pPreDrawCallback)(CGameMenuItem *pItem);
    //int nFlags;
    unsigned int bCanSelect : 1;
    unsigned int bEnable : 1;
    unsigned int bNoDraw : 1;
    CGameMenuItem();
    virtual ~CGameMenuItem();
    virtual void Draw(void) = 0;
    virtual bool Event(CGameMenuEvent &);
    virtual bool MouseEvent(CGameMenuEvent &);
};

class CGameMenuItemText : public CGameMenuItem
{
public:
    int at20;
    CGameMenuItemText();
    CGameMenuItemText(const char *, int, int, int, int);
    virtual void Draw(void);
    //virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemTitle : public CGameMenuItem
{
public:
    int at20;
    CGameMenuItemTitle();
    CGameMenuItemTitle(const char *, int, int, int, int);
    virtual void Draw(void);
    //virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemMultiplayerTitle : public CGameMenuItemTitle
{
public:
    CGameMenuItemMultiplayerTitle(const char *, int, int, int, int);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemZBool : public CGameMenuItem
{
public:
    bool at20;
    const char *at21;
    const char *at25;
    void (*at29)(CGameMenuItemZBool *);
    CGameMenuItemZBool();
    CGameMenuItemZBool(const char *,int,int,int,int,bool,void (*)(CGameMenuItemZBool *),const char *,const char *);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemChain : public CGameMenuItem
{
public:
    int at20;
    CGameMenu *at24;
    int at28;
    void(*at2c)(CGameMenuItemChain *);
    int at30;
    CGameMenuItemChain();
    CGameMenuItemChain(const char *, int, int, int, int, int, CGameMenu *, int, void(*)(CGameMenuItemChain *), int);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItem7EA1C : public CGameMenuItem
{
public:
    int at20; // text align
    CGameMenu *at24;
    int at28;
    void(*at2c)(CGameMenuItem7EA1C *);
    int at30;
    IniFile *at34;
    char at38[16];
    char at48[16];
    CGameMenuItem7EA1C();
    CGameMenuItem7EA1C(const char *a1, int a2, int a3, int a4, int a5, const char *a6, const char *a7, int a8, int a9, void(*a10)(CGameMenuItem7EA1C *), int a11);
    void Setup(void);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItem7EE34 : public CGameMenuItem
{
public:
    int at20;
    int at24;
    CGameMenu *at28;
    CGameMenu *at2c;
    CGameMenuItem7EE34();
    CGameMenuItem7EE34(const char *a1, int a2, int a3, int a4, int a5, int a6);
    void Setup(void);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemChain7F2F0 : public CGameMenuItemChain
{
public:
    int at34;
    CGameMenuItemChain7F2F0();
    CGameMenuItemChain7F2F0(char *a1, int a2, int a3, int a4, int a5, int a6, CGameMenu *a7, int a8, void(*a9)(CGameMenuItemChain *), int a10);
    //virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemBitmap : public CGameMenuItem
{
public:
    int at20;
    CGameMenuItemBitmap();
    CGameMenuItemBitmap(const char *, int, int, int, int);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemBitmapLS : public CGameMenuItemBitmap
{
public:
    int at24;
    int at28;
    CGameMenuItemBitmapLS();
    CGameMenuItemBitmapLS(const char *, int, int, int, int);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemKeyList : public CGameMenuItem
{
public:
    void(*pCallback)(CGameMenuItemKeyList *);
    int at24;
    int nRows;
    int nTopDelta;
    int nFocus;
    int nGameFuncs;
    bool bScan;
    CGameMenuItemKeyList();
    CGameMenuItemKeyList(const char * a1, int a2, int a3, int a4, int a5, int a6, int a7, void(*a8)(CGameMenuItemKeyList *));
    void Scan(void);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
    virtual bool MouseEvent(CGameMenuEvent &);
};

class CGameMenuItemSlider : public CGameMenuItem
{
public:
    int *pValue;
    int nValue;
    int nRangeLow;
    int nRangeHigh;
    int nStep;
    void(*pCallback)(CGameMenuItemSlider *);
    int nSliderTile;
    int nCursorTile;
    int nShowValue;
    CGameMenuItemSlider();
    CGameMenuItemSlider(const char *_pzText, int _nFont, int _nX, int _nY, int _nWidth, int _nValue, int _nRangeLow, int _nRangeHigh, int _nStep, void(*_pCallback)(CGameMenuItemSlider *), int _nSliderTile, int _nCursorTile, int _nShowValue = kMenuSliderNone);
    CGameMenuItemSlider(const char *_pzText, int _nFont, int _nX, int _nY, int _nWidth, int *pnValue, int _nRangeLow, int _nRangeHigh, int _nStep, void(*_pCallback)(CGameMenuItemSlider *), int _nSliderTile, int _nCursorTile, int _nShowValue = kMenuSliderNone);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
    virtual bool MouseEvent(CGameMenuEvent &);
};

class CGameMenuItemSliderFloat : public CGameMenuItem
{
public:
    float *pValue;
    float fValue;
    float fRangeLow;
    float fRangeHigh;
    float fStep;
    void(*pCallback)(CGameMenuItemSliderFloat *);
    int nSliderTile;
    int nCursorTile;
    int nShowValue;
    CGameMenuItemSliderFloat();
    CGameMenuItemSliderFloat(const char *_pzText, int _nFont, int _nX, int _nY, int _nWidth, float _fValue, float _fRangeLow, float _fRangeHigh, float _fStep, void(*_pCallback)(CGameMenuItemSliderFloat *), int _nSliderTile, int _nCursorTile, int _nShowValue = kMenuSliderNone);
    CGameMenuItemSliderFloat(const char *_pzText, int _nFont, int _nX, int _nY, int _nWidth, float *pnValue, float _fRangeLow, float _fRangeHigh, float _fStep, void(*_pCallback)(CGameMenuItemSliderFloat *), int _nSliderTile, int _nCursorTile, int _nShowValue = kMenuSliderNone);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemZEdit : public CGameMenuItem
{
public:
    char *at20;
    int at24;
    int at28;
    void(*at2c)(CGameMenuItemZEdit *, CGameMenuEvent *);
    char at30;
    char at31;
    char at32;
    CGameMenuItemZEdit();
    CGameMenuItemZEdit(const char *, int, int, int, int, char *, int, char, void(*)(CGameMenuItemZEdit *, CGameMenuEvent *), int);
    void AddChar(char);
    void BackChar(void);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemZEditBitmap : public CGameMenuItem
{
public:
    char *at20;
    int at24;
    int at28; // save game slot
    CGameMenuItemBitmapLS *at2c;
    void(*at30)(CGameMenuItemZEditBitmap *, CGameMenuEvent *);
    char bScan;
    char at35;
    char at36;
    char at37;
    CGameMenuItemZEditBitmap();
    CGameMenuItemZEditBitmap(char *, int, int, int, int, char *, int, char, void(*)(CGameMenuItemZEditBitmap *, CGameMenuEvent *), int);
    void AddChar(char);
    void BackChar(void);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemQAV : public CGameMenuItem
{
public:
    const char *at20;
    DICTNODE *at24;
    QAV *at28;
    int at2c;
    int at30;
    bool bWideScreen;
    bool bClearBackground;
    CGameMenuItemQAV();
    CGameMenuItemQAV(const char *, int, int, int, const char *, bool widescreen = false, bool clearbackground = false);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
    void Reset(void);
};

class CGameMenuItemZCycleSelect : public CGameMenuItem
{
public:
    void(*m_pCallback)(CGameMenuItemZCycleSelect *);
    int m_nRows;
    int m_nTopDelta;
    int m_nFocus;
    int m_nItems;
    int *m_pReturn;
    const char **m_pzStrings;
    CGameMenuItemZCycleSelect();
    CGameMenuItemZCycleSelect(const char *pzText, int nFont, int nX, int nY, int nWidth, int nRows, int nItems, const char **pzStrings, int *pReturn, void(*pCallback)(CGameMenuItemZCycleSelect *));
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
    virtual bool MouseEvent(CGameMenuEvent &);
};

class CGameMenuItemZCycle : public CGameMenuItem
{
public:
    int m_nItems;
    int m_nFocus;
    int m_nAlign;
    const char *m_pzStrings[kMaxGameCycleItems];
    char m_zTitle[kMaxTitleLength];
    void(*m_pCallback)(CGameMenuItemZCycle *);
    void(*m_pCallbackSelect)(CGameMenuItemZCycleSelect *);
    bool m_bMenu;
    int m_nMenuSelectReturn;
    CGameMenu *m_pMenuSelect;
    CGameMenuItemTitle *m_pItemSelectTitle;
    CGameMenuItemZCycleSelect *m_pItemSelect;
    CGameMenuItemZCycle();
    CGameMenuItemZCycle(const char *, int, int, int, int, int, void(*)(CGameMenuItemZCycle *), const char **, int, int, bool = false, void(*)(CGameMenuItemZCycleSelect*) = NULL);
    ~CGameMenuItemZCycle();
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
    void Add(const char *, bool);
    void Next(void);
    void Prev(void);
    void Clear(void);
    void SetTextArray(const char **, int, int);
    void SetTextIndex(int);
};

class CGameMenuItemYesNoQuit : public CGameMenuItem
{
public:
    int at20;
    int m_nRestart;
    CGameMenuItemYesNoQuit();
    CGameMenuItemYesNoQuit(const char *, int, int, int, int, int, int);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuItemPicCycle : public CGameMenuItem
{
public:
    int m_nItems;
    int at24;
    int at28;
    int at2c;
    int at30[kMaxPicCycleItems];
    void(*atb0)(CGameMenuItemPicCycle *);
    int atb4;
    CGameMenuItemPicCycle();
    CGameMenuItemPicCycle(int, int, void(*)(CGameMenuItemPicCycle *), int *, int, int);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
    void Add(int, bool);
    void Next(void);
    void Prev(void);
    void Clear(void);
    void SetPicArray(int *, int, int);
    void SetPicIndex(int);
};

class CGameMenuItemPassword : public CGameMenuItem
{
public:
    char at20[9];
    char at29[9];
    int at32;
    char at36;
    int at37;
    char at3b[32];
    int at5b;
    CGameMenuItemZBool *at5f;
    CGameMenuItemPassword();
    CGameMenuItemPassword(const char *, int, int, int);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent &);
};

class CGameMenuFileSelect : public CGameMenuItem
{
private:
    const char *startdir;
    const char *pattern;
    char *destination;
    void (*onFileSelectedEventHandler)();
    char doPop;
    BUILDVFS_FIND_REC *findhigh[2];
    int32_t nTopDelta[2];
    fnlist_t fnlist;
    int32_t currentList;
    bool Select(void);
    void FileSelectInit(void);
    void MovementVerify(void);
    void RemoveFilenameFromDestination(void);
    void SetDestinationToParentDir(void);
public:
    CGameMenuFileSelect(const char *_pzText, int _nFont, int _x, int _y, int _nWidth, const char *_startdir, const char *_pattern, char *_destination, void(*)() = nullptr, const char doPop = 1);
    virtual void Draw(void);
    virtual bool Event(CGameMenuEvent&);
    virtual bool MouseEvent(CGameMenuEvent &);
};

class CGameMenu
{
public:
    int m_nItems;
    int m_nFocus;
    int at8;
    char atc;
    CGameMenuItem *pItemList[kMaxGameMenuItems]; // atd
    CGameMenu();
    CGameMenu(int);
    ~CGameMenu();
    void InitializeItems(CGameMenuEvent &event);
    void Draw(void);
    bool Event(CGameMenuEvent &event);
    void Add(CGameMenuItem *pItem, bool active);
    void SetFocusItem(int nItem);
    void SetFocusItem(CGameMenuItem *Item);
    bool CanSelectItem(int nItem);
    void FocusPrevItem(void);
    void FocusNextItem(void);
    bool IsFocusItem(CGameMenuItem *pItem);
    bool MouseEvent(CGameMenuEvent &event);
};

class CGameMenuMgr
{
public:
    static bool m_bInitialized;
    static bool m_bActive;
    static bool m_bScanning;
    CGameMenu *pTempMenu;
    CGameMenu *pActiveMenu;
    CGameMenu *pMenuStack[8];
    int nMenuPointer;
    int32_t m_mouselastactivity;
    int32_t m_mousewake_watchpoint, m_menuchange_watchpoint;
    int32_t m_mousecaught;
    vec2_t m_prevmousepos, m_mousepos, m_mousedownpos;
    bool m_postPop;
    CGameMenuMgr();
    ~CGameMenuMgr();
    void InitializeMenu(void);
    void DeInitializeMenu(void);
    bool Push(CGameMenu *pMenu, int data);
    void Pop(void);
    void PostPop(void);
    void Draw(void);
    void Clear(void);
    void Process(void);
    void Deactivate(void);
    bool MouseOutsideBounds(vec2_t const * const pos, const int32_t x, const int32_t y, const int32_t width, const int32_t height);
};

extern CMenuTextMgr gMenuTextMgr;
extern CGameMenuMgr gGameMenuMgr;

repo_name: orochi/nblood

license: mit


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT

This file is part of NBlood.

NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "build.h"
#include "common_game.h"

#include "actor.h"
#include "db.h"
#include "gameutil.h"
#include "globals.h"
#include "tile.h"
#include "trig.h"

POINT2D baseWall[kMaxWalls];
POINT3D baseSprite[kMaxSprites];
int baseFloor[kMaxSectors];
int baseCeil[kMaxSectors];
int velFloor[kMaxSectors];
int velCeil[kMaxSectors];
short gUpperLink[kMaxSectors];
short gLowerLink[kMaxSectors];
HITINFO gHitInfo;

bool AreSectorsNeighbors(int sect1, int sect2)
{
    dassert(sect1 >= 0 && sect1 < kMaxSectors);
    dassert(sect2 >= 0 && sect2 < kMaxSectors);
    if (sector[sect1].wallnum < sector[sect2].wallnum)
    {
        for (int i = 0; i < sector[sect1].wallnum; i++)
        {
            if (wall[sector[sect1].wallptr+i].nextsector == sect2)
            {
                return 1;
            }
        }
    }
    else
    {
        for (int i = 0; i < sector[sect2].wallnum; i++)
        {
            if (wall[sector[sect2].wallptr+i].nextsector == sect1)
            {
                return 1;
            }
        }
    }
    return 0;
}

bool FindSector(int nX, int nY, int nZ, int *nSector)
{
    int32_t nZFloor, nZCeil;
    dassert(*nSector >= 0 && *nSector < kMaxSectors);
    if (inside(nX, nY, *nSector))
    {
        getzsofslope(*nSector, nX, nY, &nZCeil, &nZFloor);
        if (nZ >= nZCeil && nZ <= nZFloor)
        {
            return 1;
        }
    }
    walltype *pWall = &wall[sector[*nSector].wallptr];
    for (int i = sector[*nSector].wallnum; i > 0; i--, pWall++)
    {
        int nOSector = pWall->nextsector;
        if (nOSector >= 0 && inside(nX, nY, nOSector))
        {
            getzsofslope(nOSector, nX, nY, &nZCeil, &nZFloor);
            if (nZ >= nZCeil && nZ <= nZFloor)
            {
                *nSector = nOSector;
                return 1;
            }
        }
    }
    for (int i = 0; i < numsectors; i++)
    {
        if (inside(nX, nY, i))
        {
            getzsofslope(i, nX, nY, &nZCeil, &nZFloor);
            if (nZ >= nZCeil && nZ <= nZFloor)
            {
                *nSector = i;
                return 1;
            }
        }
    }
    return 0;
}

bool FindSector(int nX, int nY, int *nSector)
{
    dassert(*nSector >= 0 && *nSector < kMaxSectors);
    if (inside(nX, nY, *nSector))
    {
        return 1;
    }
    walltype *pWall = &wall[sector[*nSector].wallptr];
    for (int i = sector[*nSector].wallnum; i > 0; i--, pWall++)
    {
        int nOSector = pWall->nextsector;
        if (nOSector >= 0 && inside(nX, nY, nOSector))
        {
            *nSector = nOSector;
            return 1;
        }
    }
    for (int i = 0; i < numsectors; i++)
    {
        if (inside(nX, nY, i))
        {
            *nSector = i;
            return 1;
        }
    }
    return 0;
}

void CalcFrameRate(void)
{
    static int ticks[64];
    static int index;
    if (ticks[index] != gFrameClock)
    {
        gFrameRate = (120*64)/((int)gFrameClock-ticks[index]);
        ticks[index] = (int)gFrameClock;
    }
    index = (index+1) & 63;
}

bool CheckProximity(spritetype *pSprite, int nX, int nY, int nZ, int nSector, int nDist)
{
    dassert(pSprite != NULL);
    int oX = klabs(nX-pSprite->x)>>4;
    if (oX >= nDist) return 0;

    int oY = klabs(nY-pSprite->y)>>4;
    if (oY >= nDist) return 0;

    int oZ = klabs(nZ-pSprite->z)>>8;
    if (oZ >= nDist) return 0;

    if (approxDist(oX, oY) >= nDist) return 0;

    int bottom, top;
    GetSpriteExtents(pSprite, &top, &bottom);
    if (cansee(pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, nX, nY, nZ, nSector))
        return 1;
    if (cansee(pSprite->x, pSprite->y, bottom, pSprite->sectnum, nX, nY, nZ, nSector))
        return 1;
    if (cansee(pSprite->x, pSprite->y, top, pSprite->sectnum, nX, nY, nZ, nSector))
        return 1;
    return 0;
}

bool CheckProximityPoint(int nX1, int nY1, int nZ1, int nX2, int nY2, int nZ2, int nDist)
{
    int oX = klabs(nX2-nX1)>>4;
    if (oX >= nDist)
        return 0;
    int oY = klabs(nY2-nY1)>>4;
    if (oY >= nDist)
        return 0;
    if (nZ2 != nZ1)
    {
        int oZ = klabs(nZ2-nZ1)>>8;
        if (oZ >= nDist)
            return 0;
    }
    if (approxDist(oX, oY) >= nDist) return 0;
    return 1;
}

bool CheckProximityWall(int nWall, int x, int y, int nDist)
{
    int x1 = wall[nWall].x;
    int y1 = wall[nWall].y;
    int x2 = wall[wall[nWall].point2].x;
    int y2 = wall[wall[nWall].point2].y;
    nDist <<= 4;
    if (x1 < x2)
    {
        if (x <= x1 - nDist || x >= x2 + nDist)
        {
            return 0;
        }
    }
    else
    {
        if (x <= x2 - nDist || x >= x1 + nDist)
        {
            return 0;
        }
        if (x1 == x2)
        {
            int px1 = x - x1;
            int py1 = y - y1;
            int px2 = x - x2;
            int py2 = y - y2;
            int dist1 = px1 * px1 + py1 * py1;
            int dist2 = px2 * px2 + py2 * py2;
            if (y1 < y2)
            {
                if (y <= y1 - nDist || y >= y2 + nDist)
                {
                    return 0;
                }
                if (y < y1)
                {
                    return dist1 < nDist * nDist;
                }
                if (y > y2)
                {
                    return dist2 < nDist * nDist;
                }
            }
            else
            {
                if (y <= y2 - nDist || y >= y1 + nDist)
                {
                    return 0;
                }
                if (y < y2)
                {
                    return dist2 < nDist * nDist;
                }
                if (y > y1)
                {
                    return dist1 < nDist * nDist;
                }
            }
            return 1;
        }
    }
    if (y1 < y2)
    {
        if (y <= y1 - nDist || y >= y2 + nDist)
        {
            return 0;
        }
    }
    else
    {
        if (y <= y2 - nDist || y >= y1 + nDist)
        {
            return 0;
        }
        if (y1 == y2)
        {
            int px1 = x - x1;
            int py1 = y - y1;
            int px2 = x - x2;
            int py2 = y - y2;
            int check1 = px1 * px1 + py1 * py1;
            int check2 = px2 * px2 + py2 * py2;
            if (x1 < x2)
            {
                if (x <= x1 - nDist || x >= x2 + nDist)
                {
                    return 0;
                }
                if (x < x1)
                {
                    return check1 < nDist * nDist;
                }
                if (x > x2)
                {
                    return check2 < nDist * nDist;
                }
            }
            else
            {
                if (x <= x2 - nDist || x >= x1 + nDist)
                {
                    return 0;
                }
                if (x < x2)
                {
                    return check2 < nDist * nDist;
                }
                if (x > x1)
                {
                    return check1 < nDist * nDist;
                }
            }
        }
    }

    int dx = x2 - x1;
    int dy = y2 - y1;
    int px = x - x2;
    int py = y - y2;
    int side = px * dx + dy * py;
    if (side >= 0)
    {
        return px * px + py * py < nDist * nDist;
    }
    px = x - x1;
    py = y - y1;
    side = px * dx + dy * py;
    if (side <= 0)
    {
        return px * px + py * py < nDist * nDist;
    }
    int check1 = px * dy - dx * py;
    int check2 = dy * dy + dx * dx;
    return check1 * check1 < check2 * nDist * nDist;
}

int GetWallAngle(int nWall)
{
    int nWall2 = wall[nWall].point2;
    return getangle(wall[nWall2].x - wall[nWall].x, wall[nWall2].y - wall[nWall].y);
}

void GetWallNormal(int nWall, int *pX, int *pY)
{
    dassert(nWall >= 0 && nWall < kMaxWalls);
    int nWall2 = wall[nWall].point2;
    int dX = -(wall[nWall2].y - wall[nWall].y);
    dX >>= 4;
    int dY = wall[nWall2].x - wall[nWall].x;
    dY >>= 4;
    int nLength = ksqrt(dX*dX+dY*dY);
    if (nLength <= 0)
        nLength = 1;
    *pX = divscale16(dX, nLength);
    *pY = divscale16(dY, nLength);
}

bool IntersectRay(int wx, int wy, int wdx, int wdy, int x1, int y1, int z1, int x2, int y2, int z2, int *ix, int *iy, int *iz)
{
    int dX = x1 - x2;
    int dY = y1 - y2;
    int dZ = z1 - z2;
    int side = wdx * dY - wdy * dX;
    int dX2 = x1 - wx;
    int dY2 = y1 - wy;
    int check1 = dX2 * dY - dY2 * dX;
    int check2 = wdx * dY2 - wdy * dX2;
    if (side >= 0)
    {
        if (!side)
            return 0;
        if (check1 < 0)
            return 0;
        if (check2 < 0 || check2 >= side)
            return 0;
    }
    else
    {
        if (check1 > 0)
            return 0;
        if (check2 > 0 || check2 <= side)
            return 0;
    }
    int nScale = divscale16(check2, side);
    *ix = x1 + mulscale16(dX, nScale);
    *iy = y1 + mulscale16(dY, nScale);
    *iz = z1 + mulscale16(dZ, nScale);
    return 1;
}

int HitScan(spritetype *pSprite, int z, int dx, int dy, int dz, unsigned int nMask, int nRange)
{
    dassert(pSprite != NULL);
    dassert(dx != 0 || dy != 0);
    gHitInfo.hitsect = -1;
    gHitInfo.hitwall = -1;
    gHitInfo.hitsprite = -1;
    int x = pSprite->x;
    int y = pSprite->y;
    int nSector = pSprite->sectnum;
    int bakCstat = pSprite->cstat;
    pSprite->cstat &= ~256;
    if (nRange)
    {
        hitscangoal.x = x + mulscale30(nRange << 4, Cos(pSprite->ang));
        hitscangoal.y = y + mulscale30(nRange << 4, Sin(pSprite->ang));
    }
    else
    {
        hitscangoal.x = hitscangoal.y = 0x1fffffff;
    }
    vec3_t pos = { x, y, z };
    hitdata_t hitData;
    hitData.xyz.z = gHitInfo.hitz;
    hitscan(&pos, nSector, dx, dy, dz << 4, &hitData, nMask);
    gHitInfo.hitsect = hitData.sect;
    gHitInfo.hitwall = hitData.wall;
    gHitInfo.hitsprite = hitData.sprite;
    gHitInfo.hitx = hitData.xyz.x;
    gHitInfo.hity = hitData.xyz.y;
    gHitInfo.hitz = hitData.xyz.z;
    hitscangoal.x = hitscangoal.y = 0x1fffffff;
    pSprite->cstat = bakCstat;
    if (gHitInfo.hitsprite >= kMaxSprites || gHitInfo.hitwall >= kMaxWalls || gHitInfo.hitsect >= kMaxSectors)
        return -1;
    if (gHitInfo.hitsprite >= 0)
        return 3;
    if (gHitInfo.hitwall >= 0)
    {
        if (wall[gHitInfo.hitwall].nextsector == -1)
            return 0;
        int nZCeil, nZFloor;
        getzsofslope(wall[gHitInfo.hitwall].nextsector, gHitInfo.hitx, gHitInfo.hity, &nZCeil, &nZFloor);
        if (gHitInfo.hitz <= nZCeil || gHitInfo.hitz >= nZFloor)
            return 0;
        return 4;
    }
    if (gHitInfo.hitsect >= 0)
        return 1 + (z < gHitInfo.hitz);
    return -1;
}

int VectorScan(spritetype *pSprite, int nOffset, int nZOffset, int dx, int dy, int dz, int nRange, int ac)
{
    int nNum = 256;
    dassert(pSprite != NULL);
    gHitInfo.hitsect = -1;
    gHitInfo.hitwall = -1;
    gHitInfo.hitsprite = -1;
    int x1 = pSprite->x+mulscale30(nOffset, Cos(pSprite->ang+512));
    int y1 = pSprite->y+mulscale30(nOffset, Sin(pSprite->ang+512));
    int z1 = pSprite->z+nZOffset;
    int bakCstat = pSprite->cstat;
    pSprite->cstat &= ~256;
    int nSector = pSprite->sectnum;
    if (nRange)
    {
        hitscangoal.x = x1+mulscale30(nRange<<4, Cos(pSprite->ang));
        hitscangoal.y = y1+mulscale30(nRange<<4, Sin(pSprite->ang));
    }
    else
    {
        hitscangoal.x = hitscangoal.y = 0x1fffffff;
    }
    vec3_t pos = { x1, y1, z1 };
    hitdata_t hitData;
    hitData.xyz.z = gHitInfo.hitz;
    hitscan(&pos, nSector, dx, dy, dz << 4, &hitData, CLIPMASK1);
    gHitInfo.hitsect = hitData.sect;
    gHitInfo.hitwall = hitData.wall;
    gHitInfo.hitsprite = hitData.sprite;
    gHitInfo.hitx = hitData.xyz.x;
    gHitInfo.hity = hitData.xyz.y;
    gHitInfo.hitz = hitData.xyz.z;
    hitscangoal.x = hitscangoal.y = 0x1ffffff;
    pSprite->cstat = bakCstat;
    while (nNum--)
    {
        if (gHitInfo.hitsprite >= kMaxSprites || gHitInfo.hitwall >= kMaxWalls || gHitInfo.hitsect >= kMaxSectors)
            return -1;
        if (nRange && approxDist(gHitInfo.hitx - pSprite->x, gHitInfo.hity - pSprite->y) > nRange)
            return -1;
        if (gHitInfo.hitsprite >= 0)
        {
            spritetype *pOther = &sprite[gHitInfo.hitsprite];
            if ((pOther->flags & 8) && !(ac & 1))
                return 3;
            if ((pOther->cstat & 0x30) != 0)
                return 3;
            int nPicnum = pOther->picnum;
            if (tilesiz[nPicnum].x == 0 || tilesiz[nPicnum].y == 0)
                return 3;
            int height = (tilesiz[nPicnum].y*pOther->yrepeat)<<2;
            int otherZ = pOther->z;
            if (pOther->cstat & 0x80)
                otherZ += height / 2;
            int nOffset = picanm[nPicnum].yofs;
            if (nOffset)
                otherZ -= (nOffset*pOther->yrepeat)<<2;
            dassert(height > 0);
            int height2 = scale(otherZ-gHitInfo.hitz, tilesiz[nPicnum].y, height);
            if (!(pOther->cstat & 8))
                height2 = tilesiz[nPicnum].y-height2;
            if (height2 >= 0 && height2 < tilesiz[nPicnum].y)
            {
                int width = (tilesiz[nPicnum].x*pOther->xrepeat)>>2;
                width = (width*3)/4;
                int check1 = ((y1 - pOther->y)*dx - (x1 - pOther->x)*dy) / ksqrt(dx*dx+dy*dy);
                dassert(width > 0);
                int width2 = scale(check1, tilesiz[nPicnum].x, width);
                int nOffset = picanm[nPicnum].xofs;
                width2 += nOffset + tilesiz[nPicnum].x / 2;
                if (width2 >= 0 && width2 < tilesiz[nPicnum].x)
                {
                    char *pData = tileLoadTile(nPicnum);
                    if (pData[width2*tilesiz[nPicnum].y+height2] != (char)255)
                        return 3;
                }
            }
            int bakCstat = pOther->cstat;
            pOther->cstat &= ~256;
            gHitInfo.hitsect = -1;
            gHitInfo.hitwall = -1;
            gHitInfo.hitsprite = -1;
            x1 = gHitInfo.hitx;
            y1 = gHitInfo.hity;
            z1 = gHitInfo.hitz;
            pos = { x1, y1, z1 };
            hitData.xyz.z = gHitInfo.hitz;
            hitscan(&pos, pOther->sectnum,
                dx, dy, dz << 4, &hitData, CLIPMASK1);
            gHitInfo.hitsect = hitData.sect;
            gHitInfo.hitwall = hitData.wall;
            gHitInfo.hitsprite = hitData.sprite;
            gHitInfo.hitx = hitData.xyz.x;
            gHitInfo.hity = hitData.xyz.y;
            gHitInfo.hitz = hitData.xyz.z;
            pOther->cstat = bakCstat;
            continue;
        }
        if (gHitInfo.hitwall >= 0)
        {
            walltype *pWall = &wall[gHitInfo.hitwall];
            if (pWall->nextsector == -1)
                return 0;
            sectortype *pSector = &sector[gHitInfo.hitsect];
            sectortype *pSectorNext = &sector[pWall->nextsector];
            int nZCeil, nZFloor;
            getzsofslope(pWall->nextsector, gHitInfo.hitx, gHitInfo.hity, &nZCeil, &nZFloor);
            if (gHitInfo.hitz <= nZCeil)
                return 0;
            if (gHitInfo.hitz >= nZFloor)
            {
                if (!(pSector->floorstat&1) || !(pSectorNext->floorstat&1))
                    return 0;
                return 2;
            }
            if (!(pWall->cstat & 0x30))
                return 0;
            int nOffset;
            if (pWall->cstat & 4)
                nOffset = ClipHigh(pSector->floorz, pSectorNext->floorz);
            else
                nOffset = ClipLow(pSector->ceilingz, pSectorNext->ceilingz);
            nOffset = (gHitInfo.hitz - nOffset) >> 8;
            if (pWall->cstat & 256)
                nOffset = -nOffset;

            int nPicnum = pWall->overpicnum;
            int nSizX = tilesiz[nPicnum].x;
            int nSizY = tilesiz[nPicnum].y;
            if (!nSizX || !nSizY)
                return 0;

            int potX = nSizX == (1<<(picsiz[nPicnum]&15));
            int potY = nSizY == (1<<(picsiz[nPicnum]>>4));

            nOffset = (nOffset*pWall->yrepeat) / 8;
            nOffset += (nSizY*pWall->ypanning) / 256;
            int nLength = approxDist(pWall->x - wall[pWall->point2].x, pWall->y - wall[pWall->point2].y);
            int nHOffset;
            if (pWall->cstat & 8)
                nHOffset = approxDist(gHitInfo.hitx - wall[pWall->point2].x, gHitInfo.hity - wall[pWall->point2].y);
            else
                nHOffset = approxDist(gHitInfo.hitx - pWall->x, gHitInfo.hity - pWall->y);

            nHOffset = pWall->xpanning + ((nHOffset*pWall->xrepeat) << 3) / nLength;
            if (potX)
                nHOffset &= nSizX - 1;
            else
                nHOffset %= nSizX;
            if (potY)
                nOffset &= nSizY - 1;
            else
                nOffset %= nSizY;
            char *pData = tileLoadTile(nPicnum);
            int nPixel;
            if (potY)
                nPixel = (nHOffset<<(picsiz[nPicnum]>>4)) + nOffset;
            else
                nPixel = nHOffset*nSizY + nOffset;

            if (pData[nPixel] == (char)255)
            {
                int bakCstat = pWall->cstat;
                pWall->cstat &= ~64;
                int bakCstat2 = wall[pWall->nextwall].cstat;
                wall[pWall->nextwall].cstat &= ~64;
                gHitInfo.hitsect = -1;
                gHitInfo.hitwall = -1;
                gHitInfo.hitsprite = -1;
                x1 = gHitInfo.hitx;
                y1 = gHitInfo.hity;
                z1 = gHitInfo.hitz;
                pos = { x1, y1, z1 };
                hitData.xyz.z = gHitInfo.hitz;
                hitscan(&pos, pWall->nextsector,
                    dx, dy, dz << 4, &hitData, CLIPMASK1);
                gHitInfo.hitsect = hitData.sect;
                gHitInfo.hitwall = hitData.wall;
                gHitInfo.hitsprite = hitData.sprite;
                gHitInfo.hitx = hitData.xyz.x;
                gHitInfo.hity = hitData.xyz.y;
                gHitInfo.hitz = hitData.xyz.z;
                pWall->cstat = bakCstat;
                wall[pWall->nextwall].cstat = bakCstat2;
                continue;
            }
            return 4;
        }
        if (gHitInfo.hitsect >= 0)
        {
            int nSprite, nLink;
            if (dz > 0)
            {
                nSprite = gUpperLink[gHitInfo.hitsect];
                if (nSprite < 0)
                    return 2;
            }
            else
            {
                nSprite = gLowerLink[gHitInfo.hitsect];
                if (nSprite < 0)
                    return 1;
            }
            nLink = sprite[nSprite].owner & 0x3fff;
            gHitInfo.hitsect = gHitInfo.hitwall = gHitInfo.hitsprite = -1;
            x1 = gHitInfo.hitx + sprite[nLink].x - sprite[nSprite].x;
            y1 = gHitInfo.hity + sprite[nLink].y - sprite[nSprite].y;
            z1 = gHitInfo.hitz + sprite[nLink].z - sprite[nSprite].z;
            pos = { x1, y1, z1 };
            hitData.xyz.z = gHitInfo.hitz;
            hitscan(&pos, sprite[nLink].sectnum,
                dx, dy, dz << 4, &hitData, CLIPMASK1);
            gHitInfo.hitsect = hitData.sect;
            gHitInfo.hitwall = hitData.wall;
            gHitInfo.hitsprite = hitData.sprite;
            gHitInfo.hitx = hitData.xyz.x;
            gHitInfo.hity = hitData.xyz.y;
            gHitInfo.hitz = hitData.xyz.z;
            continue;
        }
        return -1;
    }
    return -1;
}

void GetZRange(spritetype *pSprite, int *ceilZ, int *ceilHit, int *floorZ, int *floorHit, int nDist, unsigned int nMask, unsigned int nClipParallax)
{
    dassert(pSprite != NULL);
    int bakCstat = pSprite->cstat;
    int32_t nTemp1, nTemp2;
    pSprite->cstat &= ~257;
    getzrange_old(pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, (int32_t*)ceilZ, (int32_t*)ceilHit, (int32_t*)floorZ, (int32_t*)floorHit, nDist, nMask);
    if (((*floorHit) & 0xc000) == 0x4000)
    {
        int nSector = (*floorHit) & 0x3fff;
        if ((nClipParallax & PARALLAXCLIP_FLOOR) == 0 && (sector[nSector].floorstat & 1))
            *floorZ = 0x7fffffff;
        if (sector[nSector].extra > 0)
        {
            XSECTOR *pXSector = &xsector[sector[nSector].extra];
            *floorZ += pXSector->Depth << 10;
        }
        if (gUpperLink[nSector] >= 0)
        {
            int nSprite = gUpperLink[nSector];
            int nLink = sprite[nSprite].owner & 0x3fff;
            getzrange_old(pSprite->x+sprite[nLink].x-sprite[nSprite].x, pSprite->y+sprite[nLink].y-sprite[nSprite].y,
                pSprite->z+sprite[nLink].z-sprite[nSprite].z, sprite[nLink].sectnum, &nTemp1, &nTemp2, (int32_t*)floorZ, (int32_t*)floorHit,
                nDist, nMask);
            *floorZ -= sprite[nLink].z - sprite[nSprite].z;
        }
    }
    if (((*ceilHit) & 0xc000) == 0x4000)
    {
        int nSector = (*ceilHit) & 0x3fff;
        if ((nClipParallax & PARALLAXCLIP_CEILING) == 0 && (sector[nSector].ceilingstat & 1))
            *ceilZ = 0x80000000;
        if (gLowerLink[nSector] >= 0)
        {
            int nSprite = gLowerLink[nSector];
            int nLink = sprite[nSprite].owner & 0x3fff;
            getzrange_old(pSprite->x+sprite[nLink].x-sprite[nSprite].x, pSprite->y+sprite[nLink].y-sprite[nSprite].y,
                pSprite->z+sprite[nLink].z-sprite[nSprite].z, sprite[nLink].sectnum, (int32_t*)ceilZ, (int32_t*)ceilHit, &nTemp1, &nTemp2,
                nDist, nMask);
            *ceilZ -= sprite[nLink].z - sprite[nSprite].z;
        }
    }
    pSprite->cstat = bakCstat;
}

void GetZRangeAtXYZ(int x, int y, int z, int nSector, int *ceilZ, int *ceilHit, int *floorZ, int *floorHit, int nDist, unsigned int nMask, unsigned int nClipParallax)
{
    int32_t nTemp1, nTemp2;
    getzrange_old(x, y, z, nSector, (int32_t*)ceilZ, (int32_t*)ceilHit, (int32_t*)floorZ, (int32_t*)floorHit, nDist, nMask);
    if (((*floorHit) & 0xc000) == 0x4000)
    {
        int nSector = (*floorHit) & 0x3fff;
        if ((nClipParallax & PARALLAXCLIP_FLOOR) == 0 && (sector[nSector].floorstat & 1))
            *floorZ = 0x7fffffff;
        if (sector[nSector].extra > 0)
        {
            XSECTOR *pXSector = &xsector[sector[nSector].extra];
            *floorZ += pXSector->Depth << 10;
        }
        if (gUpperLink[nSector] >= 0)
        {
            int nSprite = gUpperLink[nSector];
            int nLink = sprite[nSprite].owner & 0x3fff;
            getzrange_old(x+sprite[nLink].x-sprite[nSprite].x, y+sprite[nLink].y-sprite[nSprite].y,
                z+sprite[nLink].z-sprite[nSprite].z, sprite[nLink].sectnum, &nTemp1, &nTemp2, (int32_t*)floorZ, (int32_t*)floorHit,
                nDist, nMask);
            *floorZ -= sprite[nLink].z - sprite[nSprite].z;
        }
    }
    if (((*ceilHit) & 0xc000) == 0x4000)
    {
        int nSector = (*ceilHit) & 0x3fff;
        if ((nClipParallax & PARALLAXCLIP_CEILING) == 0 && (sector[nSector].ceilingstat & 1))
            *ceilZ = 0x80000000;
        if (gLowerLink[nSector] >= 0)
        {
            int nSprite = gLowerLink[nSector];
            int nLink = sprite[nSprite].owner & 0x3fff;
            getzrange_old(x+sprite[nLink].x-sprite[nSprite].x, y+sprite[nLink].y-sprite[nSprite].y,
                z+sprite[nLink].z-sprite[nSprite].z, sprite[nLink].sectnum, (int32_t*)ceilZ, (int32_t*)ceilHit, &nTemp1, &nTemp2,
                nDist, nMask);
            *ceilZ -= sprite[nLink].z - sprite[nSprite].z;
        }
    }
}

int GetDistToLine(int x1, int y1, int x2, int y2, int x3, int y3)
{
    int check = (y1-y3)*(x3-x2);
    int check2 = (x1-x2)*(y3-y2);
    if (check2 > check)
        return -1;
    int v8 = dmulscale4(x1-x2,x3-x2,y1-y3,y3-y2);
    int vv = dmulscale4(x3-x2,x3-x2,y3-y2,y3-y2);
    int t1, t2;
    if (v8 <= 0)
    {
        t1 = x2;
        t2 = y2;
    }
    else if (vv > v8)
    {
        t1 = x2+scale(x3-x2,v8,vv);
        t2 = y2+scale(y3-y2,v8,vv);
    }
    else
    {
        t1 = x3;
        t2 = y3;
    }
    return approxDist(t1-x1, t2-y1);
}

unsigned int ClipMove(int *x, int *y, int *z, int *nSector, int xv, int yv, int wd, int cd, int fd, unsigned int nMask)
{
    int bakX = *x;
    int bakY = *y;
    int bakZ = *z;
    short bakSect = *nSector;
    unsigned int nRes = clipmove_old((int32_t*)x, (int32_t*)y, (int32_t*)z, &bakSect, xv<<14, yv<<14, wd, cd, fd, nMask);
    if (bakSect == -1)
    {
        *x = bakX; *y = bakY; *z = bakZ;
    }
    else
    {
        *nSector = bakSect;
    }
    return nRes;
}

int GetClosestSectors(int nSector, int x, int y, int nDist, short *pSectors, char *pSectBit)
{
    char sectbits[(kMaxSectors+7)>>3];
    dassert(pSectors != NULL);
    memset(sectbits, 0, sizeof(sectbits));
    pSectors[0] = nSector;
    SetBitString(sectbits, nSector);
    int n = 1;
    int i = 0;
    if (pSectBit)
    {
        memset(pSectBit, 0, (kMaxSectors+7)>>3);
        SetBitString(pSectBit, nSector);
    }
    while (i < n)
    {
        int nCurSector = pSectors[i];
        int nStartWall = sector[nCurSector].wallptr;
        int nEndWall = nStartWall + sector[nCurSector].wallnum;
        walltype *pWall = &wall[nStartWall];
        for (int j = nStartWall; j < nEndWall; j++, pWall++)
        {
            int nNextSector = pWall->nextsector;
            if (nNextSector < 0)
                continue;
            if (TestBitString(sectbits, nNextSector))
                continue;
            SetBitString(sectbits, nNextSector);
            int dx = klabs(wall[pWall->point2].x - x)>>4;
            int dy = klabs(wall[pWall->point2].y - y)>>4;
            if (dx < nDist && dy < nDist)
            {
                if (approxDist(dx, dy) < nDist)
                {
                    if (pSectBit)
                        SetBitString(pSectBit, nNextSector);
                    pSectors[n++] = nNextSector;
                }
            }
        }
        i++;
    }
    pSectors[n] = -1;
    return n;
}

int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, short *pSectors, char *pSectBit, short *pWalls)
{
    char sectbits[(kMaxSectors+7)>>3];
    dassert(pSectors != NULL);
    memset(sectbits, 0, sizeof(sectbits));
    pSectors[0] = nSector;
    SetBitString(sectbits, nSector);
    int n = 1, m = 0;
    int i = 0;
    if (pSectBit)
    {
        memset(pSectBit, 0, (kMaxSectors+7)>>3);
        SetBitString(pSectBit, nSector);
    }
    while (i < n)
    {
        int nCurSector = pSectors[i];
        int nStartWall = sector[nCurSector].wallptr;
        int nEndWall = nStartWall + sector[nCurSector].wallnum;
        walltype *pWall = &wall[nStartWall];
        for (int j = nStartWall; j < nEndWall; j++, pWall++)
        {
            int nNextSector = pWall->nextsector;
            if (nNextSector < 0)
                continue;
            if (TestBitString(sectbits, nNextSector))
                continue;
            SetBitString(sectbits, nNextSector);
            if (CheckProximityWall(wall[j].point2, x, y, nDist))
            {
                if (pSectBit)
                    SetBitString(pSectBit, nNextSector);
                pSectors[n++] = nNextSector;
                if (pWalls && pWall->extra > 0)
                {
                    XWALL *pXWall = &xwall[pWall->extra];
                    if (pXWall->triggerVector && !pXWall->isTriggered && !pXWall->state)
                        pWalls[m++] = j;
                }
            }
        }
        i++;
    }
    pSectors[n] = -1;
    if (pWalls)
    {
        pWalls[m] = -1;
    }
    return n;
}

int picWidth(short nPic, short repeat) {
    return ClipLow((tilesiz[nPic].x * repeat) << 2, 0);
}

int picHeight(short nPic, short repeat) {
    return ClipLow((tilesiz[nPic].y * repeat) << 2, 0);
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT

This file is part of NBlood.

NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------
#pragma once
#include "build.h"
#include "common_game.h"

struct HITINFO {
    short hitsect;
    short hitwall;
    short hitsprite;
    int hitx;
    int hity;
    int hitz;
};

extern POINT2D baseWall[kMaxWalls];
extern POINT3D baseSprite[kMaxSprites];
extern int baseFloor[kMaxSectors];
extern int baseCeil[kMaxSectors];
extern int velFloor[kMaxSectors];
extern int velCeil[kMaxSectors];
extern short gUpperLink[kMaxSectors];
extern short gLowerLink[kMaxSectors];
extern HITINFO gHitInfo;

enum {
    PARALLAXCLIP_CEILING = 1,
    PARALLAXCLIP_FLOOR = 2,
};

// by NoOne: functions to quickly check range of specific arrays
inline bool spriRangeIsFine(int nIndex) {
    return (nIndex >= 0 && nIndex < kMaxSprites);
}

inline bool sectRangeIsFine(int nIndex) {
    return (nIndex >= 0 && nIndex < kMaxSectors);
}

inline bool wallRangeIsFine(int nIndex) {
    return (nIndex >= 0 && nIndex < kMaxWalls);
}
///

bool AreSectorsNeighbors(int sect1, int sect2);
bool FindSector(int nX, int nY, int nZ, int *nSector);
bool FindSector(int nX, int nY, int *nSector);
void CalcFrameRate(void);
bool CheckProximity(spritetype *pSprite, int nX, int nY, int nZ, int nSector, int nDist);
bool CheckProximityPoint(int nX1, int nY1, int nZ1, int nX2, int nY2, int nZ2, int nDist);
bool CheckProximityWall(int nWall, int x, int y, int nDist);
int GetWallAngle(int nWall);
void GetWallNormal(int nWall, int *pX, int *pY);
bool IntersectRay(int wx, int wy, int wdx, int wdy, int x1, int y1, int z1, int x2, int y2, int z2, int *ix, int *iy, int *iz);
int HitScan(spritetype *pSprite, int z, int dx, int dy, int dz, unsigned int nMask, int a8);
int VectorScan(spritetype *pSprite, int nOffset, int nZOffset, int dx, int dy, int dz, int nRange, int ac);
void GetZRange(spritetype *pSprite, int *ceilZ, int *ceilHit, int *floorZ, int *floorHit, int nDist, unsigned int nMask, unsigned int nClipParallax = 0);
void GetZRangeAtXYZ(int x, int y, int z, int nSector, int *ceilZ, int *ceilHit, int *floorZ, int *floorHit, int nDist, unsigned int nMask, unsigned int nClipParallax = 0);
int GetDistToLine(int x1, int y1, int x2, int y2, int x3, int y3);
unsigned int ClipMove(int *x, int *y, int *z, int *nSector, int xv, int yv, int wd, int cd, int fd, unsigned int nMask);
int GetClosestSectors(int nSector, int x, int y, int nDist, short *pSectors, char *pSectBit);
int GetClosestSpriteSectors(int nSector, int x, int y, int nDist, short *pSectors, char *pSectBit, short *pWalls = NULL);
int picWidth(short nPic, short repeat);
int picHeight(short nPic, short repeat);


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT

This file is part of NBlood.

NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include <stdlib.h>
#include <string.h>
#include "compat.h"
#include "getopt.h"

int margc;
char const * const *margv;

const char *OptArgv[16];
int OptArgc;
const char *OptFull;
const char *SwitchChars = "-/";

int GetOptions(SWITCH *switches)
{
    static const char *pChar = NULL;
    static int OptIndex = 1;
    if (!pChar)
    {
        if (OptIndex >= margc)
            return kOptEnd;
        pChar = margv[OptIndex++];
        if (!pChar)
            return kOptEnd;
    }
    OptFull = pChar;
    if (!strchr(SwitchChars, *pChar))
    {
        pChar = NULL;
        return kOptFull;
    }
    pChar++;
    int i;
    int nID;
    for (i = 0; true; i++)
    {
        if (!switches[i].name)
            return kOptInvalid;
        int nLength = strlen(switches[i].name);
        if (!Bstrncasecmp(pChar, switches[i].name, nLength) && (pChar[nLength]=='=' || pChar[nLength]==0))
        {
            pChar += nLength;
            if (*pChar=='=')
            {
                pChar++;
            }
            else
            {
                pChar = NULL;
            }
            break;
        }
    }
    nID = switches[i].nID;
    OptArgc = 0;
    while (OptArgc < switches[i].nArgs)
    {
        if (!pChar)
        {
            if (OptIndex >= margc)
                break;
            pChar = margv[OptIndex++];
            if (strchr(SwitchChars, *pChar) != 0)
                break;
        }
        OptArgv[OptArgc++] = pChar;
        pChar = NULL;
    }
    return nID;
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT

This file is part of NBlood.

NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------
#pragma once

extern int margc;
extern char const * const *margv;

extern const char *OptArgv[16];
extern int OptArgc;
extern const char *OptFull;

enum {
    kOptInvalid = -3,
    kOptFull    = -2,
    kOptEnd     = -1
};

struct SWITCH {
    const char *name;
    int nID, nArgs;
};
int GetOptions(SWITCH *switches);


code:
#include "compat.h"
#include "build.h"
#include "common_game.h"
#include "gfx.h"

int gColor;

void Video_SetPixel(int page, int x, int y)
{
    UNREFERENCED_PARAMETER(page);
#ifdef USE_OPENGL
    if (videoGetRenderMode() >= REND_POLYMOST)
        return;
#endif
    videoBeginDrawing();
    char* dest = (char*)frameplace+ylookup[y]+x;
    *dest = (char)gColor;
    videoEndDrawing();
}

void Video_HLine(int page, int y, int x0, int x1)
{
    UNREFERENCED_PARAMETER(page);
#ifdef USE_OPENGL
    if (videoGetRenderMode() >= REND_POLYMOST)
        return;
#endif
    videoBeginDrawing();
    char* dest = (char*)frameplace+ylookup[y]+x0;
    memset(dest, gColor, x1-x0+1);
    videoEndDrawing();
}

void Video_VLine(int page, int x, int y0, int y1)
{
    UNREFERENCED_PARAMETER(page);
#ifdef USE_OPENGL
    if (videoGetRenderMode() >= REND_POLYMOST)
        return;
#endif
    videoBeginDrawing();
    char* dest = (char*)frameplace+ylookup[y0]+x;
    for (int i = 0; i < y1-y0+1; i++, dest += ylookup[1])
        *dest = (char)gColor;
    videoEndDrawing();
}

void Video_FillBox(int page, int x0, int y0, int x1, int y1)
{
    UNREFERENCED_PARAMETER(page);
#ifdef USE_OPENGL
    if (videoGetRenderMode() >= REND_POLYMOST)
        return;
#endif
    videoBeginDrawing();
    char* dest = (char*)frameplace+ylookup[y0]+x0;
    for (int i = 0; i < y1-y0; i++, dest += ylookup[1])
        memset(dest, gColor, x1-x0);
    videoEndDrawing();
}

void Video_BlitM2V(char* src, int bpl, int width, int height, int page, int x, int y)
{
    UNREFERENCED_PARAMETER(page);
#ifdef USE_OPENGL
    if (videoGetRenderMode() >= REND_POLYMOST)
        return;
#endif
    videoBeginDrawing();
    char* dest = (char*)frameplace+ylookup[y]+x;
    int i = height;
    do
    {
        memcpy(dest, src, width);
        src += bpl;
        dest += xdim;
    } while (--i);
    videoEndDrawing();
}

void Video_BlitMT2V(char* src, char tc, int bpl, int width, int height, int page, int x, int y)
{
    UNREFERENCED_PARAMETER(page);
#ifdef USE_OPENGL
    if (videoGetRenderMode() >= REND_POLYMOST)
        return;
#endif
    videoBeginDrawing();
    char* dest = (char*)frameplace+ylookup[y]+x;
    int i = height;
    do
    {
        int j = width;
        do
        {
            if (*src != tc)
                *dest = *src;
            src++;
            dest++;
        } while (--j);
        src += bpl-width;
        dest += xdim-width;
    } while (--i);
    videoEndDrawing();
}

void Video_BlitMono(char *src, char mask, int bpl, int width, int height, int page, int x, int y)
{
    UNREFERENCED_PARAMETER(page);
#ifdef USE_OPENGL
    if (videoGetRenderMode() >= REND_POLYMOST)
        return;
#endif
    videoBeginDrawing();
    char* dest = (char*)frameplace+ylookup[y]+x;
    int i = height;
    do
    {
        int j = width;
        do
        {
            if (*src&mask)
                *dest = (char)gColor;
            src++;
            dest++;
        } while (--j);
        src -= width;
        dest += xdim-width;
        if (mask&0x80)
        {
            mask = 1;
            src += bpl;
        }
        else
            mask <<= 1;
    } while (--i);
    videoEndDrawing();
}

Rect clipRect(0, 0, 320, 200);
int clipX0 = 0, clipY0 = 0, clipX1 = 320, clipY1 = 200;

void gfxDrawBitmap(QBITMAP *qbm, int x, int y)
{
    dassert(qbm != NULL);

    Rect bitmap(x, y, x+qbm->at2, y+qbm->at4);

    bitmap &= clipRect;

    if (!bitmap)
        return;

    Rect bitmap2 = bitmap;

    bitmap2.offset(-x, -y);

    int height = bitmap.height();
    int width = bitmap.width();

    char* p = qbm->atc;

    switch (qbm->at0)
    {
    case 0:
        Video_BlitM2V(p+bitmap2.y0*qbm->at6+bitmap2.x0, qbm->at6, width, height, 0, bitmap.x0, bitmap.y0);
        break;
    case 1:
        Video_BlitMT2V(p+bitmap2.y0*qbm->at6+bitmap2.x0, qbm->at1, qbm->at6, width, height, 0, bitmap.x0, bitmap.y0);
        break;
    }
}

void gfxPixel(int x, int y)
{
    if (clipRect.inside(x, y))
        Video_SetPixel(0, x, y);
}

void gfxHLine(int y, int x0, int x1)
{
    if (y < clipRect.y0 || y >= clipRect.y1)
        return;

    x0 = ClipLow(x0, clipRect.x0);
    x1 = ClipHigh(x1, clipRect.x1-1);
    if (x0 <= x1)
        Video_HLine(0, y, x0, x1);
}

void gfxVLine(int x, int y0, int y1)
{
    if (x < clipRect.x0 || x >= clipRect.x1)
        return;

    y0 = ClipLow(y0, clipRect.y0);
    y1 = ClipHigh(y1, clipRect.y1-1);
    if (y0 <= y1)
        Video_VLine(0, x, y0, y1);
}

void gfxFillBox(int x0, int y0, int x1, int y1)
{
    Rect box(x0, y0, x1, y1);

    box &= clipRect;

    if (!box.isEmpty())
        Video_FillBox(0, box.x0, box.y0, box.x1, box.y1);
}

void gfxSetClip(int x0, int y0, int x1, int y1)
{
    clipRect.x0 = x0;
    clipRect.y0 = y0;
    clipRect.x1 = x1;
    clipRect.y1 = y1;

    clipX0 = x0 << 8;
    clipY0 = y0 << 8;
    clipX1 = (x1 << 8)-1;
    clipY1 = (y1 << 8)-1;
}

extern char textfont[2048];
char* fontTable = textfont;

void printChar(int x, int y, char c)
{
    for (int i = 0; i < 8; i++)
    {
        int mask = 0x80;
        for (int j = 0; j < 8; j++, mask >>= 1)
        {
            char data = fontTable[c*8+i];
            if (data & mask)
                Video_SetPixel(0, x+j, y+i);
        }
    }
}

int gfxGetTextNLen(const char * pzText, QFONT *pFont, int a3)
{
    if (!pFont)
        return strlen(pzText)*8;
    int nLength = -pFont->at11;

    for (const char* s = pzText; *s != 0 && a3 > 0; s++, a3--)
    {
        nLength += pFont->at20[*s].ox+pFont->at11;
    }
    return nLength;
}

int gfxGetLabelLen(const char *pzLabel, QFONT *pFont)
{
    int nLength = 0;
    if (pFont)
        nLength = -pFont->at11;

    for (const char* s = pzLabel; *s != 0; s++)
    {
        if (*s == '&')
            continue;
        if (!pFont)
            nLength += 8;
        else
            nLength += pFont->at20[*s].ox+pFont->at11;
    }
    return nLength;
}

int gfxFindTextPos(const char *pzText, QFONT *pFont, int a3)
{
    if (!pFont)
    {
        return a3 / 8;
    }
    int nLength = -pFont->at11;
    int pos = 0;

    for (const char* s = pzText; *s != 0; s++, pos++)
    {
        nLength += pFont->at20[*s].ox+pFont->at11;
        if (nLength > a3)
            break;
    }
    return pos;
}

void gfxDrawText(int x, int y, int color, const char* pzText, QFONT* pFont)
{
    if (pFont)
        y += pFont->atf;

    gColor = color;

    for (const char* s = pzText; *s != 0; s++)
    {
        if (!pFont)
        {
            Rect rect1(x, y, x+8, y+8);
            if (clipRect.inside(rect1))
                printChar(x, y, *s);
            x += 8;
        }
        else
        {
            QFONTCHAR* pChar = &pFont->at20[*s];
            Rect rect1(x, y+pChar->oy, x+pChar->w, y+pChar->oy+pChar->h);

            rect1 &= clipRect;

            if (!rect1.isEmpty())
            {
                Rect rect2 = rect1;

                rect2.offset(-x, -(y+pChar->oy));

                switch (pFont->at6)
                {
                case 0:
                    Video_BlitMono(&pFont->at820[pChar->offset+(rect2.y0/8)*pChar->w+rect2.x0], 1<<(rect2.y0&7), pChar->w,
                        rect1.x1-rect1.x0, rect1.y1-rect1.y0, 0, rect1.x0, rect1.y0);
                    break;
                case 1:
                    Video_BlitMT2V(&pFont->at820[pChar->offset+rect2.y0*pChar->w+rect2.x0], pFont->at10, pChar->w,
                        rect1.x1-rect1.x0, rect1.y1-rect1.y0, 0, rect1.x0, rect1.y0);
                    break;
                }
            }
            x += pFont->at11 + pChar->ox;
        }
    }
}

void gfxDrawLabel(int x, int y, int color, const char* pzLabel, QFONT* pFont)
{
    if (pFont)
        y += pFont->atf;

    gColor = color;

    char v4 = 0;

    for (const char* s = pzLabel; *s != 0; s++)
    {
        if (*s == '&')
        {
            v4 = 1;
            continue;
        }
        if (!pFont)
        {
            Rect rect1(x, y, x+8, y+8);
            if (clipRect.inside(rect1))
            {
                printChar(x, y, *s);
                if (v4)
                    gfxHLine(y+8, x, x+6);
                x += 8;
            }
        }
        else
        {
            QFONTCHAR* pChar = &pFont->at20[*s];
            Rect rect1(x, y+pChar->oy, x+pChar->w, y+pChar->oy+pChar->h);

            rect1 &= clipRect;

            if (!rect1.isEmpty())
            {
                Rect rect2 = rect1;

                rect2.offset(-x, -(y+pChar->oy));

                switch (pFont->at6)
                {
                case 0:
                    Video_BlitMono(&pFont->at820[pChar->offset+(rect2.y0/8)*pChar->w+rect2.x0], 1<<(rect2.y0&7), pChar->w,
                        rect1.x1-rect1.x0, rect1.y1-rect1.y0, 0, rect1.x0, rect1.y0);
                    if (v4)
                        gfxHLine(y+2, x, x+pChar->w-1);
                    break;
                case 1:
                    Video_BlitMT2V(&pFont->at820[pChar->offset+rect2.y0*pChar->w+rect2.x0], pFont->at10, pChar->w,
                        rect1.x1-rect1.x0, rect1.y1-rect1.y0, 0, rect1.x0, rect1.y0);
                    if (v4)
                        gfxHLine(y+2, x, x+pChar->w-1);
                    break;
                }
            }
            x += pFont->at11 + pChar->ox;
        }
        v4 = 0;
    }
}


code:
#pragma once
#include "compat.h"

#pragma pack(push, 1)

struct QBITMAP
{
    unsigned char at0; // type
    unsigned char at1; // transcolor
    unsigned short at2; // width
    unsigned short at4; // height
    unsigned short at6; // bpl
    unsigned short at8;
    unsigned short ata;
    char atc[1]; // data
};

typedef struct {
    int offset;
    unsigned char w;
    unsigned char h;
    unsigned char ox;
    signed char oy;
} QFONTCHAR;

struct QFONT
{
    unsigned char at0[4]; // signature
    unsigned char pad0[2];
    unsigned short at6;
    unsigned char pad1[0x7];
    unsigned char atf;
    unsigned char at10;
    signed char at11;
    unsigned char at12;
    unsigned char at13;
    unsigned char pad2[0xc];
    QFONTCHAR at20[256];
    char at820[1];
};

#pragma pack(pop)

extern int gColor;

void Video_BlitM2V(char* src, int bpl, int width, int height, int page, int x, int y);
void Video_BlitMT2V(char* src, char tc, int bpl, int width, int height, int page, int x, int y);

void gfxDrawBitmap(QBITMAP* qbm, int x, int y);
void gfxPixel(int x, int y);
void gfxHLine(int y, int x0, int x1);
void gfxVLine(int x, int y0, int y1);
void gfxFillBox(int x0, int y0, int x1, int y1);
void gfxSetClip(int x0, int y0, int x1, int y1);
int gfxGetTextNLen(const char* pzText, QFONT* pFont, int a3);
int gfxGetLabelLen(const char* pzLabel, QFONT* pFont);
int gfxFindTextPos(const char* pzText, QFONT* pFont, int a3);
void gfxDrawText(int x, int y, int color, const char* pzText, QFONT* pFont);
void gfxDrawLabel(int, int, int, const char*, QFONT*);


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT

This file is part of NBlood.

NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "compat.h"
#include "build.h"
#include "pragmas.h"
#include "common_game.h"
#include "actor.h"
#include "blood.h"
#include "db.h"
#include "callback.h"
#include "config.h"
#include "eventq.h"
#include "fx.h"
#include "gib.h"
#include "levels.h"
#include "sfx.h"
#include "tile.h"
#include "trig.h"

struct GIBFX
{
    FX_ID at0;
    int at1;
    int chance;
    int at9;
    int atd;
    int at11;
};

struct GIBTHING
{
    int at0;
    int at4;
    int chance;
    int atc;
    int at10;
};

struct GIBLIST
{
    GIBFX *at0;
    int at4;
    GIBTHING *at8;
    int atc;
    int at10;
};

GIBFX gibFxGlassT[] = {
    { FX_18, 0, 65536, 3, 200, 400 },
    { FX_31, 0, 32768, 5, 200, 400 }
};

GIBFX gibFxGlassS[] = {
    { FX_18, 0, 65536, 8, 200, 400 }
};

GIBFX gibFxBurnShard[] = {
    { FX_16, 0, 65536, 12, 500, 1000 }
};

GIBFX gibFxWoodShard[] = {
    { FX_17, 0, 65536, 12, 500, 1000 }
};

GIBFX gibFxMetalShard[] = {
    { FX_30, 0, 65536, 12, 500, 1000 }
};

GIBFX gibFxFireSpark[] = {
    { FX_14, 0, 65536, 8, 500, 1000 }
};

GIBFX gibFxShockSpark[] = {
    { FX_15, 0, 65536, 8, 500, 1000 }
};

GIBFX gibFxBloodChunks[] = {
    { FX_13, 0, 65536, 8, 90, 600 }
};

GIBFX gibFxBubblesS[] = {
    { FX_25, 0, 65536, 8, 200, 400 }
};

GIBFX gibFxBubblesM[] = {
    { FX_24, 0, 65536, 8, 200, 400 }
};

GIBFX gibFxBubblesL[] = {
    { FX_23, 0, 65536, 8, 200, 400 }
};

GIBFX gibFxIcicles[] = {
    { FX_31, 0, 65536, 15, 200, 400 }
};

GIBFX gibFxGlassCombo1[] = {
    { FX_18, 0, 65536, 15, 200, 400 },
    { FX_31, 0, 65536, 10, 200, 400 }
};

GIBFX gibFxGlassCombo2[] = {
    { FX_18, 0, 65536, 5, 200, 400 },
    { FX_20, 0, 53248, 5, 200, 400 },
    { FX_21, 0, 53248, 5, 200, 400 },
    { FX_19, 0, 53248, 5, 200, 400 },
    { FX_22, 0, 53248, 5, 200, 400 }
};

GIBFX gibFxWoodCombo[] = {
    { FX_16, 0, 65536, 8, 500, 1000 },
    { FX_17, 0, 65536, 8, 500, 1000 },
    { FX_14, 0, 65536, 8, 500, 1000 }
};

GIBFX gibFxMedicCombo[] = {
    { FX_18, 0, 32768, 7, 200, 400 },
    { FX_30, 0, 65536, 7, 500, 1000 },
    { FX_13, 0, 65536, 10, 90, 600 },
    { FX_14, 0, 32768, 7, 500, 1000 }
};

GIBFX gibFxFlareSpark[] = {
    { FX_28, 0, 32768, 15, 128, -128 }
};

GIBFX gibFxBloodBits[] = {
    { FX_13, 0, 45056, 8, 90, 600 }
};

GIBFX gibFxRockShards[] = {
    { FX_46, 0, 65536, 10, 300, 800 },
    { FX_31, 0, 32768, 10, 200, 1000 }
};

GIBFX gibFxPaperCombo1[] = {
    { FX_47, 0, 65536, 12, 300, 600 },
    { FX_14, 0, 65536, 8, 500, 1000 }
};

GIBFX gibFxPlantCombo1[] = {
    { FX_44, 0, 45056, 8, 400, 800 },
    { FX_45, 0, 45056, 8, 300, 800 },
    { FX_14, 0, 45056, 6, 500, 1000 }
};

GIBFX gibFx13BBA8[] = {
    { FX_49, 0, 65536, 4, 80, 300 }
};

GIBFX gibFx13BBC0[] = {
    { FX_50, 0, 65536, 4, 80, 0 }
};

GIBFX gibFx13BBD8[] = {
    { FX_50, 0, 65536, 20, 800, -40 },
    { FX_15, 0, 65536, 15, 400, 10 }
};

GIBFX gibFx13BC04[] = {
    { FX_32, 0, 65536, 8, 100, 0 }
};

GIBFX gibFx13BC1C[] = {
    { FX_56, 0, 65536, 8, 100, 0 }
};

GIBTHING gibHuman[] = {
    { 425, 1454, 917504, 300, 900 },
    { 425, 1454, 917504, 300, 900 },
    { 425, 1267, 917504, 300, 900 },
    { 425, 1267, 917504, 300, 900 },
    { 425, 1268, 917504, 300, 900 },
    { 425, 1269, 917504, 300, 900 },
    { 425, 1456, 917504, 300, 900 }
};

GIBTHING gibMime[] = {
    { 425, 2405, 917504, 300, 900 },
    { 425, 2405, 917504, 300, 900 },
    { 425, 2404, 917504, 300, 900 },
    { 425, 1268, 32768, 300, 900 },
    { 425, 1269, 32768, 300, 900 },
    { 425, 1456, 32768, 300, 900 },
};

GIBTHING gibHound[] = {
    { 425, 1326, 917504, 300, 900 },
    { 425, 1268, 32768, 300, 900 },
    { 425, 1269, 32768, 300, 900 },
    { 425, 1456, 32768, 300, 900 }
};

GIBTHING gibFleshGargoyle[] = {
    { 425, 1369, 917504, 300, 900 },
    { 425, 1361, 917504, 300, 900 },
    { 425, 1268, 32768, 300, 900 },
    { 425, 1269, 32768, 300, 900 },
    { 425, 1456, 32768, 300, 900 }
};

GIBTHING gibAxeZombieHead[] = {
    { 427, 3405, 917504, 0, 0 }
};

GIBLIST gibList[] = {
    { gibFxGlassT, 2, NULL, 0, 300 },
    { gibFxGlassS, 1, NULL, 0, 300 },
    { gibFxBurnShard, 1, NULL, 0, 0 },
    { gibFxWoodShard, 1, NULL, 0, 0 },
    { gibFxMetalShard, 1, NULL, 0, 0 },
    { gibFxFireSpark, 1, NULL, 0, 0 },
    { gibFxShockSpark, 1, NULL, 0, 0 },
    { gibFxBloodChunks, 1, NULL, 0, 0 },
    { gibFxBubblesS, 1, NULL, 0, 0 },
    { gibFxBubblesM, 1, NULL, 0, 0 },
    { gibFxBubblesL, 1, NULL, 0, 0 },
    { gibFxIcicles, 1, NULL, 0, 0 },
    { gibFxGlassCombo1, 2, NULL, 0, 300 },
    { gibFxGlassCombo2, 5, NULL, 0, 300 },
    { gibFxWoodCombo, 3, NULL, 0, 0 },
    { NULL, 0, gibHuman, 7, 0 },
    { gibFxMedicCombo, 4, NULL, 0, 0 },
    { gibFxFlareSpark, 1, NULL, 0, 0 },
    { gibFxBloodBits, 1, NULL, 0, 0 },
    { gibFxRockShards, 2, NULL, 0, 0 },
    { gibFxPaperCombo1, 2, NULL, 0, 0 },
    { gibFxPlantCombo1, 3, NULL, 0, 0 },
    { gibFx13BBA8, 1, NULL, 0, 0 },
    { gibFx13BBC0, 1, NULL, 0, 0 },
    { gibFx13BBD8, 2, NULL, 0, 0 },
    { gibFx13BC04, 1, NULL, 0, 0 },
    { gibFx13BC1C, 1, NULL, 0, 0 },
    { NULL, 0, gibAxeZombieHead, 1, 0 },
    { NULL, 0, gibMime, 6, 0 },
    { NULL, 0, gibHound, 4, 0 },
    { NULL, 0, gibFleshGargoyle, 5, 0 },
};

void gibCalcWallArea(int a1, int &a2, int &a3, int &a4, int &a5, int &a6, int &a7, int &a8)
{
    walltype *pWall = &wall[a1];
    a2 = (pWall->x+wall[pWall->point2].x)>>1;
    a3 = (pWall->y+wall[pWall->point2].y)>>1;
    int nSector = sectorofwall(a1);
    int32_t ceilZ, floorZ;
    getzsofslope(nSector, a2, a3, &ceilZ, &floorZ);
    int32_t ceilZ2, floorZ2;
    getzsofslope(pWall->nextsector, a2, a3, &ceilZ2, &floorZ2);
    ceilZ = ClipLow(ceilZ, ceilZ2);
    floorZ = ClipHigh(floorZ, floorZ2);
    a7 = floorZ-ceilZ;
    a5 = wall[pWall->point2].x-pWall->x;
    a6 = wall[pWall->point2].y-pWall->y;
    a8 = (a7>>8)*approxDist(a5>>4, a6>>4);
    a4 = (ceilZ+floorZ)>>1;
}

int ChanceToCount(int a1, int a2)
{
    int vb = a2;
    if (a1 < 0x10000)
    {
        for (int i = 0; i < a2; i++)
            if (!Chance(a1))
                vb--;
    }
    return vb;
}

void GibFX(spritetype *pSprite, GIBFX *pGFX, CGibPosition *pPos, CGibVelocity *pVel)
{
    int nSector = pSprite->sectnum;
    if (gbAdultContent && gGameOptions.nGameType == kGameTypeSinglePlayer && pGFX->at0 == FX_13)
        return;
    CGibPosition gPos(pSprite->x, pSprite->y, pSprite->z);
    if (pPos)
        gPos = *pPos;
    int32_t ceilZ, floorZ;
    getzsofslope(nSector, gPos.x, gPos.y, &ceilZ, &floorZ);
    int nCount = ChanceToCount(pGFX->chance, pGFX->at9);
    int dz1 = floorZ-gPos.z;
    int dz2 = gPos.z-ceilZ;
    int top, bottom;
    GetSpriteExtents(pSprite, &top, &bottom);
    for (int i = 0; i < nCount; i++)
    {
        if (!pPos && (pSprite->cstat&48) == 0)
        {
            int nAngle = Random(2048);
            gPos.x = pSprite->x+mulscale30(pSprite->clipdist<<2, Cos(nAngle));
            gPos.y = pSprite->y+mulscale30(pSprite->clipdist<<2, Sin(nAngle));
            gPos.z = bottom-Random(bottom-top);
        }
        spritetype *pFX = gFX.fxSpawn(pGFX->at0, nSector, gPos.x, gPos.y, gPos.z);
        if (pFX)
        {
            if (pGFX->at1 < 0)
                pFX->pal = pSprite->pal;
            if (pVel)
            {
                xvel[pFX->index] = pVel->vx+Random2(pGFX->atd);
                yvel[pFX->index] = pVel->vy+Random2(pGFX->atd);
                zvel[pFX->index] = pVel->vz-Random(pGFX->at11);
            }
            else
            {
                xvel[pFX->index] = Random2((pGFX->atd<<18)/120);
                yvel[pFX->index] = Random2((pGFX->atd<<18)/120);
                switch(pSprite->cstat&48)
                {
                case 16:
                    zvel[pFX->index] = Random2((pGFX->at11<<18)/120);
                    break;
                default:
                    if (dz2 < dz1 && dz2 < 0x4000)
                    {
                        zvel[pFX->index] = 0;
                    }
                    else if (dz2 > dz1 && dz1 < 0x4000)
                    {
                        zvel[pFX->index] = -Random((klabs(pGFX->at11)<<18)/120);
                    }
                    else
                    {
                        if ((pGFX->at11<<18)/120 < 0)
                            zvel[pFX->index] = -Random((klabs(pGFX->at11)<<18)/120);
                        else
                            zvel[pFX->index] = Random2((pGFX->at11<<18)/120);
                    }
                    break;
                }
            }
        }
    }
}

void GibThing(spritetype *pSprite, GIBTHING *pGThing, CGibPosition *pPos, CGibVelocity *pVel)
{
    if (gbAdultContent && gGameOptions.nGameType == kGameTypeSinglePlayer)
        switch (pGThing->at0) {
            case kThingBloodBits:
            case kThingZombieHead:
                return;
        }

    if (pGThing->chance == 65536 || Chance(pGThing->chance))
    {
        int nSector = pSprite->sectnum;
        int top, bottom;
        GetSpriteExtents(pSprite, &top, &bottom);
        int x, y, z;
        if (!pPos)
        {
            int nAngle = Random(2048);
            x = pSprite->x+mulscale30(pSprite->clipdist<<2, Cos(nAngle));
            y = pSprite->y+mulscale30(pSprite->clipdist<<2, Sin(nAngle));
            z = bottom-Random(bottom-top);
        }
        else
        {
            x = pPos->x;
            y = pPos->y;
            z = pPos->z;
        }
        int32_t ceilZ, floorZ;
        getzsofslope(nSector, x, y, &ceilZ, &floorZ);
        int dz1 = floorZ-z;
        int dz2 = z-ceilZ;
        spritetype *pGib = actSpawnThing(nSector, x, y, z, pGThing->at0);
        dassert(pGib != NULL);
        if (pGThing->at4 > -1)
            pGib->picnum = pGThing->at4;
        if (pVel)
        {
            xvel[pGib->index] = pVel->vx+Random2(pGThing->atc);
            yvel[pGib->index] = pVel->vy+Random2(pGThing->atc);
            zvel[pGib->index] = pVel->vz-Random(pGThing->at10);
        }
        else
        {
            xvel[pGib->index] = Random2((pGThing->atc<<18)/120);
            yvel[pGib->index] = Random2((pGThing->atc<<18)/120);
            switch (pSprite->cstat&48)
            {
            case 16:
                zvel[pGib->index] = Random2((pGThing->at10<<18)/120);
                break;
            default:
                if (dz2 < dz1 && dz2 < 0x4000)
                {
                    zvel[pGib->index] = 0;
                }
                else if (dz2 > dz1 && dz1 < 0x4000)
                {
                    zvel[pGib->index] = -Random((pGThing->at10<<18)/120);
                }
                else
                {
                    zvel[pGib->index] = Random2((pGThing->at10<<18)/120);
                }
                break;
            }
        }
    }
}

void GibSprite(spritetype *pSprite, GIBTYPE nGibType, CGibPosition *pPos, CGibVelocity *pVel)
{
    dassert(pSprite != NULL);
    dassert(nGibType >= 0 && nGibType < kGibMax);
    if (pSprite->sectnum < 0 || pSprite->sectnum >= numsectors)
        return;
    GIBLIST *pGib = &gibList[nGibType];
    for (int i = 0; i < pGib->at4; i++)
    {
        GIBFX *pGibFX = &pGib->at0[i];
        dassert(pGibFX->chance > 0);
        GibFX(pSprite, pGibFX, pPos, pVel);
    }
    for (int i = 0; i < pGib->atc; i++)
    {
        GIBTHING *pGibThing = &pGib->at8[i];
        dassert(pGibThing->chance > 0);
        GibThing(pSprite, pGibThing, pPos, pVel);
    }
}

void GibFX(int nWall, GIBFX * pGFX, int a3, int a4, int a5, int a6, CGibVelocity * pVel)
{
    dassert(nWall >= 0 && nWall < numwalls);
    walltype *pWall = &wall[nWall];
    int nCount = ChanceToCount(pGFX->chance, pGFX->at9);
    int nSector = sectorofwall(nWall);
    for (int i = 0; i < nCount; i++)
    {
        int r1 = Random(a6);
        int r2 = Random(a5);
        int r3 = Random(a4);
        spritetype *pGib = gFX.fxSpawn(pGFX->at0, nSector, pWall->x+r3, pWall->y+r2, a3+r1);
        if (pGib)
        {
            if (pGFX->at1 < 0)
                pGib->pal = pWall->pal;
            if (!pVel)
            {
                xvel[pGib->index] = Random2((pGFX->atd<<18)/120);
                yvel[pGib->index] = Random2((pGFX->atd<<18)/120);
                zvel[pGib->index] = -Random((pGFX->at11<<18)/120);
            }
            else
            {
                xvel[pGib->index] = Random2((pVel->vx<<18)/120);
                yvel[pGib->index] = Random2((pVel->vy<<18)/120);
                zvel[pGib->index] = -Random((pVel->vz<<18)/120);
            }
        }
    }
}

void GibWall(int nWall, GIBTYPE nGibType, CGibVelocity *pVel)
{
    dassert(nWall >= 0 && nWall < numwalls);
    dassert(nGibType >= 0 && nGibType < kGibMax);
    int cx, cy, cz, wx, wy, wz;
    walltype *pWall = &wall[nWall];
    cx = (pWall->x+wall[pWall->point2].x)>>1;
    cy = (pWall->y+wall[pWall->point2].y)>>1;
    int nSector = sectorofwall(nWall);
    int32_t ceilZ, floorZ;
    getzsofslope(nSector, cx, cy, &ceilZ, &floorZ);
    int32_t ceilZ2, floorZ2;
    getzsofslope(pWall->nextsector, cx, cy, &ceilZ2, &floorZ2);
    ceilZ = ClipLow(ceilZ, ceilZ2);
    floorZ = ClipHigh(floorZ, floorZ2);
    wz = floorZ-ceilZ;
    wx = wall[pWall->point2].x-pWall->x;
    wy = wall[pWall->point2].y-pWall->y;
    cz = (ceilZ+floorZ)>>1;
    GIBLIST *pGib = &gibList[nGibType];
    sfxPlay3DSound(cx, cy, cz, pGib->at10, nSector);
    for (int i = 0; i < pGib->at4; i++)
    {
        GIBFX *pGibFX = &pGib->at0[i];
        dassert(pGibFX->chance > 0);
        GibFX(nWall, pGibFX, ceilZ, wx, wy, wz, pVel);
    }
}

void gibPrecache(void)
{
    for (int i = 0; i < kGibMax; i++)
    {
        auto const pThing = gibList[i].at8;
        if (pThing)
        {
            for (int j = 0; j < gibList[i].atc; j++)
            {
                if (pThing[j].at4 >= 0)
                    tilePrecacheTile(pThing[j].at4);
            }
        }
    }
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT

This file is part of NBlood.

NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------
#pragma once

enum GIBTYPE {
    GIBTYPE_0 = 0,
    GIBTYPE_1,
    GIBTYPE_2,
    GIBTYPE_3,
    GIBTYPE_4,
    GIBTYPE_5,
    GIBTYPE_6,
    GIBTYPE_7,
    GIBTYPE_8,
    GIBTYPE_9,
    GIBTYPE_10,
    GIBTYPE_11,
    GIBTYPE_12,
    GIBTYPE_13,
    GIBTYPE_14,
    GIBTYPE_15,
    GIBTYPE_16,
    GIBTYPE_17,
    GIBTYPE_18,
    GIBTYPE_19,
    GIBTYPE_20,
    GIBTYPE_21,
    GIBTYPE_22,
    GIBTYPE_23,
    GIBTYPE_24,
    GIBTYPE_25,
    GIBTYPE_26,
    GIBTYPE_27,
    GIBTYPE_28,
    GIBTYPE_29,
    GIBTYPE_30,
    kGibMax
};

class CGibPosition {
public:
    int x, y, z;
    CGibPosition(int _x, int _y, int _z) : x(_x), y(_y), z(_z) {}
};

class CGibVelocity {
public:
    int vx, vy, vz;
    CGibVelocity(int _vx, int _vy, int _vz) : vx(_vx), vy(_vy), vz(_vz) {}
};

void GibSprite(spritetype *pSprite, GIBTYPE nGibType, CGibPosition *pPos, CGibVelocity *pVel);
//void GibFX(int nWall, GIBFX * pGFX, int a3, int a4, int a5, int a6, CGibVelocity * pVel);
void GibWall(int nWall, GIBTYPE nGibType, CGibVelocity *pVel);
void gibPrecache(void);


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT

This file is part of NBlood.

NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include <stdlib.h>

#include "compat.h"
#include "build.h"
#include "common_game.h"
#include "globals.h"
#include "resource.h"
#include "renderlayer.h"

ud_setup_t gSetup;
bool bVanilla = false;
ClockTicks gFrameClock;
ClockTicks gFrameTicks;
int gFrame;
//int volatile gGameClock;
int gFrameRate;
int gGamma;

char *gVersionString;
char gVersionStringBuf[16];

Resource gSysRes;

static const char *_module;
static int _line;

void _SetErrorLoc(const char *pzFile, int nLine)
{
    _module = pzFile;
    _line = nLine;
}

void _ThrowError(const char *pzFormat, ...)
{
    char buffer[256];
    va_list args;
    va_start(args, pzFormat);
    vsprintf(buffer, pzFormat, args);
    initprintf("%s(%i): %s\n", _module, _line, buffer);

#ifdef WM_MSGBOX_WINDOW
    char titlebuf[256];
    Bsprintf(titlebuf, APPNAME " %s", s_buildRev);
    wm_msgbox(titlebuf, "%s(%i): %s\n", _module, _line, buffer);
#endif

    Bfflush(NULL);
    QuitGame();
}

// by NoOne: show warning msgs in game instead of throwing errors (in some cases)
void _consoleSysMsg(const char* pzFormat, ...) {

    char buffer[1024];
    va_list args;
    va_start(args, pzFormat);
    vsprintf(buffer, pzFormat, args);
    OSD_Printf(OSDTEXT_RED "%s(%i): %s\n", _module, _line, buffer);
}

void __dassert(const char * pzExpr, const char * pzFile, int nLine)
{
    initprintf("Assertion failed: %s in file %s at line %i\n", pzExpr, pzFile, nLine);

#ifdef WM_MSGBOX_WINDOW
    char titlebuf[256];
    Bsprintf(titlebuf, APPNAME " %s", s_buildRev);
    wm_msgbox(titlebuf, "Assertion failed: %s in file %s at line %i\n", pzExpr, pzFile, nLine);
#endif

    Bfflush(NULL);
    exit(0);
}

const char *GetVersionString(void)
{
    if (!gVersionString)
    {
        gVersionString = gVersionStringBuf;
        if (!gVersionString)
            return NULL;
        sprintf(gVersionString, "%d.%02d", EXEVERSION / 100, EXEVERSION % 100);
    }
    return gVersionString;
}


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT

This file is part of NBlood.

NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------
#pragma once
#include "compat.h"
#include "build.h"
#include "resource.h"

typedef struct {
    int32_t usejoystick;
    int32_t usemouse;
    int32_t fullscreen;
    int32_t xdim;
    int32_t ydim;
    int32_t bpp;
    int32_t forcesetup;
    int32_t noautoload;
} ud_setup_t;

extern ud_setup_t gSetup;
extern ClockTicks gFrameClock;
extern ClockTicks gFrameTicks;
extern int gFrame;
//extern ClockTicks gGameClock;
extern int gFrameRate;
extern int gGamma;
extern bool bVanilla;

extern Resource gSysRes;
const char *GetVersionString(void);