shithub: rott

ref: 3e27f1b2d0d8f020e9d3509934736ee9055b8c31
dir: /src/rt_actor.c/

View raw version
/*
Copyright (C) 1994-1995  Apogee Software, Ltd.
Copyright (C) 2002-2015  icculus.org, GNU/Linux port
Copyright (C) 2017-2018  Steven LeVesque

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

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

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/


#include <string.h>
#include <stdlib.h>
#include "rt_def.h"
#include "rt_sound.h"
#include "rt_door.h"
#include "rt_ted.h"
#include "rt_draw.h"
#include "watcom.h"
#include "z_zone.h"
#include "w_wad.h"
#include "lumpy.h"
#include "gmove.h"
#include "states.h"
#include "rt_sqrt.h"
#include "rt_stat.h"
#include "sprites.h"
#include "rt_actor.h"
#include "rt_game.h"
#include "rt_main.h"
#include "rt_playr.h"
#include "rt_util.h"
#include "rt_rand.h"
#include "rt_menu.h"
#include "_rt_acto.h"
#include "rt_cfg.h"
#include "rt_floor.h"
#include "engine.h"
#include "develop.h"
#include "rt_view.h"
#include "isr.h"
#include "rt_com.h"
#include "rt_scale.h"
#include "modexlib.h"
#include "rt_net.h"
#include "rt_msg.h"
#include "fx_man.h"
#include "queue.h"




#define SGN(x)  (((x) > 0)?(1):(-1))



#define WILEYBLITZCHANCE  20
#define GIBSOUND       SD_GIBSPLASHSND
#define ACTORTHUDSND   SD_BODYLANDSND
#define ACTORLANDSND   SD_PLAYERLANDSND

//========================== Global Variables ===================================================

#define SHP(difficulty,ob)  (starthitpoints[difficulty][ob->obclass])

#define CAP_OSCUROS_HITPOINTS(ob)                         \
   {                                                      \
   if (ob->hitpoints > (SHP(gamestate.difficulty,ob)<<1)) \
      ob->hitpoints = (SHP(gamestate.difficulty,ob)<<1);  \
   }


boolean           ludicrousgibs=false;

short             colheight[15];

byte              deathshapeoffset[8] = {0,7,7,8,8,9,8,7};

unsigned long     MAXFUNCTION,MINFUNCTION,MAXSTATE,MINSTATE;

objtype           *PLAYER0MISSILE;
objtype           *SCREENEYE;
objtype           *FIRSTACTOR,*LASTACTOR;

objtype           *FIRSTFREE,*LASTFREE;
objtype           *lastactive,*firstactive,**objlist;
objtype           *firstareaactor[NUMAREAS+1],*lastareaactor[NUMAREAS+1];
int               objcount;

byte              RANDOMACTORTYPE[10];

#if (SHAREWARE == 0)
_2Dpoint          SNAKEPATH[512];
#endif
misc_stuff        mstruct,*MISCVARS = &mstruct;
int               angletodir[ANGLES];
objtype           *new;

void              *actorat[MAPSIZE][MAPSIZE];
exit_t            playstate;

void              T_SlideDownScreen(objtype*);

basic_actor_sounds  BAS[NUMCLASSES+3] =
{   {0,0,0,0,0},
    {0,0,0,0,0},
    {0,SD_LOWGUARD1SEESND,SD_LOWGUARDFIRESND,SD_LOWGUARDOUCHSND,SD_LOWGUARD1DIESND},
    {0,SD_HIGHGUARD1SEESND,SD_HIGHGUARDFIRESND,SD_HIGHGUARDOUCHSND,SD_HIGHGUARDDIESND},
    {0,SD_OVERP1SEESND,SD_OVERPFIRESND,SD_OVERPOUCHSND,SD_OVERPDIESND},
    {0,SD_STRIKE1SEESND,SD_STRIKEFIRESND,SD_STRIKEOUCHSND,SD_STRIKEDIESND},
    {0,SD_BLITZ1SEESND,SD_BLITZFIRESND,SD_BLITZOUCHSND,SD_BLITZDIESND},
    {0,SD_ENFORCERSEESND,SD_ENFORCERFIRESND,SD_ENFORCEROUCHSND,SD_ENFORCERDIESND},
    {0,SD_MONKSEESND,SD_MONKGRABSND,SD_MONKOUCHSND,SD_MONKDIESND},
    {0,SD_FIREMONKSEESND,SD_FIREMONKFIRESND,SD_FIREMONKOUCHSND,SD_FIREMONKDIESND},
    {0,SD_ROBOTSEESND,SD_ROBOTFIRESND,0,SD_ROBOTDIESND},

    //bosses
    {SD_DARIANSAY1,SD_DARIANSEESND,SD_DARIANFIRESND,0,SD_DARIANDIESND},
    {SD_KRISTSAY1,SD_KRISTSEESND,SD_KRISTFIRESND,0,SD_KRISTDIESND},
    {0,SD_NMESEESND,SD_NMEFIRE1SND,0,SD_NMEDIESND},
    {SD_DARKMONKSAY1,SD_DARKMONKSEESND,SD_DARKMONKFIRE1SND,0,SD_DARKMONKDIESND},
    {SD_SNAKESAY1,SD_SNAKESEESND,SD_SNAKESPITSND,0,SD_SNAKEDIESND},

    //specials
    {0,SD_EMPLACEMENTSEESND,SD_EMPLACEMENTFIRESND,0,0},
    {0,SD_ROBOTSEESND,SD_ROBOTFIRESND,0,SD_ROBOTDIESND}, //wallop
    {0,0,0,0,0}, //pillar
    {SD_FIREJETSND,0,0,0,0}, //firejet
    {SD_BLADESPINSND,0,0,0,0}, //blade
    {SD_CYLINDERMOVESND,0,0,0,0}, //crushcol
    {SD_BOULDERROLLSND,0,0,SD_BOULDERHITSND,0}, //boulder
    {SD_SPEARSTABSND,0,0,0,0}, //spear
    {0,0,0,0,0}, //gasgrate
    {SD_SPRINGBOARDSND,0,0,0,0}, //spring
    {0,0,0,0,0}, //shuriken
    {SD_FIREBALLSND,0,0,SD_FIREBALLHITSND,0}, //wallfire
    {0,0,0,0,0}, //net
    {SD_KRISTMINEBEEPSND,0,0,0,0}, //h_mine
    {0,0,0,0,0}, //grenade
    {0,0,0,0,0}, //fireball
    {0,0,0,0,0}, //dmfball
    {0,0,0,0,0}, //bigshuriken
    {0,0,0,0,0}, //missile
    {0,0,0,0,0}, //NMEsaucer
    {0,0,0,0,0}, //dm_weapon
    {0,0,0,0,0}, //dm_heatseek
    {0,0,0,0,0}, //dm_spit
    {SD_MISSILEFLYSND,0,SD_BAZOOKAFIRESND,SD_MISSILEHITSND,0},
    {SD_MISSILEFLYSND,0,SD_FIREBOMBFIRESND,SD_MISSILEHITSND,0},
    {SD_MISSILEFLYSND,0,SD_HEATSEEKFIRESND,SD_MISSILEHITSND,0},
    {SD_MISSILEFLYSND,0,SD_DRUNKFIRESND,SD_MISSILEHITSND,0},
    {SD_FLAMEWALLSND,0,SD_FLAMEWALLFIRESND,SD_FIREHITSND,0},
    {SD_MISSILEFLYSND,0,SD_SPLITFIRESND,SD_MISSILEHITSND,0},
    {SD_GRAVSND,0,SD_GRAVFIRESND,SD_GRAVHITSND,0},
    {SD_GRAVSND,0,SD_GODMODEFIRESND,SD_GRAVHITSND,0}

};


//========================== Local Variables ==================================================

extern boolean dopefish;


boolean Masterdisk;

static objtype    *SNAKEHEAD,*SNAKEEND,*PARTICLE_GENERATOR,*EXPLOSIONS;
#if (SHAREWARE == 0)
static int        OLDTILEX,OLDTILEY;
#endif


static char *debugstr[] = {

    "inerttype",
    "player",
    "lowguard",
    "highguard",
    "overpatrol",
    "strikeguard",
    "blitzguard",
    "triadenforcer",
    "deathmonk",
    "dfiremonk",
    "roboguard",
    "b_darian",
    "b_heinrich",
    "b_darkmonk",
    "b_roboboss",
    "b_darksnake",
    "patrolgun",
    "wallop",
    "pillar",
    "firejet",
    "blade",
    "crushcol",
    "boulder",
    "spear",
    "gasgrate",
    "spring",
    "shuriken",
    "wallfire",
    "net",
    "h_mine",
    "grenade",
    "fireball",
    "dmfball",
    "bigshuriken",
    "missile",
    "NMEsaucer",
    "dm_weapon",
    "dm_heatseek",
    "dm_spit",
    "p_bazooka",
    "p_firebomb",
    "p_heatseek",
    "p_drunkmissile",
    "p_firewall",
    "p_splitmissile",
    "p_kes",
    "p_godball",
    "collectorobj"
};







static int        starthitpoints[4][NUMENEMIES+2] =

{   {0,0,30,35,50,40,45,425,200,200,100,1500,2500,3000,3000,-1,200,2},
    {0,0,40,50,55,50,50,475,250,250,125,2300,3400,4500,3600,-1,250,2},
    {0,0,50,65,60,60,60,525,275,300,150,2400,3600,5000,4500,-1,300,2},
    {0,0,60,80,70,70,75,525,300,350,175,2800,3800,5900,4800,-1,350,2}
};


static statobj_t  *touchsprite = NULL;


static const byte dirdiff[8][8] = {{0,1,2,3,4,3,2,1},{1,0,1,2,3,4,3,2},
    {2,1,0,1,2,3,4,3},{3,2,1,0,1,2,3,4},
    {4,3,2,1,0,1,2,3},{3,4,3,2,1,0,1,2},
    {2,3,4,3,2,1,0,1},{1,2,3,4,3,2,1,0}
};

static const byte dirorder[8][2] = {{southeast,northeast},{east,north},
    {northeast,northwest},{north,west},
    {northwest,southwest},{west,south},
    {southwest,southeast},{south,east}
};

#if (SHAREWARE == 0)

static const byte dirdiff16[16][16] = {
    {0,1,2,3,4,5,6,7,8,7,6,5,4,3,2,1},
    {1,0,1,2,3,4,5,6,7,8,7,6,5,4,3,2},
    {2,1,0,1,2,3,4,5,6,7,8,7,6,5,4,3},
    {3,2,1,0,1,2,3,4,5,6,7,8,7,6,5,4},
    {4,3,2,1,0,1,2,3,4,5,6,7,8,7,6,5},
    {5,4,3,2,1,0,1,2,3,4,5,6,7,8,7,6},
    {6,5,4,3,2,1,0,1,2,3,4,5,6,7,8,7},
    {7,6,5,4,3,2,1,0,1,2,3,4,5,6,7,8},
    {8,7,6,5,4,3,2,1,0,1,2,3,4,5,6,7},
    {7,8,7,6,5,4,3,2,1,0,1,2,3,4,5,6},
    {6,7,8,7,6,5,4,3,2,1,0,1,2,3,4,5},
    {5,6,7,8,7,6,5,4,3,2,1,0,1,2,3,4},
    {4,5,6,7,8,7,6,5,4,3,2,1,0,1,2,3},
    {3,4,5,6,7,8,7,6,5,4,3,2,1,0,1,2},
    {2,3,4,5,6,7,8,7,6,5,4,3,2,1,0,1},
    {1,2,3,4,5,6,7,8,7,6,5,4,3,2,1,0}
};
#endif

static const byte dirorder16[16][2] = {
    {15,1},   {0,2},   {1,3},   {2,4},
    {3,5},   {4,6},   {5,7},   {6,8},
    {7,9},  {8,10},  {9,11}, {10,12},
    {11,13}, {12,14}, {13,15},  {14,0}
};

//static byte opposite16[16] = {8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7};

#if (SHAREWARE == 0)

static statetype * UPDATE_STATES[NUMSTATES][NUMENEMIES] =

{   {   &s_lowgrdstand,&s_highgrdstand,&s_opstand,&s_strikestand,
        &s_blitzstand,&s_enforcerstand,&s_dmonkstand,&s_firemonkstand,
        &s_robogrdstand,&s_darianstand,&s_heinrichstand,NULL,
        &s_darkmonkstand,NULL,&s_gunstand,&s_wallstand
    },

    {   &s_lowgrdpath1,&s_highgrdpath1,&s_oppath1,&s_strikepath1,
        &s_blitzpath1,&s_enforcerpath1,&s_dmonkpath1,&s_firemonkpath1,
        &s_robogrdpath1,NULL,NULL,NULL,
        NULL,NULL,NULL,&s_wallpath
    },

    {   &s_lowgrdcollide,&s_highgrdcollide,&s_opcollide,&s_strikecollide,
        &s_blitzcollide,&s_enforcercollide,&s_dmonkcollide,&s_firemonkcollide,
        &s_robogrdcollide,&s_dariancollide,NULL,NULL,
        NULL,NULL,NULL,&s_wallcollide
    },

    {   &s_lowgrdcollide2,&s_highgrdcollide2,&s_opcollide2,&s_strikecollide2,
        &s_blitzcollide2,&s_enforcercollide2,&s_dmonkcollide2,&s_firemonkcollide2,
        &s_robogrdcollide2,&s_dariancollide2,NULL,NULL,
        NULL,NULL,NULL,&s_wallcollide
    },

    {   &s_lowgrdchase1,&s_highgrdchase1,&s_opchase1,&s_strikechase1,
        &s_blitzchase1,&s_enforcerchase1,&s_dmonkchase1,&s_firemonkchase1,
        NULL/*se1*/,&s_darianchase1,&s_heinrichchase,&s_NMEchase,
        &s_darkmonkchase1,NULL,&s_gunstand,&s_wallpath
    },

    /*
    {&s_lowgrduse1,&s_highgrduse1,&s_opuse1,&s_strikeuse1,
     &s_blitzuse,&s_enforceruse1,NULL,NULL,
     NULL,&s_darianuse1,NULL,NULL,
     NULL,NULL,NULL,NULL},*/
    {0},

    {   &s_lowgrdshoot1,&s_highgrdshoot1,&s_opshoot1,&s_strikeshoot1,
        &s_blitzshoot1,&s_enforcershoot1,NULL,&s_firemonkcast1,
        &s_robogrdshoot1,&s_darianshoot1,&s_heinrichshoot1,NULL,
        NULL,NULL,&s_gunfire1,&s_wallshoot
    },

    {   &s_lowgrddie1,&s_highgrddie1,&s_opdie1,&s_strikedie1,
        &s_blitzdie1,&s_enforcerdie1,&s_dmonkdie1,&s_firemonkdie1,
        &s_robogrddie1,&s_dariandie1,&s_heinrichdie1,&s_NMEdie,
        &s_darkmonkdie1,NULL,&s_gundie1,NULL
    },

    {0},

    {   NULL,NULL,NULL,&s_strikewait,
        &s_blitzstand,&s_enforcerdie1,&s_dmonkdie1,&s_firemonkdie1,
        &s_robogrddie1,&s_dariandie1,&s_heinrichdie1,NULL,
        &s_darkmonkdie1,NULL,NULL,NULL
    },

    {   &s_lowgrdcrushed1,&s_highgrdcrushed1,&s_opcrushed1,&s_strikecrushed1,
        &s_blitzcrushed1,&s_enforcercrushed1,&s_dmonkcrushed1,&s_firemonkcrushed1,
        &s_robogrddie1,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL
    }

};

#else

static statetype * UPDATE_STATES[NUMSTATES][NUMENEMIES] =

{   {   &s_lowgrdstand,&s_highgrdstand,NULL,&s_strikestand,
        &s_blitzstand,&s_enforcerstand,NULL,NULL,
        &s_robogrdstand,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL
    },

    {   &s_lowgrdpath1,&s_highgrdpath1,NULL,&s_strikepath1,
        &s_blitzpath1,&s_enforcerpath1,NULL,NULL,
        &s_robogrdpath1,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL
    },

    {   &s_lowgrdcollide,&s_highgrdcollide,NULL,&s_strikecollide,
        &s_blitzcollide,&s_enforcercollide,NULL,NULL,
        NULL,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL
    },

    {   &s_lowgrdcollide2,&s_highgrdcollide2,NULL,&s_strikecollide2,
        &s_blitzcollide2,&s_enforcercollide2,NULL,NULL,
        &s_robogrdcollide2,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL
    },

    {   &s_lowgrdchase1,&s_highgrdchase1,NULL,&s_strikechase1,
        &s_blitzchase1,&s_enforcerchase1,NULL,NULL,
        NULL,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL
    },

    /*
    {&s_lowgrduse1,&s_highgrduse1,&s_opuse1,&s_strikeuse1,
     &s_blitzuse,&s_enforceruse1,NULL,NULL,
     NULL,&s_darianuse1,NULL,NULL,
     NULL,NULL,NULL,NULL},*/
    {0},

    {   &s_lowgrdshoot1,&s_highgrdshoot1,NULL,&s_strikeshoot1,
        &s_blitzshoot1,&s_enforcershoot1,NULL,NULL,
        &s_robogrdshoot1,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL
    },

    {   &s_lowgrddie1,&s_highgrddie1,NULL,&s_strikedie1,
        &s_blitzdie1,&s_enforcerdie1,NULL,NULL,
        &s_robogrddie1,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL
    },

    {0},

    {   NULL,NULL,NULL,&s_strikewait,
        &s_blitzstand,&s_enforcerdie1,NULL,NULL,
        &s_robogrddie1,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL
    },

    {   &s_lowgrdcrushed1,&s_highgrdcrushed1,NULL,&s_strikecrushed1,
        &s_blitzcrushed1,&s_enforcercrushed1,NULL,NULL,
        &s_robogrddie1,NULL,NULL,NULL,
        NULL,NULL,NULL,NULL
    }

};

#endif



#define TABLE_ACTOR(ob)  ((ob->obclass >= lowguardobj) && (ob->obclass <= wallopobj))


void T_Reset(objtype*ob);
void ApplyGravity(objtype *ob);
void BeginEnemyHurt(objtype *ob);
void T_PlayDead(objtype *ob);
void SpawnFirewall(objtype*ob,int which,int newz);
void SelectKristChaseDir(objtype*ob);
void ExplodeStatic(statobj_t*tempstat);
void AvoidPlayerMissile(objtype*ob);
int EnvironmentDamage(objtype *ob);

static int     STOPSPEED         =    0x200;
static int     PLAYERFRICTION    =    0xe000;
static int     ACTORFRICTION     =    0xf000;
static int     DIAGADJUST        =    0xb504;
static boolean MissileSound      =    true;




boolean FirstExplosionState(statetype *state)
{
    if (DoPanicMapping())
    {
        if (state == &s_altexplosion1)
            return true;
        else
            return false;
    }
    else
    {
        if ((state == &s_explosion1) ||
                (state == &s_grexplosion1) ||
                (state == &s_staticexplosion1)
           )
            return true;
        else
            return false;
    }

}





void SetGibSpeed(int speed)
{
    MISCVARS->gibspeed = speed;
}

void ResetGibSpeed(void)
{
    MISCVARS->gibspeed = NORMALGIBSPEED;
}

int ValidAreanumber (int areanumber)
{   if ((areanumber >=0) && (areanumber <= NUMAREAS))
        return 1;
    return 0;
}

int GetIndexForState (statetype * state)
{
    int i;

    if (state == NULL)
        return -1;

    for (i=0; i<MAXSTATES; i++)
    {
        if (statetable[i]==state)
            return i;
    }
    Error("Cannot find the state in 'GetIndexForState', state->shapenum = %d\n",state->shapenum);
    return -1;
}



statetype * GetStateForIndex (int index)
{
    if (index == -1)
        return NULL;

    return statetable[index];
}


statobj_t* GetStaticForIndex(int index)
{   statobj_t* temp;

    for(temp=FIRSTSTAT; temp; temp=temp->statnext)
        if (index == temp->whichstat)
            return temp;

    Error("Cannot find the static in 'GetStaticForIndex', statindex %d\n",index);
    return NULL;

}



void SaveActors(byte **buffer,int*size)
{   objtype*temp,*tact;
    saved_actor_type dummy;
    byte*tptr;
    int actorcount;


    for(actorcount=0,temp=FIRSTACTOR; temp; temp=temp->next)
        temp->whichactor = actorcount++;



    *size = sizeof(int) + sizeof(numplayers) + sizeof(misc_stuff) + objcount*sizeof(saved_actor_type);
    *buffer = (byte*)SafeMalloc(*size);
    tptr = *buffer;

    memcpy(tptr,MISCVARS,sizeof(misc_stuff));
    tptr += sizeof(misc_stuff);

    memcpy(tptr,&numplayers,sizeof(numplayers));
    tptr += sizeof(numplayers);

    memcpy(tptr,&consoleplayer,sizeof(consoleplayer));
    tptr += sizeof(consoleplayer);

    for(temp=FIRSTACTOR; temp; temp=temp->next)
    {   dummy.x = temp->x;
        dummy.y = temp->y;
        dummy.z = temp->z;
        dummy.flags = temp->flags;
        dummy.areanumber = temp->areanumber;
        //dummy.whichactor = temp->whichactor;
        dummy.hitpoints = temp->hitpoints;
        dummy.ticcount = temp->ticcount;
        dummy.obclass = (byte)(temp->obclass);
        dummy.stateindex = GetIndexForState(temp->state);
        dummy.shapeoffset = temp->shapeoffset;
        dummy.dirchoosetime = temp->dirchoosetime;
        dummy.door_to_open = temp->door_to_open;
        dummy.targetx = temp->targettilex;
        dummy.targety = temp->targettiley;
        dummy.dir = (signed char)temp->dir;
        dummy.angle = temp->angle;
        dummy.yzangle = temp->yzangle;
        dummy.speed = temp->speed;
        dummy.momentumx = temp->momentumx;
        dummy.momentumy = temp->momentumy;
        dummy.momentumz = temp->momentumz;

        dummy.temp1 = temp->temp1;
        dummy.temp2 = temp->temp2;
        dummy.temp3 = temp->temp3;
        if (temp->whatever)
        {   /*if ((temp->flags & FL_USE) && (temp!=player))
              {dummy.whateverindex = (GetIndexForState((statetype*)(temp->whatever))|SG_PSTATE);
            	if ((dummy.whateverindex < 0) && (dummy.whateverindex != -1))
            	  Error("Bad actor whatever save value of %d\n",dummy.whateverindex);
              }
            else*/
            {   tact = (objtype*)(temp->whatever);
                if (tact->which == ACTOR)
                    dummy.whateverindex = tact->whichactor;
                else
                {   statobj_t *tstat;

                    tstat = (statobj_t*)(temp->whatever);
                    dummy.whateverindex = (tstat->whichstat|SG_PSTAT);

                }
            }
        }
        else
            dummy.whateverindex = -1;


        if (temp->target)
        {   tact = (objtype*)(temp->target);
            if (tact->which == ACTOR)
            {   dummy.targetindex = tact->whichactor;
                Debug("\nsave actor %d, type %d has target %d",temp->whichactor,temp->obclass,tact->whichactor);
            }
            else if (tact->which == SPRITE)
            {   statobj_t *tstat;

                tstat = (statobj_t*)(temp->target);
                dummy.targetindex = (tstat->whichstat|SG_PSTAT);
            }
            else // It must be a push wall, and we don't save that
                dummy.targetindex=-1;
        }
        else
            dummy.targetindex = -1;


        memcpy(tptr,&(dummy.x),sizeof(saved_actor_type));
        tptr += sizeof(saved_actor_type);

    }

}



void LoadActors(byte *buffer,int size)
{
    int numactors,i,playerindex;
    saved_actor_type dummy;
    objtype *temp;
    short *targetindices,*whateverindices;

    InitActorList();

    memcpy(MISCVARS,buffer,sizeof(misc_stuff));
    buffer += sizeof(misc_stuff);

    memcpy(&numplayers,buffer,sizeof(numplayers));
    buffer += sizeof(numplayers);

    memcpy(&playerindex,buffer,sizeof(playerindex));
    buffer += sizeof(playerindex);

    size -= (sizeof(misc_stuff)+sizeof(numplayers)+sizeof(playerindex));
    numactors = size/sizeof(saved_actor_type);


    objlist = (objtype**)SafeMalloc(numactors*sizeof(objtype*));
    targetindices = (short*)SafeMalloc(numactors*sizeof(short));
    whateverindices = (short*)SafeMalloc(numactors*sizeof(short));

    for(i=0; i<numactors; i++)
    {
        targetindices[i] = 0;
        whateverindices[i] = 0;
        objlist[i] = NULL;
    }


    for(i=0; i<numactors; i++)
    {
        GetNewActor();
        objlist[i] = new;
        if (i < numplayers)
        {
            PLAYER[i]=new;
            if (i==playerindex)
                player=new;
        }

        memcpy(&(dummy.x),buffer,sizeof(saved_actor_type));

        //new->x = dummy.x;
        //new->y = dummy.y;
        SetFinePosition(new,dummy.x,dummy.y);
        SetVisiblePosition(new,dummy.x,dummy.y);
        new->z = dummy.z;
        new->flags = dummy.flags;
        new->hitpoints = dummy.hitpoints;
        new->ticcount = dummy.ticcount;
        new->shapeoffset = dummy.shapeoffset;
        new->obclass = (classtype)(dummy.obclass);


        new->state = GetStateForIndex(dummy.stateindex);
        if (new->state == &s_superparticles)
            PARTICLE_GENERATOR = new;
        else if
        (new->state->think == T_SlideDownScreen)
            SCREENEYE = new;
        new->dirchoosetime = dummy.dirchoosetime;
        new->door_to_open = dummy.door_to_open;
        new->targettilex = dummy.targetx;
        new->targettiley = dummy.targety;
        new->dir = (dirtype)(dummy.dir);
        new->angle = dummy.angle;
        new->yzangle = dummy.yzangle;
        new->speed = dummy.speed;
        new->momentumx = dummy.momentumx;
        new->momentumy = dummy.momentumy;
        new->momentumz = dummy.momentumz;
        new->temp1 = dummy.temp1;
        new->temp2 = dummy.temp2;
        new->temp3 = dummy.temp3;
        if (dummy.whateverindex == -1)
            new->whatever = NULL;

        else if (dummy.whateverindex & SG_PSTAT)
            new->whatever = GetStaticForIndex(dummy.whateverindex & ~SG_PSTAT);
        else
            whateverindices[i] = dummy.whateverindex+1;


        if (dummy.targetindex == -1)
            new->target = NULL;
        else if (dummy.targetindex & SG_PSTAT)
            new->target = GetStaticForIndex(dummy.targetindex & ~SG_PSTAT);
        else
        {
            targetindices[i] = dummy.targetindex+1;
            Debug("\nload actor %d, type %d has target %d",i,new->obclass,dummy.targetindex);
        }


        new->areanumber = dummy.areanumber;
        new->shapenum = new->state->shapenum + new->shapeoffset;
        new->which = ACTOR;
        if (new->flags & FL_ABP)
            MakeActive(new);
        if (new->obclass != inertobj)
            MakeLastInArea(new);

        if (!(new->flags & (FL_NEVERMARK|FL_NONMARK)))
            actorat[new->tilex][new->tiley] = new;

        PreCacheActor(new->obclass,-1);
        buffer += sizeof(saved_actor_type);
    }


    // find unique links between actors,
    // searching list AFTER all have been spawned

    for(i=0; i<numactors; i++)
    {   temp=objlist[i];
        if (whateverindices[i])
            temp->whatever = objlist[whateverindices[i]-1];
        if (targetindices[i])
            temp->target = objlist[targetindices[i]-1];
    }


    for(temp=FIRSTACTOR; temp; temp=temp->next)
    {   if (temp->obclass == b_darksnakeobj)
        {   if (!SNAKEHEAD)
                SNAKEHEAD = temp;
            else if (!temp->whatever)
                SNAKEEND = temp;
        }

    }

    if (SNAKEHEAD)
        for(temp=FIRSTACTOR; temp; temp=temp->next)
        {   if (temp->state == &s_megaexplosions)
                EXPLOSIONS = temp;
        }

    //SafeFree(objlist);
    SafeFree(targetindices);
    SafeFree(whateverindices);

}



int RandomSign(void)
{
    if (GameRandomNumber("random sign",0) < 128)
        return -1;
    return 1;


}


void AddToFreeList(objtype*ob)
{   if (!FIRSTFREE)
        FIRSTFREE = ob;
    else
    {   ob->prev = LASTFREE;
        LASTFREE->next = ob;
    }
    LASTFREE = ob;

}

void RemoveFromFreeList(objtype*ob)
{
    if (ob == LASTFREE)
        LASTFREE = ob->prev;
    else
        ob->next->prev = ob->prev;

    if (ob == FIRSTFREE)
        FIRSTFREE = ob->next;
    else
        ob->prev->next = ob->next;

    ob->prev = NULL;
    ob->next = NULL;

}


void MakeActive(objtype *ob)
{   if ((ob == firstactive) || (ob->prevactive) || (ob->nextactive))
    {
        SoftError("\ndouble make active try");
        //AddEndGameCommand ();
        return;
    }

    if (!firstactive)
        firstactive = ob;
    else
    {   ob->prevactive = lastactive;
        lastactive->nextactive = ob;
    }
    lastactive = ob;
}



void MakeLastInArea(objtype *ob)
{
    if (!ValidAreanumber(ob->areanumber))
        Error("\n ob type %s at %d,%d has illegal areanumber of %d",
              debugstr[ob->obclass],ob->tilex,ob->tiley,ob->areanumber);


    if ((ob == firstareaactor[ob->areanumber]) || (ob->previnarea) || (ob->nextinarea))
    {
        SoftError("\ndouble make last in area try");
        //AddEndGameCommand ();
        return;
    }
    if (!firstareaactor[ob->areanumber])
        firstareaactor[ob->areanumber]	= ob;
    else
    {   ob->previnarea = lastareaactor[ob->areanumber];
        lastareaactor[ob->areanumber]->nextinarea = ob;
    }
    lastareaactor[ob->areanumber] = ob;
}



void RemoveFromArea(objtype*ob)
{
    if (!((ob == firstareaactor[ob->areanumber]) || (ob->previnarea) || (ob->nextinarea)))
    {
        SoftError("\ndouble remove from area try");
        //AddEndGameCommand ();
        return;
    }

    if (ob == lastareaactor[ob->areanumber])     // remove from master list
        lastareaactor[ob->areanumber] = ob->previnarea;
    else
        ob->nextinarea->previnarea = ob->previnarea;

    if (ob == firstareaactor[ob->areanumber])
        firstareaactor[ob->areanumber] = ob->nextinarea;
    else
        ob->previnarea->nextinarea = ob->nextinarea;

    ob->previnarea = NULL;
    ob->nextinarea = NULL;
}


void MakeInactive(objtype*ob)
{
    if (!ACTIVE(ob))
//  if (!((ob == firstactive) || (ob->prevactive) || (ob->nextactive)))
    {
        SoftError("\n trying to remove inactive object");
        //AddEndGameCommand ();
        return;
    }

    //if (ob->flags & FL_ABP)
    {

        if (ob == lastactive)     // remove from master list
            lastactive = ob->prevactive;
        else
            ob->nextactive->prevactive = ob->prevactive;

        if (ob == firstactive)
            firstactive = ob->nextactive;
        else
            ob->prevactive->nextactive = ob->nextactive;


        ob->prevactive = NULL;
        ob->nextactive = NULL;
    }

}



void A_Steal(objtype*ob)
{
    int dx,dy,dz;

    ActorMovement(ob);

    dx = abs(ob->x - PLAYER[0]->x);
    dy = abs(ob->y - PLAYER[0]->y);
    dz = abs(ob->z - PLAYER[0]->z);

    if ((dx > TOUCHDIST) || (dy > TOUCHDIST) || (dz > (TOUCHDIST >> 10)))
    {
        NewState(ob,&s_blitzchase1);
        return;
    }

    if (ob->ticcount)
        return;
    //"Gimme That!"
    SD_PlaySoundRTP(SD_BLITZSTEALSND,ob->x,ob->y);
    if (PLAYER[0]->flags & FL_GASMASK)
    {
        PLAYER[0]->flags &= ~FL_GASMASK;
        PLAYERSTATE[0].protectiontime = 1;
        ob->temp3 = stat_gasmask;
        GM_UpdateBonus (PLAYERSTATE[0].poweruptime, true);

    }
    else if(PLAYER[0]->flags & FL_BPV)
    {
        PLAYER[0]->flags &= ~FL_BPV;
        PLAYERSTATE[0].protectiontime = 1;
        ob->temp3 = stat_bulletproof;
        GM_UpdateBonus (PLAYERSTATE[0].poweruptime, true);

    }
    else if(PLAYER[0]->flags & FL_AV)
    {
        PLAYER[0]->flags &= ~FL_AV;
        PLAYERSTATE[0].protectiontime = 1;
        ob->temp3 = stat_asbesto;
        GM_UpdateBonus (PLAYERSTATE[0].poweruptime, true);

    }
    else if (PLAYERSTATE[0].missileweapon != -1)
    {
        NewState(PLAYER[0],&s_player);
        PLAYERSTATE[0].attackframe = PLAYERSTATE[0].weaponframe = 0;
        PLAYERSTATE[0].new_weapon = PLAYERSTATE[0].bulletweapon;
        ob->temp3 = GetItemForWeapon(PLAYERSTATE[0].missileweapon);
        ob->temp2 = PLAYERSTATE[0].ammo;
        //ob->temp1 = oldpolltime;
        PLAYERSTATE[0].ammo = -1;

        if (PLAYERSTATE[0].weapon == PLAYERSTATE[0].missileweapon)
            PLAYERSTATE[0].weapondowntics = WEAPONS[PLAYERSTATE[0].weapon].screenheight/GMOVE;
        PLAYERSTATE[0].missileweapon = -1;

        if ( SHOW_BOTTOM_STATUS_BAR() )
            DrawBarAmmo (false);

    }
}


void FindAddresses(void)
{
    int i;
    unsigned long tstate,tfunct;

    MINFUNCTION = -1l;
    MAXFUNCTION = 0x00000000;
    MINSTATE = -1l;
    MAXSTATE = 0x00000000;

    for(i=0; i<MAXSTATES; i++)
    {
        tstate = (unsigned long)(statetable[i]);
        if (tstate < MINSTATE)
            MINSTATE = tstate;

        if (tstate > MAXSTATE)
            MAXSTATE = tstate;
        if (statetable[i]!=NULL)
        {
            tfunct = (unsigned long)(statetable[i]->think);
            if (tfunct < MINFUNCTION)
                MINFUNCTION = tfunct;

            if (tfunct > MAXFUNCTION)
                MAXFUNCTION = tfunct;
        }
    }
}

void CheckBounds(objtype*ob)
{
    unsigned long tstate,tfunct;

    tstate = (unsigned long)(ob->state);
    tfunct = (unsigned long)(ob->state->think);

    if ((tfunct < MINFUNCTION) || (tfunct > MAXFUNCTION) ||
            (tstate < MINSTATE) || (tstate > MAXSTATE))
    {
        if (tfunct < MINFUNCTION)
            Error("%s has thinking function less than MINFUNCTION",debugstr[ob->obclass]);

        else if (tfunct > MAXFUNCTION)
            Error("%s has thinking function greater than MAXFUNCTION",debugstr[ob->obclass]);

        if (tstate < MINSTATE)
            Error("%s has state less than MINSTATE",debugstr[ob->obclass]);

        else if (tstate > MAXSTATE)
            Error("%s has state greater than MAXSTATE",debugstr[ob->obclass]);

    }


}

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


/*
=====================
=
= DoActor
=
=====================
*/

void DoActor (objtype *ob)
{
    void (*think)(objtype *);
    int door;


//  for(i=0;i<tics;i++)
//	{

#if (BNACRASHPREVENT == 1)//
    if (ob->state == 0) {
        return;
    }
#endif
    ApplyGravity(ob);
    M_CheckDoor(ob);
    M_CheckBossSounds(ob);
    if ((ob->obclass >= b_darianobj) &&
            (ob->obclass < b_darksnakeobj) &&
            MISCVARS->REDTIME
       )
    {
        MISCVARS->REDTIME --;
        MISCVARS->redindex = (MISCVARS->REDTIME  & 15);
    }

    if (ob->obclass == playerobj)
        ControlPlayerObj(ob);

    think = ob->state->think;
    if (think)
    {
        //CheckBounds(ob);
        think (ob);

        if (!ob->state)
        {
            RemoveObj (ob);
            return;
        }
    }

    if (ob->ticcount)
        ob->ticcount --;

    else
    {
        if (!(ob->state->next))
        {
            RemoveObj (ob);
            return;
        }
        else
            NewState(ob,ob->state->next);
    }


    if (ob->flags&FL_NEVERMARK)
        return;

    if ((ob->flags&FL_NONMARK) && actorat[ob->tilex][ob->tiley])
        return;

    actorat[ob->tilex][ob->tiley] = ob;
}





void ApplyGravity(objtype *ob)
{
    int oldmomentumz;

    if (((ob->momentumz) || (ob->z != nominalheight)) &&
            (ob->obclass > playerobj) &&
            ((ob->obclass <= roboguardobj) || (ob->obclass == collectorobj) ||
             (ob->obclass == b_heinrichobj)) &&
            (ob->state->think != T_Stand)
       )
    {
        ob->z += (ob->momentumz>>16);
        ob->momentumz += GRAVITY;
        if (ob->z >= nominalheight)
        {
            ob->z = nominalheight;
            oldmomentumz = ob->momentumz;
            ob->momentumz = 0;
            if (oldmomentumz > 2*GRAVITY)
            {
                if (ob->flags & FL_DYING)
                    SD_PlaySoundRTP(ACTORTHUDSND,ob->x,ob->y);
                else
                {
                    int oldviolence = gamestate.violence;

                    SD_PlaySoundRTP(ACTORLANDSND,ob->x,ob->y);
                    gamestate.violence = vl_low;
                    BeginEnemyHurt(ob);
                    gamestate.violence = oldviolence;
                }
            }
            if (ob->flags&FL_FALLINGOBJECT)
            {
                RemoveObj(ob);
                return;
            }
        }
    }
}



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

void NewState (objtype *ob, statetype *newstate)
{
    if (DoPanicMapping() &&
            ((newstate == &s_explosion1) ||
             (newstate == &s_grexplosion1) ||
             (newstate == &s_staticexplosion1)
            )
       )
        ob->state = &s_altexplosion1;
    else {
#if (BNACRASHPREVENT == 1)//crashed here when oscuro and larves were all killed
        if (ob == 0) {
            return;
        }
#endif
        ob->state = newstate;
    }
    SetVisiblePosition(ob,ob->x,ob->y);
#if (BNACRASHPREVENT == 1)
    if (ob->state == 0) {
        return;
    }
#endif

    ob->ticcount = (ob->state->tictime>>1);
    ob->shapenum = ob->state->shapenum + ob->shapeoffset;

}


/*
=========================
=
= InitActorList
=
= Call to clear out the actor object lists returning them all to the free
= list.  Allocates a special spot for the player.
=
=========================
*/



void InitActorList (void)
{
    //====== NETWORK STUFF =======================================
    memset(&DEADPLAYER[0],0,sizeof(DEADPLAYER));
    NUMDEAD = 0;



    //======= NULLIFY GLOBAL POINTERS ============================

    LASTACTOR=FIRSTACTOR=NULL;
    FIRSTFREE = LASTFREE = NULL;
    firstactive = lastactive = NULL;
    memset(firstareaactor,0,sizeof(firstareaactor));
    memset(lastareaactor,0,sizeof(lastareaactor));
    NUMSPAWNLOCATIONS = 0;


    PARTICLE_GENERATOR = NULL;
    EXPLOSIONS = NULL;
    SNAKEEND=SNAKEHEAD=NULL;
    SCREENEYE = NULL;
    PLAYER0MISSILE = NULL;

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

    objcount = 0;
    memset(MISCVARS,0,sizeof(misc_stuff));
    MISCVARS->gibgravity = -1;
    MISCVARS->gibspeed = NORMALGIBSPEED;

    memset(&RANDOMACTORTYPE[0],0,sizeof(RANDOMACTORTYPE));
    FindAddresses();
    MissileSound = true;
    Masterdisk = false;

}

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

/*
=========================
=
= GetNewActor
=
= Sets the global variable new to point to a free spot in objlist.
= The free spot is inserted at the end of the liked list
=
= When the object list is full, the caller can either have it bomb out ot
= return a dummy object pointer that will never get used
=
=========================
*/

void GetNewActor (void)
{
    objtype *temp;

    if (!FIRSTFREE)
    {
        temp = (objtype*)Z_LevelMalloc(sizeof(objtype),PU_LEVELSTRUCT,NULL);
        //SoftError("\nMalloc-ing actor");
        //if (insetupgame)
        //  SoftError("in setup");
    }

    else
    {
        temp = LASTFREE;
        //SoftError("\nfree actor available");
        RemoveFromFreeList(LASTFREE);
    }

    if (temp)
    {
        new = temp;
        memset(new,0,sizeof(*new));

        if (FIRSTACTOR)
        {
            new->prev = LASTACTOR;
            LASTACTOR->next = new;
        }
        else
            FIRSTACTOR = new;
        LASTACTOR = new;

        new->door_to_open = -1;
        new->soundhandle = -1;
        objcount ++;
    }
    else
        Error("Z_LevelMalloc failed in GetNewActor");
}




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




/*
=========================
=
= RemoveObj
=
= Add the given object back into the free list, and unlink it from it's
= neighbors
=
=========================
*/


void RemoveObj (objtype *gone)
{
    if (gone == PLAYER[0])
        Error ("RemoveObj: Tried to remove the player!");

    gone->state=NULL;

    MakeInactive(gone);

    if (gone->obclass!=inertobj) {
        if (ValidAreanumber(gone->areanumber))
            RemoveFromArea(gone);
        else
            Error("tried to remove an instance of %s with invalid areanumber %d",debugstr[gone->obclass],gone->areanumber);
    }

    if (gone == LASTACTOR)
        LASTACTOR = gone->prev;
    else
        gone->next->prev = gone->prev;

    if (gone == FIRSTACTOR)
        FIRSTACTOR = gone->next;
    else
        gone->prev->next = gone->next;

    if (gone == EXPLOSIONS)
        EXPLOSIONS = NULL;
    gone->next = NULL;
    gone->prev = NULL;
//	SoftError("\nremoving instance of %s",debugstr[gone->obclass]);
    if (actorat[gone->tilex][gone->tiley] == (void*)gone)
        actorat[gone->tilex][gone->tiley] = NULL;

    gone->flags |= FL_NEVERMARK;

    if (gone->flags & FL_TARGET)
        UnTargetActor(gone);

    //Add_To_Delete_Array(gone);
    //Z_Free(gone);
    AddToFreeList(gone);

    objcount--;
}


//============== World Physics Model Functions =========================


void ParseMomentum(objtype *ob,int angle)
{
    ob->momentumx += FixedMul(ob->speed,costable[angle]);
    ob->momentumy -= FixedMul(ob->speed,sintable[angle]);
}

void Set_3D_Momenta(objtype *ob, int speed, int theta, int phi)
{
    int _2Ddiag;

    ob->momentumz = -FixedMul(speed,sintable[phi]);
    _2Ddiag = FixedMul(speed,costable[phi]);
    ob->momentumx = FixedMul(_2Ddiag,costable[theta]);
    ob->momentumy = -FixedMul(_2Ddiag,sintable[theta]);


}


int AngleBetween(objtype *source,objtype*target)
{
    int dx,dy;

    dx = target->x - source->x;
    dy = source->y - target->y;
    return (atan2_appx(dx,dy));
}


void GetMomenta(objtype *target, objtype *source, int *newmomx,
                int *newmomy, int *newmomz, int magnitude
               )
{
    int angle,dx,dy,dz,yzangle,xydist,_2Ddiag;

    dx = target->x - source->x;
    dy = source->y - target->y;
    dz = source->z - target->z;
    xydist = FindDistance(dx,dy);
    angle = atan2_appx(dx,dy);
    yzangle = atan2_appx(xydist,(dz<<10));
    _2Ddiag = FixedMul(magnitude,costable[yzangle]);

    *newmomz = -FixedMul(magnitude,sintable[yzangle]);
    *newmomx = FixedMul(_2Ddiag,costable[angle]);
    *newmomy = -FixedMul(_2Ddiag,sintable[angle]);
}

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





void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state, classtype which)
{
    int newarea;

    GetNewActor ();
    new->obclass = which;
    SetTilePosition(new,tilex,tiley);
    SetVisiblePosition(new,new->x,new->y);
    new->dir = nodir;
    new->which = ACTOR;
    if (FirstExplosionState(state))
        new->flags |= (FL_NEVERMARK|FL_NOFRICTION);

    if ((which != inertobj) && (which != diskobj))
        actorat[tilex][tiley] = new;

    newarea = AREANUMBER(tilex,tiley);
    if ((which <= springobj) && (which != inertobj))
    {
        if (ValidAreanumber(newarea))
            new->areanumber = newarea;
        else
            Error("illegal initial areanumber of %d for actor type %s"
                  "trying to spawn at %d, %d",newarea,debugstr[which],tilex,tiley);
    }
    else
        new->areanumber = newarea;

    if ((which != inertobj) && (!Masterdisk))
        MakeLastInArea(new);
    NewState(new,state);
    new->z = nominalheight;
    if (which==springobj)
        new->z+=2;

}


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

void ConsiderAlternateActor(objtype *ob,classtype which)
{
    if (((which >= lowguardobj) && (which <= blitzguardobj)) ||
            (which == dfiremonkobj))
    {   if (GameRandomNumber("SpawnStand",which) < 128)
        {   switch(which)
            {
            case lowguardobj:
                ob->shapeoffset =  W_GetNumForName("MARSHOO1") -
                                   W_GetNumForName("LWGSHOO1");
                break;
            case highguardobj:
                ob->shapeoffset =  W_GetNumForName("HIGSHOO1") -
                                   W_GetNumForName("HG2SHOO1");
                break;
            case overpatrolobj:
                ob->shapeoffset =  W_GetNumForName("PATSHOO1") -
                                   W_GetNumForName("OBPSHOO1");
                break;
            case strikeguardobj:
                ob->shapeoffset =  W_GetNumForName("XYGSHOO1") -
                                   W_GetNumForName("ANGSHOO1");
                break;
            /*case blitzguardobj:
            altstartlabel = "WIGSHOO1";
            new->shapeoffset = 80;
            break;*/
            case dfiremonkobj:
                ob->shapeoffset =  W_GetNumForName("MRKKSH1") -
                                   W_GetNumForName("ALLKSH1");
                break;
            default:
                ;
            }
        }

    }

    //if (new->shapeoffset)
    //  {if (W_CheckNumForName(altstartlabel) == -1)
    //     new->shapeoffset = 0;
    //  }

}





/*
===================
=
= StandardEnemyInit
=
===================
*/

void StandardEnemyInit(objtype *ob,int dir)
{
    int zoffset;

    if ((ob->obclass == deathmonkobj) || (ob->obclass == dfiremonkobj))
        ob->temp2 = DRAINTIME;

    else if ((ob->obclass == highguardobj) || (ob->obclass == triadenforcerobj))
        ob->flags |= FL_HASAUTO;

    ob->hitpoints = starthitpoints[gamestate.difficulty][ob->obclass];
    ob->dir = dir*2;
    ob->flags |= (FL_SHOOTABLE|FL_BLOCK);
    ob->speed = ENEMYRUNSPEED;
    ob->dirchoosetime = 0;
    ob->door_to_open = -1;

    zoffset = MAPSPOT(ob->tilex,ob->tiley,2);
    if ((zoffset&0xff00)==0xb000)
        Set_NewZ_to_MapValue(&(ob->z),zoffset,"standard enemy",ob->tilex,ob->tiley);
    else
        ob->z = PlatformHeight(ob->tilex,ob->tiley);

}


//LT added
//if under ext actor options BLITZ RANDOM WEP is enabled, this will decide what missile weapon a blitzguard will get
void OutfitBlitzguardWith(objtype *ob)
{
    int number = GameRandomNumber("outfitting blitzguard",0);
    
    srand((unsigned) number);
    
    number = rand() % 400;

    if (number < 100)
    {
        ob->temp3 = stat_bazooka;
        ob->temp2 = 3;
    }
    else if (number > 100 && number <= 150)
    {
        ob->temp3 = stat_heatseeker;
        ob->temp2 = 3;
    }
    else if (number > 150 && number <= 200)
    {
        ob->temp3 = stat_drunkmissile;
        ob->temp2 = 3;
    }
    else if (number > 200 && number <= 250)
    {
        ob->temp3 = stat_firewall;
        ob->temp2 = 3;
    }
    else if (number > 250 && number <= 300)
    {
        ob->temp3 = stat_firebomb;
        ob->temp2 = 3;
    }
#if (SHAREWARE == 0)
    else if (number > 300 && number <= 350)
    {
        //excalibat
        ob->temp3 = stat_bat;
        ob->temp2 = 3;
    }
    else if (number > 350)
    {
        //dark staff
        ob->temp3 = stat_kes;
        ob->temp2 = 3;
    }

#endif
}

extern boolean allowBlitzMoreMissileWeps;

//This decides if a Blitzguard (green dude) gets a rocket launcher
void ConsiderOutfittingBlitzguard(objtype *ob)
{
    //WILEYBLITZCHANCE is defined to be 20
    if ((GameRandomNumber("wiley blitzguard",0) < WILEYBLITZCHANCE) &&
        (gamestate.difficulty >= gd_medium)
       )
    {
        if (allowBlitzMoreMissileWeps)
        {
            OutfitBlitzguardWith(ob);
        }
        else 
        {
            ob->temp3 = stat_bazooka;
            ob->temp2 = 3;
        }
    }
}

void BlitzBatAttack(objtype*ob, objtype*target)
{   
    //objtype *grenadetarget;
    int dx,dy,dz,angle,momx,momy,op;

    if (target->flags & FL_DYING)
        return;
    dx = abs(target->x - ob->x);
    dy = abs(target->y - ob->y);
    dz = abs(target->z - ob->z);
    if ((dx > 0x10000) || (dy > 0x10000) || (dz > 20))
        return;
    
    SD_PlaySoundRTP(SD_EXCALISWINGSND,ob->x,ob->y);
    //magangle = abs(ob->angle - AngleBetween(ob,target));
    //if (magangle > VANG180)
        //magangle = ANGLES - magangle;

    //if (magangle > ANGLES/8)
        //return;


    angle= ob->angle+ANGLES/16;
    Fix(angle);

    
    //if (temp->obclass != grenadeobj)
    momx = FixedMul(0x3000l,costable[angle]);
    momy = -FixedMul(0x3000l,sintable[angle]);
    if (levelheight > 2)
    {   op = FixedMul(GRAVITY,(maxheight-100)<<16) << 1;
        target->momentumz = -FixedSqrtHP(op);
    }
    target->flags |= FL_NOFRICTION;
    SD_PlaySoundRTP(SD_EXCALIHITSND,ob->x,ob->y);
    if ((gamestate.violence == vl_excessive) && (GameRandomNumber("Bat Gibs",0) < 150))
    {   target->flags |= FL_HBM;
        DamageThing(target,50);
    }
    else
        DamageThing(target,10);
    if ((target->flags & FL_HBM) && (target->hitpoints > 0))
        target->flags &= ~FL_HBM;
    Collision(target,ob,momx,momy);
        
}



/*
===============
=
= SpawnStand
=
===============
*/


void SpawnStand (classtype which, int tilex, int tiley, int dir, int ambush)
{   statetype *temp;

#if (SHAREWARE == 1)
    switch(which)
    {
    case overpatrolobj:
    case wallopobj:
    case deathmonkobj:
    case dfiremonkobj:
    case b_darianobj:
    case b_heinrichobj:
    case b_darkmonkobj:
        Error("\n%s actor at %d,%d not allowed in shareware !",debugstr[which],tilex,tiley);
        break;
    default:
        ;
    }



#endif

    if ((which == lowguardobj) && (GameRandomNumber("SpawnStand",which) < 128))
        which = blitzguardobj;


    if ((temp = UPDATE_STATES[STAND][which-lowguardobj]) != NULL)
    {
        SpawnNewObj(tilex,tiley,temp,which);
        if (!loadedgame)
            gamestate.killtotal++;


        if (ambush)
            new->flags |= FL_AMBUSH;

        StandardEnemyInit(new,dir);

        if (which == b_darkmonkobj)
        {
            new->flags |= (FL_NOFRICTION);//|FL_INVULNERABLE);
            new->speed = ENEMYRUNSPEED*2;
        }

        if (which == blitzguardobj)
            ConsiderOutfittingBlitzguard(new);


        if ((new->obclass >= lowguardobj) && (new->obclass <= dfiremonkobj))
            RANDOMACTORTYPE[new->obclass]++;

        if (MAPSPOT(tilex,tiley,2) == 0xdead)
        {
            new->flags |= FL_KEYACTOR;
            MISCVARS->KEYACTORSLEFT++;
        }

        PreCacheActor(which,0);
    }
//else
    //Error("NULL initialization error");
}





/*
===============
=
= SpawnPatrol
=
===============
*/

void SpawnPatrol (classtype which, int tilex, int tiley, int dir)
{   statetype *temp;
    int path=PATH;

#if (SHAREWARE==1)
    switch(which)
    {
    case overpatrolobj:
    case wallopobj:
    case deathmonkobj:
    case dfiremonkobj:
    case b_darianobj:
    case b_heinrichobj:
    case b_darkmonkobj:
        Error("\n%s actor at %d,%d not allowed in shareware !",debugstr[which],tilex,tiley);
        break;
    default:
        ;
    }

#endif

    if ((which == lowguardobj) && (GameRandomNumber("SpawnStand",which) < 128))
        which = blitzguardobj;




    if ((temp= UPDATE_STATES[path][(int)(which-lowguardobj)]) != NULL)
    {
        SpawnNewObj(tilex,tiley,temp,which);

        if (!loadedgame)
            gamestate.killtotal++;

        StandardEnemyInit(new,dir);

        if ((which == wallopobj) || (which == roboguardobj))
        {   new->flags |= FL_NOFRICTION;
            //new->flags &= ~FL_SHOOTABLE;
            new->dir <<= 1;
            ParseMomentum(new,dirangle16[new->dir]);
        }
        else
            ParseMomentum(new,dirangle8[new->dir]);


        if (which == blitzguardobj)
            ConsiderOutfittingBlitzguard(new);


        if (MAPSPOT(tilex,tiley,2) == 0xdead)
        {   new->flags |= FL_KEYACTOR;
            MISCVARS->KEYACTORSLEFT++;
        }

        PreCacheActor(which,0);
    }

}




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



void SpawnDisk(int tilex, int tiley, int type, boolean master)
{   int zoffset;



    if (master == true)
    {
        Masterdisk = true;
        SpawnNewObj(tilex,tiley,&s_diskmaster,diskobj);
        Masterdisk = false;
        new->flags |= FL_MASTER;
        new->momentumz = -(DISKMOMZ << 16);
        new->flags |= FL_SYNCED;
        new->flags |= FL_NEVERMARK;
        new->temp1 = 1;
        //RemoveFromArea(new);

    }

    else
    {
        if (!type)
        {
            SpawnNewObj(tilex,tiley,&s_elevdisk,diskobj);
            new->momentumz = -(DISKMOMZ << 16);
            //new->flags |= FL_SYNCED;
            zoffset = MAPSPOT(tilex,tiley,2);
            if ((zoffset&0xff00)==0xb000)
                Set_NewZ_to_MapValue((fixed*)(&(new->temp2)),zoffset,"elev disk",tilex,tiley);
            else
                new->temp2 = 32;
            new->temp1 = 1;
        }
        else
        {
            SpawnNewObj(tilex,tiley,&s_pathdisk,diskobj);
            zoffset = MAPSPOT(tilex,tiley,2);
            if ((zoffset&0xff00)==0xb000)
                Set_NewZ_to_MapValue((fixed*)(&(new->z)),zoffset,"path disk",tilex,tiley);

            new->dir = (type-1) << 1;
            new->speed = 0x1000;

            //ParseMomentum(new,dirangle8[new->dir]);
        }
        actorat[tilex][tiley] = NULL;
        new->flags |= FL_BLOCK;
        new->flags |= (FL_NOFRICTION|FL_ACTIVE|FL_NEVERMARK);
    }
}




objtype* DiskAt(int tilex,int tiley)
{   int area;
    objtype *temp;
    statobj_t *tstat;

    area = AREANUMBER(tilex,tiley);
    for(temp = firstareaactor[area]; temp; temp = temp->nextinarea)
    {   if ((temp->tilex != tilex) || (temp->tiley != tiley) ||
                (temp->obclass != diskobj))
            continue;
        return temp;

    }

    for(tstat = firstactivestat; tstat; tstat = tstat->nextactive)
    {
        if ((tstat->tilex != tilex) || (tstat->tiley != tiley) ||
                (tstat->itemnumber != stat_disk))
            continue;
        return (objtype*)tstat;
    }


    return NULL;

}


void SetElevatorDiskVariables(objtype *ob,int newz, int newmomentumz,
                              int newtemp1,int newtemp3,int newdirchoose)
{
    ob->z = newz;
    ob->momentumz = newmomentumz;
    ob->temp1 = newtemp1;
    ob->temp3 = newtemp3;
    ob->dirchoosetime = newdirchoose;
}


void T_ElevDisk(objtype*ob)
{
    objtype *temp = (objtype*)(actorat[ob->tilex][ob->tiley]);
    objtype *master;

    if (ob->flags & FL_MASTER)
        goto masterlabel;

    master = (objtype*)(ob->target);
    if (!master)
        Error("disk without master !");


    //SoftError("\n ob->z:%d %s, master z:%d",ob->z,
    //        (ob->flags & FL_SYNCED)?("SYNCED"):("UNSYNCED"),master->z);


    if (M_ISACTOR(temp) && (temp != ob) && (!(temp->flags & FL_DYING)))
    {
        int dz = abs(ob->z - temp->z),
            dx = abs(ob->x - temp->x),
            dy = abs(ob->y - temp->y);

        if ((dx < 0x7000) && (dy < 0x7000) && (dz < 68) && (temp->z > ob->z))
        {
            ob->flags &= ~FL_SYNCED;
            return;
        }
    }

    if (master && (!(ob->flags & FL_SYNCED)))
    {
        int dz;

        dz = abs(master->z - ob->z);
        if ((dz > 0) && (dz < 8))
        {
            SetElevatorDiskVariables(ob,master->z,master->momentumz,master->temp1,
                                     master->temp3,master->dirchoosetime);
            ob->flags |= FL_SYNCED;
            //return;
        }
        return;
    }


masterlabel:

    if (ob->dirchoosetime)
    {
        ob->dirchoosetime --;
        return;
    }


    if (ob->temp1) // moving
    {
        ob->z += (ob->momentumz >> 16);
        if (ob->momentumz > 0) // down
        {
            if (ob->z >= nominalheight + 40 + DISKMOMZ)
                SetElevatorDiskVariables(ob,ob->z - (ob->momentumz>>16),0,0,0,35);
        }
        else
        {
            if (ob->z < ob->temp2) // temp2 has max height
                SetElevatorDiskVariables(ob,ob->z - (ob->momentumz>>16),0,0,1,35);
        }
    }
    else
    {
        if (ob->temp3)
            ob->momentumz = (DISKMOMZ << 16);
        else
            ob->momentumz = -(DISKMOMZ << 16);
        ob->temp1 = 1;
    }
}




void SpawnInertActor(int newx,int newy, int newz)
{
    GetNewActor ();
    MakeActive(new);

    new->obclass = inertobj;
    new->which = ACTOR;
    SetFinePosition(new,newx,newy);
    SetVisiblePosition(new,new->x,new->y);
    new->z = newz;
    new->dir = 0;
    new->speed = 0;
    new->flags = (FL_NEVERMARK|FL_ABP);

}




#if (SHAREWARE == 0)
void SpawnGroundExplosion(int x, int y, int z)
{
    SpawnInertActor(x,y,z);
    NewState(new,&s_grexplosion1);
    new->temp2 = GameRandomNumber("SpawnGroundExplosion",0)>>2;

}
#endif

void SpawnSlowParticles(int which, int numgibs, int x,int y,int z)
{   objtype *prevlast,*temp;
    int tilex,tiley;

    tilex = x>>16;
    tiley = y>>16;

    SpawnNewObj(tilex,tiley,&s_gibs1,inertobj);
    SetFinePosition(new,x,y);
    SetVisiblePosition(new,x,y);
    prevlast = new;
    prevlast->flags |= FL_ABP;
    MakeActive(prevlast);
    SpawnParticles(new,which,numgibs);

    for(temp = prevlast->next; temp; temp=temp->next)
    {   temp->z = z;
        temp->momentumx >>= 1;
        temp->momentumy >>= 1;
        temp->momentumz >>= 1;
    }
    RemoveObj(prevlast);

}


void ResolveDoorSpace(int tilex,int tiley)
{
    statobj_t* tstat,*temp;

    for(tstat = firstactivestat; tstat;)
    {
        temp = tstat->nextactive;

        if (tstat->flags & FL_DEADBODY)
        {
            if ((tstat->tilex == tilex) && (tstat->tiley == tiley))
            {
                if ((tstat->flags & FL_DEADBODY) && (tstat->linked_to != -1))
                    DEADPLAYER[tstat->linked_to] = NULL;
                RemoveStatic(tstat);
                if (tstat->flags & FL_DEADBODY)
                    SpawnSlowParticles(GUTS,8,tstat->x,tstat->y,tstat->z);
                else
                    SpawnSlowParticles(gt_sparks,8,tstat->x,tstat->y,tstat->z);
                SD_PlaySoundRTP(SD_ACTORSQUISHSND,tstat->x,tstat->y);
            }
        }
        tstat = temp;
    }
}


void SpawnSpear(int tilex,int tiley,int up)
{
    int count,i;
    statetype *tstate;


    if (BATTLEMODE && (!gamestate.BattleOptions.SpawnDangers))
        return;

    if (!up)
    {
#if (SHAREWARE == 1)
        Error("\ndownspear at %d,%d in shareware!",tilex,tiley);
#else
        SpawnNewObj(tilex,tiley,&s_speardown1,spearobj);
        new->z = 0;
#endif
    }
    else

        SpawnNewObj(tilex,tiley,&s_spearup1,spearobj);

    count = (int)(GameRandomNumber("Spawn Spear",0) % 16);
    for(i=0,tstate = new->state; i<count; i++,tstate=tstate->next);
    NewState(new,tstate);

    PreCacheActor(spearobj,up);
    new->flags |= (FL_ABP);//|FL_INVULNERABLE);
    MakeActive(new);
}



void SpawnSpring(int tilex,int tiley)
{
    int iconvalue;

    iconvalue = MAPSPOT(tilex,tiley,2);
    if (iconvalue == 3)
    {
        SpawnNewObj(tilex,tiley,&s_autospring1,springobj);
        new->ticcount = (GameRandomNumber("Spawn Spring",0) % new->ticcount)+1;
        new->temp1 = iconvalue;
    }
    else
    {
        SpawnNewObj(tilex,tiley,&s_spring1,springobj);
        if (iconvalue == 2)
            new->temp1 = iconvalue;
    }

    PreCacheActor(springobj,0);
    new->flags &= ~(FL_SHOOTABLE|FL_BLOCK);
}




void T_Spring(objtype*ob)
{
    objtype *temp;
    int op,dx,dy,dz;


    if ((ob->state->condition & SF_DOWN) && (ob->temp1))
    {
        if (ob->ticcount)
            return;
        ob->shapenum++;
        TurnActorIntoSprite(ob);
        return;
    }

    for(temp=firstareaactor[ob->areanumber]; temp; temp=temp->nextinarea)
    {
        if (temp == ob)
            continue;

        if (temp->obclass >= roboguardobj)
            continue;

        dx = abs(ob->x-temp->x);
        dy = abs(ob->y-temp->y);
        dz = abs(ob->z-temp->z);
        if ((dx > ACTORSIZE+0x2800) || (dy > ACTORSIZE+0x2800) || (dz > 40))
            continue;
        if (!temp->momentumz)
        {
            op = FixedMul(GRAVITY,(temp->z-5)<<16) << 1;
            temp->momentumz = -FixedSqrtHP(op);
            SD_PlaySoundRTP(SD_SPRINGBOARDSND,ob->x,ob->y);
        }
    }
}



void T_Count(objtype*ob)
{
    int index;
    touchplatetype *temp;
    objtype* tempactor;

    if (ob->dirchoosetime)
    {
        ob->dirchoosetime --;
        if (ob->dirchoosetime>980)
            MISCVARS->gasindex=((1050-ob->dirchoosetime)<<4)/70;
        else if (ob->dirchoosetime<35)
            MISCVARS->gasindex=(ob->dirchoosetime<<4)/35;
        if (ob->temp3)
        {
            ob->temp3 --;
            if (ob->temp3 & 1)
                SD_PlaySoundRTP(SD_GASHISSSND,ob->x,ob->y);
        }
        else
        {
            ob->temp3 = 105;
            for(tempactor=firstareaactor[ob->areanumber]; tempactor; tempactor=tempactor->nextinarea)
            {
                if (tempactor == ob)
                    continue;
                if (!(tempactor->flags & FL_SHOOTABLE))
                    continue;
                if (tempactor->obclass != playerobj)
                {
                    if ((tempactor->obclass >= lowguardobj) &&
                            (tempactor->obclass <= dfiremonkobj))
                    {
                        int oldviolence = gamestate.violence;

                        gamestate.violence = vl_low;
                        DamageThing(tempactor,EnvironmentDamage(ob));
                        Collision(tempactor,ob,-(tempactor->momentumx),-(tempactor->momentumy));
                        gamestate.violence = oldviolence;

                    }
                }
                else if (!(tempactor->flags & FL_GASMASK))
                {
                    DamageThing(tempactor,EnvironmentDamage(ob));
                    Collision(tempactor,ob,0,0);
                    M_CheckPlayerKilled(tempactor);
                }
            }
        }
    }

    else
    {
        int i;
        playertype *pstate;

        for(i=0; i<numplayers; i++)
        {
            M_LINKSTATE(PLAYER[i],pstate);
            PLAYER[i]->flags &= ~FL_GASMASK;
            pstate->protectiontime = 1;
        }

        NewState(ob,&s_gas1);
        SD_PlaySoundRTP(SD_GASENDSND,ob->x,ob->y);
        ob->flags &= ~FL_ACTIVE;
        MISCVARS->gasindex=0;
        MU_StartSong(song_level);
        MU_RestoreSongPosition();
        MISCVARS->GASON = 0;

        index = touchindices[ob->temp1][ob->temp2]-1;
        TRIGGER[index] = 0;
        for(temp = touchplate[index]; temp; temp = temp->nextaction)
            if (temp->action == EnableObject)
            {
                tempactor = (objtype*)(temp->whichobj);
                tempactor->flags &= ~FL_ACTIVE;
            }
    }
}




void SpawnBlade(int tilex, int tiley,int dir,int upordown,int moving)
{   int count,i;
    statetype *nstate;

#if (SHAREWARE == 1)
    if (!upordown)
        Error("\ndown spinblade at %d,%d not allowed in shareware !",tilex,tiley);
    if (moving)
        Error("\nupdown spinblade at %d,%d not allowed in shareware !",tilex,tiley);

#endif


    if (BATTLEMODE && (!gamestate.BattleOptions.SpawnDangers))
        return;

    if (moving)

    {
#if (SHAREWARE == 0)

        if (upordown)
            SpawnNewObj(tilex,tiley,&s_spinupblade1,bladeobj);
        else
        {   SpawnNewObj(tilex,tiley,&s_spindownblade1,bladeobj);
            new->z = 0;
        }
#endif
    }
    else
    {   if (upordown)
            SpawnNewObj(tilex,tiley,&s_upblade1,bladeobj);

#if (SHAREWARE == 0)

        else
        {   SpawnNewObj(tilex,tiley,&s_downblade1,bladeobj);
            new->z = 0;
        }
#endif
    }


    count = (int)(GameRandomNumber("SpawnBlade",0) % 16);
    for(nstate=new->state,i=0; i<count; nstate = nstate->next,i++);
    NewState(new,nstate);

    new->flags |= (FL_BLOCK);
    new->flags &= ~FL_SHOOTABLE;
    new->dir = dir;
    if (dir != nodir)
    {   new->flags |= FL_NOFRICTION;
        new->speed = ENEMYRUNSPEED;

    }
    if (!MAPSPOT(tilex,tiley,2))
    {   new->flags |= FL_ACTIVE;
        ParseMomentum(new,dirangle8[new->dir]);
    }
    PreCacheActor(bladeobj,(moving<<1)+upordown);
}


void SpawnCrushingColumn(int tilex, int tiley, int upordown)
{   int i,count;
    statetype * nstate;


#if (SHAREWARE == 1)
    if (!upordown)
        Error("\ncrush-up column at %d,%d not allowed in shareware!",tilex,tiley);
#endif


    if (BATTLEMODE && (!gamestate.BattleOptions.SpawnDangers))
        return;
#if (SHAREWARE == 0)
    if (!upordown)
        SpawnNewObj(tilex,tiley,&s_columnupup1,crushcolobj);
    else
#endif
    {   SpawnNewObj(tilex,tiley,&s_columndowndown1,crushcolobj);
        new->z = 0;
    }

    count = (int)(GameRandomNumber("SpawnCrushingColumn",0) % 8);
    for(nstate=new->state,i=0; i<count; nstate = nstate->next,i++)
    {   if ((!upordown) && (nstate->condition & SF_UP))
            new->temp1 += (((nstate->tictime>>1) + 1)<<2);

    }
    NewState(new,nstate);
    new->flags |= (FL_BLOCK);

    new->flags &= ~FL_SHOOTABLE;
    PreCacheActor(crushcolobj,upordown);
}



void SpawnFirejet(int tilex, int tiley, int dir, int upordown)
{
    int statecount,i;
    statetype *tstate;


    statecount = (int)(GameRandomNumber("SpawnFirejet",0) % 22);

    if (upordown)
    {
        for(i=0,tstate=&s_firejetup1; i<statecount; i++,tstate=tstate->next);
        SpawnNewObj(tilex,tiley,tstate,firejetobj);
    }
    else
    {
#if (SHAREWARE == 1)
        Error("\ndown firejet at %d,%d not allowed in shareware",tilex,tiley);
#else
        for(i=0,tstate=&s_firejetdown1; i<statecount; i++,tstate=tstate->next);
        SpawnNewObj(tilex,tiley,tstate,firejetobj);
        new->z = 0;
#endif
    }

    PreCacheActor(firejetobj,upordown);

    new->flags &= ~FL_SHOOTABLE;

    if (dir != nodir)
    {
        new->dir = dir*2;
        new->flags |= FL_NOFRICTION;
        new->speed = ENEMYRUNSPEED;
        ParseMomentum(new,dirangle8[new->dir]);
    }
    else
        new->dir = dir;
}


void SpawnFirebomb(objtype*ob,int damage,int which)
{
    int i,low,high,doorat;
    wall_t *tempwall;
    doorobj_t*tempdoor;

    if (which == 0)
    {
        low = (ob->dir>>1);
        high = low;
    }
    else
    {
        low = 0;
        high = which-1;

        if ((FindDistance((ob->x-player->x), (ob->y-player->y))<0x120000) &&
                (player->z==nominalheight)
           )
            SHAKETICS = 35;
    }

    for (i=low; i<=high; i++)
    {
        MissileSound = false;
        /*
        if (((which == 0) && ((low == 5) || (low == 6))) ||
            ((which == 6) && ((i==4) || (i==5)))
           )
           {

           if (((which == 0) && (low == 5)) ||
               ((which == 6) && (i == 4))
              )
              {
              newz = ob->z + 64;
              if (newz > maxheight)
                 continue;
              SpawnMissile(ob,p_firebombobj,0,0,&s_grexplosion1,0);
              new->z = newz;
              new->dir = 10;


              }
           else
              {
              newz = ob->z - 64;
              if ((sky == 0) && (newz < 0))
                 continue;
              SpawnMissile(ob,p_firebombobj,0,0,&s_grexplosion1,0);
              new->z = newz;
              new->dir = 12;

              }


           }
        else */
        {
            SpawnMissile(ob,p_firebombobj,0,dirangle8[2*i],&s_grexplosion1,0x10000);
            new->z = ob->z;
            new->dir = (i<<1);

        }

        MissileSound = true;


        SD_PlaySoundRTP(SD_EXPLODEFLOORSND,ob->x,ob->y);
        new->temp2 = FixedMul(damage,DIAGADJUST);


        tempwall = (wall_t*)actorat[new->tilex][new->tiley];
        doorat= 0;
        if (M_ISDOOR(new->tilex,new->tiley))
        {
            tempdoor = doorobjlist[tilemap[new->tilex][new->tiley]&0x3ff];
            if (tempdoor->position<0x8000)
                doorat = 1;
        }

        if ((tempwall && M_ISWALL(tempwall)) || doorat ||
                (new->tilex <=0) || (new->tilex > MAPSIZE-1) ||
                (new->tiley <=0) || (new->tiley > MAPSIZE-1)
           )
        {
            new->z = ob->z;
            SetFinePosition(new,ob->x,ob->y);
            SetVisiblePosition(new,ob->x,ob->y);
        }
        new->whatever = ob->whatever;
        new->temp3 = ob->temp3 - 1;
    }
}






void MissileHitActor(objtype *owner, objtype *missile, objtype *victim,
                     int damage, int hitmomx, int hitmomy
                    )
{
    int tcl = victim->obclass;
    int ocl = missile->obclass;

    if (
        (victim->flags & FL_DYING) || // hey, they're dying already;
        (victim->flags & FL_HEAD)  || // don't hurt overrobot's head, wheels
        (tcl == wallopobj)            || // bcraft is invulnerable
        (tcl == b_darkmonkobj)        || // darkmonk is invulnerable
        (!(victim->flags & FL_SHOOTABLE)) || // don't hurt environment dangers, dead guys
        ((tcl == b_darksnakeobj) &&
         ((SNAKELEVEL != 3) || (!victim->temp3))// return for non-red snake
        )
    )
        return;


    if ((tcl == playerobj) || (tcl == b_heinrichobj))
        victim->target = owner;


    if (tcl == NMEsaucerobj)  // can shoot over's saucer
    {
        NewState(victim,&s_explosion1);
        victim->flags &= ~FL_SHOOTABLE;
        victim->temp2 = damage;
        return;
    }

    else if (tcl == roboguardobj)       // check roboguard
    {
        DamageThing(victim,damage);
        Collision(victim,owner,0,0);
    }

    else if (tcl == collectorobj)
    {
        if (gamestate.SpawnEluder)
            return;

        DamageThing(victim,damage);
        Collision(victim,owner,0,0);
    }

    else if (tcl == patrolgunobj)
    {
        DamageThing(victim,damage);
        if (victim->hitpoints <= 0)
        {
            victim->momentumx = victim->momentumy = victim->momentumz = 0;
            victim->flags |= FL_DYING;
            if (victim->temp1 == -1)  // this is 4-way gun
                NewState(victim,&s_robogrddie1);
#if (SHAREWARE == 0)
            else                         // this is normal
                NewState(victim,&s_gundie1);
#endif
        }
    }

// bosses are "special" ==========================

    else if ((tcl >= b_darianobj) && (tcl < b_darkmonkobj))

    {
        DamageThing(victim,damage);
        if (!(victim->flags & FL_ATTACKMODE))
            FirstSighting (victim);     // put into combat mode
        if (victim->hitpoints <= 0)
        {
            victim->momentumx = victim->momentumy = victim->momentumz = 0;
            victim->flags |= FL_DYING;
            NewState(victim,UPDATE_STATES[DIE][victim->obclass-lowguardobj]);
            switch (victim->obclass)
            {
            case b_darianobj:
                AddMessage("Darian defeated!",MSG_CHEAT);
                break;

            case b_heinrichobj:
                AddMessage("Krist defeated!",MSG_CHEAT);
                break;

            case b_robobossobj:
                AddMessage("NME defeated!",MSG_CHEAT);
                break;
            default:
                ;
            }
            MU_StartSong(song_bossdie);
        }
#if (SHAREWARE == 0)
        else
        {
            MISCVARS->REDTIME = (damage >> 1);
            if (victim->obclass == b_heinrichobj)
            {
                NewState(victim,&s_heinrichdefend);
                if (Near(victim,PLAYER[0],3))
                {
                    MISCVARS->HRAMMING = 1;
                    MISCVARS->HMINING = 0;
                    victim->dirchoosetime = 0;
                }
                else
                {
                    MISCVARS->HMINING = 1;
                    MISCVARS->HRAMMING = 0;
                    victim->dirchoosetime = 5;//10;
                }
                victim->targettilex = victim->targettiley = 0;
                victim->target = NULL;
            }
        }
#endif
    }

#if (SHAREWARE == 0)
    else if ((tcl == b_darksnakeobj) && (victim->temp3)) // red snake
    {
        DamageThing(SNAKEEND,damage);
        if (victim->state->think == T_DarkSnakeChase)
            NewState(victim,&s_redheadhit);
        else
            NewState(victim,&s_redlinkhit);
        victim->temp3 = 0;
    }
#endif

//===============================================
    else // all other actors
    {

        if ((tcl == playerobj) &&
                (victim->flags & FL_AV) &&
                (ocl != p_godballobj)
           )
        {
            playertype *pstate;

            M_LINKSTATE(victim,pstate);
            pstate->protectiontime -= ((damage<<1) + damage);
            if (pstate->protectiontime < 1)
                pstate->protectiontime = 1;
            if (victim==player)
                GM_UpdateBonus (pstate->protectiontime, false);

            return;  // asbestos vest prevents victim damage
        }

        DamageThing(victim,damage);



        if ((tcl < roboguardobj) && (victim->hitpoints <= 0))
        {
            if (ocl != p_godballobj)
                victim->flags |= FL_HBM;
            else
                victim->flags |= (FL_GODSTRUCK | FL_FULLLIGHT);
        }

        if (tcl == playerobj)
        {
            playertype *pstate;

            M_LINKSTATE(victim,pstate);
            if (pstate->health <= 0)
            {
                if (ocl != p_godballobj)
                    victim->flags |= FL_HBM;
                else
                    victim->flags |= (FL_GODSTRUCK | FL_FULLLIGHT);

                if (M_ISACTOR(owner))
                {
                    if (owner->obclass == playerobj)
                    {
                        if (!victim->momentumz)
                            BATTLE_PlayerKilledPlayer(battle_kill_with_missile,owner->dirchoosetime,victim->dirchoosetime);
                        else
                            BATTLE_PlayerKilledPlayer(battle_kill_with_missile_in_air,owner->dirchoosetime,victim->dirchoosetime);
                    }
                    else
                        BATTLE_CheckGameStatus(battle_player_killed,missile->dirchoosetime);
                }
                else
                    BATTLE_CheckGameStatus(battle_player_killed,missile->dirchoosetime);
            }
        }



        if ((owner->obclass == playerobj) && (victim->flags & FL_HBM))
        {
            MISCVARS->supergibflag = true;
            //GivePoints(starthitpoints[gamestate.difficulty][victim->obclass]*5);
        }

        Collision(victim,owner,hitmomx,hitmomy);
        MISCVARS->supergibflag = false;
        if ((tcl == blitzguardobj) && (owner->obclass == playerobj))
            victim->flags |= FL_TARGET;

    }

}



void MissileHit (objtype *ob,void *hitwhat)
{
    int damage=0, random,tcl=0,ocl,sound,hitmomx,hitmomy;
    objtype* tempactor=NULL,*owner;



    if (ob==missobj)
        missobj=NULL;

    if (ob == PLAYER0MISSILE)
        PLAYER0MISSILE = NULL;


    ob->momentumz = 0;
    hitmomx = ob->momentumx;
    hitmomy = ob->momentumy;
    if (ob->soundhandle != -1)
        SD_StopSound(ob->soundhandle);

    ob->flags &= ~FL_SHOOTABLE;
    if (FirstExplosionState(ob->state))
        return;

    /*
    if ((ob->z < -28) || (IsWindow(ob->tilex,ob->tiley)))
       {
       NewState(ob,&s_megaremove);
       return;
       }
    */

    tempactor = (objtype*)hitwhat;
    owner = (objtype*)(ob->whatever);

    random = GameRandomNumber("MissileHit",0);
    ocl = ob->obclass;
    if (tempactor)
    {
        if (tempactor->which == ACTOR)
            tcl = tempactor->obclass;
        else if (tempactor->which == SPRITE)
            tcl = -1;
    }

    if ((!tcl) && (ob->z < -30))
    {
        if (ob->soundhandle != -1)
            SD_StopSound(ob->soundhandle);

        NewState(ob,&s_megaremove);
        return;

    }


    if (((ocl != p_kesobj) && (ocl != p_godballobj)) || (!tcl))
        ZEROMOM;

    if (tcl == b_darianobj)
        MISCVARS->ESAU_SHOOTING  = false;

    switch(ocl)
    {


    case p_bazookaobj:
        NewState(ob,&s_explosion1);
        if (M_ISACTOR(owner) && (owner->obclass == blitzguardobj))
            damage = 30 + (random >> 4);
        else
            damage = 2*((random>>3)+80);
        break;

    case p_heatseekobj:
        NewState(ob,&s_explosion1);
        damage = 2*((random>>3)+50);
        break;


    case p_drunkmissileobj:
        NewState(ob,&s_explosion1);
        damage = ((random>>3)+25);
        break;

    case p_firebombobj:
        NewState(ob,&s_explosion1);
        damage = 2*((random>>3)+90);
        ob->temp3 = 4;
        SpawnFirebomb(ob,damage,4);
        break;

    case p_firewallobj:

        if (tcl == playerobj)
            gamestate.DOGROUNDZEROBONUS = true;
        NewState(ob,&s_explosion1);
        damage = 2*((random>>3)+50);
        break;


    case p_godballobj:
        if ((tcl >= pillarobj) || (!tcl) || ((tcl == -1) && (!(tempactor->flags & FL_SHOOTABLE))))
            NewState(ob,&s_explosion1);
        ob->target = NULL;
        damage = 500;
        break;


    case shurikenobj:
        NewState(ob,&s_explosion1);
        damage = ((random >>3) + 30)>>2;
        break;


    case grenadeobj:
        NewState(ob,&s_explosion1);
        damage = (random >>3) + 20;
        break;

    case fireballobj:
        NewState(ob,&s_explosion1);
        damage = (random >> 3) + 10;
        break;

    case missileobj:
        NewState(ob,&s_explosion1);
        if (M_ISACTOR(owner) && (owner->obclass == wallopobj))
            damage = (random >> 5);
        else
            damage = (random >>3) + 30;
        if (tcl && (tcl != b_heinrichobj))
            damage = 3*damage>>3;
        break;

    case wallfireobj:
        if ((!tempactor) ||
                (tempactor->which == ACTOR) ||
                (tempactor->which == SPRITE)
           )
            NewState(ob,&s_explosion1);
        else if (M_ISWALL(tempactor) || (tempactor->which == DOOR))
            NewState(ob,&s_crossdone1);
        damage = EnvironmentDamage(ob);
        break;



    case inertobj:
        ob->state = NULL;
        return;
        break;


#if (SHAREWARE == 0)


    case p_splitmissileobj:
        NewState(ob,&s_explosion1);
        damage = 2*((random>>3)+50);
        break;


    case p_kesobj:
        if ((tcl >= pillarobj) ||
                (!tcl) ||
                ((tcl == -1) && (!(tempactor->flags & FL_SHOOTABLE)))
           )
            NewState(ob,&s_explosion1);
        damage = 2*((random>>3)+140);
        break;


    case netobj:
        ob->state=NULL;
        MISCVARS->NET_IN_FLIGHT = false;
        if (tempactor == PLAYER[0])
        {
            if ((tempactor->flags & FL_GODMODE) ||
                    (tempactor->flags & FL_DOGMODE) ||
                    godmode
               )
                damage = 0;
            else
            {
                damage = (random >>4) + 5;
                PLAYERSTATE[0].NETCAPTURED = -1;
                PLAYERSTATE[0].weapondowntics = WEAPONS[PLAYERSTATE[0].weapon].screenheight/GMOVE;
                NewState(PLAYER[0],&s_player);
                PLAYERSTATE[0].attackframe = PLAYERSTATE[0].weaponframe = 0;
                PLAYERSTATE[0].batblast = 0;
                if (PLAYERSTATE[0].HASKNIFE == false)
                    AddMessage("Wiggle left and right to get out of net!",
                               MSG_GAME);
            }
        }
        break;


    case bigshurikenobj:
        NewState(ob,&s_oshurikenhit1);
        if (owner->obclass == wallopobj)
            damage = (random >> 5);
        else
            damage = 4*((random >>3) + 30)/10;

        break;


    case dm_spitobj:
        NewState(ob,&s_spithit1);
        damage = 30;
        if (gamestate.difficulty == gd_hard)
            damage += 15;
        break;

    case dm_weaponobj:
        damage = 20;
        NewState(ob,&s_explosion1);
        break;

    case dm_heatseekobj:
        damage = 20;
        NewState(ob,&s_explosion1);
        break;

    case dmfballobj:
        NewState(ob,&s_explosion1);
        damage = (random >>3) + 20;
        break;

    case h_mineobj:
        NewState(ob,&s_explosion1);
        damage = (random >>3) + 20;
        break;

    case NMEsaucerobj:
        NewState(ob,&s_explosion1);
        damage = 2*((random>>3)+30);
        break;


#endif


        //default:
        //Error("Unknown ob %d called MissileHit",ob->obclass);
    }

    //if (!ob->state)
    //return;
    if ((sound = BAS[ob->obclass].hit)!=0)
        SD_PlaySoundRTP(sound,ob->x,ob->y);


    if (FirstExplosionState(ob->state))
        SD_PlaySoundRTP(SD_EXPLODESND,ob->x,ob->y);


    if (tcl>0) // actors
    {
        MissileHitActor(owner,ob,tempactor,damage,hitmomx,hitmomy);
        if ((ocl == p_kesobj) && (tcl < roboguardobj))
        {
            tempactor->momentumx = hitmomx; // kes gives wus targets its momentum
            tempactor->momentumy = hitmomy;
            //missile->flags |= FL_NOFRICTION;
        }
    }

    else if (tcl < 0)  // static
    {
        DamageThing(hitwhat,damage);
        if (FirstExplosionState(new->state))
            new->whatever = ob->whatever;
    }


}




void T_Spears(objtype*ob)
{   int dx,dy,dz,i;


    for(i=0; i<numplayers; i++)
    {   if (PLAYER[i]->flags & FL_DYING)
            continue;

        dx = abs(PLAYER[i]->x - ob->x);
        dy = abs(PLAYER[i]->y - ob->y);
        dz = abs(PLAYER[i]->z - ob->z);


        if ((!ob->ticcount)&&(ob->state->condition&SF_SOUND) &&
                areabyplayer[ob->areanumber])
            SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);

        if ((dx < STANDDIST) && (dy < STANDDIST) && (dz < 20))
        {   ob->flags &= ~FL_BLOCK;
            if ((!ob->ticcount) && (ob->state->condition & SF_CRUSH))
            {   DamageThing(PLAYER[i],EnvironmentDamage(ob));
                Collision(PLAYER[i],ob,0,0);
                M_CheckPlayerKilled(PLAYER[i]);
                return;
            }
        }
        else
        {   if (ob->state->condition & SF_DOWN)
                ob->flags &= ~FL_BLOCK;
            else
                ob->flags |= FL_BLOCK;
        }
    }
}



void T_CrushUp(objtype*ob)
{   int dx, dy,dist,dz,i;


    if ((!ob->ticcount) && (ob->state->condition & SF_SOUND) &&
            areabyplayer[ob->areanumber])
        SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);
    dist = ACTORSIZE+0x2000;
    if (ob->state->condition & SF_UP)
    {   ob->temp1 += 4;
//	  Debug("\ncol momz = 4");
    }
    else if (ob->state->condition & SF_DOWN)
    {   ob->temp1 -= 4;
//      Debug("\ncol mom z = -4");
    }
    else
    {   //ob->momentumz = 0;
        //	  Debug("\ncol mom z = 0");
    }

    ob->temp2 = maxheight - ob->temp1 + 32;


    for(i=0; i<numplayers; i++)
    {   dx = abs(PLAYER[i]->x - ob->x);
        dy = abs(PLAYER[i]->y - ob->y);
        dz = abs(ob->temp2-PLAYER[i]->z);

        if ((dx < dist) && (dy < dist) && (dz < 65))
        {   ob->flags &= ~FL_BLOCK;
            //player->temp2 = 0;
            if ((!ob->ticcount) && (ob->state->condition & SF_CRUSH) &&
                    (levelheight<2) && (!(ob->flags & FL_DYING)))
            {   DamageThing(PLAYER[i],EnvironmentDamage(ob));
                if (PLAYER[i]->hitpoints <= 0)
                    PLAYER[i]->flags |= FL_HBM;
                Collision(PLAYER[i],ob,0,0);
                M_CheckPlayerKilled(PLAYER[i]);
                //NewState(ob,ob->state); //reset ticcount
                return;
            }
            if (ob->state->condition & SF_UP)
            {
                PLAYER[i]->momentumz = -(4<<16);
                if (PLAYER[i]->z < -30)
                    PLAYER[i]->z = -30;
            }
            else if (ob->state->condition & SF_DOWN)
            {   PLAYER[i]->momentumz = (4<<16);
                if (PLAYER[i]->z >= nominalheight)
                    PLAYER[i]->z = nominalheight;
            }
            else
                PLAYER[i]->momentumz = 0;
            PLAYER[i]->whatever = ob;
            ob->whatever = PLAYER[i];
            //PLAYER[i]->flags |= FL_RIDING;

        }

    }

        if (ob->state->condition & SF_BLOCK)
            ob->flags |= FL_BLOCK;
        else
            ob->flags &= ~FL_BLOCK;

}


void T_CrushDown(objtype*ob)
{   int dx,dy,dz,i;


    if ((!ob->ticcount) && (ob->state->condition & SF_SOUND)&&
            areabyplayer[ob->areanumber])
        SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);

    ob->temp2 = ob->z;
    for(i=0; i<numplayers; i++)
    {   dx = abs(PLAYER[i]->x - ob->x);
        dy = abs(PLAYER[i]->y - ob->y);
        dz = abs(PLAYER[i]->z - ob->z);

        if ((dx < STANDDIST) && (dy < STANDDIST) && (dz < 20))
        {   //PLAYER[i]->temp2 = 0;
            ob->flags &= ~FL_BLOCK;
            if ((!ob->ticcount) && (ob->state->condition & SF_CRUSH) &&
                    (!(ob->flags & FL_DYING)))
            {   DamageThing(PLAYER[i],EnvironmentDamage(ob));
                if (PLAYER[i]->hitpoints <= 0)
                    PLAYER[i]->flags |= FL_HBM;
                Collision(PLAYER[i],ob,0,0);
                M_CheckPlayerKilled(PLAYER[i]);
                //NewState(ob,ob->state); //reset ticcount
                return;
            }
            if ((ob->state->condition & SF_DOWN) &&
                    ((ob->state != &s_columndowndown1) &&
                     (ob->state != s_columndowndown1.next)))
            {   PLAYER[i]->temp2 = COLUMNCRUSH;
                PLAYER[i]->whatever = ob;
            }
        }

    }
        if (ob->state->condition & SF_BLOCK)
            ob->flags |= FL_BLOCK;
        else
            ob->flags &= ~FL_BLOCK;
}


void T_Explosion(objtype* ob)
{
    int momx,momy,momz;
    int dx,dy,dz;
    int pdamage,dist,blastradius=0x20000,
                     fatalradius=0x9000,impulse,damage,
                     scalefactor;



    statobj_t* checkstat;
    objtype* check,*owner;

    if (ob->ticcount)
        return;

    damage = EXPLOSION_DAMAGE;
    owner = (objtype*)(ob->whatever);
    if ((ob->temp3) && (ob->obclass == p_firebombobj))
    {
        SpawnFirebomb(ob,damage,0);
        ob->temp3 = 0;
    }
//================== check surrounding actors ============================//


    for(check = firstactive; check; check=check->nextactive)
    {
        if (check == ob)
            continue;

//	  if (check == owner)
//		continue;

        dx = abs(check->x - ob->x);
        if (dx > blastradius)
            continue;

        dy = abs(ob->y - check->y);
        if (dy > blastradius)
            continue;

        dz = ((abs(ob->z - check->z))<<10);
        if (dz > blastradius)
            continue;


        if (check->flags & FL_HEAD)
            continue;

        if (check->flags & FL_DYING)
            continue;

        if (!(check->flags & FL_SHOOTABLE))
            continue;



        if ((check->obclass >= roboguardobj) && (check->obclass != b_darkmonkobj))
            //(check->obclass <= wallopobj))
            continue;


        if (!CheckLine(ob,check,SIGHT))
            continue;


        if (check->obclass == NMEsaucerobj)
        {
            NewState(check,&s_explosion1);
            check->flags &= ~FL_SHOOTABLE;
            return;
        }
        //magdx = abs(dx);
        //magdy = abs(dy);

        dist = Find_3D_Distance(dx,dy,dz);
        SoftError("\ndist: %x\n",dist);


        //if (dist < 0x10000)
        // dist = 0x10000;

        scalefactor = FixedDiv2(1<<16,FixedMul(dist,dist));
        //scalefactor = FixedDiv2(1<<16,dist);

        if (scalefactor > 0x12000)
            scalefactor = 0x12000;
        pdamage = FixedMul(damage,scalefactor);
        SoftError("\ndamage: %d, scalefactor: %x\n",pdamage,scalefactor);
        impulse = FixedMul(EXPLOSION_IMPULSE,scalefactor);
        if (check->obclass == playerobj)
        {
            check->target = owner;
            if (check->flags & FL_AV)
                pdamage = 0;
        }

        if (check->obclass < roboguardobj)
        {
            SoftError("\nhitpoints before: %d",check->hitpoints);
            DamageThing(check,pdamage);
            SoftError("\nhitpoints after: %d",check->hitpoints);
            if ((check->hitpoints <= 0) && (gamestate.violence == vl_excessive) &&
                    ((ob->obclass == p_firebombobj) ||
                     ((dx < fatalradius) && (dy < fatalradius) && (dz < 20)))
               )
                check->flags |= FL_HBM;

            GetMomenta(check,ob,&momx,&momy,&momz,impulse);
            //Debug("\nhitmomx = %d, hitmomy = %d",momx,momy);

            /*if (M_ISACTOR(owner) &&
                (owner->obclass == playerobj) &&
                (check->hitpoints <=0)
               )
               GivePoints(starthitpoints[gamestate.difficulty][check->obclass]*5);
            */
            Collision(check,owner,momx,momy);
            check->momentumz += (momz<<6);
            if ((check->obclass == playerobj) && (check->flags & FL_DYING) &&
                    M_ISACTOR(owner))
            {
                if (owner->obclass == playerobj)
                {
                    if (check->z != nominalheight)
                        BATTLE_PlayerKilledPlayer(battle_kill_with_missile_in_air,owner->dirchoosetime,check->dirchoosetime);
                    else
                        BATTLE_PlayerKilledPlayer(battle_kill_with_missile,owner->dirchoosetime,check->dirchoosetime);
                }
                else
                    BATTLE_CheckGameStatus(battle_player_killed,check->dirchoosetime);
            }

        }
        else
        {
            if (check->obclass != b_darkmonkobj) {
                SoftError("non-darkmonk actor %d being helped by explosion",check->obclass);
            }
            check->hitpoints += pdamage;
        }
    }

//======================== check surrounding statics ================


    for(checkstat = firstactivestat; checkstat; checkstat=checkstat->nextactive)
    {

        if ((!(checkstat->flags & FL_SHOOTABLE)) && (checkstat->itemnumber != stat_priestporridge))
            continue;

        if ((checkstat->itemnumber >= stat_lifeitem1) &&
                (checkstat->itemnumber <= stat_lifeitem4))
            continue;

        dx = abs(checkstat->x - ob->x);
        dy = abs(checkstat->y - ob->y);
        dz = ((abs(checkstat->z - ob->z))<<10);

        if ((dx < blastradius) && (dy < blastradius) && (dz < blastradius))
        {
            dist = Find_3D_Distance(dx,dy,dz)+0xc00;

            if (dist < 0x10000)
                dist = 0x10000;

            scalefactor = FixedDiv2(1<<16,FixedMul(dist,dist));
            pdamage = FixedMul(damage,scalefactor);

            if (checkstat->itemnumber != stat_priestporridge)
                DamageThing(checkstat,pdamage);
            else if (!(checkstat->flags & FL_ACTIVE))
            {
                checkstat->flags |= FL_ACTIVE;
                checkstat->count = 1;
                //checkstat->numanims = 6;
                SD_PlaySoundRTP(SD_COOKHEALTHSND,ob->x,ob->y);
            }
        }
    }

//======================== check surrounding walls ================
    {
        int tilexlow,tilexhigh;
        int tileylow,tileyhigh;
        int radius =0x10000;
        int x,y;

        tilexlow = (int)((ob->x-radius) >>TILESHIFT);
        tileylow = (int)((ob->y-radius) >>TILESHIFT);

        tilexhigh = (int)((ob->x+radius) >>TILESHIFT);
        tileyhigh = (int)((ob->y+radius) >>TILESHIFT);

        for (y=tileylow; y<=tileyhigh; y++)
        {
            for (x=tilexlow; x<=tilexhigh; x++)
            {
                if ((tilemap[x][y]&0x8000) && (tilemap[x][y]&0x4000) && (abs(ob->z - nominalheight) < 32))
                {
                    maskedwallobj_t * mw;

                    mw=maskobjlist[tilemap[x][y]&0x3ff];
                    if (mw->flags&MW_SHOOTABLE)
                        UpdateMaskedWall(tilemap[x][y]&0x3ff);
                }
            }
        }
    }

}



void SpawnScreenEye(objtype *ob)
{
    SpawnNewObj(ob->tilex,ob->tiley,&s_eye1,inertobj);
    new->targettiley = 0;
    new->targettilex = GameRandomNumber("eye position",0) + 20;
    SCREENEYE = new;
    //RemoveFromArea(new);
    new->flags |= FL_ABP;
    MakeActive(new);

}






void SpawnSuperFatalityGibs(objtype *ob,objtype *attacker)
{
    int crazygibs = (GameRandomNumber("crazy gibs",0) % 6) + 4;
    int i;


    if ((MISCVARS->supergibflag == true) &&
            ((crazygibs == 9) || (ludicrousgibs == true))
       )

    {
        int olddirect = MISCVARS->directgibs;

        MISCVARS->directgibs = false;
        if (ludicrousgibs == false)
        {
            if (attacker == player)
            {
                AddMessage("Ludicrous Gibs!",MSG_GAME);
                if (!(attacker->flags&FL_DOGMODE))
                    SD_Play(PlayerSnds[locplayerstate->player]);
            }
        }
        else
        {
            MISCVARS->randgibspeed = true;

#ifdef MEDGIBS
            SpawnParticles(ob,GUTS,150);
#else
            SpawnParticles(ob,GUTS,75);
#endif

            MISCVARS->randgibspeed = false;
        }
        SpawnParticles(ob,GUTS,40);
        MISCVARS->directgibs = olddirect;

    }

    for (i=gt_head; i<=crazygibs; i++)
    {

        if (((ob->obclass == dfiremonkobj) || (ob->obclass == deathmonkobj)) &&
                (i == gt_leg)
           )
            SpawnParticles(ob,gt_arm,1);
        else
            SpawnParticles(ob,i,1);
    }


}





boolean Vicious_Annihilation(objtype *ob, objtype *attacker)
{
    if ((ob->flags & FL_HBM) && (gamestate.violence >= vl_high))
    {
        ob->shapeoffset = 0;
        ob->flags &= ~FL_FULLLIGHT;
        NewState(ob,(ob->obclass == playerobj)?(&s_remoteguts1):(&s_guts1));
        SD_PlaySoundRTP(SD_ACTORSQUISHSND,ob->x,ob->y);
        if (gamestate.violence == vl_excessive)
        {
            int numgibs;
            objtype *prevlast;

            numgibs = (GameRandomNumber("excessive guts",0) & 7) + 4;
            //SoftError("\nnumgibs = %d,gamestate.difficulty = %d",numgibs,gamestate.difficulty);
            prevlast = LASTACTOR;
            MISCVARS->fulllightgibs = true;
            SpawnParticles(ob,GUTS,numgibs);
            MISCVARS->fulllightgibs = false;
            for(prevlast = prevlast->next; prevlast; prevlast = prevlast->next)
                prevlast->momentumz += (prevlast->momentumz >> 1);

            if ((GameRandomNumber("super gib chance",0) < 100) ||
                    (ludicrousgibs == true)
               )
            {
                MISCVARS->directgibs = true;
                MISCVARS->gibgravity = GRAVITY/2;
//            MISCVARS->gibgravity = GRAVITY*2;
                MISCVARS->fulllightgibs = true;
                SpawnSuperFatalityGibs(ob,attacker);
                MISCVARS->fulllightgibs = false;
                MISCVARS->gibgravity = -1;
                MISCVARS->directgibs = false;
            }
        }
        return true;

    }


    if (ob->flags & FL_GODSTRUCK)
    {
        ob->shapeoffset = 0;
        ob->flags |= (FL_FULLLIGHT);
        ob->flags &= ~FL_COLORED;
        ob->momentumx = ob->momentumy = ob->momentumz = 0;
        KillActor(ob);
        NewState(ob,&s_vaporized1);
        return true;
    }

    if (ob->flags & FL_SKELETON)
    {
        KillActor(ob);
        ob->shapeoffset = 0;
        ob->flags &= ~FL_COLORED;
        ob->momentumx = ob->momentumy = ob->momentumz = 0;
        NewState(ob,&s_skeleton1);
        SD_PlaySoundRTP(SD_ACTORBURNEDSND,ob->x,ob->y);
        return true;
    }

    return false;

}

void SetReverseDeathState(objtype * actor)
{
    switch(actor->obclass)
    {
        case lowguardobj:
            NewState(actor, &s_lowgrddie4rev);
            //actor->state = &s_lowgrddie4rev;
            break;
        case highguardobj:
            NewState(actor, &s_highgrddie5rev);
            break;
        case strikeguardobj:
            NewState(actor, &s_strikedie4rev);
            //actor->state = &s_strikedie4rev;
            break;
        case blitzguardobj:
            NewState(actor, &s_blitzdie4rev);
            //actor->state = &s_blitzdie4rev;
            break;
        case triadenforcerobj:
            NewState(actor, &s_enforcerdie4rev);
            //actor->state = &s_enforcerdie4rev;
            break;
    #if (SHAREWARE == 0)
        case overpatrolobj:
            NewState(actor, &s_opdie5rev);
            //actor->state = &s_opdie5rev;
            break;
        case deathmonkobj:
            NewState(actor, &s_dmonkdie4rev);
            //actor->state = &s_dmonkdie4rev;
            break;
        case dfiremonkobj:
            NewState(actor, &s_firemonkdie4rev);
            //actor->state = &s_firemonkdie4rev;
            break;
    #endif
        default:
            Error("SetReverseDeathState was called with something that can't be handled!");
            break;
    }
}

int DetermineTimeUntilEnemyIsResurrected(classtype obclass)
{
    switch(obclass)
    {
        case lowguardobj:
            return gamestate.TimeCount/VBLCOUNTER + 60;
            break;
        case highguardobj:
            return gamestate.TimeCount/VBLCOUNTER + 90;
            break;

        case strikeguardobj:
            return gamestate.TimeCount/VBLCOUNTER + 65;
            break;
        case blitzguardobj:
            return gamestate.TimeCount/VBLCOUNTER + 60;
            break;
        case triadenforcerobj:
            return gamestate.TimeCount/VBLCOUNTER + 200;
            break;
    #if (SHAREWARE == 0)
        case overpatrolobj:
            return gamestate.TimeCount/VBLCOUNTER + 75;
            break;
        case deathmonkobj:
            return gamestate.TimeCount/VBLCOUNTER + 150;
            break;
        case dfiremonkobj:
            return gamestate.TimeCount/VBLCOUNTER + 175;
            break;
    #endif
        default:
            return -1; //TODO: Return -1 for every entry that isn't any of the above
            break;
    }
    
}

extern Queue * enemiesToRes[8];

void AddEnemyToResurrectList(objtype * ob)
{
    ob->resurrectAtTime = DetermineTimeUntilEnemyIsResurrected(ob->obclass);
    if (ob->resurrectAtTime == -1)
    {
        free(ob);
        return;
    }
    SetReverseDeathState(ob);
    switch(ob->obclass)
    {
        case lowguardobj:
            Enqueue(enemiesToRes[0], ob);
            break;
        case highguardobj:
            Enqueue(enemiesToRes[1], ob);
            break;

        case strikeguardobj:
            Enqueue(enemiesToRes[2], ob);
            break;
        case blitzguardobj:
            Enqueue(enemiesToRes[3], ob);
            break;
        case triadenforcerobj:
            Enqueue(enemiesToRes[4], ob);
            break;
    #if (SHAREWARE == 0)
        case overpatrolobj:
            Enqueue(enemiesToRes[5], ob);
            break;
        case deathmonkobj:
            Enqueue(enemiesToRes[6], ob);
            break;
        case dfiremonkobj:
            Enqueue(enemiesToRes[7], ob);
            break;
    #endif
        default:
            Error("Unknown organic enemy type detected in AddEnemyToResurrectList");
            break;
    }
    //enqueue(&enemiesToRes, ob);
}

void FreeUpResurrectList()
{
    int x = 0;
    for (x = 0; x < 8; x++)
    {
        ClearQueue(enemiesToRes[x]);
    }
}

void SetAfterResurrectState(objtype * actor, statetype * doWhat)
{
    statetype * state = actor->state;
    
    while(state->next != NULL)
    {
         state = state->next;
    }
    state->next = doWhat;
}

void SpawnDuringGameWithState (classtype which, int tilex, int tiley, int dir, int ambush, statetype * temp)
{
    //statetype *temp;

#if (SHAREWARE == 1)
    switch(which)
    {
    case overpatrolobj:
    case wallopobj:
    case deathmonkobj:
    case dfiremonkobj:
    case b_darianobj:
    case b_heinrichobj:
    case b_darkmonkobj:
        Error("\n%s actor at %d,%d not allowed in shareware !",debugstr[which],tilex,tiley);
        break;
    default:
        ;
    }

#endif
    if (which > dfiremonkobj)
    {
        return;
    }
    
    if (!CheckTile(tilex,tiley))
        FindEmptyTile(&tilex, &tiley);
        
    SpawnNewObj(tilex,tiley,temp,which);

    if (ambush)
        new->flags |= FL_AMBUSH;

    StandardEnemyInit(new,dir);

    if (which == b_darkmonkobj)
    {
        new->flags |= (FL_NOFRICTION);//|FL_INVULNERABLE);
        new->speed = ENEMYRUNSPEED*2;
    }

    ConnectAreas();
}


void ResurrectEnemies()
{   
    objtype * actor;
    
    int currTime = gamestate.TimeCount/VBLCOUNTER;
    
    int index;
    
    for (index = 0; index < 8; index++)
    {
        if (enemiesToRes[index]->NumOfItems == 0)
        {
            continue;
        }
        actor = enemiesToRes[index]->Head->data;
        
        if (currTime >= actor->resurrectAtTime)
        {
            SD_PlaySoundRTP(SD_PLAYERSPAWNSND, actor->x, actor->y);
            SpawnDuringGameWithState (actor->obclass,actor->tilex,actor->tiley,actor->dir, 1, actor->state);
            Dequeue(enemiesToRes[index]);
            gamestate.killcount--;
        }
    }
}

void SpawnDuringGame (classtype which, int tilex, int tiley, int dir, int ambush)
{   
    statetype *temp;

#if (SHAREWARE == 1)
    switch(which)
    {
    case overpatrolobj:
    case wallopobj:
    case deathmonkobj:
    case dfiremonkobj:
    case b_darianobj:
    case b_heinrichobj:
    case b_darkmonkobj:
        Error("\n%s actor at %d,%d not allowed in shareware !",debugstr[which],tilex,tiley);
        break;
    default:
        ;
    }

#endif
    if (which > dfiremonkobj)
    {
        return;
    }
    
    if ((temp = UPDATE_STATES[STAND][which-lowguardobj]) != NULL)
    {       
        if (!CheckTile(tilex, tiley))
            FindEmptyTile(&tilex, &tiley);
        
        SpawnNewObj(tilex,tiley,temp,which);

        if (ambush)
            new->flags |= FL_AMBUSH;

        StandardEnemyInit(new,dir);

        if (which == b_darkmonkobj)
        {
            new->flags |= (FL_NOFRICTION);//|FL_INVULNERABLE);
            new->speed = ENEMYRUNSPEED*2;
        }
    }
    ConnectAreas();
}

/*
========================
=
= BeginEnemyFatality
=
========================
*/

extern boolean enableZomROTT;

void BeginEnemyFatality(objtype *ob,objtype *attacker)
{
    if ((attacker == player) && (ob->obclass < (NUMENEMIES + 2)))
    {
        GivePoints(starthitpoints[gamestate.difficulty][ob->obclass]*5);
        if (timelimitenabled)
            timelimit+=VBLCOUNTER;
    }

    ob->flags |= FL_DYING;
    ob->soundhandle = -1;

    if (Vicious_Annihilation(ob,attacker))
        return;
    else if (enableZomROTT)
    {
        objtype * copyOfObject = malloc(sizeof(objtype));
        
        memcpy(copyOfObject, ob, sizeof(objtype));
        
        AddEnemyToResurrectList(copyOfObject);
    }

    if ((ob->obclass == patrolgunobj) && (ob->temp1 == -1))
        NewState(ob,&s_robogrddie1);
    else if (ob->obclass == collectorobj)
    {
        if ((!M_ISACTOR(attacker)) || (attacker->obclass != playerobj))
            RespawnEluder();
        else
            BATTLE_CheckGameStatus(battle_shot_deluder,attacker->dirchoosetime);

        NewState(ob,&s_explosion1);
        MISCVARS->gibgravity = GRAVITY/2;
        MISCVARS->fulllightgibs = true;
        SpawnParticles(ob,gt_sparks,100);
        MISCVARS->fulllightgibs = false;
        MISCVARS->gibgravity = -1;
    }
    else
    {
        statetype *temp;

        if ((ob->obclass == blitzguardobj) &&
                (ob->state->condition & SF_FAKING)
           )
        {
            NewState(ob,&s_blitzstruggledie1);
            ob->flags &= ~FL_FULLLIGHT;
        }
        else if
        ((ob->obclass == blitzguardobj) &&
                (ob->state->condition & SF_DOWN)
        )
        {
            NewState(ob,&s_blitzplead7);
            ob->flags &= ~FL_FULLLIGHT;
        }

        else if
        ((temp= M_S(DIE)) != NULL)
        {
            if (LOW_VIOLENCE_DEATH_SHOULD_BE_SET(ob))
                SET_DEATH_SHAPEOFFSET(ob);
            NewState(ob,temp);
            ob->flags &= ~FL_FULLLIGHT;
        }
        else
            Error("Null dead state called in Collision, obclass %d",ob->obclass);
    }


}



/*
========================
=
= BeginPlayerFatality
=
========================
*/




void BeginPlayerFatality(objtype *ob,objtype *attacker)
{
    playertype *pstate;
    M_LINKSTATE(ob,pstate);

    ob->flags &= ~(FL_ELASTO|FL_GODMODE|FL_DOGMODE|FL_NOFRICTION|FL_RIDING);

    ob->flags |= FL_DYING;
    pstate->weapon = -1;


    if (BATTLEMODE)
        SD_PlaySoundRTP (SD_PLAYERTCDEATHSND+(pstate->player),ob->x,ob->y);

    if (Vicious_Annihilation(ob,attacker) == false)
    {
        if (LOW_VIOLENCE_DEATH_SHOULD_BE_SET(ob))
            SET_DEATH_SHAPEOFFSET(ob);

        NewState(ob,&s_remotedie1);
        ob->flags &= ~FL_FULLLIGHT;
    }



}



/*
========================
=
= BeginEnemyHurt
=
========================
*/


void BeginEnemyHurt(objtype *ob)
{
    statetype *temp;

    if ((temp= M_S(COLLIDE1)) != NULL)
    {


        if ((ob->obclass == blitzguardobj) &&
                (ob->state->condition & SF_FAKING)
           )
        {
            ob->temp1 = 1;
            ob->dirchoosetime = 0;
            T_PlayDead(ob);

        }

        else
        {
            if ((ob->obclass == triadenforcerobj) &&
                    (GameRandomNumber("george pain chance",0) <
                     (50 + (gamestate.difficulty<<6))
                    )
               )
            {
                ob->flags &= ~FL_FULLLIGHT;
                return;

            }


            if (LOW_VIOLENCE_PAIN_SHOULD_BE_SET(ob))
                SET_PAIN_SHAPEOFFSET(ob);

            if (GameRandomNumber("Collision",0) < 128)
                NewState(ob,temp);
            else
                NewState(ob,M_S(COLLIDE2));
        }

        ob->flags &= ~FL_FULLLIGHT;
        ob->ticcount = PAINTIME;
        if (ob->obclass == strikeguardobj)
            ob->ticcount >>= 1;
    }
}


void  Collision(objtype*ob,objtype *attacker,int hitmomentumx,int hitmomentumy)
{
    int ocl;

    if ((!(ob->flags & FL_SHOOTABLE)) || (ob->flags & FL_DYING))
        return;

    ocl = ob->obclass;

    ob->momentumx += hitmomentumx;
    ob->momentumy += hitmomentumy;

    if ((ocl == playerobj) && (gamestate.battlemode == battle_Eluder))
        return;

    //insertion 5

    if (ocl != playerobj)
    {
        if ((!(ob->flags & FL_ATTACKMODE)) && (TABLE_ACTOR(ob)))
            ActivateEnemy(ob);


        if (ob->hitpoints <= 0)
            BeginEnemyFatality(ob,attacker);

        else if (ocl != roboguardobj)// && (ob->state->think != T_Collide))
            BeginEnemyHurt(ob);
    }
    else
    {
        playertype *pstate;

        if ((ob->flags & FL_GODMODE) || (ob->flags & FL_DOGMODE) || godmode)
            return;

        M_LINKSTATE(ob,pstate);
        if (pstate->health<=0)
            BeginPlayerFatality(ob,attacker);
        else
            ob->flags |= FL_PAIN;

    }

}




void T_BossExplosions(objtype*ob)
{

    if (ob->temp1)
    {   if (ob->dirchoosetime)
            ob->dirchoosetime --;
        else
        {   int randtime,randangle,randdist,sound;
            statetype *nstate;

            ob->temp1 --;
            randtime = GameRandomNumber("Boss Explosion Time",0);
            ob->dirchoosetime = 10;
            if (randtime < 128)
                ob->dirchoosetime >>= 1;
            randangle = (GameRandomNumber("Boss Explosion Angle",0) << 3);
            randdist = (GameRandomNumber("Boss Explosion Distance",0) << 7)+0x4000;
            sound = SD_EXPLODEFLOORSND;
            if (randtime < 128)
            {   nstate = &s_explosion1;
                sound++;
            }
#if (SHAREWARE == 0)
            else
                nstate = &s_grexplosion1;
#endif


            SpawnMissile(ob,inertobj,0,randangle,nstate,randdist);
            SD_PlaySoundRTP(sound,new->x,new->y);
        }
    }
}




gib_t RandomGutsType(void)
{
    int rand = GameRandomNumber("gut random",0);


    if (rand < 128)
        return gt_organ;

    //if (rand < 160)
    return gt_rib;

    //return gt_pinkorgan;


}


void SpawnParticles(objtype*ob,int which,int numparticles)
{
    int randphi,randtheta,i,nspeed;
    boolean eyespawned = false;
    int gibtype;
    int randadj;


    if ((ob->z <= -64) && (sky == 0)) //shouldn't happen
        return;


    if (((which == GUTS) || (which == RANDOM)) && (gamestate.violence < vl_high))
        which = gt_sparks;

    gibtype = which;


    for(i=0; i<numparticles; i++)
    {
        int ordertemp;	/* DDOI - Watcom evaluates the mult order diff */
        randphi = (GameRandomNumber("particle generate phi",0) << 3);
        // randadj = RandomSign() * (GameRandomNumber("rand gib adjust",0) >> 4);
        ordertemp = (GameRandomNumber("rand gib adjust",0) >> 4);
        randadj = RandomSign() * ordertemp;



        if (ob->z > (nominalheight - 32))
            randphi &= ((ANGLES/2) - 1);

        randtheta = (GameRandomNumber("particle generate theta",0) << 3);
        nspeed = MISCVARS->gibspeed;


        if (which == RANDOM)
        {
            if (GameRandomNumber("random gib",0) < 128)
                gibtype = RandomGutsType();
            else
                gibtype = gt_sparks;

        }

        if ((which == GUTS) || (which == DISEMBOWEL))
        {

            gibtype = RandomGutsType();
            if (which == DISEMBOWEL)
            {
                randphi>>=2;
                randtheta=ob->temp1+(randtheta>>3)-(randtheta>>4);
            }

        }

        if (lowmemory && (gibtype >= gt_rib) && (gibtype <= gt_limb))
            gibtype = gt_organ;

        if
        (
            // (gibtype >= gt_organ) && (gibtype <= gt_limb) &&
            (MISCVARS->numgibs >= MAXGIBS)
        )
            return;



        if (gibtype == gt_lsoul)
        {
            SpawnNewObj(ob->tilex,ob->tiley,&s_littlesoul,inertobj);
            randphi = 0;
        }


#if (SHAREWARE == 0)
        else if (gibtype == gt_spit)

            SpawnNewObj(ob->tilex,ob->tiley,&s_slop1,inertobj);
#endif
        else
        {
            SpawnNewObj(ob->tilex,ob->tiley,&s_gibs1,inertobj);
            new->shapeoffset = gibtype*12;
            NewState(new,new->state);
        }

        if (MISCVARS->directgibs == true)
        {
            int dx,dy,dz;

            dx = PLAYER[0]->x - ob->x;
            dy = ob->y - PLAYER[0]->y;
            randtheta = AngleBetween(ob,PLAYER[0]) +
                        (randadj<<4);
            dz = 100 + (randadj<<3);

#ifdef MEDGIBS
            nspeed = 0x2800;
#else
            nspeed = 0x2800 + (randadj<<7);
#endif

            randphi = atan2_appx(FindDistance(dx,dy),dz<<10);
        }


        if ((eyespawned == false) && (which == GUTS) &&
                (ob->obclass != playerobj)
           )
        {
            eyespawned = true;
            new->flags |= FL_EYEBALL;
        }

        if ((gibtype >= gt_organ) && (gibtype <= gt_limb))
        {
            new->dirchoosetime = GIBVALUE;
            MISCVARS->numgibs ++;
        }
        new->temp2 = gibtype;
        new->temp3 = (MISCVARS->gibgravity == -1)?(GRAVITY):(MISCVARS->gibgravity);

        new->speed = nspeed>>1;

#ifndef MEDGIBS
        if (MISCVARS->randgibspeed == true)
            new->speed += (randadj << 11);
#endif

//      if (ob->state == &s_snakefireworks)
        new->z = ob->z;

        Fix(randphi);
        Fix(randtheta);

        Set_3D_Momenta(new,new->speed,randtheta,randphi);
        new->momentumz <<= 6;
        new->flags |= (FL_NOFRICTION|FL_CRAZY|FL_ABP|FL_NEVERMARK);
        if (MISCVARS->fulllightgibs == true)
            new->flags |= FL_FULLLIGHT;
        new->dir = west;
        new->whatever = ob;
        MakeActive(new);
    }
}



void T_SlideDownScreen(objtype *ob)
{

    ob->targettiley += 12;
    if (ob->targettiley > 300)
    {
        NewState(ob,&s_megaremove);
        SCREENEYE = NULL;
    }

}

void T_SpawnSoul(objtype*ob)
{
    if (ob->ticcount)
        return;

    SpawnNewObj(ob->tilex,ob->tiley,&s_bigsoul,inertobj);
    new->momentumz = -4000;
    new->flags |= (FL_NOFRICTION|FL_CRAZY|FL_ABP|FL_NEVERMARK);
    new->z = ob->z;
    new->dir = west;
    MakeActive(new);

    SpawnParticles(ob,gt_lsoul,6);


}


void BloodDrip(objtype *ob,int tilex,int tiley)
{   int dx,dy,x,y,scale;

    dx = ob->tilex - tilex;
    dy = ob->tiley - tiley;

    if (!dy)
    {
        scale = (ob->momentumx)?(FixedDiv2(ob->momentumy,ob->momentumx)):(0);
        x = (dx < 0)?(tilex << 16):((tilex+1) << 16);
        y = FixedMul(x - ob->x,scale) + ob->y;
    }

    else if (!dx)
    {
        scale = (ob->momentumy)?(FixedDiv2(ob->momentumx,ob->momentumy)):(0);
        y = (dy < 0)?(tiley << 16):((tiley+1) << 16);
        x = FixedMul(y - ob->y,scale) + ob->x;
    }

    ob->temp2 = (GameRandomNumber("BloodDrip",0) << 9) + 0xc000;
    ob->temp1 = (ob->z<<16);
    SetFinePosition(ob,x,y);
    ob->shapeoffset = 0;
    NewState(ob,&s_blooddrip1);

}



void T_BloodFall(objtype*ob)
{
    ob->temp1 += ob->temp2;
    ob->z = (ob->temp1 >> 16);
    if (ob->z >= maxheight)
    {
        ob->shapeoffset = 12;
        MISCVARS->numgibs--;
        NewState(ob,&s_gibsdone1);
        ob->z = nominalheight;
    }

}




void T_Xylophone(objtype*ob)
{
    if (!ob->ticcount)
        SD_PlaySoundRTP(SD_ACTORSKELETONSND,ob->x,ob->y);
}



void T_ParticleGenerate(objtype*ob)
{

    if (ob->dirchoosetime)
        ob->dirchoosetime--;
    else
    {
        SetGibSpeed(0x3000);
        SpawnParticles(ob,gt_sparks,(GameRandomNumber("particle count",0) % 10) + 7);
        ResetGibSpeed();
        ob->dirchoosetime = 10;
        if (GameRandomNumber("particle generator choose time",0) < 128)
            ob->dirchoosetime >>= 1;
    }

}



void T_Particle(objtype*ob)
{   int dx,dy,dz;

    ob->z += (ob->momentumz>>16);

    if ((ob->z >= nominalheight) || (!ob->momentumz))

    {
        if (ob->z >= nominalheight)
            ob->z = nominalheight;
//done:
        if (ob->temp2 == gt_spit)
            ob->state = NULL;
        else
        {
            if (ob->dirchoosetime == GIBVALUE)
            {
                MISCVARS->numgibs--;
                SD_PlaySoundRTP(GIBSOUND,ob->x,ob->y);
            }
            NewState(ob,&s_gibsdone1);
        }
        return;
    }
    else if ((ob->z < -64) && (sky == 0))
    {
        ob->momentumz = 1; //any positive value will do
        ob->z = -64;

    }


    ob->momentumz += ob->temp3;
    ActorMovement(ob);


    if (!BATTLEMODE)
    {
        dx = abs(ob->x - PLAYER[0]->x);
        dy = abs(ob->y - PLAYER[0]->y);
        dz = abs(ob->z - PLAYER[0]->z);

#if (SHAREWARE==0)
        if ((ob->flags & FL_EYEBALL) && (dx < 0x20000) && (dy < 0x20000) &&
                (dz < 64) && (GameRandomNumber("eye chance",0) < 15) &&
                (SCREENEYE == NULL) && (locplayerstate->weapon != wp_dog)
           )
#else
        if ((ob->flags & FL_EYEBALL) && (dx < 0x20000) && (dy < 0x20000) &&
                (dz < 64) && (GameRandomNumber("eye chance",0) < 15) &&
                (SCREENEYE == NULL)
           )
#endif
            SpawnScreenEye(ob);
    }

//MoveActor(ob);
//if ((!ob->momentumx) && (!ob->momentumy))
    //goto done;


}



void DropItemInEmptyTile(int item,int tilex,int tiley)
{
    int stilex = tilex;
    int stiley = tiley;

    FindEmptyTile(&stilex,&stiley);
    SpawnStatic(stilex,stiley,item,9);
    LASTSTAT->flags |= FL_ABP;
    MakeStatActive(LASTSTAT);

}

extern boolean enableExtraPistolDrops;

void KillActor(objtype*ob)
{   int ocl;

    ocl = ob->obclass;


//GivePoints(starthitpoints[gamestate.difficulty][ob->obclass]*5);
    if ((ocl == highguardobj) &&
            (GameRandomNumber("Drop mp40 chance",0) < 25))
    {
        DropItemInEmptyTile(stat_mp40,ob->tilex,ob->tiley);
    }

    else if ((ocl == blitzguardobj) && (ob->temp3))
    {
        DropItemInEmptyTile(ob->temp3,ob->tilex,ob->tiley);
        LASTSTAT->ammo = ob->temp2;
    }
    else if ((ocl >= lowguardobj && ocl != highguardobj && ocl <= blitzguardobj) &&
            (GameRandomNumber("Drop extra pistol chance", 0) < 25) && enableExtraPistolDrops)
    {
        DropItemInEmptyTile(stat_twopistol, ob->tilex, ob->tiley);
    }
    

    if (actorat[ob->tilex][ob->tiley] == (void*)ob)
        actorat[ob->tilex][ob->tiley] = NULL;

    gamestate.killcount++;
    ob->flags &= ~FL_SHOOTABLE;
    ob->flags &= ~FL_BLOCK;
    ob->flags |= FL_NEVERMARK;
#if (SHAREWARE == 0)
    if (ocl == b_darksnakeobj)
    {   if (ob == SNAKEHEAD)
        {   SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj);
            new->temp1 = 7000;
            new->flags |= FL_ABP;
            EXPLOSIONS = new;
            NewState(ob,&s_darkmonkheaddie1);
            MakeActive(new);
            ob->dirchoosetime = 0;

        }
        else
        {   objtype *temp;

            SNAKEEND = (objtype*)(ob->target);
            SNAKEEND->whatever = NULL;
            NewState(ob,&s_explosion1);
            for(temp=SNAKEHEAD; temp; temp = (objtype*)(temp->whatever))
                temp->speed += 0x500;
        }
    }
    else
#endif
    {   ob->whatever = NULL;
        if (ob->obclass!=playerobj)
            ob->target   = NULL;
    }

    if ((ob->flags & FL_KEYACTOR) && (ocl!=playerobj) && (ocl != blitzguardobj))
    {   MISCVARS->KEYACTORSLEFT --;
        if (!MISCVARS->KEYACTORSLEFT)
        {   SpawnNewObj(ob->tilex,ob->tiley,&s_timekeeper,inertobj);
            new->flags |= FL_ABP;
            MakeActive(new);
        }
    }

}


void T_End(objtype *ob)
{
    if (ob->ticcount)
        return;

    if (MAPSPOT(0,5,2) == LASTLEVELVALUE)
        playstate = ex_gameover;
    else
        playstate = ex_bossdied;

}



void T_Convert(objtype*ob)
{
    if (ob->ticcount)
        return;

    if (ob->obclass == playerobj)
    {
        if (ob->state == &s_vaporized8)
        {
            T_SpawnSoul(ob);
            NewState(ob,&s_voidwait);
        }
        else if (ob->state == &s_skeleton48)
        {
            playertype *pstate;

            M_LINKSTATE(ob,pstate);
            if ((pstate->falling == true) ||
                    (!ob->momentumz)
               )
                NewState(ob,&s_ashwait);
            else
                CheckPlayerSpecials(ob);


        }
    }
    else
    {
        if (ob->state == &s_vaporized8)
            T_SpawnSoul(ob);
        else if (ob->state == &s_skeleton48)
            TurnActorIntoSprite(ob);
    }
}

void TurnActorIntoSprite(objtype *ob)
{   statobj_t*temp;
    objtype *tactor;


    if (!firstemptystat)
        temp = (statobj_t*)Z_LevelMalloc(sizeof(statobj_t),PU_LEVELSTRUCT,NULL);

    else
    {   temp = lastemptystat;
        //SoftError("\nfree actor available");
        RemoveFromFreeStaticList(lastemptystat);
    }


    if (temp)
    {
        if ((ob->obclass == blitzguardobj) &&
                ((ob->flags & FL_PLEADING) || (ob->flags & FL_UNDEAD))
           )
            MISCVARS->NUMBEGGINGKEVINS = 0;


        if (ob->obclass == roboguardobj)
        {   for(tactor=firstareaactor[ob->areanumber]; tactor; tactor=tactor->nextinarea)
            {   if (tactor == ob)
                    continue;
                if (tactor->obclass != ob->obclass)
                    continue;

                if (tactor->flags & FL_DYING)
                    continue;
                if (!tactor->state->think)
                    NewState(tactor,UPDATE_STATES[PATH][tactor->obclass-lowguardobj]);

            }
        }

        memset(temp,0,sizeof(*temp));
        temp->shapenum = ob->shapenum;
        temp->linked_to = -1;
        temp->whichstat = statcount ++;
        SetFinePosition(temp,ob->x,ob->y);
        temp->areanumber = MAPSPOT(temp->tilex,temp->tiley,0)-AREATILE;
//	if ((temp->areanumbers<=0) || (temp->areanumber>NUMAREAS))
        //		  Error ("Sprite at x=%ld y=%ld type=%ld has an illegal areanumber\n",tilex,tiley,mtype);

        temp->visspot = &spotvis[temp->tilex][temp->tiley];
        temp->which = SPRITE;
        temp->itemnumber = -1;
        temp->flags = FL_DEADBODY;
        if (ob->flags & FL_COLORED)
        {   playertype *pstate;

            M_LINKSTATE(ob,pstate);
            temp->flags |= FL_COLORED;
            temp->hitpoints = pstate->uniformcolor;
        }
        temp->z = ob->z;
        AddStatic(temp);
//	sprites[temp->tilex][temp->tiley] = temp;

        if (areabyplayer[temp->areanumber])
        {   temp->flags |= FL_ABP;
            MakeStatActive(temp);
        }
        if (ob->state != &s_guts12)
            actorat[ob->tilex][ob->tiley] = temp;
        ob->state = NULL; // say goodbye actor
    }
    else
        Error("Z_LevelMalloc failed in TurnActorIntoSprite!");

}


void T_Blood(objtype*ob)
{
    if (ob->dirchoosetime)
    {   ob->dirchoosetime --;
        return;
    }

    ob->dirchoosetime = 35 + (GameRandomNumber("blood time",0) % 20);
    NewState(ob,&s_deadblood1);

}




void ActorDeath(objtype*ob)
{
#if (SHAREWARE == 0)
    if (ob->obclass == b_heinrichobj)
    {
        KillActor(ob);
        ob->temp1 = ob->dirchoosetime = 5;//10; // init. spin counter for heinrich
        ob->temp3 = 7; //number of times to stay at fast spin
        ob->temp2 = ob->dir; //temp2 holds orig. dir.
    }

    else if (ob->obclass == b_robobossobj)
    {
        objtype *wheels,*head;


        head = (objtype*)(ob->whatever);
        wheels = (objtype*)(ob->target);
        head->flags &= ~(FL_HEAD|FL_SHOOTABLE|FL_BLOCK);
        head->temp2 = 5;
        head->flags |= (FL_NOFRICTION|FL_CRAZY);
//      head->obclass = inertobj;
        //RemoveObj(wheels);   // remove wheels
        KillActor(ob);
        ob->whatever = head;
        ob->target = wheels;
        //ob->temp1 = 25;
        //ob->shapeoffset = 0;
        SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj);
        new->temp1 = 18;
        new->flags |= FL_ABP;
        MakeActive(new);
        //ob->state = NULL;
        NewState(ob,&s_NMEdeathbuildup);
    }
    else
#endif
        if ((ob->state == ob->state->next) &&
                (ob->flags & FL_DYING)
           )
        {
            KillActor(ob);
            TurnActorIntoSprite(ob);
            if (LASTSTAT->z < nominalheight)
            {
                if ((!IsPlatform(LASTSTAT->tilex,LASTSTAT->tiley)) &&
                        (DiskAt(LASTSTAT->tilex,LASTSTAT->tiley) == NULL)
                   )
                {
                    SpawnParticles(ob,GUTS,10 + gamestate.difficulty);
                    RemoveStatic(LASTSTAT);
                }
            }
            /*
            else if ((GameRandomNumber("blood spray",0) < 300) && areabyplayer[ob->areanumber])
               {ob->shapeoffset = 0;
               ob->temp2 = ob->temp3 = 0;
               ob->temp1 = 10;
               NewState(ob,&s_deadblood1);
               return;
               }
               */
        }
}




void BeginPostPainAction(objtype *ob)
{

    if ((ob->obclass == strikeguardobj) &&
            (ob->target == (void*)PLAYER[0])
       )
    {   //ob->target = NULL;
        if (LOW_VIOLENCE_DEATH_IS_SET(ob))
            RESET_DEATH_SHAPEOFFSET(ob);

        if (GameRandomNumber("T_Collide",0) < 128)
            NewState(ob,&s_strikerollright1);
        else
            NewState(ob,&s_strikerollleft1);

        SelectRollDir(ob);

        if (ob->dirchoosetime)
        {
            SD_PlaySoundRTP(SD_STRIKEROLLSND,ob->x,ob->y);
            return;
        }

    }

    if (LOW_VIOLENCE_PAIN_IS_SET(ob))
        RESET_PAIN_SHAPEOFFSET(ob);

    if (ob->obclass < roboguardobj)
        ob->flags &= ~FL_NOFRICTION;

    if (
        (ob->obclass == blitzguardobj) &&
        (gamestate.violence == vl_excessive) &&
        (GameRandomNumber("blitzplead",0) < 128) &&
        (MISCVARS->NUMBEGGINGKEVINS == 0) &&
        (ob->flags & FL_TARGET) &&

        (ob->hitpoints < (starthitpoints[gamestate.difficulty][ob->obclass] >> 1)) &&
        (ob->momentumz == 0) &&
        (!(ob->flags & FL_UNDEAD))
    )

    {
        NewState(ob,&s_blitzplead1);
        MISCVARS->NUMBEGGINGKEVINS = 1;
        ob->momentumx = ob->momentumy = 0;
        ob->flags |= FL_PLEADING;
        ob->flags &= ~FL_TARGET;
        ob->dirchoosetime = 165;
        ob->hitpoints = 1;
    }

    else
    {
        NewState(ob,M_S(CHASE));
        ob->targettilex = ob->targettiley = 0;
        ob->dirchoosetime = 0;
    }

}



void T_Collide(objtype*ob)
{
    if (!(ob->flags & FL_SHOOTABLE))
        return;

    ActorMovement(ob);

    if (ob->state == NULL)
        return;

    if (ob->ticcount)
        return;

    if (ob->hitpoints <= 0)
    {

        if ((ob->soundhandle == -1) &&
                (!ob->ticcount) &&
                (ob->state->next->tictime == 0)
           )
        {
            ob->soundhandle = SD_PlaySoundRTP(ACTORTHUDSND,ob->x,ob->y);

        }

        if (ob->momentumx || ob->momentumy || ob->momentumz)
            return;

        ActorDeath(ob);
        return;
    }

    BeginPostPainAction(ob);

}



/*
=========================================================================
=
=                Special Blitzguard Functions
=
=========================================================================
*/


/*
=================
=
=   T_Plead
=
=================
*/



void T_Plead(objtype*ob)
{
    int handle;

    ActorMovement(ob);
    if (ob->dirchoosetime)
    {
        if (!(ob->dirchoosetime & 31))
        {
            int random = GameRandomNumber("blitz plead sound",0);
            if (random < 80)
                SD_PlaySoundRTP(SD_BLITZPLEADSND,ob->x,ob->y);
            else if (random < 160)
                SD_PlaySoundRTP(SD_BLITZPLEAD1SND,ob->x,ob->y);
            else
                SD_PlaySoundRTP(SD_BLITZPLEAD2SND,ob->x,ob->y);
        }
        ob->dirchoosetime --;
        return;
    }
    ob->hitpoints = (starthitpoints[gamestate.difficulty][blitzguardobj]>>1);
//ob->flags |= FL_DYING;
    ob->flags |= FL_UNDEAD;
    SET_DEATH_SHAPEOFFSET(ob);
    NewState(ob,&s_blitzfakedie1);
    ob->flags &= ~FL_PLEADING;
    ob->dirchoosetime = (GameRandomNumber("blitz fake time",0) >> 2) + 70;
    handle = SD_PlaySoundRTP(SD_BLITZOUCHSND,ob->x,ob->y);
    SD_SetSoundPitch (handle,-500);
    ob->temp1 = 0;
    ob->temp1 = (GameRandomNumber("blitz visible rise",0) < 60);

}



/*
=================
=
=   T_ReallyDead
=
=================
*/


void T_ReallyDead(objtype *ob)
{
    ActorMovement(ob);
    if ((!ob->ticcount) && (LOW_VIOLENCE_DEATH_SHOULD_BE_SET(ob)))
        SET_DEATH_SHAPEOFFSET(ob);


}



/*
=================
=
=   T_PlayDead
=
=================
*/



void T_PlayDead(objtype *ob)
{
    int dangle;

    ob->flags &= ~FL_DYING;

    ActorMovement(ob);
    if (ob->dirchoosetime)
    {
        ob->dirchoosetime--;
        return;
    }

    dangle = abs(player->angle - AngleBetween(player,ob));
    if (dangle > ANGLES/2)
        dangle = ANGLES - dangle;

    if (ob->temp1 || (dangle > ANGLES/4))
    {
        if (LOW_VIOLENCE_DEATH_IS_SET(ob))
            RESET_DEATH_SHAPEOFFSET(ob);
        ob->temp1 = 0;

        NewState(ob,&s_blitzrise2);

    }

}


void AdjustAngle(int maxadjust, short int *currangle,int targetangle)
{
    int dangle,i,magangle;

    for(i=0; i<maxadjust; i++)
    {
        dangle = *currangle - targetangle;

        if (dangle)
        {
            magangle = abs(dangle);
            if (magangle > (ANGLES/2))
            {
                if (dangle > 0)
                    (*currangle) ++;
                else
                    (*currangle) --;
            }
            else
            {
                if (dangle > 0)
                    (*currangle) --;
                else
                    (*currangle) ++;
            }
            Fix(*currangle);
        }
    }
}


void ResolveMinimumDistance(objtype *heatseeker, objtype *potential_target,
                            int *currmin)
{
    int currdist,angle,magangle;


    currdist = FindDistance((heatseeker->x-potential_target->x),
                            (heatseeker->y-potential_target->y));
    angle = AngleBetween(heatseeker,potential_target);


    if (heatseeker->obclass != p_godballobj)
    {
        magangle = abs(heatseeker->angle - angle);
        if (magangle > VANG180)
            magangle = ANGLES - magangle;
        if (magangle > ANGLESDIV8)
            return;
    }

    if (currdist > LOOKAHEAD)
        return;

    if (currdist < (*currmin))
    {
        *currmin = currdist;
        heatseeker->target = potential_target;
    }
}




void HeatSeek(objtype*ob)
{   int        xydist;

    int        mindist;
    objtype*   tactor;
    objtype*   owner;
    statobj_t* tstat;
    int        angle,dz,yzangle,adjust;
    int        dx,dy;


    owner=(objtype *)ob->whatever;

    if (ob->dirchoosetime)
        ob->dirchoosetime --;

    else
    {
        if (!ob->target)
        {   mindist = 0x7fffffff;

            for (tactor=firstactive; tactor; tactor=tactor->nextactive)
            {
                if (tactor == owner)
                    continue;

                if (tactor->flags & FL_HEAD)
                    continue;

                if ((tactor == ob) ||
                        (!(tactor->flags & FL_SHOOTABLE)) ||
                        (tactor->flags & FL_DYING))
                    continue;

                if (!CheckLine(ob,tactor,SIGHT))
                    continue;

                if ((tactor->obclass == bladeobj) || (tactor->obclass == NMEsaucerobj))
                    continue;


                ResolveMinimumDistance(ob,tactor,&mindist);

            }

            if (ob->obclass != p_godballobj)
            {
                for(tstat=firstactivestat; tstat; tstat=tstat->nextactive)
                {
                    if (!(tstat->flags & FL_HEAT))
                        continue;

                    if (!CheckLine(ob,tstat,SHOOT))
                        continue;

                    ResolveMinimumDistance(ob,(objtype*)tstat,&mindist);
                }
            }


            if (!ob->target)
                ob->dirchoosetime = 5;
        }

        else //if (ob->target != owner)
        {
            tactor = (objtype*)ob->target;

            if ((!tactor->nextactive) && (!tactor->prevactive))
            {
                ob->target = NULL;
                return;
            }
            dx = tactor->x - ob->x;
            dy = ob->y - tactor->y;
            dz = ob->z - tactor->z;
            //xydist = FixedSqrtHP((FixedMul(dx,dx) + FixedMul(dy,dy))>>16);
            xydist = FindDistance(dx,dy);
            angle = atan2_appx(dx,dy);
            adjust = (ob->obclass == p_godballobj)?(GODHAPT):(HAAPT);
            AdjustAngle(adjust,&(ob->angle),angle);
            ob->dir = angletodir[ob->angle];
            ob->momentumx = FixedMul(ob->speed,costable[ob->angle]);
            ob->momentumy = -FixedMul(ob->speed,sintable[ob->angle]);

            yzangle = atan2_appx(xydist,(dz<<10));
            adjust = (ob->obclass == p_godballobj)?(GODVAPT):(VAAPT);
            AdjustAngle(adjust,&(ob->yzangle),yzangle);
            ob->momentumz = -(FixedMul(ob->speed,sintable[ob->yzangle]));

        }
    }

}


void Stagger(objtype*ob)
{
    int randadj;


    randadj = (int)(GameRandomNumber("Stagger",1) >> 3);

    if (!ob->dirchoosetime)
    {
        ob->momentumz = ob->temp1 + (RandomSign() << 12);
        ob->dirchoosetime = 6;
    }
    else
        ob->dirchoosetime --;

    if ((ob->z + (ob->momentumz >> 10)) > (maxheight-12))
        ob->momentumz = -ob->momentumz;
    else if ((ob->z < 5) && (!sky))
        ob->z = 5;

    ob->angle += (RandomSign()*randadj);
    Fix(ob->angle);
    ob->momentumx = FixedMul(ob->speed,costable[ob->angle]);
    ob->momentumy = -FixedMul(ob->speed,sintable[ob->angle]);
    ob->dir = angletodir[ob->angle];

}




void SpawnSplit(objtype *ob,int angle)
{
    Fix(angle);
    SpawnMissile(ob,p_heatseekobj,ob->speed,angle,&s_p_bazooka1,0x4000);
    new->momentumz = ob->momentumz;
    new->whatever = ob->whatever;

}



void SplitMissile(objtype*ob)
{

    SD_PlaySoundRTP(SD_SPLITSND,ob->x,ob->y);
    if (ob->soundhandle != -1)
        SD_StopSound(ob->soundhandle);

    SpawnSplit(ob,ob->angle + ANGLES/12);
    SpawnSplit(ob,ob->angle - ANGLES/12);

    if (missobj == ob)
    {
        if (GameRandomNumber("split misscam",0)<128)
            missobj = LASTACTOR;
        else
            missobj = LASTACTOR->prev;
    }

    ob->state=NULL; // get rid of current missile

}




void SpawnMissileSmoke(objtype *ob)
{
    if (!ValidAreanumber(AREANUMBER(ob->tilex,ob->tiley)))
        return;

    SpawnStatic(ob->tilex,ob->tiley,stat_missmoke,-1);
    LASTSTAT->flags |= FL_ABP;
    MakeStatActive(LASTSTAT);
    SetFinePosition(LASTSTAT,ob->x,ob->y);
    LASTSTAT->z = ob->z+3;

}


void T_Projectile (objtype *ob)
{
    objtype *owner;
    playertype * pstate;

    owner = (objtype*)(ob->whatever);

    if (owner->obclass == playerobj)
        M_LINKSTATE(owner,pstate);

    if ((ob->soundhandle != -1) &&
            (!(oldpolltime & 7))
       )
        SD_PanRTP(ob->soundhandle,ob->x,ob->y);
#if (SHAREWARE == 0)
    if (ob->obclass == h_mineobj)
    {
        if (!ob->dirchoosetime)
        {
            NewState(ob,&s_grexplosion1);
            SD_PlaySoundRTP(SD_KRISTMINEHITSND,ob->x,ob->y);
        }
        else
            ob->dirchoosetime --;
        if (
            (ob->state == &s_mine1) &&
            (!ob->ticcount)
        )
            SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);

    }
#endif


    if (!ob->ticcount)
    {
        if (ob->state == &s_p_grenade)
            ob->momentumz += (GRAVITY>>6);
        else if
        (ob->state == &s_grenade_fall6)
        {
            NewState(ob,&s_explosion1);
            return;
        }
    }




    if (ob->obclass == p_drunkmissileobj)
    {
        if (ob->temp3 > 0)
        {
            ob->temp3 --;
            Stagger(ob);
        }
        else
        {
            if (ob->target == NULL)
                Stagger(ob);
            HeatSeek(ob);
        }

    }

    else if (ob->temp1 == NME_DRUNKTYPE)

        Stagger(ob);


    else if ((ob->obclass == p_heatseekobj) ||
             (ob->obclass == dm_heatseekobj) ||
             (ob->temp1 == NME_HEATSEEKINGTYPE) ||
             (ob->obclass == p_godballobj)
            )
        HeatSeek(ob);


    else if
    ((ob->obclass == p_splitmissileobj) &&
            (!pstate->buttonstate[bt_attack])
    )
    {
        SplitMissile(ob);
        return;
    }




    if ((!BATTLEMODE) &&
            (!(ob->ticcount & 7)) &&
            (ob->obclass != p_firewallobj) &&
            (ob->obclass != p_kesobj) &&
            (ob->obclass != p_godballobj) &&
            ((ob->obclass >= p_bazookaobj) ||  (ob->obclass == missileobj))
       )// &&

        SpawnMissileSmoke(ob);

    MissileMovement(ob);

    if (ob->obclass == grenadeobj)

    {
        if (ob->temp1 > 0)
            ob->temp1 -= ob->speed;
        else if (!(ob->flags & FL_DONE))
        {
            NewState(ob,&s_grenade_fall1);
            ob->flags |= FL_DONE;
        }
    }
}



void StartFirewall(objtype*ob, int newz)
{
    objtype *owner = (objtype*)(ob->whatever);

    MISCVARS->firespawned = 0;
    owner->temp1 = 0;
    SpawnFirewall(ob,2,newz);
    if (missobj == ob)
        missobj = LASTACTOR;
    NewState(ob,&s_megaremove);

}



void MissileMovement(objtype*ob)
{   int tryx, tryy,tryz;

    tryx = ob->x + ob->momentumx;
    tryy = ob->y + ob->momentumy;
    tryz = ob->z + (ob->momentumz >> 10);
    if (!MissileTryMove (ob, tryx, tryy, tryz))
        return;
    ob->z += (ob->momentumz >> 10);
    MoveActor(ob);
}



#define DetonateMissile(x,y) \
{MissileHit(x,y);             \
 return false;                 \
}                                \

#define QuietDetonate(ob)            \
   {                                 \
   if (ob->soundhandle != -1)        \
      SD_StopSound(ob->soundhandle); \
   if (ob == missobj)                \
      missobj = NULL;                \
   NewState(ob,&s_megaremove);       \
   return false;                     \
   }

extern boolean ricochetingRocketsEnabled;

boolean MissileTryMove(objtype*ob,int tryx,int tryy,int tryz)
{
    int             tilexlow,tileylow,tilexhigh,tileyhigh,x,y,
                    trytilex,trytiley,dx,dy,dzt,dztp1,radius,
                    sprrad,actrad,tcl,ocl,oldsrad,area,zdist,
                    wall;

    objtype         *temp;
    wall_t          *tempwall;
    doorobj_t       *tempdoor;
    int             doorn;
    statobj_t       *tempstat;
    boolean         areatried[NUMAREAS] = {0};

    sprrad = 0x4500;
    actrad = ACTORSIZE+0x2800;
    ocl = ob->obclass;
    radius = PROJSIZE-0x2200;
    if (ocl==wallfireobj)
        radius-=0x3000;
    trytilex = (tryx >> TILESHIFT);
    trytiley = (tryy >> TILESHIFT);


    if (IsWindow(trytilex,trytiley) || (!InMapBounds((tryx>>16),(tryy>>16))))
    {
        QuietDetonate(ob);
    }



    /*
    */
//==== ceiling/floor clipping =================//

    if (tryz < -30)
    {   if ((sky==0) || (ocl == inertobj))
        {
            DetonateMissile(ob,NULL);
        }
        /*
        else
           return true;
           */
        /*
        else
           {
           NewState(ob,&s_megaremove);
           if (missobj == ob)
              missobj = NULL;

           return false;
           }
         */
    }


    if (tryz > (maxheight-10))
    {
        if ((ocl == p_firewallobj) && (!(ob->flags & FL_ISFIRE)))
            StartFirewall(ob,nominalheight);
        else
            MissileHit(ob,NULL);
        return false;
    }

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

    sprrad = PROJSIZE+0x1000;


    tilexlow = (int)((tryx-radius) >>TILESHIFT);
    tileylow = (int)((tryy-radius) >>TILESHIFT);

    tilexhigh = (int)((tryx+radius) >>TILESHIFT);
    tileyhigh = (int)((tryy+radius) >>TILESHIFT);

    oldsrad = sprrad;


    if (ocl == inertobj)
        goto walls;

    area = ob->areanumber;
    areatried[area] = true;
actors:
    for(temp=firstareaactor[area]; temp; temp=temp->nextinarea)
    {
        if (temp == ob)
            continue;

        dx = abs(tryx - temp->x);
        dy = abs(tryy - temp->y);
        if ((dx > actrad) || (dy > actrad))
            continue;

        if (temp->flags & FL_HEAD)
            continue;

        if ((!(temp->flags & FL_BLOCK)) || (temp->flags & FL_DYING))
            continue;

        tcl = temp->obclass;


        zdist = 32;
        dzt = abs(tryz - temp->z);

        if ((tcl == playerobj) && (temp->flags & FL_DOGMODE))
        {
            dzt = abs(tryz - (temp->z + 30));
            zdist = 10;
        }
        else if (tcl == diskobj)
        {
            zdist = 50;
        }

        if (dzt > zdist)
            continue;



        //if ((ocl==wallfireobj) && (tcl==playerobj) && (temp->flags&FL_DOGMODE) && (dz>15))
        // continue;

        //if ((ocl==playerobj) &&
        //  (ob->whatever == (void*)temp))
        //continue;

        if (ob->whatever && (ob->whatever == temp->whatever))// &&
            //      (ocl == tcl))// missiles with same owner
            // go through each other
            continue;

        if (!(ob->flags & FL_ISFIRE))
        {

            int random;

            if (tcl != b_darkmonkobj)
            {
                MissileHit(ob,temp);
                ob->target = NULL;
                if (tcl == wallfireobj)
                    MissileHit(temp,NULL);
                if (((ocl == p_kesobj) || (ocl == p_godballobj)) && (tcl < pillarobj))
                    continue;
                else
                    return false;
            }
            random = GameRandomNumber("empower darkmonk",0);
#if (SHAREWARE == 0)

            if (ocl == p_kesobj)
            {
                NewState(ob,&s_megaremove);
                //ob->state = NULL;
                temp->hitpoints += (((random>>3)+140)<<1);
                CAP_OSCUROS_HITPOINTS(temp);
            }
            else if (ocl == p_firebombobj)
            {
                NewState(ob,&s_explosion1);
                temp->hitpoints += (((random>>3)+70)<<1);
                CAP_OSCUROS_HITPOINTS(temp);

                ob->target = NULL;
            }
            else
            {
                NewState(ob,&s_explosion1);
                temp->hitpoints += (((random>>3)+50)<<1);
                CAP_OSCUROS_HITPOINTS(temp);

                ob->target = NULL;
            }
            temp->temp3 = ocl;
            temp->speed = 5*SPDPATROL;
            NewState(temp,&s_darkmonkreact);
#endif
            return false;
        }

        else if (tcl < roboguardobj)
        {   if ((temp->z == nominalheight) &&
                    (!((tcl == playerobj) && ((temp->flags & FL_GODMODE) || (temp->flags & FL_DOGMODE) || godmode))))
            {
                if (tcl == playerobj)
                {
                    playertype *pstate;
                    objtype *owner = (objtype*)(ob->whatever);

                    M_LINKSTATE(temp,pstate);
                    if (temp->flags & FL_AV)
                    {   pstate->protectiontime = 1;
                        if (temp==player)
                            GM_UpdateBonus (pstate->protectiontime, false);
                        continue;
                    }


                    //temp->flags &= ~FL_COLORED;
                    pstate->health = 0;
                    pstate->weapon = -1;
                    if (owner->obclass == playerobj)
                        BATTLE_PlayerKilledPlayer(battle_kill_with_missile,owner->dirchoosetime,temp->dirchoosetime);


                }


                temp->flags |= FL_SKELETON;
                temp->hitpoints = 0;
                Collision(temp,ob->whatever,-temp->momentumx,-temp->momentumy);
            }
            continue;
        }



        else
        {
            NewState(ob,&s_megaremove);
            ob->target = NULL;
#if (SHAREWARE == 0)
            if (tcl == b_darkmonkobj)
                NewState(temp,&s_darkmonkfspark1);
#endif
        }

        return false;

    }


    for (y=tileylow; y<=tileyhigh; y++)
        for (x=tilexlow; x<=tilexhigh; x++)
        {
            area = AREANUMBER(x,y);
            if (ValidAreanumber(area) && (areatried[area]==false))
            {
                areatried[area] = true;
                goto actors;
            }
        }


    /******************* WALLS/PWALLS *****************************************/

walls:

    for (y=tileylow; y<=tileyhigh; y++)
        for (x=tilexlow; x<=tilexhigh; x++)
        {


            tempwall = (wall_t*)actorat[x][y];
            wall=tilemap[x][y];

            if (tempwall && M_ISWALL(tempwall) && (tempwall->which!=MWALL))
            {   if (ocl == h_mineobj)
                {
                    if (WallCheck(ob->x-ob->momentumx, tryy))
                    {   ob->momentumx = -ob->momentumx;
                        continue;
                    }
                    else if (WallCheck(tryx, ob->y-ob->momentumy))
                    {   ob->momentumy = -ob->momentumy;
                        continue;
                    }
                }
                //richocheting rockets stuff
                if ((ocl == p_bazookaobj ||
                        ocl == p_firebombobj ||
                        ocl == p_heatseekobj ||
                        ocl == p_drunkmissileobj ||
                        ocl == p_firewallobj ||
                        ocl == p_splitmissileobj ||
                        ocl == p_kesobj) && ricochetingRocketsEnabled)
                {
                    if (WallCheck(ob->x-ob->momentumx, tryy))
                    {
                        ob->momentumx = -ob->momentumx;
                        int rand;

                        rand = RandomNumber("Spawn Ricochet Sound",0);
                        if (rand < 80)
                            SD_PlaySoundRTP(SD_RICOCHET1SND,ob->x,ob->y);
                        else if (rand < 160)
                            SD_PlaySoundRTP(SD_RICOCHET2SND,ob->x,ob->y);
                        else
                            SD_PlaySoundRTP(SD_RICOCHET3SND,ob->x,ob->y);
                        continue;
                    }
                    else if (WallCheck(tryx, ob->y-ob->momentumy))
                    {
                        ob->momentumy = -ob->momentumy;
                        int rand;

                        rand = RandomNumber("Spawn Ricochet Sound",0);
                        if (rand < 80)
                            SD_PlaySoundRTP(SD_RICOCHET1SND,ob->x,ob->y);
                        else if (rand < 160)
                            SD_PlaySoundRTP(SD_RICOCHET2SND,ob->x,ob->y);
                        else
                            SD_PlaySoundRTP(SD_RICOCHET3SND,ob->x,ob->y);
                        
                        continue;
                    }
                    else
                    {
                        DetonateMissile(ob,tempwall);
                    }
                }
                else {
                    DetonateMissile(ob,tempwall);
                }
                //MissileHit(ob,tempwall);
                //return false;
            }


            tempstat = sprites[x][y];
            sprrad = oldsrad;

            if (tempstat &&
                    ((tempstat->flags & FL_SHOOTABLE) || (tempstat->flags & FL_BLOCK)))
            {

                if ((tempstat->itemnumber >= stat_bcolumn) &&
                        (tempstat->itemnumber <= stat_icolumn))
                    sprrad += 0x5000;

                dx = tryx - tempstat->x;
                if ((dx < -sprrad) || (dx > sprrad))
                    continue;

                dy = tryy - tempstat->y;
                if ((dy < -sprrad) || (dy > sprrad))
                    continue;

//#define MINSTATZDIFF 60

                dzt = abs(ob->z - tempstat->z);
                dztp1 = abs(tryz - tempstat->z);
                /*
                  if (ocl == p_firewallobj)// && (dztp1 <= MINSTATZDIFF))
                     {
                     if (ob->flags & FL_ISFIRE)
                        {
                        int cz = (ob->z - tempstat->z + MINSTATZDIFF);

                        if ((cz >= -MAXSTEPHEIGHT) && (cz <= 0))
                           {
                           ob->z = tempstat->z - MINSTATZDIFF;
                           tryz = ob->z + (ob->momentumz >> 16);
                           dzt = MINSTATZDIFF;
                           }
                        }

                     if ((dztp1 >= MINSTATZDIFF) || (dzt >= MINSTATZDIFF))
                        continue;

                     if (!(ob->flags & FL_ISFIRE))
                        {
                        StartFirewall(ob,tempstat->z - MINSTATZDIFF);
                        return false;
                        }
                     }


                  else*/
                {
                    if (dztp1 > 50)
                        continue;

                    DetonateMissile(ob,tempstat);
                }
                //MissileHit(ob,tempstat);
                //return false;
            }
        }


//mwalls:



    if (M_ISMWALL(trytilex,trytiley))
    {
        maskedwallobj_t * mw;

        wall=tilemap[trytilex][trytiley];
        //tempwall = (wall_t*)actorat[trytilex][trytiley];


        mw=maskobjlist[wall&0x3ff];

        if (!(mw->flags&MW_BLOCKING))
        {

            if ((levelheight > 1) &&
                    (((!(mw->flags & MW_ABOVEPASSABLE)) && (tryz <= 32)) ||
                     ((!(mw->flags & MW_MIDDLEPASSABLE)) && (tryz > 25) && (tryz < nominalheight-32)) ||
                     ((!(mw->flags & MW_BOTTOMPASSABLE)) && (tryz > maxheight - 74))
                    )
               )
                DetonateMissile(ob,NULL);

        }

        else if (mw->flags&MW_SHOOTABLE)
        {
            if (ob->z >= maxheight-64)
            {
                UpdateMaskedWall(tilemap[trytilex][trytiley]&0x3ff);
            }
            else
                DetonateMissile(ob,NULL);

        }

        else
            DetonateMissile(ob,NULL);
        //MissileHit(ob,tempwall);
        //return false;
    }



    /******************* DOOR STUFF ******************************************/


    else if (M_ISDOOR(trytilex,trytiley))
    {
        doorn = tilemap[trytilex][trytiley];
        tempdoor = doorobjlist[doorn&0x3ff];
        if (tempdoor->position>=0x8000)
        {
            if (ob->z>maxheight-64)
                return true;
        }
        DetonateMissile(ob,tempdoor);
    }

    return true;


}



void SpawnFirewall(objtype*ob,int which,int newz)
{   int i,j,count,newx,newy;
    objtype* owner;
    wall_t*tempwall;
    statetype* frame;
    int offset;

    owner = (objtype*)(ob->whatever);

    if ((owner->temp1 < 2) && (MISCVARS->firespawned < 14))
    {   for(i=0; i<=which; i++)
        {
            GetNewActor ();
            MakeActive(new);
            MISCVARS->firespawned ++;
            new->obclass = p_firewallobj;
            new->which = ACTOR;
            new->areanumber = ob->areanumber;
            MakeLastInArea(new);
            offset = 0x6000;
            if (!which)
                new->temp1 = ob->temp1;
            else if (i==1)
                new->temp1 = ob->angle + ANGLES/4;
            else if (i==2)
                new->temp1 = ob->angle - ANGLES/4;
            else
            {
                new->temp1 = 0;
                offset = 0;
                new->flags |= FL_DONE;
            }

            Fix(new->temp1);

            new->speed = 0x8000;
            new->angle = ob->angle;
            ParseMomentum(new,new->angle);
            newx = ob->x + FixedMul(offset,costable[new->temp1]);
            newy = ob->y - FixedMul(offset,sintable[new->temp1]);
            SetFinePosition(new,newx,newy);
            SetVisiblePosition(new,newx,newy);
            new->whatever = ob->whatever;
            new->dirchoosetime = 2;
            new->flags |= (FL_NEVERMARK|FL_ABP|FL_NOFRICTION);
            count = (int)(GameRandomNumber("SpawFireWall",0) & 15);

            for(frame = &s_fireunit1,j=0; j<count; frame = frame->next,j++);

            NewState(new,frame);
            new->flags |= FL_ISFIRE;
            //SD_Play(SD_EXPL);
            tempwall = (wall_t*)actorat[new->tilex][new->tiley];
            new->z = newz;
            if (tempwall && M_ISWALL(tempwall))
            {
                SetFinePosition(new,ob->x,ob->y);
                SetVisiblePosition(new,ob->x,ob->y);
                owner->temp1++;
            }
        }
    }

}


void T_Firethink(objtype*ob)
{

    if (ob->dirchoosetime)
        ob->dirchoosetime--;
    else if (!(ob->flags & FL_DONE))
    {
        SpawnFirewall(ob,0,ob->z);
        ob->flags |= FL_DONE;
    }

    MissileMovement(ob);

}



void ResolveRide(objtype *ob)
{
    objtype *ride = (objtype*)(ob->whatever);

    if (M_ISACTOR(ride) && (ob->obclass != playerobj))
    {
        if (ob->flags & FL_RIDING)
        {
            int dx,dy;

            dx = ob->x - ride->x;
            dy = ob->y - ride->y;
            if ((dx < -MINACTORDIST) || (dx > MINACTORDIST) ||
                    (dy < -MINACTORDIST) || (dy > MINACTORDIST) )
            {
                ride->whatever = NULL;
                ob->whatever = NULL;
                ob->flags &= ~FL_RIDING;
            }
        }
    }

}




void MoveActor(objtype*ob)
{
    int oldarea,newarea,
        tilex,tiley,oldtilex,oldtiley;

    ResolveRide(ob);

    oldtilex = ob->tilex;
    oldtiley = ob->tiley;

    SetFinePosition(ob,ob->x+ob->momentumx,ob->y+ob->momentumy);
    /*
    if (ob->state == &s_explosion1)
     Error("moving explosion"); */

    if ((ob->obclass == playerobj) || (ob->flags & FL_NOFRICTION) || (ob->state->think == T_Collide) ||
            (ob->obclass == b_heinrichobj) || (ob->obclass == h_mineobj))

        SetVisiblePosition(ob,ob->x,ob->y);



    if (ob->obclass == inertobj)
        return;

    if ((ob->obclass == b_darksnakeobj) && (ob != SNAKEHEAD))
    {
        oldarea = ob->areanumber;
        newarea = SNAKEHEAD->areanumber;
        if (oldarea != newarea)
        {
            RemoveFromArea(ob);
            ob->areanumber = newarea;
            MakeLastInArea(ob);
        }
        return;

    }


    oldarea = ob->areanumber;
    newarea = AREANUMBER(ob->tilex,ob->tiley);
    if (!(ob->flags & (FL_NONMARK|FL_NEVERMARK)))
    {
        if ((oldtilex != ob->tilex) || (oldtiley != ob->tiley))
        {
            if (actorat[oldtilex][oldtiley] == (void*)ob)
                actorat[oldtilex][oldtiley] = NULL;
            if (actorat[ob->tilex][ob->tiley])
            {
                objtype* temp;

                temp = (objtype*)actorat[ob->tilex][ob->tiley];
                if (temp->which != SPRITE)
                    actorat[ob->tilex][ob->tiley] = ob;
            }
            else
                actorat[ob->tilex][ob->tiley] = ob;
        }
    }


#define CheckAdjacentArea(x,y)        \
   {                                  \
   if (InMapBounds(x,y))              \
      {                               \
      temparea = AREANUMBER(x,y);     \
      if (ValidAreanumber(temparea))  \
         newarea = temparea;          \
      }                               \
   }


    if (!ValidAreanumber(newarea)) //find empty tile
    {
        int temparea;
        tilex = ob->tilex;
        tiley = ob->tiley;

        CheckAdjacentArea(tilex+1,tiley);
        CheckAdjacentArea(tilex-1,tiley);
        CheckAdjacentArea(tilex,tiley+1);
        CheckAdjacentArea(tilex,tiley-1);

    }
    //Error("new area invalid for actor %d, class %d",
    //	  ob-&objlist[0],ob->obclass);


//======================  swap in linked lists =====================
    if (oldarea != newarea)
    {
        RemoveFromArea(ob);
        ob->areanumber = newarea;
        MakeLastInArea(ob);
    }
}



void SpawnPushColumn(int tilex,int tiley,int which,int dir, int linked)
{
    if (which==0)
    {
        SpawnNewObj(tilex,tiley,&s_pushcolumn1,pillarobj);
//     for (i=0;i<(levelheight-1);i++)
//        SpawnStatic(tilex,tiley,stat_bcolumn,-(i<<6));
    }
    else if (which==1)
    {
        SpawnNewObj(tilex,tiley,&s_pushcolumn2,pillarobj);
//     for (i=0;i<(levelheight-1);i++)
//        SpawnStatic(tilex,tiley,stat_gcolumn,-(i<<6));
    }
    else
    {
        SpawnNewObj(tilex,tiley,&s_pushcolumn3,pillarobj);
//     for (i=0;i<(levelheight-1);i++)
//        SpawnStatic(tilex,tiley,stat_icolumn,-(i<<6));
    }
    PreCacheActor(pillarobj,which);

    gamestate.secrettotal++;
    new->speed = PILLARMOM;
    new->temp1 = 0x20000;
    new->temp2 = linked;
    new->flags |= (FL_BLOCK|FL_NOFRICTION);
    new->flags &= ~FL_SHOOTABLE;
    new->flags |= FL_HEIGHTFLIPPABLE;
    new->dir = dir;
    if (dir != nodir)
        ParseMomentum(new,dirangle8[new->dir]);


}



void SpawnWallfire(int tilex, int tiley, int dir)
{   int offx,offy;


    GetNewActor();
    new->speed = 0x2000;
    SetTilePosition(new,tilex,tiley);
    SetVisiblePosition(new,new->x,new->y);
    new->obclass = wallfireobj;
    new->dir = dir*2;
    new->flags |= (FL_BLOCK|FL_NOFRICTION|FL_NEVERMARK);
    new->flags &= ~FL_SHOOTABLE;
    new->which = ACTOR;
    new->angle = dirangle8[new->dir];
    offx = FixedMul(0x10000,costable[new->angle])>>TILESHIFT;
    offy = -(FixedMul(0x10000,sintable[new->angle])>>TILESHIFT);

    new->areanumber = MAPSPOT(new->tilex+offx,new->tiley+offy,0)-AREATILE;
    MakeLastInArea(new);

    NewState(new,&s_wallfireball);

}



void SpawnSneaky(int tilex,int tiley)
{

    SpawnNewObj(tilex,tiley,&s_sneakydown,lowguardobj);
    new->temp3 = SNEAKY;
    if (!loadedgame)
        gamestate.killtotal++;
    StandardEnemyInit(new,north>>1);

    PreCacheActor(lowguardobj,0);
}


void RespawnEluder(void)
{
    int rand,count;
    int nx,ny;

    rand = (GameRandomNumber("eluder respawn",0) % NUMSPAWNLOCATIONS);

    for(count=0; count < NUMSPAWNLOCATIONS; count++)
    {
        if (!actorat[SPAWNLOC[rand].x][SPAWNLOC[rand].y])
        {
            SpawnCollector(SPAWNLOC[rand].x,SPAWNLOC[rand].y);
            return;
        }
        rand= ((rand + 1) % NUMSPAWNLOCATIONS);

    }

    nx = SPAWNLOC[rand].x;
    ny = SPAWNLOC[rand].y;
    FindEmptyTile(&nx,&ny);
    SpawnCollector(nx,ny);
}

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

void SpawnCollector(int tilex, int tiley)
{
#if (SHAREWARE == 0)
    if ( dopefish==true )
    {
        SpawnNewObj(tilex,tiley,&s_scottwander1,collectorobj);
    }
    else
#endif
    {
        SpawnNewObj(tilex,tiley,&s_collectorwander1,collectorobj);
    }

    new->flags |= (FL_SHOOTABLE|FL_BLOCK|FL_NOFRICTION|FL_FULLLIGHT);
    new->hitpoints = 500;
    new->speed = 0x3000;
    new->dir = north;
    new->dirchoosetime = 175;
    new->z = PlatformHeight(tilex,tiley);
    if (new->z == -10)
        new->z = 0;
    if (areabyplayer[new->areanumber])
    {   new->flags |= FL_ABP;
        MakeActive(new);
    }

}



void SelectDoorDir(objtype*ob)
{   int dx,dy,actrad;
    dirtype dtry1,dtry2,tdir,olddir;

    dx= ob->targettilex - ob->x;
    dy= ob->y - ob->targettiley;
    olddir = ob->dir;
    if ((abs(dx) < 0x4000) && (abs(dy) < 0x4000))
    {   ZEROMOM;
        SetFinePosition(ob,ob->targettilex,ob->targettiley);
        SetVisiblePosition(ob,ob->x,ob->y);
        ParseMomentum(ob,dirangle8[ob->temp2]);
        ActorMovement(ob);
        ob->temp2 = 0;
        ob->temp1 = 20;
#if (SHAREWARE == 0)
        if ( dopefish==true )
        {
            NewState(ob,&s_scottwander1);
        }
        else
#endif
        {
            NewState(ob,&s_collectorwander1);
        }
        ob->targettilex = ob->targettiley = 0;
        ob->dirchoosetime = 165;
        return;
    }


    ZEROMOM;

    ParseMomentum(ob,atan2_appx(dx,dy));
    ActorMovement(ob);
    if (ob->momentumx || ob->momentumy)
        return;
    actrad = ACTORSIZE;
    dtry1=nodir;
    dtry2=nodir;

    if (dx> actrad)
        dtry1= east;
    else if (dx< -actrad)
        dtry1= west;
    if (dy> actrad)
        dtry2= north;
    else if (dy < -actrad)
        dtry2= south;


    if (abs(dy)>abs(dx))
    {   tdir=dtry1;
        dtry1=dtry2;
        dtry2=tdir;
    }


    if (dtry1 != nodir)
        M_CHECKDIR(ob,dtry1);

    if (dtry2 != nodir)
        M_CHECKDIR(ob,dtry2);

    if (dtry1 != nodir)
        M_CHECKDIR(ob,dirorder[dtry1][NEXT]);

    if (dtry2 != nodir)
        M_CHECKDIR(ob,dirorder[dtry2][NEXT]);

    for(tdir = dirorder[olddir][NEXT]; tdir != olddir; tdir = dirorder[tdir][NEXT])
        M_CHECKDIR(ob,tdir);
    ob->dir = olddir;

}


boolean EluderCaught(objtype*ob)
{
    objtype *temp;
    int dx,dy,dz;
    playertype *pstate;
    int dist = 0xc000;

    for(temp = PLAYER[0]; temp != PLAYER[numplayers-1]->next; temp=temp->next)
    {
#if (SHAREWARE == 0)
        if (temp->state != &s_doguse)
            continue;
#endif

        dx = M_ABS(temp->x - ob->x);
        if (dx > dist)
            continue;

        dy = M_ABS(temp->y - ob->y);
        if (dy > dist)
            continue;

        dz = M_ABS(temp->z - ob->z);
        if (dz > (dist>>10))
            continue;

        M_LINKSTATE(temp,pstate);
        //if (DOGSCRATCH.attackinfo[pstate->attackframe].attack == at_pulltrigger)
        {   BATTLE_CheckGameStatus(battle_caught_eluder,temp->dirchoosetime);
            SpawnNewObj(ob->tilex,ob->tiley,&s_itemspawn1,inertobj);
            new->flags |= FL_ABP;
            SetFinePosition(new,ob->x,ob->y);
            SetVisiblePosition(new,ob->x,ob->y);
            new->z = ob->z;
            SD_PlaySoundRTP(SD_GETBONUSSND,ob->x,ob->y);
            MakeActive(new);
            NewState(ob,&s_megaremove);
            return true;
        }

    }

    return false;
}


void T_CollectorFindDoor(objtype*ob)
{

    if (EluderCaught(ob))
        return;

    if (!(gamestate.TimeCount % 17))
        SD_PlaySoundRTP(SD_MONKGRABSND,ob->x,ob->y);


    if ((ob->z != nominalheight) && (!IsPlatform(ob->tilex,ob->tiley)))
        ZEROMOM;



    if (ob->dirchoosetime)
        ob->dirchoosetime --;
    else
    {
#if (SHAREWARE == 0)
        if ( dopefish==true )
        {
            NewState(ob,&s_scottwander1);
        }
        else
#endif
        {
            NewState(ob,&s_collectorwander1);
        }
        ob->dirchoosetime = 165;
        ob->targettilex = ob->targettiley = 0;
        return;
    }

    if (ob->temp1)
    {   int dx,dy;

        ob->temp1 --;
        ActorMovement(ob);
        dx = ob->targettilex-ob->x;
        dy = ob->targettiley-ob->y;
        if ((abs(dx) < 0x4000) && (abs(dy) < 0x4000))
        {   ZEROMOM;
            SetFinePosition(ob,ob->targettilex,ob->targettiley);
            SetVisiblePosition(ob,ob->x,ob->y);

            ParseMomentum(ob,dirangle8[ob->temp2]);
            ActorMovement(ob);
            ob->temp2 = 0;
            ob->temp1 = 35;
#if (SHAREWARE == 0)
            if ( dopefish==true )
            {
                NewState(ob,&s_scottwander1);
            }
            else
#endif
            {
                NewState(ob,&s_collectorwander1);
            }
            ob->targettilex = ob->targettiley = 0;
            ob->dirchoosetime = 165;
            return;
        }
        if (NOMOM)
            ob->temp1 = 0;
        return;
    }



    ob->temp1 = 5;

    if (ob->targettilex || ob->targettiley)
        SelectDoorDir(ob);

    else
    {   int i;
        doorobj_t* dptr;

//==========================================================================
#define SetCollectorTarget(xoffset,yoffset,newdir)                         \
   {                                                                       \
   ob->targettilex = ((dptr->tilex + (xoffset)) << TILESHIFT) + HALFGLOBAL1; \
   ob->targettiley = ((dptr->tiley + (yoffset)) << TILESHIFT) + HALFGLOBAL1; \
   ob->temp2 = newdir;                                                     \
   if (GameRandomNumber("collector door search",0) < 100)                  \
      return;                                                              \
   }
//==========================================================================

        for(i=0; i<doornum; i++)
        {   dptr = doorobjlist[i];
            if (dptr->vertical)
            {
                int area1 = AREANUMBER(dptr->tilex-1,dptr->tiley),
                    area2 = AREANUMBER(dptr->tilex+1,dptr->tiley);

                if (area1 == ob->areanumber)
                    SetCollectorTarget(-1,0,east)

                    else if (area2 == ob->areanumber)
                        SetCollectorTarget(1,0,west);

            }
            else
            {
                int area1 = AREANUMBER(dptr->tilex,dptr->tiley-1),
                    area2 = AREANUMBER(dptr->tilex,dptr->tiley+1);

                if (area1 == ob->areanumber)
                    SetCollectorTarget(0,-1,south)

                    else if (area2 == ob->areanumber)
                        SetCollectorTarget(0,1,north);

            }
        }
    }

}


void T_CollectorWander(objtype*ob)
{

    int newtilex,newtiley;

    if (EluderCaught(ob))
        return;

    if ((ob->z != nominalheight) && (!IsPlatform(ob->tilex,ob->tiley)))
        ZEROMOM;

    if (!(gamestate.TimeCount & 15))//%17
        SD_PlaySoundRTP(SD_MONKGRABSND,ob->x,ob->y);

    if (ob->dirchoosetime)
    {
        if (doornum > 0)
            ob->dirchoosetime --;
    }

    else
    {
#if (SHAREWARE == 0)
        if ( dopefish==true )
        {
            NewState(ob,&s_scottwanderdoor1);
        }
        else
#endif
        {
            NewState(ob,&s_collectorfdoor1);
        }
        ob->temp1 = 0;
        ob->dirchoosetime = 165;
        ob->targettilex = ob->targettiley = 0;
        return;
    }

    if (ob->temp1) // temp1 holds direction time
        ob->temp1 --;
    else
    {
        dirtype bestdir,tempdir;

        bestdir = angletodir[GameRandomNumber("collector theta",0) << 3];

        for(tempdir = bestdir; tempdir != dirorder[bestdir][PREV]; tempdir = dirorder[tempdir][NEXT])
        {
            ParseMomentum(ob,dirangle8[tempdir]);
            newtilex = ((ob->x + ob->momentumx)>>16);
            newtiley = ((ob->y + ob->momentumy)>>16);
            if (IsWindow(newtilex,newtiley))
                continue;
            ActorMovement(ob);
            if (ob->momentumx || ob->momentumy)
            {
                ob->temp1 = (GameRandomNumber("collector choose time",0) >> 2);
                return;
            }
        }

    }

    newtilex = ((ob->x + ob->momentumx)>>16);
    newtiley = ((ob->y + ob->momentumy)>>16);


    if (IsWindow(newtilex,newtiley))
    {
        ob->temp1 = 0;
        return;
    }


    ActorMovement(ob);

    if (NOMOM)
        ob->temp1 = 0;
}





boolean CheckDoor(objtype *ob,doorobj_t * door,int trytilex,int trytiley)
{   boolean doorok=false;


    switch(ob->dir)
    {
    case north:
        if ((ob->tiley == (door->tiley + 1)) && (trytilex == ob->tilex))
            doorok = true;
        break;

    case east:
        if ((ob->tilex == (door->tilex - 1)) &&	(trytiley == ob->tiley))
            doorok = true;
        break;

    case south:
        if ((ob->tiley == (door->tiley - 1)) &&	(trytilex == ob->tilex))
            doorok = true;
        break;

    case west:
        if ((ob->tilex == (door->tilex + 1)) &&	(trytiley == ob->tiley))
            doorok = true;
        break;
    default:
        ;
    }


    if (doorok)
    {   SetTilePosition(ob,ob->tilex,ob->tiley);
        SetVisiblePosition(ob,ob->x,ob->y);
        return true;
    }
    return false;
}


boolean WallCheck(int tryx,int tryy)
{   int tilexlow,tilexhigh,tileylow,tileyhigh,y,x;

    tilexlow = (int)((tryx -PLAYERSIZE) >>TILESHIFT);
    tileylow = (int)((tryy -PLAYERSIZE) >>TILESHIFT);

    tilexhigh = (int)((tryx + PLAYERSIZE) >>TILESHIFT);
    tileyhigh = (int)((tryy + PLAYERSIZE) >>TILESHIFT);


    for (y=tileylow; y<=tileyhigh; y++)
        for (x=tilexlow; x<=tilexhigh; x++)
        {   //tempwall = (wall_t*)actorat[x][y];
            //if (tempwall && M_ISWALL(tempwall))
            if (tilemap[x][y])
                return false;
        }

    return true;

}


boolean QuickSpaceCheck(objtype*ob,int tryx, int tryy)
{   int xlow,xhigh,ylow,yhigh,x,y,dx,dy;
    objtype* temp;

    xlow = (int)((tryx-ACTORSIZE) >>TILESHIFT);
    ylow = (int)((tryy-ACTORSIZE) >>TILESHIFT);

    xhigh = (int)((tryx+ACTORSIZE) >>TILESHIFT);
    yhigh = (int)((tryy+ACTORSIZE) >>TILESHIFT);
    /******************* WALLS/PWALLS *****************************************/

    for (y=ylow; y<=yhigh; y++)
        for (x=xlow; x<=xhigh; x++)
        {   temp = (objtype*)actorat[x][y];
            if ((temp && (temp->which != ACTOR)) ||
                    (sprites[x][y] && (sprites[x][y]->flags & FL_BLOCK))

                    || tilemap[x][y])
                return false;
        }

    for(temp=firstareaactor[ob->areanumber]; temp; temp=temp->nextinarea)
    {   if (temp == ob)
            continue;
        if ((temp->flags & FL_NONMARK) || (temp->flags & FL_NEVERMARK))
            continue;
        dx = tryx - temp->x;
        if ((dx < -MINACTORDIST) || (dx > MINACTORDIST))
            continue;
        dy = tryy - temp->y;
        if ((dy < -MINACTORDIST) || (dy > MINACTORDIST))
            continue;
        if (ob->whatever == (void*)temp)
            continue;
        if (temp->whatever == ob->whatever)
            continue;
        return false;
    }

    return true;


}


//=========================================================================
//
//                    ACTOR TRY MOVE MADNESS
//
//=========================================================================

typedef enum
{
    NO_MOVEMENT,
    Z_MOVEMENT_ONLY,
    OK_TO_CONTINUE

} movement_status;



//==================== Some ActorTryMove macros ==============================

#define CheckProximitySpecials(ob,temp)                             \
{                                                                   \
   if (ocl == b_heinrichobj)                                       \
      {                                                            \
      if (tcl == playerobj)                                        \
         {                                                         \
         playertype *pstate;                                      \
                                                                     \
         M_LINKSTATE(temp,pstate);                                \
         DamageThing(temp,5);                                     \
         temp->whatever = ob;                                     \
         temp->temp2 = COLUMNCRUSH;                               \
         pstate->heightoffset += 4;                               \
         if (pstate->heightoffset >= 30)                          \
            pstate->heightoffset = 30;                           \
         pstate->oldheightoffset = pstate->heightoffset;          \
         }                                                         \
      else                                                         \
         {                                                         \
         temp->momentumx = temp->momentumy = temp->momentumz = 0; \
         temp->hitpoints = 0;                                     \
         }                                                         \
      if (temp->hitpoints <= 0)                                    \
         temp->flags |= FL_HBM;                                     \
      Collision(temp,ob,0,0);                                      \
      continue;                                                    \
      }                                                             \
                                                                    \
   else if ((ocl == b_darksnakeobj) && (tcl == playerobj)) \
      {                                                     \
      DamageThing(temp,1);                                      \
      Collision(temp,ob,0,0);     \
      M_CheckPlayerKilled(temp);                                   \
      } \
        \
   if ((ocl == boulderobj) && (tcl >= lowguardobj) && (tcl < roboguardobj))\
      {temp->momentumx = temp->momentumy = temp->momentumz = 0;     \
       temp->hitpoints = 0;                                         \
       temp->flags |= FL_HBM;                                       \
       Collision(temp,ob,0,0);                                      \
       SD_PlaySoundRTP(SD_ACTORSQUISHSND,temp->x,temp->y);          \
       continue;                                                    \
      }                                                             \
                                                                    \
   if (pusher && (ocl != tcl) && (!(temp->flags & FL_DYING))  &&    \
       (tcl < roboguardobj)                                         \
      )                                                             \
      {if ((!ob->ticcount) && (ocl != collectorobj) && (ocl != diskobj))\
          DamageThing(temp,5);                                      \
                                                                    \
       if (tcl == playerobj)                                        \
         temp->flags |= FL_PUSHED;                                  \
       Collision(temp,ob,ob->momentumx-temp->momentumx,ob->momentumy-temp->momentumy);\
       M_CheckPlayerKilled(temp);                                   \
       continue;                                                    \
      }                                                             \
                                                                    \
   if (bouncer)                                                    \
      {ob->momentumx = -ob->momentumx;                              \
       continue;                                                    \
      }                                                             \
   }



#define CheckStepping(ob,step,minzdiff)                         \
{                                                               \
 int cz = (ob->z - step->z + minzdiff);                         \
                                                                \
 if ((cz >= -MAXSTEPHEIGHT) && (cz <= MAXSTEPHEIGHT))           \
      {if ((ob->obclass == playerobj) && (ob->temp2 == 0) &&    \
           (ob->z != (step->z - minzdiff))                      \
          )                                                     \
         {                                                      \
         playertype *pstate;                                    \
                                                                \
         M_LINKSTATE(ob,pstate);                                \
                                                                \
         pstate->heightoffset = pstate->oldheightoffset + cz;   \
         ob->temp2 = (cz >= 0)?(STEPUP):(STEPDOWN);             \
         }                                                      \
      ob->z = step->z - minzdiff;                               \
      tryz = ob->z + (ob->momentumz >> 16);                     \
      dzt = minzdiff;                                           \
      }                                                         \
}                                                               \




//============ Players crushing other players =====================

void BattleCrushCheck(objtype *ob,objtype *listrover)                              \
{
    if ((ob->obclass == playerobj) && (listrover->obclass == playerobj))
    {
        playertype * pstate;

        M_LINKSTATE(listrover,pstate);
        if (pstate->health <= 0)
            BATTLE_PlayerKilledPlayer(battle_kill_by_crushing,ob->dirchoosetime,
                                      listrover->dirchoosetime);
    }
}


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





movement_status CheckOtherActors(objtype*ob,int tryx,int tryy,int tryz)
{
    objtype *listrover;
    int area;
    int op;
    int areatried[NUMAREAS]= {0};
    int tilexlow,tilexhigh,tileylow,tileyhigh;
    int radius,actrad,oldrad;
    boolean bouncer,pusher,thinkingactor,zstoppable,ACTORSTOP;
    int dzt,dztp1,checkz;
    int x,y,dx,dy;
    int ocl,tcl;

    ocl = ob->obclass;

    actrad = MINACTORDIST;//ACTORSIZE+0x2800;
    pusher =  ((ocl == wallopobj) || (ocl == pillarobj) ||
               (ocl == roboguardobj) || (ocl == collectorobj) ||
               (ocl == boulderobj) || (ocl == diskobj)
              );

    thinkingactor = ((ocl != playerobj) && (ob->state->think != T_Collide) &&
                     (ocl < roboguardobj)
                    );

    zstoppable = (!(ob->flags & FL_DYING));
    bouncer = ((ocl == playerobj) && (ob->flags & FL_ELASTO));
    radius = ACTORSIZE;


    if (ocl != playerobj)
    {
        //actrad = MINACTORDIST;
        //if ((ob->dir == nodir) && (ocl != b_robobossobj) &&
        //   (ocl != wallopobj) && (ocl != roboguardobj) && (ocl != diskobj)
        // )
        // Error("ob called with nodir");
        if (ocl == boulderobj)
            radius += (ACTORSIZE/4);
        else if (ocl == b_darksnakeobj)
            radius -= 6000;
        else if (ocl == inertobj)
            radius -= 0x2000;
    }

    tilexlow = (int)((tryx-radius) >>TILESHIFT);
    tileylow = (int)((tryy-radius) >>TILESHIFT);

    tilexhigh = (int)((tryx+radius) >>TILESHIFT);
    tileyhigh = (int)((tryy+radius) >>TILESHIFT);

    area = ob->areanumber;
    areatried[area] = 1;
    ACTORSTOP = false;
    oldrad = actrad;

actors:
    for(listrover=firstareaactor[area]; listrover; listrover=listrover->nextinarea)
    {
        actrad = oldrad;

        if (listrover == ob)
            continue;


        tcl = listrover->obclass;

        if ((tcl == b_darksnakeobj) && (listrover != SNAKEHEAD))
            continue;

        if (((tcl == bladeobj) || (tcl == firejetobj)) && thinkingactor)

            actrad += 0x3000;


        dx = tryx - listrover->x;
        if ((dx < -actrad) || (dx > actrad))
            continue;

        dy = tryy - listrover->y;
        if ((dy < -actrad) || (dy > actrad))
            continue;


        if ((ocl == b_darksnakeobj) && (tcl == ocl))
            continue;

        if ((tcl == springobj) && (listrover->state->condition & SF_UP) &&
                (listrover->temp1!=3) && (levelheight > 1) &&
                (abs(listrover->z - ob->z) < 5) && (!ob->momentumz)
           )
        {
            {
                op = (FixedMul((int)GRAVITY,(int)((ob->z-10)<<16))<<1);
                ob->momentumz = -FixedSqrtHP(op);
            }
            SD_PlaySoundRTP(SD_SPRINGBOARDSND,listrover->x,listrover->y);
            NewState(listrover,&s_spring2);

        }



        if ((tcl == firejetobj) && (ob->z < listrover->z))
            continue;

        if ((!(listrover->flags & FL_BLOCK)) && (actrad == oldrad)) // if not blocking
            // and actor not avoiding
            // env. danger
            continue;



        if (tcl == crushcolobj)
            checkz = listrover->temp2;
        else
            checkz = listrover->z;

#define  MINACTORZDIFF 58

        dzt = abs(checkz - ob->z);
        dztp1 = abs(checkz - tryz);

        if ((tcl == diskobj) && (dztp1 <= MINACTORZDIFF) && zstoppable &&
                (ocl != b_heinrichobj)
           )
            CheckStepping(ob,listrover,MINACTORZDIFF);

        dztp1 = abs(checkz - tryz);



        if ((dzt > (MINACTORZDIFF - 25)) && (dzt < MINACTORZDIFF) &&
                (dztp1 < MINACTORZDIFF) && (tcl < roboguardobj) &&
                (ocl < roboguardobj)
           )
        {
            int rdx,rdy;

            rdx = abs(ob->x - listrover->x);
            rdy = abs(ob->y - listrover->y);
            if ((rdx < actrad) && (rdy < actrad))
            {
                if (ob->z > listrover->z)
                    listrover->z = ob->z - MINACTORZDIFF;
                else
                    ob->z = listrover->z - MINACTORZDIFF;

                dzt = dztp1 = MINACTORZDIFF;
            }

        }


        if ((dztp1 >= MINACTORZDIFF) || (dzt >= MINACTORZDIFF))
        {
            if ((dzt >= MINACTORZDIFF) && (dztp1 <= MINACTORZDIFF) &&
                    zstoppable
               )
            {   //ob->momentumz = 0;
                if (ob->z < listrover->z)
                {
                    ob->z = listrover->z - MINACTORZDIFF;
                    ob->momentumz = 0;
                }
                else
                    ob->momentumz = 2*GRAVITY;
                if ((listrover->z > ob->z) && (tcl < roboguardobj) && (ocl < roboguardobj) &&
                        (!(listrover->flags & FL_DYING))
                   )

                {
                    DamageThing(listrover,5);
                    BattleCrushCheck(ob,listrover);
                    Collision(listrover,ob,listrover->momentumx,listrover->momentumy);
                    /*
                    if ((ocl == playerobj) && (listrover->flags & FL_DYING))
                       GivePoints(starthitpoints[gamestate.difficulty][tcl]);
                    */
                }

                if (((tcl == bladeobj) || (tcl == diskobj)) && (ob->z < listrover->z))
                {
                    ob->whatever = listrover;
                    if (listrover->flags & FL_ACTIVE)
                        ob->flags |= FL_RIDING;
                    listrover->whatever = ob;
                }

                //Debug("\nplayerz %d, tryz %d momz zeroed at %d, clearances %d and %d",
                //   ob->z,tryz,listrover->z-64 + (listrover->momentumz >> 16),dzt,dztp1);

            }

            continue;
        }


        CheckProximitySpecials(ob,listrover);


        ACTORSTOP = true;
        if (!ob->momentumz)
            return NO_MOVEMENT;

    }


    for (y=tileylow; y<=tileyhigh; y++)
        for (x=tilexlow; x<=tilexhigh; x++)
        {
            area = AREANUMBER(x,y);
            if (ValidAreanumber(area) && (areatried[area]==0))
            {
                areatried[area] = 1;
                goto actors;
            }
        }



    if (ACTORSTOP==true)
        return Z_MOVEMENT_ONLY;

    return OK_TO_CONTINUE;
}



movement_status CheckRegularWalls(objtype *ob,int tryx,int tryy,int tryz)
{
    int tilexlow,tilexhigh,tileylow,tileyhigh,x,y,radius;
    classtype ocl;
    boolean ISPLAYER=false;

    ocl = ob->obclass;
    tryz=tryz;


    if (ocl != playerobj)
    {
        radius = ACTORSIZE - 0x1000;
        //actrad = MINACTORDIST;
        //if ((ob->dir == nodir) && (ocl != b_robobossobj) &&
        //   (ocl != wallopobj) && (ocl != roboguardobj) && (ocl != diskobj)
        //  )
        // Error("ob called with nodir");
        if (ocl == boulderobj)
            radius += (ACTORSIZE/4);
        else if (ocl == b_darksnakeobj)
            radius -= 6000;
        else if (ocl == inertobj)
            radius -= 0x2000;
    }

    else
    {
        radius = PLAYERSIZE;
        ISPLAYER = true;
    }


    tilexlow = (int)((tryx-radius) >>TILESHIFT);
    tileylow = (int)((tryy-radius) >>TILESHIFT);

    tilexhigh = (int)((tryx+radius) >>TILESHIFT);
    tileyhigh = (int)((tryy+radius) >>TILESHIFT);

    for (y=tileylow; y<=tileyhigh; y++)
        for (x=tilexlow; x<=tilexhigh; x++)
        {
            wall_t          *tempwall;

            tempwall = (wall_t*)actorat[x][y];
            if (tempwall)
            {
                if (tempwall->which==WALL)// && IsWindow(x,y)==false)
                {
                    if (ocl == boulderobj)
                    {
#if (SHAREWARE == 0)
                        NewState(ob,&s_bouldersink1);
#endif
                        SD_PlaySoundRTP(SD_BOULDERHITSND,ob->x,ob->y);
                    }
                    else if (ISPLAYER && (!(ob->flags & FL_DYING)) &&
                             (!(ob->flags & FL_AV)) &&
                             (tempwall->flags & FL_W_DAMAGE))
                    {
                        DamageThing(ob,5);
                        Collision(ob,(objtype*)tempwall,0,0);
                        M_CheckPlayerKilled(ob);
                        SD_PlaySoundRTP(SD_PLAYERBURNEDSND,ob->x,ob->y);
                    }

                    //return false;
                    if ((ocl == inertobj) &&
                            (ob->dirchoosetime == GIBVALUE) &&

                            (((ob->tilex - x) == 0) != ((ob->tiley - y) == 0)) &&
                            (ob->z > -28)

                       )
                    {
                        //                SoftError ("Blood Dripping oldpolltime=%ld\n",oldpolltime);
                        BloodDrip(ob,x,y);
                        return NO_MOVEMENT;
                    }

                    if (!ob->momentumz)
                        return NO_MOVEMENT;
                    else// if (ocl != inertobj)
                        return Z_MOVEMENT_ONLY;
                    //else
                    //goto doors;
                }

                else if (tempwall->which==PWALL)
                {
                    pwallobj_t*pw;
                    int dx,dy;

                    pw=(pwallobj_t *)tempwall;
                    dx = abs(pw->x - tryx);
                    if (dx > PWALLRAD+0x5000)
                        continue;

                    dy = abs(pw->y - tryy);
                    if (dy > PWALLRAD+0x5000)
                        continue;

                    return NO_MOVEMENT;
                }

            }
        }

    return OK_TO_CONTINUE;

}



movement_status CheckStaticObjects(objtype *ob,int tryx,int tryy,int tryz)
{
    int dx,dy,dzt,dztp1,x,y;
    statobj_t*tempstat;
    int sprrad,oldsrad,sprtrad;
    boolean specialstat=false,widestat=false,zstoppable;
    int sprxlow,sprxhigh,sprylow,spryhigh;
    boolean SPRSTOP;
    classtype ocl;

    ocl = ob->obclass;

    if (ocl != playerobj)
        sprtrad = ACTORSIZE - 0x1000;
    else
        sprtrad = ACTORSIZE - 0x1000 + 0x10000;



    sprxlow = (int)((tryx-sprtrad) >>TILESHIFT);
    sprylow = (int)((tryy-sprtrad) >>TILESHIFT);

    sprxhigh = (int)((tryx+sprtrad) >>TILESHIFT);
    spryhigh = (int)((tryy+sprtrad) >>TILESHIFT);

    if (sprxlow < 0)
        sprxlow = 0;

    if (sprxhigh > (MAPSIZE-1))
        sprxhigh = MAPSIZE-1;

    if (sprylow < 0)
        sprylow = 0;

    if (spryhigh > (MAPSIZE-1))
        spryhigh = MAPSIZE-1;

    SPRSTOP = false;
    sprrad = 0x4500;
    zstoppable = (!(ob->flags & FL_DYING));
    oldsrad = sprrad;

    for (y=sprylow; y<=spryhigh; y++)
        for (x=sprxlow; x<=sprxhigh; x++)
        {
            tempstat = sprites[x][y];
            sprrad = oldsrad;

            if (tempstat)
            {
                specialstat = ((tempstat->itemnumber == stat_heatgrate) ||
                               (tempstat->itemnumber == stat_pit)
                              );

                widestat = (((tempstat->itemnumber >= stat_bcolumn) &&
                             (tempstat->itemnumber <= stat_icolumn)) ||
                            (tempstat->itemnumber == stat_disk)
                           );

                if ((tempstat->flags & FL_BLOCK) || (specialstat==true))
                {
                    if ((specialstat==true) && (ocl !=playerobj) &&
                            (ob->state->think != T_Collide)
                       )
                        sprrad += 0x5000;

                    if (widestat==true)
                        sprrad += 0x3b00;


                    if ((tempstat->itemnumber == stat_ironbarrel) ||
                            (tempstat->itemnumber == stat_bonusbarrel))
                        sprrad += 0x5000;

                    dx = abs(tryx - tempstat->x);
                    if (dx > sprrad)
                        continue;
                    dy = abs(tryy - tempstat->y);
                    if (dy > sprrad)
                        continue;

#define MINSTATZDIFF 58

                    dzt = abs(ob->z - tempstat->z);
                    dztp1 = abs(tryz - tempstat->z);

                    if (widestat && (dztp1 <= MINSTATZDIFF) && zstoppable &&
                            (ocl != b_heinrichobj)
                       )
                        CheckStepping(ob,tempstat,MINSTATZDIFF);


                    dztp1 = abs(tryz - tempstat->z);

#if (SHAREWARE == 0)
                    if ((ocl == b_darksnakeobj) && (tempstat->itemnumber == stat_heatgrate))
                    {
                        if (ob->state->think == T_DarkSnakeChase)
                            NewState(ob,&s_darkmonkredhead);
                        else
                            NewState(ob,&s_darkmonkredlink);
                        ob->temp3 ++; // make shootable
                    }
#endif

                    if (specialstat==true)
                        continue;


                    if ((dztp1 >= MINSTATZDIFF) || (dzt >= MINSTATZDIFF))
                    {   if ((dzt >= MINSTATZDIFF) && (dztp1 <= MINSTATZDIFF) && zstoppable)
                        {   //ob->momentumz = 0;
                            if (ob->z <= tempstat->z)
                            {
                                ob->z = tempstat->z - MINSTATZDIFF;
                                ob->momentumz = 0;
                            }
                            else
                                ob->momentumz = 2*GRAVITY; // ((2*GRAVITY + GRAVITY) >> 16) = 1
                        }
                        continue;
                    }


                    if (ocl == boulderobj)
                    {   if ((tempstat->itemnumber < stat_bcolumn) ||
                                (tempstat->itemnumber > stat_icolumn)
                           )
                        {
                            tempstat->flags |= FL_SHOOTABLE;
                            DamageThing(tempstat,tempstat->hitpoints);
                            continue;
                        }
#if (SHAREWARE == 0)
                        else
                            NewState(ob,&s_bouldersink1);
#endif
                    }
                    //ob->momentumz=0;
                    //return false;
                    SPRSTOP=true;
                    if (!ob->momentumz)
                        return NO_MOVEMENT;
                }
            }
        }
    if (SPRSTOP == true)
        return Z_MOVEMENT_ONLY;

    return OK_TO_CONTINUE;

}





//============== Platform craziness ======================================


#define ClipHeight(ob,clipz)                                \
{  ob->momentumz = 0;                                       \
                                                            \
   if (ISPLAYER && (ob->z != clipz) && (ob->temp2 == 0))    \
      {playertype *pstate;                                  \
       int dz = ob->z - clipz;                              \
                                                            \
       M_LINKSTATE(ob,pstate);                              \
                                                            \
       pstate->heightoffset = pstate->oldheightoffset + dz; \
       ob->temp2 = (dz >= 0)?(STEPUP):(STEPDOWN);           \
      }                                                     \
                                                            \
   ob->z = clipz;                                           \
}

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

#define CheckSpecialGibMovement(blocker)            \
   {                                                \
   int centerx = ((trytilex<<16) + 0x8000);         \
   int centery = ((trytiley<<16) + 0x8000);         \
                                                    \
   if (blocker->vertical==false)                    \
      {                                             \
      int dyt = centery - ob->y;                    \
      int dytp1 = centery - tryy;                   \
                                                    \
      if ((abs(dytp1) > abs(dyt)) &&                \
         (SGN(dyt) == SGN(dytp1))                   \
         )                                          \
         return OK_TO_CONTINUE;                     \
                                                    \
      }                                             \
   else                                             \
      {                                             \
      int dxt = centerx - ob->x;                    \
      int dxtp1 = centerx - tryx;                   \
                                                    \
      if ((abs(dxtp1) > abs(dxt)) &&                \
         (SGN(dxt) == SGN(dxtp1))                   \
         )                                          \
         return OK_TO_CONTINUE;                     \
                                                    \
      }                                             \
   }


movement_status CheckMaskedWalls(objtype *ob,int tryx,int tryy,int tryz)
{
    int trytilex,trytiley;
    boolean MWALLSTOP;
    int ISPLAYER = (ob->obclass == playerobj);
    classtype ocl = ob->obclass;

    trytilex = (tryx >> TILESHIFT);
    trytiley = (tryy >> TILESHIFT);
    MWALLSTOP = false;
//for (y=tileylow;y<=tileyhigh;y++)
// for (x=tilexlow;x<=tilexhigh;x++)

    if (M_ISMWALL(trytilex,trytiley))
    {
        int wall = tilemap[trytilex][trytiley];
        maskedwallobj_t * mw;

        mw=maskobjlist[wall&0x3ff];

        if (ocl == inertobj)
            CheckSpecialGibMovement(mw);

        if (!(mw->flags&MW_BLOCKING))
        {
            if (mw->flags&MW_NONDOGBLOCKING)
            {
                if ((ocl==playerobj)&&(ob->flags&FL_DOGMODE))
                {
                    if (ob->z < nominalheight)
                    {
                        MWALLSTOP = true;
                        if (!ob->momentumz)
                            return NO_MOVEMENT;
                    }
                }
                else
                {
                    MWALLSTOP = true;
                    if (!ob->momentumz)
                        return NO_MOVEMENT;
                }
            }


            else
            {
                if (mw->flags & MW_ABOVEPASSABLE)
                {   if (mw->flags & MW_MIDDLEPASSABLE) // ==> not bottom
                    {   if (ob->z > LOWFALLCLIPZ+MAXSTEPHEIGHT)
                            MWALLSTOP = true;
                        else if (tryz >= LOWFALLCLIPZ)
                            ClipHeight(ob,LOWFALLCLIPZ);
                    }
                    else if (mw->flags & MW_BOTTOMPASSABLE)
                    {   if ((ob->z > HIGHFALLCLIPZ+MAXSTEPHEIGHT) && (ob->z < LOWRISECLIPZ))
                            MWALLSTOP = true;
                        else if (ob->z <= HIGHFALLCLIPZ+MAXSTEPHEIGHT)
                        {   if (tryz >= HIGHFALLCLIPZ)
                                ClipHeight(ob,HIGHFALLCLIPZ);
                        }
                        else if (tryz <= LOWRISECLIPZ)
                            ob->momentumz = 0;

                    }
                    else // ==> above only
                    {   if (ob->z > HIGHFALLCLIPZ+MAXSTEPHEIGHT)
                            MWALLSTOP = true;
                        else if (tryz >= HIGHFALLCLIPZ)
                            ClipHeight(ob,HIGHFALLCLIPZ);
                    }

                }
                else if (mw->flags & MW_MIDDLEPASSABLE)
                {   if (mw->flags & MW_BOTTOMPASSABLE) //==> not above passable
                    {   if (ob->z >= HIGHRISECLIPZ)
                        {   if (tryz <= HIGHRISECLIPZ)
                                ob->momentumz = 0;
                        }
                        else if (tryz <= HIGHRISECLIPZ)
                            MWALLSTOP = true;
                    }

                    else  //==> middle only
                    {   if (ob->z > LOWFALLCLIPZ+MAXSTEPHEIGHT)
                            MWALLSTOP = true;
                        else if (tryz >= LOWFALLCLIPZ)
                            ClipHeight(ob,LOWFALLCLIPZ)
                            else
                            {   if (ob->z >= HIGHRISECLIPZ)
                                {   if (tryz <= HIGHRISECLIPZ)
                                        ob->momentumz = 0;
                                }
                                else if (tryz <= HIGHRISECLIPZ)
                                    MWALLSTOP = true;
                            }
                    }

                }
                else // ==> bottompassable only
                {   if (ob->z < LOWRISECLIPZ)
                        MWALLSTOP = true;
                    else if (tryz < LOWRISECLIPZ)
                        ob->momentumz = 0;
                }

            }
        }
        else
        {
            if ( (mw->flags&MW_SHOOTABLE) &&
                    (mw->flags&MW_BLOCKINGCHANGES) &&
                    (ob->z >= nominalheight)

               )
            {
                int speed=FindDistance(ob->momentumx,ob->momentumy);
                if ((speed>0x2800) && (!(ob->flags & FL_DYING)))
                {
                    if (ob->obclass == playerobj)
                    {
                        DamageThing(ob,10);
                        Collision(ob,(objtype*)mw,0,0);
                    }
                    UpdateMaskedWall(wall&0x3ff);
                    if (tryz < nominalheight)
                        ob->momentumz = 0;
                }
                else
                {
                    MWALLSTOP = true;
                    if (!ob->momentumz)
                        return NO_MOVEMENT;
                }
            }
            else
            {
                MWALLSTOP = true;
                if (!ob->momentumz)
                    return NO_MOVEMENT;
            }
        }
    }

    if (MWALLSTOP == true)
        return Z_MOVEMENT_ONLY;

    return OK_TO_CONTINUE;

}



movement_status CheckDoors(objtype *ob,int tryx,int tryy,int tryz)
{
    int trytilex,trytiley;
    int ocl;


    trytilex = (tryx >> TILESHIFT);
    trytiley = (tryy >> TILESHIFT);
    ocl = ob->obclass;


    if (M_ISDOOR(trytilex,trytiley))
    {
        doorobj_t*tempdoor;
        int doorn;

        doorn = tilemap[trytilex][trytiley];

        tempdoor = doorobjlist[doorn&0x3ff];
        if (tempdoor->action == dr_open)
        {

            if (ob->z >= nominalheight)
            {
                if (tryz < nominalheight)
                    ob->momentumz = 0;
                return OK_TO_CONTINUE;
            }

        }
        if (ocl == inertobj)
        {
            CheckSpecialGibMovement(tempdoor);
        }
        else if ((ocl == playerobj) || (ocl > b_darksnakeobj))
            return NO_MOVEMENT;
        else if (ob->state->think != T_Collide)
        {

#define DOOR_LOCKED(door)                                      \
           (((door->flags & DF_ELEVLOCKED) || (door->lock)) && \
             (ob->obclass != b_darianobj)                       \
           )
#define GAS_DOOR(x,y) (MISCVARS->GASON && (MAPSPOT(x,y,1) == GASVALUE))

            if ((!DOOR_LOCKED(tempdoor)) &&
                    (!GAS_DOOR(trytilex,trytiley))
               )


                //)
            {
                ob->door_to_open = doorn&0x3ff;
                LinkedOpenDoor(ob->door_to_open);
                if (tempdoor->eindex != -1)
                    OperateElevatorDoor(doorn&0x3ff);
            }

            //if ((nstate = M_S(USE)) != NULL)
            //{ob->whatever = ob->state;
            //	NewState(ob,nstate);
            //	ob->flags |= FL_USE;
            // }
            return NO_MOVEMENT;
        }
        else
            return NO_MOVEMENT;
    }

    return OK_TO_CONTINUE;
}



boolean ActorTryMove(objtype*ob,int tryx, int tryy, int tryz)
{

    movement_status (*reduced_movement_check[3])(objtype*,int,int,int)=
    {
        CheckRegularWalls,
        CheckMaskedWalls,
        CheckDoors,
    };


    movement_status (*complete_movement_check[5])(objtype*,int,int,int)=
    {
        CheckOtherActors,
        CheckRegularWalls,
        CheckStaticObjects,
        CheckMaskedWalls,
        CheckDoors,
    };

    movement_status (**movement_function)(objtype*,int,int,int);
    movement_status movement_check_result;
    int             numcheckfunctions;
    int             i;
    boolean         xyblocked;




    if ((tryz < -30) && (sky==0) && (ob->obclass != inertobj))
    {
        ob->z = -28;
        ob->momentumz = 0;
        return false;
    }

    if ((!InMapBounds(tryx>>16,tryy>>16)) ||
            ((ob->obclass != playerobj) && (IsWindow((tryx>>16),(tryy>>16))))
       )
        return false;

    switch(ob->obclass)
    {
    case inertobj:
    case bladeobj:
    case firejetobj:
        movement_function = &reduced_movement_check[0];
        numcheckfunctions = 3;
        break;

    default:
        movement_function = &complete_movement_check[0];
        numcheckfunctions = 5;
        break;
    }


    for(xyblocked=false,i=0; i<numcheckfunctions; i++)
    {
        movement_check_result = movement_function[i](ob,tryx,tryy,tryz);
        if (movement_check_result == Z_MOVEMENT_ONLY)
            xyblocked = true;

        else if (movement_check_result == NO_MOVEMENT)
            return false;
    }

    if (xyblocked == true)
        return false;


    return true;

}


void PushWallMove(int num)
{
    int             tcl;
    pwallobj_t      *pwall;
    int             dx,dy;
    int             actrad;
    objtype         *temp;
    boolean         pushem;
    int             tryx,tryy,areanumber,trytilex,trytiley;


    pwall=pwallobjlist[num];

    actrad = PWALLRAD + 0x5000;
    tryx = (pwall->x + pwall->momentumx);
    tryy = (pwall->y + pwall->momentumy);
    trytilex = (tryx >> 16);
    trytiley = (tryy >> 16);

    areanumber = AREANUMBER(trytilex,trytiley);


    for(temp=firstareaactor[areanumber]; temp; temp=temp->nextinarea)
    {
        tcl = temp->obclass;

        if (temp->flags & FL_HEAD)  //ignore NME's head and wheels
            continue;

        if ((temp->flags & FL_DYING) || (!(temp->flags & FL_SHOOTABLE)))
            continue;

        if (tcl > b_darianobj)
            continue;


        dx = abs(tryx - temp->x);
        if (dx > actrad)
            continue;

        dy = abs(tryy - temp->y);
        if (dy > actrad)
            continue;

        if (pwall->flags&PW_DAMAGE)
        {   if (!((tcl == playerobj) && (temp->flags & FL_AV)))
                DamageThing(temp,5);

            Collision(temp,(objtype*)pwall,0,0);
            M_CheckPlayerKilled(temp);
            if (temp->flags & FL_DYING)
                return;

        }

        pushem=false;
        switch (pwall->dir)
        {

#define PWALLTOL (0xc000)

        case north:
            if ((temp->y<pwall->y) && (dx<PWALLTOL))
                pushem=true;
            break;
        case east:
            if ((temp->x>pwall->x) && (dy<PWALLTOL))
                pushem=true;
            break;
        case northeast:
            if ((temp->y<pwall->y) && (dx<PWALLTOL))
                pushem=true;
            else if ((temp->x>pwall->x) && (dy<PWALLTOL))
                pushem=true;
            break;
        case northwest:
            if ((temp->y<pwall->y) && (dx<PWALLTOL))
                pushem=true;
            else if ((temp->x<pwall->x) && (dy<PWALLTOL))
                pushem=true;
            break;
        case south:
            if ((temp->y>pwall->y) && (dx<PWALLTOL))
                pushem=true;
            break;
        case west:
            if ((temp->x<pwall->x) && (dy<PWALLTOL))
                pushem=true;
            break;
        case southeast:
            if ((temp->y>pwall->y) && (dx<PWALLTOL))
                pushem=true;
            else if ((temp->x>pwall->x) && (dy<PWALLTOL))
                pushem=true;
            break;
        case southwest:
            if ((temp->y>pwall->y) && (dx<PWALLTOL))
                pushem=true;
            else if ((temp->x<pwall->x) && (dy<PWALLTOL))
                pushem=true;
            break;
        default:
            //Error ("Pushwall #%d has an illegal direction %d \n",num,pwall->dir);
            break;
        }


        //if (!pushem)
        //continue;

        //temp->momentumx = temp->momentumy = 0;
        if (temp->obclass==playerobj)
            temp->flags|=FL_PUSHED;

        if (!pushem)
        {
            Collision(temp,(objtype*)pwall,-temp->momentumx,-temp->momentumy);
            continue;
        }

        if ((temp->obclass >= lowguardobj) && (temp->obclass < roboguardobj))
        {

            temp->momentumx = temp->momentumy = temp->momentumz = 0;
            temp->hitpoints = 0;

            if (gamestate.violence >= vl_high)
                temp->flags |= FL_HBM;
            Collision(temp,(objtype*)pwall,0,0);
            /*
            	 if (gamestate.violence < vl_high)
            		{if ((tstate = UPDATE_STATES[CRUSH][temp->obclass - lowguardobj])!=NULL)
            			 NewState(temp,tstate);

            		 else
            			 Error("\n\Null low-violence crush state in push wall crush, instance of %s",debugstr[temp->obclass]);
            		}
            	 else
            		{temp->shapeoffset = 0;
            		 //tactor->flags|=FL_HBM;
            		 NewState(temp,&s_guts1);
            		 //KillActor(temp);
            		}*/

            SD_PlaySoundRTP(SD_ACTORSQUISHSND,temp->x,temp->y);
        }
        else
        {
            if (!ActorTryMove(temp,temp->x + temp->momentumx,temp->y + temp->momentumy,
                              temp->z + (temp->momentumz >> 16))
               )
            {
                DamageThing(temp,30);
                if ((temp->obclass==playerobj) && (temp->hitpoints <= 0))
                    temp->target = (objtype *)pwall;
            }

            Collision(temp,(objtype*)pwall,pwall->momentumx-temp->momentumx,pwall->momentumy-temp->momentumy);
            M_CheckPlayerKilled(temp);
        }



    }
}

void ActorMovement (objtype *ob)
{   
    int tryx,tryy,tryz,limitok,max,friction,ocl;



    if ((ob->obclass == strikeguardobj) && (!(ob->flags & FL_DYING)) &&
            (gamestate.difficulty > gd_easy)
       )
    {
        AvoidPlayerMissile(ob);
        ob->flags &= ~FL_FULLLIGHT;
    }


    if ((!ob->momentumx) && (!ob->momentumy) && (!ob->momentumz))
    {   if (ob->flags & FL_RIDING)
            goto ride;
        else
            return;
    }


    limitok = 1;

    friction = ACTORFRICTION;
    if (!(ob->flags & FL_DYING))
        friction >>= 1;
    ocl = ob->obclass;
    if (ocl == playerobj)
    {
        playertype *pstate;

        M_LINKSTATE(ob,pstate);
        max = pstate->topspeed;
        friction = PLAYERFRICTION;
        if ((ob->temp2 == PITFALL) || (ob->temp2 == PITRISE))
            friction >>= 4;
    }


    else if (/*(ob->state->think != T_Collide) &&*/ (ocl != b_robobossobj) &&
            (ocl != boulderobj) && (ocl !=b_darkmonkobj) && (ocl != b_darksnakeobj) &&
            (ocl != inertobj) && (ocl != collectorobj))
        max = MAXMOVE;

    else
        limitok = 0;

    if (limitok)
    {   if (ocl == playerobj)
        {   int dist,scale;

            dist = FindDistance(ob->momentumx,ob->momentumy);
            if (dist > max)
            {   scale = FixedDiv2(max,dist);
                ob->momentumx = FixedMul(ob->momentumx,scale);
                ob->momentumy = FixedMul(ob->momentumy,scale);
            }
        }
        else
        {
            if (ob->momentumx > max)
                ob->momentumx = max;
            else if (ob->momentumx < -max)
                ob->momentumx = -max;
            if (ob->momentumy > max)
                ob->momentumy = max;
            else if (ob->momentumy < -max)
                ob->momentumy = -max;
        }

    }

    tryx = ob->x + ob->momentumx;
    tryy = ob->y + ob->momentumy;
    tryz = ob->z + (ob->momentumz >> 16);

    if (ocl != playerobj)
        ob->flags &= ~FL_STUCK;


    if (!ActorTryMove (ob, tryx, tryy, tryz))
    {   if (ocl == playerobj)
        {   if (!(ob->flags & FL_ELASTO))
                PlayerSlideMove (ob);
            else
            {   if (ActorTryMove(ob,tryx, ob->y-ob->momentumy,tryz))
                    ob->momentumy = -(ob->momentumy);
                else if (ActorTryMove(ob,ob->x-ob->momentumx,tryy,tryz))
                    ob->momentumx = -(ob->momentumx);
                else
                    ZEROMOM;
            }
        }

        else
        {   ZEROMOM;
            ob->flags |= FL_STUCK;
            return;
        }
    }

    MoveActor(ob);


ride:

    if (ob->flags & FL_RIDING)
    {
        objtype *ride = (objtype*)(ob->whatever);

        ob->z += (ride->momentumz >> 16);

        if ((ride->momentumx || ride->momentumy) &&
                ActorTryMove(ob,ob->x+ride->momentumx,ob->y+ride->momentumy,tryz)
           )
            SetFinePosition(ob,ob->x+ride->momentumx,ob->y+ride->momentumy);
    }


#define SLIDER(ob)  ((ob->flags & FL_NOFRICTION) && (ob->state->think != T_Collide))
#define AIRBORNE(ob) ((ob->obclass != playerobj) && (ob->z != nominalheight) &&\
                      (!IsPlatform(ob->tilex,ob->tiley)) && \
                      (DiskAt(ob->tilex,ob->tiley) == NULL) \
                     )

    if (SLIDER(ob) || AIRBORNE(ob))
        return;

    if ( (abs(ob->momentumx) < STOPSPEED) &&
            (abs(ob->momentumy) < STOPSPEED)
       )
    {
        ZEROMOM;
    }

    else if ((ob->flags & FL_DYING) && (ob->state == ob->state->next))
    {   ob->momentumx = FixedMul (ob->momentumx, DEADFRICTION);
        ob->momentumy = FixedMul (ob->momentumy, DEADFRICTION);

    }

    else
    {   ob->momentumx = FixedMul (ob->momentumx, friction);
        ob->momentumy = FixedMul (ob->momentumy, friction);

    }



}




void T_Guts(objtype*ob)
{   if (ob->ticcount)
        return;
    SpawnParticles(ob,GUTS,50);

}


void T_Special(objtype*ob)
{
    if (ob->ticcount)
        return;

#if (SHAREWARE == 0)
    if (ob->state == &s_NMEheadexplosion)
    {
        ob->z -= 42;
        SetGibSpeed(0x4000);
        SpawnParticles(ob,gt_sparks,100);
        ResetGibSpeed();
        SD_PlaySoundRTP(SD_EXPLODESND,ob->x,ob->y);
        return;
    }
#endif
    if (ob->obclass != b_robobossobj)
        return;

    NewState(ob,&s_bossdeath);
}




void SpawnBoulder(int tilex,int tiley,int dir)
{
#if (SHAREWARE == 1)
    tilex = tilex;
    tiley = tiley;
    dir = dir;
    Error("Boulders aren't allowed in shareware!");
#endif
#if (SHAREWARE == 0)
    SpawnNewObj(tilex,tiley,&s_boulderspawn,inertobj);
    new->z = 0;
    PreCacheActor(boulderobj,0);
    new->dir = 2*dir;
#endif

}



#define InitSprayPart(newflags)                                          \
   {                                                                     \
   new->hitpoints = starthitpoints[gamestate.difficulty][b_robobossobj]; \
   new->dir = dir*4;                                                     \
   new->speed = 7*SPDPATROL;                                             \
   new->door_to_open = -1;                                               \
   new->flags |= (newflags);                                             \
   }                                                                     \


void SpawnMultiSpriteActor(classtype actorclass, int tilex,int tiley,int dir)
{



#if (SHAREWARE==1)

    actorclass = actorclass;
    tilex = tilex;
    tiley = tiley;
    dir  = dir;
    Error("\nSPRAY not allowed in shareware !");

#else

    {
        objtype *temp;

        gamestate.killtotal++;

        SpawnNewObj(tilex,tiley,&s_NMEstand,actorclass);
        InitSprayPart(FL_BLOCK|FL_NOFRICTION|FL_SHOOTABLE);

        new->temp1 = -1; // temp1 used as one-event queue for directions when chasing
        // -1 when isn't waiting to try new dir, dirnumber when waiting
        temp = new;

        SpawnNewObj(tilex,tiley,&s_NMEhead1,actorclass);
        InitSprayPart(FL_NOFRICTION|FL_SHOOTABLE|FL_HEAD|FL_NEVERMARK);

        //new->whatever = temp;  // head points to body

        temp->whatever = new;  // body points to head

        SpawnNewObj(tilex,tiley,&s_NMEwheels2,actorclass);
        InitSprayPart(FL_NOFRICTION|FL_SHOOTABLE|FL_HEAD|FL_NEVERMARK);


        //new->whatever = temp;  // head points to body
        temp->target = new;     // body also points to wheels
        actorat[tilex][tiley] = NULL;
        PreCacheActor(b_robobossobj,0);
    }
#endif
}


void SpawnSnake(int tilex,int tiley)
{

#if (SHAREWARE == 1)
    tilex = tilex;
    tiley = tiley;
    Error("snake not allowed in shareware!");
#else

    GetNewActor();
    MakeActive(new);
    new->flags |= (FL_DONE|FL_ABP|FL_NEVERMARK);
    SetTilePosition(new,tilex,tiley);
    SetVisiblePosition(new,new->x,new->y);
    new->obclass = b_darkmonkobj;
    new->which = ACTOR;
    new->z = nominalheight;
    if (SNAKELEVEL == 2)
        NewState(new,&s_darkmonkfastspawn);
    else
        NewState(new,&s_darkmonkhspawn);
#endif

}

void SpawnGunThingy(classtype which, int tilex, int tiley, int dir)
{
#if (SHAREWARE == 1)
    which = which;
    tilex = tilex;
    tiley = tiley;
    dir  = dir;

    Error("no emplacements allowed in shareware!");
#else
    SpawnNewObj(tilex,tiley,&s_gunstand,which);


    if (!loadedgame)
        gamestate.killtotal++;

    PreCacheActor(patrolgunobj,0);

    new->hitpoints = starthitpoints[gamestate.difficulty][which];
    new->dir = dir*2;
//  new->speed = 0x500;
//  ParseMomentum(new,dirangle8[new->dir]);
    new->flags |= (FL_BLOCK|FL_SHOOTABLE);
#endif
}


void SpawnFourWayGun(int tilex, int tiley)
{
#if (SHAREWARE == 1)
    tilex = tilex;
    tiley = tiley;
    Error("no 4-way emplacements allowed in shareware!");
#else


    SpawnNewObj(tilex,tiley,&s_4waygun,patrolgunobj);
    if (!loadedgame)
        gamestate.killtotal++;

    PreCacheActor(patrolgunobj,0);
    new->temp1 = -1;
    new->hitpoints = starthitpoints[gamestate.difficulty][patrolgunobj]*3;
    new->flags |= (FL_BLOCK|FL_SHOOTABLE);
#endif
}




/*
=======================================================================
=
=                          NON-SHAREWARE CODE
=
=======================================================================
*/

#if (SHAREWARE == 0)

void T_BoulderSpawn(objtype*ob)
{   objtype *tactor;
    int dx,dy,cl;

    if (!(ob->flags & FL_ACTIVE))
        return;

    else if (!ob->ticcount)
    {   for(tactor = firstareaactor[ob->areanumber]; tactor; tactor = tactor->nextinarea)
        {   cl = tactor->obclass;
            if (tactor == ob)
                continue;

            if (!(tactor->flags & FL_SHOOTABLE))
                continue;
            dx = abs(tactor->x - ob->x);
            if (dx > MINACTORDIST)
                continue;
            dy = abs(tactor->y - ob->y);
            if (dy > MINACTORDIST)
                continue;
            if ((cl == b_heinrichobj) || (cl== b_darkmonkobj) ||
                    (cl == b_darianobj) || (cl == b_robobossobj) ||
                    (cl == pillarobj) || (cl == wallopobj) ||
                    (cl == boulderobj))
                return;
            else break;
        }


        SpawnNewObj(ob->tilex,ob->tiley,&s_boulderdrop1,boulderobj);
        new->z = 0;
        new->dir = ob->dir;
        //new->angle = dirangle8[new->dir];
        new->speed = 0x4000;
        ParseMomentum(new,dirangle8[new->dir]);
        new->flags |= (FL_BLOCK|FL_NOFRICTION);
        new->flags &= ~FL_SHOOTABLE;
        new->whatever = ob;
        if (tactor)
            new->target = tactor;
        MakeActive(new);
        new->flags |= FL_ABP;

    }

}

void T_BoulderDrop(objtype*ob)
{   int dx,dy,dz;
    objtype * tactor;
    statetype *tstate;


    if (ob->state == &s_boulderdrop12)
    {

        if (ob->z == nominalheight)
            NewState(ob,&s_boulderroll1);
        else if (ob->momentumz)
        {   ob->z += (ob->momentumz>>16);
            ob->momentumz += (GRAVITY<<1);
            if (ob->z > nominalheight)
            {   ob->z = nominalheight;
                ob->momentumz = 0;
                //ob->flags &= ~FL_NOFRICTION;
            }
        }
        else if (!ob->temp1)
        {   ob->momentumz = (GRAVITY<<6);
            ob->temp1  = 1;
        }

    }

    if (ob->ticcount)
        return;
    if (ob->state->condition & SF_SOUND)

        SD_PlaySoundRTP(SD_BOULDERFALLSND,ob->x,ob->y);
    tactor = (objtype*)(ob->target);
    if (tactor && (!(tactor->flags & FL_DYING)))
    {   dx = tactor->x - ob->x;
        dy = tactor->y - ob->y;
        dz = tactor->z - ob->z;
        if ((abs(dx) < MINACTORDIST) && (abs(dy) < MINACTORDIST) &&
                (abs(dz) < 50))
        {   if (tactor->obclass != playerobj)
            {   tactor->momentumx = tactor->momentumy = tactor->momentumz = 0;
                tactor->flags |= FL_DYING;
                tactor->hitpoints = 0;
                if (gamestate.violence < vl_high)
                {   if ((tstate = UPDATE_STATES[CRUSH][tactor->obclass - lowguardobj])!=NULL)
                        NewState(tactor,tstate);

                    //else
                    //Error("\n\Null low-violence crush state in boulder drop, instance of %s",debugstr[tactor->obclass]);
                }
                else
                {   tactor->shapeoffset = 0;
                    //tactor->flags|=FL_HBM;
                    NewState(tactor,&s_guts1);
                }
            }
            else
            {   DamageThing(tactor,200);
                Collision(tactor,ob,0,0);
                M_CheckPlayerKilled(tactor);
            }
            SD_PlaySoundRTP(SD_ACTORSQUISHSND,tactor->x,tactor->y);
            ob->target = NULL;
        }

    }
}


void CheckCrush(objtype*ob)
{
    objtype *temp;
    int dx,dy,dz;

    for(temp = PLAYER[0]; temp != PLAYER[numplayers-1]->next; temp=temp->next)
    {
        if (ob->flags & FL_DYING)
            continue;

        dx = abs(temp->x - ob->x);
        if (dx > MINACTORDIST)
            continue;

        dy = abs(temp->y - ob->y);
        if (dy > MINACTORDIST)
            continue;

        dz = abs(temp->z - ob->z);
        if (dz > (MINACTORDIST>>10))
            continue;

        if (!ob->ticcount)
            DamageThing(temp,EnvironmentDamage(ob));
        Collision(temp,ob,ob->momentumx-temp->momentumx,ob->momentumy-temp->momentumy);
        M_CheckPlayerKilled(temp);
    }
}


void T_BoulderMove(objtype*ob)
{


    if (MAPSPOT(ob->tilex,ob->tiley,1) == 395)
    {   NewState(ob,&s_bouldersink1);
        return;
    }
    if (NOMOM)
        ParseMomentum(ob,dirangle8[ob->dir]);
    if ((!ob->ticcount) && (ob->state->condition & SF_SOUND) &&
            areabyplayer[ob->areanumber])
        SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);
    SelectPathDir(ob);

}


/*
=========================================================================
=
=                           Boss Functions
=
=========================================================================
*/

//***************************** Esau ************************************


enum {
    ESAU_USING_HOLES=1,
    ESAU_LEAVING_CONTROL_ROOM,
    ESAU_USING_TOUCH_PEDASTALS,
    ESAU_CHASING_PLAYER
};




void T_EsauWait(objtype*ob)
{
    int dist;

    dist = FindDistance(ob->tilex-PLAYER[0]->tilex,ob->tiley-PLAYER[0]->tiley);
    MISCVARS->ESAU_SHOOTING = false;

    if (ob->dirchoosetime)
        ob->dirchoosetime --;

    if ((dist>81) || (dist<36))
    {
        if (CheckLine(ob,PLAYER[0],MISSILE))
        {
            NewState(ob,&s_darianshoot1);
            ob->momentumx = ob->momentumy = 0;
        }
        return;
    }
    else if ((!ob->dirchoosetime) && (CheckLine(ob,PLAYER[0],SHOOT)))
    {
        NewState(ob,&s_dariandefend1);
        ob->dirchoosetime = (GameRandomNumber("T_EsauWait",0) % 35) + 17;//35;
        return;
    }
}


void T_EsauRise(objtype*ob)
{
    int newarea,oldarea;

    // if (gamestate.victoryflag)
    // return;

    if (!ob->ticcount)
    {   //Debug("\n tx before: %d, ty before: %d",
        //         ob->targettilex,ob->targettiley);

        SelectTouchDir(ob);
        if (ob->targettilex || ob->targettiley)
        {   //Debug("\n ob->tilex: %d, ob->tiley: %d, targettilex: %d, targettiley: %d",
            //         ob->tilex, ob->tiley, ob->targettilex, ob->targettiley);

            SetTilePosition(ob,ob->targettilex,ob->targettiley);
            SetVisiblePosition(ob,ob->x,ob->y);
            oldarea = ob->areanumber;
            newarea = AREANUMBER(ob->tilex,ob->tiley);
            if (oldarea != newarea)
            {
                RemoveFromArea(ob);
                ob->areanumber = newarea;
                MakeLastInArea(ob);
            }
        }
        else
            MISCVARS->EPOP[ob->temp3].x = MISCVARS->EPOP[ob->temp3].y = 0;

        ob->dirchoosetime= (GameRandomNumber("T_EsauRise",0) % 35) + 17;
        MISCVARS->ESAU_HIDING = false;
        MISCVARS->ESAU_SHOOTING = true;
        ob->flags |= FL_SHOOTABLE;
    }
}



void T_EsauChase(objtype*ob)
{
    int dx,dy,chance,dist;
    statetype *temp;



    if ((ob->tilex == ob->targettilex) && (ob->tiley == ob->targettiley))
    {
        if (MISCVARS->DSTATE == ESAU_USING_HOLES)
        {
            MISCVARS->ESAU_HIDING = true;
            MISCVARS->ESAU_SHOOTING = false;
            SD_PlaySoundRTP(SD_DARIANHIDESND,ob->x,ob->y);
            NewState(ob,&s_dariansink1);
            ob->flags &= ~FL_SHOOTABLE;
            return;
        }
        else if (MISCVARS->DSTATE == ESAU_LEAVING_CONTROL_ROOM)
        {
            if (!MISCVARS->doorcount)
            {
                SetTilePosition(ob,ob->tilex,ob->tiley);
                SetVisiblePosition(ob,ob->x,ob->y);
            }
            MISCVARS->doorcount ++;
            if (MISCVARS->doorcount == 4)
                MISCVARS->DSTATE = ESAU_USING_HOLES;
            else // hack to FORCE esau to walk through door
            {
                switch (ob->temp1)
                {
                case east:
                    ob->targettilex ++;
                    break;
                case west:
                    ob->targettilex --;
                    break;
                case north:
                    ob->targettiley --;
                    break;
                case south:
                    ob->targettiley ++;
                    break;
                }
            }
            SelectTouchDir(ob);
            return;
        }
    }

    if (touchsprite && (touchsprite->itemnumber == stats[stat_dariantouch].type))
    {
        dx = touchsprite->x - ob->x;
        dy = touchsprite->y - ob->y;

        if (((dx > -0x5000) && (dx < 0x5000)) &&
                ((dy > -0x5000) && (dy < 0x5000)))
        {
            SD_PlaySoundRTP(SD_DARIANGONNAUSESND,ob->x,ob->y);
            NewState(ob,&s_darianuse1);
            return;
        }
    }

    if (ob->dirchoosetime)
        ob->dirchoosetime --;

    if (NOMOM || (!ob->dirchoosetime))
    {
        SelectTouchDir(ob);
        ob->dirchoosetime = M_CHOOSETIME(ob);
    }
    else
        ActorMovement(ob);


    if (!ob->ticcount)
    {
        if (CheckLine(ob,PLAYER[0],MISSILE))   // got a shot at player?
        {
            if (Near(ob,PLAYER[0],1))
                chance = 300;
            else
            {
                dx = abs(PLAYER[0]->tilex-ob->tilex);
                dy = abs(PLAYER[0]->tiley-ob->tiley);
                dist = (dx>dy)?dx:dy;
                chance = 400/dist;
            }
            if (GameRandomNumber("T_EsauChase",0) <chance)
            {
                if ((temp=M_S(AIM)) != NULL)
                {
                    NewState(ob,temp);
                    ob->dirchoosetime = 0;
                    ob->momentumx = ob->momentumy = 0;
                    SetVisiblePosition(ob,ob->x,ob->y);
                    return;
                }
            }
            if (MISCVARS->ESAU_SHOOTING)
            {
                SetVisiblePosition(ob,ob->x,ob->y);
                return;
            }
        }
    }
}


void T_EsauSpears(objtype*ob)
{

    if (ob->ticcount == (ob->state->tictime>>1)-1)
    {
        OLDTILEX = PLAYER[0]->tilex;
        OLDTILEY = PLAYER[0]->tiley;
    }

    else if (!ob->ticcount)
    {
        SpawnNewObj(OLDTILEX,OLDTILEY,&s_speardown1,spearobj);
        new->flags |= FL_ABP;
        MakeActive(new);
    }
}



void FindDoor(objtype*ob)
{
    int i,area1,area2,min,curr,
        dest1x,dest1y,dest2x,dest2y,
        d1,d2;

    dirtype tdir1,tdir2;
    doorobj_t*dr;

    min = 0x7fffffff;
    for(i=0; i<doornum; i++)
    {
        dr = doorobjlist[i];
        if (dr->vertical)
        {
            area1 = MAPSPOT(dr->tilex-1,dr->tiley,0)-AREATILE;
            dest1x = dr->tilex-1;
            dest1y = dr->tiley;
            tdir1 = east;
            area2 = MAPSPOT(dr->tilex+1,dr->tiley,0)-AREATILE;
            dest2x = dr->tilex+1;
            dest2y = dr->tiley;
            tdir2 = west;
        }
        else
        {
            area1 = MAPSPOT(dr->tilex,dr->tiley-1,0)-AREATILE;
            dest1x = dr->tilex;
            dest1y = dr->tiley-1;
            tdir1 = south;
            area2 = MAPSPOT(dr->tilex,dr->tiley+1,0)-AREATILE;
            dest2x = dr->tilex;
            dest2y = dr->tiley+1;
            tdir2 = north;
        }

//============================================================
#define CheckMinDist(destx,desty,dir)                    \
   {                                                     \
   curr = FindDistance(destx-ob->tilex,desty-ob->tiley); \
   if (curr < min)                                       \
      {                                                  \
      min = curr;                                        \
      ob->targettilex = destx;                           \
      ob->targettiley = desty;                           \
      ob->temp1 = dir;                                   \
      }                                                  \
   }
//============================================================

        if (area1 == ob->areanumber)
        {
            if (area1 == area2)
            {
                d1 = FindDistance(dest1x-ob->tilex,dest1y-ob->tiley);
                d2 = FindDistance(dest2x-ob->tilex,dest2y-ob->tiley);
                if (d2 < d1) //swap areas
                {
                    CheckMinDist(dest2x,dest2y,tdir2);
                    continue;
                }
            }

            CheckMinDist(dest1x,dest1y,tdir1);

        }
        else if (area2 == ob->areanumber)
            CheckMinDist(dest2x,dest2y,tdir2);

    }
}


int FindTouch(objtype *ob)
{
    int i,curr,min,tx,ty,noneleft;
    statobj_t* tempstat;


    min = 0x7fffffff;
    noneleft = 1;
    for(i=0; i<MISCVARS->nexttouch; i++)
    {
        if (MISCVARS->ETOUCH[i].x || MISCVARS->ETOUCH[i].y)
        {
            noneleft = 0;
            tx = MISCVARS->ETOUCH[i].x;
            ty = MISCVARS->ETOUCH[i].y;
            tempstat = sprites[tx][ty];
            curr = FindDistance(tx-ob->tilex,ty-ob->tiley);

            if (curr < min)
            {
                min = curr;
                ob->targettilex = tx;
                ob->targettiley = ty;
                touchsprite = tempstat;
            }
        }
    }
    return (!noneleft);
}




typedef enum
{
    down_in_a_hole=-1,
    no_holes_available=0,
    holes_unreachable=1,
    hole_targetted=2

} hiding_status;



hiding_status HoleStatus(objtype*ob)
{
    int i,tx,ty,dist,noneleft,invisible,curr,min;
    tpoint dummy,*dptr = &dummy;
    objtype *tactor;
    _2Dpoint *tdptr;

    min = 0x7fffffff;
    noneleft = 1;



    for(i=0; i<MISCVARS->nextpop; i++)
    {
        tdptr = &(MISCVARS->EPOP[i]);

        if (tdptr->x || tdptr->y)
        {
            tactor = (objtype*)actorat[tdptr->x][tdptr->y];
            if (tactor && (tactor->obclass == pillarobj))
            {
                tdptr->x = 0;
                tdptr->y = 0;
                MISCVARS->popsleft --;
            }
        }
    }


    if (MISCVARS->popsleft > 1)
    {
        for(i=0; i<MISCVARS->nextpop; i++)
        {
            tdptr = &(MISCVARS->EPOP[i]);

            if (tdptr->x || tdptr->y)
            {
                tx = tdptr->x;
                ty = tdptr->y;

                if ((PLAYER[0]->tilex == tx) || (PLAYER[0]->tiley == ty))
                    continue;

                if (MISCVARS->ESAU_HIDING)
                {
                    dist = FindDistance(PLAYER[0]->tilex-tx,PLAYER[0]->tiley-ty);
                    if ((ob->tilex == tx) && (ob->tiley == ty) && (MISCVARS->popsleft != 1))
                        continue;
                    noneleft = 0;
                    if ((MAPSPOT(tx,ty,0)-AREATILE) == ob->areanumber)
                    {
                        ob->targettilex = tx;
                        ob->targettiley = ty;
                        ob->temp3 = i;
                        if ((dist < 81) && (dist > 36))
                            return down_in_a_hole;
                    }
                }

                else if (!MISCVARS->ESAU_SHOOTING)
                {
                    curr = FindDistance(tx-ob->tilex,ty-ob->tiley);
                    if (curr < min)
                    {
                        min = curr;
                        noneleft = 0;
                        dptr->which = ACTOR;
                        SetTilePosition(dptr,tx,ty);
                        //dptr->x = (tx << TILESHIFT) + TILEGLOBAL/2;
                        //dptr->y = (ty << TILESHIFT) + TILEGLOBAL/2;
                        dptr->z = ob->z;
                        invisible = 0;
                        if ((!CheckLine(ob,dptr,SHOOT)) && (MISCVARS->DSTATE != ESAU_USING_HOLES))
                        {
                            invisible = 1;
                            MISCVARS->DSTATE = ESAU_LEAVING_CONTROL_ROOM;
                        }
                        else
                            MISCVARS->DSTATE = ESAU_USING_HOLES;
                        ob->targettilex = tx;
                        ob->targettiley = ty;
                    }
                }
            }
        }
    }

    if (MISCVARS->ESAU_HIDING)
        return down_in_a_hole;

    if (noneleft)
    {
        MISCVARS->DSTATE = ESAU_CHASING_PLAYER;
        return  no_holes_available;
    }

    if (invisible) //leave present room
        return holes_unreachable;

    return hole_targetted;
}


void SelectTouchDir (objtype *ob)
{
    int dx,dy;
    hiding_status hole;


    dirtype d[3];
    dirtype tdir, olddir, turnaround;


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

    if (!MISCVARS->notouch)
    {
        if (!FindTouch(ob))
            MISCVARS->notouch = 1;
        else
            MISCVARS->DSTATE = ESAU_USING_TOUCH_PEDASTALS;
    }

    else if ((!MISCVARS->noholes) && (MISCVARS->DSTATE != ESAU_LEAVING_CONTROL_ROOM))
    {
        hole = HoleStatus(ob);

        switch(hole)
        {
        case down_in_a_hole:
            return;

        case no_holes_available:
            MISCVARS->noholes = 1;
            break;

        case holes_unreachable:
            FindDoor(ob);
            break;

        default:
            break;
        }
    }

    else if (MISCVARS->DSTATE == ESAU_CHASING_PLAYER)

        // only gets here if all gimmicks (touch tables,
        // holes) are inoperative
    {
        ob->flags |= FL_SHOOTABLE;
        ob->targettilex = PLAYER[0]->tilex;
        ob->targettiley = PLAYER[0]->tiley;
    }
    /*
    if (DSTATE == SDOOR)
    {dx = ((ob->targettilex<<16)+TILEGLOBAL/2) - ob->x;
       dy = ob->y - ((ob->targettiley<<16)+TILEGLOBAL/2);
       angle = atan2_appx(dx,dy);
       ZEROMOM;
       ParseMomentum(ob,angle);
       ActorMovement(ob);
       if (ob->momentumx || ob->momentumy)
       {ob->angle = angle;
       ob->dir = angletodir[ob->angle];
       return;
       }
    }
    else */
    dx = ob->targettilex - ob->tilex;
    dy = ob->tiley - ob->targettiley;




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


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


    if (GameRandomNumber("SelectTouchDir",0)<128)
    {
        tdir=d[1];
        d[1]=d[2];
        d[2]=tdir;
    }

    ZEROMOM;


    if (d[1]!=nodir)
        M_CHECKDIR(ob,d[1]);


    if (d[2]!=nodir)
        M_CHECKDIR(ob,d[2]);



    if (GameRandomNumber("SelectTouchDir",ob->obclass)>128)   //randomly determine direction of search
    {
        for (tdir=north; tdir<=west; tdir++)
        {
            if (tdir!=turnaround)
                M_CHECKDIR(ob,tdir);
        }
    }
    else
    {
        for (tdir=west; tdir>=north; tdir--)
        {
            if (tdir!=turnaround)
                M_CHECKDIR(ob,tdir);
        }
    }

    if (turnaround !=  nodir)
        M_CHECKDIR(ob,turnaround);


    if (olddir!=nodir)
        M_CHECKDIR(ob,olddir);

}




//************** Krist ****************************************************



void CheckRunover(objtype*ob)
{   int dx,dy,dz;

    dx = abs(PLAYER[0]->x - ob->x);
    if (dx > MINACTORDIST)
        return;

    dy = abs(PLAYER[0]->y - ob->y);
    if (dy > MINACTORDIST)
        return;

    dz = abs(PLAYER[0]->z - ob->z);
    if (dz > 10)
        return;

    locplayerstate->heightoffset = 18 + locplayerstate->playerheight;
    locplayerstate->oldheightoffset = locplayerstate->heightoffset;
    PLAYER[0]->temp2 = RENORMALIZE;
    DamageThing(PLAYER[0],30);
    Collision(PLAYER[0],ob,0,0);
    M_CheckPlayerKilled(PLAYER[0]);

}


void T_HeinrichChase(objtype*ob)
{
    int   dx,dy,dist,chance,perpangle;
// statetype *temp;
    boolean doorok;

    CheckRunover(ob);

    //	ob->flags &= ~FL_DODGE;
    if (CheckLine(ob,PLAYER[0],SIGHT))
    {   ob->targettilex = PLAYER[0]->x;
        ob->targettiley = PLAYER[0]->y;
    }

    if (!ob->ticcount)
    {

//	  if (gamestate.victoryflag)
//		return;


        if (CheckLine(ob,PLAYER[0],SHOOT))   // got a shot at PLAYER[0]?
        {   dx = abs(ob->tilex - PLAYER[0]->tilex);
            dy = abs(ob->tiley - PLAYER[0]->tiley);
            dist = dx>dy ? dx : dy;
            if (!dist || dist==1)
                chance = 300;
            else
                chance = 2400/dist;

            if (GameRandomNumber("T_HeinrichChase",0) <chance)
            {   tpoint dummy,*dptr=&dummy;

                if (Near(ob,PLAYER[0],2))
                    goto cdoor;


                perpangle = AngleBetween(ob,PLAYER[0]) + ANGLES/4;
                Fix(perpangle);
                dptr->which = ACTOR;
                dptr->x = ob->x + FixedMul(0x10000l,costable[perpangle]);
                dptr->y = ob->y - FixedMul(0x10000l,sintable[perpangle]);

                dptr->z = ob->z;
                if (!CheckLine(dptr,PLAYER[0],SHOOT))
                    goto cdoor;

                ob->target = PLAYER[0];
                NewState(ob,M_S(AIM));
                ob->dirchoosetime = 0;
                return;


            }
        }

    }

cdoor:
    doorok = NextToDoor(ob);


    if (ob->dirchoosetime)
        ob->dirchoosetime--;

    if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime) || doorok)
    {   /*if ((ob->flags & FL_DODGE) && (!doorok))
         SelectKristDodgeDir (ob);
        else */
        SD_PlaySoundRTP(SD_KRISTMOTORSND,ob->x,ob->y);
        SelectKristChaseDir(ob);

        ob->dirchoosetime = 4*M_CHOOSETIME(ob);

    }

    else
    {   if (NOMOM)
            ParseMomentum(ob,dirangle8[ob->dir]);
        ActorMovement(ob);

    }

}

void T_Heinrich_Defend (objtype*ob)
{
    CheckRunover(ob);

    if (ob->dirchoosetime)
        ob->dirchoosetime--;


    if (MISCVARS->HRAMMING)
        ParseMomentum(ob,dirangle8[ob->dir]);


    if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime))
    {   if (MISCVARS->HRAMMING)
        {   if (!Near(ob,PLAYER[0],3))
            {   NewState(ob,M_S(CHASE));
                ob->dirchoosetime = 0;
                return;
            }
            SelectKristChaseDir(ob);
        }
        else if (MISCVARS->HMINING)
        {   SelectMineDir(ob);
            if (!MISCVARS->HMINING)
                goto hchase;
            ob->dirchoosetime = 5;//10;
            return;
        }
        else
hchase:
            NewState(ob,M_S(CHASE));
        ob->dirchoosetime = 0;
    }
    else
    {   if (NOMOM)
            ParseMomentum(ob,dirangle8[ob->dir]);
        ActorMovement(ob);
    }
}


void T_Heinrich_Out_of_Control(objtype*ob)
{
    if (ob->dirchoosetime)
        ob->dirchoosetime --;
    else
    {
        if (!ob->temp1)
        {
            SetGibSpeed(0x4000);
            SpawnParticles(ob,RANDOM,120);
            ResetGibSpeed();

            NewState(ob,&s_dexplosion1);
            SD_PlaySoundRTP(SD_EXPLODESND,ob->x,ob->y);
        }
        else
        {
            ob->dir = dirorder[ob->dir][PREV];
            ob->angle = dirangle8[ob->dir];
            if (ob->dir == (unsigned)ob->temp2)
            {
                if (ob->temp1 > 1)
                    ob->temp1--;
                else
                {
                    if (ob->temp3 == 7)
                    {
                        SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj);
                        new->temp1 = 25;
                        new->flags |= FL_ABP;
                        MakeActive(new);
                        SpawnNewObj(ob->tilex,ob->tiley,&s_superparticles,inertobj);
                        new->flags |= FL_ABP;
                        PARTICLE_GENERATOR = new;
                        MakeActive(new);
                    }
                    if (ob->temp3)
                        ob->temp3 --;
                    else
                        ob->temp1 --;
                }
            }

            if (ob->temp1)
                ob->dirchoosetime = ob->temp1;
            else
            {
                ob->dirchoosetime = 70; // end of spin wait for megaexplosion
                if (PARTICLE_GENERATOR)
                {
                    NewState(PARTICLE_GENERATOR,&s_megaremove);
                    PARTICLE_GENERATOR = NULL;
                }
            }
        }
    }
}




void SelectKristChaseDir(objtype*ob)
{   int dx,dy,tx,ty,angle;
    dirtype dtry1,dtry2,tdir,olddir,next,prev,straight;
//tpoint dummy,*dptr=&dummy;

    olddir=ob->dir;


//dptr->which = ACTOR;
//dptr->z = ob->z;
    if (ob->targettilex || ob->targettiley)
    {   tx = ob->targettilex;
        ty = ob->targettiley;
        dx= tx - ob->x;
        dy= ob->y - ty;
        // SetFinePosition(dptr,tx,ty);
        if ( ((dx < 0x20000) && (dx > -0x20000)) &&
                ((dy < 0x20000) && (dy > -0x20000)))
        {
            dx= PLAYER[0]->x-ob->x;
            dy= ob->y-PLAYER[0]->y;
            // SetFinePosition(dptr,PLAYER[0]->x,PLAYER[0]->y);
        }
    }
    else
    {
        dx= PLAYER[0]->x-ob->x;
        dy= ob->y-PLAYER[0]->y;
        //SetFinePosition(dptr,PLAYER[0]->x,PLAYER[0]->y);

    }

    angle = atan2_appx(dx,dy);
    straight = angletodir[angle];
    /*
    if (ob->areanumber == PLAYER[0]->areanumber)
      {//tpoint newpos1,newpos2;
    	//dirtype leftdir;
    	//int leftangle1,leftangle2;

    	if (CheckLine(ob,&dummy,DIRCHECK))
    	  {//Debug("\ntrying straight dir %d",straight);
    		M_CHECKTURN(ob,straight);
    		//Debug("\nstraight dir %d failed",straight);
    	  }
    	//leftdir = dirorder[straight][PREV];
    	//leftangle1 = dirangle8[leftdir];
    	//newpos1.which = ACTOR;
    	//rightangle = dirangle[dirorder[straight][NEXT]];
    	//newpos1.x = ob->x + FixedMul(0x10000,costable[leftangle1]);
    	//newpos1.y = ob->y - FixedMul(0x10000,sintable[leftangle1]);
    	//newpos1.z = ob->z;

    	//leftangle2 = dirangle8[dirorder[leftdir][PREV]];
    	//newpos2.which = ACTOR;
    	//rightangle = dirangle[dirorder[straight][NEXT]];
    	//newpos2.x = ob->x + FixedMul(0x10000,costable[leftangle2]);
    	//newpos2.y = ob->y - FixedMul(0x10000,sintable[leftangle2]);
    	//newpos2.z = ob->z;
    	//if (CheckLine(&newpos1,&dummy,SHOOT))// || CheckLine(&newpos2,&dummy,SHOOT))
    		{for(tdir = dirorder[straight][PREV];tdir != dirorder[straight][NEXT];tdir = dirorder[tdir][PREV])
    			{//Debug("\ntried left-hand rule dir %d",tdir);
    			 M_CHECKTURN(ob,tdir);
    			}
    		}
    	//else
    	  //{for(tdir = dirorder[straight][NEXT];tdir != dirorder[straight][PREV];tdir = dirorder[tdir][NEXT])
    		 // {//Debug("\ntrying right-hand rule dir %d",tdir);
    		 //	M_CHECKTURN(ob,tdir);
    			//Debug("\nright-hand rule dir %d failed\n",tdir);
    	  //	  }
    	 // }
      }
    else*/
    {   dtry1=nodir;
        dtry2=nodir;

        if (dx> ACTORSIZE)
            dtry1= east;
        else if (dx< -ACTORSIZE)
            dtry1= west;
        if (dy> ACTORSIZE)
            dtry2=north;
        else if (dy < -ACTORSIZE)
            dtry2= south;


        if (abs(dy)>abs(dx))
        {   tdir=dtry1;
            dtry1=dtry2;
            dtry2=tdir;
        }

        //	ZEROMOM;
        ob->momentumx = FixedMul (ob->momentumx, DEADFRICTION>>gamestate.difficulty);
        ob->momentumy = FixedMul (ob->momentumy, DEADFRICTION>>gamestate.difficulty);


        M_CHECKTURN(ob,straight);

        if (dtry1 != nodir)
            M_CHECKTURN(ob,dtry1);

        if (dtry2 != nodir)
            M_CHECKTURN(ob,dtry2);

        if (dtry1 != nodir)
        {   M_CHECKTURN(ob,dirorder[dtry1][NEXT]);
            M_CHECKTURN(ob,dirorder[dtry1][PREV]);
        }

        for(tdir = dirorder[olddir][NEXT]; tdir != olddir; tdir = dirorder[tdir][NEXT])
            M_CHECKTURN(ob,tdir);

        ob->dir = olddir;
    }



}



void T_KristLeft(objtype*ob)
{   CheckRunover(ob);
    ActorMovement(ob);
    if (!ob->ticcount)
    {   SD_PlaySoundRTP(SD_KRISTTURNSND,ob->x,ob->y);
        if (ob->dir != (unsigned)ob->temp1)
            ob->dir = dirorder[ob->dir][NEXT];
        else
        {   ob->temp1 = 0;
            NewState(ob,&s_heinrichchase);
        }
    }

}

void T_KristRight(objtype*ob)
{   CheckRunover(ob);
    ActorMovement(ob);
    if (!ob->ticcount)
    {   SD_PlaySoundRTP(SD_KRISTTURNSND,ob->x,ob->y);
        if (ob->dir != (unsigned)ob->temp1)
            ob->dir = dirorder[ob->dir][PREV];
        else
        {   ob->temp1 = 0;
            NewState(ob,&s_heinrichchase);
        }
    }
}


void T_KristCheckFire(objtype*ob)
{   int perpangle,angle;
    tpoint dummy;

    if (!ob->ticcount)
    {   angle = AngleBetween(ob,PLAYER[0]);

        if (ob->state == &s_heinrichshoot1)
            perpangle = angle + ANGLES/4;
        else
            perpangle = angle - ANGLES/4;

        Fix(perpangle);


        dummy.which = ACTOR;
        dummy.x = ob->x + FixedMul(0x4000,costable[angle]) + FixedMul(0x4000l,costable[perpangle]) +
                  FixedMul(PROJSIZE,costable[perpangle]); // offset ahead plus
        // offset for left/right missile plus offset for missile
        // radius (will missile reach player without hitting wall,etc.)

        dummy.y = ob->y - FixedMul(0x4000,sintable[angle]) - FixedMul(0x4000l,sintable[perpangle]) -
                  FixedMul(PROJSIZE,sintable[perpangle]);

        dummy.x -= (FixedMul(PROJSIZE,costable[perpangle])<<1);

        dummy.y += (FixedMul(PROJSIZE,sintable[perpangle])<<1);
        dummy.z = ob->z;

        if (!CheckLine(&dummy,PLAYER[0],SHOOT))
        {   NewState(ob,&s_heinrichchase);
            return;
        }


    }
}



void SelectMineDir(objtype*ob)
{   int angle,missangle;
    dirtype olddir,tdir,next,prev,destdir;
    static int nummines=0;

    if (!CheckLine(ob,PLAYER[0],SIGHT))
    {   NewState(ob,M_S(CHASE));
        MISCVARS->HMINING = 0;
        return;
    }

    olddir = ob->dir;

    angle = AngleBetween(ob,PLAYER[0]);
    tdir = angletodir[angle];
    destdir = opposite[tdir];

    if (destdir != olddir)
    {   next = dirorder[olddir][NEXT];
        prev = dirorder[olddir][PREV];
        if (dirdiff[destdir][next] < dirdiff[destdir][prev])
            ob->dir = next;
        else
            ob->dir = prev;
        return;
    }

    nummines ++;
    missangle  = angle;
    if (nummines == 2)
        missangle -= (ANGLES/36);
    else if (nummines == 3)
        missangle += (ANGLES/36);

    Fix(missangle);
// if (missangle > (ANGLES - 1))
//  missangle -= ANGLES;
// else if (missangle < 0)
//  missangle += ANGLES;


    SpawnMissile(ob,h_mineobj,0x2000,missangle,&s_mine1,0xa000);
    new->dirchoosetime = 140;
    SD_PlaySoundRTP(SD_KRISTDROPSND,ob->x,ob->y);

    if (nummines == 3)
    {   MISCVARS->HMINING = 0;
        nummines = 0;
    }
}



void  A_HeinrichShoot(objtype* ob)
{   int angle,perpangle;

    if (!ob->ticcount)
    {   angle = AngleBetween(ob,PLAYER[0]);
        if (ob->state == &s_heinrichshoot4)
            perpangle = angle + ANGLES/4;
        else
            perpangle = angle - ANGLES/4;

        Fix(perpangle);

        SpawnMissile(ob,missileobj,0x4000,angle,&s_missile1,0x8000);
        SD_PlaySoundRTP(BAS[ob->obclass].fire,ob->x,ob->y);

        SetFinePosition(new,new->x + FixedMul(0x4000l,costable[perpangle]),
                        new->y - FixedMul(0x4000l,sintable[perpangle]));
        SetVisiblePosition(new,new->x,new->y);
    }


}


//***************************///////**************************************
//***************************/ NME /**************************************
//***************************///////**************************************





void UpdateNMELinkedActors(objtype*ob)
{
    objtype *head,*wheels;
    int oldarea;


    head = (objtype*)(ob->whatever);
    wheels = (objtype*)(ob->target);

    oldarea = head->areanumber;

    SetFinePosition(head,ob->x,ob->y);
    SetFinePosition(wheels,ob->x,ob->y);
    SetVisiblePosition(head,ob->x,ob->y);
    SetVisiblePosition(wheels,ob->x,ob->y);

    if (oldarea != ob->areanumber)
    {
        RemoveFromArea(head);
        head->areanumber = ob->areanumber;
        MakeLastInArea(head);
        RemoveFromArea(wheels);
        wheels->areanumber = ob->areanumber;
        MakeLastInArea(wheels);
    }

}


void T_OrobotChase(objtype*ob)
{
    int dx,dy;


    if (CheckLine(ob,PLAYER[0],SIGHT))
    {

        ob->targettilex = PLAYER[0]->tilex;
        ob->targettiley = PLAYER[0]->tiley;
    }



    if (!ob->ticcount)
    {
        if (NMEspincheck(ob))
            return;

        dx = PLAYER[0]->x - ob->x;
        dy = ob->y - PLAYER[0]->y;
        /*
        if ((dx > -0x18000) && (dx < 0x18000) && (dy > -0x18000) && (dy < 0x18000))
           {NewState(ob,&s_NMEavoid);
           return;
           }
        */

        if (CheckLine(ob,PLAYER[0],SIGHT))
        {
            int inrange;

            switch(gamestate.difficulty)
            {
            case gd_baby:
                inrange = Near(ob,PLAYER[0],6);
                break;
            case gd_easy:
                inrange = Near(ob,PLAYER[0],9);
                break;
            case gd_medium:
                inrange = Near(ob,PLAYER[0],12);
                break;
            case gd_hard:
                inrange = 1;
                break;
            }

            if ((!Near(ob,PLAYER[0],3)) && inrange)
            {
                SD_PlaySoundRTP(SD_NMEREADYSND,ob->x,ob->y);
                if ((ob->hitpoints < 2000) && (GameRandomNumber("NME special attack",0) < 120))
                {
                    int next,prev;

                    next = dirorder16[ob->dir][NEXT];
                    prev = dirorder16[ob->dir][PREV];
                    ob->targettilex = (angletodir[atan2_appx(dx,dy)]<<1);

                    if (dirdiff16[prev][ob->targettilex] < dirdiff16[next][ob->targettiley])
                        ob->temp3 = PREV;
                    else
                        ob->temp3 = NEXT;
                    NewState(ob,&s_NMEspinfire);
                }
                else
                {
                    NewState(ob,&s_NMEwindup);
                    ob->temp3 = 0;
                }
                //NewState((objtype*)(ob->target),&s_NMEwheelspin);

                NewState((objtype*)(ob->target),&s_NMEwheels120);
                return;
            }
        }
    }

    if (ob->dirchoosetime)
        ob->dirchoosetime --;

    if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime))
    {
        SelectOrobotChaseDir(ob);
        ob->dirchoosetime = 4;//8;
    }

    else
    {
        ActorMovement(ob);
        UpdateNMELinkedActors(ob);
    }
}



void T_Saucer(objtype*ob)
{   int angle,dangle;

    if (!ob->ticcount)  // if on track at end of each state, accelerate
        // towards PLAYER[0]
    {   if (ob->state->condition & SF_SOUND)
            SD_PlaySoundRTP(SD_NMEREADYSND,ob->x,ob->y);
        angle = AngleBetween(ob,PLAYER[0]);
        dangle = ob->angle - angle;
        if ((dangle > -(ANGLES/72)) && (dangle < (ANGLES/72)))
        {   if (ob->speed < 0x10000)
            {   ob->speed += 0x200;
                ZEROMOM;
                ParseMomentum(ob,ob->angle);
            }
        }
        else // off track; zero mom. and select new dir.
        {   ob->speed = 0x1000;
            ZEROMOM;
            ob->angle = angle;
            ParseMomentum(ob,ob->angle);
        }
    }
    MissileMovement(ob);


}


void T_NME_WindUp(objtype*ob)
{   objtype *head,*wheels;

    head = (objtype*)(ob->whatever);
    wheels = (objtype*)(ob->target);

    if (ob->dirchoosetime)
    {   ob->dirchoosetime--;
        return;
    }

    ob->dirchoosetime = 0;//3;

    if (MISCVARS->NMErotate < 3)
    {   head->dir = dirorder16[head->dir][NEXT];
        MISCVARS->NMErotate ++;
    }
    else if (MISCVARS->NMErotate < 6)
    {   head->dir = dirorder16[head->dir][PREV];
        MISCVARS->NMErotate ++;
    }
    else if (MISCVARS->NMErotate < 9)
    {   ob->dir = dirorder16[ob->dir][NEXT];
        wheels->dir = ob->dir;
        MISCVARS->NMErotate++;
    }
    else if (MISCVARS->NMErotate < 12)
    {   ob->dir = dirorder16[ob->dir][PREV];
        wheels->dir = ob->dir;
        MISCVARS->NMErotate ++;
    }
    else
    {   MISCVARS->NMErotate = 0;

        NewState(ob,&s_NMEattack);
        ob->dirchoosetime = 0;
        //ob->dirchoosetime = 50 - (ob->shapeoffset >> 2) - (gamestate.difficulty << 2);//70;
        if (!ob->temp2)
            NewState((objtype*)(ob->whatever),&s_NMEhead1rl);
        else
            NewState((objtype*)(ob->whatever),&s_NMEhead2rl);
        NewState(wheels,&s_NMEwheels2);
    }

}

#define SPRAYDIST 0x12000

void SelectOrobotChaseDir(objtype*ob)     // this code is for head
{
    int dx,dy,angle,tx,ty;
    int tdir,olddir,nextdir,prevdir;
    objtype* head,*wheels;



    head = (objtype*)(ob->whatever);
    wheels = (objtype*)(ob->target);
    olddir=head->dir;

findplayer:
    if (ob->temp1 == -1)
    {
        if (ob->targettilex || ob->targettiley)
        {
            tx = (int)((ob->targettilex << TILESHIFT) + HALFGLOBAL1);
            ty = (int)((ob->targettiley << TILESHIFT) + HALFGLOBAL1);
            dx= tx - ob->x;
            dy= ob->y - ty;
            if (((dx <SPRAYDIST ) && (dx > -SPRAYDIST)) &&
                    ((dy <SPRAYDIST ) && (dy > -SPRAYDIST)))
            {
                dx= PLAYER[0]->x-ob->x;
                dy= ob->y - PLAYER[0]->y;
            }
        }
        else
        {
            dx= PLAYER[0]->x - ob->x;
            dy= ob->y - PLAYER[0]->y;
        }

        angle = atan2_appx(dx,dy);

        tdir = (((angletodir[angle])<<1) & 0xf);
    }
    else
    {
        tdir = (ob->temp1 & 0xf);

        if ((head->dir == (unsigned)tdir) && (ob->dir == (unsigned)tdir)) // increment
            // tried dir if robot will attempt to move at tdir =>
            // head and body are at move try dir
        {   //Debug("\ntrying next queue dir %d",tdir);
            MISCVARS->NMEdirstried ++;
            if (MISCVARS->NMEdirstried == MISCVARS->NMEqueuesize) //gone through all queue entries
            {   //Debug("\nqueue exhausted");
                ob->temp1 = -1;
                MISCVARS->NMEdirstried = 0;
                goto findplayer;
            }
        }
    }


    if (tdir != olddir) //rotate head to new chase direction
    {
        nextdir = dirorder16[olddir][NEXT];
        prevdir = dirorder16[olddir][PREV];
        if (dirdiff16[tdir][nextdir] < dirdiff16[tdir][prevdir])
            head->dir = nextdir;
        else
            head->dir = prevdir;
        return;
    }
    //Debug("\nhead aligned to dir %d",tdir);

    //oddir = ob->dir;
    if (ob->dir != head->dir)   // align body and wheels with head
    {
        ZEROMOM;
        NewState(wheels,&s_NMEwheels120);  //rotate wheels for spinning
        nextdir = dirorder16[ob->dir][NEXT];
        prevdir = dirorder16[ob->dir][PREV];
        if (dirdiff16[head->dir][nextdir] < dirdiff16[head->dir][prevdir])
            ob->dir = nextdir;
        else
            ob->dir = prevdir;
        wheels->dir = ob->dir;
        return;
    }

    // Debug("\nbody aligned to head at dir %d",ob->dir);

    ZEROMOM;
    ParseMomentum(ob,dirangle16[head->dir]);
    // Debug("\ntrying to move at dir %d",head->dir);
    ActorMovement(ob);
    UpdateNMELinkedActors(ob);

    if (ob->momentumx || ob->momentumy)
    {
        NewState(wheels,&s_NMEwheels2); // align wheels for movement
        //Debug("\nmove at dir %d succesful, resetting queue",head->dir);
        ob->temp1 = -1; //clear direction queue
        return;
    }
    else if (ob->temp1 == -1) // if queue is empty
        //make a queue of directions (byte packed)
    {
        //Debug("\nmove at dir %d failed and queue empty",head->dir);
        ob->temp1 = 0;
        MISCVARS->NMEdirstried = 0;
        MISCVARS->NMEqueuesize = 0;

        nextdir = ((tdir + 6) & 0xf);
        prevdir = ((tdir - 6) & 0xf);

        for(; MISCVARS->NMEqueuesize < 6; MISCVARS->NMEqueuesize += 2)
        {
            ob->temp1 <<= 4;
            ob->temp1 += nextdir;
            ob->temp1 <<= 4;
            ob->temp1 += prevdir;
            nextdir = ((nextdir-2) & 0xf);
            prevdir = ((prevdir+2) & 0xf);

        }
    }
    else             // else goto next queue dir;
    {
        ob->temp1 >>= 4;
    }

}



void T_NME_Explode(objtype*ob)
{

    if (ob->ticcount == 35)
    {   objtype*head;
        int op;

        head = (objtype*)(ob->whatever);

        op = FixedMul(GRAVITY,(head->z-25)<<16) << 1;
        head->momentumz = -FixedSqrtHP(op);
        head->momentumx = (GameRandomNumber("NME head momx",0) << 2);
        head->momentumy = (GameRandomNumber("NME head momy",0) << 2);
        head->hitpoints = 0;
        head->flags |= FL_DYING;
        NewState(head,&s_shootinghead);

        //RemoveObj((objtype*)(ob->whatever)); // remove head
    }
    else if (!ob->ticcount)
    {   ob->shapeoffset = 0;
        NewState(ob,&s_explosion1);
        SetGibSpeed(0x4000);
        SpawnParticles(ob,gt_sparks,200);
        ResetGibSpeed();
        RemoveObj((objtype*)(ob->target));
    }

}

void T_NME_HeadShoot(objtype*ob)
{   //int randtheta,i,offx,offy;

    ob->z += (ob->momentumz>>16);

    /*if (ob->momentumz < 0)
     {for(i=0;i<3;i++)
     {randtheta = (GameRandomNumber("NME spark drop",0) << 3);
      SpawnNewObj(ob->tilex,ob->tiley,&s_particle1,inertobj);
      new->temp2 = 1;
      offx = FixedMul(0x400,costable[randtheta]);
      offy = -FixedMul(0x400,sintable[randtheta]);
      new->x = new->drawx = ob->x + offx;
      new->y = new->drawy = ob->y + offy;
      new->z = ob->z-15;
      new->flags |= (FL_NOFRICTION|FL_CRAZY|FL_ABP);
      new->dir = west;
      MakeActive(new);
     }
     }*/

    ob->momentumz += GRAVITY;
    if (ob->z >= (nominalheight+45))
    {   ob->z = nominalheight+45;
        if (ob->temp2)
        {   ob->momentumz = -30000*ob->temp2;
            ob->temp2--;
        }
        else
        {   ob->momentumx = ob->momentumy = ob->momentumz = 0;
            ob->shapeoffset = 0;
            NewState(ob,&s_NMEheadexplosion);

            return;
        }
    }
    ActorMovement(ob);

}


boolean NMEspincheck(objtype*ob)
{
    int dx,dy,dz;

    dx = abs(PLAYER[0]->x - ob->x);
    dy = abs(PLAYER[0]->y - ob->y);
    dz = abs(PLAYER[0]->z - ob->z);
    if ((dx < 0x10000) && (dy < 0x10000) && (dz < 32))
    {
        NewState(ob,&s_NMEspinattack);
        NewState((objtype*)(ob->target),&s_NMEwheelspin);
        if (!ob->temp2)
            NewState((objtype*)(ob->whatever),&s_NMEhead1);
        else
            NewState((objtype*)(ob->whatever),&s_NMEhead2);
        ob->dirchoosetime = 1;
        return true;
    }
    return false;
}





void T_NME_SpinAttack(objtype* ob)
{   int mx,my,mz;
    objtype*head,*wheels;



    if (ob->ticcount == 30)  // knock player back
    {   GetMomenta(PLAYER[0],ob,&mx,&my,&mz,0x4000);
        DamageThing(PLAYER[0],20);
        Collision(PLAYER[0],ob,mx,my);
        M_CheckPlayerKilled(PLAYER[0]);
    }
    if (ob->dirchoosetime)
        ob->dirchoosetime --;
    else
    {   head = (objtype*)(ob->whatever);
        wheels = (objtype*)(ob->target);
        wheels->dir = head->dir = ob->dir = dirorder16[dirorder16[ob->dir][NEXT]][NEXT];

        ob->dirchoosetime = 1;
    }


}


void T_NME_SpinFire(objtype*ob)
{
    int randtheta,oldyzangle,dx,dy,xydist,dz;
    objtype *head,*wheels;


    head = (objtype*)(ob->whatever);
    wheels = (objtype*)(ob->target);

    if (ob->dir != (unsigned)ob->targettilex)
    {   ob->dir = head->dir = wheels->dir = dirorder16[ob->dir][ob->temp3];
        return;
    }

    if (ob->dirchoosetime)
    {   ob->dirchoosetime --;
        return;
    }

    if (ob->temp3 < 20)
    {   //randphi = (GameRandomNumber("NME generate phi",0) << 3) & ((ANGLES/2) -1);
        if (GameRandomNumber("NME generate theta",0) < 128)
            randtheta = (GameRandomNumber("NME generate theta",0)>>4);
        else
            randtheta = -(GameRandomNumber("NME generate theta",0)>>4);
        dx = PLAYER[0]->x-ob->x;
        dy = ob->y-PLAYER[0]->y;
        if (GameRandomNumber("bcraft shoot up/down",0) < 128)
            dz = 5;
        else
            dz = -5;
        xydist = FindDistance(dx,dy);
        randtheta += atan2_appx(dx,dy);
        Fix(randtheta);
        oldyzangle = ob->yzangle;
        ob->yzangle = atan2_appx(xydist,dz<<10);
        //ob->yzangle = randphi;
        SD_PlaySoundRTP(BAS[ob->obclass].fire+1,ob->x,ob->y);
        //wheels->dir = head->dir = ob->dir = dirorder16[dirorder16[ob->dir][NEXT]][NEXT];
        SpawnMissile(ob,fireballobj,0x6000,randtheta,&s_NMEminiball1,0x10000);
        ob->dirchoosetime = 1;
        ob->yzangle = oldyzangle;
        ob->temp3 ++;
    }
    else
    {   ob->temp3 = 0;
        NewState(ob,&s_NMEchase);
        NewState((objtype*)(ob->target),&s_NMEwheels2);
        if (!ob->temp2)
            NewState((objtype*)(ob->whatever),&s_NMEhead1);
        else
            NewState((objtype*)(ob->whatever),&s_NMEhead2);


    }


}

void T_NME_Attack(objtype*ob)
{   int angle,perpangle,i;



    if (NMEspincheck(ob))
    {   //ob->temp3 = 0;
        return;
    }
    if (ob->dirchoosetime)
    {   ob->dirchoosetime --;
        return;
    }


    if (!CheckLine(ob,PLAYER[0],SIGHT))
    {   //ob->temp3 = 0;
        NewState(ob,&s_NMEchase);
        NewState((objtype*)(ob->target),&s_NMEwheels2);
        if (!ob->temp2)
            NewState((objtype*)(ob->whatever),&s_NMEhead1);
        else
            NewState((objtype*)(ob->whatever),&s_NMEhead2);
        return;
    }
    //sound = BAS[ob->obclass].fire;
    angle = AngleBetween(ob,PLAYER[0]);



    if ((ob->temp3 == 0) || (ob->temp3 == 1)) //heatseek

    {   SD_PlaySoundRTP(BAS[ob->obclass].fire+2,ob->x,ob->y);
        angle = AngleBetween(ob,PLAYER[0]);
        SpawnMissile(ob,missileobj,0x6000,angle,&s_missile1,0x8000);
        if (ob->temp3 == 3)
            perpangle = angle + ANGLES/4;
        else
            perpangle = angle - ANGLES/4;
        Fix(perpangle);

        new->temp1 = NME_HEATSEEKINGTYPE;
        SetFinePosition(new,new->x + FixedMul(0x8000l,costable[perpangle]),
                        new->y - FixedMul(0x8000l,sintable[perpangle]));
        SetVisiblePosition(new,new->x,new->y);
        if (!ob->temp3)
            ob->dirchoosetime = 20;
        else
        {   ob->dirchoosetime = 35 - (ob->shapeoffset >> 2) - (gamestate.difficulty << 2);//70;
            if (!ob->temp2)
                NewState((objtype*)(ob->whatever),&s_NMEhead1);
            else
                NewState((objtype*)(ob->whatever),&s_NMEhead2);
        }
        ob->temp3 ++;

    }

    else if (ob->temp3 == 2)          // saucer
    {   SpawnMissile(ob,NMEsaucerobj,0x1000,angle,&s_NMEsaucer1,0xc000);
        new->flags |= FL_SHOOTABLE;
        ob->temp3++;
        ob->dirchoosetime = 35 - (ob->shapeoffset >> 2) - (gamestate.difficulty << 2);//70;
        if (!ob->temp2)
            NewState((objtype*)(ob->whatever),&s_NMEhead1rl);
        else
            NewState((objtype*)(ob->whatever),&s_NMEhead2rl);
    }

    else if ((ob->temp3 == 3) || (ob->temp3 == 4))    // drunk
    {   SD_PlaySoundRTP(BAS[ob->obclass].fire+2,ob->x,ob->y);
        if (!ob->temp3)
            perpangle = angle + ANGLES/4;
        else
            perpangle = angle - ANGLES/4;
        Fix(perpangle);
        for(i=0; i<(2+gamestate.difficulty); i++)
        {
            SpawnMissile(ob,missileobj,0x6000,angle,&s_missile1,0x8000);
            new->temp1 = NME_DRUNKTYPE;
            SetFinePosition(new,new->x + FixedMul(0x8000l,costable[perpangle]),
                            new->y - FixedMul(0x8000l,sintable[perpangle]));
            SetVisiblePosition(new,new->x,new->y);
        }

        if (ob->temp3 == 3)
            ob->dirchoosetime = 20;
        else
        {   ob->temp3 = 0;
            NewState(ob,&s_NMEchase);
            if (!ob->temp2)
                NewState((objtype*)(ob->whatever),&s_NMEhead1);
            else
                NewState((objtype*)(ob->whatever),&s_NMEhead2);
        }

        ob->temp3 ++;

    }



}



//================== Tom/Snake ============================================





void T_DarkSnakeSpawn(objtype*ob)
{
    objtype * linkinfront;

    if (((ob->state == &s_darkmonkhspawn) && (!(ob->ticcount%8))) ||
            ((ob->state == &s_darkmonkfastspawn) && (!(ob->ticcount%4))))
    {
        GetNewActor();
        MakeActive(new);
        SetFinePosition(new,ob->x,ob->y);
        SetVisiblePosition(new,ob->x,ob->y);
        new->z = nominalheight;
        new->areanumber = MAPSPOT(new->tilex,new->tiley,0)-AREATILE;
        MakeLastInArea(new);
        new->obclass = b_darksnakeobj;
        new->which = ACTOR;
        new->angle = AngleBetween(ob,PLAYER[0]);
        new->dir = angletodir[new->angle];
        if (SNAKELEVEL == 1)
            new->speed = 0x5000;
        else if (SNAKELEVEL == 2)
            new->speed = 0x5800;
        else
            new->speed = 0x2000;


        new->hitpoints = 1000;
        new->dirchoosetime = 0;
        new->door_to_open = -1;

        new->flags |= (FL_ABP|FL_NOFRICTION|FL_SHOOTABLE|FL_BLOCK);

        if (ob->whatever)
        {
            linkinfront = (objtype*)(ob->whatever);
            linkinfront->whatever = new;
            new->target = linkinfront;
            new->targettilex = linkinfront->x;
            new->targettiley = linkinfront->y;
            new->angle = AngleBetween(new,linkinfront);
            new->dir = angletodir[new->angle];
            new->flags |= FL_NEVERMARK;
            ParseMomentum(new,new->angle);
            NewState(new,&s_darkmonksnakelink);
        }

        else
        {
            SNAKEHEAD = new;
            if (SNAKELEVEL == 3)
                NewState(new,&s_darkmonkhead);
            else if (SNAKELEVEL == 1)
            {
                NewState(new,&s_snakefindpath);
                new->flags |= FL_ATTACKMODE;
            }
            else if (SNAKELEVEL == 2)
            {
                NewState(new,&s_snakepath);
                new->angle = 3*ANGLES/4;
                new->dir = angletodir[new->angle];
                new->flags |= FL_ATTACKMODE;

            }
            ob->targettilex = ob->targettiley = 0;
            ParseMomentum(new,new->angle);
        }

        if (!ob->ticcount)
            SNAKEEND = new;

        ob->whatever = new;

    }
}


void T_GenericMove(objtype*ob)
{   int dx,dy;

    if (ob->temp3 == -1)
        return;


    if (!(SNAKEHEAD->flags & FL_ATTACKMODE))
        return;

    if (ob->hitpoints <= 0)
    {   KillActor(ob);
        ob->temp3 = 0;
        return;
    }

    if (!ob->ticcount)
    {   if (ob->state == &s_darkmonkredlink)
            ob->temp3 = 0;
        else if ((ob!=SNAKEEND) && (ob->state == &s_redlinkhit))
            NewState((objtype*)(ob->whatever),&s_redlinkhit);
    }

    dx = ob->targettilex-ob->x;
    dy = ob->y-ob->targettiley;
    if ((dx > -0xa000) && (dx < 0xa000) && (dy > -0xa000) && (dy < 0xa000))
    {   if (ob->temp1 && ob->temp2)
        {   dx = ob->temp1 - ob->x;
            dy = ob->y - ob->temp2;
            ZEROMOM;
            /*
            if ((ob->targettilex == ob->temp1) && (ob->targettiley == ob->temp2))
            return; */
            //ob->x = ob->drawx = ob->targettilex;
            //ob->y = ob->drawy = ob->targettiley;
            //ob->tilex = ob->x >> TILESHIFT;
            //ob->tiley = ob->y >> TILESHIFT;
            ob->targettilex = ob->temp1;
            ob->targettiley = ob->temp2;
            ob->angle = atan2_appx(dx,dy);
            ob->dir = angletodir[ob->angle];
            ParseMomentum(ob,ob->angle);
        }
    }
    else if (NOMOM)
    {   //SNAKEHEAD->dirchoosetime = 0;
        ParseMomentum(ob,ob->angle);
    }
    if (ob->momentumx || ob->momentumy)
        MoveActor(ob);

// ActorMovement(ob);

}


/*
===============
=
= SelectSnakeDir
=
===============
*/


void SelectSnakeDir (objtype *ob)
{
    int spot,centerx,centery,dx,dy;

    spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS;

    if ((spot >= 0) && (spot<= 7) && ((ob->dir!=(unsigned)spot)||(!(ob->flags & FL_DONE))))
    {   centerx= (ob->tilex << 16) + HALFGLOBAL1;
        centery= (ob->tiley << 16) + HALFGLOBAL1;
        dx = abs(centerx - ob->x);
        dy = abs(centery - ob->y);

        if ((dx < SNAKERAD) && (dy < SNAKERAD))
            // new direction
        {   ZEROMOM;
            ob->dir = spot;
            ob->flags |= FL_DONE;
            ParseMomentum(ob,dirangle8[ob->dir]);
            SetFinePosition(ob,centerx,centery);
            SetVisiblePosition(ob,ob->x,ob->y);

            if (ob==SNAKEHEAD) {
                SoftError("\n path changed at %d, %d",ob->tilex,ob->tiley);
            }
        }
    }

    MoveActor(ob);

}


void T_SnakePath(objtype*ob)
{   objtype*temp,*follower;

    if (SNAKEEND && (SNAKELEVEL == 2))
    {   if (CheckLine(SNAKEEND,PLAYER[0],SIGHT))
        {   if (ob->temp3 == -1)               //if snake can see player
                //and he's presently stopped, restart
            {   for(temp=ob; temp; temp=(objtype*)(temp->whatever))
                {   temp->temp3 = 0;
                    temp->momentumx = temp->temp1;
                    temp->momentumy = temp->temp2;
                }
                ob->dirchoosetime = 0;
            }
        }
        else if (ob->temp3 != -1)     //else if he hasn't been stopped, stop him
        {   for(temp=ob; temp; temp = (objtype*)(temp->whatever))
            {   temp->temp1 = temp->momentumx;
                temp->temp2 = temp->momentumy;
                temp->temp3 = -1;
                temp->momentumx = temp->momentumy = 0;
            }
        }
        else
            return;
    }


    if (ob->dirchoosetime)
        ob->dirchoosetime--;

    else
    {   int count = 0;

        for(temp=ob; temp->whatever; temp=(objtype*)(temp->whatever))
        {   follower = (objtype*)(temp->whatever);
            follower->temp1 = temp->x;
            follower->temp2 = temp->y;

            SoftError("\n follower %d temp1 set to %4x, temp2 set to %4x",
                      count,temp->x,temp->y);
            count ++;
        }
        ob->dirchoosetime = 2 ;//15
    }

    if (ob->momentumx || ob->momentumy)
        SelectSnakeDir(ob);
//else
// {ParseMomentum(ob,ob->angle);
//	MoveActor(ob);
// }


}

void FindClosestPath(objtype*ob)
{   int tx,ty,dx,dy,angle;


    tx = (ob->targettilex << 16) + TILEGLOBAL/2;
    ty = (ob->targettiley << 16) + TILEGLOBAL/2;

    dx= tx - ob->x;
    dy= ob->y - ty;
    angle = atan2_appx(dx,dy);

    ZEROMOM;
    ParseMomentum(ob,angle);
    MoveActor(ob);

}


void T_SnakeFindPath(objtype*ob)
{   int i,dx,dy,currdist,mindist,map;
    tpoint dstruct,*dummy=&dstruct;
    objtype*temp,*follower;

    if (ob->targettilex || ob->targettiley)
    {   FindClosestPath(ob);
        dx = ob->targettilex - ob->tilex;
        dy = ob->targettiley - ob->tiley;
        if ((!dx) && (!dy))
        {   SetTilePosition(ob,ob->tilex,ob->tiley);
            SetVisiblePosition(ob,ob->x,ob->y);
            ob->y = ob->drawy = (ob->tiley << TILESHIFT) + TILEGLOBAL/2;
            NewState(ob,&s_snakepath);
            return;
        }
    }

    else
    {   dummy->which = ACTOR;
        mindist = 0x7fffffff;
        for(i=0; i<whichpath; i++)
        {
            SetTilePosition(dummy,SNAKEPATH[i].x,SNAKEPATH[i].y);
            dummy->z = ob->z;
            if (CheckLine(ob,dummy,SIGHT))
            {   currdist = FindDistance(ob->tilex-dummy->tilex,ob->tiley-dummy->tiley);
                map = MAPSPOT(ob->tilex,ob->tiley,0)-AREATILE;
                if ((currdist < mindist) && (map >= 0) && (map <= NUMAREAS))
                {   ob->targettilex = dummy->tilex;
                    ob->targettiley = dummy->tiley;
                    mindist = currdist;
                }
            }
        }
    }

    if (ob->dirchoosetime)
        ob->dirchoosetime--;
    else
    {   for(temp=ob; temp->whatever; temp=(objtype*)(temp->whatever))
        {   follower = (objtype*)(temp->whatever);
            follower->temp1 = temp->x;
            follower->temp2 = temp->y;
        }
        ob->dirchoosetime = 2 ;//15
    }
}



void T_SnakeFinale(objtype*ob)
{

    if ((ob->state == &s_snakefireworks1)||(ob->state == &s_snakefireworks2))
    {
        if (ob->z != (maxheight-200))
        {
            ob->z --;
            return;
        }
        SetGibSpeed(0x4500);
        SpawnParticles(ob,RANDOM,100);

        SpawnParticles(ob,gt_spit,100);
        ResetGibSpeed();
        NewState(ob,&s_dexplosion1);
    }

    else
    {
        if (!ob->ticcount)
        {
            NewState(EXPLOSIONS,&s_megaremove);
            //  SpawnParticles(ob,RANDOM,100);
            // SpawnParticles(ob,SPIT,100);
            return;
        }

        if (ob->dirchoosetime)
            ob->dirchoosetime --;
        else
        {
            ob->dirchoosetime = (GameRandomNumber("snake finale choose",0) % 7) + 15;
            SetGibSpeed(0x3000);
            SpawnParticles(ob,RANDOM,30);
            SpawnParticles(ob,gt_spit,20);
            ResetGibSpeed();
        }
    }
}



void T_DarkSnakeChase(objtype*ob)
{
    objtype* temp,*follower;
    int tdir,angle;


    if (!(ob->flags & FL_ATTACKMODE))
    {
        if (!(CheckSight(ob,player) || Near(ob,player,4)))
            return;
        else
        {
            ob->flags |= FL_ATTACKMODE;
            MU_StartSong(song_bosssee);
        }

    }



    if (ob->hitpoints <= 0)
    {
        MU_StartSong(song_bossdie);
        KillActor(ob);
        AddMessage("Oscuro defeated!",MSG_CHEAT);
        return;
    }

    angle = AngleBetween(ob,PLAYER[0]);
    tdir = angletodir[angle];
    if (Near(ob,PLAYER[0],6) && (ob->dir == (unsigned)tdir) && (!(ob->state->condition & SF_DOWN)))
    {
        NewState(ob,&s_snakefire1);
        SD_PlaySoundRTP(SD_SNAKEREADYSND,ob->x,ob->y);
    }

    if (!ob->ticcount)
    {
        if (ob->state == &s_darkmonkredhead)
            ob->temp3 = 0; // no longer hitable
        else if ((ob->state == &s_redheadhit) && (ob != SNAKEEND))
            NewState((objtype*)(ob->whatever),&s_redlinkhit);
        else if (ob->state->condition & SF_UP)
        {
            SpawnMissile(ob,dm_spitobj,0x6000,angle,&s_spit1,0x6000);
            SD_PlaySoundRTP(BAS[ob->obclass].fire,ob->x,ob->y);
            //new->z -= 5;
        }
        //spawn spit;
    }

    if (CheckLine(ob,PLAYER[0],SIGHT))
    {
        ob->targettilex = PLAYER[0]->x;
        ob->targettiley = PLAYER[0]->y;
    }

    if (ob->dirchoosetime)
    {
        ob->dirchoosetime--;
        ActorMovement(ob);
        if (NOMOM)
            ob->dirchoosetime = 0;
    }

    else
    {   //if (ob)
        for(temp=ob; temp->whatever; temp=(objtype*)(temp->whatever))
        {
            follower = (objtype*)(temp->whatever);
            follower->temp1 = temp->x;
            follower->temp2 = temp->y;
        }
        SelectChaseDir(ob);
        ob->dirchoosetime = 7 ;//15
    }
}



void T_DarkmonkReact(objtype*ob)
{
    if (ob->z < nominalheight)
    {   MISCVARS->monkz += MZADJUST;
        ob->z = nominalheight + (MISCVARS->monkz >> 16);
        //ob->z++;
        return;
    }

    else
    {   int ocl;

        ocl = ob->temp3;

        if (ocl == p_kesobj)
            NewState(ob,&s_darkmonkabsorb1);
        else if (ocl == p_heatseekobj)
            NewState(ob,&s_darkmonkhball1);
        else if (ocl == p_firebombobj)
            NewState(ob,&s_darkmonkbreathe1);
        else
            NewState(ob,&s_darkmonkchase1);
        ob->dirchoosetime = 0;
    }

}



void T_DarkmonkCharge(objtype*ob)
{   int dx,dy;

    dx = abs(PLAYER[0]->x - ob->x);
    dy = abs(PLAYER[0]->y - ob->y);
    if ((dx < 0xa000) && (dy < 0xa000))
    {   DamageThing(PLAYER[0],10);
        Collision(PLAYER[0],ob,0,0);
        M_CheckPlayerKilled(PLAYER[0]);
    }

    if (!ob->ticcount)
        ob->speed >>= 1;

    if (ob->dirchoosetime)
        ob->dirchoosetime --;

    if (NOMOM || (!ob->dirchoosetime))
    {   ob->angle = AngleBetween(ob,PLAYER[0]);
        ob->dir = angletodir[ob->angle];
        ParseMomentum(ob,ob->angle);
        ob->dirchoosetime = 5;
    }

    ActorMovement(ob);


}


void T_DarkmonkLandAndFire(objtype*ob)
{

    if (ob->z < nominalheight)
    {   MISCVARS->monkz += MZADJUST;
        ob->z = nominalheight + (MISCVARS->monkz >> 16);
        //ob->z++;
        return;
    }
    if (Near(ob,PLAYER[0],3))
    {   if (GameRandomNumber("darkmonkland",0)<128)
            NewState(ob,&s_darkmonkbball1);
        else
        {   ob->angle = AngleBetween(ob,PLAYER[0]);
            ob->dir = angletodir[ob->angle];
            ob->speed <<= 1;       // goes twice as fast
            ZEROMOM;
            ParseMomentum(ob,ob->angle);
            ob->dirchoosetime = 5; // change dir every 5 tics
            ob->hitpoints -= 200; // big penalty for charging
            if (ob->hitpoints <= 0)
            {   objtype*column = (objtype*)(ob->whatever);

                EnableObject((long)column);
                ob->whatever = NULL;

                KillActor(ob);
                NewState(ob,&s_darkmonkfastspawn);
                AddMessage("Oscuro flees!",MSG_CHEAT);
                return;
            }
            NewState(ob,&s_darkmonkcharge1);
        }

    }
    else if (ob->temp1)
        NewState(ob,&s_darkmonklightning1);
    else
        NewState(ob,&s_dmgreenthing1);
    ob->temp1 ^= 1;
    ob->dirchoosetime = 0;

}


void T_DarkmonkChase(objtype*ob)
{   int chance,dx,dy,dist;


    if (!Near(ob,PLAYER[0],2))
    {   if (ob->z > (maxheight - 100))
        {   MISCVARS->monkz -= MZADJUST;
            ob->z = nominalheight + (MISCVARS->monkz >> 16);
            //ob->z--;
            return;
        }
    }
    else if (ob->z < nominalheight)
    {   MISCVARS->monkz += MZADJUST;
        ob->z = nominalheight + (MISCVARS->monkz >> 16);
        //ob->z++;
        return;
    }


    if (CheckLine(ob,PLAYER[0],SIGHT))
    {   ob->targettilex = PLAYER[0]->x;
        ob->targettiley = PLAYER[0]->y;
    }

    if (!ob->ticcount)
    {
        if (CheckLine(ob,PLAYER[0],SHOOT))   // got a shot at player?
        {   dx = abs(ob->tilex - PLAYER[0]->tilex);
            dy = abs(ob->tiley - PLAYER[0]->tiley);
            dist = dx>dy ? dx : dy;
            if (!dist || dist==1)
                chance = 300;//300;
            else
                chance = 400/dist;//300/dist;

            if (GameRandomNumber("T_DarkMonkChase",0) < chance)
            {   NewState(ob,&s_dmlandandfire);
                return;
            }
        }
    }

    if (ob->dirchoosetime)
        ob->dirchoosetime--;

    if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime))
    {   SelectChaseDir(ob);
        ob->dirchoosetime = M_CHOOSETIME(ob);
    }

    else
    {   if (NOMOM)
            ParseMomentum(ob,dirangle8[ob->dir]);
        ActorMovement(ob);

    }

}


//====================== End of Boss Functions ===========================//






void T_GunStand(objtype*ob)
{   int dy,dx,infrontof,dz;
    objtype* temp;

// if (ob->target)
//  Error("gun reset with non-null target");
    for(temp = firstareaactor[ob->areanumber]; temp; temp= temp->nextinarea)
    {   if (temp == ob)
            continue;
        if (temp->obclass == ob->obclass)
            continue;
        if ((!(temp->flags & FL_SHOOTABLE)) || (temp->flags & FL_DYING))
            continue;

        dy = ob->y - temp->y;
        dx = ob->x - temp->x;
        dz = ob->z - temp->z;
        if ((abs(dy)>0x40000) || (abs(dx)>0x40000) || (abs(dz) > 20))
            continue;


        infrontof = 0;

        switch (ob->dir)
        {
        case north:
            if ((dy > 0) && (abs(dx)<0x8000))
                infrontof = 1;
            break;

        case east:
            if ((dx < 0) && (abs(dy)<0x8000))
                infrontof = 1;
            break;

        case south:
            if ((dy < 0) && (abs(dx)<0x8000))
                infrontof = 1;
            break;

        case west:
            if ((dx > 0) && (abs(dy)<0x8000))
                infrontof = 1;
            break;

        default:
            break;
        }

        if (infrontof && CheckLine(ob,temp,SHOOT))
        {   ob->target = temp;
            NewState(ob,&s_gunraise1);
            return;
        }
    }

}


void T_4WayGunStand(objtype*ob)
{
    int dy,dx,dz;
    objtype* temp;

    if (ob->target)
        Error("gun reset with non-null target");
    for(temp = firstareaactor[ob->areanumber]; temp; temp= temp->nextinarea)
    {
        if (temp == ob)
            continue;

        if (temp->obclass == ob->obclass)
            continue;

        if ((!(temp->flags & FL_SHOOTABLE)) || (temp->flags & FL_DYING))
            continue;

        dy = abs(ob->x-temp->x);
        dx = abs(ob->y-temp->y);
        dz = abs(ob->z-temp->z);
        if ((dx < 0x40000) && (dy < 0x40000) && (dz< 20) && CheckLine(ob,temp,SHOOT))
        {   //if ((dx < 0x8000) || (dy <0x8000))
            ob->target = temp;
            NewState(ob,&s_4waygunfire1);
            return;
        }
    }
}


void A_GunShoot(objtype*ob)
{   int   dx,dy,dz,damage,infrontof,tnear,savedangle;
    objtype * target;

    if (!ob->ticcount)
    {   target = (objtype*)(ob->target);
        if (!target)
            Error("an instance of %s called gunshoot without a target\n",debugstr[ob->obclass]);
        if ((!(target->flags & FL_SHOOTABLE)) || (target->flags & FL_DYING))
        {   NewState(ob,&s_gunlower1);
            ob->target = NULL;
            return;
        }

        dx = target->x-ob->x;
        dy = ob->y-target->y;
        dz = ob->z-target->z;


        tnear = ((abs(dy)<0x40000) && (abs(dx)<0x40000) && (abs(dz) < 20));
        infrontof = 0;

        switch (ob->dir)
        {
        case north:
            if ((dy > 0) && (abs(dx)<0x8000))
                infrontof = 1;
            break;

        case east:
            if ((dx < 0) && (abs(dy)<0x8000))
                infrontof = 1;
            break;

        case south:
            if ((dy < 0) && (abs(dx)<0x8000))
                infrontof = 1;
            break;

        case west:
            if ((dx > 0) && (abs(dy)<0x8000))
                infrontof = 1;
            break;

        default:
            break;
        }

        if ((!infrontof) || (!CheckLine(ob,target,SHOOT)) ||
                (!tnear))
        {   NewState(ob,&s_gunlower1);
            ob->target = NULL;
            return;
        }

        //SD_PlaySoundRTP(SD_FIRE,PLAYER[0]->x,PLAYER[0]->y,ob->x,ob->y);
        //hitchance = 128;

//	if (!target)
        //		Error("object called shoot without a target\n");


        damage = DMG_AHGUN;


        if (target->obclass == playerobj)
        {   target->target = ob;
            if (target->flags & FL_BPV)
                damage >>= 1;

        }
        savedangle = ob->angle;
        ob->angle = atan2_appx(dx,dy);
        RayShoot(ob,damage,GameRandomNumber("A_GunShoot Accuracy",0) % 20);
        ob->angle = savedangle;
        SD_PlaySoundRTP(SD_BIGEMPLACEFIRESND,ob->x,ob->y);
    }

}


void A_4WayGunShoot(objtype*ob)
{
    int   dx,dy,dz,damage,savedangle;
    objtype * target;

    if (ob->ticcount == (ob->state->tictime >> 1))
    {
        target = (objtype*)(ob->target);
        if (!target)
            Error("an instance of %s called 4waygunshoot without a target\n",debugstr[ob->obclass]);
        dx = abs(target->x-ob->x);
        dy = abs(ob->y-target->y);
        dz = abs(ob->z-target->z);
        if ((dx > 0x40000) || (dy > 0x40000) || (dz > 20) ||
                (!CheckLine(ob,target,SHOOT)) ||
                (!(target->flags & FL_SHOOTABLE)) ||
                (target->flags & FL_DYING)
           )
        {
            ob->target = NULL;
            NewState(ob,&s_4waygun);
            return;
        }


        //SD_PlaySoundRTP(SD_FIRE,PLAYER[0]->x,PLAYER[0]->y,ob->x,ob->y);
        //hitchance = 128;

        // if (!target)
        //     Error("object called shoot without a target\n");


        damage = DMG_AHGUN;
        SD_PlaySoundRTP(BAS[ob->obclass].fire,ob->x,ob->y);

        if (target->obclass == playerobj)
        {
            target->target = ob;
            if (target->flags & FL_BPV)
                damage >>= 1;
        }

        savedangle = ob->angle;
        ob->angle = 0;
        RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20);
        ob->angle = ANG90;
        RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20);
        ob->angle = ANG180;
        RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20);
        ob->angle = ANG270;
        RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20);
        ob->angle = savedangle;
    }
}


void A_Drain (objtype *ob)
{
    int dx,dy,dz,damage;

    dx = abs(PLAYER[0]->x - ob->x);
    dy = abs(PLAYER[0]->y - ob->y);
    dz = abs(PLAYER[0]->z - ob->z);

    if ((dx > TOUCHDIST) || (dy > TOUCHDIST) || (dz > (TOUCHDIST>>10)))
    {
        NewState(ob,&s_dmonkshoot5);
        return;
    }

    if (ob->ticcount)
        return;

    else
    {
        damage = (GameRandomNumber("A_Drain",ob->obclass) >> 3);
        DamageThing (PLAYER[0],damage);
        ob->hitpoints += damage;
        if (ob->hitpoints > starthitpoints[gamestate.difficulty][ob->obclass])
            ob->hitpoints = starthitpoints[gamestate.difficulty][ob->obclass];

        Collision(PLAYER[0],ob,0,0);
        if (PLAYER[0]->flags & FL_DYING)
            PLAYER[0]->target = ob;
        M_CheckPlayerKilled(PLAYER[0]);
        SD_PlaySoundRTP(SD_MONKGRABSND,ob->x,ob->y);
    }
}






void  A_DmonkAttack(objtype*ob)
{   int angle,nobclass,nspeed,altangle1=0,altangle2=0,zoff=0,sound;
    statetype *nstate;


    if (!ob->ticcount)
    {
        ob->hitpoints -= 120;//120;
        if (ob->hitpoints <= 0)
        {
            objtype*column = (objtype*)(ob->whatever);

            EnableObject((long)column);
            ob->whatever = NULL;

            KillActor(ob);
            NewState(ob,&s_darkmonkfastspawn);
            return;
        }
    }


    if (ob->dirchoosetime)
        ob->dirchoosetime --;

    else
    {   sound = BAS[ob->obclass].fire;
        angle = AngleBetween(ob,PLAYER[0]);
        nspeed = 0x6000;

        if (ob->state == &s_darkmonksphere8)
        {   nstate = &s_kessphere1;
            nobclass = p_kesobj;
            ob->dirchoosetime = 70;
        }

        else if (ob->state == &s_darkmonkhball7)
        {   nstate = &s_handball1;
            nobclass = dm_heatseekobj;
            nspeed = 0x3000;
            ob->dirchoosetime = 5;
        }

        else if (ob->state == &s_darkmonkbball7)
        {   nstate = &s_faceball1;
            nobclass = dm_weaponobj;
            nspeed = 0x3000;
            ob->dirchoosetime = 5;
        }

        else if (ob->state == &s_darkmonklightning9)
        {   nstate = &s_lightning;
            nobclass = dm_weaponobj;
            ob->dirchoosetime = 3;
            sound++;
        }

        else if (ob->state == &s_dmgreenthing8)
        {   nstate = &s_energysphere1;
            nobclass = dm_weaponobj;
            sound +=2;
            ob->dirchoosetime = 70;
        }

        else if (ob->state == &s_darkmonkfspark5)
        {   nstate = &s_floorspark1;

            altangle1 = angle + ANGLES/24;
            altangle2 = angle - ANGLES/24;
            Fix(altangle1);
            Fix(altangle2);
            nobclass = dm_weaponobj;
            ob->dirchoosetime = 3;
            sound += 3;
        }

        else if (ob->state == &s_darkmonkbreathe6)
        {   nstate = &s_crossfire1;
            ob->dirchoosetime = 3;
            nobclass = dm_weaponobj;
            zoff = -15;
            sound += 3;
        }

        SpawnMissile(ob,nobclass,nspeed,angle,nstate,0xb000);
        SD_PlaySoundRTP(sound,ob->x,ob->y);

        new->z = ob->z+zoff;
        if (altangle1)
        {
            SpawnMissile(ob,nobclass,nspeed,altangle1,nstate,0xb000);
            SpawnMissile(ob,nobclass,nspeed,altangle2,nstate,0xb000);
        }
    }



}



#endif // SHAREWARE endif




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




/*
===============
=
= T_Stand
=
===============
*/

void T_Stand (objtype *ob)
{
    if (!ob->ticcount)
        SightPlayer (ob);
    else
        SoftError("\n ob type %s ticcount of %d in T_Stand",debugstr[ob->obclass],
                  ob->ticcount);
}





void DamagePlayerActor(objtype *ob, int damage)

{
    playertype *pstate;

    switch (gamestate.difficulty)
    {
    case 0:
        damage >>= 1;
        break;
    case 1:
        damage -= (damage >> 2);
        break;
    case 2:
        break;
    case 3:  //damage += (damage>>2);
        break;
        //default: Error("Um, Gamestate.Difficulty, uh, has problems.\n");
    }

    if (!damage) damage++;

    M_LINKSTATE(ob,pstate);


    pstate->health -= damage;
    ob->hitpoints = pstate->health;

    SD_PlaySoundRTP(SD_PLAYERTCHURTSND+(pstate->player),ob->x,ob->y);
    if (ob==player)
    {
        damagecount += damage;
        if ( SHOW_BOTTOM_STATUS_BAR() )
            DrawBarHealth (false);
    }

    if (pstate->health<=0)
    {
        pstate->health = 0;
        ob->hitpoints = 0;
    }
}




void DamageNonPlayerActor(objtype *ob,int damage)
{
    //if ((ob->obclass == b_darksnakeobj) && (!ob->temp3))
    // return;

    if (!(ob->flags & FL_ATTACKMODE))
        damage <<= 1;

    ob->hitpoints -= damage;
    if (ob->hitpoints <= 0)
    {
        int sound;

        sound = BAS[ob->obclass].die;
        if (ob->obclass == lowguardobj)
        {
            if (ob->shapeoffset)
                sound ++;
        }
        SD_PlaySoundRTP(sound,ob->x,ob->y);
    }
    else
        SD_PlaySoundRTP(BAS[ob->obclass].hit,ob->x,ob->y);

#if (SHAREWARE == 0)
    if ((ob->obclass == b_robobossobj) && (ob->temp2 <= 2))
    {
        if (ob->hitpoints <
                ((3-ob->temp2)*starthitpoints[gamestate.difficulty][ob->obclass]>>2)
           )
        {
            SD_PlaySoundRTP(SD_NMEAPARTSND,ob->x,ob->y);
            ob->temp2++;
            ob->shapeoffset += 16;
            ob->speed += 0x500;
            SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj);
            new->temp1 = 3;
            new->flags |= FL_ABP;
            MakeActive(new);
        }
        if (ob->temp2 == 1)
            NewState((objtype*)(ob->whatever),&s_NMEhead2);
    }
#endif
    MISCVARS->madenoise = true;
}



void DamageStaticObject(statobj_t*tempstat,int damage)
{


    tempstat->hitpoints -= damage;
    if (tempstat->hitpoints <= 0)
    {
        sprites[tempstat->tilex][tempstat->tiley]=NULL;
        tempstat->flags |= FL_NONMARK;
        if (tempstat->flags&FL_LIGHT)
        {

            if (MAPSPOT(tempstat->tilex,tempstat->tiley,2))
            {   touchplatetype *tplate;

                for(tplate=touchplate[tempstat->linked_to]; tplate; tplate = tplate->nextaction)
                    if (tplate->whichobj == (long)(tempstat))
                        RemoveTouchplateAction(tplate,tempstat->linked_to);
            }

            if (tempstat->flags & FL_LIGHTON)
                TurnOffLight(tempstat->tilex,tempstat->tiley);


            if (tempstat->itemnumber<=stat_chandelier)
                //SpawnFallingDebris(tempstat->x,tempstat->y,tempstat->z-32);
            {
                objtype *prevlast = LASTACTOR;

                SpawnSlowParticles(gt_sparks,4,tempstat->x,tempstat->y,tempstat->z-32);
                for(prevlast = prevlast->next; prevlast; prevlast= prevlast->next)
                {
                    prevlast->momentumz = 1; // any positive value will do
                    prevlast->momentumx >>= 1;
                    prevlast->momentumy >>= 1;
                }
            }
            else
            {

                SpawnStatic(tempstat->tilex,tempstat->tiley,stat_metalshards,-1);
                LASTSTAT->flags |= (FL_ABP|FL_NONMARK);
                sprites[tempstat->tilex][tempstat->tiley] = NULL;
                MakeStatActive(LASTSTAT);
                switch (tempstat->itemnumber)
                {
                case stat_lamp:
                case stat_altbrazier1:
                case stat_altbrazier2:
                case stat_torch:
                    SpawnSlowParticles(gt_sparks,5,tempstat->x,tempstat->y,tempstat->z-32);
                    break;
                case stat_floorfire:
                    SpawnSlowParticles(gt_sparks,5,tempstat->x,tempstat->y,tempstat->z);
                    break;
                default:
                    ;
                }
            }
            SpawnSolidStatic(tempstat);
            SD_PlaySoundRTP(SD_ITEMBLOWSND,tempstat->x,tempstat->y);
        }
        else
        {
            switch (tempstat->itemnumber)
            {
            case stat_dariantouch:
                MISCVARS->ETOUCH[tempstat->linked_to].x = MISCVARS->ETOUCH[tempstat->linked_to].y = 0;
            case stat_tntcrate:
            case stat_bonusbarrel:
                SpawnNewObj(tempstat->tilex,tempstat->tiley,&s_staticexplosion1,inertobj);
                MakeActive(new);
                new->flags |= FL_ABP;
                new->whatever = tempstat;
                new->temp2 = damage;

                if (tempstat->itemnumber == stat_bonusbarrel)
                {
                    int rand = GameRandomNumber("DamageThing",0);

                    if (rand < 80)
                    {
                        if (rand & 1)
                            SpawnStatic(tempstat->tilex,tempstat->tiley,stat_monkmeal,-1);
                        else
                            SpawnStatic(tempstat->tilex,tempstat->tiley,stat_priestporridge,-1);
                        gamestate.healthtotal ++;
                    }
                    else if (rand < 160)
                    {
                        if (rand & 1)
                            SpawnStatic(tempstat->tilex,tempstat->tiley,stat_lifeitem1,-1);
                        else
                            SpawnStatic(tempstat->tilex,tempstat->tiley,stat_lifeitem3,-1);
                    }
                    else
                    {
                        if (rand & 1)
                            SpawnStatic(tempstat->tilex,tempstat->tiley,stat_mp40,-1);
                        else
                        {
                            SpawnStatic(tempstat->tilex,tempstat->tiley,stat_heatseeker,-1);
                            gamestate.missiletotal ++;
                        }
                        LASTSTAT->flags &= ~FL_RESPAWN;
                    }
                    //LASTSTAT->flags &= ~FL_SHOOTABLE;
                    LASTSTAT->flags |= FL_ABP;
                    MakeStatActive(LASTSTAT);
                    SD_PlaySoundRTP(SD_BONUSBARRELSND,tempstat->x,tempstat->y);
                }
                else
                {
                    ExplodeStatic(tempstat);
                    if (tempstat == touchsprite)
                        touchsprite = NULL;
                }
                SpawnSolidStatic(tempstat);
                //SD_Play(SD_EXPL);
                break;
#if (SHAREWARE == 0)
            case stat_mine:
                SpawnNewObj(tempstat->tilex,tempstat->tiley,&s_grexplosion1,inertobj);
                MakeActive(new);
                new->flags |= FL_ABP;
                new->whatever = tempstat;
                new->temp2 = damage;
                RemoveStatic(tempstat);
                break;

            case stat_tomlarva:
                SD_PlaySoundRTP(SD_ACTORSQUISHSND,tempstat->x,tempstat->y);
                SpawnGroundExplosion(tempstat->x,tempstat->y,tempstat->z);
                //MISCVARS->gibgravity = GRAVITY/2;
                MISCVARS->fulllightgibs = true;
                SetGibSpeed(0x4000);
                SpawnSlowParticles(GUTS,30,tempstat->x, tempstat->y,tempstat->z);
                ResetGibSpeed();
                MISCVARS->fulllightgibs = false;
                //MISCVARS->gibgravity = -1;
                RemoveStatic(tempstat);
                break;
#endif

            case stat_lifeitem1:
            case stat_lifeitem2:
            case stat_lifeitem3:
            case stat_lifeitem4:
                SD_PlaySoundRTP(SD_ITEMBLOWSND,tempstat->x,tempstat->y);
                gamestate.treasurecount ++;
                SpawnSlowParticles(gt_sparks,10,tempstat->x,tempstat->y,tempstat->z);
                SpawnSolidStatic(tempstat);
                break;

            default:

                if ((tempstat->itemnumber == stat_plant) ||
                        (tempstat->itemnumber == stat_tree))
                    gamestate.plantcount++;

                //tempstat->shapenum = -1;
                //tempstat->flags &= ~FL_SHOOTABLE;
                ExplodeStatic(tempstat);
                SpawnSolidStatic(tempstat);
                break;
            }
        }
    }
}


void DamageThing (void *thing, int damage)
{
    objtype* tempactor;
    statobj_t* tempstat;


    tempactor = (objtype*)thing;
    if (!tempactor)
        return;

    if ((tempactor->which == ACTOR) && (!(tempactor->flags & FL_SHOOTABLE)))
        return;

    if ((tempactor->which == ACTOR) || (tempactor->which == SPRITE))
    {
        if (tempactor->which == ACTOR)
        {
            if (tempactor->obclass == playerobj)
            {
                if ((tempactor->flags & FL_GODMODE) ||
                        (tempactor->flags & FL_DOGMODE) ||
                        godmode ||
                        (gamestate.battlemode == battle_Eluder)
                   )
                    return;
                DamagePlayerActor(tempactor,damage);
            }

            else
            {
                if ((tempactor->obclass == collectorobj) && (gamestate.SpawnEluder))
                    return;
                if (tempactor->hitpoints <= 0)
                    return;
                DamageNonPlayerActor(tempactor,damage);
            }
        }
        else
        {
            tempstat = (statobj_t*)thing;

            MISCVARS->madenoise = true;
            if (!(tempstat->flags & FL_SHOOTABLE))
                return;
            DamageStaticObject(tempstat,damage);
        }
    }
}


void ExplodeStatic(statobj_t*tempstat)
{
//SpawnSolidStatic(tempstat);

    if (tempstat->flags & FL_WOODEN)
    {
        SpawnStatic(tempstat->tilex,tempstat->tiley,stat_woodfrag,-1);
        if ((gamestate.BattleOptions.RespawnItems) &&
                (tempstat->itemnumber == stat_tntcrate)
           )
        {
            tempstat->linked_to = (long)(LASTSTAT);
            tempstat->flags |= FL_RESPAWN;
        }

    }
    else if (tempstat->flags & FL_METALLIC)
        SpawnStatic(tempstat->tilex,tempstat->tiley,stat_metalfrag,-1);
    else
        SpawnStatic(tempstat->tilex,tempstat->tiley,stat_rubble,-1);
    LASTSTAT->flags |= (FL_ABP|FL_NONMARK);
    sprites[tempstat->tilex][tempstat->tiley] = NULL;
    MakeStatActive(LASTSTAT);
    SD_PlaySoundRTP(SD_ITEMBLOWSND,tempstat->x,tempstat->y);


}





void EnableObject(long object)
{
    objtype* ob;
    int i,gasicon;
    doorobj_t*tdoor;

    ob = (objtype*)object;

#if (BNACRASHPREVENT == 1)//
    if (ob == 0) {
        return;
    }
#endif

    ob->flags |= FL_ACTIVE;
    if (ob->obclass == bladeobj)
    {
        ParseMomentum(ob,dirangle8[ob->dir]);
        if (ob->whatever)
        {
            objtype *passenger=(objtype*)(ob->whatever);

            passenger->momentumx += ob->momentumx;
            passenger->momentumy += ob->momentumy;
        }
    }

    if (ob->obclass == gasgrateobj)
    {
        NewState(ob,&s_gas2);
        SD_PlaySoundRTP(SD_GASSTARTSND,ob->x,ob->y);
        ob->dirchoosetime = GASTICS;
        for(i=0; i<doornum; i++)
        {

            tdoor = doorobjlist[i];
            gasicon = MAPSPOT(tdoor->tilex,tdoor->tiley,1);
            if (gasicon == GASVALUE)
                LinkedCloseDoor(i);
        }

        MU_StoreSongPosition();
        MU_StartSong(song_gason);
        MISCVARS->GASON = 1;
        ob->temp3 = 105;
    }
    else if (ob->obclass == pillarobj)
    {
        ob->flags |= FL_FLIPPED;
        gamestate.secretcount++;
    }
    if (!(ob->flags & FL_ABP))
    {
        ob->flags |= FL_ABP;
        MakeActive(ob);
    }
}

void DisableObject(long object)
{   objtype*ob;

    ob = (objtype*)object;
    ob->flags &= ~FL_ACTIVE;
}


void T_MoveColumn(objtype* ob)
{   int spot,index;


    if (!(ob->flags & FL_ACTIVE))
        return;
    /*
     switch (ob->dir)
      {case north:
    	 ob->momentumy = -PILLARMOM;
    	 break;
    	case south:
    	 ob->momentumy =  PILLARMOM;
    	 break;
    	case east:
    	 ob->momentumx =  PILLARMOM;
    	 break;
    	case west:
    	 ob->momentumx = -PILLARMOM;
    	 break;
      }

    */
    ActorMovement(ob);
    index = touchindices[ob->tilex][ob->tiley];
    if (index)
        TRIGGER[index-1] = 1;
    ob->temp1 -= PILLARMOM;

    if ((ob->temp1 <= 0) || NOMOM)
    {   ZEROMOM;
        ob->temp1 = 0x20000;
        ob->flags &= ~FL_ACTIVE;
        spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS;
        if ((spot >= 0) && (spot <= 7))
        {   ob->dir = spot;
            if (!ob->temp2)
            {
                gamestate.secrettotal++;
            }
            else
            {
                ob->flags |= FL_ACTIVE;
            }
            ParseMomentum(ob,dirangle8[ob->dir]);
        }
        else
            ob->flags |= FL_DONE;

    }
}



boolean NextToDoor(objtype*ob)
{
    int tilex,tiley,centerx,centery,dx,dy;

    tilex = ob->tilex;
    tiley = ob->tiley;


    if (M_ISDOOR(tilex+1,tiley) || M_ISDOOR(tilex-1,tiley))
    {   centery = (tiley << TILESHIFT) + HALFGLOBAL1;
        dy = abs(ob->y - centery);
        if (dy < 0x2000)
            return true;
    }

    if (M_ISDOOR(tilex,tiley+1) || M_ISDOOR(tilex,tiley-1))
    {   centerx = (tilex << TILESHIFT) + HALFGLOBAL1;
        dx = abs(ob->x - centerx);
        if (dx < 0x2000)
            return true;
    }

    return false;
}


/*
=================
=
= T_Use
=
=================
*/


void T_Use(objtype*ob)
{   if (ob->ticcount)
        return;

    switch (ob->obclass)
    {
#if (SHAREWARE == 0)
    case b_darianobj:
        if (touchsprite && !DoPanicMapping())
            touchsprite->flags |= FL_ACTIVE;
        if ((!sprites[PLAYER[0]->tilex][PLAYER[0]->tiley]) && (ob->areanumber == PLAYER[0]->areanumber))
        {   SpawnNewObj(PLAYER[0]->tilex,PLAYER[0]->tiley,&s_dspear1,spearobj);
            new->flags |= (FL_ABP);//|FL_INVULNERABLE);
            new->z = 0;
            MakeActive(new);
        }
        ZEROMOM;
        ob->flags |= FL_STUCK;
        SD_PlaySoundRTP(SD_DARIANUSESND,ob->x,ob->y);
        //NewState(ob,&s_darianspears);
        break;
#endif
    default:
        ;
    }


}





#define RollStart(ob,state,angle)     \
   {                                  \
   int oldspeed = ob->speed;          \
                                      \
   ob->speed = ROLLMOMENTUM+0x200;    \
   NewState(ob,state);                \
   ParseMomentum(ob,angle);           \
   ob->speed = oldspeed;              \
   }                                  \





void AvoidPlayerMissile(objtype*ob)
{
    objtype *temp;
    int dx,dy,dz;
    int magangle,angle1,rollangle1,rollangle2,dangle1,dangle2;

    if (PLAYER0MISSILE == NULL)
        return;

    if (GameRandomNumber("scott missile avoid",0) > 160)
        return;

    if (ob->momentumz)
        return;

    if ((ob->state->think == T_Roll) || (ob->state->think == T_Reset))
        return;

    temp = PLAYER0MISSILE;

    dx = abs(temp->x - ob->x);
    dy = abs(ob->y - temp->y);
    dz = abs(ob->z - temp->z);
    angle1 = AngleBetween(temp,ob);
    magangle = abs(temp->angle - angle1);

    if (magangle > VANG180)
        magangle = ANGLES - magangle;


    if ((magangle > ANGLES/48) || (dx > 0x50000) || (dy > 0x50000) ||
            (dz > 32))
        return;

    rollangle1 = angle1 + ANGLES/4;
    Fix(rollangle1);
    dangle1 = abs(temp->angle - rollangle1);
    if (dangle1 > VANG180)
        dangle1 = ANGLES - dangle1;

    rollangle2 = angle1 - ANGLES/4;
    Fix(rollangle2);
    dangle2 = abs(temp->angle - rollangle2);
    if (dangle2 > VANG180)
        dangle2 = ANGLES - dangle2;

    ob->momentumx = ob->momentumy = 0;

    if (dangle1 > dangle2)
    {
        RollStart(ob,&s_strikerollleft1,rollangle1);
    }
    else
    {
        RollStart(ob,&s_strikerollright1,rollangle2);
    }
    ob->flags |= FL_NOFRICTION;

    ob->target = PLAYER[0];
    //SelectRollDir(ob);


}




/*
=================
=
= T_Chase
=
=================
*/

void T_Chase (objtype *ob)
{
    int   dx,dy,dz,dist,chance;
    classtype ocl;
    statetype *temp;
    boolean doorok;

    ocl = ob->obclass;


    if ((ocl == deathmonkobj) || (ocl == blitzguardobj))
    {   dx = abs(PLAYER[0]->x - ob->x);
        dy = abs(ob->y - PLAYER[0]->y);
        dz = abs(ob->z - PLAYER[0]->z);

        if ((dx < TOUCHDIST) && (dy < TOUCHDIST) && (dz < (TOUCHDIST >> 10)))
        {
#if (SHAREWARE == 0)
            if (ocl == deathmonkobj)
            {   NewState(ob,&s_dmonkshoot1);
                STOPACTOR(ob);
                return;

            }
            else
#endif
                if ((!ob->temp3) && (PLAYERSTATE[0].missileweapon != -1) &&
                        (PLAYERSTATE[0].missileweapon < wp_godhand))
                {   NewState(ob,&s_blitzsteal1);
                    STOPACTOR(ob);
                    return;
                }
        }
    }

    ob->flags &= ~FL_DODGE;

    //if (CheckLine(ob,PLAYER[0],DIRCHECK) && (ocl != roboguardobj))

    if (!ob->ticcount)
    {   if (CheckLine(ob,PLAYER[0],SIGHT))   // got a shot at player?
        {   if (ocl != roboguardobj)
            {   ob->targettilex = PLAYER[0]->x;
                ob->targettiley = PLAYER[0]->y;
            }
        }

        if (CheckLine(ob,PLAYER[0],SHOOT) && (!(player->flags&FL_DYING)))   // got a shot at player?
        {
            dx = abs(ob->tilex - PLAYER[0]->tilex);
            dy = abs(ob->tiley - PLAYER[0]->tiley);
            dist = (dx>dy)?(dx):(dy);
            if ((!dist) || (dist==1))
                chance = 300;
            else if (ocl >= b_darianobj)
                chance = 400/dist;
            else
                chance = 300/dist;
            

            if (GameRandomNumber("T_Chase",ocl) <chance)
            {   if ((ocl == b_heinrichobj) && (Near(ob,PLAYER[0],4)))
                    goto cdoor;

                ob->dir = angletodir[AngleBetween(ob,PLAYER[0])];
                STOPACTOR(ob);
#if (SHAREWARE == 0)
                if ((ocl == overpatrolobj) && (!Near(ob,PLAYER[0],3)) &&
                        (!PLAYERSTATE[0].NETCAPTURED) && (!MISCVARS->NET_IN_FLIGHT))
                {   NewState(ob,&s_opbolo1);
                    MISCVARS->NET_IN_FLIGHT = true;
                    return;
                }
#endif
                if ((ocl == triadenforcerobj) && (!Near(ob,PLAYER[0],3)))
                {   NewState(ob,&s_enforcerthrow1);
                    return;
                }

                if ((temp=M_S(AIM)) != NULL)
                {   if ((ob->flags & FL_HASAUTO) && (!ob->temp3))
                        ob->temp3 = (GameRandomNumber("T_Chase FL_HASAUTO",ocl) % 5) + 3;
                    ob->target = PLAYER[0];
                    NewState(ob,temp);
                    return;
                }

            }
            //if ((CheckSight(ob,PLAYER[0])) && (!ob->angle))// &&
            //(ocl != b_heinrichobj))
            //ob->flags |= FL_DODGE;
        }

    }
cdoor:
    doorok = NextToDoor(ob);


    if (ob->dirchoosetime)
        ob->dirchoosetime--;

    if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime) || doorok)
    {   //if (ob->flags & FL_DODGE)
        // SelectDodgeDir(ob);
        //else
        SelectChaseDir(ob);
        ob->dirchoosetime = M_CHOOSETIME(ob);
    }

    else
    {   if (NOMOM)
            ParseMomentum(ob,dirangle8[ob->dir]);
        ActorMovement(ob);
    }
}



void SpawnMissile(objtype* shooter,classtype nobclass,int nspeed,int nangle,statetype*nstate,int offset)
{
    GetNewActor();
    MakeActive(new);
    new->which = ACTOR;
    new->obclass = nobclass;
    new->angle = nangle;
    new->speed = nspeed;
    if (shooter->obclass == playerobj)
        offset += FindDistance(shooter->momentumx,shooter->momentumy);

    SetFinePosition(new,shooter->x + FixedMul(offset,costable[nangle]),
                    shooter->y - FixedMul(offset,sintable[nangle]));
    SetVisiblePosition(new,new->x,new->y);
    //SoftError("\n missx:%d, missy:%d, speed:%d, offset:%d, angle%d, drawx:%d, drawy:%d",
    //         new->x,new->y,nspeed,offset,nangle,new->drawx,new->drawy);

    new->z = shooter->z;
    new->areanumber = shooter->areanumber;
    new->soundhandle = -1;
    if (nobclass != inertobj)
    {
        MakeLastInArea(new);
        if (MissileSound == true)
            new->soundhandle = SD_PlaySoundRTP(BAS[new->obclass].operate,new->x,new->y);
    }

    if ((shooter->obclass == playerobj) || (shooter->obclass == wallopobj) ||
            (shooter->obclass == b_robobossobj))
    {
        Set_3D_Momenta(new,new->speed,new->angle,shooter->yzangle);

        if (nobclass == p_drunkmissileobj)
            new->temp1 = new->momentumz;

        new->z -= FixedMulShift(offset,sintable[shooter->yzangle],26);
        if ((shooter->obclass == playerobj) && (shooter->flags & FL_GODMODE))
            new->z -= 10;
    }
    else
        ParseMomentum(new,new->angle);

    if (nobclass == p_drunkmissileobj)
        new->temp3 = VBLCOUNTER/3;


    new->flags |= (FL_NEVERMARK|FL_ABP|FL_NOFRICTION|FL_FULLLIGHT);
    new->whatever = shooter; // keep track of missile possession
    NewState(new,nstate);
}



/*
=================
=
= T_Roll
=
=================
*/


void T_Roll (objtype *ob)
{
    ActorMovement(ob);

}




/*
===============
=
= T_Path
=
===============
*/

void T_Path (objtype *ob)
{   int dx,dy,dz,ocl,damage=1;
    objtype*temp,*ttarg,*twhat;


    ocl = ob->obclass;

    if (((ocl == firejetobj) || (ocl == bladeobj)) && (!ob->ticcount))
    {
        if (ocl == bladeobj)
        {
            if (ob->state->condition & SF_DOWN )
                ob->flags &= ~FL_BLOCK;
            else if (ob->state->condition & SF_UP)
            {
                ob->flags |= FL_BLOCK;
                damage = 0;
            }
        }

        if ((ob->state->condition & SF_SOUND) && areabyplayer[ob->areanumber])
            SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y);

        if (damage)
        {
            for(temp=firstareaactor[ob->areanumber]; temp; temp=temp->nextinarea)
            {
                if (temp == ob)
                    continue;

                if (temp->obclass >= roboguardobj)
                    continue;

                //WAS
                ttarg = (objtype*)(temp->target);
                twhat = (objtype*)(temp->whatever);

                if ((M_ISACTOR(ttarg) && (ttarg->obclass == b_robobossobj)) ||
                        (M_ISACTOR(twhat) && (twhat->obclass == b_robobossobj))
                   )
                    continue;

                if ((!(temp->flags & FL_SHOOTABLE)) || (temp->flags & FL_DYING))
                    continue;

                if (temp->obclass == playerobj)
                {
                    if ((temp->flags & FL_GODMODE) || (temp->flags & FL_DOGMODE))
                        continue;
                    if ((temp->flags & FL_AV) && (ocl == firejetobj))
                        continue;
                }
                dx = temp->x - ob->x;
                if (abs(dx) > 0xa000)
                    continue;
                dy = temp->y - ob->y;
                if (abs(dy) > 0xa000)
                    continue;

                //if (temp->obclass == playerobj)
                //Collision(temp,-temp->momentumx+ob->momentumx,-temp->momentumy + ob->momentumy);
                dz = temp->z - ob->z;
                if (abs(dz) > 32)
                    continue;

                DamageThing(temp,EnvironmentDamage(ob));
                if ((ocl == firejetobj) && (temp->obclass < roboguardobj))
                    SD_PlaySoundRTP(SD_PLAYERBURNEDSND,temp->x,temp->y);

                if ((gamestate.violence == vl_excessive) && (temp->obclass < roboguardobj))
                {
                    if (ocl == bladeobj)
                    {
                        SpawnParticles(temp,GUTS,1);
                        if (temp->hitpoints <= 0)
                            temp->flags |= FL_HBM;
                    }
                    else if (ocl == firejetobj)
                    {
                        if ((temp->hitpoints <= 0) && (temp->z == nominalheight))
                        {

                            temp->hitpoints = 0;
                            temp->flags |= FL_SKELETON;
                            if (temp->obclass == playerobj)
                            {
                                playertype *pstate;

                                temp->flags &= ~FL_COLORED;
                                M_LINKSTATE(temp,pstate);
                                pstate->health = 0;
                                pstate->weapon = -1;
                            }

                            Collision(temp,ob,-temp->momentumx,-temp->momentumy);
                            M_CheckPlayerKilled(temp);

                            continue;
                        }
                    }
                }
                //SD_PlaySoundRTP(SD_ACTORBURNEDSND,temp->x,temp->y);

                //        if ((ocl == bladeobj) || (ob->state->condition == SF_CRUSH))
                Collision(temp,ob,-temp->momentumx,-temp->momentumy);
                M_CheckPlayerKilled(temp);

            }
        }
    }


    if (ob->dir == nodir)
        return;

    if ((ocl != firejetobj) && (ocl != bladeobj) && (ocl != diskobj))
    {
        if (!ob->ticcount)
        {
            if (SightPlayer (ob))
                return;
        }
        else
            SoftError("\n ob type %s with ticcount %d in T_Path",
                      debugstr[ob->obclass],ob->ticcount);
    }

    SelectPathDir (ob);
    if (NOMOM)
        ParseMomentum(ob,dirangle8[ob->dir]);
}



int EnvironmentDamage(objtype *ob)
{
    if (BATTLEMODE && (gamestate.BattleOptions.DangerDamage != bo_danger_normal))
    {
        return(gamestate.BattleOptions.DangerDamage);
    }
    else
    {
        int damage = 1;

        switch(ob->obclass)
        {
        case firejetobj:
        case bladeobj:
            damage = 6;
            break;

        case boulderobj:
            damage = 50;
            break;

        case spearobj:
            damage = 7;
            break;

        case gasgrateobj:
            damage = 20;
            break;

        case wallfireobj:
            damage = ((GameRandomNumber("wallfire damage",0) >>3) + 10);
            break;

        case crushcolobj:
            damage = 10;
            break;
        default:
            ;
        }

        if (gamestate.difficulty < gd_hard)
            damage >>= 1;

        return damage;
    }
    //SoftError("unknown environment danger");

}




void T_AutoShootAlign(objtype*ob)
{
    if (ob->dir != (dirtype)ob->temp1)
        ob->dir = dirorder16[ob->dir][NEXT];
    else
        NewState(ob,M_S(AIM));

}


void T_AutoRealign(objtype*ob)
{
    if (ob->dir != (dirtype)ob->targettilex)
        ob->dir = dirorder16[ob->dir][NEXT];
    else
    {   objtype *temp;

        NewState(ob,M_S(PATH));
        for(temp=firstareaactor[ob->areanumber]; temp; temp=temp->nextinarea)
        {   if (temp == ob)
                continue;
            if (temp->obclass != ob->obclass)
                continue;
            if (!temp->state->think)
                NewState(temp,UPDATE_STATES[PATH][temp->obclass-lowguardobj]);
        }
    }
}


/*
===============
=
= T_AutoPath
=
===============
*/

void T_AutoPath (objtype *ob)
{   objtype *temp;

    // ob->temp3 holds random number of shots before resuming path

    if (CheckLine(ob,PLAYER[0],SIGHT) && (Near(ob,PLAYER[0],4) || MISCVARS->madenoise))

    {   int dx,dy,destdir,ocl;
        statetype *align,*wait;

        ocl = ob->obclass;
        dx = player->x - ob->x;
        dy = ob->y - player->y;
        destdir = (angletodir[atan2_appx(dx,dy)] << 1);
        ob->temp1 = destdir;
        ob->targettilex = ob->dir; //save old dir
#if (SHAREWARE == 0)
        if (ocl == wallopobj)
        {   //if (ob->temp3)
            // Error("may be writing over temp3");
            ob->temp3 = (GameRandomNumber("T_WallPath",0)%4) + 1;
            align = &s_wallalign;
            wait = &s_wallwait;
        }
        else
#endif
        {   align = &s_roboalign;
            wait = &s_robowait;
        }

        NewState(ob,align);
        for(temp=firstareaactor[ob->areanumber]; temp; temp=temp->nextinarea)
        {   if (temp == ob)
                continue;

            if (temp->obclass != ob->obclass)
                continue;

            if (temp->flags & FL_DYING)
                continue;

            if (CheckLine(temp,PLAYER[0],SIGHT) && (Near(temp,PLAYER[0],4) || MISCVARS->madenoise))
            {   dx = PLAYER[0]->x - temp->x;
                dy = temp->y - PLAYER[0]->y;
                destdir = (angletodir[atan2_appx(dx,dy)] << 1);

                temp->temp1 = destdir;
                temp->targettilex = temp->dir;
                NewState(temp,align);
                temp->temp3 = ob->temp3;
            }
            else
                NewState(temp,wait);
        }
        return;
    }

    SD_PlaySoundRTP(SD_ROBOTMOVESND,ob->x,ob->y);

    SelectPathDir(ob);

}


void A_Shoot (objtype *ob)
{
    int   dx,dy,dz,dist;
    int   accuracy,damage,sound;
    objtype * target;
    int   num;
    int   savedangle;

    ActorMovement(ob);

    //ob->flags |= FL_FULLLIGHT; bats don't emit light
//if (!(ob->flags & FL_SHOOTABLE))
    //Error("\na dead instance of %s is shooting at you",debugstr[ob->obclass]);

    if (!ob->ticcount)
    {   if (ob->obclass == strikeguardobj)
            ob->flags &= ~FL_NOFRICTION;

        target = (objtype*)(ob->target);
        if (!target)
            Error("an instance of %s called shoot without a target\n",debugstr[ob->obclass]);

        if(!(ob->obclass == blitzguardobj && ob->temp3 == stat_bat))
        {
            ob->flags &= ~FL_FULLLIGHT;
        }
        


        dx = (target->x - ob->x);
        dy = (ob->y - target->y);
        dz = target->z-ob->z;


        if(ob->obclass == blitzguardobj && ob->temp3 == stat_bat )
        {
            //is the target close enough for me to hit with my bat?
            if ((abs(dx) <= 0x10000) && (abs(dy) <= 0x10000) && (abs(dz) <= 20))
                BlitzBatAttack(ob, target);
            else
                //resort to pistol to damage target
                goto pistol;
            ob->target = NULL;
            return;
        }
        
        else if ((ob->obclass == blitzguardobj) && (ob->temp3) &&
                (ob->temp3 != stat_gasmask) && (ob->temp3 != stat_asbesto) &&
                (ob->temp3 != stat_bulletproof) &&
                (gamestate.difficulty >= gd_medium) &&
                ((abs(dx) > 0xc000) || (abs(dy) > 0xc000))
           )
        {
            int i;
            missile_stats* newmissiledata;

            newmissiledata = &PlayerMissileData[GetWeaponForItem(ob->temp3)];

            // ready to annihilate this poor bastard

            SpawnMissile(ob,newmissiledata->obclass,newmissiledata->speed,
                         AngleBetween(ob,player), newmissiledata->state,
                         newmissiledata->offset);

            if (newmissiledata->obclass == p_drunkmissileobj)
            {
                for(i=0; i<4; i++)
                {
                    SpawnMissile(ob,newmissiledata->obclass,newmissiledata->speed,
                                 AngleBetween(ob,player), newmissiledata->state,
                                 newmissiledata->offset);
                }
            }
            ob->target = NULL;
            ob->temp2 --;
            if (ob->temp2 == 0)
                ob->temp3 = 0;
            return;
        }


        if ((!areabyplayer[ob->areanumber]) && (target->obclass ==  playerobj))
            return;

        //if (!CheckLine(ob,target,SHOOT))       // player is behind a wall
        //return;

pistol:
        savedangle=ob->angle;
        ob->angle = atan2_appx (dx,dy);
        dist = FindDistance(dx,dy);
        ob->yzangle = FINEANGLES-atan2_appx(dist, dz<<10);

        if ((ob->yzangle>MAXYZANGLE) && (ob->yzangle<FINEANGLES-MAXYZANGLE))
            ob->yzangle=MAXYZANGLE;

        dist>>=16;

        accuracy=(WHICHACTOR<<4)+((gamestate.difficulty) << 6);

        num = GameRandomNumber("A_Shoot3",ob->obclass);

        if (num<128) num=128; // Don't let accuracy fall below 50% original

        accuracy=FixedMulShift(num,accuracy,8); // scale accuracy based off randomness

        // check for maximum accuracy;

        if (accuracy>255) accuracy=255;

        if (ob->obclass==highguardobj)
            damage=DMG_MP40;
        else if (ob->obclass == triadenforcerobj)
            damage=DMG_MP40;
        else
            damage=DMG_ENEMYBULLETWEAPON;


        RayShoot (ob, damage, 255-accuracy);

        ob->angle=savedangle;
        sound = BAS[ob->obclass].fire;
        SD_PlaySoundRTP(sound,ob->x,ob->y);
        MISCVARS->madenoise = true;
        if ((!(ob->flags& FL_HASAUTO)) || (!ob->temp3))
            ob->target = NULL;
    }
}



/*
===============
=
= A_Shoot
=
= Try to damage the player, based on skill level and player's speed
=
===============
*/

/*
void A_Shoot (objtype *ob)
{
    int   dx,dy,dz,dist;
    int   accuracy,damage,sound;
    objtype * target;
    int   num;
    int   savedangle;

    ActorMovement(ob);

    ob->flags |= FL_FULLLIGHT;
//if (!(ob->flags & FL_SHOOTABLE))
    //Error("\na dead instance of %s is shooting at you",debugstr[ob->obclass]);

    if (!ob->ticcount)
    {   if (ob->obclass == strikeguardobj)
            ob->flags &= ~FL_NOFRICTION;

        target = (objtype*)(ob->target);
        if (!target)
            Error("an instance of %s called shoot without a target\n",debugstr[ob->obclass]);


        ob->flags &= ~FL_FULLLIGHT;


        dx = (target->x - ob->x);
        dy = (ob->y - target->y);
        dz = target->z-ob->z;


        if ((ob->obclass == blitzguardobj) && (ob->temp3) &&
                (ob->temp3 != stat_gasmask) && (ob->temp3 != stat_asbesto) &&
                (ob->temp3 != stat_bulletproof) &&
                (gamestate.difficulty >= gd_medium) &&
                ((abs(dx) > 0xc000) || (abs(dy) > 0xc000))
           )
        {
            int i;
            missile_stats* newmissiledata;

            newmissiledata = &PlayerMissileData[GetWeaponForItem(ob->temp3)];

            // ready to annihilate this poor bastard

            SpawnMissile(ob,newmissiledata->obclass,newmissiledata->speed,
                         AngleBetween(ob,player), newmissiledata->state,
                         newmissiledata->offset);

            if (newmissiledata->obclass == p_drunkmissileobj)
            {
                for(i=0; i<4; i++)
                {
                    SpawnMissile(ob,newmissiledata->obclass,newmissiledata->speed,
                                 AngleBetween(ob,player), newmissiledata->state,
                                 newmissiledata->offset);
                }
            }
            ob->target = NULL;
            ob->temp2 --;
            if (ob->temp2 == 0)
                ob->temp3 = 0;
            return;
        }


        if ((!areabyplayer[ob->areanumber]) && (target->obclass ==  playerobj))
            return;

        //if (!CheckLine(ob,target,SHOOT))       // player is behind a wall
        //return;


        savedangle=ob->angle;
        ob->angle = atan2_appx (dx,dy);
        dist = FindDistance(dx,dy);
        ob->yzangle = FINEANGLES-atan2_appx(dist, dz<<10);

        if ((ob->yzangle>MAXYZANGLE) && (ob->yzangle<FINEANGLES-MAXYZANGLE))
            ob->yzangle=MAXYZANGLE;

        dist>>=16;

        accuracy=(WHICHACTOR<<4)+((gamestate.difficulty) << 6);

        num = GameRandomNumber("A_Shoot3",ob->obclass);

        if (num<128) num=128; // Don't let accuracy fall below 50% original

        accuracy=FixedMulShift(num,accuracy,8); // scale accuracy based off randomness

        // check for maximum accuracy;

        if (accuracy>255) accuracy=255;

        if (ob->obclass==highguardobj)
            damage=DMG_MP40;
        else if (ob->obclass == triadenforcerobj)
            damage=DMG_MP40;
        else
            damage=DMG_ENEMYBULLETWEAPON;


        RayShoot (ob, damage, 255-accuracy);

        ob->angle=savedangle;
        sound = BAS[ob->obclass].fire;
        SD_PlaySoundRTP(sound,ob->x,ob->y);
        MISCVARS->madenoise = true;
        if ((!(ob->flags& FL_HASAUTO)) || (!ob->temp3))
            ob->target = NULL;
    }
}
*/




void A_Repeat(objtype*ob)
{
    ActorMovement(ob);

    if (!ob->ticcount)
    {   ob->temp3 --;
        if (ob->temp3 <= 0)
            NewState(ob,M_S(CHASE));
    }

}


void  A_MissileWeapon(objtype *ob)
{
    int    sound,nspeed,noffset,zoffset;

#if (SHAREWARE == 0)
    int oldyzangle;
#endif
    classtype nobclass;
    statetype*nstate;


    if ((ob->obclass == wallopobj) || (ob->obclass == roboguardobj));
    //SelectPathDir(ob);
    else
        ActorMovement(ob);

    if (!ob->ticcount)
    {
#if (SHAREWARE == 0)
        if ((ob->obclass == wallopobj) && (!ob->temp3))
        {
            NewState(ob,&s_wallrestore);
            return;
        }
#endif
        if ((ob->obclass == b_darianobj) && (!CheckLine(ob,PLAYER[0],SHOOT)))
        {   NewState(ob,M_S(CHASE));
            return;
        }
// Move sounds, flags into switch cases

        sound = BAS[ob->obclass].fire;
        nspeed = 0x4000;
        noffset = 0x8000;
        zoffset = 0;
        switch (ob->obclass)
        {

        case triadenforcerobj:
            nobclass = grenadeobj;
            nstate= &s_grenade1;
            sound++;
            break;


        case roboguardobj:
            nobclass = shurikenobj;
            nspeed = 0x2000;
            noffset = 0x10000;
            nstate = &s_robogrdshuriken1;
            break;

            /*
             case b_darkmonkobj:
            nobclass = dmfballobj;
            nstate = &s_dmfball1;
            break;
             */
            /*
            case b_robobossobj:
            nobclass = bigshurikenobj;
            nstate = &s_oshuriken1;
            break;
             */
#if (SHAREWARE == 0)
        case b_darianobj:
            nobclass = missileobj;
            //nspeed = 0x100;
            //noffset = 0x18000;
            nstate = &s_missile1;
            zoffset = -20;
            break;


        case dfiremonkobj:
            nobclass = fireballobj;
            nstate = &s_monkfire1;
            break;


        case overpatrolobj:
            nobclass = netobj;
            nstate = &s_bolocast1;
            sound ++;
            break;


        case wallopobj:
        {   int dx,dy,dz,xydist;

            ob->temp2 ^= 1; // increment numfired
            ob->temp3 --; // decrement random fire no.


            dx = PLAYER[0]->x-ob->x;
            dy = ob->y-PLAYER[0]->y;
            if (GameRandomNumber("bcraft shoot up/down",0) < 128)
                dz = 10;
            else
                dz = -10;
            xydist = FindDistance(dx,dy);
            oldyzangle = ob->yzangle;
            ob->yzangle = atan2_appx(xydist,dz<<10);
            if (ob->temp2)
            {   nobclass = missileobj;
                nstate = &s_missile1;
            }
            else
            {   nobclass = bigshurikenobj;
                nstate = &s_bstar1;
            }
        }
        break;
#endif
        default:
            ;
        }

        SpawnMissile(ob,nobclass,nspeed,AngleBetween(ob,PLAYER[0]),nstate,noffset);
        new->z += zoffset;
        SD_PlaySoundRTP(sound,ob->x,ob->y);
        MISCVARS->madenoise = true;
        if (ob->obclass == triadenforcerobj)
        {   //new->flags |= (FL_SHOOTABLE);
            new->temp1 = 0x50000;
        }
#if (SHAREWARE == 0)
        else if (ob->obclass == wallopobj)
            ob->yzangle = oldyzangle;
#endif


    }
}


void   A_Wallfire(objtype *ob)
{
    if (!(ob->flags & FL_ACTIVE))
        return;

    if (!ob->ticcount)
    {   SpawnMissile(ob,wallfireobj,0x4000,ob->angle,&s_crossfire1,0xa000);
        if (areabyplayer[ob->areanumber])
            SD_PlaySoundRTP(SD_FIRECHUTESND,ob->x,ob->y);
        new->dir = angletodir[new->angle];
        new->z = nominalheight;
    }

}


void T_Reset(objtype *ob)
{
    ActorMovement(ob);

    if (ob->ticcount)
        return;

    ob->momentumx = ob->momentumy = ob->dirchoosetime = 0;
    ob->flags &= ~FL_NOFRICTION;



}

void SelectRollDir(objtype *ob)
{   int angle,tryx,tryy;


    if (ob->state == &s_strikerollright1)
        angle = AngleBetween(ob,PLAYER[0]) + ANGLES/4;
    else
        angle = AngleBetween(ob,PLAYER[0]) - ANGLES/4;

    Fix(angle);
    tryx = ob->x + FixedMul(0x20000l,costable[angle]);
    tryy = ob->y - FixedMul(0x20000l,sintable[angle]);
    ZEROMOM;
    if (QuickSpaceCheck(ob,tryx,tryy))
    {   int oldspeed;

        oldspeed = ob->speed;
        ob->speed = ROLLMOMENTUM;
        ParseMomentum(ob,angle);
        ob->speed = oldspeed;
        ob->dirchoosetime = 5;
        ob->flags |= FL_NOFRICTION;
    }
    else
        ob->dirchoosetime = 0;


}


void SelectDodgeDir (objtype *ob)
{
    int      dx,dy,i,tx,ty;
    unsigned absdx,absdy;
    dirtype  dirtry[5];
    dirtype  turnaround,tdir,olddir;


    olddir = ob->dir;
    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];


    if (ob->targettilex || ob->targettiley)
    {   tx = ob->targettilex;
        ty = ob->targettiley;
        dx= tx - ob->x;
        dy= ty - ob->y;
        if ( ((dx < MINACTORDIST) && (dx > -MINACTORDIST)) &&
                ((dy < MINACTORDIST) && (dy > -MINACTORDIST)))
        {   dx= PLAYER[0]->x-ob->x;
            dy= PLAYER[0]->y-ob->y;
        }
    }
    else
    {   dx= PLAYER[0]->x-ob->x;
        dy= PLAYER[0]->y-ob->y;
    }



//
// arange 5 direction choices in order of preference
// the four cardinal directions plus the diagonal straight towards
// the player
//
    if (dx>ACTORSIZE)
    {
        dirtry[1]= east;
        dirtry[3]= west;
    }
    else if (dx < -ACTORSIZE)
    {
        dirtry[1]= west;
        dirtry[3]= east;
    }

    if (dy>ACTORSIZE)
    {
        dirtry[2]= south; // south
        dirtry[4]= north; // north
    }
    else if (dy <-ACTORSIZE)
    {
        dirtry[2]= north; // north
        dirtry[4]= south; // south
    }

//
// randomize a bit for dodging
//
    absdx = abs(dx);
    absdy = abs(dy);

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

    if (GameRandomNumber("SelectDogeDir",ob->obclass) < 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]];

    ZEROMOM;

    for (i=0; i<5; i++)
    {   if ((dirtry[i] == nodir) || (dirdiff[dirtry[i]][olddir] > 1))
            continue;

        M_CHECKDIR(ob,dirtry[i]);
    }

//
// turn around only as a last resort
//
//	for(tdir = east;tdir<=southeast;tdir++)
    //if (tdir != turnaround)
//	 M_CHECKDIR(ob,tdir);

    if (turnaround != nodir)
        M_CHECKDIR(ob,turnaround);

}




#define TryAbruptProximalDirections(trydir)             \
   {                                                    \
   next = dirorder[trydir][NEXT];                       \
   prev = dirorder[trydir][PREV];                       \
   if (GameRandomNumber("actor choose dir",0) < 128)    \
      {                                                 \
      dirtype temp = next;                              \
                                                        \
      next = prev;                                      \
      prev = temp;                                      \
      }                                                 \
                                                        \
   if (!dirtried[next])                                 \
      {                                                 \
      M_CHECKDIR(ob,next);                              \
      dirtried[next]=1;                                 \
      }                                                 \
                                                        \
   if (!dirtried[prev])                                 \
      {                                                 \
      M_CHECKDIR(ob,prev);                              \
      dirtried[prev]=1;                                 \
      }                                                 \
                                                        \
   }


#define TrySmoothProximalDirections(trydir)                        \
   {                                                               \
                                                                   \
   if (((trydir == olddir) || (dirdiff[trydir][olddir] < 2)) &&    \
      (!dirtried[trydir]))                                         \
      {                                                            \
      M_CHECKDIR(ob,trydir);                                       \
      dirtried[trydir] = 1;                                        \
      }                                                            \
   next = dirorder[olddir][NEXT];                                  \
   prev = dirorder[olddir][PREV];                                  \
                                                                   \
   if (dirdiff[trydir][next] <= dirdiff[trydir][prev])             \
      {                                                            \
      start = next;                                                \
      whichway = NEXT;                                             \
      }                                                            \
   else                                                            \
      {                                                            \
      start = prev;                                                \
      whichway = PREV;                                             \
      }                                                            \
                                                                   \
   for (tdir= start; tdir != dirorder[trydir][whichway];           \
        tdir = dirorder[tdir][whichway]                            \
       )                                                           \
      {                                                            \
      if (dirtried[tdir])                                          \
         continue;                                                 \
      M_CHECKDIR(ob,tdir);                                         \
      dirtried[tdir]=1;                                            \
      }                                                            \
                                                                   \
   }



#define ChasePlayer(ob)                          \
   {                                             \
   dx= player->x-ob->x;                          \
   dy= ob->y-player->y;                          \
   if ((abs(dx) < 0xb000) && (abs(dy) < 0xb000)) \
      return;                                    \
   dummy.x = player->x;                          \
   dummy.y = player->y;                          \
   }


void SelectChaseDir (objtype *ob)
{
    int dx,dy,whichway,tx,ty,actrad,visible,
        realdiff;
    dirtype dtry1,dtry2,tdir,olddir,next,prev,start,straight;
    tpoint dummy;
    byte dirtried[9] = {0};

    olddir=ob->dir;
    visible = CheckLine(ob,PLAYER[0],SIGHT);

    /*
    if (ob->flags & FL_FIRSTATTACK)
    {
    //
    // turning around is only ok the very first time after noticing the
    // player
    //
    	turnaround = opposite[ob->dir];
    	ob->flags &= ~FL_FIRSTATTACK;
    }
    else
    	turnaround=nodir;
    */
    if (ob->targettilex || ob->targettiley)
    {
        tx = ob->targettilex;
        ty = ob->targettiley;
        dx= tx - ob->x;
        dy= ob->y - ty;
        if ((abs(dx) < 0x2000) && (abs(dy) < 0x2000))
            ChasePlayer(ob);
    }
    else
        ChasePlayer(ob);

    //if ((abs(dx) < 0x10000) && (abs(dy) < 0x10000))
    //return;
    straight = angletodir[atan2_appx(dx,dy)];
    realdiff = dirdiff[straight][ob->dir];
    ZEROMOM;

    //insertion 20

    actrad = ACTORSIZE;
    dtry1=nodir;
    dtry2=nodir;

    if (dx> actrad)
        dtry1= east;
    else if (dx< -actrad)
        dtry1= west;
    if (dy> actrad)
        dtry2=north;
    else if (dy < -actrad)
        dtry2= south;


    if (abs(dy)>abs(dx))
    {
        tdir=dtry1;
        dtry1=dtry2;
        dtry2=tdir;
    }

    if (GameRandomNumber("chase minor",0) < 80)
    {
        tdir=dtry1;
        dtry1=dtry2;
        dtry2=tdir;
    }

    ZEROMOM;

    if ((!visible) || (realdiff > 2))  // don't worry about abrupt or unrealistic turns if player
        // can't see guards
    {
        M_CHECKDIR(ob,straight);
        dirtried[straight]=1;
        next = dirorder[straight][NEXT];
        prev = dirorder[straight][PREV];

        if ((dtry1 != nodir) && (dtry1 != straight))
        {
            M_CHECKDIR(ob,dtry1);
            dirtried[dtry1]=1;
        }


        if ((dtry2 != nodir) && (!dirtried[dtry2]))
        {
            M_CHECKDIR(ob,dtry2);
            dirtried[dtry2]=1;
        }

        if (dtry1 != nodir)
            TryAbruptProximalDirections(dtry1);

        if (dtry2 != nodir)
            TryAbruptProximalDirections(dtry2);
    }

    else
    {
        if (realdiff < 2)
        {
            M_CHECKDIR(ob,straight);
            dirtried[straight]=1;
        }

        if (dtry1 != nodir)
            TrySmoothProximalDirections(dtry1);

        if (dtry2 != nodir)
            TrySmoothProximalDirections(dtry2);

    }

    if ((dtry1!=nodir) || (dtry2!=nodir))
    {
        if (GameRandomNumber("actor choose dir",0) < 128)
            whichway = NEXT;
        else
            whichway = PREV;

        for(tdir = dirorder[olddir][whichway]; tdir != olddir; tdir = dirorder[tdir][whichway])
        {
            if (dirtried[tdir])
                continue;
            M_CHECKDIR(ob,tdir);
        }
    }

    ob->dir = olddir;
}



int Near(objtype *ob,void* what,int distance)

{
    objtype *aw;

    aw = (objtype*) what;

    if (FindDistance((aw->x - ob->x),(aw->y - ob->y)) <= (distance<<16))
        return 1;
    return 0;

}



/*
===============
=
= SelectPathDir
=
===============
*/

void SelectPathDir (objtype *ob)
{
    int spot,centerx,centery,dx,dy,set,done,radius,ocl;

    ocl = ob->obclass;


    if ((ocl == bladeobj) && (!(ob->flags & FL_ACTIVE)))
        return;

    spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS;
    set = ((ocl == wallopobj) || (ocl == roboguardobj));
    done = (((!set) && (ob->dir == (dirtype)spot)) ||
            (set && (ob->dir == (dirtype)(spot<<1))));

    if ((spot >= 0) && (spot<= 7) && (!done))
    {
        centerx= (ob->tilex << 16) + HALFGLOBAL1;
        centery= (ob->tiley << 16) + HALFGLOBAL1;
        dx = abs(centerx - ob->x);
        dy = abs(centery - ob->y);
        //radius = (ob->speed > 0x4800)?(0xb000):(0x4000);
        radius = 0x4000;

        if ((dx < radius) && (dy < radius))
            // new direction
        {
            ZEROMOM;
            if ((ocl == wallopobj) || (ocl == roboguardobj))
            {
                ob->dir = spot<<1;
                ParseMomentum(ob,dirangle16[ob->dir]);
            }
            else
            {
                ob->dir = spot;
                ParseMomentum(ob,dirangle8[ob->dir]);
            }
            dx = centerx - ob->x;
            dy = centery - ob->y;
            SetFinePosition(ob,centerx,centery);
            SetVisiblePosition(ob,centerx,centery);
            /*
            if (((ocl == bladeobj) || (ocl == diskobj)) && ob->whatever)
            {objtype*passenger = (objtype*)(ob->whatever);

             passenger->x += dx;
             passenger->y += dy;
             passenger->drawx = passenger->x;
             passenger->drawy = passenger->y;
             passenger->tilex = passenger->x >> 16;
             passenger->tiley = passenger->y >> 16;
            }*/
//		 if (ob==SNAKEHEAD)
//		  Debug("\n path changed at %d, %d",
//			ob->tilex,ob->tiley);
        }
    }
    if (NOMOM)
    {
        if ((ocl == wallopobj) || (ocl == roboguardobj))
            ParseMomentum(ob,dirangle16[ob->dir]);
        else
            ParseMomentum(ob,dirangle8[ob->dir]);
    }

    //if ((ob->obclass == firejetobj) || (ob->obclass == bladeobj))
    //MoveActor(ob);
    //else
    ActorMovement(ob);

}



/*
================
=
= 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
=
================
*/


boolean CheckSight (objtype *ob,void *atwhat)
{
    long     deltax,deltay;
    objtype * what;
//
// 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
//
    what = (objtype*)atwhat;
    deltax = what->x - ob->x;
    deltay = what->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;
    default:
        ;
    }

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

}



void ActivateEnemy(objtype*ob)
{   statetype *temp;


    ob->flags |= (FL_ATTACKMODE|FL_FIRSTATTACK);
    if (ob->obclass == roboguardobj)
        return;

    if (ob->temp3 == SNEAKY)
    {   NewState(ob,&s_sneakyrise1);
        ob->temp3=0;
    }
    else if ((temp = M_S(CHASE)) != NULL)
        NewState(ob,temp);
    /*
    ob->speed = ENEMYRUNSPEED;
    */
    if (ob->obclass == b_heinrichobj)
        ob->speed = 7*ob->speed/2;
    else if (ob->obclass == b_darianobj)
        ob->speed = 3*SPDPATROL;
    if (ob->door_to_open != -1)
        ob->door_to_open = -1; // ignore the door opening command
    ob->dirchoosetime = 0;


}


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

void FirstSighting (objtype *ob)
{
    statetype *temp;
    int sound;

    if (ob->temp3 == SNEAKY)
    {
        NewState(ob,&s_sneakyrise1);
        ob->temp3=0;
        if (ob->shapeoffset==0)
            SD_PlaySoundRTP(SD_SNEAKYSPRINGMSND,ob->x,ob->y);
        else
            SD_PlaySoundRTP(SD_SNEAKYSPRINGFSND,ob->x,ob->y);
    }
    else if ((temp = M_S(CHASE)) != NULL)
    {
        int rand;

        NewState(ob,temp);
        sound = BAS[ob->obclass].see;
        rand = GameRandomNumber("FirstSighting low",0);
        if ((ob->obclass > lowguardobj) && (ob->obclass <= blitzguardobj) && (rand < 128)) //hack for alternate
            sound++;
        //if ((ob->obclass == lowguardobj) && (rand < 80))
        //sound ++;
        else if (ob->obclass == lowguardobj)
        {   if (rand < 128)
            {   if ((PLAYERSTATE[0].player == 1) || (PLAYERSTATE[0].player == 3))
                    sound++;
            }
            else
                sound += 2;

            if (ob->shapeoffset)
                sound += 4;

        }
        SD_PlaySoundRTP(sound,ob->x,ob->y);
        if ((ob->obclass>=b_darianobj) && (ob->obclass<=b_darksnakeobj))
        {
            MU_StartSong(song_bosssee);
        }
    }

    /*
    ob->speed = ENEMYRUNSPEED;*/
    if (ob->obclass == b_heinrichobj)
        ob->speed = 7*ob->speed/2;
    else if (ob->obclass == b_darianobj)
        ob->speed = 3*SPDPATROL;
    if (ob->door_to_open != -1)
        ob->door_to_open = -1; // ignore the door opening command
    ob->dirchoosetime = 0;
    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)
    //Error ("An instance of %s in ATTACKMODE called SightPlayer!",debugstr[ob->obclass]);

    if (!areabyplayer[ob->areanumber])
        return false;

    if (ob->obclass == b_robobossobj)
    {
        if (!(CheckSight(ob,player) || Near(ob,player,6)))
            return false;

    }


    else if (ob->flags & FL_AMBUSH)
    {
        if (!CheckSight (ob,PLAYER[0]))
        {
            //SoftError("\n failed from ambush in SightPlayer");
            return false;
        }
        ob->flags &= ~FL_AMBUSH;
    }
    else
    {
        if (ob->temp3 == SNEAKY)
        {
            if (!Near(ob,PLAYER[0],2))
                return false;
        }
        else if (!((MISCVARS->madenoise) ||
                   (CheckSight (ob,player)) ||
                   (Near(ob,player,4))
                  )
                )
        {
            //SoftError("\n failed from SightPlayer");
            return false;
        }
    }

    FirstSighting (ob);

    return true;
}


/*
=====================
=
= CheckLine
=
= Returns true if a straight line between two obs is unobstructed
=
=====================
*/



boolean CheckLine (void *from, void *to, int condition)
{
    objtype   *tempactor,*ob,*orig;
    statobj_t *checksprite;
    int destx,desty,destz;
    int desttilex,desttiley;
    int snx,sny;
    int incr[2];
    int thedir[2];
    int cnt;
    int grid[2];
    int index;
    int vx,vy;
    int yzangle;
    int value;
    int dx,dy,dz;
    int xydist;
    int otx,oty,count=0;



    ob = (objtype*)to;
    orig = (objtype*)from;
    if (ob->which == SPRITE)
    {
        destx = ((statobj_t*)to)->x;
        desty = ((statobj_t*)to)->y;
        destz = ((statobj_t*)to)->z;
    }
    else
    {
        destx = ob->x;
        desty = ob->y;
        destz = ob->z;
    }

    desttilex=destx>>16;
    desttiley=desty>>16;

    if ((desttilex == orig->tilex) && (desttiley == orig->tiley))
        return true;


    dx=destx-orig->x;
    dy=orig->y-desty;
    dz=orig->z-destz;
    xydist = FindDistance(dx,dy);
    yzangle = atan2_appx(xydist,dz<<10);

    if ((yzangle>MAXYZANGLE) && (yzangle<FINEANGLES-MAXYZANGLE))
    {
        return false;
    }

    //angle = atan2_appx(dx,dy);
    otx = orig->x >> TILESHIFT;
    oty = orig->y >> TILESHIFT;

    if (xydist==0)
    {
        /*
        SoftError("\nCheckLine:xydist=0");
        if (orig->which == ACTOR)
          SoftError("shooter: %s",debugstr[orig->obclass]);
        if (ob->which == ACTOR)
          SoftError("target: %s",debugstr[ob->obclass]);*/
        vy=-dy;
        vx=dx;
    }
    else
    {
        vy = -FixedDiv2(dy,xydist);
        vx = FixedDiv2(dx,xydist);
    }
    snx=orig->x&0xffff;
    sny=orig->y&0xffff;

    grid[0]=otx;
    grid[1]=oty;

    if (vx>0)
    {
        thedir[0]=1;
        snx^=0xffff;
        incr[1]=-vx;
    }
    else
    {
        thedir[0]=-1;
        incr[1]=vx;
    }
    if (vy>0)
    {
        thedir[1]=1;
        sny^=0xffff;
        incr[0]=vy;
    }
    else
    {
        thedir[1]=-1;
        incr[0]=-vy;
    }
    cnt=FixedMul(snx,incr[0])+FixedMul(sny,incr[1]);


    do
    {   count ++;
        /*
        if (count > 1000)
          Error("possible infinite loop in CheckLine");
         if ((grid[0] < 0) || (grid[0] > (MAPSIZE-1)) ||
        	  (grid[1] < 0) || (grid[1] > (MAPSIZE-1)))
        	Error("out of bounds in check line, grid[0] = %d, grid[1] = %d",grid[0],grid[1]);
         */
        if ((grid[0]==desttilex) && (grid[1]==desttiley))
            return true;
        tempactor = (objtype*)actorat[grid[0]][grid[1]];
        value = tilemap[grid[0]][grid[1]];
        checksprite = sprites[grid[0]][grid[1]];
        if (value)
        {
            if (value&0x8000)
            {
                if (!(value&0x4000))
                {
                    doorobj_t*dptr = doorobjlist[value&0x3ff];

                    if (dptr->position < 0x8000)
                    {

                        int x = (grid[0] << 16) + 0x8000;
                        int y = (grid[1] << 16) + 0x8000;

                        if (dptr->vertical)
                        {
                            if (abs(dx) > abs(x-orig->x))
                                return false;
                        }
                        else
                        {
                            if (abs(dy) > abs(orig->y-y))
                                return false;
                        }
                    }
                }
                else
                {
                    if (condition == SHOOT)
                    {
                        if ( maskobjlist[value&0x3ff]->flags & MW_SHOOTABLE )
                        {
                            return false;
                        }
                        else if ( maskobjlist[value&0x3ff]->flags & MW_WEAPONBLOCKING )
                        {
                            return false;
                        }
                    }
                    else if ((condition == MISSILE) &&
                             ( maskobjlist[value&0x3ff]->flags & MW_BLOCKING )
                            )
                        return false;
                }
            }
            else
            {
                return false;
            }
        }
        if (condition == SHOOT)
        {
            if (tempactor && (tempactor->which == ACTOR) &&
                    (tempactor->flags & FL_BLOCK) && (tempactor != orig) &&
                    (tempactor != ob)) //&&
//   			(InRange(orig,tempactor,
//             FindDistance(orig->x-tempactor->x,orig->y-tempactor->y) )
//             ==true) )
            {
                return false;
            }
        }

        if (checksprite && (checksprite->flags & FL_BLOCK) && (condition == SHOOT) &&
                ((void *)checksprite != to) &&
                (checksprite->itemnumber!=stat_disk) &&
                (InRange(orig,(objtype *)checksprite,
                         FindDistance(orig->x-checksprite->x,orig->y-checksprite->y) )
                 ==true) )

        {
            return false;
        }

        if (tempactor && (tempactor->which == PWALL))
        {
            return false;
        }
        index=(cnt>=0);
        cnt+=incr[index];
        grid[index]+=thedir[index];
    }
    while (1);
}







/*
=====================
=
= ShootActor
=
= Shoot an actor.
=
=====================
*/
void ShootActor(objtype * shooter, objtype * target, int damage, int accuracy, int angle)
{
    int dx,dy,dist;
    int newmomx, newmomy;
    int tcl;


    if (target->flags & FL_DYING)
        return;

    dx = abs(shooter->x - target->x);
    dy = abs(shooter->y - target->y);
    dist = FindDistance(dx,dy)>>16;

    tcl=target->obclass;

    if (tcl==playerobj)
    {
        target->target=shooter;
        if (target->flags&FL_BPV)
        {
            playertype *pstate;

            M_LINKSTATE(target,pstate);
            pstate->protectiontime -= (damage<<1);
            if (pstate->protectiontime < 1)
                pstate->protectiontime = 1;
            if (target==player)
                GM_UpdateBonus (pstate->protectiontime, false);
            return;
        }
        else if ((target->flags&FL_GODMODE) || (target->flags&FL_DOGMODE) || godmode)
            return;
        //damage=FixedMulShift((gamestate.difficulty+1),damage,2); // player object difficulty
    }

    else if (tcl == NMEsaucerobj)
    {
        target->momentumx = target->momentumy = 0;
        NewState(target,&s_explosion1);
        target->flags &= ~FL_SHOOTABLE;
        return;
    }
    else if (tcl == b_darianobj)
        MISCVARS->ESAU_SHOOTING = false;
    else if ((tcl == strikeguardobj) || (tcl == b_heinrichobj))
        target->target = shooter;

    if ((  (!(target->flags & FL_SHOOTABLE)) ||
            (tcl == roboguardobj) || (tcl == wallopobj) ||
            (tcl == patrolgunobj) ) &&
            (tcl!=playerobj)  )
        SpawnMetalSparks(target,angle);

    else if ((tcl < b_darianobj) || (tcl > b_darksnakeobj))
    {

        //target->flags &= ~FL_USE;

        damage=FixedMulShift(511-accuracy,damage,9); // Min half damage
        if (dist<64)
        {
            if (dist>2)
                damage=FixedMulShift(63-dist,damage,6);
            if (damage<1)
                damage=1;
        }
        else
            damage=1;

        if (damage>MAXDAMAGE) damage=MAXDAMAGE; // absolutely clip it

        DamageThing(target,damage);
        if ((tcl == collectorobj) && gamestate.SpawnDeluder)
        {
            Collision(target,shooter,0,0);
            if (target->hitpoints <= 0)
                BATTLE_CheckGameStatus(battle_shot_deluder,shooter->dirchoosetime);
        }

        else
        {   newmomx = FixedMul(damage<<7,costable[angle]);
            newmomy = -FixedMul(damage<<7,sintable[angle]);
            Collision(target,shooter,-(target->momentumx)+newmomx,-(target->momentumy)+newmomy);
            if (tcl == playerobj)
            {
                playertype * pstate;

                M_LINKSTATE(target,pstate);
                if (pstate->health <= 0)
                {

                    if (shooter->obclass == playerobj) {
                        if (!target->momentumz)
                            BATTLE_PlayerKilledPlayer(battle_kill_with_bullet,shooter->dirchoosetime,target->dirchoosetime);
                        else
                            BATTLE_PlayerKilledPlayer(battle_kill_with_bullet_in_air,shooter->dirchoosetime,target->dirchoosetime);
                    }
                }
            }
//      SoftError("ShootActor: damage=%ld dist=%ld\n",damage,dist);

            if ((GameRandomNumber("disembowel",0)<64) && (gamestate.violence == vl_excessive))
            {
                int temp;
                temp=target->temp1;
                target->temp1=angle;

                SpawnParticles(target,DISEMBOWEL,damage>>3);
                target->temp1=temp;
            }
            else if (gamestate.violence > 0)
                SpawnBlood(target,angle);
        }
    }
}


/*
=====================
=
= ShootSprite
=
= Shoot a sprite.
=
=====================
*/
void ShootSprite(objtype * shooter, statobj_t * target, int damage, int accuracy, int angle)
{
    int dx,dy,dist;


    if (!(target->flags & FL_SHOOTABLE))
// Watchout for sprite being passed in as actor WARNING
        SpawnMetalSparks((objtype *)target,angle);

    else
    {
        dx = abs(shooter->x - target->x);
        dy = abs(shooter->y - target->y);
        dist = FindDistance(dx,dy)>>16;

        damage=FixedMulShift(511-accuracy,damage,9); // Min half damage
        if (dist<64)
        {
            if (dist>2)
                damage=FixedMulShift(63-dist,damage,6);
            if (damage<1)
                damage=1;
        }
        else
            damage=1;

        if (damage>MAXDAMAGE) damage=MAXDAMAGE; // absolutely clip it

        //   SoftError("ShootSprite: damage=%ld dist=%ld\n",damage,dist);

        DamageThing((objtype *)target,damage);
        if (FirstExplosionState(new->state))
            new->whatever = shooter;

        SpawnStaticDamage(target, angle);
    }
}


/*
=====================
=
= RayShoot
=
= Cast a ray out at the shooter's angle and yzangle, return
=
=====================
*/



void RayShoot (objtype * shooter, int damage, int accuracy)
{
    objtype   *tempactor;
    statobj_t *checksprite;
    int snx,sny;
    int incr[2];
    int zincr[2];
    int thedir[2];
    int cnt;
    int grid[2];
    int index;
    int vx,vy;
    int angle;
    int yzangle;
    int value;
    int offset;
    int z;
    int lastcnt;
    int bullethole=0;
    enum {gs_door, gs_wall, gs_floor, gs_ceiling, gs_pushwall};
    int smokecondition=0;


    if ((shooter->areanumber==player->areanumber) && (Near(shooter,player,3)))
        SetIllumination(2);

    offset = ((GameRandomNumber("RayShoot",0)-128)>>MAXSHOOTSHIFT);
    offset = FixedMulShift(accuracy,offset,8);

    if (offset>MAXSHOOTOFFSET)
        offset=MAXSHOOTOFFSET;

    else if (offset<-MAXSHOOTOFFSET)
        offset=-MAXSHOOTOFFSET;

    angle=(shooter->angle+offset)&(FINEANGLES-1);

    offset = ((GameRandomNumber("RayShoot",1)-128)>>MAXSHOOTSHIFT);
    offset = FixedMulShift(accuracy,offset,8);

    if (offset>MAXSHOOTOFFSET)
        offset=MAXSHOOTOFFSET;

    else if (offset<-MAXSHOOTOFFSET)
        offset=-MAXSHOOTOFFSET;

    yzangle=(shooter->yzangle+offset)&(FINEANGLES-1);

    vy = -sintable[angle];
    vx = costable[angle];
    snx=shooter->x&0xffff;
    sny=shooter->y&0xffff;
    grid[0]=shooter->tilex;
    grid[1]=shooter->tiley;
    if (shooter->obclass==playerobj)
    {
        playertype * pstate;

        M_LINKSTATE(shooter,pstate);
        z=shooter->z+pstate->playerheight-32;
    }
    else
        z=shooter->z-7;
    if (vx>0)
    {
        thedir[0]=1;
        snx^=0xffff;
        incr[1]=-vx;
    }
    else
    {
        thedir[0]=-1;
        incr[1]=vx;
    }
    if (vy>0)
    {
        thedir[1]=1;
        sny^=0xffff;
        incr[0]=vy;
    }
    else
    {
        thedir[1]=-1;
        incr[0]=-vy;
    }
    zincr[0]=-FixedMulShift(sintable[yzangle],abs(vx),26);
    zincr[1]=-FixedMulShift(sintable[yzangle],abs(vy),26);

    cnt=FixedMul(snx,incr[0])+FixedMul(sny,incr[1]);
    index= (cnt >= 0);
    do
    {
        tempactor = (objtype*)actorat[grid[0]][grid[1]];
        value = tilemap[grid[0]][grid[1]];
        checksprite = sprites[grid[0]][grid[1]];
        if (value)
        {
            if (value&0x8000)
            {
                if (!(value&0x4000))
                {
                    if ((doorobjlist[value&0x3ff]->action==dr_closed) || (z<maxheight-64))
                    {
                        smokecondition=gs_door;
                        break;
                    }
                    else if (doorobjlist[value&0x3ff]->position<=0x8000)
                    {
                        smokecondition=gs_door;
                        break;
                    }
                }
                else
                {
                    if ( maskobjlist[value&0x3ff]->flags & MW_SHOOTABLE )
                    {
                        if (z>maxheight-64) // Are we shooting above the glass
                        {
                            UpdateMaskedWall(value&0x3ff);
                            return;
                        }
                    }
                    else if ( maskobjlist[value&0x3ff]->flags & MW_WEAPONBLOCKING )
                    {
                        smokecondition=gs_door;
                        break;
                    }
                }
            }
            else
            {
                smokecondition=gs_wall;
                break;
            }
        }

        if (checksprite &&
                ((checksprite->flags & FL_BLOCK)||(checksprite->flags & FL_SHOOTABLE)) &&
                (abs(checksprite->z-z)<32) &&
                (InRange(shooter,(objtype *)checksprite,
                         FindDistance(shooter->x-checksprite->x,shooter->y-checksprite->y) )
                 ==true
                )
           )
        {
            ShootSprite(shooter, checksprite, damage, accuracy, angle);
            return;
        }


        if (tempactor)
        {   if (tempactor->which == ACTOR)
            {   if ((abs(tempactor->z-z)<32 ) && (!(tempactor->flags & FL_DYING)) &&
                        (tempactor->flags & FL_BLOCK) && (tempactor != shooter) &&
                        (tempactor->obclass!=diskobj) &&
                        (InRange(shooter,tempactor,
                                 FindDistance(shooter->x-tempactor->x,shooter->y-tempactor->y) )
                         ==true
                        )
                   )
                {   ShootActor(shooter, tempactor, damage, accuracy, angle);
                    return;
                }
            }
            else if (tempactor->which == PWALL)
                return;
        }

        if (z<-32)
        {
            smokecondition=gs_ceiling;
            break;
        }
        else if (z>maxheight)
        {
            smokecondition=gs_floor;
            break;
        }
        index= (cnt >= 0);
        cnt+=incr[index];
        z  +=zincr[index];
        grid[index]+=thedir[index];
    }
    while (1);

    if (IsWindow(grid[0],grid[1]))
        return;

    lastcnt=cnt-incr[index];

    if (smokecondition==gs_floor)
    {
        int dist;
        int tangentangle;

        tangentangle=tantable[yzangle];
        if (tangentangle!=0)
        {
            dist=FixedDiv2(((shooter->z-maxheight)<<10),(tangentangle<<1));
            xintercept=shooter->x+FixedMul(dist,costable[angle]);
            yintercept=shooter->y-FixedMul(dist,sintable[angle]);
        }
        z=maxheight;
//      bullethole=5;
    }
    else if (smokecondition==gs_ceiling)
    {
        int dist;
        int tangentangle;

        if (sky!=0)
            return;
        tangentangle=tantable[yzangle];
        if (tangentangle!=0)
        {
            dist=FixedDiv2(((shooter->z+32)<<10),(tangentangle<<1));
            xintercept=shooter->x+FixedMul(dist,costable[angle]);
            yintercept=shooter->y-FixedMul(dist,sintable[angle]);
        }
        z=-32;
//      bullethole=5;
    }
    else
    {
        int dx,dy,xydist;


#define CORNERVALUE  0x500


        if (IsWindow(grid[0],grid[1]))
            return;
        if (lastcnt<0)
        {
            xintercept=grid[0]<<16;
            if (smokecondition==gs_door)
            {
                if (thedir[0]<0)
                    xintercept+=0x9fff;
                else
                    xintercept+=0x5fff;
                yintercept=FixedScale(xintercept-shooter->x,vy,vx)+shooter->y;
                if ((yintercept>>16)!=grid[1])
                {
                    if ((yintercept>>16)>grid[1])
                        yintercept=(grid[1]<<16)+0xffff;
                    else
                        yintercept=(grid[1]<<16);
                }
            }
            else if (smokecondition==gs_wall)
            {
                if (thedir[0]<0)
                {
                    objtype * ta;

                    xintercept += 0x10000;
                    yintercept=FixedScale(xintercept-shooter->x,vy,vx)+shooter->y;

                    xintercept += SMOKEWALLOFFSET;
                    bullethole=1;
                    if (yintercept < ((grid[1] << 16) + CORNERVALUE))
                        bullethole = 0;
                    else if (yintercept > ((grid[1] << 16) + 0x10000 - CORNERVALUE))
                        bullethole = 0;

                    ta = (objtype*)actorat[grid[0]][grid[1]];
                    if ((ta) && (ta->which==PWALL))
                        bullethole=0;
                }
                else
                {
                    objtype * ta;

                    yintercept=FixedScale(xintercept-shooter->x,vy,vx)+shooter->y;
                    xintercept-=SMOKEWALLOFFSET;
                    bullethole=2;
                    if (yintercept < ((grid[1] << 16) + CORNERVALUE))
                        bullethole = 0;
                    else if (yintercept > ((grid[1] << 16) + 0x10000 - CORNERVALUE))
                        bullethole = 0;

                    ta = (objtype*)actorat[grid[0]][grid[1]];
                    if ((ta) && (ta->which==PWALL))
                        bullethole=0;
                }
            }
        }
        else
        {
            yintercept=grid[1]<<16;
            if (smokecondition==gs_door)
            {
                if (thedir[1]<0)
                    yintercept+=0x9fff;
                else
                    yintercept+=0x5fff;
                xintercept=FixedScale(yintercept-shooter->y,vx,vy)+shooter->x;
                if ((xintercept>>16)!=grid[0])
                {
                    if ((xintercept>>16)>grid[0])
                        xintercept=(grid[0]<<16)+0xffff;
                    else
                        xintercept=(grid[0]<<16);
                }
            }
            else if (smokecondition==gs_wall)
            {
                if (thedir[1]<0)
                {
                    objtype * ta;


                    yintercept += 0x10000;
                    xintercept=FixedScale(yintercept-shooter->y,vx,vy)+shooter->x;

                    yintercept += SMOKEWALLOFFSET;
                    bullethole=3;
                    if (xintercept < ((grid[0] << 16) + CORNERVALUE))
                        bullethole = 0;
                    else if (xintercept > ((grid[0] << 16) + 0x10000 - CORNERVALUE))
                        bullethole = 0;

                    ta = (objtype*)actorat[grid[0]][grid[1]];
                    if ((ta) && (ta->which==PWALL))
                        bullethole=0;
                }
                else
                {
                    objtype * ta;

                    xintercept=FixedScale(yintercept-shooter->y,vx,vy)+shooter->x;
                    yintercept-=SMOKEWALLOFFSET;
                    bullethole=4;
                    if (xintercept < ((grid[0] << 16) + CORNERVALUE))
                        bullethole = 0;
                    else if (xintercept > ((grid[0] << 16) + 0x10000 - CORNERVALUE))
                        bullethole = 0;

                    ta = (objtype*)actorat[grid[0]][grid[1]];
                    if ((ta) && (ta->which==PWALL))
                        bullethole=0;
                }
            }
        }
        dx = xintercept - shooter->x;
        dy = shooter->y - yintercept;
        xydist = FindDistance(dx,dy);
        if (shooter->obclass==playerobj)
        {
            playertype * pstate;

            M_LINKSTATE(shooter,pstate);
            z=shooter->z-FixedMulShift(xydist,tantable[yzangle],25)+pstate->playerheight-32;
        }
        else
            z=shooter->z-FixedMulShift(xydist,tantable[yzangle],25);
        if (smokecondition==gs_wall)
        {
            if (z<-32)
                z=-32;
        }
    }
    SpawnGunSmoke(xintercept,yintercept,z,angle,bullethole);
}


/*
=====================
=
= T_BossDied ()
=
=====================
*/

void T_BossDied (objtype *ob)
{

    if (ob->ticcount)
        return;

    switch (ob->obclass)
    {
    case b_darianobj:
    case b_heinrichobj:
    case b_darkmonkobj:
    case b_robobossobj:
    case b_darksnakeobj:
        playstate = ex_bossdied;
        break;
    default:
        ;
    }
}


/*
=====================
=
= T_Wind ()
=
=====================
*/

static int WindDistance = 1000;
static int WindCurrentDistance = 1000;
static int WindHandle = -1;
static int WindLastTic = -1;
static int WindPlaying = false;
static int WindPitch = 0;
static int WindDestPitch = 0;
static int WindPitchRate = 0;

void T_Wind
(
    objtype *ob
)

{
    int distance;
    int dx;
    int dy;

    if ( ( GetTicCount() - WindLastTic ) > 0 )
    {
        WindDistance = 1000;

        WindPitch += WindPitchRate;
        if ( WindPitch == WindDestPitch )
        {
            WindDestPitch = ( RandomNumber( "Wind Pitch", 0 ) - 128 ) << 3;
            WindPitchRate = 1;
            if ( WindDestPitch < WindPitch )
            {
                WindPitchRate = -WindPitchRate;
            }
        }
    }
    WindLastTic = GetTicCount();

    dx = ( ob->x - PLAYER[0]->x );
    dy = ( PLAYER[0]->y - ob->y );

    distance = 1000;
    if ( areabyplayer[ ob->areanumber ] )
    {
        distance = ( FindDistance( dx, dy ) ) >> 13;
    }

    if ( distance < WindDistance )
    {
        WindDistance = distance;
    }

    if ( WindDistance < 255 )
    {
        WindPlaying = true;
        WindCurrentDistance = WindDistance;
    }
    else
    {
        if ( WindPlaying )
        {
            WindCurrentDistance += 3;
        }
    }

    if ( WindPlaying )
    {
        if ( WindCurrentDistance < 255 )
        {
            if ( !SD_SoundActive( WindHandle ) )
            {
                WindHandle = SD_PlayPitchedSound( SD_WINDSND,
                                                  255 - WindCurrentDistance, WindPitch );
            }
            else
            {
                SD_SetSoundPitch( WindHandle, WindPitch );
                SD_SetPan( WindHandle, 255 - WindCurrentDistance, 255 - WindCurrentDistance,
                           255 - WindCurrentDistance );
            }
        }
        else
        {
            SD_StopSound( WindHandle );
            WindPlaying = false;
        }
    }
}

/*
=====================
=
= StopWind ()
=
=====================
*/

void StopWind
(
    void
)

{
    objtype *temp;

    FX_SetReverb( 0 );

    SD_StopSound( WindHandle );
    WindDistance        = 1000;
    WindCurrentDistance = 1000;
    WindHandle          = -1;
    WindLastTic         = -1;
    WindPlaying         = false;
    WindPitch           = 0;
    WindDestPitch       = 0;
    WindPitchRate       = 0;


    for(temp=FIRSTACTOR; temp; temp=temp->next)
    {
        if (temp->soundhandle != -1)
            SD_StopSound(temp->soundhandle);
    }
}