//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment

This file is part of Shadow Warrior version 1.2

Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

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

See the GNU General Public License for more details.

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.

Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------

#undef MAIN
#include "build.h"
#include "common.h"

#include "keys.h"
#include "names2.h"
#include "panel.h"
#include "lists.h"
#include "game.h"
#include "common_game.h"
#include "network.h"
#include "pal.h"
#include "vis.h"
#include "text.h"
#include "player.h"

#include "weapon.h"
#include "fx_man.h"

//#define UK_VERSION TRUE

#define PANF_UZI_XFLIP (BIT(21))
extern SWBOOL UsingMenus;

#define XDIM 320
#define YDIM 200

typedef struct
{
    short xoff, yoff, skip;
    int lo_jump_speed, hi_jump_speed, lo_xspeed, hi_xspeed;
    PANEL_STATEp state[2];
} PANEL_SHRAP, *PANEL_SHRAPp;

PANEL_SPRITEp pSpawnFullScreenSprite(PLAYERp pp, short pic, short pri, int x, int y);
void DisplayFragNumbers(PLAYERp pp_kill_chg);
void PanelInvTestSuicide(PANEL_SPRITEp psp);

void InsertPanelSprite(PLAYERp pp, PANEL_SPRITEp psp);
void pKillSprite(PANEL_SPRITEp psp);
void pWeaponBob(PANEL_SPRITEp psp, short condition);
void pSuicide(PANEL_SPRITEp psp);
void pNextState(PANEL_SPRITEp psp);
void pStatePlusOne(PANEL_SPRITEp psp);
void pSetState(PANEL_SPRITEp psp, PANEL_STATEp panel_state);
void pStateControl(PANEL_SPRITEp psp);

int DoPanelFall(PANEL_SPRITEp psp);
int DoBeginPanelFall(PANEL_SPRITEp psp);
int DoPanelJump(PANEL_SPRITEp psp);
int DoBeginPanelJump(PANEL_SPRITEp psp);
void SpawnHeartBlood(PANEL_SPRITEp psp);
void SpawnUziShell(PANEL_SPRITEp psp);
void PlayerUpdateWeaponSummary(PLAYERp pp,short UpdateWeaponNum);

SWBOOL pWeaponUnHideKeys(PANEL_SPRITEp psp, PANEL_STATEp state);
SWBOOL pWeaponHideKeys(PANEL_SPRITEp psp, PANEL_STATEp state);
void pHotHeadOverlays(PANEL_SPRITEp psp, short mode);

char UziRecoilYadj = 0;

extern SWBOOL QuitFlag;
extern short screenpeek;

BORDER_INFO BorderInfo;

ANIMATOR NullAnimator;
pANIMATOR pNullAnimator;
int InitStar(PLAYERp);
int ChangeWeapon(PLAYERp);

ANIMATOR InitFire;

int
NullAnimator(short UNUSED(SpriteNum))
{
    return 0;
}

void pNullAnimator(PANEL_SPRITEp UNUSED(psp))
{
    return;
}

PANEL_SPRITEp
pFindMatchingSprite(PLAYERp pp, int x, int y, short pri)
{
    PANEL_SPRITEp psp=NULL, next;

    TRAVERSE(&pp->PanelSpriteList, psp, next)
    {
        // early out
        if (psp->priority > pri)
            return NULL;

        if (psp->x == x && psp->y == y && psp->priority == pri)
        {
            return psp;
        }
    }

    return NULL;
}

PANEL_SPRITEp
pFindMatchingSpriteID(PLAYERp pp, short id, int x, int y, short pri)
{
    PANEL_SPRITEp psp=NULL, next;

    TRAVERSE(&pp->PanelSpriteList, psp, next)
    {
        // early out
        if (psp->priority > pri)
            return NULL;

        if (psp->ID == id && psp->x == x && psp->y == y && psp->priority == pri)
        {
            return psp;
        }
    }

    return NULL;
}

SWBOOL
pKillScreenSpiteIDs(PLAYERp pp, short id)
{
    PANEL_SPRITEp psp=NULL, next;
    SWBOOL found = FALSE;

    // Kill ALL sprites with the correct id
    TRAVERSE(&pp->PanelSpriteList, psp, next)
    {
        if (psp->ID == id)
        {
            pKillSprite(psp);
            found = TRUE;
        }
    }

    return found;
}


// Used to sprites in the view at correct aspect ratio and x,y location.

PANEL_SPRITEp
pSpawnFullViewSprite(PLAYERp pp, short pic, short pri, int x, int y)
{
    PANEL_SPRITEp nsp;

    if ((nsp = pFindMatchingSprite(pp, x, y, pri)) == NULL)
    {
        nsp = pSpawnSprite(pp, NULL, pri, x, y);
    }

    nsp->numpages = numpages;
    nsp->picndx = -1;
    nsp->picnum = pic;
    nsp->x1 = 0;
    nsp->y1 = 0;
    nsp->x2 = xdim - 1;
    nsp->y2 = ydim - 1;
    SET(nsp->flags, PANF_STATUS_AREA | PANF_SCREEN_CLIP);

    return nsp;
}

// Used to display panel info at correct aspect ratio and x,y location on the
// status panel.  Sprites will kill themselves after writing to all pages.

PANEL_SPRITEp
pSpawnFullScreenSprite(PLAYERp pp, short pic, short pri, int x, int y)
{
    PANEL_SPRITEp nsp;

    if ((nsp = pFindMatchingSprite(pp, x, y, pri)) == NULL)
    {
        nsp = pSpawnSprite(pp, NULL, pri, x, y);
    }

    nsp->numpages = numpages;
    nsp->picndx = -1;
    nsp->picnum = pic;
    nsp->x1 = 0;
    nsp->y1 = 0;
    nsp->x2 = xdim - 1;
    nsp->y2 = ydim - 1;
    SET(nsp->flags, PANF_STATUS_AREA | PANF_SCREEN_CLIP | PANF_KILL_AFTER_SHOW);

    return nsp;
}


void pSetSuicide(PANEL_SPRITEp psp)
{
    //SET(psp->flags, PANF_SUICIDE);
    //psp->State = NULL;
    psp->PanelSpriteFunc = pSuicide;
}

void pToggleCrosshair(void)
{
    if (gs.Crosshair)
        gs.Crosshair = FALSE;
    else
        gs.Crosshair = TRUE;
}

// Player has a chance of yelling out during combat, when firing a weapon.
void DoPlayerChooseYell(PLAYERp pp)
{
    int choose_snd = 0;
    short weapon;

    weapon = TEST(pp->input.bits, SK_WEAPON_MASK);

    if (weapon == WPN_FIST)
    {
        if (RANDOM_RANGE(1000) < 900) return;
    }
    else if (RANDOM_RANGE(1000) < 990) return;

    choose_snd = STD_RANDOM_RANGE(MAX_YELLSOUNDS);

    if (pp == Player+myconnectindex)
        PlayerSound(PlayerYellVocs[choose_snd],&pp->posx,&pp->posy,&pp->posz,v3df_follow|v3df_dontpan,pp);
}

void ArmorCalc(int damage_amt, int *armor_damage, int *player_damage)
{
    int damage_percent;

    if (damage_amt == 1)
    {
        *player_damage = RANDOM_P2(2<<8)>>8;
        *armor_damage = 1;
        return;
    }

    // note: this could easily be converted to a mulscale and save a
    // bit of processing for floats
    damage_percent = ((0.6 * damage_amt)+0.5);

    *player_damage = damage_amt - damage_percent;
    *armor_damage = damage_percent;
}


void
PlayerUpdateHealth(PLAYERp pp, short value)
{
    USERp u = User[pp->PlayerSprite];
    short x,y;

#define PANEL_HEALTH_BOX_X 20
#define PANEL_BOX_Y (174-6)
#define PANEL_HEALTH_XOFF 2
#define PANEL_HEALTH_YOFF 4
#define HEALTH_ERASE 2401

    if (Prediction)
        return;

    if (GodMode)
    {
        if (value < 0)
            return;
    }

    if (TEST(pp->Flags, PF_DEAD))
        return;

    if (value < 0)
    {
        SWBOOL IsChem = FALSE;
        SWBOOL NoArmor = FALSE;

        if (value <= -2000)
        {
            value += 2000;
            NoArmor = TRUE;
        }
        else if (value <= -1000)
        {
            //DSPRINTF(ds,"value = %d\n",value);
            MONO_PRINT(ds);

            value += 1000;
            IsChem = TRUE;
        }

        // TAKE SOME DAMAGE
        u->LastDamage = -value;

        // adjust for armor
        if (pp->Armor && !NoArmor)
        {
            int armor_damage, player_damage;
            ArmorCalc(labs(value), &armor_damage, &player_damage);
            PlayerUpdateArmor(pp, -armor_damage);
            value = -player_damage;
        }

        u->Health += value;

        if (value < 0)
        {
            int choosesnd = 0;

            choosesnd = RANDOM_RANGE(MAX_PAIN);

            if (u->Health > 50)
            {
                PlayerSound(PlayerPainVocs[choosesnd],&pp->posx,
                            &pp->posy,&pp->posy,v3df_dontpan|v3df_doppler|v3df_follow,pp);
            }
            else
            {
                PlayerSound(PlayerLowHealthPainVocs[choosesnd],&pp->posx,
                            &pp->posy,&pp->posy,v3df_dontpan|v3df_doppler|v3df_follow,pp);
            }

        }
        // Do redness based on damage taken.
        if (pp == Player + screenpeek)
        {
            if (IsChem)
                SetFadeAmt(pp,-40,144);  // ChemBomb green color
            else
            {
                if (value <= -10)
                    SetFadeAmt(pp,value,112);
                else if (value > -10 && value < 0)
                    SetFadeAmt(pp,-20,112);
            }
        }
        if (u->Health <= 100)
            pp->MaxHealth = 100; // Reset max health if sank below 100
    }
    else
    {
        // ADD SOME HEALTH
        if (value > 1000)
            u->Health += (value-1000);
        else
        {
            if (u->Health < pp->MaxHealth)
            {
                u->Health+=value;
            }
        }


        if (value >= 1000)
            value -= 1000;  // Strip out high value
    }

    if (u->Health < 0)
        u->Health = 0;

    if (u->Health > pp->MaxHealth)
        u->Health = pp->MaxHealth;

    if (gs.BorderNum < BORDER_BAR || pp - Player != screenpeek)
        return;

    // erase old info
    pSpawnFullScreenSprite(pp, HEALTH_ERASE, PRI_MID, PANEL_HEALTH_BOX_X, PANEL_BOX_Y);

    x = PANEL_HEALTH_BOX_X + PANEL_HEALTH_XOFF;
    y = PANEL_BOX_Y + PANEL_HEALTH_YOFF;
    DisplayPanelNumber(pp, x, y, u->Health);
}


void
PlayerUpdateAmmo(PLAYERp pp, short UpdateWeaponNum, short value)
{
    USERp u = User[pp->PlayerSprite];
    short x,y;
    short WeaponNum;

#define PANEL_AMMO_BOX_X 197
#define PANEL_AMMO_XOFF 1
#define PANEL_AMMO_YOFF 4
#define AMMO_ERASE 2404

    if (Prediction)
        return;

    if (DamageData[UpdateWeaponNum].max_ammo == -1)
    {
        if (gs.BorderNum < BORDER_BAR || pp - Player != screenpeek)
            return;

        // erase old info
        pSpawnFullScreenSprite(pp, AMMO_ERASE, PRI_MID, PANEL_AMMO_BOX_X, PANEL_BOX_Y);
        //pSpawnFullScreenSprite(pp, AMMO_ERASE, PRI_FRONT_MAX+1, PANEL_AMMO_BOX_X, PANEL_BOX_Y);
        return;
    }

    WeaponNum = UpdateWeaponNum;

    // get the WeaponNum of the ammo
    if (DamageData[UpdateWeaponNum].with_weapon != -1)
    {
        WeaponNum = DamageData[UpdateWeaponNum].with_weapon;
    }

    pp->WpnAmmo[WeaponNum] += value;

    if (pp->WpnAmmo[WeaponNum] <= 0)
    {
        // star and mine
        if (TEST(WeaponIsAmmo, BIT(WeaponNum)))
            RESET(pp->WpnFlags, BIT(WeaponNum));

        pp->WpnAmmo[WeaponNum] = 0;
    }

    if (pp->WpnAmmo[WeaponNum] > DamageData[WeaponNum].max_ammo)
    {
        pp->WpnAmmo[WeaponNum] = DamageData[WeaponNum].max_ammo;
    }

    if (gs.BorderNum < BORDER_BAR || pp - Player != screenpeek)
        return;

    PlayerUpdateWeaponSummary(pp, WeaponNum);

    if (UpdateWeaponNum != u->WeaponNum)
        return;

    // erase old info
    pSpawnFullScreenSprite(pp, AMMO_ERASE, PRI_MID, PANEL_AMMO_BOX_X, PANEL_BOX_Y);

    x = PANEL_AMMO_BOX_X + PANEL_AMMO_XOFF;
    y = PANEL_BOX_Y + PANEL_AMMO_YOFF;

    DisplayPanelNumber(pp, x, y, pp->WpnAmmo[WeaponNum]);
}

void
PlayerUpdateWeaponSummary(PLAYERp pp, short UpdateWeaponNum)
{
    USERp u = User[pp->PlayerSprite];
    short x,y;
    short pos;
    short column;
    short WeaponNum,wpntmp;
    short color,shade;

#define WSUM_X 93
#define WSUM_Y PANEL_BOX_Y+1
#define WSUM_XOFF 25
#define WSUM_YOFF 6

    static short wsum_xoff[3] = {0,36,66};
    static const char *wsum_fmt1[3] = {"%d:", "%d:", "%d:"};
    static const char *wsum_fmt2[3] = {"%3d/%-3d", "%2d/%-2d", "%2d/%-2d"};
    static short wsum_back_pic[3] = {2405, 2406, 2406};

    if (Prediction)
        return;

    WeaponNum = UpdateWeaponNum;

    if (DamageData[WeaponNum].with_weapon != -1)
    {
        WeaponNum = DamageData[WeaponNum].with_weapon;
    }

    if (gs.BorderNum < BORDER_BAR || pp - Player != screenpeek)
        return;

    pos = WeaponNum-1;
    column = pos/3;
    if (column > 2) column = 2;
    x = WSUM_X + wsum_xoff[column];
    y = WSUM_Y + (WSUM_YOFF * (pos%3));

    // erase old info
    pSpawnFullScreenSprite(pp, wsum_back_pic[column], PRI_MID, x, y);

    if (UpdateWeaponNum == u->WeaponNum)
    {
        shade = 0;
        color = 0;
    }
    else
    {
        shade = 11;
        color = 0;
    }

    wpntmp = WeaponNum+1;
    if (wpntmp > 9)
        wpntmp = 0;
    sprintf(ds, wsum_fmt1[column], wpntmp);

    if (TEST(pp->WpnFlags, BIT(WeaponNum)))
        DisplaySummaryString(pp, x, y, 1, shade, ds);
    else
        DisplaySummaryString(pp, x, y, 2, shade+6, ds);

    sprintf(ds, wsum_fmt2[column], pp->WpnAmmo[WeaponNum], DamageData[WeaponNum].max_ammo);
    DisplaySummaryString(pp, x+6, y, color, shade, ds);
}

void PlayerUpdateWeaponSummaryAll(PLAYERp pp)
{
    short i;

    if (Prediction)
        return;

    for (i = WPN_STAR; i <= WPN_HEART; i++)
    {
        PlayerUpdateWeaponSummary(pp, i);
    }
}

void
PlayerUpdateWeapon(PLAYERp pp, short WeaponNum)
{
    USERp u = User[pp->PlayerSprite];

    // Weapon Change
    if (Prediction)
        return;

    u->WeaponNum = WeaponNum;

    if (gs.BorderNum < BORDER_BAR || pp - Player != screenpeek)
        return;

    PlayerUpdateAmmo(pp, u->WeaponNum, 0);
    PlayerUpdateWeaponSummaryAll(pp);
}

void
PlayerUpdateKills(PLAYERp pp, short value)
{
#define PANEL_KILLS_X 31
#define PANEL_KILLS_Y 164

    if (Prediction)
        return;

    if (gNet.MultiGameType == MULTI_GAME_COOPERATIVE)
        return;

    // Team play
    if (gNet.MultiGameType == MULTI_GAME_COMMBAT && gNet.TeamPlay)
    {
        short pnum;
        PLAYERp opp;

        TRAVERSE_CONNECT(pnum)
        {
            opp = Player + pnum;

            // for everyone on the same team
            if (opp != pp && User[opp->PlayerSprite]->spal == User[pp->PlayerSprite]->spal)
            {
                opp->Kills += value;
                if (opp->Kills > 999)
                    opp->Kills = 0;
                if (opp->Kills < -99)
                    opp->Kills = -99;

                if (numplayers >= 2)
                    DisplayFragNumbers(opp);
            }
        }
    }

    pp->Kills += value;
    if (pp->Kills > 999)
        pp->Kills = 0;
    if (pp->Kills < -99)
        pp->Kills = -99;

    if (numplayers >= 2)
        DisplayFragNumbers(pp);
}

void
PlayerUpdateArmor(PLAYERp pp, short value)
{
    short x,y;

#define PANEL_ARMOR_BOX_X 56
#define PANEL_ARMOR_XOFF 2
#define PANEL_ARMOR_YOFF 4
#define ARMOR_ERASE 2401

    if (Prediction)
        return;

    if (value >= 1000)
        pp->Armor = value-1000;
    else
        pp->Armor += value;

    if (pp->Armor > 100)
        pp->Armor = 100;
    if (pp->Armor < 0)
        pp->Armor = 0;

    if (gs.BorderNum < BORDER_BAR || pp - Player != screenpeek)
        return;

    // erase old info
    pSpawnFullScreenSprite(pp, ARMOR_ERASE, PRI_MID, PANEL_ARMOR_BOX_X, PANEL_BOX_Y);

    x = PANEL_ARMOR_BOX_X + PANEL_ARMOR_XOFF;
    y = PANEL_BOX_Y + PANEL_ARMOR_YOFF;
    DisplayPanelNumber(pp, x, y, pp->Armor);
}


void
PlayerUpdateKeys(PLAYERp pp)
{
#define PANEL_KEYS_BOX_X 276
#define PANEL_KEYS_XOFF 0
#define PANEL_KEYS_YOFF 2
#define KEYS_ERASE 2402

    short x,y;
    short row,col;
    short i, xsize, ysize;

#define PANEL_KEY_RED       2392
#define PANEL_KEY_GREEN     2393
#define PANEL_KEY_BLUE      2394
#define PANEL_KEY_YELLOW    2395
#define PANEL_SKELKEY_GOLD  2448
#define PANEL_SKELKEY_SILVER 2449
#define PANEL_SKELKEY_BRONZE 2458
#define PANEL_SKELKEY_RED   2459

    static short StatusKeyPics[] =
    {
        PANEL_KEY_RED,
        PANEL_KEY_BLUE,
        PANEL_KEY_GREEN,
        PANEL_KEY_YELLOW,
        PANEL_SKELKEY_GOLD,
        PANEL_SKELKEY_SILVER,
        PANEL_SKELKEY_BRONZE,
        PANEL_SKELKEY_RED
    };

    if (Prediction)
        return;

    if (gNet.MultiGameType == MULTI_GAME_COMMBAT)
        return;

    if (gs.BorderNum < BORDER_BAR || pp - Player != screenpeek)
        return;

    xsize = tilesiz[PANEL_KEY_RED].x+1;
    ysize = tilesiz[PANEL_KEY_RED].y+2;

    // erase old info
    pSpawnFullScreenSprite(pp, KEYS_ERASE, PRI_MID, PANEL_KEYS_BOX_X, PANEL_BOX_Y);

    i = 0;
    for (row = 0; row < 2; row++)
    {
        for (col = 0; col < 2; col++)
        {
            if (pp->HasKey[i])
            {
                x = PANEL_KEYS_BOX_X + PANEL_KEYS_XOFF + (row * xsize);
                y = PANEL_BOX_Y + PANEL_KEYS_YOFF + (col * ysize);
                pSpawnFullScreenSprite(pp, StatusKeyPics[i], PRI_FRONT_MAX, x, y);
            }

            i++;
        }
    }

    // Check for skeleton keys
    i = 0;
    for (row = 0; row < 2; row++)
    {
        for (col = 0; col < 2; col++)
        {
            if (pp->HasKey[i+4])
            {
                x = PANEL_KEYS_BOX_X + PANEL_KEYS_XOFF + (row * xsize);
                y = PANEL_BOX_Y + PANEL_KEYS_YOFF + (col * ysize);
                pSpawnFullScreenSprite(pp, StatusKeyPics[i+4], PRI_FRONT_MAX, x, y);
            }

            i++;
        }
    }
}

void
PlayerUpdateTimeLimit(PLAYERp pp)
{
    int seconds;

    if (Prediction)
        return;

    if (gs.BorderNum < BORDER_BAR || pp - Player != screenpeek)
        return;

    if (gNet.MultiGameType != MULTI_GAME_COMMBAT)
        return;

    if (!gNet.TimeLimit)
        return;

    // erase old info
    pSpawnFullScreenSprite(pp, KEYS_ERASE, PRI_MID, PANEL_KEYS_BOX_X, PANEL_BOX_Y);

    seconds = gNet.TimeLimitClock/120;
    sprintf(ds,"%03d:%02d",seconds/60, seconds%60);
    DisplaySummaryString(pp, PANEL_KEYS_BOX_X+1, PANEL_BOX_Y+6, 0, 0, ds);
}

void
PlayerUpdatePanelInfo(PLAYERp pp)
{
    USERp u = User[pp->PlayerSprite];

    if (Prediction)
        return;

    PlayerUpdateHealth(pp, 0);
    PlayerUpdateInventory(pp, pp->InventoryNum);
    PlayerUpdateAmmo(pp, u->WeaponNum, 0);
    PlayerUpdateWeapon(pp, u->WeaponNum);
    PlayerUpdateKeys(pp);
    PlayerUpdateArmor(pp, 0);
    PlayerUpdateWeaponSummaryAll(pp);
    PlayerUpdateTimeLimit(pp);
}

int
WeaponOperate(PLAYERp pp)
{
    short weapon;
    int DoPlayerSpriteReset(short SpriteNum);
    USERp u = User[pp->PlayerSprite];


    InventoryKeys(pp);

    // UziType >= 3 means they are reloading
    if (pp->WpnUziType >= 3) return TRUE;

    //if (CheatInputMode)
    //    return (0);

    if (pp->FirePause != 0)
    {
        pp->FirePause -= synctics;
        if (pp->FirePause <= 0)
            pp->FirePause = 0;
    }

    if (pp->sop)
    {
        switch (pp->sop->track)
        {
        case SO_TANK:
        case SO_TURRET:
        case SO_TURRET_MGUN:
        case SO_SPEED_BOAT:

            if (!TEST(pp->sop->flags, SOBJ_HAS_WEAPON))
                break;

            if (TEST_SYNC_KEY(pp, SK_SHOOT))
            {
                if (FLAG_KEY_PRESSED(pp, SK_SHOOT))
                {
                    if (!pp->FirePause)
                    {
                        InitSobjGun(pp);
                    }
                }
            }

            return 0;
        }
    }

    weapon = TEST(pp->input.bits, SK_WEAPON_MASK);

    if (weapon)
    {
        if (FLAG_KEY_PRESSED(pp, SK_WEAPON_BIT0))
        {
            FLAG_KEY_RELEASE(pp, SK_WEAPON_BIT0);

            weapon -= 1;

            // Special uzi crap
            if (weapon != WPN_UZI) pp->WpnUziType = 2; // This means we aren't on uzi

            switch (weapon)
            {
            case WPN_FIST:
                //case WPN_SWORD:
                if (u->WeaponNum == WPN_FIST
                    || u->WeaponNum == WPN_SWORD)
                {
                    // toggle
                    if (pp->WpnFirstType == WPN_FIST)
                        pp->WpnFirstType = WPN_SWORD;
                    else if (pp->WpnFirstType == WPN_SWORD)
                        pp->WpnFirstType = WPN_FIST;
                }

                switch (pp->WpnFirstType)
                {
                case WPN_SWORD:
                    InitWeaponSword(pp);
                    break;
                case WPN_FIST:
                    InitWeaponFist(pp);
                    break;
                }
                break;
            case WPN_STAR:
                InitWeaponStar(pp);
                break;
            case WPN_UZI:
                if (u->WeaponNum == WPN_UZI)
                {
                    if (TEST(pp->Flags, PF_TWO_UZI))
                    {
                        pp->WpnUziType++;
                        if (pp->WpnUziType > 1)
                            pp->WpnUziType = 0;
                    }
                    else
                        pp->WpnUziType = 1; // Use retracted state for single uzi
                }
                InitWeaponUzi(pp);
                break;
            case WPN_MICRO:
                if (u->WeaponNum == WPN_MICRO)
                {
                    pp->WpnRocketType++;
                    if (pp->WpnRocketType > 2)
                        pp->WpnRocketType = 0;
                    if (pp->WpnRocketType == 2 && pp->WpnRocketNuke == 0)
                        pp->WpnRocketType = 0;
                    if (pp->WpnRocketType == 2)
                        pp->TestNukeInit = TRUE; // Init the nuke
                    else
                        pp->TestNukeInit = FALSE;
                }
                InitWeaponMicro(pp);
                break;
            case WPN_SHOTGUN:
                if (u->WeaponNum == WPN_SHOTGUN)
                {
                    pp->WpnShotgunType++;
                    if (pp->WpnShotgunType > 1)
                        pp->WpnShotgunType = 0;
                    PlaySound(DIGI_SHOTGUN_UP, &pp->posx, &pp->posy, &pp->posz, v3df_follow);
                }
                InitWeaponShotgun(pp);
                break;
            case WPN_RAIL:
                if (!SW_SHAREWARE)
                {
#if 0
                    if (u->WeaponNum == WPN_RAIL)
                    {
                        pp->WpnRailType++;
                        if (pp->WpnRailType > 1)
                            pp->WpnRailType = 0;
                    }
                    if (pp->WpnRailType == 1)
                        PlaySound(DIGI_RAIL_UP, &pp->posx, &pp->posy, &pp->posz, v3df_follow);
#endif
                    InitWeaponRail(pp);
                }
                else
                {
                    PutStringInfo(pp,"Order the full version");
                }
                break;
            case WPN_HOTHEAD:
                if (!SW_SHAREWARE)
                {
                    if (u->WeaponNum == WPN_HOTHEAD
                        || u->WeaponNum == WPN_RING
                        || u->WeaponNum == WPN_NAPALM)
                    {
                        pp->WpnFlameType++;
                        if (pp->WpnFlameType > 2)
                            pp->WpnFlameType = 0;
//                      if(pp->Wpn[WPN_HOTHEAD])
                        pHotHeadOverlays(pp->Wpn[WPN_HOTHEAD], pp->WpnFlameType);
                        PlaySound(DIGI_HOTHEADSWITCH, &pp->posx, &pp->posy, &pp->posz, v3df_dontpan|v3df_follow);
                    }

                    InitWeaponHothead(pp);
                }
                else
                {
                    PutStringInfo(pp,"Order the full version");
                }
                break;
            case WPN_HEART:
                if (!SW_SHAREWARE)
                {
                    InitWeaponHeart(pp);
                }
                else
                {
                    PutStringInfo(pp,"Order the full version");
                }
                break;
            case WPN_GRENADE:
                InitWeaponGrenade(pp);
                break;
            case WPN_MINE:
                //InitChops(pp);
                InitWeaponMine(pp);
                break;
            case 13:
                pp->WpnFirstType = WPN_FIST;
                InitWeaponFist(pp);
                break;
            case 14:
                pp->WpnFirstType = WPN_SWORD;
                InitWeaponSword(pp);
                break;
            }
        }
    }
    else
    {
        FLAG_KEY_RESET(pp, SK_WEAPON_BIT0);
    }

    // Shut that computer chick up if weapon has changed!
    if (pp->WpnRocketType != 2 || pp->CurWpn != pp->Wpn[WPN_MICRO])
    {
        pp->InitingNuke = FALSE;
        if (pp->nukevochandle > 0)
        {
            FX_StopSound(pp->nukevochandle);
            pp->nukevochandle = 0;
        }
    }

    return 0;
}

SWBOOL
WeaponOK(PLAYERp pp)
{
    USERp u = User[pp->PlayerSprite];
    short min_ammo, WeaponNum, FindWeaponNum;
    static char wpn_order[] = {2,3,4,5,6,7,8,9,1,0};
    unsigned wpn_ndx=0;

    // sword
    if (DamageData[u->WeaponNum].max_ammo == -1)
        return TRUE;

    WeaponNum = u->WeaponNum;
    FindWeaponNum = u->WeaponNum;

    //if (WeaponNum == WPN_HOTHEAD)
    //    {
    //if (pp->WpnFlameType == 0)
    //    min_ammo = 1;
    //else
    //    min_ammo = 10;
    //min_ammo = DamageData[pp->WeaponType].min_ammo;
    //    }
    //else
    min_ammo = DamageData[WeaponNum].min_ammo;

    // if ran out of ammo switch to something else
    if (pp->WpnAmmo[WeaponNum] < min_ammo)
    {
        if (u->WeaponNum == WPN_UZI) pp->WpnUziType = 2; // Set it for retract

        // Special HotHead check
        //if (WeaponNum == WPN_HOTHEAD)
        //    {
        //    if (pp->WpnFlameType > 0 && pp->WpnAmmo[WeaponNum] > 0)
        //        return(TRUE);
        //    }

        // Still got a nuke, it's ok.
        if (WeaponNum == WPN_MICRO && pp->WpnRocketNuke)
        {
            //pp->WpnRocketType = 2; // Set it to Nuke
            if (!pp->NukeInitialized) pp->TestNukeInit = TRUE;

            u->WeaponNum = WPN_MICRO;
            (*DamageData[u->WeaponNum].Init)(pp);

            return TRUE;
        }

        FLAG_KEY_RELEASE(pp, SK_SHOOT);

        FindWeaponNum = WPN_SHOTGUN; // Start at the top

        while (TRUE)
        {
            // ran out of weapons - choose SWORD
            if (wpn_ndx > sizeof(wpn_order))
            {
                FindWeaponNum = WPN_SWORD;
                break;
            }

            // if you have the weapon and the ammo is greater than 0
            if (TEST(pp->WpnFlags, BIT(FindWeaponNum)) && pp->WpnAmmo[FindWeaponNum] >= min_ammo)
                break;

            wpn_ndx++;
            FindWeaponNum = wpn_order[wpn_ndx];
        }

        u->WeaponNum = FindWeaponNum;

        if (u->WeaponNum == WPN_HOTHEAD)
        {
            pp->WeaponType = WPN_HOTHEAD;
            pp->WpnFlameType = 0;
        }

        (*DamageData[u->WeaponNum].Init)(pp);

        return FALSE;
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// SWORD
//
//////////////////////////////////////////////////////////////////////////////////////////

#if 0
static short SwordAngTable[] =
{
    48,
    128,
    256
};
#else
static short SwordAngTable[] =
{
    82,
    168,
    256+64
};
#endif

short SwordAng = 0;

void
SwordBlur(PANEL_SPRITEp psp)
{
    psp->kill_tics -= synctics;
    if (psp->kill_tics <= 0)
    {
        pKillSprite(psp);
        return;
    }
    else if (psp->kill_tics <= 6)
    {
        SET(psp->flags, PANF_TRANS_FLIP);
    }

    psp->shade += 10;
    // change sprites priority
    REMOVE(psp);
    psp->priority--;
    InsertPanelSprite(psp->PlayerP, psp);
}

void
SpawnSwordBlur(PANEL_SPRITEp psp)
{
    PANEL_SPRITEp nsp;
    //PICITEMp pip;

    if (psp->PlayerP->SwordAng > 200)
        return;

    nsp = pSpawnSprite(psp->PlayerP, NULL, PRI_BACK, psp->x, psp->y);

    SET(nsp->flags, PANF_WEAPON_SPRITE);
    nsp->xfract = psp->xfract;
    nsp->yfract = psp->yfract;
    nsp->ang = psp->ang;
    nsp->vel = psp->vel;
    nsp->PanelSpriteFunc = SwordBlur;
    nsp->kill_tics = 9;
    nsp->shade = psp->shade + 10;

    nsp->picndx = -1;
    nsp->picnum = psp->picndx;

    if (TEST(psp->State->flags, psf_Xflip))
        SET(nsp->flags, PANF_XFLIP);

    nsp->rotate_ang = psp->rotate_ang;
    nsp->scale = psp->scale;

    SET(nsp->flags, PANF_TRANSLUCENT);
}

void pSwordPresent(PANEL_SPRITEp psp);
void pSwordRetract(PANEL_SPRITEp psp);
void pSwordAction(PANEL_SPRITEp psp);
void pSwordRest(PANEL_SPRITEp psp);
void pSwordAttack(PANEL_SPRITEp psp);
void pSwordSlide(PANEL_SPRITEp psp);
void pSwordSlideDown(PANEL_SPRITEp psp);
void pSwordHide(PANEL_SPRITEp psp);
void pSwordUnHide(PANEL_SPRITEp psp);

void pSwordSlideR(PANEL_SPRITEp psp);
void pSwordSlideDownR(PANEL_SPRITEp psp);

extern PANEL_STATE ps_SwordSwing[];
extern PANEL_STATE ps_ReloadSword[];

#define Sword_BEAT_RATE 24
#define Sword_ACTION_RATE 10

PANEL_STATE ps_PresentSword[] =
{
    {ID_SwordPresent0, Sword_BEAT_RATE, pSwordPresent, &ps_PresentSword[0], 0,0,0}
};

PANEL_STATE ps_SwordRest[] =
{
    {ID_SwordPresent0, Sword_BEAT_RATE, pSwordRest, &ps_SwordRest[0], 0,0,0}
};

PANEL_STATE ps_SwordHide[] =
{
    {ID_SwordPresent0, Sword_BEAT_RATE, pSwordHide, &ps_SwordHide[0], 0,0,0}
};

#define SWORD_PAUSE_TICS 10
//#define SWORD_SLIDE_TICS 14
#define SWORD_SLIDE_TICS 10
#define SWORD_MID_SLIDE_TICS 14

PANEL_STATE ps_SwordSwing[] =
{
    {ID_SwordSwing0, SWORD_PAUSE_TICS,                    pNullAnimator,      &ps_SwordSwing[1], 0,0,0},
    {ID_SwordSwing1, SWORD_SLIDE_TICS, /* start slide */ pSwordSlide,        &ps_SwordSwing[2], 0,0,0},
    {ID_SwordSwing1, 0, /* damage */ pSwordAttack,       &ps_SwordSwing[3], psf_QuickCall, 0,0},
    {ID_SwordSwing2, SWORD_MID_SLIDE_TICS, /* mid slide */ pSwordSlideDown,    &ps_SwordSwing[4], 0,0,0},

    {ID_SwordSwing2, 99, /* end slide */ pSwordSlideDown,    &ps_SwordSwing[4], 0,0,0},

    {ID_SwordSwingR1, SWORD_SLIDE_TICS, /* start slide */ pSwordSlideR,       &ps_SwordSwing[6], psf_Xflip, 0,0},
    {ID_SwordSwingR2, 0, /* damage */ pSwordAttack,       &ps_SwordSwing[7], psf_QuickCall|psf_Xflip, 0,0},
    {ID_SwordSwingR2, SWORD_MID_SLIDE_TICS, /* mid slide */ pSwordSlideDownR,   &ps_SwordSwing[8], psf_Xflip, 0,0},

    {ID_SwordSwingR2, 99, /* end slide */ pSwordSlideDownR,   &ps_SwordSwing[8], psf_Xflip, 0,0},
    {ID_SwordSwingR2, 2, /* end slide */ pNullAnimator,      &ps_SwordSwing[1], psf_Xflip, 0,0},
};

PANEL_STATE ps_RetractSword[] =
{
    {ID_SwordPresent0, Sword_BEAT_RATE, pSwordRetract, &ps_RetractSword[0], 0,0,0}
};

#define SWORD_SWAY_AMT 12

// left swing
#define SWORD_XOFF (320 + SWORD_SWAY_AMT)
#define SWORD_YOFF 200

// right swing
#define SWORDR_XOFF (0 - 80)

#define SWORD_VEL 1700
#define SWORD_POWER_VEL 2500


void SpecialUziRetractFunc(PANEL_SPRITEp psp)
{
    psp->y += 4 * synctics;

    if (psp->y >= 200 + tilesiz[psp->picnum].y)
    {
        pKillSprite(psp);
    }
}

void
RetractCurWpn(PLAYERp pp)
{
    // Retract old weapon
    if (pp->CurWpn)
    {
        PANEL_SPRITEp cur,nxt;

        if ((pp->CurWpn == pp->Wpn[WPN_UZI] && pp->WpnUziType == 2) || pp->CurWpn != pp->Wpn[WPN_UZI])
        {
            pSetState(pp->CurWpn, pp->CurWpn->RetractState);
            SET(pp->Flags, PF_WEAPON_RETRACT);
        }

        if (pp->CurWpn->sibling)
        {
            // primarily for double uzi to take down the second uzi
            pSetState(pp->CurWpn->sibling, pp->CurWpn->sibling->RetractState);
        }
        else
        {
            // check for any outstanding siblings that need to go away also
            TRAVERSE(&pp->PanelSpriteList, cur, nxt)
            {
                if (cur->sibling && cur->sibling == pp->CurWpn)
                {
                    // special case for uzi reload pieces
                    cur->picnum = cur->picndx;
                    cur->State = NULL;
                    cur->PanelSpriteFunc = SpecialUziRetractFunc;
                    cur->sibling = NULL;
                }
            }
        }
    }
}

void
InitWeaponSword(PLAYERp pp)
{
    PANEL_SPRITEp psp;
    short rnd_num;


    if (Prediction)
        return;

    if (!TEST(pp->WpnFlags, BIT(WPN_SWORD)) ||
        TEST(pp->Flags, PF_WEAPON_RETRACT))
        return;

    // needed for death sequence when the SWORD was your weapon when you died
    if (pp->Wpn[WPN_SWORD] && TEST(pp->Wpn[WPN_SWORD]->flags, PANF_DEATH_HIDE))
    {
        RESET(pp->Wpn[WPN_SWORD]->flags, PANF_DEATH_HIDE);
        RESET(pp->Flags, PF_WEAPON_RETRACT|PF_WEAPON_DOWN);
        pSetState(pp->CurWpn, pp->CurWpn->PresentState);
        return;
    }


    if (!pp->Wpn[WPN_SWORD])
    {
        psp = pp->Wpn[WPN_SWORD] = pSpawnSprite(pp, ps_PresentSword, PRI_MID, SWORD_XOFF, SWORD_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    if (pp->CurWpn == pp->Wpn[WPN_SWORD])
    {
        return;
    }

    PlayerUpdateWeapon(pp, WPN_SWORD);

    pp->WpnUziType = 2; // Make uzi's go away!
    RetractCurWpn(pp);

    // Set up the new Weapon variables
    psp = pp->CurWpn = pp->Wpn[WPN_SWORD];
    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = ps_SwordSwing;
    psp->RetractState = ps_RetractSword;
    psp->PresentState = ps_PresentSword;
    psp->RestState = ps_SwordRest;
    pSetState(psp, psp->PresentState);

    PlaySound(DIGI_SWORD_UP, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan);

    if (pp == Player+myconnectindex)
    {
        rnd_num = STD_RANDOM_RANGE(1024);
        if (rnd_num > 900)
            PlaySound(DIGI_TAUNTAI2, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan);
        else if (rnd_num > 800)
            PlaySound(DIGI_PLAYERYELL1, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan);
        else if (rnd_num > 700)
            PlaySound(DIGI_PLAYERYELL2, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan);
        else if (rnd_num > 600)
            PlayerSound(DIGI_ILIKESWORD,&pp->posx,&pp->posy,&pp->posz,v3df_follow|v3df_dontpan,pp);
    }

    FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
    FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);
}


void
pSwordPresent(PANEL_SPRITEp psp)
{
    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    psp->y -= 3 * synctics;

    if (psp->y < SWORD_YOFF)
    {
        psp->y = SWORD_YOFF;
        psp->yorig = psp->y;
        pSetState(psp, psp->RestState);
    }
}

//
// LEFT SWING
//

void
pSwordSlide(PANEL_SPRITEp psp)
{
    int nx, ny;
    short vel_adj;

    nx = FIXED(psp->x, psp->xfract);
    ny = FIXED(psp->y, psp->yfract);

    SpawnSwordBlur(psp);
    vel_adj = 24;

    nx += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang + 512)] >> 6;
    ny += psp->vel * synctics * (int) -sintable[psp->ang] >> 6;

    psp->xfract = LSW(nx);
    psp->x = MSW(nx);
    psp->yfract = LSW(ny);
    psp->y = MSW(ny);

    psp->vel += vel_adj * synctics;
}

void
pSwordSlideDown(PANEL_SPRITEp psp)
{
    int nx, ny;
    short vel, vel_adj;

    nx = FIXED(psp->x, psp->xfract);
    ny = FIXED(psp->y, psp->yfract);

    SpawnSwordBlur(psp);
    vel_adj = 20;
    vel = 2500;

    nx += psp->vel * synctics * (int) sintable[NORM_ANGLE(SwordAng + psp->ang + psp->PlayerP->SwordAng + 512)] >> 6;
    ny += psp->vel * synctics * (int) -sintable[NORM_ANGLE(SwordAng + psp->ang + psp->PlayerP->SwordAng)] >> 6;

    psp->xfract = LSW(nx);
    psp->x = MSW(nx);
    psp->yfract = LSW(ny);
    psp->y = MSW(ny);

    psp->vel += vel_adj * synctics;

    if (psp->x < -40)
    {
        // if still holding down the fire key - continue swinging
        if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT))
        {
            if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT))
            {
                DoPlayerChooseYell(psp->PlayerP);
                // continue to next state to swing right
                pStatePlusOne(psp);
                psp->x = SWORDR_XOFF;
                psp->y = SWORD_YOFF;
                psp->yorig = psp->y;
                psp->ang = 1024;
                psp->PlayerP->SwordAng = SwordAngTable[RANDOM_RANGE(SIZ(SwordAngTable))];
                psp->vel = vel;
                DoPlayerSpriteThrow(psp->PlayerP);
                return;
            }
        }

        // NOT still holding down the fire key - stop swinging
        pSetState(psp, psp->PresentState);
        psp->x = SWORD_XOFF;
        psp->y = SWORD_YOFF;
        psp->y += tilesiz[psp->picndx].y;
        psp->yorig = psp->y;
    }
}

//
// RIGHT SWING
//

void
pSwordSlideR(PANEL_SPRITEp psp)
{
    int nx, ny;
    short vel_adj;

    nx = FIXED(psp->x, psp->xfract);
    ny = FIXED(psp->y, psp->yfract);

    SpawnSwordBlur(psp);
    vel_adj = 24;

    nx += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang + 1024 + 512)] >> 6;
    ny += psp->vel * synctics * (int) -sintable[NORM_ANGLE(psp->ang + 1024)] >> 6;

    psp->xfract = LSW(nx);
    psp->x = MSW(nx);
    psp->yfract = LSW(ny);
    psp->y = MSW(ny);

    psp->vel += vel_adj * synctics;
}

void
pSwordSlideDownR(PANEL_SPRITEp psp)
{
    int nx, ny;
    short vel, vel_adj;

    nx = FIXED(psp->x, psp->xfract);
    ny = FIXED(psp->y, psp->yfract);

    SpawnSwordBlur(psp);
    vel_adj = 24;
    vel = 2500;

    nx += psp->vel * synctics * (int) sintable[NORM_ANGLE(SwordAng + psp->ang - psp->PlayerP->SwordAng + 1024 + 512)] >> 6;
    ny += psp->vel * synctics * (int) -sintable[NORM_ANGLE(SwordAng + psp->ang - psp->PlayerP->SwordAng + 1024)] >> 6;

    psp->xfract = LSW(nx);
    psp->x = MSW(nx);
    psp->yfract = LSW(ny);
    psp->y = MSW(ny);

    psp->vel += vel_adj * synctics;

    if (psp->x > 350)
    {
        // if still holding down the fire key - continue swinging
        if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT))
        {
            if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT))
            {
                DoPlayerChooseYell(psp->PlayerP);
                // back to action state
                pStatePlusOne(psp);
                psp->x = SWORD_XOFF + 80;
                psp->y = SWORD_YOFF;
                psp->yorig = psp->y;
                psp->PlayerP->SwordAng = SwordAngTable[RANDOM_RANGE(SIZ(SwordAngTable))];
                psp->ang = 1024;
                psp->vel = vel;
                DoPlayerSpriteThrow(psp->PlayerP);
                return;
            }
        }

        // NOT still holding down the fire key - stop swinging
        pSetState(psp, psp->PresentState);
        psp->x = SWORD_XOFF;
        psp->y = SWORD_YOFF;
        psp->y += tilesiz[psp->picndx].y;
        psp->yorig = psp->y;
    }
}

void
pSwordBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = SWORD_SWAY_AMT;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 3;
}

void
pSwordHide(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= SWORD_YOFF + tilesiz[picnum].y)
    {
        psp->y = SWORD_YOFF + tilesiz[picnum].y;
        psp->x = SWORD_XOFF;

        pWeaponUnHideKeys(psp, psp->PresentState);
    }
}

void
pSwordRest(PANEL_SPRITEp psp)
{
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

#if 0
    if (KEY_PRESSED(KEYSC_SEMI))
    {
        KEY_PRESSED(KEYSC_SEMI) = 0;
        SwordAng -= 4;
        //DSPRINTF(ds,"SwordAng %d", SwordAng);
        MONO_PRINT(ds);
    }

    if (KEY_PRESSED(KEYSC_QUOTE))
    {
        KEY_PRESSED(KEYSC_QUOTE) = 0;
        SwordAng += 4;
        //DSPRINTF(ds,"SwordAng %d", SwordAng);
        MONO_PRINT(ds);
    }
#endif

    if (pWeaponHideKeys(psp, ps_SwordHide))
        return;

    psp->yorig += synctics;

    if (psp->yorig > SWORD_YOFF)
    {
        psp->yorig = SWORD_YOFF;
    }

    psp->y = psp->yorig;

    pSwordBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

            pSetState(psp, psp->ActionState);

            psp->vel = 2500;

            psp->ang = 1024;
            psp->PlayerP->SwordAng = SwordAngTable[RANDOM_RANGE(SIZ(SwordAngTable))];
            DoPlayerSpriteThrow(psp->PlayerP);
        }
    }
}

void
pSwordAction(PANEL_SPRITEp psp)
{
    pSwordBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));
}


void
pSwordAttack(PANEL_SPRITEp psp)
{
    int InitSwordAttack(PLAYERp pp);

    InitSwordAttack(psp->PlayerP);
}

void
pSwordRetract(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= SWORD_YOFF + tilesiz[picnum].y)
    {
        RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
        psp->PlayerP->Wpn[WPN_SWORD] = NULL;
        pKillSprite(psp);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// STAR
//
//////////////////////////////////////////////////////////////////////////////////////////

void pStarPresent(PANEL_SPRITEp psp);
void pStarRetract(PANEL_SPRITEp psp);
void pStarAction(PANEL_SPRITEp psp);
void pStarRest(PANEL_SPRITEp psp);
void pStarThrow(PANEL_SPRITEp psp);

void pStarGrab(PANEL_SPRITEp psp);
void pStarDone(PANEL_SPRITEp psp);
void pStarFollowThru(PANEL_SPRITEp psp);
void pStarFollowThru2(PANEL_SPRITEp psp);
void pStarStartThrow(PANEL_SPRITEp psp);

void pStarFollowUp(PANEL_SPRITEp psp);
void pStarFollowDown(PANEL_SPRITEp psp);

void pStarHide(PANEL_SPRITEp psp);
void pStarRestTest(PANEL_SPRITEp psp);

extern PANEL_STATE ps_StarThrow[];

#define PRESENT_STAR_RATE 5

PANEL_STATE ps_PresentStar[] =
{
    {ID_StarPresent0, PRESENT_STAR_RATE, pStarPresent, &ps_PresentStar[0], 0,0,0}
};

PANEL_STATE ps_StarHide[] =
{
    {ID_StarPresent0, PRESENT_STAR_RATE, pStarHide, &ps_StarHide[0], 0,0,0}
};

#define Star_RATE 2  // was 5

PANEL_STATE ps_StarRest[] =
{
    {ID_StarPresent0, Star_RATE,        pStarRest,          &ps_StarRest[0], 0,0,0},
};

PANEL_STATE ps_ThrowStar[] =
{
    {ID_StarDown0, Star_RATE+3,          pNullAnimator,      &ps_ThrowStar[1], 0,0,0},
    {ID_StarDown1, Star_RATE+3,          pNullAnimator,      &ps_ThrowStar[2], 0,0,0},
    {ID_StarDown1, Star_RATE*2,          pNullAnimator,      &ps_ThrowStar[3], psf_Invisible, 0,0},
    {ID_StarDown1, Star_RATE,            pNullAnimator,      &ps_ThrowStar[4], 0,0,0},
    {ID_StarDown0, Star_RATE,           pNullAnimator,      &ps_ThrowStar[5], 0,0,0},
    {ID_ThrowStar0, 1,                  pNullAnimator,       &ps_ThrowStar[6], 0,0,0},
    {ID_ThrowStar0, Star_RATE,          pStarThrow,         &ps_ThrowStar[7], psf_QuickCall, 0,0},
    {ID_ThrowStar0, Star_RATE,          pNullAnimator,      &ps_ThrowStar[8], 0,0,0},
    {ID_ThrowStar1, Star_RATE,          pNullAnimator,      &ps_ThrowStar[9], 0,0,0},
    {ID_ThrowStar2, Star_RATE*2,          pNullAnimator,      &ps_ThrowStar[10], 0,0,0},
    {ID_ThrowStar3, Star_RATE*2,          pNullAnimator,      &ps_ThrowStar[11], 0,0,0},
    {ID_ThrowStar4, Star_RATE*2,          pNullAnimator,      &ps_ThrowStar[12], 0,0,0},
    // start up
    {ID_StarDown1, Star_RATE+3,         pNullAnimator,         &ps_ThrowStar[13], 0,0,0},
    {ID_StarDown0, Star_RATE+3,         pNullAnimator,         &ps_ThrowStar[14], 0,0,0},
    {ID_StarPresent0, Star_RATE+3,         pNullAnimator,         &ps_ThrowStar[15], 0,0,0},
    // maybe to directly to rest state
    {ID_StarDown0, 3,                    pStarRestTest,      &ps_ThrowStar[16], psf_QuickCall, 0,0},
    // if holding the fire key we get to here
    {ID_ThrowStar4, 3,                    pNullAnimator,      &ps_ThrowStar[5], 0,0,0},
};

PANEL_STATE ps_RetractStar[] =
{
    {ID_StarPresent0, PRESENT_STAR_RATE, pStarRetract, &ps_RetractStar[0], 0,0,0}
};

//
// Right hand star routines
//

//#define STAR_YOFF 220
//#define STAR_XOFF (160+25)
#define STAR_YOFF 208
#define STAR_XOFF (160+80)

void
pStarRestTest(PANEL_SPRITEp psp)
{
    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT))
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT))
        {
            if (!WeaponOK(psp->PlayerP))
                return;

            // continue to next state to swing right
            DoPlayerChooseYell(psp->PlayerP);
            pStatePlusOne(psp);
            return;
        }
    }

    pSetState(psp, psp->RestState);
}

void
InitWeaponStar(PLAYERp pp)
{
    PANEL_SPRITEp psp = NULL;

    if (Prediction)
        return;

    if (!TEST(pp->WpnFlags, BIT(WPN_STAR)) ||
        pp->WpnAmmo[WPN_STAR] < 3 ||
        TEST(pp->Flags, PF_WEAPON_RETRACT))
    {
        //pp->WpnFirstType = WPN_SWORD;
        //InitWeaponSword(pp);
        return;
    }

    // needed for death sequence when the STAR was your weapon when you died
    if (pp->Wpn[WPN_STAR] && TEST(pp->Wpn[WPN_STAR]->flags, PANF_DEATH_HIDE))
    {
        RESET(pp->Wpn[WPN_STAR]->flags, PANF_DEATH_HIDE);
        RESET(pp->Flags, PF_WEAPON_RETRACT);
        pSetState(pp->CurWpn, pp->CurWpn->PresentState);
        return;
    }

    if (!pp->Wpn[WPN_STAR])
    {
        psp = pp->Wpn[WPN_STAR] = pSpawnSprite(pp, ps_PresentStar, PRI_MID, STAR_XOFF, STAR_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    if (pp->CurWpn == pp->Wpn[WPN_STAR])
    {
        return;
    }

    PlayerUpdateWeapon(pp, WPN_STAR);

    pp->WpnUziType = 2; // Make uzi's go away!
    RetractCurWpn(pp);

    // Set up the new Weapon variables
    pp->CurWpn = pp->Wpn[WPN_STAR];
    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = ps_ThrowStar;
    //psp->ActionState = &ps_ThrowStar[1];
    psp->RetractState = ps_RetractStar;
    psp->PresentState = ps_PresentStar;
    psp->RestState = ps_StarRest;
    //psp->RestState = ps_ThrowStar;
    pSetState(psp, psp->PresentState);

    PlaySound(DIGI_PULL, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan);
    if (STD_RANDOM_RANGE(1000) > 900 && pp == Player+myconnectindex)
    {
        if (!gs.Darts)
            PlayerSound(DIGI_ILIKESHURIKEN,&pp->posx,&pp->posy,&pp->posz,v3df_follow|v3df_dontpan,pp);
    }

    FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
    FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);
}

void
pStarPresent(PANEL_SPRITEp psp)
{
    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    psp->y -= 3 * synctics;

    if (psp->y < STAR_YOFF)
    {
        psp->y = STAR_YOFF;
    }

    if (psp->y <= STAR_YOFF)
    {
        pSetState(psp, psp->RestState);
    }
}

void
pStarBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = 10;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 3;
}

void
pLStarBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = 6;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 4;
}

void
pStarHide(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= STAR_YOFF + tilesiz[picnum].y)
    {
        psp->y = STAR_YOFF + tilesiz[picnum].y;

        pWeaponUnHideKeys(psp, psp->PresentState);
    }
}

void
pStarRest(PANEL_SPRITEp psp)
{
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (pWeaponHideKeys(psp, ps_StarHide))
        return;

    pStarBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

            if (!WeaponOK(psp->PlayerP))
                return;

//            //DSPRINTF(ds,"StarFire");
//            MONO_PRINT(ds);

            DoPlayerChooseYell(psp->PlayerP);

            pSetState(psp, psp->ActionState);
            DoPlayerSpriteThrow(psp->PlayerP);
        }
    }
    else
        WeaponOK(psp->PlayerP);
}


void
pStarAction(PANEL_SPRITEp psp)
{
    pStarBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));
}


void
pStarThrow(PANEL_SPRITEp psp)
{
    InitStar(psp->PlayerP);
}

void
pStarRetract(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= STAR_YOFF + tilesiz[picnum].y)
    {
        RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);

        // kill only in its own routine
        psp->PlayerP->Wpn[WPN_STAR] = NULL;
        pKillSprite(psp);
    }
}


//////////////////////////////////////////////////////////////////////////////////////////
//
// UZI
//
//////////////////////////////////////////////////////////////////////////////////////////

void pUziPresent(PANEL_SPRITEp);
void pUziFire(PANEL_SPRITEp);
void pUziRetract(PANEL_SPRITEp);
void pUziAction(PANEL_SPRITEp);
void pUziRest(PANEL_SPRITEp);
void pUziHide(PANEL_SPRITEp);
void pUziPresentReload(PANEL_SPRITEp);

void pSpawnUziClip(PANEL_SPRITEp);
void pSpawnUziReload(PANEL_SPRITEp);
void pUziReload(PANEL_SPRITEp);
void pUziReloadRetract(PANEL_SPRITEp);
void pUziClip(PANEL_SPRITEp);
void pUziDoneReload(PANEL_SPRITEp);

void pUziEjectDown(PANEL_SPRITEp);
void pUziEjectUp(PANEL_SPRITEp);

// CTW MODIFICATION
//void SetVisNorm(void);
int SetVisNorm(void);
// CTW MODIFICATION END
pANIMATOR pSetVisNorm;

// Right Uzi
PANEL_STATE ps_FireUzi[] =
{
    {ID_UziPresent0, 3, pUziRest, &ps_FireUzi[0], 0,0,0},
    {ID_UziFire0, 1, pUziAction, &ps_FireUzi[2], psf_ShadeHalf, 0,0},

    {ID_UziFire1, 0, pUziFire, &ps_FireUzi[3], psf_ShadeNone|psf_QuickCall, 0,0},
    {ID_UziFire1, 4, pUziAction, &ps_FireUzi[4], psf_ShadeNone, 0,0},
    {ID_UziFire1, 0, pSetVisNorm, &ps_FireUzi[5], psf_ShadeNone|psf_QuickCall, 0,0},
    {ID_UziFire1, 4, pUziAction, &ps_FireUzi[6], psf_ShadeNone, 0,0},
    {ID_UziFire1, 0, pUziFire, &ps_FireUzi[7], psf_ShadeNone|psf_QuickCall, 0,0},
    {ID_UziFire1, 4, pUziAction, &ps_FireUzi[8], psf_ShadeNone, 0,0},
    {ID_UziFire1, 0, pSetVisNorm, &ps_FireUzi[9], psf_ShadeNone, 0,0},

    {ID_UziFire0, 4, pUziAction, &ps_FireUzi[10], psf_ShadeHalf, 0,0},
    {ID_UziFire0, 0, pUziFire, &ps_FireUzi[11], psf_QuickCall, 0,0},
    {ID_UziFire0, 4, pUziAction, &ps_FireUzi[12], psf_ShadeHalf, 0,0},
    {ID_UziFire0, 4, pUziAction, &ps_FireUzi[13], psf_ShadeHalf, 0,0},

    {ID_UziFire1, 5, pUziRest, &ps_FireUzi[0], psf_ShadeNone|psf_QuickCall, 0,0},
};

#define PRESENT_UZI_RATE 6
#define RELOAD_UZI_RATE 1

PANEL_STATE ps_UziNull[] =
{
    {ID_UziPresent0, PRESENT_UZI_RATE, pNullAnimator, &ps_UziNull[0], 0,0,0}
};

PANEL_STATE ps_UziHide[] =
{
    {ID_UziPresent0, PRESENT_UZI_RATE, pUziHide, &ps_UziHide[0], 0,0,0}
};

PANEL_STATE ps_PresentUzi[] =
{
    {ID_UziPresent0, PRESENT_UZI_RATE, pUziPresent, &ps_PresentUzi[0], 0,0,0},
};

// present of secondary uzi for reload needs to be faster
PANEL_STATE ps_PresentUziReload[] =
{
    {ID_UziPresent0, RELOAD_UZI_RATE, pUziPresentReload, &ps_PresentUziReload[0], 0,0,0},
};

PANEL_STATE ps_RetractUzi[] =
{
    {ID_UziPresent0, PRESENT_UZI_RATE, pUziRetract, &ps_RetractUzi[0], 0,0,0},
};

// Left Uzi

PANEL_STATE ps_FireUzi2[] =
{
    {ID_Uzi2Present0, 3, pUziRest, &ps_FireUzi2[0], psf_Xflip, 0,0},
    {ID_Uzi2Fire0, 1, pUziAction, &ps_FireUzi2[2], psf_ShadeHalf|psf_Xflip, 0,0},

    {ID_Uzi2Fire1, 0, pUziFire, &ps_FireUzi2[3], psf_ShadeNone|psf_QuickCall|psf_Xflip, 0,0},
    {ID_Uzi2Fire1, 4, pUziAction, &ps_FireUzi2[4], psf_ShadeNone|psf_Xflip, 0,0},
    {ID_Uzi2Fire1, 4, pUziAction, &ps_FireUzi2[5], psf_ShadeNone|psf_Xflip, 0,0},
    {ID_Uzi2Fire1, 0, pUziFire, &ps_FireUzi2[6], psf_ShadeNone|psf_QuickCall|psf_Xflip, 0,0},
    {ID_Uzi2Fire1, 4, pUziAction, &ps_FireUzi2[7], psf_ShadeNone|psf_Xflip, 0,0},

    {ID_Uzi2Fire0, 4, pUziAction, &ps_FireUzi2[8], psf_ShadeHalf|psf_Xflip, 0,0},
    {ID_Uzi2Fire0, 0, pUziFire, &ps_FireUzi2[9], psf_ShadeHalf|psf_QuickCall|psf_Xflip, 0,0},
    {ID_Uzi2Fire0, 4, pUziAction, &ps_FireUzi2[10], psf_ShadeHalf|psf_Xflip, 0,0},
    {ID_Uzi2Fire0, 4, pUziAction, &ps_FireUzi2[11], psf_ShadeHalf|psf_Xflip, 0,0},

    {ID_Uzi2Fire1, 5, pUziRest, &ps_FireUzi2[0], psf_QuickCall, 0,0},
};


PANEL_STATE ps_PresentUzi2[] =
{
    {ID_Uzi2Present0, PRESENT_UZI_RATE, pUziPresent, &ps_PresentUzi2[0], psf_Xflip, 0,0},
};

PANEL_STATE ps_Uzi2Hide[] =
{
    {ID_Uzi2Present0, PRESENT_UZI_RATE, pUziHide, &ps_Uzi2Hide[0], psf_Xflip, 0,0},
};

PANEL_STATE ps_RetractUzi2[] =
{
    {ID_Uzi2Present0, PRESENT_UZI_RATE, pUziRetract, &ps_RetractUzi2[0], psf_Xflip, 0,0},
};

PANEL_STATE ps_Uzi2Suicide[] =
{
    {ID_Uzi2Present0, PRESENT_UZI_RATE, pSuicide, &ps_Uzi2Suicide[0], psf_Xflip, 0,0}
};

PANEL_STATE ps_Uzi2Null[] =
{
    {ID_Uzi2Present0, PRESENT_UZI_RATE, pNullAnimator, &ps_Uzi2Null[0], psf_Xflip, 0,0}
};

PANEL_STATE ps_UziEject[] =
{
    {ID_UziPresent0, 1, pNullAnimator, &ps_UziEject[1], 0,0,0},
    {ID_UziPresent0, RELOAD_UZI_RATE, pUziEjectDown, &ps_UziEject[1], 0,0,0},
    {ID_UziEject0, RELOAD_UZI_RATE, pUziEjectUp, &ps_UziEject[2], 0,0,0},
    {ID_UziEject0, 1, pNullAnimator, &ps_UziEject[4], 0,0,0},
    {ID_UziEject0, RELOAD_UZI_RATE, pSpawnUziClip, &ps_UziEject[5], psf_QuickCall, 0,0},
    {ID_UziEject0, RELOAD_UZI_RATE, pNullAnimator, &ps_UziEject[5], 0,0,0},
};

PANEL_STATE ps_UziClip[] =
{
    {ID_UziClip0, RELOAD_UZI_RATE, pUziClip, &ps_UziClip[0], 0,0,0}
};

PANEL_STATE ps_UziReload[] =
{
    {ID_UziReload0, RELOAD_UZI_RATE, pUziReload, &ps_UziReload[0], 0,0,0},
    {ID_UziReload0, RELOAD_UZI_RATE, pUziReloadRetract, &ps_UziReload[1], 0,0,0}
};

PANEL_STATE ps_UziDoneReload[] =
{
    {ID_UziEject0, RELOAD_UZI_RATE, pUziDoneReload, &ps_UziDoneReload[0], 0,0,0}
};

#define CHAMBER_REST 0
#define CHAMBER_FIRE 1
#define CHAMBER_RELOAD 2
void
pUziOverlays(PANEL_SPRITEp psp, short mode)
{
#define UZI_CHAMBER_XOFF 32
#define UZI_CHAMBER_YOFF -73

#define UZI_CHAMBERRELOAD_XOFF 14
#define UZI_CHAMBERRELOAD_YOFF -100

    if (!TEST(psp->flags, PANF_SECONDARY)) return;

    if (psp->over[0].xoff == -1)
    {
        psp->over[0].xoff = UZI_CHAMBER_XOFF;
        psp->over[0].yoff = UZI_CHAMBER_YOFF;
    }

    switch (mode)
    {
    case 0: // At rest
        psp->over[0].pic = UZI_COPEN;
        break;
    case 1: // Firing
        psp->over[0].pic = UZI_CLIT;
        break;
    case 2: // Reloading
        psp->over[0].pic = UZI_CRELOAD;
        psp->over[0].xoff = UZI_CHAMBERRELOAD_XOFF;
        psp->over[0].yoff = UZI_CHAMBERRELOAD_YOFF;
        break;
    }
}

#define UZI_CLIP_XOFF 16
#define UZI_CLIP_YOFF (-84)

//#define UZI_XOFF (80)
#define UZI_XOFF (100)
#define UZI_YOFF 208

#define UZI_RELOAD_YOFF 200


//
// Uzi Reload
//

void
pUziEjectDown(PANEL_SPRITEp gun)
{
    gun->y += 5 * synctics;

    if (gun->y > 260)
    {
        gun->y = 260;
        pStatePlusOne(gun);
    }
}

void
pUziEjectUp(PANEL_SPRITEp gun)
{

    pUziOverlays(gun, CHAMBER_RELOAD);

    gun->y -= 5 * synctics;

    if (gun->y < UZI_RELOAD_YOFF)
    {
        gun->y = UZI_RELOAD_YOFF;
        pStatePlusOne(gun);
    }
}


void
pSpawnUziClip(PANEL_SPRITEp gun)
{
    PANEL_SPRITEp New;

    PlaySound(DIGI_REMOVECLIP, &gun->PlayerP->posx, &gun->PlayerP->posy,
              &gun->PlayerP->posz,v3df_follow|v3df_dontpan|v3df_doppler|v3df_follow);

    if (TEST(gun->flags, PANF_XFLIP))
    {
        New = pSpawnSprite(gun->PlayerP, ps_UziClip, PRI_BACK, gun->x - UZI_CLIP_XOFF, gun->y + UZI_CLIP_YOFF);
        SET(New->flags, PANF_XFLIP);
        New->ang = NORM_ANGLE(1024 + 256 + 22);
        New->ang = NORM_ANGLE(New->ang + 512);
    }
    else
    {
        New = pSpawnSprite(gun->PlayerP, ps_UziClip, PRI_BACK, gun->x + UZI_CLIP_XOFF, gun->y + UZI_CLIP_YOFF);
        New->ang = NORM_ANGLE(1024 + 256 - 22);
    }

    New->vel = 1050;
    SET(New->flags, PANF_WEAPON_SPRITE);


    // carry Eject sprite with clip
    New->sibling = gun;
}

void
pSpawnUziReload(PANEL_SPRITEp oclip)
{
    PANEL_SPRITEp nclip;

    nclip = pSpawnSprite(oclip->PlayerP, ps_UziReload, PRI_BACK, oclip->x, UZI_RELOAD_YOFF);
    SET(nclip->flags, PANF_WEAPON_SPRITE);

    if (TEST(oclip->flags, PANF_XFLIP))
        SET(nclip->flags, PANF_XFLIP);

    // move Reload in oposite direction of clip
    nclip->ang = NORM_ANGLE(oclip->ang + 1024);
    nclip->vel = 900;

    // move gun sprite from clip to reload
    nclip->sibling = oclip->sibling;
}

void
pUziReload(PANEL_SPRITEp nclip)
{
    int nx, ny;

    int x = FIXED(nclip->x, nclip->xfract);
    int y = FIXED(nclip->y, nclip->yfract);

    PANEL_SPRITEp gun = nclip->sibling;
    int xgun = FIXED(gun->x, gun->xfract);
    int ygun = FIXED(gun->y, gun->yfract);

    nx = nclip->vel * synctics * (int) sintable[NORM_ANGLE(nclip->ang + 512)] >> 6;
    ny = nclip->vel * synctics * (int) -sintable[nclip->ang] >> 6;

    nclip->vel += 14 * synctics;

    x += nx;
    y += ny;

    nclip->xfract = LSW(x);
    nclip->x = MSW(x);
    nclip->yfract = LSW(y);
    nclip->y = MSW(y);

    nx = gun->vel * synctics * (int) sintable[NORM_ANGLE(gun->ang + 512)] >> 6;
    ny = gun->vel * synctics * (int) -sintable[gun->ang] >> 6;

    xgun -= nx;
    ygun -= ny;

    gun->xfract = LSW(xgun);
    gun->x = MSW(xgun);
    gun->yfract = LSW(ygun);
    gun->y = MSW(ygun);

    if (TEST(nclip->flags, PANF_XFLIP))
    {
        if (nclip->x < gun->x)
        {
            PlaySound(DIGI_REPLACECLIP, &nclip->PlayerP->posx, &nclip->PlayerP->posy,
                      &nclip->PlayerP->posz,v3df_follow|v3df_dontpan|v3df_doppler);

            nclip->x = gun->x - UZI_CLIP_XOFF;
            nclip->y = gun->y + UZI_CLIP_YOFF;
            nclip->vel = 680;
            nclip->ang = NORM_ANGLE(nclip->ang - 128 - 64);
            // go to retract phase
            pSetState(nclip, &ps_UziReload[1]);
        }
    }
    else
    {
        if (nclip->x > gun->x)
        {
            PlaySound(DIGI_REPLACECLIP, &nclip->PlayerP->posx, &nclip->PlayerP->posy,
                      &nclip->PlayerP->posz,v3df_follow|v3df_dontpan|v3df_doppler);

            nclip->x = gun->x + UZI_CLIP_XOFF;
            nclip->y = gun->y + UZI_CLIP_YOFF;
            nclip->vel = 680;
            nclip->ang = NORM_ANGLE(nclip->ang + 128 + 64);
            // go to retract phase
            pSetState(nclip, &ps_UziReload[1]);
        }
    }
}

void
pUziReloadRetract(PANEL_SPRITEp nclip)
{
    int nx, ny;

    int x = FIXED(nclip->x, nclip->xfract);
    int y = FIXED(nclip->y, nclip->yfract);

    PANEL_SPRITEp gun = nclip->sibling;
    int xgun = FIXED(gun->x, gun->xfract);
    int ygun = FIXED(gun->y, gun->yfract);

    nx = nclip->vel * synctics * (int) sintable[NORM_ANGLE(nclip->ang + 512)] >> 6;
    ny = nclip->vel * synctics * (int) -sintable[nclip->ang] >> 6;

    nclip->vel += 18 * synctics;

    x -= nx;
    y -= ny;

    nclip->xfract = LSW(x);
    nclip->x = MSW(x);
    nclip->yfract = LSW(y);
    nclip->y = MSW(y);

    xgun -= nx;
    ygun -= ny;

    gun->xfract = LSW(xgun);
    gun->x = MSW(xgun);
    gun->yfract = LSW(ygun);
    gun->y = MSW(ygun);

    if (gun->y > UZI_RELOAD_YOFF + tilesiz[gun->picndx].y)
    {
        pSetState(gun, ps_UziDoneReload);
        pKillSprite(nclip);
    }
}

void
pUziDoneReload(PANEL_SPRITEp psp)
{
    PANEL_SPRITEp InitWeaponUziSecondaryReload(PANEL_SPRITEp);
    PLAYERp pp = psp->PlayerP;


    if (TEST(psp->flags, PANF_PRIMARY) && pp->WpnUziType == 3)
    {
        // if 2 uzi's and the first one has been reloaded
        // kill the first one and make the second one the CurWeapon
        // Set uzi's back to previous state
        PANEL_SPRITEp New;


        if (pp->WpnUziType > 2)
            pp->WpnUziType -= 3;

        New = InitWeaponUziSecondaryReload(psp);
        pp->Wpn[WPN_UZI] = New;
        pp->CurWpn = New;
        pp->CurWpn->sibling = NULL;

        pKillSprite(psp);
        return;
    }
    else
    {
        // Reset everything

        // Set uzi's back to previous state
        if (pp->WpnUziType > 2)
            pp->WpnUziType -= 3;

        // reset uzi variable
        pp->Wpn[WPN_UZI] = NULL;
        pp->CurWpn = NULL;

        // kill uzi eject sequence for good
        pKillSprite(psp);

        // give the uzi back
        InitWeaponUzi(pp);
    }
}

void
pUziClip(PANEL_SPRITEp oclip)
{
    int nx, ny, ox, oy;
    int x = FIXED(oclip->x, oclip->xfract);
    int y = FIXED(oclip->y, oclip->yfract);

    ox = x;
    oy = y;

    nx = oclip->vel * synctics * (int) sintable[NORM_ANGLE(oclip->ang + 512)] >> 6;
    ny = oclip->vel * synctics * (int) -sintable[oclip->ang] >> 6;

    oclip->vel += 16 * synctics;

    x += nx;
    y += ny;

    oclip->xfract = LSW(x);
    oclip->x = MSW(x);
    oclip->yfract = LSW(y);
    oclip->y = MSW(y);

    if (oclip->y > UZI_RELOAD_YOFF)
    {
        PANEL_SPRITEp gun = oclip->sibling;

        // as synctics gets bigger, oclip->x can be way off
        // when clip goes off the screen - recalc oclip->x from scratch
        // so it will end up the same for all synctic values
        for (x = ox, y = oy; oclip->y < UZI_RELOAD_YOFF; )
        {
            x += oclip->vel * (int) sintable[NORM_ANGLE(oclip->ang + 512)] >> 6;
            y += oclip->vel * (int) -sintable[oclip->ang] >> 6;
        }

        oclip->xfract = LSW(x);
        oclip->x = MSW(x);
        oclip->yfract = LSW(y);
        oclip->y = MSW(y);

        oclip->y = UZI_RELOAD_YOFF;

        gun->vel = 800;
        gun->ang = NORM_ANGLE(oclip->ang + 1024);


        pSpawnUziReload(oclip);
        pKillSprite(oclip);
    }
}

//
// Uzi Basic Stuff
//

void
InitWeaponUzi(PLAYERp pp)
{
    PANEL_SPRITEp InitWeaponUzi2(PANEL_SPRITEp);
    PANEL_SPRITEp psp = NULL;

    if (Prediction)
        return;

    pp->WeaponType = WPN_UZI;

    // make sure you have the uzi, uzi ammo, and not retracting another
    // weapon
    if (!TEST(pp->WpnFlags, BIT(WPN_UZI)) ||
//        pp->WpnAmmo[WPN_UZI] <= 0 ||
        TEST(pp->Flags, PF_WEAPON_RETRACT))
        return;

    // if players uzi is null
    if (!pp->Wpn[WPN_UZI])
    {
        psp = pp->Wpn[WPN_UZI] = pSpawnSprite(pp, ps_PresentUzi, PRI_MID, 160 + UZI_XOFF, UZI_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    // if Current weapon is uzi
    if (pp->CurWpn == pp->Wpn[WPN_UZI])
    {
        // Retracting other uzi?
        if (pp->CurWpn->sibling && pp->WpnUziType == 1)
        {
            RetractCurWpn(pp);
        }
        else
        // Is player toggling between one and two uzi's?
        if (pp->CurWpn->sibling && TEST(pp->Wpn[WPN_UZI]->flags, PANF_PRIMARY) && pp->WpnUziType == 0)
        {
            if (!TEST(pp->CurWpn->flags, PANF_RELOAD))
                InitWeaponUzi2(pp->Wpn[WPN_UZI]);
        }

        // if actually picked an uzi up and don't currently have double uzi
        if (TEST(pp->Flags, PF_PICKED_UP_AN_UZI) && !TEST(pp->Wpn[WPN_UZI]->flags, PANF_PRIMARY))
        {
            RESET(pp->Flags, PF_PICKED_UP_AN_UZI);

            if (!TEST(pp->CurWpn->flags, PANF_RELOAD))
                InitWeaponUzi2(pp->Wpn[WPN_UZI]);
        }
        return;
    }
    else
    {
        RESET(pp->Flags, PF_PICKED_UP_AN_UZI);
    }

    PlayerUpdateWeapon(pp, WPN_UZI);

    RetractCurWpn(pp);

    // Set up the new Weapon variables
    pp->CurWpn = pp->Wpn[WPN_UZI];
    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = &ps_FireUzi[1];
    psp->RetractState = ps_RetractUzi;
    psp->PresentState = ps_PresentUzi;
    psp->RestState = ps_FireUzi;
    pSetState(psp, psp->PresentState);

    // power up
    // NOTE: PRIMARY is ONLY set when there is a powerup
    if (TEST(pp->Flags, PF_TWO_UZI))
    {
        InitWeaponUzi2(psp);
    }

    PlaySound(DIGI_UZI_UP, &pp->posx, &pp->posy, &pp->posz, v3df_follow);

    FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
    FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);
}

PANEL_SPRITEp
InitWeaponUzi2(PANEL_SPRITEp uzi_orig)
{
    PANEL_SPRITEp New;
    PLAYERp pp = uzi_orig->PlayerP;


    // There is already a second uzi, or it's retracting
    if (pp->WpnUziType == 1 || pp->CurWpn->sibling || TEST(pp->Flags, PF_WEAPON_RETRACT)) return NULL;

    // NOTE: PRIMARY is ONLY set when there is a powerup
    SET(uzi_orig->flags, PANF_PRIMARY);

    // Spawning a 2nd uzi, set weapon mode
    pp->WpnUziType = 0; // 0 is up, 1 is retract

    New = pSpawnSprite(pp, ps_PresentUzi2, PRI_MID, 160 - UZI_XOFF, UZI_YOFF);
    uzi_orig->sibling = New;

    New->y += tilesiz[New->picndx].y;

    // Set up the New Weapon variables
    SET(New->flags, PANF_WEAPON_SPRITE);
    New->ActionState = &ps_FireUzi2[1];
    New->RetractState = ps_RetractUzi2;
    New->PresentState = ps_PresentUzi2;
    New->RestState = ps_FireUzi2;
    pSetState(New, New->PresentState);

    New->sibling = uzi_orig;
    SET(New->flags, PANF_SECONDARY);
    pUziOverlays(New, CHAMBER_REST);

    return New;
}

PANEL_SPRITEp
InitWeaponUziSecondaryReload(PANEL_SPRITEp uzi_orig)
{
    PANEL_SPRITEp New;
    PLAYERp pp = uzi_orig->PlayerP;

    New = pSpawnSprite(pp, ps_PresentUzi, PRI_MID, 160 - UZI_XOFF, UZI_YOFF);
    New->y += tilesiz[New->picndx].y;

    SET(New->flags, PANF_XFLIP);

    // Set up the New Weapon variables
    SET(New->flags, PANF_WEAPON_SPRITE);
    New->ActionState = ps_UziEject;
    New->RetractState = ps_RetractUzi;
    New->PresentState = ps_PresentUzi;
    New->RestState = ps_UziEject;
    // pSetState(New, New->PresentState);
    pSetState(New, ps_PresentUziReload);

    New->sibling = uzi_orig;
    SET(New->flags, PANF_SECONDARY|PANF_RELOAD);

    return New;
}

void
pUziPresent(PANEL_SPRITEp psp)
{
    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    psp->y -= 3 * synctics;

    if (psp->y < UZI_YOFF)
    {
        RESET(psp->flags, PANF_RELOAD);

        psp->y = UZI_YOFF;
        psp->xorig = psp->x;
        psp->yorig = psp->y;
        pSetState(psp, psp->RestState);
    }
}

// same as pUziPresent only faster for reload sequence
void
pUziPresentReload(PANEL_SPRITEp psp)
{
    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    psp->y -= 5 * synctics;

    if (psp->y < UZI_YOFF)
    {
        psp->y = UZI_YOFF;
        psp->xorig = psp->x;
        psp->yorig = psp->y;
        pSetState(psp, psp->RestState);
    }
}

void
pUziBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = 12;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 3;
}

void
pUziStartReload(PANEL_SPRITEp psp)
{
    SetVisNorm();

    // Set uzi's to reload state
    if (psp->PlayerP->WpnUziType < 3)
        psp->PlayerP->WpnUziType += 3;

    // Uzi #1 reload - starting from a full up position
    pSetState(psp, ps_UziEject);

    SET(psp->flags, PANF_RELOAD);

    if (TEST(psp->flags, PANF_PRIMARY) && psp->sibling)
    {
        // this is going to KILL Uzi #2 !!!
        pSetState(psp->sibling, psp->sibling->RetractState);
    }
}

void
pUziHide(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= 200 + tilesiz[picnum].y)
    {
        psp->y = 200 + tilesiz[picnum].y;

        if (TEST(psp->flags, PANF_PRIMARY) && psp->PlayerP->WpnUziType != 1)
        {
            if (pWeaponUnHideKeys(psp, psp->PresentState))
                pSetState(psp->sibling, psp->sibling->PresentState);
        }
        else if (!TEST(psp->flags, PANF_SECONDARY))
        {
            pWeaponUnHideKeys(psp, psp->PresentState);
        }
    }
}

void
pUziRest(PANEL_SPRITEp psp)
{
    char shooting;
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);


    // If you have two uzi's, but one didn't come up, spawn it
    if (TEST(psp->PlayerP->Flags, PF_TWO_UZI) && psp->sibling == NULL)
    {
        InitWeaponUzi2(psp);
    }

    if (TEST(psp->flags, PANF_PRIMARY) && psp->sibling)
    {
        if (pWeaponHideKeys(psp, ps_UziHide))
        {
            if (psp->sibling != NULL) // !JIM! Without this line, will ASSERT if reloading here
                pSetState(psp->sibling, ps_Uzi2Hide);
            return;
        }
    }
    else if (!TEST(psp->flags, PANF_SECONDARY))
    {
        if (pWeaponHideKeys(psp, ps_UziHide))
            return;
    }

    if (TEST(psp->flags, PANF_SECONDARY))
        pUziOverlays(psp, CHAMBER_REST);

    SetVisNorm();

    shooting = TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) && FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT);
    shooting |= force;

    pUziBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP) || shooting);

    if (shooting)
    {
        if (!WeaponOK(psp->PlayerP))
            return;

        RESET(psp->flags, PANF_UNHIDE_SHOOT);

        pSetState(psp, psp->ActionState);
    }
    else
        WeaponOK(psp->PlayerP);
}

void
pUziAction(PANEL_SPRITEp psp)
{
    char shooting;
    static SWBOOL alternate = FALSE;

    shooting = TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) && FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT);

    if (shooting)
    {
        if (TEST(psp->flags, PANF_SECONDARY))
        {
            alternate++;
            if (alternate > 6) alternate = 0;
            if (alternate <= 3)
                pUziOverlays(psp, CHAMBER_FIRE);
            else
                pUziOverlays(psp, CHAMBER_REST);
        }
        // Only Recoil if shooting
        pUziBobSetup(psp);
        UziRecoilYadj = DIV256(RANDOM_P2(1024));        // global hack for
        // Weapon Bob
        pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP) || shooting);
        UziRecoilYadj = 0;              // reset my global hack
        if (RANDOM_P2(1024) > 990)
            DoPlayerChooseYell(psp->PlayerP);
    }
    else
    {
        if (TEST(psp->flags, PANF_SECONDARY))
            pUziOverlays(psp, CHAMBER_REST);
        pUziBobSetup(psp);
        pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP) || shooting);
    }
}

void
pUziFire(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;

    if (!WeaponOK(psp->PlayerP))
        return;

    if (TEST(psp->flags, PANF_SECONDARY) && pp->WpnUziType > 0) return;

    InitUzi(psp->PlayerP);
    SpawnUziShell(psp);

    // If its the second Uzi, give the shell back only if it's a reload count to keep #'s even
    if (TEST(psp->flags, PANF_SECONDARY))
    {
        if (TEST(pp->Flags, PF_TWO_UZI) && psp->sibling)
        {
            if ((pp->WpnAmmo[WPN_UZI] % 100) == 0)
                pp->WpnAmmo[WPN_UZI]++;
        }
        else if ((pp->WpnAmmo[WPN_UZI] % 50) == 0)
            pp->WpnAmmo[WPN_UZI]++;
    }
    else
    {
        SpawnVis(psp->PlayerP->PlayerSprite, -1, -1, -1, -1, 32);

        if (!WeaponOK(psp->PlayerP))
            return;

        // Reload if done with clip
        if (TEST(pp->Flags, PF_TWO_UZI) && psp->sibling)
        {
            if ((pp->WpnAmmo[WPN_UZI] % 100) == 0)
                pUziStartReload(psp);
        }
        else if ((pp->WpnAmmo[WPN_UZI] % 50) == 0)
        {
            // clip has run out
            pUziStartReload(psp);
        }
    }
}

void
pUziRetract(PANEL_SPRITEp psp)
{
    // PANEL_SPRITEp sib = psp->sibling;
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= 200 + tilesiz[picnum].y)
    {
        // if in the reload phase and its retracting then get rid of uzi
        // no matter whether it is PRIMARY/SECONDARY/neither.
        if (TEST(psp->flags, PANF_RELOAD))
        {
            RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
            psp->PlayerP->Wpn[WPN_UZI] = NULL;
        }
        else
        {
            // NOT reloading here
            if (TEST(psp->flags, PANF_PRIMARY))
            {
                // only reset when primary goes off the screen
                RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
                psp->PlayerP->Wpn[WPN_UZI] = NULL;
            }
            else if (TEST(psp->flags, PANF_SECONDARY))
            {
                // primarily for beginning of reload sequence where seconary
                // is taken off of the screen.  Lets the primary know that
                // he is alone.
                if (psp->sibling && psp->sibling->sibling == psp)
                    psp->sibling->sibling = NULL;
            }
            else
            {
                // only one uzi here is retracting
                RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
                psp->PlayerP->Wpn[WPN_UZI] = NULL;
            }
        }


        pKillSprite(psp);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// UZI SHELL
//
//////////////////////////////////////////////////////////////////////////////////////////

void pUziShell(PANEL_SPRITEp);

#define UZI_SHELL_RATE 10

PANEL_STATE ps_UziShell[] =
{
    {ID_UziShell0, UZI_SHELL_RATE, pUziShell, &ps_UziShell[1], 0,0,0},
    {ID_UziShell1, UZI_SHELL_RATE, pUziShell, &ps_UziShell[2], 0,0,0},
    {ID_UziShell2, UZI_SHELL_RATE, pUziShell, &ps_UziShell[3], 0,0,0},
    {ID_UziShell3, UZI_SHELL_RATE, pUziShell, &ps_UziShell[4], 0,0,0},
    {ID_UziShell4, UZI_SHELL_RATE, pUziShell, &ps_UziShell[5], 0,0,0},
    {ID_UziShell5, UZI_SHELL_RATE, pUziShell, &ps_UziShell[0], 0,0,0},
};

PANEL_STATE ps_Uzi2Shell[] =
{
    {ID_Uzi2Shell0, UZI_SHELL_RATE, pUziShell, &ps_Uzi2Shell[1], psf_Xflip, 0,0},
    {ID_Uzi2Shell1, UZI_SHELL_RATE, pUziShell, &ps_Uzi2Shell[2], psf_Xflip, 0,0},
    {ID_Uzi2Shell2, UZI_SHELL_RATE, pUziShell, &ps_Uzi2Shell[3], psf_Xflip, 0,0},
    {ID_Uzi2Shell3, UZI_SHELL_RATE, pUziShell, &ps_Uzi2Shell[4], psf_Xflip, 0,0},
    {ID_Uzi2Shell4, UZI_SHELL_RATE, pUziShell, &ps_Uzi2Shell[5], psf_Xflip, 0,0},
    {ID_Uzi2Shell5, UZI_SHELL_RATE, pUziShell, &ps_Uzi2Shell[0], psf_Xflip, 0,0},
};

#if 0
void
SpawnUziShell(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;
    PANEL_SPRITEp shell;
    int i, rand_val;

    if (psp->State && TEST(psp->State->flags, psf_Xflip))
    {
        // LEFT side
        shell = pSpawnSprite(pp, ps_Uzi2Shell, PRI_BACK, 0, 0);
        shell->x = psp->x - 8 - RANDOM_P2(8);
        pSetState(shell, ps_Uzi2Shell + RANDOM_P2(2));
    }
    else
    {
        // RIGHT side
        shell = pSpawnSprite(pp, ps_UziShell, PRI_BACK, 0, 0);
        shell->x = psp->x + 4 + RANDOM_P2(8);
        pSetState(shell, ps_UziShell + RANDOM_P2(2));
    }

    SET(shell->flags, PANF_WEAPON_SPRITE);
    shell->y = shell->yorig = psp->y - tilesiz[psp->picndx].y + 20;

    shell->sin_ndx = 0;
    shell->sin_amt = 13 + RANDOM_P2(8);
    shell->sin_arc_speed = 3 + RANDOM_P2(2);

    rand_val = RANDOM_P2(1024);

    if (rand_val < 200)
        shell->sin_amt = 34 + RANDOM_P2(16);
    else if (rand_val < 400)
        shell->sin_amt = 8 + RANDOM_P2(8);
    else
        shell->sin_amt = 13 + RANDOM_P2(8);
}
#endif

void
SpawnUziShell(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;

    if (psp->State && TEST(psp->State->flags, psf_Xflip))
    {
        // LEFT side
        pp->UziShellLeftAlt = !pp->UziShellLeftAlt;
        if (pp->UziShellLeftAlt)
            SpawnShell(pp->PlayerSprite,-3);
    }
    else
    {
        // RIGHT side
        pp->UziShellRightAlt = !pp->UziShellRightAlt;
        if (pp->UziShellRightAlt)
            SpawnShell(pp->PlayerSprite,-2);
    }
}

void
pUziShell(PANEL_SPRITEp psp)
{
    if (psp->State && TEST(psp->State->flags, psf_Xflip))
    {
        psp->x -= 3 * synctics;
    }
    else
    {
        psp->x += 3 * synctics;
    }

    // increment the ndx into the sin table and wrap at 1024
    psp->sin_ndx = (psp->sin_ndx + (synctics << psp->sin_arc_speed) + 1024) & 1023;

    // get height
    psp->y = psp->yorig;
    psp->y += psp->sin_amt * -sintable[psp->sin_ndx] >> 14;

    // if off of the screen kill them
    if (psp->x > 330 || psp->x < -10)
    {
        pKillSprite(psp);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// SHOTGUN SHELL
//
//////////////////////////////////////////////////////////////////////////////////////////

void pShotgunShell(PANEL_SPRITEp);

#define SHOTGUN_SHELL_RATE 7

PANEL_STATE ps_ShotgunShell[] =
{
    {ID_ShotgunShell0, SHOTGUN_SHELL_RATE, pShotgunShell, &ps_ShotgunShell[1], 0,0,0},
    {ID_ShotgunShell1, SHOTGUN_SHELL_RATE, pShotgunShell, &ps_ShotgunShell[2], 0,0,0},
    {ID_ShotgunShell2, SHOTGUN_SHELL_RATE, pShotgunShell, &ps_ShotgunShell[3], 0,0,0},
    {ID_ShotgunShell3, SHOTGUN_SHELL_RATE, pShotgunShell, &ps_ShotgunShell[4], 0,0,0},
    {ID_ShotgunShell4, SHOTGUN_SHELL_RATE, pShotgunShell, &ps_ShotgunShell[5], 0,0,0},
    {ID_ShotgunShell5, SHOTGUN_SHELL_RATE, pShotgunShell, &ps_ShotgunShell[6], 0,0,0},
    {ID_ShotgunShell6, SHOTGUN_SHELL_RATE, pShotgunShell, &ps_ShotgunShell[7], 0,0,0},
    {ID_ShotgunShell6, SHOTGUN_SHELL_RATE, pShotgunShell, &ps_ShotgunShell[0], 0,0,0},
};

void
SpawnShotgunShell(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;


    SpawnShell(pp->PlayerSprite,-4);

#if 0
    typedef struct
    {
        short xoff, yoff, skip;
        int lo_jump_speed, hi_jump_speed, lo_xspeed, hi_xspeed;
        PANEL_STATEp state[2];
    } PANEL_SHRAP, *PANEL_SHRAPp;

    PANEL_SPRITEp shell;

    static PANEL_SHRAP ShellShrap[] =
    {
        {0, 0, 0, FIXED(2,0), FIXED(4,0), FIXED(3,32000), FIXED(10,32000) },
    };

    PANEL_SHRAPp ss;

    ss = &ShellShrap[0];

    shell = pSpawnSprite(pp, ps_ShotgunShell, PRI_FRONT, 0, 0);
    shell->x = psp->x + 25;
    shell->y = shell->yorig = psp->y - tilesiz[psp->picndx].y + 85;

    shell->xspeed = ss->lo_xspeed + (RANDOM_RANGE((ss->hi_xspeed - ss->lo_xspeed)>>4) << 4);
    SET(shell->flags, PANF_WEAPON_SPRITE);

    shell->jump_speed = -ss->lo_jump_speed - (RANDOM_RANGE((ss->hi_jump_speed - ss->lo_jump_speed)>>4) << 4);
    DoBeginPanelJump(shell);
#endif
}

void
pShotgunShell(PANEL_SPRITEp psp)
{
    int x = FIXED(psp->x, psp->xfract);

    if (TEST(psp->flags, PANF_JUMPING))
    {
        DoPanelJump(psp);
    }
    else if (TEST(psp->flags, PANF_FALLING))
    {
        DoPanelFall(psp);
    }

    x += psp->xspeed;

    psp->xfract = LSW(x);
    psp->x = MSW(x);

    if (psp->x > 320 || psp->x < 0 || psp->y > 200)
    {
        pKillSprite(psp);
        return;
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// SHOTGUN
//
//////////////////////////////////////////////////////////////////////////////////////////

void pShotgunPresent(PANEL_SPRITEp psp);
void pShotgunRetract(PANEL_SPRITEp psp);
void pShotgunAction(PANEL_SPRITEp psp);
void pShotgunRest(PANEL_SPRITEp psp);
void pShotgunFire(PANEL_SPRITEp psp);
void pShotgunHide(PANEL_SPRITEp psp);
void pShotgunRestTest(PANEL_SPRITEp psp);

void pShotgunReloadDown(PANEL_SPRITEp psp);
void pShotgunReloadUp(PANEL_SPRITEp psp);
void pShotgunBobSetup(PANEL_SPRITEp psp);
void pShotgunRecoilUp(PANEL_SPRITEp psp);
void pShotgunRecoilDown(PANEL_SPRITEp psp);

SWBOOL pShotgunReloadTest(PANEL_SPRITEp psp);

extern PANEL_STATE ps_ShotgunReload[];

#define Shotgun_BEAT_RATE 24
#define Shotgun_ACTION_RATE 4

PANEL_STATE ps_PresentShotgun[] =
{
    {ID_ShotgunPresent0, Shotgun_BEAT_RATE, pShotgunPresent, &ps_PresentShotgun[0], 0,0,0}
};

PANEL_STATE ps_ShotgunRest[] =
{
    {ID_ShotgunPresent0, Shotgun_BEAT_RATE, pShotgunRest, &ps_ShotgunRest[0], 0,0,0}
};

PANEL_STATE ps_ShotgunHide[] =
{
    {ID_ShotgunPresent0, Shotgun_BEAT_RATE, pShotgunHide, &ps_ShotgunHide[0], 0,0,0}
};

PANEL_STATE ps_ShotgunRecoil[] =
{
    // recoil
    {ID_ShotgunReload0, Shotgun_ACTION_RATE, pShotgunRecoilDown, &ps_ShotgunRecoil[0], 0,0,0},
    {ID_ShotgunReload0, Shotgun_ACTION_RATE, pShotgunRecoilUp, &ps_ShotgunRecoil[1], 0,0,0},
    // reload
    {ID_ShotgunReload0, Shotgun_ACTION_RATE*5,  pNullAnimator,      &ps_ShotgunRecoil[3], 0,0,0},
    {ID_ShotgunReload1, Shotgun_ACTION_RATE,    pNullAnimator,      &ps_ShotgunRecoil[4], 0,0,0},
    {ID_ShotgunReload2, Shotgun_ACTION_RATE*5,  pNullAnimator,      &ps_ShotgunRecoil[5], 0,0,0},
    {ID_ShotgunPresent0,Shotgun_ACTION_RATE,    pShotgunRestTest,   &ps_ShotgunRecoil[6], 0,0,0},
    {ID_ShotgunPresent0,Shotgun_ACTION_RATE/2,  pShotgunAction,     &ps_ShotgunRecoil[7], 0,0,0},
    {ID_ShotgunPresent0,Shotgun_ACTION_RATE/2,  pShotgunAction,     &ps_ShotgunRecoil[8], 0,0,0},
    // ready to fire again
    {ID_ShotgunPresent0, 3, pNullAnimator, &ps_ShotgunRest[0], 0,0,0}
};

PANEL_STATE ps_ShotgunRecoilAuto[] =
{
    // recoil
    {ID_ShotgunReload0, 1,    pShotgunRecoilDown, &ps_ShotgunRecoilAuto[0], 0,0,0},
    {ID_ShotgunReload0, 1,    pShotgunRecoilUp,   &ps_ShotgunRecoilAuto[1], 0,0,0},
    // Reload
    {ID_ShotgunReload0, 1,    pNullAnimator,      &ps_ShotgunRecoilAuto[3], 0,0,0},
    {ID_ShotgunReload0, 1,    pNullAnimator,      &ps_ShotgunRecoilAuto[4], 0,0,0},
    {ID_ShotgunReload0, 1,    pNullAnimator,      &ps_ShotgunRecoilAuto[5], 0,0,0},

    {ID_ShotgunPresent0,1,    pShotgunRestTest,   &ps_ShotgunRecoilAuto[6], 0,0,0},
    {ID_ShotgunPresent0,1,    pShotgunAction,     &ps_ShotgunRecoilAuto[7], 0,0,0},
    {ID_ShotgunPresent0,1,    pShotgunRest,       &ps_ShotgunRest[0],psf_QuickCall, 0,0},
};

PANEL_STATE ps_ShotgunFire[] =
{
    {ID_ShotgunFire0,   Shotgun_ACTION_RATE,    pShotgunAction,     &ps_ShotgunFire[1], psf_ShadeHalf, 0,0},
    {ID_ShotgunFire1,   Shotgun_ACTION_RATE,    pShotgunFire,       &ps_ShotgunFire[2], psf_ShadeNone|psf_QuickCall, 0,0},
    {ID_ShotgunFire1,   Shotgun_ACTION_RATE,    pShotgunAction,     &ps_ShotgunFire[3], psf_ShadeNone, 0,0},
    {ID_ShotgunReload0, 0,                      SpawnShotgunShell,  &ps_ShotgunFire[4], psf_QuickCall, 0,0},
    {ID_ShotgunReload0, Shotgun_ACTION_RATE,    pShotgunAction,     &ps_ShotgunRecoil[0], 0,0,0}
};


#if 1
PANEL_STATE ps_ShotgunAutoFire[] =
{
    {ID_ShotgunFire1,   2,    pShotgunAction,     &ps_ShotgunAutoFire[1], psf_ShadeHalf, 0,0},
    {ID_ShotgunFire1,   2,    pShotgunFire,       &ps_ShotgunAutoFire[2], psf_ShadeNone, 0,0},
    {ID_ShotgunFire1,   2,    pShotgunAction,     &ps_ShotgunAutoFire[3], psf_ShadeNone, 0,0},
    {ID_ShotgunReload0, 0,    SpawnShotgunShell,  &ps_ShotgunAutoFire[4], psf_QuickCall, 0,0},
    {ID_ShotgunReload0, 1,    pShotgunAction,     &ps_ShotgunRecoilAuto[0], 0,0,0}
};
#endif

#if 1
PANEL_STATE ps_ShotgunReload[] =
{
    {ID_ShotgunPresent0, Shotgun_BEAT_RATE, pShotgunReloadDown, &ps_ShotgunReload[0], 0,0,0},
    {ID_ShotgunPresent0, 30,            pNullAnimator, &ps_ShotgunReload[2], 0,0,0},
    // make reload sound here
    {ID_ShotgunPresent0, Shotgun_BEAT_RATE, pNullAnimator, &ps_ShotgunReload[3], psf_QuickCall, 0,0},
    {ID_ShotgunPresent0, 30,            pNullAnimator, &ps_ShotgunReload[4], 0,0,0},
    {ID_ShotgunPresent0, Shotgun_BEAT_RATE, pShotgunReloadUp, &ps_ShotgunReload[4], 0,0,0},
    {ID_ShotgunPresent0, 3, pNullAnimator, &ps_ShotgunRest[0], 0,0,0}
};
#endif

PANEL_STATE ps_RetractShotgun[] =
{
    {ID_ShotgunPresent0, Shotgun_BEAT_RATE, pShotgunRetract, &ps_RetractShotgun[0], 0,0,0}
};

#define SHOTGUN_YOFF 200
#define SHOTGUN_XOFF (160+42)

void
InitWeaponShotgun(PLAYERp pp)
{
    PANEL_SPRITEp psp = NULL;

    if (Prediction)
        return;

    pp->WeaponType = WPN_SHOTGUN;

    if (!TEST(pp->WpnFlags, BIT(pp->WeaponType)) ||
//        pp->WpnAmmo[pp->WeaponType] <= 0 ||
        TEST(pp->Flags, PF_WEAPON_RETRACT))
        return;

    if (!pp->Wpn[pp->WeaponType])
    {
        psp = pp->Wpn[pp->WeaponType] = pSpawnSprite(pp, ps_PresentShotgun, PRI_MID, SHOTGUN_XOFF, SHOTGUN_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    if (pp->CurWpn == pp->Wpn[pp->WeaponType])
    {
        return;
    }

    psp->WeaponType = pp->WeaponType;
    PlayerUpdateWeapon(pp, pp->WeaponType);

    pp->WpnUziType = 2; // Make uzi's go away!
    RetractCurWpn(pp);

    // Set up the new Weapon variables
    psp = pp->CurWpn = pp->Wpn[pp->WeaponType];
    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = ps_ShotgunFire;
    //psp->ActionState = ps_ShotgunAutoFire;
    psp->RetractState = ps_RetractShotgun;
    psp->PresentState = ps_PresentShotgun;
    psp->RestState = ps_ShotgunRest;
    pSetState(psp, psp->PresentState);

    PlaySound(DIGI_SHOTGUN_UP, &pp->posx, &pp->posy, &pp->posz, v3df_follow);

    FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
    FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);
}

void
pShotgunSetRecoil(PANEL_SPRITEp psp)
{
    psp->vel = 900;
    psp->ang = NORM_ANGLE(-256);
}

void
pShotgunRecoilDown(PANEL_SPRITEp psp)
{
    int targetvel;

    int x = FIXED(psp->x, psp->xfract);
    int y = FIXED(psp->y, psp->yfract);

    if (psp->PlayerP->WpnShotgunType == 1)
        targetvel = 890;
    else
        targetvel = 780;

    x += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang + 512)] >> 6;
    y += psp->vel * synctics * (int) -sintable[psp->ang] >> 6;

    psp->xfract = LSW(x);
    psp->x = MSW(x);
    psp->yfract = LSW(y);
    psp->y = MSW(y);

    psp->vel -= 24 * synctics;

    if (psp->vel < targetvel)
    {
        psp->vel = targetvel;
        psp->ang = NORM_ANGLE(psp->ang + 1024);

        pStatePlusOne(psp);
    }
}

void
pShotgunRecoilUp(PANEL_SPRITEp psp)
{
    int x = FIXED(psp->x, psp->xfract);
    int y = FIXED(psp->y, psp->yfract);

    x += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang + 512)] >> 6;
    y += psp->vel * synctics * (int) -sintable[psp->ang] >> 6;

    psp->xfract = LSW(x);
    psp->x = MSW(x);
    psp->yfract = LSW(y);
    psp->y = MSW(y);

    psp->vel += 15 * synctics;

    if (psp->y < SHOTGUN_YOFF)
    {
        psp->y = SHOTGUN_YOFF;
        psp->x = SHOTGUN_XOFF;

        pShotgunSetRecoil(psp);

        pStatePlusOne(psp);
        RESET(psp->flags, PANF_BOB);
    }
}

#if 1
void
pShotgunReloadDown(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= SHOTGUN_YOFF + (tilesiz[picnum].y/2))
    {
        PlaySound(DIGI_ROCKET_UP, &psp->PlayerP->posx, &psp->PlayerP->posy,
                  &psp->PlayerP->posz,v3df_follow|v3df_dontpan|v3df_doppler);

        psp->y = SHOTGUN_YOFF + (tilesiz[picnum].y/2);

        pStatePlusOne(psp);
    }
}

void
pShotgunReloadUp(PANEL_SPRITEp psp)
{
    psp->x = SHOTGUN_XOFF;

    psp->y -= 3 * synctics;

    if (psp->y < SHOTGUN_YOFF)
    {
        PlaySound(DIGI_SHOTGUN_UP, &psp->PlayerP->posx, &psp->PlayerP->posy,
                  &psp->PlayerP->posz,v3df_follow|v3df_dontpan|v3df_doppler);

        psp->y = SHOTGUN_YOFF;

        pStatePlusOne(psp);
        RESET(psp->flags, PANF_BOB);
    }
}
#endif

void
pShotgunPresent(PANEL_SPRITEp psp)
{
    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    // Needed for recoil
    psp->ang = NORM_ANGLE(256 + 128);
    pShotgunSetRecoil(psp);
    ///

    psp->y -= 3 * synctics;

    if (psp->y < SHOTGUN_YOFF)
    {
        psp->y = SHOTGUN_YOFF;
        psp->yorig = psp->y;
        pSetState(psp, psp->RestState);
    }
}

void
pShotgunBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = 12;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 3;
}

SWBOOL
pShotgunOverlays(PANEL_SPRITEp psp)
{
#define SHOTGUN_AUTO_XOFF 28
#define SHOTGUN_AUTO_YOFF -17

    if (psp->over[SHOTGUN_AUTO_NUM].xoff == -1)
    {
        psp->over[SHOTGUN_AUTO_NUM].xoff = SHOTGUN_AUTO_XOFF;
        psp->over[SHOTGUN_AUTO_NUM].yoff = SHOTGUN_AUTO_YOFF;
    }

    //if(psp->PlayerP->WpnShotgunAuto == 0 && psp->PlayerP->WpnRocketType == 1)
    //psp->PlayerP->WpnShotgunType--;

    switch (psp->PlayerP->WpnShotgunType)
    {
    case 0:
        psp->over[SHOTGUN_AUTO_NUM].pic = -1;
        SET(psp->over[SHOTGUN_AUTO_NUM].flags, psf_ShadeNone);
        return FALSE;
    case 1:
        psp->over[SHOTGUN_AUTO_NUM].pic = SHOTGUN_AUTO;
        SET(psp->over[SHOTGUN_AUTO_NUM].flags, psf_ShadeNone);
        return FALSE;
    }
    return FALSE;
}

PANEL_STATE ps_ShotgunFlash[] =
{
    {SHOTGUN_AUTO, 30, NULL, &ps_ShotgunFlash[1], 0,0,0},
    {0,            30, NULL, &ps_ShotgunFlash[2], 0,0,0},
    {SHOTGUN_AUTO, 30, NULL, &ps_ShotgunFlash[3], 0,0,0},
    {0,            30, NULL, &ps_ShotgunFlash[4], 0,0,0},
    {SHOTGUN_AUTO, 30, NULL, &ps_ShotgunFlash[5], 0,0,0},
    {0,            30, NULL, &ps_ShotgunFlash[6], 0,0,0},
    {SHOTGUN_AUTO, 30, NULL, &ps_ShotgunFlash[7], 0,0,0},
    {0,            30, NULL, &ps_ShotgunFlash[8], 0,0,0},
    {SHOTGUN_AUTO, 30, NULL, &ps_ShotgunFlash[9], 0,0,0},
    {0,             0, NULL, NULL, 0,0,0}
};


void
pShotgunHide(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= SHOTGUN_YOFF + tilesiz[picnum].y)
    {
        psp->y = SHOTGUN_YOFF + tilesiz[picnum].y;

        pWeaponUnHideKeys(psp, psp->PresentState);
    }
}

#if 1
SWBOOL
pShotgunReloadTest(PANEL_SPRITEp psp)
{
    //short ammo = psp->PlayerP->WpnAmmo[psp->PlayerP->WeaponType];
    short ammo = psp->PlayerP->WpnAmmo[WPN_SHOTGUN];

    // Reload if done with clip
    if (ammo > 0 && (ammo % 4) == 0)
    {
        // clip has run out
        RESET(psp->flags, PANF_REST_POS);
        pSetState(psp, ps_ShotgunReload);
        return TRUE;
    }

    return FALSE;
}
#endif

void
pShotgunRest(PANEL_SPRITEp psp)
{
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);
    //short ammo = psp->PlayerP->WpnAmmo[psp->PlayerP->WeaponType];
    short ammo = psp->PlayerP->WpnAmmo[WPN_SHOTGUN];
    char lastammo = psp->PlayerP->WpnShotgunLastShell;

    if (pWeaponHideKeys(psp, ps_ShotgunHide))
        return;

    if (psp->PlayerP->WpnShotgunType == 1 && ammo > 0 && ((ammo % 4) != 0) && lastammo != ammo && TEST(psp->flags, PANF_REST_POS))
    {
        force = TRUE;
    }

    pShotgunOverlays(psp);

    pShotgunBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));


    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

            if (!WeaponOK(psp->PlayerP))
                return;

            SET(psp->flags, PANF_REST_POS); // Used for reload checking in autofire

            if (psp->PlayerP->WpnShotgunType == 0)
                psp->PlayerP->WpnShotgunLastShell = ammo-1;

            DoPlayerChooseYell(psp->PlayerP);
            if (psp->PlayerP->WpnShotgunType==0)
                pSetState(psp, ps_ShotgunFire);
            else
                pSetState(psp, ps_ShotgunAutoFire);
        }
        if (!WeaponOK(psp->PlayerP))
            return;
    }
    else
        WeaponOK(psp->PlayerP);
}

void
pShotgunRestTest(PANEL_SPRITEp psp)
{
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (psp->PlayerP->WpnShotgunType == 1 && !pShotgunReloadTest(psp))
        force = TRUE;

    if (pShotgunReloadTest(psp))
        return;

    if (pWeaponHideKeys(psp, ps_ShotgunHide))
        return;

    pShotgunBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

            if (!WeaponOK(psp->PlayerP))
                return;

            DoPlayerChooseYell(psp->PlayerP);
            return;
        }
    }

    pSetState(psp, psp->RestState);
}

void
pShotgunAction(PANEL_SPRITEp psp)
{
    pShotgunBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));
}


void
pShotgunFire(PANEL_SPRITEp psp)
{
    int InitShotgun(PLAYERp pp);

    SpawnVis(psp->PlayerP->PlayerSprite, -1, -1, -1, -1, 32);
    InitShotgun(psp->PlayerP);
    //SpawnShotgunShell(psp);
}

void
pShotgunRetract(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= SHOTGUN_YOFF + tilesiz[picnum].y + 50)
    {
        RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
        psp->PlayerP->Wpn[psp->WeaponType] = NULL;
        pKillSprite(psp);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// RAIL
//
//////////////////////////////////////////////////////////////////////////////////////////

void pRailPresent(PANEL_SPRITEp psp);
void pRailRetract(PANEL_SPRITEp psp);
void pRailAction(PANEL_SPRITEp psp);
void pRailRest(PANEL_SPRITEp psp);
void pRailFire(PANEL_SPRITEp psp);
void pRailHide(PANEL_SPRITEp psp);
void pRailRestTest(PANEL_SPRITEp psp);
void pRailOkTest(PANEL_SPRITEp psp);
void pRailRecoilUp(PANEL_SPRITEp psp);
void pRailRecoilDown(PANEL_SPRITEp psp);

void pRailBobSetup(PANEL_SPRITEp psp);

SWBOOL pRailReloadTest(PANEL_SPRITEp psp);

#define Rail_BEAT_RATE 24
#define Rail_ACTION_RATE 3  // !JIM! Was 10
#define Rail_CHARGE_RATE 3

PANEL_STATE ps_PresentRail[] =
{
    {ID_RailPresent0, Rail_BEAT_RATE, pRailPresent, &ps_PresentRail[0], psf_ShadeNone, 0,0}
};

PANEL_STATE ps_RailRest[] =
{
    {ID_RailRest0, Rail_BEAT_RATE, pRailRest, &ps_RailRest[1], psf_ShadeNone, 0,0},
    {ID_RailRest1, Rail_BEAT_RATE, pRailRest, &ps_RailRest[2], psf_ShadeNone, 0,0},
    {ID_RailRest2, Rail_BEAT_RATE, pRailRest, &ps_RailRest[3], psf_ShadeNone, 0,0},
    {ID_RailRest3, Rail_BEAT_RATE, pRailRest, &ps_RailRest[4], psf_ShadeNone, 0,0},
    {ID_RailRest4, Rail_BEAT_RATE, pRailRest, &ps_RailRest[0], psf_ShadeNone, 0,0},
};

PANEL_STATE ps_RailHide[] =
{
    {ID_RailPresent0, Rail_BEAT_RATE, pRailHide, &ps_RailHide[0], psf_ShadeNone, 0,0}
};

PANEL_STATE ps_RailRecoil[] =
{
    // recoil
    {ID_RailPresent0, Rail_BEAT_RATE, pRailRecoilDown, &ps_RailRecoil[0], 0,0,0},
    {ID_RailPresent0, Rail_BEAT_RATE, pRailRecoilUp, &ps_RailRecoil[1], 0,0,0},
    // ready to fire again
    {ID_RailPresent0, 3, pNullAnimator, &ps_RailRest[0], 0,0,0}
};

PANEL_STATE ps_RailFire[] =
{
    {ID_RailCharge0,       Rail_CHARGE_RATE, pRailAction,    &ps_RailFire[1], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE, pRailAction,    &ps_RailFire[2], psf_ShadeNone, 0,0},
    {ID_RailCharge2,       Rail_CHARGE_RATE, pRailAction,    &ps_RailFire[3], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE, pRailAction,    &ps_RailFire[4], psf_ShadeNone, 0,0},

    {ID_RailFire0,       Rail_ACTION_RATE, pRailAction,    &ps_RailFire[5], psf_ShadeNone, 0,0},
    {ID_RailFire1,       Rail_ACTION_RATE, pRailAction,    &ps_RailFire[6], psf_ShadeNone, 0,0},
    {ID_RailFire1,       Rail_ACTION_RATE, pRailAction,    &ps_RailFire[7], psf_ShadeNone, 0,0},
    {ID_RailFire1,       0,                pRailFire,      &ps_RailFire[8], psf_ShadeNone|psf_QuickCall, 0,0},

    // recoil
    {ID_RailPresent0,      Rail_BEAT_RATE, pRailRecoilDown,  &ps_RailFire[8], psf_ShadeNone, 0,0},
    {ID_RailPresent0,      Rail_BEAT_RATE, pRailRecoilUp,    &ps_RailFire[9], psf_ShadeNone, 0,0},
    // !JIM! I added these to introduce firing delay, that looks like a charge down.
    {ID_RailCharge0,       Rail_CHARGE_RATE, pRailOkTest,    &ps_RailFire[11], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE, pRailAction,    &ps_RailFire[12], psf_ShadeNone, 0,0},
    {ID_RailCharge2,       Rail_CHARGE_RATE, pRailOkTest,    &ps_RailFire[13], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE, pRailAction,    &ps_RailFire[14], psf_ShadeNone, 0,0},
    {ID_RailCharge0,       Rail_CHARGE_RATE+1, pRailAction,    &ps_RailFire[15], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE+1, pRailAction,    &ps_RailFire[16], psf_ShadeNone, 0,0},
    {ID_RailCharge2,       Rail_CHARGE_RATE+1, pRailAction,    &ps_RailFire[17], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE+2, pRailAction,    &ps_RailFire[18], psf_ShadeNone, 0,0},
    {ID_RailCharge0,       Rail_CHARGE_RATE+2, pRailAction,    &ps_RailFire[19], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE+2, pRailAction,    &ps_RailFire[20], psf_ShadeNone, 0,0},
    {ID_RailCharge2,       Rail_CHARGE_RATE+3, pRailAction,    &ps_RailFire[21], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE+3, pRailAction,    &ps_RailFire[22], psf_ShadeNone, 0,0},
    {ID_RailCharge0,       Rail_CHARGE_RATE+4, pRailAction,    &ps_RailFire[23], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE+4, pRailAction,    &ps_RailFire[24], psf_ShadeNone, 0,0},
    {ID_RailCharge2,       Rail_CHARGE_RATE+4, pRailAction,    &ps_RailFire[25], psf_ShadeNone, 0,0},
    {ID_RailCharge0,       Rail_CHARGE_RATE+5, pRailAction,    &ps_RailFire[26], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE+5, pRailAction,    &ps_RailFire[27], psf_ShadeNone, 0,0},
    {ID_RailCharge2,       Rail_CHARGE_RATE+5, pRailAction,    &ps_RailFire[28], psf_ShadeNone, 0,0},
//    {ID_RailCharge0,       Rail_CHARGE_RATE+6, pRailAction,    &ps_RailFire[27], psf_ShadeNone, 0,0},
//    {ID_RailCharge1,       Rail_CHARGE_RATE+6, pRailAction,    &ps_RailFire[28], psf_ShadeNone, 0,0},
//    {ID_RailCharge2,       Rail_CHARGE_RATE+6, pRailAction,    &ps_RailFire[29], psf_ShadeNone, 0,0},
//    {ID_RailCharge0,       Rail_CHARGE_RATE+7, pRailAction,    &ps_RailFire[30], psf_ShadeNone, 0,0},
//    {ID_RailCharge1,       Rail_CHARGE_RATE+7, pRailAction,    &ps_RailFire[31], psf_ShadeNone, 0,0},
//    {ID_RailCharge2,       Rail_CHARGE_RATE+7, pRailAction,    &ps_RailFire[32], psf_ShadeNone, 0,0},
//    {ID_RailCharge1,       Rail_CHARGE_RATE+7, pRailAction,    &ps_RailFire[33], psf_ShadeNone, 0,0},
//    {ID_RailCharge0,       Rail_CHARGE_RATE+8, pRailAction,    &ps_RailFire[34], psf_ShadeNone, 0,0},
//    {ID_RailCharge1,       Rail_CHARGE_RATE+8, pRailAction,    &ps_RailFire[35], psf_ShadeNone, 0,0},

    {ID_RailCharge0,      Rail_ACTION_RATE, pRailRestTest,  &ps_RailFire[29], psf_ShadeNone, 0,0},
    {ID_RailCharge1,      Rail_ACTION_RATE, pRailRest,      &ps_RailRest[0], psf_ShadeNone, 0,0},
//    {ID_RailFire1,      Rail_ACTION_RATE, pRailRestTest,  &ps_RailFire[27], psf_ShadeNone, 0,0},
//    {ID_RailFire1,      Rail_ACTION_RATE, pRailRest,      &ps_RailRest[0], psf_ShadeNone, 0,0},
};

PANEL_STATE ps_RailFireEMP[] =
{
    {ID_RailCharge0,       Rail_CHARGE_RATE, pRailAction,    &ps_RailFireEMP[1], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE, pRailAction,    &ps_RailFireEMP[2], psf_ShadeNone, 0,0},
    {ID_RailCharge2,       Rail_CHARGE_RATE, pRailAction,    &ps_RailFireEMP[3], psf_ShadeNone, 0,0},
    {ID_RailCharge1,       Rail_CHARGE_RATE, pRailAction,    &ps_RailFireEMP[4], psf_ShadeNone, 0,0},

    {ID_RailFire0,       Rail_ACTION_RATE, pRailAction,    &ps_RailFireEMP[5], psf_ShadeNone, 0,0},
    {ID_RailFire1,       Rail_ACTION_RATE, pRailAction,    &ps_RailFireEMP[6], psf_ShadeNone, 0,0},
    {ID_RailFire1,       Rail_ACTION_RATE, pRailAction,    &ps_RailFireEMP[7], psf_ShadeNone, 0,0},
    {ID_RailFire1,       0,                pRailFire,      &ps_RailFireEMP[8], psf_ShadeNone|psf_QuickCall, 0,0},

    {ID_RailCharge0,      Rail_ACTION_RATE, pRailRestTest,  &ps_RailFireEMP[9], psf_ShadeNone, 0,0},
    {ID_RailCharge1,      Rail_ACTION_RATE, pRailRest,      &ps_RailRest[0], psf_ShadeNone, 0,0},
};


PANEL_STATE ps_RetractRail[] =
{
    {ID_RailPresent0, Rail_BEAT_RATE, pRailRetract, &ps_RetractRail[0], psf_ShadeNone, 0,0}
};

#define RAIL_YOFF 200
//#define RAIL_XOFF (160+60)
#define RAIL_XOFF (160+6)

static int railvochandle=0;

void
InitWeaponRail(PLAYERp pp)
{
    PANEL_SPRITEp psp = NULL;

    if (SW_SHAREWARE) return;

    if (Prediction)
        return;

    pp->WeaponType = WPN_RAIL;

    if (!TEST(pp->WpnFlags, BIT(pp->WeaponType)) ||
//        pp->WpnAmmo[pp->WeaponType] <= 0 ||
        TEST(pp->Flags, PF_WEAPON_RETRACT))
        return;

    if (!pp->Wpn[pp->WeaponType])
    {
        psp = pp->Wpn[pp->WeaponType] = pSpawnSprite(pp, ps_PresentRail, PRI_MID, RAIL_XOFF, RAIL_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    if (pp->CurWpn == pp->Wpn[pp->WeaponType])
    {
        return;
    }

    psp->WeaponType = pp->WeaponType;
    PlayerUpdateWeapon(pp, pp->WeaponType);

    pp->WpnUziType = 2; // Make uzi's go away!
    RetractCurWpn(pp);

    // Set up the new Weapon variables
    psp = pp->CurWpn = pp->Wpn[pp->WeaponType];
    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = ps_RailFire;
    psp->RetractState = ps_RetractRail;
    psp->PresentState = ps_PresentRail;
    psp->RestState = ps_RailRest;
    pSetState(psp, psp->PresentState);

    PlaySound(DIGI_RAIL_UP, &pp->posx, &pp->posy, &pp->posz, v3df_follow);
    railvochandle = PlaySound(DIGI_RAILREADY, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan);
    Set3DSoundOwner(psp->PlayerP->PlayerSprite);

    FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
    FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);
}

void
pRailSetRecoil(PANEL_SPRITEp psp)
{
    psp->vel = 900;
    psp->ang = NORM_ANGLE(-256);
}

void
pRailRecoilDown(PANEL_SPRITEp psp)
{
    int x = FIXED(psp->x, psp->xfract);
    int y = FIXED(psp->y, psp->yfract);

    x += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang + 512)] >> 6;
    y += psp->vel * synctics * (int) -sintable[psp->ang] >> 6;

    psp->xfract = LSW(x);
    psp->x = MSW(x);
    psp->yfract = LSW(y);
    psp->y = MSW(y);

    psp->vel -= 24 * synctics;

    if (psp->vel < 800)
    {
        psp->vel = 800;
        psp->ang = NORM_ANGLE(psp->ang + 1024);

        pStatePlusOne(psp);
    }
}

void
pRailRecoilUp(PANEL_SPRITEp psp)
{
    int x = FIXED(psp->x, psp->xfract);
    int y = FIXED(psp->y, psp->yfract);

    x += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang + 512)] >> 6;
    y += psp->vel * synctics * (int) -sintable[psp->ang] >> 6;

    psp->xfract = LSW(x);
    psp->x = MSW(x);
    psp->yfract = LSW(y);
    psp->y = MSW(y);

    psp->vel += 15 * synctics;

    if (psp->y < RAIL_YOFF)
    {
        psp->y = RAIL_YOFF;
        psp->x = RAIL_XOFF;

        pRailSetRecoil(psp);

        pStatePlusOne(psp);
        RESET(psp->flags, PANF_BOB);
    }
}

void
pRailPresent(PANEL_SPRITEp psp)
{
    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    // Needed for recoil
    psp->ang = NORM_ANGLE(256 + 128);
    pRailSetRecoil(psp);
    ///

    psp->y -= 3 * synctics;

    if (psp->y < RAIL_YOFF)
    {
        psp->y = RAIL_YOFF;
        psp->yorig = psp->y;
        pSetState(psp, psp->RestState);
    }
}

void
pRailBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = 12;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 3;
}

void
pRailHide(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= RAIL_YOFF + tilesiz[picnum].y)
    {
        psp->y = RAIL_YOFF + tilesiz[picnum].y;

        pWeaponUnHideKeys(psp, psp->PresentState);
    }
}

void
pRailOkTest(PANEL_SPRITEp psp)
{
    if (pWeaponHideKeys(psp, ps_RailHide))
        return;

    WeaponOK(psp->PlayerP);
}

void
pRailRest(PANEL_SPRITEp psp)
{
    int InitLaserSight(PLAYERp pp);
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (SW_SHAREWARE) return;

    if (pWeaponHideKeys(psp, ps_RailHide))
        return;

    pRailBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

            if (!WeaponOK(psp->PlayerP))
                return;

            //PlaySound(DIGI_RAILPWRUP, &psp->PlayerP->posx, &psp->PlayerP->posy, &psp->PlayerP->posz, v3df_follow);

            DoPlayerChooseYell(psp->PlayerP);
            if (psp->PlayerP->WpnRailType == 0)
                pSetState(psp, ps_RailFire);
            else
                pSetState(psp, ps_RailFireEMP);
        }
    }
    else
        WeaponOK(psp->PlayerP);
}

void
pRailRestTest(PANEL_SPRITEp psp)
{
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (pWeaponHideKeys(psp, ps_RailHide))
        return;

    pRailBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

            if (!WeaponOK(psp->PlayerP))
                return;

            DoPlayerChooseYell(psp->PlayerP);
            return;
        }
    }

    pSetState(psp, psp->RestState);
}

void
pRailAction(PANEL_SPRITEp psp)
{
    pRailBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));
}


void
pRailFire(PANEL_SPRITEp psp)
{
    int InitRail(PLAYERp pp);
    int InitEMP(PLAYERp pp);

    SpawnVis(psp->PlayerP->PlayerSprite, -1, -1, -1, -1, 16);
    if (psp->PlayerP->WpnRailType == 0)
        InitRail(psp->PlayerP);
    else
        InitEMP(psp->PlayerP);
}

void
pRailRetract(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= RAIL_YOFF + tilesiz[picnum].y + 50)
    {
        RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
        psp->PlayerP->Wpn[psp->WeaponType] = NULL;
        DeleteNoSoundOwner(psp->PlayerP->PlayerSprite);
        pKillSprite(psp);
    }
}


//////////////////////////////////////////////////////////////////////////////////////////
//
// HOTHEAD
//
//////////////////////////////////////////////////////////////////////////////////////////

void pHotheadPresent(PANEL_SPRITEp psp);
void pHotheadRetract(PANEL_SPRITEp psp);
void pHotheadAction(PANEL_SPRITEp psp);
void pHotheadActionCenter(PANEL_SPRITEp psp);
void pHotheadRest(PANEL_SPRITEp psp);
void pHotheadRestCenter(PANEL_SPRITEp psp);
void pHotheadAttack(PANEL_SPRITEp psp);
void pHotheadRing(PANEL_SPRITEp psp);
void pHotheadNapalm(PANEL_SPRITEp psp);
void pHotheadHide(PANEL_SPRITEp psp);
void pHotheadRestTest(PANEL_SPRITEp psp);

extern PANEL_STATE ps_HotheadAttack[];
extern PANEL_STATE ps_ReloadHothead[];
extern PANEL_STATE ps_HotheadTurn[];


#define Hothead_BEAT_RATE 24
#define Hothead_ACTION_RATE_PRE 2  // !JIM! Was 1
#define Hothead_ACTION_RATE_POST 7

PANEL_STATE ps_PresentHothead[] =
{
    {ID_HotheadPresent0, Hothead_BEAT_RATE, pHotheadPresent, &ps_PresentHothead[0], 0,0,0}
};

PANEL_STATE ps_HotheadHide[] =
{
    {ID_HotheadRest0, Hothead_BEAT_RATE, pHotheadHide, &ps_HotheadHide[0], 0,0,0}
};

PANEL_STATE ps_RetractHothead[] =
{
    {ID_HotheadPresent0, Hothead_BEAT_RATE, pHotheadRetract, &ps_RetractHothead[0], 0,0,0}
};

PANEL_STATE ps_HotheadRest[] =
{
    {ID_HotheadRest0, Hothead_BEAT_RATE, pHotheadRest, &ps_HotheadRest[0], 0,0,0}
};

PANEL_STATE ps_HotheadRestRing[] =
{
    {ID_HotheadRest0, Hothead_BEAT_RATE, pHotheadRest, &ps_HotheadRest[0], 0,0,0}
};

PANEL_STATE ps_HotheadRestNapalm[] =
{
    {ID_HotheadRest0, Hothead_BEAT_RATE, pHotheadRest, &ps_HotheadRest[0], 0,0,0}
};
// Turns - attacks

PANEL_STATE ps_HotheadAttack[] =
{
    {ID_HotheadAttack0, Hothead_ACTION_RATE_PRE,  pHotheadAction, &ps_HotheadAttack[1], psf_ShadeHalf, 0,0},
    {ID_HotheadAttack0, 3,                         pHotheadAction, &ps_HotheadAttack[2], psf_ShadeHalf, 0,0},
    {ID_HotheadAttack0, 0,                         pHotheadAttack, &ps_HotheadAttack[3], psf_QuickCall, 0,0},
    {ID_HotheadAttack0, 3,                         pHotheadAction, &ps_HotheadAttack[4], psf_ShadeHalf, 0,0},
    {ID_HotheadAttack0, 0,                         pHotheadRestTest, &ps_HotheadAttack[4], psf_QuickCall, 0,0},
    {ID_HotheadAttack0, 0,                         pHotheadAction, &ps_HotheadAttack[0], psf_ShadeHalf, 0,0}
};

PANEL_STATE ps_HotheadRing[] =
{
    {ID_HotheadAttack0, Hothead_ACTION_RATE_PRE,  pHotheadAction, &ps_HotheadRing[1], psf_ShadeHalf, 0,0},
    {ID_HotheadAttack0, 10,                        pHotheadAction, &ps_HotheadRing[2], psf_ShadeHalf, 0,0},
    {ID_HotheadAttack0, 0,                         pHotheadAttack, &ps_HotheadRing[3], psf_QuickCall, 0,0},
    {ID_HotheadAttack0, 40,                        pHotheadAction, &ps_HotheadRing[4], psf_ShadeHalf, 0,0},
    {ID_HotheadAttack0, 0,                         pHotheadRestTest, &ps_HotheadRing[4], psf_QuickCall, 0,0},
    {ID_HotheadAttack0, 3,                         pHotheadAction, &ps_HotheadRing[0], psf_ShadeHalf, 0,0}
};

PANEL_STATE ps_HotheadNapalm[] =
{
    {ID_HotheadAttack0, Hothead_ACTION_RATE_PRE,  pHotheadAction, &ps_HotheadNapalm[1], psf_ShadeHalf, 0,0},
    {ID_HotheadAttack0, 3,                        pHotheadAction, &ps_HotheadNapalm[2], psf_ShadeHalf, 0,0},
    {ID_HotheadAttack0, 0,                         pHotheadAttack, &ps_HotheadNapalm[3], psf_QuickCall, 0,0},
    {ID_HotheadAttack0, 50,                        pHotheadAction, &ps_HotheadNapalm[4], psf_ShadeHalf, 0,0},
    {ID_HotheadAttack0, 0,                         pHotheadRestTest, &ps_HotheadNapalm[4], psf_QuickCall, 0,0},
    {ID_HotheadAttack0, 3,                         pHotheadAction, &ps_HotheadNapalm[0], psf_ShadeHalf, 0,0}
};

// Turns - can do three different turns

PANEL_STATE ps_HotheadTurn[] =
{
    {ID_HotheadTurn0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurn[1], 0,0,0},
    {ID_HotheadTurn1, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurn[2], 0,0,0},
    {ID_HotheadTurn2, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurn[3], 0,0,0},
    {ID_HotheadTurn3, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurn[4], 0,0,0},
    {ID_HotheadChomp0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurn[5], 0,0,0},
    {ID_HotheadTurn3, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurn[6], 0,0,0},
    {ID_HotheadChomp0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurn[7], 0,0,0},
    {ID_HotheadTurn3, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurn[8], 0,0,0},
    {ID_HotheadTurn2, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurn[9], 0,0,0},
    {ID_HotheadTurn1, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurn[10], 0,0,0},
    {ID_HotheadTurn0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurn[11], 0,0,0},
    {ID_HotheadTurn0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadRest[0], 0,0,0}
};

PANEL_STATE ps_HotheadTurnRing[] =
{
    {ID_HotheadTurn0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnRing[1], 0,0,0},
    {ID_HotheadTurn1, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnRing[2], 0,0,0},
    {ID_HotheadTurn2, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnRing[3], 0,0,0},
    {ID_HotheadTurn3, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnRing[4], 0,0,0},
    {ID_HotheadChomp0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnRing[5], 0,0,0},
    {ID_HotheadTurn3, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnRing[6], 0,0,0},
    {ID_HotheadTurn2, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnRing[7], 0,0,0},
    {ID_HotheadTurn1, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnRing[8], 0,0,0},
    {ID_HotheadTurn0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnRing[9], 0,0,0},
    {ID_HotheadTurn0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadRestRing[0], 0,0,0}
};

PANEL_STATE ps_HotheadTurnNapalm[] =
{
    {ID_HotheadTurn0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnNapalm[1], 0,0,0},
    {ID_HotheadTurn1, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnNapalm[2], 0,0,0},
    {ID_HotheadTurn2, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnNapalm[3], 0,0,0},
    {ID_HotheadTurn3, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnNapalm[4], 0,0,0},
    {ID_HotheadTurn3, Hothead_BEAT_RATE*2, pNullAnimator, &ps_HotheadTurnNapalm[5], 0,0,0},
    {ID_HotheadTurn2, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnNapalm[6], 0,0,0},
    {ID_HotheadTurn1, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnNapalm[7], 0,0,0},
    {ID_HotheadTurn0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadTurnNapalm[8], 0,0,0},
    {ID_HotheadTurn0, Hothead_BEAT_RATE, pNullAnimator, &ps_HotheadRestNapalm[0], 0,0,0}
};


PANEL_STATEp HotheadAttackStates[] =
{
    ps_HotheadAttack,
    ps_HotheadRing,
    ps_HotheadNapalm
};

PANEL_STATEp HotheadRestStates[] =
{
    ps_HotheadRest,
    ps_HotheadRestRing,
    ps_HotheadRestNapalm
};

PANEL_STATEp HotheadTurnStates[] =
{
    ps_HotheadTurn,
    ps_HotheadTurnRing,
    ps_HotheadTurnNapalm
};

#define FIREBALL_MODE 0
#define     RING_MODE 1
#define   NAPALM_MODE 2
void
pHotHeadOverlays(PANEL_SPRITEp psp, short mode)
{
#define HOTHEAD_FINGER_XOFF 0
#define HOTHEAD_FINGER_YOFF 0

    switch (mode)
    {
    case 0: // Great balls o' fire
        psp->over[0].pic = HEAD_MODE1;
        break;
    case 1: // Ring of fire
        psp->over[0].pic = HEAD_MODE2;
        break;
    case 2: // I love the smell of napalm in the morning
        psp->over[0].pic = HEAD_MODE3;
        break;
    }
}

#define HOTHEAD_BOB_X_AMT 10

#define HOTHEAD_XOFF (200 + HOTHEAD_BOB_X_AMT + 6)
#define HOTHEAD_YOFF 200

void
InitWeaponHothead(PLAYERp pp)
{
    PANEL_SPRITEp psp = NULL;

    if (SW_SHAREWARE) return;

    if (Prediction)
        return;

    if (!TEST(pp->WpnFlags, BIT(WPN_HOTHEAD)) ||
//        pp->WpnAmmo[WPN_HOTHEAD] <= 0 ||
        TEST(pp->Flags, PF_WEAPON_RETRACT))
        return;

    if (!pp->Wpn[WPN_HOTHEAD])
    {
        psp = pp->Wpn[WPN_HOTHEAD] = pSpawnSprite(pp, ps_PresentHothead, PRI_MID, HOTHEAD_XOFF, HOTHEAD_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    if (pp->CurWpn == pp->Wpn[WPN_HOTHEAD])
    {
        return;
    }

    psp->WeaponType = WPN_HOTHEAD;
    PlayerUpdateWeapon(pp, WPN_HOTHEAD);

    pp->WpnUziType = 2; // Make uzi's go away!
    RetractCurWpn(pp);

    // Set up the new Weapon variables
    psp = pp->CurWpn = pp->Wpn[WPN_HOTHEAD];
    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = ps_HotheadAttack;
    psp->PresentState = ps_PresentHothead;
    psp->RestState = HotheadRestStates[psp->PlayerP->WpnFlameType];
    psp->RetractState = ps_RetractHothead;
    pSetState(psp, psp->PresentState);
    psp->ang = 768;
    psp->vel = 512;

    FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
    FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);
    pHotHeadOverlays(psp, pp->WpnFlameType);
    psp->over[0].xoff = HOTHEAD_FINGER_XOFF;
    psp->over[0].yoff = HOTHEAD_FINGER_YOFF;

    PlaySound(DIGI_GRDALERT, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan);
}

void
pHotheadRestTest(PANEL_SPRITEp psp)
{
    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT))
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT))
        {
            //if (!TEST(psp->PlayerP->Flags,PF_DIVING))
            {
                if (!WeaponOK(psp->PlayerP))
                    return;

                if (psp->PlayerP->WpnAmmo[WPN_HOTHEAD] < 10)
                {
                    psp->PlayerP->WpnFlameType = 0;
                    WeaponOK(psp->PlayerP);
                }

                DoPlayerChooseYell(psp->PlayerP);
            }

            pStatePlusOne(psp);
            return;
        }
    }

    pSetState(psp, HotheadRestStates[psp->PlayerP->WpnFlameType]);
    psp->over[0].xoff = HOTHEAD_FINGER_XOFF;
    psp->over[0].yoff = HOTHEAD_FINGER_YOFF;
}

void
pHotheadPresent(PANEL_SPRITEp psp)
{
    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    psp->y -= 3 * synctics;

    if (psp->y < HOTHEAD_YOFF)
    {
        psp->y = HOTHEAD_YOFF;
        psp->yorig = psp->y;
        pSetState(psp, psp->RestState);
        //pSetState(psp, HotheadTurnStates[psp->PlayerP->WpnFlameType]);
    }
}

void
pHotheadBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = HOTHEAD_BOB_X_AMT;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 2;
}

void
pHotheadHide(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->x += 3 * synctics;

    if (psp->x >= HOTHEAD_XOFF + tilesiz[picnum].x || psp->y >= HOTHEAD_YOFF + tilesiz[picnum].y)
    {
        psp->x = HOTHEAD_XOFF;
        psp->y = HOTHEAD_YOFF + tilesiz[picnum].y;

        pWeaponUnHideKeys(psp, psp->PresentState);
    }
}


void
pHotheadRest(PANEL_SPRITEp psp)
{
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (SW_SHAREWARE) return;

    if (pWeaponHideKeys(psp, ps_HotheadHide))
        return;

    if (HotheadRestStates[psp->PlayerP->WpnFlameType] != psp->RestState)
    {
        psp->RestState = HotheadRestStates[psp->PlayerP->WpnFlameType];
        pSetState(psp, HotheadRestStates[psp->PlayerP->WpnFlameType]);
    }

    // in rest position - only bob when in rest pos
    pHotheadBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

            //if (TEST(psp->PlayerP->Flags,PF_DIVING))
            //    return;

            if (!WeaponOK(psp->PlayerP))
                return;

            if (psp->PlayerP->WpnAmmo[WPN_HOTHEAD] < 10)
            {
                psp->PlayerP->WpnFlameType = 0;
                WeaponOK(psp->PlayerP);
            }

            DoPlayerChooseYell(psp->PlayerP);
            //pSetState(psp, psp->ActionState);
            pSetState(psp, HotheadAttackStates[psp->PlayerP->WpnFlameType]);
            psp->over[0].xoff = HOTHEAD_FINGER_XOFF-1;
            psp->over[0].yoff = HOTHEAD_FINGER_YOFF-10;
        }
    }
    else
        WeaponOK(psp->PlayerP);
}

void
pHotheadAction(PANEL_SPRITEp psp)
{
    char shooting;

    shooting = TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) && FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT);

    if (shooting)
    {
        pUziBobSetup(psp);
        pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP) || shooting);
    }
}

void
pHotheadAttack(PANEL_SPRITEp psp)
{
    switch (psp->PlayerP->WpnFlameType)
    {
    case 0:
        SpawnVis(psp->PlayerP->PlayerSprite, -1, -1, -1, -1, 32);
        InitFireball(psp->PlayerP);
        break;
    case 1:
        SpawnVis(psp->PlayerP->PlayerSprite, -1, -1, -1, -1, 20);
        InitSpellRing(psp->PlayerP);
        break;
    case 2:
        SpawnVis(psp->PlayerP->PlayerSprite, -1, -1, -1, -1, 16);
        InitSpellNapalm(psp->PlayerP);
        break;
    }
}

void
pHotheadRetract(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= HOTHEAD_YOFF + tilesiz[picnum].y)
    {
        RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
        psp->PlayerP->Wpn[WPN_HOTHEAD] = NULL;
        pKillSprite(psp);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// HOTHEAD ON_FIRE
//
//////////////////////////////////////////////////////////////////////////////////////////

void pOnFire(PANEL_SPRITEp);

#define ON_FIRE_RATE 10

PANEL_STATE ps_OnFire[] =
{
    {ID_OnFire0, ON_FIRE_RATE, pOnFire, &ps_OnFire[1], 0,0,0},
    {ID_OnFire1, ON_FIRE_RATE, pOnFire, &ps_OnFire[2], 0,0,0},
    {ID_OnFire2, ON_FIRE_RATE, pOnFire, &ps_OnFire[3], 0,0,0},
    {ID_OnFire3, ON_FIRE_RATE, pOnFire, &ps_OnFire[4], 0,0,0},
    {ID_OnFire4, ON_FIRE_RATE, pOnFire, &ps_OnFire[5], 0,0,0},
    {ID_OnFire5, ON_FIRE_RATE, pOnFire, &ps_OnFire[6], 0,0,0},
    {ID_OnFire6, ON_FIRE_RATE, pOnFire, &ps_OnFire[7], 0,0,0},
    {ID_OnFire7, ON_FIRE_RATE, pOnFire, &ps_OnFire[8], 0,0,0},
    {ID_OnFire8, ON_FIRE_RATE, pOnFire, &ps_OnFire[9], 0,0,0},
    {ID_OnFire9, ON_FIRE_RATE, pOnFire, &ps_OnFire[10], 0,0,0},
    {ID_OnFire10, ON_FIRE_RATE, pOnFire, &ps_OnFire[11], 0,0,0},
    {ID_OnFire11, ON_FIRE_RATE, pOnFire, &ps_OnFire[12], 0,0,0},
    {ID_OnFire12, ON_FIRE_RATE, pOnFire, &ps_OnFire[0], 0,0,0},
};

#define ON_FIRE_Y_TOP 190
#define ON_FIRE_Y_BOT 230

void
SpawnOnFire(PLAYERp pp)
{
    PANEL_SPRITEp fire;
    short x = 50;

    while (x < 320)
    {
        fire = pSpawnSprite(pp, &ps_OnFire[RANDOM_P2(8<<8)>>8], PRI_FRONT, x, ON_FIRE_Y_BOT);
        SET(fire->flags, PANF_WEAPON_SPRITE);
        x += tilesiz[fire->picndx].x;
    }
}

void
pOnFire(PANEL_SPRITEp psp)
{
    // Kill immediately - in case of death/water
    if (User[psp->PlayerP->PlayerSprite]->flame <= -2)
    {
        pKillSprite(psp);
        return;
    }

    if (User[psp->PlayerP->PlayerSprite]->flame == -1)
    {
        // take flames down and kill them
        psp->y += 1;
        if (psp->y > ON_FIRE_Y_BOT)
        {
            pKillSprite(psp);
            return;
        }
    }
    else
    {
        // bring flames up
        psp->y -= 2;
        if (psp->y < ON_FIRE_Y_TOP)
            psp->y = ON_FIRE_Y_TOP;
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// MICRO
//
//////////////////////////////////////////////////////////////////////////////////////////

void pMicroPresent(PANEL_SPRITEp psp);
void pMicroRetract(PANEL_SPRITEp psp);
void pMicroAction(PANEL_SPRITEp psp);
void pMicroRest(PANEL_SPRITEp psp);
void pMicroFire(PANEL_SPRITEp psp);
void pMicroHide(PANEL_SPRITEp psp);
void pMicroUnHide(PANEL_SPRITEp psp);
void pMicroRecoilDown(PANEL_SPRITEp psp);
void pMicroRecoilUp(PANEL_SPRITEp psp);
void pMicroBobSetup(PANEL_SPRITEp psp);
void pMicroReloadUp(PANEL_SPRITEp psp);
void pMicroReloadDown(PANEL_SPRITEp psp);
void pMicroStandBy(PANEL_SPRITEp psp);
void pMicroCount(PANEL_SPRITEp psp);
void pMicroReady(PANEL_SPRITEp psp);
void pNukeAction(PANEL_SPRITEp psp);

extern PANEL_STATE ps_MicroReload[];

#define Micro_REST_RATE 24
#define Micro_ACTION_RATE 6  // !JIM! was 9

PANEL_STATE ps_PresentMicro[] =
{
    {ID_MicroPresent0, Micro_REST_RATE, pMicroPresent, &ps_PresentMicro[0], 0,0,0}
};

PANEL_STATE ps_MicroRest[] =
{
    {ID_MicroPresent0, Micro_REST_RATE, pMicroRest, &ps_MicroRest[0], 0,0,0}
};

PANEL_STATE ps_MicroHide[] =
{
    {ID_MicroPresent0, Micro_REST_RATE, pMicroHide, &ps_MicroHide[0], 0,0,0}
};

PANEL_STATE ps_InitNuke[] =
{
    {ID_MicroPresent0, Micro_ACTION_RATE,pNukeAction,  &ps_InitNuke[1], 0,0,0},
    {ID_MicroPresent0, 0,               pMicroStandBy,  &ps_InitNuke[2], psf_QuickCall, 0,0},
    {ID_MicroPresent0, 120*2,           pNukeAction, &ps_InitNuke[3], 0,0,0},
    {ID_MicroPresent0, 0,               pMicroCount,  &ps_InitNuke[4], psf_QuickCall, 0,0},
    {ID_MicroPresent0, 120*3,           pNukeAction, &ps_InitNuke[5], 0,0,0},
    {ID_MicroPresent0, 0,               pMicroReady,  &ps_InitNuke[6], psf_QuickCall, 0,0},
    {ID_MicroPresent0, 120*2,           pNukeAction, &ps_InitNuke[7], 0,0,0},
    {ID_MicroPresent0, 3,               pNukeAction, &ps_MicroRest[0], 0,0,0}
};

PANEL_STATE ps_MicroRecoil[] =
{
    // recoil
    {ID_MicroPresent0, Micro_ACTION_RATE, pMicroRecoilDown, &ps_MicroRecoil[0], 0,0,0},
    {ID_MicroPresent0, Micro_ACTION_RATE, pMicroRecoilUp,   &ps_MicroRecoil[1], 0,0,0},

    // Firing delay.
    {ID_MicroPresent0, 30, pNullAnimator,   &ps_MicroRecoil[3], 0,0,0},
    // ready to fire again
    {ID_MicroPresent0, 3, pNullAnimator, &ps_MicroRest[0], 0,0,0}
};

PANEL_STATE ps_MicroFire[] =
{
    {ID_MicroFire0, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[1], psf_ShadeNone, 0,0},
    {ID_MicroFire1, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[2], psf_ShadeNone, 0,0},
    {ID_MicroFire2, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[3], psf_ShadeHalf, 0,0},
    {ID_MicroFire3, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[4], psf_ShadeHalf, 0,0},
    {ID_MicroPresent0, 0,              pMicroFire, &ps_MicroFire[5], psf_ShadeNone|psf_QuickCall, 0,0},

#if 0
    {ID_MicroFire0, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[6], psf_ShadeNone},
    {ID_MicroFire1, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[7], psf_ShadeNone},
    {ID_MicroFire3, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[8], psf_ShadeHalf},
    {ID_MicroFire1, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[9], psf_ShadeNone},
    {ID_MicroPresent0, 0,              pMicroFire, &ps_MicroFire[10], psf_ShadeNone|psf_QuickCall},
    {ID_MicroFire3, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[11], psf_ShadeHalf},
    {ID_MicroFire0, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[12], psf_ShadeNone},
    {ID_MicroFire1, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[13], psf_ShadeNone},
    {ID_MicroFire2, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[14], psf_ShadeHalf},
    {ID_MicroPresent0, 0,              pMicroFire, &ps_MicroFire[15], psf_ShadeNone|psf_QuickCall},
    {ID_MicroFire3, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[16], psf_ShadeHalf},
    {ID_MicroFire1, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[17], psf_ShadeNone},
    {ID_MicroFire0, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[18], psf_ShadeNone},
    {ID_MicroFire1, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[19], psf_ShadeNone},
    {ID_MicroPresent0, 0,              pMicroFire, &ps_MicroFire[20], psf_ShadeNone|psf_QuickCall},
    {ID_MicroFire3, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[21], psf_ShadeHalf},
    {ID_MicroFire1, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[22], psf_ShadeNone},
    {ID_MicroFire0, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[23], psf_ShadeNone},
    {ID_MicroFire1, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[24], psf_ShadeNone},
    {ID_MicroPresent0, 0,              pMicroFire, &ps_MicroFire[25], psf_ShadeNone|psf_QuickCall},
    {ID_MicroFire2, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[26], psf_ShadeHalf},
    {ID_MicroFire3, Micro_ACTION_RATE, pMicroAction, &ps_MicroFire[27], psf_ShadeHalf},
#endif

    // !JIM! After firing delay so rockets can't fire so fast!
    // Putting a BIG blast radius for rockets, this is better than small and fast for this weap.
    {ID_MicroPresent0, 120, pMicroAction, &ps_MicroFire[6], 0,0,0},

    {ID_MicroPresent0, 3, pMicroAction, &ps_MicroRecoil[0], 0,0,0}
};

#define Micro_SINGLE_RATE 8
#define Micro_DISSIPATE_RATE 6

PANEL_STATE ps_MicroSingleFire[] =
{
    {ID_MicroSingleFire0, Micro_SINGLE_RATE,    pMicroAction,   &ps_MicroSingleFire[1], psf_ShadeHalf, 0,0},
    {ID_MicroSingleFire1, Micro_SINGLE_RATE,    pMicroAction,   &ps_MicroSingleFire[2], psf_ShadeNone, 0,0},
    {ID_MicroSingleFire1, 0,                    pMicroFire,     &ps_MicroSingleFire[3], psf_ShadeNone|psf_QuickCall, 0,0},
    {ID_MicroSingleFire2, Micro_DISSIPATE_RATE, pMicroAction,   &ps_MicroSingleFire[4], psf_ShadeNone, 0,0},
    {ID_MicroSingleFire3, Micro_DISSIPATE_RATE, pMicroAction,   &ps_MicroSingleFire[5], psf_ShadeHalf, 0,0},

    // !JIM! Put in firing delay.
    //{ID_MicroPresent0, 60, pMicroAction,   &ps_MicroSingleFire[6]},

    {ID_MicroPresent0,    3,                    pMicroAction,   &ps_MicroRecoil[0], 0,0,0}
};

PANEL_STATE ps_RetractMicro[] =
{
    {ID_MicroPresent0, Micro_REST_RATE, pMicroRetract, &ps_RetractMicro[0], 0,0,0}
};

#define MICRO_BOB_X_AMT 10
#define MICRO_YOFF 205
#define MICRO_XOFF (150+MICRO_BOB_X_AMT)

void
pMicroSetRecoil(PANEL_SPRITEp psp)
{
    psp->vel = 900;
    psp->ang = NORM_ANGLE(-256);
}

void
InitWeaponMicro(PLAYERp pp)
{
    PANEL_SPRITEp psp;

    if (Prediction)
        return;

    if (!TEST(pp->WpnFlags, BIT(WPN_MICRO)) ||
//        pp->WpnAmmo[WPN_MICRO] <= 0 ||
        TEST(pp->Flags, PF_WEAPON_RETRACT))
        return;

    if (!pp->Wpn[WPN_MICRO])
    {
        psp = pp->Wpn[WPN_MICRO] = pSpawnSprite(pp, ps_PresentMicro, PRI_MID, MICRO_XOFF, MICRO_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    if (pp->CurWpn == pp->Wpn[WPN_MICRO])
    {
        if (pp->TestNukeInit && pp->WpnRocketType == 2 && !pp->InitingNuke && pp->WpnRocketNuke && !pp->NukeInitialized)
        {
            pp->TestNukeInit = FALSE;
            pp->InitingNuke = TRUE;
            psp = pp->Wpn[WPN_MICRO];
            pSetState(psp, ps_InitNuke);
        }
        return;
    }

    PlayerUpdateWeapon(pp, WPN_MICRO);

    pp->WpnUziType = 2; // Make uzi's go away!
    RetractCurWpn(pp);

    // Set up the new Weapon variables
    psp = pp->CurWpn = pp->Wpn[WPN_MICRO];
    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = ps_MicroFire;
    psp->RetractState = ps_RetractMicro;
    psp->RestState = ps_MicroRest;
    psp->PresentState = ps_PresentMicro;
    pSetState(psp, psp->PresentState);

    if (pp->WpnRocketType == 2 && !pp->InitingNuke && !pp->NukeInitialized)
        pp->TestNukeInit = pp->InitingNuke = TRUE;

    PlaySound(DIGI_ROCKET_UP, &pp->posx, &pp->posy, &pp->posz, v3df_follow);

    FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
    FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);

}


void
pMicroRecoilDown(PANEL_SPRITEp psp)
{
    int x = FIXED(psp->x, psp->xfract);
    int y = FIXED(psp->y, psp->yfract);

    x += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang + 512)] >> 6;
    y += psp->vel * synctics * (int) -sintable[psp->ang] >> 6;

    psp->xfract = LSW(x);
    psp->x = MSW(x);
    psp->yfract = LSW(y);
    psp->y = MSW(y);

    psp->vel -= 24 * synctics;

    if (psp->vel < 550)
    {
        psp->vel = 550;
        psp->ang = NORM_ANGLE(psp->ang + 1024);

        pStatePlusOne(psp);
    }
}

void
pMicroRecoilUp(PANEL_SPRITEp psp)
{
    int x = FIXED(psp->x, psp->xfract);
    int y = FIXED(psp->y, psp->yfract);

    x += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang + 512)] >> 6;
    y += psp->vel * synctics * (int) -sintable[psp->ang] >> 6;

    psp->xfract = LSW(x);
    psp->x = MSW(x);
    psp->yfract = LSW(y);
    psp->y = MSW(y);

    psp->vel += 15 * synctics;

    if (psp->y < MICRO_YOFF)
    {
        psp->y = MICRO_YOFF;
        psp->x = MICRO_XOFF;

        pMicroSetRecoil(psp);

        pStatePlusOne(psp);
        RESET(psp->flags, PANF_BOB);
    }
}

void
pMicroPresent(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;

    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    // Needed for recoil
    psp->ang = NORM_ANGLE(256 + 96);
    pMicroSetRecoil(psp);
    ///

    psp->y -= 3 * synctics;

    if (psp->y < MICRO_YOFF)
    {
        psp->y = MICRO_YOFF;
        psp->yorig = psp->y;
        if (pp->WpnRocketType == 2 && !pp->NukeInitialized)
        {
            pp->TestNukeInit = FALSE;
            pSetState(psp, ps_InitNuke);
        }
        else
            pSetState(psp, psp->RestState);
    }
}

void
pMicroBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = MICRO_BOB_X_AMT;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 3;
}

void
pMicroHide(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= MICRO_YOFF + tilesiz[picnum].y + 20)
    {
        psp->y = MICRO_YOFF + tilesiz[picnum].y + 20;
        psp->x = MICRO_XOFF;

        pWeaponUnHideKeys(psp, psp->PresentState);
    }
}

SWBOOL
pMicroOverlays(PANEL_SPRITEp psp)
{
    //#define MICRO_SIGHT_XOFF 29
    //#define MICRO_SIGHT_YOFF -58
#define MICRO_SIGHT_XOFF 29
#define MICRO_SIGHT_YOFF -58

#define MICRO_SHOT_XOFF 65
#define MICRO_SHOT_YOFF -41

#define MICRO_HEAT_XOFF 78
#define MICRO_HEAT_YOFF -51

    if (psp->over[MICRO_SIGHT_NUM].xoff == -1)
    {
        psp->over[MICRO_SIGHT_NUM].xoff = MICRO_SIGHT_XOFF;
        psp->over[MICRO_SIGHT_NUM].yoff = MICRO_SIGHT_YOFF;
        psp->over[MICRO_SHOT_NUM].xoff = MICRO_SHOT_XOFF;
        psp->over[MICRO_SHOT_NUM].yoff = MICRO_SHOT_YOFF;
        psp->over[MICRO_HEAT_NUM].xoff = MICRO_HEAT_XOFF;
        psp->over[MICRO_HEAT_NUM].yoff = MICRO_HEAT_YOFF;

    }

    if (psp->PlayerP->WpnRocketNuke == 0 && psp->PlayerP->WpnRocketType == 2)
        psp->PlayerP->WpnRocketType=0;

    switch (psp->PlayerP->WpnRocketType)
    {
    case 0:
        psp->over[MICRO_SIGHT_NUM].pic = MICRO_SIGHT;
        psp->over[MICRO_SHOT_NUM].pic = MICRO_SHOT_1;
        SET(psp->over[MICRO_SHOT_NUM].flags, psf_ShadeNone);
        psp->over[MICRO_HEAT_NUM].pic = -1;
        return FALSE;
    case 1:
        if (psp->PlayerP->WpnRocketHeat)
        {
            psp->over[MICRO_SIGHT_NUM].pic = MICRO_SIGHT;
            psp->over[MICRO_SHOT_NUM].pic = MICRO_SHOT_1;
            SET(psp->over[MICRO_SHOT_NUM].flags, psf_ShadeNone);

            ASSERT(psp->PlayerP->WpnRocketHeat < 6);

            psp->over[MICRO_HEAT_NUM].pic = MICRO_HEAT + (5 - psp->PlayerP->WpnRocketHeat);
        }
        else
        {
            psp->over[MICRO_SIGHT_NUM].pic = MICRO_SIGHT;
            psp->over[MICRO_SHOT_NUM].pic = MICRO_SHOT_1;
            SET(psp->over[MICRO_SHOT_NUM].flags, psf_ShadeNone);
            psp->over[MICRO_HEAT_NUM].pic = -1;
        }

        return FALSE;
    case 2:
        psp->over[MICRO_SIGHT_NUM].pic = -1;
        psp->over[MICRO_HEAT_NUM].pic = -1;

        psp->over[MICRO_SHOT_NUM].pic = MICRO_SHOT_20;
        SET(psp->over[MICRO_SHOT_NUM].flags, psf_ShadeNone);
        SET(psp->over[MICRO_HEAT_NUM].flags, psf_ShadeNone);
        return TRUE;
    }
    return FALSE;
}

PANEL_STATE ps_MicroHeatFlash[] =
{
    {MICRO_HEAT, 30, NULL, &ps_MicroHeatFlash[1], 0,0,0},
    {0,             30, NULL, &ps_MicroHeatFlash[2], 0,0,0},
    {MICRO_HEAT,    30, NULL, &ps_MicroHeatFlash[3], 0,0,0},
    {0,             30, NULL, &ps_MicroHeatFlash[4], 0,0,0},
    {MICRO_HEAT,    30, NULL, &ps_MicroHeatFlash[5], 0,0,0},
    {0,             30, NULL, &ps_MicroHeatFlash[6], 0,0,0},
    {MICRO_HEAT,    30, NULL, &ps_MicroHeatFlash[7], 0,0,0},
    {0,             30, NULL, &ps_MicroHeatFlash[8], 0,0,0},
    {MICRO_HEAT,    30, NULL, &ps_MicroHeatFlash[9], 0,0,0},
    {0,              0, NULL, NULL, 0,0,0}
};

PANEL_STATE ps_MicroNukeFlash[] =
{
    {MICRO_SHOT_20, 30, NULL, &ps_MicroNukeFlash[1], 0,0,0},
    {0,             30, NULL, &ps_MicroNukeFlash[2], 0,0,0},
    {MICRO_SHOT_20, 30, NULL, &ps_MicroNukeFlash[3], 0,0,0},
    {0,             30, NULL, &ps_MicroNukeFlash[4], 0,0,0},
    {MICRO_SHOT_20, 30, NULL, &ps_MicroNukeFlash[5], 0,0,0},
    {0,             30, NULL, &ps_MicroNukeFlash[6], 0,0,0},
    {MICRO_SHOT_20, 30, NULL, &ps_MicroNukeFlash[7], 0,0,0},
    {0,             30, NULL, &ps_MicroNukeFlash[8], 0,0,0},
    {MICRO_SHOT_20, 30, NULL, &ps_MicroNukeFlash[9], 0,0,0},
    {0,              0, NULL, NULL, 0,0,0}
};

void
pMicroRest(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (pWeaponHideKeys(psp, ps_MicroHide))
        return;

    pMicroOverlays(psp);

    pMicroBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    if (pp->InitingNuke)
    {
        int choose_voc=0;

        pp->InitingNuke = FALSE;
        pp->NukeInitialized = TRUE;

        if (pp == Player+myconnectindex)
        {
            choose_voc = STD_RANDOM_RANGE(1024);
            if (choose_voc > 600)
                PlayerSound(DIGI_TAUNTAI2,&psp->PlayerP->posx,
                            &psp->PlayerP->posy,&psp->PlayerP->posy,v3df_dontpan|v3df_follow,psp->PlayerP);
            else if (choose_voc > 300)
                PlayerSound(DIGI_TAUNTAI4,&psp->PlayerP->posx,
                            &psp->PlayerP->posy,&psp->PlayerP->posy,v3df_dontpan|v3df_follow,psp->PlayerP);
        }
    }

    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

            if (!WeaponOK(psp->PlayerP))
                return;

            if (psp->PlayerP->WpnAmmo[WPN_MICRO] <= 0 && psp->PlayerP->WpnRocketType != 2)
            {
                psp->PlayerP->WpnRocketNuke = 0;
                WeaponOK(psp->PlayerP);
                psp->PlayerP->WpnRocketNuke = 1;
                return;
            }

            switch (psp->PlayerP->WpnRocketType)
            {
            case 0:
            case 1:
                pSetState(psp, ps_MicroSingleFire);
                DoPlayerChooseYell(psp->PlayerP);
                break;
            case 2:
                if (psp->PlayerP->WpnRocketNuke > 0)
                    pSetState(psp, ps_MicroSingleFire);
                break;
            }
        }
    }
    else
        WeaponOK(psp->PlayerP);
}

void
pMicroAction(PANEL_SPRITEp psp)
{
    pMicroBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));
}


void
pMicroFire(PANEL_SPRITEp psp)
{
    int InitMicro(PLAYERp pp);
    int InitRocket(PLAYERp pp);
    int InitNuke(PLAYERp pp);

    SpawnVis(psp->PlayerP->PlayerSprite, -1, -1, -1, -1, 20);
    switch (psp->PlayerP->WpnRocketType)
    {
    case 0:
        if (psp->PlayerP->BunnyMode)
            InitBunnyRocket(psp->PlayerP);
        else
            InitRocket(psp->PlayerP);
        break;
    case 1:
#if 0
        if (psp->PlayerP->WpnRocketHeat<=1)
        {
            // have no heat seakers so move back to regular
            psp->PlayerP->WpnRocketType = 0;
        }
#endif
        if (psp->PlayerP->BunnyMode)
            InitBunnyRocket(psp->PlayerP);
        else
            InitRocket(psp->PlayerP);
        break;
    case 2:
        PlaySound(DIGI_WARNING,&psp->PlayerP->posx,&psp->PlayerP->posy,&psp->PlayerP->posz,v3df_dontpan|v3df_follow);
        InitNuke(psp->PlayerP);
        psp->PlayerP->NukeInitialized = FALSE;
        break;
    }
}

void
pMicroRetract(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= MICRO_YOFF + tilesiz[picnum].y)
    {
        RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
        psp->PlayerP->Wpn[WPN_MICRO] = NULL;
        pKillSprite(psp);
    }
}

void
pNukeAction(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;

    psp->y -= 3 * synctics;

    if (psp->y < MICRO_YOFF)
    {
        psp->y = MICRO_YOFF;
        psp->yorig = psp->y;
    }

    pMicroBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));
    if (!pp->InitingNuke)
        pSetState(psp, psp->PresentState);
}

void
pMicroStandBy(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;

    pMicroOverlays(psp);
    pp->nukevochandle =
        PlaySound(DIGI_NUKESTDBY, &psp->PlayerP->posx, &psp->PlayerP->posy, &psp->PlayerP->posz, v3df_follow|v3df_dontpan);
}

void
pMicroCount(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;

    pp->nukevochandle =
        PlaySound(DIGI_NUKECDOWN, &psp->PlayerP->posx, &psp->PlayerP->posy, &psp->PlayerP->posz, v3df_follow|v3df_dontpan);
}

void
pMicroReady(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;

    pp->nukevochandle =
        PlaySound(DIGI_NUKEREADY, &psp->PlayerP->posx, &psp->PlayerP->posy, &psp->PlayerP->posz, v3df_follow|v3df_dontpan);
    pp->NukeInitialized = TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// HEART
//
//////////////////////////////////////////////////////////////////////////////////////////

void pHeartPresent(PANEL_SPRITEp psp);
void pHeartRetract(PANEL_SPRITEp psp);
void pHeartAction(PANEL_SPRITEp psp);
void pHeartRest(PANEL_SPRITEp psp);
void pHeartAttack(PANEL_SPRITEp psp);
void pHeartHide(PANEL_SPRITEp psp);
void pHeartActionBlood(PANEL_SPRITEp psp);
void SpawnSmallHeartBlood(PANEL_SPRITEp psp);

extern PANEL_STATE ps_HeartAttack[];
extern PANEL_STATE ps_ReloadHeart[];


#define Heart_BEAT_RATE 60
#define Heart_ACTION_RATE 10

PANEL_STATE ps_PresentHeart[] =
{
    {ID_HeartPresent0, Heart_BEAT_RATE, pHeartPresent, &ps_PresentHeart[1], 0,0,0},
    {ID_HeartPresent1, Heart_BEAT_RATE, pHeartPresent, &ps_PresentHeart[0], 0,0,0}
};

PANEL_STATE ps_HeartRest[] =
{
    {ID_HeartPresent0, Heart_BEAT_RATE, pHeartRest, &ps_HeartRest[1], 0,0,0},
    {ID_HeartPresent1, Heart_BEAT_RATE, pHeartRest, &ps_HeartRest[2], 0,0,0},
    {ID_HeartPresent1, Heart_BEAT_RATE, SpawnSmallHeartBlood, &ps_HeartRest[3], psf_QuickCall, 0,0},
    {ID_HeartPresent1, 0,               pHeartRest, &ps_HeartRest[0], 0,0,0},
};

PANEL_STATE ps_HeartHide[] =
{
    {ID_HeartPresent0, Heart_BEAT_RATE, pHeartHide, &ps_HeartHide[1], 0,0,0},
    {ID_HeartPresent1, Heart_BEAT_RATE, pHeartHide, &ps_HeartHide[0], 0,0,0}
};

PANEL_STATE ps_HeartAttack[] =
{
    // squeeze
    {ID_HeartAttack0, Heart_ACTION_RATE, pHeartActionBlood, &ps_HeartAttack[1], psf_ShadeHalf, 0,0},
    {ID_HeartAttack1, Heart_ACTION_RATE, pHeartActionBlood, &ps_HeartAttack[2], psf_ShadeNone, 0,0},
    {ID_HeartAttack1, Heart_ACTION_RATE, pHeartActionBlood, &ps_HeartAttack[3], psf_ShadeNone, 0,0},
    // attack
    {ID_HeartAttack1, Heart_ACTION_RATE, pHeartAttack, &ps_HeartAttack[4], psf_QuickCall, 0,0},
    // unsqueeze
    {ID_HeartAttack1, Heart_ACTION_RATE, pHeartAction, &ps_HeartAttack[5], psf_ShadeNone, 0,0},
    {ID_HeartAttack1, Heart_ACTION_RATE, pHeartAction, &ps_HeartAttack[6], psf_ShadeNone, 0,0},
    {ID_HeartAttack0, Heart_ACTION_RATE, pHeartAction, &ps_HeartAttack[7], psf_ShadeHalf, 0,0},

    {ID_HeartAttack0, Heart_ACTION_RATE, pHeartAction, &ps_HeartRest[0], psf_ShadeHalf, 0,0},
};

PANEL_STATE ps_RetractHeart[] =
{
    {ID_HeartPresent0, Heart_BEAT_RATE, pHeartRetract, &ps_RetractHeart[1], 0,0,0},
    {ID_HeartPresent1, Heart_BEAT_RATE, pHeartRetract, &ps_RetractHeart[0], 0,0,0}
};

#define HEART_YOFF 212

void
InitWeaponHeart(PLAYERp pp)
{
    PANEL_SPRITEp psp;

    if (SW_SHAREWARE) return;

    if (Prediction)
        return;

    if (!TEST(pp->WpnFlags, BIT(WPN_HEART)) ||
//        pp->WpnAmmo[WPN_HEART] <= 0 ||
        TEST(pp->Flags, PF_WEAPON_RETRACT))
        return;

    if (!pp->Wpn[WPN_HEART])
    {
        psp = pp->Wpn[WPN_HEART] = pSpawnSprite(pp, ps_PresentHeart, PRI_MID, 160 + 10, HEART_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    if (pp->CurWpn == pp->Wpn[WPN_HEART])
    {
        return;
    }

    PlayerUpdateWeapon(pp, WPN_HEART);

    pp->WpnUziType = 2; // Make uzi's go away!
    RetractCurWpn(pp);

    PlaySound(DIGI_HEARTBEAT, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan|v3df_doppler);

    // Set up the new Weapon variables
    psp = pp->CurWpn = pp->Wpn[WPN_HEART];
    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = ps_HeartAttack;
    psp->RetractState = ps_RetractHeart;
    psp->PresentState = ps_PresentHeart;
    psp->RestState = ps_HeartRest;
    pSetState(psp, psp->PresentState);

    FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
    FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);
}

void
pHeartPresent(PANEL_SPRITEp psp)
{
    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    psp->y -= 3 * synctics;

    if (psp->y < HEART_YOFF)
    {
        psp->y = HEART_YOFF;
        psp->yorig = psp->y;
        pSetState(psp, psp->RestState);
    }
}

void
pHeartBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = 12;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 3;
}

void
pHeartHide(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= HEART_YOFF + tilesiz[picnum].y)
    {
        psp->y = HEART_YOFF + tilesiz[picnum].y;

        pWeaponUnHideKeys(psp, psp->PresentState);
    }
}

void
pHeartRest(PANEL_SPRITEp psp)
{
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (pWeaponHideKeys(psp, ps_HeartHide))
        return;

    psp->yorig += synctics;

    if (psp->yorig > HEART_YOFF)
    {
        psp->yorig = HEART_YOFF;
    }

    psp->y = psp->yorig;

    pHeartBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

            if (!WeaponOK(psp->PlayerP))
                return;

            DoPlayerChooseYell(psp->PlayerP);
            pSetState(psp, psp->ActionState);
        }
    }
    else
    {
        FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);
        WeaponOK(psp->PlayerP);
    }
}

void
pHeartAction(PANEL_SPRITEp psp)
{
    psp->yorig -= synctics;

    if (psp->yorig < 200)
    {
        psp->yorig = 200;
    }

    psp->y = psp->yorig;

    pHeartBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));
}

void
pHeartActionBlood(PANEL_SPRITEp psp)
{
    psp->yorig -= synctics;

    if (psp->yorig < 200)
    {
        psp->yorig = 200;
    }

    psp->y = psp->yorig;

    pHeartBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    SpawnHeartBlood(psp);
}


void
pHeartAttack(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;
    // CTW MODIFICATION
    //int InitHeartAttack(PLAYERp pp);
    void InitHeartAttack(PLAYERp pp);
    // CTW MODIFICATION END

    PlaySound(DIGI_HEARTFIRE,&pp->posx,&pp->posy,&pp->posz,v3df_follow|v3df_dontpan);
    if (RANDOM_RANGE(1000) > 800)
        PlayerSound(DIGI_JG9009,&pp->posx,&pp->posy,&pp->posz,v3df_follow|v3df_dontpan,pp);
    InitHeartAttack(psp->PlayerP);
}

void
pHeartRetract(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= HEART_YOFF + tilesiz[picnum].y)
    {
        RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
        psp->PlayerP->Wpn[WPN_HEART] = NULL;
        pKillSprite(psp);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// HEART BLOOD
//
//////////////////////////////////////////////////////////////////////////////////////////

void pHeartBlood(PANEL_SPRITEp);

#define HEART_BLOOD_RATE 10

#if 1
PANEL_STATE ps_HeartBlood[] =
{
    {ID_HeartBlood0, HEART_BLOOD_RATE, pHeartBlood, &ps_HeartBlood[1], 0,0,0},
    {ID_HeartBlood1, HEART_BLOOD_RATE, pHeartBlood, &ps_HeartBlood[2], 0,0,0},
    {ID_HeartBlood2, HEART_BLOOD_RATE, pHeartBlood, &ps_HeartBlood[3], 0,0,0},
    {ID_HeartBlood3, HEART_BLOOD_RATE, pHeartBlood, &ps_HeartBlood[4], 0,0,0},
    {ID_HeartBlood4, HEART_BLOOD_RATE, pHeartBlood, &ps_HeartBlood[5], 0,0,0},
    {ID_HeartBlood5, HEART_BLOOD_RATE, pHeartBlood, &ps_HeartBlood[6], 0,0,0},
    {ID_HeartBlood5, HEART_BLOOD_RATE, pSuicide, &ps_HeartBlood[6], 0,0,0},
};

#define HEART_BLOOD_SMALL_RATE 7

PANEL_STATE ps_HeartBloodSmall[] =
{
    {ID_HeartBlood0, HEART_BLOOD_SMALL_RATE, pHeartBlood, &ps_HeartBlood[1], 0,0,0},
    {ID_HeartBlood1, HEART_BLOOD_SMALL_RATE, pHeartBlood, &ps_HeartBlood[2], 0,0,0},
    {ID_HeartBlood2, HEART_BLOOD_SMALL_RATE, pHeartBlood, &ps_HeartBlood[3], 0,0,0},
    {ID_HeartBlood3, HEART_BLOOD_SMALL_RATE, pHeartBlood, &ps_HeartBlood[4], 0,0,0},
    {ID_HeartBlood4, HEART_BLOOD_SMALL_RATE, pHeartBlood, &ps_HeartBlood[5], 0,0,0},
    {ID_HeartBlood5, HEART_BLOOD_SMALL_RATE, pHeartBlood, &ps_HeartBlood[6], 0,0,0},
    {ID_HeartBlood5, HEART_BLOOD_SMALL_RATE, pSuicide, &ps_HeartBlood[6], 0,0,0},
};
#else
PANEL_STATE ps_HeartBlood[] =
{
    {ID_HeartBlood0, HEART_BLOOD_RATE, pHeartBlood, &ps_HeartBlood[1], 0,0,0},
    {ID_HeartBlood1, HEART_BLOOD_RATE, pHeartBlood, &ps_HeartBlood[2], 0,0,0},
    {ID_HeartBlood2, HEART_BLOOD_RATE, pHeartBlood, &ps_HeartBlood[3], 0,0,0},
    {ID_HeartBlood3, HEART_BLOOD_RATE, pHeartBlood, &ps_HeartBlood[0], 0,0,0},
};
#endif

void
SpawnHeartBlood(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;
    PANEL_SPRITEp blood;
    PANEL_SHRAPp hsp;

    static PANEL_SHRAP HeartShrap[] =
    {
        {-10, -80, 2, -FIXED(1,32000), -FIXED(2,32000), -FIXED(5,32000), -FIXED(3,32000), {ps_HeartBlood, ps_HeartBlood}},
        {0, -85, 0, -FIXED(3,32000), -FIXED(8,32000), -FIXED(3,32000), -FIXED(1,32000), {ps_HeartBlood, ps_HeartBlood}},
        {10, -85, 2, -FIXED(1,32000), -FIXED(2,32000), FIXED(2,32000), FIXED(3,32000), {ps_HeartBlood, ps_HeartBlood}},
        {25, -80, 2, -FIXED(1,32000), -FIXED(2,32000), FIXED(5,32000), FIXED(6,32000), {ps_HeartBlood, ps_HeartBlood}},
        {0, 0, 0, 0, 0, 0, 0, {0, 0}},
    };

    for (hsp = HeartShrap; hsp->lo_jump_speed; hsp++)
    {
        if (hsp->skip == 2)
        {
            if (MoveSkip2 != 0)
                continue;
        }

        // RIGHT side
        blood = pSpawnSprite(pp, hsp->state[RANDOM_P2(2<<8)>>8], PRI_BACK, 0, 0);
        blood->x = psp->x + hsp->xoff;
        blood->y = psp->y + hsp->yoff;
        blood->xspeed = hsp->lo_xspeed + (RANDOM_RANGE((hsp->hi_xspeed - hsp->lo_xspeed)>>4) << 4);
        SET(blood->flags, PANF_WEAPON_SPRITE);

        blood->scale = 20000 + RANDOM_RANGE(50000 - 20000);

        blood->jump_speed = hsp->lo_jump_speed + (RANDOM_RANGE((hsp->hi_jump_speed + hsp->lo_jump_speed)>>4) << 4);
        DoBeginPanelJump(blood);
    }
}

void
SpawnSmallHeartBlood(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;
    PANEL_SPRITEp blood;
    PANEL_SHRAPp hsp;

    static PANEL_SHRAP HeartShrap[] =
    {
        {-10, -80, 0, -FIXED(1,0), -FIXED(2,0), -FIXED(1,0), -FIXED(3,0), {ps_HeartBloodSmall, ps_HeartBloodSmall}},
        {0, -85, 0, -FIXED(1,0), -FIXED(5,0), -FIXED(1,0), -FIXED(1,0), {ps_HeartBloodSmall, ps_HeartBloodSmall}},
        {10, -85, 0, -FIXED(1,0), -FIXED(2,0), FIXED(1,0), FIXED(2,0), {ps_HeartBloodSmall, ps_HeartBloodSmall}},
        {25, -80, 0, -FIXED(1,0), -FIXED(2,0), FIXED(3,0), FIXED(4,0), {ps_HeartBloodSmall, ps_HeartBloodSmall}},
        {0, 0, 0, 0, 0, 0, 0, {0,0}},
    };

    PlaySound(DIGI_HEARTBEAT, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan|v3df_doppler);

    for (hsp = HeartShrap; hsp->lo_jump_speed; hsp++)
    {
        // RIGHT side
        blood = pSpawnSprite(pp, hsp->state[RANDOM_P2(2<<8)>>8], PRI_BACK, 0, 0);
        blood->x = psp->x + hsp->xoff;
        blood->y = psp->y + hsp->yoff;
        blood->xspeed = hsp->lo_xspeed + (RANDOM_RANGE((hsp->hi_xspeed - hsp->lo_xspeed)>>4) << 4);
        SET(blood->flags, PANF_WEAPON_SPRITE);

        blood->scale = 10000 + RANDOM_RANGE(30000 - 10000);

        blood->jump_speed = hsp->lo_jump_speed + (RANDOM_RANGE((hsp->hi_jump_speed + hsp->lo_jump_speed)>>4) << 4);
        DoBeginPanelJump(blood);
    }
}

void
pHeartBlood(PANEL_SPRITEp psp)
{
    int x = FIXED(psp->x, psp->xfract);

    if (TEST(psp->flags, PANF_JUMPING))
    {
        DoPanelJump(psp);
    }
    else if (TEST(psp->flags, PANF_FALLING))
    {
        DoPanelFall(psp);
    }

    x += psp->xspeed;

    psp->xfract = LSW(x);
    psp->x = MSW(x);

    if (psp->x > 320 || psp->x < 0 || psp->y > 200)
    {
        pKillSprite(psp);
        return;
    }
}

int
DoBeginPanelJump(PANEL_SPRITEp psp)
{
#define PANEL_JUMP_GRAVITY FIXED(0,8000)

    SET(psp->flags, PANF_JUMPING);
    RESET(psp->flags, PANF_FALLING);

    // set up individual actor jump gravity
    psp->jump_grav = PANEL_JUMP_GRAVITY;

    DoPanelJump(psp);

    return 0;
}

int
DoPanelJump(PANEL_SPRITEp psp)
{
    int jump_adj;

    int y = FIXED(psp->y, psp->yfract);

    // precalculate jump value to adjust jump speed by
    jump_adj = psp->jump_grav;

    // adjust jump speed by gravity - if jump speed greater than 0 player
    // have started falling
    if ((psp->jump_speed += jump_adj) > 0)
    {
        // Start falling
        DoBeginPanelFall(psp);
        return 0;
    }

    // adjust height by jump speed
    y += psp->jump_speed * synctics;

    psp->yfract = LSW(y);
    psp->y = MSW(y);

    return 0;
}


int
DoBeginPanelFall(PANEL_SPRITEp psp)
{
    SET(psp->flags, PANF_FALLING);
    RESET(psp->flags, PANF_JUMPING);

    psp->jump_grav = PANEL_JUMP_GRAVITY;

    DoPanelFall(psp);

    return 0;
}


int
DoPanelFall(PANEL_SPRITEp psp)
{
    int y = FIXED(psp->y, psp->yfract);

    // adjust jump speed by gravity
    psp->jump_speed += psp->jump_grav;

    // adjust player height by jump speed
    y += psp->jump_speed * synctics;

    psp->yfract = LSW(y);
    psp->y = MSW(y);

    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// GRENADE
//
//////////////////////////////////////////////////////////////////////////////////////////

void pGrenadePresent(PANEL_SPRITEp psp);
void pGrenadeRetract(PANEL_SPRITEp psp);
void pGrenadeAction(PANEL_SPRITEp psp);
void pGrenadeRest(PANEL_SPRITEp psp);
void pGrenadeFire(PANEL_SPRITEp psp);
void pGrenadeHide(PANEL_SPRITEp psp);
void pGrenadeUnHide(PANEL_SPRITEp psp);
void pGrenadeRecoilDown(PANEL_SPRITEp psp);
void pGrenadeRecoilUp(PANEL_SPRITEp psp);
void pGrenadeBobSetup(PANEL_SPRITEp psp);

extern PANEL_STATE ps_GrenadeRecoil[];

#define Grenade_REST_RATE 24
#define Grenade_ACTION_RATE 6

PANEL_STATE ps_PresentGrenade[] =
{
    {ID_GrenadePresent0, Grenade_REST_RATE, pGrenadePresent, &ps_PresentGrenade[0], 0,0,0}
};

PANEL_STATE ps_GrenadeRest[] =
{
    {ID_GrenadePresent0, Grenade_REST_RATE, pGrenadeRest, &ps_GrenadeRest[0], 0,0,0}
};

PANEL_STATE ps_GrenadeHide[] =
{
    {ID_GrenadePresent0, Grenade_REST_RATE, pGrenadeHide, &ps_GrenadeHide[0], 0,0,0}
};

PANEL_STATE ps_GrenadeFire[] =
{
    {ID_GrenadeFire0, Grenade_ACTION_RATE, pGrenadeAction, &ps_GrenadeFire[1], psf_ShadeHalf, 0,0},
    {ID_GrenadeFire1, Grenade_ACTION_RATE, pGrenadeAction, &ps_GrenadeFire[2], psf_ShadeNone, 0,0},
    {ID_GrenadeFire2, Grenade_ACTION_RATE, pGrenadeAction, &ps_GrenadeFire[3], psf_ShadeNone, 0,0},

    {ID_GrenadePresent0, 0, pGrenadeFire, &ps_GrenadeFire[4], psf_QuickCall, 0,0},
    {ID_GrenadePresent0, 3, pGrenadeAction, &ps_GrenadeRecoil[0], 0,0,0}
};

PANEL_STATE ps_GrenadeRecoil[] =
{
    // recoil
    {ID_GrenadePresent0, Grenade_REST_RATE, pGrenadeRecoilDown, &ps_GrenadeRecoil[0], 0,0,0},
    {ID_GrenadePresent0, Grenade_REST_RATE, pGrenadeRecoilUp, &ps_GrenadeRecoil[1], 0,0,0},
    // reload
    {ID_GrenadeReload0, Grenade_REST_RATE/2, pNullAnimator, &ps_GrenadeRecoil[3], 0,0,0},
    {ID_GrenadeReload1, Grenade_REST_RATE/2, pNullAnimator, &ps_GrenadeRecoil[4], 0,0,0},
    // ready to fire again
    {ID_GrenadePresent0, 3, pNullAnimator, &ps_GrenadeRest[0], 0,0,0}
};

PANEL_STATE ps_RetractGrenade[] =
{
    {ID_GrenadePresent0, Grenade_REST_RATE, pGrenadeRetract, &ps_RetractGrenade[0], 0,0,0}
};

#define GRENADE_YOFF 200
#define GRENADE_XOFF (160+20)

void
pGrenadeSetRecoil(PANEL_SPRITEp psp)
{
    psp->vel = 900;
    psp->ang = NORM_ANGLE(-256);
}

void
pGrenadePresentSetup(PANEL_SPRITEp psp)
{
    psp->rotate_ang = 1800;
    psp->y += 34;
    psp->x -= 45;
    psp->ang = 256 + 128;
    psp->vel = 680;
}

void
InitWeaponGrenade(PLAYERp pp)
{
    PANEL_SPRITEp psp;

    if (Prediction)
        return;

    if (!TEST(pp->WpnFlags, BIT(WPN_GRENADE)) ||
//        pp->WpnAmmo[WPN_GRENADE] <= 0 ||
        TEST(pp->Flags, PF_WEAPON_RETRACT))
        return;

    if (!pp->Wpn[WPN_GRENADE])
    {
        psp = pp->Wpn[WPN_GRENADE] = pSpawnSprite(pp, ps_PresentGrenade, PRI_MID, GRENADE_XOFF, GRENADE_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    if (pp->CurWpn == pp->Wpn[WPN_GRENADE])
    {
        return;
    }

    PlayerUpdateWeapon(pp, WPN_GRENADE);

    pp->WpnUziType = 2; // Make uzi's go away!
    RetractCurWpn(pp);

    // Set up the new Weapon variables
    psp = pp->CurWpn = pp->Wpn[WPN_GRENADE];
    psp = pp->CurWpn = pp->Wpn[WPN_GRENADE];
    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = ps_GrenadeFire;
    psp->RetractState = ps_RetractGrenade;
    psp->PresentState = ps_PresentGrenade;
    psp->RestState = ps_GrenadeRest;
    pSetState(psp, psp->PresentState);

    pGrenadePresentSetup(psp);

    PlaySound(DIGI_GRENADE_UP, &pp->posx, &pp->posy, &pp->posz, v3df_follow);

    FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
    FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);
}

void
pGrenadeRecoilDown(PANEL_SPRITEp psp)
{
//    short picnum = psp->picndx;

    int x = FIXED(psp->x, psp->xfract);
    int y = FIXED(psp->y, psp->yfract);

    x += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang + 512)] >> 6;
    y += psp->vel * synctics * (int) -sintable[psp->ang] >> 6;

    psp->xfract = LSW(x);
    psp->x = MSW(x);
    psp->yfract = LSW(y);
    psp->y = MSW(y);

    psp->vel -= 24 * synctics;

    // if (psp->y >= GRENADE_YOFF + tilesiz[picnum].y)
    if (psp->vel < 400)
    {
        // psp->y = GRENADE_YOFF + tilesiz[picnum].y;

        psp->vel = 400;
        psp->ang = NORM_ANGLE(psp->ang + 1024);

        pStatePlusOne(psp);
    }
}

void
pGrenadeRecoilUp(PANEL_SPRITEp psp)
{
    int x = FIXED(psp->x, psp->xfract);
    int y = FIXED(psp->y, psp->yfract);

    x += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang + 512)] >> 6;
    y += psp->vel * synctics * (int) -sintable[psp->ang] >> 6;

    psp->xfract = LSW(x);
    psp->x = MSW(x);
    psp->yfract = LSW(y);
    psp->y = MSW(y);

    psp->vel += 15 * synctics;

    if (psp->y < GRENADE_YOFF)
    {
        psp->y = GRENADE_YOFF;
        psp->x = GRENADE_XOFF;

        pGrenadeSetRecoil(psp);

        pStatePlusOne(psp);
        RESET(psp->flags, PANF_BOB);
    }
}

void
pGrenadePresent(PANEL_SPRITEp psp)
{
    int x = FIXED(psp->x, psp->xfract);
    int y = FIXED(psp->y, psp->yfract);

    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    x += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang + 512)] >> 6;
    y += psp->vel * synctics * (int) -sintable[psp->ang] >> 6;

    psp->xfract = LSW(x);
    psp->x = MSW(x);
    psp->yfract = LSW(y);
    psp->y = MSW(y);

    psp->rotate_ang = NORM_ANGLE(psp->rotate_ang + (6 * synctics));

    if (psp->rotate_ang < 1024)
        psp->rotate_ang = 0;

    if (psp->y < GRENADE_YOFF)
    {
        pGrenadeSetRecoil(psp);
        psp->x = GRENADE_XOFF;
        psp->y = GRENADE_YOFF;
        psp->rotate_ang = 0;
        psp->yorig = psp->y;
        pSetState(psp, psp->RestState);
    }
}

void
pGrenadeBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = 12;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 3;
}

void
pGrenadeHide(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= GRENADE_YOFF + tilesiz[picnum].y)
    {
        psp->y = GRENADE_YOFF + tilesiz[picnum].y;
        psp->x = GRENADE_XOFF;

        pGrenadePresentSetup(psp);

        pWeaponUnHideKeys(psp, psp->PresentState);
    }
}

void
pGrenadeRest(PANEL_SPRITEp psp)
{
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (pWeaponHideKeys(psp, ps_GrenadeHide))
        return;

    pGrenadeBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

            if (!WeaponOK(psp->PlayerP))
                return;

            DoPlayerChooseYell(psp->PlayerP);
            pSetState(psp, psp->ActionState);
        }
    }
    else
        WeaponOK(psp->PlayerP);
}

void
pGrenadeAction(PANEL_SPRITEp psp)
{
    pGrenadeBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));
}


void
pGrenadeFire(PANEL_SPRITEp psp)
{
    int InitGrenade(PLAYERp pp);

    SpawnVis(psp->PlayerP->PlayerSprite, -1, -1, -1, -1, 32);
    InitGrenade(psp->PlayerP);
}

void
pGrenadeRetract(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= GRENADE_YOFF + tilesiz[picnum].y)
    {
        RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
        psp->PlayerP->Wpn[WPN_GRENADE] = NULL;
        pKillSprite(psp);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// MINE
//
//////////////////////////////////////////////////////////////////////////////////////////

void pMinePresent(PANEL_SPRITEp psp);
void pMineRetract(PANEL_SPRITEp psp);
void pMineAction(PANEL_SPRITEp psp);
void pMineRest(PANEL_SPRITEp psp);
void pMineThrow(PANEL_SPRITEp psp);
void pMineLower(PANEL_SPRITEp psp);
void pMineRaise(PANEL_SPRITEp psp);
void pMineHide(PANEL_SPRITEp psp);
void pMineUnHide(PANEL_SPRITEp psp);
void pMineBobSetup(PANEL_SPRITEp psp);
void pMineUpSound(PANEL_SPRITEp psp);

#define Mine_REST_RATE 24
#define Mine_ACTION_RATE 6

PANEL_STATE ps_PresentMine[] =
{
    {ID_MinePresent0, Mine_REST_RATE, pMinePresent, &ps_PresentMine[0], 0,0,0}
};

PANEL_STATE ps_MineRest[] =
{
    {ID_MinePresent0, 36,               pMineRest,      &ps_MineRest[1], 0,0,0},
    {ID_MinePresent0, 0,                pMineUpSound,   &ps_MineRest[2], psf_QuickCall, 0,0},
    {ID_MinePresent1, Mine_REST_RATE,   pMineRest,      &ps_MineRest[2], 0,0,0},
};

PANEL_STATE ps_MineHide[] =
{
    {ID_MinePresent0, Mine_REST_RATE, pMineHide, &ps_MineHide[0], 0,0,0}
};

PANEL_STATE ps_MineThrow[] =
{
    {ID_MineThrow0,   3,                pNullAnimator,  &ps_MineThrow[1], 0,0,0},
    {ID_MineThrow0, Mine_ACTION_RATE, pMineThrow,       &ps_MineThrow[2],psf_QuickCall, 0,0},
    {ID_MineThrow0, Mine_ACTION_RATE, pMineLower,       &ps_MineThrow[2], 0,0,0},
    {ID_MineThrow0, Mine_ACTION_RATE*5, pNullAnimator,  &ps_MineThrow[4], 0,0,0},
    {ID_MinePresent0, Mine_ACTION_RATE, pMineRaise,     &ps_MineThrow[4], 0,0,0},
    {ID_MinePresent0, Mine_ACTION_RATE, pNullAnimator,  &ps_MineThrow[6], 0,0,0},
    {ID_MinePresent0, 3, pMineAction, &ps_MineRest[0], 0,0,0}
};

PANEL_STATE ps_RetractMine[] =
{
    {ID_MinePresent0, Mine_REST_RATE, pMineRetract, &ps_RetractMine[0], 0,0,0}
};

#define MINE_YOFF 200
//#define MINE_XOFF (160+20)
#define MINE_XOFF (160+50)

void
InitWeaponMine(PLAYERp pp)
{
    PANEL_SPRITEp psp;

    if (Prediction)
        return;

    if (pp->WpnAmmo[WPN_MINE] <= 0)
        PutStringInfo(pp,"Out of Sticky Bombs!");

    if (!TEST(pp->WpnFlags, BIT(WPN_MINE)) ||
        pp->WpnAmmo[WPN_MINE] <= 0 ||
        TEST(pp->Flags, PF_WEAPON_RETRACT))
        return;

    if (!pp->Wpn[WPN_MINE])
    {
        psp = pp->Wpn[WPN_MINE] = pSpawnSprite(pp, ps_PresentMine, PRI_MID, MINE_XOFF, MINE_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    if (pp->CurWpn == pp->Wpn[WPN_MINE])
    {
        return;
    }

    PlayerUpdateWeapon(pp, WPN_MINE);

    pp->WpnUziType = 2; // Make uzi's go away!
    RetractCurWpn(pp);

    // Set up the new Weapon variables
    psp = pp->CurWpn = pp->Wpn[WPN_MINE];
    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = ps_MineThrow;
    psp->RetractState = ps_RetractMine;
    psp->PresentState = ps_PresentMine;
    psp->RestState = ps_MineRest;
    pSetState(psp, psp->PresentState);

    PlaySound(DIGI_PULL, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan);

    FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
    FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);
}

void
pMineUpSound(PANEL_SPRITEp psp)
{
    PLAYERp pp = psp->PlayerP;

    PlaySound(DIGI_MINE_UP, &pp->posx, &pp->posy, &pp->posz, v3df_follow);
}

void
pMineLower(PANEL_SPRITEp psp)
{
    psp->y += 4 * synctics;

    if (psp->y > MINE_YOFF + tilesiz[psp->picndx].y)
    {
        if (!WeaponOK(psp->PlayerP))
            return;
        psp->y = MINE_YOFF + tilesiz[psp->picndx].y;
        pStatePlusOne(psp);
    }
}

void
pMineRaise(PANEL_SPRITEp psp)
{
    psp->y -= 4 * synctics;

    if (psp->y < MINE_YOFF)
    {
        psp->y = MINE_YOFF;
        pStatePlusOne(psp);
    }
}

void
pMinePresent(PANEL_SPRITEp psp)
{
    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    psp->y -= 3 * synctics;

    if (psp->y < MINE_YOFF)
    {
        psp->y = MINE_YOFF;
        psp->rotate_ang = 0;
        psp->yorig = psp->y;
        pSetState(psp, psp->RestState);
    }
}

void
pMineBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = 12;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 3;
}

void
pMineHide(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= MINE_YOFF + tilesiz[picnum].y)
    {
        psp->y = MINE_YOFF + tilesiz[picnum].y;
        psp->x = MINE_XOFF;

        pWeaponUnHideKeys(psp, psp->PresentState);
    }
}

void
pMineRest(PANEL_SPRITEp psp)
{
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (pWeaponHideKeys(psp, ps_MineHide))
        return;

    pMineBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

//            if (!WeaponOK(psp->PlayerP))
//                return;

            DoPlayerChooseYell(psp->PlayerP);
            pSetState(psp, psp->ActionState);
        }
    }
    else
        WeaponOK(psp->PlayerP);
}

void
pMineAction(PANEL_SPRITEp psp)
{
    pMineBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));
}


void
pMineThrow(PANEL_SPRITEp psp)
{
    int InitMine(PLAYERp pp);

    InitMine(psp->PlayerP);
}

void
pMineRetract(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= MINE_YOFF + tilesiz[picnum].y)
    {
        RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
        psp->PlayerP->Wpn[WPN_MINE] = NULL;
        pKillSprite(psp);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
//
// CHOP STICKS
//
//////////////////////////////////////////////////////////////////////////////////////////

void pChopsUp(PANEL_SPRITEp psp);
void pChopsDown(PANEL_SPRITEp psp);
void pChopsDownSlow(PANEL_SPRITEp psp);
void pChopsWait(PANEL_SPRITEp psp);
void pChopsShake(PANEL_SPRITEp psp);
void pChopsClick(PANEL_SPRITEp psp);

void pChopsRetract(PANEL_SPRITEp psp);

#define Chops_REST_RATE 24
#define Chops_ACTION_RATE 6

#define ID_ChopsRest  2000
#define ID_ChopsOpen  2001
#define ID_ChopsClose 2002

PANEL_STATE ps_ChopsAttack1[] =
{
    {ID_ChopsRest,  Chops_REST_RATE*3,  pNullAnimator,  &ps_ChopsAttack1[1], 0,0,0},
    {ID_ChopsRest,  Chops_REST_RATE,    pChopsUp,       &ps_ChopsAttack1[1], 0,0,0},
    {ID_ChopsOpen,  Chops_REST_RATE/3,  pNullAnimator,  &ps_ChopsAttack1[3], 0,0,0},
    {ID_ChopsClose, 0,                  pChopsClick,    &ps_ChopsAttack1[4], psf_QuickCall, 0,0},
    {ID_ChopsClose, Chops_REST_RATE/3,  pNullAnimator,  &ps_ChopsAttack1[5], 0,0,0},
    {ID_ChopsClose, Chops_REST_RATE,    pChopsDown,     &ps_ChopsAttack1[5], 0,0,0},
};

PANEL_STATE ps_ChopsAttack2[] =
{
    {ID_ChopsOpen,  Chops_REST_RATE*3,  pNullAnimator,  &ps_ChopsAttack2[1], 0,0,0},
    {ID_ChopsOpen,  Chops_REST_RATE,    pChopsUp,       &ps_ChopsAttack2[1], 0,0,0},
    {ID_ChopsOpen,  0,                  pChopsClick,    &ps_ChopsAttack2[3], psf_QuickCall, 0,0},
    {ID_ChopsOpen,  8,                  pNullAnimator,  &ps_ChopsAttack2[4], 0,0,0},
    {ID_ChopsRest,  Chops_REST_RATE,    pNullAnimator,  &ps_ChopsAttack2[5], 0,0,0},
    {ID_ChopsRest,  Chops_REST_RATE,    pChopsDown,     &ps_ChopsAttack2[5], 0,0,0},
};

PANEL_STATE ps_ChopsAttack3[] =
{
    {ID_ChopsOpen,  Chops_REST_RATE*3,  pNullAnimator,  &ps_ChopsAttack3[1], 0,0,0},
    {ID_ChopsOpen,  Chops_REST_RATE,    pChopsUp,       &ps_ChopsAttack3[1], 0,0,0},

    {ID_ChopsRest,  0,                  pNullAnimator,  &ps_ChopsAttack3[3], 0,0,0},
    {ID_ChopsRest,  0,                  pChopsClick,    &ps_ChopsAttack3[4], psf_QuickCall, 0,0},

    {ID_ChopsRest,  Chops_REST_RATE,    pNullAnimator,  &ps_ChopsAttack3[5], 0,0,0},
    {ID_ChopsRest,  24,                 pNullAnimator,  &ps_ChopsAttack3[6], 0,0,0},
    {ID_ChopsOpen,  16,                 pNullAnimator,  &ps_ChopsAttack3[7], 0,0,0},

    {ID_ChopsRest,  0,                  pChopsClick,    &ps_ChopsAttack3[8], psf_QuickCall, 0,0},
    {ID_ChopsRest,  16,                 pNullAnimator,  &ps_ChopsAttack3[9], 0,0,0},
    {ID_ChopsOpen,  16,                 pNullAnimator,  &ps_ChopsAttack3[10], 0,0,0},

    {ID_ChopsOpen,  8,                  pChopsDownSlow, &ps_ChopsAttack3[11], 0,0,0},
    {ID_ChopsRest,  10,                 pChopsDownSlow, &ps_ChopsAttack3[12], 0,0,0},
    {ID_ChopsRest,  0,                  pChopsClick,    &ps_ChopsAttack3[13], psf_QuickCall, 0,0},
    {ID_ChopsRest,  10,                 pChopsDownSlow, &ps_ChopsAttack3[14], 0,0,0},
    {ID_ChopsOpen,  10,                 pChopsDownSlow, &ps_ChopsAttack3[11], 0,0,0},
};

PANEL_STATE ps_ChopsAttack4[] =
{
    {ID_ChopsOpen,  Chops_REST_RATE*3,  pNullAnimator,  &ps_ChopsAttack4[1], 0,0,0},
    {ID_ChopsOpen,  Chops_REST_RATE,    pChopsUp,       &ps_ChopsAttack4[1], 0,0,0},

    {ID_ChopsOpen,  0,                  pChopsClick,    &ps_ChopsAttack4[3], psf_QuickCall, 0,0},
    {ID_ChopsOpen,  8,                  pNullAnimator,  &ps_ChopsAttack4[4], 0,0,0},

    {ID_ChopsRest,  Chops_REST_RATE,    pNullAnimator,  &ps_ChopsAttack4[5], 0,0,0},
    {ID_ChopsRest,  Chops_REST_RATE*4,  pChopsShake,    &ps_ChopsAttack4[6], 0,0,0},
    {ID_ChopsRest,  Chops_REST_RATE,    pChopsDown,     &ps_ChopsAttack4[6], 0,0,0},
};

PANEL_STATEp psp_ChopsAttack[] = {ps_ChopsAttack1, ps_ChopsAttack2, ps_ChopsAttack3, ps_ChopsAttack4};

PANEL_STATE ps_ChopsWait[] =
{
    {ID_ChopsRest,  Chops_REST_RATE, pChopsWait,     &ps_ChopsWait[0], 0,0,0},
};

PANEL_STATE ps_ChopsRetract[] =
{
    {ID_ChopsRest, Chops_REST_RATE, pChopsRetract, &ps_ChopsRetract[0], 0,0,0}
};

#define CHOPS_YOFF 200
#define CHOPS_XOFF (160+20)

void
InitChops(PLAYERp pp)
{
    PANEL_SPRITEp psp;

    if (!pp->Chops)
    {
        psp = pp->Chops = pSpawnSprite(pp, ps_ChopsAttack1, PRI_MID, CHOPS_XOFF, CHOPS_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    if (Prediction)
        return;

    // Set up the new Weapon variables
    psp = pp->Chops;

    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = ps_ChopsAttack1;
    psp->PresentState = ps_ChopsAttack1;
    psp->RetractState = ps_ChopsRetract;
    psp->RestState = ps_ChopsAttack1;

    PlaySound(DIGI_BUZZZ,&psp->PlayerP->posx,&psp->PlayerP->posy,&psp->PlayerP->posz,v3df_none);

    if (RANDOM_RANGE(1000) > 750)
        PlayerSound(DIGI_MRFLY,&psp->PlayerP->posx,&psp->PlayerP->posy,&psp->PlayerP->posz,v3df_follow|v3df_dontpan,psp->PlayerP);

}

void
pChopsClick(PANEL_SPRITEp psp)
{
    int16_t rnd_rng;
    PlaySound(DIGI_CHOP_CLICK,&psp->PlayerP->posx,&psp->PlayerP->posy,&psp->PlayerP->posz,v3df_none);

    rnd_rng = RANDOM_RANGE(1000);
    if (rnd_rng > 950)
        PlayerSound(DIGI_SEARCHWALL,&psp->PlayerP->posx,&psp->PlayerP->posy,&psp->PlayerP->posz,v3df_follow|v3df_dontpan,psp->PlayerP);
    else if (rnd_rng > 900)
        PlayerSound(DIGI_EVADEFOREVER,&psp->PlayerP->posx,&psp->PlayerP->posy,&psp->PlayerP->posz,v3df_follow|v3df_dontpan,psp->PlayerP);
    else if (rnd_rng > 800)
        PlayerSound(DIGI_SHISEISI,&psp->PlayerP->posx,&psp->PlayerP->posy,&psp->PlayerP->posz,v3df_follow|v3df_dontpan,psp->PlayerP);
}

void
pChopsUp(PANEL_SPRITEp psp)
{
    psp->y -= 3 * synctics;

    if (psp->y < CHOPS_YOFF)
    {
        psp->y = CHOPS_YOFF;
        pStatePlusOne(psp);
    }
}

void
pChopsDown(PANEL_SPRITEp psp)
{
    psp->y += 3 * synctics;

    if (psp->y > CHOPS_YOFF+110)
    {
        psp->y = CHOPS_YOFF+110;
        pSetState(psp, ps_ChopsWait);
    }
}

void
pChopsDownSlow(PANEL_SPRITEp psp)
{
    psp->y += 1 * synctics;

    if (psp->y > CHOPS_YOFF+110)
    {
        psp->y = CHOPS_YOFF+110;
        pSetState(psp, ps_ChopsWait);
    }
}

void
pChopsShake(PANEL_SPRITEp psp)
{
    psp->x += (RANDOM_P2(4<<8)>>8) - 2;
    psp->y += (RANDOM_P2(4<<8)>>8) - 2;

    if (psp->y < CHOPS_YOFF)
    {
        psp->y = CHOPS_YOFF;
    }
}

void
pChopsWait(PANEL_SPRITEp psp)
{
    //if (!GamePaused && RANDOM_P2(1024) < 10)
    if (RANDOM_P2(1024) < 10)
    {
        // random x position
        // do a random attack here
        psp->x = CHOPS_XOFF + (RANDOM_P2(128) - 64);

        PlaySound(DIGI_BUZZZ,&psp->PlayerP->posx,&psp->PlayerP->posy,&psp->PlayerP->posz,v3df_none);
        pSetState(psp, psp_ChopsAttack[RANDOM_RANGE(SIZ(psp_ChopsAttack))]);
    }
}

void
ChopsSetRetract(PLAYERp pp)
{
    pSetState(pp->Chops, pp->Chops->RetractState);
}

void
pChopsRetract(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 6 * synctics;

    if (psp->y >= CHOPS_YOFF + tilesiz[picnum].y)
    {
        if (RANDOM_RANGE(1000) > 800)
            PlayerSound(DIGI_GETTINGSTIFF,&psp->PlayerP->posx,&psp->PlayerP->posy,&psp->PlayerP->posz,v3df_follow|v3df_dontpan,psp->PlayerP);
        psp->PlayerP->Chops = NULL;
        pKillSprite(psp);
    }
}


//////////////////////////////////////////////////////////////////////////////////////////
//
// FIST
//
// KungFu Move Numbers: (Used to calculate damages, etc....
// 0: Normal Punch
// 1: Palm Heel Strike
// 2: Kick
// 3: Block
// 4: FISTS OF FURY - (Flury of fast punches) DUCK, then BLOCK and ATTACK
// 5: CHI STRIKE - (Devastating Palm Heel)    DUCK 2 secs, then ATTACK
// 6: DEATH TOUCH - (Make enemy bleed and does alot of damage) JUMP, DUCK, BLOCK, ATTACK
// 7: FIREBALL - BLOCK 4 secs, then ATTACK
// 8: HAMMER KICK - Run forward while holding BLOCK, JUMP, ATTACK
// 9: CYCLONE KICK - (Spin 5 circles with leg out to hit multiples)
//       180 KEY, BLOCK, JUMP, ATTACK
// 10: DESPERATION - Finish Move (Cling to enemy like sticky bomb and beat the crap out of him)
//       Player is invincible until he unattaches
//       LOW HEALTH < 20, TOUCH ENEMY, BLOCK then ATTACK
// 11: SUPER NOVA - Become a human nuclear bomb!
//       SUPER SECRET MOVE
//       >150 Health + >50 Armor + Shadow Spell ACTIVE, DUCK + BLOCK 3 secs, JUMP, ATTACK
//       Player will die, so it's only useful if you get more than 1 frag in the deal.
// 12: TOTAL INVISIBILITY - Worse than shadow spell, you go 100% invisible for 10 secs!
//       SUPER SECRET MOVE
//       >180 Health + Shadow Spell ACTIVE, BLOCK 4 secs, JUMP, ATTACK
// 13: FREEZE - Turn all enemies in your view to ice for 10 secs
//       SUPER SECRET MOVE
//       100 Armor + Shadow Spell ACTIVE, BLOCK 5 secs, DUCK, ATTACK
//
//////////////////////////////////////////////////////////////////////////////////////////
#if 1
static short FistAngTable[] =
{
    82,
    168,
    256+64
};
#else
static short FistAngTable[] =
{
    256+128,
};
#endif

short FistAng = 0;

void
FistBlur(PANEL_SPRITEp psp)
{
    psp->kill_tics -= synctics;
    if (psp->kill_tics <= 0)
    {
        pKillSprite(psp);
        return;
    }
    else if (psp->kill_tics <= 6)
    {
        SET(psp->flags, PANF_TRANS_FLIP);
    }

    psp->shade += 10;
    // change sprites priority
    REMOVE(psp);
    psp->priority--;
    InsertPanelSprite(psp->PlayerP, psp);
}

void
SpawnFistBlur(PANEL_SPRITEp psp)
{
    PANEL_SPRITEp nsp;
    //PICITEMp pip;

    if (psp->PlayerP->FistAng > 200)
        return;

    nsp = pSpawnSprite(psp->PlayerP, NULL, PRI_BACK, psp->x, psp->y);

    SET(nsp->flags, PANF_WEAPON_SPRITE);
    nsp->xfract = psp->xfract;
    nsp->yfract = psp->yfract;
    nsp->ang = psp->ang;
    nsp->vel = psp->vel;
    nsp->PanelSpriteFunc = FistBlur;
    nsp->kill_tics = 9;
    nsp->shade = psp->shade + 10;

    nsp->picndx = -1;
    nsp->picnum = psp->picndx;

    if (TEST(psp->State->flags, psf_Xflip))
        SET(nsp->flags, PANF_XFLIP);

    nsp->rotate_ang = psp->rotate_ang;
    nsp->scale = psp->scale;

    SET(nsp->flags, PANF_TRANSLUCENT);
}

void pFistPresent(PANEL_SPRITEp psp);
void pFistRetract(PANEL_SPRITEp psp);
void pFistAction(PANEL_SPRITEp psp);
void pFistRest(PANEL_SPRITEp psp);
void pFistAttack(PANEL_SPRITEp psp);
void pFistSlide(PANEL_SPRITEp psp);
void pFistSlideDown(PANEL_SPRITEp psp);
void pFistHide(PANEL_SPRITEp psp);
void pFistUnHide(PANEL_SPRITEp psp);

void pFistSlideR(PANEL_SPRITEp psp);
void pFistSlideDownR(PANEL_SPRITEp psp);
void pFistBlock(PANEL_SPRITEp psp);

extern PANEL_STATE ps_FistSwing[];
extern PANEL_STATE ps_ReloadFist[];

#define Fist_BEAT_RATE 16
#define Fist_ACTION_RATE 5

PANEL_STATE ps_PresentFist[] =
{
    {ID_FistPresent0, Fist_BEAT_RATE, pFistPresent, &ps_PresentFist[0], 0,0,0}
};

PANEL_STATE ps_FistRest[] =
{
    {ID_FistPresent0, Fist_BEAT_RATE, pFistRest, &ps_FistRest[0], 0,0,0}
};

PANEL_STATE ps_FistHide[] =
{
    {ID_FistPresent0, Fist_BEAT_RATE, pFistHide, &ps_FistHide[0], 0,0,0}
};

PANEL_STATE ps_PresentFist2[] =
{
    {ID_Fist2Present0, Fist_BEAT_RATE, pFistPresent, &ps_PresentFist2[0], 0,0,0}
};

PANEL_STATE ps_Fist2Rest[] =
{
    {ID_Fist2Present0, Fist_BEAT_RATE, pFistRest, &ps_Fist2Rest[0], 0,0,0}
};

PANEL_STATE ps_Fist2Hide[] =
{
    {ID_Fist2Present0, Fist_BEAT_RATE, pFistHide, &ps_Fist2Hide[0], 0,0,0}
};

#if 0
PANEL_STATE ps_PresentFist3[] =
{
    {ID_Fist3Present0, Fist_BEAT_RATE, pFistPresent, &ps_PresentFist3[0]}
};

PANEL_STATE ps_Fist3Rest[] =
{
    {ID_Fist3Present0, Fist_BEAT_RATE, pFistRest, &ps_Fist3Rest[0]}
};

PANEL_STATE ps_Fist3Hide[] =
{
    {ID_Fist3Present0, Fist_BEAT_RATE, pFistHide, &ps_Fist3Hide[0]}
};
#endif

#define FIST_PAUSE_TICS 6
#define FIST_SLIDE_TICS 6
#define FIST_MID_SLIDE_TICS 16

PANEL_STATE ps_FistSwing[] =
{
    {ID_FistSwing0, FIST_PAUSE_TICS,                    pNullAnimator,      &ps_FistSwing[1], 0,0,0},
    {ID_FistSwing1, FIST_SLIDE_TICS, /* start slide */ pNullAnimator,        &ps_FistSwing[2], 0,0,0},
    {ID_FistSwing2, 0, /* damage */ pFistAttack,       &ps_FistSwing[3], psf_QuickCall, 0,0},
    {ID_FistSwing2, FIST_MID_SLIDE_TICS, /* mid slide */ pFistSlideDown,    &ps_FistSwing[4], 0,0,0},

    {ID_FistSwing2, 2, /* end slide */ pFistSlideDown,    &ps_FistSwing[4], 0,0,0},

    {ID_FistSwing1, FIST_SLIDE_TICS, /* start slide */ pFistSlideR,       &ps_FistSwing[6], psf_Xflip, 0,0},
    {ID_FistSwing2, 0, /* damage */ pFistAttack,       &ps_FistSwing[7], psf_QuickCall|psf_Xflip, 0,0},
    {ID_FistSwing2, FIST_MID_SLIDE_TICS, /* mid slide */ pFistSlideDownR,   &ps_FistSwing[8], psf_Xflip, 0,0},

    {ID_FistSwing2, 2, /* end slide */ pFistSlideDownR,   &ps_FistSwing[8], psf_Xflip, 0,0},
    {ID_FistSwing2, 2, /* end slide */ pNullAnimator,      &ps_FistSwing[1], psf_Xflip, 0,0},
};

PANEL_STATE ps_Fist2Swing[] =
{
    {4058, FIST_PAUSE_TICS,                    pNullAnimator,      &ps_Fist2Swing[1], 0,0,0},
    {4058, FIST_SLIDE_TICS, /* start slide */ pNullAnimator,      &ps_Fist2Swing[2], 0,0,0},
    {4058, 0, /* damage */ pFistBlock,         &ps_Fist2Swing[0], psf_QuickCall, 0,0},
    {4058, FIST_MID_SLIDE_TICS+5, /* mid slide */ pFistSlideDown,     &ps_Fist2Swing[4], 0,0,0},

    {4058, 2, /* end slide */ pFistSlideDown,    &ps_Fist2Swing[4], 0,0,0},
};

PANEL_STATE ps_Fist3Swing[] =
{
    {ID_Fist3Swing0, FIST_PAUSE_TICS+25,                    pNullAnimator,      &ps_Fist3Swing[1], 0,0,0},
    {ID_Fist3Swing1, 0, /* damage */ pFistAttack,       &ps_Fist3Swing[2], psf_QuickCall, 0,0},
    {ID_Fist3Swing2, FIST_PAUSE_TICS+10,                    pNullAnimator,      &ps_Fist3Swing[3], 0,0,0},
    {ID_Fist3Swing2, FIST_MID_SLIDE_TICS+3, /* mid slide */ pFistSlideDown,    &ps_Fist3Swing[4], 0,0,0},

    {ID_Fist3Swing2, 8, /* end slide */ pFistSlideDown,    &ps_Fist3Swing[4], 0,0,0},

    {ID_Fist3Swing1, FIST_SLIDE_TICS+20, /* start slide */ pFistSlideR,       &ps_Fist3Swing[6], psf_Xflip, 0,0},
    {ID_Fist3Swing2, 0, /* damage */ pFistAttack,       &ps_Fist3Swing[7], psf_QuickCall|psf_Xflip, 0,0},
    {ID_Fist3Swing2, FIST_MID_SLIDE_TICS+3, /* mid slide */ pFistSlideDownR,   &ps_Fist3Swing[8], psf_Xflip, 0,0},

    {ID_Fist3Swing2, 8, /* end slide */ pFistSlideDownR,   &ps_Fist3Swing[8], psf_Xflip, 0,0},
    {ID_Fist3Swing2, 8, /* end slide */ pNullAnimator,      &ps_Fist3Swing[1], psf_Xflip, 0,0},
};

#define KICK_PAUSE_TICS 40
#define KICK_SLIDE_TICS 30
#define KICK_MID_SLIDE_TICS 20

PANEL_STATE ps_Kick[] =
{
    {ID_Kick0, KICK_PAUSE_TICS,                    pNullAnimator,     &ps_Kick[1], 0,0,0},
    {ID_Kick1, 0, /* damage */ pFistAttack,       &ps_Kick[2], psf_QuickCall, 0,0},
    {ID_Kick1, KICK_SLIDE_TICS, /* start slide */ pNullAnimator,     &ps_Kick[3], 0,0,0},
    {ID_Kick1, KICK_MID_SLIDE_TICS, /* mid slide */ pFistSlideDown,    &ps_Kick[4], 0,0,0},

    {ID_Kick1, 30, /* end slide */ pFistSlideDown,    &ps_Kick[4], 0,0,0},

    {ID_Kick0, KICK_SLIDE_TICS, /* start slide */ pNullAnimator,     &ps_Kick[6], psf_Xflip, 0,0},
    {ID_Kick1, 0, /* damage */ pFistAttack,       &ps_Kick[7], psf_QuickCall|psf_Xflip, 0,0},
    {ID_Kick1, KICK_MID_SLIDE_TICS,/* mid slide */ pFistSlideDownR,   &ps_Kick[8], psf_Xflip, 0, 0},

    {ID_Kick1, 30, /* end slide */ pFistSlideDownR,   &ps_Kick[8], psf_Xflip, 0,0},
    {ID_Kick1, 30, /* end slide */ pNullAnimator,     &ps_Kick[1], psf_Xflip, 0,0},
};

PANEL_STATE ps_RetractFist[] =
{
    {ID_FistPresent0, Fist_BEAT_RATE, pFistRetract, &ps_RetractFist[0], 0,0,0}
};

#define FIST_SWAY_AMT 12

// left swing
#define FIST_XOFF (290 + FIST_SWAY_AMT)
#define FIST_YOFF 200

// right swing
#define FISTR_XOFF (0 - 80)

#define FIST_VEL 3000
#define FIST_POWER_VEL 3000

void
InitWeaponFist(PLAYERp pp)
{
    PANEL_SPRITEp psp;
    short rnd_num;


    if (Prediction)
        return;

    if (!TEST(pp->WpnFlags, BIT(WPN_FIST)) ||
        //pp->WpnAmmo[WPN_FIST] <= 0 ||
        TEST(pp->Flags, PF_WEAPON_RETRACT))
    {
        pp->WpnFirstType = WPN_SWORD;
        InitWeaponSword(pp);
        return;
    }

    if (!pp->Wpn[WPN_FIST])
    {
        psp = pp->Wpn[WPN_FIST] = pSpawnSprite(pp, ps_PresentFist, PRI_MID, FIST_XOFF, FIST_YOFF);
        psp->y += tilesiz[psp->picndx].y;
    }

    if (pp->CurWpn == pp->Wpn[WPN_FIST])
    {
        return;
    }

    PlayerUpdateWeapon(pp, WPN_FIST);

    pp->WpnUziType = 2; // Make uzi's go away!
    RetractCurWpn(pp);

    // Set up the new Weapon variables
    psp = pp->CurWpn = pp->Wpn[WPN_FIST];
    SET(psp->flags, PANF_WEAPON_SPRITE);
    psp->ActionState = ps_FistSwing;
    psp->RetractState = ps_RetractFist;
    psp->PresentState = ps_PresentFist;
    psp->RestState = ps_FistRest;
    pSetState(psp, psp->PresentState);

    pp->WpnKungFuMove = 0; // Set to default strike

    rnd_num = RANDOM_P2(1024);
    if (rnd_num > 900)
        PlaySound(DIGI_TAUNTAI2, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan);
    else if (rnd_num > 800)
        PlaySound(DIGI_PLAYERYELL1, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan);
    else if (rnd_num > 700)
        PlaySound(DIGI_PLAYERYELL2, &pp->posx, &pp->posy, &pp->posz, v3df_follow|v3df_dontpan);

    FLAG_KEY_RELEASE(psp->PlayerP, SK_SHOOT);
    FLAG_KEY_RESET(psp->PlayerP, SK_SHOOT);
}


void
pFistPresent(PANEL_SPRITEp psp)
{
    int rnd;

    if (TEST(psp->PlayerP->Flags, PF_WEAPON_RETRACT))
        return;

    psp->y -= 3 * synctics;

    if (psp->y < FIST_YOFF)
    {
        psp->y = FIST_YOFF;
        psp->yorig = psp->y;

        rnd = RANDOM_RANGE(1000);
        if (rnd > 500)
        {
            psp->PresentState = ps_PresentFist;
            psp->RestState = ps_FistRest;
        }
        else
        {
            psp->PresentState = ps_PresentFist2;
            psp->RestState = ps_Fist2Rest;
        }
        pSetState(psp, psp->RestState);
    }
}

//
// LEFT SWING
//

void
pFistSlide(PANEL_SPRITEp psp)
{
//    int nx;
    int ny;
    short vel_adj;

    //nx = FIXED(psp->x, psp->xfract);
    ny = FIXED(psp->y, psp->yfract);

    SpawnFistBlur(psp);
    vel_adj = 68;

    //nx += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang)] >> 6;
    ny += psp->vel * synctics * (int) -sintable[psp->ang] >> 6;

    //psp->xfract = LSW(nx);
    //psp->x = MSW(nx);
    psp->yfract = LSW(ny);
    psp->y = MSW(ny);

    psp->vel += vel_adj * synctics;
}

void
pFistSlideDown(PANEL_SPRITEp psp)
{
    int nx, ny;
    short vel, vel_adj;

    nx = FIXED(psp->x, psp->xfract);
    ny = FIXED(psp->y, psp->yfract);

    SpawnFistBlur(psp);
    vel_adj = 48;
    vel = 3500;

    if (psp->ActionState == ps_Kick || psp->PlayerP->WpnKungFuMove == 3)
        ny += (psp->vel * synctics * (int) -sintable[NORM_ANGLE(FistAng + psp->ang + psp->PlayerP->FistAng)] >> 6);
    else
    {
        nx -= psp->vel * synctics * (int) sintable[NORM_ANGLE(FistAng + psp->ang + psp->PlayerP->FistAng)] >> 6;
        ny += 3*(psp->vel * synctics * (int) -sintable[NORM_ANGLE(FistAng + psp->ang + psp->PlayerP->FistAng)] >> 6);
    }

    psp->xfract = LSW(nx);
    psp->x = MSW(nx);
    psp->yfract = LSW(ny);
    psp->y = MSW(ny);

    psp->vel += vel_adj * synctics;

    if (psp->y > 440)
    {
        // if still holding down the fire key - continue swinging
        if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT))
        {
            if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT))
            {
                DoPlayerChooseYell(psp->PlayerP);

                if (RANDOM_RANGE(1000) > 500)
                {
                    //if(RANDOM_RANGE(1000) > 300)
                    //    {
                    psp->ActionState = ps_FistSwing;
                    psp->PlayerP->WpnKungFuMove = 0;
                    //    } else
                    //    {
                    //    psp->ActionState = ps_Fist3Swing;
                    //    psp->PlayerP->WpnKungFuMove = 1;
                    //    }
                    pSetState(psp, psp->ActionState);

                    psp->x = FIST_XOFF;
                    psp->y = FIST_YOFF;
                    psp->yorig = psp->y;
                    psp->PlayerP->FistAng = FistAngTable[RANDOM_RANGE(SIZ(FistAngTable))];
                    psp->ang = 1024;
                    psp->vel = vel;
                    DoPlayerSpriteThrow(psp->PlayerP);
                    return;
                }
                else
                {
                    //if (RANDOM_RANGE(1000) > 300)
                    //    {
                    pSetState(psp, ps_FistSwing+(psp->State - psp->ActionState)+1);
                    psp->ActionState = ps_FistSwing;
                    psp->PlayerP->WpnKungFuMove = 0;
                    //pStatePlusOne(psp);
                    //    }else
                    //    {
                    //if (RANDOM_RANGE(1000) > 400)
                    //    {
                    //    pSetState(psp, ps_Fist3Swing+(psp->State - psp->ActionState)+1);
                    //    psp->ActionState = ps_Fist3Swing;
                    //    psp->PlayerP->WpnKungFuMove = 1;
                    //    } else
                    //    {
                    //    pSetState(psp, ps_Kick+(psp->State - psp->ActionState)+1);
                    //    psp->ActionState = ps_Kick;
                    //    psp->PlayerP->WpnKungFuMove = 2;
                    //    }
                    //    }
                }

                psp->x = FISTR_XOFF+100;
                psp->y = FIST_YOFF;
                psp->yorig = psp->y;
                psp->ang = 1024;
                psp->PlayerP->FistAng = FistAngTable[RANDOM_RANGE(SIZ(FistAngTable))];
                psp->vel = vel;
                DoPlayerSpriteThrow(psp->PlayerP);
                return;
            }
        }

        // NOT still holding down the fire key - stop swinging
        pSetState(psp, psp->PresentState);
        psp->x = FIST_XOFF;
        psp->y = FIST_YOFF;
        psp->y += tilesiz[psp->picndx].y;
        psp->yorig = psp->y;
    }
}

//
// RIGHT SWING
//

void
pFistSlideR(PANEL_SPRITEp psp)
{
//    int nx
    int ny;
    short vel_adj;

    //nx = FIXED(psp->x, psp->xfract);
    ny = FIXED(psp->y, psp->yfract);

    SpawnFistBlur(psp);
    vel_adj = 68;

    //nx += psp->vel * synctics * (int) sintable[NORM_ANGLE(psp->ang)] >> 6;
    ny += psp->vel * synctics * (int) -sintable[NORM_ANGLE(psp->ang + 1024)] >> 6;

    //psp->xfract = LSW(nx);
    //psp->x = MSW(nx);
    psp->yfract = LSW(ny);
    psp->y = MSW(ny);

    psp->vel += vel_adj * synctics;
}

void
pFistSlideDownR(PANEL_SPRITEp psp)
{
    int nx, ny;
    short vel, vel_adj;

    nx = FIXED(psp->x, psp->xfract);
    ny = FIXED(psp->y, psp->yfract);

    SpawnFistBlur(psp);
    vel_adj = 48;
    vel = 3500;

    if (psp->ActionState == ps_Kick || psp->PlayerP->WpnKungFuMove == 3)
        ny += (psp->vel * synctics * (int) -sintable[NORM_ANGLE(FistAng + psp->ang + psp->PlayerP->FistAng)] >> 6);
    else
    {
        nx -= psp->vel * synctics * (int) sintable[NORM_ANGLE(FistAng + psp->ang + psp->PlayerP->FistAng)] >> 6;
        ny += 3*(psp->vel * synctics * (int) -sintable[NORM_ANGLE(FistAng + psp->ang + psp->PlayerP->FistAng)] >> 6);
    }

    psp->xfract = LSW(nx);
    psp->x = MSW(nx);
    psp->yfract = LSW(ny);
    psp->y = MSW(ny);

    psp->vel += vel_adj * synctics;

    if (psp->y > 440)
    {
        // if still holding down the fire key - continue swinging
        if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT))
        {
            if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT))
            {
                DoPlayerChooseYell(psp->PlayerP);

                if (RANDOM_RANGE(1000) > 500)
                {
                    //if(RANDOM_RANGE(1000) > 300)
                    //    {
                    psp->ActionState = ps_FistSwing+5;
                    psp->PlayerP->WpnKungFuMove = 0;
                    //     } else
                    //     {
                    //     psp->ActionState = ps_Fist3Swing+5;
                    //     psp->PlayerP->WpnKungFuMove = 1;
                    //     }
                    pSetState(psp, psp->ActionState);

                    psp->x = FISTR_XOFF+100;
                    psp->y = FIST_YOFF;
                    psp->yorig = psp->y;
                    psp->ang = 1024;
                    psp->PlayerP->FistAng = FistAngTable[RANDOM_RANGE(SIZ(FistAngTable))];
                    psp->vel = vel;
                    DoPlayerSpriteThrow(psp->PlayerP);
                    return;
                }
                else
                {
                    //if (psp->ActionState == ps_FistSwing && RANDOM_RANGE(1000) > 300)
                    //    {
                    pSetState(psp, ps_FistSwing+(psp->State - psp->ActionState)+1);
                    psp->ActionState = ps_FistSwing;
                    psp->PlayerP->WpnKungFuMove = 0;
                    //pStatePlusOne(psp);
                    //    }else
                    //if (RANDOM_RANGE(1000) > 400)
                    //    {
                    //    pSetState(psp, ps_Fist3Swing+(psp->State - psp->ActionState)+1);
                    //    psp->ActionState = ps_Fist3Swing;
                    //    psp->PlayerP->WpnKungFuMove = 1;
                    //    } else
                    //    {
                    //    pSetState(psp, ps_Kick+(psp->State - psp->ActionState)+1);
                    //    psp->ActionState = ps_Kick;
                    //    psp->PlayerP->WpnKungFuMove = 2;
                    //    }
                }

                psp->x = FIST_XOFF;
                psp->y = FIST_YOFF;
                psp->yorig = psp->y;
                psp->PlayerP->FistAng = FistAngTable[RANDOM_RANGE(SIZ(FistAngTable))];
                psp->ang = 1024;
                psp->vel = vel;
                DoPlayerSpriteThrow(psp->PlayerP);
                return;
            }
        }

        // NOT still holding down the fire key - stop swinging
        pSetState(psp, psp->PresentState);
        psp->x = FIST_XOFF;
        psp->y = FIST_YOFF;
        psp->y += tilesiz[psp->picndx].y;
        psp->yorig = psp->y;
    }
}

void
pFistBobSetup(PANEL_SPRITEp psp)
{
    if (TEST(psp->flags, PANF_BOB))
        return;

    psp->xorig = psp->x;
    psp->yorig = psp->y;

    psp->sin_amt = FIST_SWAY_AMT;
    psp->sin_ndx = 0;
    psp->bob_height_shift = 3;
}

void
pFistHide(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= FIST_YOFF + tilesiz[picnum].y)
    {
        psp->y = FIST_YOFF + tilesiz[picnum].y;

        pWeaponUnHideKeys(psp, psp->PresentState);
    }
}

void
pFistRest(PANEL_SPRITEp psp)
{
    SWBOOL force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (pWeaponHideKeys(psp, ps_FistHide))
        return;

    psp->yorig += synctics;

    if (psp->yorig > FIST_YOFF)
    {
        psp->yorig = FIST_YOFF;
    }

    psp->y = psp->yorig;

    pFistBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    force = !!TEST(psp->flags, PANF_UNHIDE_SHOOT);

    if (psp->ActionState == ps_Kick)
        psp->ActionState = ps_FistSwing;

    // Reset move to default
    psp->PlayerP->WpnKungFuMove = 0;

    //if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force || TEST_SYNC_KEY(psp->PlayerP, SK_OPERATE))
    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT) || force)
    {
        //if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force || FLAG_KEY_PRESSED(psp->PlayerP, SK_OPERATE))
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT) || force)
        {
            RESET(psp->flags, PANF_UNHIDE_SHOOT);

#if 0
            if (!TEST(psp->PlayerP->Flags, PF_JUMPING) && RANDOM_RANGE(1000) > 300)
            {
                //if (TEST_SYNC_KEY(psp->PlayerP, SK_OPERATE))
                //    {
                //    psp->ActionState = ps_Fist2Swing;
                //    psp->PlayerP->WpnKungFuMove = 3;
                //    } else
                if (RANDOM_RANGE(1000) > 500)
                {
                    psp->ActionState = ps_FistSwing;
                    psp->PlayerP->WpnKungFuMove = 0;
                }
                else
                {
                    psp->ActionState = ps_Fist3Swing;
                    psp->PlayerP->WpnKungFuMove = 1;
                }
            }
            else
            {
                psp->ActionState = ps_Kick;
                psp->PlayerP->WpnKungFuMove = 2;
            }
#else
            psp->ActionState = ps_FistSwing;
            psp->PlayerP->WpnKungFuMove = 0;
#endif

            pSetState(psp, psp->ActionState);

            psp->vel = 5500;

            psp->ang = 1024;
            psp->PlayerP->FistAng = FistAngTable[RANDOM_RANGE(SIZ(FistAngTable))];
            DoPlayerSpriteThrow(psp->PlayerP);
        }
    }
}

void
pFistAction(PANEL_SPRITEp psp)
{
    pFistBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));
}


void
pFistAttack(PANEL_SPRITEp psp)
{
    int InitFistAttack(PLAYERp pp);

    InitFistAttack(psp->PlayerP);
}

void
pFistRetract(PANEL_SPRITEp psp)
{
    short picnum = psp->picndx;

    psp->y += 3 * synctics;

    if (psp->y >= FIST_YOFF + tilesiz[picnum].y)
    {
        RESET(psp->PlayerP->Flags, PF_WEAPON_RETRACT);
        psp->PlayerP->Wpn[WPN_FIST] = NULL;
        pKillSprite(psp);
    }
}

void
pFistBlock(PANEL_SPRITEp psp)
{
    psp->yorig += synctics;

    if (psp->yorig > FIST_YOFF)
    {
        psp->yorig = FIST_YOFF;
    }

    psp->y = psp->yorig;

    pFistBobSetup(psp);
    pWeaponBob(psp, PLAYER_MOVING(psp->PlayerP));

    if (!TEST_SYNC_KEY(psp->PlayerP, SK_OPERATE))
    {
        pStatePlusOne(psp);
    }
}


//////////////////////////////////////////////////////////////////////////////////////////
//
// PANEL SPRITE GENERAL ROUTINES
//
//////////////////////////////////////////////////////////////////////////////////////////



void
pWeaponForceRest(PLAYERp pp)
{
    pSetState(pp->CurWpn, pp->CurWpn->RestState);
}

SWBOOL
pWeaponUnHideKeys(PANEL_SPRITEp psp, PANEL_STATEp state)
{
    // initing the other weapon will take care of this
    if (TEST(psp->flags, PANF_DEATH_HIDE))
    {
        return FALSE;
    }

    if (TEST(psp->flags, PANF_WEAPON_HIDE))
    {
        if (!TEST(psp->PlayerP->Flags, PF_WEAPON_DOWN))
        {
            RESET(psp->flags, PANF_WEAPON_HIDE);
            pSetState(psp, state);
            return TRUE;
        }

        return FALSE;
    }

    if (TEST_SYNC_KEY(psp->PlayerP, SK_HIDE_WEAPON))
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_HIDE_WEAPON))
        {
            FLAG_KEY_RELEASE(psp->PlayerP, SK_HIDE_WEAPON);
            pSetState(psp, state);
            return TRUE;
        }
    }
    else
    {
        FLAG_KEY_RESET(psp->PlayerP, SK_HIDE_WEAPON);
    }

    if (TEST_SYNC_KEY(psp->PlayerP, SK_SHOOT))
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_SHOOT))
        {
            SET(psp->flags, PANF_UNHIDE_SHOOT);
            pSetState(psp, state);
            return TRUE;
        }
    }

    return FALSE;
}

SWBOOL
pWeaponHideKeys(PANEL_SPRITEp psp, PANEL_STATEp state)
{
    if (TEST(psp->PlayerP->Flags, PF_DEAD))
    {
        SET(psp->flags, PANF_DEATH_HIDE);
        pSetState(psp, state);
        return TRUE;
    }

    if (TEST(psp->PlayerP->Flags, PF_WEAPON_DOWN))
    {
        SET(psp->flags, PANF_WEAPON_HIDE);
        pSetState(psp, state);
        return TRUE;
    }

    if (TEST_SYNC_KEY(psp->PlayerP, SK_HIDE_WEAPON))
    {
        if (FLAG_KEY_PRESSED(psp->PlayerP, SK_HIDE_WEAPON))
        {
            PutStringInfo(psp->PlayerP,"Weapon Holstered");
            FLAG_KEY_RELEASE(psp->PlayerP, SK_HIDE_WEAPON);
            pSetState(psp, state);
            return TRUE;
        }
    }
    else
    {
        FLAG_KEY_RESET(psp->PlayerP, SK_HIDE_WEAPON);
    }

    return FALSE;

}

void
InsertPanelSprite(PLAYERp pp, PANEL_SPRITEp psp)
{
    PANEL_SPRITEp cur, nxt;

    ASSERT(psp);
    ASSERT(ValidPtr(psp));

    // if list is empty, insert at front
    if (EMPTY(&pp->PanelSpriteList))
    {
        INSERT(&pp->PanelSpriteList, psp);
        return;
    }

    // if new pri is <= first pri in list, insert at front
    if (psp->priority <= pp->PanelSpriteList.Next->priority)
    {
        INSERT(&pp->PanelSpriteList, psp);
        return;
    }

    // search for first pri in list thats less than the new pri
    TRAVERSE(&pp->PanelSpriteList, cur, nxt)
    {
        // if the next pointer is the end of the list, insert it
        if ((LIST) cur->Next == (LIST) &pp->PanelSpriteList)
        {
            INSERT(cur, psp);
            return;
        }


        // if between cur and next, insert here
        if (psp->priority >= cur->priority && psp->priority <= cur->Next->priority)
        {
            INSERT(cur, psp);
            return;
        }
    }
}


PANEL_SPRITEp
pSpawnSprite(PLAYERp pp, PANEL_STATEp state, uint8_t priority, int x, int y)
{
    unsigned i;
    PANEL_SPRITEp psp;


    ASSERT(pp);

    psp = (PANEL_SPRITEp)CallocMem(sizeof(PANEL_SPRITE), 1);

    PRODUCTION_ASSERT(psp);

    psp->priority = priority;
    InsertPanelSprite(pp, psp);
    // INSERT(&pp->PanelSpriteList, psp);

    psp->PlayerP = pp;

    psp->x = x;
    psp->y = y;
    pSetState(psp, state);
    if (state == NULL)
        psp->picndx = -1;
    else
        psp->picndx = state->picndx;
    psp->ang = 0;
    psp->vel = 0;
    psp->rotate_ang = 0;
    psp->scale = 1 << 16;
    psp->ID = 0;

    for (i = 0; i < SIZ(psp->over); i++)
    {
        psp->over[i].State = NULL;
        psp->over[i].pic = -1;
        psp->over[i].xoff = -1;
        psp->over[i].yoff = -1;
    }

    return psp;
}

void
pSuicide(PANEL_SPRITEp psp)
{
    pKillSprite(psp);
}

void
pKillSprite(PANEL_SPRITEp psp)
{
    PRODUCTION_ASSERT(psp);
    ASSERT(ValidPtr(psp));

    REMOVE(psp);

    FreeMem(psp);
}

void
pClearSpriteList(PLAYERp pp)
{
    PANEL_SPRITEp psp=NULL, next_psp=NULL;

    TRAVERSE(&pp->PanelSpriteList, psp, next_psp)
    {
        pKillSprite(psp);
    }
}

void
pWeaponBob(PANEL_SPRITEp psp, short condition)
{
    int xdiff = 0, ydiff = 0;
    short bob_amt, bob_ndx;
    short bobvel;
    PLAYERp pp = psp->PlayerP;

    bobvel = FindDistance2D(pp->xvect, pp->yvect) >> 15;
    bobvel = bobvel + DIV4(bobvel);
    bobvel = min(bobvel, short(128));

    if (condition)
    {
        SET(psp->flags, PANF_BOB);
    }
    else
    {
        if (labs((psp->sin_ndx & 1023) - 0) < 70)
        {
            RESET(psp->flags, PANF_BOB);
            psp->sin_ndx = (RANDOM_P2(1024) < 512) ? 1024 : 0;
        }
    }

    if (TEST(psp->flags, PANF_BOB))
    {
        // //
        // sin_xxx moves the weapon left-right
        // //

        // increment the ndx into the sin table
        psp->sin_ndx = psp->sin_ndx + (synctics << 3);
        // add a random factor to it
        psp->sin_ndx += (RANDOM_P2(8) * synctics);
        // wrap
        psp->sin_ndx &= 2047;

        // get height
        xdiff = psp->sin_amt * sintable[psp->sin_ndx] >> 14;

        // //
        // bob_xxx moves the weapon up-down
        // //

        // as the weapon moves left-right move the weapon up-down in the same
        // proportion
        bob_ndx = (psp->sin_ndx + 512) & 1023;

        // base bob_amt on the players velocity - Max of 128
        bob_amt = bobvel >> psp->bob_height_shift;
        ydiff = bob_amt * sintable[bob_ndx] >> 14;
    }

    psp->x = psp->xorig + xdiff;
    psp->y = psp->yorig + ydiff + UziRecoilYadj;
}


//////////////////////////////////////////////////////////////////////////////////////////
//
// PANEL SPRITE CONTROL ROUTINES
//
//////////////////////////////////////////////////////////////////////////////////////////

SWBOOL DrawBeforeView = FALSE;
void
pDisplaySprites(PLAYERp pp)
{
    USERp u = User[pp->PlayerSprite];
    PANEL_SPRITEp psp=NULL, next=NULL;
    short shade, picnum, overlay_shade = 0;
    int x, y;
    unsigned i;

    uint8_t pal = 0;
    short ang;
    int flags;
    int x1,y1,x2,y2;

    TRAVERSE(&pp->PanelSpriteList, psp, next)
    {
        ASSERT(ValidPtr(psp));
        ang = psp->rotate_ang;
        shade = 0;
        flags = 0;
        x = psp->x;
        y = psp->y;
        // initilize pal here - jack with it below
        pal = psp->pal;

        if (DrawBeforeView)
            if (!TEST(psp->flags, PANF_DRAW_BEFORE_VIEW))
                continue;

        if (TEST(psp->flags, PANF_SUICIDE))
        {
            //pKillSprite(psp);
            continue;
        }

        // force kill before showing again
        if (TEST(psp->flags, PANF_KILL_AFTER_SHOW) && psp->numpages == 0)
        {
            pKillSprite(psp);
            continue;
        }

        // if the state is null get the picnum for other than picndx
        if (psp->picndx == -1 || !psp->State)
            picnum = psp->picnum;
        else
            picnum = psp->picndx;

        // UK panzies have to have darts instead of shurikens.
        if (gs.Darts)
            switch (picnum)
            {
            case STAR_REST:
                picnum = 2510;
                break;
            case STAR_REST + 1:
                picnum = 2511;
                break;
            case STAR_REST + 2:
                picnum = 2512;
                break;
            case STAR_REST + 3:
                picnum = 2513;
                break;
            case STAR_REST + 4:
                picnum = 2514;
                break;
            case STAR_REST + 5:
                picnum = 2515;
                break;
            case STAR_REST + 6:
                picnum = 2516;
                break;
            case STAR_REST + 7:
                picnum = 2517;
                break;
            }

        if (pp->Bloody && !gs.ParentalLock && !Global_PLock)
        {
            switch (picnum)
            {
            case SWORD_REST:
                picnum = BLOODYSWORD_REST;
                break;
            case SWORD_SWING0:
                picnum = BLOODYSWORD_SWING0;
                break;
            case SWORD_SWING1:
                picnum = BLOODYSWORD_SWING1;
                break;
            case SWORD_SWING2:
                picnum = BLOODYSWORD_SWING2;
                break;

            case FIST_REST:
                picnum = 4077;
                break;
            case FIST2_REST:
                picnum = 4051;
                break;
            case FIST_SWING0:
                picnum = 4074;
                break;
            case FIST_SWING1:
                picnum = 4075;
                break;
            case FIST_SWING2:
                picnum = 4076;
                break;

            case STAR_REST:
            case 2510:
                if (!gs.Darts)
                    picnum = 2138;
                else
                    picnum = 2518; // Bloody Dart Hand
                break;
            }
        }

        if (pp->WpnShotgunType == 1)
        {
            switch (picnum)
            {
            case SHOTGUN_REST:
            case SHOTGUN_RELOAD0:
                picnum = 2227;
                break;
            case SHOTGUN_RELOAD1:
                picnum = 2226;
                break;
            case SHOTGUN_RELOAD2:
                picnum = 2225;
                break;
            }
        }

        // don't draw
        if (TEST(psp->flags, PANF_INVISIBLE))
            continue;

        if (psp->State && TEST(psp->State->flags, psf_Invisible))
            continue;

        // if its a weapon sprite and the view is set to the outside don't draw the sprite
        if (TEST(psp->flags, PANF_WEAPON_SPRITE))
        {
            SECT_USERp sectu = nullptr;
            int16_t floorshade = 0;
            if (pp->cursectnum >= 0)
            {
                sectu = SectUser[pp->cursectnum];
                pal = sector[pp->cursectnum].floorpal;
                floorshade = sector[pp->cursectnum].floorshade;

                if (pal != PALETTE_DEFAULT)
                {
                    if (sectu && TEST(sectu->flags, SECTFU_DONT_COPY_PALETTE))
                        pal = PALETTE_DEFAULT;
                }

                if (pal == PALETTE_FOG || pal == PALETTE_DIVE || pal == PALETTE_DIVE_LAVA)
                    pal = psp->pal; // Set it back
            }

            ///////////

            if (pp->InventoryActive[INVENTORY_CLOAK])
            {
                SET(flags, ROTATE_SPRITE_TRANSLUCENT);
            }

            //shade = overlay_shade = DIV2(sector[pp->cursectnum].floorshade + sector[pp->cursectnum].ceilingshade);
            shade = overlay_shade = floorshade - 10;

            if (TEST(psp->PlayerP->Flags, PF_VIEW_FROM_OUTSIDE))
            {
                if (!TEST(psp->PlayerP->Flags, PF_VIEW_OUTSIDE_WEAPON))
                    continue;
            }

            if (TEST(psp->PlayerP->Flags, PF_VIEW_FROM_CAMERA))
                continue;

            // !FRANK - this was moved from BELOW this IF statement
            // if it doesn't have a picflag or its in the view
            if (sectu && TEST(sectu->flags, SECTFU_DONT_COPY_PALETTE))
                pal = 0;
        }

        //PANF_STATUS_AREA | PANF_SCREEN_CLIP | PANF_KILL_AFTER_SHOW,

        SET(flags, ROTATE_SPRITE_VIEW_CLIP);

        if (TEST(psp->flags, PANF_TRANSLUCENT))
            SET(flags, ROTATE_SPRITE_TRANSLUCENT);

        SET(flags, TEST(psp->flags, PANF_TRANS_FLIP));

        if (TEST(psp->flags, PANF_CORNER))
            SET(flags, ROTATE_SPRITE_CORNER);

        if (TEST(psp->flags, PANF_STATUS_AREA))
        {
            SET(flags,ROTATE_SPRITE_CORNER);
            RESET(flags,ROTATE_SPRITE_VIEW_CLIP);

            if (TEST(psp->flags, PANF_SCREEN_CLIP))
                SET(flags, ROTATE_SPRITE_SCREEN_CLIP);

            if (TEST(psp->flags, PANF_IGNORE_START_MOST))
                SET(flags, ROTATE_SPRITE_IGNORE_START_MOST);

            x1 = psp->x1;
            y1 = psp->y1;
            x2 = psp->x2;
            y2 = psp->y2;
            shade = psp->shade;
        }
        else
        {
            x1 = windowxy1.x;
            y1 = windowxy1.y;
            x2 = windowxy2.x;
            y2 = windowxy2.y;
        }

        if ((psp->State && TEST(psp->State->flags, psf_Xflip)) || TEST(psp->flags, PANF_XFLIP))
        {
            // this is what you have to do to x-flip
            ang = NORM_ANGLE(ang + 1024);
            SET(flags, ROTATE_SPRITE_YFLIP);
        }

        // shading
        if (psp->State && TEST(psp->State->flags, psf_ShadeHalf|psf_ShadeNone))
        {
            if (TEST(psp->State->flags, psf_ShadeNone))
                shade = 0;
            else if (TEST(psp->State->flags, psf_ShadeHalf))
                shade /= 2;
        }

        if (pal == PALETTE_DEFAULT)
        {
            switch (picnum)
            {
            case 4080:
            case 4081:
            case 2220:
            case 2221:
                pal = u->spal;
                break;
            }
        }

#if 1
        extern SWBOOL UsingMenus;
        if (TEST(psp->flags, PANF_KILL_AFTER_SHOW) && !TEST(psp->flags, PANF_NOT_ALL_PAGES) && !UsingMenus)
        {
            psp->numpages = 0;
            SET(flags, ROTATE_SPRITE_ALL_PAGES);
        }
#endif

        // temporary hack to fix fist artifacts until a solution is found in the panel system itself
        switch (picnum)
        {
            case FIST_SWING0:
            case FIST_SWING1:
            case FIST_SWING2:
            case FIST2_SWING0:
            case FIST2_SWING1:
            case FIST2_SWING2:
            case FIST3_SWING0:
            case FIST3_SWING1:
            case FIST3_SWING2:
            case BLOODYFIST_SWING0:
            case BLOODYFIST_SWING1:
            case BLOODYFIST_SWING2:
            case BLOODYFIST2_SWING0:
            case BLOODYFIST2_SWING1:
            case BLOODYFIST2_SWING2:
            case BLOODYFIST3_SWING0:
            case BLOODYFIST3_SWING1:
            case BLOODYFIST3_SWING2:
                if (TEST(flags, BIT(2)) && x > 160)
                    x = 65;
                else if (!TEST(flags, BIT(2)) && x < 160)
                    x = 345;
                break;
            default:
                break;
        }

        rotatesprite(x << 16, y << 16,
                     psp->scale, ang,
                     picnum, shade, pal,
                     flags, x1, y1, x2, y2);

        // do overlays (if any)
        for (i = 0; i < SIZ(psp->over); i++)
        {
            // get pic from state
            if (psp->over[i].State)
                picnum = psp->over[i].State->picndx;
            else
            // get pic from over variable
            if (psp->over[i].pic >= 0)
                picnum = psp->over[i].pic;
            else
                continue;

            if (TEST(psp->over[i].flags, psf_ShadeNone))
                overlay_shade = 0;

            if (picnum)
            {
                rotatesprite((x + psp->over[i].xoff) << 16, (y + psp->over[i].yoff) << 16,
                             psp->scale, ang,
                             picnum, overlay_shade, pal,
                             flags, x1, y1, x2, y2);
            }
        }

        if (TEST(psp->flags, PANF_KILL_AFTER_SHOW))
        {
            psp->numpages--;
            if (psp->numpages <= 0)
                pKillSprite(psp);
        }
    }
}

void pFlushPerms(PLAYERp pp)
{
    PANEL_SPRITEp psp=NULL, next=NULL;

    TRAVERSE(&pp->PanelSpriteList, psp, next)
    {
        ASSERT(ValidPtr(psp));

        // force kill before showing again
        if (TEST(psp->flags, PANF_KILL_AFTER_SHOW))
        {
            pKillSprite(psp);
        }
    }
}

void
pSpriteControl(PLAYERp pp)
{
    PANEL_SPRITEp psp=NULL, next=NULL;

    TRAVERSE(&pp->PanelSpriteList, psp, next)
    {
        // reminder - if these give an assertion look for pKillSprites
        // somewhere else other than by themselves
        // RULE: Sprites can only kill themselves
        PRODUCTION_ASSERT(psp);
        ASSERT(ValidPtr(psp));
        // ASSERT((uint32_t) psp->Next != 0xCCCCCCCC);

        if (psp->State)
            pStateControl(psp);
        else
        // for sprits that are not state controled but still need to call
        // something
        if (psp->PanelSpriteFunc)
        {
            (*psp->PanelSpriteFunc)(psp);
        }
    }
}

void
pSetState(PANEL_SPRITEp psp, PANEL_STATEp panel_state)
{
    PRODUCTION_ASSERT(psp);
    ASSERT(ValidPtr(psp));
    psp->tics = 0;
    psp->State = panel_state;
    psp->picndx = panel_state ? panel_state->picndx : 0;
}


void
pNextState(PANEL_SPRITEp psp)
{
    // Transition to the next state
    psp->State = psp->State->NextState;

    if (TEST(psp->State->flags, psf_QuickCall))
    {
        (*psp->State->Animator)(psp);
        psp->State = psp->State->NextState;
    }
}

void
pStatePlusOne(PANEL_SPRITEp psp)
{
    psp->tics = 0;
    psp->State++;

    if (TEST(psp->State->flags, psf_QuickCall))
    {
        (*psp->State->Animator)(psp);
        psp->State = psp->State->NextState;
    }
}


void
pStateControl(PANEL_SPRITEp psp)
{
    unsigned i;
    short tics = synctics;

    psp->tics += tics;

    // Skip states if too much time has passed
    while (psp->tics >= psp->State->tics)
    {
        // Set Tics
        psp->tics -= psp->State->tics;

        pNextState(psp);
    }

    // Set picnum to the correct pic
    psp->picndx = psp->State->picndx;

    // do overlay states
    for (i = 0; i < SIZ(psp->over); i++)
    {
        if (!psp->over[i].State)
            continue;

        psp->over[i].tics += tics;

        // Skip states if too much time has passed
        while (psp->over[i].tics >= psp->over[i].State->tics)
        {
            // Set Tics
            psp->over[i].tics -= psp->over[i].State->tics;
            psp->over[i].State = psp->over[i].State->NextState;

            if (!psp->over[i].State)
                break;
        }
    }

    // Call the correct animator
    if (psp->State->Animator)
        (*psp->State->Animator)(psp);

}


void
UpdatePanel(void)
{
    short pnum;
    extern SWBOOL DebugPanel,PanelUpdateMode;

    if (!PanelUpdateMode)
        return;

    if (DebugPanel)
        return;

    TRAVERSE_CONNECT(pnum)
    {
        if (dimensionmode != 2 && pnum == screenpeek)
            pDisplaySprites(Player + pnum);
    }
}

void
PreUpdatePanel(void)
{
    short pnum;
    extern SWBOOL DebugPanel,PanelUpdateMode;

    if (!PanelUpdateMode)
        return;

    if (DebugPanel)
        return;

    DrawBeforeView = TRUE;

    //if (DrawBeforeView)
    TRAVERSE_CONNECT(pnum)
    {
        if (dimensionmode != 2 && pnum == screenpeek)
            pDisplaySprites(Player + pnum);
    }

    DrawBeforeView = FALSE;
}

void rotatespritetile(short tilenum,
                      signed char shade, int cx1, int cy1,
                      int cx2, int cy2, char dapalnum)
{
    int x, y, xsiz, ysiz, tx1, ty1, tx2, ty2;

    xsiz = tilesiz[tilenum].x; tx1 = cx1/xsiz; tx2 = cx2/xsiz;
    ysiz = tilesiz[tilenum].y; ty1 = cy1/ysiz; ty2 = cy2/ysiz;

    for (x=tx1; x<=tx2; x++)
    {
        for (y=ty1; y<=ty2; y++)
        {
            rotatesprite((x*xsiz)<<16,(y*ysiz)<<16,65536L,0,tilenum,shade,dapalnum,
                         ROTATE_SPRITE_NON_MASK|ROTATE_SPRITE_CORNER|ROTATE_SPRITE_IGNORE_START_MOST,
                         cx1,cy1,cx2,cy2);
        }
    }
}

#define EnvironSuit_RATE 10
#define Fly_RATE 10
#define Cloak_RATE 10
#define Night_RATE 10
#define Box_RATE 10
#define Medkit_RATE 10
#define RepairKit_RATE 10
#define ChemBomb_RATE 10
#define FlashBomb_RATE 10
#define SmokeBomb_RATE 10
#define Caltrops_RATE 10

#define ID_PanelEnvironSuit 2397
PANEL_STATE ps_PanelEnvironSuit[] =
{
    {ID_PanelEnvironSuit, EnvironSuit_RATE, PanelInvTestSuicide, &ps_PanelEnvironSuit[0], 0,0,0}
};

#define ID_PanelCloak 2397 //2400
PANEL_STATE ps_PanelCloak[] =
{
    {ID_PanelCloak, Cloak_RATE, PanelInvTestSuicide, &ps_PanelCloak[0], 0,0,0}
};

#define ID_PanelRepairKit 2399
PANEL_STATE ps_PanelRepairKit[] =
{
    {ID_PanelRepairKit, RepairKit_RATE, PanelInvTestSuicide, &ps_PanelRepairKit[0], 0,0,0}
};

#define ID_PanelMedkit 2396
PANEL_STATE ps_PanelMedkit[] =
{
    {ID_PanelMedkit, Medkit_RATE, PanelInvTestSuicide, &ps_PanelMedkit[0], 0,0,0}
};

#define ID_PanelNightVision 2398
PANEL_STATE ps_PanelNightVision[] =
{
    {ID_PanelNightVision, Night_RATE, PanelInvTestSuicide, &ps_PanelNightVision[0], 0,0,0}
};

#define ID_PanelChemBomb 2407
PANEL_STATE ps_PanelChemBomb[] =
{
    {ID_PanelChemBomb, ChemBomb_RATE, PanelInvTestSuicide, &ps_PanelChemBomb[0], 0,0,0}
};

#define ID_PanelFlashBomb 2408
PANEL_STATE ps_PanelFlashBomb[] =
{
    {ID_PanelFlashBomb, FlashBomb_RATE, PanelInvTestSuicide, &ps_PanelFlashBomb[0], 0,0,0}
};

//#define ID_PanelSmokeBomb 2397
//PANEL_STATE ps_PanelSmokeBomb[] = {
//    {ID_PanelSmokeBomb, SmokeBomb_RATE, PanelInvTestSuicide, &ps_PanelSmokeBomb[0], 0,0,0}
//    };

#define ID_PanelCaltrops 2409
PANEL_STATE ps_PanelCaltrops[] =
{
    {ID_PanelCaltrops, Caltrops_RATE, PanelInvTestSuicide, &ps_PanelCaltrops[0], 0,0,0}
};

#define ID_SelectionBox 2435
PANEL_STATE ps_PanelSelectionBox[] =
{
    {ID_SelectionBox, Box_RATE, PanelInvTestSuicide, &ps_PanelSelectionBox[0], 0,0,0}
};

#define ID_KeyRed 2392
PANEL_STATE ps_PanelKeyRed[] =
{
    {ID_KeyRed, Box_RATE, PanelInvTestSuicide, &ps_PanelKeyRed[0], 0,0,0}
};

#define ID_KeyGreen 2393
PANEL_STATE ps_PanelKeyGreen[] =
{
    {ID_KeyGreen, Box_RATE, PanelInvTestSuicide, &ps_PanelKeyGreen[0], 0,0,0}
};

#define ID_KeyBlue 2394
PANEL_STATE ps_PanelKeyBlue[] =
{
    {ID_KeyBlue, Box_RATE, PanelInvTestSuicide, &ps_PanelKeyBlue[0], 0,0,0}
};

#define ID_KeyYellow 2395
PANEL_STATE ps_PanelKeyYellow[] =
{
    {ID_KeyYellow, Box_RATE, PanelInvTestSuicide, &ps_PanelKeyYellow[0], 0,0,0}
};


#include "saveable.h"

static saveable_code saveable_panel_code[] =
{
    SAVE_CODE(pSuicide),
    SAVE_CODE(SwordBlur),
    SAVE_CODE(SpecialUziRetractFunc),
    SAVE_CODE(FistBlur),
    SAVE_CODE(StringTimer),
};

static saveable_data saveable_panel_data[] =
{
    SAVE_DATA(ps_PresentSword),
    SAVE_DATA(ps_SwordRest),
    SAVE_DATA(ps_SwordHide),
    SAVE_DATA(ps_SwordSwing),
    SAVE_DATA(ps_RetractSword),

    SAVE_DATA(ps_PresentStar),
    SAVE_DATA(ps_StarHide),
    SAVE_DATA(ps_StarRest),
    SAVE_DATA(ps_ThrowStar),
    SAVE_DATA(ps_RetractStar),

    SAVE_DATA(ps_FireUzi),
    SAVE_DATA(ps_UziNull),
    SAVE_DATA(ps_UziHide),
    SAVE_DATA(ps_PresentUzi),
    SAVE_DATA(ps_PresentUziReload),
    SAVE_DATA(ps_RetractUzi),
    SAVE_DATA(ps_FireUzi2),
    SAVE_DATA(ps_PresentUzi2),
    SAVE_DATA(ps_Uzi2Hide),
    SAVE_DATA(ps_RetractUzi2),
    SAVE_DATA(ps_Uzi2Suicide),
    SAVE_DATA(ps_Uzi2Null),
    SAVE_DATA(ps_UziEject),
    SAVE_DATA(ps_UziClip),
    SAVE_DATA(ps_UziReload),
    SAVE_DATA(ps_UziDoneReload),
    SAVE_DATA(ps_UziShell),
    SAVE_DATA(ps_Uzi2Shell),

    SAVE_DATA(ps_ShotgunShell),
    SAVE_DATA(ps_PresentShotgun),
    SAVE_DATA(ps_ShotgunRest),
    SAVE_DATA(ps_ShotgunHide),
    SAVE_DATA(ps_ShotgunRecoil),
    SAVE_DATA(ps_ShotgunRecoilAuto),
    SAVE_DATA(ps_ShotgunFire),
    SAVE_DATA(ps_ShotgunAutoFire),
    SAVE_DATA(ps_ShotgunReload),
    SAVE_DATA(ps_RetractShotgun),
    SAVE_DATA(ps_ShotgunFlash),

    SAVE_DATA(ps_PresentRail),
    SAVE_DATA(ps_RailRest),
    SAVE_DATA(ps_RailHide),
    SAVE_DATA(ps_RailRecoil),
    SAVE_DATA(ps_RailFire),
    SAVE_DATA(ps_RailFireEMP),
    SAVE_DATA(ps_RetractRail),

    SAVE_DATA(ps_PresentHothead),
    SAVE_DATA(ps_HotheadHide),
    SAVE_DATA(ps_RetractHothead),
    SAVE_DATA(ps_HotheadRest),
    SAVE_DATA(ps_HotheadRestRing),
    SAVE_DATA(ps_HotheadRestNapalm),
    SAVE_DATA(ps_HotheadAttack),
    SAVE_DATA(ps_HotheadRing),
    SAVE_DATA(ps_HotheadNapalm),
    SAVE_DATA(ps_HotheadTurn),
    SAVE_DATA(ps_HotheadTurnRing),
    SAVE_DATA(ps_HotheadTurnNapalm),
    SAVE_DATA(ps_OnFire),

    SAVE_DATA(ps_PresentMicro),
    SAVE_DATA(ps_MicroRest),
    SAVE_DATA(ps_MicroHide),
    SAVE_DATA(ps_InitNuke),
    SAVE_DATA(ps_MicroRecoil),
    SAVE_DATA(ps_MicroFire),
    SAVE_DATA(ps_MicroSingleFire),
    SAVE_DATA(ps_RetractMicro),
    SAVE_DATA(ps_MicroHeatFlash),
    SAVE_DATA(ps_MicroNukeFlash),

    SAVE_DATA(ps_PresentHeart),
    SAVE_DATA(ps_HeartRest),
    SAVE_DATA(ps_HeartHide),
    SAVE_DATA(ps_HeartAttack),
    SAVE_DATA(ps_RetractHeart),
    SAVE_DATA(ps_HeartBlood),
    SAVE_DATA(ps_HeartBloodSmall),
    SAVE_DATA(ps_HeartBlood),

    SAVE_DATA(ps_PresentGrenade),
    SAVE_DATA(ps_GrenadeRest),
    SAVE_DATA(ps_GrenadeHide),
    SAVE_DATA(ps_GrenadeFire),
    SAVE_DATA(ps_GrenadeRecoil),
    SAVE_DATA(ps_RetractGrenade),

    SAVE_DATA(ps_PresentMine),
    SAVE_DATA(ps_MineRest),
    SAVE_DATA(ps_MineHide),
    SAVE_DATA(ps_MineThrow),
    SAVE_DATA(ps_RetractMine),

    SAVE_DATA(ps_ChopsAttack1),
    SAVE_DATA(ps_ChopsAttack2),
    SAVE_DATA(ps_ChopsAttack3),
    SAVE_DATA(ps_ChopsAttack4),
    SAVE_DATA(ps_ChopsWait),
    SAVE_DATA(ps_ChopsRetract),

    SAVE_DATA(ps_PresentFist),
    SAVE_DATA(ps_FistRest),
    SAVE_DATA(ps_FistHide),
    SAVE_DATA(ps_PresentFist2),
    SAVE_DATA(ps_Fist2Rest),
    SAVE_DATA(ps_Fist2Hide),
    /*
    SAVE_DATA(ps_PresentFist3),
    SAVE_DATA(ps_Fist3Rest),
    SAVE_DATA(ps_Fist3Hide),
    */
    SAVE_DATA(ps_FistSwing),
    SAVE_DATA(ps_Fist2Swing),
    SAVE_DATA(ps_Fist3Swing),
    SAVE_DATA(ps_Kick),
    SAVE_DATA(ps_RetractFist),

    SAVE_DATA(ps_PanelEnvironSuit),
    SAVE_DATA(ps_PanelCloak),
    SAVE_DATA(ps_PanelRepairKit),
    SAVE_DATA(ps_PanelMedkit),
    SAVE_DATA(ps_PanelNightVision),
    SAVE_DATA(ps_PanelChemBomb),
    SAVE_DATA(ps_PanelFlashBomb),
    SAVE_DATA(ps_PanelCaltrops),
    SAVE_DATA(ps_PanelSelectionBox),
    SAVE_DATA(ps_PanelKeyRed),
    SAVE_DATA(ps_PanelKeyGreen),
    SAVE_DATA(ps_PanelKeyBlue),
    SAVE_DATA(ps_PanelKeyYellow),
};

saveable_module saveable_panel =
{
    // code
    saveable_panel_code,
    SIZ(saveable_panel_code),

    // data
    saveable_panel_data,
    SIZ(saveable_panel_data)
};

