gzdoom-gles/src/playsim/po_man.cpp

1849 lines
43 KiB
C++
Raw Normal View History

//-----------------------------------------------------------------------------
//
// Copyright 1994-1996 Raven Software
// Copyright 1999-2016 Randy Heit
// Copyright 2002-2018 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/
//
//-----------------------------------------------------------------------------
//
2016-03-01 15:47:10 +00:00
// HEADER FILES ------------------------------------------------------------
#include "doomdef.h"
#include "p_local.h"
#include "m_bbox.h"
#include "s_sndseq.h"
#include "a_sharedglobal.h"
#include "p_3dmidtex.h"
#include "p_lnspec.h"
#include "r_data/r_interpolate.h"
#include "po_man.h"
#include "p_setup.h"
#include "serializer_doom.h"
#include "serialize_obj.h"
2016-03-01 15:47:10 +00:00
#include "p_blockmap.h"
#include "p_maputl.h"
#include "r_utility.h"
#include "g_levellocals.h"
#include "actorinlines.h"
#include "v_text.h"
2016-03-01 15:47:10 +00:00
// TYPES -------------------------------------------------------------------
class DRotatePoly : public DPolyAction
{
DECLARE_CLASS (DRotatePoly, DPolyAction)
public:
void Construct(FPolyObj *polyNum);
2016-03-01 15:47:10 +00:00
void Tick ();
friend bool EV_RotatePoly (FLevelLocals *Level, line_t *line, int polyNum, int speed, int byteAngle, int direction, bool overRide);
2016-03-01 15:47:10 +00:00
};
class DMovePoly : public DPolyAction
{
DECLARE_CLASS (DMovePoly, DPolyAction)
public:
void Construct(FPolyObj *polyNum);
void Serialize(FSerializer &arc);
2016-03-01 15:47:10 +00:00
void Tick ();
protected:
DAngle m_Angle;
DVector2 m_Speedv;
2016-03-01 15:47:10 +00:00
friend bool EV_MovePoly(FLevelLocals *Level, line_t *line, int polyNum, double speed, DAngle angle, double dist, bool overRide);
2016-03-01 15:47:10 +00:00
};
class DMovePolyTo : public DPolyAction
{
DECLARE_CLASS(DMovePolyTo, DPolyAction)
public:
void Construct(FPolyObj *polyNum);
void Serialize(FSerializer &arc);
2016-03-01 15:47:10 +00:00
void Tick();
protected:
DVector2 m_Speedv;
DVector2 m_Target;
2016-03-01 15:47:10 +00:00
friend bool EV_MovePolyTo(FLevelLocals *Level, line_t *line, int polyNum, double speed, const DVector2 &pos, bool overRide);
2016-03-01 15:47:10 +00:00
};
class DPolyDoor : public DMovePoly
{
DECLARE_CLASS (DPolyDoor, DMovePoly)
public:
void Construct(FPolyObj *polyNum, podoortype_t type);
void Serialize(FSerializer &arc);
2016-03-01 15:47:10 +00:00
void Tick ();
protected:
DAngle m_Direction;
double m_TotalDist;
2016-03-01 15:47:10 +00:00
int m_Tics;
int m_WaitTics;
podoortype_t m_Type;
bool m_Close;
friend bool EV_OpenPolyDoor(FLevelLocals *Level, line_t *line, int polyNum, double speed, DAngle angle, int delay, double distance, podoortype_t type);
2016-03-01 15:47:10 +00:00
};
class FPolyMirrorIterator
{
FPolyObj *CurPoly;
int UsedPolys[100]; // tracks mirrored polyobjects we've seen
int NumUsedPolys;
public:
FPolyMirrorIterator(FPolyObj *poly);
FPolyObj *NextMirror();
};
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void UnLinkPolyobj (FPolyObj *po);
static void LinkPolyobj (FPolyObj *po);
static bool CheckMobjBlocking (side_t *seg, FPolyObj *po);
static void SpawnPolyobj (int index, int tag, int type);
static void DoMovePolyobj (FPolyObj *po, const DVector2 & move);
2016-03-01 15:47:10 +00:00
static FPolyNode *NewPolyNode();
static void FreePolyNode();
static void ReleaseAllPolyNodes();
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static FPolyNode *FreePolyNodes;
// CODE --------------------------------------------------------------------
//==========================================================================
//
//
//
//==========================================================================
IMPLEMENT_CLASS(DPolyAction, false, true)
IMPLEMENT_POINTERS_START(DPolyAction)
IMPLEMENT_POINTER(m_Interpolation)
IMPLEMENT_POINTERS_END
2016-03-01 15:47:10 +00:00
void DPolyAction::Serialize(FSerializer &arc)
2016-03-01 15:47:10 +00:00
{
Super::Serialize (arc);
arc("polyobj", m_PolyObj)
("speed", m_Speed)
("dist", m_Dist)
("interpolation", m_Interpolation);
2016-03-01 15:47:10 +00:00
}
void DPolyAction::Construct(FPolyObj *polyNum)
2016-03-01 15:47:10 +00:00
{
m_PolyObj = polyNum;
m_Speed = 0;
m_Dist = 0;
SetInterpolation ();
}
void DPolyAction::OnDestroy()
2016-03-01 15:47:10 +00:00
{
if (m_PolyObj->specialdata == this)
2016-03-01 15:47:10 +00:00
{
m_PolyObj->specialdata = nullptr;
2016-03-01 15:47:10 +00:00
}
StopInterpolation();
Super::OnDestroy();
2016-03-01 15:47:10 +00:00
}
void DPolyAction::Stop()
{
SN_StopSequence(m_PolyObj);
2016-03-01 15:47:10 +00:00
Destroy();
}
void DPolyAction::SetInterpolation ()
{
m_Interpolation = m_PolyObj->SetInterpolation();
2016-03-01 15:47:10 +00:00
}
void DPolyAction::StopInterpolation ()
{
if (m_Interpolation != nullptr)
2016-03-01 15:47:10 +00:00
{
m_Interpolation->DelRef();
m_Interpolation = nullptr;
2016-03-01 15:47:10 +00:00
}
}
//==========================================================================
//
//
//
//==========================================================================
IMPLEMENT_CLASS(DRotatePoly, false, false)
2016-03-01 15:47:10 +00:00
void DRotatePoly::Construct(FPolyObj *polyNum)
2016-03-01 15:47:10 +00:00
{
Super::Construct(polyNum);
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
IMPLEMENT_CLASS(DMovePoly, false, false)
2016-03-01 15:47:10 +00:00
void DMovePoly::Serialize(FSerializer &arc)
2016-03-01 15:47:10 +00:00
{
Super::Serialize (arc);
arc("angle", m_Angle)
("speedv", m_Speedv);
2016-03-01 15:47:10 +00:00
}
void DMovePoly::Construct(FPolyObj *polyNum)
2016-03-01 15:47:10 +00:00
{
Super::Construct(polyNum);
m_Angle = 0.;
m_Speedv = { 0,0 };
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//
//==========================================================================
IMPLEMENT_CLASS(DMovePolyTo, false, false)
2016-03-01 15:47:10 +00:00
void DMovePolyTo::Serialize(FSerializer &arc)
2016-03-01 15:47:10 +00:00
{
Super::Serialize(arc);
arc("speedv", m_Speedv)
("target", m_Target);
2016-03-01 15:47:10 +00:00
}
void DMovePolyTo::Construct(FPolyObj *polyNum)
2016-03-01 15:47:10 +00:00
{
Super::Construct(polyNum);
m_Speedv = m_Target = { 0,0 };
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
IMPLEMENT_CLASS(DPolyDoor, false, false)
2016-03-01 15:47:10 +00:00
void DPolyDoor::Serialize(FSerializer &arc)
2016-03-01 15:47:10 +00:00
{
Super::Serialize (arc);
arc.Enum("type", m_Type)
("direction", m_Direction)
("totaldist", m_TotalDist)
("tics", m_Tics)
("waittics", m_WaitTics)
("close", m_Close);
2016-03-01 15:47:10 +00:00
}
void DPolyDoor::Construct (FPolyObj * polyNum, podoortype_t type)
2016-03-01 15:47:10 +00:00
{
Super::Construct(polyNum);
m_Type = type;
m_Direction = 0.;
2016-03-01 15:47:10 +00:00
m_TotalDist = 0;
m_Tics = 0;
m_WaitTics = 0;
m_Close = false;
}
// ===== Polyobj Event Code =====
//==========================================================================
//
// T_RotatePoly
//
//==========================================================================
void DRotatePoly::Tick ()
{
if (m_PolyObj == nullptr) return;
2016-03-01 15:47:10 +00:00
// Don't let non-perpetual polyobjs overshoot their targets.
if (m_Dist != -1 && m_Dist < fabs(m_Speed))
2016-03-01 15:47:10 +00:00
{
m_Speed = m_Speed < 0 ? -m_Dist : m_Dist;
}
if (m_PolyObj->RotatePolyobj (m_Speed))
2016-03-01 15:47:10 +00:00
{
if (m_Dist == -1)
{ // perpetual polyobj
return;
}
m_Dist -= fabs(m_Speed);
2016-03-01 15:47:10 +00:00
if (m_Dist == 0)
{
SN_StopSequence (m_PolyObj);
2016-03-01 15:47:10 +00:00
Destroy ();
}
}
}
//==========================================================================
//
// EV_RotatePoly
//
//==========================================================================
bool EV_RotatePoly (FLevelLocals *Level, line_t *line, int polyNum, int speed, int byteAngle, int direction, bool overRide)
2016-03-01 15:47:10 +00:00
{
DRotatePoly *pe = nullptr;
2016-03-01 15:47:10 +00:00
FPolyObj *poly;
if ((poly = Level->GetPolyobj(polyNum)) == nullptr)
2016-03-01 15:47:10 +00:00
{
Printf("EV_RotatePoly: Invalid polyobj num: %d\n", polyNum);
return false;
}
FPolyMirrorIterator it(poly);
while ((poly = it.NextMirror()) != nullptr)
2016-03-01 15:47:10 +00:00
{
if ((poly->specialdata != nullptr || poly->bBlocked) && !overRide)
2016-03-01 15:47:10 +00:00
{ // poly is already in motion
break;
}
if (poly->bHasPortals == 2)
{
// cannot do rotations on linked polyportals.
break;
}
pe = Level->CreateThinker<DRotatePoly>(poly);
2016-03-01 15:47:10 +00:00
poly->specialdata = pe;
poly->bBlocked = false;
2016-03-01 15:47:10 +00:00
if (byteAngle != 0)
{
if (byteAngle == 255)
{
pe->m_Dist = -1.;
2016-03-01 15:47:10 +00:00
}
else
{
pe->m_Dist = byteAngle*(90./64); // Angle
2016-03-01 15:47:10 +00:00
}
}
else
{
pe->m_Dist = 360.;
2016-03-01 15:47:10 +00:00
}
pe->m_Speed = speed*direction*(90./(64<<3));
2016-03-01 15:47:10 +00:00
SN_StartSequence (poly, poly->seqType, SEQ_DOOR, 0);
direction = -direction; // Reverse the direction
}
return pe != nullptr; // Return true if something started moving.
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// T_MovePoly
//
//==========================================================================
void DMovePoly::Tick ()
{
if (m_PolyObj != nullptr)
2016-03-01 15:47:10 +00:00
{
if (m_PolyObj->MovePolyobj (m_Speedv))
2016-03-01 15:47:10 +00:00
{
double absSpeed = fabs (m_Speed);
2016-03-01 15:47:10 +00:00
m_Dist -= absSpeed;
if (m_Dist <= 0)
{
SN_StopSequence (m_PolyObj);
2016-03-01 15:47:10 +00:00
Destroy ();
}
else if (m_Dist < absSpeed)
{
m_Speed = m_Dist * (m_Speed < 0 ? -1 : 1);
m_Speedv = m_Angle.ToVector(m_Speed);
2016-03-01 15:47:10 +00:00
}
m_PolyObj->UpdateLinks();
2016-03-01 15:47:10 +00:00
}
}
}
//==========================================================================
//
// EV_MovePoly
//
//==========================================================================
bool EV_MovePoly (FLevelLocals *Level, line_t *line, int polyNum, double speed, DAngle angle, double dist, bool overRide)
2016-03-01 15:47:10 +00:00
{
DMovePoly *pe = nullptr;
2016-03-01 15:47:10 +00:00
FPolyObj *poly;
DAngle an = angle;
2016-03-01 15:47:10 +00:00
if ((poly = Level->GetPolyobj(polyNum)) == nullptr)
2016-03-01 15:47:10 +00:00
{
Printf("EV_MovePoly: Invalid polyobj num: %d\n", polyNum);
return false;
}
FPolyMirrorIterator it(poly);
while ((poly = it.NextMirror()) != nullptr)
2016-03-01 15:47:10 +00:00
{
if ((poly->specialdata != nullptr || poly->bBlocked) && !overRide)
2016-03-01 15:47:10 +00:00
{ // poly is already in motion
break;
}
pe = Level->CreateThinker<DMovePoly>(poly);
2016-03-01 15:47:10 +00:00
poly->specialdata = pe;
poly->bBlocked = false;
2016-03-01 15:47:10 +00:00
pe->m_Dist = dist; // Distance
pe->m_Speed = speed;
pe->m_Angle = angle;
pe->m_Speedv = angle.ToVector(speed);
2016-03-01 15:47:10 +00:00
SN_StartSequence (poly, poly->seqType, SEQ_DOOR, 0);
// Do not interpolate very fast moving polyobjects. The minimum tic count is
// 3 instead of 2, because the moving crate effect in Massmouth 2, Hostitality
// that this fixes isn't quite fast enough to move the crate back to its start
// in just 1 tic.
if (dist/speed <= 2)
{
pe->StopInterpolation ();
}
angle += 180.; // Reverse the angle.
2016-03-01 15:47:10 +00:00
}
return pe != nullptr; // Return true if something started moving.
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// DMovePolyTo :: Tick
//
//==========================================================================
void DMovePolyTo::Tick ()
{
if (m_PolyObj != nullptr)
2016-03-01 15:47:10 +00:00
{
if (m_PolyObj->MovePolyobj (m_Speedv))
2016-03-01 15:47:10 +00:00
{
double absSpeed = fabs (m_Speed);
2016-03-01 15:47:10 +00:00
m_Dist -= absSpeed;
if (m_Dist <= 0)
{
SN_StopSequence (m_PolyObj);
2016-03-01 15:47:10 +00:00
Destroy ();
}
else if (m_Dist < absSpeed)
{
m_Speed = m_Dist * (m_Speed < 0 ? -1 : 1);
m_Speedv = m_Target - m_PolyObj->StartSpot.pos;
2016-03-01 15:47:10 +00:00
}
m_PolyObj->UpdateLinks();
2016-03-01 15:47:10 +00:00
}
}
}
//==========================================================================
//
// EV_MovePolyTo
//
//==========================================================================
bool EV_MovePolyTo(FLevelLocals *Level, line_t *line, int polyNum, double speed, const DVector2 &targ, bool overRide)
2016-03-01 15:47:10 +00:00
{
DMovePolyTo *pe = nullptr;
2016-03-01 15:47:10 +00:00
FPolyObj *poly;
DVector2 dist;
2016-03-01 15:47:10 +00:00
double distlen;
if ((poly = Level->GetPolyobj(polyNum)) == nullptr)
2016-03-01 15:47:10 +00:00
{
Printf("EV_MovePolyTo: Invalid polyobj num: %d\n", polyNum);
return false;
}
FPolyMirrorIterator it(poly);
dist = targ - poly->StartSpot.pos;
2016-03-01 15:47:10 +00:00
distlen = dist.MakeUnit();
while ((poly = it.NextMirror()) != nullptr)
2016-03-01 15:47:10 +00:00
{
if ((poly->specialdata != nullptr || poly->bBlocked) && !overRide)
2016-03-01 15:47:10 +00:00
{ // poly is already in motion
break;
}
pe = Level->CreateThinker<DMovePolyTo>(poly);
2016-03-01 15:47:10 +00:00
poly->specialdata = pe;
poly->bBlocked = false;
pe->m_Dist = distlen;
2016-03-01 15:47:10 +00:00
pe->m_Speed = speed;
pe->m_Speedv = dist * speed;
pe->m_Target = poly->StartSpot.pos + dist * distlen;
2016-03-01 15:47:10 +00:00
if ((pe->m_Dist / pe->m_Speed) <= 2)
{
pe->StopInterpolation();
}
dist = -dist; // reverse the direction
}
return pe != nullptr; // Return true if something started moving.
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// T_PolyDoor
//
//==========================================================================
void DPolyDoor::Tick ()
{
if (m_PolyObj == nullptr) return;
2016-03-01 15:47:10 +00:00
if (m_Tics)
{
if (!--m_Tics)
{
SN_StartSequence (m_PolyObj, m_PolyObj->seqType, SEQ_DOOR, m_Close);
2016-03-01 15:47:10 +00:00
}
return;
}
switch (m_Type)
{
case PODOOR_SLIDE:
if (m_Dist <= 0 || m_PolyObj->MovePolyobj (m_Speedv))
2016-03-01 15:47:10 +00:00
{
double absSpeed = fabs (m_Speed);
2016-03-01 15:47:10 +00:00
m_Dist -= absSpeed;
if (m_Dist <= 0)
{
SN_StopSequence (m_PolyObj);
if (!m_Close && m_WaitTics >= 0)
2016-03-01 15:47:10 +00:00
{
m_Dist = m_TotalDist;
m_Close = true;
m_Tics = m_WaitTics;
m_Direction = -m_Direction;
m_Speedv = -m_Speedv;
2016-03-01 15:47:10 +00:00
}
else
{
// if set to wait infinitely, Hexen kept the dead thinker to block the polyobject from getting activated again but that causes some problems
// with the subsectorlinks and the interpolation. Better delete the thinker and use a different means to block it.
if (!m_Close) m_PolyObj->bBlocked = true;
2016-03-01 15:47:10 +00:00
Destroy ();
}
}
m_PolyObj->UpdateLinks();
2016-03-01 15:47:10 +00:00
}
else
{
if (m_PolyObj->crush || !m_Close)
2016-03-01 15:47:10 +00:00
{ // continue moving if the poly is a crusher, or is opening
return;
}
else
{ // open back up
m_Dist = m_TotalDist - m_Dist;
m_Direction = -m_Direction;
m_Speedv = -m_Speedv;
2016-03-01 15:47:10 +00:00
m_Close = false;
SN_StartSequence (m_PolyObj, m_PolyObj->seqType, SEQ_DOOR, 0);
2016-03-01 15:47:10 +00:00
}
}
break;
case PODOOR_SWING:
if (m_Dist <= 0 || m_PolyObj->RotatePolyobj (m_Speed))
2016-03-01 15:47:10 +00:00
{
double absSpeed = fabs (m_Speed);
2016-03-01 15:47:10 +00:00
m_Dist -= absSpeed;
if (m_Dist <= 0)
{
SN_StopSequence (m_PolyObj);
if (!m_Close && m_WaitTics >= 0)
2016-03-01 15:47:10 +00:00
{
m_Dist = m_TotalDist;
m_Close = true;
m_Tics = m_WaitTics;
m_Speed = -m_Speed;
}
else
{
if (!m_Close) m_PolyObj->bBlocked = true;
2016-03-01 15:47:10 +00:00
Destroy ();
}
}
}
else
{
if(m_PolyObj->crush || !m_Close)
2016-03-01 15:47:10 +00:00
{ // continue moving if the poly is a crusher, or is opening
return;
}
else
{ // open back up and rewait
m_Dist = m_TotalDist - m_Dist;
m_Speed = -m_Speed;
m_Close = false;
SN_StartSequence (m_PolyObj, m_PolyObj->seqType, SEQ_DOOR, 0);
2016-03-01 15:47:10 +00:00
}
}
break;
default:
break;
}
}
//==========================================================================
//
// EV_OpenPolyDoor
//
//==========================================================================
bool EV_OpenPolyDoor(FLevelLocals *Level, line_t *line, int polyNum, double speed, DAngle angle, int delay, double distance, podoortype_t type)
2016-03-01 15:47:10 +00:00
{
DPolyDoor *pd = nullptr;
2016-03-01 15:47:10 +00:00
FPolyObj *poly;
int swingdir = 1; // ADD: PODOOR_SWINGL, PODOOR_SWINGR
if ((poly = Level->GetPolyobj(polyNum)) == nullptr)
2016-03-01 15:47:10 +00:00
{
Printf("EV_OpenPolyDoor: Invalid polyobj num: %d\n", polyNum);
return false;
}
FPolyMirrorIterator it(poly);
while ((poly = it.NextMirror()) != nullptr)
2016-03-01 15:47:10 +00:00
{
if ((poly->specialdata != nullptr || poly->bBlocked))
2016-03-01 15:47:10 +00:00
{ // poly is already moving
break;
}
if (poly->bHasPortals == 2 && type == PODOOR_SWING)
{
// cannot do rotations on linked polyportals.
break;
}
pd = Level->CreateThinker<DPolyDoor>(poly, type);
2016-03-01 15:47:10 +00:00
poly->specialdata = pd;
if (type == PODOOR_SLIDE)
{
pd->m_WaitTics = delay;
pd->m_Speed = speed;
pd->m_Dist = pd->m_TotalDist = distance; // Distance
pd->m_Direction = angle;
pd->m_Speedv = angle.ToVector(speed);
2016-03-01 15:47:10 +00:00
SN_StartSequence (poly, poly->seqType, SEQ_DOOR, 0);
angle += 180.; // reverse the angle
2016-03-01 15:47:10 +00:00
}
else if (type == PODOOR_SWING)
{
pd->m_WaitTics = delay;
pd->m_Direction.Degrees = swingdir;
pd->m_Speed = (speed*swingdir*(90. / 64)) / 8;
pd->m_Dist = pd->m_TotalDist = angle.Degrees;
2016-03-01 15:47:10 +00:00
SN_StartSequence (poly, poly->seqType, SEQ_DOOR, 0);
swingdir = -swingdir; // reverse the direction
}
}
return pd != nullptr; // Return true if something started moving.
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// EV_StopPoly
//
//==========================================================================
bool EV_StopPoly(FLevelLocals *Level, int polynum)
2016-03-01 15:47:10 +00:00
{
FPolyObj *poly;
if (nullptr != (poly = Level->GetPolyobj(polynum)))
2016-03-01 15:47:10 +00:00
{
if (poly->specialdata != nullptr)
2016-03-01 15:47:10 +00:00
{
poly->specialdata->Stop();
}
return true;
}
return false;
}
// ===== Higher Level Poly Interface code =====
//==========================================================================
//
//
//
//==========================================================================
FPolyObj::FPolyObj()
{
StartSpot.pos = { 0,0 };
Angle = 0.;
2016-03-01 15:47:10 +00:00
tag = 0;
memset(bbox, 0, sizeof(bbox));
validcount = 0;
crush = 0;
bHurtOnTouch = false;
seqType = 0;
Size = 0;
bBlocked = false;
subsectorlinks = nullptr;
specialdata = nullptr;
interpolation = nullptr;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// GetPolyobjMirror
//
//==========================================================================
int FPolyObj::GetMirror()
{
return MirrorNum;
}
//==========================================================================
//
// ThrustMobj
//
//==========================================================================
void FPolyObj::ThrustMobj (AActor *actor, side_t *side)
{
DAngle thrustAngle;
2016-03-01 15:47:10 +00:00
DPolyAction *pe;
double force;
2016-03-01 15:47:10 +00:00
if (!(actor->flags&MF_SHOOTABLE) && !actor->player)
{
return;
}
vertex_t *v1 = side->V1();
vertex_t *v2 = side->V2();
thrustAngle = (v2->fPos() - v1->fPos()).Angle() - 90.;
2016-03-01 15:47:10 +00:00
pe = static_cast<DPolyAction *>(specialdata);
if (pe)
{
if (pe->IsKindOf (RUNTIME_CLASS (DRotatePoly)))
{
force = pe->GetSpeed() * (90. / 2048); // For DRotatePoly m_Speed stores an angle which needs to be converted differently
2016-03-01 15:47:10 +00:00
}
else
{
force = pe->GetSpeed() / 8;
2016-03-01 15:47:10 +00:00
}
force = clamp(force, 1., 4.);
2016-03-01 15:47:10 +00:00
}
else
{
force = 1;
2016-03-01 15:47:10 +00:00
}
DVector2 thrust = thrustAngle.ToVector(force);
actor->Vel += thrust;
2016-03-01 15:47:10 +00:00
if (crush)
{
DVector2 pos = actor->Vec2Offset(thrust.X, thrust.Y);
if (bHurtOnTouch || !P_CheckMove (actor, pos))
2016-03-01 15:47:10 +00:00
{
int newdam = P_DamageMobj (actor, nullptr, nullptr, crush, NAME_Crush);
2016-03-01 15:47:10 +00:00
P_TraceBleed (newdam > 0 ? newdam : crush, actor);
}
}
if (Level->flags2 & LEVEL2_POLYGRIND) actor->CallGrind(false); // crush corpses that get caught in a polyobject's way
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// UpdateSegBBox
//
//==========================================================================
void FPolyObj::UpdateLinks()
{
if (bHasPortals == 2)
{
TMap<int, bool> processed;
for (unsigned i = 0; i < Linedefs.Size(); i++)
{
if (Linedefs[i]->isLinePortal())
{
FLinePortal *port = Linedefs[i]->getPortal();
if (port->mType == PORTT_LINKED)
{
DVector2 old = port->mDisplacement;
port->mDisplacement = port->mDestination->v2->fPos() - port->mOrigin->v1->fPos();
FLinePortal *port2 = port->mDestination->getPortal();
if (port2) port2->mDisplacement = -port->mDisplacement;
int destgroup = port->mDestination->frontsector->PortalGroup;
bool *done = processed.CheckKey(destgroup);
if (!done || !*done)
{
processed[destgroup] = true;
DVector2 delta = port->mDisplacement - old;
Level->Displacements.MoveGroup(destgroup, delta);
}
}
}
}
}
}
2016-03-01 15:47:10 +00:00
void FPolyObj::UpdateBBox ()
{
for(unsigned i=0;i<Linedefs.Size(); i++)
{
Linedefs[i]->AdjustLine();
2016-03-01 15:47:10 +00:00
}
CalcCenter();
}
void FPolyObj::CalcCenter()
{
DVector2 c = { 0, 0 };
2016-03-01 15:47:10 +00:00
for(unsigned i=0;i<Vertices.Size(); i++)
{
c += Vertices[i]->fPos();
2016-03-01 15:47:10 +00:00
}
CenterSpot.pos = c / Vertices.Size();
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// PO_MovePolyobj
//
//==========================================================================
bool FPolyObj::MovePolyobj (const DVector2 &pos, bool force)
2016-03-01 15:47:10 +00:00
{
FBoundingBox oldbounds = Bounds;
UnLinkPolyobj ();
DoMovePolyobj (pos);
2016-03-01 15:47:10 +00:00
if (!force)
{
bool blocked = false;
for(unsigned i=0;i < Sidedefs.Size(); i++)
{
if (CheckMobjBlocking(Sidedefs[i]))
{
blocked = true;
}
}
if (blocked)
{
DoMovePolyobj (-pos);
2016-03-01 15:47:10 +00:00
LinkPolyobj();
return false;
}
}
StartSpot.pos += pos;
CenterSpot.pos += pos;
2016-03-01 15:47:10 +00:00
LinkPolyobj ();
ClearSubsectorLinks();
RecalcActorFloorCeil(Bounds | oldbounds);
return true;
}
//==========================================================================
//
// DoMovePolyobj
//
//==========================================================================
void FPolyObj::DoMovePolyobj (const DVector2 &pos)
2016-03-01 15:47:10 +00:00
{
for(unsigned i=0;i < Vertices.Size(); i++)
{
Vertices[i]->set(Vertices[i]->fX() + pos.X, Vertices[i]->fY() + pos.Y);
PrevPts[i].pos += pos;
2016-03-01 15:47:10 +00:00
}
for (unsigned i = 0; i < Linedefs.Size(); i++)
{
2016-03-30 23:22:49 +00:00
Linedefs[i]->bbox[BOXTOP] += pos.Y;
Linedefs[i]->bbox[BOXBOTTOM] += pos.Y;
Linedefs[i]->bbox[BOXLEFT] += pos.X;
Linedefs[i]->bbox[BOXRIGHT] += pos.X;
2016-03-01 15:47:10 +00:00
}
}
//==========================================================================
//
// RotatePt
//
//==========================================================================
static void RotatePt (DAngle an, DVector2 &out, const DVector2 &start)
2016-03-01 15:47:10 +00:00
{
DVector2 tr = out;
2016-03-01 15:47:10 +00:00
double s = an.Sin();
double c = an.Cos();
out.X = tr.X * c - tr.Y * s + start.X;
out.Y = tr.X * s + tr.Y * c + start.Y;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// PO_RotatePolyobj
//
//==========================================================================
bool FPolyObj::RotatePolyobj (DAngle angle, bool fromsave)
2016-03-01 15:47:10 +00:00
{
DAngle an;
2016-03-01 15:47:10 +00:00
bool blocked;
FBoundingBox oldbounds = Bounds;
an = Angle + angle;
2016-03-01 15:47:10 +00:00
UnLinkPolyobj();
for(unsigned i=0;i < Vertices.Size(); i++)
{
PrevPts[i].pos = Vertices[i]->fPos();
FPolyVertex torot = OriginalPts[i];
RotatePt(an, torot.pos, StartSpot.pos);
Vertices[i]->set(torot.pos.X, torot.pos.Y);
2016-03-01 15:47:10 +00:00
}
blocked = false;
validcount++;
UpdateBBox();
// If we are loading a savegame we do not really want to damage actors and be blocked by them. This can also cause crashes when trying to damage incompletely deserialized player pawns.
if (!fromsave)
{
for (unsigned i = 0; i < Sidedefs.Size(); i++)
{
if (CheckMobjBlocking(Sidedefs[i]))
{
blocked = true;
}
}
if (blocked)
{
for(unsigned i=0;i < Vertices.Size(); i++)
{
Vertices[i]->set(PrevPts[i].pos.X, PrevPts[i].pos.Y);
2016-03-01 15:47:10 +00:00
}
UpdateBBox();
LinkPolyobj();
return false;
}
}
Angle += angle;
2016-03-01 15:47:10 +00:00
LinkPolyobj();
ClearSubsectorLinks();
RecalcActorFloorCeil(Bounds | oldbounds);
return true;
}
//==========================================================================
//
// UnLinkPolyobj
//
//==========================================================================
void FPolyObj::UnLinkPolyobj ()
{
polyblock_t *link;
int i, j;
int index;
// remove the polyobj from each blockmap section
for(j = bbox[BOXBOTTOM]; j <= bbox[BOXTOP]; j++)
{
index = j*Level->blockmap.bmapwidth;
2016-03-01 15:47:10 +00:00
for(i = bbox[BOXLEFT]; i <= bbox[BOXRIGHT]; i++)
{
if(i >= 0 && i < Level->blockmap.bmapwidth && j >= 0 && j < Level->blockmap.bmapheight)
2016-03-01 15:47:10 +00:00
{
link = Level->PolyBlockMap[index+i];
while(link != nullptr && link->polyobj != this)
2016-03-01 15:47:10 +00:00
{
link = link->next;
}
if(link == nullptr)
2016-03-01 15:47:10 +00:00
{ // polyobj not located in the link cell
continue;
}
link->polyobj = nullptr;
2016-03-01 15:47:10 +00:00
}
}
}
}
//==========================================================================
//
// CheckMobjBlocking
//
//==========================================================================
bool FPolyObj::CheckMobjBlocking (side_t *sd)
{
static TArray<AActor *> checker;
FBlockNode *block;
AActor *mobj;
int i, j, k;
int left, right, top, bottom;
line_t *ld;
bool blocked;
bool performBlockingThrust;
int bmapwidth = Level->blockmap.bmapwidth;
int bmapheight = Level->blockmap.bmapheight;
2016-03-01 15:47:10 +00:00
ld = sd->linedef;
top = Level->blockmap.GetBlockY(ld->bbox[BOXTOP]);
bottom = Level->blockmap.GetBlockY(ld->bbox[BOXBOTTOM]);
left = Level->blockmap.GetBlockX(ld->bbox[BOXLEFT]);
right = Level->blockmap.GetBlockX(ld->bbox[BOXRIGHT]);
2016-03-01 15:47:10 +00:00
blocked = false;
checker.Clear();
bottom = bottom < 0 ? 0 : bottom;
bottom = bottom >= bmapheight ? bmapheight-1 : bottom;
top = top < 0 ? 0 : top;
top = top >= bmapheight ? bmapheight-1 : top;
left = left < 0 ? 0 : left;
left = left >= bmapwidth ? bmapwidth-1 : left;
right = right < 0 ? 0 : right;
right = right >= bmapwidth ? bmapwidth-1 : right;
for (j = bottom*bmapwidth; j <= top*bmapwidth; j += bmapwidth)
{
for (i = left; i <= right; i++)
{
for (block = Level->blockmap.blocklinks[j+i]; block != nullptr; block = block->NextActor)
2016-03-01 15:47:10 +00:00
{
mobj = block->Me;
for (k = (int)checker.Size()-1; k >= 0; --k)
{
if (checker[k] == mobj)
{
break;
}
}
if (k < 0)
{
checker.Push (mobj);
if ((mobj->flags&MF_SOLID) && !(mobj->flags&MF_NOCLIP))
{
FLineOpening open;
open.top = LINEOPEN_MAX;
open.bottom = LINEOPEN_MIN;
2016-03-01 15:47:10 +00:00
// [TN] Check wether this actor gets blocked by the line.
if (ld->backsector != nullptr &&
2016-03-01 15:47:10 +00:00
!(ld->flags & (ML_BLOCKING|ML_BLOCKEVERYTHING))
&& !(ld->flags & ML_BLOCK_PLAYERS && (mobj->player || (mobj->flags8 & MF8_BLOCKASPLAYER)))
2016-03-01 15:47:10 +00:00
&& !(ld->flags & ML_BLOCKMONSTERS && mobj->flags3 & MF3_ISMONSTER)
&& !((mobj->flags & MF_FLOAT) && (ld->flags & ML_BLOCK_FLOATERS))
&& (!(ld->flags & ML_3DMIDTEX) ||
(!P_LineOpening_3dMidtex(mobj, ld, open) &&
(mobj->Top() < open.top)
2016-03-20 18:52:35 +00:00
) || (open.abovemidtex && mobj->Z() > mobj->floorz))
2016-03-01 15:47:10 +00:00
)
{
// [BL] We can't just continue here since we must
// determine if the line's backsector is going to
// be blocked.
performBlockingThrust = false;
}
else
{
performBlockingThrust = true;
}
DVector2 pos = mobj->PosRelative(ld);
FBoundingBox box(pos.X, pos.Y, mobj->radius);
2016-03-01 15:47:10 +00:00
2016-03-30 23:22:49 +00:00
if (!box.inRange(ld) || box.BoxOnLineSide(ld) != -1)
2016-03-01 15:47:10 +00:00
{
continue;
}
if (ld->isLinePortal())
{
// Fixme: this still needs to figure out if the polyobject move made the player cross the portal line.
if (P_TryMove(mobj, mobj->Pos(), false))
{
continue;
}
}
2016-03-01 15:47:10 +00:00
// We have a two-sided linedef so we should only check one side
// so that the thrust from both sides doesn't cancel each other out.
// Best use the one facing the player and ignore the back side.
if (ld->sidedef[1] != nullptr)
2016-03-01 15:47:10 +00:00
{
int side = P_PointOnLineSidePrecise(mobj->Pos(), ld);
2016-03-01 15:47:10 +00:00
if (ld->sidedef[side] != sd)
{
continue;
}
// [BL] See if we hit below the floor/ceiling of the poly.
else if(!performBlockingThrust && (
mobj->Z() < ld->sidedef[!side]->sector->GetSecPlane(sector_t::floor).ZatPoint(mobj) ||
mobj->Top() > ld->sidedef[!side]->sector->GetSecPlane(sector_t::ceiling).ZatPoint(mobj)
2016-03-01 15:47:10 +00:00
))
{
performBlockingThrust = true;
}
}
if(performBlockingThrust)
{
ThrustMobj (mobj, sd);
blocked = true;
}
else
continue;
}
}
}
}
}
return blocked;
}
//==========================================================================
//
// LinkPolyobj
//
//==========================================================================
void FPolyObj::LinkPolyobj ()
{
polyblock_t **link;
polyblock_t *tempLink;
int bmapwidth = Level->blockmap.bmapwidth;
int bmapheight = Level->blockmap.bmapheight;
2016-03-01 15:47:10 +00:00
// calculate the polyobj bbox
Bounds.ClearBox();
for(unsigned i = 0; i < Sidedefs.Size(); i++)
{
vertex_t *vt;
vt = Sidedefs[i]->linedef->v1;
2016-03-31 08:38:54 +00:00
Bounds.AddToBox(vt->fPos());
2016-03-01 15:47:10 +00:00
vt = Sidedefs[i]->linedef->v2;
2016-03-31 08:38:54 +00:00
Bounds.AddToBox(vt->fPos());
2016-03-01 15:47:10 +00:00
}
bbox[BOXRIGHT] = Level->blockmap.GetBlockX(Bounds.Right());
bbox[BOXLEFT] = Level->blockmap.GetBlockX(Bounds.Left());
bbox[BOXTOP] = Level->blockmap.GetBlockY(Bounds.Top());
bbox[BOXBOTTOM] = Level->blockmap.GetBlockY(Bounds.Bottom());
2016-03-01 15:47:10 +00:00
// add the polyobj to each blockmap section
for(int j = bbox[BOXBOTTOM]*bmapwidth; j <= bbox[BOXTOP]*bmapwidth;
j += bmapwidth)
{
for(int i = bbox[BOXLEFT]; i <= bbox[BOXRIGHT]; i++)
{
if(i >= 0 && i < bmapwidth && j >= 0 && j < bmapheight*bmapwidth)
{
link = &Level->PolyBlockMap[j+i];
2016-03-01 15:47:10 +00:00
if(!(*link))
{ // CreateThinker a new link at the current block cell
2016-03-01 15:47:10 +00:00
*link = new polyblock_t;
(*link)->next = nullptr;
(*link)->prev = nullptr;
2016-03-01 15:47:10 +00:00
(*link)->polyobj = this;
continue;
}
else
{
tempLink = *link;
while(tempLink->next != nullptr && tempLink->polyobj != nullptr)
2016-03-01 15:47:10 +00:00
{
tempLink = tempLink->next;
}
}
if(tempLink->polyobj == nullptr)
2016-03-01 15:47:10 +00:00
{
tempLink->polyobj = this;
continue;
}
else
{
tempLink->next = new polyblock_t;
tempLink->next->next = nullptr;
2016-03-01 15:47:10 +00:00
tempLink->next->prev = tempLink;
tempLink->next->polyobj = this;
}
}
// else, don't link the polyobj, since it's off the map
}
}
}
//===========================================================================
//
// FPolyObj :: RecalcActorFloorCeil
//
// For each actor within the bounding box, recalculate its floorz, ceilingz,
// and related values.
//
//===========================================================================
void FPolyObj::RecalcActorFloorCeil(FBoundingBox bounds) const
{
FBlockThingsIterator it(Level, bounds);
2016-03-01 15:47:10 +00:00
AActor *actor;
while ((actor = it.Next()) != nullptr)
2016-03-01 15:47:10 +00:00
{
// skip everything outside the bounding box.
if (actor->X() + actor->radius <= bounds.Left() ||
actor->X() - actor->radius >= bounds.Right() ||
actor->Y() + actor->radius <= bounds.Bottom() ||
actor->Y() - actor->radius >= bounds.Top())
{
continue;
}
// Todo: Be a little more thorough with what gets altered here
// because this can dislocate a lot of items that were spawned on
// the lower side of a sector boundary.
2016-03-01 15:47:10 +00:00
P_FindFloorCeiling(actor);
}
}
//===========================================================================
//
// PO_ClosestPoint
//
// Given a point (x,y), returns the point (ox,oy) on the polyobject's walls
// that is nearest to (x,y). Also returns the seg this point came from.
//
//===========================================================================
void FPolyObj::ClosestPoint(const DVector2 &fpos, DVector2 &out, side_t **side) const
2016-03-01 15:47:10 +00:00
{
unsigned int i;
double x = fpos.X, y = fpos.Y;
2016-03-01 15:47:10 +00:00
double bestdist = HUGE_VAL;
double bestx = 0, besty = 0;
side_t *bestline = nullptr;
2016-03-01 15:47:10 +00:00
for (i = 0; i < Sidedefs.Size(); ++i)
{
vertex_t *v1 = Sidedefs[i]->V1();
vertex_t *v2 = Sidedefs[i]->V2();
double a = v2->fX() - v1->fX();
double b = v2->fY() - v1->fY();
2016-03-01 15:47:10 +00:00
double den = a*a + b*b;
double ix, iy, dist;
if (den == 0)
{ // Line is actually a point!
ix = v1->fX();
iy = v1->fY();
2016-03-01 15:47:10 +00:00
}
else
{
double num = (x - v1->fX()) * a + (y - v1->fY()) * b;
2016-03-01 15:47:10 +00:00
double u = num / den;
if (u <= 0)
{
ix = v1->fX();
iy = v1->fY();
2016-03-01 15:47:10 +00:00
}
else if (u >= 1)
{
ix = v2->fX();
iy = v2->fY();
2016-03-01 15:47:10 +00:00
}
else
{
ix = v1->fX() + u * a;
iy = v1->fY() + u * b;
2016-03-01 15:47:10 +00:00
}
}
a = (ix - x);
b = (iy - y);
dist = a*a + b*b;
if (dist < bestdist)
{
bestdist = dist;
bestx = ix;
besty = iy;
bestline = Sidedefs[i];
}
}
out = { bestx, besty };
if (side != nullptr)
2016-03-01 15:47:10 +00:00
{
*side = bestline;
}
}
//==========================================================================
//
// PO_Busy
//
//==========================================================================
bool PO_Busy (FLevelLocals *Level, int polyobj)
2016-03-01 15:47:10 +00:00
{
FPolyObj *poly;
poly = Level->GetPolyobj(polyobj);
return (poly != nullptr && poly->specialdata != nullptr);
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
void FPolyObj::ClearSubsectorLinks()
{
while (subsectorlinks != nullptr)
2016-03-01 15:47:10 +00:00
{
assert(subsectorlinks->state == 1337);
FPolyNode *next = subsectorlinks->snext;
if (subsectorlinks->pnext != nullptr)
2016-03-01 15:47:10 +00:00
{
assert(subsectorlinks->pnext->state == 1337);
subsectorlinks->pnext->pprev = subsectorlinks->pprev;
}
if (subsectorlinks->pprev != nullptr)
2016-03-01 15:47:10 +00:00
{
assert(subsectorlinks->pprev->state == 1337);
subsectorlinks->pprev->pnext = subsectorlinks->pnext;
}
else
{
subsectorlinks->subsector->polys = subsectorlinks->pnext;
}
if (subsectorlinks->subsector->BSP != nullptr)
2016-03-01 15:47:10 +00:00
{
subsectorlinks->subsector->BSP->bDirty = true;
}
subsectorlinks->state = -1;
delete subsectorlinks;
subsectorlinks = next;
}
subsectorlinks = nullptr;
2016-03-01 15:47:10 +00:00
}
void FLevelLocals::ClearAllSubsectorLinks()
2016-03-01 15:47:10 +00:00
{
for(auto &poly : Polyobjects)
2016-03-01 15:47:10 +00:00
{
poly.ClearSubsectorLinks();
2016-03-01 15:47:10 +00:00
}
ReleaseAllPolyNodes();
}
//==========================================================================
//
// GetIntersection
//
// adapted from P_InterceptVector
//
//==========================================================================
static bool GetIntersection(FPolySeg *seg, node_t *bsp, FPolyVertex *v)
{
double frac;
double num;
double den;
double v2x = seg->v1.pos.X;
double v2y = seg->v1.pos.Y;
double v2dx = seg->v2.pos.X - v2x;
double v2dy = seg->v2.pos.Y - v2y;
double v1x = FIXED2DBL(bsp->x);
double v1y = FIXED2DBL(bsp->y);
double v1dx = FIXED2DBL(bsp->dx);
double v1dy = FIXED2DBL(bsp->dy);
2016-03-01 15:47:10 +00:00
den = v1dy*v2dx - v1dx*v2dy;
if (den == 0)
return false; // parallel
num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx;
frac = num / den;
if (frac < 0. || frac > 1.) return false;
v->pos.X = v2x + frac * v2dx;
v->pos.Y = v2y + frac * v2dy;
2016-03-01 15:47:10 +00:00
return true;
}
//==========================================================================
//
// PartitionDistance
//
// Determine the distance of a vertex to a node's partition line.
//
//==========================================================================
static double PartitionDistance(FPolyVertex *vt, node_t *node)
{
return fabs(FIXED2DBL(-node->dy) * (vt->pos.X - FIXED2DBL(node->x)) + FIXED2DBL(node->dx) * (vt->pos.Y - FIXED2DBL(node->y))) / node->len;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// AddToBBox
//
//==========================================================================
2016-05-01 03:37:02 +00:00
static void AddToBBox(float child[4], float parent[4])
2016-03-01 15:47:10 +00:00
{
if (child[BOXTOP] > parent[BOXTOP])
{
parent[BOXTOP] = child[BOXTOP];
}
if (child[BOXBOTTOM] < parent[BOXBOTTOM])
{
parent[BOXBOTTOM] = child[BOXBOTTOM];
}
if (child[BOXLEFT] < parent[BOXLEFT])
{
parent[BOXLEFT] = child[BOXLEFT];
}
if (child[BOXRIGHT] > parent[BOXRIGHT])
{
parent[BOXRIGHT] = child[BOXRIGHT];
}
}
//==========================================================================
//
// AddToBBox
//
//==========================================================================
2016-05-01 03:37:02 +00:00
static void AddToBBox(FPolyVertex *v, float bbox[4])
2016-03-01 15:47:10 +00:00
{
2016-05-01 03:37:02 +00:00
float x = float(v->pos.X);
float y = float(v->pos.Y);
if (x < bbox[BOXLEFT])
2016-03-01 15:47:10 +00:00
{
bbox[BOXLEFT] = x;
2016-03-01 15:47:10 +00:00
}
if (x > bbox[BOXRIGHT])
2016-03-01 15:47:10 +00:00
{
bbox[BOXRIGHT] = x;
2016-03-01 15:47:10 +00:00
}
if (y < bbox[BOXBOTTOM])
2016-03-01 15:47:10 +00:00
{
bbox[BOXBOTTOM] = y;
2016-03-01 15:47:10 +00:00
}
if (y > bbox[BOXTOP])
2016-03-01 15:47:10 +00:00
{
bbox[BOXTOP] = y;
2016-03-01 15:47:10 +00:00
}
}
//==========================================================================
//
// SplitPoly
//
//==========================================================================
2016-05-01 03:37:02 +00:00
static void SplitPoly(FPolyNode *pnode, void *node, float bbox[4])
2016-03-01 15:47:10 +00:00
{
static TArray<FPolySeg> lists[2];
static const double POLY_EPSILON = 0.3125;
if (!((size_t)node & 1)) // Keep going until found a subsector
{
node_t *bsp = (node_t *)node;
int centerside = R_PointOnSide(pnode->poly->CenterSpot.pos, bsp);
2016-03-01 15:47:10 +00:00
lists[0].Clear();
lists[1].Clear();
for(unsigned i=0;i<pnode->segs.Size(); i++)
{
FPolySeg *seg = &pnode->segs[i];
// Parts of the following code were taken from Eternity and are
// being used with permission.
// get distance of vertices from partition line
// If the distance is too small, we may decide to
// change our idea of sidedness.
double dist_v1 = PartitionDistance(&seg->v1, bsp);
double dist_v2 = PartitionDistance(&seg->v2, bsp);
// If the distances are less than epsilon, consider the points as being
// on the same side as the polyobj origin. Why? People like to build
// polyobject doors flush with their door tracks. This breaks using the
// usual assumptions.
// Addition to Eternity code: We must also check any seg with only one
// vertex inside the epsilon threshold. If not, these lines will get split but
// adjoining ones with both vertices inside the threshold won't thus messing up
// the order in which they get drawn.
if(dist_v1 <= POLY_EPSILON)
{
if (dist_v2 <= POLY_EPSILON)
{
lists[centerside].Push(*seg);
}
else
{
int side = R_PointOnSide(seg->v2.pos, bsp);
2016-03-01 15:47:10 +00:00
lists[side].Push(*seg);
}
}
else if (dist_v2 <= POLY_EPSILON)
{
int side = R_PointOnSide(seg->v1.pos, bsp);
2016-03-01 15:47:10 +00:00
lists[side].Push(*seg);
}
else
{
int side1 = R_PointOnSide(seg->v1.pos, bsp);
int side2 = R_PointOnSide(seg->v2.pos, bsp);
2016-03-01 15:47:10 +00:00
if(side1 != side2)
{
// if the partition line crosses this seg, we must split it.
FPolyVertex vert;
if (GetIntersection(seg, bsp, &vert))
{
lists[0].Push(*seg);
lists[1].Push(*seg);
lists[side1].Last().v2 = vert;
lists[side2].Last().v1 = vert;
}
else
{
// should never happen
lists[side1].Push(*seg);
}
}
else
{
// both points on the same side.
lists[side1].Push(*seg);
}
}
}
if (lists[1].Size() == 0)
{
SplitPoly(pnode, bsp->children[0], bsp->bbox[0]);
AddToBBox(bsp->bbox[0], bbox);
}
else if (lists[0].Size() == 0)
{
SplitPoly(pnode, bsp->children[1], bsp->bbox[1]);
AddToBBox(bsp->bbox[1], bbox);
}
else
{
// create the new node
FPolyNode *newnode = NewPolyNode();
newnode->poly = pnode->poly;
newnode->segs = lists[1];
// set segs for original node
pnode->segs = lists[0];
// recurse back side
SplitPoly(newnode, bsp->children[1], bsp->bbox[1]);
// recurse front side
SplitPoly(pnode, bsp->children[0], bsp->bbox[0]);
AddToBBox(bsp->bbox[0], bbox);
AddToBBox(bsp->bbox[1], bbox);
}
}
else
{
// we reached a subsector so we can link the node with this subsector
subsector_t *sub = (subsector_t *)((uint8_t *)node - 1);
2016-03-01 15:47:10 +00:00
// Link node to subsector
pnode->pnext = sub->polys;
if (pnode->pnext != nullptr)
2016-03-01 15:47:10 +00:00
{
assert(pnode->pnext->state == 1337);
pnode->pnext->pprev = pnode;
}
pnode->pprev = nullptr;
2016-03-01 15:47:10 +00:00
sub->polys = pnode;
// link node to polyobject
pnode->snext = pnode->poly->subsectorlinks;
pnode->poly->subsectorlinks = pnode;
pnode->subsector = sub;
// calculate bounding box for this polynode
assert(pnode->segs.Size() != 0);
2016-05-01 03:37:02 +00:00
float subbbox[4] = { FLT_MIN, FLT_MAX, FLT_MAX, FLT_MIN };
2016-03-01 15:47:10 +00:00
for (unsigned i = 0; i < pnode->segs.Size(); ++i)
{
AddToBBox(&pnode->segs[i].v1, subbbox);
AddToBBox(&pnode->segs[i].v2, subbbox);
}
// Potentially expand the parent node's bounding box to contain these bits of polyobject.
AddToBBox(subbbox, bbox);
}
}
//==========================================================================
//
//
//
//==========================================================================
void FPolyObj::CreateSubsectorLinks()
{
FPolyNode *node = NewPolyNode();
// Even though we don't care about it, we need to initialize this
// bounding box to something so that Valgrind won't complain about it
// when SplitPoly modifies it.
2016-05-01 03:37:02 +00:00
float dummybbox[4] = { 0 };
2016-03-01 15:47:10 +00:00
node->poly = this;
node->segs.Resize(Sidedefs.Size());
for(unsigned i=0; i<Sidedefs.Size(); i++)
{
FPolySeg *seg = &node->segs[i];
side_t *side = Sidedefs[i];
seg->v1 = side->V1();
seg->v2 = side->V2();
seg->wall = side;
}
if (!(Level->i_compatflags & COMPATF_POLYOBJ))
2016-03-01 15:47:10 +00:00
{
SplitPoly(node, Level->HeadNode(), dummybbox);
2016-03-01 15:47:10 +00:00
}
else
{
subsector_t *sub = CenterSubsector;
// Link node to subsector
node->pnext = sub->polys;
if (node->pnext != nullptr)
2016-03-01 15:47:10 +00:00
{
assert(node->pnext->state == 1337);
node->pnext->pprev = node;
}
node->pprev = nullptr;
2016-03-01 15:47:10 +00:00
sub->polys = node;
// link node to polyobject
node->snext = node->poly->subsectorlinks;
node->poly->subsectorlinks = node;
node->subsector = sub;
}
}
//==========================================================================
//
//
//
//==========================================================================
void PO_LinkToSubsectors(FLevelLocals *Level)
2016-03-01 15:47:10 +00:00
{
for(auto &poly : Level->Polyobjects)
2016-03-01 15:47:10 +00:00
{
if (poly.subsectorlinks == nullptr)
2016-03-01 15:47:10 +00:00
{
poly.CreateSubsectorLinks();
2016-03-01 15:47:10 +00:00
}
}
}
//==========================================================================
//
// NewPolyNode
//
//==========================================================================
static FPolyNode *NewPolyNode()
{
FPolyNode *node;
if (FreePolyNodes != nullptr)
2016-03-01 15:47:10 +00:00
{
node = FreePolyNodes;
FreePolyNodes = node->pnext;
}
else
{
node = new FPolyNode;
}
node->state = 1337;
node->poly = nullptr;
node->pnext = nullptr;
node->pprev = nullptr;
node->subsector = nullptr;
node->snext = nullptr;
2016-03-01 15:47:10 +00:00
return node;
}
//==========================================================================
//
// FreePolyNode
//
//==========================================================================
void FreePolyNode(FPolyNode *node)
{
node->segs.Clear();
node->pnext = FreePolyNodes;
FreePolyNodes = node;
}
//==========================================================================
//
// ReleaseAllPolyNodes
//
//==========================================================================
void ReleaseAllPolyNodes()
{
FPolyNode *node, *next;
for (node = FreePolyNodes; node != nullptr; node = next)
2016-03-01 15:47:10 +00:00
{
next = node->pnext;
delete node;
}
}
//==========================================================================
//
// FPolyMirrorIterator Constructor
//
// This class is used to avoid infinitely looping on cyclical chains of
// mirrored polyobjects.
//
//==========================================================================
FPolyMirrorIterator::FPolyMirrorIterator(FPolyObj *poly)
{
CurPoly = poly;
if (poly != nullptr)
2016-03-01 15:47:10 +00:00
{
UsedPolys[0] = poly->tag;
NumUsedPolys = 1;
}
else
{
NumUsedPolys = 0;
}
}
//==========================================================================
//
// FPolyMirrorIterator :: NextMirror
//
// Returns the polyobject that mirrors the current one, or nullptr if there
2016-03-01 15:47:10 +00:00
// is no mirroring polyobject, or there is a mirroring polyobject but it was
// already returned.
//
//==========================================================================
FPolyObj *FPolyMirrorIterator::NextMirror()
{
FPolyObj *poly = CurPoly, *nextpoly;
if (poly == nullptr)
2016-03-01 15:47:10 +00:00
{
return nullptr;
2016-03-01 15:47:10 +00:00
}
// Do the work to decide which polyobject to return the next time this
// function is called.
int mirror = poly->GetMirror(), i;
nextpoly = nullptr;
2016-03-01 15:47:10 +00:00
// Is there a mirror and we have room to remember it?
if (mirror != 0 && NumUsedPolys != countof(UsedPolys))
{
// Has this polyobject been returned already?
for (i = 0; i < NumUsedPolys; ++i)
{
if (UsedPolys[i] == mirror)
{
break; // Yes, it has been returned.
}
}
if (i == NumUsedPolys)
{ // No, it has not been returned.
UsedPolys[NumUsedPolys++] = mirror;
nextpoly = poly->Level->GetPolyobj(mirror);
if (nextpoly == nullptr)
2016-03-01 15:47:10 +00:00
{
Printf("Invalid mirror polyobj num %d for polyobj num %d\n", mirror, UsedPolys[i - 1]);
}
}
}
CurPoly = nextpoly;
return poly;
}