Source to src/p_floor.c


Enter a symbol's name here to quickly find it.


//**************************************************************************
//**
//** p_floor.c : Heretic 2 : Raven Software, Corp.
//**
//** $RCSfile: p_floor.c,v $
//** $Revision: 1.23 $
//** $Date: 95/10/06 16:53:19 $
//** $Author: paul $
//**
//**************************************************************************

#include "h2def.h"
#include "p_local.h"
#include "soundst.h"

extern fixed_t FloatBobOffsets[64];

//==================================================================
//==================================================================
//
//                                                              FLOORS
//
//==================================================================
//==================================================================

//==================================================================
//
//      Move a plane (floor or ceiling) and check for crushing
//
//==================================================================
result_e	T_MovePlane(sector_t *sector,fixed_t speed,
			fixed_t dest, int crush,int floorOrCeiling,int direction)
{
	boolean flag;
	fixed_t lastpos;

	switch(floorOrCeiling)
	{
		case 0:         // FLOOR
			switch(direction)
			{
				case -1:        // DOWN
					if (sector->floorheight - speed < dest)
					{
						lastpos = sector->floorheight;
						sector->floorheight = dest;
						flag = P_ChangeSector(sector,crush);
						if (flag == true)
						{
							sector->floorheight =lastpos;
							P_ChangeSector(sector,crush);
							//return RES_CRUSHED;
						}
						return RES_PASTDEST;
					}
					else
					{
						lastpos = sector->floorheight;
						sector->floorheight -= speed;
						flag = P_ChangeSector(sector,crush);
						if (flag == true)
						{
							sector->floorheight = lastpos;
							P_ChangeSector(sector,crush);
							return RES_CRUSHED;
						}
					}
					break;

				case 1:         // UP
					if (sector->floorheight + speed > dest)
					{
						lastpos = sector->floorheight;
						sector->floorheight = dest;
						flag = P_ChangeSector(sector,crush);
						if (flag == true)
						{
							sector->floorheight = lastpos;
							P_ChangeSector(sector,crush);
							//return RES_CRUSHED;
						}
						return RES_PASTDEST;
					}
					else    // COULD GET CRUSHED
					{
						lastpos = sector->floorheight;
						sector->floorheight += speed;
						flag = P_ChangeSector(sector,crush);
						if (flag == true)
						{
							//if (crush == true)
							//{
							//	return RES_CRUSHED;
							//}
							sector->floorheight = lastpos;
							P_ChangeSector(sector,crush);
							return RES_CRUSHED;
						}
					}
					break;
			}
			break;

		case 1:         // CEILING
			switch(direction)
			{
				case -1:        // DOWN
					if (sector->ceilingheight - speed < dest)
					{
						lastpos = sector->ceilingheight;
						sector->ceilingheight = dest;
						flag = P_ChangeSector(sector,crush);
						if (flag == true)
						{
							sector->ceilingheight = lastpos;
							P_ChangeSector(sector,crush);
							//return RES_CRUSHED;
						}
						return RES_PASTDEST;
					}
					else    // COULD GET CRUSHED
					{
						lastpos = sector->ceilingheight;
						sector->ceilingheight -= speed;
						flag = P_ChangeSector(sector,crush);
						if (flag == true)
						{
							//if (crush == true)
							//{
							//	return RES_CRUSHED;
							//}
							sector->ceilingheight = lastpos;
							P_ChangeSector(sector,crush);
							return RES_CRUSHED;
						}
					}
					break;

				case 1:         // UP
					if (sector->ceilingheight + speed > dest)
					{
						lastpos = sector->ceilingheight;
						sector->ceilingheight = dest;
						flag = P_ChangeSector(sector,crush);
						if (flag == true)
						{
							sector->ceilingheight = lastpos;
							P_ChangeSector(sector,crush);
							//return RES_CRUSHED;
						}
						return RES_PASTDEST;
					}
					else
					{
						lastpos = sector->ceilingheight;
						sector->ceilingheight += speed;
						flag = P_ChangeSector(sector,crush);
						#if 0
						if (flag == true)
						{
							sector->ceilingheight = lastpos;
							P_ChangeSector(sector,crush);
							return RES_CRUSHED;
						}
						#endif
					}
					break;
			}
			break;

	}
	return RES_OK;
}

//==================================================================
//
//      MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN)
//
//==================================================================
void T_MoveFloor(floormove_t *floor)
{
	result_e        res;

	if(floor->resetDelayCount)
	{
		floor->resetDelayCount--;
		if(!floor->resetDelayCount)
		{
			floor->floordestheight = floor->resetHeight;
			floor->direction = -floor->direction;
			floor->resetDelay = 0;
			floor->delayCount = 0;
			floor->delayTotal = 0;
		}
	}					
	if(floor->delayCount)
	{
		floor->delayCount--;
		if(!floor->delayCount && floor->textureChange)
		{
			floor->sector->floorpic += floor->textureChange;
		}
		return;
	}

	res = T_MovePlane(floor->sector,floor->speed,
			floor->floordestheight,floor->crush,0,floor->direction);

	if(floor->type == FLEV_RAISEBUILDSTEP)
	{
		if((floor->direction == 1 && floor->sector->floorheight >=
			floor->stairsDelayHeight) || (floor->direction == -1 &&
			floor->sector->floorheight <= floor->stairsDelayHeight))
		{
			floor->delayCount = floor->delayTotal;
			floor->stairsDelayHeight += floor->stairsDelayHeightDelta;
		}		
	}
	if (res == RES_PASTDEST)
	{
		SN_StopSequence((mobj_t *)&floor->sector->soundorg);
		if(floor->delayTotal)
		{
			floor->delayTotal = 0;
		}
		if(floor->resetDelay)
		{
//			floor->resetDelayCount = floor->resetDelay;
//			floor->resetDelay = 0;
			return;
		}			
		floor->sector->specialdata = NULL;
		/*
		if (floor->direction == 1)
			switch(floor->type)
			{
				case donutRaise:
					floor->sector->special = floor->newspecial;
					floor->sector->floorpic = floor->texture;
				default:
					break;
			}
		else if (floor->direction == -1)
			switch(floor->type)
			{
				case lowerAndChange:
					floor->sector->special = floor->newspecial;
					floor->sector->floorpic = floor->texture;
				default:
					break;
			}
		*/
		if(floor->textureChange)
		{
			floor->sector->floorpic -= floor->textureChange;
		}
		P_TagFinished(floor->sector->tag);
		P_RemoveThinker(&floor->thinker);
	}
}

//==================================================================
//
//      HANDLE FLOOR TYPES
//
//==================================================================
int EV_DoFloor(line_t *line, byte *args, floor_e floortype)
{
	int                     secnum;
	int                     rtn;
	sector_t        *sec;
	floormove_t     *floor=NULL;

	secnum = -1;
	rtn = 0;
	while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0)
	{
		sec = &sectors[secnum];

		//      ALREADY MOVING?  IF SO, KEEP GOING...
		if (sec->specialdata)
			continue;

		//
		//      new floor thinker
		//
		rtn = 1;
		floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0);
		memset(floor, 0, sizeof(*floor));
		P_AddThinker (&floor->thinker);
		sec->specialdata = floor;
		floor->thinker.function = T_MoveFloor;
		floor->type = floortype;
		floor->crush = 0;
		floor->speed = args[1]*(FRACUNIT/8);
		if(floortype == FLEV_LOWERTIMES8INSTANT || 
			floortype == FLEV_RAISETIMES8INSTANT)
		{
			floor->speed = 2000<<FRACBITS;
		}
		switch(floortype)
		{
			case FLEV_LOWERFLOOR:
				floor->direction = -1;
				floor->sector = sec;
				floor->floordestheight =
					P_FindHighestFloorSurrounding(sec);
				break;
			case FLEV_LOWERFLOORTOLOWEST:
				floor->direction = -1;
				floor->sector = sec;
				floor->floordestheight =
					P_FindLowestFloorSurrounding(sec);
				break;
			case FLEV_LOWERFLOORBYVALUE:
				floor->direction = -1;
				floor->sector = sec;
				floor->floordestheight = floor->sector->floorheight-
					args[2]*FRACUNIT;
				break;
			case FLEV_LOWERTIMES8INSTANT:
			case FLEV_LOWERBYVALUETIMES8:
				floor->direction = -1;
				floor->sector = sec;
				floor->floordestheight = floor->sector->floorheight-
					args[2]*FRACUNIT*8;
				break;
			case FLEV_RAISEFLOORCRUSH:
				floor->crush = args[2]; // arg[2] = crushing value
				floor->direction = 1;
				floor->sector = sec;
				floor->floordestheight = sec->ceilingheight-8*FRACUNIT;
				break;
			case FLEV_RAISEFLOOR:
				floor->direction = 1;
				floor->sector = sec;
				floor->floordestheight =
					P_FindLowestCeilingSurrounding(sec);
				if (floor->floordestheight > sec->ceilingheight)
					floor->floordestheight = sec->ceilingheight;
				break;
			case FLEV_RAISEFLOORTONEAREST:
				floor->direction = 1;
				floor->sector = sec;
				floor->floordestheight =
					P_FindNextHighestFloor(sec,sec->floorheight);
				break;
			case FLEV_RAISEFLOORBYVALUE:
				floor->direction = 1;
				floor->sector = sec;
				floor->floordestheight = floor->sector->floorheight+
					args[2]*FRACUNIT;
				break;
			case FLEV_RAISETIMES8INSTANT:
			case FLEV_RAISEBYVALUETIMES8:
				floor->direction = 1;
				floor->sector = sec;
				floor->floordestheight = floor->sector->floorheight+
					args[2]*FRACUNIT*8;
				break;
			case FLEV_MOVETOVALUETIMES8:
				floor->sector = sec;
				floor->floordestheight = args[2]*FRACUNIT*8;
				if(args[3])
				{
					floor->floordestheight = -floor->floordestheight;
				}
				if(floor->floordestheight > floor->sector->floorheight)
				{
					floor->direction = 1;
				}
				else if(floor->floordestheight < floor->sector->floorheight)
				{
					floor->direction = -1;
				}
				else
				{ // already at lowest position
					rtn = 0;
				}
				break;
			default:
				rtn = 0;
				break;
		}
	}
	if(rtn)
	{
		SN_StartSequence((mobj_t *)&floor->sector->soundorg, 
			SEQ_PLATFORM+floor->sector->seqType);
	}
	return rtn;
}

//============================================================================
//
// EV_DoFloorAndCeiling
//
//============================================================================

int EV_DoFloorAndCeiling(line_t *line, byte *args, boolean raise)
{
	boolean floor, ceiling;
	int                     secnum;
	sector_t        *sec;

	if(raise)
	{
		floor = EV_DoFloor(line, args, FLEV_RAISEFLOORBYVALUE);
		secnum = -1;
		while((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0)
		{
			sec = &sectors[secnum];
			sec->specialdata = NULL;
		}
		ceiling = EV_DoCeiling(line, args, CLEV_RAISEBYVALUE);
	}		
	else 
	{
		floor = EV_DoFloor(line, args, FLEV_LOWERFLOORBYVALUE);
		secnum = -1;
		while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0)
		{
			sec = &sectors[secnum];
			sec->specialdata = NULL;
		}
		ceiling = EV_DoCeiling(line, args, CLEV_LOWERBYVALUE);
	}
	return (floor|ceiling);
}
			
// ===== Build Stairs Private Data =====

#define STAIR_SECTOR_TYPE       26
#define STAIR_QUEUE_SIZE        32

struct
{
	sector_t *sector;
	int type;
	int height;
} StairQueue[STAIR_QUEUE_SIZE];

static int QueueHead;
static int QueueTail;

static int StepDelta;
static int Direction;
static int Speed;
static int Texture;
static int StartDelay;
static int StartDelayDelta;
static int TextureChange;
static int StartHeight;

//==========================================================================
//
// QueueStairSector
//
//==========================================================================

static void QueueStairSector(sector_t *sec, int type, int height)
{
	if((QueueTail+1)%STAIR_QUEUE_SIZE == QueueHead)
	{
		I_Error("BuildStairs:  Too many branches located.\n");
	}
	StairQueue[QueueTail].sector = sec;
	StairQueue[QueueTail].type = type;
	StairQueue[QueueTail].height = height;

	QueueTail = (QueueTail+1)%STAIR_QUEUE_SIZE;
}

//==========================================================================
//
// DequeueStairSector
//
//==========================================================================

static sector_t *DequeueStairSector(int *type, int *height)
{
	sector_t *sec;

	if(QueueHead == QueueTail)
	{ // queue is empty
		return NULL;
	}
	*type = StairQueue[QueueHead].type;
	*height = StairQueue[QueueHead].height;
	sec = StairQueue[QueueHead].sector;
	QueueHead = (QueueHead+1)%STAIR_QUEUE_SIZE;

	return sec;
}

//==========================================================================
//
// ProcessStairSector
//
//==========================================================================

static void ProcessStairSector(sector_t *sec, int type, int height, 
	stairs_e stairsType, int delay, int resetDelay)
{
	int             i;
	sector_t        *tsec;
	floormove_t     *floor;

	//
	// new floor thinker
	//
	height += StepDelta;
	floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0);
	memset(floor, 0, sizeof(*floor));
	P_AddThinker(&floor->thinker);
	sec->specialdata = floor;
	floor->thinker.function = T_MoveFloor;
	floor->type = FLEV_RAISEBUILDSTEP;
	floor->direction = Direction;
	floor->sector = sec;
	floor->floordestheight = height;
	switch(stairsType)
	{
		case STAIRS_NORMAL:
			floor->speed = Speed;
			if(delay)
			{
				floor->delayTotal = delay;
				floor->stairsDelayHeight = sec->floorheight+StepDelta;
				floor->stairsDelayHeightDelta = StepDelta;
			}
			floor->resetDelay = resetDelay;
			floor->resetDelayCount = resetDelay;
			floor->resetHeight = sec->floorheight;
			break;
		case STAIRS_SYNC:
			floor->speed = FixedMul(Speed, FixedDiv(height-StartHeight,
				StepDelta));
			floor->resetDelay = delay; //arg4
			floor->resetDelayCount = delay;
			floor->resetHeight = sec->floorheight;
			break;
/*
		case STAIRS_PHASED:
			floor->floordestheight = sec->floorheight+StepDelta;
			floor->speed = Speed;
			floor->delayCount = StartDelay;
			StartDelay += StartDelayDelta;
			floor->textureChange = TextureChange;
			floor->resetDelayCount = StartDelay;
			break;
*/
		default:
			break;
	}
	SN_StartSequence((mobj_t *)&sec->soundorg, SEQ_PLATFORM+sec->seqType);
	//
	// Find next sector to raise
	// Find nearby sector with sector special equal to type
	//
	for (i = 0; i < sec->linecount; i++)
	{
		if(!((sec->lines[i])->flags&ML_TWOSIDED))
		{
			continue;
		}
		tsec = (sec->lines[i])->frontsector;
		if(tsec->special == type+STAIR_SECTOR_TYPE && !tsec->specialdata
			&& tsec->floorpic == Texture && tsec->validcount != validcount)
		{
			QueueStairSector(tsec, type^1, height);
			tsec->validcount = validcount;
			//tsec->special = 0;
		}
		tsec = (sec->lines[i])->backsector;
		if(tsec->special == type+STAIR_SECTOR_TYPE && !tsec->specialdata
			&& tsec->floorpic == Texture && tsec->validcount != validcount)
		{
			QueueStairSector(tsec, type^1, height);
			tsec->validcount = validcount;
			//tsec->special = 0;
		}
	}
}

//==================================================================
//
//      BUILD A STAIRCASE!
//
// Direction is either positive or negative, denoting build stairs
//      up or down.
//==================================================================

int EV_BuildStairs(line_t *line, byte *args, int direction, 
	stairs_e stairsType)
{
	int secnum;
	int height;
	int	delay;
	int resetDelay;
	sector_t        *sec;
	sector_t *qSec;
	int type;

	// Set global stairs variables
	TextureChange = 0;
	Direction = direction;
	StepDelta = Direction*(args[2]*FRACUNIT);
	Speed = args[1]*(FRACUNIT/8);
	resetDelay = args[4];
	delay = args[3];
	if(stairsType == STAIRS_PHASED)
	{
		StartDelayDelta = args[3];
		StartDelay = StartDelayDelta;
		resetDelay = StartDelayDelta;
		delay = 0;
		TextureChange = args[4];
	}

	secnum = -1;

	validcount++; 
	while ((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0)
	{
		sec = &sectors[secnum];

		Texture = sec->floorpic;
		StartHeight = sec->floorheight;

		// ALREADY MOVING?  IF SO, KEEP GOING...
		if (sec->specialdata)
			continue;

		QueueStairSector(sec, 0, sec->floorheight);
		sec->special = 0;
	}
	while((qSec = DequeueStairSector(&type, &height)) != NULL)
	{
		ProcessStairSector(qSec, type, height, stairsType, delay, resetDelay);
	}
	return(1);
}

//=========================================================================
//
// T_BuildPillar
//
//=========================================================================

void T_BuildPillar(pillar_t *pillar)
{
	result_e        res1;
	result_e res2;

	// First, raise the floor
	res1 = T_MovePlane(pillar->sector, pillar->floorSpeed, pillar->floordest,
		pillar->crush, 0, pillar->direction); // floorOrCeiling, direction
	// Then, lower the ceiling
	res2 = T_MovePlane(pillar->sector, pillar->ceilingSpeed,
		 pillar->ceilingdest, pillar->crush, 1, -pillar->direction);
	if (res1 == RES_PASTDEST && res2 == RES_PASTDEST)
	{
		pillar->sector->specialdata = NULL;
		SN_StopSequence((mobj_t *)&pillar->sector->soundorg);
		P_TagFinished(pillar->sector->tag);
		P_RemoveThinker(&pillar->thinker);
	}
}

//=========================================================================
//
// EV_BuildPillar
//
//=========================================================================

int EV_BuildPillar(line_t *line, byte *args, boolean crush)
{
	int secnum;
	sector_t *sec;
	pillar_t *pillar;
	int newHeight;
	int rtn;

	rtn = 0;
	secnum = -1;
	while((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0)
	{
		sec = &sectors[secnum];
		if(sec->specialdata)
			continue; // already moving
		if(sec->floorheight == sec->ceilingheight)
		{ // pillar is already closed
			continue;
		}
		rtn = 1;
		if(!args[2])
		{
			newHeight = sec->floorheight+
				((sec->ceilingheight-sec->floorheight)/2);
		}
		else
		{
			newHeight = sec->floorheight+(args[2]<<FRACBITS);
		}

		pillar = Z_Malloc(sizeof(*pillar), PU_LEVSPEC, 0);
		sec->specialdata = pillar;
		P_AddThinker(&pillar->thinker);
		pillar->thinker.function = T_BuildPillar;
		pillar->sector = sec;
		if(!args[2])
		{
			pillar->ceilingSpeed = pillar->floorSpeed = args[1]*(FRACUNIT/8);
		}
		else if(newHeight-sec->floorheight > sec->ceilingheight-newHeight)
		{
			pillar->floorSpeed = args[1]*(FRACUNIT/8);
			pillar->ceilingSpeed = FixedMul(sec->ceilingheight-newHeight,
				FixedDiv(pillar->floorSpeed, newHeight-sec->floorheight));
		}
		else
		{
			pillar->ceilingSpeed = args[1]*(FRACUNIT/8);
			pillar->floorSpeed = FixedMul(newHeight-sec->floorheight,
				FixedDiv(pillar->ceilingSpeed, sec->ceilingheight-newHeight));
		}
		pillar->floordest = newHeight;
		pillar->ceilingdest = newHeight;
		pillar->direction = 1;
		pillar->crush = crush*args[3];
		SN_StartSequence((mobj_t *)&pillar->sector->soundorg, 
			SEQ_PLATFORM+pillar->sector->seqType);
	}
	return rtn;
}

//=========================================================================
//
// EV_OpenPillar
//
//=========================================================================

int EV_OpenPillar(line_t *line, byte *args)
{
	int secnum;
	sector_t *sec;
	pillar_t *pillar;
	int rtn;

	rtn = 0;
	secnum = -1;
	while((secnum = P_FindSectorFromTag(args[0], secnum)) >= 0)
	{
		sec = &sectors[secnum];
		if(sec->specialdata)
			continue; // already moving
		if(sec->floorheight != sec->ceilingheight)
		{ // pillar isn't closed
			continue;
		}
		rtn = 1;
		pillar = Z_Malloc(sizeof(*pillar), PU_LEVSPEC, 0);
		sec->specialdata = pillar;
		P_AddThinker(&pillar->thinker);
		pillar->thinker.function = T_BuildPillar;
		pillar->sector = sec;
		if(!args[2])
		{
			pillar->floordest = P_FindLowestFloorSurrounding(sec);
		}
		else
		{
			pillar->floordest = sec->floorheight-(args[2]<<FRACBITS);
		}
		if(!args[3])
		{
			pillar->ceilingdest = P_FindHighestCeilingSurrounding(sec);
		}
		else
		{
			pillar->ceilingdest = sec->ceilingheight+(args[3]<<FRACBITS);
		}
		if(sec->floorheight-pillar->floordest >= pillar->ceilingdest-
			sec->ceilingheight)
		{
			pillar->floorSpeed = args[1]*(FRACUNIT/8);
			pillar->ceilingSpeed = FixedMul(sec->ceilingheight-
				pillar->ceilingdest, FixedDiv(pillar->floorSpeed,
				pillar->floordest-sec->floorheight));
		}
		else
		{
			pillar->ceilingSpeed = args[1]*(FRACUNIT/8);
			pillar->floorSpeed = FixedMul(pillar->floordest-sec->floorheight,
				FixedDiv(pillar->ceilingSpeed, sec->ceilingheight-
				pillar->ceilingdest));
		}
		pillar->direction = -1; // open the pillar
		SN_StartSequence((mobj_t *)&pillar->sector->soundorg, 
			SEQ_PLATFORM+pillar->sector->seqType);
	}
	return rtn;
}

//=========================================================================
//
// EV_FloorCrushStop
//
//=========================================================================

int EV_FloorCrushStop(line_t *line, byte *args)
{
	thinker_t *think;
	floormove_t *floor;
	boolean rtn;

	rtn = 0;
	for(think = thinkercap.next; think != &thinkercap; think = think->next)
	{
		if(think->function != T_MoveFloor)
		{
			continue;
		}
		floor = (floormove_t *)think;
		if(floor->type != FLEV_RAISEFLOORCRUSH)
		{
			continue;
		}
		// Completely remove the crushing floor
		SN_StopSequence((mobj_t *)&floor->sector->soundorg);
		floor->sector->specialdata = NULL;
		P_TagFinished(floor->sector->tag);
		P_RemoveThinker(&floor->thinker);
		rtn = 1;
	}	
	return rtn;
}

//==========================================================================
//
// T_FloorWaggle
//
//==========================================================================

#define WGLSTATE_EXPAND 1
#define WGLSTATE_STABLE 2
#define WGLSTATE_REDUCE 3

void T_FloorWaggle(floorWaggle_t *waggle)
{
	switch(waggle->state)
	{
		case WGLSTATE_EXPAND:
			if((waggle->scale += waggle->scaleDelta)
				>= waggle->targetScale)
			{
				waggle->scale = waggle->targetScale;
				waggle->state = WGLSTATE_STABLE;
			}
			break;
		case WGLSTATE_REDUCE:
			if((waggle->scale -= waggle->scaleDelta) <= 0)
			{ // Remove
				waggle->sector->floorheight = waggle->originalHeight;
				P_ChangeSector(waggle->sector, true);
				waggle->sector->specialdata = NULL;
				P_TagFinished(waggle->sector->tag);
				P_RemoveThinker(&waggle->thinker);
				return;
			}
			break;
		case WGLSTATE_STABLE:
			if(waggle->ticker != -1)
			{
				if(!--waggle->ticker)
				{
					waggle->state = WGLSTATE_REDUCE;
				}
			}
			break;
	}
	waggle->accumulator += waggle->accDelta;
	waggle->sector->floorheight = waggle->originalHeight
		+FixedMul(FloatBobOffsets[(waggle->accumulator>>FRACBITS)&63],
		waggle->scale);
	P_ChangeSector(waggle->sector, true);
}

//==========================================================================
//
// EV_StartFloorWaggle
//
//==========================================================================

boolean EV_StartFloorWaggle(int tag, int height, int speed, int offset,
	int timer)
{
	int sectorIndex;
	sector_t *sector;
	floorWaggle_t *waggle;
	boolean retCode;

	retCode = false;
	sectorIndex = -1;
	while((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
	{
		sector = &sectors[sectorIndex];
		if(sector->specialdata)
		{ // Already busy with another thinker
			continue;
		}
		retCode = true;
		waggle = Z_Malloc(sizeof(*waggle), PU_LEVSPEC, 0);
		sector->specialdata = waggle;
		waggle->thinker.function = T_FloorWaggle;
		waggle->sector = sector;
		waggle->originalHeight = sector->floorheight;
		waggle->accumulator = offset*FRACUNIT;
		waggle->accDelta = speed<<10;
		waggle->scale = 0;
		waggle->targetScale = height<<10;
		waggle->scaleDelta = waggle->targetScale
			/(35+((3*35)*height)/255);
		waggle->ticker = timer ? timer*35 : -1;
		waggle->state = WGLSTATE_EXPAND;
		P_AddThinker(&waggle->thinker);
	}
	return retCode;
}