mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-12-02 00:43:29 +00:00
608 lines
16 KiB
C++
608 lines
16 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// Copyright 1993-1996 id Software
|
|
// Copyright 1994-1996 Raven Software
|
|
// Copyright 1998-1998 Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
|
|
// Copyright 1999-2016 Randy Heit
|
|
// Copyright 2002-2016 Christoph Oelckers
|
|
//
|
|
// 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.
|
|
//
|
|
// 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.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see http://www.gnu.org/licenses/
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//
|
|
// DESCRIPTION: Ceiling animation (lowering, crushing, raising)
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#include "doomdef.h"
|
|
#include "p_local.h"
|
|
#include "s_sound.h"
|
|
#include "s_sndseq.h"
|
|
#include "doomstat.h"
|
|
#include "r_state.h"
|
|
#include "gi.h"
|
|
#include "serializer.h"
|
|
#include "p_spec.h"
|
|
#include "g_levellocals.h"
|
|
#include "textures.h"
|
|
#include "vm.h"
|
|
|
|
//============================================================================
|
|
//
|
|
// CEILINGS
|
|
//
|
|
//============================================================================
|
|
|
|
IMPLEMENT_CLASS(DCeiling, false, false)
|
|
|
|
DCeiling::DCeiling ()
|
|
{
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
//
|
|
//
|
|
//============================================================================
|
|
|
|
void DCeiling::Serialize(FSerializer &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
arc.Enum("type", m_Type)
|
|
("bottomheight", m_BottomHeight)
|
|
("topheight", m_TopHeight)
|
|
("speed", m_Speed)
|
|
("speed1", m_Speed1)
|
|
("speed2", m_Speed2)
|
|
("crush", m_Crush)
|
|
("silent", m_Silent)
|
|
("direction", m_Direction)
|
|
("texture", m_Texture)
|
|
("newspecial", m_NewSpecial)
|
|
("tag", m_Tag)
|
|
("olddirecton", m_OldDirection)
|
|
.Enum("crushmode", m_CrushMode);
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
//
|
|
//
|
|
//============================================================================
|
|
|
|
void DCeiling::PlayCeilingSound ()
|
|
{
|
|
if (m_Sector->Flags & SECF_SILENTMOVE) return;
|
|
if (m_Sector->seqType >= 0)
|
|
{
|
|
SN_StartSequence (m_Sector, CHAN_CEILING, m_Sector->seqType, SEQ_PLATFORM, 0, false);
|
|
}
|
|
else if (m_Sector->SeqName != NAME_None)
|
|
{
|
|
SN_StartSequence (m_Sector, CHAN_CEILING, m_Sector->SeqName, 0);
|
|
}
|
|
else
|
|
{
|
|
if (m_Silent == 2)
|
|
SN_StartSequence (m_Sector, CHAN_CEILING, "Silence", 0);
|
|
else if (m_Silent == 1)
|
|
SN_StartSequence (m_Sector, CHAN_CEILING, "CeilingSemiSilent", 0);
|
|
else
|
|
SN_StartSequence (m_Sector, CHAN_CEILING, "CeilingNormal", 0);
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// DCeiling :: Tick
|
|
//
|
|
//============================================================================
|
|
|
|
void DCeiling::Tick ()
|
|
{
|
|
EMoveResult res;
|
|
|
|
switch (m_Direction)
|
|
{
|
|
case 0:
|
|
// IN STASIS
|
|
break;
|
|
case 1:
|
|
// UP
|
|
res = m_Sector->MoveCeiling (m_Speed, m_TopHeight, m_Direction);
|
|
|
|
if (res == EMoveResult::pastdest)
|
|
{
|
|
switch (m_Type)
|
|
{
|
|
case DCeiling::ceilCrushAndRaise:
|
|
m_Direction = -1;
|
|
m_Speed = m_Speed1;
|
|
if (!SN_IsMakingLoopingSound (m_Sector))
|
|
PlayCeilingSound ();
|
|
break;
|
|
|
|
// movers with texture change, change the texture then get removed
|
|
case genCeilingChgT:
|
|
case genCeilingChg0:
|
|
m_Sector->SetSpecial(&m_NewSpecial);
|
|
// fall through
|
|
case genCeilingChg:
|
|
m_Sector->SetTexture(sector_t::ceiling, m_Texture);
|
|
// fall through
|
|
default:
|
|
SN_StopSequence (m_Sector, CHAN_CEILING);
|
|
Destroy ();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case -1:
|
|
// DOWN
|
|
res = m_Sector->MoveCeiling (m_Speed, m_BottomHeight, m_Crush, m_Direction, m_CrushMode == ECrushMode::crushHexen);
|
|
|
|
if (res == EMoveResult::pastdest)
|
|
{
|
|
switch (m_Type)
|
|
{
|
|
case DCeiling::ceilCrushAndRaise:
|
|
case DCeiling::ceilCrushRaiseAndStay:
|
|
m_Speed = m_Speed2;
|
|
m_Direction = 1;
|
|
if (!SN_IsMakingLoopingSound (m_Sector))
|
|
PlayCeilingSound ();
|
|
break;
|
|
|
|
// in the case of ceiling mover/changer, change the texture
|
|
// then remove the active ceiling
|
|
case genCeilingChgT:
|
|
case genCeilingChg0:
|
|
m_Sector->SetSpecial(&m_NewSpecial);
|
|
// fall through
|
|
case genCeilingChg:
|
|
m_Sector->SetTexture(sector_t::ceiling, m_Texture);
|
|
// fall through
|
|
default:
|
|
SN_StopSequence (m_Sector, CHAN_CEILING);
|
|
Destroy ();
|
|
break;
|
|
}
|
|
}
|
|
else // ( res != pastdest )
|
|
{
|
|
if (res == EMoveResult::crushed)
|
|
{
|
|
switch (m_Type)
|
|
{
|
|
case DCeiling::ceilCrushAndRaise:
|
|
case DCeiling::ceilLowerAndCrush:
|
|
if (m_CrushMode == ECrushMode::crushSlowdown)
|
|
m_Speed = 1. / 8;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
//
|
|
//
|
|
//============================================================================
|
|
|
|
DCeiling::DCeiling (sector_t *sec)
|
|
: DMovingCeiling (sec)
|
|
{
|
|
}
|
|
|
|
DCeiling::DCeiling (sector_t *sec, double speed1, double speed2, int silent)
|
|
: DMovingCeiling (sec)
|
|
{
|
|
m_Crush = -1;
|
|
m_CrushMode = ECrushMode::crushDoom;
|
|
m_Speed = m_Speed1 = speed1;
|
|
m_Speed2 = speed2;
|
|
m_Silent = silent;
|
|
m_BottomHeight = 0;
|
|
m_TopHeight = 0;
|
|
m_Direction = 0;
|
|
m_Texture = FNullTextureID();
|
|
m_Tag = 0;
|
|
m_OldDirection = 0;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
//
|
|
//
|
|
//============================================================================
|
|
|
|
bool P_CreateCeiling(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag,
|
|
double speed, double speed2, double height,
|
|
int crush, int silent, int change, DCeiling::ECrushMode hexencrush)
|
|
{
|
|
double targheight = 0; // Silence, GCC
|
|
|
|
// if ceiling already moving, don't start a second function on it
|
|
if (sec->PlaneMoving(sector_t::ceiling))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// new door thinker
|
|
DCeiling *ceiling = Create<DCeiling> (sec, speed, speed2, silent & ~4);
|
|
vertex_t *spot = sec->Lines[0]->v1;
|
|
|
|
switch (type)
|
|
{
|
|
case DCeiling::ceilCrushAndRaise:
|
|
case DCeiling::ceilCrushRaiseAndStay:
|
|
ceiling->m_TopHeight = sec->ceilingplane.fD();
|
|
case DCeiling::ceilLowerAndCrush:
|
|
targheight = sec->FindHighestFloorPoint (&spot);
|
|
targheight += height;
|
|
ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = -1;
|
|
break;
|
|
|
|
case DCeiling::ceilRaiseToHighest:
|
|
targheight = sec->FindHighestCeilingSurrounding (&spot);
|
|
ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = 1;
|
|
break;
|
|
|
|
case DCeiling::ceilLowerByValue:
|
|
targheight = sec->ceilingplane.ZatPoint (spot) - height;
|
|
ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = -1;
|
|
break;
|
|
|
|
case DCeiling::ceilRaiseByValue:
|
|
targheight = sec->ceilingplane.ZatPoint (spot) + height;
|
|
ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = 1;
|
|
break;
|
|
|
|
case DCeiling::ceilMoveToValue:
|
|
{
|
|
double diff = height - sec->ceilingplane.ZatPoint (spot);
|
|
|
|
targheight = height;
|
|
if (diff < 0)
|
|
{
|
|
ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, height);
|
|
ceiling->m_Direction = -1;
|
|
}
|
|
else
|
|
{
|
|
ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, height);
|
|
ceiling->m_Direction = 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DCeiling::ceilLowerToHighestFloor:
|
|
targheight = sec->FindHighestFloorSurrounding (&spot) + height;
|
|
ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = -1;
|
|
break;
|
|
|
|
case DCeiling::ceilRaiseToHighestFloor:
|
|
targheight = sec->FindHighestFloorSurrounding (&spot);
|
|
ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = 1;
|
|
break;
|
|
|
|
case DCeiling::ceilLowerInstant:
|
|
targheight = sec->ceilingplane.ZatPoint (spot) - height;
|
|
ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = -1;
|
|
ceiling->m_Speed = height;
|
|
break;
|
|
|
|
case DCeiling::ceilRaiseInstant:
|
|
targheight = sec->ceilingplane.ZatPoint (spot) + height;
|
|
ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = 1;
|
|
ceiling->m_Speed = height;
|
|
break;
|
|
|
|
case DCeiling::ceilLowerToNearest:
|
|
targheight = sec->FindNextLowestCeiling (&spot);
|
|
ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = -1;
|
|
break;
|
|
|
|
case DCeiling::ceilRaiseToNearest:
|
|
targheight = sec->FindNextHighestCeiling (&spot);
|
|
ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = 1;
|
|
break;
|
|
|
|
case DCeiling::ceilLowerToLowest:
|
|
targheight = sec->FindLowestCeilingSurrounding (&spot);
|
|
ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = -1;
|
|
break;
|
|
|
|
case DCeiling::ceilRaiseToLowest:
|
|
targheight = sec->FindLowestCeilingSurrounding (&spot);
|
|
ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = 1;
|
|
break;
|
|
|
|
case DCeiling::ceilLowerToFloor:
|
|
targheight = sec->FindHighestFloorPoint (&spot) + height;
|
|
ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = -1;
|
|
break;
|
|
|
|
case DCeiling::ceilRaiseToFloor: // [RH] What's this for?
|
|
targheight = sec->FindHighestFloorPoint (&spot) + height;
|
|
ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = 1;
|
|
break;
|
|
|
|
case DCeiling::ceilLowerToHighest:
|
|
targheight = sec->FindHighestCeilingSurrounding (&spot);
|
|
ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = -1;
|
|
break;
|
|
|
|
case DCeiling::ceilLowerByTexture:
|
|
targheight = sec->ceilingplane.ZatPoint (spot) - sec->FindShortestUpperAround ();
|
|
ceiling->m_BottomHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = -1;
|
|
break;
|
|
|
|
case DCeiling::ceilRaiseByTexture:
|
|
targheight = sec->ceilingplane.ZatPoint (spot) + sec->FindShortestUpperAround ();
|
|
ceiling->m_TopHeight = sec->ceilingplane.PointToDist (spot, targheight);
|
|
ceiling->m_Direction = 1;
|
|
break;
|
|
|
|
default:
|
|
break; // Silence GCC
|
|
}
|
|
|
|
ceiling->m_Tag = tag;
|
|
ceiling->m_Type = type;
|
|
ceiling->m_Crush = crush;
|
|
ceiling->m_CrushMode = hexencrush;
|
|
|
|
// Do not interpolate instant movement ceilings.
|
|
// Note for ZDoomGL: Check to make sure that you update the sector
|
|
// after the ceiling moves, because it hasn't actually moved yet.
|
|
double movedist;
|
|
|
|
if (ceiling->m_Direction < 0)
|
|
{
|
|
movedist = sec->ceilingplane.fD() - ceiling->m_BottomHeight;
|
|
}
|
|
else
|
|
{
|
|
movedist = ceiling->m_TopHeight - sec->ceilingplane.fD();
|
|
}
|
|
if (ceiling->m_Speed >= movedist)
|
|
{
|
|
ceiling->StopInterpolation(true);
|
|
if (silent & 4) ceiling->m_Silent = 2;
|
|
}
|
|
|
|
// set texture/type change properties
|
|
if (change & 3) // if a texture change is indicated
|
|
{
|
|
if (change & 4) // if a numeric model change
|
|
{
|
|
sector_t *modelsec;
|
|
|
|
//jff 5/23/98 find model with floor at target height if target
|
|
//is a floor type
|
|
modelsec = (/*type == ceilRaiseToHighest ||*/
|
|
type == DCeiling::ceilRaiseToFloor ||
|
|
/*type == ceilLowerToHighest ||*/
|
|
type == DCeiling::ceilLowerToFloor) ?
|
|
sec->FindModelFloorSector (targheight) :
|
|
sec->FindModelCeilingSector (targheight);
|
|
if (modelsec != NULL)
|
|
{
|
|
ceiling->m_Texture = modelsec->GetTexture(sector_t::ceiling);
|
|
switch (change & 3)
|
|
{
|
|
case 1: // type is zeroed
|
|
ceiling->m_NewSpecial.Clear();
|
|
ceiling->m_Type = DCeiling::genCeilingChg0;
|
|
break;
|
|
case 2: // type is copied
|
|
sec->GetSpecial(&ceiling->m_NewSpecial);
|
|
ceiling->m_Type = DCeiling::genCeilingChgT;
|
|
break;
|
|
case 3: // type is left alone
|
|
ceiling->m_Type = DCeiling::genCeilingChg;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (line) // else if a trigger model change
|
|
{
|
|
ceiling->m_Texture = line->frontsector->GetTexture(sector_t::ceiling);
|
|
switch (change & 3)
|
|
{
|
|
case 1: // type is zeroed
|
|
ceiling->m_NewSpecial.Clear();
|
|
ceiling->m_Type = DCeiling::genCeilingChg0;
|
|
break;
|
|
case 2: // type is copied
|
|
line->frontsector->GetSpecial(&ceiling->m_NewSpecial);
|
|
ceiling->m_Type = DCeiling::genCeilingChgT;
|
|
break;
|
|
case 3: // type is left alone
|
|
ceiling->m_Type = DCeiling::genCeilingChg;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ceiling->PlayCeilingSound ();
|
|
return ceiling != NULL;
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(DCeiling, CreateCeiling)
|
|
{
|
|
PARAM_PROLOGUE;
|
|
PARAM_POINTER_NOT_NULL(sec, sector_t);
|
|
PARAM_INT(type);
|
|
PARAM_POINTER(ln, line_t);
|
|
PARAM_FLOAT(speed);
|
|
PARAM_FLOAT(speed2);
|
|
PARAM_FLOAT_DEF(height);
|
|
PARAM_INT_DEF(crush);
|
|
PARAM_INT_DEF(silent);
|
|
PARAM_INT_DEF(change);
|
|
PARAM_INT_DEF(crushmode);
|
|
ACTION_RETURN_BOOL(P_CreateCeiling(sec, (DCeiling::ECeiling)type, ln, 0, speed, speed2, height, crush, silent, change, (DCeiling::ECrushMode)crushmode));
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// EV_DoCeiling
|
|
// Move a ceiling up/down and all around!
|
|
//
|
|
// [RH] Added tag, speed, speed2, height, crush, silent, change params
|
|
//
|
|
//============================================================================
|
|
|
|
bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line,
|
|
int tag, double speed, double speed2, double height,
|
|
int crush, int silent, int change, DCeiling::ECrushMode hexencrush)
|
|
{
|
|
int secnum;
|
|
bool rtn;
|
|
sector_t* sec;
|
|
|
|
rtn = false;
|
|
|
|
// check if a manual trigger, if so do just the sector on the backside
|
|
if (tag == 0)
|
|
{
|
|
if (!line || !(sec = line->backsector))
|
|
return rtn;
|
|
secnum = sec->sectornum;
|
|
// [RH] Hack to let manual crushers be retriggerable, too
|
|
tag ^= secnum | 0x1000000;
|
|
P_ActivateInStasisCeiling (tag);
|
|
return P_CreateCeiling(sec, type, line, tag, speed, speed2, height, crush, silent, change, hexencrush);
|
|
}
|
|
|
|
// Reactivate in-stasis ceilings...for certain types.
|
|
// This restarts a crusher after it has been stopped
|
|
if (type == DCeiling::ceilCrushAndRaise)
|
|
{
|
|
P_ActivateInStasisCeiling (tag);
|
|
}
|
|
|
|
// affects all sectors with the same tag as the linedef
|
|
FSectorTagIterator it(tag);
|
|
while ((secnum = it.Next()) >= 0)
|
|
{
|
|
rtn |= P_CreateCeiling(&level.sectors[secnum], type, line, tag, speed, speed2, height, crush, silent, change, hexencrush);
|
|
}
|
|
return rtn;
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
//
|
|
// Restart a ceiling that's in-stasis
|
|
// [RH] Passed a tag instead of a line and rewritten to use a list
|
|
//
|
|
//============================================================================
|
|
|
|
void P_ActivateInStasisCeiling (int tag)
|
|
{
|
|
DCeiling *scan;
|
|
TThinkerIterator<DCeiling> iterator;
|
|
|
|
while ( (scan = iterator.Next ()) )
|
|
{
|
|
if (scan->m_Tag == tag && scan->m_Direction == 0)
|
|
{
|
|
scan->m_Direction = scan->m_OldDirection;
|
|
scan->PlayCeilingSound ();
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// EV_CeilingCrushStop
|
|
// Stop a ceiling from crushing!
|
|
// [RH] Passed a tag instead of a line and rewritten to use a list
|
|
//
|
|
//============================================================================
|
|
|
|
bool EV_CeilingCrushStop (int tag, bool remove)
|
|
{
|
|
bool rtn = false;
|
|
DCeiling *scan;
|
|
TThinkerIterator<DCeiling> iterator;
|
|
|
|
scan = iterator.Next();
|
|
while (scan != nullptr)
|
|
{
|
|
DCeiling *next = iterator.Next();
|
|
if (scan->m_Tag == tag && scan->m_Direction != 0)
|
|
{
|
|
if (!remove)
|
|
{
|
|
SN_StopSequence(scan->m_Sector, CHAN_CEILING);
|
|
scan->m_OldDirection = scan->m_Direction;
|
|
scan->m_Direction = 0; // in-stasis;
|
|
}
|
|
else
|
|
{
|
|
scan->Destroy();
|
|
}
|
|
rtn = true;
|
|
}
|
|
scan = next;
|
|
}
|
|
|
|
return rtn;
|
|
}
|
|
|
|
bool EV_StopCeiling(int tag, line_t *line)
|
|
{
|
|
int sec;
|
|
FSectorTagIterator it(tag, line);
|
|
while ((sec = it.Next()) >= 0)
|
|
{
|
|
if (level.sectors[sec].ceilingdata)
|
|
{
|
|
SN_StopSequence(&level.sectors[sec], CHAN_CEILING);
|
|
level.sectors[sec].ceilingdata->Destroy();
|
|
level.sectors[sec].ceilingdata = nullptr;
|
|
}
|
|
}
|
|
return true;
|
|
}
|