og-odamex/common/p_floor.cpp

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 = &sectors[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 = &sectors[sec->nextsec];
if (sec->nextsec == -1) // if all thinkers ahead are done too
{
while (sec->prevsec != -1) // clear all locks
{
sec->stairlock = 0;
sec = &sectors[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(&sectors[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(&sectors[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(&sectors[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 = &sectors[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(&sectors[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(&sectors[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 = &sectors[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 = &sectors[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 = &sectors[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$")