mirror of
https://github.com/odamex/odamex
synced 2026-08-19 02:23:09 -04:00
1960 lines
48 KiB
C++
1960 lines
48 KiB
C++
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id$
|
|
//
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
// Copyright (C) 2006-2026 by The Odamex Team.
|
|
//
|
|
// 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.
|
|
//
|
|
// DESCRIPTION:
|
|
// Floor animation: raising stairs.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
#include "odamex.h"
|
|
|
|
#include "p_local.h"
|
|
#include "p_lnspec.h"
|
|
#include "s_sound.h"
|
|
#include "r_state.h"
|
|
#include "tables.h"
|
|
|
|
void P_ResetTransferSpecial(newspecial_s* newspecial);
|
|
unsigned int P_ResetSectorTransferFlags(const unsigned int flags);
|
|
void SV_BroadcastSectorProperties(int sectornum);
|
|
|
|
EXTERN_CVAR(co_boomphys)
|
|
|
|
extern bool predicting;
|
|
|
|
// ============================================================================
|
|
// FLOORS
|
|
// ============================================================================
|
|
|
|
void P_SetFloorDestroy(DFloor *floor)
|
|
{
|
|
if (!floor || predicting)
|
|
return;
|
|
|
|
floor->m_Status = DFloor::destroy;
|
|
|
|
if (clientside && floor->m_Sector)
|
|
{
|
|
floor->m_Sector->floordata = NULL;
|
|
floor->Destroy();
|
|
}
|
|
}
|
|
|
|
IMPLEMENT_SERIAL (DFloor, DMovingFloor)
|
|
|
|
void DFloor::Serialize (FArchive &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
if (arc.IsStoring ())
|
|
{
|
|
arc << m_Type
|
|
<< m_Status
|
|
<< m_Crush
|
|
<< m_Direction
|
|
<< m_HexenCrush
|
|
<< m_NewSpecial
|
|
<< m_NewDamageRate
|
|
<< m_NewDmgInterval
|
|
<< m_NewFlags
|
|
<< m_NewLeakRate
|
|
<< m_Texture
|
|
<< m_FloorDestHeight
|
|
<< m_Speed
|
|
<< m_ResetCount
|
|
<< m_OrgHeight
|
|
<< m_Delay
|
|
<< m_PauseTime
|
|
<< m_StepTime
|
|
<< m_PerStepTime;
|
|
}
|
|
else
|
|
{
|
|
arc >> m_Type
|
|
>> m_Status
|
|
>> m_Crush
|
|
>> m_Direction
|
|
>> m_HexenCrush
|
|
>> m_NewSpecial
|
|
>> m_NewDamageRate
|
|
>> m_NewDmgInterval
|
|
>> m_NewFlags
|
|
>> m_NewLeakRate
|
|
>> m_Texture
|
|
>> m_FloorDestHeight
|
|
>> m_Speed
|
|
>> m_ResetCount
|
|
>> m_OrgHeight
|
|
>> m_Delay
|
|
>> m_PauseTime
|
|
>> m_StepTime
|
|
>> m_PerStepTime;
|
|
}
|
|
}
|
|
|
|
void DFloor::PlayFloorSound()
|
|
{
|
|
if (predicting || !m_Sector)
|
|
return;
|
|
|
|
S_StopSound(m_Sector->soundorg);
|
|
|
|
if (m_Status == init)
|
|
S_LoopedSound(m_Sector->soundorg, CHAN_BODY, "plats/pt1_mid", 1, ATTN_NORM);
|
|
if (m_Status == finished)
|
|
S_Sound(m_Sector->soundorg, CHAN_BODY, "plats/pt1_stop", 1, ATTN_NORM);
|
|
}
|
|
|
|
//
|
|
// MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN)
|
|
//
|
|
void DFloor::RunThink ()
|
|
{
|
|
if (m_Status == finished)
|
|
{
|
|
PlayFloorSound();
|
|
P_SetFloorDestroy(this);
|
|
}
|
|
|
|
if (m_Status == destroy)
|
|
return;
|
|
|
|
// [RH] Handle resetting stairs
|
|
if (m_Type == buildStair || m_Type == waitStair)
|
|
{
|
|
if (m_ResetCount)
|
|
{
|
|
if (--m_ResetCount == 0)
|
|
{
|
|
m_Type = resetStair;
|
|
m_Direction = (m_Direction > 0) ? -1 : 1;
|
|
m_FloorDestHeight = m_OrgHeight;
|
|
}
|
|
}
|
|
if (m_PauseTime)
|
|
{
|
|
m_PauseTime--;
|
|
return;
|
|
}
|
|
else if (m_StepTime)
|
|
{
|
|
if (--m_StepTime == 0)
|
|
{
|
|
m_PauseTime = m_Delay;
|
|
m_StepTime = m_PerStepTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_Type == waitStair)
|
|
return;
|
|
|
|
EResult res = MoveFloor (m_Speed, m_FloorDestHeight, m_Crush, m_Direction, m_HexenCrush);
|
|
|
|
if (res == pastdest)
|
|
{
|
|
m_Status = finished;
|
|
PlayFloorSound();
|
|
|
|
if (m_Type == buildStair)
|
|
m_Type = waitStair;
|
|
|
|
if (m_Type != waitStair || m_ResetCount == 0)
|
|
{
|
|
if (m_Direction == 1)
|
|
{
|
|
switch (m_Type)
|
|
{
|
|
case donutRaise:
|
|
case genFloorChgT:
|
|
case genFloorChg0:
|
|
m_Sector->special = m_NewSpecial;
|
|
m_Sector->flags = m_NewFlags;
|
|
m_Sector->damageamount = m_NewDamageRate;
|
|
m_Sector->damageinterval = m_NewDmgInterval;
|
|
m_Sector->leakrate = m_NewLeakRate;
|
|
[[fallthrough]];
|
|
case genFloorChg:
|
|
m_Sector->floorpic = m_Texture;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (m_Direction == -1)
|
|
{
|
|
switch(m_Type)
|
|
{
|
|
case floorLowerAndChange:
|
|
case genFloorChgT:
|
|
case genFloorChg0:
|
|
m_Sector->special = m_NewSpecial;
|
|
m_Sector->flags = m_NewFlags;
|
|
m_Sector->damageamount = m_NewDamageRate;
|
|
m_Sector->damageinterval = m_NewDmgInterval;
|
|
m_Sector->leakrate = m_NewLeakRate;
|
|
[[fallthrough]];
|
|
case genFloorChg:
|
|
m_Sector->floorpic = m_Texture;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
//jff 2/26/98 implement stair retrigger lockout while still building
|
|
// note this only applies to the retriggerable generalized stairs
|
|
|
|
if (m_Sector->stairlock == -2) // if this sector is stairlocked
|
|
{
|
|
sector_t *sec = m_Sector;
|
|
sec->stairlock = -1; // thinker done, promote lock to -1
|
|
|
|
while (sec->prevsec != -1 && sectors[sec->prevsec].stairlock != -2)
|
|
sec = §ors[sec->prevsec]; // search for a non-done thinker
|
|
if (sec->prevsec == -1) // if all thinkers previous are done
|
|
{
|
|
sec = m_Sector; // search forward
|
|
while (sec->nextsec != -1 && sectors[sec->nextsec].stairlock != -2)
|
|
sec = §ors[sec->nextsec];
|
|
if (sec->nextsec == -1) // if all thinkers ahead are done too
|
|
{
|
|
while (sec->prevsec != -1) // clear all locks
|
|
{
|
|
sec->stairlock = 0;
|
|
sec = §ors[sec->prevsec];
|
|
}
|
|
sec->stairlock = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
P_SetFloorDestroy(this);
|
|
}
|
|
}
|
|
|
|
DFloor::DFloor (sector_t *sec)
|
|
: DMovingFloor (sec)
|
|
{
|
|
}
|
|
|
|
DFloor::DFloor(sector_t* sec, DFloor::EFloor floortype, const line_t* line, fixed_t speed,
|
|
fixed_t height, int crush, int change, bool hexencrush, bool hereticlower)
|
|
: DMovingFloor(sec)
|
|
{
|
|
fixed_t floorheight = P_FloorHeight(sec);
|
|
fixed_t ceilingheight = P_CeilingHeight(sec);
|
|
|
|
m_Type = floortype;
|
|
m_Crush = crush;
|
|
m_HexenCrush = hexencrush;
|
|
m_Speed = speed;
|
|
m_ResetCount = 0; // [RH]
|
|
m_Direction = 1;
|
|
m_OrgHeight = floorheight;
|
|
m_Line = line;
|
|
|
|
PlayFloorSound();
|
|
|
|
switch (floortype)
|
|
{
|
|
case DFloor::floorLowerToHighest:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = P_FindHighestFloorSurrounding(sec);
|
|
// [RH] DOOM's turboLower type did this. I've just extended it
|
|
// to be applicable to all LowerToHighest types.
|
|
if (hereticlower || m_FloorDestHeight != floorheight)
|
|
m_FloorDestHeight += height;
|
|
break;
|
|
|
|
case DFloor::floorLowerToLowest:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = P_FindLowestFloorSurrounding(sec);
|
|
break;
|
|
|
|
case DFloor::floorLowerToNearest:
|
|
// jff 02/03/30 support lowering floor to next lowest floor
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = P_FindNextLowestFloor(sec);
|
|
break;
|
|
|
|
case DFloor::floorLowerInstant:
|
|
m_Speed = height;
|
|
[[fallthrough]];
|
|
case DFloor::floorLowerByValue:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = floorheight - height;
|
|
break;
|
|
|
|
case DFloor::floorRaiseInstant:
|
|
m_Speed = height;
|
|
[[fallthrough]];
|
|
case DFloor::floorRaiseByValue:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = floorheight + height;
|
|
break;
|
|
|
|
case DFloor::floorMoveToValue:
|
|
m_FloorDestHeight = height;
|
|
m_Direction = (m_FloorDestHeight > floorheight) ? 1 : -1;
|
|
break;
|
|
|
|
case DFloor::floorRaiseAndCrushDoom:
|
|
height = 8 * FRACUNIT;
|
|
[[fallthrough]];
|
|
case DFloor::floorRaiseToLowestCeiling:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = P_FindLowestCeilingSurrounding(sec);
|
|
if (m_FloorDestHeight > ceilingheight)
|
|
m_FloorDestHeight = ceilingheight - height;
|
|
break;
|
|
|
|
case DFloor::floorRaiseToHighest:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = P_FindHighestFloorSurrounding(sec);
|
|
break;
|
|
|
|
case DFloor::floorRaiseToNearest:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = P_FindNextHighestFloor(sec);
|
|
break;
|
|
|
|
case DFloor::floorRaiseToLowest:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = P_FindLowestFloorSurrounding(sec);
|
|
break;
|
|
|
|
case DFloor::floorRaiseAndCrush:
|
|
height = 8 * FRACUNIT;
|
|
[[fallthrough]];
|
|
case DFloor::floorRaiseToCeiling:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = ceilingheight - height;
|
|
break;
|
|
|
|
case DFloor::floorLowerToLowestCeiling:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = P_FindLowestCeilingSurrounding(sec);
|
|
break;
|
|
|
|
case DFloor::floorLowerByTexture:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = floorheight - P_FindShortestTextureAround(sec);
|
|
break;
|
|
|
|
case DFloor::floorLowerToCeiling:
|
|
// [RH] Essentially instantly raises the floor to the ceiling
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = ceilingheight - height;
|
|
break;
|
|
|
|
case DFloor::floorRaiseByTexture:
|
|
m_Direction = 1;
|
|
// [RH] Use P_FindShortestTextureAround from BOOM to do this
|
|
// since the code is identical to what was here. (Oddly
|
|
// enough, BOOM preserved the code here even though it
|
|
// also had this function.)
|
|
m_FloorDestHeight = floorheight + P_FindShortestTextureAround(sec);
|
|
break;
|
|
|
|
case DFloor::floorRaiseAndChange:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = floorheight + height;
|
|
if (line)
|
|
{
|
|
sec->floorpic = line->frontsector->floorpic;
|
|
P_CopySectorSpecial(sec, line->frontsector);
|
|
}
|
|
else
|
|
{
|
|
P_ResetSectorSpecial(sec);
|
|
}
|
|
break;
|
|
|
|
case DFloor::floorLowerAndChange:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = P_FindLowestFloorSurrounding(sec);
|
|
m_Texture = sec->floorpic;
|
|
// jff 1/24/98 make sure m_NewSpecial gets initialized
|
|
// in case no surrounding sector is at floordestheight
|
|
// --> should not affect compatibility <--
|
|
m_NewSpecial = sec->special;
|
|
m_NewDamageRate = sec->damageamount;
|
|
m_NewDmgInterval = sec->damageinterval;
|
|
m_NewLeakRate = sec->leakrate;
|
|
m_NewFlags = sec->flags;
|
|
|
|
// jff 5/23/98 use model subroutine to unify fixes and handling
|
|
sec = P_FindModelFloorSector(m_FloorDestHeight, sec);
|
|
if (sec)
|
|
{
|
|
m_Texture = sec->floorpic;
|
|
m_NewSpecial = sec->special;
|
|
m_NewDamageRate = sec->damageamount;
|
|
m_NewDmgInterval = sec->damageinterval;
|
|
m_NewLeakRate = sec->leakrate;
|
|
m_NewFlags = sec->flags;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (m_Direction == 1)
|
|
m_Status = up;
|
|
else if (m_Direction == -1)
|
|
m_Status = down;
|
|
|
|
if (change & 3)
|
|
{
|
|
// [RH] Need to do some transferring
|
|
if (change & 4)
|
|
{
|
|
// Numeric model change
|
|
sector_t* found = NULL;
|
|
if (floortype == DFloor::floorRaiseToLowestCeiling ||
|
|
floortype == DFloor::floorLowerToLowestCeiling ||
|
|
floortype == DFloor::floorRaiseToCeiling ||
|
|
floortype == DFloor::floorLowerToCeiling)
|
|
{
|
|
found = P_FindModelCeilingSector(m_FloorDestHeight, sec);
|
|
}
|
|
else
|
|
{
|
|
found = P_FindModelFloorSector(m_FloorDestHeight, sec);
|
|
}
|
|
|
|
if (found != NULL)
|
|
{
|
|
m_Texture = found->floorpic;
|
|
switch (change & 3)
|
|
{
|
|
case 1:
|
|
newspecial_s ns;
|
|
P_ResetTransferSpecial(&ns);
|
|
m_NewSpecial = ns.special;
|
|
m_NewDamageRate = ns.damageamount;
|
|
m_NewDmgInterval = ns.damageinterval;
|
|
m_NewLeakRate = ns.damageleakrate;
|
|
m_NewFlags = P_ResetSectorTransferFlags(found->flags);
|
|
m_Type = DFloor::genFloorChg0;
|
|
break;
|
|
case 2:
|
|
m_Type = DFloor::genFloorChg;
|
|
break;
|
|
case 3:
|
|
m_NewSpecial = found->special;
|
|
m_NewDamageRate = found->damageamount;
|
|
m_NewDmgInterval = found->damageinterval;
|
|
m_NewLeakRate = found->leakrate;
|
|
m_NewFlags = found->flags;
|
|
m_Type = DFloor::genFloorChgT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (line != NULL)
|
|
{
|
|
// Trigger model change
|
|
m_Texture = line->frontsector->floorpic;
|
|
|
|
switch (change & 3)
|
|
{
|
|
case 1:
|
|
newspecial_s ns;
|
|
P_ResetTransferSpecial(&ns);
|
|
m_NewSpecial = ns.special;
|
|
m_NewDamageRate = ns.damageamount;
|
|
m_NewDmgInterval = ns.damageinterval;
|
|
m_NewLeakRate = ns.damageleakrate;
|
|
m_NewFlags = P_ResetSectorTransferFlags(line->frontsector->flags);
|
|
m_Type = DFloor::genFloorChg0;
|
|
break;
|
|
case 2:
|
|
m_Type = DFloor::genFloorChg;
|
|
break;
|
|
case 3:
|
|
m_NewSpecial = line->frontsector->special;
|
|
m_NewDamageRate = line->frontsector->damageamount;
|
|
m_NewDmgInterval = line->frontsector->damageinterval;
|
|
m_NewLeakRate = line->frontsector->leakrate;
|
|
m_NewFlags = line->frontsector->flags;
|
|
m_Type = DFloor::genFloorChgT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generalized floor init
|
|
DFloor::DFloor(sector_t* sec, const line_t* line, int speed,
|
|
int target, int crush, int change, int direction, int model)
|
|
: DMovingFloor(sec)
|
|
{
|
|
fixed_t floorheight = P_FloorHeight(sec);
|
|
|
|
m_Type = genFloor;
|
|
m_Crush = crush ? DOOM_CRUSH : NO_CRUSH;
|
|
m_HexenCrush = false;
|
|
m_ResetCount = 0; // [RH]
|
|
m_Sector = sec;
|
|
m_OrgHeight = floorheight;
|
|
m_Line = line;
|
|
m_Direction = direction ? 1 : -1;
|
|
m_NewSpecial = sec->special;
|
|
m_NewDamageRate = sec->damageamount;
|
|
m_NewDmgInterval = sec->damageinterval;
|
|
m_NewLeakRate = sec->leakrate;
|
|
m_NewFlags = sec->flags;
|
|
|
|
if (m_Direction == 1)
|
|
m_Status = up;
|
|
else if (m_Direction == -1)
|
|
m_Status = down;
|
|
|
|
PlayFloorSound();
|
|
|
|
switch (speed)
|
|
{
|
|
case SpeedSlow:
|
|
m_Speed = FLOORSPEED;
|
|
break;
|
|
case SpeedNormal:
|
|
m_Speed = FLOORSPEED * 2;
|
|
break;
|
|
case SpeedFast:
|
|
m_Speed = FLOORSPEED * 4;
|
|
break;
|
|
case SpeedTurbo:
|
|
m_Speed = FLOORSPEED * 8;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// set the destination height
|
|
switch (target)
|
|
{
|
|
case FtoHnF:
|
|
m_FloorDestHeight = P_FindHighestFloorSurrounding(sec);
|
|
break;
|
|
case FtoLnF:
|
|
m_FloorDestHeight = P_FindLowestFloorSurrounding(sec);
|
|
break;
|
|
case FtoNnF:
|
|
m_FloorDestHeight =
|
|
direction ? P_FindNextHighestFloor(sec) : P_FindNextLowestFloor(sec);
|
|
break;
|
|
case FtoLnC:
|
|
m_FloorDestHeight = P_FindLowestCeilingSurrounding(sec);
|
|
break;
|
|
case FtoC:
|
|
m_FloorDestHeight = sec->ceilingheight;
|
|
break;
|
|
case FbyST:
|
|
m_FloorDestHeight =
|
|
(sec->floorheight >> FRACBITS) +
|
|
m_Direction * (P_FindShortestTextureAround(sec) >> FRACBITS);
|
|
if (m_FloorDestHeight > 32000) // jff 3/13/98 prevent overflow
|
|
m_FloorDestHeight = 32000; // wraparound in floor height
|
|
if (m_FloorDestHeight < -32000)
|
|
m_FloorDestHeight = -32000;
|
|
m_FloorDestHeight <<= FRACBITS;
|
|
break;
|
|
case Fby24:
|
|
m_FloorDestHeight =
|
|
sec->floorheight + m_Direction * 24 * FRACUNIT;
|
|
break;
|
|
case Fby32:
|
|
m_FloorDestHeight = sec->floorheight + m_Direction * 32 * FRACUNIT;
|
|
break;
|
|
}
|
|
|
|
// set texture/type change properties
|
|
if (change) // if a texture change is indicated
|
|
{
|
|
if (model) // if a numeric model change
|
|
{
|
|
sector_t* chgsec = NULL;
|
|
|
|
// jff 5/23/98 find model with ceiling at target height if target
|
|
// is a ceiling type
|
|
chgsec = (target == FtoLnC || target == FtoC)
|
|
? P_FindModelCeilingSector(m_FloorDestHeight, sec)
|
|
: P_FindModelFloorSector(m_FloorDestHeight, sec);
|
|
if (chgsec)
|
|
{
|
|
m_Texture = chgsec->floorpic;
|
|
m_NewSpecial = chgsec->special;
|
|
m_NewDamageRate = chgsec->damageamount;
|
|
m_NewDmgInterval = chgsec->damageinterval;
|
|
m_NewLeakRate = chgsec->leakrate;
|
|
m_NewFlags = chgsec->flags;
|
|
switch (change)
|
|
{
|
|
case FChgZero: // zero type
|
|
newspecial_s ns;
|
|
P_ResetTransferSpecial(&ns);
|
|
m_NewSpecial = ns.special;
|
|
m_NewDamageRate = ns.damageamount;
|
|
m_NewDmgInterval = ns.damageinterval;
|
|
m_NewLeakRate = ns.damageleakrate;
|
|
m_NewFlags = P_ResetSectorTransferFlags(chgsec->flags);
|
|
m_Type = genFloorChg0;
|
|
break;
|
|
case FChgTyp: // copy type
|
|
m_NewSpecial = chgsec->special;
|
|
m_NewDamageRate = chgsec->damageamount;
|
|
m_NewDmgInterval = chgsec->damageinterval;
|
|
m_NewLeakRate = chgsec->leakrate;
|
|
m_NewFlags = chgsec->flags;
|
|
m_Type = genFloorChgT;
|
|
break;
|
|
case FChgTxt: // leave type be
|
|
m_Type = genFloorChg;
|
|
break;
|
|
case FNoChg:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else // else if a trigger model change
|
|
{
|
|
m_Texture = line->frontsector->floorpic;
|
|
m_NewSpecial = line->frontsector->special;
|
|
m_NewDamageRate = line->frontsector->damageamount;
|
|
m_NewDmgInterval = line->frontsector->damageinterval;
|
|
m_NewLeakRate = line->frontsector->leakrate;
|
|
m_NewFlags = line->frontsector->flags;
|
|
switch (change)
|
|
{
|
|
case FChgZero: // zero type
|
|
newspecial_s ns;
|
|
P_ResetTransferSpecial(&ns);
|
|
m_NewSpecial = ns.special;
|
|
m_NewDamageRate = ns.damageamount;
|
|
m_NewDmgInterval = ns.damageinterval;
|
|
m_NewLeakRate = ns.damageleakrate;
|
|
m_NewFlags = P_ResetSectorTransferFlags(line->frontsector->flags);
|
|
m_Type = genFloorChg0;
|
|
break;
|
|
case FChgTyp: // copy type
|
|
m_NewSpecial = line->frontsector->special;
|
|
m_NewDamageRate = line->frontsector->damageamount;
|
|
m_NewDmgInterval = line->frontsector->damageinterval;
|
|
m_NewLeakRate = line->frontsector->leakrate;
|
|
m_NewFlags = line->frontsector->flags;
|
|
m_Type = genFloorChgT;
|
|
break;
|
|
case FChgTxt: // leave type be
|
|
m_Type = genFloorChg;
|
|
case FNoChg:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DFloor::DFloor(sector_t *sec, DFloor::EFloor floortype, const line_t *line,
|
|
fixed_t speed, fixed_t height, bool crush, int change)
|
|
: DMovingFloor (sec)
|
|
{
|
|
fixed_t floorheight = P_FloorHeight(sec);
|
|
fixed_t ceilingheight = P_CeilingHeight(sec);
|
|
|
|
m_Type = floortype;
|
|
m_Crush = crush ? DOOM_CRUSH : NO_CRUSH;
|
|
m_HexenCrush = false;
|
|
m_Speed = speed;
|
|
m_ResetCount = 0; // [RH]
|
|
m_Direction = 1;
|
|
m_OrgHeight = floorheight;
|
|
m_Line = line;
|
|
|
|
PlayFloorSound();
|
|
|
|
switch (floortype)
|
|
{
|
|
case DFloor::floorLowerToHighest:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = P_FindHighestFloorSurrounding(sec);
|
|
// [RH] DOOM's turboLower type did this. I've just extended it
|
|
// to be applicable to all LowerToHighest types.
|
|
if (m_FloorDestHeight != floorheight)
|
|
m_FloorDestHeight += height;
|
|
break;
|
|
|
|
case DFloor::floorLowerToLowest:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = P_FindLowestFloorSurrounding(sec);
|
|
break;
|
|
|
|
case DFloor::floorLowerToNearest:
|
|
//jff 02/03/30 support lowering floor to next lowest floor
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = P_FindNextLowestFloor(sec);
|
|
break;
|
|
|
|
case DFloor::floorLowerInstant:
|
|
m_Speed = height;
|
|
[[fallthrough]];
|
|
case DFloor::floorLowerByValue:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = floorheight - height;
|
|
break;
|
|
|
|
case DFloor::floorRaiseInstant:
|
|
m_Speed = height;
|
|
[[fallthrough]];
|
|
case DFloor::floorRaiseByValue:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = floorheight + height;
|
|
break;
|
|
|
|
case DFloor::floorMoveToValue:
|
|
m_Direction = (height - floorheight) > 0 ? 1 : -1;
|
|
m_FloorDestHeight = height;
|
|
break;
|
|
|
|
case DFloor::floorRaiseAndCrush:
|
|
case DFloor::floorRaiseToLowestCeiling:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight =
|
|
P_FindLowestCeilingSurrounding(sec);
|
|
if (m_FloorDestHeight > ceilingheight)
|
|
m_FloorDestHeight = ceilingheight;
|
|
if (floortype == DFloor::floorRaiseAndCrush)
|
|
m_FloorDestHeight -= 8 * FRACUNIT;
|
|
break;
|
|
|
|
case DFloor::floorRaiseToHighest:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = P_FindHighestFloorSurrounding(sec);
|
|
break;
|
|
|
|
case DFloor::floorRaiseToNearest:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = P_FindNextHighestFloor(sec);
|
|
break;
|
|
|
|
case DFloor::floorRaiseToLowest:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = P_FindLowestFloorSurrounding(sec);
|
|
break;
|
|
|
|
case DFloor::floorRaiseToCeiling:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = ceilingheight;
|
|
break;
|
|
|
|
case DFloor::floorLowerToLowestCeiling:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = P_FindLowestCeilingSurrounding(sec);
|
|
break;
|
|
|
|
case DFloor::floorLowerByTexture:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = floorheight - P_FindShortestTextureAround (sec);
|
|
break;
|
|
|
|
case DFloor::floorLowerToCeiling:
|
|
// [RH] Essentially instantly raises the floor to the ceiling
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = ceilingheight;
|
|
break;
|
|
|
|
case DFloor::floorRaiseByTexture:
|
|
m_Direction = 1;
|
|
// [RH] Use P_FindShortestTextureAround from BOOM to do this
|
|
// since the code is identical to what was here. (Oddly
|
|
// enough, BOOM preserved the code here even though it
|
|
// also had this function.)
|
|
m_FloorDestHeight = floorheight +
|
|
P_FindShortestTextureAround (sec);
|
|
break;
|
|
|
|
case DFloor::floorRaiseAndChange:
|
|
m_Direction = 1;
|
|
m_FloorDestHeight = floorheight + height;
|
|
if (line)
|
|
{
|
|
sec->floorpic = line->frontsector->floorpic;
|
|
sec->special = line->frontsector->special;
|
|
sec->flags = line->frontsector->flags;
|
|
sec->damageinterval = line->frontsector->damageinterval;
|
|
sec->leakrate = line->frontsector->leakrate;
|
|
sec->damageamount = line->frontsector->damageamount;
|
|
}
|
|
break;
|
|
|
|
case DFloor::floorLowerAndChange:
|
|
m_Direction = -1;
|
|
m_FloorDestHeight = P_FindLowestFloorSurrounding (sec);
|
|
m_Texture = sec->floorpic;
|
|
// jff 1/24/98 make sure m_NewSpecial gets initialized
|
|
// in case no surrounding sector is at floordestheight
|
|
// --> should not affect compatibility <--
|
|
m_NewSpecial = sec->special;
|
|
m_NewFlags = sec->flags;
|
|
m_NewDmgInterval = sec->damageinterval;
|
|
m_NewLeakRate = sec->leakrate;
|
|
m_NewDamageRate = sec->damageamount;
|
|
|
|
//jff 5/23/98 use model subroutine to unify fixes and handling
|
|
sec = P_FindModelFloorSector (m_FloorDestHeight,sec);
|
|
if (sec)
|
|
{
|
|
m_Texture = sec->floorpic;
|
|
m_NewSpecial = sec->special;
|
|
m_NewFlags = sec->flags;
|
|
m_NewDmgInterval = sec->damageinterval;
|
|
m_NewLeakRate = sec->leakrate;
|
|
m_NewDamageRate = sec->damageamount;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (m_Direction == 1)
|
|
m_Status = up;
|
|
else if (m_Direction == -1)
|
|
m_Status = down;
|
|
|
|
if (change & 3)
|
|
{
|
|
// [RH] Need to do some transferring
|
|
if (change & 4)
|
|
{
|
|
// Numeric model change
|
|
sector_t* found = NULL;
|
|
if (floortype == DFloor::floorRaiseToLowestCeiling ||
|
|
floortype == DFloor::floorLowerToLowestCeiling ||
|
|
floortype == DFloor::floorRaiseToCeiling ||
|
|
floortype == DFloor::floorLowerToCeiling)
|
|
{
|
|
found = P_FindModelCeilingSector(m_FloorDestHeight, sec);
|
|
}
|
|
else
|
|
{
|
|
found = P_FindModelFloorSector(m_FloorDestHeight, sec);
|
|
}
|
|
|
|
if (found != NULL)
|
|
{
|
|
m_Texture = found->floorpic;
|
|
switch (change & 3)
|
|
{
|
|
case 1:
|
|
m_NewSpecial = 0;
|
|
m_NewFlags = 0;
|
|
m_NewDmgInterval = 0;
|
|
m_NewLeakRate = 0;
|
|
m_NewDamageRate = 0;
|
|
m_Type = DFloor::genFloorChg0;
|
|
break;
|
|
case 2:
|
|
m_NewSpecial = found->special;
|
|
m_NewFlags = found->flags;
|
|
m_NewDmgInterval = found->damageinterval;
|
|
m_NewLeakRate = found->leakrate;
|
|
m_NewDamageRate = found->damageamount;
|
|
m_Type = DFloor::genFloorChgT;
|
|
break;
|
|
case 3:
|
|
m_Type = DFloor::genFloorChg;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (line != NULL)
|
|
{
|
|
// Trigger model change
|
|
m_Texture = line->frontsector->floorpic;
|
|
|
|
switch (change & 3) {
|
|
case 1:
|
|
m_NewSpecial = 0;
|
|
m_NewFlags = 0;
|
|
m_Type = DFloor::genFloorChg0;
|
|
break;
|
|
case 2:
|
|
m_NewSpecial = line->frontsector->special;
|
|
m_NewFlags = line->frontsector->flags;
|
|
m_NewDmgInterval = line->frontsector->damageinterval;
|
|
m_NewLeakRate = line->frontsector->leakrate;
|
|
m_NewDamageRate = line->frontsector->damageamount;
|
|
m_Type = DFloor::genFloorChgT;
|
|
break;
|
|
case 3:
|
|
m_Type = DFloor::genFloorChg;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clones a DFloor and returns a pointer to that clone.
|
|
//
|
|
// The caller owns the pointer, and it must be deleted with `delete`.
|
|
DFloor* DFloor::Clone(sector_t* sec) const
|
|
{
|
|
DFloor* dfloor = new DFloor(*this);
|
|
|
|
dfloor->Orphan();
|
|
dfloor->m_Sector = sec;
|
|
|
|
return dfloor;
|
|
}
|
|
|
|
//
|
|
// HANDLE FLOOR TYPES
|
|
// [RH] Added tag, speed, height, crush, change params.
|
|
//
|
|
bool EV_DoFloor (DFloor::EFloor floortype, line_t *line, int tag,
|
|
fixed_t speed, fixed_t height, bool crush, int change)
|
|
{
|
|
const auto helper = [&](sector_t* sec) -> bool
|
|
{
|
|
// ALREADY MOVING? IF SO, KEEP GOING...
|
|
if (sec->floordata || (demoplayback && sec->ceilingdata))
|
|
return false;
|
|
|
|
// new floor thinker
|
|
new DFloor(sec, floortype, line, speed, height, crush, change);
|
|
P_AddMovingFloor(sec);
|
|
return true;
|
|
};
|
|
|
|
// check if a manual trigger; if so do just the sector on the backside
|
|
if (co_boomphys && tag == 0)
|
|
{
|
|
if (!line || !line->backsector)
|
|
return false;
|
|
|
|
return helper(line->backsector);
|
|
}
|
|
|
|
bool rtn = false;
|
|
int secnum = -1;
|
|
while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0)
|
|
{
|
|
rtn |= helper(§ors[secnum]);
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
//
|
|
// EV_DoGenFloor()
|
|
//
|
|
// Handle generalized floor types
|
|
//
|
|
// Passed the line activating the generalized floor function
|
|
// Returns true if a thinker is created
|
|
//
|
|
// jff 02/04/98 Added this routine (and file) to handle generalized
|
|
// floor movers using bit fields in the line special type.
|
|
//
|
|
bool EV_DoGenFloor(line_t& line)
|
|
{
|
|
const uint32_t value = static_cast<uint32_t>(line.special) - GenFloorBase;
|
|
|
|
// parse the bit fields in the line's special type
|
|
|
|
const int Crsh = (value & FloorCrush) >> FloorCrushShift;
|
|
const int ChgT = (value & FloorChange) >> FloorChangeShift;
|
|
const int Targ = (value & FloorTarget) >> FloorTargetShift;
|
|
const int Dirn = (value & FloorDirection) >> FloorDirectionShift;
|
|
const int ChgM = (value & FloorModel) >> FloorModelShift;
|
|
const int Sped = (value & FloorSpeed) >> FloorSpeedShift;
|
|
const int Trig = (value & TriggerType) >> TriggerTypeShift;
|
|
|
|
const auto helper = [&](sector_t* sec) -> bool
|
|
{
|
|
// ALREADY MOVING? IF SO, KEEP GOING...
|
|
if (sec->floordata)
|
|
return false;
|
|
|
|
// new floor thinker
|
|
new DFloor(sec, &line, Sped, Targ, Crsh, ChgT, Dirn, ChgM);
|
|
P_AddMovingFloor(sec);
|
|
return true;
|
|
};
|
|
|
|
// check if a manual trigger; if so do just the sector on the backside
|
|
if (Trig == PushOnce || Trig == PushMany)
|
|
{
|
|
if (!line.backsector)
|
|
return false;
|
|
|
|
return helper(line.backsector);
|
|
}
|
|
|
|
bool rtn = false;
|
|
int secnum = -1;
|
|
while ((secnum = P_FindSectorFromTag(line.id, secnum)) >= 0)
|
|
{
|
|
rtn |= helper(§ors[secnum]);
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
bool EV_DoZDoomFloor(DFloor::EFloor floortype, line_t* line, int tag, fixed_t speed,
|
|
fixed_t height, int crush, int change, bool hexencrush, bool hereticlower)
|
|
{
|
|
height *= FRACUNIT;
|
|
|
|
const auto helper = [&](sector_t* sec) -> bool
|
|
{
|
|
// ALREADY MOVING? IF SO, KEEP GOING...
|
|
if (sec->floordata)
|
|
return false;
|
|
|
|
// new floor thinker
|
|
new DFloor(sec, floortype, line, speed, height, crush, change, hexencrush, hereticlower);
|
|
P_AddMovingFloor(sec);
|
|
return true;
|
|
};
|
|
|
|
// check if a manual trigger; if so do just the sector on the backside
|
|
if (co_boomphys && tag == 0)
|
|
{
|
|
if (!line || !line->backsector)
|
|
return false;
|
|
|
|
return helper(line->backsector);
|
|
}
|
|
|
|
bool rtn = false;
|
|
int secnum = -1;
|
|
while ((secnum = P_FindSectorFromTag(tag, secnum)) >= 0)
|
|
{
|
|
rtn |= helper(§ors[secnum]);
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
//
|
|
// EV_DoChange()
|
|
//
|
|
// Handle pure change types. These change floor texture and sector type
|
|
// by trigger or numeric model without moving the floor.
|
|
//
|
|
// The linedef causing the change and the type of change is passed
|
|
// Returns true if any sector changes
|
|
//
|
|
// jff 3/15/98 added to better support generalized sector types
|
|
// [RH] Added tag parameter.
|
|
//
|
|
bool EV_DoChange (line_t *line, EChange changetype, int tag)
|
|
{
|
|
sector_t *secm;
|
|
|
|
int secnum = -1;
|
|
bool rtn = false;
|
|
// change all sectors with the same tag as the linedef
|
|
while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0)
|
|
{
|
|
sector_t* sec = §ors[secnum];
|
|
|
|
rtn = true;
|
|
|
|
// handle trigger or numeric change type
|
|
switch(changetype)
|
|
{
|
|
case trigChangeOnly:
|
|
if (line) { // [RH] if no line, no change
|
|
sec->floorpic = line->frontsector->floorpic;
|
|
sec->special = line->frontsector->special;
|
|
sec->SectorChanges |= SPC_FlatPic | SPC_Special;
|
|
SERVER_ONLY(
|
|
SV_BroadcastSectorProperties(secnum);
|
|
)
|
|
}
|
|
break;
|
|
case numChangeOnly:
|
|
secm = P_FindModelFloorSector(P_FloorHeight(sec), sec);
|
|
if (secm) // if no model, no change
|
|
{
|
|
sec->floorpic = secm->floorpic;
|
|
sec->special = secm->special;
|
|
sec->SectorChanges |= SPC_FlatPic | SPC_Special;
|
|
SERVER_ONLY(
|
|
SV_BroadcastSectorProperties(secnum);
|
|
)
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
//
|
|
// [Blair] Generic staircase building
|
|
//
|
|
bool EV_DoGenStairs(line_t& line)
|
|
{
|
|
const uint32_t value = static_cast<uint32_t>(line.special) - GenStairsBase;
|
|
|
|
// parse the bit fields in the line's special type
|
|
const int Igno = (value & StairIgnore) >> StairIgnoreShift;
|
|
const int Dirn = (value & StairDirection) >> StairDirectionShift;
|
|
const int Step = (value & StairStep) >> StairStepShift;
|
|
const int Sped = (value & StairSpeed) >> StairSpeedShift;
|
|
const int Trig = (value & TriggerType) >> TriggerTypeShift;
|
|
|
|
const auto helper = [&](sector_t* sec, int secnum) -> bool
|
|
{
|
|
// ALREADY MOVING? IF SO, KEEP GOING...
|
|
// jff 2/26/98 add special lockout condition to wait for entire
|
|
// staircase to build before retriggering
|
|
if (sec->floordata || sec->stairlock)
|
|
return false;
|
|
|
|
fixed_t floorheight = P_FloorHeight(sec);
|
|
|
|
// new floor thinker
|
|
DFloor* floor = new DFloor(sec);
|
|
P_AddMovingFloor(sec);
|
|
|
|
floor->m_Direction = Dirn ? 1 : -1;
|
|
floor->m_OrgHeight = floorheight; // [RH] Height to reset to
|
|
floor->m_PauseTime = 0;
|
|
|
|
// setup speed of stair building
|
|
switch (Sped)
|
|
{
|
|
default:
|
|
case SpeedSlow:
|
|
floor->m_Speed = FLOORSPEED / 4;
|
|
break;
|
|
case SpeedNormal:
|
|
floor->m_Speed = FLOORSPEED / 2;
|
|
break;
|
|
case SpeedFast:
|
|
floor->m_Speed = FLOORSPEED * 2;
|
|
break;
|
|
case SpeedTurbo:
|
|
floor->m_Speed = FLOORSPEED * 4;
|
|
break;
|
|
}
|
|
|
|
// setup stepsize for stairs
|
|
fixed_t stairsize;
|
|
switch (Step)
|
|
{
|
|
case StepSize4:
|
|
stairsize = 4 * FRACUNIT;
|
|
break;
|
|
case StepSize8:
|
|
stairsize = 8 * FRACUNIT;
|
|
break;
|
|
case StepSize16:
|
|
stairsize = 16 * FRACUNIT;
|
|
break;
|
|
case StepSize24:
|
|
stairsize = 24 * FRACUNIT;
|
|
break;
|
|
}
|
|
|
|
const fixed_t speed = floor->m_Speed;
|
|
int height = sec->floorheight + floor->m_Direction * stairsize;
|
|
floor->m_FloorDestHeight = height;
|
|
const int texture = sec->floorpic;
|
|
floor->m_Crush = NO_CRUSH;
|
|
floor->m_Type = DFloor::genBuildStair; // jff 3/31/98 do not leave uninited
|
|
|
|
sec->stairlock = -2;
|
|
sec->nextsec = -1;
|
|
sec->prevsec = -1;
|
|
|
|
// Find next sector to raise
|
|
// 1. Find 2-sided line with same sector side[0] (lowest numbered)
|
|
// 2. Other side is the next sector to raise
|
|
// 3. Unless already moving, or different texture, then stop building
|
|
bool ok = false;
|
|
sector_t* tsec = nullptr;
|
|
int newsecnum = 0;
|
|
do
|
|
{
|
|
ok = false;
|
|
for (const line_t* secline : sec->getLines())
|
|
{
|
|
if (!(secline->backsector))
|
|
continue;
|
|
|
|
tsec = secline->frontsector;
|
|
newsecnum = tsec - sectors;
|
|
|
|
if (secnum != newsecnum)
|
|
continue;
|
|
|
|
tsec = secline->backsector;
|
|
newsecnum = tsec - sectors;
|
|
|
|
if (!Igno && tsec->floorpic != texture)
|
|
continue;
|
|
|
|
/* jff 6/19/98 prevent double stepsize */
|
|
//if (compatibility_level < boom_202_compatibility)
|
|
//height += floor->m_Direction * stairsize;
|
|
|
|
// if sector's floor already moving, look for another
|
|
// jff 2/26/98 special lockout condition for retriggering
|
|
if (tsec->floordata || tsec->stairlock)
|
|
continue;
|
|
|
|
height += floor->m_Direction * stairsize;
|
|
|
|
ok = true;
|
|
break;
|
|
}
|
|
|
|
if (ok)
|
|
{
|
|
// jff 2/26/98
|
|
// link the stair chain in both directions
|
|
// lock the stair sector until building complete
|
|
sec->nextsec = newsecnum; // link step to next
|
|
tsec->prevsec = secnum; // link next back
|
|
tsec->nextsec = -1; // set next forward link as end
|
|
tsec->stairlock = -2; // lock the step
|
|
|
|
sec = tsec;
|
|
secnum = newsecnum;
|
|
|
|
// create and initialize a thinker for the next step
|
|
floor = new DFloor(sec);
|
|
P_AddMovingFloor(sec);
|
|
|
|
floor->PlayFloorSound();
|
|
floor->m_Direction = Dirn ? 1 : -1;
|
|
floor->m_FloorDestHeight = height;
|
|
// [RH] Set up delay values
|
|
floor->m_Delay = 0;
|
|
floor->m_PauseTime = 0;
|
|
|
|
floor->m_Speed = speed;
|
|
floor->m_Type =
|
|
DFloor::genBuildStair; // jff 3/31/98 do not leave uninited
|
|
// jff 2/27/98 fix uninitialized crush field
|
|
floor->m_Crush = NO_CRUSH;
|
|
floor->m_ResetCount = 0; // [RH] Tics until reset (0 if never)
|
|
floor->m_OrgHeight = floorheight; // [RH] Height to reset to
|
|
}
|
|
} while (ok);
|
|
|
|
line.special ^= StairDirection; // alternate dir on succ activations
|
|
|
|
return true;
|
|
};
|
|
|
|
// check if a manual trigger, if so do just the sector on the backside
|
|
if (Trig == PushOnce || Trig == PushMany || line.id == 0)
|
|
{
|
|
if (!line.backsector)
|
|
return false;
|
|
|
|
return helper(line.backsector, line.backsector - sectors);
|
|
}
|
|
|
|
bool rtn = false;
|
|
int secnum = -1;
|
|
while ((secnum = P_FindSectorFromTag(line.id, secnum)) >= 0)
|
|
{
|
|
rtn |= helper(§ors[secnum], secnum);
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
//
|
|
// BUILD A STAIRCASE!
|
|
// [RH] Added stairsize, srcspeed, delay, reset, igntxt, usespecials parameters
|
|
// If usespecials is non-zero, then each sector in a stair is determined
|
|
// by its special. If usespecials is 2, each sector stays in "sync" with
|
|
// the others.
|
|
//
|
|
bool EV_BuildStairs(const int tag, const DFloor::EStair type, const line_t *line,
|
|
const fixed_t stairsize, const fixed_t speed, const int delay,
|
|
const int reset, int igntxt, const int usespecials)
|
|
{
|
|
sector_t* prev = nullptr;
|
|
|
|
if (speed == 0)
|
|
return false;
|
|
|
|
const int persteptime = FixedDiv (stairsize, speed) >> FRACBITS;
|
|
|
|
const auto helper = [&](sector_t* sec, int secnum) -> bool
|
|
{
|
|
if (sec->floordata || sec->stairlock)
|
|
return false;
|
|
|
|
const fixed_t floorheight = P_FloorHeight(sec);
|
|
|
|
const auto spawnStairFloor = [&]() -> DFloor*
|
|
{
|
|
// new floor thinker
|
|
DFloor* floor = new DFloor(sec);
|
|
P_AddMovingFloor(sec);
|
|
|
|
floor->m_Direction = (type == DFloor::buildUp) ? 1 : -1;
|
|
floor->m_Type = DFloor::buildStair; //jff 3/31/98 do not leave uninited
|
|
// [RH] Set up delay values
|
|
floor->m_Delay = delay;
|
|
floor->m_PauseTime = 0;
|
|
floor->m_StepTime = floor->m_PerStepTime = persteptime;
|
|
//jff 2/27/98 fix uninitialized crush field
|
|
floor->m_Crush =
|
|
(!usespecials && speed == 4 * FRACUNIT) ? DOOM_CRUSH : NO_CRUSH;
|
|
floor->m_ResetCount = reset; // [RH] Tics until reset (0 if never)
|
|
floor->m_OrgHeight = floorheight; // [RH] Height to reset to
|
|
|
|
return floor;
|
|
};
|
|
|
|
// new floor thinker
|
|
DFloor* floor = spawnStairFloor();
|
|
|
|
floor->m_Speed = speed;
|
|
int height = floorheight + stairsize * floor->m_Direction;
|
|
floor->m_FloorDestHeight = height;
|
|
|
|
const int texture = sec->floorpic;
|
|
|
|
// Find next sector to raise
|
|
// 1. Find 2-sided line with same sector side[0] (lowest numbered)
|
|
// 2. Other side is the next sector to raise
|
|
// 3. Unless already moving, or different texture, then stop building
|
|
bool ok = false;
|
|
int newsecnum = 0;
|
|
sector_t* tsec = nullptr;
|
|
do
|
|
{
|
|
ok = false;
|
|
|
|
if (usespecials)
|
|
{
|
|
// [RH] Find the next sector by scanning for Stairs_Special?
|
|
tsec = P_NextSpecialSector (sec,
|
|
(sec->special & 0xff) == Stairs_Special1 ?
|
|
Stairs_Special2 : Stairs_Special1, prev);
|
|
|
|
if ( (ok = (tsec != nullptr)) )
|
|
{
|
|
height += floor->m_Direction * stairsize;
|
|
|
|
// if sector's floor already moving, look for another
|
|
//jff 2/26/98 special lockout condition for retriggering
|
|
if (tsec->floordata || tsec->stairlock)
|
|
{
|
|
prev = sec;
|
|
sec = tsec;
|
|
continue;
|
|
}
|
|
}
|
|
newsecnum = tsec - sectors;
|
|
}
|
|
else
|
|
{
|
|
for (const line_t* secline : sec->getLines())
|
|
{
|
|
if (!(secline->flags & ML_TWOSIDED))
|
|
continue;
|
|
|
|
tsec = secline->frontsector;
|
|
if (!tsec) continue; //jff 5/7/98 if no backside, continue
|
|
newsecnum = tsec-sectors;
|
|
|
|
if (secnum != newsecnum)
|
|
continue;
|
|
|
|
tsec = secline->backsector;
|
|
newsecnum = tsec - sectors;
|
|
|
|
if (!igntxt && tsec->floorpic != texture)
|
|
continue;
|
|
|
|
height += floor->m_Direction * stairsize;
|
|
|
|
// if sector's floor already moving, look for another
|
|
//jff 2/26/98 special lockout condition for retriggering
|
|
if (tsec->floordata || tsec->stairlock)
|
|
continue;
|
|
|
|
ok = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ok)
|
|
{
|
|
// jff 2/26/98
|
|
// link the stair chain in both directions
|
|
// lock the stair sector until building complete
|
|
sec->nextsec = newsecnum; // link step to next
|
|
tsec->prevsec = secnum; // link next back
|
|
tsec->nextsec = -1; // set next forward link as end
|
|
tsec->stairlock = -2; // lock the step
|
|
|
|
prev = sec;
|
|
sec = tsec;
|
|
secnum = newsecnum;
|
|
|
|
// create and initialize a thinker for the next step
|
|
floor = spawnStairFloor();
|
|
|
|
floor->PlayFloorSound();
|
|
floor->m_FloorDestHeight = height;
|
|
|
|
if (usespecials == 2)
|
|
{
|
|
// [RH]
|
|
fixed_t rise = (floor->m_FloorDestHeight - floorheight)
|
|
* floor->m_Direction;
|
|
floor->m_Speed = FixedDiv (FixedMul (speed, rise), stairsize);
|
|
}
|
|
else
|
|
{
|
|
floor->m_Speed = speed;
|
|
}
|
|
}
|
|
} while(ok);
|
|
|
|
return true;
|
|
};
|
|
|
|
// check if a manual trigger, if so do just the sector on the backside
|
|
if (tag == 0)
|
|
{
|
|
if (!line || !line->backsector)
|
|
return false;
|
|
|
|
return helper(line->backsector, line->backsector - sectors);
|
|
}
|
|
|
|
bool rtn = false;
|
|
int secnum = -1;
|
|
while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0)
|
|
{
|
|
rtn |= helper(§ors[secnum], secnum);
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
bool P_SpawnDonut(int, line_t*, fixed_t, fixed_t);
|
|
|
|
bool EV_DoZDoomDonut(int tag, line_t* line, fixed_t pillarspeed, fixed_t slimespeed)
|
|
{
|
|
return P_SpawnDonut(line->id, line, pillarspeed, slimespeed);
|
|
}
|
|
|
|
bool EV_DoDonut(line_t* line)
|
|
{
|
|
return P_SpawnDonut(line->id, line, FLOORSPEED / 2, FLOORSPEED / 2);
|
|
}
|
|
|
|
// [RH] Added pillarspeed and slimespeed parameters
|
|
bool P_SpawnDonut(int tag, line_t* line, fixed_t pillarspeed, fixed_t slimespeed)
|
|
{
|
|
int secnum = -1;
|
|
bool rtn = false;
|
|
while ((secnum = P_FindSectorFromTag(tag,secnum)) >= 0)
|
|
{
|
|
sector_t* s1 = §ors[secnum]; // s1 is pillar's sector
|
|
|
|
// ALREADY MOVING? IF SO, KEEP GOING...
|
|
if (s1->floordata)
|
|
continue;
|
|
|
|
rtn = true;
|
|
sector_t* s2 = getNextSector (s1->lines[0], s1); // s2 is pool's sector
|
|
if (!s2) // note lowest numbered line around
|
|
continue; // pillar must be two-sided
|
|
|
|
for (int i = 0; i < s2->linecount; i++)
|
|
{
|
|
if (!(s2->lines[i]->flags & ML_TWOSIDED) ||
|
|
(s2->lines[i]->backsector == s1))
|
|
continue;
|
|
sector_t* s3 = s2->lines[i]->backsector;
|
|
|
|
// Spawn rising slime
|
|
DFloor* floor = new DFloor (s2);
|
|
P_AddMovingFloor(s2);
|
|
|
|
newspecial_s newspec;
|
|
P_ResetTransferSpecial(&newspec);
|
|
|
|
floor->m_Type = DFloor::donutRaise;
|
|
floor->m_Crush = false;
|
|
floor->m_HexenCrush = false;
|
|
floor->m_Direction = 1;
|
|
floor->m_Sector = s2;
|
|
floor->m_Speed = slimespeed;
|
|
floor->m_Texture = s3->floorpic;
|
|
floor->m_NewSpecial = newspec.special;
|
|
floor->m_NewDamageRate = newspec.damageamount;
|
|
floor->m_NewFlags = newspec.flags;
|
|
floor->m_NewDmgInterval = newspec.damageinterval;
|
|
floor->m_NewLeakRate = newspec.damageleakrate;
|
|
floor->m_FloorDestHeight = P_FloorHeight(s3);
|
|
floor->m_Line = NULL;
|
|
floor->PlayFloorSound();
|
|
|
|
// Spawn lowering donut-hole
|
|
floor = new DFloor (s1);
|
|
P_AddMovingFloor(s1);
|
|
|
|
floor->m_Type = DFloor::floorLowerToNearest;
|
|
floor->m_Crush = false;
|
|
floor->m_HexenCrush = false;
|
|
floor->m_Direction = -1;
|
|
floor->m_Sector = s1;
|
|
floor->m_Speed = pillarspeed;
|
|
floor->m_FloorDestHeight = P_FloorHeight(s3);
|
|
floor->m_Line = NULL;
|
|
floor->PlayFloorSound();
|
|
break;
|
|
}
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
// ============================================================================
|
|
// ELEVATORS
|
|
// ============================================================================
|
|
|
|
void P_SetElevatorDestroy(DElevator *elevator)
|
|
{
|
|
if (!elevator || predicting)
|
|
return;
|
|
|
|
elevator->m_Status = DElevator::destroy;
|
|
|
|
if (clientside && elevator->m_Sector)
|
|
{
|
|
elevator->m_Sector->ceilingdata = NULL;
|
|
elevator->m_Sector->floordata = NULL;
|
|
elevator->Destroy();
|
|
}
|
|
}
|
|
|
|
IMPLEMENT_SERIAL (DElevator, DMover)
|
|
|
|
void DElevator::Serialize (FArchive &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
if (arc.IsStoring ())
|
|
{
|
|
arc << m_Type
|
|
<< m_Status
|
|
<< m_Direction
|
|
<< m_FloorDestHeight
|
|
<< m_CeilingDestHeight
|
|
<< m_Speed;
|
|
}
|
|
else
|
|
{
|
|
arc >> m_Type
|
|
>> m_Status
|
|
>> m_Direction
|
|
>> m_FloorDestHeight
|
|
>> m_CeilingDestHeight
|
|
>> m_Speed;
|
|
}
|
|
}
|
|
|
|
void DElevator::PlayElevatorSound()
|
|
{
|
|
if (predicting || !m_Sector)
|
|
return;
|
|
|
|
S_StopSound(m_Sector->soundorg);
|
|
|
|
if (m_Status == init)
|
|
S_LoopedSound(m_Sector->soundorg, CHAN_BODY, "plats/pt1_mid", 1, ATTN_NORM);
|
|
else
|
|
S_Sound(m_Sector->soundorg, CHAN_BODY, "plats/pt1_stop", 1, ATTN_NORM);
|
|
}
|
|
|
|
//
|
|
// T_MoveElevator()
|
|
//
|
|
// Move an elevator to it's destination (up or down)
|
|
// Called once per tick for each moving floor.
|
|
//
|
|
// Passed an elevator_t structure that contains all pertinent info about the
|
|
// move. See P_SPEC.H for fields.
|
|
// No return.
|
|
//
|
|
// jff 02/22/98 added to support parallel floor/ceiling motion
|
|
//
|
|
void DElevator::RunThink ()
|
|
{
|
|
if (m_Status == destroy)
|
|
return;
|
|
|
|
EResult res;
|
|
|
|
if (m_Direction < 0) // moving down
|
|
{
|
|
res = MoveCeiling (m_Speed, m_CeilingDestHeight, m_Direction);
|
|
if (res == ok || res == pastdest)
|
|
MoveFloor (m_Speed, m_FloorDestHeight, m_Direction);
|
|
}
|
|
else // up
|
|
{
|
|
res = MoveFloor (m_Speed, m_FloorDestHeight, m_Direction);
|
|
if (res == ok || res == pastdest)
|
|
MoveCeiling (m_Speed, m_CeilingDestHeight, m_Direction);
|
|
}
|
|
|
|
if (res == pastdest) // if destination height acheived
|
|
{
|
|
m_Status = finished;
|
|
// make floor stop sound
|
|
PlayElevatorSound();
|
|
|
|
P_SetElevatorDestroy(this);
|
|
}
|
|
}
|
|
|
|
DElevator::DElevator (sector_t *sec)
|
|
: Super (sec)
|
|
{
|
|
}
|
|
|
|
// Clones a DElevator and returns a pointer to that clone.
|
|
//
|
|
// The caller owns the pointer, and it must be deleted with `delete`.
|
|
DElevator* DElevator::Clone(sector_t* sec) const
|
|
{
|
|
DElevator* ele = new DElevator(*this);
|
|
|
|
ele->Orphan();
|
|
ele->m_Sector = sec;
|
|
|
|
return ele;
|
|
}
|
|
|
|
static bool SpawnCommonElevator(const line_t* line, DElevator::EElevator type, fixed_t speed,
|
|
fixed_t height, int tag)
|
|
{
|
|
if (!line && (type == DElevator::elevateCurrent))
|
|
return false;
|
|
|
|
int secnum = -1;
|
|
bool rtn = false;
|
|
// act on all sectors with the same tag as the triggering linedef
|
|
while ((secnum = P_FindSectorFromTag(tag, secnum)) >= 0)
|
|
{
|
|
sector_t* sec = §ors[secnum];
|
|
|
|
// If either floor or ceiling is already activated, skip it
|
|
if (sec->ceilingdata && P_MovingCeilingCompleted(sec))
|
|
{
|
|
sec->ceilingdata->Destroy();
|
|
sec->ceilingdata = NULL;
|
|
}
|
|
if (sec->floordata && P_MovingFloorCompleted(sec))
|
|
{
|
|
sec->floordata->Destroy();
|
|
sec->floordata = NULL;
|
|
}
|
|
|
|
if (sec->floordata || sec->ceilingdata) // jff 2/22/98
|
|
continue;
|
|
|
|
const fixed_t floorheight = P_FloorHeight(sec);
|
|
const fixed_t ceilingheight = P_CeilingHeight(sec);
|
|
|
|
// create and initialize new elevator thinker
|
|
rtn = true;
|
|
DElevator* elevator = new DElevator(sec);
|
|
|
|
// [SL] 2012-04-19 - Elevators have both moving ceilings and floors.
|
|
// Consider them as moving ceilings for consistency sake.
|
|
P_AddMovingCeiling(sec);
|
|
|
|
elevator->m_Type = type;
|
|
elevator->m_Speed = speed;
|
|
elevator->m_Status = DElevator::init;
|
|
elevator->PlayElevatorSound();
|
|
|
|
sec->floordata = sec->ceilingdata = elevator;
|
|
|
|
// set up the fields according to the type of elevator action
|
|
switch (type)
|
|
{
|
|
// elevator down to next floor
|
|
case DElevator::elevateDown:
|
|
elevator->m_Direction = -1;
|
|
elevator->m_FloorDestHeight = P_FindNextLowestFloor(sec);
|
|
elevator->m_CeilingDestHeight =
|
|
elevator->m_FloorDestHeight + ceilingheight - floorheight;
|
|
break;
|
|
|
|
// elevator up to next floor
|
|
case DElevator::elevateUp:
|
|
elevator->m_Direction = 1;
|
|
elevator->m_FloorDestHeight = P_FindNextHighestFloor(sec);
|
|
elevator->m_CeilingDestHeight =
|
|
elevator->m_FloorDestHeight + ceilingheight - floorheight;
|
|
break;
|
|
|
|
// elevator to floor height of activating switch's front sector
|
|
case DElevator::elevateCurrent:
|
|
elevator->m_FloorDestHeight = P_FloorHeight(line->frontsector);
|
|
elevator->m_CeilingDestHeight =
|
|
elevator->m_FloorDestHeight + ceilingheight - floorheight;
|
|
elevator->m_Direction = elevator->m_FloorDestHeight > floorheight ? 1 : -1;
|
|
break;
|
|
|
|
// [RH] elevate up by a specific amount
|
|
case DElevator::elevateRaise:
|
|
elevator->m_Direction = 1;
|
|
elevator->m_FloorDestHeight = floorheight + height;
|
|
elevator->m_CeilingDestHeight = ceilingheight + height;
|
|
break;
|
|
|
|
// [RH] elevate down by a specific amount
|
|
case DElevator::elevateLower:
|
|
elevator->m_Direction = -1;
|
|
elevator->m_FloorDestHeight = floorheight - height;
|
|
elevator->m_CeilingDestHeight = ceilingheight - height;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rtn;
|
|
}
|
|
|
|
//
|
|
// EV_DoElevator
|
|
//
|
|
// Handle elevator linedef types
|
|
//
|
|
// Passed the linedef that triggered the elevator and the elevator action
|
|
//
|
|
// jff 2/22/98 new type to move floor and ceiling in parallel
|
|
// [RH] Added speed, tag, and height parameters and new types.
|
|
//
|
|
bool EV_DoElevator (const line_t *line, DElevator::EElevator elevtype,
|
|
fixed_t speed, fixed_t height, int tag)
|
|
{
|
|
return SpawnCommonElevator(line, elevtype, speed, height, tag);
|
|
}
|
|
|
|
// Almost identical to the above, but height is multiplied by FRACUNIT
|
|
bool EV_DoZDoomElevator(const line_t* line, DElevator::EElevator type, fixed_t speed,
|
|
fixed_t height, int tag)
|
|
{
|
|
height *= FRACUNIT;
|
|
return SpawnCommonElevator(line, type, speed, height, tag);
|
|
}
|
|
|
|
///////////////////////////////////////
|
|
/// Waggle
|
|
///////////////////////////////////////
|
|
|
|
static constexpr fixed_t FloatBobOffsets[64] = {
|
|
0, 51389, 102283, 152192, 200636, 247147, 291278, 332604,
|
|
370727, 405280, 435929, 462380, 484378, 501712, 514213, 521763,
|
|
524287, 521763, 514213, 501712, 484378, 462380, 435929, 405280,
|
|
370727, 332604, 291278, 247147, 200636, 152192, 102283, 51389,
|
|
-1, -51390, -102284, -152193, -200637, -247148, -291279, -332605,
|
|
-370728, -405281, -435930, -462381, -484380, -501713, -514215, -521764,
|
|
-524288, -521764, -514214, -501713, -484379, -462381, -435930, -405280,
|
|
-370728, -332605, -291279, -247148, -200637, -152193, -102284, -51389};
|
|
/*
|
|
bool EV_StartPlaneWaggle(int tag, line_t* line, int height, int speed, int offset,
|
|
int timer, bool ceiling)
|
|
{
|
|
int sectorIndex;
|
|
sector_t* sector;
|
|
bool retCode;
|
|
|
|
retCode = false;
|
|
sectorIndex = -1;
|
|
while ((sectorIndex = P_FindSectorFromTagOrLine(tag, line, sectorIndex)) >= 0)
|
|
{
|
|
sector = §ors[sectorIndex];
|
|
if (ceiling ? P_CeilingActive(sector) : P_FloorActive(sector))
|
|
{ // Already busy with another thinker
|
|
continue;
|
|
}
|
|
retCode = true;
|
|
new DWaggle(sector, height, speed, offset, timer, ceiling);
|
|
|
|
if (ceiling)
|
|
{
|
|
P_AddMovingCeiling(sector);
|
|
}
|
|
else
|
|
{
|
|
P_AddMovingFloor(sector);
|
|
}
|
|
}
|
|
|
|
return retCode;
|
|
}
|
|
|
|
IMPLEMENT_SERIAL(DWaggle, DMover)
|
|
|
|
void DWaggle::Serialize(FArchive& arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
if (arc.IsStoring ())
|
|
{
|
|
arc << m_OriginalHeight
|
|
<< m_Accumulator
|
|
<< m_AccDelta
|
|
<< m_TargetScale
|
|
<< m_Scale
|
|
<< m_ScaleDelta
|
|
<< m_Ticker
|
|
<< m_State
|
|
<< m_Ceiling
|
|
<< m_StartTic;
|
|
}
|
|
else
|
|
{
|
|
arc >> m_OriginalHeight
|
|
>> m_Accumulator
|
|
>> m_AccDelta
|
|
>> m_TargetScale
|
|
>> m_Scale
|
|
>> m_ScaleDelta
|
|
>> m_Ticker
|
|
>> m_State
|
|
>> m_Ceiling
|
|
>> m_StartTic;
|
|
}
|
|
}
|
|
|
|
DWaggle::DWaggle()
|
|
{
|
|
|
|
}
|
|
|
|
DWaggle::DWaggle(sector_t* sector, int height, int speed, int offset, int timer,
|
|
bool ceiling)
|
|
{
|
|
if (ceiling)
|
|
{
|
|
sector->ceilingdata = this;
|
|
m_OriginalHeight = sector->ceilingheight;
|
|
}
|
|
else
|
|
{
|
|
sector->floordata = this;
|
|
m_OriginalHeight = sector->floorheight;
|
|
}
|
|
m_Sector = sector;
|
|
m_Accumulator = offset * FRACUNIT;
|
|
m_AccDelta = speed << 10;
|
|
m_Scale = 0;
|
|
m_TargetScale = height << 10;
|
|
m_ScaleDelta = m_TargetScale / (35 + ((3 * 35) * height) / 255);
|
|
m_Ticker = timer ? timer * 35 : -1;
|
|
m_State = init;
|
|
m_Ceiling = ceiling;
|
|
m_StartTic = ::level.time; // [Blair] Used for client side synchronization
|
|
}
|
|
|
|
void DWaggle::RunThink()
|
|
{
|
|
switch (m_State)
|
|
{
|
|
case finished:
|
|
Destroy();
|
|
m_State = destroy;
|
|
case destroy:
|
|
return;
|
|
break;
|
|
case init:
|
|
m_State = expand;
|
|
// fall thru
|
|
case expand:
|
|
if ((m_Scale += m_ScaleDelta) >= m_TargetScale)
|
|
{
|
|
m_Scale = m_TargetScale;
|
|
m_State = stable;
|
|
}
|
|
break;
|
|
case reduce:
|
|
if ((m_Scale -= m_ScaleDelta) <= 0)
|
|
{ // Remove
|
|
if (m_Ceiling)
|
|
{
|
|
P_SetCeilingHeight(m_Sector, m_OriginalHeight);
|
|
}
|
|
else
|
|
{
|
|
P_SetFloorHeight(m_Sector, m_OriginalHeight);
|
|
}
|
|
P_ChangeSector(m_Sector, DOOM_CRUSH);
|
|
m_State = finished;
|
|
return;
|
|
}
|
|
break;
|
|
case stable:
|
|
if (m_Ticker != -1)
|
|
{
|
|
if (!--m_Ticker)
|
|
{
|
|
m_State = reduce;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
m_Accumulator += m_AccDelta;
|
|
fixed_t changeamount = m_OriginalHeight + FixedMul(FloatBobOffsets[(m_Accumulator >> FRACBITS) & 63], m_Scale);
|
|
|
|
if (m_Ceiling)
|
|
{
|
|
P_SetCeilingHeight(m_Sector, changeamount);
|
|
}
|
|
else
|
|
{
|
|
P_SetFloorHeight(m_Sector, changeamount);
|
|
}
|
|
P_ChangeSector(m_Sector, DOOM_CRUSH);
|
|
}
|
|
|
|
DWaggle::DWaggle(sector_t* sec) : Super(sec) { }
|
|
|
|
// Clones a DWaggle and returns a pointer to that clone.
|
|
//
|
|
// The caller owns the pointer, and it must be deleted with `delete`.
|
|
DWaggle* DWaggle::Clone(sector_t* sec) const
|
|
{
|
|
DWaggle* ele = new DWaggle(*this);
|
|
|
|
ele->Orphan();
|
|
ele->m_Sector = sec;
|
|
|
|
return ele;
|
|
}
|
|
*/
|
|
VERSION_CONTROL (p_floor_cpp, "$Id$")
|