code:
//-------------------------------------------------------------------------
/*
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
*/
//-------------------------------------------------------------------------
#include "build.h"

#include "keys.h"
#include "names2.h"
#include "panel.h"
#include "game.h"
#include "tags.h"
#include "ai.h"
#include "pal.h"
#include "sprite.h"
#include "actor.h"
#include "weapon.h"
#include "track.h"

short Bunny_Count = 0;
ANIMATOR DoActorMoveJump;
ANIMATOR DoBunnyMoveJump;
ANIMATOR DoBunnyQuickJump;

DECISION BunnyBattle[] =
{
    {748, InitActorMoveCloser},
    {750, InitActorAlertNoise},
    {760, InitActorAttackNoise},
    {1024, InitActorMoveCloser}
};

DECISION BunnyOffense[] =
{
    {600, InitActorMoveCloser},
    {700, InitActorAlertNoise},
    {1024, InitActorMoveCloser}
};

DECISION BunnyBroadcast[] =
{
    {21, InitActorAlertNoise},
    {51, InitActorAmbientNoise},
    {1024, InitActorDecide}
};

DECISION BunnySurprised[] =
{
    {500, InitActorRunAway},
    {701, InitActorMoveCloser},
    {1024, InitActorDecide}
};

DECISION BunnyEvasive[] =
{
    {500,  InitActorWanderAround},
    {1020, InitActorRunAway},
    {1024, InitActorAmbientNoise}
};

DECISION BunnyLostTarget[] =
{
    {900, InitActorFindPlayer},
    {1024, InitActorWanderAround}
};

DECISION BunnyCloseRange[] =
{
    {1024,  InitActorAttack             },
//    {1024,  InitActorReposition         }
};

DECISION BunnyWander[] =
{
    {1024, InitActorReposition}
};

PERSONALITY WhiteBunnyPersonality =
{
    BunnyBattle,
    BunnyOffense,
    BunnyBroadcast,
    BunnySurprised,
    BunnyEvasive,
    BunnyLostTarget,
    BunnyCloseRange,
    BunnyCloseRange
};

PERSONALITY BunnyPersonality =
{
    BunnyEvasive,
    BunnyEvasive,
    BunnyEvasive,
    BunnyWander,
    BunnyWander,
    BunnyWander,
    BunnyEvasive,
    BunnyEvasive
};

ATTRIBUTE BunnyAttrib =
{
    {100, 120, 140, 180},               // Speeds
    {5, 0, -2, -4},                     // Tic Adjusts
    3,                                  // MaxWeapons;
    {
        DIGI_BUNNYAMBIENT, 0, DIGI_BUNNYATTACK,
        DIGI_BUNNYATTACK, DIGI_BUNNYDIE2, 0,
        0,0,0,0
    }
};

ATTRIBUTE WhiteBunnyAttrib =
{
    {200, 220, 340, 380},               // Speeds
    {5, 0, -2, -4},                     // Tic Adjusts
    3,                                  // MaxWeapons;
    {
        DIGI_BUNNYAMBIENT, 0, DIGI_BUNNYATTACK,
        DIGI_BUNNYATTACK, DIGI_BUNNYDIE2, 0,
        0,0,0,0
    }
};

//////////////////////
//
// BUNNY RUN
//
//////////////////////

#define BUNNY_RUN_RATE 10

ANIMATOR DoBunnyMove, NullBunny, DoActorDebris, DoBunnyGrowUp;

STATE s_BunnyRun[5][6] =
{
    {
        {BUNNY_RUN_R0 + 0, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[0][1]},
        {BUNNY_RUN_R0 + 1, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[0][2]},
        {BUNNY_RUN_R0 + 2, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[0][3]},
        {BUNNY_RUN_R0 + 3, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[0][4]},
        {BUNNY_RUN_R0 + 4, SF_QUICK_CALL,                DoBunnyGrowUp, &s_BunnyRun[0][5]},
        {BUNNY_RUN_R0 + 4, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[0][0]},
    },
    {
        {BUNNY_RUN_R1 + 0, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[1][1]},
        {BUNNY_RUN_R1 + 1, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[1][2]},
        {BUNNY_RUN_R1 + 2, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[1][3]},
        {BUNNY_RUN_R1 + 3, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[1][4]},
        {BUNNY_RUN_R1 + 4, SF_QUICK_CALL,                DoBunnyGrowUp, &s_BunnyRun[1][5]},
        {BUNNY_RUN_R1 + 4, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[1][0]},
    },
    {
        {BUNNY_RUN_R2 + 0, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[2][1]},
        {BUNNY_RUN_R2 + 1, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[2][2]},
        {BUNNY_RUN_R2 + 2, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[2][3]},
        {BUNNY_RUN_R2 + 3, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[2][4]},
        {BUNNY_RUN_R2 + 4, SF_QUICK_CALL,                DoBunnyGrowUp, &s_BunnyRun[2][5]},
        {BUNNY_RUN_R2 + 4, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[2][0]},
    },
    {
        {BUNNY_RUN_R3 + 0, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[3][1]},
        {BUNNY_RUN_R3 + 1, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[3][2]},
        {BUNNY_RUN_R3 + 2, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[3][3]},
        {BUNNY_RUN_R3 + 3, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[3][4]},
        {BUNNY_RUN_R3 + 4, SF_QUICK_CALL,                DoBunnyGrowUp, &s_BunnyRun[3][5]},
        {BUNNY_RUN_R3 + 4, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[3][0]},
    },
    {
        {BUNNY_RUN_R4 + 0, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[4][1]},
        {BUNNY_RUN_R4 + 1, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[4][2]},
        {BUNNY_RUN_R4 + 2, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[4][3]},
        {BUNNY_RUN_R4 + 3, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[4][4]},
        {BUNNY_RUN_R4 + 4, SF_QUICK_CALL,                DoBunnyGrowUp, &s_BunnyRun[4][5]},
        {BUNNY_RUN_R4 + 4, BUNNY_RUN_RATE | SF_TIC_ADJUST, DoBunnyMove, &s_BunnyRun[4][0]},
    }
};

STATEp sg_BunnyRun[] =
{
    &s_BunnyRun[0][0],
    &s_BunnyRun[1][0],
    &s_BunnyRun[2][0],
    &s_BunnyRun[3][0],
    &s_BunnyRun[4][0]
};

//////////////////////
//
// BUNNY STAND
//
//////////////////////

#define BUNNY_STAND_RATE 12
ANIMATOR DoBunnyEat;

STATE s_BunnyStand[5][3] =
{
    {
        {BUNNY_STAND_R0 + 0, BUNNY_STAND_RATE, DoBunnyEat, &s_BunnyStand[0][1]},
        {BUNNY_STAND_R0 + 4, SF_QUICK_CALL, DoBunnyGrowUp, &s_BunnyStand[0][2]},
        {BUNNY_STAND_R0 + 4, BUNNY_STAND_RATE, DoBunnyEat, &s_BunnyStand[0][0]},
    },
    {
        {BUNNY_STAND_R1 + 0, BUNNY_STAND_RATE, DoBunnyEat, &s_BunnyStand[1][1]},
        {BUNNY_STAND_R1 + 4, SF_QUICK_CALL, DoBunnyGrowUp, &s_BunnyStand[1][2]},
        {BUNNY_STAND_R1 + 4, BUNNY_STAND_RATE, DoBunnyEat, &s_BunnyStand[1][0]},
    },
    {
        {BUNNY_STAND_R2 + 0, BUNNY_STAND_RATE, DoBunnyEat, &s_BunnyStand[2][1]},
        {BUNNY_STAND_R2 + 4, SF_QUICK_CALL, DoBunnyGrowUp, &s_BunnyStand[2][2]},
        {BUNNY_STAND_R2 + 4, BUNNY_STAND_RATE, DoBunnyEat, &s_BunnyStand[2][0]},
    },
    {
        {BUNNY_STAND_R3 + 0, BUNNY_STAND_RATE, DoBunnyEat, &s_BunnyStand[3][1]},
        {BUNNY_STAND_R3 + 4, SF_QUICK_CALL, DoBunnyGrowUp, &s_BunnyStand[3][2]},
        {BUNNY_STAND_R3 + 4, BUNNY_STAND_RATE, DoBunnyEat, &s_BunnyStand[3][0]},
    },
    {
        {BUNNY_STAND_R4 + 0, BUNNY_STAND_RATE, DoBunnyEat, &s_BunnyStand[4][1]},
        {BUNNY_STAND_R4 + 4, SF_QUICK_CALL, DoBunnyGrowUp, &s_BunnyStand[4][2]},
        {BUNNY_STAND_R4 + 4, BUNNY_STAND_RATE, DoBunnyEat, &s_BunnyStand[4][0]},
    },
};

STATEp sg_BunnyStand[] =
{
    s_BunnyStand[0],
    s_BunnyStand[1],
    s_BunnyStand[2],
    s_BunnyStand[3],
    s_BunnyStand[4]
};

//////////////////////
//
// BUNNY GET LAYED
//
//////////////////////

#define BUNNY_SCREW_RATE 16
ANIMATOR DoBunnyScrew;

STATE s_BunnyScrew[5][2] =
{
    {
        {BUNNY_STAND_R0 + 0, BUNNY_SCREW_RATE, DoBunnyScrew, &s_BunnyScrew[0][1]},
        {BUNNY_STAND_R0 + 2, BUNNY_SCREW_RATE, DoBunnyScrew, &s_BunnyScrew[0][0]},
    },
    {
        {BUNNY_STAND_R1 + 0, BUNNY_SCREW_RATE, DoBunnyScrew, &s_BunnyScrew[1][1]},
        {BUNNY_STAND_R1 + 2, BUNNY_SCREW_RATE, DoBunnyScrew, &s_BunnyScrew[1][0]},
    },
    {
        {BUNNY_STAND_R2 + 0, BUNNY_SCREW_RATE, DoBunnyScrew, &s_BunnyScrew[2][1]},
        {BUNNY_STAND_R2 + 2, BUNNY_SCREW_RATE, DoBunnyScrew, &s_BunnyScrew[2][0]},
    },
    {
        {BUNNY_STAND_R3 + 0, BUNNY_SCREW_RATE, DoBunnyScrew, &s_BunnyScrew[3][1]},
        {BUNNY_STAND_R3 + 2, BUNNY_SCREW_RATE, DoBunnyScrew, &s_BunnyScrew[3][0]},
    },
    {
        {BUNNY_STAND_R4 + 0, BUNNY_SCREW_RATE, DoBunnyScrew, &s_BunnyScrew[4][1]},
        {BUNNY_STAND_R4 + 2, BUNNY_SCREW_RATE, DoBunnyScrew, &s_BunnyScrew[4][0]},
    },
};

STATEp sg_BunnyScrew[] =
{
    s_BunnyScrew[0],
    s_BunnyScrew[1],
    s_BunnyScrew[2],
    s_BunnyScrew[3],
    s_BunnyScrew[4]
};

//////////////////////
//
// BUNNY SWIPE
//
//////////////////////

#define BUNNY_SWIPE_RATE 8
ANIMATOR InitActorDecide;
ANIMATOR InitBunnySlash;

STATE s_BunnySwipe[5][8] =
{
    {
        {BUNNY_SWIPE_R0 + 0, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[0][1]},
        {BUNNY_SWIPE_R0 + 1, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[0][2]},
        {BUNNY_SWIPE_R0 + 1, 0 | SF_QUICK_CALL, InitBunnySlash, &s_BunnySwipe[0][3]},
        {BUNNY_SWIPE_R0 + 2, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[0][4]},
        {BUNNY_SWIPE_R0 + 3, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[0][5]},
        {BUNNY_SWIPE_R0 + 3, 0 | SF_QUICK_CALL, InitBunnySlash, &s_BunnySwipe[0][6]},
        {BUNNY_SWIPE_R0 + 3, 0 | SF_QUICK_CALL, InitActorDecide, &s_BunnySwipe[0][7]},
        {BUNNY_SWIPE_R0 + 3, BUNNY_SWIPE_RATE, DoBunnyMove, &s_BunnySwipe[0][7]},
    },
    {
        {BUNNY_SWIPE_R1 + 0, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[1][1]},
        {BUNNY_SWIPE_R1 + 1, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[1][2]},
        {BUNNY_SWIPE_R1 + 1, 0 | SF_QUICK_CALL, InitBunnySlash, &s_BunnySwipe[1][3]},
        {BUNNY_SWIPE_R1 + 2, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[1][4]},
        {BUNNY_SWIPE_R1 + 3, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[1][5]},
        {BUNNY_SWIPE_R1 + 3, 0 | SF_QUICK_CALL, InitBunnySlash, &s_BunnySwipe[1][6]},
        {BUNNY_SWIPE_R1 + 3, 0 | SF_QUICK_CALL, InitActorDecide, &s_BunnySwipe[1][7]},
        {BUNNY_SWIPE_R1 + 3, BUNNY_SWIPE_RATE, DoBunnyMove, &s_BunnySwipe[1][7]},
    },
    {
        {BUNNY_SWIPE_R2 + 0, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[2][1]},
        {BUNNY_SWIPE_R2 + 1, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[2][2]},
        {BUNNY_SWIPE_R2 + 1, 0 | SF_QUICK_CALL, InitBunnySlash, &s_BunnySwipe[2][3]},
        {BUNNY_SWIPE_R2 + 2, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[2][4]},
        {BUNNY_SWIPE_R2 + 3, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[2][5]},
        {BUNNY_SWIPE_R2 + 3, 0 | SF_QUICK_CALL, InitBunnySlash, &s_BunnySwipe[2][6]},
        {BUNNY_SWIPE_R2 + 3, 0 | SF_QUICK_CALL, InitActorDecide, &s_BunnySwipe[2][7]},
        {BUNNY_SWIPE_R2 + 3, BUNNY_SWIPE_RATE, DoBunnyMove, &s_BunnySwipe[2][7]},
    },
    {
        {BUNNY_SWIPE_R3 + 0, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[3][1]},
        {BUNNY_SWIPE_R3 + 1, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[3][2]},
        {BUNNY_SWIPE_R3 + 1, 0 | SF_QUICK_CALL, InitBunnySlash, &s_BunnySwipe[3][3]},
        {BUNNY_SWIPE_R3 + 2, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[3][4]},
        {BUNNY_SWIPE_R3 + 3, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[3][5]},
        {BUNNY_SWIPE_R3 + 3, 0 | SF_QUICK_CALL, InitBunnySlash, &s_BunnySwipe[3][6]},
        {BUNNY_SWIPE_R3 + 3, 0 | SF_QUICK_CALL, InitActorDecide, &s_BunnySwipe[3][7]},
        {BUNNY_SWIPE_R3 + 3, BUNNY_SWIPE_RATE, DoBunnyMove, &s_BunnySwipe[3][7]},
    },
    {
        {BUNNY_SWIPE_R4 + 0, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[4][1]},
        {BUNNY_SWIPE_R4 + 1, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[4][2]},
        {BUNNY_SWIPE_R4 + 1, 0 | SF_QUICK_CALL, InitBunnySlash, &s_BunnySwipe[4][3]},
        {BUNNY_SWIPE_R4 + 2, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[4][4]},
        {BUNNY_SWIPE_R4 + 3, BUNNY_SWIPE_RATE, NullBunny, &s_BunnySwipe[4][5]},
        {BUNNY_SWIPE_R4 + 3, 0 | SF_QUICK_CALL, InitBunnySlash, &s_BunnySwipe[4][6]},
        {BUNNY_SWIPE_R4 + 3, 0 | SF_QUICK_CALL, InitActorDecide, &s_BunnySwipe[4][7]},
        {BUNNY_SWIPE_R4 + 3, BUNNY_SWIPE_RATE, DoBunnyMove, &s_BunnySwipe[4][7]},
    }
};

STATEp sg_BunnySwipe[] =
{
    &s_BunnySwipe[0][0],
    &s_BunnySwipe[1][0],
    &s_BunnySwipe[2][0],
    &s_BunnySwipe[3][0],
    &s_BunnySwipe[4][0]
};

//////////////////////
//
// BUNNY HEART - show players heart
//
//////////////////////

#define BUNNY_HEART_RATE 14
ANIMATOR DoBunnyStandKill;

STATE s_BunnyHeart[5][4] =
{
    {
        {BUNNY_SWIPE_R0 + 0, BUNNY_HEART_RATE, DoBunnyStandKill, &s_BunnyHeart[0][0]},
    },
    {
        {BUNNY_SWIPE_R1 + 0, BUNNY_HEART_RATE, DoBunnyStandKill, &s_BunnyHeart[1][0]},
    },
    {
        {BUNNY_SWIPE_R2 + 0, BUNNY_HEART_RATE, DoBunnyStandKill, &s_BunnyHeart[2][0]},
    },
    {
        {BUNNY_SWIPE_R3 + 0, BUNNY_HEART_RATE, DoBunnyStandKill, &s_BunnyHeart[3][0]},
    },
    {
        {BUNNY_SWIPE_R4 + 0, BUNNY_HEART_RATE, DoBunnyStandKill, &s_BunnyHeart[4][0]},
    }
};

STATEp sg_BunnyHeart[] =
{
    &s_BunnyHeart[0][0],
    &s_BunnyHeart[1][0],
    &s_BunnyHeart[2][0],
    &s_BunnyHeart[3][0],
    &s_BunnyHeart[4][0]
};

//////////////////////
//
// BUNNY PAIN
//
//////////////////////

#define BUNNY_PAIN_RATE 38
ANIMATOR DoBunnyPain;

STATE s_BunnyPain[5][1] =
{
    {
        {BUNNY_SWIPE_R0 + 0, BUNNY_PAIN_RATE, DoBunnyPain, &s_BunnyPain[0][0]},
    },
    {
        {BUNNY_SWIPE_R0 + 0, BUNNY_PAIN_RATE, DoBunnyPain, &s_BunnyPain[1][0]},
    },
    {
        {BUNNY_SWIPE_R0 + 0, BUNNY_PAIN_RATE, DoBunnyPain, &s_BunnyPain[2][0]},
    },
    {
        {BUNNY_SWIPE_R0 + 0, BUNNY_PAIN_RATE, DoBunnyPain, &s_BunnyPain[3][0]},
    },
    {
        {BUNNY_SWIPE_R0 + 0, BUNNY_PAIN_RATE, DoBunnyPain, &s_BunnyPain[4][0]},
    }
};

STATEp sg_BunnyPain[] =
{
    &s_BunnyPain[0][0],
    &s_BunnyPain[1][0],
    &s_BunnyPain[2][0],
    &s_BunnyPain[3][0],
    &s_BunnyPain[4][0]
};

//////////////////////
//
// BUNNY JUMP
//
//////////////////////

#define BUNNY_JUMP_RATE 25

STATE s_BunnyJump[5][6] =
{
    {
        {BUNNY_RUN_R0 + 1, BUNNY_JUMP_RATE, DoBunnyMoveJump, &s_BunnyJump[0][1]},
        {BUNNY_RUN_R0 + 2, BUNNY_JUMP_RATE, DoBunnyMoveJump, &s_BunnyJump[0][1]},
    },
    {
        {BUNNY_RUN_R1 + 1, BUNNY_JUMP_RATE, DoBunnyMoveJump, &s_BunnyJump[1][1]},
        {BUNNY_RUN_R1 + 2, BUNNY_JUMP_RATE, DoBunnyMoveJump, &s_BunnyJump[1][1]},
    },
    {
        {BUNNY_RUN_R2 + 1, BUNNY_JUMP_RATE, DoBunnyMoveJump, &s_BunnyJump[2][1]},
        {BUNNY_RUN_R2 + 2, BUNNY_JUMP_RATE, DoBunnyMoveJump, &s_BunnyJump[2][1]},
    },
    {
        {BUNNY_RUN_R3 + 1, BUNNY_JUMP_RATE, DoBunnyMoveJump, &s_BunnyJump[3][1]},
        {BUNNY_RUN_R3 + 2, BUNNY_JUMP_RATE, DoBunnyMoveJump, &s_BunnyJump[3][1]},
    },
    {
        {BUNNY_RUN_R4 + 1, BUNNY_JUMP_RATE, DoBunnyMoveJump, &s_BunnyJump[4][1]},
        {BUNNY_RUN_R4 + 2, BUNNY_JUMP_RATE, DoBunnyMoveJump, &s_BunnyJump[4][1]},
    }
};

STATEp sg_BunnyJump[] =
{
    &s_BunnyJump[0][0],
    &s_BunnyJump[1][0],
    &s_BunnyJump[2][0],
    &s_BunnyJump[3][0],
    &s_BunnyJump[4][0]
};

//////////////////////
//
// BUNNY FALL
//
//////////////////////

#define BUNNY_FALL_RATE 25

STATE s_BunnyFall[5][6] =
{
    {
        {BUNNY_RUN_R0 + 3, BUNNY_FALL_RATE, DoBunnyMoveJump, &s_BunnyFall[0][0]},
    },
    {
        {BUNNY_RUN_R1 + 3, BUNNY_FALL_RATE, DoBunnyMoveJump, &s_BunnyFall[1][0]},
    },
    {
        {BUNNY_RUN_R2 + 3, BUNNY_FALL_RATE, DoBunnyMoveJump, &s_BunnyFall[2][0]},
    },
    {
        {BUNNY_RUN_R3 + 3, BUNNY_FALL_RATE, DoBunnyMoveJump, &s_BunnyFall[3][0]},
    },
    {
        {BUNNY_RUN_R4 + 3, BUNNY_FALL_RATE, DoBunnyMoveJump, &s_BunnyFall[4][0]},
    }
};

STATEp sg_BunnyFall[] =
{
    &s_BunnyFall[0][0],
    &s_BunnyFall[1][0],
    &s_BunnyFall[2][0],
    &s_BunnyFall[3][0],
    &s_BunnyFall[4][0]
};

//////////////////////
//
// BUNNY JUMP ATTACK
//
//////////////////////

#define BUNNY_JUMP_ATTACK_RATE 35
int DoBunnyBeginJumpAttack(short SpriteNum);

STATE s_BunnyJumpAttack[5][6] =
{
    {
        {BUNNY_RUN_R0 + 1, BUNNY_JUMP_ATTACK_RATE, NullBunny, &s_BunnyJumpAttack[0][1]},
        {BUNNY_RUN_R0 + 1, 0 | SF_QUICK_CALL, DoBunnyBeginJumpAttack, &s_BunnyJumpAttack[0][2]},
        {BUNNY_RUN_R0 + 2, BUNNY_JUMP_ATTACK_RATE, DoBunnyMoveJump, &s_BunnyJumpAttack[0][2]},
    },
    {
        {BUNNY_RUN_R1 + 1, BUNNY_JUMP_ATTACK_RATE, NullBunny, &s_BunnyJumpAttack[1][1]},
        {BUNNY_RUN_R1 + 1, 0 | SF_QUICK_CALL, DoBunnyBeginJumpAttack, &s_BunnyJumpAttack[1][2]},
        {BUNNY_RUN_R1 + 2, BUNNY_JUMP_ATTACK_RATE, DoBunnyMoveJump, &s_BunnyJumpAttack[1][2]},
    },
    {
        {BUNNY_RUN_R2 + 1, BUNNY_JUMP_ATTACK_RATE, NullBunny, &s_BunnyJumpAttack[2][1]},
        {BUNNY_RUN_R2 + 1, 0 | SF_QUICK_CALL, DoBunnyBeginJumpAttack, &s_BunnyJumpAttack[2][2]},
        {BUNNY_RUN_R2 + 2, BUNNY_JUMP_ATTACK_RATE, DoBunnyMoveJump, &s_BunnyJumpAttack[2][2]},
    },
    {
        {BUNNY_RUN_R3 + 1, BUNNY_JUMP_ATTACK_RATE, NullBunny, &s_BunnyJumpAttack[3][1]},
        {BUNNY_RUN_R3 + 1, 0 | SF_QUICK_CALL, DoBunnyBeginJumpAttack, &s_BunnyJumpAttack[3][2]},
        {BUNNY_RUN_R3 + 2, BUNNY_JUMP_ATTACK_RATE, DoBunnyMoveJump, &s_BunnyJumpAttack[3][2]},
    },
    {
        {BUNNY_RUN_R4 + 1, BUNNY_JUMP_ATTACK_RATE, NullBunny, &s_BunnyJumpAttack[4][1]},
        {BUNNY_RUN_R4 + 1, 0 | SF_QUICK_CALL, DoBunnyBeginJumpAttack, &s_BunnyJumpAttack[4][2]},
        {BUNNY_RUN_R4 + 2, BUNNY_JUMP_ATTACK_RATE, DoBunnyMoveJump, &s_BunnyJumpAttack[4][2]},
    }
};

STATEp sg_BunnyJumpAttack[] =
{
    &s_BunnyJumpAttack[0][0],
    &s_BunnyJumpAttack[1][0],
    &s_BunnyJumpAttack[2][0],
    &s_BunnyJumpAttack[3][0],
    &s_BunnyJumpAttack[4][0]
};

//////////////////////
//
// BUNNY DIE
//
//////////////////////

#define BUNNY_DIE_RATE 16
ANIMATOR BunnySpew;

STATE s_BunnyDie[] =
{
    {BUNNY_DIE + 0, BUNNY_DIE_RATE, NullBunny, &s_BunnyDie[1]},
    {BUNNY_DIE + 0, SF_QUICK_CALL,  BunnySpew, &s_BunnyDie[2]},
    {BUNNY_DIE + 1, BUNNY_DIE_RATE, NullBunny, &s_BunnyDie[3]},
    {BUNNY_DIE + 2, BUNNY_DIE_RATE, NullBunny, &s_BunnyDie[4]},
    {BUNNY_DIE + 2, BUNNY_DIE_RATE, NullBunny, &s_BunnyDie[5]},
    {BUNNY_DEAD, BUNNY_DIE_RATE, DoActorDebris, &s_BunnyDie[5]},
};

#define BUNNY_DEAD_RATE 8

STATE s_BunnyDead[] =
{
    {BUNNY_DIE + 0, BUNNY_DEAD_RATE, NullAnimator, &s_BunnyDie[1]},
    {BUNNY_DIE + 0, SF_QUICK_CALL,  BunnySpew, &s_BunnyDie[2]},
    {BUNNY_DIE + 1, BUNNY_DEAD_RATE, NullAnimator, &s_BunnyDead[3]},
    {BUNNY_DIE + 2, BUNNY_DEAD_RATE, NullAnimator, &s_BunnyDead[4]},
    {BUNNY_DEAD, SF_QUICK_CALL, QueueFloorBlood, &s_BunnyDead[5]},
    {BUNNY_DEAD, BUNNY_DEAD_RATE, DoActorDebris, &s_BunnyDead[5]},
};

STATEp sg_BunnyDie[] =
{
    s_BunnyDie
};

STATEp sg_BunnyDead[] =
{
    s_BunnyDead
};

STATE s_BunnyDeathJump[] =
{
    {BUNNY_DIE + 0, BUNNY_DIE_RATE, DoActorDeathMove, &s_BunnyDeathJump[0]}
};

STATE s_BunnyDeathFall[] =
{
    {BUNNY_DIE + 1, BUNNY_DIE_RATE, DoActorDeathMove, &s_BunnyDeathFall[0]}
};

STATEp sg_BunnyDeathJump[] =
{
    s_BunnyDeathJump
};

STATEp sg_BunnyDeathFall[] =
{
    s_BunnyDeathFall
};

/*
STATEp *Stand[MAX_WEAPONS];
STATEp *Run;
STATEp *Jump;
STATEp *Fall;
STATEp *Crawl;
STATEp *Swim;
STATEp *Fly;
STATEp *Rise;
STATEp *Sit;
STATEp *Look;
STATEp *Climb;
STATEp *Pain;
STATEp *Death1;
STATEp *Death2;
STATEp *Dead;
STATEp *DeathJump;
STATEp *DeathFall;
STATEp *CloseAttack[2];
STATEp *Attack[6];
STATEp *Special[2];
*/

ACTOR_ACTION_SET BunnyActionSet =
{
    sg_BunnyStand,
    sg_BunnyRun,
    sg_BunnyJump,
    sg_BunnyFall,
    NULL,                               // sg_BunnyCrawl,
    NULL,                               // sg_BunnySwim,
    NULL,                               // sg_BunnyFly,
    NULL,                               // sg_BunnyRise,
    NULL,                               // sg_BunnySit,
    NULL,                               // sg_BunnyLook,
    NULL,                               // climb
    sg_BunnyPain,
    sg_BunnyDie,
    NULL,
    sg_BunnyDead,
    sg_BunnyDeathJump,
    sg_BunnyDeathFall,
    {NULL},
    {1024},
    {NULL},
    {1024},
    {sg_BunnyHeart, sg_BunnyRun},
    NULL,
    NULL
};

ACTOR_ACTION_SET BunnyWhiteActionSet =
{
    sg_BunnyStand,
    sg_BunnyRun,
    sg_BunnyJump,
    sg_BunnyFall,
    NULL,                               // sg_BunnyCrawl,
    NULL,                               // sg_BunnySwim,
    NULL,                               // sg_BunnyFly,
    NULL,                               // sg_BunnyRise,
    NULL,                               // sg_BunnySit,
    NULL,                               // sg_BunnyLook,
    NULL,                               // climb
    sg_BunnyPain,                       // pain
    sg_BunnyDie,
    NULL,
    sg_BunnyDead,
    sg_BunnyDeathJump,
    sg_BunnyDeathFall,
    {sg_BunnySwipe},
    {1024},
//    {sg_BunnyJumpAttack, sg_BunnySwipe},
//    {800, 1024},
    {sg_BunnySwipe},
    {1024},
    {sg_BunnyHeart, sg_BunnySwipe},
    NULL,
    NULL
};

int
SetupBunny(short SpriteNum)
{
    SPRITEp sp = &sprite[SpriteNum];
    USERp u;
    ANIMATOR DoActorDecide;

    if (TEST(sp->cstat, CSTAT_SPRITE_RESTORE))
    {
        u = User[SpriteNum];
        ASSERT(u);
    }
    else
    {
        User[SpriteNum] = u = SpawnUser(SpriteNum, BUNNY_RUN_R0, s_BunnyRun[0]);
        u->Health = 10;
    }

    Bunny_Count++;
    //if(Bunny_Count > 20)
    //    {
    //    KillSprite(SpriteNum);
    //    Bunny_Count--;
    //    return(0);
    //    }

    ChangeState(SpriteNum, s_BunnyRun[0]);
    u->StateEnd = s_BunnyDie;
    u->Rot = sg_BunnyRun;
    //sp->xrepeat = 64;
    //sp->yrepeat = 64;
    u->ShellNum = 0; // Not Pregnant right now
    u->FlagOwner = 0;

    sp->clipdist = (150) >> 2;

    if (sp->pal == PALETTE_PLAYER1)
    {
        EnemyDefaults(SpriteNum, &BunnyWhiteActionSet, &WhiteBunnyPersonality);
        u->Attrib = &WhiteBunnyAttrib;
        sp->xrepeat = 96;
        sp->yrepeat = 90;

        sp->clipdist = 200>>2;

        if (!TEST(sp->cstat, CSTAT_SPRITE_RESTORE))
            u->Health = 60;
    }
    else if (sp->pal == PALETTE_PLAYER8) // Male Rabbit
    {
        EnemyDefaults(SpriteNum, &BunnyActionSet, &BunnyPersonality);
        u->Attrib = &BunnyAttrib;
        //sp->xrepeat = 76;
        //sp->yrepeat = 70;

        //sp->shade = 0; // darker
        if (!TEST(sp->cstat, CSTAT_SPRITE_RESTORE))
            u->Health = 20;
        u->Flag1 = 0;
    }
    else
    {
        // Female Rabbit
        EnemyDefaults(SpriteNum, &BunnyActionSet, &BunnyPersonality);
        u->Attrib = &BunnyAttrib;
        u->spal = sp->pal = PALETTE_PLAYER0;
        u->Flag1 = SEC(5);
        //sp->shade = 0; // darker
    }

    DoActorSetSpeed(SpriteNum, FAST_SPEED);

    SET(u->Flags, SPR_XFLIP_TOGGLE);

    u->zclip = Z(16);
    u->floor_dist = Z(8);
    u->ceiling_dist = Z(8);
    u->lo_step = Z(16);

    return 0;
}

int
GetBunnyJumpHeight(short jump_speed, short jump_grav)
{
    int jump_iterations;
    int height;

    jump_speed = labs(jump_speed);

    jump_iterations = jump_speed / (jump_grav * ACTORMOVETICS);

    height = jump_speed * jump_iterations * ACTORMOVETICS;

    height = DIV256(height);

    return DIV2(height);
}

int
PickBunnyJumpSpeed(short SpriteNum, int pix_height)
{
    USERp u = User[SpriteNum];

    ASSERT(pix_height < 128);

    u->jump_speed = -600;
    u->jump_grav = 8;

    while (TRUE)
    {
        if (GetBunnyJumpHeight(u->jump_speed, u->jump_grav) > pix_height + 20)
            break;

        u->jump_speed -= 100;

        ASSERT(u->jump_speed > -3000);
    }

    return u->jump_speed;
}

//
// JUMP ATTACK
//

int
DoBunnyBeginJumpAttack(short SpriteNum)
{
    SPRITEp sp = &sprite[SpriteNum];
    USERp u = User[SpriteNum];
    SPRITEp psp = User[SpriteNum]->tgt_sp;
    int CanSeePlayer(short SpriteNum);
    short tang;

    tang = getangle(psp->x - sp->x, psp->y - sp->y);

    if (move_sprite(SpriteNum, sintable[NORM_ANGLE(tang+512)] >> 7, sintable[tang] >> 7,
                    0L, u->ceiling_dist, u->floor_dist, CLIPMASK_ACTOR, ACTORMOVETICS))
        sp->ang = NORM_ANGLE(sp->ang + 1024) + (RANDOM_NEG(256, 6) >> 6);
    else
        sp->ang = NORM_ANGLE(tang + (RANDOM_NEG(256, 6) >> 6));

    DoActorSetSpeed(SpriteNum, FAST_SPEED);

    //u->jump_speed = -800;
    PickJumpMaxSpeed(SpriteNum, -400); // was -800

    SET(u->Flags, SPR_JUMPING);
    RESET(u->Flags, SPR_FALLING);

    // set up individual actor jump gravity
    u->jump_grav = 17; // was 8

    // if I didn't do this here they get stuck in the air sometimes
    DoActorZrange(SpriteNum);

    DoJump(SpriteNum);

    return 0;
}

int
DoBunnyMoveJump(short SpriteNum)
{
    SPRITEp sp = &sprite[SpriteNum];
    USERp u = User[SpriteNum];

    if (TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
    {
        int nx, ny;

        // Move while jumping
        nx = sp->xvel * (int) sintable[NORM_ANGLE(sp->ang + 512)] >> 14;
        ny = sp->xvel * (int) sintable[sp->ang] >> 14;

        move_actor(SpriteNum, nx, ny, 0L);

        if (TEST(u->Flags, SPR_JUMPING))
            DoActorJump(SpriteNum);
        else
            DoActorFall(SpriteNum);
    }

    DoActorZrange(SpriteNum);

    if (!TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
    {
//        if (DoBunnyQuickJump(SpriteNum))
//            return (0);

        InitActorDecide(SpriteNum);
    }

    return 0;
}

int
DoPickCloseBunny(short SpriteNum)
{
    USERp u = User[SpriteNum],tu;
    SPRITEp sp = &sprite[SpriteNum],tsp;
    int dist, near_dist = 1000, a,b,c;
    short i, nexti;
    //short BunnyCount=0, Bunny_Result = -1;

    // if actor can still see the player
    int look_height = SPRITEp_TOS(sp);
    SWBOOL ICanSee = FALSE;

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_ENEMY], i, nexti)
    {
        tsp = &sprite[i];
        tu = User[i];

        if (sp == tsp) continue;

        if (tu->ID != BUNNY_RUN_R0) continue;

        DISTANCE(tsp->x, tsp->y, sp->x, sp->y, dist, a, b, c);

        if (dist > near_dist) continue;

        ICanSee = FAFcansee(sp->x, sp->y, look_height, sp->sectnum, tsp->x, tsp->y, SPRITEp_UPPER(tsp), tsp->sectnum);

        if (ICanSee && dist < near_dist && tu->ID == BUNNY_RUN_R0)
        {
            near_dist = dist;
            u->tgt_sp = u->lo_sp = tsp;
            //Bunny_Result = i;
            return i;
        }
    }
    return -1;
}

int
DoBunnyQuickJump(short SpriteNum)
{
    SPRITEp sp = &sprite[SpriteNum];
    USERp u = User[SpriteNum];

    if (u->spal != PALETTE_PLAYER8) return FALSE;

    if (!u->lo_sp && u->spal == PALETTE_PLAYER8 && MoveSkip4)
        DoPickCloseBunny(SpriteNum);

    // Random Chance of like sexes fighting
    if (u->lo_sp)
    {
        short hit_sprite = u->lo_sp - sprite;
        SPRITEp tsp = u->lo_sp;
        USERp tu = User[hit_sprite];

        if (!tu || tu->ID != BUNNY_RUN_R0) return FALSE;

        // Not mature enough yet
        if (sp->xrepeat != 64 || sp->yrepeat != 64) return FALSE;
        if (tsp->xrepeat != 64 || tsp->yrepeat != 64) return FALSE;

        // Kill a rival
        // Only males fight
        if (tu->spal == sp->pal && RANDOM_RANGE(1000) > 995)
        {
            if (u->spal == PALETTE_PLAYER8 && tu->spal == PALETTE_PLAYER8)
            {
                PlaySound(DIGI_BUNNYATTACK, &sp->x, &sp->y, &sp->z, v3df_follow);
                PlaySound(DIGI_BUNNYDIE2, &tsp->x, &tsp->y, &tsp->z, v3df_follow);
                tu->Health = 0;

                // Blood fountains
                InitBloodSpray(hit_sprite,TRUE,-1);

                if (SpawnShrap(hit_sprite, SpriteNum))
                {
                    SetSuicide(hit_sprite);
                }
                else
                    DoActorDie(hit_sprite, SpriteNum);

                Bunny_Count--; // Bunny died

                u->lo_sp = NULL;
                return TRUE;
            }
        }
    }

    // Get layed!
    if (u->lo_sp && u->spal == PALETTE_PLAYER8) // Only males check this
    {
        short hit_sprite = u->lo_sp - sprite;
        SPRITEp tsp = u->lo_sp;
        USERp tu = User[hit_sprite];

        if (!tu || tu->ID != BUNNY_RUN_R0) return FALSE;

        // Not mature enough to mate yet
        if (sp->xrepeat != 64 || sp->yrepeat != 64) return FALSE;
        if (tsp->xrepeat != 64 || tsp->yrepeat != 64) return FALSE;

        if (tu->ShellNum <= 0 && tu->WaitTics <= 0 && u->WaitTics <= 0)
        {
            if (TEST(tsp->extra, SPRX_PLAYER_OR_ENEMY))
            {
                PLAYERp pp = NULL;

                if (RANDOM_RANGE(1000) < 995 && tu->spal != PALETTE_PLAYER0) return FALSE;

                DoActorPickClosePlayer(SpriteNum);

                if (User[u->tgt_sp-sprite]->PlayerP)
                    pp = User[u->tgt_sp-sprite]->PlayerP;

                if (tu->spal != PALETTE_PLAYER0)
                {
                    if (tu->Flag1 > 0) return FALSE;
                    tu->FlagOwner = 1; // FAG!
                    tu->Flag1 = SEC(10);
                    if (pp)
                    {
                        short choose_snd;
                        int fagsnds[] = {DIGI_FAGRABBIT1,DIGI_FAGRABBIT2,DIGI_FAGRABBIT3};

                        if (pp == Player+myconnectindex)
                        {
                            choose_snd = STD_RANDOM_RANGE(2<<8)>>8;
                            if (FAFcansee(sp->x,sp->y,SPRITEp_TOS(sp),sp->sectnum,pp->posx, pp->posy, pp->posz, pp->cursectnum) && FACING(sp, u->tgt_sp))
                                PlayerSound(fagsnds[choose_snd],&pp->posx,&pp->posy,&pp->posz,v3df_doppler|v3df_follow|v3df_dontpan,pp);
                        }
                    }
                }
                else
                {
                    if (pp && RANDOM_RANGE(1000) > 200)
                    {
                        short choose_snd;
                        int straightsnds[] = {DIGI_RABBITHUMP1,DIGI_RABBITHUMP2,
                                              DIGI_RABBITHUMP3,DIGI_RABBITHUMP4};

                        if (pp == Player+myconnectindex)
                        {
                            choose_snd = STD_RANDOM_RANGE(3<<8)>>8;
                            if (FAFcansee(sp->x,sp->y,SPRITEp_TOS(sp),sp->sectnum,pp->posx, pp->posy, pp->posz, pp->cursectnum) && FACING(sp, u->tgt_sp))
                                PlayerSound(straightsnds[choose_snd],&pp->posx,&pp->posy,&pp->posz,v3df_doppler|v3df_follow|v3df_dontpan,pp);
                        }
                    }
                }

                sp->x = tsp->x; // Mount up little bunny
                sp->y = tsp->y;
                sp->ang = tsp->ang;
                sp->ang = NORM_ANGLE(sp->ang + 1024);
                HelpMissileLateral(SpriteNum, 2000L);
                sp->ang = tsp->ang;
                u->Vis = sp->ang;  // Remember angles for later
                tu->Vis = tsp->ang;

                NewStateGroup(SpriteNum, sg_BunnyScrew);
                NewStateGroup(hit_sprite, sg_BunnyScrew);
                if (gs.ParentalLock || Global_PLock)
                {
                    SET(sp->cstat, CSTAT_SPRITE_INVISIBLE); // Turn em' invisible
                    SET(tsp->cstat, CSTAT_SPRITE_INVISIBLE); // Turn em' invisible
                }
                u->WaitTics = tu->WaitTics = SEC(10);  // Mate for this long
                return TRUE;
            }
        }
    }

    return FALSE;
}

int
NullBunny(short SpriteNum)
{
    USERp u = User[SpriteNum];

    if (TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
    {
        if (TEST(u->Flags, SPR_JUMPING))
            DoActorJump(SpriteNum);
        else
            DoActorFall(SpriteNum);
    }

    // stay on floor unless doing certain things
    if (!TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
        KeepActorOnFloor(SpriteNum);

    if (TEST(u->Flags,SPR_SLIDING))
        DoActorSlide(SpriteNum);

    DoActorSectorDamage(SpriteNum);

    return 0;
}

int DoBunnyPain(short SpriteNum)
{
    USERp u = User[SpriteNum];

    NullBunny(SpriteNum);

    if ((u->WaitTics -= ACTORMOVETICS) <= 0)
        InitActorDecide(SpriteNum);
    return 0;
}

int DoBunnyRipHeart(short SpriteNum)
{
    SPRITEp sp = &sprite[SpriteNum];
    USERp u = User[SpriteNum];

    SPRITEp tsp = u->tgt_sp;

    NewStateGroup(SpriteNum, sg_BunnyHeart);
    u->WaitTics = 6 * 120;

    // player face bunny
    tsp->ang = getangle(sp->x - tsp->x, sp->y - tsp->y);
    return 0;
}

int DoBunnyStandKill(short SpriteNum)
{
    SPRITEp sp = &sprite[SpriteNum];
    USERp u = User[SpriteNum];

    NullBunny(SpriteNum);

    // Growl like the bad ass bunny you are!
    if (RANDOM_RANGE(1000) > 800)
        PlaySound(DIGI_BUNNYATTACK,&sp->x,&sp->y,&sp->z,v3df_none);

    if ((u->WaitTics -= ACTORMOVETICS) <= 0)
        NewStateGroup(SpriteNum, sg_BunnyRun);
    return 0;
}

void BunnyHatch(short Weapon)
{
    SPRITEp wp = &sprite[Weapon];
    USERp wu = User[Weapon];

    short New,i;
    SPRITEp np;
    USERp nu;
#define MAX_BUNNYS 1
    short rip_ang[MAX_BUNNYS];

    rip_ang[0] = RANDOM_P2(2048);

    for (i = 0; i < MAX_BUNNYS; i++)
    {
        New = COVERinsertsprite(wp->sectnum, STAT_DEFAULT);
        np = &sprite[New];
        memset(np,0,sizeof(SPRITE));
        np->sectnum = wp->sectnum;
        np->statnum = STAT_DEFAULT;
        np->x = wp->x;
        np->y = wp->y;
        np->z = wp->z;
        np->owner = -1;
        np->xrepeat = 30;  // Baby size
        np->yrepeat = 24;
        np->ang = rip_ang[i];
        np->pal = 0;
        SetupBunny(New);
        nu = User[New];
        np->shade = wp->shade;

        // make immediately active
        SET(nu->Flags, SPR_ACTIVE);
        if (RANDOM_RANGE(1000) > 500) // Boy or Girl?
            nu->spal = np->pal = PALETTE_PLAYER0; // Girl
        else
        {
            nu->spal = np->pal = PALETTE_PLAYER8; // Boy
            // Oops, mommy died giving birth to a boy
            if (RANDOM_RANGE(1000) > 500)
            {
                wu->Health = 0;
                Bunny_Count--; // Bunny died

                // Blood fountains
                InitBloodSpray(Weapon,TRUE,-1);

                if (SpawnShrap(Weapon, New))
                {
                    SetSuicide(Weapon);
                }
                else
                    DoActorDie(Weapon, New);
            }
        }

        nu->ShellNum = 0; // Not Pregnant right now

        NewStateGroup(New, nu->ActorActionSet->Jump);
        nu->ActorActionFunc = DoActorMoveJump;
        DoActorSetSpeed(New, FAST_SPEED);
        PickJumpMaxSpeed(New, -600);

        SET(nu->Flags, SPR_JUMPING);
        RESET(nu->Flags, SPR_FALLING);

        nu->jump_grav = 8;

        // if I didn't do this here they get stuck in the air sometimes
        DoActorZrange(New);

        DoActorJump(New);
    }
}

int BunnyHatch2(short Weapon)
{
    SPRITEp wp = &sprite[Weapon];

    short New;
    SPRITEp np;
    USERp nu;

    New = COVERinsertsprite(wp->sectnum, STAT_DEFAULT);
    np = &sprite[New];
    memset(np,0,sizeof(SPRITE));
    np->sectnum = wp->sectnum;
    np->statnum = STAT_DEFAULT;
    np->x = wp->x;
    np->y = wp->y;
    np->z = wp->z;
    np->owner = -1;
    np->xrepeat = 30;  // Baby size
    np->yrepeat = 24;
    np->ang = RANDOM_P2(2048);
    np->pal = 0;
    SetupBunny(New);
    nu = User[New];
    np->shade = wp->shade;

    // make immediately active
    SET(nu->Flags, SPR_ACTIVE);
    if (RANDOM_RANGE(1000) > 500) // Boy or Girl?
    {
        nu->spal = np->pal = PALETTE_PLAYER0; // Girl
        nu->Flag1 = SEC(5);
    }
    else
    {
        nu->spal = np->pal = PALETTE_PLAYER8; // Boy
        nu->Flag1 = 0;
    }

    nu->ShellNum = 0; // Not Pregnant right now

    NewStateGroup(New, nu->ActorActionSet->Jump);
    nu->ActorActionFunc = DoActorMoveJump;
    DoActorSetSpeed(New, FAST_SPEED);
    if (TEST_BOOL3(wp))
    {
        PickJumpMaxSpeed(New, -600-RANDOM_RANGE(600));
        np->xrepeat = np->yrepeat = 64;
        np->xvel = 150 + RANDOM_RANGE(1000);
        nu->Health = 1; // Easy to pop. Like shootn' skeet.
        np->ang -= RANDOM_RANGE(128);
        np->ang += RANDOM_RANGE(128);
    }
    else
        PickJumpMaxSpeed(New, -600);

    SET(nu->Flags, SPR_JUMPING);
    RESET(nu->Flags, SPR_FALLING);

    nu->jump_grav = 8;
    nu->FlagOwner = 0;

    nu->active_range = 75000; // Set it far

    // if I didn't do this here they get stuck in the air sometimes
    DoActorZrange(New);

    DoActorJump(New);

    return New;
}

int
DoBunnyMove(short SpriteNum)
{
    SPRITEp sp = &sprite[SpriteNum];
    USERp u = User[SpriteNum];

    // Parental lock crap
    if (TEST(sp->cstat, CSTAT_SPRITE_INVISIBLE))
        RESET(sp->cstat, CSTAT_SPRITE_INVISIBLE); // Turn em' back on

    // Sometimes they just won't die!
    if (u->Health <= 0)
        SetSuicide(SpriteNum);

    if (u->scale_speed)
    {
        DoScaleSprite(SpriteNum);
    }

    if (TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
    {
        if (TEST(u->Flags, SPR_JUMPING))
            DoActorJump(SpriteNum);
        else
            DoActorFall(SpriteNum);
    }

    // if on a player/enemy sprite jump quickly
    if (!TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
    {
        DoBunnyQuickJump(SpriteNum);
    }

    if (TEST(u->Flags, SPR_SLIDING))
        DoActorSlide(SpriteNum);

    if (u->track >= 0)
        ActorFollowTrack(SpriteNum, ACTORMOVETICS);
    else
        (*u->ActorActionFunc)(SpriteNum);

    // stay on floor unless doing certain things
    if (!TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
        KeepActorOnFloor(SpriteNum);

    DoActorSectorDamage(SpriteNum);

    if (RANDOM_RANGE(1000) > 985 && sp->pal != PALETTE_PLAYER1 && u->track < 0)
    {
        switch (sector[sp->sectnum].floorpicnum)
        {
        case 153:
        case 154:
        case 193:
        case 219:
        case 2636:
        case 2689:
        case 3561:
        case 3562:
        case 3563:
        case 3564:
            NewStateGroup(SpriteNum,sg_BunnyStand);
            break;
        default:
            sp->ang = NORM_ANGLE(RANDOM_RANGE(2048 << 6) >> 6);
            u->jump_speed = -350;
            DoActorBeginJump(SpriteNum);
            u->ActorActionFunc = DoActorMoveJump;
            break;
        }
    }

    return 0;
}

int
BunnySpew(short SpriteNum)
{
    //InitBloodSpray(SpriteNum,TRUE,-1);
    InitBloodSpray(SpriteNum,TRUE,-1);
    return 0;
}

int
DoBunnyEat(short SpriteNum)
{
    SPRITEp sp = &sprite[SpriteNum];
    USERp u = User[SpriteNum];

    if (TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
    {
        if (TEST(u->Flags, SPR_JUMPING))
            DoActorJump(SpriteNum);
        else
            DoActorFall(SpriteNum);
    }

    // if on a player/enemy sprite jump quickly
    if (!TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
    {
        DoBunnyQuickJump(SpriteNum);
    }

    if (TEST(u->Flags, SPR_SLIDING))
        DoActorSlide(SpriteNum);

    // stay on floor unless doing certain things
    if (!TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
        KeepActorOnFloor(SpriteNum);

    DoActorSectorDamage(SpriteNum);

    switch (sector[sp->sectnum].floorpicnum)
    {
    case 153:
    case 154:
    case 193:
    case 219:
    case 2636:
    case 2689:
    case 3561:
    case 3562:
    case 3563:
    case 3564:
        if (RANDOM_RANGE(1000) > 970)
            NewStateGroup(SpriteNum,sg_BunnyRun);
        break;
    default:
        NewStateGroup(SpriteNum,sg_BunnyRun);
        break;
    }
    return 0;
}

int
DoBunnyScrew(short SpriteNum)
{
    SPRITEp sp = &sprite[SpriteNum];
    USERp u = User[SpriteNum];

    if (TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
    {
        if (TEST(u->Flags, SPR_JUMPING))
            DoActorJump(SpriteNum);
        else
            DoActorFall(SpriteNum);
    }

    if (TEST(u->Flags, SPR_SLIDING))
        DoActorSlide(SpriteNum);

    // stay on floor unless doing certain things
    if (!TEST(u->Flags, SPR_JUMPING | SPR_FALLING))
        KeepActorOnFloor(SpriteNum);

    DoActorSectorDamage(SpriteNum);

    if (RANDOM_RANGE(1000) > 990) // Bunny sex sounds
    {
        if (!gs.ParentalLock && !Global_PLock)
            PlaySound(DIGI_BUNNYATTACK, &sp->x, &sp->y, &sp->z, v3df_follow);
    }

    u->WaitTics -= ACTORMOVETICS;

    if ((u->FlagOwner || u->spal == PALETTE_PLAYER0) && u->WaitTics > 0) // Keep Girl still
        NewStateGroup(SpriteNum,sg_BunnyScrew);

    if (u->spal == PALETTE_PLAYER0 && u->WaitTics <= 0) // Female has baby
    {
        u->Flag1 = SEC(5); // Count down to babies
        u->ShellNum = 1; // She's pregnant now
    }

    if (u->WaitTics <= 0)
    {
        RESET(sp->cstat, CSTAT_SPRITE_INVISIBLE); // Turn em' back on
        u->FlagOwner = 0;
        NewStateGroup(SpriteNum,sg_BunnyRun);
    }

    return 0;
}

int
DoBunnyGrowUp(short SpriteNum)
{
    SPRITEp sp = &sprite[SpriteNum];
    USERp u = User[SpriteNum];

    if (sp->pal == PALETTE_PLAYER1) return 0;   // Don't bother white bunnies

    if ((u->Counter -= ACTORMOVETICS) <= 0)
    {
        if ((++sp->xrepeat) > 64) sp->xrepeat = 64;
        if ((++sp->yrepeat) > 64) sp->yrepeat = 64;
        u->Counter = 60;
    }

    // Don't go homo too much!
    if (sp->pal != PALETTE_PLAYER0 && u->Flag1 > 0)
        u->Flag1 -= ACTORMOVETICS;

    // Gestation period for female rabbits
    if (sp->pal == PALETTE_PLAYER0 && u->ShellNum > 0)
    {
        if ((u->Flag1 -= ACTORMOVETICS) <= 0)
        {
            if (Bunny_Count < 20)
            {
                PlaySound(DIGI_BUNNYDIE2, &sp->x, &sp->y, &sp->z, v3df_follow);
                BunnyHatch(SpriteNum); // Baby time
            }
            u->ShellNum = 0; // Not pregnent anymore
        }
    }

    return 0;
}

#include "saveable.h"

static saveable_code saveable_bunny_code[] =
{
    SAVE_CODE(SetupBunny),
    SAVE_CODE(GetBunnyJumpHeight),
    SAVE_CODE(PickBunnyJumpSpeed),
    SAVE_CODE(DoBunnyBeginJumpAttack),
    SAVE_CODE(DoBunnyMoveJump),
    SAVE_CODE(DoPickCloseBunny),
    SAVE_CODE(DoBunnyQuickJump),
    SAVE_CODE(NullBunny),
    SAVE_CODE(DoBunnyPain),
    SAVE_CODE(DoBunnyRipHeart),
    SAVE_CODE(DoBunnyStandKill),
    SAVE_CODE(BunnyHatch),
    SAVE_CODE(BunnyHatch2),
    SAVE_CODE(DoBunnyMove),
    SAVE_CODE(BunnySpew),
    SAVE_CODE(DoBunnyEat),
    SAVE_CODE(DoBunnyScrew),
    SAVE_CODE(DoBunnyGrowUp),
};

static saveable_data saveable_bunny_data[] =
{
    SAVE_DATA(BunnyBattle),
    SAVE_DATA(BunnyOffense),
    SAVE_DATA(BunnyBroadcast),
    SAVE_DATA(BunnySurprised),
    SAVE_DATA(BunnyEvasive),
    SAVE_DATA(BunnyLostTarget),
    SAVE_DATA(BunnyCloseRange),
    SAVE_DATA(BunnyWander),

    SAVE_DATA(WhiteBunnyPersonality),
    SAVE_DATA(BunnyPersonality),

    SAVE_DATA(WhiteBunnyAttrib),
    SAVE_DATA(BunnyAttrib),

    SAVE_DATA(s_BunnyRun),
    SAVE_DATA(sg_BunnyRun),
    SAVE_DATA(s_BunnyStand),
    SAVE_DATA(sg_BunnyStand),
    SAVE_DATA(s_BunnyScrew),
    SAVE_DATA(sg_BunnyScrew),
    SAVE_DATA(s_BunnySwipe),
    SAVE_DATA(sg_BunnySwipe),
    SAVE_DATA(s_BunnyHeart),
    SAVE_DATA(sg_BunnyHeart),
    SAVE_DATA(s_BunnyPain),
    SAVE_DATA(sg_BunnyPain),
    SAVE_DATA(s_BunnyJump),
    SAVE_DATA(sg_BunnyJump),
    SAVE_DATA(s_BunnyFall),
    SAVE_DATA(sg_BunnyFall),
    SAVE_DATA(s_BunnyJumpAttack),
    SAVE_DATA(sg_BunnyJumpAttack),
    SAVE_DATA(s_BunnyDie),
    SAVE_DATA(sg_BunnyDie),
    SAVE_DATA(s_BunnyDead),
    SAVE_DATA(sg_BunnyDead),
    SAVE_DATA(s_BunnyDeathJump),
    SAVE_DATA(sg_BunnyDeathJump),
    SAVE_DATA(s_BunnyDeathFall),
    SAVE_DATA(sg_BunnyDeathFall),

    SAVE_DATA(BunnyActionSet),
    SAVE_DATA(BunnyWhiteActionSet),
};

saveable_module saveable_bunny =
{
    // code
    saveable_bunny_code,
    SIZ(saveable_bunny_code),

    // data
    saveable_bunny_data,
    SIZ(saveable_bunny_data)
};


code:
//-------------------------------------------------------------------------
/*
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
*/
//-------------------------------------------------------------------------

/****************************************
Removed sounds that were causing "Could
not load" error messages.
****************************************/

#include "build.h"

#include "names2.h"
#include "game.h"
#include "tags.h"
#include "common_game.h"
#include "break.h"
#include "quake.h"
#include "pal.h"
#include "cache.h"
#include "sounds.h"
#include "network.h"

// Run the game with the -CACHEPRINT option and redirect to a file.
// It will save out the tile and sound number every time one caches.
//
// sw -map $bullet -cacheprint > foofile
extern SWBOOL PreCaching;

// player weaponry, item usage, etc. Precache every time.
short Player_SCTable[] =
{
    1,2,3,4,5,6,7,8,9,10, // weapons
    11,12,14,16,18,20,
    22,23,24,25,26,27,28,30,
    31,32,33,34,35,40,145,291,445,362,269,
    158,476, // underwater
    47,359, // cloaking
    48,50, // dead head
    196,52,53,54,55, // splash & getting items
    56,57,58,73,74,410, // bodies hitting ground
    484,442, // teleport & respawn
    417,418, // healing
    238,239,240,241,242,243,244, // bring weapons up
    181,182,183,184,187,216, // explosions
    272,273,274,275,486, // nuke associated sounds
    276,277,278,279,477, // chem bomb
    280,281,282,283,284,288,289,290,450, // various player sounds
    295, // armor hit
    312, // sword clank
    324,325,209, // unlocking sound
    395,396,411, // gibs
    175, // drip
    435,436,311,221,220,227, // breakage
    246,247,248,249,250,251,252,253,254,255,256, // common player talk
    257,258,259,260,261,262,263,264,266,267,438,439,440,441,
    326, // ancient chinese secret
    330,331,332,333,334,335,336,337,338,339,340, // player kill talk
    341,342,343,344,345,346,347,348,
    376,377,378,379,380,381,382,383,384,385,386,387, // more asst. talking
    370,443 // repair talk
};

// Actor specific sound tables. Cache only if the actor appears on the map.
// Exceptions would include ghosts, which are spawned by coolies, and
// rippers, which are spawned by the serpent boss.
short Coolie_SCTable[] =
{
    75,76,77,78,79
};

short Ghost_SCTable[] =
{
    80,81,82,83,84,85,86,87,213
};

short Ninja_SCTable[] =
{
    88,89,90,91,92,93,94,412,
    319, // firing sound
    430 // death by sword
};

short Ripper_SCTable[] =
{
    95,96,97,98,99,100,431
};

short Ripper2_SCTable[] =
{
    313,314,315,316,317,318,431
};

short Head_SCTable[] =
{
    115,116,117,118 // accursed heads
};

short Hornet_SCTable[] =
{
    119,120,121,122
};

short Guardian_SCTable[] =
{
    101,102,103,104,105,106,107
};

short Serpent_SCTable[] =
{
    123,124,125,126,127,128,129,130,131 // serpent boss
};

short Sumo_SCTable[] =
{
    320,321,322,323 // sumo boss
};

short Bunny_SCTable[] =
{
    424,425,426,427,428
};

short Toilet_SCTable[] =
{
    388,389,390,391,392,393,488,489,490 // anime girl on toilet
}; // I suspect some of these are no longer in use

short Trash_SCTable[] =
{
    416 // I heard the trash can was an actor, so here is is
};

short Pachinko_SCTable[] =
{
    419,420,421,422,423
};

void PreCacheSoundList(short table[], int num);
void PreCacheTable(short table[], int num);
void PreCacheGhost(void);

void
SetupPreCache(void)
{
    if (PreCaching)
    {
        precache();

        PreCacheSoundList(Player_SCTable, SIZ(Player_SCTable));

        // actors cache ranges are called from SpriteSetup
        // only caches the actor if its on the level

        // weapons
        PreCacheRange(2000, 2227);
        PreCacheRange(4090, 4093);
        // Explosions
        PreCacheRange(3072, 3225);
        // ninja player character
        PreCacheRange(1024, 1175);
        // console
        PreCacheRange(2380, 2409);
        PreCacheRange(3600, 3645);
        PreCacheRange(2434, 2435);
        // common
        PreCacheRange(204, 208);
        // message font
        PreCacheRange(4608, 4701);
        // gibs
        PreCacheRange(1150,1568);
        PreCacheRange(1685,1690);
        PreCacheRange(900,944);
        PreCacheRange(1670,1681);
        // blood
        PreCacheRange(1710,1715);
        PreCacheRange(2410,2425);
        PreCacheRange(389,389); // blood puddle by itself in art file
        PreCacheRange(2500,2503);
        // shrap
        PreCacheRange(3840,3911);
        PreCacheRange(3924,3947);
        PreCacheRange(1397,1398);
        // water *** animated tiles, can be deleted now ***
        // PreCacheRange(780,794);
        // switches
        PreCacheRange(561,584);
        PreCacheRange(551,552);
        PreCacheRange(1846,1847);
        PreCacheRange(1850,1859);
        // bullet smoke
        PreCacheRange(1748,1753);
        // small blue font
        PreCacheRange(2930,3023);
        // gas can
        PreCacheRange(3038,3042);
        // lava *** animated tiles, can be deleted now ***
        // PreCacheRange(175,182);
        // gas clouds & teleport effect
        PreCacheRange(3240,3277);
        // nuke mushroom cloud
        PreCacheRange(3280,3300);
        // blood drops
        PreCacheRange(1718,1721);
        // smoke
        PreCacheRange(3948,3968);
        // footprints
        PreCacheRange(2490,2492);
        // player fists
        PreCacheRange(4070,4077);
        PreCacheRange(4050,4051);
        PreCacheRange(4090,4093);
        // fish actor
        PreCacheRange(3760,3771);
        PreCacheRange(3780,3795);
        // coins
        PreCacheRange(2531,2533);
        // respawn markers & console keys
        PreCacheRange(2440,2467);
        // light/torch sprites
        PreCacheRange(537,548);
        PreCacheRange(521,528);
        PreCacheRange(512,515);
        PreCacheRange(396,399);
        PreCacheRange(443,446);
        // bubbles
        PreCacheRange(716,720);
        // bullet splashes
        PreCacheRange(772,776);
    }
}

void PreCacheRipper(void)
{
    PreCacheSoundList(Ripper_SCTable, SIZ(Ripper_SCTable));
    PreCacheRange(1580, 1644);
}

void PreCacheRipper2(void)
{
    PreCacheSoundList(Ripper2_SCTable, SIZ(Ripper2_SCTable));
    PreCacheRange(4320, 4427);
}

void PreCacheCoolie(void)
{
    PreCacheGhost();
    PreCacheSoundList(Coolie_SCTable, SIZ(Coolie_SCTable));
    PreCacheRange(1400, 1440);
    PreCacheRange(4260, 4276); // coolie explode
}

void PreCacheGhost(void)
{
    PreCacheSoundList(Ghost_SCTable, SIZ(Ghost_SCTable));
    PreCacheRange(4277, 4312);
}

void PreCacheSerpent(void)
{
    PreCacheSoundList(Serpent_SCTable, SIZ(Serpent_SCTable));
    PreCacheRange(960, 1016);
    PreCacheRange(1300, 1314);
}

void PreCacheGuardian(void)
{
    PreCacheSoundList(Guardian_SCTable, SIZ(Guardian_SCTable));
    PreCacheRange(1469,1497);
}

void PreCacheNinja(void)
{
    PreCacheSoundList(Ninja_SCTable, SIZ(Ninja_SCTable));
    PreCacheRange(4096, 4239);
}

void PreCacheNinjaGirl(void)
{
    //PreCacheSoundList(NinjaGirl_SCTable, SIZ(NinjaGirl_SCTable));
    PreCacheRange(5162, 5260);
}

void PreCacheSumo(void)
{
    PreCacheSoundList(Sumo_SCTable, SIZ(Sumo_SCTable));
    PreCacheRange(4490, 4544);
}

void PreCacheZilla(void)
{
    PreCacheSoundList(Sumo_SCTable, SIZ(Sumo_SCTable));
    PreCacheRange(4490, 4544);
}

void PreCacheEel(void)
{
    PreCacheRange(4430, 4479);
}

void PreCacheToiletGirl(void)
{
    PreCacheSoundList(Toilet_SCTable, SIZ(Toilet_SCTable));
    PreCacheRange(5023, 5027);
}

void PreCacheWashGirl(void)
{
    PreCacheSoundList(Toilet_SCTable, SIZ(Toilet_SCTable));
    PreCacheRange(5032, 5035);
}

void PreCacheCarGirl(void)
{
    PreCacheSoundList(Toilet_SCTable, SIZ(Toilet_SCTable));
    PreCacheRange(4594,4597);
}

void PreCacheMechanicGirl(void)
{
    PreCacheSoundList(Toilet_SCTable, SIZ(Toilet_SCTable));
    PreCacheRange(4590,4593);
}

void PreCacheSailorGirl(void)
{
    PreCacheSoundList(Toilet_SCTable, SIZ(Toilet_SCTable));
    PreCacheRange(4600,4602);
}

void PreCachePruneGirl(void)
{
    PreCacheSoundList(Toilet_SCTable, SIZ(Toilet_SCTable));
    PreCacheRange(4604,4604);
}

void PreCacheTrash(void)
{
    PreCacheSoundList(Trash_SCTable, SIZ(Trash_SCTable));
    PreCacheRange(2540, 2546);
}

void PreCacheBunny(void)
{
    PreCacheSoundList(Bunny_SCTable, SIZ(Bunny_SCTable));
    PreCacheRange(4550, 4584);
}

void PreCacheSkel(void)
{
    PreCacheRange(1320, 1396);
}

void PreCacheHornet(void)
{
    PreCacheSoundList(Hornet_SCTable, SIZ(Hornet_SCTable));
    PreCacheRange(800, 811);
}

void PreCacheSkull(void)
{
    PreCacheSoundList(Head_SCTable, SIZ(Head_SCTable));
    PreCacheRange(820, 854);
}

void PreCacheBetty(void)
{
    PreCacheRange(817, 819);
}

void PreCachePachinko(void)
{
    PreCacheRange(618,623);
    PreCacheRange(618,623);
    PreCacheRange(4768,4790);
    PreCacheRange(4792,4814);
    PreCacheRange(4816,4838);
    PreCacheRange(4840,4863);
    PreCacheSoundList(Pachinko_SCTable, SIZ(Pachinko_SCTable));
}

void PreCacheSoundList(short table[], int num)
{
    short j;

    for (j = 0; j < num; j++)
    {
        CacheSound(table[j], CACHE_SOUND_PRECACHE);
        AnimateCacheCursor();
    }
}

void
PreCacheTable(short table[], int num)
{
    short j;

    for (j = 0; j < num; j++)
    {
        SET(gotpic[table[j]>>3], 1<<(table[j]&7));
    }
}

void
PreCacheRange(short start_pic, short end_pic)
{
    short j;

    for (j = start_pic; j <= end_pic; j++)
    {
        SET(gotpic[j>>3], 1<<(j&7));
    }
}

void PreCacheAmbient(void)
{
    int i,nexti;
    int num;
    SPRITEp sp;
    extern AMB_INFO ambarray[];

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_AMBIENT], i, nexti)
    {
        sp = &sprite[i];

        num = sp->lotag;
        num = ambarray[num].diginame;

        CacheSound(num, CACHE_SOUND_PRECACHE);
    }
}

void PreCacheOverride(void)
{
    int i,nexti;

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_CEILING_FLOOR_PIC_OVERRIDE], i, nexti)
    {
        ASSERT(SPRITE_TAG2(i) >= 0 && SPRITE_TAG2(i) <= MAXTILES);
        SET_GOTPIC(SPRITE_TAG2(i));
    }
}

void PreCacheSoundSpot(void)
{
    int i,nexti;
    int num;
    SPRITEp sp;

    TRAVERSE_SPRITE_STAT(headspritestat[STAT_SOUND_SPOT], i, nexti)
    {
        sp = &sprite[i];

        num = SP_TAG13(sp); // tag4 is copied to tag13
        if (num > 0 && num < DIGI_MAX)
            CacheSound(num, CACHE_SOUND_PRECACHE);

        num = SP_TAG5(sp);
        if (num > 0 && num < DIGI_MAX)
            CacheSound(num, CACHE_SOUND_PRECACHE);

        num = SP_TAG6(sp);
        if (num > 0 && num < DIGI_MAX)
            CacheSound(num, CACHE_SOUND_PRECACHE);

        CacheSound(num, CACHE_SOUND_PRECACHE);
    }
}

void
PreCacheActor(void)
{
    int i;
    short pic;

    for (i=0; i < MAXSPRITES; i++)
    {
        if (sprite[i].statnum >= MAXSTATUS)
            continue;

        if (User[i])
            pic = User[i]->ID;
        else
            pic = sprite[i].picnum;

        switch (pic)
        {
        case COOLIE_RUN_R0:
            PreCacheCoolie();
            break;

        case NINJA_RUN_R0:
        case NINJA_CRAWL_R0:
            PreCacheNinja();
            break;

        case GORO_RUN_R0:
            PreCacheGuardian();
            break;

        case 1441:
        case COOLG_RUN_R0:
            PreCacheGhost();
            break;

        case EEL_RUN_R0:
            PreCacheEel();
            break;

        case SUMO_RUN_R0:
            PreCacheZilla();
            break;

        case ZILLA_RUN_R0:
            PreCacheSumo();
            break;

        case TOILETGIRL_R0:
            PreCacheToiletGirl();
            break;

        case WASHGIRL_R0:
            PreCacheWashGirl();
            break;

        case CARGIRL_R0:
            PreCacheCarGirl();
            break;

        case MECHANICGIRL_R0:
            PreCacheMechanicGirl();
            break;

        case SAILORGIRL_R0:
            PreCacheSailorGirl();
            break;

        case PRUNEGIRL_R0:
            PreCachePruneGirl();
            break;

        case TRASHCAN:
            PreCacheTrash();
            break;

        case BUNNY_RUN_R0:
            PreCacheBunny();
            break;

        case RIPPER_RUN_R0:
            PreCacheRipper();
            break;

        case RIPPER2_RUN_R0:
            PreCacheRipper2();
            break;

        case SERP_RUN_R0:
            PreCacheSerpent();
            break;

        case LAVA_RUN_R0:
            break;

        case SKEL_RUN_R0:
            PreCacheSkel();
            break;

        case HORNET_RUN_R0:
            PreCacheHornet();
            break;

        case SKULL_R0:
            PreCacheSkull();
            break;

        case BETTY_R0:
            PreCacheBetty();
            break;

        case GIRLNINJA_RUN_R0:
            PreCacheNinjaGirl();
            break;

        case 623:   // Pachinko win light
        case PACHINKO1:
        case PACHINKO2:
        case PACHINKO3:
        case PACHINKO4:
            PreCachePachinko();
            break;
        }
    }
}

void DoTheCache(void)
{
    extern char CacheLastLevel[32],LevelName[20];
    int i, cnt=0;

    PreCacheAmbient();
    PreCacheSoundSpot();
    PreCacheActor();
    PreCacheOverride();

    for (i = 0; i < MAXTILES; i++)
    {
        if ((TEST(gotpic[i>>3], 1<<(i&7))) && (!waloff[i]))
        {
            tileLoad(i);
            cnt++;
            if (!(cnt&7))
            {
                AnimateCacheCursor();
                handleevents();
                getpackets();
            }
        }
    }

    memset(gotpic,0,sizeof(gotpic));
    strcpy(CacheLastLevel, LevelName);
}

void
precache(void)
{
    int i;
    short j;
    SECTORp sectp;
    WALLp wp;
    SPRITEp sp;

    memset(gotpic,0,sizeof(gotpic));

    for (sectp = sector; sectp < &sector[numsectors]; sectp++)
    {
        j = sectp->ceilingpicnum;

        SET(gotpic[j>>3], 1<<(j&7));

        if (TEST(picanm[j].sf, PICANM_ANIMTYPE_MASK) >> PICANM_ANIMTYPE_SHIFT)
        {
            for (i = 1; i <= picanm[j].num; i++)
            {
                SET(gotpic[(j+i)>>3], 1<<((j+i)&7));
            }
        }

        j = sectp->floorpicnum;

        SET(gotpic[j>>3], 1<<(j&7));

        if (TEST(picanm[j].sf, PICANM_ANIMTYPE_MASK) >> PICANM_ANIMTYPE_SHIFT)
        {
            for (i = 1; i <= picanm[j].num; i++)
            {
                SET(gotpic[(j+i)>>3], 1<<((j+i)&7));
            }
        }

    }

    for (wp = wall; wp < &wall[numwalls]; wp++)
    {
        j = wp->picnum;

        SET(gotpic[j>>3], 1<<(j&7));

        if (TEST(picanm[j].sf, PICANM_ANIMTYPE_MASK) >> PICANM_ANIMTYPE_SHIFT)
        {
            for (i = 1; i <= picanm[j].num; i++)
            {
                SET(gotpic[(j+i)>>3], 1<<((j+i)&7));
            }
        }

        if (wp->overpicnum > 0 && wp->overpicnum < MAXTILES)
        {
            j = wp->overpicnum;
            SET(gotpic[j>>3], 1<<(j&7));

            if (TEST(picanm[j].sf, PICANM_ANIMTYPE_MASK) >> PICANM_ANIMTYPE_SHIFT)
            {
                for (i = 1; i <= picanm[j].num; i++)
                {
                    SET(gotpic[(j+i)>>3], 1<<((j+i)&7));
                }
            }

        }
    }

    for (sp = sprite; sp < &sprite[MAXSPRITES]; sp++)
    {
        if (sp->statnum < MAXSTATUS)
        {
            j = sp->picnum;

            SET(gotpic[j>>3], 1<<(j&7));
        }
    }
}

repo_name: orochi/nblood

license: mit


code:
//-------------------------------------------------------------------------
/*
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
*/
//-------------------------------------------------------------------------

#define CACHE_NONE          0       // don't use this - this is for ken
#define CACHE_LOCK_MAX      255
#define CACHE_LOCK_START    200
#define CACHE_UNLOCK_START  1
#define CACHE_UNLOCK_MAX    199

#define CACHE_SOUND_PRECACHE 0
#define CACHE_SOUND_PLAY     1

void SetupPreCache(void);
void PreCacheRange(short start_pic, short end_pic);
void DoTheCache(void);
void precache(void);


code:
//-------------------------------------------------------------------------
/*
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
*/
//-------------------------------------------------------------------------
#include "build.h"

#include "keys.h"
#include "names2.h"
#include "panel.h"
#include "game.h"
#include "mytypes.h"
#include "text.h"

#include "control.h"
#include "function.h"
//#include "inv.h"

SWBOOL CheatInputMode = FALSE;
char CheatInputString[256];
SWBOOL EveryCheat = FALSE;
SWBOOL ResCheat = FALSE;

void ResCheatOn(PLAYERp UNUSED(pp), char *UNUSED(cheat_string))
{
    ResCheat = TRUE;
}

void VoxCheat(PLAYERp UNUSED(pp), char *UNUSED(cheat_string))
{
    //gs.Voxel ^= 1;
}

void RestartCheat(PLAYERp UNUSED(pp), char *UNUSED(cheat_string))
{
    ExitLevel = TRUE;
}

void RoomCheat(PLAYERp UNUSED(pp), char *UNUSED(cheat_string))
{
    extern SWBOOL FAF_DebugView;
    FAF_DebugView ^= 1;
}

void SecretCheat(PLAYERp UNUSED(pp), char *UNUSED(cheat_string))
{
    gs.Stats = !gs.Stats;
}

void NextCheat(PLAYERp UNUSED(pp), char *UNUSED(cheat_string))
{
    Level++;
    ExitLevel = TRUE;
}

void PrevCheat(PLAYERp UNUSED(pp), char *UNUSED(cheat_string))
{
    Level--;
    ExitLevel = TRUE;
}

void MapCheat(PLAYERp pp, char *UNUSED(cheat_string))
{
    automapping ^= 1;

    if (automapping)
        MapSetAll2D(0);
    else
        MapSetAll2D(0xFF);

    sprintf(ds, "AUTOMAPPING %s", automapping ? "ON" : "OFF");
    PutStringInfo(pp, ds);
}

void LocCheat(PLAYERp UNUSED(pp), char *UNUSED(cheat_string))
{
    extern SWBOOL LocationInfo;
    LocationInfo++;
    if (LocationInfo > 2)
        LocationInfo = 0;
}

void WeaponCheat(PLAYERp UNUSED(pp), char *UNUSED(cheat_string))
{
    PLAYERp p;
    short pnum;
    unsigned int i;
    USERp u;

    TRAVERSE_CONNECT(pnum)
    {
        p = &Player[pnum];
        u = User[p->PlayerSprite];

        if (!TEST(p->Flags, PF_TWO_UZI))
        {
            SET(p->Flags, PF_TWO_UZI);
            SET(p->Flags, PF_PICKED_UP_AN_UZI);
        }

        // ALL WEAPONS
        if (!SW_SHAREWARE)
            p->WpnFlags = 0xFFFFFFFF;
        else
            p->WpnFlags = 0x0000207F;  // Disallows high weapon cheat in shareware

        for (i = 0; i < SIZ(p->WpnAmmo); i++)
        {
            p->WpnAmmo[i] = DamageData[i].max_ammo;
        }

        PlayerUpdateWeapon(p, u->WeaponNum);
    }
}

void GodCheat(PLAYERp pp, char *UNUSED(cheat_string))
{
    //
    // GOD mode
    //
    GodMode ^= 1;

    sprintf(ds, "GOD MODE %s", GodMode ? "ON" : "OFF");
    PutStringInfo(pp, ds);
}

void ClipCheat(PLAYERp pp, char *UNUSED(cheat_string))
{
    FLIP(pp->Flags, PF_CLIP_CHEAT);

    sprintf(ds, "NO CLIP MODE %s", TEST(pp->Flags, PF_CLIP_CHEAT) ? "ON" : "OFF");
    PutStringInfo(pp, ds);
}

void WarpCheat(PLAYERp pp, char *cheat_string)
{
    char *cp = cheat_string;
    int level_num;

    cp += sizeof("swtrek")-1;
    level_num = atol(cp);

    //int episode_num;
    //DSPRINTF(ds,"ep %d, lev %d",episode_num, level_num);
    //MONO_PRINT(ds);

    if (!SW_SHAREWARE)
    {
        if (level_num > 28 || level_num < 1)
            return;
    }
    else
    {
        if (level_num > 4 || level_num < 1)
            return;
    }

    Level = level_num;
    ExitLevel = TRUE;

    sprintf(ds, "ENTERING %1d", Level);
    PutStringInfo(pp, ds);
}

void ItemCheat(PLAYERp pp, char *cheat_string)
{
    //
    // Get all ITEMS
    //
    PLAYERp p;
    short pnum;
    short inv;
    int i;

    PutStringInfo(pp, "ITEMS");

    TRAVERSE_CONNECT(pnum)
    {
        p = &Player[pnum];
        memset(p->HasKey, TRUE, sizeof(p->HasKey));

        p->WpnShotgunAuto = 50;
        p->WpnRocketHeat = 5;
        p->WpnRocketNuke = 1;
        p->Armor = 100;

        for (inv = 0; inv < MAX_INVENTORY; inv++)
        {
            p->InventoryPercent[inv] = 100;
            //p->InventoryAmount[inv] = 1;
            p->InventoryAmount[inv] = InventoryData[inv].MaxInv;
            //PlayerUpdateInventory(p, inv);
        }

        PlayerUpdateInventory(p, p->InventoryNum);
        //p->InventoryNum = 0;
    }

    for (i=0; i<numsectors; i++)
    {
        if (SectUser[i] && SectUser[i]->stag == SECT_LOCK_DOOR)
            SectUser[i]->number = 0;  // unlock all doors of this type
    }

    WeaponCheat(pp, cheat_string);
    PlayerUpdateKeys(pp);
}

void EveryCheatToggle(PLAYERp pp, char *cheat_string)
{
    EveryCheat ^= 1;

    WeaponCheat(pp, cheat_string);
    GodCheat(pp, cheat_string);
    ItemCheat(pp, cheat_string);

    sprintf(ds, "EVERY CHEAT %s", EveryCheat ? "ON" : "OFF");
    PutStringInfo(pp, ds);
}

void SaveCheat(PLAYERp pp, char *UNUSED(cheat_string))
{
    saveboard("swsave.map", &pp->pos,
              fix16_to_int(pp->q16ang), pp->cursectnum);
}

void GeorgeFunc(PLAYERp pp, char *UNUSED(cheat_string))
{
    PlayerSound(DIGI_TAUNTAI9,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_doppler|v3df_follow,pp);
}

void BlackburnFunc(PLAYERp pp, char *UNUSED(cheat_string))
{
    PlayerSound(DIGI_TAUNTAI3,&pp->posx,&pp->posy,&pp->posz,v3df_dontpan|v3df_doppler|v3df_follow,pp);
}

int cheatcmp(const char *str1, const char *str2, int len)
{
    const char *cp1 = str1;
    const char *cp2 = str2;

    do
    {
        if (*cp1 != *cp2)
        {
            if (!((*cp1 == '#' && isdigit(*cp2)) || (*cp2 == '#' && isdigit(*cp1))))
                return -1;
        }

        cp1++;
        cp2++;
    }
    while (--len);

    return 0;
}

#define CF_ALL    BIT(0)
#define CF_NOTSW  BIT(1)

typedef struct
{
    const char *CheatInputCode;
    void (*CheatInputFunc)(PLAYERp, char *);
    char flags;
} CHEAT_INFO, *CHEAT_INFOp;

CHEAT_INFO ci[] =
{
    {"swchan",      GodCheat, 0},
    {"swgimme",     ItemCheat, 0},
    {"swtrek##",    WarpCheat, 0},
    {"swgreed",     EveryCheatToggle, 0},
    {"swghost",      ClipCheat, 0},

    {"swstart",     RestartCheat, 0},

    {"swres",       ResCheatOn, 0},
    {"swloc",       LocCheat, 0},
    {"swmap",       MapCheat, 0},
    {"swsave",      SaveCheat, CF_ALL},
    {"swroom",      RoomCheat, CF_NOTSW}, // Room above room dbug
#if DEBUG
    {"swsecret",    SecretCheat, CF_ALL},
#endif
};

// !JIM! My simplified version of CheatInput which simply processes MessageInputString
void CheatInput(void)
{
    SWBOOL match = FALSE;
    unsigned int i;

    //if (CommEnabled)
    //    return;

    strcpy(CheatInputString,MessageInputString);

    // make sure string is lower cased
    Bstrlwr(CheatInputString);

    // check for at least one single match
    for (i = 0; i < SIZ(ci); i++)
    {
        // compare without the NULL
        if (cheatcmp(CheatInputString, ci[i].CheatInputCode, strlen(CheatInputString)) == 0)
        {

            // if they are equal in length then its a complet match
            if (strlen(CheatInputString) == strlen(ci[i].CheatInputCode))
            {
                match = TRUE;
                CheatInputMode = FALSE;

                if (TEST(ci[i].flags, CF_NOTSW) && SW_SHAREWARE)
                    return;

                if (!TEST(ci[i].flags, CF_ALL))
                {
                    if (CommEnabled)
                        return;

                    if (Skill >= 3)
                    {
                        PutStringInfo(Player, "You're too skillful to cheat\n");
                        return;
                    }
                }

                if (ci[i].CheatInputFunc)
                    (*ci[i].CheatInputFunc)(Player, CheatInputString);

                return;
            }
            else
            {
                match = TRUE;
                break;
            }
        }
    }

    if (!match)
    {
        ////DSPRINTF(ds,"Lost A Match %s", CheatInputString);
        //MONO_PRINT(ds);

        CheatInputMode = FALSE;
    }
}

/*    OLD CODE
void CheatInput(void)
    {
    static SWBOOL cur_show;
    signed char MNU_InputString(char *, short);
    int ret;
    SWBOOL match = FALSE;
    short i;

    // don't use InputMode here - its set for CheatInputMode
    if (MessageInputMode || MenuInputMode)
        return;

    if (!CheatInputMode)
        {
        if (KEY_PRESSED(KEYSC_S))
            {
            //KEY_PRESSED(KEYSC_S) = FALSE;
            CheatInputMode = TRUE;
            strcpy(CheatInputString,"s");
            }
        }

    if (CheatInputMode)
        {
        // get new chars
        ret = MNU_InputString(CheatInputString, 320-20);

        // quick check input
        switch (ret)
            {
            case FALSE: // Input finished (RETURN)
            case -1: // Cancel Input (pressed ESC) or Err
                CheatInputMode = FALSE;
                KB_FlushKeyboardQueue();
                return;

            case TRUE: // Got input
                break;
            }

        // make sure string is lower cased
        Bstrlwr(CheatInputString);

        // check for at least one single match
        for (i = 0; i < SIZ(ci); i++)
            {
            // compare without the NULL
            if (cheatcmp(CheatInputString, ci[i].CheatInputCode, strlen(CheatInputString)) == 0)
                {
                ////DSPRINTF(ds,"%s",CheatInputString);
                //MONO_PRINT(ds);

                // if they are equal in length then its a complet match
                if (strlen(CheatInputString) == strlen(ci[i].CheatInputCode))
                    {
                    ////DSPRINTF(ds,"Found A Match %s", CheatInputString);
                    //MONO_PRINT(ds);

                    match = TRUE;

                    CheatInputMode = FALSE;
                    KB_FlushKeyboardQueue();

                    if (ci[i].CheatInputFunc)
                        (*ci[i].CheatInputFunc)(Player, CheatInputString);

                    return;
                    }
                else
                    {
                    match = TRUE;
                    break;
                    }
                }
            }

        if (!match)
            {
            ////DSPRINTF(ds,"Lost A Match %s", CheatInputString);
            //MONO_PRINT(ds);

            CheatInputMode = FALSE;
            KB_FlushKeyboardQueue();
            }
        }
    }

*/


code:
//-------------------------------------------------------------------------
/*
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
*/
//-------------------------------------------------------------------------
#include "build.h"

#include "keys.h"
#include "pal.h"
#include "game.h"

#include "common_game.h"

short f_c = 3;
static char tempbuf[256];
unsigned char DefaultPalette[256 * 32];
#if 1
void
MapColors(short num, COLOR_MAP cm, short create)
{
    int i;
    float inc;

    if (create)
    {
        for (i = 0; i < 256; i++)
            tempbuf[i] = i;
    }

    if (cm.FromRange == 0 || num <= 0 || num >= 256)
    {
        return;
    }

    inc = cm.ToRange/((float)cm.FromRange);

    for (i = 0; i < cm.FromRange; i++)
        tempbuf[i + cm.FromColor] = (i*inc) + cm.ToColor;
}
#else
void
MapColors(short num, COLOR_MAP cm, short create)
{
    int i;

    if (create)
    {
        for (i = 0; i < 256; i++)
            tempbuf[i] = i;
    }

    if (cm.FromRange == 0 || num <= 0 || num >= 256)
    {
        return;
    }

    // from 32 to 32 || 16 to 16
    if (cm.ToRange == cm.FromRange)
    {
        for (i = 0; i < cm.FromRange; i++)
            tempbuf[i + cm.FromColor] = i + cm.ToColor;

        // Quick fix for grey
        if (cm.ToColor == LT_GREY)
            tempbuf[cm.FromColor+31] = 0; // Set to black
    }
    else
    // from 32 to 16
    if (cm.ToRange == DIV2(cm.FromRange))
    {
        for (i = 0; i < cm.FromRange; i++)
            tempbuf[cm.FromColor + i] = cm.ToColor + DIV2(i);
    }
    else
    // from 16 to 32
    if (DIV2(cm.ToRange) == cm.FromRange)
    {
        for (i = 0; i < cm.FromRange; i++)
            tempbuf[cm.FromColor + DIV2(i)] = cm.ToColor;

        // Quick fix for grey
        if (cm.ToColor == LT_GREY)
            tempbuf[cm.FromColor+31] = 0;         // Set to black
    }
}
#endif

#define PLAYER_COLOR_MAPS 15
static COLOR_MAP PlayerColorMap[PLAYER_COLOR_MAPS][1] =
{

    {
        {32, 32, LT_BLUE, LT_BROWN},
    },
    {
        {32, 31, LT_BLUE, LT_GREY},
    },
    {
        {32, 16, LT_BLUE, PURPLE}
    },
    {
        {32, 16, LT_BLUE, RUST_RED},
    },
    {
        {32, 16, LT_BLUE, YELLOW},
    },
    {
        {32, 16, LT_BLUE, DK_GREEN},
    },
    {
        {32, 16, LT_BLUE, GREEN},
    },
    {
        {32, 32, LT_BLUE, LT_BLUE},  // Redundant, but has to be here for position
    },
    {
        {32, 32, LT_BLUE, LT_TAN},
    },
    {
        {32, 16, LT_BLUE, RED},
    },
    {
        {32, 16, LT_BLUE, DK_GREY},
    },
    {
        {32, 16, LT_BLUE, BRIGHT_GREEN},
    },
    {
        {32, 16, LT_BLUE, DK_BLUE},
    },
    {
        {32, 16, LT_BLUE, FIRE},
    },
    {
        {32, 16, LT_BLUE, FIRE},
    }

};

void
InitPalette(void)
{
    static COLOR_MAP AllToRed[] =
    {
        {31, 16, LT_GREY, RED},
        {32, 16, LT_BROWN, RED},
        {32, 16, LT_TAN, RED},
        {16, 16, RUST_RED, RED},
        {16, 16, YELLOW, RED},
        {16, 16, BRIGHT_GREEN, RED},
        {16, 16, DK_GREEN, RED},
        {16, 16, GREEN, RED},
        {32, 16, LT_BLUE, RED},
        {16, 16, PURPLE, RED},
        {16, 16, FIRE, RED}
    };

    static COLOR_MAP AllToBlue[] =
    {
        {31, 32, LT_GREY, LT_BLUE},
        {32, 32, LT_BROWN, LT_BLUE},
        {32, 32, LT_TAN, LT_BLUE},
        {16, 32, RUST_RED, LT_BLUE},
        {16, 32, YELLOW, LT_BLUE},
        {16, 32, BRIGHT_GREEN, LT_BLUE},
        {16, 32, DK_GREEN, LT_BLUE},
        {16, 32, GREEN, LT_BLUE},
        {16, 32, RED, LT_BLUE},
        {16, 32, PURPLE, LT_BLUE},
        {16, 32, FIRE, LT_BLUE}
    };

    static COLOR_MAP AllToGreen[] =
    {
        {31, 16, LT_GREY, GREEN},
        {32, 16, LT_BROWN, GREEN},
        {32, 16, LT_TAN, GREEN},
        {16, 16, RUST_RED, GREEN},
        {16, 16, YELLOW, GREEN},
        {16, 16, BRIGHT_GREEN, GREEN},
        {16, 16, DK_GREEN, GREEN},
        {16, 16, GREEN, GREEN},
        {32, 16, LT_BLUE, GREEN},
        {16, 16, RED, GREEN},
        {16, 16, PURPLE, GREEN},
        {16, 16, FIRE, GREEN}
    };

    static COLOR_MAP NinjaBasic[] =
    {
        {32, 16, LT_TAN, DK_GREY},
        {32, 16, LT_BROWN,DK_GREY},
        {32, 31, LT_BLUE,LT_GREY},
        {16, 16, DK_GREEN,DK_GREY},
        {16, 16, GREEN,  DK_GREY},
        {16, 16, YELLOW, DK_GREY}
    };

    static COLOR_MAP NinjaRed[] =
    {
        {16, 16, DK_TAN, DK_GREY},
        {16, 16, GREEN, DK_TAN},
        {16, 8, DK_BROWN, RED + 8},

        {32, 16, LT_BLUE, RED}
    };

    static COLOR_MAP NinjaGreen[] =
    {
        {16, 16, DK_TAN, DK_GREY},
        {16, 16, GREEN, DK_TAN},
        {16, 8, DK_BROWN, GREEN + 6},

        {32, 16, LT_BLUE, GREEN}
    };

    static COLOR_MAP Illuminate[] =
    {
        {16, 8, LT_GREY, BRIGHT_GREEN},
        {16, 8, DK_GREY, BRIGHT_GREEN},
        {16, 8, LT_BROWN, BRIGHT_GREEN},
        {16, 8, DK_BROWN, BRIGHT_GREEN},
        {16, 8, LT_TAN, BRIGHT_GREEN},
        {16, 8, DK_TAN, BRIGHT_GREEN},
        {16, 8, RUST_RED, BRIGHT_GREEN},
        {16, 8, YELLOW, BRIGHT_GREEN},
        {16, 8, DK_GREEN, BRIGHT_GREEN},
        {16, 8, GREEN, BRIGHT_GREEN},
        {32, 8, LT_BLUE, BRIGHT_GREEN},
        {16, 8, RED, BRIGHT_GREEN},
        {16, 8, PURPLE, BRIGHT_GREEN},
        {16, 8, FIRE, BRIGHT_GREEN}
    };

    static COLOR_MAP BrownRipper = {31, 32, LT_GREY, LT_TAN};

    static COLOR_MAP SkelGore = {16, 16, RED, BRIGHT_GREEN};
    static COLOR_MAP ElectroGore = {16, 16, RED, DK_BLUE};

    static COLOR_MAP MenuHighlight = {16, 16, RED, FIRE};

    unsigned int i;
    short play;

#if 0
    // I need this for doing fog... Not sure why it wasn't already here.
    initfastcolorlookup(1,1,1);
#endif

    //
    // Save default palette
    //

    memcpy(DefaultPalette, palookup[PALETTE_DEFAULT], 256 * numshades);

    //
    // Dive palettes
    //

    for (i = 0; i < 256; i++)
        tempbuf[i] = i;
    // palette for underwater
    paletteMakeLookupTable(PALETTE_DIVE, tempbuf, 0, 0, 60, TRUE);

#define FOG_AMT 60
    for (i = 0; i < 256; i++)
        tempbuf[i] = i;
    paletteMakeLookupTable(PALETTE_FOG, tempbuf, FOG_AMT, FOG_AMT, FOG_AMT, TRUE);

    for (i = 0; i < 256; i++)
        tempbuf[i] = i;
    paletteMakeLookupTable(PALETTE_DIVE_LAVA, tempbuf, 44, 0, 0, TRUE);

    //
    // 1 Range changes
    //

    MapColors(PALETTE_BROWN_RIPPER, BrownRipper, TRUE);
    paletteMakeLookupTable(PALETTE_BROWN_RIPPER, tempbuf, 0, 0, 0, TRUE);

    MapColors(PALETTE_SKEL_GORE, SkelGore, TRUE);
    paletteMakeLookupTable(PALETTE_SKEL_GORE, tempbuf, 0, 0, 0, TRUE);

    MapColors(PALETTE_ELECTRO_GORE, ElectroGore, TRUE);
    paletteMakeLookupTable(PALETTE_ELECTRO_GORE, tempbuf, 0, 0, 0, TRUE);

    MapColors(PALETTE_MENU_HIGHLIGHT, MenuHighlight, TRUE);
    paletteMakeLookupTable(PALETTE_MENU_HIGHLIGHT, tempbuf, 0, 0, 0, TRUE);

    //
    // Multiple range changes
    //

    MapColors(PALETTE_BASIC_NINJA, NinjaBasic[0], TRUE);
    for (i = 1; i < SIZ(NinjaBasic); i++)
        MapColors(PALETTE_BASIC_NINJA, NinjaBasic[i], FALSE);
    paletteMakeLookupTable(PALETTE_BASIC_NINJA, tempbuf, 0, 0, 0, TRUE);

    MapColors(PALETTE_RED_NINJA, NinjaRed[0], TRUE);
    for (i = 1; i < SIZ(NinjaRed); i++)
        MapColors(PALETTE_RED_NINJA, NinjaRed[i], FALSE);
    paletteMakeLookupTable(PALETTE_RED_NINJA, tempbuf, 0, 0, 0, TRUE);

    MapColors(PALETTE_GREEN_NINJA, NinjaGreen[0], TRUE);
    for (i = 1; i < SIZ(NinjaGreen); i++)
        MapColors(PALETTE_GREEN_NINJA, NinjaGreen[i], FALSE);
    paletteMakeLookupTable(PALETTE_GREEN_NINJA, tempbuf, 0, 0, 0, TRUE);

    MapColors(PALETTE_GREEN_LIGHTING, AllToGreen[0], TRUE);
    for (i = 1; i < SIZ(AllToGreen); i++)
        MapColors(PALETTE_GREEN_LIGHTING, AllToGreen[i], FALSE);
    paletteMakeLookupTable(PALETTE_GREEN_LIGHTING, tempbuf, 0, 0, 0, TRUE);

    MapColors(PALETTE_RED_LIGHTING, AllToRed[0], TRUE);
    for (i = 1; i < SIZ(AllToRed); i++)
        MapColors(PALETTE_RED_LIGHTING, AllToRed[i], FALSE);
    paletteMakeLookupTable(PALETTE_RED_LIGHTING, tempbuf, 0, 0, 0, TRUE);

    MapColors(PALETTE_BLUE_LIGHTING, AllToBlue[0], TRUE);
    for (i = 1; i < SIZ(AllToBlue); i++)
        MapColors(PALETTE_BLUE_LIGHTING, AllToBlue[i], FALSE);
    paletteMakeLookupTable(PALETTE_BLUE_LIGHTING, tempbuf, 0, 0, 0, TRUE);

    MapColors(PALETTE_ILLUMINATE, Illuminate[0], TRUE);
    for (i = 1; i < SIZ(Illuminate); i++)
        MapColors(PALETTE_ILLUMINATE, Illuminate[i], FALSE);
    paletteMakeLookupTable(PALETTE_ILLUMINATE, tempbuf, 0, 0, 0, TRUE);

    // PLAYER COLORS - ALSO USED FOR OTHER THINGS
    for (play = 0; play < PLAYER_COLOR_MAPS; play++)
    {
        MapColors(PALETTE_PLAYER0 + play, PlayerColorMap[play][0], TRUE);
        MapColors(PALETTE_PLAYER0 + play, PlayerColorMap[play][0], FALSE);
        paletteMakeLookupTable(PALETTE_PLAYER0 + play, tempbuf, 0, 0, 0, TRUE);
    }

    //
    // Special Brown sludge
    //

    for (i = 0; i < 256; i++)
        tempbuf[i] = i;
    // invert the brown palette
    for (i = 0; i < 32; i++)
        tempbuf[LT_BROWN + i] = (LT_BROWN + 32) - i;
    paletteMakeLookupTable(PALETTE_SLUDGE, tempbuf, 0, 0, 0, TRUE);

}

/*
2. You must now use my function to set or get any palette
 registers.This means that the keywords "3c7", "3c8", and "3c9" should not even exist in your code.I really
 didn 't want to force you to use my palette functions, but
 since VESA 2.0 supports non VGA compatible cards, you must
do
     it this way.If you use setbrightness for all of your
     palette setting, then you can ignore this.Note that the
     palette format here is VESA 's palette format, which is
     different than my other palette control functions.It 's
     4 bytes and RGB are backwards.Here are the function
  prototypes:

 VBE_setPalette(long palstart, long palnum, char *dapal);

VBE_getPalette(long palstart, long palnum, char *dapal);
palstart is the offset of the first palette to set
 palnum is the number of the palette entries to set
 dapal is a pointer to the palette buffer.The palette
  buffer must be in this format:
char Blue, Green, Red, reserved;
I think this format stinks, but since VESA 2.0 uses
 it, the code will run fastest if the buffer is not
 copied.You can make your own cover up function if
 you don 't like this format.

  This example sets up a wasteful gray scale palette:

char mypalette[1024];

for (i = 0; i < 256; i++)
    {
    mypalette[i * 4 + 0] = (i >> 2);    // Blue
    mypalette[i * 4 + 1] = (i >> 2);    // Green
    mypalette[i * 4 + 2] = (i >> 2);    // Red
    mypalette[i * 4 + 3] = 0;           // reserved
    }
VBE_setPalette(0, 256, mypalette);
*/

#define ORED 0
#define OGREEN 1
#define OBLUE 2

#define NBLUE 0
#define NGREEN 1
#define NRED 2
#define NRESERVED 3

void SetPaletteToVESA(unsigned char *pal)
{
    /*
    char pal_buff[1024];
    short i;

    for (i = 0; i < 256; i++)
    {
    pal_buff[i * 4 + NRED] = pal[i * 3 + ORED];
    pal_buff[i * 4 + NGREEN] = pal[i * 3 + OGREEN];
    pal_buff[i * 4 + NBLUE] = pal[i * 3 + OBLUE];
    pal_buff[i * 4 + NRESERVED] = 0;
    }

    VBE_setPalette(0, 256, pal_buff);
    */
    paletteSetColorTable(BASEPAL, pal);
    videoSetPalette(0, BASEPAL, 4|2);
    //  fprintf(stderr,"SetPaletteToVESA() called\n");
}

void set_pal(unsigned char *pal)
{
    SetPaletteToVESA(pal);
}

void GetPaletteFromVESA(unsigned char *pal)
{
    /*
    char pal_buff[1024];
    short i;

    VBE_getPalette(0, 256, pal_buff);

    for (i = 0; i < 256; i++)
    {
    pal[i * 3 + ORED] = pal_buff[i * 4 + NRED];
    pal[i * 3 + OGREEN] = pal_buff[i * 4 + NGREEN];
    pal[i * 3 + OBLUE] = pal_buff[i * 4 + NBLUE];
    }
    */
    int i;
    for (i=0; i<256; i++)
    {
        pal[i*3+0] = curpalette[i].r;
        pal[i*3+1] = curpalette[i].g;
        pal[i*3+2] = curpalette[i].b;
    }
    //    fprintf(stderr,"GetPaletteFromVESA() called\n");
}


code:
#ifndef colormap_public_
#define colormap_public_

extern unsigned char DefaultPalette[];

void MapColors(short num,COLOR_MAP cm,short create);
void InitPalette(void);
void SetPaletteToVESA(unsigned char *pal);
void set_pal(unsigned char *pal);
void GetPaletteFromVESA(unsigned char *pal);
void InitPalette(void);

#endif