ref: 12ecb4550e46ffdc28248be185738a88be033afd
dir: /src/hexen/p_floor.c/
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
// Copyright(C) 2005-2014 Simon Howard
//
// 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.
//
#include "h2def.h"
#include "i_system.h"
#include "p_local.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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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;
}