// WL_STATE.C

#include "WL_DEF.H"
#pragma hdrstop

/*
=============================================================================

                                                 LOCAL CONSTANTS

=============================================================================
*/


/*
=============================================================================

                                                 GLOBAL VARIABLES

=============================================================================
*/


dirtype opposite[9] =
        {west,southwest,south,southeast,east,northeast,north,northwest,nodir};

dirtype diagonal[9][9] =
{
/* east */      {nodir,nodir,northeast,nodir,nodir,nodir,southeast,nodir,nodir},
                        {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
/* north */ {northeast,nodir,nodir,nodir,northwest,nodir,nodir,nodir,nodir},
                        {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
/* west */  {nodir,nodir,northwest,nodir,nodir,nodir,southwest,nodir,nodir},
                        {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
/* south */ {southeast,nodir,nodir,nodir,southwest,nodir,nodir,nodir,nodir},
                        {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir},
                        {nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir,nodir}
};



void    SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state);
void    NewState (objtype *ob, statetype *state);

boolean TryWalk (objtype *ob);
void    MoveObj (objtype *ob, long move);

void    KillActor (objtype *ob);
void    DamageActor (objtype *ob, unsigned damage);

boolean CheckLine (objtype *ob);
void FirstSighting (objtype *ob);
boolean CheckSight (objtype *ob);

/*
=============================================================================

                                                 LOCAL VARIABLES

=============================================================================
*/



//===========================================================================


/*
===================
=
= SpawnNewObj
=
= Spaws a new actor at the given TILE coordinates, with the given state, and
= the given size in GLOBAL units.
=
= new                   = a pointer to an initialized new actor
=
===================
*/

void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state)
{
        GetNewActor ();
        newobj->state = state;
        if (state->tictime)
                newobj->ticcount = US_RndT () % state->tictime + 1;     // Chris' moonwalk bugfix ;D
        else
                newobj->ticcount = 0;

        newobj->tilex = (short) tilex;
        newobj->tiley = (short) tiley;
        newobj->x = ((long)tilex<<TILESHIFT)+TILEGLOBAL/2;
        newobj->y = ((long)tiley<<TILESHIFT)+TILEGLOBAL/2;
        newobj->dir = nodir;

        actorat[tilex][tiley] = newobj;
        newobj->areanumber =
                *(mapsegs[0] + (newobj->tiley<<mapshift)+newobj->tilex) - AREATILE;
}



/*
===================
=
= NewState
=
= Changes ob to a new state, setting ticcount to the max for that state
=
===================
*/

void NewState (objtype *ob, statetype *state)
{
        ob->state = state;
        ob->ticcount = state->tictime;
}



/*
=============================================================================

                                ENEMY TILE WORLD MOVEMENT CODE

=============================================================================
*/


/*
==================================
=
= TryWalk
=
= Attempts to move ob in its current (ob->dir) direction.
=
= If blocked by either a wall or an actor returns FALSE
=
= If move is either clear or blocked only by a door, returns TRUE and sets
=
= ob->tilex                     = new destination
= ob->tiley
= ob->areanumber    = the floor tile number (0-(NUMAREAS-1)) of destination
= ob->distance          = TILEGLOBAl, or -doornumber if a door is blocking the way
=
= If a door is in the way, an OpenDoor call is made to start it opening.
= The actor code should wait until
=       doorobjlist[-ob->distance].action = dr_open, meaning the door has been
=       fully opened
=
==================================
*/

#define CHECKDIAG(x,y)                                                          \
{                                                   \
        temp=(unsigned)actorat[x][y];                   \
        if (temp)                                       \
        {                                               \
                if (temp<256)                               \
                        return false;                           \
                if (((objtype *)temp)->flags&FL_SHOOTABLE)  \
                        return false;                           \
        }                                               \
}

#define CHECKSIDE(x,y)                                                          \
{                                                   \
        temp=(unsigned)actorat[x][y];                   \
        if (temp)                                       \
        {                                               \
                if (temp<128)                               \
                        return false;                           \
                if (temp<256)                               \
                {                                       \
                        doornum = temp&127;              \
                        OpenDoor (doornum);             \
                        ob->distance = -doornum-1;      \
                        return true;                    \
                }                                       \
                else if (((objtype *)temp)->flags&FL_SHOOTABLE)\
                        return false;                           \
        }                                               \
}


boolean TryWalk (objtype *ob)
{
        int                     doornum;
        unsigned temp;

        if (ob->obclass == inertobj)
        {
                switch (ob->dir)
                {
                case north:
                        ob->tiley--;
                        break;

                case northeast:
                        ob->tilex++;
                        ob->tiley--;
                        break;

                case east:
                        ob->tilex++;
                        break;

                case southeast:
                        ob->tilex++;
                        ob->tiley++;
                        break;

                case south:
                        ob->tiley++;
                        break;

                case southwest:
                        ob->tilex--;
                        ob->tiley++;
                        break;

                case west:
                        ob->tilex--;
                        break;

                case northwest:
                        ob->tilex--;
                        ob->tiley--;
                        break;
                }
        }
        else
                switch (ob->dir)
                {
                case north:
                        if (ob->obclass == dogobj || ob->obclass == fakeobj)
                        {
                                CHECKDIAG(ob->tilex,ob->tiley-1);
                        }
                        else
                        {
                                CHECKSIDE(ob->tilex,ob->tiley-1);
                        }
                        ob->tiley--;
                        break;

                case northeast:
                        CHECKDIAG(ob->tilex+1,ob->tiley-1);
                        CHECKDIAG(ob->tilex+1,ob->tiley);
                        CHECKDIAG(ob->tilex,ob->tiley-1);
                        ob->tilex++;
                        ob->tiley--;
                        break;

                case east:
                        if (ob->obclass == dogobj || ob->obclass == fakeobj)
                        {
                                CHECKDIAG(ob->tilex+1,ob->tiley);
                        }
                        else
                        {
                                CHECKSIDE(ob->tilex+1,ob->tiley);
                        }
                        ob->tilex++;
                        break;

                case southeast:
                        CHECKDIAG(ob->tilex+1,ob->tiley+1);
                        CHECKDIAG(ob->tilex+1,ob->tiley);
                        CHECKDIAG(ob->tilex,ob->tiley+1);
                        ob->tilex++;
                        ob->tiley++;
                        break;

                case south:
                        if (ob->obclass == dogobj || ob->obclass == fakeobj)
                        {
                                CHECKDIAG(ob->tilex,ob->tiley+1);
                        }
                        else
                        {
                                CHECKSIDE(ob->tilex,ob->tiley+1);
                        }
                        ob->tiley++;
                        break;

                case southwest:
                        CHECKDIAG(ob->tilex-1,ob->tiley+1);
                        CHECKDIAG(ob->tilex-1,ob->tiley);
                        CHECKDIAG(ob->tilex,ob->tiley+1);
                        ob->tilex--;
                        ob->tiley++;
                        break;

                case west:
                        if (ob->obclass == dogobj || ob->obclass == fakeobj)
                        {
                                CHECKDIAG(ob->tilex-1,ob->tiley);
                        }
                        else
                        {
                                CHECKSIDE(ob->tilex-1,ob->tiley);
                        }
                        ob->tilex--;
                        break;

                case northwest:
                        CHECKDIAG(ob->tilex-1,ob->tiley-1);
                        CHECKDIAG(ob->tilex-1,ob->tiley);
                        CHECKDIAG(ob->tilex,ob->tiley-1);
                        ob->tilex--;
                        ob->tiley--;
                        break;

                case nodir:
                        return false;

                default:
                        Quit ("Walk: Bad dir");
                }

        ob->areanumber =
                *(mapsegs[0] + (ob->tiley<<mapshift)+ob->tilex) - AREATILE;

        ob->distance = TILEGLOBAL;
        return true;
}


/*
==================================
=
= SelectDodgeDir
=
= Attempts to choose and initiate a movement for ob that sends it towards
= the player while dodging
=
= If there is no possible move (ob is totally surrounded)
=
= ob->dir                       =       nodir
=
= Otherwise
=
= ob->dir                       = new direction to follow
= ob->distance          = TILEGLOBAL or -doornumber
= ob->tilex                     = new destination
= ob->tiley
= ob->areanumber    = the floor tile number (0-(NUMAREAS-1)) of destination
=
==================================
*/

void SelectDodgeDir (objtype *ob)
{
        int             deltax,deltay,i;
        unsigned absdx,absdy;
        dirtype         dirtry[5];
        dirtype         turnaround,tdir;

        if (ob->flags & FL_FIRSTATTACK)
        {
        //
        // turning around is only ok the very first time after noticing the
        // player
        //
                turnaround = nodir;
                ob->flags &= ~FL_FIRSTATTACK;
        }
        else
                turnaround=opposite[ob->dir];

        deltax = player->tilex - ob->tilex;
        deltay = player->tiley - ob->tiley;

//
// arange 5 direction choices in order of preference
// the four cardinal directions plus the diagonal straight towards
// the player
//

        if (deltax>0)
        {
                dirtry[1]= east;
                dirtry[3]= west;
        }
        else
        {
                dirtry[1]= west;
                dirtry[3]= east;
        }

        if (deltay>0)
        {
                dirtry[2]= south;
                dirtry[4]= north;
        }
        else
        {
                dirtry[2]= north;
                dirtry[4]= south;
        }

//
// randomize a bit for dodging
//
        absdx = abs(deltax);
        absdy = abs(deltay);

        if (absdx > absdy)
        {
                tdir = dirtry[1];
                dirtry[1] = dirtry[2];
                dirtry[2] = tdir;
                tdir = dirtry[3];
                dirtry[3] = dirtry[4];
                dirtry[4] = tdir;
        }

        if (US_RndT() < 128)
        {
                tdir = dirtry[1];
                dirtry[1] = dirtry[2];
                dirtry[2] = tdir;
                tdir = dirtry[3];
                dirtry[3] = dirtry[4];
                dirtry[4] = tdir;
        }

        dirtry[0] = diagonal [ dirtry[1] ] [ dirtry[2] ];

//
// try the directions util one works
//
        for (i=0;i<5;i++)
        {
                if ( dirtry[i] == nodir || dirtry[i] == turnaround)
                        continue;

                ob->dir = dirtry[i];
                if (TryWalk(ob))
                        return;
        }

//
// turn around only as a last resort
//
        if (turnaround != nodir)
        {
                ob->dir = turnaround;

                if (TryWalk(ob))
                        return;
        }

        ob->dir = nodir;
}


/*
============================
=
= SelectChaseDir
=
= As SelectDodgeDir, but doesn't try to dodge
=
============================
*/

void SelectChaseDir (objtype *ob)
{
        int deltax,deltay;
        dirtype d[3];
        dirtype tdir, olddir, turnaround;


        olddir=ob->dir;
        turnaround=opposite[olddir];

        deltax=player->tilex - ob->tilex;
        deltay=player->tiley - ob->tiley;

        d[1]=nodir;
        d[2]=nodir;

        if (deltax>0)
                d[1]= east;
        else if (deltax<0)
                d[1]= west;
        if (deltay>0)
                d[2]=south;
        else if (deltay<0)
                d[2]=north;

        if (abs(deltay)>abs(deltax))
        {
                tdir=d[1];
                d[1]=d[2];
                d[2]=tdir;
        }

        if (d[1]==turnaround)
                d[1]=nodir;
        if (d[2]==turnaround)
                d[2]=nodir;


        if (d[1]!=nodir)
        {
                ob->dir=d[1];
                if (TryWalk(ob))
                        return;     /*either moved forward or attacked*/
        }

        if (d[2]!=nodir)
        {
                ob->dir=d[2];
                if (TryWalk(ob))
                        return;
        }

/* there is no direct path to the player, so pick another direction */

        if (olddir!=nodir)
        {
                ob->dir=olddir;
                if (TryWalk(ob))
                        return;
        }

        if (US_RndT()>128)      /*randomly determine direction of search*/
        {
                for (tdir=north;tdir<=west;tdir=(dirtype)(tdir+1))
                {
                        if (tdir!=turnaround)
                        {
                                ob->dir=tdir;
                                if ( TryWalk(ob) )
                                        return;
                        }
                }
        }
        else
        {
                for (tdir=west;tdir>=north;tdir=(dirtype)(tdir-1))
                {
                        if (tdir!=turnaround)
                        {
                          ob->dir=tdir;
                          if ( TryWalk(ob) )
                                return;
                        }
                }
        }

        if (turnaround !=  nodir)
        {
                ob->dir=turnaround;
                if (ob->dir != nodir)
                {
                        if ( TryWalk(ob) )
                                return;
                }
        }

        ob->dir = nodir;                // can't move
}


/*
============================
=
= SelectRunDir
=
= Run Away from player
=
============================
*/

void SelectRunDir (objtype *ob)
{
        int deltax,deltay;
        dirtype d[3];
        dirtype tdir;


        deltax=player->tilex - ob->tilex;
        deltay=player->tiley - ob->tiley;

        if (deltax<0)
                d[1]= east;
        else
                d[1]= west;
        if (deltay<0)
                d[2]=south;
        else
                d[2]=north;

        if (abs(deltay)>abs(deltax))
        {
                tdir=d[1];
                d[1]=d[2];
                d[2]=tdir;
        }

        ob->dir=d[1];
        if (TryWalk(ob))
                return;     /*either moved forward or attacked*/

        ob->dir=d[2];
        if (TryWalk(ob))
                return;

/* there is no direct path to the player, so pick another direction */

        if (US_RndT()>128)      /*randomly determine direction of search*/
        {
                for (tdir=north;tdir<=west;tdir=(dirtype)(tdir+1))
                {
                        ob->dir=tdir;
                        if ( TryWalk(ob) )
                                return;
                }
        }
        else
        {
                for (tdir=west;tdir>=north;tdir=(dirtype)(tdir-1))
                {
                        ob->dir=tdir;
                        if ( TryWalk(ob) )
                          return;
                }
        }

        ob->dir = nodir;                // can't move
}


/*
=================
=
= MoveObj
=
= Moves ob be move global units in ob->dir direction
= Actors are not allowed to move inside the player
= Does NOT check to see if the move is tile map valid
=
= ob->x                 = adjusted for new position
= ob->y
=
=================
*/

void MoveObj (objtype *ob, long move)
{
        long    deltax,deltay;

        switch (ob->dir)
        {
        case north:
                ob->y -= move;
                break;
        case northeast:
                ob->x += move;
                ob->y -= move;
                break;
        case east:
                ob->x += move;
                break;
        case southeast:
                ob->x += move;
                ob->y += move;
                break;
        case south:
                ob->y += move;
                break;
        case southwest:
                ob->x -= move;
                ob->y += move;
                break;
        case west:
                ob->x -= move;
                break;
        case northwest:
                ob->x -= move;
                ob->y -= move;
                break;

        case nodir:
                return;

        default:
                Quit ("MoveObj: bad dir!");
        }

//
// check to make sure it's not on top of player
//
        if (areabyplayer[ob->areanumber])
        {
                deltax = ob->x - player->x;
                if (deltax < -MINACTORDIST || deltax > MINACTORDIST)
                        goto moveok;
                deltay = ob->y - player->y;
                if (deltay < -MINACTORDIST || deltay > MINACTORDIST)
                        goto moveok;
                
                if (ob->hidden)          // move closer until he meets CheckLine
                        goto moveok;

                if (ob->obclass == ghostobj || ob->obclass == spectreobj)
                        TakeDamage (tics*2,ob);

        //
        // back up
        //
                switch (ob->dir)
                {
                case north:
                        ob->y += move;
                        break;
                case northeast:
                        ob->x -= move;
                        ob->y += move;
                        break;
                case east:
                        ob->x -= move;
                        break;
                case southeast:
                        ob->x -= move;
                        ob->y -= move;
                        break;
                case south:
                        ob->y -= move;
                        break;
                case southwest:
                        ob->x += move;
                        ob->y -= move;
                        break;
                case west:
                        ob->x += move;
                        break;
                case northwest:
                        ob->x += move;
                        ob->y += move;
                        break;

                case nodir:
                        return;
                }
                return;
        }
moveok:
        ob->distance -=move;
}

/*
=============================================================================

                                                        STUFF

=============================================================================
*/

/*
===============
=
= DropItem
=
= Tries to drop a bonus item somewhere in the tiles surrounding the
= given tilex/tiley
=
===============
*/

void DropItem (stat_t itemtype, int tilex, int tiley)
{
        int     x,y,xl,xh,yl,yh;

//
// find a free spot to put it in
//
        if (!actorat[tilex][tiley])
        {
                PlaceItemType (itemtype, tilex,tiley);
                return;
        }

        xl = tilex-1;
        xh = tilex+1;
        yl = tiley-1;
        yh = tiley+1;

        for (x=xl ; x<= xh ; x++)
                for (y=yl ; y<= yh ; y++)
                        if (!actorat[x][y])
                        {
                                PlaceItemType (itemtype, x,y);
                                return;
                        }
}



/*
===============
=
= KillActor
=
===============
*/

void KillActor (objtype *ob)
{
        int     tilex,tiley;

        tilex = ob->tilex = (word)(ob->x >> TILESHIFT);         // drop item on center
        tiley = ob->tiley = (word)(ob->y >> TILESHIFT);

        switch (ob->obclass)
        {
        case guardobj:
                GivePoints (100);
                NewState (ob,&s_grddie1);
                PlaceItemType (bo_clip2,tilex,tiley);
                break;

        case officerobj:
                GivePoints (400);
                NewState (ob,&s_ofcdie1);
                PlaceItemType (bo_clip2,tilex,tiley);
                break;

        case mutantobj:
                GivePoints (700);
                NewState (ob,&s_mutdie1);
                PlaceItemType (bo_clip2,tilex,tiley);
                break;

        case ssobj:
                GivePoints (500);
                NewState (ob,&s_ssdie1);
                if (gamestate.bestweapon < wp_machinegun)
                        PlaceItemType (bo_machinegun,tilex,tiley);
                else
                        PlaceItemType (bo_clip2,tilex,tiley);
                break;

        case dogobj:
                GivePoints (200);
                NewState (ob,&s_dogdie1);
                break;

#ifndef SPEAR
        case bossobj:
                GivePoints (5000);
                NewState (ob,&s_bossdie1);
                PlaceItemType (bo_key1,tilex,tiley);
                break;

        case gretelobj:
                GivePoints (5000);
                NewState (ob,&s_greteldie1);
                PlaceItemType (bo_key1,tilex,tiley);
                break;

        case giftobj:
                GivePoints (5000);
                gamestate.killx = player->x;
                gamestate.killy = player->y;
                NewState (ob,&s_giftdie1);
                break;

        case fatobj:
                GivePoints (5000);
                gamestate.killx = player->x;
                gamestate.killy = player->y;
                NewState (ob,&s_fatdie1);
                break;

        case schabbobj:
                GivePoints (5000);
                gamestate.killx = player->x;
                gamestate.killy = player->y;
                NewState (ob,&s_schabbdie1);
                A_DeathScream(ob);
                break;
        case fakeobj:
                GivePoints (2000);
                NewState (ob,&s_fakedie1);
                break;

        case mechahitlerobj:
                GivePoints (5000);
                NewState (ob,&s_mechadie1);
                break;
        case realhitlerobj:
                GivePoints (5000);
                gamestate.killx = player->x;
                gamestate.killy = player->y;
                NewState (ob,&s_hitlerdie1);
                A_DeathScream(ob);
                break;
#else
        case spectreobj:
                if (ob->flags&FL_BONUS)
                {
                        GivePoints (200);       // Get points once for each
                        ob->flags &= ~FL_BONUS;
                }                
                NewState (ob,&s_spectredie1);
                break;

        case angelobj:
                GivePoints (5000);
                NewState (ob,&s_angeldie1);
                break;

        case transobj:
                GivePoints (5000);
                NewState (ob,&s_transdie0);
                PlaceItemType (bo_key1,tilex,tiley);
                break;

        case uberobj:
                GivePoints (5000);
                NewState (ob,&s_uberdie0);
                PlaceItemType (bo_key1,tilex,tiley);
                break;

        case willobj:
                GivePoints (5000);
                NewState (ob,&s_willdie1);
                PlaceItemType (bo_key1,tilex,tiley);
                break;

        case deathobj:
                GivePoints (5000);
                NewState (ob,&s_deathdie1);
                PlaceItemType (bo_key1,tilex,tiley);
                break;
#endif
        }

        gamestate.killcount++;
        ob->flags &= ~FL_SHOOTABLE;
        actorat[ob->tilex][ob->tiley] = NULL;
        ob->flags |= FL_NONMARK;
}



/*
===================
=
= DamageActor
=
= Called when the player succesfully hits an enemy.
=
= Does damage points to enemy ob, either putting it into a stun frame or
= killing it.
=
===================
*/

void DamageActor (objtype *ob, unsigned damage)
{
        madenoise = true;

//
// do double damage if shooting a non attack mode actor
//
        if ( !(ob->flags & FL_ATTACKMODE) )
                damage <<= 1;

        ob->hitpoints -= (short)damage;

        if (ob->hitpoints<=0)
                KillActor (ob);
        else
        {
                if (! (ob->flags & FL_ATTACKMODE) )
                        FirstSighting (ob);             // put into combat mode

                switch (ob->obclass)            // dogs only have one hit point
                {
                case guardobj:
                        if (ob->hitpoints&1)
                                NewState (ob,&s_grdpain);
                        else
                                NewState (ob,&s_grdpain1);
                        break;

                case officerobj:
                        if (ob->hitpoints&1)
                                NewState (ob,&s_ofcpain);
                        else
                                NewState (ob,&s_ofcpain1);
                        break;

                case mutantobj:
                        if (ob->hitpoints&1)
                                NewState (ob,&s_mutpain);
                        else
                                NewState (ob,&s_mutpain1);
                        break;

                case ssobj:
                        if (ob->hitpoints&1)
                                NewState (ob,&s_sspain);
                        else
                                NewState (ob,&s_sspain1);

                        break;

                }
        }
}

/*
=============================================================================

                                                        CHECKSIGHT

=============================================================================
*/


/*
=====================
=
= CheckLine
=
= Returns true if a straight line between the player and ob is unobstructed
=
=====================
*/

boolean CheckLine (objtype *ob)
{
        int     x1,y1,xt1,yt1,x2,y2,xt2,yt2;
        int     x,y;
        int     xdist,ydist,xstep,ystep;
        int     partial,delta;
        long    ltemp;
        int     xfrac,yfrac,deltafrac;
        unsigned value,intercept;

        x1 = ob->x >> UNSIGNEDSHIFT;            // 1/256 tile precision
        y1 = ob->y >> UNSIGNEDSHIFT;
        xt1 = x1 >> 8;
        yt1 = y1 >> 8;

        x2 = plux;
        y2 = pluy;
        xt2 = player->tilex;
        yt2 = player->tiley;


        xdist = abs(xt2-xt1);

        if (xdist > 0)
        {
                if (xt2 > xt1)
                {
                        partial = 256-(x1&0xff);
                        xstep = 1;
                }
                else
                {
                        partial = x1&0xff;
                        xstep = -1;
                }

                deltafrac = abs(x2-x1);
                delta = y2-y1;
                ltemp = ((long)delta<<8)/deltafrac;
                if (ltemp > 0x7fffl)
                        ystep = 0x7fff;
                else if (ltemp < -0x7fffl)
                        ystep = -0x7fff;
                else
                        ystep = ltemp;
                yfrac = y1 + (((long)ystep*partial) >>8);

                x = xt1+xstep;
                xt2 += xstep;
                do
                {
                        y = yfrac>>8;
                        yfrac += ystep;

                        value = (unsigned)tilemap[x][y];
                        x += xstep;

                        if (!value)
                                continue;

                        if (value<128 || value>256)
                                return false;

                        //
                        // see if the door is open enough
                        //
                        value &= ~0x80;
                        intercept = yfrac-ystep/2;

                        if (intercept>doorposition[value])
                                return false;

                } while (x != xt2);
        }

        ydist = abs(yt2-yt1);

        if (ydist > 0)
        {
                if (yt2 > yt1)
                {
                        partial = 256-(y1&0xff);
                        ystep = 1;
                }
                else
                {
                        partial = y1&0xff;
                        ystep = -1;
                }

                deltafrac = abs(y2-y1);
                delta = x2-x1;
                ltemp = ((long)delta<<8)/deltafrac;
                if (ltemp > 0x7fffl)
                        xstep = 0x7fff;
                else if (ltemp < -0x7fffl)
                        xstep = -0x7fff;
                else
                        xstep = ltemp;
                xfrac = x1 + (((long)xstep*partial) >>8);

                y = yt1 + ystep;
                yt2 += ystep;
                do
                {
                        x = xfrac>>8;
                        xfrac += xstep;

                        value = (unsigned)tilemap[x][y];
                        y += ystep;

                        if (!value)
                                continue;

                        if (value<128 || value>256)
                                return false;

                        //
                        // see if the door is open enough
                        //
                        value &= ~0x80;
                        intercept = xfrac-xstep/2;

                        if (intercept>doorposition[value])
                                return false;
                } while (y != yt2);
        }

        return true;
}



/*
================
=
= CheckSight
=
= Checks a straight line between player and current object
=
= If the sight is ok, check alertness and angle to see if they notice
=
= returns true if the player has been spoted
=
================
*/

#define MINSIGHT        0x18000l

boolean CheckSight (objtype *ob)
{
        long            deltax,deltay;

//
// don't bother tracing a line if the area isn't connected to the player's
//
        if (!areabyplayer[ob->areanumber])
                return false;

//
// if the player is real close, sight is automatic
//
        deltax = player->x - ob->x;
        deltay = player->y - ob->y;

        if (deltax > -MINSIGHT && deltax < MINSIGHT
        && deltay > -MINSIGHT && deltay < MINSIGHT)
                return true;

//
// see if they are looking in the right direction
//
        switch (ob->dir)
        {
        case north:
                if (deltay > 0)
                        return false;
                break;

        case east:
                if (deltax < 0)
                        return false;
                break;

        case south:
                if (deltay < 0)
                        return false;
                break;

        case west:
                if (deltax > 0)
                        return false;
                break;
                
        // check diagonal moving guards fix

        case northwest:
                if (deltay > -deltax)
                        return false;
                break;

        case northeast:
                if (deltay > deltax)
                        return false;
                break;

        case southwest:
                if (deltax > deltay)
                        return false;
                break;

        case southeast:
                if (-deltax > deltay)
                        return false;
                break;
        }

//
// trace a line to check for blocking tiles (corners)
//
        return CheckLine (ob);

}



/*
===============
=
= FirstSighting
=
= Puts an actor into attack mode and possibly reverses the direction
= if the player is behind it
=
===============
*/

void FirstSighting (objtype *ob)
{
//
// react to the player
//
        switch (ob->obclass)
        {
        case guardobj:
                PlaySoundLocActor(HALTSND,ob);
                NewState (ob,&s_grdchase1);
                ob->speed *= 3;                 // go faster when chasing player
                break;

        case officerobj:
                PlaySoundLocActor(SPIONSND,ob);
                NewState (ob,&s_ofcchase1);
                ob->speed *= 5;                 // go faster when chasing player
                break;

        case mutantobj:
                NewState (ob,&s_mutchase1);
                ob->speed *= 3;                 // go faster when chasing player
                break;

        case ssobj:
                PlaySoundLocActor(SCHUTZADSND,ob);
                NewState (ob,&s_sschase1);
                ob->speed *= 4;                 // go faster when chasing player
                break;

        case dogobj:
                PlaySoundLocActor(DOGBARKSND,ob);
                NewState (ob,&s_dogchase1);
                ob->speed *= 2;                 // go faster when chasing player
                break;

#ifndef SPEAR
        case bossobj:
                SD_PlaySound(GUTENTAGSND);
                NewState (ob,&s_bosschase1);
                ob->speed = SPDPATROL*3;        // go faster when chasing player
                break;

        case gretelobj:
                SD_PlaySound(KEINSND);
                NewState (ob,&s_gretelchase1);
                ob->speed *= 3;                 // go faster when chasing player
                break;

        case giftobj:
                SD_PlaySound(EINESND);
                NewState (ob,&s_giftchase1);
                ob->speed *= 3;                 // go faster when chasing player
                break;

        case fatobj:
                SD_PlaySound(ERLAUBENSND);
                NewState (ob,&s_fatchase1);
                ob->speed *= 3;                 // go faster when chasing player
                break;

        case schabbobj:
                SD_PlaySound(SCHABBSHASND);
                NewState (ob,&s_schabbchase1);
                ob->speed *= 3;                 // go faster when chasing player
                break;

        case fakeobj:
                SD_PlaySound(TOT_HUNDSND);
                NewState (ob,&s_fakechase1);
                ob->speed *= 3;                 // go faster when chasing player
                break;

        case mechahitlerobj:
                SD_PlaySound(DIESND);
                NewState (ob,&s_mechachase1);
                ob->speed *= 3;                 // go faster when chasing player
                break;

        case realhitlerobj:
                SD_PlaySound(DIESND);
                NewState (ob,&s_hitlerchase1);
                ob->speed *= 5;                 // go faster when chasing player
                break;

        case ghostobj:
                NewState (ob,&s_blinkychase1);
                ob->speed *= 2;                 // go faster when chasing player
                break;
#else

        case spectreobj:
                SD_PlaySound(GHOSTSIGHTSND);
                NewState (ob,&s_spectrechase1);
                ob->speed = 800;                        // go faster when chasing player
                break;

        case angelobj:
                SD_PlaySound(ANGELSIGHTSND);
                NewState (ob,&s_angelchase1);
                ob->speed = 1536;                       // go faster when chasing player
                break;

        case transobj:
                SD_PlaySound(TRANSSIGHTSND);
                NewState (ob,&s_transchase1);
                ob->speed = 1536;                       // go faster when chasing player
                break;

        case uberobj:
                NewState (ob,&s_uberchase1);
                ob->speed = 3000;                       // go faster when chasing player
                break;

        case willobj:
                SD_PlaySound(WILHELMSIGHTSND);
                NewState (ob,&s_willchase1);
                ob->speed = 2048;                       // go faster when chasing player
                break;

        case deathobj:
                SD_PlaySound(KNIGHTSIGHTSND);
                NewState (ob,&s_deathchase1);
                ob->speed = 2048;                       // go faster when chasing player
                break;

#endif
        }

        if (ob->distance < 0)
                ob->distance = 0;       // ignore the door opening command

        ob->flags |= FL_ATTACKMODE|FL_FIRSTATTACK;
}



/*
===============
=
= SightPlayer
=
= Called by actors that ARE NOT chasing the player.  If the player
= is detected (by sight, noise, or proximity), the actor is put into
= it's combat frame and true is returned.
=
= Incorporates a random reaction delay
=
===============
*/

boolean SightPlayer (objtype *ob)
{
        if (ob->flags & FL_ATTACKMODE)
                Quit ("An actor in ATTACKMODE called SightPlayer!");

        if (ob->temp2)
        {
        //
        // count down reaction time
        //
                ob->temp2 -= (short) tics;
                if (ob->temp2 > 0)
                        return false;
                ob->temp2 = 0;                                  // time to react
        }
        else
        {
                if (!areabyplayer[ob->areanumber])
                        return false;

                if (ob->flags & FL_AMBUSH)
                {
                        if (!CheckSight (ob))
                                return false;
                        ob->flags &= ~FL_AMBUSH;
                }
                else
                {
                        if (!madenoise && !CheckSight (ob))
                                return false;
                }


                switch (ob->obclass)
                {
                case guardobj:
                        ob->temp2 = 1+US_RndT()/4;
                        break;
                case officerobj:
                        ob->temp2 = 2;
                        break;
                case mutantobj:
                        ob->temp2 = 1+US_RndT()/6;
                        break;
                case ssobj:
                        ob->temp2 = 1+US_RndT()/6;
                        break;
                case dogobj:
                        ob->temp2 = 1+US_RndT()/8;
                        break;

                case bossobj:
                case schabbobj:
                case fakeobj:
                case mechahitlerobj:
                case realhitlerobj:
                case gretelobj:
                case giftobj:
                case fatobj:
                case spectreobj:
                case angelobj:
                case transobj:
                case uberobj:
                case willobj:
                case deathobj:
                        ob->temp2 = 1;
                        break;
                }
                return false;
        }

        FirstSighting (ob);

        return true;
}


