/*
Copyright (C) 1994-1995 Apogee Software, Ltd.

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.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/


#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_swift.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"
//MED
#include "memcheck.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];
#if (DEVELOPMENT == 1)
FILE *            williamdebug;
#endif
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;

  #if ((DEVELOPMENT == 1))
  #if ((LOADSAVETEST == 1))
  if (!lastactive)
	Debug("\nlastactive = NULL !");
  else
	Debug("\nlastactive = %8x",lastactive);

  #endif
  #endif
 }



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;

   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);

   }




void ConsiderOutfittingBlitzguard(objtype *ob)
   {
   if ((GameRandomNumber("wiley blitzguard",0) < WILEYBLITZCHANCE) &&
       (gamestate.difficulty >= gd_medium)
      )
      {
      ob->temp3 = stat_bazooka;
      ob->temp2 = 3;
      }
   }



/*
===============
=
= 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;


 #if 0
 if (gamestate.Product == ROTT_SUPERCD)
   ConsiderAlternateActor(new,which);
 #endif

   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 0
if (gamestate.Product == ROTT_SUPERCD)
 char *altstartlabel;
#endif



 #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++;


   #if 0
   if (gamestate.Product == ROTT_SUPERCD)
   ConsiderAlternateActor(new,which);
   #endif

   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;
   wall_t* tswitch;
   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;
            }

      tswitch = (wall_t*)actorat[ob->temp1][ob->temp2];
      /*
      if (tswitch && (tswitch->which != ACTOR))
         {
         tilemap[ob->temp1][ob->temp2]--;
         tswitch->flags &= ~FL_ON;
         }
      */

      }
   }




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,fireweapon=0,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;
         fireweapon = 1;
         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);
         fireweapon = 1;
         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;
         fireweapon = 1;
         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,playeron;


  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;


  playeron = 0;
  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;
			playeron  = 1;
			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 (!playeron)
	 {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,playeron;


  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;
  playeron = 0;
  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;
			  playeron = 1;
			  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 (!playeron)
	 {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;
         }
#if 0
      dist = FindDistance(dx,dy);
      scalefactor = (blastradius-dist)>>4;
      if (scalefactor > 0xffff)
         scalefactor = 0xffff;
      pdamage = FixedMul(damage,scalefactor);
#endif
//#if 0
     //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);
//#endif
      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;
//MED
            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;

   }


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



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 0
   if ((ob->obclass == blitzguardobj) &&
       (ob->state->condition & SF_DOWN)
      )

      SD_Play(PlayerSnds[locplayerstate->player]);
#endif

   if (Vicious_Annihilation(ob,attacker))
      return;

   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;


   }


//MED
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);

   }


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;
    }

  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;                     \
   }

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;
					 }
				}

         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 linked,oldarea,newarea,
       tilex,tiley,oldtilex,oldtiley;

	linked = 0;

   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);

      }

//MED
   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;
   int ISPLAYER = 0;
   int hoffset;

   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;
         }

   else
         {
         ISPLAYER = 1;
         if (ob->flags & FL_DOGMODE)
            hoffset = 10;
         }



   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 WALLSTOP,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);


   WALLSTOP = false;

   for (y=tileylow;y<=tileyhigh;y++)
      for (x=tilexlow;x<=tilexhigh;x++)
         {
         wall_t          *tempwall;
         int             wall;

         tempwall = (wall_t*)actorat[x][y];
         wall=tilemap[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;
               WALLSTOP = true;
               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,noneleft,invisible;
   hiding_status hole;


   dirtype d[3];
   dirtype tdir, olddir, turnaround;


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


   invisible = 0;
   noneleft = 1;

   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);

         }
   #if 0
      SoftError("\n straight dir: %d\n queue dirs ",tdir);
      for(count = 0;count < MISCVARS->NMEqueuesize;count++)
         {
         SoftError("\n dir %d: %d",MISCVARS->NMEqueuesize-count,
                   ((ob->temp1 >> (4*count)) &0xf)
                  );

         }
   #endif
      }
   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;
    //#if ((DEVELOPMENT == 1))
	 //Debug("\nCheckLine failed in NME Attack");
	 //#endif
	 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;
        //#if ((DEVELOPMENT == 1))
		  //	Debug("\nfollower %d being moved to targetx %4x and targety %4x",
		//	  ob-SNAKEHEAD,ob->x,ob->y);
		 // #endif
		  ob->targettilex = ob->temp1;
		  ob->targettiley = ob->temp2;
		  #if (0)
			Debug("\nfollower %d's new targetx %4x, targety %4x",
			 ob-SNAKEHEAD,ob->temp1,ob->temp2);
		  #endif
		  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 (cybermanenabled)
         SWIFT_TactileFeedback (10*damage, 15, 15);
      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);

}




/*
===============
=
= 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;
	*/
	dummy.which = ACTOR;
	dummy.z = ob->z;
	if (ob->targettilex || ob->targettiley)
      {
      tx = ob->targettilex;
      ty = ob->targettiley;
      dx= tx - ob->x;
      dy= ob->y - ty;
      dummy.x = tx;
      dummy.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))
	  {
		#if (0)
		 Debug("\nfailed from yzangle");
		#endif
		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 )
							{
                      #if (0)
                      SoftError("\nfailed from shootable mask");
                    #endif
							 return false;
							}
                  else if ( maskobjlist[value&0x3ff]->flags & MW_WEAPONBLOCKING )
							{
                      #if (0)
                      SoftError("\nfailed from block mask");
                      #endif
							 return false;
							}
						}
               else if ((condition == MISSILE) &&
                        ( maskobjlist[value&0x3ff]->flags & MW_BLOCKING )
                       )
                  return false;
					}
				}
			else
				{
             #if (0)
            SoftError("\n obx %d, oby %d, origx %d, origy %d"
                      "\n xydist %d, vx %d, vy %d",ob->x,ob->y,orig->x,
                      orig->y,xydist,vx,vy);

            SoftError("\nfailed from normal wall");
             #endif
            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) )
			  {
            #if (0)
             SoftError("\nfailed from actor");
            #endif
				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) )

			{
          #if (0)
          SoftError("\nfailed from sprite");
          #endif
			 return false;
			}

		if (tempactor && (tempactor->which == PWALL))
			{
        #if (0)
          SoftError("\nfailed from pushwall");
          #endif
			 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);
      }
   }

