/*
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 "rt_def.h"
#include "rt_sound.h"
#include "rt_door.h"
#include "rt_actor.h"
#include "rt_stat.h"
#include "_rt_door.h"
#include "z_zone.h"
#include "w_wad.h"
#include "rt_ted.h"
#include "rt_draw.h"
#include "rt_main.h"
#include "rt_playr.h"
#include "rt_util.h"
#include "rt_menu.h"
#include "rt_ted.h"
#include "rt_msg.h"
#include "rt_game.h"
#include "rt_vid.h"
#include "rt_net.h"
#include "isr.h"
#include "develop.h"
#include "rt_rand.h"
#include "engine.h"
#include <stdlib.h>
#include <string.h>

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

							DOORS

doorobjlist[] holds most of the information for the doors

Open doors conect two areas, so sounds will travel between them and sight
	will be checked when the player is in a connected area.

Areaconnect is incremented/decremented by each door. If >0 they connect

Every time a door opens or closes the areabyplayer matrix gets recalculated.
	An area is true if it connects with the player's current spor.

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


// Global Variables


#define ELEVATORMUSICTIME   560

elevator_t     ELEVATOR[MAXELEVATORS];
int            _numelevators;
animmaskedwallobj_t* FIRSTANIMMASKEDWALL,*LASTANIMMASKEDWALL;
maskedwallobj_t* FIRSTMASKEDWALL,*LASTMASKEDWALL;
byte             touchindices[MAPSIZE][MAPSIZE],lasttouch;
touchplatetype   *touchplate[MAXTOUCHPLATES],*lastaction[MAXTOUCHPLATES];

byte             numactions[MAXTOUCHPLATES];
int              totalactions;

byte             TRIGGER[MAXTOUCHPLATES];
doorobj_t	   *doorobjlist[MAXDOORS];
int			   doornum;
maskedwallobj_t *maskobjlist[MAXMASKED];
int            maskednum;

pwallobj_t     *pwallobjlist[MAXPWALLS];
int            pwallnum;

byte	         areaconnect[NUMAREAS][NUMAREAS];

boolean	      areabyplayer[NUMAREAS];


// Local Variables

static void (*touchactions[NUMTOUCHPLATEACTIONS])(intptr_t) =
						 {ActivatePushWall,
						  ActivateMoveWall,
						  LinkedOpenDoor,
						  LinkedCloseDoor,
						  EnableObject,
						  DisableObject,
						  ActivateLight,
						  DeactivateLight};

void UtilizeDoor (int door,void (*action)(int));
void UseDoor (int door);
void Teleport(elevator_t*eptr,int destination);
void ConnectPushWall (int pwall);
void SetupPushWall (int pwall);
void WallMoving (int pwall);
int SetNextAction(elevator_t*eptr,int action);

/*
===============
=
= MakeMaskedWallActive
=
===============
*/

void MakeMaskedWallActive(maskedwallobj_t* tmwall)
 {if (!FIRSTMASKEDWALL)
	 FIRSTMASKEDWALL	= tmwall;
  else
	  {tmwall->prev = LASTMASKEDWALL;
		LASTMASKEDWALL->next = tmwall;
	  }
  LASTMASKEDWALL = tmwall;
 }

/*
===============
=
= MakeMaskedWallInactive
=
===============
*/


void MakeMaskedWallInactive(maskedwallobj_t* tmwall)
{
 if (tmwall == LASTMASKEDWALL)
	LASTMASKEDWALL = tmwall->prev;
 else
	tmwall->next->prev = tmwall->prev;

 if (tmwall == FIRSTMASKEDWALL)
	FIRSTMASKEDWALL = tmwall->next;
 else
	tmwall->prev->next = tmwall->next;

 tmwall->prev = NULL;
 tmwall->next = NULL;

}


/*
===============
=
= ActivateAnimMaskedWall
=
===============
*/

void ActivateAnimMaskedWall(animmaskedwallobj_t* amwall)
{
   if (!FIRSTANIMMASKEDWALL)
	   FIRSTANIMMASKEDWALL	= amwall;
   else
	   {
      amwall->prev = LASTANIMMASKEDWALL;
		LASTANIMMASKEDWALL->next = amwall;
	   }
   LASTANIMMASKEDWALL = amwall;
}

/*
===============
=
= DeactivateAnimMaskedWall
=
===============
*/


void DeactivateAnimMaskedWall(animmaskedwallobj_t* amwall)
{
 if (amwall == LASTANIMMASKEDWALL)
	LASTANIMMASKEDWALL = amwall->prev;
 else
	amwall->next->prev = amwall->prev;

 if (amwall == FIRSTANIMMASKEDWALL)
	FIRSTANIMMASKEDWALL = amwall->next;
 else
	amwall->prev->next = amwall->next;

 amwall->prev = NULL;
 amwall->next = NULL;

}


int PlatformHeight(int tilex,int tiley)
   {
   int platform;

   if (!IsPlatform(tilex,tiley))
      return nominalheight;

   platform = MAPSPOT(tilex,tiley,2);

   switch(platform)
      {
      case 1:
         return -10;
      case 4:
         return nominalheight;
      case 5:
      case 6:
         return nominalheight - 64;
      case 7:
         return nominalheight;
      case 8:
      case 9:
         return -10;
      }

   return -1000;
   }

void SpawnAnimatedMaskedWall ( int num )
{
   animmaskedwallobj_t * temp;

	temp = (animmaskedwallobj_t *)Z_LevelMalloc(sizeof(animmaskedwallobj_t),PU_LEVELSTRUCT,NULL);
   if (!temp)
      Error("SpawnAnimatedMaskedWall: Failed on allocation of animated masked wall");
   temp->num=num;
   temp->count=AMW_NUMFRAMES;
   temp->ticcount=AMW_TICCOUNT;
   temp->next=NULL;
   temp->prev=NULL;
   ActivateAnimMaskedWall(temp);
}

void KillAnimatedMaskedWall ( animmaskedwallobj_t * temp )
{
   DeactivateAnimMaskedWall(temp);
   Z_Free(temp);
}


void DoAnimatedMaskedWalls ( void )
{
   boolean done;
   animmaskedwallobj_t * temp;

   for(temp=FIRSTANIMMASKEDWALL;temp;)
      {
      done=false;
      temp->ticcount-=tics;
      while (temp->ticcount<0)
         {
         temp->ticcount+=AMW_TICCOUNT;
         temp->count--;
         maskobjlist[temp->num]->bottomtexture++;
         if (temp->count==0)
            {
            done=true;
            break;
            }
         }
      if (done==true)
         {
         animmaskedwallobj_t * temp2;

         temp2=temp->next;
         KillAnimatedMaskedWall(temp);
         temp=temp2;
         }
      else
         temp=temp->next;
      }
}


int GetIndexForAction(void (*action)(intptr_t))
{int i;

 for(i=0;i<NUMTOUCHPLATEACTIONS;i++)
  if (action == touchactions[i])
	 return i;

 Error("Touchplate Action Not Matched");
 return -1;
}


void SaveTouchPlates(byte ** buffer,int *size)
{int i,k;
 byte * tptr;
 touchplatetype *temp;
 saved_touch_type dummy;

 *size = sizeof(TRIGGER);
 *size += sizeof(numactions);
 *size += sizeof(saved_touch_type)*totalactions;

 *buffer = (byte *)SafeMalloc(*size);
 tptr = *buffer;
 memcpy(tptr,&TRIGGER[0],sizeof(TRIGGER));
 tptr+=sizeof(TRIGGER);

 memcpy(tptr,&numactions[0],sizeof(numactions));
 tptr+=sizeof(numactions);

 for(i=0;i<lasttouch;i++)
	 {
	 for(k=0,temp=touchplate[i];temp;k++,temp = temp->nextaction)
		 {
		 dummy.tictime = temp->tictime;
		 dummy.ticcount = temp->ticcount;
		 dummy.triggered = temp->triggered;
		 dummy.done = temp->done;
		 dummy.complete = temp->complete;

		 if (temp->action)
			dummy.actionindex = GetIndexForAction(temp->action);
		 else
			dummy.actionindex = -1;

		 if (temp->swapaction)
			dummy.swapactionindex = GetIndexForAction(temp->swapaction);
		 else
			dummy.swapactionindex = -1;
		 if ((dummy.actionindex > 5) || (dummy.swapactionindex > 5)) // means whichobj holds pointer to actor
			{
			statobj_t *tstat;

			tstat = (statobj_t*)(temp->whichobj);
			dummy.whichobj = (tstat->whichstat|FL_TSTAT);
			}

		 else if ((dummy.actionindex > 3) || (dummy.swapactionindex > 3))

			{
			objtype *tactor;

			tactor = (objtype*)(temp->whichobj);
			dummy.whichobj = (tactor->whichactor|FL_TACT);
			}

		 else
			dummy.whichobj = temp->whichobj;

		 memcpy(tptr,&dummy,sizeof(saved_touch_type));
		 tptr+=sizeof(saved_touch_type);
		}
	 }
}

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

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

 SoftError("\nstat not found in GetStatForIndex");
 return NULL;

}


void LoadTouchPlates(byte * buffer, int size)
{touchplatetype *temp;
 int i,savedactions,loadedactions,index=0;
 saved_touch_type dummy;

 savedactions = (size-sizeof(TRIGGER)-sizeof(numactions))/sizeof(saved_touch_type);
 memset(touchplate,0,sizeof(touchplate));
 memset(lastaction,0,sizeof(lastaction));
 memset(numactions,0,sizeof(numactions));
 totalactions = 0;

 memcpy(&TRIGGER[0],buffer,sizeof(TRIGGER));
 buffer += sizeof(TRIGGER);

 memcpy(&numactions[0],buffer,sizeof(numactions));
 buffer += sizeof(numactions);

 for(loadedactions=0,index=0,i=0;i<savedactions;i++)
  {memcpy(&dummy,buffer,sizeof(saved_touch_type));
	temp = (touchplatetype*)Z_LevelMalloc(sizeof(touchplatetype),PU_LEVELSTRUCT,NULL);
	if (!temp)
		Error("LoadTouchplates: Failed on allocation of touchplates %d of %d",i,savedactions);
	memset(temp,0,sizeof(*temp));

	temp->tictime = dummy.tictime;
	temp->ticcount = dummy.ticcount;
	temp->triggered = dummy.triggered;
	temp->done = dummy.done;
	temp->complete = dummy.complete;

	if (dummy.whichobj & FL_TACT)
	  temp->whichobj = (intptr_t)(objlist[dummy.whichobj & ~FL_TACT]);

	else if (dummy.whichobj & FL_TSTAT)
	  temp->whichobj = (intptr_t)(GetStatForIndex(dummy.whichobj & ~FL_TSTAT));
	else
	  temp->whichobj = dummy.whichobj;
	if (dummy.actionindex != -1)
	 temp->action = touchactions[dummy.actionindex];
	else
	 temp->action = NULL;

	if (dummy.swapactionindex != -1)
	 temp->swapaction = touchactions[dummy.swapactionindex];
	else
	 temp->swapaction = NULL;

	buffer+=sizeof(saved_touch_type);

	while (!numactions[index])
	  index ++;

	AddTouchplateAction(temp,index);

	/*if (touchplate[index])
	  lastaction[index]->nextaction = temp;
	else
	  touchplate[index] = temp;
	lastaction[index] = temp;*/

	totalactions ++;

	loadedactions++;
	if (loadedactions == numactions[index]) // found end of a touchplate's actions, goto next touch.
	 {loadedactions = 0;
	  index++;
	 }
  }

   SafeFree(objlist);

}




void AddTouchplateAction(touchplatetype *tplate,int index)
{
 if (touchplate[index])
	 {tplate->prevaction = lastaction[index];
	  lastaction[index]->nextaction = tplate;
	 }
  else
	 touchplate[index] = tplate;
  lastaction[index] = tplate;

}




void  RemoveTouchplateAction(touchplatetype *tplate,int index)
{
  if (tplate == lastaction[index])     // remove from master list
	 lastaction[index] = tplate->prevaction;
  else
	 tplate->nextaction->prevaction = tplate->prevaction;

  if (tplate == touchplate[index])
	 touchplate[index] = tplate->nextaction;
  else
	 tplate->prevaction->nextaction = tplate->nextaction;

  Z_Free(tplate);
  numactions[index]--;
  totalactions--;

}



void  Link_To_Touchplate(word touchlocx, word touchlocy, void (*maction)(intptr_t), void (*swapaction)(intptr_t), intptr_t wobj, int delaytime)
{touchplatetype *temp;
 int index;

 index = touchindices[touchlocx][touchlocy]-1;

 temp = (touchplatetype*)Z_LevelMalloc(sizeof(touchplatetype),PU_LEVELSTRUCT,NULL);
 if (!temp)
	 Error("Link_To_Touchplate: Failed on allocation of touchplate\n");
 memset(temp,0,sizeof(*temp));
 temp->action = maction;
 temp->swapaction = swapaction;
 temp->whichobj = wobj;
 temp->tictime = temp->ticcount = delaytime;

 AddTouchplateAction(temp,index);
 /*if(touchplate[index])
	lastaction[index]->nextaction=temp;
 else
	touchplate[index] = temp;
 lastaction[index] = temp;*/
 numactions[index]++;
 totalactions++;
}



void ClockLink (void (*saction)(intptr_t), void (*eaction)(intptr_t), intptr_t wobj,int whichclock)
{touchplatetype*temp;


 // adding two actions per clock
 temp = (touchplatetype*)Z_LevelMalloc(sizeof(touchplatetype),PU_LEVELSTRUCT,NULL);
 if (!temp)
	 Error("ClockLink: Failed on allocation of clock");
 memset(temp,0,sizeof(*temp));
 temp->action = saction;
 temp->swapaction = eaction;
 temp->whichobj = wobj;
 temp->clocktype = 1;

 AddTouchplateAction(temp,whichclock);
/*  if(touchplate[whichclock])
	lastaction[whichclock]->nextaction = temp;
  else
	touchplate[whichclock] = temp;
  lastaction[whichclock]=temp;*/

 numactions[whichclock]++;
 totalactions ++;
}


void DisplayMessageForAction(touchplatetype *temp, boolean *wallmessage,
                             boolean *doormessage, boolean*columnmessage)
   {

   if ((temp->action == ActivatePushWall) ||
      (temp->action == ActivateMoveWall)
      )
      {
      if (*wallmessage == false)
         {
         if (temp->clocktype)
            AddMessage("Time-delay wall moves.",MSG_GAME);
         else
            AddMessage("A wall moves.",MSG_GAME);
         *wallmessage = true;
         }
      }

   else if (temp->action == LinkedCloseDoor)
      {
      if (*doormessage == false)
         {
         if (temp->clocktype)
            AddMessage("Time-delay door closes.",MSG_GAME);
         else
            AddMessage("A door closes.",MSG_GAME);
         *doormessage = true;
         }
      }

   else if (temp->action == LinkedOpenDoor)
      {
      if (*doormessage == false)
         {
         if (temp->clocktype)
            AddMessage("Time-delay door opens.",MSG_GAME);
         else
            AddMessage("A door opens.",MSG_GAME);
         *doormessage = true;
         }
      }

   else if (temp->action == EnableObject)
      {
      objtype *tempactor = (objtype*)(temp->whichobj);

      if (M_ISACTOR(tempactor) && (tempactor->obclass == pillarobj))
         {
         if (*columnmessage == false)
            {
            if (temp->clocktype)
               AddMessage("Time-delay column moves.",MSG_GAME);
            else
               AddMessage("A column moves.",MSG_GAME);
            *columnmessage = true;
            }
         }
      }
   }

void TriggerStuff(void)
   {
   touchplatetype *temp;
   int i,touchcomplete,j;
   int playeron;
   void (*tempact)(intptr_t);
   boolean wallmessage,doormessage,columnmessage;

   for(i=0;i<lasttouch;i++)
      {
      playeron = false;
      for( j = 0; j < numplayers; j++ )
         {
         if ( i == touchindices[ PLAYER[ j ]->tilex ][ PLAYER[ j ]->tiley ] - 1 )
            {
            playeron = true;
            break;
            }
         }
#if (BNACRASHPREVENT == 1)
	  //SetTextMode (  ); qwert
	  //	CRASH IN SHAREWARE 'ride em cowboy' BNA FIX
      // DONT ALLOW BAD touchplate ( == 0 ) see rt_playr.c
	  if (touchplate[i] == 0) {continue;}
#endif

      if (!TRIGGER[i])
         continue;

      else if (touchplate[i]->complete)
         {
         if (!playeron)
            TRIGGER[i] = 0;
         continue;
         }

      if (touchplate[i]->done)
         {
         if (!playeron)
            {
            for(temp = touchplate[i];temp;temp = temp->nextaction)
               temp->triggered=false;
            TRIGGER[i] = 0;
            touchplate[i]->done = false;
            }
         }

      else
         {
         wallmessage = false;
         doormessage = false;
         columnmessage = false;

         for(temp = touchplate[i];temp;temp = temp->nextaction)
            {
            if (temp->action && (!temp->triggered))
               {
               if (!temp->ticcount)
                  {
                  temp->action(temp->whichobj);
                  if (temp->action == ActivateMoveWall)
                     {
                     int tilex,tiley;

                     tilex = pwallobjlist[temp->whichobj]->tilex;
                     tiley = pwallobjlist[temp->whichobj]->tiley;
                     tilemap[tilex][tiley] = 0;

                     }
                  if (gamestate.difficulty == gd_baby)
                     {
                     DisplayMessageForAction(temp,&wallmessage,&doormessage,&columnmessage);
                     }

                  tempact = temp->action;
                  temp->action = temp->swapaction;
                  temp->swapaction = tempact;
                  temp->ticcount = temp->tictime;
                  temp->triggered = true;
                  }

               else
                  temp->ticcount --;
               }
            }
   //done:

         // check to see if any actions will ever be triggered by this
         // touchplate again; if not, null touchplate out; else,
         // check status of other actions

         touchcomplete = 1;
         for(temp = touchplate[i];temp;temp = temp->nextaction)
            {
            if (temp->action)
               {
               touchcomplete = 0;
               break;
               }
            }

         if (touchcomplete)
            touchplate[i]->complete = 1; // this touchplate is out of commission
         else
            {
            touchplate[i]->done = true;
            for(temp = touchplate[i];temp;temp = temp->nextaction)
               {
               if (temp->action && (!temp->triggered))
                  {
                  touchplate[i]->done = false;
                  break;
                  }
               }
            }
         }
      }
   }


//==================== Tile stuff ====================================

boolean CheckTile(int x, int y)
{

	if ((x < 2) || (x > (MAPSIZE-1)) || (y < 2) || (y > (MAPSIZE - 1)))
	  return false;

	if (actorat[x][y])
	  {objtype *check = (objtype*)(actorat[x][y]);
		if (insetupgame)
		  return false;
		if (!(M_ISACTOR(check) && (check->obclass == playerobj)))
		  return false;
     }
   if (DiskAt(x,y))
      return false;
   if (sprites[x][y])
		return false;
	if ((tilemap[x][y]) && (IsPlatform(x,y)==false))
		return false;
	if ((AREANUMBER(x,y)<=0) || (AREANUMBER(x,y)>NUMAREAS))
		return false;
   if (IsWindow(x,y))
      return false;
   return true;
}


#define CountTile(x,y) \
{                       \
 if (oldarea == AREANUMBER(x,y))\
    {if (CheckTile(x,y))         \
        numemptytiles ++;                   \
                                             \
     areanumbercount++;                       \
     if (areanumbercount == numareatiles[oldarea])\
        return numemptytiles;                     \
    }                                              \
                                                    \
}                                                   \


int Number_of_Empty_Tiles_In_Area_Around(int x, int y)
{int roverx,rovery,areanumbercount=0,
     numemptytiles=0,oldarea,i,limit,j;

 oldarea = AREANUMBER(x,y);

   for (i=1;;i++)
      {roverx = x-i;
       rovery = y-i;

       CountTile(roverx,rovery);
       limit = i<<1;

       for(j=0;j<limit;j++)
         {roverx++;
          CountTile(roverx,rovery);
         }

       for(j=0;j<limit;j++)
         {rovery++;
          CountTile(roverx,rovery);
         }

       for(j=0;j<limit;j++)
         {roverx--;
          CountTile(roverx,rovery);
         }

       for(j=0;j<limit-1;j++)
         {rovery--;
          CountTile(roverx,rovery);
         }
      }
}



#define CheckSet(x,y)      \
{if (CheckTile(x,y) && (oldarea == AREANUMBER(x,y))) \
   {*stilex = x;    \
    *stiley = y;    \
    return;              \
   } \
}                     \



void FindEmptyTile(int *stilex, int *stiley)
{
   int i,j,x,y,oldarea,roverx,rovery,limit;

	oldarea = AREANUMBER(*stilex,*stiley);

   x = *stilex;
   y = *stiley;

   if (CheckTile(x,y) && (oldarea == AREANUMBER(x,y)))
     return;

   for (i=1;;i++)
      {roverx = x-i;
       rovery = y-i;

       CheckSet(roverx,rovery);
       limit = i<<1;

       for(j=0;j<limit;j++)
         {roverx++;
          CheckSet(roverx,rovery);
         }

       for(j=0;j<limit;j++)
         {rovery++;
          CheckSet(roverx,rovery);
         }

       for(j=0;j<limit;j++)
         {roverx--;
          CheckSet(roverx,rovery);
         }

       for(j=0;j<limit-1;j++)
         {rovery--;
          CheckSet(roverx,rovery);
         }
      }
}

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




void RecursiveConnect (int areanumber)
{
	int	i;

	for (i=0;i<NUMAREAS;i++)
	{
		if (areaconnect[areanumber][i] && !areabyplayer[i])
		{
			areabyplayer[i] = true;
			RecursiveConnect (i);
		}
	}
}


/*
==============
=
= ConnectAreas
=
= Scans outward from playerarea, marking all connected areas
=
==============
*/

void ConnectAreas (void)
{objtype*temp;
 statobj_t*tstat;
 int i;
#define MASTER_DISK(ob) ((ob->obclass == diskobj) && (ob->flags & FL_MASTER))

   memset (areabyplayer,0,sizeof(areabyplayer));
   for (i=0;i<numplayers;i++)
      {
      areabyplayer[PLAYER[i]->areanumber] = true;
      RecursiveConnect (PLAYER[i]->areanumber);
      }
	for(temp=FIRSTACTOR;temp;temp=temp->next)
		{
      if (MASTER_DISK(temp))
        continue;
		if (!areabyplayer[temp->areanumber])
			continue;
		if (!(temp->flags & FL_ABP))
			{temp->flags |= FL_ABP;
			 MakeActive(temp);
			}
		}

	for(tstat=FIRSTSTAT;tstat;tstat=tstat->statnext)
	  {if (areabyplayer[tstat->areanumber])
		  {if (!(tstat->flags & FL_ABP))
			  {tstat->flags |= FL_ABP;
				MakeStatActive(tstat);
			  }
		  }
		else if (tstat->flags & FL_ABP)
		 {MakeStatInactive(tstat);
		  tstat->flags &= ~FL_ABP;
		 }
	  }

	for(i=0;i<maskednum;i++)
	  {if (areabyplayer[maskobjlist[i]->areanumber])
		  {if (!(maskobjlist[i]->flags & MW_ABP))
			  {maskobjlist[i]->flags |= MW_ABP;
				MakeMaskedWallActive(maskobjlist[i]);
			  }
		  }
		else if (maskobjlist[i]->flags & MW_ABP)
		 {MakeMaskedWallInactive(maskobjlist[i]);
		  maskobjlist[i]->flags &= ~MW_ABP;
		 }
	  }
}


void InitAreas (void)
{
	memset (areabyplayer,0,sizeof(areabyplayer));
	memset (areaconnect,0,sizeof(areaconnect));
}


/*
===============
=
= InitDoorList
=
===============
*/

void InitDoorList (void)
{
   doornum=0;
   pwallnum=0;
	maskednum=0;
	lasttouch = 0;
   numclocks=0;

	memset(touchindices,0,sizeof(touchindices));
	memset(touchplate,0,sizeof(touchplate));
	memset(lastaction,0,sizeof(lastaction));
	memset(numactions,0,sizeof(numactions));
	totalactions = 0;
	memset(TRIGGER,0,sizeof(TRIGGER));
	memset(Clocks,0,sizeof(Clocks));
   FIRSTMASKEDWALL=NULL;
   LASTMASKEDWALL=NULL;
   FIRSTANIMMASKEDWALL=NULL;
   LASTANIMMASKEDWALL=NULL;
}

/*
===============
=
= IsWall
=
===============
*/

int IsWall (int tilex, int tiley)
{
	int map;

	map=MAPSPOT(tilex,tiley,0);

	if ((map>=1) && (map<=89))
		return 1;

	else if ((map>=106) && (map<=107))
		return 1;

	else if ((map>=224) && (map<=233))
		return 1;

	else if ((map>=242) && (map<=244))
		return 1;

	return 0;
}



/*
===============
=
= InitElevators
=
===============
*/

void InitElevators(void)
{_numelevators = 0;
 memset(ELEVATOR,0,sizeof(ELEVATOR));

}



/*
===============
=
= IsDoor
=
===============
*/

int IsDoor (int tilex, int tiley)
{
	int map;

	map=MAPSPOT(tilex,tiley,0);

	if ((map>=33) && (map<=35))
		return 1;

	if ((map>=90) && (map<=104))
		return 1;

	if ((map>=154) && (map<=156))
		return 1;

	if (M_ISDOOR(tilex,tiley))
      return 1;

	return 0;
}


/*
===============
=
= SpawnDoor
=
===============
*/

void SpawnDoor (int tilex, int tiley, int lock, int texture)
{
   int i;
   doorobj_t * lastdoorobj;
   int up,dn,lt,rt;
   int basetexture;

   doorobjlist[doornum]=(doorobj_t*)Z_LevelMalloc(sizeof(doorobj_t),PU_LEVELSTRUCT,NULL);
   if (!doorobjlist[doornum])
      Error("SpawnDoor: Failed on allocation of door %d ",doornum);
   memset(doorobjlist[doornum],0,sizeof(doorobj_t));
   lastdoorobj=doorobjlist[doornum];

   if (
        ( MAPSPOT(tilex,tiley,1) >= 29 ) &&
        ( MAPSPOT(tilex,tiley,1) <= 32 )
      )
      {
      lock = MAPSPOT(tilex,tiley,1) - 28;
      }

   lastdoorobj->position = 0;
	lastdoorobj->tilex = tilex;
	lastdoorobj->tiley = tiley;
	lastdoorobj->lock = lock;
	lastdoorobj->action = dr_closed;
	lastdoorobj->which = DOOR;
   lastdoorobj->flags = 0;
	lastdoorobj->eindex = -1;

   //
   // make the door space solid
   //

   if (loadedgame==false)
      actorat[tilex][tiley] = lastdoorobj;

   if (IsDoor(tilex,tiley-1)) up=2;
   else if (IsWall(tilex,tiley-1)) up=1;
   else up=0;

   if (IsDoor(tilex,tiley+1)) dn=2;
   else if (IsWall(tilex,tiley+1)) dn=1;
   else dn=0;

   if (IsDoor(tilex-1,tiley)) lt=2;
   else if (IsWall(tilex-1,tiley)) lt=1;
   else lt=0;

   if (IsDoor(tilex+1,tiley)) rt=2;
   else if (IsWall(tilex+1,tiley)) rt=1;
   else rt=0;

   if ((up==1) && (dn==1))
		lastdoorobj->vertical = true;
   else if ((lt==1) && (rt==1))
		lastdoorobj->vertical = false;
   else if ((up>0) && (dn>0))
		lastdoorobj->vertical = true;
   else if ((lt>0) && (rt>0))
		lastdoorobj->vertical = false;
   else if (up>0)
		lastdoorobj->vertical = true;
   else if (dn>0)
		lastdoorobj->vertical = true;
   else if (lt>0)
		lastdoorobj->vertical = false;
   else if (rt>0)
		lastdoorobj->vertical = false;

   switch (texture)
      {

      case 0:
      case 8:
         basetexture = W_GetNumForName("RAMDOOR1\0");
         break;

      case 1:
      case 9:
         basetexture = W_GetNumForName("DOOR2\0");
         break;


      case 2:
      case 3:
      case 13:
         basetexture = W_GetNumForName("TRIDOOR1\0");
         break;

      case 10:
      case 11:
      case 14:
         basetexture = W_GetNumForName("SDOOR4\0");
         break;



      case 12:
         basetexture = W_GetNumForName("EDOOR\0");
         break;
      case 15:
         basetexture = W_GetNumForName("SNDOOR\0");
         break;
      case 16:
         basetexture = W_GetNumForName("SNADOOR\0");
         break;
      case 17:
         basetexture = W_GetNumForName("SNKDOOR\0");
         break;

      case 18:
         basetexture = W_GetNumForName("TNDOOR\0");
         break;
      case 19:
         basetexture = W_GetNumForName("TNADOOR\0");
         break;
      case 20:
         basetexture = W_GetNumForName("TNKDOOR\0");
         break;
      default:
         Error("Illegal door value encountered\n");
         break;
      }
   lastdoorobj->basetexture = basetexture;
	lastdoorobj->texture = lastdoorobj->basetexture;

   SD_PreCacheSoundGroup(SD_OPENDOORSND,SD_CLOSEDOORSND);

//
// make the door tile a special tile, and mark the adjacent tiles
// for door sides
//
   tilemap[tilex][tiley] = doornum | 0x8000;

   switch (texture)
      {
      case 0:
      case 1:
      case 2:
      case 3:
      case 8:
      case 9:
      case 10:
      case 11:
      case 12:
      case 13:
      case 14:
         lastdoorobj->sidepic    = W_GetNumForName("SIDE8");
         lastdoorobj->alttexture = W_GetNumForName("ABOVEW3");
         break;

      case 15:
      case 16:
      case 17:
      #if (SHAREWARE == 1)
         lastdoorobj->sidepic = W_GetNumForName("SIDE8");

      #else
         lastdoorobj->sidepic    = W_GetNumForName("SIDE16");
      #endif

         lastdoorobj->alttexture = W_GetNumForName("ABOVEW16");
         break;

      case 18:
      case 19:
      case 20:
      #if (SHAREWARE == 1)
         lastdoorobj->sidepic = W_GetNumForName("SIDE8");
         lastdoorobj->alttexture = W_GetNumForName("ABOVEW3");

      #else
         lastdoorobj->sidepic    = W_GetNumForName("SIDE17");
         lastdoorobj->alttexture = W_GetNumForName("ABOVEW17");

      #endif

         break;
      default:
         Error("Illegal door value encountered\n");
         break;
      }



   if ((lock>0) && (lock<5))
      lastdoorobj->sidepic    = W_GetNumForName("lock1")+lock-1;

   PreCacheLump(lastdoorobj->sidepic,PU_CACHEWALLS,cache_pic_t);
   PreCacheLump(lastdoorobj->alttexture,PU_CACHEWALLS,cache_pic_t);

	if (lastdoorobj->vertical==true)
	{
      if (up==1)
		   tilemap[tilex][tiley-1] |= 0x4000;
      else if (up==2)
         lastdoorobj->flags|=DF_MULTI;
      if (dn==1)
		   tilemap[tilex][tiley+1] |= 0x4000;
      else if (dn==2)
         lastdoorobj->flags|=DF_MULTI;
	}
	else
	{
      if (lt==1)
		   tilemap[tilex-1][tiley] |= 0x4000;
      else if (lt==2)
         lastdoorobj->flags|=DF_MULTI;
      if (rt==1)
		   tilemap[tilex+1][tiley] |= 0x4000;
      else if (rt==2)
         lastdoorobj->flags|=DF_MULTI;
	}

   PreCacheLump(lastdoorobj->texture,PU_CACHEWALLS,cache_pic_t);
   for (i=1;i<9;i++) // only first texture is pic_t!
      PreCacheLump(lastdoorobj->texture+i,PU_CACHEWALLS,cache_patch_t);
	doornum++;
	lastdoorobj++;
	if (doornum==MAXDOORS)
		Error ("Too many doors on level!");

}

/*
===============
=
= MakeWideDoorVisible
=
===============
*/
void MakeWideDoorVisible ( int doornum )
{
   int dx,dy;
   doorobj_t * dr2;
   doorobj_t * dr;
   int tx,ty;

   dr=doorobjlist[doornum];

   dx=0;dy=0;
   if (dr->vertical==true)
      dy=1;
   else
      dx=1;
   spotvis[dr->tilex][dr->tiley]=1;
   tx=dr->tilex+dx;
   ty=dr->tiley+dy;
	while (M_ISDOOR(tx,ty))
      {
      int num;

      num=tilemap[tx][ty]&0x3ff;
      dr2=doorobjlist[num];
      if (!(dr2->flags&DF_MULTI))
         break;
      spotvis[tx][ty]=1;

      tx+=dx;
      ty+=dy;
      }
   tx=dr->tilex-dx;
   ty=dr->tiley-dy;
	while (M_ISDOOR(tx,ty))
      {
      int num;

      num=tilemap[tx][ty]&0x3ff;
      dr2=doorobjlist[num];
      if (!(dr2->flags&DF_MULTI))
         break;
      spotvis[tx][ty]=1;

      tx-=dx;
      ty-=dy;
      }
}

/*
=====================
=
= LockLinkedDoor
=
=====================
*/

void LockLinkedDoor (int door)
{
	doorobj_t*dptr;

	dptr = doorobjlist[door];
   if (!dptr->lock)
      dptr->lock=5;
}

/*
=====================
=
= IsDoorLinked
=
=====================
*/

boolean IsDoorLinked (int door)
{
	doorobj_t*dptr;

	dptr = doorobjlist[door];
   if (dptr->lock==5)
      return true;
   return false;
}


/*
===============
=
= FixDoorAreaNumbers
=
===============
*/
void FixDoorAreaNumbers ( void )
{
   int i;
   int up,dn,lt,rt;
   int tilex,tiley;

   for (i=0;i<doornum;i++)
      {
      tilex=doorobjlist[i]->tilex;
      tiley=doorobjlist[i]->tiley;
      up=MAPSPOT(tilex,tiley-1,0)-AREATILE;
		dn=MAPSPOT(tilex,tiley+1,0)-AREATILE;
		lt=MAPSPOT(tilex-1,tiley,0)-AREATILE;
		rt=MAPSPOT(tilex+1,tiley,0)-AREATILE;

      up = ((up>0) && (up<=NUMAREAS));
		dn = ((dn>0) && (dn<=NUMAREAS));
		lt = ((lt>0) && (lt<=NUMAREAS));
		rt = ((rt>0) && (rt<=NUMAREAS));


		if (doorobjlist[i]->vertical==true)
			{
         if (rt)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex+1,tiley,0);
         else if (lt)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex-1,tiley,0);
			else
				Error("FixDoors: Couldn't fix up area at x=%d y=%d\n",tilex,tiley);
			}
		else
			{
         if (dn)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex,tiley+1,0);
         else if (up)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex,tiley-1,0);
			else
				Error("FixDoors: Couldn't fix up area at x=%d y=%d\n",tilex,tiley);
			}
      if (IsDoorLinked(i))
         UtilizeDoor(i,LockLinkedDoor);
		}
}


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

/*
=====================
=
= OpenDoor
=
=====================
*/

void OpenDoor (int door)
{
	if (doorobjlist[door]->action == dr_open)
		doorobjlist[door]->ticcount = 0;			// reset open time
	else
      {
		doorobjlist[door]->action = dr_opening;	// start it opening
      }
}

/*
=====================
=
= DoorUnBlocked
=
=====================
*/

boolean DoorUnBlocked (int door)
{
	int	tilex,tiley;
	objtype *check;
	doorobj_t*dptr;

	dptr = doorobjlist[door];

//
// don't close on anything solid
//

	tilex = dptr->tilex;
	tiley = dptr->tiley;
	check = (objtype*)actorat[tilex][tiley];

	if (check && (check->which == ACTOR))
		return false;

	if (dptr->vertical==true)
	{
		check = (objtype*)actorat[tilex-1][tiley];
		if (check && (check->which==ACTOR) && ((check->x+MINDIST) >> TILESHIFT) == tilex )
			return false;
		check = (objtype*)actorat[tilex+1][tiley];
		if (check && (check->which==ACTOR) && ((check->x-MINDIST) >> TILESHIFT) == tilex )
			return false;
	}
	else if (dptr->vertical==false)
	{
		check = (objtype*)actorat[tilex][tiley-1];
		if (check && (check->which==ACTOR) && ((check->y+MINDIST) >> TILESHIFT) == tiley )
			return false;
		check = (objtype*)actorat[tilex][tiley+1];
		if (check && (check->which==ACTOR) && ((check->y-MINDIST) >> TILESHIFT) == tiley )
			return false;
	}
   return true;
}


/*
=====================
=
= DoorReadyToClose
=
= Alter the door's state
=
=====================
*/

boolean DoorReadyToClose(int door)
{
	doorobj_t*dptr;
	int dx,dy;
	doorobj_t * dr2;
	int tx,ty;


	dptr = doorobjlist[door];

	if (dptr->action==dr_closed)
		return true;

	if (DoorUnBlocked(door)==false)
		return false;

	dx=0;dy=0;
	if (dptr->vertical==true)
		dy=1;
	else
		dx=1;
	tx=dptr->tilex+dx;
	ty=dptr->tiley+dy;
	while (M_ISDOOR(tx,ty))
		{
		int num;

		num=tilemap[tx][ty]&0x3ff;
		dr2=doorobjlist[num];
		if (!(dr2->flags&DF_MULTI))
			break;
		if (DoorUnBlocked(num)==false)
			return false;
		tx+=dx;
		ty+=dy;
		}
	tx=dptr->tilex-dx;
	ty=dptr->tiley-dy;
	while (M_ISDOOR(tx,ty))
		{
		int num;

		num=tilemap[tx][ty]&0x3ff;
		dr2=doorobjlist[num];
		if (!(dr2->flags&DF_MULTI))
			break;
		if (DoorUnBlocked(num)==false)
			return false;
		tx-=dx;
		ty-=dy;
		}
	return true;
}


/*
=====================
=
= CloseDoor
=
=====================
*/

void CloseDoor (int door)
{
	int	tilex,tiley,area;
	doorobj_t*dptr;

	dptr = doorobjlist[door];
   if (dptr->action == dr_closed)
      return;
	tilex = dptr->tilex;
	tiley = dptr->tiley;

//
// play door sound
//
   area = MAPSPOT(tilex,tiley,0)-AREATILE;
   if (areabyplayer[area])
      {
      dptr->soundhandle=SD_PlaySoundRTP ( SD_CLOSEDOORSND, dptr->tilex<<16, dptr->tiley<<16 );
      }

	dptr->action = dr_closing;
//
// make the door space solid
//
	actorat[tilex][tiley] = dptr;
}



/*
=====================
=
= OperateDoor
=
= The player wants to change the door's direction
=
=====================
*/

void OperateDoor (int keys, int door, boolean localplayer )
{
	int	lock;
	doorobj_t*dptr;

	dptr = doorobjlist[door];
	if ( ( dptr->flags & DF_ELEVLOCKED ) ||
      ( MISCVARS->GASON && ( MAPSPOT( dptr->tilex,
      dptr->tiley, 1 ) == GASVALUE ) ) )
      {
      if ( localplayer )
         {
         // locked
         SD_Play ( SD_NOITEMSND );
         }
      return;
      }

   lock = dptr->lock;
	if ( lock && !( keys & ( 1 << ( lock - 1 ) ) ) )
      {
      if ( localplayer )
         {
         // locked
         switch (lock)
            {
            case 1:
               AddMessage("You need the \\EGOLD key",MSG_DOOR);
               break;

            case 2:
               AddMessage("You need the \\FSILVER key",MSG_DOOR);
               break;

            case 3:
               AddMessage("You need the \\8IRON key",MSG_DOOR);
               break;

            case 4:
               AddMessage("You need the \\AOSCURO key",MSG_DOOR);
               break;

            default:
               AddMessage("This door appears to be locked",MSG_DOOR);
               break;
            }

         SD_Play( SD_NOITEMSND );
         }
      return;
      }
   UseDoor(door);
   }

/*
=====================
=
= LinkedOpenDoor
=
=====================
*/

void LinkedOpenDoor (intptr_t door)
{
   UtilizeDoor(door,OpenDoor);
}

/*
=====================
=
= LinkedCloseDoor
=
=====================
*/

void LinkedCloseDoor (intptr_t door)
{
   if (DoorReadyToClose(door)==true)
      UtilizeDoor(door,CloseDoor);
}

/*
=====================
=
= UtilizeDoor
=
= Alter the door's state
=
=====================
*/

void UtilizeDoor (int door,void (*action)(int))
{
	doorobj_t*dptr;
   int dx,dy;
   doorobj_t * dr2;
   int tx,ty;

	dptr = doorobjlist[door];

   action(door);

   dx=0;dy=0;
   if (dptr->vertical==true)
      dy=1;
   else
      dx=1;
   tx=dptr->tilex+dx;
   ty=dptr->tiley+dy;
	while (M_ISDOOR(tx,ty))
      {
      int num;

      num=tilemap[tx][ty]&0x3ff;
      dr2=doorobjlist[num];
      if (!(dr2->flags&DF_MULTI))
         break;
      action(num);
      tx+=dx;
      ty+=dy;
      }
   tx=dptr->tilex-dx;
   ty=dptr->tiley-dy;
	while (M_ISDOOR(tx,ty))
      {
      int num;

      num=tilemap[tx][ty]&0x3ff;
      dr2=doorobjlist[num];
      if (!(dr2->flags&DF_MULTI))
         break;
      action(num);
      tx-=dx;
      ty-=dy;
      }
}

/*
=====================
=
= UseDoor
=
= Alter the door's state
=
=====================
*/

void UseDoor (int door)
{
	switch (doorobjlist[door]->action)
	{
	case dr_closing:
		SD_StopSound(doorobjlist[door]->soundhandle);
	case dr_closed:
      UtilizeDoor(door,OpenDoor);
		break;
	case dr_opening:
		SD_StopSound(doorobjlist[door]->soundhandle);
	case dr_open:
      if (DoorReadyToClose(door)==true)
         UtilizeDoor(door,CloseDoor);
		break;
	}
}

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

/*
===============
=
= DoorOpen
=
= Close the door after three seconds
=
===============
*/

void DoorOpen (int door)
{doorobj_t* dptr;

 dptr = doorobjlist[door];
 dptr->ticcount += 1;
 if ((dptr->ticcount >= OPENTICS) &&
     (!(dptr->flags & DF_TIMED)) &&
     (DoorReadyToClose(door)==true))
    UtilizeDoor(door,CloseDoor);
}



/*
===============
=
= DoorOpening
=
===============
*/

void DoorOpening (int door)
{
	int		area1,area2;
	word  	*map;
	long	   position;
	int      tilex,tiley;

	position = doorobjlist[door]->position;
	tilex = doorobjlist[door]->tilex;
	tiley = doorobjlist[door]->tiley;
	if (!position)
	{
	//
	// door is just starting to open, so connect the areas
	//
		map = &MAPSPOT(tilex,tiley,0);

		if (doorobjlist[door]->vertical==true)
		{
			area1 =	*(map+1);
			area2 =	*(map-1);
		}
		else
		{
		 area1 =	*(map-mapwidth);
		 area2 =	*(map+mapwidth);
		}
		area1 -= AREATILE;
		area2 -= AREATILE;
		areaconnect[area1][area2]++;
		areaconnect[area2][area1]++;
		if ((insetupgame==false) && (loadedgame==false))
			ConnectAreas ();
	   if (areabyplayer[area1])
    	   {
         doorobjlist[door]->soundhandle=SD_PlaySoundRTP ( SD_OPENDOORSND, doorobjlist[door]->tilex<<16, doorobjlist[door]->tiley<<16 );
         }
	}

//
// slide the door by an adaptive amount
//
	position += 1<<12;
	if (position >= 0xffff)
	{
	//
	// door is all the way open
	//
		position = 0xffff;
		doorobjlist[door]->ticcount = 0;
		doorobjlist[door]->action = dr_open;
		if (doorobjlist[door] == actorat[tilex][tiley])
			actorat[tilex][tiley] = 0;
	}

	doorobjlist[door]->position = position;
	doorobjlist[door]->texture=doorobjlist[door]->basetexture+((position+1)>>13);
}


/*
===============
=
= DoorClosing
=
===============
*/

void DoorClosing (int door)
{
	int		area1,area2;
	word	   *map;
	long	   position;
	int		tilex,tiley;
	doorobj_t *dptr;

	dptr = doorobjlist[door];

	tilex = dptr->tilex;
	tiley = dptr->tiley;

	position = dptr->position;

//
// slide the door by an adaptive amount
//
	position -= 1<<12;
	if (position < (0xffff >> 1))
	   ResolveDoorSpace(tilex,tiley);


	if (position <= 0)
	{
	//
	// door is closed all the way, so disconnect the areas
	//
		position = 0;

		dptr->action = dr_closed;



		map = &MAPSPOT(tilex,tiley,0);

      if (areabyplayer[(*map-AREATILE)])
         {
         dptr->soundhandle=SD_PlaySoundRTP ( SD_DOORHITSND, dptr->tilex<<16, dptr->tiley<<16 );
         }

      if (dptr->vertical==true)
		{
			area1 =	*(map+1);
			area2 =	*(map-1);
		}
		else
		{
			area1 =	*(map-mapwidth);
			area2 =	*(map+mapwidth);
		}
		area1 -= AREATILE;
		area2 -= AREATILE;
		areaconnect[area1][area2]--;
		areaconnect[area2][area1]--;

		ConnectAreas ();
   }

	dptr->position = position;
	dptr->texture=dptr->basetexture+((position+1)>>13);
}

/*
===============
=
= IsMaskedWall
=
===============
*/

int IsMaskedWall (int tilex, int tiley)
{
	int map;

	if (IsPlatform(tilex,tiley))
		return 1;

	map=MAPSPOT(tilex,tiley,0);

	if ((map>=157) && (map<=160))
		return 1;

	if ((map>=162) && (map<=179))
		return 1;

	if (M_ISMWALL(tilex,tiley))
      return 1;

	return 0;
}

/*
===============
=
= SpawnMaskedWall
=
===============
*/


void SpawnMaskedWall (int tilex, int tiley, int which, int flags)
{word *map;
 int area1, area2;
 int up,dn,lt,rt;
 int himask;
 boolean sidepic;
 int side, middle, above, bottom;
 maskedwallobj_t * lastmaskobj;
 boolean metal;

	himask=W_GetNumForName("HMSKSTRT")+1;

	maskobjlist[maskednum]=(maskedwallobj_t*)Z_LevelMalloc(sizeof(maskedwallobj_t),PU_LEVELSTRUCT,NULL);
	memset(maskobjlist[maskednum],0,sizeof(maskedwallobj_t));
	lastmaskobj=maskobjlist[maskednum];

   sidepic=true;

	lastmaskobj->tilex = tilex;
	lastmaskobj->tiley = tiley;
	lastmaskobj->which = MWALL;
	up=MAPSPOT(tilex,tiley-1,0)-AREATILE;
	dn=MAPSPOT(tilex,tiley+1,0)-AREATILE;
	lt=MAPSPOT(tilex-1,tiley,0)-AREATILE;
	rt=MAPSPOT(tilex+1,tiley,0)-AREATILE;

   if (IsMaskedWall(tilex,tiley-1)) up=2;
   else if (IsWall(tilex,tiley-1)) up=1;
   else up=0;

   if (IsMaskedWall(tilex,tiley+1)) dn=2;
   else if (IsWall(tilex,tiley+1)) dn=1;
   else dn=0;

   if (IsMaskedWall(tilex-1,tiley)) lt=2;
   else if (IsWall(tilex-1,tiley)) lt=1;
   else lt=0;

   if (IsMaskedWall(tilex+1,tiley)) rt=2;
   else if (IsWall(tilex+1,tiley)) rt=1;
   else rt=0;

   if ((up==1) && (dn==1))
		lastmaskobj->vertical = true;
   else if ((lt==1) && (rt==1))
		lastmaskobj->vertical = false;
   else if ((up>0) && (dn>0))
		lastmaskobj->vertical = true;
   else if ((lt>0) && (rt>0))
		lastmaskobj->vertical = false;
   else if (up>0)
		lastmaskobj->vertical = true;
   else if (dn>0)
		lastmaskobj->vertical = true;
   else if (lt>0)
		lastmaskobj->vertical = false;
   else if (rt>0)
		lastmaskobj->vertical = false;

   tilemap[tilex][tiley] = maskednum | 0xc000;
	map = &MAPSPOT(tilex,tiley,0);

	if (lastmaskobj->vertical==true)
		{
		area1 =	*(map+1);
		area2 =	*(map-1);
      area1 -= AREATILE;
		area2 -= AREATILE;
		if (lt==0 && rt==0)
			{
			areaconnect[area1][area2]++;
			areaconnect[area2][area1]++;
			}
		}
	else
		{
		area1 =	*(map-mapwidth);
		area2 =	*(map+mapwidth);
      area1 -= AREATILE;
		area2 -= AREATILE;
		if (up==0 && dn==0)
			{
			areaconnect[area1][area2]++;
			areaconnect[area2][area1]++;
			}
		}
	lastmaskobj->flags=flags;

   if (IsPlatform(tilex,tiley))
      {
      if (MAPSPOT(tilex,tiley,0)==21)
         {
         metal=true;
			actorat[tilex][tiley]=0;
         }
      else
         metal=false;
      }



   switch (which)
      {
      case mw_peephole:

      //#if (SHAREWARE == 1)
         side = W_GetNumForName("SIDE21");
         middle = W_GetNumForName("ABOVEM4A") ;
         above  = W_GetNumForName("ABOVEM4") ;
      /*
      #else
         side   = W_GetNumForName("SIDE16");
         middle = W_GetNumForName("ABOVEM3A") ;
         above  = W_GetNumForName("ABOVEM2A") ;

      #endif
      */
         bottom = W_GetNumForName("PEEPMASK");
         break;


      case mw_dogwall:

         side = W_GetNumForName("SIDE21");
         above  = W_GetNumForName("ABOVEM4") ;

   #if (SHAREWARE == 1)
         middle = W_GetNumForName("ABOVEM4A") ;
   #else
         middle = W_GetNumForName("ABOVEM9") ;

   #endif
         bottom = W_GetNumForName("DOGMASK");
         break;

      case mw_multi1:

  /*
      #if (SHAREWARE == 1)
         side = W_GetNumForName("SIDE21");
         middle = W_GetNumForName("ABOVEM4A") ;
         above  = W_GetNumForName("ABOVEM4") ;

      #else
  */
         //side   = W_GetNumForName("SIDE23") ;
         side   = W_GetNumForName("SIDE21") ;
         middle = W_GetNumForName("ABOVEM5A") ;
         above  = W_GetNumForName("ABOVEM5") ;

     // #endif

         bottom = W_GetNumForName("MULTI1");
         break;

      case mw_multi2:
      /*
      #if (SHAREWARE == 1)
         side = W_GetNumForName("SIDE21");
         middle = W_GetNumForName("ABOVEM4A");
         above  = W_GetNumForName("ABOVEM4") ;

      #else
      */
         //side   = W_GetNumForName("SIDE23") ;
         side   = W_GetNumForName("SIDE21") ;
         middle = W_GetNumForName("ABOVEM5B");
         above  = W_GetNumForName("ABOVEM5") ;


      //#endif

         bottom = W_GetNumForName("MULTI2");
         break;

      case mw_multi3:

      /*
      #if (SHAREWARE == 1)
         side = W_GetNumForName("SIDE21");
         middle = W_GetNumForName("ABOVEM4A") ;
         above  = W_GetNumForName("ABOVEM4")  ;

      #else
      */
         //side   = W_GetNumForName("SIDE23") ;
         side   = W_GetNumForName("SIDE21") ;
         middle = W_GetNumForName("ABOVEM5C") ;
         above  = W_GetNumForName("ABOVEM5")  ;


      //#endif

         bottom = W_GetNumForName("MULTI3");
         break;

      case mw_singlepane:

   //   #if (SHAREWARE == 1)
         side = W_GetNumForName("SIDE21");

   //   #else
   //      side   = W_GetNumForName("SIDE22") ;
   //   #endif

         middle = W_GetNumForName("ABOVEM4A") ;
         above  = W_GetNumForName("ABOVEM4")  ;
         bottom = W_GetNumForName("MASKED4");
         break;

      case mw_normal1:
         side = W_GetNumForName("SIDE21");


     // #if (SHAREWARE == 1)
         middle = W_GetNumForName("ABOVEM4A") ;
         above  = W_GetNumForName("ABOVEM4")  ;

      //#else
       //  middle = W_GetNumForName("ABOVEM1A") ;
       //  above  = W_GetNumForName("ABOVEM1")  ;


      //#endif

         bottom = W_GetNumForName("MASKED1");
         break;

      case mw_normal2:
         side = W_GetNumForName("SIDE21");

      //#if (SHAREWARE == 1)
         middle = W_GetNumForName("ABOVEM4A") ;
         above  = W_GetNumForName("ABOVEM4")  ;

      //#else
      //   middle = W_GetNumForName("ABOVEM2A") ;
      //   above  = W_GetNumForName("ABOVEM2")  ;

      //#endif

         bottom = W_GetNumForName("MASKED2");
         break;

      case mw_normal3:
         side = W_GetNumForName("SIDE21");

      //#if (SHAREWARE == 1)
         middle = W_GetNumForName("ABOVEM4A") ;
         above  = W_GetNumForName("ABOVEM4")  ;

      //#else
      //   middle = W_GetNumForName("ABOVEM3A") ;
      //   above  = W_GetNumForName("ABOVEM3")  ;

      //#endif

         bottom = W_GetNumForName("MASKED3");
         break;

      case mw_exitarch:

         side = W_GetNumForName("SIDE21");

      //#if (SHAREWARE == 1)
         middle = W_GetNumForName("ABOVEM4A") ;
         above  = W_GetNumForName("ABOVEM4")  ;

      //#else
      //   middle = W_GetNumForName("ABOVEM6A") ;
      //   above  = W_GetNumForName("ABOVEM6")  ;

      //#endif

         bottom = W_GetNumForName("EXITARCH");
         break;

      case mw_secretexitarch:

         side = W_GetNumForName("SIDE21");

      //#if (SHAREWARE == 1)
         middle = W_GetNumForName("ABOVEM4A") ;
         above  = W_GetNumForName("ABOVEM4")  ;

      //#else
      //   middle = W_GetNumForName("ABOVEM8A") ;
      //   above  = W_GetNumForName("ABOVEM8")  ;

      //#endif

         bottom = W_GetNumForName("EXITARCA");
         break;

      case mw_railing:
         sidepic = false;
         middle = -1;
         above  = -1;
         bottom = W_GetNumForName("RAILING");
         break;

      case mw_hiswitchon:
         sidepic = false;
         middle = himask+1;
         above = himask+3;
         bottom = himask;
         break;

      case mw_hiswitchoff:
         sidepic = false;
         middle = himask+1;
         above = himask+2;
         bottom = himask;
         break;

      case mw_entrygate:
         side = W_GetNumForName("SIDE21");

      //#if (SHAREWARE == 1)
         //side = W_GetNumForName("SIDE21");
         middle = W_GetNumForName("ABOVEM4A") ;
         above  = W_GetNumForName("ABOVEM4")  ;

      //#else
         //side   = W_GetNumForName("SIDE20") ;
      //   middle = W_GetNumForName("ABOVEM7A") ;
      //   above  = W_GetNumForName("ABOVEM7")  ;


      //#endif

         bottom = W_GetNumForName("ENTRARCH");
         break;

      case mw_platform1:
         sidepic = false;
         bottom = -1;
         middle = -1;
         above  = himask+10;
         if (metal==true)
            {
            bottom = -1;
            middle = -1;
            above  = himask+15;
            }
         break;
      case mw_platform2:
         sidepic = false;
         bottom = himask+8;
         middle = -1;
         above = -1;
         if (metal==true)
            {
            bottom = himask+14;
            middle = -1;
            above = -1;
            }
         else
            lastmaskobj->flags|=MW_BOTTOMFLIPPING;
         break;
      case mw_platform3:
         sidepic = false;
         bottom = himask+8;
         middle = -1;
         above = himask+10;
         if (metal==true)
            {
            bottom = himask+14;
            middle = -1;
            above = himask+15;
            }
         else
            lastmaskobj->flags|=MW_BOTTOMFLIPPING;
         break;
      case mw_platform4:
         sidepic = false;
         bottom = himask+12;
         middle = himask+7;
         above = himask+7;
         if (metal==true)
            {
            bottom = -1;
            middle = himask+15;
            above = himask+15;
            }
         break;
      case mw_platform5:
         sidepic = false;
         bottom = himask+12;
         middle = himask+7;
         above = himask+5;
         if (metal==true)
            {
            bottom = -1;
            middle = himask+15;
            above = -1;
            }
         else
            lastmaskobj->flags|=MW_TOPFLIPPING;
         break;
      case mw_platform6:
         sidepic = false;
         bottom = himask+4;
         middle = himask+7;
         above = himask+5;
         if (metal==true)
            {
            bottom = himask+14;
            middle = himask+15;
            above = -1;
            }
         else
            lastmaskobj->flags|=MW_TOPFLIPPING;
         break;
      case mw_platform7:
         sidepic = false;
         bottom = himask+4;
         middle = himask+7;
         above = himask+5;
         if ((up==1) || (dn==1))
            lastmaskobj->vertical=true;
         else if ((lt==1) || (rt==1))
            lastmaskobj->vertical=false;
         else
            Error("Perpendicular platform used with no wall near it\n");
         if (metal==true)
            {
            bottom = himask+14;
            middle = himask+15;
            above = -1;
            }
         else
            lastmaskobj->flags|=MW_TOPFLIPPING;
         break;
      }

   switch (which)
      {
      case mw_multi1:
      case mw_multi2:
      case mw_multi3:
      case mw_singlepane:
      case mw_normal1:
      case mw_normal2:
      case mw_normal3:
         if (!(flags & MW_SHOOTABLE))
            bottom+=9;
         break;
      }
   lastmaskobj->midtexture=middle;
   lastmaskobj->toptexture=above;
   lastmaskobj->bottomtexture=bottom;

   if (sidepic == true)
      {
      lastmaskobj->sidepic=side;
      if (lastmaskobj->vertical==true)
         {
         if (up==1)
            tilemap[tilex][tiley-1] |= 0x4000;
         if (dn==1)
            tilemap[tilex][tiley+1] |= 0x4000;
         }
      else
         {
         if (lt==1)
            tilemap[tilex-1][tiley] |= 0x4000;
         if (rt==1)
            tilemap[tilex+1][tiley] |= 0x4000;
         }
      }

	// Cache in the broken version

	if (lastmaskobj->flags & MW_SHOOTABLE)
      {
      int i;

      for (i=1;i<AMW_NUMFRAMES;i++)
         {
         PreCacheLump(lastmaskobj->bottomtexture+i,PU_CACHEWALLS,cache_transpatch_t);
         }
      SD_PreCacheSound(SD_GLASSBREAKSND);
		}
   if (sidepic==true)
      {
      PreCacheLump(lastmaskobj->sidepic,PU_CACHEWALLS,cache_pic_t);
      }
   if (lastmaskobj->bottomtexture>=0)
      PreCacheLump(lastmaskobj->bottomtexture,PU_CACHEWALLS,cache_transpatch_t);
   if (lastmaskobj->toptexture>=0)
      PreCacheLump(lastmaskobj->toptexture,PU_CACHEWALLS,cache_patch_t);
   if (lastmaskobj->midtexture>=0)
      PreCacheLump(lastmaskobj->midtexture,PU_CACHEWALLS,cache_patch_t);
	maskednum++;
	lastmaskobj++;
	if (maskednum==MAXMASKED)
		Error ("Too many masked walls\n");
}

/*
===============
=
= FixMaskedWallAreaNumbers
=
===============
*/
void FixMaskedWallAreaNumbers ( void )
{
	int i;
	int up,dn,lt,rt;
	int tilex,tiley;

	for (i=0;i<maskednum;i++)
		{
		int tile;
		tilex=maskobjlist[i]->tilex;
		tiley=maskobjlist[i]->tiley;
      tile=MAPSPOT(tilex,tiley,0)-AREATILE;
		if ((tile<=NUMAREAS) && (tile>0))
		   {
         maskobjlist[i]->areanumber = tile;
			continue;
		   }
		up=MAPSPOT(tilex,tiley-1,0)-AREATILE;
		dn=MAPSPOT(tilex,tiley+1,0)-AREATILE;
		lt=MAPSPOT(tilex-1,tiley,0)-AREATILE;
		rt=MAPSPOT(tilex+1,tiley,0)-AREATILE;



		up = ((up>0) && (up<=NUMAREAS));
		dn = ((dn>0) && (dn<=NUMAREAS));
		lt = ((lt>0) && (lt<=NUMAREAS));
		rt = ((rt>0) && (rt<=NUMAREAS));

		if (maskobjlist[i]->vertical==true)
			{
			if (rt)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex+1,tiley,0);
			else if (lt)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex-1,tiley,0);
			else if (up)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex,tiley-1,0);
			else if (dn)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex,tiley+1,0);
			else
				Error("FixMaskedWalls: Couldn't fix up area at x=%d y=%d\n",tilex,tiley);
			}
		else
			{
			if (dn)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex,tiley+1,0);
			else if (up)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex,tiley-1,0);
			else if (rt)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex+1,tiley,0);
			else if (lt)
				MAPSPOT(tilex,tiley,0) = MAPSPOT(tilex-1,tiley,0);
			else
				Error("FixMaskedWalls: Couldn't fix up area at x=%d y=%d\n",tilex,tiley);
			}
		maskobjlist[i]->areanumber = MAPSPOT(tilex,tiley,0)-AREATILE;
		if ((maskobjlist[i]->areanumber <0) || (maskobjlist[i]->areanumber > NUMAREAS))
		  Error("Bad masked wall areanumber of %d",maskobjlist[i]->areanumber);
		}
}


/*
===============
=
= CheckMaskedWall
=
===============
*/

int CheckMaskedWall( maskedwallobj_t * mw )
{
	int result;

	result=0;
	if (mw->flags & MW_SHOOTABLE)
		{
		if (mw->flags & MW_BLOCKINGCHANGES)
         {
         mw->flags&=~MW_BLOCKINGCHANGES;
         mw->flags&=~MW_BLOCKING;
         mw->flags|=MW_BOTTOMPASSABLE;
         }
      mw->flags&=~MW_SHOOTABLE;
//      mw->bottomtexture++;
      result=1;
      }
   return result;
}


/*
===============
=
= UpdateMaskedWall
=
===============
*/

int UpdateMaskedWall (int num)
{
   maskedwallobj_t * mw;
   int result;


   mw=maskobjlist[num];
   result=CheckMaskedWall(mw);
   if (result==1)
      {
      SpawnAnimatedMaskedWall(num);
      if (loadedgame==false)
	      SD_PlaySoundRTP(SD_GLASSBREAKSND,mw->tilex<<16,mw->tiley<<16);
      if (mw->flags&MW_MULTI)
         {
         int i;
         int dx,dy;
         int r;
         maskedwallobj_t * mw2;

         dx=0;dy=0;
         if (mw->vertical==true)
            dy=1;
         else
            dx=1;
         i=1;
			while (M_ISMWALL(mw->tilex+(dx*i),mw->tiley+(dy*i)))
            {
            int num;

            num=tilemap[mw->tilex+(dx*i)][mw->tiley+(dy*i)]&0x3ff;
            mw2=maskobjlist[num];
            if (!(mw2->flags&MW_MULTI))
               break;
            r=CheckMaskedWall(mw2);
            if (r==1)
               {
               SpawnAnimatedMaskedWall(num);
               if (loadedgame==false)
	               SD_PlaySoundRTP(SD_GLASSBREAKSND,mw2->tilex<<16,mw2->tiley<<16);
               }
            i++;
            }
         i=1;
			while (M_ISMWALL(mw->tilex-(dx*i),mw->tiley-(dy*i)))
            {
            int num;

            num=tilemap[mw->tilex-(dx*i)][mw->tiley-(dy*i)]&0x3ff;
            mw2=maskobjlist[num];
            if (!(mw2->flags&MW_MULTI))
               break;
            r=CheckMaskedWall(mw2);
            if (r==1)
               {
               SpawnAnimatedMaskedWall(num);
               if (loadedgame==false)
	               SD_PlaySoundRTP(SD_GLASSBREAKSND,mw2->tilex<<16,mw2->tiley<<16);
               }
            i++;
            }
         }
      }
   return result;
}



/*
============================
=
= ExecuteElevatorStopActions
=
============================
*/


void ExecuteElevatorStopActions(elevator_t *eptr, int teleport_location,
                                int desttilex,int desttiley)
   {
   eptr->state = ev_doorclosing;
   eptr->doorclosing = eptr->doortoopen;
   doorobjlist[eptr->doortoopen]->flags &= ~DF_ELEVLOCKED;
   OpenDoor(eptr->doortoopen);
   SD_PlaySoundRTP(SD_ELEVATORENDSND,desttilex,desttiley);
   Teleport(eptr,teleport_location);
   eptr->ticcount = OPENTICS;
   eptr->doortoopen = -1;
   if (MISCVARS->elevatormusicon == true)
      {
      MU_StartSong(song_level);
      MU_RestoreSongPosition();
      MISCVARS->elevatormusicon = false;

      }
   }


boolean PlayerInElevator(elevator_t *eptr)
   {
   if (eptr->state == ev_mts)
      {
      if ((eptr->dx == player->tilex) && (eptr->dy == player->tiley))
         return true;
      }
   else if (eptr->state == ev_mtd)
      {
      if ((eptr->sx == player->tilex) && (eptr->sy == player->tiley))
         return true;
      }

   return false;

   }

#define SHOULD_START_ELEVATOR_MUSIC(eptr)                              \
        ((demoplayback == false) && (demorecord == false) &&           \
         (MusicStarted() == true) &&                                   \
         (!BATTLEMODE) && \
         (!(player->flags & FL_GODMODE)) &&\
         (GameRandomNumber("elevator music",0) < 25) && \
         (PlayerInElevator(eptr))                                      \
        )                                                              \


/*
==========================
=
= SetElevatorOperationTime
=
==========================
*/


void SetElevatorOperationTime(elevator_t*eptr)
   {
   if (SHOULD_START_ELEVATOR_MUSIC(eptr))
      {
      MU_StoreSongPosition();
      MU_StartSong(song_elevator);
      MISCVARS->elevatormusicon = true;
      eptr->ticcount = ELEVATORMUSICTIME;
      }

   else if (AREANUMBER(eptr->sx,eptr->sy) == AREANUMBER(eptr->dx,eptr->dy))
      eptr->ticcount = 70;
   else
      eptr->ticcount = 170;

   }


/*
=====================
=
= CheckElevatorStart
=
=====================
*/

void CheckElevatorStart (elevator_t*eptr)
   {
   doorobj_t *dptr = doorobjlist[eptr->doorclosing];

   if (dptr->action == dr_closed)
      {

      if (eptr->nextaction!=-1)
         {
         eptr->state = eptr->nextaction;
         eptr->nextaction = -1;
         switch (eptr->state)
            {
            case ev_mtd:
               eptr->doortoopen = eptr->door2;
               SD_PlaySoundRTP(SD_ELEVATORONSND,eptr->sx<<16,eptr->sy<<16);
               //eptr->doorclosing = eptr->door1;

               SetElevatorOperationTime(eptr);
               break;

            case ev_mts:
               eptr->doortoopen = eptr->door1;

               SD_PlaySoundRTP(SD_ELEVATORONSND,eptr->dx<<16,eptr->dy<<16);

               SetElevatorOperationTime(eptr);
               break;
            }
         }

      else if (eptr->doorclosing == eptr->door1)
         eptr->state = ev_ras;


      else if (eptr->doorclosing == eptr->door2)
         eptr->state = ev_rad;


      eptr->doorclosing = -1;
      }
   }


/*
=====================
=
= ProcessElevators
=
= Called from PlayLoop
=
=====================
*/

void ProcessElevators (void)
   {
	int		  ectr;
	elevator_t *eptr;

	for (ectr = 0 ; ectr < _numelevators ; ectr++)
      {
      eptr = &ELEVATOR[ectr];
		if (eptr->ticcount)
			eptr->ticcount --;
		else
         {
         switch (eptr->state)
            {
            /*
            case ev_ras:
               break;

            case ev_rad:
               break;
            */
            case ev_mts:
               ExecuteElevatorStopActions(eptr,0,(eptr->sx << 16),(eptr->sy << 16));
               break;

            case ev_mtd:
               ExecuteElevatorStopActions(eptr,1,(eptr->dx << 16),(eptr->dy << 16));
               break;

            case ev_doorclosing:
               CheckElevatorStart(eptr);
               break;
            }
         }
      }
   }



void Teleport(elevator_t*eptr,int destination)
{statobj_t*tstat;
 objtype*temp;
 int startx,starty,destx,desty;

 if (destination) // move to dest
	{startx = eptr->sx;
	 starty = eptr->sy;
	 destx = eptr->dx;
	 desty = eptr->dy;
    tilemap[eptr->esx][eptr->esy] = (elevatorstart + 5) | 0x2000;

	}
 else
	{startx = eptr->dx;
	 starty = eptr->dy;
	 destx = eptr->sx;
	 desty = eptr->sy;
	 tilemap[eptr->edx][eptr->edy] = (elevatorstart + 5) | 0x2000;
	}

 for(tstat=firstactivestat;tstat;tstat=tstat->nextactive)
	{if ((tstat->tilex == startx) && (tstat->tiley == starty))
		{
		 tstat->x += ((destx - tstat->tilex) << TILESHIFT);
		 tstat->y += ((desty - tstat->tiley) << TILESHIFT);
		 tstat->tilex = tstat->x >> TILESHIFT;
		 tstat->tiley = tstat->y >> TILESHIFT;
		 tstat->visspot = &spotvis[tstat->tilex][tstat->tiley];
		 if (sprites[startx][starty] == tstat)
			{sprites[startx][starty] = NULL;
			 sprites[destx][desty] = tstat;

			}

		}
	}

 for(temp=firstactive;temp;temp=temp->nextactive)
	{if ((temp->tilex == startx) && (temp->tiley == starty))
		{temp->x += ((destx - temp->tilex) << TILESHIFT);
		 temp->y += ((desty - temp->tiley) << TILESHIFT);
		 temp->tilex = temp->x >> TILESHIFT;
		 temp->tiley = temp->y >> TILESHIFT;
		 if (temp->obclass!=inertobj)
			 {
			 RemoveFromArea (temp);
			 temp->areanumber = AREANUMBER(temp->tilex,temp->tiley);
			 MakeLastInArea (temp);
			 }
		 if (temp == player)
			SHAKETICS = 10;
		}
	}



}



void OperateElevatorDoor(int dnum)
{
 elevator_t*eptr;
 doorobj_t *dptr;

 dptr = doorobjlist[dnum];
 eptr = &ELEVATOR[dptr->eindex];

 switch(eptr->state)
	{/*
	 case ev_mtd:               // if already on the way to request,
										 // ignore; else, put request in
		if (dnum == eptr->door1)
		  {eptr->nextaction = ev_mts;
			//eptr->doortoopen = eptr->door1;
		  }
		break;

	 case ev_mts:
		if (dnum == eptr->door2)
		  {eptr->nextaction = ev_mtd;
			//eptr->doortoopen = eptr->door2;
 		  }
		break;
	 */
	 case ev_rad:                          // if ready at other place,
		if ((dnum == eptr->door1) && (eptr->nextaction != ev_mts))  // process request, lock doors,

			{
 												// start moving to current loc;
			 SetNextAction(eptr,0);		// if already there, do nothing

			}
		break;

	 case ev_ras:
		if ((dnum == eptr->door2) && (eptr->nextaction != ev_mtd))
			{
 			 SetNextAction(eptr,1);

			}
		break;

	 case ev_doorclosing:
		if (eptr->doorclosing == dnum)      // if opening door at current loc,
														// reset elev state to ready
		  {//if (eptr->door1 == dnum)
			// eptr->nextaction = ev_ras;
			//else
			 //eptr->nextaction = ev_rad;
		  }
		else                                //else prepare for movement
		  {if ((eptr->door1 == dnum) && (eptr->nextaction != ev_mts))
			  {
 				SetNextAction(eptr,0);

			  }
			else if ((eptr->door2 == dnum) && (eptr->nextaction != ev_mtd))
			  {
 				SetNextAction(eptr,1);

			  }
		  }
	  break;


	 }

}


int SetNextAction(elevator_t*eptr,int action)
{int dn;

 if (action)
	{if (!DoorReadyToClose(eptr->door1))
	  return false;

	 eptr->nextaction = ev_mtd;
	 dn = eptr->door1;
	}
 else
	{if (!DoorReadyToClose(eptr->door2))
	  return false;

	 eptr->nextaction = ev_mts;
	 dn = eptr->door2;
	}
 eptr->state = ev_doorclosing;

 eptr->doorclosing = dn;
 if (doorobjlist[dn]->action != dr_closed)
  CloseDoor(dn);
 doorobjlist[dn]->flags |= DF_ELEVLOCKED;

 return true;
}


void OperateElevatorSwitch(objtype*ob,int elevnum,int checkx,int checky)
{elevator_t*eptr;
 doorobj_t *door1;

 eptr = &ELEVATOR[elevnum];

 if ((eptr->state == ev_mts) ||
	  (eptr->state == ev_mtd))
	{
	 return;
	}

 door1 = doorobjlist[eptr->door1];

 if ((abs(ob->tilex-door1->tilex)<=1) && //switch at source
	  (abs(ob->tiley-door1->tiley)<=1))
	{if (!SetNextAction(eptr,1)) // set next to dest
		return;
	 eptr->ticcount = 0;
	}
 else //switch at dest
	{if (!SetNextAction(eptr,0)) // set next to src
		return;
 	 eptr->ticcount = 0;
	}

 tilemap[checkx][checky] = (elevatorstart + 6) | 0x2000;
 SD_PlaySoundRTP(SD_TOUCHPLATESND,ob->x,ob->y);

}




/*
=====================
=
= MoveDoors
=
= Called from PlayLoop
=
=====================
*/

void MoveDoors (void)
{
	int		door;

	for (door = 0 ; door < doornum ; door++)
		switch (doorobjlist[door]->action)
		{
		case dr_open:
			DoorOpen (door);
			break;

		case dr_opening:
			DoorOpening(door);
         SD_PanRTP ( doorobjlist[door]->soundhandle, doorobjlist[door]->tilex<<16, doorobjlist[door]->tiley<<16 );
			break;

		case dr_closing:
			DoorClosing(door);
         SD_PanRTP ( doorobjlist[door]->soundhandle, doorobjlist[door]->tilex<<16, doorobjlist[door]->tiley<<16 );
			break;
		default:
		    ;
		}
}


//===========================================================
//
//                   PUSHWALLS
//
//===========================================================


/*
===============
=
= GetAreaNumber
=
===============
*/
int GetAreaNumber ( int tilex, int tiley, int dir )
{
   int up,dn,lt,rt;

   up=MAPSPOT(tilex,tiley-1,0)-AREATILE;
   dn=MAPSPOT(tilex,tiley+1,0)-AREATILE;
   lt=MAPSPOT(tilex-1,tiley,0)-AREATILE;
   rt=MAPSPOT(tilex+1,tiley,0)-AREATILE;
   if ((up<=0) || (up>NUMAREAS)) up=0;
   if ((dn<=0) || (dn>NUMAREAS)) dn=0;
   if ((lt<=0) || (lt>NUMAREAS)) lt=0;
   if ((rt<=0) || (rt>NUMAREAS)) rt=0;
	switch (dir)
		{
		case north:
			if (up)
				return up;
			else if (dn)
				return dn;
			break;
		case south:
			if (dn)
				return dn;
			else if (up)
				return up;
			break;
		case east:
			if (rt)
				return rt;
			else if (lt)
				return lt;
			break;
		case west:
			if (lt)
				return lt;
			else if (rt)
				return rt;
			break;
		}
	if (up)
		return up;
	else if (dn)
		return dn;
	else if (lt)
		return lt;
	else if (rt)
		return rt;
	else
		Error("Cannot find an area number for tile at x=%d y=%d\n",tilex,tiley);
	return -1;
}

/*
===============
=
= SpawnPushWall
=
===============
*/

void SpawnPushWall (int tilex, int tiley, int lock, int texture, int dir, int type)
{
   pwallobj_t * lastpwallobj;

	if (pwallnum==MAXPWALLS)
		Error ("MAXPWALLS on level!");

   pwallobjlist[pwallnum]=(pwallobj_t*)Z_LevelMalloc(sizeof(pwallobj_t),PU_LEVELSTRUCT,NULL);
   memset(pwallobjlist[pwallnum],0,sizeof(pwallobj_t));
   lastpwallobj=pwallobjlist[pwallnum];

	lastpwallobj->x = (tilex<<16)+0x8000;
	lastpwallobj->y = (tiley<<16)+0x8000;
   lastpwallobj->momentumx=0;
   lastpwallobj->momentumy=0;
   lastpwallobj->tilex = tilex;
   lastpwallobj->tiley = tiley;
	lastpwallobj->lock = lock;
	lastpwallobj->action = pw_npushed;
	lastpwallobj->which = PWALL;
	lastpwallobj->dir = dir;
   lastpwallobj->num = pwallnum;
	actorat[tilex][tiley] = (pwallobj_t*)(lastpwallobj);	// consider it a solid wall

   if ( (MAPSPOT(tilex,tiley,0)==44) ||
        (MAPSPOT(tilex,tiley,0)==233)
      )
      lastpwallobj->flags=PW_DAMAGE;

	lastpwallobj->texture = texture;
   if (!(texture&0x1000))
	   PreCacheLump(texture,PU_CACHEWALLS,cache_pic_t);
   lastpwallobj->areanumber = GetAreaNumber(tilex,tiley,lastpwallobj->dir);

   MAPSPOT (tilex, tiley, 0)=(word)(lastpwallobj->areanumber+AREATILE);

   switch(type)
      {
      case 0:
      case 1:
		case 3:
	      lastpwallobj->speed = 2;
         break;
      case 2:
      case 4:
	      lastpwallobj->speed = 4;
         break;
      default:
         Error("Illegal PushWall type passed into SpawnPushWall\n");
         break;
      }
   if (type>2)
      {
      tilemap[tilex][tiley] = 0;
	   ActivateMoveWall(pwallnum);
      }
   else
      {
      tilemap[tilex][tiley] = texture|0x800;
      if ((loadedgame==false) && (type==0))
         gamestate.secrettotal++;
      }

	pwallnum++;
	lastpwallobj++;

	SD_PreCacheSoundGroup(SD_PUSHWALLSND,SD_TURBOWALLSND);
}



/*
=====================
=
= OperatePushWall
=
= The player wants to change the pushwall's direction
=
=====================
*/
void OperatePushWall (int pwall, int dir, boolean localplayer )
{
   pwallobj_t * pw;

   pw=pwallobjlist[pwall];

   if (pw->lock)
		{
      if ( localplayer )
         {
         // Can't push
         AddMessage("This push wall appears to be locked...",MSG_DOOR);
         PlayNoWaySound();
         }
		return;
		}
	switch (pw->action)
	   {
	   case pw_npushed:
         if ((dir!=pw->dir) && (pw->dir!=nodir))
            {
            // Can't push
            if ( localplayer )
               {
               PlayNoWaySound();
               }
            return;
            }
         else if (localplayer && (gamestate.difficulty == gd_baby))
            AddMessage("Push Wall Activated.",MSG_GAME);

         pw->action=pw_pushing;
         pw->dir=dir;
         SD_PlaySoundRTP ( SD_TOUCHPLATESND, pw->x, pw->y );
         ConnectPushWall(pwall);
         SetupPushWall(pwall);
   	   gamestate.secretcount++;
		   break;
	   default:
         // Can't push
         if ( localplayer )
            {
            PlayNoWaySound();
            }
		   break;
	   }
}

/*
=====================
=
= ActivateAllPushWalls
=
= A Push wall has beeen activated by a touch plate
=
=====================
*/

void ActivateAllPushWalls(void)
   {
   int i;

   for(i=0;i<pwallnum;i++)
      {
      if (pwallobjlist[i]->dir != nodir)
         {
         ActivatePushWall(i);
         }
      }
   }

/*
=====================
=
= ActivatePushWall
=
= A Push wall has beeen activated by a touch plate
=
=====================
*/

void ActivatePushWall (intptr_t pwall)
{
   pwallobj_t * pw;

   pw=pwallobjlist[pwall];

	switch (pw->action)
	{
	case pw_npushed:
      pw->action=pw_pushing;
      ConnectPushWall(pwall);
      SetupPushWall(pwall);
   	gamestate.secretcount++;
		break;
	default:
      // Can't push
      SD_Play( SD_BADTOUCHSND );
		break;
	}
}

/*
=====================
=
= ActivateMoveWall
=
= A Push wall has beeen activated by a touch plate
=
=====================
*/

void ActivateMoveWall (intptr_t pwall)
{
   pwallobj_t * pw;

   pw=pwallobjlist[pwall];

	switch (pw->action)
	{
	case pw_npushed:
      pw->action=pw_moving;
      SetupPushWall(pwall);
      break;
	default:
      SD_Play( SD_BADTOUCHSND );
		break;
	}
}



/*
===============
=
= ConnectPushWall
=
===============
*/

void ConnectPushWall (int pwall)
{
	int      checkx;
	int      checky;
	int		area1,area2;
	int		area3,area4;
	word  	*map;
	pwallobj_t * pw;

	pw=pwallobjlist[pwall];

	checkx=pw->tilex;
	checky=pw->tiley;
	tilemap[checkx][checky] = 0;
	map = &MAPSPOT (checkx, checky, 0);

	area1 =	*(map-mapwidth);
	area2 =	*(map+mapwidth);
	area3 =  *(map+1);
	area4 =  *(map-1);

	area1 -= AREATILE;
	area2 -= AREATILE;
	area3 -= AREATILE;
	area4 -= AREATILE;
	if (((area1>0) && (area1<NUMAREAS)) &&
		 ((area2>0) && (area2<NUMAREAS)))
		{
		areaconnect[area1][area2]++;
		areaconnect[area2][area1]++;

		if ((insetupgame==false) && (loadedgame==false))
			ConnectAreas ();
		}
	if (((area3>0) && (area3<NUMAREAS)) &&
		 ((area4>0) && (area4<NUMAREAS)))
		{
		areaconnect[area3][area4]++;
		areaconnect[area4][area3]++;

		if ((insetupgame==false) && (loadedgame==false))
			ConnectAreas ();
		}
}

/*
===============
=
= SetupPushWall
=
===============
*/

void SetupPushWall (int pwall)
{
	pwallobj_t * pw;
	int speed;

	pw=pwallobjlist[pwall];
	speed=pw->speed<<PUSHWALLSPEED;
	switch (pw->dir)
	{
	case north:
		pw->momentumx=0;
		pw->momentumy=-speed;
		break;

	case east:
		pw->momentumx=speed;
		pw->momentumy=0;
		break;

	case northeast:
		pw->momentumx=speed;
		pw->momentumy=-speed;
		break;

	case southeast:
		pw->momentumx=speed;
		pw->momentumy=speed;
		break;

	case south:
		pw->momentumx=0;
		pw->momentumy=speed;
		break;

	case west:
		pw->momentumx=-speed;
		pw->momentumy=0;
		break;

	case northwest:
		pw->momentumx=-speed;
		pw->momentumy=-speed;
		break;

	case southwest:
		pw->momentumx=-speed;
		pw->momentumy=speed;
		break;
	}
	if (pw->action==pw_pushing)
		{
      if (loadedgame==false)
         pw->soundhandle=SD_PlaySoundRTP ( SD_PUSHWALLSND, pw->x, pw->y );
      pw->state=(0x20000L/speed);
		}
	if (pw->action==pw_moving)
      pw->state=(0x10000L/speed);
}


/*
=====================
=
= MovePWalls
=
= Called from PlayLoop
=
=====================
*/

void MovePWalls (void)
{
	int		pwall;

	for (pwall = 0 ; pwall < pwallnum ; pwall++)
		{
		if (pwallobjlist[pwall]->action==pw_pushing)
			{
			WallPushing (pwall);
			SD_PanRTP (pwallobjlist[pwall]->soundhandle, pwallobjlist[pwall]->x, pwallobjlist[pwall]->y );
			}
		if (pwallobjlist[pwall]->action==pw_moving)
			{
			WallMoving (pwall);
			SD_PanRTP (pwallobjlist[pwall]->soundhandle, pwallobjlist[pwall]->x, pwallobjlist[pwall]->y );
			}
		}
}


void ClearActorat(pwallobj_t*pw)
{int txhigh,txlow,tyhigh,tylow;
 int tryx,tryy,x,y;
 int pwrad = 0x6fff;


 tryx = pw->x;
 tryy = pw->y;
 txlow = (tryx - pwrad) >> 16;
 txhigh = (tryx + pwrad) >> 16;
 tylow = (tryy - pwrad) >> 16;
 tyhigh = (tryy + pwrad) >> 16;
 for(y=tylow;y<=tyhigh;y++)
	 for(x=txlow;x<=txhigh;x++)
		{if (actorat[x][y] == pw)
			actorat[x][y] = NULL;
		}
}

void SetActorat(pwallobj_t*pw)
{int txhigh,txlow,tyhigh,tylow;
 int tryx,tryy,x,y;
 int pwrad = 0x6fff;

 tryx = pw->x;
 tryy = pw->y;
 txlow = (tryx - pwrad) >> 16;
 txhigh = (tryx + pwrad) >> 16;
 tylow = (tryy - pwrad) >> 16;
 tyhigh = (tryy + pwrad) >> 16;

 for(y=tylow;y<=tyhigh;y++)
	 for(x=txlow;x<=txhigh;x++)
		actorat[x][y] = pw;
}

/*
=================
=
= FinishPushWall
=
=================
*/
void FinishPushWall (pwallobj_t * pw)
{
   pw->action = pw_pushed;
   actorat[pw->tilex][pw->tiley] = (wall_t*)&walls[GetWallIndex(pw->texture)];
   tilemap[pw->tilex][pw->tiley] = pw->texture;
}

/*
=================
=
= ResetPushWall
=
=================
*/
void ResetPushWall (pwallobj_t * pw)
{
   SetActorat(pw);
   tilemap[pw->tilex][pw->tiley] = pw->texture|0x800;
}

/*
=================
=
= WallPushing
=
=================
*/
void WallPushing (int pwall)
{
	int      checkx,checky;
	int      spot;
	pwallobj_t * pw;

	pw=pwallobjlist[pwall];

   ClearActorat(pw);

	PushWallMove(pwall);
	pw->x+=pw->momentumx;
	pw->y+=pw->momentumy;

	pw->state--;

	checkx=pw->tilex;
	checky=pw->tiley;

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

	if ((pw->tilex!=checkx) || (pw->tiley!=checky))
		{
		int x,y;
		int area = MAPSPOT(pw->tilex,pw->tiley,0)-AREATILE;

		if ((area<=0) || (area>NUMAREAS))
			{
			area=pw->areanumber;
			MAPSPOT (pw->tilex, pw->tiley, 0)=(word)(pw->areanumber+AREATILE);
			}
		// block crossed into a new block
		//
		// the tile can now be walked into
		//
		mapseen[checkx][checky] = 0;
		pw->areanumber = area;
		if (pw->momentumx>0)
			x=1;
		else if (pw->momentumx<0)
			x=-1;
		else
			x=0;
		if (pw->momentumy>0)
			y=1;
		else if (pw->momentumy<0)
			y=-1;
		else
			y=0;
		if (tilemap[pw->tilex+x][pw->tiley+y])
			{
			pw->state=(0x8000L/(pw->speed<<PUSHWALLSPEED));
			}
		if (actorat[pw->tilex+x][pw->tiley+y])
		  ResolveDoorSpace(pw->tilex+x,pw->tiley+y);
		}


	if (pw->state==0)
		{
		pw->x=(pw->tilex<<16)+0x8000;
		pw->y=(pw->tiley<<16)+0x8000;
		spot = MAPSPOT(pw->tilex,pw->tiley,1)-ICONARROWS;
		if ((spot >= 0) && (spot <= 7))
			{
         pw->action = pw_npushed;
			pw->dir = spot;
         ResetPushWall (pw);
         if (pw->lock)
				{
            pw->action=pw_pushing;
				ConnectPushWall(pwall);
				SetupPushWall(pwall);
				}
         else
            {
            gamestate.secrettotal++;
            }
         }
		else
			{
         FinishPushWall (pw);
			}
		}
   else
      SetActorat(pw);
}


/*
=================
=
= WallMoving
=
=================
*/
void WallMoving (int pwall)
{
	int      checkx,checky;
	int      spot;
	pwallobj_t * pw;

	pw=pwallobjlist[pwall];

   ClearActorat(pw);

	PushWallMove(pwall);
	pw->x+=pw->momentumx;
	pw->y+=pw->momentumy;

	pw->state--;

	checkx=pw->tilex;
	checky=pw->tiley;

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

	if ((pw->tilex!=checkx) || (pw->tiley!=checky))
		{
		int area = MAPSPOT(pw->tilex,pw->tiley,0)-AREATILE;

		if ((area<=0) || (area>NUMAREAS))
			{
			area=pw->areanumber;
			MAPSPOT (pw->tilex, pw->tiley, 0)=(word)(pw->areanumber+AREATILE);
			}
		// block crossed into a new block
		//
		// the tile can now be walked into
		//
		if (areabyplayer[area])
			{
			if (pw->speed==2)
				pw->soundhandle=SD_PlaySoundRTP ( SD_GOWALLSND, pw->x, pw->y );
			else
				pw->soundhandle=SD_PlaySoundRTP ( SD_TURBOWALLSND, pw->x, pw->y );
			}

		if (actorat[pw->tilex][pw->tilex])
		  ResolveDoorSpace(pw->tilex,pw->tiley);
		mapseen[checkx][checky] = 0;
		pw->areanumber = MAPSPOT (pw->tilex, pw->tiley, 0)-AREATILE;
		//actorat[pw->tilex][pw->tiley]=pw;
		if ( (pw->tilex==0) || (pw->tilex==127) ||
			  (pw->tiley==0) || (pw->tiley==127) )
			{
         if (W_CheckNumForName("imfree")>=0)
            {
            lbm_t *LBM;

            LBM = (lbm_t *) W_CacheLumpNum (W_GetNumForName ("imfree"), PU_CACHE, Cvt_lbm_t, 1);
            VL_DecompressLBM (LBM,true);
            VW_UpdateScreen ();
            I_Delay (2000);
            }
			Error ("PushWall Attempting to escape off the edge of the map\nIt is located at x=%d y=%d\nI'm Free!!!!\n",
					  pw->tilex, pw->tiley);
			}
		}
	if (pw->state==0)
		{
		pw->x=(pw->tilex<<16)+0x8000;
		pw->y=(pw->tiley<<16)+0x8000;
		spot = MAPSPOT(pw->tilex,pw->tiley,1)-ICONARROWS;
		if ((spot >= 0) && (spot <= 7))
			{
			int area = MAPSPOT(pw->tilex,pw->tiley,0)-AREATILE;

			if ((area<=0) || (area>NUMAREAS))
				{
				area=pw->areanumber;
				MAPSPOT (pw->tilex, pw->tiley, 0)=(word)(pw->areanumber+AREATILE);
				}

			if (areabyplayer[area] && (abs(spot-pw->dir)==4))
				SD_PlaySoundRTP ( SD_PUSHWALLHITSND, pw->x, pw->y );
			pw->dir = spot;
			}
		SetupPushWall(pwall);
		}
   else
      SetActorat(pw);
}



/*
=================
=
= SavePushWalls
=
=================
*/
void SavePushWalls(byte ** buf, int * sz)
{
  int unitsize;
  pwallobj_t * pw;
  byte * bufptr;
  int i;
  int size;

  if (pwallnum==0)
	  {
	  *sz=0;
	  *buf=SafeMalloc(16);
	  return;
	  }
  pw=pwallobjlist[0];
  unitsize=0;
  unitsize+=sizeof(pw->state);
  unitsize+=sizeof(pw->x);
  unitsize+=sizeof(pw->y);
  unitsize+=sizeof(pw->dir);
  unitsize+=sizeof(pw->speed);
  unitsize+=sizeof(pw->action);

  *sz=pwallnum*unitsize;

  *buf=SafeMalloc(*sz);
  bufptr=*buf;

  for (i=0;i<pwallnum;i++)
     {
     pw=pwallobjlist[i];
     size=sizeof(pw->state);
     memcpy(bufptr,&(pw->state),size);
     bufptr+=size;

     size=sizeof(pw->x);
     memcpy(bufptr,&(pw->x),size);
     bufptr+=size;

     size=sizeof(pw->y);
     memcpy(bufptr,&(pw->y),size);
     bufptr+=size;

     size=sizeof(pw->dir);
     memcpy(bufptr,&(pw->dir),size);
     bufptr+=size;

     size=sizeof(pw->speed);
     memcpy(bufptr,&(pw->speed),size);
     bufptr+=size;

     size=sizeof(pw->action);
     memcpy(bufptr,&(pw->action),size);
     bufptr+=size;
     }
}

/*
=================
=
= LoadPushWalls
=
=================
*/
void LoadPushWalls(byte * bufptr, int sz)
{
  int unitsize;
  pwallobj_t * pw;
  pwallobj_t new;
  int i;
  int num;
  int size;
  int area;

  if (sz==0)
     return;
  SetupPushWalls();
  pw=pwallobjlist[0];
  unitsize=0;
  unitsize+=sizeof(pw->state);
  unitsize+=sizeof(pw->x);
  unitsize+=sizeof(pw->y);
  unitsize+=sizeof(pw->dir);
  unitsize+=sizeof(pw->speed);
  unitsize+=sizeof(pw->action);

  num=sz/unitsize;
  if (pwallnum!=num)
     Error("Different number of Push Walls when trying to load a game\npwallnum=%d num=%d",pwallnum,num);

  for (i=0;i<pwallnum;i++)
     {
     pw=pwallobjlist[i];

     size=sizeof(new.state);
     memcpy(&(new.state),bufptr,size);
     bufptr+=size;

     size=sizeof(new.x);
     memcpy(&(new.x),bufptr,size);
     bufptr+=size;

     size=sizeof(new.y);
     memcpy(&(new.y),bufptr,size);
     bufptr+=size;

     size=sizeof(new.dir);
     memcpy(&(new.dir),bufptr,size);
     bufptr+=size;

     size=sizeof(new.speed);
     memcpy(&(new.speed),bufptr,size);
     bufptr+=size;

     size=sizeof(new.action);
     memcpy(&(new.action),bufptr,size);
     bufptr+=size;

	  actorat[pw->tilex][pw->tiley] = 0;
     mapseen[pw->tilex][pw->tiley] = 0;

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

     if ((new.tilex!=pw->tilex) || (new.tiley!=pw->tiley))
        {
        ClearActorat(pw);
        tilemap[pw->tilex][pw->tiley] = 0;
        if (pw->state!=pw_moving)
           {
           ConnectPushWall(i);
           }
        }

//   fixup area if needed

     area = MAPSPOT(new.tilex,new.tiley,0)-AREATILE;
     if ((area<=0) || (area>NUMAREAS))
        {
        MAPSPOT (new.tilex, new.tiley, 0)=(word)(pw->areanumber+AREATILE);
        }

     pw->tilex=new.tilex;
     pw->tiley=new.tiley;
     pw->x=new.x;
     pw->y=new.y;
     pw->action=new.action;
     pw->dir=new.dir;
     pw->speed=new.speed;
     SetupPushWall(i);
     pw->state=new.state;

     pw->areanumber = MAPSPOT (pw->tilex, pw->tiley, 0)-AREATILE;

     if (pw->action==pw_pushed)
        {
        FinishPushWall (pw);
        }
     else if (pw->action==pw_npushed)
        {
        ResetPushWall (pw);
        }
     else
        {
        SetActorat(pw);
        }
     }
}





/*
=================
=
= SaveMaskedWalls
=
=================
*/
void SaveMaskedWalls(byte ** buf, int * size)
{
  int unitsize;
  maskedwallobj_t * mw;
  byte * bufptr;
  int i;
  int sz;

  if (maskednum==0)
     {
     *size=0;
     *buf=SafeMalloc(16);
     return;
     }
  mw=maskobjlist[0];
  unitsize=0;
  unitsize+=sizeof(mw->flags);

  *size=maskednum*unitsize;

  *buf=SafeMalloc(*size);
  bufptr=*buf;

  for (i=0;i<maskednum;i++)
     {
     mw=maskobjlist[i];
     sz=sizeof(mw->flags);
     memcpy(bufptr,&(mw->flags),sz);
     bufptr+=sz;
     }
}

/*
=================
=
= LoadMaskedWalls
=
=================
*/
void LoadMaskedWalls(byte * bufptr, int sz)
{
  int unitsize;
  maskedwallobj_t * mw;
  int i;
  int size;
  int num;

  if (sz==0)
     return;

  SetupMaskedWalls();
  FixMaskedWallAreaNumbers();

  mw=maskobjlist[0];
  unitsize=0;
  unitsize+=sizeof(mw->flags);

  num=sz/unitsize;
  if (maskednum!=num)
     Error("Different number of Masked Walls when trying to load a game\nmaskednum=%d num=%d",maskednum,num);

  for (i=0;i<maskednum;i++)
     {
     word flags;	// Endianness fix thanks to DrLex - DDOI

     mw=maskobjlist[i];
     size=sizeof(mw->flags);
     memcpy(&flags,bufptr,size);
     bufptr+=size;
     if ((flags&0xff)!=mw->flags)	// Preserves original behavior
        UpdateMaskedWall(i);
     if (mw->flags&MW_SWITCHON)
        mw->toptexture--;
     }
}


/*
=================
=
= SaveDoors
=
=================
*/

void SaveDoors (byte ** buf, int * size)
{
   int door;
   int doorsave;
   byte doorflag;
   byte doorlocked;
   signed char dooreindex;
   short int doortime;
   int unitsize;
   byte *ptr;

   if (doornum==0)
      {
      *size=0;
      *buf=SafeMalloc(16);
      return;
      }

   //
   // Size = (int + byte + byte) * numdoors
   //

   unitsize=0;
   unitsize+=sizeof(doorsave);
   unitsize+=sizeof(doorflag);
   unitsize+=sizeof(doorlocked);
   unitsize+=sizeof(doortime);
   unitsize+=sizeof(dooreindex);

   *size = unitsize*doornum;
   *buf = (byte *) SafeMalloc (*size);

   ptr = *buf;

	for (door = 0; door < doornum ; door++)
   {
      doorsave   = doorobjlist[door]->position & ~3;
      doorsave  |= doorobjlist[door]->action;
      doorflag   = doorobjlist[door]->flags;
      doorlocked = doorobjlist[door]->lock;
      doortime   = doorobjlist[door]->ticcount;
      dooreindex = doorobjlist[door]->eindex;

      memcpy (ptr, &doorsave, sizeof (doorsave));
      ptr += sizeof (doorsave);
      memcpy (ptr, &doorflag, sizeof (doorflag));
      ptr += sizeof (doorflag);
      memcpy (ptr, &doorlocked, sizeof (doorlocked));
      ptr += sizeof (doorlocked);
      memcpy (ptr, &doortime, sizeof (doortime));
      ptr += sizeof (doortime);
      memcpy (ptr, &dooreindex, sizeof (dooreindex));
      ptr += sizeof (dooreindex);
   }
}


/*
=================
=
= LoadDoors
=
=================
*/

void LoadDoors (byte * buf, int size)
{
   int door;
   int doorsave;
   byte doorflag;
   byte doorlocked;
   signed char dooreindex;
   short int doortime;
   byte *ptr;
   int unitsize;
   int num;

   SetupDoors ();
   FixDoorAreaNumbers();
   ptr  = buf;

   unitsize=0;
   unitsize+=sizeof(doorsave);
   unitsize+=sizeof(doorflag);
   unitsize+=sizeof(doorlocked);
   unitsize+=sizeof(doortime);
   unitsize+=sizeof(dooreindex);

   num=size/unitsize;
   if (doornum!=num)
      Error("Different number of Doors when trying to load a game\ndoornum=%d num=%d",doornum,num);

   for (door = 0; door < doornum; door++)
   {
      memcpy (&doorsave, ptr, sizeof (doorsave));
      ptr += sizeof (doorsave);
      memcpy (&doorflag, ptr, sizeof (doorflag));
      ptr += sizeof (doorflag);
      memcpy (&doorlocked, ptr, sizeof (doorlocked));
      ptr += sizeof (doorlocked);
      memcpy (&doortime, ptr, sizeof (doortime));
      ptr += sizeof (doortime);
      memcpy (&dooreindex, ptr, sizeof (dooreindex));
      ptr += sizeof (dooreindex);

      doorobjlist[door]->action    = doorsave & 3;

      // Update Areas

      if (doorobjlist[door]->action != dr_closed)
         DoorOpening(door);

      doorobjlist[door]->action    = doorsave & 3;
      doorobjlist[door]->position = doorsave;
      doorobjlist[door]->flags     = doorflag;
      doorobjlist[door]->lock      = doorlocked;
      doorobjlist[door]->ticcount  = doortime;
      doorobjlist[door]->eindex    = dooreindex;

      if (doorobjlist[door]->action == dr_open)
         doorobjlist[door]->position = 0xFFFF;

      else if (doorobjlist[door]->action == dr_closed)
         doorobjlist[door]->position = 0;

      if (
           (doorobjlist[door]->action == dr_closing) ||
           (doorobjlist[door]->action == dr_closed)
         )
         {
         actorat[doorobjlist[door]->tilex][doorobjlist[door]->tiley] = doorobjlist[door];
         }
      doorobjlist[door]->texture = doorobjlist[door]->basetexture +
                                  ((doorobjlist[door]->position+1)>>13);
   }
}


/*
=====================
=
= SaveElevators
=
=
=====================
*/

void SaveElevators(byte ** buffer,int *size)
{int i;
 byte * tptr;

 *size = _numelevators*sizeof(elevator_t);

 *buffer = (byte *)SafeMalloc(*size);
 tptr = *buffer;

 for(i=0;i<_numelevators;i++)
  {memcpy(tptr,&ELEVATOR[i],sizeof(elevator_t));
	tptr += sizeof(elevator_t);
  }
}


/*
=====================
=
= LoadElevators
=
=
=====================
*/

void LoadElevators(byte * buffer,int size)
{int i;

 _numelevators = size/sizeof(elevator_t);


 for(i=0;i<_numelevators;i++)
  {memcpy(&ELEVATOR[i],buffer,sizeof(elevator_t));
	buffer += sizeof(elevator_t);
  }
}

