/**************************************************************************/
/*                                                                         */
/*                                                                         */
/* Raven 3D Engine                                                         */
/* Copyright (C) 1996 by Softdisk Publishing                               */
/*                                                                         */
/* Original Design:                                                        */
/*  John Carmack of id Software                                            */
/*                                                                         */
/* Enhancements by:                                                        */
/*  Robert Morgan of Channel 7............................Main Engine Code */
/*  Todd Lewis of Softdisk Publishing......Tools,Utilities,Special Effects */
/*  John Bianca of Softdisk Publishing..............Low-level Optimization */
/*  Carlos Hasan..........................................Music/Sound Code */
/*                                                                         */
/*                                                                         */
/***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "d_disk.h"
#include "d_global.h"
#include "r_refdef.h"
#include "d_ints.h"
#include "protos.h"
#include "d_misc.h"


/**** VARIABLES ****/

scaleobj_t *msprite, probe;
boolean     spritehit, playerhit;
fixed_t     hitx, hity, targx, targy, targz;
int         spriteloc;  // where did it hit on a sprite


/**** FUNCTIONS ****/

boolean SP_TryDoor(fixed_t xcenter, fixed_t ycenter)
{
    int        xl, yl, xh, yh, x, y;
    doorobj_t *door_p, *last_p;

    if (msprite == &probe)
        return true;
    // These values will probably have to be tweaked for doors that are along
    // the vertical opposite axis (northwall)
    xl = (int) ((xcenter - msprite->movesize) >> FRACTILESHIFT);
    yl = (int) ((ycenter - msprite->movesize /* - (TILEUNIT >> 1)*/) >> FRACTILESHIFT);
    xh = (int) ((xcenter + msprite->movesize) >> FRACTILESHIFT);
    yh = (int) ((ycenter + msprite->movesize /* - (TILEUNIT >> 1)*/) >> FRACTILESHIFT);
    // check for doors on the north wall
    for (y = yl + 1; y <= yh; y++)
        for (x = xl; x <= xh; x++)
        {
            if (mapflags[y * MAPSIZE + x] & FL_DOOR)  // if tile has a door
            {
                last_p = &doorlist[numdoors];
                for (door_p = doorlist; door_p != last_p; door_p++)
                    if (door_p->tilex == x && door_p->tiley == y
                        && (door_p->orientation == dr_horizontal
                            || door_p->orientation == dr_horizontal2))
                    {
                        if (door_p->doorOpen && !door_p->doorClosing)
                            return true;  // can move, door is open
                        else if (!door_p->doorOpen && door_p->doorBumpable && !door_p->doorOpening)
                        {
                            door_p->doorOpening = true;
                            door_p->doorClosing = false;
                            SoundEffect(SN_DOOR,
                                        15,
                                        door_p->tilex << FRACTILESHIFT,
                                        door_p->tiley << FRACTILESHIFT);
                            door_p->doorTimer += 20;
                            if (door_p->orientation == dr_horizontal)
                                SP_TryDoor(xcenter + 64 * FRACUNIT, ycenter);
                            else
                                SP_TryDoor(xcenter - 64 * FRACUNIT, ycenter);
                            return false;
                        }
                        else if (!door_p->doorOpen && door_p->doorBumpable && door_p->doorClosing)
                        {
                            door_p->doorClosing = false;
                            door_p->doorOpening = true;
                            SoundEffect(SN_DOOR,
                                        15,
                                        door_p->tilex << FRACTILESHIFT,
                                        door_p->tiley << FRACTILESHIFT);
                            door_p->doorTimer += 20;
                            if (door_p->orientation == dr_horizontal)
                                SP_TryDoor(xcenter + 64 * FRACUNIT, ycenter);
                            else
                                SP_TryDoor(xcenter - 64 * FRACUNIT, ycenter);
                            return false;
                        }
                        else
                            return false;
                    }
            }
        }
    // check for doors on the west wall
    xl = (int) ((xcenter - msprite->movesize /* - (TILEUNIT >> 1)*/) >> FRACTILESHIFT);
    yl = (int) ((ycenter - msprite->movesize) >> FRACTILESHIFT);
    xh = (int) ((xcenter + msprite->movesize /* - (TILEUNIT >> 1)*/) >> FRACTILESHIFT);
    yh = (int) ((ycenter + msprite->movesize) >> FRACTILESHIFT);
    for (y = yl; y <= yh; y++)
        for (x = xl + 1; x <= xh; x++)
        {
            if (mapflags[y * MAPSIZE + x] & FL_DOOR)  // if tile has a door
            {
                last_p = &doorlist[numdoors];
                for (door_p = doorlist; door_p != last_p; door_p++)
                    if (door_p->tilex == x && door_p->tiley == y
                        && (door_p->orientation == dr_vertical
                            || door_p->orientation == dr_vertical2))
                    {
                        if (door_p->doorOpen && !door_p->doorClosing)
                            return true;  // can move, door is open
                        else if (!door_p->doorOpen && door_p->doorBumpable && !door_p->doorOpening)
                        {
                            door_p->doorOpening = true;
                            door_p->doorClosing = false;
                            SoundEffect(SN_DOOR,
                                        15,
                                        door_p->tilex << FRACTILESHIFT,
                                        door_p->tiley << FRACTILESHIFT);
                            door_p->doorTimer += 20;
                            if (door_p->orientation == dr_vertical)
                                SP_TryDoor(xcenter, ycenter + 64 * FRACUNIT);
                            else
                                SP_TryDoor(xcenter, ycenter - 64 * FRACUNIT);
                            return false;
                        }
                        else if (!door_p->doorOpen && door_p->doorBumpable && door_p->doorClosing)
                        {
                            door_p->doorClosing = false;
                            door_p->doorOpening = true;
                            SoundEffect(SN_DOOR,
                                        15,
                                        door_p->tilex << FRACTILESHIFT,
                                        door_p->tiley << FRACTILESHIFT);
                            door_p->doorTimer += 20;
                            if (door_p->orientation == dr_vertical)
                                SP_TryDoor(xcenter, ycenter + 64 * FRACUNIT);
                            else
                                SP_TryDoor(xcenter, ycenter - 64 * FRACUNIT);
                            return false;
                        }
                        else
                            return false;
                    }
            }
        }
    return true;
}


int SP_TryMove(fixed_t xcenter, fixed_t ycenter)
{
    int xl, yl, xh, yh, x, y, mapspot;

    xl = (int) ((xcenter - msprite->movesize) >> FRACTILESHIFT);
    yl = (int) ((ycenter - msprite->movesize) >> FRACTILESHIFT);
    xh = (int) ((xcenter + msprite->movesize) >> FRACTILESHIFT);
    yh = (int) ((ycenter + msprite->movesize) >> FRACTILESHIFT);
    for (y = yl; y <= yh; y++)
        for (x = xl; x <= xh; x++)
        {
            mapspot = MAPCOLS * y + x;
            if ((y > yl && northwall[mapspot] && !(northflags[mapspot] & F_NOCLIP)
                 && !(northflags[mapspot] & F_NOBULLETCLIP))
                || (x > xl && westwall[mapspot] && !(westflags[mapspot] & F_NOCLIP)
                    && !(westflags[mapspot] & F_NOBULLETCLIP)))
                return 2;  // wall hit
            if (msprite != &probe)
            {
                if (msprite->z < RF_GetFloorZ((x << FRACTILESHIFT) + (32 << FRACBITS),
                                              (y << FRACTILESHIFT) + (32 << FRACBITS)))
                    return 2;  // below floor
                if (msprite->z > RF_GetCeilingZ((x << FRACTILESHIFT) + (32 << FRACBITS),
                                                (y << FRACTILESHIFT) + (32 << FRACBITS)))
                    return 2;  // below ceiling
            }

            if (mapsprites[mapspot] == 64)
                return 2;  // instawall

            if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128
                && mapspot != msprite->startspot)
            {
                spritehit = true;
                spriteloc = mapspot;
                return 1;
            }
            if (mapspot == player.mapspot && mapspot != msprite->startspot
                && msprite->spawnid != playernum)  // can't shot yourself
            {
                playerhit = true;
                return 1;
            }
        }
    return 0;
}


byte SP_ClipMove(fixed_t xmove, fixed_t ymove, fixed_t zmove)
{
    int     result;
    fixed_t dx, dy;

    dx        = msprite->x + xmove;
    dy        = msprite->y + ymove;
    spritehit = false;
    result    = SP_TryMove(dx, dy);
    if (result)
    {
        hitx = dx;
        hity = dy;
    }
    if (result != 2 && !SP_TryDoor(dx, dy))
        result = 2;  // door hit or wall hit
    if (result != 2)
    {
        msprite->x += xmove;
        msprite->y += ymove;
        msprite->z += zmove;
    }
    return result;
}


byte SP_Thrust()
{
    msprite->angle &= ANGLES;
    msprite->angle2 &= ANGLES;
    return SP_ClipMove(
        costable[msprite->angle], -sintable[msprite->angle], sintable[msprite->angle2]);
}


boolean SP_TryMove2(int angle, fixed_t xcenter, fixed_t ycenter, int smapspot)
{
    int     xl, yl, xh, yh, x, y, mapspot;
    fixed_t sz, sz2, floorz, ceilingz;

    if (angle < NORTH || angle > SOUTH)
    {
        xl = xcenter >> FRACTILESHIFT;
        xh = (xcenter + msprite->movesize) >> FRACTILESHIFT;
    }
    else if (angle > NORTH && angle < SOUTH)
    {
        xh = xcenter >> FRACTILESHIFT;
        xl = (xcenter - msprite->movesize) >> FRACTILESHIFT;
    }
    else
    {
        xl = (xcenter - msprite->movesize) >> FRACTILESHIFT;
        xh = (xcenter + msprite->movesize) >> FRACTILESHIFT;
    }

    if (angle > WEST)
    {
        yl = ycenter >> FRACTILESHIFT;
        yh = (ycenter + msprite->movesize) >> FRACTILESHIFT;
    }
    else if (angle < WEST && angle != EAST)
    {
        yl = (ycenter - msprite->movesize) >> FRACTILESHIFT;
        yh = ycenter >> FRACTILESHIFT;
    }
    else
    {
        yl = (ycenter - msprite->movesize) >> FRACTILESHIFT;
        yh = (ycenter + msprite->movesize) >> FRACTILESHIFT;
    }
    sz  = msprite->z - msprite->zadj + (20 << FRACBITS);
    sz2 = msprite->z - msprite->zadj;
    for (y = yl; y <= yh; y++)
        for (x = xl; x <= xh; x++)
        {
            mapspot = MAPCOLS * y + x;
            if (mapspot == player.mapspot
                || (y > yl && northwall[mapspot] && !(northflags[mapspot] & F_NOCLIP))
                || (x > xl && westwall[mapspot] && !(westflags[mapspot] & F_NOCLIP)))
                return false;  // wall hit
            floorz = RF_GetFloorZ((x << FRACTILESHIFT) + (32 << FRACBITS),
                                  (y << FRACTILESHIFT) + (32 << FRACBITS));
            if (floorz > sz)
                return false;
            if (msprite->nofalling && floorz + (5 << FRACBITS) < sz2)
                return false;
            ceilingz = RF_GetCeilingZ((x << FRACTILESHIFT) + (32 << FRACBITS),
                                      (y << FRACTILESHIFT) + (32 << FRACBITS));
            if (ceilingz < msprite->z + msprite->height)
                return false;
            if (ceilingz - floorz < msprite->height)
                return false;
            if (mapspot != smapspot && mapsprites[mapspot])
                return false;
        }
    return true;
}


byte SP_ClipMove2(fixed_t xmove, fixed_t ymove)
{
    fixed_t dx, dy;
    int     smapspot, angle2, ms;

    if (msprite->type == S_CLONE)
        ms = SM_CLONE;
    else
        ms = 1;
    dx       = msprite->x + xmove;
    dy       = msprite->y + ymove;
    smapspot = (msprite->y >> FRACTILESHIFT) * MAPCOLS + (msprite->x >> FRACTILESHIFT);
    if (SP_TryMove2(msprite->angle, dx, dy, smapspot) && SP_TryDoor(dx, dy))
    {
        if (floorpic[(dy >> FRACTILESHIFT) * MAPCOLS + (dx >> FRACTILESHIFT)] == 0)
            return 0;
        mapsprites[smapspot] = 0;
        msprite->x += xmove;
        msprite->y += ymove;
        mapsprites[(msprite->y >> FRACTILESHIFT) * MAPCOLS + (msprite->x >> FRACTILESHIFT)] = ms;
        return 1;
    }
    // the move goes into a wall, so try and move along one axis
    if (xmove > 0)
    {
        angle2 = EAST;
        dx     = msprite->x + msprite->moveSpeed;
    }
    else
    {
        angle2 = WEST;
        dx     = msprite->x - msprite->moveSpeed;
    }
    if (SP_TryMove2(angle2, dx, msprite->y, smapspot) && SP_TryDoor(dx, msprite->y))
    {
        if (floorpic[(msprite->y >> FRACTILESHIFT) * MAPCOLS + (dx >> FRACTILESHIFT)] == 0)
            return 0;
        mapsprites[smapspot] = 0;
        msprite->x += xmove;
        mapsprites[(msprite->y >> FRACTILESHIFT) * MAPCOLS + (msprite->x >> FRACTILESHIFT)] = ms;
        return 2;
    }
    if (ymove > 0)
    {
        angle2 = SOUTH;
        dy     = msprite->y + msprite->moveSpeed;
    }
    else
    {
        angle2 = NORTH;
        dy     = msprite->y - msprite->moveSpeed;
    }
    if (SP_TryMove2(angle2, msprite->x, dy, smapspot) && SP_TryDoor(msprite->x, dy))
    {
        if (floorpic[(dy >> FRACTILESHIFT) * MAPCOLS + (msprite->x >> FRACTILESHIFT)] == 0)
            return 0;
        mapsprites[smapspot] = 0;
        msprite->y += ymove;
        mapsprites[(msprite->y >> FRACTILESHIFT) * MAPCOLS + (msprite->x >> FRACTILESHIFT)] = ms;
        return 3;
    }
    return 0;
}


byte SP_Thrust2()
{
    fixed_t xmove, ymove;

    msprite->angle &= ANGLES;
    xmove = FIXEDMUL(msprite->moveSpeed, costable[msprite->angle]);
    ymove = -FIXEDMUL(msprite->moveSpeed, sintable[msprite->angle]);
    return SP_ClipMove2(xmove, ymove);
}


void ActivationSound(scaleobj_t* sp)
{
    switch (sp->type)
    {
        case S_MONSTER1:
            SoundEffect(SN_MON1_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER2:
            SoundEffect(SN_MON2_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER3:
            SoundEffect(SN_MON3_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER4:
            SoundEffect(SN_MON4_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER5:
            SoundEffect(SN_MON5_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER6:
            SoundEffect(SN_MON6_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER7:
            SoundEffect(SN_MON7_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER8:
            SoundEffect(SN_MON8_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER9:
            SoundEffect(SN_MON9_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER10:
            SoundEffect(SN_MON10_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER11:
            SoundEffect(SN_MON11_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER12:
            SoundEffect(SN_MON12_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER13:
            SoundEffect(SN_MON13_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER14:
            SoundEffect(SN_MON14_WAKE, 7, sp->x, sp->y);
            break;
        case S_MONSTER15:
            SoundEffect(SN_MON15_WAKE, 7, sp->x, sp->y);
            break;
    }
}


void ActivateSprites(int sx, int sy)
/* proximity activation (recursive chain reaction) */
{
    scaleobj_t* sp;
    int         x, y;

    for (sp = firstscaleobj.next; sp != &lastscaleobj; sp = sp->next)
        if (sp->active == false && sp->moveSpeed)
        {
            x = sp->x >> FRACTILESHIFT;
            y = sp->y >> FRACTILESHIFT;
            if (abs(x - sx) < 5 && abs(y - sy) < 5)
            {
                sp->active     = true;
                sp->actiontime = timecount + 40;
                ActivationSound(sp);
                ActivationSound(sp);
                ActivateSprites(x, y);
            }
        }
}


void ShowWallPuff()
{
    int i;

    switch (msprite->type)
    {
        case S_BULLET3:
        case S_BULLET12:
        case S_BULLET17:
        case S_MONSTERBULLET2:
        case S_MONSTERBULLET4:
        case S_MONSTERBULLET6:
        case S_MONSTERBULLET8:
        case S_GRENADEBULLET:
            i = S_SMALLEXPLODE;
            break;
        case S_BULLET4:
        case S_MONSTERBULLET5:
        case S_MONSTERBULLET11:
            i = S_PLASMAWALLPUFF;
            break;
        case S_HANDBULLET:
        case S_BLOODSPLAT:
        case S_BULLET7:
        case S_MONSTERBULLET7:
        case S_MONSTERBULLET10:
        case S_MONSTERBULLET12:
        case S_MONSTERBULLET15:
        case S_SOULBULLET:
            return;
        case S_BULLET9:
            i = S_ARROWPUFF;
            break;
        case S_BULLET10:
        case S_BULLET18:
            i = S_GREENPUFF;
            break;
        case S_MINEBULLET:
            i = S_MINEPUFF;
            break;
        default:
            i = S_WALLPUFF;
    }
    SpawnSprite(i, msprite->x, msprite->y, msprite->z, msprite->zadj, 0, 0, false, 0);
    ActivateSprites(msprite->x >> FRACTILESHIFT, msprite->y >> FRACTILESHIFT);
}


void HitSprite(scaleobj_t* sp)
{
    switch (sp->type)
    {
        case S_CLONE:
            if (!sp->active)
            {
                ActivateSprites((int) (sp->x >> FRACTILESHIFT), (int) (sp->y >> FRACTILESHIFT));
                sp->active = true;
            }
            sp->modetime = timecount + 8;
            sp->basepic  = sp->startpic + 32;
            SpawnSprite(
                S_BLOODSPLAT, msprite->x, msprite->y, msprite->z, msprite->zadj, 0, 0, false, 0);
            if (msprite->type != S_BULLET17)
            {
                SpawnSprite(S_BLOODSPLAT,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            msprite->zadj,
                            0,
                            0,
                            false,
                            0);
                SpawnSprite(S_BLOODSPLAT,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            msprite->zadj,
                            0,
                            0,
                            false,
                            0);
                SpawnSprite(S_BLOODSPLAT,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            msprite->zadj,
                            0,
                            0,
                            false,
                            0);
                SpawnSprite(S_BLOODSPLAT,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            msprite->zadj,
                            0,
                            0,
                            false,
                            0);
                SpawnSprite(S_BLOODSPLAT,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            msprite->zadj,
                            0,
                            0,
                            false,
                            0);
            }
            break;
        case S_MONSTER1:
        case S_MONSTER2:
        case S_MONSTER7:
        case S_MONSTER9:
        case S_MONSTER10:
        case S_MONSTER12:
        case S_MONSTER13:
        case S_MONSTER14:
        case S_MONSTER15:
            if (!sp->active)
            {
                ActivateSprites((int) (sp->x >> FRACTILESHIFT), (int) (sp->y >> FRACTILESHIFT));
                sp->active = true;
            }
            sp->modetime = timecount + 8;
            sp->basepic  = sp->startpic + 40;
            SpawnSprite(
                S_BLOODSPLAT, msprite->x, msprite->y, msprite->z, msprite->zadj, 0, 0, false, 0);
            if (msprite->type != S_BULLET17)
            {
                SpawnSprite(S_BLOODSPLAT,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            msprite->zadj,
                            0,
                            0,
                            false,
                            0);
                SpawnSprite(S_BLOODSPLAT,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            msprite->zadj,
                            0,
                            0,
                            false,
                            0);
                SpawnSprite(S_BLOODSPLAT,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            msprite->zadj,
                            0,
                            0,
                            false,
                            0);
                SpawnSprite(S_BLOODSPLAT,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            msprite->zadj,
                            0,
                            0,
                            false,
                            0);
                SpawnSprite(S_BLOODSPLAT,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            msprite->zadj,
                            0,
                            0,
                            false,
                            0);
            }
            break;
        case S_MONSTER3:
        case S_MONSTER4:
        case S_MONSTER5:
        case S_MONSTER6:
        case S_MONSTER8:
        case S_MONSTER11:
            ShowWallPuff();
            break;
    }
}


boolean Int0(void)
/* control of bullets, explosions, and other objects (not monsters!!) */
{
    scaleobj_t *hsprite, *sp;
    int         counter, mapspot, result, angle, angleinc, i, oldfall;
    int         oldangle, oldmovespeed;
    boolean     killed, blood;

    counter = 0;
    killed  = false;

    if (msprite->type == S_GRENADE)
        msprite->angle2 -= 4;
    else if ((msprite->type == S_BLOODSPLAT || msprite->type == S_METALPARTS)
             && (msprite->angle2 < NORTH || msprite->angle2 > SOUTH))
        msprite->angle2 -= 32;

    if (msprite->maxmove)
    {
        --msprite->maxmove;
        if (msprite->maxmove <= 0)
        {
            ShowWallPuff();
            return true;
        }
    }

    if (msprite->type == S_MONSTERBULLET4 || msprite->type == S_MONSTERBULLET6
        || msprite->type == S_BULLET17)
        SpawnSprite(S_WALLPUFF, msprite->x, msprite->y, msprite->z, msprite->zadj, 0, 0, false, 0);
    blood = false;
    while (counter++ < msprite->moveSpeed)
    {
        result = SP_Thrust();
        if (msprite->type == S_BULLET3)
            msprite->z = RF_GetFloorZ(msprite->x, msprite->y) + (20 << FRACBITS);

        if (result != 0)
        {
            if (msprite->type == S_BLOODSPLAT)
            {
                if (result == 2)
                    return true;
                spritehit = false;
                playerhit = false;
            }
            else if (msprite->type == S_METALPARTS && result == 2)
            {
                playerhit = false;
                return true;
            }
            if (spritehit)
            {
                if (mapsprites[spriteloc] == SM_NETPLAYER)
                {
                    for (hsprite = firstscaleobj.next; hsprite != &lastscaleobj;
                         hsprite = hsprite->next)
                        if (hsprite != msprite)
                        {
                            mapspot = (hsprite->y >> FRACTILESHIFT) * MAPCOLS
                                      + (hsprite->x >> FRACTILESHIFT);
                            if (mapspot == spriteloc)
                            {
                                if (msprite->z < hsprite->z
                                    || msprite->z > hsprite->z + hsprite->height
                                    || hsprite->type != S_NETPLAYER)
                                    break;
                                {
                                    SpawnSprite(S_BLOODSPLAT,
                                                msprite->x,
                                                msprite->y,
                                                msprite->z,
                                                msprite->zadj,
                                                0,
                                                0,
                                                false,
                                                0);
                                    //	if (msprite->type!=S_BULLET17)
                                    //	 {
                                    SpawnSprite(S_BLOODSPLAT,
                                                msprite->x,
                                                msprite->y,
                                                msprite->z,
                                                msprite->zadj,
                                                0,
                                                0,
                                                false,
                                                0);
                                    SpawnSprite(S_BLOODSPLAT,
                                                msprite->x,
                                                msprite->y,
                                                msprite->z,
                                                msprite->zadj,
                                                0,
                                                0,
                                                false,
                                                0);
                                    SpawnSprite(S_BLOODSPLAT,
                                                msprite->x,
                                                msprite->y,
                                                msprite->z,
                                                msprite->zadj,
                                                0,
                                                0,
                                                false,
                                                0);
                                    blood  = true;
                                    killed = true;
                                    //	  }
                                }
                            }
                        }
                }
                else
                    for (hsprite = firstscaleobj.next; hsprite != &lastscaleobj;
                         hsprite = hsprite->next)
                        if (hsprite != msprite)
                        {
                            mapspot = (hsprite->y >> FRACTILESHIFT) * MAPCOLS
                                      + (hsprite->x >> FRACTILESHIFT);
                            if (mapspot == spriteloc)
                            {
                                if (msprite->z < hsprite->z
                                    || msprite->z > hsprite->z + hsprite->height)
                                    continue;
                                if (hsprite->hitpoints)
                                {
                                    if (hsprite->type != S_MONSTER5)
                                        hsprite->actiontime += 15;
                                    else
                                        hsprite->actiontime += 5;
                                    hsprite->hitpoints -= msprite->damage;
                                    if (msprite->spawnid == 255)
                                        hsprite->enraged++;

                                    if (msprite->type == S_SOULBULLET
                                        && msprite->spawnid == playernum)
                                    {
                                        heal(msprite->damage / 2);
                                        medpaks(msprite->damage / 2);
                                    }

                                    killed = true;
                                    if (hsprite->hitpoints <= 0)
                                    {
                                        if (msprite->spawnid == playernum
                                            || msprite->spawnid - 200 == playernum)
                                        {
                                            ++player.bodycount;
                                            addscore(hsprite->score);
                                        }
                                        mapsprites[spriteloc] = 0;
                                        blood                 = true;
                                        HitSprite(hsprite);
                                        KillSprite(hsprite, msprite->type);
                                    }
                                    else if (msprite->damage)
                                    {
                                        oldangle           = hsprite->angle;
                                        oldmovespeed       = hsprite->moveSpeed;
                                        hsprite->angle     = msprite->angle;
                                        hsprite->moveSpeed = (msprite->damage >> 2) << FRACBITS;
                                        sp                 = msprite;
                                        msprite            = hsprite;
                                        oldfall            = msprite->nofalling;
                                        msprite->nofalling = 0;
                                        SP_Thrust2();
                                        msprite->nofalling = oldfall;
                                        msprite            = sp;
                                        hsprite->angle     = oldangle;
                                        hsprite->moveSpeed = oldmovespeed;
                                        blood              = true;
                                        HitSprite(hsprite);
                                    }
                                    break;
                                }
                            }
                        }
            }
            else if (playerhit && msprite->z > player.z - player.height && msprite->z < player.z)
            {
                if (player.angst != 0)  // don't keep hitting
                {
                    hurt(msprite->damage);
                    if (player.angst == 0 && netmode)
                        NetDeath(msprite->spawnid);
                }
                Thrust(msprite->angle, msprite->damage << (FRACBITS - 3));
                playerhit = false;
                killed    = true;
                if (msprite->damage > 50)
                {
                    player.angle += -15 + (MS_RndT() & 31);
                    player.angle &= ANGLES;
                }
            }
            if (result == 2)
                killed = true;
            if (killed)
            {
                if (!blood)
                    ShowWallPuff();
                break;
            }
        }
    }
    if (killed && msprite->type == S_GRENADE)
    {
        angleinc = ANGLES / 12;
        angle    = 0;
        for (i = 0, angle = 0; i < 12; i++, angle += angleinc)
        {
            sp            = SpawnSprite(S_GRENADEBULLET,
                             msprite->x,
                             msprite->y,
                             msprite->z,
                             20 << FRACBITS,
                             angle,
                             0,
                             true,
                             msprite->spawnid);
            sp->maxmove   = 3;
            sp->startspot = -1;
        }
        angleinc = ANGLES / 8;
        angle    = 0;
        for (i = 0, angle = 0; i < 8; i++, angle += angleinc)
        {
            sp            = SpawnSprite(S_GRENADEBULLET,
                             msprite->x,
                             msprite->y,
                             msprite->z,
                             20 << FRACBITS,
                             angle,
                             64,
                             true,
                             msprite->spawnid);
            sp->maxmove   = 2;
            sp->startspot = -1;
        }
        angleinc = ANGLES / 8;
        angle    = 0;
        for (i = 0, angle = 0; i < 8; i++, angle += angleinc)
        {
            sp            = SpawnSprite(S_GRENADEBULLET,
                             msprite->x,
                             msprite->y,
                             msprite->z,
                             20 << FRACBITS,
                             angle,
                             -64,
                             true,
                             msprite->spawnid);
            sp->maxmove   = 2;
            sp->startspot = -1;
        }
        sp = SpawnSprite(S_EXPLODE, msprite->x, msprite->y, msprite->z, 0, 0, 0, true, 255);
        SoundEffect(SN_EXPLODE1 + (MS_RndT() & 1), 15, msprite->x, msprite->y);
    }
    else if (killed && msprite->type == S_BULLET17)
    {
        angleinc = ANGLES / 8;
        angle    = 0;
        for (i = 0, angle = 0; i < 8; i++, angle += angleinc)
        {
            sp            = SpawnSprite(S_GRENADEBULLET,
                             msprite->x,
                             msprite->y,
                             msprite->z,
                             20 << FRACBITS,
                             angle,
                             0,
                             true,
                             msprite->spawnid);
            sp->maxmove   = 3;
            sp->startspot = -1;
        }
        angleinc = ANGLES / 6;
        angle    = 0;
        for (i = 0, angle = 0; i < 6; i++, angle += angleinc)
        {
            sp            = SpawnSprite(S_GRENADEBULLET,
                             msprite->x,
                             msprite->y,
                             msprite->z,
                             20 << FRACBITS,
                             angle,
                             64,
                             true,
                             msprite->spawnid);
            sp->maxmove   = 2;
            sp->startspot = -1;
        }
        angleinc = ANGLES / 6;
        angle    = 0;
        for (i = 0, angle = 0; i < 6; i++, angle += angleinc)
        {
            sp            = SpawnSprite(S_GRENADEBULLET,
                             msprite->x,
                             msprite->y,
                             msprite->z,
                             20 << FRACBITS,
                             angle,
                             -64,
                             true,
                             msprite->spawnid);
            sp->maxmove   = 2;
            sp->startspot = -1;
        }
        sp = SpawnSprite(S_EXPLODE, msprite->x, msprite->y, msprite->z, 0, 0, 0, true, 255);
        SoundEffect(SN_EXPLODE1 + (MS_RndT() & 1), 15, msprite->x, msprite->y);
    }
    return killed;
}

/***************************************************************************/

int ScanX(int limit1, int x1, int y1, int x2, int y2, int* tx, int* ty)
/* check for the player along the x axis */
{
    int mapspot, wall, x, limit, flags;

    mapspot = y1 * MAPCOLS + x1 + 1;
    x       = x1;
    limit   = limit1;
    while (1)
    {
        if (mapsprites[mapspot] == SM_NETPLAYER || mapsprites[mapspot] == SM_CLONE
            || (x == x2 && y1 == y2))
        {
            *tx = x + 1;
            return 2;
        }
        if (msprite->enraged >= 6 - player.difficulty && mapsprites[mapspot] == 1)
        {
            *tx = x + 1;
            *ty = y1;
            return 2;
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            break;
        wall  = westwall[mapspot];
        flags = westflags[mapspot];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            break;
        ++mapspot;
        ++x;
        --limit;
        if (!limit)
            break;
    }
    limit   = limit1;
    mapspot = y1 * MAPCOLS + x1 - 1;
    x       = x1;
    while (1)
    {
        if (mapsprites[mapspot] == SM_NETPLAYER || mapsprites[mapspot] == SM_CLONE
            || (x == x2 && y1 == y2))
        {
            *tx = x - 1;
            return 2;
        }
        if (msprite->enraged >= 6 - player.difficulty && mapsprites[mapspot] == 1)
        {
            *tx = x - 1;
            *ty = y1;
            return 2;
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            return 1;
        wall  = westwall[mapspot + 1];
        flags = westflags[mapspot + 1];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            return 0;
        --mapspot;
        --x;
        --limit;
        if (!limit)
            break;
    }
    return 0;
}


int ScanY(int limit1, int x1, int y1, int x2, int y2, int* tx, int* ty)
/* check for the player along the y axis */
{
    int mapspot, wall, y, limit, flags;

    limit   = limit1;
    mapspot = y1 * MAPCOLS + x1 + MAPCOLS;
    y       = y1;
    while (1)
    {
        if (mapsprites[mapspot] == SM_NETPLAYER || mapsprites[mapspot] == SM_CLONE
            || (y == y2 && x1 == x2))
        {
            *ty = y + 1;
            return 2;
        }
        if (msprite->enraged >= 6 - player.difficulty && mapsprites[mapspot] == 1)
        {
            *tx = x1;
            *ty = y + 1;
            return 2;
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            break;
        wall  = northwall[mapspot];
        flags = northflags[mapspot];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            break;
        mapspot += MAPCOLS;
        ++y;
        --limit;
        if (!limit)
            break;
    }
    limit   = limit1;
    mapspot = y1 * MAPCOLS + x1 - MAPCOLS;
    y       = y1;
    while (1)
    {
        if (mapsprites[mapspot] == SM_NETPLAYER || mapsprites[mapspot] == SM_CLONE
            || (y == y2 && x1 == x2))
        {
            *ty = y - 1;
            return 2;
        }
        if (msprite->enraged >= 6 - player.difficulty && mapsprites[mapspot] == 1)
        {
            *tx = x1;
            *ty = y - 1;
            return 2;
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            break;
        wall  = northwall[mapspot + MAPCOLS];
        flags = northflags[mapspot + MAPCOLS];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            break;
        mapspot -= MAPCOLS;
        --y;
        --limit;
        if (!limit)
            break;
    }
    return 0;
}


int ScanAngle(int limit1, int x1, int y1, int x2, int y2, int* tx, int* ty)
/* scan for the player along a 45 degree angle
   this is not very accurate!! approximate only */
{
    int mapspot, wall, x, y, limit, flags;

    limit   = limit1;
    mapspot = y1 * MAPCOLS + x1 + MAPCOLS + 1;
    y       = y1;
    x       = x1;
    while (1)
    {
        wall  = northwall[mapspot - 1];
        flags = northflags[mapspot - 1];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            break;

        wall  = westwall[mapspot - MAPCOLS];
        flags = westflags[mapspot - MAPCOLS];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            return 0;

        if (mapsprites[mapspot] == SM_NETPLAYER || mapsprites[mapspot] == SM_CLONE
            || (y == y2 && x == x2))
        {
            *tx = x + 1;
            *ty = y + 1;
            return 2;
        }
        if (msprite->enraged >= 6 - player.difficulty && mapsprites[mapspot] == 1)
        {
            *tx = x + 1;
            *ty = y + 1;
            return 2;
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            break;

        mapspot += MAPCOLS + 1;
        ++y;
        ++x;
        --limit;
        if (!limit)
            break;
    }

    limit   = limit1;
    mapspot = y1 * MAPCOLS + x1 + MAPCOLS - 1;
    y       = y1;
    x       = x1;
    while (1)
    {
        wall  = northwall[mapspot + 1];
        flags = northflags[mapspot + 1];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            break;

        wall  = westwall[mapspot - MAPCOLS];
        flags = westflags[mapspot - MAPCOLS];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            return 0;

        if (mapsprites[mapspot] == SM_NETPLAYER || mapsprites[mapspot] == SM_CLONE
            || (y == y2 && x == x2))
        {
            *tx = x - 1;
            *ty = y + 1;
            return 2;
        }
        if (msprite->enraged >= 6 - player.difficulty && mapsprites[mapspot] == 1)
        {
            *tx = x - 1;
            *ty = y + 1;
            return 2;
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            break;

        mapspot += MAPCOLS - 1;
        ++y;
        --x;
        --limit;
        if (!limit)
            break;
    }

    limit   = limit1;
    mapspot = y1 * MAPCOLS + x1 - MAPCOLS + 1;
    y       = y1;
    x       = x1;
    while (1)
    {
        wall  = northwall[mapspot - 1 + MAPCOLS];
        flags = northflags[mapspot - 1 + MAPCOLS];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            break;

        wall  = westwall[mapspot - MAPCOLS];
        flags = westflags[mapspot - MAPCOLS];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            return 0;

        if (mapsprites[mapspot] == SM_NETPLAYER || mapsprites[mapspot] == SM_CLONE
            || (y == y2 && x == x2))
        {
            *tx = x + 1;
            *ty = y - 1;
            return 2;
        }
        if (msprite->enraged >= 6 - player.difficulty && mapsprites[mapspot] == 1)
        {
            *tx = x + 1;
            *ty = y - 1;
            return 2;
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            break;

        mapspot -= MAPCOLS + 1;
        --y;
        ++x;
        --limit;
        if (!limit)
            break;
    }

    limit   = limit1;
    mapspot = y1 * MAPCOLS + x1 - MAPCOLS - 1;
    y       = y1;
    x       = x1;
    while (1)
    {
        wall  = northwall[mapspot + 1 + MAPCOLS];
        flags = northflags[mapspot + 1 + MAPCOLS];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            break;

        wall  = westwall[mapspot - MAPCOLS];
        flags = westflags[mapspot - MAPCOLS];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            return 0;

        if (mapsprites[mapspot] == SM_NETPLAYER || mapsprites[mapspot] == SM_CLONE
            || (y == y2 && x == x2))
        {
            *tx = x - 1;
            *ty = y - 1;
            return 2;
        }
        if (msprite->enraged >= 6 - player.difficulty && mapsprites[mapspot] == 1)
        {
            *tx = x - 1;
            *ty = y - 1;
            return 2;
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            break;

        mapspot -= MAPCOLS - 1;
        --y;
        --x;
        --limit;
        if (!limit)
            break;
    }

    return 0;
}

/***************************************************************************/

int GetFireAngle(fixed_t sz, int x1, int y1, fixed_t px, fixed_t py, fixed_t pz)
{
    scaleobj_t* hsprite;
    int         x, y, z, d, spriteloc, mapspot;
    boolean     found;

    sz += msprite->z;
    if (x1 != px >> FRACTILESHIFT || y1 != py >> FRACTILESHIFT)
    {
        spriteloc = y1 * MAPCOLS + x1;
        found     = false;
        for (hsprite = firstscaleobj.next; hsprite != &lastscaleobj; hsprite = hsprite->next)
            if (hsprite->hitpoints)
            {
                mapspot = (hsprite->y >> FRACTILESHIFT) * MAPCOLS + (hsprite->x >> FRACTILESHIFT);
                if (mapspot == spriteloc)
                {
                    found = true;
                    break;
                }
            }
        if (found)
        {
            px = hsprite->x;
            py = hsprite->y;
            pz = hsprite->z + (32 << FRACBITS);
        }
    }
    else
        pz += 20 << FRACBITS;
    if (sz > pz)
    {
        z = (sz - pz) >> (FRACBITS + 2);
        if (z >= MAXAUTO)
            return 0;
        x = (msprite->x - px) >> (FRACBITS + 2);
        y = (msprite->y - py) >> (FRACBITS + 2);
        d = sqrt(x * x + y * y);
        if (d >= MAXAUTO || autoangle2[d][z] == -1)
            return 0;
        return -autoangle2[d][z];
    }
    else if (sz < pz)
    {
        z = (pz - sz) >> (FRACBITS + 2);
        if (z >= MAXAUTO)
            return 0;
        x = (msprite->x - px) >> (FRACBITS + 2);
        y = (msprite->y - py) >> (FRACBITS + 2);
        d = sqrt(x * x + y * y);
        if (d >= MAXAUTO || autoangle2[d][z] == -1)
            return 0;
        return autoangle2[d][z];
    }
    else
        return 0;
}

/***************************************************************************/

void Int5(void)  // priests / viscount lords
{
    int     angle, sx, sy, px, py, tx, ty, pangle;
    fixed_t floorz, oldspeed, fheight;

    sx = msprite->x >> FRACTILESHIFT;
    sy = msprite->y >> FRACTILESHIFT;
    if (netmode)
        NetGetClosestPlayer(sx, sy);
    else
    {
        if (specialeffect == SE_INVISIBILITY)
        {
            targx = 0;
            targy = 0;
            targz = 0;
        }
        else
        {
            targx = player.x;
            targy = player.y;
            targz = player.z;
        }
    }
    px = targx >> FRACTILESHIFT;
    py = targy >> FRACTILESHIFT;

    oldspeed = msprite->moveSpeed;
    if (abs(px - sx) < 6 && abs(py - sy) < 6)
        msprite->moveSpeed = msprite->moveSpeed * 2;

    if (timecount > msprite->movetime)
    {
        if (px > sx)
            angle = EAST;
        else if (px < sx)
            angle = WEST;
        else
            angle = -1;
        if (py < sy)
        {
            if (angle == EAST)
                angle += DEGREE45;
            else if (angle == WEST)
                angle -= DEGREE45;
            else
                angle = NORTH;
        }
        else if (py > sy)
        {
            if (angle == EAST)
                angle -= DEGREE45;
            else if (angle == WEST)
                angle += DEGREE45;
            else
                angle = SOUTH;
        }
        angle             = angle - DEGREE45 + MS_RndT();
        msprite->angle    = angle & ANGLES;
        msprite->movetime = timecount + 140;  // 350
    }

    if (timecount > msprite->firetime && timecount > msprite->scantime)
    {
        tx = px;
        ty = py;
        if (ScanX(10, sx, sy, px, py, &tx, &ty) > 1 || ScanY(10, sx, sy, px, py, &tx, &ty) > 1
            || ScanAngle(10, sx, sy, px, py, &tx, &ty) > 1)
        {
            if (tx > sx)
                angle = EAST;
            else if (tx < sx)
                angle = WEST;
            else
                angle = -1;
            if (ty < sy)
            {
                if (angle == EAST)
                    angle += DEGREE45;
                else if (angle == WEST)
                    angle -= DEGREE45;
                else
                    angle = NORTH;
            }
            else if (ty > sy)
            {
                if (angle == EAST)
                    angle -= DEGREE45;
                else if (angle == WEST)
                    angle += DEGREE45;
                else
                    angle = SOUTH;
            }
            msprite->angle      = angle & ANGLES;
            msprite->basepic    = msprite->startpic + 24;
            msprite->movemode   = 4;
            msprite->firetime   = timecount + (40 + 5 * player.difficulty);
            msprite->actiontime = timecount + 30;
            msprite->modetime   = timecount + 15;
        }
        msprite->scantime = timecount + 30;
    }

    if (timecount > msprite->modetime)
    {
        msprite->modetime = timecount + 10;
        switch (msprite->movemode)
        {
            case 0:  // left
            case 1:  // mid
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    ++msprite->movemode;
                    msprite->basepic = msprite->startpic + msprite->movemode * 8;
                    msprite->lasty   = msprite->y;
                    msprite->lastx   = msprite->x;
                }
                break;
            case 2:  // right
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    msprite->basepic = msprite->startpic + 8;  // midstep
                    ++msprite->movemode;
                    msprite->lasty = msprite->y;
                    msprite->lastx = msprite->x;
                }
                break;
            case 3:  // mid #2
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    msprite->movemode = 0;
                    msprite->basepic  = msprite->startpic;
                    msprite->lasty    = msprite->y;
                    msprite->lastx    = msprite->x;
                }
                break;
            case 4:  // fire #1
                tx = px;
                ty = py;
                if (ScanX(10, sx, sy, px, py, &tx, &ty) > 1
                    || ScanY(10, sx, sy, px, py, &tx, &ty) > 1
                    || ScanAngle(10, sx, sy, px, py, &tx, &ty) > 1)
                {
                    if (tx > sx)
                        angle = EAST;
                    else if (tx < sx)
                        angle = WEST;
                    else
                        angle = -1;
                    if (ty < sy)
                    {
                        if (angle == EAST)
                            angle += DEGREE45;
                        else if (angle == WEST)
                            angle -= DEGREE45;
                        else
                            angle = NORTH;
                    }
                    else if (ty > sy)
                    {
                        if (angle == EAST)
                            angle -= DEGREE45;
                        else if (angle == WEST)
                            angle += DEGREE45;
                        else
                            angle = SOUTH;
                    }
                    msprite->angle    = angle & ANGLES;
                    msprite->movemode = 5;
                    msprite->basepic  = msprite->startpic + 32;
                    if (msprite->type == S_MONSTER7)
                        fheight = 15 << FRACBITS;
                    else
                        fheight = 40 << FRACBITS;
                    pangle = GetFireAngle(fheight, tx, ty, targx, targy, targz) - 15
                             + (MS_RndT() & 31);
                    SpawnSprite(msprite->bullet,
                                msprite->x,
                                msprite->y,
                                msprite->z,
                                fheight,
                                msprite->angle - 15 + (MS_RndT() & 31),
                                pangle,
                                true,
                                255);
                    msprite->modetime += 8;
                }
                else
                {
                    msprite->movemode = 0;
                    msprite->basepic  = msprite->startpic;
                }
                break;
            case 5:  // fire #2
                msprite->movemode = 0;
                msprite->basepic  = msprite->startpic;
                break;
        }
    }

    if (timecount > msprite->actiontime && SP_Thrust2() != 1)
    {
        angle          = msprite->angle + DEGREE45;
        msprite->angle = angle & ANGLES;
    }
    floorz = RF_GetFloorZ(msprite->x, msprite->y);
    if (floorz + msprite->zadj < msprite->z)
        msprite->z -= FRACUNIT << 4;
    if (floorz + msprite->zadj > msprite->z)
        msprite->z = floorz + msprite->zadj;
    msprite->moveSpeed = oldspeed;
    if (MS_RndT() >= 255)
        ActivationSound(msprite);
}

/***************************************************************************/

boolean Int6()  // intelligence for mines
{
    scaleobj_t* sp;
    int         i, j, angle, angleinc, x, y, sx, sy;
    boolean     activate;

    if (timecount > msprite->actiontime)  // now active
    {
        if (msprite->type == S_TIMEMINE)
        {
            angleinc = ANGLES / 20;
            angle    = 0;
            for (i = 0, angle = 0; i < 20; i++, angle += angleinc)
                sp = SpawnSprite(S_MINEBULLET,
                                 msprite->x,
                                 msprite->y,
                                 msprite->z,
                                 20 << FRACBITS,
                                 angle,
                                 0,
                                 true,
                                 msprite->spawnid);
            sp = SpawnSprite(S_EXPLODE, msprite->x, msprite->y, msprite->z, 0, 0, 0, true, 255);
            SoundEffect(SN_EXPLODE1 + (MS_RndT() & 1), 15, msprite->x, msprite->y);
            return true;
        }
        else if (msprite->type == S_PROXMINE)
        {
            if (MS_RndT() & 1)
                msprite->angle += 8;
            else
                msprite->angle -= 8;
            msprite->angle &= ANGLES;

            x        = msprite->x >> FRACTILESHIFT;
            y        = msprite->y >> FRACTILESHIFT;
            activate = false;
            if (abs(x - (player.x >> FRACTILESHIFT)) < 2
                && abs(y - (player.y >> FRACTILESHIFT)) < 2)
                activate = true;
            if (!activate)
                for (sp = firstscaleobj.next; sp != &lastscaleobj; sp = sp->next)
                    if (sp->hitpoints)
                    {
                        sx = sp->x >> FRACTILESHIFT;
                        sy = sp->y >> FRACTILESHIFT;
                        if (abs(x - sx) < 2 && abs(y - sy) < 2)
                        {
                            activate = true;
                            break;
                        }
                    }
            for (i = -1; i < 2; i++)
                for (j = -1; j < 2; j++)
                    if (mapsprites[(i + y) * MAPCOLS + j + x] == SM_NETPLAYER)
                        activate = true;
            if (activate)
            {
                angleinc = ANGLES / 16;
                angle    = 0;
                for (i = 0, angle = 0; i < 16; i++, angle += angleinc)
                    sp = SpawnSprite(S_MINEBULLET,
                                     msprite->x,
                                     msprite->y,
                                     msprite->z,
                                     20 << FRACBITS,
                                     angle,
                                     0,
                                     true,
                                     msprite->spawnid);
                sp = SpawnSprite(S_EXPLODE, msprite->x, msprite->y, msprite->z, 0, 0, 0, true, 255);
                SoundEffect(SN_EXPLODE1 + (MS_RndT() & 1), 15, msprite->x, msprite->y);
                return true;
            }
        }
        else if (msprite->type == S_INSTAWALL)
        {
            mapsprites[(msprite->y >> FRACTILESHIFT) * MAPCOLS + (msprite->x >> FRACTILESHIFT)] = 0;
            return true;
        }
    }
    return false;
}

/***************************************************************************/

int CloneScanX(int x, int y, int* x2)
/* check for the target along the x axis */
{
    int mapspot, wall, x1, limit, flags;

    mapspot = y * MAPCOLS + x + 1;
    x1      = x;
    limit   = 10;
    while (1)
    {
        if (mapsprites[mapspot] == SM_CLONE || mapsprites[mapspot] == 1)
        {
            *x2 = x1 + 1;
            return 1 + (MS_RndT() & 1);
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            break;
        wall  = westwall[mapspot];
        flags = westflags[mapspot];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            break;
        ++mapspot;
        ++x1;
        --limit;
        if (!limit)
            break;
    }
    limit   = 10;
    mapspot = y * MAPCOLS + x - 1;
    x1      = x;
    while (1)
    {
        if (mapsprites[mapspot] == SM_CLONE || mapsprites[mapspot] == 1)
        {
            *x2 = x1 - 1;
            return 1 + (MS_RndT() & 1);
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            return 1;
        wall  = westwall[mapspot + 1];
        flags = westflags[mapspot + 1];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            return 0;
        --mapspot;
        --x1;
        --limit;
        if (!limit)
            break;
    }
    return 0;
}


int CloneScanY(int x, int y, int* y2)
/* check for the player along the y axis */
{
    int mapspot, wall, y1, limit, flags;

    limit   = 10;
    mapspot = y * MAPCOLS + x + MAPCOLS;
    y1      = y;
    while (1)
    {
        if (mapsprites[mapspot] == SM_CLONE || mapsprites[mapspot] == 1)
        {
            *y2 = y1 + 1;
            return 1 + (MS_RndT() & 1);
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            break;
        wall  = northwall[mapspot];
        flags = northflags[mapspot];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            break;
        mapspot += MAPCOLS;
        ++y1;
        --limit;
        if (!limit)
            break;
    }
    limit   = 10;
    mapspot = y * MAPCOLS + x - MAPCOLS;
    y1      = y;
    while (1)
    {
        if (mapsprites[mapspot] == SM_CLONE || mapsprites[mapspot] == 1)
        {
            *y2 = y1 - 1;
            return 1 + (MS_RndT() & 1);
        }
        if (mapsprites[mapspot] > 0 && mapsprites[mapspot] < 128)
            break;
        wall  = northwall[mapspot + MAPCOLS];
        flags = northflags[mapspot + MAPCOLS];
        if (wall && !(flags & F_NOCLIP) && !(flags & F_NOBULLETCLIP))
            break;
        mapspot -= MAPCOLS;
        --y1;
        --limit;
        if (!limit)
            break;
    }
    return 0;
}


void Int7(void)  // clone ai
{
    int     angle = 0, sx, sy, px, py, pangle, r;
    fixed_t floorz, fheight;

    sx = msprite->x >> FRACTILESHIFT;
    sy = msprite->y >> FRACTILESHIFT;

    if (timecount > msprite->movetime)
    {
        angle = msprite->angle - DEGREE45;
        r     = MS_RndT() % 3;

        if (r == 1)
            angle += DEGREE45;
        else if (r == 2)
            angle += NORTH;

        msprite->angle    = angle & ANGLES;
        msprite->movetime = timecount + 250;
    }

    if (timecount > msprite->firetime && timecount > msprite->scantime)
    {
        px = sx;
        py = sy;
        if (CloneScanX(sx, sy, &px) > 1)
        {
            if (px > sx)
                angle = EAST;
            else if (px < sx)
                angle = WEST;
            msprite->angle    = angle & ANGLES;
            msprite->movemode = 4;
            msprite->basepic  = msprite->startpic + 24;
            fheight           = 40 << FRACBITS;
            pangle            = GetFireAngle(fheight, px, py, 0, 0, 0);
            SpawnSprite(msprite->bullet,
                        msprite->x,
                        msprite->y,
                        msprite->z,
                        fheight,
                        msprite->angle,
                        pangle,
                        true,
                        255);
            msprite->modetime   = timecount + 8;
            msprite->actiontime = timecount + 30;
            msprite->firetime   = timecount + 30;
        }
        else if (CloneScanY(sx, sy, &py) > 1)
        {
            if (py > sy)
                angle = SOUTH;
            else if (py < sy)
                angle = NORTH;
            msprite->angle    = angle & ANGLES;
            msprite->movemode = 4;
            msprite->basepic  = msprite->startpic + 24;
            fheight           = 40 << FRACBITS;
            pangle            = GetFireAngle(fheight, px, py, 0, 0, 0);
            SpawnSprite(msprite->bullet,
                        msprite->x,
                        msprite->y,
                        msprite->z,
                        fheight,
                        msprite->angle,
                        pangle,
                        true,
                        255);
            msprite->modetime   = timecount + 8;
            msprite->actiontime = timecount + 30;
            msprite->firetime   = timecount + 30;
        }
        msprite->scantime = timecount + 20;
    }

    if (timecount > msprite->modetime)
    {
        msprite->modetime = timecount + 10;
        switch (msprite->movemode)
        {
            case 0:  // left
            case 1:  // mid
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    ++msprite->movemode;
                    msprite->basepic = msprite->startpic + msprite->movemode * 8;
                    msprite->lasty   = msprite->y;
                    msprite->lastx   = msprite->x;
                }
                break;
            case 2:  // right
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    msprite->basepic = msprite->startpic + 8;
                    ++msprite->movemode;
                    msprite->lasty = msprite->y;
                    msprite->lastx = msprite->x;
                }
                break;
            case 3:  // mid #2
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    msprite->movemode = 0;
                    msprite->basepic  = msprite->startpic;
                    msprite->lasty    = msprite->y;
                    msprite->lastx    = msprite->x;
                }
                break;
            case 4:  // fire
                msprite->movemode = 0;
                msprite->basepic  = msprite->startpic;
                break;
        }
    }
    if (timecount > msprite->actiontime && SP_Thrust2() != 1)
    {
        angle          = msprite->angle + DEGREE45;
        msprite->angle = angle & ANGLES;
    }
    floorz = RF_GetFloorZ(msprite->x, msprite->y);
    if (floorz + msprite->zadj < msprite->z)
        msprite->z -= FRACUNIT << 4;
    if (floorz + msprite->zadj > msprite->z)
        msprite->z = floorz + msprite->zadj;
}

/***************************************************************************/

void Int8(void)  // prisoners
{
    int     angle, sx, sy, px, py, tx, ty, pangle;
    fixed_t floorz, oldspeed, fheight;

    sx = msprite->x >> FRACTILESHIFT;
    sy = msprite->y >> FRACTILESHIFT;
    if (netmode)
        NetGetClosestPlayer(sx, sy);
    else
    {
        if (specialeffect == SE_INVISIBILITY)
        {
            targx = 0;
            targy = 0;
            targz = 0;
        }
        else
        {
            targx = player.x;
            targy = player.y;
            targz = player.z;
        }
    }
    px = targx >> FRACTILESHIFT;
    py = targy >> FRACTILESHIFT;

    oldspeed = msprite->moveSpeed;
    if (abs(px - sx) < 6 && abs(py - sy) < 6)
        msprite->moveSpeed = msprite->moveSpeed * 2;

    if (timecount > msprite->movetime)
    {
        if (px > sx)
            angle = EAST;
        else if (px < sx)
            angle = WEST;
        else
            angle = -1;
        if (py < sy)
        {
            if (angle == EAST)
                angle += DEGREE45;
            else if (angle == WEST)
                angle -= DEGREE45;
            else
                angle = NORTH;
        }
        else if (py > sy)
        {
            if (angle == EAST)
                angle -= DEGREE45;
            else if (angle == WEST)
                angle += DEGREE45;
            else
                angle = SOUTH;
        }
        angle             = angle - DEGREE45 + MS_RndT();
        msprite->angle    = angle & ANGLES;
        msprite->movetime = timecount + 350;
    }

    if (timecount > msprite->firetime && timecount > msprite->scantime)
    {
        tx = px;
        ty = py;
        if (ScanX(7, sx, sy, px, py, &tx, &ty) > 1 || ScanY(7, sx, sy, px, py, &tx, &ty) > 1
            || ScanAngle(7, sx, sy, px, py, &tx, &ty) > 1)
        {
            if (tx > sx)
                angle = EAST;
            else if (tx < sx)
                angle = WEST;
            else
                angle = -1;
            if (ty < sy)
            {
                if (angle == EAST)
                    angle += DEGREE45;
                else if (angle == WEST)
                    angle -= DEGREE45;
                else
                    angle = NORTH;
            }
            else if (ty > sy)
            {
                if (angle == EAST)
                    angle -= DEGREE45;
                else if (angle == WEST)
                    angle += DEGREE45;
                else
                    angle = SOUTH;
            }
            msprite->angle = angle & ANGLES;

            if (abs(tx - sx) > 2 || abs(ty - sy) > 2)
            {
                msprite->scantime = timecount + 45;
                goto endscan;
            }

            msprite->basepic    = msprite->startpic + 24;
            msprite->movemode   = 4;
            msprite->firetime   = timecount + (80 + 5 * player.difficulty);
            msprite->actiontime = timecount + 30;
            msprite->modetime   = timecount + 15;
        }
        msprite->scantime = timecount + 45;
    }

endscan:
    if (timecount > msprite->modetime)
    {
        msprite->modetime = timecount + 10;
        switch (msprite->movemode)
        {
            case 0:  // left
            case 1:  // mid
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    ++msprite->movemode;
                    msprite->basepic = msprite->startpic + msprite->movemode * 8;
                    msprite->lasty   = msprite->y;
                    msprite->lastx   = msprite->x;
                }
                break;
            case 2:  // right
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    msprite->basepic = msprite->startpic + 8;  // midstep
                    ++msprite->movemode;
                    msprite->lasty = msprite->y;
                    msprite->lastx = msprite->x;
                }
                break;
            case 3:  // mid #2
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    msprite->movemode = 0;
                    msprite->basepic  = msprite->startpic;
                    msprite->lasty    = msprite->y;
                    msprite->lastx    = msprite->x;
                }
                break;
            case 4:  // fire #1
                tx = px;
                ty = py;
                if (ScanX(7, sx, sy, px, py, &tx, &ty) > 1 || ScanY(7, sx, sy, px, py, &tx, &ty) > 1
                    || ScanAngle(7, sx, sy, px, py, &tx, &ty) > 1)
                {
                    if (tx > sx)
                        angle = EAST;
                    else if (tx < sx)
                        angle = WEST;
                    else
                        angle = -1;
                    if (ty < sy)
                    {
                        if (angle == EAST)
                            angle += DEGREE45;
                        else if (angle == WEST)
                            angle -= DEGREE45;
                        else
                            angle = NORTH;
                    }
                    else if (ty > sy)
                    {
                        if (angle == EAST)
                            angle -= DEGREE45;
                        else if (angle == WEST)
                            angle += DEGREE45;
                        else
                            angle = SOUTH;
                    }

                    msprite->angle = angle & ANGLES;

                    if (abs(tx - sx) > 2 || abs(ty - sy) > 2)
                    {
                        msprite->movemode = 0;
                        msprite->basepic  = msprite->startpic;
                        break;
                    }

                    msprite->movemode = 5;
                    msprite->basepic  = msprite->startpic + 32;
                    fheight           = 40 << FRACBITS;
                    pangle            = GetFireAngle(fheight, tx, ty, targx, targy, targz);
                    SpawnSprite(msprite->bullet,
                                msprite->x,
                                msprite->y,
                                msprite->z,
                                fheight,
                                msprite->angle,
                                pangle,
                                true,
                                255);
                    msprite->modetime += 8;
                }
                else
                {
                    msprite->movemode = 0;
                    msprite->basepic  = msprite->startpic;
                }
                break;
            case 5:  // fire #2
                msprite->movemode = 0;
                msprite->basepic  = msprite->startpic;
                break;
        }
    }

    if (timecount > msprite->actiontime && SP_Thrust2() != 1)
    {
        angle          = msprite->angle + DEGREE45;
        msprite->angle = angle & ANGLES;
    }
    floorz = RF_GetFloorZ(msprite->x, msprite->y);
    if (floorz + msprite->zadj < msprite->z)
        msprite->z -= FRACUNIT << 4;
    if (floorz + msprite->zadj > msprite->z)
        msprite->z = floorz + msprite->zadj;
    msprite->moveSpeed = oldspeed;
    if (MS_RndT() >= 255)
        ActivationSound(msprite);
}

/***************************************************************************/

void Int9(void)  // big guards only
{
    int     i, angleinc, angle, sx, sy, px, py, tx, ty, pangle;
    fixed_t floorz, oldspeed, fheight;

    if (msprite->hitpoints < 1000)
        msprite->hitpoints += 10;
    msprite->enraged = 0;
    sx               = msprite->x >> FRACTILESHIFT;
    sy               = msprite->y >> FRACTILESHIFT;
    if (netmode)
        NetGetClosestPlayer(sx, sy);
    else
    {
        if (specialeffect == SE_INVISIBILITY)
        {
            targx = 0;
            targy = 0;
            targz = 0;
        }
        else
        {
            targx = player.x;
            targy = player.y;
            targz = player.z;
        }
    }
    px = targx >> FRACTILESHIFT;
    py = targy >> FRACTILESHIFT;

    oldspeed = msprite->moveSpeed;
    if (abs(px - sx) < 6 && abs(py - sy) < 6)
        msprite->moveSpeed = msprite->moveSpeed * 2;

    if (timecount > msprite->movetime)
    {
        if (px > sx)
            angle = EAST;
        else if (px < sx)
            angle = WEST;
        else
            angle = -1;
        if (py < sy)
        {
            if (angle == EAST)
                angle += DEGREE45;
            else if (angle == WEST)
                angle -= DEGREE45;
            else
                angle = NORTH;
        }
        else if (py > sy)
        {
            if (angle == EAST)
                angle -= DEGREE45;
            else if (angle == WEST)
                angle += DEGREE45;
            else
                angle = SOUTH;
        }
        angle             = angle - DEGREE45 + MS_RndT();
        msprite->angle    = angle & ANGLES;
        msprite->movetime = timecount + 200;
    }

    if (timecount > msprite->firetime && timecount > msprite->scantime)
    {
        SoundEffect(SN_MON11_WAKE, 7, msprite->x, msprite->y);
        tx = px;
        ty = py;
        if (ScanX(8, sx, sy, px, py, &tx, &ty) > 1 || ScanY(8, sx, sy, px, py, &tx, &ty) > 1
            || ScanAngle(8, sx, sy, px, py, &tx, &ty) > 1)
        {
            if (tx > sx)
                angle = EAST;
            else if (tx < sx)
                angle = WEST;
            else
                angle = -1;
            if (ty < sy)
            {
                if (angle == EAST)
                    angle += DEGREE45;
                else if (angle == WEST)
                    angle -= DEGREE45;
                else
                    angle = NORTH;
            }
            else if (ty > sy)
            {
                if (angle == EAST)
                    angle -= DEGREE45;
                else if (angle == WEST)
                    angle += DEGREE45;
                else
                    angle = SOUTH;
            }
            msprite->angle      = angle & ANGLES;
            msprite->basepic    = msprite->startpic + 24;
            msprite->movemode   = 4;
            msprite->firetime   = timecount + (120 + 5 * player.difficulty);
            msprite->actiontime = timecount + 30;
            msprite->modetime   = timecount + 20;
        }
        msprite->scantime = timecount + 45;
    }

    if (timecount > msprite->modetime)
    {
        msprite->modetime = timecount + 20;
        switch (msprite->movemode)
        {
            case 0:  // left
            case 1:  // mid
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    ++msprite->movemode;
                    msprite->basepic = msprite->startpic + msprite->movemode * 8;
                    msprite->lasty   = msprite->y;
                    msprite->lastx   = msprite->x;
                }
                if (MS_RndT() < 32)
                {
                    angle    = 0;
                    angleinc = ANGLES / 16;
                    for (i = 0; i < 16; i++, angle += angleinc)
                        SpawnSprite(msprite->bullet,
                                    msprite->x,
                                    msprite->y,
                                    msprite->z,
                                    32 << FRACBITS,
                                    angle,
                                    0,
                                    true,
                                    255);
                    SoundEffect(SN_MON11_FIRE, 7, msprite->x, msprite->y);
                }
                break;
            case 2:  // right
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    msprite->basepic = msprite->startpic + 8;  // midstep
                    ++msprite->movemode;
                    msprite->lasty = msprite->y;
                    msprite->lastx = msprite->x;
                }
                if (MS_RndT() < 32)
                {
                    angle    = 0;
                    angleinc = ANGLES / 16;
                    for (i = 0; i < 16; i++, angle += angleinc)
                        SpawnSprite(msprite->bullet,
                                    msprite->x,
                                    msprite->y,
                                    msprite->z,
                                    32 << FRACBITS,
                                    angle,
                                    0,
                                    true,
                                    255);
                    SoundEffect(SN_MON11_FIRE, 7, msprite->x, msprite->y);
                }
                break;
            case 3:  // mid #2
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    msprite->movemode = 0;
                    msprite->basepic  = msprite->startpic;
                    msprite->lasty    = msprite->y;
                    msprite->lastx    = msprite->x;
                }
                if (MS_RndT() < 32)
                {
                    angle    = 0;
                    angleinc = ANGLES / 16;
                    for (i = 0; i < 16; i++, angle += angleinc)
                        SpawnSprite(msprite->bullet,
                                    msprite->x,
                                    msprite->y,
                                    msprite->z,
                                    32 << FRACBITS,
                                    angle,
                                    0,
                                    true,
                                    255);
                    SoundEffect(SN_MON11_FIRE, 7, msprite->x, msprite->y);
                }
                break;
            case 4:  // firing bullets
            case 5:
            case 6:
            case 7:
                tx = px;
                ty = py;
                if (ScanX(8, sx, sy, px, py, &tx, &ty) > 1 || ScanY(8, sx, sy, px, py, &tx, &ty) > 1
                    || ScanAngle(8, sx, sy, px, py, &tx, &ty) > 1)
                {
                    if (tx > sx)
                        angle = EAST;
                    else if (tx < sx)
                        angle = WEST;
                    else
                        angle = -1;
                    if (ty < sy)
                    {
                        if (angle == EAST)
                            angle += DEGREE45;
                        else if (angle == WEST)
                            angle -= DEGREE45;
                        else
                            angle = NORTH;
                    }
                    else if (ty > sy)
                    {
                        if (angle == EAST)
                            angle -= DEGREE45;
                        else if (angle == WEST)
                            angle += DEGREE45;
                        else
                            angle = SOUTH;
                    }
                    msprite->angle = angle & ANGLES;
                    ++msprite->movemode;
                    msprite->basepic = msprite->startpic + 32;
                    fheight          = 70 << FRACBITS;
                    if (msprite->movemode == 5 && MS_RndT() < 32)
                    {
                        SpawnSprite(S_GRENADE,
                                    msprite->x,
                                    msprite->y,
                                    msprite->z,
                                    fheight,
                                    msprite->angle - 15 + (MS_RndT() & 31),
                                    0,
                                    true,
                                    255);
                        SpawnSprite(S_GRENADE,
                                    msprite->x,
                                    msprite->y,
                                    msprite->z,
                                    fheight,
                                    msprite->angle + 15 + (MS_RndT() & 31),
                                    0,
                                    true,
                                    255);
                        SoundEffect(SN_GRENADE, 0, msprite->x, msprite->y);
                        msprite->movemode   = 0;
                        msprite->basepic    = msprite->startpic;
                        msprite->firetime   = timecount + (120 + 5 * player.difficulty);
                        msprite->actiontime = timecount + 30;
                    }
                    else
                    {
                        pangle = GetFireAngle(fheight, tx, ty, targx, targy, targz) - 15
                                 + (MS_RndT() & 31);
                        SpawnSprite(msprite->bullet,
                                    msprite->x,
                                    msprite->y,
                                    msprite->z,
                                    fheight,
                                    msprite->angle - 15 + (MS_RndT() & 31),
                                    pangle,
                                    true,
                                    255);
                        SoundEffect(SN_MON11_FIRE, 7, msprite->x, msprite->y);
                        msprite->modetime = timecount + 15;
                    }
                }
                else
                {
                    msprite->movemode   = 0;
                    msprite->basepic    = msprite->startpic;
                    msprite->firetime   = timecount + (120 + 5 * player.difficulty);
                    msprite->actiontime = timecount + 30;
                }
                break;
            case 8:  // fire #2
                msprite->movemode   = 0;
                msprite->basepic    = msprite->startpic;
                msprite->firetime   = timecount + (120 + 5 * player.difficulty);
                msprite->actiontime = timecount + 30;
                break;
        }
    }

    if (timecount > msprite->actiontime && SP_Thrust2() != 1)
    {
        angle          = msprite->angle + DEGREE45;
        msprite->angle = angle & ANGLES;
    }
    floorz = RF_GetFloorZ(msprite->x, msprite->y);
    if (floorz + msprite->zadj < msprite->z)
        msprite->z -= FRACUNIT << 4;
    if (floorz + msprite->zadj > msprite->z)
        msprite->z = floorz + msprite->zadj;
    msprite->moveSpeed = oldspeed;
    if (MS_RndT() >= 255)
        ActivationSound(msprite);
}

/***************************************************************************/

void Int10(void)
{
    int     angle, sx, sy, px, py, tx, ty, pangle;
    fixed_t floorz, oldspeed, fheight;

    if (msprite->type == S_MONSTER5 && msprite->hitpoints < 5000)
        msprite->hitpoints += 4;
    else if (msprite->type == S_MONSTER13 && msprite->hitpoints < 300)
    {
        msprite->hitpoints += 25;
        msprite->enraged = 0;
    }
    else if (msprite->type == S_MONSTER15 && msprite->hitpoints < 2000)
    {
        msprite->hitpoints += 6;
        msprite->enraged = 0;
    }
    else if (msprite->type == S_MONSTER14 && msprite->hitpoints < 350)
        msprite->hitpoints += 1;
    sx = msprite->x >> FRACTILESHIFT;
    sy = msprite->y >> FRACTILESHIFT;
    if (netmode)
        NetGetClosestPlayer(sx, sy);
    else
    {
        if (specialeffect == SE_INVISIBILITY)
        {
            targx = 0;
            targy = 0;
            targz = 0;
        }
        else
        {
            targx = player.x;
            targy = player.y;
            targz = player.z;
        }
    }
    px = targx >> FRACTILESHIFT;
    py = targy >> FRACTILESHIFT;

    oldspeed = msprite->moveSpeed;
    if (abs(px - sx) < 6 && abs(py - sy) < 6)
        msprite->moveSpeed = msprite->moveSpeed * 2;

    if (timecount > msprite->movetime)
    {
        if (px > sx)
            angle = EAST;
        else if (px < sx)
            angle = WEST;
        else
            angle = -1;
        if (py < sy)
        {
            if (angle == EAST)
                angle += DEGREE45;
            else if (angle == WEST)
                angle -= DEGREE45;
            else
                angle = NORTH;
        }
        else if (py > sy)
        {
            if (angle == EAST)
                angle -= DEGREE45;
            else if (angle == WEST)
                angle += DEGREE45;
            else
                angle = SOUTH;
        }
        angle             = angle - DEGREE45 + MS_RndT();
        msprite->angle    = angle & ANGLES;
        msprite->movetime = timecount + 350;
    }

    if (timecount > msprite->firetime && timecount > msprite->scantime)
    {
        tx = px;
        ty = py;
        if (ScanX(10, sx, sy, px, py, &tx, &ty) > 1 || ScanY(10, sx, sy, px, py, &tx, &ty) > 1
            || ScanAngle(10, sx, sy, px, py, &tx, &ty) > 1)
        {
            if (tx > sx)
                angle = EAST;
            else if (tx < sx)
                angle = WEST;
            else
                angle = -1;
            if (ty < sy)
            {
                if (angle == EAST)
                    angle += DEGREE45;
                else if (angle == WEST)
                    angle -= DEGREE45;
                else
                    angle = NORTH;
            }
            else if (ty > sy)
            {
                if (angle == EAST)
                    angle -= DEGREE45;
                else if (angle == WEST)
                    angle += DEGREE45;
                else
                    angle = SOUTH;
            }
            msprite->angle = angle & ANGLES;

            if (msprite->type == S_MONSTER7 && (abs(tx - sx) > 2 || abs(ty - sy) > 2))
            {
                msprite->scantime = timecount + 30;
                goto endscan;
            }

            if (msprite->type == S_MONSTER15 && (abs(tx - sx) > 4 || abs(ty - sy) > 4))
            {
                msprite->scantime = timecount + 30;
                goto endscan;
            }

            msprite->basepic  = msprite->startpic + 32;
            msprite->movemode = 6;
            if (msprite->type == S_MONSTER5)
                msprite->firetime = timecount + (10 + 3 * player.difficulty);
            else
                msprite->firetime = timecount + (40 + 5 * player.difficulty);

            msprite->actiontime = timecount + 30;
            msprite->modetime   = timecount + 15;
            if (msprite->type == S_MONSTER3)
                fheight = 3 << FRACBITS;
            else if (msprite->type == S_MONSTER6)
                fheight = 100 << FRACBITS;
            else
                fheight = 40 << FRACBITS;

            pangle = GetFireAngle(fheight, tx, ty, targx, targy, targz) - 15 + (MS_RndT() & 31);
            if (msprite->type == S_MONSTER13 || msprite->type == S_MONSTER6
                || msprite->type == S_MONSTER15 || msprite->type == S_MONSTER5)
            {
                SpawnSprite(msprite->bullet,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            fheight,
                            msprite->angle - 15 + (MS_RndT() & 31) + 16,
                            pangle,
                            true,
                            255);
                SpawnSprite(msprite->bullet,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            fheight,
                            msprite->angle - 15 + (MS_RndT() & 31) - 16,
                            pangle,
                            true,
                            255);
                SpawnSprite(msprite->bullet,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            fheight,
                            msprite->angle - 15 + (MS_RndT() & 31),
                            pangle,
                            true,
                            255);
            }
            else if (msprite->type == S_MONSTER4)
            {
                SpawnSprite(msprite->bullet,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            fheight,
                            msprite->angle - 15 + (MS_RndT() & 31) + 8,
                            pangle,
                            true,
                            255);
                SpawnSprite(msprite->bullet,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            fheight,
                            msprite->angle - 15 + (MS_RndT() & 31) - 8,
                            pangle,
                            true,
                            255);
            }
            else
                SpawnSprite(msprite->bullet,
                            msprite->x,
                            msprite->y,
                            msprite->z,
                            fheight,
                            msprite->angle - 15 + (MS_RndT() & 31),
                            pangle,
                            true,
                            255);
        }
        msprite->scantime = timecount + 30;
    }
endscan:
    if (timecount > msprite->modetime)
    {
        msprite->modetime = timecount + 8;
        switch (msprite->movemode)
        {
            case 0:  // 1
            case 1:  // 2
            case 2:  // 3
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    ++msprite->movemode;
                    msprite->basepic = msprite->startpic + msprite->movemode * 8;
                    msprite->lasty   = msprite->y;
                    msprite->lastx   = msprite->x;
                }
                break;
            case 3:  // 2
            case 4:  // 1
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    msprite->movemode++;
                    msprite->basepic = msprite->startpic + (6 - msprite->movemode) * 8;
                    msprite->lasty   = msprite->y;
                    msprite->lastx   = msprite->x;
                }
                break;
            case 5:
                if (msprite->lastx != msprite->x || msprite->lasty != msprite->y)
                {
                    msprite->movemode = 0;
                    msprite->basepic  = msprite->startpic;
                    msprite->lasty    = msprite->y;
                    msprite->lastx    = msprite->x;
                }
                break;
            case 6:  // fire
                msprite->movemode = 0;
                msprite->basepic  = msprite->startpic;
                break;
        }
    }

    if (timecount > msprite->actiontime && SP_Thrust2() != 1)
    {
        angle          = msprite->angle + DEGREE45;
        msprite->angle = angle & ANGLES;
    }
    floorz = RF_GetFloorZ(msprite->x, msprite->y) + msprite->zadj;
    if (floorz < msprite->z)
        msprite->z -= FRACUNIT << 4;
    if (floorz > msprite->z)
        msprite->z = floorz;
    msprite->moveSpeed = oldspeed;
    if (MS_RndT() >= 255)
        ActivationSound(msprite);
}


/***************************************************************************/

void MoveSprites(void)
{
    int     mapspot, i, j, c, px = 0, py = 0, sx, sy;
    boolean killed;
    fixed_t floor;

    if (!netmode)
    {
        targx = player.x;
        targy = player.y;
        px    = targx >> FRACTILESHIFT;
        py    = targy >> FRACTILESHIFT;
    }

    for (msprite = firstscaleobj.next; msprite != &lastscaleobj; msprite = msprite->next)
    {
        if (msprite->active)
        {
            if (msprite->moveSpeed)
                switch (msprite->intelligence)
                {
                    case 0:
                        killed = Int0();
                        if (killed)
                        {
                            if (msprite->type == S_BLOODSPLAT)
                            {
                                msprite->intelligence = 128;
                                break;
                            }
                            else if (msprite->type == S_METALPARTS && !spritehit)
                            {
                                killed = false;
                                continue;
                            }
                            msprite = msprite->prev;
                            RF_RemoveSprite(msprite->next);
                            killed = false;
                            continue;
                        }
                        break;
                    case 5:
                        Int5();
                        break;
                    case 6:
                        killed = Int6();
                        if (killed)
                        {
                            msprite = msprite->prev;
                            RF_RemoveSprite(msprite->next);
                            killed = false;
                            continue;
                        }
                        break;
                    case 7:
                        Int7();
                        break;
                    case 8:
                        Int8();
                        break;
                    case 9:
                        Int9();
                        break;
                    case 10:
                        Int10();
                        break;
                    case 128:
                        floor = RF_GetFloorZ(msprite->x, msprite->y);
                        if (msprite->z > floor + FRACUNIT)
                            msprite->z -= FRACUNIT * 2;
                        if (msprite->z < floor)
                            msprite->z = floor;
                        break;
                }
            if (!killed && msprite->heat)
            {
                mapspot = (msprite->y >> FRACTILESHIFT) * MAPCOLS + (msprite->x >> FRACTILESHIFT);
                if (msprite->heat > 256)
                {
                    c = msprite->heat >> 1;
                    for (i = -1; i < 2; i++)
                        for (j = -1; j < 2; j++)
                            reallight[mapspot + (i * MAPCOLS) + j] -= c;
                    reallight[mapspot] -= msprite->heat >> 2;
                }
                else
                    reallight[mapspot] -= msprite->heat;
            }
            killed = false;
        }
        else if (msprite->intelligence != 255)
        {
            if (msprite->moveSpeed)
            {
                sx = msprite->x >> FRACTILESHIFT;
                sy = msprite->y >> FRACTILESHIFT;
                if (netmode)
                {
                    NetGetClosestPlayer(sx, sy);
                    px = targx >> FRACTILESHIFT;
                    py = targy >> FRACTILESHIFT;
                }

                if ((abs(px - sx) < 6 && abs(py - sy) < 6))
                {
                    msprite->active = true;
                    ActivateSprites(sx, sy);
                }
            }

            floor = RF_GetFloorZ(msprite->x, msprite->y) + msprite->zadj;
            if (msprite->z > floor)
                msprite->z -= FRACUNIT << 4;
            if (msprite->z < floor)
                msprite->z = floor;
            if (msprite->heat)
            {
                mapspot = (msprite->y >> FRACTILESHIFT) * MAPCOLS + (msprite->x >> FRACTILESHIFT);
                if (msprite->heat > 256)
                {
                    c = msprite->heat >> 1;
                    for (i = -1; i < 2; i++)
                        for (j = -1; j < 2; j++)
                            reallight[mapspot + (i * MAPCOLS) + j] -= c;
                    reallight[mapspot] -= msprite->heat >> 2;
                    msprite->heat -= 64;
                }
                else
                    reallight[mapspot] -= msprite->heat;
            }
        }
    }
}
