code:
// Copyright 2020 Nuke.YKT, EDuke32 developers
// Polymost code: Copyright Ken Silverman, Copyright (c) 2018, Alex Dawson
#pragma once

#include "build.h"
#include "reality.h"

extern float rt_viewhorizang;
extern tileinfo_t rt_tileinfo[RT_TILENUM];
extern int32_t rt_tilemap[MAXTILES];
extern vec3f_t rt_sky_color[2];

#define MOVESECTNUM 40
#define MOVESECTVTXNUM 1024

extern int ms_list[MOVESECTNUM], ms_listvtxptr[MOVESECTNUM];
extern int ms_list_cnt, ms_vtx_cnt;
extern int ms_dx[MOVESECTVTXNUM], ms_dy[MOVESECTVTXNUM];
extern int ms_vx[MOVESECTVTXNUM], ms_vy[MOVESECTVTXNUM];

#define MAXEXPLOSIONS 16

struct explosioninstance_t {
    uint8_t status;
    uint8_t type;
    uint8_t c_enable, r_enable;
    int16_t x, y, z;
    int16_t c_size, r_size, r_time, c_time, c_step, r_step;
};

struct smokeinstance_t {
    uint8_t status;
    uint8_t type;
    int16_t x, y, z;
    int16_t orientation;
    float scale, phase, phase_step;
};

extern explosioninstance_t explosions[MAXEXPLOSIONS];
extern smokeinstance_t smoke[MAXEXPLOSIONS];

#define BOSS2_VTXNUM 418
#define BOSS2_TRIS 469
#define BOSS2_FRAMES 15

#pragma pack(push, 1)
struct boss2vtx_t {
    int16_t x, y, z, u, v;
    int16_t color[3];
};

struct boss2tris_t {
    int16_t vtx[3];
    int16_t tile;
};

#pragma pack(pop)

extern int boss2seq, boss2seqframe;
extern int boss2mdlstate, boss2mdlstate2;
extern int boss2timer_step;
extern int boss2_frame, boss2_frame2;
extern int boss2timer;
extern float boss2_interp;

void RT_LoadTiles(void);
void RT_DrawRooms(int x, int y, int z, fix16_t ang, fix16_t horiz, int16_t sectnum, int smoothRatio);
void RT_GLInit(void);

void RT_DisablePolymost(int useShader);
void RT_EnablePolymost();

enum {
    RTRS_SCALED = 4096,
};

void RT_MS_Reset(void);
void RT_MS_Add(int sectnum, int x, int y);
void RT_MS_Update(int sectnum, int ang, int x, int y);
void RT_MS_SetInterpolation(int sectnum);
void RT_AdjustCeilingPanning(int sectnum, int x, int y);
void RT_AdjustFloorPanning(int sectnum, int x, int y);
void RT_RotateSpriteSetColor(int a1, int a2, int a3, int a4);
void RT_RotateSpriteSetShadePal(int ss, int shade, int pal);
void RT_RotateSpriteSetShadePalAlpha(int shade, int pal, int alpha);
void RT_RotateSprite(float x, float y, float sx, float sy, int tilenum, int orientation, bool screenCorrection = true);
void RT_RotateSpriteText(float x, float y, float sx, float sy, int tilenum, int orientation, bool buildcoords = false);
void RT_DrawTileFlash(int x, int y, int picnum, float sx, float sy, int orientation, int color);
void RT_AddSmoke(int16_t x, int16_t y, int16_t z, uint8_t type);
void RT_AddExplosion(int16_t x, int16_t y, int16_t z, uint8_t type);
void RT_AnimateModels(void);
void RT_LoadBOSS2MDL(void);
void RT_RenderScissor(float x1, float y1, float x2, float y2, bool absolute = false);
void RT_RenderUnsetScissor(void);

repo_name: orochi/nblood

license: mit


code:
// Copyright: 2020 Nuke.YKT, EDuke32 developers
// License: GPLv2
#include "compat.h"
#include "reality.h"
#include "reality_sbar.h"
#include "../duke3d.h"

float borderx1 = 16.f * 4.f / 3.f;
float bordery1 = 16;
float borderx2 = 320.f - 16.f * 4.f / 3.f;
float bordery2 = 240.f - 16.f;

float bartilescroll;
int barscrolldir;
float bartilescrollspeed;

float RT_SBarScale(float x)
{
    return x * ud.statusbarscale / 100.f;
}

void RT_ResetBarScroll(void)
{
    if (numplayers > 1)
    {
        bartilescrollspeed = 0.f;
        bartilescroll = -99.f;
        barscrolldir = 1;
    }
    else
    {
        bartilescrollspeed = 12.f;
        bartilescroll = -24.f;
        barscrolldir = 0;
    }
}

void RT_DrawBarOverlay(bool drawhud)
{
    if (bartilescroll < -70)
        return;
    if (drawhud)
    {
        RT_RotateSpriteSetColor(255, 255, 255, 666);
        float x1 = RT_SBarScale(bartilescroll + 5 - borderx1) + borderx1;
        float x2 = RT_SBarScale(314 - bartilescroll - borderx2) + borderx2;
        float y = RT_SBarScale(-14) + bordery2;
        float sc = RT_SBarScale(100.f);
        RT_RotateSprite(x1, y, sc, sc, 0xe31, 0|256|RTRS_SCALED);
        RT_RotateSprite(x2, y, sc, sc, 0xe31, 0|4|512|RTRS_SCALED);
    }
    static int timer = 0;
    static int cnt = 0;
    if (timer > (int)totalclock + 16)
    {
        timer = 0;
        cnt = 0;
    }
    while (timer < (int)totalclock && bartilescroll >= -70.f)
    {
        timer = (int)totalclock + 1;
        if (!barscrolldir)
        {
            bartilescroll = bartilescroll + bartilescrollspeed * 0.25f;
            if (bartilescroll > 56.f)
            {
                bartilescroll = 56.f;
                barscrolldir = 1;
                bartilescrollspeed = 0.f;
            }
            if ((cnt&3) == 3)
            {
                bartilescrollspeed -= 1.f;
                if (bartilescrollspeed < 1.f)
                    bartilescrollspeed = 1.f;
            }
        }
        else
        {
            bartilescroll -= bartilescrollspeed * 0.25f;
            if ((cnt&3) == 3)
                bartilescrollspeed += 1.f;
        }
        cnt++;
    }
}

void RT_DrawBarBG(bool drawhud)
{
    if (numplayers <= 1)
    {
        if (!drawhud)
            return;
        float o = bartilescroll;
        if (barscrolldir)
            o = 56;

        float x1 = RT_SBarScale(o + 10 - borderx1) + borderx1;
        float x2 = RT_SBarScale(319 - o - borderx2) + borderx2;
        float y = RT_SBarScale(-14) + bordery2;
        float sc = RT_SBarScale(100.f);
        RT_RotateSpriteSetColor(255, 255, 255, 666);
        RT_RotateSprite(x1, y, sc, sc, 0xe30, 0|256|RTRS_SCALED);
        RT_RotateSprite(x2, y, sc, sc, 0xe2f, 0|512|RTRS_SCALED);
    }
    else
        barscrolldir = 1;
}

int RT_InvTileScale(int s, int tile)
{
    int rt_tile = rt_tilemap[tile];
    if (rt_tile == -1)
        return 0;

    if (rt_tileinfo[rt_tile].dimy == 0)
        return 0;

    return (100 * s) / rt_tileinfo[rt_tile].dimy;
}

void RT_PrintNumber(float x, float y, const char *string, int orientation)
{
    while (*string != '\0' && *string != '\n' && *string != '\r')
    {
        int tile = *string;
        if (*string >= '0' && *string <= '9')
            tile = *string + 3689;
        if (*string == '%')
            tile = 3736;

        float sx, sy;
        if (orientation & 256)
            sx = RT_SBarScale(x - borderx1) + borderx1;
        else if (orientation & 512)
            sx = RT_SBarScale(x - borderx2) + borderx2;
        else
            sx = RT_SBarScale(x - (borderx1 + borderx2) * 0.5f) + (borderx1 + borderx2) * 0.5f;
        sy = RT_SBarScale(y - bordery2) + bordery2;
        float sc = RT_SBarScale(100.f);
        RT_RotateSpriteText(sx, sy, sc, sc, tile, orientation);
        x += 4.f;
        string++;
    }
}

void RT_DrawBar(DukePlayer_t *const pPlayer, bool drawhud)
{
    char buf[10];
    static int ammo_icon[MAX_WEAPONS] = {
        BOOTS, AMMO, SHOTGUNAMMO, BATTERYAMMO, RPGAMMO, HBOMBAMMO, CRYSTALAMMO, GROWAMMO,
        DEVISTATORAMMO, TRIPBOMBSPRITE, FREEZEAMMO, HBOMBAMMO, 34, 50, 43
    };
    if (!barscrolldir)
        return;

    // if (ud.multimode > 1 && !ud.coop && dukematch_mode != 4)
    // {
    // }

    if (pPlayer->dn64_372 != KNEE_WEAPON && drawhud)
    {
        if (numplayers < 3)
        {
            int tile = ammo_icon[pPlayer->dn64_372];
            int s = RT_InvTileScale(16, tile);
            float x;
            if (numplayers < 3)
                x = 54.f;
            else
                x = 14.f;
            x = RT_SBarScale(x) + borderx1;
            float y = RT_SBarScale(-13.f) + bordery2;
            float sc = RT_SBarScale(s);
            RT_RotateSpriteSetColor(128, 128, 128, 256);
            RT_RotateSprite(x, y, sc, sc, tile, 256|RTRS_SCALED);
        }
        int weaponNum = pPlayer->dn64_372;
        if (weaponNum == HANDREMOTE_WEAPON)
            weaponNum = HANDBOMB_WEAPON;

        sprintf(buf, "%d\n", pPlayer->ammo_amount[weaponNum]);
        if (weaponNum == BOWLINGBALL_WEAPON || weaponNum == MOTORCYCLE_WEAPON || weaponNum == BOAT_WEAPON)
            RT_RotateSpriteSetColor(255, 255, 128, 666);
        else
            RT_RotateSpriteSetColor(255, 255, 255, 666);
        if (numplayers < 3)
            RT_PrintNumber(borderx1 + 52.f, bordery2 - 16.f, buf, 256|RTRS_SCALED);
        // else
        //     RT_GameText();

    }
    int invTile = 0;
    int invAmount = 0;
    bool invActive = false;

    switch (pPlayer->inven_icon)
    {
    case ICON_NONE:
        invTile = 0;
        invAmount = 0;
        break;
    case ICON_FIRSTAID:
        invTile = FIRSTAID;
        invAmount = pPlayer->inv_amount[GET_FIRSTAID];
        break;
    case ICON_STEROIDS:
        invTile = STEROIDS;
        invAmount = (pPlayer->inv_amount[GET_STEROIDS] + 3) / 4;
        invActive = invAmount < 100;
        break;
    case ICON_HOLODUKE:
        invTile = HOLODUKE;
        invActive = pPlayer->holoduke_on > 0;
        invAmount = (pPlayer->inv_amount[GET_HOLODUKE] + 15) / 24;
        break;
    case ICON_JETPACK:
        invTile = JETPACK;
        invActive = pPlayer->jetpack_on != 0;
        invAmount = (pPlayer->inv_amount[GET_JETPACK] + 15) / 16;
        break;
    case ICON_HEATS:
        invTile = HEATSENSOR;
        invActive = pPlayer->heat_on != 0;
        invAmount = pPlayer->inv_amount[GET_HEATS] / 12;
        break;
    case ICON_SCUBA:
        invTile = AIRTANK;
        invAmount = (pPlayer->inv_amount[GET_SCUBA] + 63) / 64;
        break;
    case ICON_BOOTS:
        invTile = BOOTS;
        invAmount = pPlayer->inv_amount[GET_BOOTS] / 2;
        break;
    case ICON_SHIELD:
        invTile = SHIELD;
        invAmount = pPlayer->inv_amount[GET_SHIELD];
        break;
    default:
        break;
    }
    if (numplayers > 2 && invActive && invAmount < 30)
        pPlayer->invdisptime = 20;
    if (invTile && drawhud)
    {
        int s = RT_InvTileScale(16, invTile);
        int alpha;
        if (numplayers < 3)
            alpha = 256;
        else
            alpha = (pPlayer->invdisptime << 8) / 10;
        RT_RotateSpriteSetColor(255, 255, 255, alpha);
        float x = RT_SBarScale(-40.f) + borderx2;
        float y = RT_SBarScale(-11.f) + bordery2;
        float sc = RT_SBarScale(s);
        RT_RotateSprite(x, y, sc, sc, invTile, 512|RTRS_SCALED);
        sprintf(buf, "%d%%\n", invAmount);
        if (invAmount < 30)
            RT_RotateSpriteSetColor(255, 255, 64, alpha);
        else
            RT_RotateSpriteSetColor(255, 255, 255, alpha);
        RT_PrintNumber(borderx2 - 23.f, bordery2 - 10.f, buf, 512|RTRS_SCALED);
        if (pPlayer->inven_icon > 2)
        {
            x = RT_SBarScale(-23.f) + borderx2;
            y = RT_SBarScale(-20.f) + bordery2;
            sc = RT_SBarScale(100.f);
            if (pPlayer->inven_icon < 6)
                RT_RotateSpriteText(x, y, sc, sc, invActive ? 0xea5 : 0xea4, 512|RTRS_SCALED);
            else
                RT_RotateSpriteText(x, y, sc, sc, 0xea3, 512|RTRS_SCALED);
        }
    }
    if (drawhud)
    {
        sprintf(buf, "%d", pPlayer->last_extra);
        if (pPlayer->last_extra < 30)
            RT_RotateSpriteSetColor(255, 64, 64, 666);
        else
            RT_RotateSpriteSetColor(255, 255, 255, 666);
        if (numplayers < 3)
        {
            float x = RT_SBarScale(14.f) + borderx1;
            float y = RT_SBarScale(-18.f) + bordery2;
            float sc = RT_SBarScale(65536.f);
            G_ScreenText(STARTALPHANUM, (int)(x * 65536.f), (int)(y * 65536.f), (int)sc, 0, 0, buf, 0, 0, ROTATESPRITE_FULL16|256,
                0, 7, 0, 0, 0, TEXT_N64COORDS|TEXT_N64NOPAL|TEXT_XCENTER, 0, 0, xdim-1, ydim-1);
        }
    }
    RT_RotateSpriteSetColor(255, 255, 255, 666);
    if (drawhud && (ud.multimode == 1 || ud.coop == 1))
    {
        if (numplayers < 2)
        {
            if (pPlayer->got_access & 1)
            {
                float x = RT_SBarScale(-0x3d) + borderx2;
                float y = RT_SBarScale(-0x18) + bordery2;
                float sc = RT_SBarScale(100.f);
                RT_RotateSprite(x, y, sc, sc, 0xe25, 512|RTRS_SCALED);
            }
            if (pPlayer->got_access & 2)
            {
                float x = RT_SBarScale(-0x41) + borderx2;
                float y = RT_SBarScale(-0x12) + bordery2;
                float sc = RT_SBarScale(100.f);
                RT_RotateSprite(x, y, sc, sc, 0xe26, 512|RTRS_SCALED);
            }
            if (pPlayer->got_access & 4)
            {
                float x = RT_SBarScale(-0x41) + borderx2;
                float y = RT_SBarScale(-10) + bordery2;
                float sc = RT_SBarScale(100.f);
                RT_RotateSprite(x, y, sc, sc, 0xe27, 512|RTRS_SCALED);
            }
        }
    }
}

void RT_DisplayWeaponIcon(float x, float y, int offset, int tile)
{
    int ox = sintable[(offset * 6) & 2047];
    float oy = sintable[(offset * 6 + 512) & 2047] / 65536.f + 0.75f;
    int shade = (int)((oy * 5.f - 4.f) * 255.f);
    int alpha = (int)((oy * 2.f - 1.f) * 256.f);
    RT_RotateSpriteSetColor(shade, shade, shade, alpha);;
    float sx = RT_InvTileScale(16, tile);
    float sy = RT_InvTileScale(16, tile);
    RT_RotateSprite(RT_SBarScale(x + (float)((ox * 60) >> 14) - (borderx1+borderx2)/2.f)+(borderx1+borderx2)/2.f,
        RT_SBarScale(y - bordery2) + bordery2, RT_SBarScale(sx), RT_SBarScale(sy), tile, RTRS_SCALED);
}

void RT_DrawWeaponWheel(DukePlayer_t *const pPlayer, bool drawhud)
{
    if (pPlayer->dn64_378 > 0)
    {
        float x = (borderx1 + borderx2) / 2.f;
        float y = bordery2;
        float yfactor = drawhud ? 1.f : 1.5f;
        int weaponNum = pPlayer->dn64_372;
        if (weaponNum == BOWLINGBALL_WEAPON)
            weaponNum = PISTOL_WEAPON;
        else if (weaponNum == MOTORCYCLE_WEAPON)
            weaponNum = SHOTGUN_WEAPON;
        else if (weaponNum == BOAT_WEAPON)
            weaponNum = RPG_WEAPON;
        int wl1 = P_NextWeapon(pPlayer, weaponNum, -1);
        int wr1 = P_NextWeapon(pPlayer, weaponNum, 1);
        int wl2 = P_NextWeapon(pPlayer, wl1, -1);
        int wr2 = P_NextWeapon(pPlayer, wr1, 1);

        RT_DisplayWeaponIcon(x, y - pPlayer->dn64_378 * yfactor, -120 - pPlayer->dn64_374, WeaponPickupSprites[wl2]);
        RT_DisplayWeaponIcon(x, y - pPlayer->dn64_378 * yfactor, 120 - pPlayer->dn64_374, WeaponPickupSprites[wr2]);
        RT_DisplayWeaponIcon(x, y - pPlayer->dn64_378 * yfactor, -60 - pPlayer->dn64_374, WeaponPickupSprites[wl1]);
        RT_DisplayWeaponIcon(x, y - pPlayer->dn64_378 * yfactor, 60 - pPlayer->dn64_374, WeaponPickupSprites[wr1]);
        RT_DisplayWeaponIcon(x, y - pPlayer->dn64_378 * yfactor, -pPlayer->dn64_374, WeaponPickupSprites[weaponNum]);
    }
}

int RT_DrawStatusBar(int snum)
{
    RT_DisablePolymost(0);
    DukePlayer_t *const p = g_player[snum].ps;

    bool drawhud = ud.screen_size == 4 && !ud.althud;

    RT_DrawWeaponWheel(p, drawhud);

    RT_DrawBarBG(drawhud);

    RT_DrawBar(p, drawhud);

    RT_DrawBarOverlay(drawhud);

    RT_EnablePolymost();

    return drawhud;
}


code:
// Copyright: 2020 Nuke.YKT, EDuke32 developers
// License: GPLv2
#pragma once

void RT_ResetBarScroll(void);
int RT_DrawStatusBar(int snum);


code:
// Copyright: 2020 Nuke.YKT, EDuke32 developers
// License: GPLv2
#include "compat.h"
#include "build.h"
#include "reality.h"
#include "../duke3d.h"
#include "../input.h"

const char *rt_scenestrings[] = {
    "WHEN WE LAST SAW DUKE NUKEM, THE "
    "EARTH'S ONLY REMAINING HOPE HAD "
    "SINGLE-HANDEDLY DEFEATED THE "
    "DESPICABLE DR. PROTON AND HIS "
    "LEGION OF EVIL ROBOTS. ",

    "WITH PROTON OUT OF THE WAY, "
    "DUKE RETURNED HOME TO A HERO'S "
    "WELCOME, AND WAS IMMEDIATELY "
    "ABDUCTED BY RIGELATINS - BEINGS "
    "GENERALLY KNOWN AS ALIEN SCUM "
    "FROM THE SEWERS OF SPACE. ",

    "USING EXPLOSIVES WISELY HIDDEN "
    "INSIDE A MOLAR, DUKE ESCAPED, "
    "DESTROYED THE RIGELATIN "
    "MOTHERSHIP AND CRUISED HOME IN A "
    "STOLEN, YET SUITABLY COOL, SHIP. ",

    "HIS LONG HAUL BACK TO EARTH, "
    "SHORTENED BY THOUGHTS OF ADORING "
    "CROWDS, CURVACEOUS BEACHES... ",

    "AND NO MORE FREAKIN' ALIENS! ",

    "WARNING!! INCOMING!! ",

    "HEY, EARTH? ANYBODY LISTENING? "
    "I GOT A LITTLE PROB... ",

    "AFTER WIPING THE BLOOD AND "
    "BRAINS FROM HIS BOOTS, DUKE "
    "EXPLORED THE ALIEN CRAFT. ",

    "MONITORS SHOWED A TITANIC ALIEN "
    "SHIP HOVERING ABOVE EARTH, WITH "
    "DOZENS OF SMALLER CRAFT "
    "OFF-LOADING COCOON-LIKE PODS. ",

    "ONE SHOWED THEM IN CLOSE-UP. "
    "THEY ALL HELD WOMEN, STILL "
    "ALIVE, JUST LIKE THE ONES DUKE "
    "HAD ENCOUNTERED. ",

    "DUKE GLOWERED IN THE PALE GREEN "
    "MONITOR LIGHT, AND SET THE "
    "AUTO-DESTRUCT SEQUENCE ON THE "
    "ALIEN SHIP. ",

    "HE STARED AT THE SCREEN ONCE "
    "MORE... ",

    "NOBODY MESSES WITH DUKE NUKEM! "
    "TIME FOR THAT VACATION, AND NO "
    "MORE FREAKIN' ALIENS!! ",

    "A HOLOGRAM FLICKERED ON, AND AN "
    "INCREDIBLY UGLY FACE SPOKE AN "
    "OMINOUS MESSAGE. ",

    "THE MOON ASSAULT OVERLORD HAS "
    "BEEN DEFEATED, AS HAS OUR "
    "BATTLELORD ON EARTH. ",

    "BUT WHILE DUKE NUKEM HAS BEEN "
    "DISTRACTED, OUR MAIN ATTACK "
    "FORCE HAS BEGUN IT'S FINAL "
    "ASSAULT ON EARTH. ",

    "WE SHALL OBLITERATE ALL "
    "RESISTANCE! "
};

struct {
    int type;
    int y;
    const char *string;
} rt_credits[] = {
    2, 0xfa, "DUKE NUKEM 64",
    2, 0x122, "DEVELOPED BY",
    1, 0x136, "EUROCOM ENTERTAINMENT SOFTWARE",
    2, 0x154, "PRODUCER",
    1, 0x168, "RICK RAYMO",
    2, 0x186, "PROGRAMMING",
    1, 0x19a, "ASHLEY FINNEY",
    1, 0x1a9, "SIMON MILLS",
    2, 0x1c7, "LEVEL DESIGN",
    1, 0x1db, "BILL BEACHAM",
    1, 0x1ea, "KEV HARVEY",
    2, 0x208, "GRAPHICS",
    1, 0x21c, "STEVE BAMFORD",
    1, 0x22b, "ANDY BEE",
    1, 0x23a, "ROB BENTON",
    1, 0x249, "NICK DRY",
    1, 0x258, "PAUL GREGORY",
    1, 0x267, "DARREN HYLAND",
    1, 0x276, "ADRIAN MANNION",
    2, 0x294, "AUDIO",
    1, 0x2a8, "NEIL BALDWIN",
    1, 0x2b7, "STEVE DUCKWORTH",
    2, 0x2d5, "PROJECT MANAGEMENT",
    1, 0x2e9, "HUGH BINNS",
    1, 0x2f8, "BILL BEACHAM",
    2, 0x316, "QUALITY APPROVAL",
    1, 0x32a, "MIKE BOTHAM",
    1, 0x339, "ANDY COLLINS",
    1, 0x348, "AARON JENKINS",
    2, 0x366, "GT EUROPE PRODUCER",
    1, 0x37a, "ROB LETTS",
    2, 0x398, "EUROPEAN LOCALISATION",
    1, 0x3ac, "CARA MCMULLAN",
    2, 0x3ca, "MANUAL",
    1, 0x3de, "NIC LAVROFF",
    1, 0x3ed, "BILL BEACHAM",
    2, 0x40b, "VOICE TALENT",
    1, 0x41f, "JON ST. JOHN AS 'DUKE NUKEM'",
    2, 0x43d, "SPECIAL THANKS TO",
    1, 0x451, "PAUL BATES",
    1, 0x460, "TIM ROGERS",
    1, 0x46f, "MAT SNEAP",
    1, 0x47e, "MR WHIPPLE AND THE MARIO CLUB",
    1, 0x48d, "KEN SILVERMAN",
    1, 0x49c, "TODD REPLOGLE",
    1, 0x4ab, "SCOTT MILLER",
    1, 0x4ba, "GEORGE BROUSSARD",
    1, 0x4c9, "TONY KEE",
    1, 0x4d8, "DAN HARNETT",
    1, 0x4e7, "MAX TAYLOR",
    1, 0x4f6, "STEW KOSOY",
    1, 0x505, "GRAEME BOXALL",
    1, 0x514, "JIM TRIPP",
    2, 0x546, "DUKE NUKEM 3D",
    2, 0x56e, "3D REALMS TEAM",
    2, 0x596, "ORIGINAL CONCEPT",
    1, 0x5aa, "TODD REPLOGLE",
    1, 0x5b9, "ALLEN H. BLUM III",
    2, 0x5d7, "EXECUTIVE PRODUCER",
    1, 0x5eb, "GEORGE BROUSSARD",
    2, 0x609, "PRODUCER",
    1, 0x61d, "GREG MALONE",
    2, 0x63b, "BUILD ENGINE AND TOOLS",
    1, 0x64f, "KEN SILVERMAN",
    2, 0x66d, "GAME PROGRAMMING",
    1, 0x681, "TODD REPLOGLE",
    2, 0x69f, "NETWORK LAYER",
    1, 0x6b3, "MARK DOCHTERMANN",
    2, 0x6d1, "MAP DESIGN",
    1, 0x6e5, "ALLEN H. BLUM III",
    1, 0x6f4, "RICHARD GRAY",
    2, 0x712, "3D MODELLING",
    1, 0x726, "CHUCK JONES",
    1, 0x735, "SAPPHIRE",
    2, 0x753, "ARTWORK",
    1, 0x767, "DIRK JONES",
    1, 0x776, "STEPHEN HORNBACK",
    1, 0x785, "JAMES STOREY",
    1, 0x794, "DAVID DEMARET",
    1, 0x7a3, "DOUGLAS R. WOOD",
    2, 0x7c1, "SOUND ENGINE",
    1, 0x7d5, "JIM DOSE",
    2, 0x7f3, "SOUND AND MUSIC",
    1, 0x807, "ROBERT PRINCE",
    1, 0x816, "LEE JACKSON",
    2, 0x834, "VOICE PRODUCER",
    1, 0x848, "LANI MINELLA",
    2, 0x866, "VOICE TALENT",
    1, 0x87a, "JON ST. JOHN AS 'DUKE NUKEM'",
};

static int intro_state = 0;
static int intro_sndcnt = 0;

void RT_GameText(float x, float y, const char *text)
{
    if (x == -1)
    {
        G_ScreenText(STARTALPHANUM, 160 * 65536.f, y * 65536.f, 65536, 0, 0, text, 0, 0, ROTATESPRITE_FULL16, 0, 7 * 65536, 0, 0, 0,
            TEXT_XCENTER | TEXT_N64COORDS | TEXT_N64NOPAL, 0, 0, xdim, ydim);
        return;
    }
    G_ScreenText(STARTALPHANUM, x * 65536.f, y * 65536.f, 65536, 0, 0, text, 0, 0, ROTATESPRITE_FULL16, 0, 7 * 65536, 0, 0, 0,
        TEXT_N64COORDS | TEXT_N64NOPAL, 0, 0, xdim, ydim);
}

void RT_MenuText(float x, float y, const char *text)
{
    if (x == -1)
    {
        G_ScreenText(BIGALPHANUM, 160 * 65535.f, y * 65535.f, 65536, 0, 0, text, 0, 0, ROTATESPRITE_FULL16, 0, 12 * 65536, 0, 0, 0,
            TEXT_XCENTER | TEXT_N64COORDS | TEXT_N64NOPAL | TEXT_BIGALPHANUM, 0, 0, xdim, ydim);
        return;
    }
    G_ScreenText(BIGALPHANUM, x * 65535.f, y * 65535.f, 65536, 0, 0, text, 0, 0, ROTATESPRITE_FULL16, 0, 12 * 65536, 0, 0, 0,
        TEXT_N64COORDS | TEXT_N64NOPAL | TEXT_BIGALPHANUM, 0, 0, xdim, ydim);
}

void RT_IntroAdvance(bool advance)
{
    if (advance)
    {
        intro_state++;
        ototalclock = totalclock;
        intro_sndcnt = 0;
    }
}

void RT_IntroText(int textId, int totaltime)
{
    char textline[34] = "";
    int intro_time = (int)(totalclock - ototalclock);
    int y = textId < 7 ? 20 : 180;
    int alpha = 0;
    if (totaltime - 16 * 4 < intro_time)
        alpha = (totaltime - intro_time) * 4;
    else
        alpha = min(intro_time * 4, 256);

    int l = strlen(rt_scenestrings[textId]);
    switch (textId)
    {
    case 4:
    case 6:
    case 12:
        RT_RotateSpriteSetColor(127, 255, 127, alpha);
        break;
    case 5:
        RT_RotateSpriteSetColor(255, 127, 127, alpha);
        break;
    case 14:
    case 15:
    case 16:
        RT_RotateSpriteSetColor(255, 127, 255, alpha);
        break;
    default:
        RT_RotateSpriteSetColor(255, 255, 255, alpha);
        break;
    }
    RT_RenderUnsetScissor();

    for (int i = 0; i < l;)
    {
        const char *t = rt_scenestrings[textId];
        int j;
        for (j = 0; j < 33 && i + j < l; j++)
            textline[j] = t[i + j];
        textline[j] = '\0';
        while (textline[j] != ' ' && j > 0)
        {
            textline[j] = '\0';
            j--;
        }
        RT_GameText(-1, y, textline);
        i += j + 1;
        y += 10;
    }
}

void RT_Intro(void)
{
    I_ClearAllInput();
    ototalclock = totalclock;
    intro_state = 1;
    S_TryPlaySpecialMusic(MUS_INTRO);
    bool playing = true;
    while (playing && !I_CheckAllInput())
    {
        if (engineFPSLimit())
        {
            RT_RenderUnsetScissor();
            videoClearScreen(0L);
            int intro_time = (int)(totalclock - ototalclock);
            RT_DisablePolymost(0);
            switch (intro_state)
            {
            case 0:
            {
                int alpha = max(256 - intro_time * 2, 0);
                RT_RotateSpriteSetColor(alpha, alpha, alpha, 256);
                RT_RotateSprite(160, 120, 100, 100, 0xe56, RTRS_SCALED);
                RT_IntroAdvance(alpha == 0);
                break;
            }
            case 1:
            {
                RT_IntroText(0, 160 * 4);
                RT_IntroAdvance(intro_time >= 160 * 4);
                break;
            }
            case 2:
            {
                RT_RenderScissor(91, 68, 229, 172);
                int alpha = min(intro_time * 2, 255);
                RT_RotateSpriteSetColor(alpha, alpha, alpha, 256);
                RT_RotateSprite(160, 120, 100, 100, 0xf56, RTRS_SCALED);
                RT_IntroText(1, 160 * 4);
                RT_IntroAdvance(intro_time >= 160 * 4);
                break;
            }
            case 3:
            {
                if (intro_time >= 60 * 4 && intro_sndcnt == 0)
                {
                    S_PlaySound(0xba);
                    intro_sndcnt++;
                }
                RT_RenderScissor(91, 68, 229, 172);
                int alpha = min(intro_time * 2, 255);
                RT_RotateSpriteSetColor(255, 255, 255, 256);
                RT_RotateSprite(160, 120, 100, 100, 0xf56, RTRS_SCALED);
                if (intro_time >= 60 * 4)
                {
                    float x = min<float>(intro_time * (1.f/4.f) - 60.f, 97.f);
                    float z = max<float>(50.f - (intro_time * (1.f/4.f) + 40.f) * 0.25f, 0.75);
                    RT_RotateSprite(x + 100, 120, 100.f / z, 100.f /z, 0xf57, RTRS_SCALED);
                }
                RT_IntroText(2, 160 * 4);
                RT_IntroAdvance(intro_time >= 160 * 4);
                break;
            }
            case 4:
            {
                float z = max(140.f - max(intro_time - 20 * 4, 0) * (1.f/4.f), 100.f);
                RT_RotateSpriteSetColor(255, 255, 255, 256);
                RT_RotateSprite(160, 120, z, z, 0xf58, RTRS_SCALED);
                RT_IntroText(3, 130 * 4);
                RT_IntroAdvance(intro_time >= 130 * 4);
                break;
            }
            case 5:
            {
                float z = max(140.f - max(intro_time - 20 * 4, 0) * (1.f/4.f), 100.f);
                RT_RotateSpriteSetColor(255, 255, 255, 256);
                RT_RotateSprite(160, 120, 100, 100, 0xf5a, RTRS_SCALED);
                RT_IntroAdvance(intro_time >= 40 * 4);
                break;
            }
            case 6:
            {
                int alpha = clamp(intro_time - 80, 0, 256);
                RT_RotateSpriteSetColor(255, 255, 255, 256 - alpha);
                RT_RotateSprite(160, 120, 100, 100, 0xf59, RTRS_SCALED);
                RT_RotateSpriteSetColor(255, 255, 255, alpha);
                RT_RotateSprite(160, 120, 100, 100, 0xf5b, RTRS_SCALED);
                RT_IntroText(4, 154 * 4);
                RT_IntroAdvance(intro_time >= 154 * 4);
                break;
            }
            case 7:
            {
                if (intro_time >= 4 && intro_sndcnt == 0)
                {
                    S_PlaySound(0x2d);
                    intro_sndcnt++;
                }
                if (intro_time >= 16*4 && intro_sndcnt == 1)
                {
                    S_PlaySound(0x37);
                    intro_sndcnt++;
                }
                if (intro_time >= 32*4 && intro_sndcnt == 2)
                {
                    S_PlaySound(0xb4);
                    intro_sndcnt++;
                }
                RT_RenderScissor(91, 68, 229, 172);
                RT_RotateSpriteSetColor(255, 255, 255, 256);
                float z = intro_time * (1.f/4.f) * 2.3f + 100.f;
                if ((intro_time & 32) == 0 && intro_time < 64 * 4)
                    RT_RotateSprite(160.f + intro_time * (1.f/4.f), 120.f + intro_time * (1.f/8.f), z, z, 0xf59, RTRS_SCALED);
                else
                    RT_RotateSprite(160.f, 120.f, 100.f, 100.f, 0xf5c, RTRS_SCALED);
                RT_IntroText(5, 79 * 4);
                RT_IntroAdvance(intro_time >= 79 * 4);
                break;
            }
            case 8:
            {
                if (intro_time >= 4 && intro_sndcnt == 0)
                {
                    S_PlaySound(0xc3);
                    intro_sndcnt++;
                }
                int o = 230 - intro_time * 2;
                RT_RotateSpriteSetColor(255, 255, 255, 256);
                RT_RotateSprite(160 + o, 120 - o / 3.f, 100, 100, 0xf5d, RTRS_SCALED);
                RT_IntroText(6, 57 * 4);
                RT_IntroAdvance(o < -230);
                break;
            }
            case 9:
            {
                if (intro_time >= 4 && intro_sndcnt == 0)
                {
                    S_StopMusic();
                    S_PlaySound(8);
                    intro_sndcnt++;
                }
                int alpha = max(376 - intro_time * 3 / 2, 0);
                RT_RotateSpriteSetColor(alpha, alpha, alpha, 256);
                float z = intro_time * (1.f/4.f) * 2.35f + 75.f;
                RT_RotateSprite(160, 120, z, z, 0xf5e, RTRS_SCALED);
                RT_IntroAdvance(alpha == 0);
                break;
            }
            default:
                playing = false;
                break;
            }
            RT_EnablePolymost();
            videoNextPage();

            G_HandleAsync();
        }
    }

    RT_RenderUnsetScissor();
    videoClearScreen(0L);
    videoNextPage();
}

void RT_BossScene(void)
{
    I_ClearAllInput();
    ototalclock = totalclock;
    intro_state = 0;
    bool playing = true;

    int tile = rt_levelnum == 6 ? 0xe6b : 0xe6c;
    int text = rt_levelnum == 6 ? 7 : 12;

    while (playing)
    {
        if (engineFPSLimit())
        {
            if (I_CheckAllInput() && intro_state < 6)
            {
                I_ClearAllInput();
                intro_state = 6;
            }
            videoClearScreen(0L);
            int intro_time = (int)(totalclock - ototalclock);
            RT_DisablePolymost(0);
            switch (intro_state)
            {
            case 0:
            {
                int alpha = min(intro_time * 4, 255);
                RT_RotateSpriteSetColor(alpha, alpha, alpha, 256);
                RT_RotateSprite(160, 120, 100, 100, tile, RTRS_SCALED);
                RT_IntroAdvance(intro_time * 4 >= 256);
                break;
            }
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
            {
                RT_RotateSpriteSetColor(255, 255, 255, 256);
                RT_RotateSprite(160, 120, 100, 100, tile, RTRS_SCALED);
                RT_IntroText(text + intro_state - 1, 150 * 4);
                RT_IntroAdvance(intro_time >= 150 * 4);
                break;
            }
            case 6:
            {
                int alpha = max(256 - intro_time * 4, 0);
                RT_RotateSpriteSetColor(alpha, alpha, alpha, 256);
                RT_RotateSprite(160, 120, 100, 100, tile, RTRS_SCALED);
                RT_IntroAdvance(alpha == 0);
                break;
            }
            default:
                playing = false;
                break;
            }
            RT_EnablePolymost();
            videoNextPage();

            G_HandleAsync();
        }
    }

    videoClearScreen(0L);
    videoNextPage();
}

void RT_CreditsText(int type, float y, const char *string)
{
    if (y >= 0 && y <= 240)
    {
        int alpha = 0;
        if (y > 192)
            alpha = (3328 - y * 16);
        else
            alpha = min<int>(y * 16 - 256, 256);
        RT_RotateSpriteSetColor(255, 255, 255, alpha);
        switch (type)
        {
        case 1:
            RT_GameText(-1, y, string);
            break;
        case 2:
        default:
            RT_MenuText(-1, y, string);
            break;
        }
    }
}

void RT_FinalBossScene(void)
{
    I_ClearAllInput();
    ototalclock = totalclock;
    intro_state = 0;
    int soundcnt = 0;
    bool playing = true;

    while (playing)
    {
        if (engineFPSLimit())
        {
            if (I_CheckAllInput())
            {
                if (intro_state < 6)
                {
                    I_ClearAllInput();
                    intro_state = 6;
                    ototalclock = totalclock;
                }
                else if (intro_state == 6)
                {
                    I_ClearAllInput();
                    intro_state++;
                }
            }
            videoClearScreen(0L);
            int intro_time = (int)(totalclock - ototalclock);
            RT_DisablePolymost(0);
            switch (intro_state)
            {
            case 0:
            {
                int alpha = min(intro_time * 4, 255);
                RT_RotateSpriteSetColor(alpha, alpha, alpha, 256);
                RT_RotateSprite(160, 120, 100, 100, 0xe6d, RTRS_SCALED);
                RT_IntroAdvance(intro_time >= 60 * 4);
                break;
            }
            case 1:
            {
                if (intro_time >= 4)
                {
                    if (soundcnt < 1)
                    {
                        S_PlaySound(0x15);
                        soundcnt = 1;
                    }
                }
                RT_RotateSpriteSetColor(255, 255, 255, 256);
                RT_RotateSprite(160, 120, 100, 100, 0xe6e, RTRS_SCALED);
                RT_IntroAdvance(intro_time >= 60 * 4);
                break;
            }
            case 2:
            {
                RT_RotateSpriteSetColor(255, 255, 255, 256);
                RT_RotateSprite(160, 120, 100, 100, 0xe6f, RTRS_SCALED);
                RT_IntroAdvance(intro_time >= 60 * 4);
                break;
            }
            case 3:
            {
                if (intro_time >= 4)
                {
                    if (soundcnt < 2)
                    {
                        S_PlaySound(0x2c);
                        soundcnt = 2;
                    }
                }
                RT_RotateSpriteSetColor(255, 255, 255, 256);
                RT_RotateSprite(160, 120, 100, 100, 0xe70, RTRS_SCALED);
                RT_IntroAdvance(intro_time >= 60 * 4);
                break;
            }
            case 4:
            {
                if (intro_time >= 4)
                {
                    if (soundcnt < 3)
                    {
                        S_PlaySound(0x19);
                        soundcnt = 3;
                    }
                }
                RT_RotateSpriteSetColor(255, 255, 255, 256);
                RT_RotateSprite(160, 120, 100, 100, 0xe71, RTRS_SCALED);
                RT_IntroAdvance(intro_time >= 60 * 4);
                break;
            }
            case 5:
            {
                if (intro_time >= 4)
                {
                    if (soundcnt < 4)
                    {
                        S_PlaySound(0x18);
                        soundcnt = 4;
                    }
                }
                int alpha = max(60 * 16 - intro_time * 4, 0);
                RT_RotateSpriteSetColor(alpha, alpha, alpha, 256);
                RT_RotateSprite(160, 120, 100, 100, 0xe72, RTRS_SCALED);
                RT_IntroAdvance(intro_time >= 60 * 4);
                break;
            }
            case 6:
            {
                if (soundcnt < 5)
                {
                    S_PlaySpecialMusicOrNothing(MUS_INTRO);
                    soundcnt = 5;
                }
                int alpha = min(intro_time * 4, 255);
                RT_RotateSpriteSetColor(alpha, alpha, alpha, 256);
                RT_RotateSprite(160, 120, 100, 100, 0xe56, RTRS_SCALED);
                float y = -intro_time * (1.f / 8.f);
                for (int i = 0; i < ARRAY_SIZE(rt_credits); i++)
                {
                    RT_CreditsText(rt_credits[i].type, rt_credits[i].y + y, rt_credits[i].string);
                }
                RT_IntroAdvance(y + 2190 < 0);
                break;
            }
            default:
                playing = false;
                break;
            }
            RT_EnablePolymost();
            videoNextPage();

            G_HandleAsync();
        }
    }
    I_ClearAllInput();

    videoClearScreen(0L);
    videoNextPage();
}

void RT_Bonus(void)
{
    G_UpdateAppTitle();
    videoClearScreen(0L);
    videoNextPage();

    S_StopMusic();
    FX_StopAllSounds();
    S_ClearSoundLocks();
    FX_SetReverb(0L);
    CONTROL_BindsEnabled = 1; // so you can use your screenshot bind on the score screens

    if (ud.multimode == 1 || ud.coop)
    {
        if (rt_levelnum == 6 || rt_levelnum == 17)
        {
            RT_BossScene();
        }
        if (rt_levelnum == 27)
        {
            RT_FinalBossScene();

            I_ClearAllInput();

            videoClearScreen(0);
            videoNextPage();
            return;
        }
    }

    totalclock = 0;

    int bonus_state = 0;
    int soundcnt = 0;
    int soundcnt2 = 0;
    int soundcnt3 = 0;
    float bonus_oalpha = 0.f;

    S_StopMusic();
    FX_StopAllSounds();
    S_ClearSoundLocks();

    I_ClearAllInput();
    do
    {
        G_HandleAsync();
        MUSIC_Update();
        if (engineFPSLimit())
        {
            videoClearScreen(0L);
            RT_DisablePolymost(0);
            int buttons = 0;
            float bonus_alpha = 0.f;
            if (bonus_state == 0)
            {
                bonus_alpha = min(((int)totalclock * 4) * (1.f / 256.f), 1.f);
                buttons = I_CheckAllInput() != 0;
            }
            else
            {
                bonus_alpha = bonus_oalpha - ((int)totalclock - (int)ototalclock) * (1.f / 64.f);
                buttons = 0;
            }
            if (buttons)
            {
                if (soundcnt == 0)
                {
                    S_PlaySound(12);
                    soundcnt++;
                    bonus_state = 69;
                    bonus_oalpha = bonus_alpha;
                    ototalclock = totalclock;
                }
            }
            if (bonus_state != 69 || bonus_alpha > 0)
            {
                /*if (ud.multimode > 2 && !ud.coop)
                {
                    bonusmp();
                }
                else*/
                {
                    char buf[8];
                    RT_RotateSpriteSetColor(bonus_alpha * 255.f, bonus_alpha * 255.f, bonus_alpha * 255.f, 256);
                    RT_RotateSprite(160.f, 120.f, 100.f, 100.f, 0xf09, RTRS_SCALED);
                    int v3 = max((int)totalclock - 112 * 4, 0);
                    int dukeframe;
                    if (v3 < 20 * 4)
                        dukeframe = 0;
                    else
                    {
                        dukeframe = 2;
                        if (v3 < 36 * 4)
                            dukeframe = (v3 - 20 * 4) / 16;
                    }
                    if (v3 >= 28 * 4)
                    {
                        if (!soundcnt2)
                        {
                            S_PlaySound(0x83);
                            soundcnt2++;
                        }
                    }
                    RT_RotateSprite(79, 137.5, 100, 100, 0xf0a + dukeframe, RTRS_SCALED); // hack
                    RT_RotateSprite(79, 138, 100, 100, 0xf0a + dukeframe, RTRS_SCALED);
                    RT_RotateSpriteSetColor(bonus_alpha * 150.f, bonus_alpha * 200.f, bonus_alpha * 255.f, 256);
                    RT_MenuText(-1, 30, rt_level_names[rt_levelnum]);
                    RT_GameText(-1, 47, "COMPLETED");
                    int kills = g_player[0].ps->actors_killed;
                    int secrets = g_player[0].ps->secret_rooms;
                    int babes = g_player[0].ps->dn64_36e;
                    int secretstotal = g_player[0].ps->max_secret_rooms;
                    int babestotal = g_player[0].ps->dn64_36d;
                    int killstotal = 0;
                    for (int i = headspritestat[1]; i != -1; i = nextspritestat[i])
                    {
                        if (A_CheckEnemySprite(&sprite[i]) && sprite[i].picnum != RAT && sprite[i].extra > 0)
                        {
                            killstotal++;
                        }
                    }
                    for (int i = headspritestat[2]; i != -1; i = nextspritestat[i])
                    {
                        if (A_CheckEnemySprite(&sprite[i]) && sprite[i].picnum != RAT && sprite[i].extra > 0)
                        {
                            killstotal++;
                        }
                    }
                    /*if ((buttons & 0x8000) && controllerpak_port != -1)
                    {
                        S_PlaySound(12);
                        bonus_state = 42;
                    }
                    if (bonus_state == 42 && bonus_alpa <= 0.f)
                    {
                        // savegame
                    }
                    */
                    if ((int)totalclock > 16 * 4)
                    {
                        RT_RotateSpriteSetColor(bonus_alpha * 150, bonus_alpha * 255, bonus_alpha * 150, 256);
                        RT_GameText(0x8c, 0x46, "ENEMIES KILLED");
                        sprintf(buf, "%d", kills);
                        RT_GameText(0x10e, 0x46, buf);
                    }
                    if ((int)totalclock > 32 * 4)
                    {
                        RT_RotateSpriteSetColor(bonus_alpha * 255, bonus_alpha * 150, bonus_alpha * 150, 256);
                        RT_GameText(0x8c, 0x50, "ENEMIES LEFT");
                        sprintf(buf, "%d", killstotal);
                        RT_GameText(0x10e, 0x50, buf);
                    }
                    if ((int)totalclock > 48 * 4)
                    {
                        RT_RotateSpriteSetColor(bonus_alpha * 150, bonus_alpha * 255, bonus_alpha * 150, 256);
                        RT_GameText(0x8c, 0x69, "SECRETS FOUND");
                        sprintf(buf, "%d", secrets);
                        RT_GameText(0x10e, 0x69, buf);
                    }
                    if ((int)totalclock > 64 * 4)
                    {
                        RT_RotateSpriteSetColor(bonus_alpha * 255, bonus_alpha * 150, bonus_alpha * 150, 256);
                        RT_GameText(0x8c, 0x73, "SECRETS MISSED");
                        sprintf(buf, "%d", secretstotal - secrets);
                        RT_GameText(0x10e, 0x73, buf);
                    }
                    if ((int)totalclock > 80 * 4)
                    {
                        RT_RotateSpriteSetColor(bonus_alpha * 150, bonus_alpha * 255, bonus_alpha * 150, 256);
                        RT_GameText(0x8c, 0x8c, "BABES SAVED");
                        sprintf(buf, "%d", babes);
                        RT_GameText(0x10e, 0x8c, buf);
                    }
                    if ((int)totalclock > 96 * 4)
                    {
                        RT_RotateSpriteSetColor(bonus_alpha * 255, bonus_alpha * 150, bonus_alpha * 150, 256);
                        RT_GameText(0x8c, 0x96, "BABES LEFT");
                        sprintf(buf, "%d", babestotal - babes);
                        RT_GameText(0x10e, 0x96, buf);
                    }
                    int cnt = (int)totalclock >> 6;
                    if (soundcnt3 <= cnt && cnt < 7)
                    {
                        S_PlaySound(12);
                        soundcnt3 = cnt+1;
                    }
                    int alpha2 = (192 + (sintable[((int)totalclock & 255) * 8] / 256)) * bonus_alpha;
                    RT_RotateSpriteSetColor(alpha2, alpha2, alpha2, 256);
                    RT_GameText(-1, 0xc3, "PRESS START TO ENTER");
                    RT_MenuText(-1, 0xcd, rt_level_names[RT_NextLevel()]);
                    RT_RotateSpriteSetColor(255, 255, 255, 256);
                }
            }
            else
            {
                RT_EnablePolymost();
                break;
            }
            RT_EnablePolymost();
            videoNextPage();
        }
    } while (1);

    I_ClearAllInput();

    videoClearScreen(0);
    videoNextPage();
}


code:
// Copyright: 2020 Nuke.YKT, EDuke32 developers
// License: GPLv2
#pragma once

void RT_Intro(void);
void RT_Bonus(void);


code:
// Copyright: 2020 Nuke.YKT, EDuke32 developers
// License: GPLv2
#include "compat.h"
#include "../duke3d.h"
#include "reality.h"
#include "reality_sound.h"

//
// Uses info and snippets from this code:
// https://github.com/jombo23/N64-Tools/blob/master/N64%20Sound%20Tool/N64SoundListToolUpdated/N64SoundLibrary/N64AIFCAudio.cpp
//

rt_CTL_t *soundCtl;
rt_instrument_t *soundInfo;
rt_soundinstance_t rt_soundinstance[MAXRTSOUNDINSTANCES];

int16_t rt_soundrate[MAXSOUNDS];

enum {
    SE_ATTACK = 0,
    SE_DECAY,
    SE_SUSTAIN,
    SE_RELEASE
};

static rt_env_t *RT_LoadEnv(uint32_t ctlOffset, uint32_t envOffset)
{
    rt_env_t *env = (rt_env_t*)Xcalloc(1, sizeof(rt_env_t));
    RT_ROMSeek(ctlOffset+envOffset);
    RT_ROMRead(&env->attack_time, sizeof(env->attack_time));
    env->attack_time = B_BIG32(env->attack_time);
    RT_ROMRead(&env->decay_time, sizeof(env->decay_time));
    env->decay_time = B_BIG32(env->decay_time);
    RT_ROMRead(&env->release_time, sizeof(env->release_time));
    env->release_time = B_BIG32(env->release_time);
    RT_ROMRead(&env->attack_volume, sizeof(env->attack_volume));
    RT_ROMRead(&env->decay_volume, sizeof(env->decay_volume));
    return env;
}

static rt_key_t *RT_LoadKey(uint32_t ctlOffset, uint32_t keyOffset)
{
    rt_key_t*key = (rt_key_t*)Xcalloc(1, sizeof(rt_key_t));
    RT_ROMSeek(ctlOffset+keyOffset);
    RT_ROMRead(&key->velocitymin, sizeof(key->velocitymin));
    RT_ROMRead(&key->velocitymax, sizeof(key->velocitymax));
    RT_ROMRead(&key->keymin, sizeof(key->keymin));
    RT_ROMRead(&key->keymax, sizeof(key->keymax));
    RT_ROMRead(&key->keybase, sizeof(key->keybase));
    RT_ROMRead(&key->detune, sizeof(key->detune));
    return key;
}

static rt_adpcm_loop_t *RT_LoadADPCMLoop(uint32_t ctlOffset, uint32_t loopOffset)
{
    rt_adpcm_loop_t *loop = (rt_adpcm_loop_t*)Xcalloc(1, sizeof(rt_adpcm_loop_t));
    RT_ROMSeek(ctlOffset+loopOffset);
    RT_ROMRead(&loop->start, sizeof(loop->start));
    loop->start = B_BIG32(loop->start);
    RT_ROMRead(&loop->end, sizeof(loop->end));
    loop->end = B_BIG32(loop->end);
    RT_ROMRead(&loop->count, sizeof(loop->count));
    loop->count = B_BIG32(loop->count);
    RT_ROMRead(&loop->state, sizeof(loop->state));
    for (int i = 0; i < 16; i++)
    {
        loop->state[i] = B_BIG16(loop->state[i]);
    }
    return loop;
}

static rt_adpcm_book_t *RT_LoadADPCMBook(uint32_t ctlOffset, uint32_t bookOffset)
{
    rt_adpcm_book_t *book = (rt_adpcm_book_t*)Xcalloc(1, sizeof(rt_adpcm_book_t));
    RT_ROMSeek(ctlOffset+bookOffset);
    RT_ROMRead(&book->order, sizeof(book->order));
    book->order = B_BIG32(book->order);
    RT_ROMRead(&book->npredictors, sizeof(book->npredictors));
    book->npredictors = B_BIG32(book->npredictors);
    book->predictors = (int16_t*)Xcalloc(book->order * book->npredictors * 8, sizeof(int16_t));
    RT_ROMRead(book->predictors, book->order * book->npredictors * 8 * sizeof(int16_t));
    for (int i = 0; i < book->order * book->npredictors * 8; i++)
    {
        book->predictors[i] = B_BIG16(book->predictors[i]);
    }
    return book;
}

static rt_raw_loop_t *RT_LoadRAWLoop(uint32_t ctlOffset, uint32_t loopOffset)
{
    rt_raw_loop_t *loop = (rt_raw_loop_t*)Xcalloc(1, sizeof(rt_raw_loop_t));
    RT_ROMSeek(ctlOffset+loopOffset);
    RT_ROMRead(&loop->start, sizeof(loop->start));
    loop->start = B_BIG32(loop->start);
    RT_ROMRead(&loop->end, sizeof(loop->end));
    loop->end = B_BIG32(loop->end);
    RT_ROMRead(&loop->count, sizeof(loop->count));
    loop->count = B_BIG32(loop->count);
    return loop;
}

static rt_wave_t *RT_LoadWave(uint32_t ctlOffset, uint32_t waveOffset, uint32_t tblOffset)
{
    rt_wave_t *wave = (rt_wave_t*)Xcalloc(1, sizeof(rt_wave_t));
    RT_ROMSeek(ctlOffset+waveOffset);
    RT_ROMRead(&wave->base, sizeof(wave->base));
    wave->base = B_BIG32(wave->base) + tblOffset;
    RT_ROMRead(&wave->len, sizeof(wave->len));
    wave->len = B_BIG32(wave->len);
    RT_ROMRead(&wave->type, sizeof(wave->type));
    RT_ROMRead(&wave->flags, sizeof(wave->flags));
    uint16_t pad;
    RT_ROMRead(&pad, sizeof(pad));
    switch (wave->type)
    {
    case 0: // ADPCM
    {
        uint32_t loopOffset, bookOffset;
        RT_ROMRead(&loopOffset, sizeof(loopOffset));
        loopOffset = B_BIG32(loopOffset);
        RT_ROMRead(&bookOffset, sizeof(bookOffset));
        bookOffset = B_BIG32(bookOffset);
        wave->adpcm = (rt_adpcm_wave_t*)Xcalloc(1, sizeof(rt_adpcm_wave_t));
        if (loopOffset)
            wave->adpcm->loop = RT_LoadADPCMLoop(ctlOffset, loopOffset);
        if (bookOffset)
            wave->adpcm->book = RT_LoadADPCMBook(ctlOffset, bookOffset);
        break;
    }
    case 1: // RAW
    {
        uint32_t loopOffset;
        RT_ROMRead(&loopOffset, sizeof(loopOffset));
        loopOffset = B_BIG32(loopOffset);
        wave->raw = (rt_raw_wave_t*)Xcalloc(1, sizeof(rt_raw_wave_t));
        if (loopOffset)
            wave->raw->loop = RT_LoadRAWLoop(ctlOffset, loopOffset);
        break;
    }
    }
    return wave;
}

static rt_sound_t *RT_LoadSound(uint32_t ctlOffset, uint32_t soundOffset, uint32_t tblOffset)
{
    rt_sound_t *snd = (rt_sound_t*)Xcalloc(1, sizeof(rt_sound_t));
    RT_ROMSeek(ctlOffset+soundOffset);
    uint32_t envOffset, keyOffset, waveOffset;
    RT_ROMRead(&envOffset, sizeof(envOffset));
    envOffset = B_BIG32(envOffset);
    RT_ROMRead(&keyOffset, sizeof(keyOffset));
    keyOffset = B_BIG32(keyOffset);
    RT_ROMRead(&waveOffset, sizeof(waveOffset));
    waveOffset = B_BIG32(waveOffset);
    RT_ROMRead(&snd->sample_pan, sizeof(snd->sample_pan));
    RT_ROMRead(&snd->sample_volume, sizeof(snd->sample_volume));
    RT_ROMRead(&snd->flags, sizeof(snd->flags));
    snd->flags = B_BIG16(snd->flags);
    snd->env = RT_LoadEnv(ctlOffset, envOffset);
    snd->key = RT_LoadKey(ctlOffset, keyOffset);
    snd->wave = RT_LoadWave(ctlOffset, waveOffset, tblOffset);
    return snd;
}

static rt_instrument_t *RT_LoadInstrument(uint32_t ctlOffset, uint32_t instOffset, uint32_t tblOffset)
{
    rt_instrument_t *inst = (rt_instrument_t*)Xcalloc(1, sizeof(rt_instrument_t));
    RT_ROMSeek(ctlOffset+instOffset);
    RT_ROMRead(&inst->volume, sizeof(inst->volume));
    RT_ROMRead(&inst->pan, sizeof(inst->pan));
    RT_ROMRead(&inst->priority, sizeof(inst->priority));
    RT_ROMRead(&inst->flags, sizeof(inst->flags));
    RT_ROMRead(&inst->trem_type, sizeof(inst->trem_type));
    RT_ROMRead(&inst->trem_rate, sizeof(inst->trem_rate));
    RT_ROMRead(&inst->trem_depth, sizeof(inst->trem_depth));
    RT_ROMRead(&inst->trem_delay, sizeof(inst->trem_delay));
    RT_ROMRead(&inst->vib_type, sizeof(inst->vib_type));
    RT_ROMRead(&inst->vib_rate, sizeof(inst->vib_rate));
    RT_ROMRead(&inst->vib_depth, sizeof(inst->vib_depth));
    RT_ROMRead(&inst->vib_delay, sizeof(inst->vib_delay));
    RT_ROMRead(&inst->bend_range, sizeof(inst->bend_range));
    inst->bend_range = B_BIG16(inst->bend_range);
    RT_ROMRead(&inst->sound_count, sizeof(inst->sound_count));
    inst->sound_count = B_BIG16(inst->sound_count);
    uint32_t *soundOffset = (uint32_t*)Xcalloc(inst->sound_count, sizeof(uint32_t));
    RT_ROMRead(soundOffset, sizeof(uint32_t) * inst->sound_count);
    inst->sounds = (rt_sound_t**)Xcalloc(inst->sound_count, sizeof(rt_sound_t*));
    for (int i = 0; i < inst->sound_count; i++)
    {
        soundOffset[i] = B_BIG32(soundOffset[i]);
    }

    for (int gap = inst->sound_count / 2; gap > 0; gap /= 2)
        for (int i = gap; i < inst->sound_count; i++)
            for (int j = i - gap; j >= 0 && soundOffset[j] > soundOffset[j + gap]; j -= gap)
                swap(&soundOffset[j], &soundOffset[j + gap]);

    for (int i = 0; i < inst->sound_count; i++)
    {
        inst->sounds[i] = RT_LoadSound(ctlOffset, soundOffset[i], tblOffset);
    }
    Xfree(soundOffset);
    return inst;
}

static rt_bank_t *RT_LoadBank(uint32_t ctlOffset, uint32_t bankOffset, uint32_t tblOffset)
{
    rt_bank_t *bank = (rt_bank_t*)Xcalloc(1, sizeof(rt_bank_t));
    RT_ROMSeek(ctlOffset+bankOffset);
    RT_ROMRead(&bank->inst_count, sizeof(bank->inst_count));
    bank->inst_count = B_BIG16(bank->inst_count);
    RT_ROMRead(&bank->flags, sizeof(bank->flags));
    bank->flags = B_BIG16(bank->flags);
    RT_ROMRead(&bank->unused, sizeof(bank->unused));
    bank->unused = B_BIG16(bank->unused);
    RT_ROMRead(&bank->rate, sizeof(bank->rate));
    bank->rate = B_BIG16(bank->rate);
    uint32_t percOffset, *instOffset;
    RT_ROMRead(&percOffset, sizeof(percOffset));
    percOffset = B_BIG32(percOffset);
    instOffset = (uint32_t*)Xcalloc(bank->inst_count, sizeof(uint32_t));
    RT_ROMRead(instOffset, sizeof(uint32_t) * bank->inst_count);

    bank->inst = (rt_instrument_t**)Xcalloc(bank->inst_count, sizeof(rt_instrument_t*));

    if (percOffset)
        bank->perc = RT_LoadInstrument(ctlOffset, percOffset, tblOffset);
    for (int i = 0; i < bank->inst_count; i++)
    {
        bank->inst[i] = RT_LoadInstrument(ctlOffset, B_BIG32(instOffset[i]), tblOffset);
    }

    Xfree(instOffset);

    return bank;
}

rt_CTL_t *RT_LoadCTL(uint32_t ctlOffset, uint32_t tblOffset)
{
    rt_CTL_t *ctl = (rt_CTL_t*)Xcalloc(1, sizeof(rt_CTL_t));
    RT_ROMSeek(ctlOffset);
    RT_ROMRead(&ctl->signature, sizeof(ctl->signature));
    ctl->signature = B_BIG16(ctl->signature);
    RT_ROMRead(&ctl->bank_count, sizeof(ctl->bank_count));
    ctl->bank_count = B_BIG16(ctl->bank_count);
    if (ctl->signature != CTLSIGNATURE || ctl->bank_count <= 0)
    {
        initprintf("Error loading sound bank %x\n", ctlOffset);
        Xfree(ctl);
        return nullptr;
    }
    uint32_t *bank_offset = (uint32_t*)Xcalloc(ctl->bank_count, sizeof(uint32_t));
    RT_ROMRead(bank_offset, sizeof(uint32_t) * ctl->bank_count);
    // Load banks
    ctl->bank = (rt_bank_t**)Xcalloc(ctl->bank_count, sizeof(rt_bank_t*));
    for (int i = 0; i < ctl->bank_count; i++)
    {
        ctl->bank[i] = RT_LoadBank(ctlOffset, B_BIG32(bank_offset[i]), tblOffset);
    }
    Xfree(bank_offset);
    return ctl;
}

void RT_InitSound(void)
{
    static const uint32_t soundCtlOffset = RT_ROMGetOffset(GO_SOUNDCTLOFFSET);
    static const uint32_t soundTblOffset = RT_ROMGetOffset(GO_SOUNDTBLOFFSET);
    soundCtl = RT_LoadCTL(soundCtlOffset, soundTblOffset);
    if (soundCtl && soundCtl->bank[0] && soundCtl->bank[0]->inst_count > 0)
        soundInfo = soundCtl->bank[0]->inst[0];

    RT_MusicInit();

    if (!soundInfo)
        return;

    g_highestSoundIdx = soundInfo->sound_count;

    memset(rt_soundinstance, 0, sizeof(rt_soundinstance));

    static const uint32_t soundvoOffset = RT_ROMGetOffset(GO_SOUNDVOOFFSET);
    static const uint32_t soundpeOffset = RT_ROMGetOffset(GO_SOUNDPEOFFSET);
    static const uint32_t soundpsOffset = RT_ROMGetOffset(GO_SOUNDPSFFSET);
    static const uint32_t soundmOffset = RT_ROMGetOffset(GO_SOUNDMOFFSET);
    static const uint32_t soundrateOffset = RT_ROMGetOffset(GO_SOUNDRATEOFFSET);
    int16_t *tempshort = (int16_t*)tempbuf;
    RT_ROMSeek(soundvoOffset);
    RT_ROMRead(tempshort, soundInfo->sound_count * sizeof(int16_t));
    for (int i = 0; i < soundInfo->sound_count; i++)
    {
        if (g_sounds[i].filename == nullptr)
        {
            g_sounds[i].vo = B_BIG16(tempshort[i]);
            g_sounds[i].pr = 200; // FIXME
            g_sounds[i].volume = fix16_one;
        }
    }
    RT_ROMSeek(soundpeOffset);
    RT_ROMRead(tempshort, soundInfo->sound_count * sizeof(int16_t));
    for (int i = 0; i < soundInfo->sound_count; i++)
    {
        if (g_sounds[i].filename == nullptr)
            g_sounds[i].pe = B_BIG16(tempshort[i]);
    }
    RT_ROMSeek(soundpsOffset);
    RT_ROMRead(tempshort, soundInfo->sound_count * sizeof(int16_t));
    for (int i = 0; i < soundInfo->sound_count; i++)
    {
        if (g_sounds[i].filename == nullptr)
            g_sounds[i].ps = B_BIG16(tempshort[i]);
    }
    RT_ROMSeek(soundmOffset);
    RT_ROMRead(tempbuf, soundInfo->sound_count * sizeof(uint8_t));
    for (int i = 0; i < soundInfo->sound_count; i++)
    {
        if (g_sounds[i].filename == nullptr)
        {
            g_sounds[i].m = tempbuf[i];
            g_sounds[i].m &= ~SF_ONEINST_INTERNAL;
            if (g_sounds[i].m & SF_LOOP)
                g_sounds[i].m |= SF_ONEINST_INTERNAL;
            g_sounds[i].m |= SF_REALITY_INTERNAL;
        }
    }
    RT_ROMSeek(soundrateOffset);
    RT_ROMRead(rt_soundrate, soundInfo->sound_count * sizeof(int16_t));
    for (int i = 0; i < soundInfo->sound_count; i++)
    {
        rt_soundrate[i] = B_BIG16(rt_soundrate[i]);
    }
}

int RT_LoadSound(int num)
{
    if (!soundCtl)
        return 0;
    if (num < 0 || num >= soundInfo->sound_count)
        return 0;

    auto &snd = g_sounds[num];
    rt_sound_t *sound = soundInfo->sounds[num];
    RT_ROMSeek(sound->wave->base);
    int l = sound->wave->len;
    g_soundlocks[num] = 200;
    snd.siz = sound->wave->len;
    g_cache.allocateBlock((intptr_t *)&snd.ptr, l, (char *)&g_soundlocks[num]);
    l = RT_ROMRead(snd.ptr, l);

    return l;
}

void RT_Decode8(char *in, int16_t *out, int index, int16_t *pred1, int16_t lastsmp[8])
{
    static int16_t itable[16] = {
        0, 1, 2, 3, 4, 5, 6, 7,
        -8, -7, -6, -5, -4, -3, -2, -1,
    };
    int16_t tmp[8];
    int total = 0;
    int16_t *pred2 = (pred1 + 8);
    for (int i = 0; i < 8; i++)
        tmp[i] = itable[(i&1) ? (*in++ & 0xf) : ((*in >> 4) & 0xf)] << index;

    for (int i = 0; i < 8; i++)
    {
        total = (pred1[i] * lastsmp[6]);
        total += (pred2[i] * lastsmp[7]);
        if (i > 0)
        {
            for (int x = i - 1; x > -1; x--)
            {
                total += tmp[((i - 1) - x)] * pred2[x];
            }
        }

        int result = ((tmp[i] << 0xb) + total) >> 0xb;
        out[i] = clamp(result, INT16_MIN, INT16_MAX);
    }
    // update the last sample set for subsequent iterations
    memcpy(lastsmp, out, sizeof(int16_t)*8);
}

void RT_SoundDecode(const char **ptr, uint32_t *length, void *userdata)
{
    auto snd = (rt_soundinstance_t*)userdata;
    if (snd->endofdata)
    {
        *ptr = nullptr;
        *length = 0;
        RT_FreeSoundSlot(snd);
        return;
    }
    if (snd->rtsound->wave->adpcm)
    {
        *ptr = (char*)snd->buf;
        auto predictors = snd->rtsound->wave->adpcm->book->predictors;
        auto wavelen = snd->rtsound->wave->len;
        int i;
        for (i = 0; i < RTSNDBLOCKSIZE; i++)
        {
            while (snd->outleft == 0)
            {
                if (snd->loop && snd->samples + 16 > snd->loop_end)
                {
                    if (snd->samples < snd->loop_end)
                    {
                        int todo = snd->loop_end - snd->samples;
                        int index = (*snd->ptr >> 4) & 0xf;
                        int pred = (*snd->ptr) & 0xf;

                        int16_t *pred1 = &predictors[pred * 16];

                        RT_Decode8(++snd->ptr, &snd->out[0], index, pred1, snd->lastsmp);
                        snd->ptr += 4;

                        RT_Decode8(snd->ptr, &snd->out[8], index, pred1, snd->lastsmp);
                        snd->ptr += 4;
                        snd->outleft = todo;
                        snd->samples += todo;
                        snd->outptr = 0;
                    }
                    else
                    {
                        if (snd->loop > 0)
                            snd->loop--;
                        Bmemcpy(snd->out, snd->loopstate, 16 * sizeof(int16_t));
                        Bmemcpy(snd->lastsmp, snd->loopstate+8, 8 * sizeof(int16_t));
                        int b = (snd->loop_start / 16) + 1;
                        int s = b * 16 - snd->loop_start;
                        snd->ptr = snd->snd + b * 9;
                        snd->outleft = s;
                        snd->outptr = 16 - s;
                        snd->samples = b * 16;
                    }
                }
                else
                {
                    if (snd->ptr >= snd->snd + snd->rtsound->wave->len)
                    {
                        snd->endofdata = 1;
                        break;
                    }
                    int index = (*snd->ptr >> 4) & 0xf;
                    int pred = (*snd->ptr) & 0xf;

                    int16_t *pred1 = &predictors[pred * 16];

                    RT_Decode8(++snd->ptr, &snd->out[0], index, pred1, snd->lastsmp);
                    snd->ptr += 4;

                    RT_Decode8(snd->ptr, &snd->out[8], index, pred1, snd->lastsmp);
                    snd->ptr += 4;

                    snd->outleft = 16;
                    snd->samples += 16;
                    snd->outptr = 0;
                }
            }
            if (snd->endofdata)
                break;
            snd->buf[i] = snd->out[snd->outptr++];
            snd->outleft--;
        }
        *length = i;
    }
    else
    {
        *length = 0;
    }
}

void RT_SoundDecodeEnv(const char **ptr, uint32_t *length, void *userdata)
{
    RT_SoundDecode(ptr, length, userdata);
    // Apply env volume
    auto snd = (rt_soundinstance_t*)userdata;
    int i;
    for (i = 0; i < *length;)
    {
        if (--snd->envTimer <= 0)
        {
            if (snd->envState == SE_ATTACK)
            {
                if (snd->rtsound->env->decay_time == -1)
                {
                    snd->envState = SE_SUSTAIN;
                    snd->envVol = snd->envTarget;
                    snd->envTimer = 0;
                    snd->envStep = 0.0;
                }
                else
                {
                    snd->envState = SE_DECAY;
                    snd->envVol = snd->envTarget;
                    snd->envTarget = snd->rtsound->env->decay_volume;
                    snd->envTimer = snd->rate * (snd->rtsound->env->decay_time / 1000000.0);
                    snd->envStep = (double)(snd->envTarget - snd->envVol) / (double)snd->envTimer;
                }
            }
            else if (snd->envState == SE_DECAY)
            {
                snd->envState = SE_RELEASE;
                snd->envVol = snd->envTarget;
                snd->envTarget = 0.0;
                snd->envTimer = snd->rate * (snd->rtsound->env->release_time / 1000000.0);
                snd->envStep = (double)(snd->envTarget - snd->envVol) / (double)snd->envTimer;
            }
            else if (snd->envState == SE_RELEASE)
            {
                snd->endofdata = 1;
                break;
            }
        }
        else
        {
            snd->envVol += snd->envStep;
        }
        snd->buf[i] = clamp((int)(snd->buf[i] * snd->envVol * (1.0 / 127.f)), INT16_MIN, INT16_MAX);
        i++;
    }
    *length = i;
}

rt_soundinstance_t *RT_FindSoundSlot(int snum, int id)
{
    if (!soundCtl)
        return nullptr;
    if (snum < 0 || snum >= soundInfo->sound_count)
        return nullptr;
    rt_sound_t *rtsound = soundInfo->sounds[snum];
    for (int i = 0; i < MAXRTSOUNDINSTANCES; i++)
    {
        auto snd = &rt_soundinstance[i];
        if (!snd->status)
        {
            memset(snd, 0, sizeof(rt_soundinstance_t));
            snd->status = 1;
            snd->ptr = snd->snd = g_sounds[snum].ptr;
            snd->rtsound = rtsound;
            snd->id = id;
            snd->rate = soundCtl->bank[0]->rate;
            if (rtsound->wave->adpcm && rtsound->wave->adpcm->loop)
            {
                snd->loop = rtsound->wave->adpcm->loop->count;
                snd->loop_start = rtsound->wave->adpcm->loop->start;
                snd->loop_end = rtsound->wave->adpcm->loop->end;
            }
            // if (rtsound->wave->raw && rtsound->wave->raw->loop)
            //     snd->loop = rtsound->wave->raw->loop->count;
            snd->envState = SE_ATTACK;
            snd->envVol = 0.0;
            snd->envTarget = rtsound->env->attack_volume;
            snd->envTimer = snd->rate * (rtsound->env->attack_time / 1000000.0);
            snd->envStep = (double)(snd->envTarget - snd->envVol) / (double)snd->envTimer;
            return snd;
        }
    }
    return nullptr;
}

void RT_FreeSoundSlot(rt_soundinstance_t *snd)
{
    if (!snd)
        return;

    snd->status = 0;
}

void RT_FreeSoundSlotId(int id)
{
    for (int i = 0; i < MAXSOUNDINSTANCES; i++)
    {
        auto snd = &rt_soundinstance[i];
        if (snd->status && snd->id == id)
            RT_FreeSoundSlot(snd);
    }
}


code:
// Copyright: 2020 Nuke.YKT, EDuke32 developers
// License: GPLv2
#pragma once

#include "compat.h"

#define RTSNDBLOCKSIZE 1024
#define MAXRTSOUNDINSTANCES 256
#define CTLSIGNATURE 0x4231 // B1

struct rt_adpcm_book_t {
    uint32_t order;
    uint32_t npredictors;
    int16_t *predictors;
};

struct rt_adpcm_loop_t {
    uint32_t start;
    uint32_t end;
    int32_t count;
    int16_t state[16];
    uint32_t unknown1;
};

struct rt_raw_loop_t {
    uint32_t start;
    uint32_t end;
    int32_t count;
};

struct rt_adpcm_wave_t {
    rt_adpcm_loop_t *loop;
    rt_adpcm_book_t *book;
};

struct rt_raw_wave_t {
    rt_raw_loop_t *loop;
};

struct rt_wave_t {
    uint32_t base;
    uint32_t len;
    uint8_t type;
    uint8_t flags;
    rt_adpcm_wave_t *adpcm;
    rt_raw_wave_t *raw;
};

struct rt_env_t {
    int32_t attack_time;
    int32_t decay_time;
    int32_t release_time;
    uint8_t attack_volume;
    uint8_t decay_volume;
    uint16_t pad;
};

struct rt_key_t {
    uint8_t velocitymin;
    uint8_t velocitymax;
    uint8_t keymin;
    uint8_t keymax;
    uint8_t keybase;
    uint8_t detune;
};

struct rt_sound_t {
    rt_env_t *env;
    rt_key_t *key;
    rt_wave_t *wave;
    uint8_t sample_pan;
    uint8_t sample_volume;
    uint16_t flags;

    char *ptr;
    char lock;
};

struct rt_instrument_t {
    uint8_t volume;
    uint8_t pan;
    uint8_t priority;
    uint8_t flags;
    uint8_t trem_type;
    uint8_t trem_rate;
    uint8_t trem_depth;
    uint8_t trem_delay;
    uint8_t vib_type;
    uint8_t vib_rate;
    uint8_t vib_depth;
    uint8_t vib_delay;
    int16_t bend_range;
    int16_t sound_count;
    rt_sound_t **sounds;
};

struct rt_bank_t {
    int16_t inst_count;
    int16_t flags;
    int16_t unused;
    int16_t rate;
    rt_instrument_t *perc;
    rt_instrument_t **inst;
};

struct rt_CTL_t {
    uint16_t signature;
    uint16_t bank_count;
    rt_bank_t **bank;
};

struct rt_soundinstance_t {
    int status;
    int endofdata;
    int id;
    int loop;
    int loop_start;
    int loop_end;
    int rate;
    char *snd;
    char *ptr;
    int samples;
    int outleft;
    int outptr;
    double envVol;
    double envStep;
    double envTarget;
    int envTimer;
    int envState;
    rt_sound_t *rtsound;
    int16_t lastsmp[16];
    int16_t loopstate[16];
    int16_t out[16];
    int16_t buf[RTSNDBLOCKSIZE];
};

extern int16_t rt_soundrate[];

rt_CTL_t *RT_LoadCTL(uint32_t ctlOffset, uint32_t tblOffset);
void RT_InitSound(void);
int RT_LoadSound(int num);
void RT_SoundDecode(const char **ptr, uint32_t *length, void *userdata);
void RT_SoundDecodeEnv(const char **ptr, uint32_t *length, void *userdata);
rt_soundinstance_t *RT_FindSoundSlot(int snum, int id);
void RT_FreeSoundSlot(rt_soundinstance_t *snd);
void RT_FreeSoundSlotId(int id);


code:
// Copyright: 2020 Nuke.YKT, EDuke32 developers
// License: GPLv2
#include "compat.h"
#include "reality.h"
#include "../duke3d.h"

struct Huffman {
    uint32_t f_0;
    uint16_t f_2;
    uint32_t f_8;
    uint16_t f_c;
};

static char nBitsLoaded;
static const char *pStream;
static const char *pStreamEnd;
static char *pOutput;
static char *pOutputEnd;
static uint32_t loadedBits;
static Huffman huffVars[3][16];

static void SetupDecompression(const char *inbuf, char *outbuf)
{
    nBitsLoaded = 0;
    pStream = inbuf + 18; // skip header
    pStreamEnd = pStream + B_BIG32(*(int*)(inbuf + 8));
    pOutput = outbuf;
    pOutputEnd = outbuf + B_BIG32(*(int*)(inbuf + 4));
}

static int GetBitsFromStream(uint8_t nBits)
{
    int ret = 0;
    int v1 = 1;

    for (int i = 0; i < nBits; i++)
    {
        if (!nBitsLoaded)
        {
            nBitsLoaded = 16;
            loadedBits = B_LITTLE32(*(int*)pStream);
            pStream += 2;
        }
        nBitsLoaded--;

        char v3 = loadedBits & 1;
        loadedBits >>= 1;
        if (v3)
            ret |= v1;
        v1 <<= 1;
    }

    return ret;
}

static void InitTable(Huffman *table, int nSize)
{
    for (int i = 0; i < nSize; i++)
    {
        table[i].f_0 = 0;
        table[i].f_2 = -1;
        table[i].f_8 = 0;
        table[i].f_c = 0;
    }
}

static uint32_t sub_80070488(uint32_t a, int a2)
{
    int v2 = 0;
    for (int i = a2 - 1; i >= 0; i--)
    {
        v2 <<= 1;
        if (a & 1)
            v2 |= 1;
        a >>= 1;
    }
    return v2;
}

static void sub_800704dc(Huffman *table, int nSize)
{
    uint32_t v4 = 0;
    uint32_t v5 = 1UL << 31;

    for (int i = 1; i < 17; i++)
    {
        for (int j = 0; j < nSize; j++)
        {
            if (table[j].f_c == i)
            {
                table[j].f_8 = sub_80070488(v4 / v5, i);
                v4 += v5;
            }
        }
        v5 >>= 1;
    }
}

static void LoadTable(Huffman *table, int nSize)
{
    InitTable(table, nSize);

    int nLeafNodes = GetBitsFromStream(5);
    if (nLeafNodes > 16)
        nLeafNodes = 16;

    for (int i = 0; i < nLeafNodes; i++)
    {
        table[i].f_c = GetBitsFromStream(4); // leaf depth
    }

    sub_800704dc(table, nSize);
}

static int GetTableLength(Huffman *a1)
{
    int v6 = 0;
    while (a1->f_c == 0 || a1->f_8 != (((1UL << ((a1->f_c & 31))) - 1) & loadedBits))
    {
        a1++;
        v6++;
    }
    GetBitsFromStream(a1->f_c);
    if (v6 > 1)
    {
        v6--;
        return GetBitsFromStream(v6) | (1 << ((v6) & 31));
    }
    return v6;
}

static int RNCDecompress1(const char *inbuf, char *outbuf)
{
    SetupDecompression(inbuf, outbuf);
    GetBitsFromStream(2); // ignore first two bits

    while (pOutput < pOutputEnd) // go through the pack chunks
    {
        LoadTable(huffVars[0], 16); // raw table
        LoadTable(huffVars[1], 16); // distance table
        LoadTable(huffVars[2], 16); // length table

        int nSubChunks = GetBitsFromStream(16); // get count of subchunks in this pack chunk

        for (int j = 0; j < nSubChunks; j++)
        {
            int nLength = GetTableLength(huffVars[0]);

            for (int i = 0; i < nLength; i++)
            {
                *pOutput++ = *pStream++;
            }

            loadedBits = (loadedBits & ((1 << (nBitsLoaded & 31)) - 1))
                | ((pStream[0] + (pStream[1] << 8) + (pStream[2] << 16)) << (nBitsLoaded & 31));

            if (j < (nSubChunks - 1))
            {
                nLength = GetTableLength(huffVars[1]) + 1;
                char* v3 = pOutput - nLength;
                nLength = GetTableLength(huffVars[2]) + 2;

                for (int i = 0; i < nLength; i++)
                {
                    *pOutput++ = *v3++;
                }
            }
        }
    }
    return 0;
}

static int RNCDecompress2(const char *inbuf, char *outbuf)
{
    return 0;
}

int RNCDecompress(const char *inbuf, char *outbuf)
{
    if (inbuf[0] == 'R' && inbuf[1] == 'N' && inbuf[2] == 'C')
    {
        int const type = inbuf[3];
        switch (type)
        {
        case 0:
            Bmemcpy(outbuf, inbuf + 8, B_BIG32(*(int*)(inbuf + 4)));
            return 0;
        case 1:
            return RNCDecompress1(inbuf, outbuf);
        case 2:
            return RNCDecompress2(inbuf, outbuf);
        default:
            return -2;
        }
    }
    return -1;
}

int RT_FakeKRand(void)
{
    // if (rt_gamestate == 2)
        return _krand();
    // return 42;
}

int RT_KRand2(void)
{
    // if (rt_gamestatus == 0)
        return _krand();
    // return 62007;
}

static float rt_gatable[513];

void RT_BuildAngleTable(void)
{
    for (int i = 0; i <= 512; i++)
        rt_gatable[i] = atanf(i * (1.f / 512.f));
}

float RT_GetAngle(float dy, float dx)
{
    if (dy == 0.f && dx == 0.f)
        return 0.f;

    if (dy < 0)
    {
        if (dx < 0)
        {
            if (dy < dx)
                return -(fPI/2.f) - rt_gatable[Blrintf(dx / dy * 512.f)];
            else
                return -fPI + rt_gatable[Blrintf(dy / dx * 512.f)];
        }
        else
        {
            if (-dy > dx)
                return -(fPI/2.f) + rt_gatable[Blrintf(-dx / dy * 512.f)];
            else
                return -rt_gatable[Blrintf(-dy / dx * 512.f)];
        }
    }
    else
    {
        if (dx < 0)
        {
            if (dy > -dx)
                return (fPI/2.f) + rt_gatable[Blrintf(-dx / dy * 512.f)];
            else
                return fPI - rt_gatable[Blrintf(-dy / dx * 512.f)];
        }
        else
        {
            if (dy > dx)
                return (fPI/2.f) - rt_gatable[Blrintf(dx / dy * 512.f)];
            else
                return rt_gatable[Blrintf(dy / dx * 512.f)];
        }
    }
    return 0.f;
}

float RT_AngleMod(float a)
{
    while (a < 0.f)
    {
        a += 360.f;
    }
    while (a >= 360.f)
    {
        a -= 360.f;
    }
    return a;
}


code:
// Copyright: 2020 Nuke.YKT, EDuke32 developers
// License: GPLv2
#pragma once

int RT_FakeKRand(void);
int RT_KRand2(void);
int RNCDecompress(const char* inbuf, char* outbuf);
void RT_BuildAngleTable(void);
float RT_GetAngle(float dx, float dy);
float RT_AngleMod(float a);


code:
//-------------------------------------------------------------------------
/*
Copyright (C) 2010 EDuke32 developers and contributors

This file is part of EDuke32.

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

#define SECTOREFFECTOR 1
#define ACTIVATOR 2
#define TOUCHPLATE 3
#define ACTIVATORLOCKED 4
#define MUSICANDSFX 5
#define LOCATORS 6
#define CYCLER 7
#define MASTERSWITCH 8
#define RESPAWN 9
#define GPSPEED 10
#define FOF 13
#define ARROW 20
#define FIRSTGUNSPRITE 21
#define CHAINGUNSPRITE 22
#define RPGSPRITE 23
#define FREEZESPRITE 24
#define SHRINKERSPRITE 25
#define HEAVYHBOMB 26
#define TRIPBOMBSPRITE 27
#define SHOTGUNSPRITE 28
#define DEVISTATORSPRITE 29
#define HEALTHBOX 30
#define AMMOBOX 31
#define GROWSPRITEICON 32
#define INVENTORYBOX 33
#define FREEZEAMMO 37
#define AMMO 40
#define BATTERYAMMO 41
#define DEVISTATORAMMO 42
#define RPGAMMO 44
#define GROWAMMO 45
#define CRYSTALAMMO 46
#define HBOMBAMMO 47
#define AMMOLOTS 48
#define SHOTGUNAMMO 49
#define COLA 51
#define SIXPAK 52
#define FIRSTAID 53
#define SHIELD 54
#define STEROIDS 55
#define AIRTANK 56
#define JETPACK 57
#define HEATSENSOR 59
#define ACCESSCARD 60
#define BOOTS 61
#define MIRRORBROKE 70
#define CLOUDYOCEAN 78
#define CLOUDYSKIES 79
#define MOONSKY1 80
#define MOONSKY2 81
#define MOONSKY3 82
#define MOONSKY4 83
#define BIGORBIT1 84
#define BIGORBIT2 85
#define BIGORBIT3 86
#define BIGORBIT4 87
#define BIGORBIT5 88
#define LA 89
#define REDSKY1 98
#define REDSKY2 99
#define ATOMICHEALTH 100
#define TECHLIGHT2 120
#define TECHLIGHTBUST2 121
#define TECHLIGHT4 122
#define TECHLIGHTBUST4 123
#define WALLLIGHT4 124
#define WALLLIGHTBUST4 125
#define ACCESSSWITCH 130
#define SLOTDOOR 132
#define LIGHTSWITCH 134
#define SPACEDOORSWITCH 136
#define SPACELIGHTSWITCH 138
#define FRANKENSTINESWITCH 140
#define NUKEBUTTON 142
#define MULTISWITCH 146
#define DOORTILE5 150
#define DOORTILE6 151
#define DOORTILE1 152
#define DOORTILE2 153
#define DOORTILE3 154
#define DOORTILE4 155
#define DOORTILE7 156
#define DOORTILE8 157
#define DOORTILE9 158
#define DOORTILE10 159
#define DOORSHOCK 160
#define DIPSWITCH 162
#define DIPSWITCH2 164
#define TECHSWITCH 166
#define DIPSWITCH3 168
#define ACCESSSWITCH2 170
#define REFLECTWATERTILE 180
#define FLOORSLIME 200
#define BIGFORCE 230
#define EPISODE 247
#define MASKWALL9 255
#define W_LIGHT 260
#define SCREENBREAK1 263
#define SCREENBREAK2 264
#define SCREENBREAK3 265
#define SCREENBREAK4 266
#define SCREENBREAK5 267
#define SCREENBREAK6 268
#define SCREENBREAK7 269
#define SCREENBREAK8 270
#define SCREENBREAK9 271
#define SCREENBREAK10 272
#define SCREENBREAK11 273
#define SCREENBREAK12 274
#define SCREENBREAK13 275
#define MASKWALL1 285
#define W_TECHWALL1 293
#define W_TECHWALL2 297
#define W_TECHWALL15 299
#define W_TECHWALL3 301
#define W_TECHWALL4 305
#define W_TECHWALL10 306
#define W_TECHWALL16 307
#define WATERTILE2 336
#define BPANNEL1 341
#define PANNEL1 342
#define PANNEL2 343
#define WATERTILE 344
#define STATIC 351
#define W_SCREENBREAK 357
#define W_HITTECHWALL3 360
#define W_HITTECHWALL4 361
#define W_HITTECHWALL2 362
#define W_HITTECHWALL1 363
#define MASKWALL10 387
#define MASKWALL11 391
#define DOORTILE22 395
#define FANSPRITE 407
#define FANSPRITEBROKE 411
#define FANSHADOW 412
#define FANSHADOWBROKE 416
#define DOORTILE18 447
#define DOORTILE19 448
#define DOORTILE20 449
#define SPACESHUTTLE 487
#define SATELLITE 489
#define VIEWSCREEN2 499
#define VIEWSCREENBROKE 501
#define VIEWSCREEN 502
#define GLASS 503
#define GLASS2 504
#define STAINGLASS1 510
#define MASKWALL5 514
#define SATELITE 516
#define FUELPOD 517
#define SLIMEPIPE 538
#define CRACK1 546
#define CRACK2 547
#define CRACK3 548
#define CRACK4 549
#define FOOTPRINTS 550
#define DOMELITE 551
#define CAMERAPOLE 554
#define CHAIR1 556
#define CHAIR2 557
#define BROKENCHAIR 559
#define MIRROR 560
#define WATERFOUNTAIN 563
#define WATERFOUNTAINBROKE 567
#define FEMMAG1 568
#define TOILET 569
#define STALL 571
#define STALLBROKE 573
#define FEMMAG2 577
#define REACTOR2 578
#define REACTOR2BURNT 579
#define REACTOR2SPARK 580
#define GRATE1 595
#define BGRATE1 596
#define SOLARPANNEL 602
#define NAKED1 603
#define ANTENNA 607
#define MASKWALL12 609
#define TOILETBROKE 615
#define PIPE2 616
#define PIPE1B 617
#define PIPE3 618
#define PIPE1 619
#define CAMERA1 621
#define BRICK 626
#define SPLINTERWOOD 630
#define PIPE2B 633
#define BOLT1 634
#define W_NUMBERS 640
#define WATERDRIP 660
#define WATERBUBBLE 661
#define WATERBUBBLEMAKER 662
#define W_FORCEFIELD 663
#define VACUUM 669
#define FOOTPRINTS2 672
#define FOOTPRINTS3 673
#define FOOTPRINTS4 674
#define EGG 675
#define SCALE 678
#define CHAIR3 680
#define CAMERALIGHT 685
#define MOVIECAMERA 686
#define IVUNIT 689
#define POT1 694
#define POT2 695
#define POT3 697
#define PIPE3B 700
#define WALLLIGHT3 701
#define WALLLIGHTBUST3 702
#define WALLLIGHT1 703
#define WALLLIGHTBUST1 704
#define WALLLIGHT2 705
#define WALLLIGHTBUST2 706
#define LIGHTSWITCH2 712
#define WAITTOBESEATED 716
#define DOORTILE14 717
#define STATUE 753
#define MIKE 762
#define VASE 765
#define SUSHIPLATE1 768
#define SUSHIPLATE2 769
#define SUSHIPLATE3 774
#define SUSHIPLATE4 779
#define DOORTILE16 781
#define SUSHIPLATE5 792
#define OJ 806
#define MASKWALL13 830
#define HURTRAIL 859
#define POWERSWITCH1 860
#define LOCKSWITCH1 862
#define POWERSWITCH2 864
#define ATM 867
#define STATUEFLASH 869
#define ATMBROKE 888
#define BIGHOLE2 893
#define STRIPEBALL 901
#define QUEBALL 902
#define POCKET 903
#define WOODENHORSE 904
#define TREE1 908
#define TREE2 910
#define CACTUS 911
#define MASKWALL2 913
#define MASKWALL3 914
#define MASKWALL4 915
#define FIREEXT 916
#define TOILETWATER 921
#define NEON1 925
#define NEON2 926
#define CACTUSBROKE 939
#define BOUNCEMINE 940
#define BROKEFIREHYDRENT 950
#define BOX 951
#define BULLETHOLE 952
#define BOTTLE1 954
#define BOTTLE2 955
#define BOTTLE3 956
#define BOTTLE4 957
#define FEMPIC5 963
#define FEMPIC6 964
#define FEMPIC7 965
#define HYDROPLANT 969
#define OCEANSPRITE1 971
#define OCEANSPRITE2 972
#define OCEANSPRITE3 973
#define OCEANSPRITE4 974
#define OCEANSPRITE5 975
#define GENERICPOLE 977
#define CONE 978
#define HANGLIGHT 979
#define HYDRENT 981
#define MASKWALL14 988
#define TIRE 990
#define PIPE5 994
#define PIPE6 995
#define PIPE4 996
#define PIPE4B 997
#define BROKEHYDROPLANT 1003
#define PIPE5B 1005
#define NEON3 1007
#define NEON4 1008
#define NEON5 1009
#define BOTTLE5 1012
#define BOTTLE6 1013
#define BOTTLE8 1014
#define SPOTLITE 1020
#define HANGOOZ 1022
#define MASKWALL15 1024
#define BOTTLE7 1025
#define HORSEONSIDE 1026
#define GLASSPIECES 1031
#define HORSELITE 1034
#define DONUTS 1045
#define NEON6 1046
#define MASKWALL6 1059
#define CLOCK 1060
#define RUBBERCAN 1062
#define BROKENCLOCK 1067
#define PLUG 1069
#define OOZFILTER 1079
#define FLOORPLASMA 1082
#define REACTOR 1088
#define REACTORSPARK 1092
#define REACTORBURNT 1096
#define DOORTILE15 1102
#define HANDSWITCH 1111
#define CIRCLEPANNEL 1113
#define CIRCLEPANNELBROKE 1114
#define PULLSWITCH 1122
#define MASKWALL8 1124
#define BIGHOLE 1141
#define ALIENSWITCH 1142
#define DOORTILE21 1144
#define HANDPRINTSWITCH 1155
#define BOTTLE10 1157
#define BOTTLE11 1158
#define BOTTLE12 1159
#define BOTTLE13 1160
#define BOTTLE14 1161
#define BOTTLE15 1162
#define BOTTLE16 1163
#define BOTTLE17 1164
#define BOTTLE18 1165
#define BOTTLE19 1166
#define DOORTILE17 1169
#define MASKWALL7 1174
#define JAILBARBREAK 1175
#define DOORTILE11 1178
#define DOORTILE12 1179
#define VENDMACHINE 1212
#define VENDMACHINEBROKE 1214
#define COLAMACHINE 1215
#define COLAMACHINEBROKE 1217
#define CRANEPOLE 1221
#define CRANE 1222
#define BARBROKE 1225
#define BLOODPOOL 1226
#define NUKEBARREL 1227
#define NUKEBARRELDENTED 1228
#define NUKEBARRELLEAKED 1229
#define CANWITHSOMETHING 1232
#define MONEY 1233
#define BANNER 1236
#define EXPLODINGBARREL 1238
#define EXPLODINGBARREL2 1239
#define FIREBARREL 1240
#define SEENINE 1247
#define SEENINEDEAD 1248
#define STEAM 1250
#define CEILINGSTEAM 1255
#define PIPE6B 1260
#define TRANSPORTERBEAM 1261
#define RAT 1267
#define TRASH 1272
#define FEMPIC1 1280
#define FEMPIC2 1289
#define BLANKSCREEN 1293
#define PODFEM1 1294
#define FEMPIC3 1298
#define FEMPIC4 1306
#define FEM1 1312
#define FEM2 1317
#define FEM3 1321
#define FEM5 1323
#define BLOODYPOLE 1324
#define FEM4 1325
#define FEM6 1334
#define FEM6PAD 1335
#define FEM8 1336
#define HELECOPT 1346
#define FETUSJIB 1347
#define HOLODUKE 1348
#define SPACEMARINE 1353
#define INDY 1355
#define FETUS 1358
#define FETUSBROKE 1359
#define MONK 1352
#define LUKE 1354
#define COOLEXPLOSION1 1360
#define WATERSPLASH2 1380
#define FIREVASE 1390
#define SCRATCH 1393
#define FEM7 1395
#define APLAYERTOP 1400
#define APLAYER 1405
#define PLAYERONWATER 1420
#define DUKELYINGDEAD 1518
#define DUKETORSO 1520
#define DUKEGUN 1528
#define DUKELEG 1536
#define SHARK 1550
#define BLOOD 1620
#define FIRELASER 1625
#define TRANSPORTERSTAR 1630
#define SPIT 1636
#define LOOGIE 1637
#define FIST 1640
#define FREEZEBLAST 1641
#define DEVISTATORBLAST 1642
#define SHRINKSPARK 1646
#define TONGUE 1647
#define MORTER 1650
#define SHRINKEREXPLOSION 1656
#define RADIUSEXPLOSION 1670
#define FORCERIPPLE 1671
#define LIZTROOP 1680
#define LIZTROOPRUNNING 1681
#define LIZTROOPSTAYPUT 1682
#define LIZTOP 1705
#define LIZTROOPSHOOT 1715
#define LIZTROOPJETPACK 1725
#define LIZTROOPDSPRITE 1734
#define LIZTROOPONTOILET 1741
#define LIZTROOPJUSTSIT 1742
#define LIZTROOPDUCKING 1744
#define HEADJIB1 1768
#define ARMJIB1 1772
#define LEGJIB1 1776
#define CANNON 1810
#define CANNONBALL 1817
#define CANNONBALLS 1818
#define OCTABRAIN 1820
#define OCTABRAINSTAYPUT 1821
#define OCTATOP 1845
#define OCTADEADSPRITE 1855
#define INNERJAW 1860
#define DRONE 1880
#define EXPLOSION2 1890
#define COMMANDER 1920
#define COMMANDERSTAYPUT 1921
#define RECON 1960
#define TANK 1975
#define PIGCOP 2000
#define PIGCOPSTAYPUT 2001
#define PIGCOPDIVE 2045
#define PIGCOPDEADSPRITE 2060
#define PIGTOP 2061
#define LIZMAN 2120
#define LIZMANSTAYPUT 2121
#define LIZMANSPITTING 2150
#define LIZMANFEEDING 2160
#define LIZMANJUMP 2165
#define LIZMANDEADSPRITE 2185
#define FECES 2200
#define LIZMANHEAD1 2201
#define LIZMANARM1 2205
#define LIZMANLEG1 2209
#define EXPLOSION2BOT 2219
#define USERWEAPON 2235
#define HEADERBAR 2242
#define JIBS1 2245
#define JIBS2 2250
#define JIBS3 2255
#define JIBS4 2260
#define JIBS5 2265
#define BURNING 2270
#define FIRE 2271
#define JIBS6 2286
#define BLOODSPLAT1 2296
#define BLOODSPLAT3 2297
#define BLOODSPLAT2 2298
#define BLOODSPLAT4 2299
#define OOZ 2300
#define OOZ2 2309
#define WALLBLOOD1 2301
#define WALLBLOOD2 2302
#define WALLBLOOD3 2303
#define WALLBLOOD4 2304
#define WALLBLOOD5 2305
#define WALLBLOOD6 2306
#define WALLBLOOD7 2307
#define WALLBLOOD8 2308
#define BURNING2 2310
#define FIRE2 2311
#define CRACKKNUCKLES 2324
#define SMALLSMOKE 2329
#define SMALLSMOKEMAKER 2330
#define FLOORFLAME 2333
#define ROTATEGUN 2360
#define GREENSLIME 2370
#define WATERDRIPSPLASH 2380
#define SCRAP6 2390
#define SCRAP1 2400
#define SCRAP2 2404
#define SCRAP3 2408
#define SCRAP4 2412
#define SCRAP5 2416
#define ORGANTIC 2420
#define BETAVERSION 2440
#define PLAYERISHERE 2442
#define PLAYERWASHERE 2443
#define SELECTDIR 2444
#define F1HELP 2445
#define NOTCHON 2446
#define NOTCHOFF 2447
#define GROWSPARK 2448
#define DUKEICON 2452
#define BADGUYICON 2453
#define FOODICON 2454
#define GETICON 2455
#define MENUSCREEN 2456
#define MENUBAR 2457
#define KILLSICON 2458
#define FIRSTAID_ICON 2460
#define HEAT_ICON 2461
#define BOTTOMSTATUSBAR 2462
#define BOOT_ICON 2463
#define FRAGBAR 2465
#define JETPACK_ICON 2467
#define AIRTANK_ICON 2468
#define STEROIDS_ICON 2469
#define HOLODUKE_ICON 2470
#define ACCESS_ICON 2471
#define DIGITALNUM 2472
#define DUKECAR 2491
#define CAMCORNER 2482
#define CAMLIGHT 2484
#define LOGO 2485
#define TITLE 2486
#define NUKEWARNINGICON 2487
#define MOUSECURSOR 2488
#define SLIDEBAR 2489
#define DREALMS 2492
#define BETASCREEN 2493
#define WINDOWBORDER1 2494
#define TEXTBOX 2495
#define WINDOWBORDER2 2496
#define DUKENUKEM 2497
#define THREEDEE 2498
#define INGAMEDUKETHREEDEE 2499
#define TENSCREEN 2500
#define PLUTOPAKSPRITE 2501
#define DEVISTATOR 2510
#define KNEE 2521
#define CROSSHAIR 2523
#define FIRSTGUN 2524
#define FIRSTGUNRELOAD 2528
#define FALLINGCLIP 2530
#define CLIPINHAND 2531
#define HAND 2532
#define SHELL 2533
#define SHOTGUNSHELL 2535
#define CHAINGUN 2536
#define RPGGUN 2544
#define RPGMUZZLEFLASH 2545
#define FREEZE 2548
#define CATLITE 2552
#define SHRINKER 2556
#define HANDHOLDINGLASER 2563
#define TRIPBOMB 2566
#define LASERLINE 2567
#define HANDHOLDINGACCESS 2568
#define HANDREMOTE 2570
#define HANDTHROW 2573
#define TIP 2576
#define GLAIR 2578
#define SCUBAMASK 2581
#define SPACEMASK 2584
#define FORCESPHERE 2590
#define SHOTSPARK1 2595
#define RPG 2605
#define LASERSITE 2612
#define SHOTGUN 2613
#define BOSS1 2630
#define BOSS1STAYPUT 2631
#define BOSS1SHOOT 2660
#define BOSS1LOB 2670
#define BOSSTOP 2696
#define BOSS2 2710
#define BOSS3 2760
#define SPINNINGNUKEICON 2813
#define BIGFNTCURSOR 2820
#define SMALLFNTCURSOR 2821
#define STARTALPHANUM 2822
#define ENDALPHANUM 2915
#define BIGALPHANUM 2940
#define BIGPERIOD 3002
#define BIGCOMMA 3003
#define BIGX 3004
#define BIGQ 3005
#define BIGSEMI 3006
#define BIGCOLIN 3007
#define THREEBYFIVE 3010
#define BIGAPPOS 3022
#define BLANK 3026
#define MINIFONT 3072
#define BUTTON1 3164
#define GLASS3 3187
#define RESPAWNMARKERRED 3190
#define RESPAWNMARKERYELLOW 3200
#define RESPAWNMARKERGREEN 3210
#define BONUSSCREEN 3240
#define VIEWBORDER 3250
#define VICTORY1 3260
#define ORDERING 3270
#define TEXTSTORY 3280
#define LOADSCREEN 3281
#define BORNTOBEWILDSCREEN 3370
#define BLIMP 3400
#define FEM9 3450
#define FOOTPRINT 3701
#define FRAMEEFFECT1_13 3999
#define POOP 4094
#define FRAMEEFFECT1 4095
#define PANNEL3 4099
#define SCREENBREAK14 4120
#define SCREENBREAK15 4123
#define SCREENBREAK19 4125
#define SCREENBREAK16 4127
#define SCREENBREAK17 4128
#define SCREENBREAK18 4129
#define W_TECHWALL11 4130
#define W_TECHWALL12 4131
#define W_TECHWALL13 4132
#define W_TECHWALL14 4133
#define W_TECHWALL5 4134
#define W_TECHWALL6 4136
#define W_TECHWALL7 4138
#define W_TECHWALL8 4140
#define W_TECHWALL9 4142
#define BPANNEL3 4100
#define W_HITTECHWALL16 4144
#define W_HITTECHWALL10 4145
#define W_HITTECHWALL15 4147
#define W_MILKSHELF 4181
#define W_MILKSHELFBROKE 4203
#define PURPLELAVA 4240
#define LAVABUBBLE 4340
#define DUKECUTOUT 4352
#define TARGET 4359
#define GUNPOWDERBARREL 4360
#define DUCK 4361
#define HATRACK 4367
#define DESKLAMP 4370
#define COFFEEMACHINE 4372
#define CUPS 4373
#define GAVALS 4374
#define GAVALS2 4375
#define POLICELIGHTPOLE 4377
#define FLOORBASKET 4388
#define PUKE 4389
#define DOORTILE23 4391
#define TOPSECRET 4396
#define SPEAKER 4397
#define TEDDYBEAR 4400
#define ROBOTDOG 4402
#define ROBOTPIRATE 4404
#define ROBOTMOUSE 4407
#define MAIL 4410
#define MAILBAG 4413
#define HOTMEAT 4427
#define COFFEEMUG 4438
#define DONUTS2 4440
#define TRIPODCAMERA 4444
#define METER 4453
#define DESKPHONE 4454
#define GUMBALLMACHINE 4458
#define GUMBALLMACHINEBROKE 4459
#define PAPER 4460
#define MACE 4464
#define GENERICPOLE2 4465
#define XXXSTACY 4470
#define WETFLOOR 4495
#define BROOM 4496
#define MOP 4497
#define LETTER 4502
#define PIRATE1A 4510
#define PIRATE4A 4511
#define PIRATE2A 4512
#define PIRATE5A 4513
#define PIRATE3A 4514
#define PIRATE6A 4515
#define PIRATEHALF 4516
#define CHESTOFGOLD 4520
#define SIDEBOLT1 4525
#define FOODOBJECT1 4530
#define FOODOBJECT2 4531
#define FOODOBJECT3 4532
#define FOODOBJECT4 4533
#define FOODOBJECT5 4534
#define FOODOBJECT6 4535
#define FOODOBJECT7 4536
#define FOODOBJECT8 4537
#define FOODOBJECT9 4538
#define FOODOBJECT10 4539
#define FOODOBJECT11 4540
#define FOODOBJECT12 4541
#define FOODOBJECT13 4542
#define FOODOBJECT14 4543
#define FOODOBJECT15 4544
#define FOODOBJECT16 4545
#define FOODOBJECT17 4546
#define FOODOBJECT18 4547
#define FOODOBJECT19 4548
#define FOODOBJECT20 4549
#define HEADLAMP 4550
#define TAMPON 4557
#define SKINNEDCHICKEN 4554
#define FEATHEREDCHICKEN 4555
#define ROBOTDOG2 4560
#define JOLLYMEAL 4569
#define DUKEBURGER 4570
#define SHOPPINGCART 4576
#define CANWITHSOMETHING2 4580
#define CANWITHSOMETHING3 4581
#define CANWITHSOMETHING4 4582
#define SNAKEP 4590
#define DOLPHIN1 4591
#define DOLPHIN2 4592
#define NEWBEAST 4610
#define NEWBEASTSTAYPUT 4611
#define NEWBEASTJUMP 4690
#define NEWBEASTHANG 4670
#define NEWBEASTHANGDEAD 4671
#define BOSS4 4740
#define BOSS4STAYPUT 4741
#define FEM10 4864
#define TOUGHGAL 4866
#define MAN 4871
#define MAN2 4872
#define WOMAN 4874
#define PLEASEWAIT 4887
#define NATURALLIGHTNING 4890
#define WEATHERWARN 4893
#define DUKETAG 4900
#define SIGN1 4909
#define SIGN2 4912
#define JURYGUY 4943

// These tile positions are reserved!
#define RESERVEDSLOT1 6132
#define RESERVEDSLOT2 6133
#define RESERVEDSLOT3 6134
#define RESERVEDSLOT4 6135
#define RESERVEDSLOT5 6136
#define RESERVEDSLOT6 6137
#define RESERVEDSLOT7 6138
#define RESERVEDSLOT8 6139
#define RESERVEDSLOT9 6140
#define RESERVEDSLOT10 6141
#define RESERVEDSLOT11 6142
#define RESERVEDSLOT12 6143