2016-03-01 15:47:10 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
2017-04-17 11:33:19 +00:00
|
|
|
// Copyright 1993-1996 id Software
|
|
|
|
// Copyright 1999-2016 Randy Heit
|
|
|
|
// Copyright 2002-2016 Christoph Oelckers
|
2016-03-01 15:47:10 +00:00
|
|
|
//
|
2017-04-17 11:33:19 +00:00
|
|
|
// 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 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
2016-03-01 15:47:10 +00:00
|
|
|
//
|
2017-04-17 11:33:19 +00:00
|
|
|
// This program is distributed in the hope that it will be useful,
|
2016-03-01 15:47:10 +00:00
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2017-04-17 11:33:19 +00:00
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see http://www.gnu.org/licenses/
|
2016-03-01 15:47:10 +00:00
|
|
|
//
|
2017-04-17 11:33:19 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
2016-03-01 15:47:10 +00:00
|
|
|
//
|
|
|
|
// DESCRIPTION: Door animation code (opening/closing)
|
|
|
|
// [RH] Removed sliding door code and simplified for Hexen-ish specials
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "doomdef.h"
|
|
|
|
#include "p_local.h"
|
|
|
|
#include "s_sound.h"
|
|
|
|
#include "s_sndseq.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "r_state.h"
|
|
|
|
#include "c_console.h"
|
|
|
|
#include "gi.h"
|
|
|
|
#include "a_keys.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "sc_man.h"
|
|
|
|
#include "cmdlib.h"
|
2016-09-19 17:14:30 +00:00
|
|
|
#include "serializer.h"
|
2016-03-01 15:47:10 +00:00
|
|
|
#include "d_player.h"
|
|
|
|
#include "p_spec.h"
|
2017-01-08 17:45:30 +00:00
|
|
|
#include "g_levellocals.h"
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// VERTICAL DOORS
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
2016-11-24 20:36:02 +00:00
|
|
|
IMPLEMENT_CLASS(DDoor, false, false)
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
DDoor::DDoor ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-09-19 17:14:30 +00:00
|
|
|
void DDoor::Serialize(FSerializer &arc)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
Super::Serialize (arc);
|
2016-09-19 17:14:30 +00:00
|
|
|
arc.Enum("type", m_Type)
|
|
|
|
("topdist", m_TopDist)
|
|
|
|
("botspot", m_BotSpot)
|
|
|
|
("botdist", m_BotDist)
|
|
|
|
("oldfloordist", m_OldFloorDist)
|
|
|
|
("speed", m_Speed)
|
|
|
|
("direction", m_Direction)
|
|
|
|
("topwait", m_TopWait)
|
|
|
|
("topcountdown", m_TopCountdown)
|
|
|
|
("lighttag", m_LightTag);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// T_VerticalDoor
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
void DDoor::Tick ()
|
|
|
|
{
|
2016-04-08 11:59:03 +00:00
|
|
|
EMoveResult res;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-30 07:41:46 +00:00
|
|
|
// Adjust bottom height - but only if there isn't an active lift attached to the floor.
|
|
|
|
if (m_Sector->floorplane.fD() != m_OldFloorDist)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
if (!m_Sector->floordata || !m_Sector->floordata->IsKindOf(RUNTIME_CLASS(DPlat)) ||
|
|
|
|
!(barrier_cast<DPlat*>(m_Sector->floordata))->IsLift())
|
|
|
|
{
|
2016-03-30 07:41:46 +00:00
|
|
|
m_OldFloorDist = m_Sector->floorplane.fD();
|
|
|
|
m_BotDist = m_Sector->ceilingplane.PointToDist (m_BotSpot, m_Sector->floorplane.ZatPoint (m_BotSpot));
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (m_Direction)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
// WAITING
|
|
|
|
if (!--m_TopCountdown)
|
|
|
|
{
|
|
|
|
switch (m_Type)
|
|
|
|
{
|
|
|
|
case doorRaise:
|
|
|
|
m_Direction = -1; // time to go back down
|
|
|
|
DoorSound (false);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case doorCloseWaitOpen:
|
|
|
|
m_Direction = 1;
|
|
|
|
DoorSound (true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
// INITIAL WAIT
|
|
|
|
if (!--m_TopCountdown)
|
|
|
|
{
|
|
|
|
switch (m_Type)
|
|
|
|
{
|
|
|
|
case doorWaitRaise:
|
|
|
|
m_Direction = 1;
|
|
|
|
m_Type = doorRaise;
|
|
|
|
DoorSound (true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case -1:
|
|
|
|
// DOWN
|
2016-04-08 12:18:46 +00:00
|
|
|
res = m_Sector->MoveCeiling (m_Speed, m_BotDist, -1, m_Direction, false);
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
// killough 10/98: implement gradual lighting effects
|
2016-03-30 07:41:46 +00:00
|
|
|
if (m_LightTag != 0 && m_TopDist != -m_Sector->floorplane.fD())
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-26 22:19:38 +00:00
|
|
|
EV_LightTurnOnPartway (m_LightTag,
|
2016-03-30 07:41:46 +00:00
|
|
|
(m_Sector->ceilingplane.fD() + m_Sector->floorplane.fD()) / (m_TopDist + m_Sector->floorplane.fD()));
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
2016-04-08 11:59:03 +00:00
|
|
|
if (res == EMoveResult::pastdest)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
SN_StopSequence (m_Sector, CHAN_CEILING);
|
|
|
|
switch (m_Type)
|
|
|
|
{
|
|
|
|
case doorRaise:
|
|
|
|
case doorClose:
|
|
|
|
m_Sector->ceilingdata = NULL; //jff 2/22/98
|
|
|
|
Destroy (); // unlink and free
|
|
|
|
break;
|
|
|
|
|
|
|
|
case doorCloseWaitOpen:
|
|
|
|
m_Direction = 0;
|
|
|
|
m_TopCountdown = m_TopWait;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-04-08 11:59:03 +00:00
|
|
|
else if (res == EMoveResult::crushed)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
switch (m_Type)
|
|
|
|
{
|
|
|
|
case doorClose: // DO NOT GO BACK UP!
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
m_Direction = 1;
|
|
|
|
DoorSound (true);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
// UP
|
2016-04-08 12:18:46 +00:00
|
|
|
res = m_Sector->MoveCeiling (m_Speed, m_TopDist, -1, m_Direction, false);
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
// killough 10/98: implement gradual lighting effects
|
2016-03-30 07:41:46 +00:00
|
|
|
if (m_LightTag != 0 && m_TopDist != -m_Sector->floorplane.fD())
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-26 22:19:38 +00:00
|
|
|
EV_LightTurnOnPartway (m_LightTag,
|
2016-03-30 07:41:46 +00:00
|
|
|
(m_Sector->ceilingplane.fD() + m_Sector->floorplane.fD()) / (m_TopDist + m_Sector->floorplane.fD()));
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
2016-04-08 11:59:03 +00:00
|
|
|
if (res == EMoveResult::pastdest)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
SN_StopSequence (m_Sector, CHAN_CEILING);
|
|
|
|
switch (m_Type)
|
|
|
|
{
|
|
|
|
case doorRaise:
|
|
|
|
m_Direction = 0; // wait at top
|
|
|
|
m_TopCountdown = m_TopWait;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case doorCloseWaitOpen:
|
|
|
|
case doorOpen:
|
|
|
|
m_Sector->ceilingdata = NULL; //jff 2/22/98
|
|
|
|
Destroy (); // unlink and free
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-04-08 11:59:03 +00:00
|
|
|
else if (res == EMoveResult::crushed)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
switch (m_Type)
|
|
|
|
{
|
|
|
|
case doorRaise:
|
|
|
|
case doorWaitRaise:
|
|
|
|
m_Direction = -1;
|
|
|
|
DoorSound(false);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// [RH] DoorSound: Plays door sound depending on direction and speed
|
|
|
|
//
|
|
|
|
// If curseq is non-NULL, then it will check if the desired sound sequence
|
|
|
|
// will result in a different command stream than the current one. If not,
|
|
|
|
// then it does nothing.
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
void DDoor::DoorSound(bool raise, DSeqNode *curseq) const
|
|
|
|
{
|
|
|
|
int choice;
|
|
|
|
|
|
|
|
// For multiple-selection sound sequences, the following choices are used:
|
|
|
|
// 0 Opening
|
|
|
|
// 1 Closing
|
|
|
|
// 2 Opening fast
|
|
|
|
// 3 Closing fast
|
|
|
|
|
|
|
|
choice = !raise;
|
|
|
|
|
|
|
|
if (m_Sector->Flags & SECF_SILENTMOVE) return;
|
|
|
|
|
2016-04-01 14:37:19 +00:00
|
|
|
if (m_Speed >= 8)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
choice += 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_Sector->seqType >= 0)
|
|
|
|
{
|
|
|
|
if (curseq == NULL || !SN_AreModesSame(m_Sector->seqType, SEQ_DOOR, choice, curseq->GetModeNum()))
|
|
|
|
{
|
|
|
|
SN_StartSequence(m_Sector, CHAN_CEILING, m_Sector->seqType, SEQ_DOOR, choice);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (m_Sector->SeqName != NAME_None)
|
|
|
|
{
|
|
|
|
if (curseq == NULL || !SN_AreModesSame(m_Sector->SeqName, choice, curseq->GetModeNum()))
|
|
|
|
{
|
|
|
|
SN_StartSequence(m_Sector, CHAN_CEILING, m_Sector->SeqName, choice);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const char *snd;
|
|
|
|
|
|
|
|
switch (gameinfo.gametype)
|
|
|
|
{
|
|
|
|
default: /* Doom and Hexen */
|
|
|
|
snd = "DoorNormal";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GAME_Heretic:
|
|
|
|
snd = "HereticDoor";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case GAME_Strife:
|
|
|
|
snd = "DoorSmallMetal";
|
|
|
|
|
|
|
|
// Search the front top textures of 2-sided lines on the door sector
|
|
|
|
// for a door sound to use.
|
2017-01-02 20:40:52 +00:00
|
|
|
for (auto line : m_Sector->Lines)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
const char *texname;
|
|
|
|
|
|
|
|
if (line->backsector == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
FTexture *tex = TexMan[line->sidedef[0]->GetTexture(side_t::top)];
|
|
|
|
texname = tex ? tex->Name.GetChars() : NULL;
|
|
|
|
if (texname != NULL && texname[0] == 'D' && texname[1] == 'O' && texname[2] == 'R')
|
|
|
|
{
|
|
|
|
switch (texname[3])
|
|
|
|
{
|
|
|
|
case 'S':
|
|
|
|
snd = "DoorStone";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'M':
|
|
|
|
if (texname[4] == 'L')
|
|
|
|
{
|
|
|
|
snd = "DoorLargeMetal";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'W':
|
|
|
|
if (texname[4] == 'L')
|
|
|
|
{
|
|
|
|
snd = "DoorLargeWood";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
snd = "DoorSmallWood";
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (curseq == NULL || !SN_AreModesSame(snd, choice, curseq->GetModeNum()))
|
|
|
|
{
|
|
|
|
SN_StartSequence(m_Sector, CHAN_CEILING, snd, choice);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DDoor::DDoor (sector_t *sector)
|
|
|
|
: DMovingCeiling (sector)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// [RH] SpawnDoor: Helper function for EV_DoDoor
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
2016-03-30 07:41:46 +00:00
|
|
|
DDoor::DDoor (sector_t *sec, EVlDoor type, double speed, int delay, int lightTag, int topcountdown)
|
2016-03-01 15:47:10 +00:00
|
|
|
: DMovingCeiling (sec),
|
|
|
|
m_Type (type), m_Speed (speed), m_TopWait (delay), m_TopCountdown(topcountdown), m_LightTag (lightTag)
|
|
|
|
{
|
|
|
|
vertex_t *spot;
|
2016-03-30 07:41:46 +00:00
|
|
|
double height;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
if (i_compatflags & COMPATF_NODOORLIGHT)
|
|
|
|
{
|
|
|
|
m_LightTag = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case doorClose:
|
|
|
|
m_Direction = -1;
|
|
|
|
height = sec->FindLowestCeilingSurrounding (&spot);
|
2016-03-30 07:41:46 +00:00
|
|
|
m_TopDist = sec->ceilingplane.PointToDist (spot, height - 4);
|
2016-03-01 15:47:10 +00:00
|
|
|
DoorSound (false);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case doorOpen:
|
|
|
|
case doorRaise:
|
|
|
|
m_Direction = 1;
|
|
|
|
height = sec->FindLowestCeilingSurrounding (&spot);
|
2016-03-30 07:41:46 +00:00
|
|
|
m_TopDist = sec->ceilingplane.PointToDist (spot, height - 4);
|
|
|
|
if (m_TopDist != sec->ceilingplane.fD())
|
2016-03-01 15:47:10 +00:00
|
|
|
DoorSound (true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case doorCloseWaitOpen:
|
2016-03-30 07:41:46 +00:00
|
|
|
m_TopDist = sec->ceilingplane.fD();
|
2016-03-01 15:47:10 +00:00
|
|
|
m_Direction = -1;
|
|
|
|
DoorSound (false);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case doorWaitRaise:
|
|
|
|
m_Direction = 2;
|
|
|
|
height = sec->FindLowestCeilingSurrounding (&spot);
|
2016-03-30 07:41:46 +00:00
|
|
|
m_TopDist = sec->ceilingplane.PointToDist (spot, height - 4);
|
2016-03-01 15:47:10 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case doorWaitClose:
|
|
|
|
m_Direction = 0;
|
|
|
|
m_Type = DDoor::doorRaise;
|
|
|
|
height = sec->FindHighestFloorPoint (&m_BotSpot);
|
|
|
|
m_BotDist = sec->ceilingplane.PointToDist (m_BotSpot, height);
|
2016-03-30 07:41:46 +00:00
|
|
|
m_OldFloorDist = sec->floorplane.fD();
|
|
|
|
m_TopDist = sec->ceilingplane.fD();
|
2016-03-01 15:47:10 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_Sector->floordata || !m_Sector->floordata->IsKindOf(RUNTIME_CLASS(DPlat)) ||
|
|
|
|
!(barrier_cast<DPlat*>(m_Sector->floordata))->IsLift())
|
|
|
|
{
|
|
|
|
height = sec->FindHighestFloorPoint (&m_BotSpot);
|
|
|
|
m_BotDist = sec->ceilingplane.PointToDist (m_BotSpot, height);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
height = sec->FindLowestCeilingPoint(&m_BotSpot);
|
|
|
|
m_BotDist = sec->ceilingplane.PointToDist (m_BotSpot, height);
|
|
|
|
}
|
2016-03-30 07:41:46 +00:00
|
|
|
m_OldFloorDist = sec->floorplane.fD();
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// [RH] Merged EV_VerticalDoor and EV_DoLockedDoor into EV_DoDoor
|
|
|
|
// and made them more general to support the new specials.
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
bool EV_DoDoor (DDoor::EVlDoor type, line_t *line, AActor *thing,
|
2016-03-30 07:41:46 +00:00
|
|
|
int tag, double speed, int delay, int lock, int lightTag, bool boomgen, int topcountdown)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
bool rtn = false;
|
|
|
|
int secnum;
|
|
|
|
sector_t* sec;
|
|
|
|
|
|
|
|
if (lock != 0 && !P_CheckKeys (thing, lock, tag != 0))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (tag == 0)
|
|
|
|
{ // [RH] manual door
|
|
|
|
if (!line)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// if the wrong side of door is pushed, give oof sound
|
|
|
|
if (line->sidedef[1] == NULL) // killough
|
|
|
|
{
|
|
|
|
S_Sound (thing, CHAN_VOICE, "*usefail", 1, ATTN_NORM);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the sector on the second side of activating linedef
|
|
|
|
sec = line->sidedef[1]->sector;
|
2017-01-07 18:32:24 +00:00
|
|
|
secnum = sec->sectornum;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
// if door already has a thinker, use it
|
|
|
|
if (sec->PlaneMoving(sector_t::ceiling))
|
|
|
|
{
|
|
|
|
// Boom used remote door logic for generalized doors, even if they are manual
|
|
|
|
if (boomgen)
|
|
|
|
return false;
|
|
|
|
if (sec->ceilingdata->IsKindOf (RUNTIME_CLASS(DDoor)))
|
|
|
|
{
|
|
|
|
DDoor *door = barrier_cast<DDoor *>(sec->ceilingdata);
|
|
|
|
|
|
|
|
// ONLY FOR "RAISE" DOORS, NOT "OPEN"s
|
|
|
|
if (door->m_Type == DDoor::doorRaise && type == DDoor::doorRaise)
|
|
|
|
{
|
|
|
|
if (door->m_Direction == -1)
|
|
|
|
{
|
|
|
|
door->m_Direction = 1; // go back up
|
|
|
|
door->DoorSound (true); // [RH] Make noise
|
|
|
|
}
|
|
|
|
else if (!(line->activation & (SPAC_Push|SPAC_MPush)))
|
|
|
|
// [RH] activate push doors don't go back down when you
|
|
|
|
// run into them (otherwise opening them would be
|
|
|
|
// a real pain).
|
|
|
|
{
|
|
|
|
if (!thing->player || thing->player->Bot != NULL)
|
|
|
|
return false; // JDC: bad guys never close doors
|
|
|
|
//Added by MC: Neither do bots.
|
|
|
|
|
|
|
|
door->m_Direction = -1; // start going down immediately
|
|
|
|
|
|
|
|
// Start the door close sequence.
|
|
|
|
door->DoorSound(false, SN_CheckSequence(sec, CHAN_CEILING));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-14 11:31:58 +00:00
|
|
|
if (Create<DDoor> (sec, type, speed, delay, lightTag, topcountdown))
|
2016-03-01 15:47:10 +00:00
|
|
|
rtn = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // [RH] Remote door
|
|
|
|
|
|
|
|
FSectorTagIterator it(tag);
|
|
|
|
while ((secnum = it.Next()) >= 0)
|
|
|
|
{
|
2017-01-07 18:32:24 +00:00
|
|
|
sec = &level.sectors[secnum];
|
2016-03-01 15:47:10 +00:00
|
|
|
// if the ceiling is already moving, don't start the door action
|
|
|
|
if (sec->PlaneMoving(sector_t::ceiling))
|
|
|
|
continue;
|
|
|
|
|
2017-04-14 11:31:58 +00:00
|
|
|
if (Create<DDoor>(sec, type, speed, delay, lightTag, topcountdown))
|
2016-03-01 15:47:10 +00:00
|
|
|
rtn = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return rtn;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// animated doors
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
2016-11-24 20:36:02 +00:00
|
|
|
IMPLEMENT_CLASS(DAnimatedDoor, false, false)
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
DAnimatedDoor::DAnimatedDoor ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
DAnimatedDoor::DAnimatedDoor (sector_t *sec)
|
2016-06-13 11:58:04 +00:00
|
|
|
: DMovingCeiling (sec, false)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-09-19 17:14:30 +00:00
|
|
|
void DAnimatedDoor::Serialize(FSerializer &arc)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
Super::Serialize (arc);
|
|
|
|
|
2016-09-19 17:14:30 +00:00
|
|
|
arc("line1", m_Line1)
|
|
|
|
("line2", m_Line2)
|
|
|
|
("frame", m_Frame)
|
|
|
|
("timer", m_Timer)
|
|
|
|
("botdist", m_BotDist)
|
|
|
|
("status", m_Status)
|
|
|
|
("speed", m_Speed)
|
|
|
|
("delay", m_Delay)
|
|
|
|
("dooranim", m_DoorAnim)
|
|
|
|
("setblock1", m_SetBlocking1)
|
2017-02-16 17:35:58 +00:00
|
|
|
("setblock2", m_SetBlocking2)
|
2017-02-16 22:17:19 +00:00
|
|
|
("type", m_Type);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// Starts a closing action on an animated door
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
bool DAnimatedDoor::StartClosing ()
|
|
|
|
{
|
|
|
|
// CAN DOOR CLOSE?
|
|
|
|
if (m_Sector->touching_thinglist != NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-03-30 07:41:46 +00:00
|
|
|
double topdist = m_Sector->ceilingplane.fD();
|
2016-04-08 12:18:46 +00:00
|
|
|
if (m_Sector->MoveCeiling (2048., m_BotDist, 0, -1, false) == EMoveResult::crushed)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-04-08 12:18:46 +00:00
|
|
|
m_Sector->MoveCeiling (2048., topdist, 1);
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
m_Line1->flags |= ML_BLOCKING;
|
|
|
|
m_Line2->flags |= ML_BLOCKING;
|
|
|
|
if (m_DoorAnim->CloseSound != NAME_None)
|
|
|
|
{
|
|
|
|
SN_StartSequence (m_Sector, CHAN_CEILING, m_DoorAnim->CloseSound, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_Status = Closing;
|
|
|
|
m_Timer = m_Speed;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
void DAnimatedDoor::Tick ()
|
|
|
|
{
|
|
|
|
if (m_DoorAnim == NULL)
|
|
|
|
{
|
|
|
|
// can only happen when a bad savegame is loaded.
|
|
|
|
Destroy();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (m_Status)
|
|
|
|
{
|
|
|
|
case Dead:
|
|
|
|
m_Sector->ceilingdata = NULL;
|
|
|
|
Destroy ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Opening:
|
|
|
|
if (!m_Timer--)
|
|
|
|
{
|
|
|
|
if (++m_Frame >= m_DoorAnim->NumTextureFrames)
|
|
|
|
{
|
|
|
|
// IF DOOR IS DONE OPENING...
|
|
|
|
m_Line1->flags &= ~ML_BLOCKING;
|
|
|
|
m_Line2->flags &= ~ML_BLOCKING;
|
|
|
|
|
|
|
|
if (m_Delay == 0)
|
|
|
|
{
|
|
|
|
m_Sector->ceilingdata = NULL;
|
|
|
|
Destroy ();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_Timer = m_Delay;
|
2017-03-14 09:05:35 +00:00
|
|
|
m_Status = Waiting;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// IF DOOR NEEDS TO ANIMATE TO NEXT FRAME...
|
|
|
|
m_Timer = m_Speed;
|
|
|
|
|
|
|
|
m_Line1->sidedef[0]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]);
|
|
|
|
m_Line1->sidedef[1]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]);
|
|
|
|
m_Line2->sidedef[0]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]);
|
|
|
|
m_Line2->sidedef[1]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Waiting:
|
|
|
|
// IF DOOR IS DONE WAITING...
|
2017-02-16 17:35:58 +00:00
|
|
|
if (m_Type == adClose || !m_Timer--)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
if (!StartClosing())
|
|
|
|
{
|
|
|
|
m_Timer = m_Delay;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Closing:
|
|
|
|
if (!m_Timer--)
|
|
|
|
{
|
|
|
|
if (--m_Frame < 0)
|
|
|
|
{
|
|
|
|
// IF DOOR IS DONE CLOSING...
|
2016-04-08 12:18:46 +00:00
|
|
|
m_Sector->MoveCeiling (2048., m_BotDist, -1);
|
2016-03-01 15:47:10 +00:00
|
|
|
m_Sector->ceilingdata = NULL;
|
|
|
|
Destroy ();
|
|
|
|
// Unset blocking flags on lines that didn't start with them. Since the
|
|
|
|
// ceiling is down now, we shouldn't need this flag anymore to keep things
|
|
|
|
// from getting through.
|
|
|
|
if (!m_SetBlocking1)
|
|
|
|
{
|
|
|
|
m_Line1->flags &= ~ML_BLOCKING;
|
|
|
|
}
|
|
|
|
if (!m_SetBlocking2)
|
|
|
|
{
|
|
|
|
m_Line2->flags &= ~ML_BLOCKING;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// IF DOOR NEEDS TO ANIMATE TO NEXT FRAME...
|
|
|
|
m_Timer = m_Speed;
|
|
|
|
|
|
|
|
m_Line1->sidedef[0]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]);
|
|
|
|
m_Line1->sidedef[1]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]);
|
|
|
|
m_Line2->sidedef[0]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]);
|
|
|
|
m_Line2->sidedef[1]->SetTexture(side_t::mid, m_DoorAnim->TextureFrames[m_Frame]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
2017-02-16 17:35:58 +00:00
|
|
|
DAnimatedDoor::DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim, DAnimatedDoor::EADType type)
|
2016-07-03 11:39:33 +00:00
|
|
|
: DMovingCeiling (sec, false)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-30 07:41:46 +00:00
|
|
|
double topdist;
|
2016-03-01 15:47:10 +00:00
|
|
|
FTextureID picnum;
|
|
|
|
|
|
|
|
m_DoorAnim = anim;
|
|
|
|
|
|
|
|
m_Line1 = line;
|
|
|
|
m_Line2 = line;
|
|
|
|
|
2017-01-02 20:40:52 +00:00
|
|
|
for (auto l : sec->Lines)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2017-01-02 20:40:52 +00:00
|
|
|
if (l == line)
|
2016-03-01 15:47:10 +00:00
|
|
|
continue;
|
|
|
|
|
2017-01-02 20:40:52 +00:00
|
|
|
if (l->sidedef[0]->GetTexture(side_t::top) == line->sidedef[0]->GetTexture(side_t::top))
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2017-01-02 20:40:52 +00:00
|
|
|
m_Line2 = l;
|
2016-03-01 15:47:10 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-12-29 09:47:30 +00:00
|
|
|
auto &tex1 = m_Line1->sidedef[0]->textures;
|
|
|
|
tex1[side_t::mid].InitFrom(tex1[side_t::top]);
|
|
|
|
|
|
|
|
auto &tex2 = m_Line2->sidedef[0]->textures;
|
|
|
|
tex2[side_t::mid].InitFrom(tex2[side_t::top]);
|
|
|
|
|
|
|
|
picnum = tex1[side_t::top].texture;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
// don't forget texture scaling here!
|
|
|
|
FTexture *tex = TexMan[picnum];
|
|
|
|
topdist = tex ? tex->GetScaledHeight() : 64;
|
|
|
|
|
2016-03-30 07:41:46 +00:00
|
|
|
topdist = m_Sector->ceilingplane.fD() - topdist * m_Sector->ceilingplane.fC();
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2017-02-16 17:35:58 +00:00
|
|
|
m_Type = type;
|
|
|
|
m_Status = type == adClose? Waiting : Opening;
|
2016-03-01 15:47:10 +00:00
|
|
|
m_Speed = speed;
|
|
|
|
m_Delay = delay;
|
|
|
|
m_Timer = m_Speed;
|
|
|
|
m_Frame = 0;
|
|
|
|
m_SetBlocking1 = !!(m_Line1->flags & ML_BLOCKING);
|
|
|
|
m_SetBlocking2 = !!(m_Line2->flags & ML_BLOCKING);
|
|
|
|
m_Line1->flags |= ML_BLOCKING;
|
|
|
|
m_Line2->flags |= ML_BLOCKING;
|
2016-03-30 07:41:46 +00:00
|
|
|
m_BotDist = m_Sector->ceilingplane.fD();
|
2016-04-08 12:18:46 +00:00
|
|
|
m_Sector->MoveCeiling (2048., topdist, 1);
|
2017-02-16 17:35:58 +00:00
|
|
|
if (type == adOpenClose)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2017-02-16 17:35:58 +00:00
|
|
|
if (m_DoorAnim->OpenSound != NAME_None)
|
|
|
|
{
|
|
|
|
SN_StartSequence(m_Sector, CHAN_INTERIOR, m_DoorAnim->OpenSound, 1);
|
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// EV_SlidingDoor : slide a door horizontally
|
|
|
|
// (animate midtexture, then set noblocking line)
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
2017-02-16 17:35:58 +00:00
|
|
|
bool EV_SlidingDoor (line_t *line, AActor *actor, int tag, int speed, int delay, DAnimatedDoor::EADType type)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
sector_t *sec;
|
|
|
|
int secnum;
|
|
|
|
bool rtn;
|
|
|
|
|
|
|
|
secnum = -1;
|
|
|
|
rtn = false;
|
|
|
|
|
|
|
|
if (tag == 0)
|
|
|
|
{
|
|
|
|
// Manual sliding door
|
|
|
|
sec = line->backsector;
|
|
|
|
|
|
|
|
// Make sure door isn't already being animated
|
2017-02-16 17:35:58 +00:00
|
|
|
if (sec->ceilingdata != NULL )
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
if (actor->player == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (sec->ceilingdata->IsA (RUNTIME_CLASS(DAnimatedDoor)))
|
|
|
|
{
|
|
|
|
DAnimatedDoor *door = barrier_cast<DAnimatedDoor *>(sec->ceilingdata);
|
|
|
|
if (door->m_Status == DAnimatedDoor::Waiting)
|
|
|
|
{
|
|
|
|
return door->StartClosing();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
FDoorAnimation *anim = TexMan.FindAnimatedDoor (line->sidedef[0]->GetTexture(side_t::top));
|
|
|
|
if (anim != NULL)
|
|
|
|
{
|
2017-04-14 11:31:58 +00:00
|
|
|
Create<DAnimatedDoor>(sec, line, speed, delay, anim, type);
|
2016-03-01 15:47:10 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
FSectorTagIterator it(tag);
|
|
|
|
while ((secnum = it.Next()) >= 0)
|
|
|
|
{
|
2017-01-07 18:32:24 +00:00
|
|
|
sec = &level.sectors[secnum];
|
2016-03-01 15:47:10 +00:00
|
|
|
if (sec->ceilingdata != NULL)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-01-02 20:40:52 +00:00
|
|
|
for (auto line : sec->Lines)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
if (line->backsector == NULL)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
FDoorAnimation *anim = TexMan.FindAnimatedDoor (line->sidedef[0]->GetTexture(side_t::top));
|
|
|
|
if (anim != NULL)
|
|
|
|
{
|
|
|
|
rtn = true;
|
2017-04-14 11:31:58 +00:00
|
|
|
Create<DAnimatedDoor>(sec, line, speed, delay, anim, type);
|
2016-03-01 15:47:10 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rtn;
|
|
|
|
}
|
|
|
|
|