mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-23 12:32:34 +00:00
394 lines
12 KiB
C++
394 lines
12 KiB
C++
/*
|
|
** p_linkedsectors.cpp
|
|
**
|
|
** Linked sector movement
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 2008 Christoph Oelckers
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
#include "templates.h"
|
|
#include "p_local.h"
|
|
#include "p_lnspec.h"
|
|
#include "p_spec.h"
|
|
#include "g_levellocals.h"
|
|
#include "vm.h"
|
|
|
|
enum
|
|
{
|
|
// The different movement types.
|
|
LINK_NONE=0,
|
|
LINK_FLOOR=1,
|
|
LINK_CEILING=2,
|
|
LINK_BOTH=3,
|
|
LINK_FLOORMIRRORFLAG=4,
|
|
LINK_CEILINGMIRRORFLAG=8,
|
|
|
|
LINK_FLOORMIRROR=5,
|
|
LINK_CEILINGMIRROR=10,
|
|
LINK_BOTHMIRROR=15,
|
|
|
|
LINK_FLOORBITS=5,
|
|
LINK_CEILINGBITS=10,
|
|
|
|
LINK_FLAGMASK = 15
|
|
|
|
};
|
|
|
|
|
|
//============================================================================
|
|
//
|
|
// Checks whether the other sector is linked to this one
|
|
// Used to ignore linked sectors in the FindNextLowest/Highest*
|
|
// functions
|
|
//
|
|
// NOTE: After looking at Eternity's code I discovered that this check
|
|
// is not done for the FindNext* function but instead only for
|
|
// FindLowestCeilingSurrounding where IMO it makes much less sense
|
|
// because the most frequent use of this feature is most likely lifts
|
|
// with a more detailed surface. Needs to be investigated!
|
|
//
|
|
//============================================================================
|
|
|
|
bool sector_t::IsLinked(sector_t *other, bool ceiling) const
|
|
{
|
|
extsector_t::linked::plane &scrollplane = ceiling? e->Linked.Ceiling : e->Linked.Floor;
|
|
int flag = ceiling? LINK_CEILING : LINK_FLOOR;
|
|
|
|
for(unsigned i = 0; i < scrollplane.Sectors.Size(); i++)
|
|
{
|
|
if (other == scrollplane.Sectors[i].Sector && scrollplane.Sectors[i].Type & flag) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
DEFINE_ACTION_FUNCTION(_Sector, isLinked)
|
|
{
|
|
PARAM_SELF_STRUCT_PROLOGUE(sector_t);
|
|
PARAM_POINTER(other, sector_t);
|
|
PARAM_BOOL(ceiling);
|
|
ACTION_RETURN_BOOL(self->IsLinked(other, ceiling));
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// Helper functions for P_MoveLinkedSectors
|
|
//
|
|
//============================================================================
|
|
|
|
static bool MoveCeiling(sector_t *sector, int crush, double move, bool instant)
|
|
{
|
|
sector->ceilingplane.ChangeHeight (move);
|
|
sector->ChangePlaneTexZ(sector_t::ceiling, move);
|
|
|
|
if (P_ChangeSector(sector, crush, move, 1, true, instant)) return false;
|
|
|
|
// Don't let the ceiling go below the floor
|
|
if (!sector->ceilingplane.isSlope() && !sector->floorplane.isSlope() &&
|
|
!sector->PortalIsLinked(sector_t::floor) &&
|
|
sector->GetPlaneTexZ(sector_t::floor) > sector->GetPlaneTexZ(sector_t::ceiling)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool MoveFloor(sector_t *sector, int crush, double move, bool instant)
|
|
{
|
|
sector->floorplane.ChangeHeight (move);
|
|
sector->ChangePlaneTexZ(sector_t::floor, move);
|
|
|
|
if (P_ChangeSector(sector, crush, move, 0, true, instant)) return false;
|
|
|
|
// Don't let the floor go above the ceiling
|
|
if (!sector->ceilingplane.isSlope() && !sector->floorplane.isSlope() &&
|
|
!sector->PortalIsLinked(sector_t::ceiling) &&
|
|
sector->GetPlaneTexZ(sector_t::floor) > sector->GetPlaneTexZ(sector_t::ceiling)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// P_MoveLinkedSectors
|
|
//
|
|
// Moves all floors/ceilings linked to the control sector
|
|
// Important: All sectors must complete their movement
|
|
// even if a previous one already failed.
|
|
//
|
|
//============================================================================
|
|
|
|
bool P_MoveLinkedSectors(sector_t *sector, int crush, double move, bool ceiling, bool instant)
|
|
{
|
|
extsector_t::linked::plane &scrollplane = ceiling? sector->e->Linked.Ceiling : sector->e->Linked.Floor;
|
|
bool ok = true;
|
|
|
|
for(unsigned i = 0; i < scrollplane.Sectors.Size(); i++)
|
|
{
|
|
switch(scrollplane.Sectors[i].Type)
|
|
{
|
|
case LINK_FLOOR:
|
|
ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, move, instant);
|
|
break;
|
|
|
|
case LINK_CEILING:
|
|
ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, move, instant);
|
|
break;
|
|
|
|
case LINK_BOTH:
|
|
if (move < 0)
|
|
{
|
|
ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, move, instant);
|
|
ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, move, instant);
|
|
}
|
|
else
|
|
{
|
|
ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, move, instant);
|
|
ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, move, instant);
|
|
}
|
|
break;
|
|
|
|
case LINK_FLOORMIRROR:
|
|
ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, -move, instant);
|
|
break;
|
|
|
|
case LINK_CEILINGMIRROR:
|
|
ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, -move, instant);
|
|
break;
|
|
|
|
case LINK_BOTHMIRROR:
|
|
if (move > 0)
|
|
{
|
|
ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, -move, instant);
|
|
ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, -move, instant);
|
|
}
|
|
else
|
|
{
|
|
ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, -move, instant);
|
|
ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, -move, instant);
|
|
}
|
|
break;
|
|
|
|
case LINK_FLOOR+LINK_CEILINGMIRROR:
|
|
ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, move, instant);
|
|
ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, -move, instant);
|
|
break;
|
|
|
|
case LINK_CEILING+LINK_FLOORMIRROR:
|
|
ok &= MoveFloor(scrollplane.Sectors[i].Sector, crush, -move, instant);
|
|
ok &= MoveCeiling(scrollplane.Sectors[i].Sector, crush, move, instant);
|
|
break;
|
|
|
|
default:
|
|
// all other types are invalid and have to be elimintated in the attachment stage
|
|
break;
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// P_StartLinkedSectorInterpolations
|
|
//
|
|
// Starts interpolators for every sector plane is being changed by moving
|
|
// this sector
|
|
//
|
|
//============================================================================
|
|
|
|
void P_StartLinkedSectorInterpolations(TArray<DInterpolation *> &list, sector_t *sector, bool ceiling)
|
|
{
|
|
extsector_t::linked::plane &scrollplane = ceiling? sector->e->Linked.Ceiling : sector->e->Linked.Floor;
|
|
|
|
for(unsigned i = 0; i < scrollplane.Sectors.Size(); i++)
|
|
{
|
|
if (scrollplane.Sectors[i].Type & LINK_FLOOR)
|
|
{
|
|
list.Push(scrollplane.Sectors[i].Sector->SetInterpolation(sector_t::FloorMove, false));
|
|
}
|
|
if (scrollplane.Sectors[i].Type & LINK_CEILING)
|
|
{
|
|
list.Push(scrollplane.Sectors[i].Sector->SetInterpolation(sector_t::CeilingMove, false));
|
|
}
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// AddSingleSector
|
|
//
|
|
// Adds a single sector to a scroll plane. Checks for invalid
|
|
// flag combinations if the sector is already added and removes it if necessary.
|
|
//
|
|
//============================================================================
|
|
|
|
static void AddSingleSector(extsector_t::linked::plane &scrollplane, sector_t *sector, int movetype)
|
|
{
|
|
// First we have to check the list if the sector is already in it
|
|
// If so the move type must be adjusted
|
|
|
|
for(unsigned i = 0; i < scrollplane.Sectors.Size(); i++)
|
|
{
|
|
if (scrollplane.Sectors[i].Sector == sector)
|
|
{
|
|
if (movetype & LINK_FLOORBITS)
|
|
{
|
|
scrollplane.Sectors[i].Type &= ~LINK_FLOORBITS;
|
|
}
|
|
|
|
if (movetype & LINK_CEILINGBITS)
|
|
{
|
|
scrollplane.Sectors[i].Type &= ~LINK_CEILINGBITS;
|
|
}
|
|
|
|
scrollplane.Sectors[i].Type |= movetype;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// The sector hasn't been attached yet so do it now
|
|
|
|
FLinkedSector newlink = {sector, movetype};
|
|
scrollplane.Sectors.Push(newlink);
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// RemoveTaggedSectors
|
|
//
|
|
// Remove all sectors with the given tag from the link list
|
|
//
|
|
//============================================================================
|
|
|
|
static void RemoveTaggedSectors(extsector_t::linked::plane &scrollplane, int tag)
|
|
{
|
|
for(int i = scrollplane.Sectors.Size()-1; i>=0; i--)
|
|
{
|
|
if (tagManager.SectorHasTag(scrollplane.Sectors[i].Sector, tag))
|
|
{
|
|
scrollplane.Sectors.Delete(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
//
|
|
// P_AddSectorLink
|
|
//
|
|
// Links sector planes to a control sector based on the sector's tag
|
|
//
|
|
//============================================================================
|
|
|
|
bool P_AddSectorLinks(sector_t *control, int tag, INTBOOL ceiling, int movetype)
|
|
{
|
|
int param = movetype;
|
|
|
|
// can't be done if the control sector is moving.
|
|
if ((ceiling && control->PlaneMoving(sector_t::ceiling)) ||
|
|
(!ceiling && control->PlaneMoving(sector_t::floor))) return false;
|
|
|
|
// Make sure we have only valid combinations
|
|
movetype &= LINK_FLAGMASK;
|
|
if ((movetype & LINK_FLOORMIRROR) == LINK_FLOORMIRRORFLAG) movetype &= ~LINK_FLOORMIRRORFLAG;
|
|
if ((movetype & LINK_CEILINGMIRROR) == LINK_CEILINGMIRRORFLAG) movetype &= ~LINK_CEILINGMIRRORFLAG;
|
|
|
|
// Don't remove any sector if the parameter is invalid.
|
|
// Addition may still be performed based on the given value.
|
|
if (param != 0 && movetype == 0) return false;
|
|
|
|
extsector_t::linked::plane &scrollplane = ceiling? control->e->Linked.Ceiling : control->e->Linked.Floor;
|
|
|
|
if (movetype > 0)
|
|
{
|
|
int sec;
|
|
FSectorTagIterator itr(tag);
|
|
while ((sec = itr.Next()) >= 0)
|
|
{
|
|
// Don't attach to self (but allow attaching to this sector's oposite plane.
|
|
if (control == &level.sectors[sec])
|
|
{
|
|
if (ceiling == sector_t::floor && movetype & LINK_FLOOR) continue;
|
|
if (ceiling == sector_t::ceiling && movetype & LINK_CEILING) continue;
|
|
}
|
|
AddSingleSector(scrollplane, &level.sectors[sec], movetype);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RemoveTaggedSectors(scrollplane, tag);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// P_AddSectorLinksByID
|
|
//
|
|
// Links sector planes to a control sector based on a control linedef
|
|
// touching the sectors. This is the method Eternity uses and is here
|
|
// mostly so that the Eternity line types can be emulated
|
|
//
|
|
//============================================================================
|
|
|
|
void P_AddSectorLinksByID(sector_t *control, int id, INTBOOL ceiling)
|
|
{
|
|
extsector_t::linked::plane &scrollplane = ceiling? control->e->Linked.Ceiling : control->e->Linked.Floor;
|
|
|
|
FLineIdIterator itr(id);
|
|
int line;
|
|
while ((line = itr.Next()) >= 0)
|
|
{
|
|
line_t *ld = &level.lines[line];
|
|
|
|
if (ld->special == Static_Init && ld->args[1] == Init_SectorLink)
|
|
{
|
|
int movetype = ld->args[3];
|
|
|
|
// [GZ] Eternity does allow the attached sector to be the control sector,
|
|
// this permits elevator effects (ceiling attached to floors), so instead
|
|
// of checking whether the two sectors are the same, we prevent a plane
|
|
// from being attached to itself. This should be enough to do the trick.
|
|
if (ld->frontsector == control)
|
|
{
|
|
if (ceiling) movetype &= ~LINK_CEILING;
|
|
else movetype &= ~LINK_FLOOR;
|
|
}
|
|
|
|
// Make sure we have only valid combinations
|
|
movetype &= LINK_FLAGMASK;
|
|
if ((movetype & LINK_FLOORMIRROR) == LINK_FLOORMIRRORFLAG) movetype &= ~LINK_FLOORMIRRORFLAG;
|
|
if ((movetype & LINK_CEILINGMIRROR) == LINK_CEILINGMIRRORFLAG) movetype &= ~LINK_CEILINGMIRRORFLAG;
|
|
|
|
if (movetype != 0 && ld->frontsector != NULL)//&& ld->frontsector != control) Needs to be allowed!
|
|
{
|
|
AddSingleSector(scrollplane, ld->frontsector, movetype);
|
|
}
|
|
}
|
|
}
|
|
}
|