2016-03-01 15:47:10 +00:00
//**************************************************************************
//**
//** PO_MAN.C : Heretic 2 : Raven Software, Corp.
//**
//** $RCSfile: po_man.c,v $
//** $Revision: 1.22 $
//** $Date: 95/09/28 18:20:56 $
//** $Author: cjr $
//**
//**************************************************************************
// HEADER FILES ------------------------------------------------------------
# include "doomdef.h"
# include "p_local.h"
# include "i_system.h"
# include "w_wad.h"
# include "m_swap.h"
# include "m_bbox.h"
# include "tables.h"
# include "s_sndseq.h"
# include "a_sharedglobal.h"
# include "p_3dmidtex.h"
# include "p_lnspec.h"
# include "r_data/r_interpolate.h"
# include "g_level.h"
# include "po_man.h"
# include "p_setup.h"
# include "vectors.h"
# include "farchive.h"
# include "p_blockmap.h"
# include "p_maputl.h"
# include "r_utility.h"
// MACROS ------------------------------------------------------------------
# define PO_MAXPOLYSEGS 64
// TYPES -------------------------------------------------------------------
inline vertex_t * side_t : : V1 ( ) const
{
return this = = linedef - > sidedef [ 0 ] ? linedef - > v1 : linedef - > v2 ;
}
inline vertex_t * side_t : : V2 ( ) const
{
return this = = linedef - > sidedef [ 0 ] ? linedef - > v2 : linedef - > v1 ;
}
FArchive & operator < < ( FArchive & arc , FPolyObj * & poly )
{
return arc . SerializePointer ( polyobjs , ( BYTE * * ) & poly , sizeof ( FPolyObj ) ) ;
}
FArchive & operator < < ( FArchive & arc , const FPolyObj * & poly )
{
return arc . SerializePointer ( polyobjs , ( BYTE * * ) & poly , sizeof ( FPolyObj ) ) ;
}
inline FArchive & operator < < ( FArchive & arc , podoortype_t & type )
{
BYTE val = ( BYTE ) type ;
arc < < val ;
type = ( podoortype_t ) val ;
return arc ;
}
class DPolyAction : public DThinker
{
DECLARE_CLASS ( DPolyAction , DThinker )
HAS_OBJECT_POINTERS
public :
DPolyAction ( int polyNum ) ;
void Serialize ( FArchive & arc ) ;
void Destroy ( ) ;
void Stop ( ) ;
int GetSpeed ( ) const { return m_Speed ; }
void StopInterpolation ( ) ;
protected :
DPolyAction ( ) ;
int m_PolyObj ;
int m_Speed ;
int m_Dist ;
TObjPtr < DInterpolation > m_Interpolation ;
void SetInterpolation ( ) ;
} ;
class DRotatePoly : public DPolyAction
{
DECLARE_CLASS ( DRotatePoly , DPolyAction )
public :
DRotatePoly ( int polyNum ) ;
void Tick ( ) ;
private :
DRotatePoly ( ) ;
friend bool EV_RotatePoly ( line_t * line , int polyNum , int speed , int byteAngle , int direction , bool overRide ) ;
} ;
class DMovePoly : public DPolyAction
{
DECLARE_CLASS ( DMovePoly , DPolyAction )
public :
DMovePoly ( int polyNum ) ;
void Serialize ( FArchive & arc ) ;
void Tick ( ) ;
protected :
DMovePoly ( ) ;
int m_Angle ;
fixed_t m_xSpeed ; // for sliding walls
fixed_t m_ySpeed ;
friend bool EV_MovePoly ( line_t * line , int polyNum , int speed , angle_t angle , fixed_t dist , bool overRide ) ;
} ;
class DMovePolyTo : public DPolyAction
{
DECLARE_CLASS ( DMovePolyTo , DPolyAction )
public :
DMovePolyTo ( int polyNum ) ;
void Serialize ( FArchive & arc ) ;
void Tick ( ) ;
protected :
DMovePolyTo ( ) ;
fixed_t m_xSpeed ;
fixed_t m_ySpeed ;
fixed_t m_xTarget ;
fixed_t m_yTarget ;
friend bool EV_MovePolyTo ( line_t * line , int polyNum , int speed , fixed_t x , fixed_t y , bool overRide ) ;
} ;
class DPolyDoor : public DMovePoly
{
DECLARE_CLASS ( DPolyDoor , DMovePoly )
public :
DPolyDoor ( int polyNum , podoortype_t type ) ;
void Serialize ( FArchive & arc ) ;
void Tick ( ) ;
protected :
int m_Direction ;
int m_TotalDist ;
int m_Tics ;
int m_WaitTics ;
podoortype_t m_Type ;
bool m_Close ;
friend bool EV_OpenPolyDoor ( line_t * line , int polyNum , int speed , angle_t angle , int delay , int distance , podoortype_t type ) ;
private :
DPolyDoor ( ) ;
} ;
class FPolyMirrorIterator
{
FPolyObj * CurPoly ;
int UsedPolys [ 100 ] ; // tracks mirrored polyobjects we've seen
int NumUsedPolys ;
public :
FPolyMirrorIterator ( FPolyObj * poly ) ;
FPolyObj * NextMirror ( ) ;
} ;
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void PO_Init ( void ) ;
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void UnLinkPolyobj ( FPolyObj * po ) ;
static void LinkPolyobj ( FPolyObj * po ) ;
static bool CheckMobjBlocking ( side_t * seg , FPolyObj * po ) ;
static void InitBlockMap ( void ) ;
static void IterFindPolySides ( FPolyObj * po , side_t * side ) ;
static void SpawnPolyobj ( int index , int tag , int type ) ;
static void TranslateToStartSpot ( int tag , int originX , int originY ) ;
static void DoMovePolyobj ( FPolyObj * po , int x , int y ) ;
static void InitSegLists ( ) ;
static void KillSegLists ( ) ;
static FPolyNode * NewPolyNode ( ) ;
static void FreePolyNode ( ) ;
static void ReleaseAllPolyNodes ( ) ;
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern seg_t * segs ;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
polyblock_t * * PolyBlockMap ;
FPolyObj * polyobjs ; // list of all poly-objects on the level
int po_NumPolyobjs ;
polyspawns_t * polyspawns ; // [RH] Let P_SpawnMapThings() find our thingies for us
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static TArray < SDWORD > KnownPolySides ;
static FPolyNode * FreePolyNodes ;
// CODE --------------------------------------------------------------------
//==========================================================================
//
//
//
//==========================================================================
IMPLEMENT_POINTY_CLASS ( DPolyAction )
DECLARE_POINTER ( m_Interpolation )
END_POINTERS
DPolyAction : : DPolyAction ( )
{
}
void DPolyAction : : Serialize ( FArchive & arc )
{
Super : : Serialize ( arc ) ;
arc < < m_PolyObj < < m_Speed < < m_Dist < < m_Interpolation ;
}
DPolyAction : : DPolyAction ( int polyNum )
{
m_PolyObj = polyNum ;
m_Speed = 0 ;
m_Dist = 0 ;
SetInterpolation ( ) ;
}
void DPolyAction : : Destroy ( )
{
FPolyObj * poly = PO_GetPolyobj ( m_PolyObj ) ;
if ( poly - > specialdata = = this )
{
poly - > specialdata = NULL ;
}
StopInterpolation ( ) ;
Super : : Destroy ( ) ;
}
void DPolyAction : : Stop ( )
{
FPolyObj * poly = PO_GetPolyobj ( m_PolyObj ) ;
SN_StopSequence ( poly ) ;
Destroy ( ) ;
}
void DPolyAction : : SetInterpolation ( )
{
FPolyObj * poly = PO_GetPolyobj ( m_PolyObj ) ;
m_Interpolation = poly - > SetInterpolation ( ) ;
}
void DPolyAction : : StopInterpolation ( )
{
if ( m_Interpolation ! = NULL )
{
m_Interpolation - > DelRef ( ) ;
m_Interpolation = NULL ;
}
}
//==========================================================================
//
//
//
//==========================================================================
IMPLEMENT_CLASS ( DRotatePoly )
DRotatePoly : : DRotatePoly ( )
{
}
DRotatePoly : : DRotatePoly ( int polyNum )
: Super ( polyNum )
{
}
//==========================================================================
//
//
//
//==========================================================================
IMPLEMENT_CLASS ( DMovePoly )
DMovePoly : : DMovePoly ( )
{
}
void DMovePoly : : Serialize ( FArchive & arc )
{
Super : : Serialize ( arc ) ;
arc < < m_Angle < < m_xSpeed < < m_ySpeed ;
}
DMovePoly : : DMovePoly ( int polyNum )
: Super ( polyNum )
{
m_Angle = 0 ;
m_xSpeed = 0 ;
m_ySpeed = 0 ;
}
//==========================================================================
//
//
//
//
//==========================================================================
IMPLEMENT_CLASS ( DMovePolyTo )
DMovePolyTo : : DMovePolyTo ( )
{
}
void DMovePolyTo : : Serialize ( FArchive & arc )
{
Super : : Serialize ( arc ) ;
arc < < m_xSpeed < < m_ySpeed < < m_xTarget < < m_yTarget ;
}
DMovePolyTo : : DMovePolyTo ( int polyNum )
: Super ( polyNum )
{
m_xSpeed = 0 ;
m_ySpeed = 0 ;
m_xTarget = 0 ;
m_yTarget = 0 ;
}
//==========================================================================
//
//
//
//==========================================================================
IMPLEMENT_CLASS ( DPolyDoor )
DPolyDoor : : DPolyDoor ( )
{
}
void DPolyDoor : : Serialize ( FArchive & arc )
{
Super : : Serialize ( arc ) ;
arc < < m_Direction < < m_TotalDist < < m_Tics < < m_WaitTics < < m_Type < < m_Close ;
}
DPolyDoor : : DPolyDoor ( int polyNum , podoortype_t type )
: Super ( polyNum ) , m_Type ( type )
{
m_Direction = 0 ;
m_TotalDist = 0 ;
m_Tics = 0 ;
m_WaitTics = 0 ;
m_Close = false ;
}
// ===== Polyobj Event Code =====
//==========================================================================
//
// T_RotatePoly
//
//==========================================================================
void DRotatePoly : : Tick ( )
{
FPolyObj * poly = PO_GetPolyobj ( m_PolyObj ) ;
if ( poly = = NULL ) return ;
// Don't let non-perpetual polyobjs overshoot their targets.
if ( m_Dist ! = - 1 & & ( unsigned int ) m_Dist < ( unsigned int ) abs ( m_Speed ) )
{
m_Speed = m_Speed < 0 ? - m_Dist : m_Dist ;
}
if ( poly - > RotatePolyobj ( m_Speed ) )
{
if ( m_Dist = = - 1 )
{ // perpetual polyobj
return ;
}
m_Dist - = abs ( m_Speed ) ;
if ( m_Dist = = 0 )
{
SN_StopSequence ( poly ) ;
Destroy ( ) ;
}
}
}
//==========================================================================
//
// EV_RotatePoly
//
//==========================================================================
bool EV_RotatePoly ( line_t * line , int polyNum , int speed , int byteAngle ,
int direction , bool overRide )
{
DRotatePoly * pe = NULL ;
FPolyObj * poly ;
if ( ( poly = PO_GetPolyobj ( polyNum ) ) = = NULL )
{
Printf ( " EV_RotatePoly: Invalid polyobj num: %d \n " , polyNum ) ;
return false ;
}
FPolyMirrorIterator it ( poly ) ;
while ( ( poly = it . NextMirror ( ) ) ! = NULL )
{
if ( poly - > specialdata ! = NULL & & ! overRide )
{ // poly is already in motion
break ;
}
pe = new DRotatePoly ( poly - > tag ) ;
poly - > specialdata = pe ;
if ( byteAngle ! = 0 )
{
if ( byteAngle = = 255 )
{
pe - > m_Dist = ~ 0 ;
}
else
{
pe - > m_Dist = byteAngle * ( ANGLE_90 / 64 ) ; // Angle
}
}
else
{
pe - > m_Dist = ANGLE_MAX - 1 ;
}
pe - > m_Speed = speed * direction * ( ANGLE_90 / ( 64 < < 3 ) ) ;
SN_StartSequence ( poly , poly - > seqType , SEQ_DOOR , 0 ) ;
direction = - direction ; // Reverse the direction
}
return pe ! = NULL ; // Return true if something started moving.
}
//==========================================================================
//
// T_MovePoly
//
//==========================================================================
void DMovePoly : : Tick ( )
{
FPolyObj * poly = PO_GetPolyobj ( m_PolyObj ) ;
if ( poly ! = NULL )
{
if ( poly - > MovePolyobj ( m_xSpeed , m_ySpeed ) )
{
int absSpeed = abs ( m_Speed ) ;
m_Dist - = absSpeed ;
if ( m_Dist < = 0 )
{
SN_StopSequence ( poly ) ;
Destroy ( ) ;
}
else if ( m_Dist < absSpeed )
{
m_Speed = m_Dist * ( m_Speed < 0 ? - 1 : 1 ) ;
m_xSpeed = FixedMul ( m_Speed , finecosine [ m_Angle ] ) ;
m_ySpeed = FixedMul ( m_Speed , finesine [ m_Angle ] ) ;
}
}
}
}
//==========================================================================
//
// EV_MovePoly
//
//==========================================================================
bool EV_MovePoly ( line_t * line , int polyNum , int speed , angle_t angle ,
fixed_t dist , bool overRide )
{
DMovePoly * pe = NULL ;
FPolyObj * poly ;
angle_t an = angle ;
if ( ( poly = PO_GetPolyobj ( polyNum ) ) = = NULL )
{
Printf ( " EV_MovePoly: Invalid polyobj num: %d \n " , polyNum ) ;
return false ;
}
FPolyMirrorIterator it ( poly ) ;
while ( ( poly = it . NextMirror ( ) ) ! = NULL )
{
if ( poly - > specialdata ! = NULL & & ! overRide )
{ // poly is already in motion
break ;
}
pe = new DMovePoly ( poly - > tag ) ;
poly - > specialdata = pe ;
pe - > m_Dist = dist ; // Distance
pe - > m_Speed = speed ;
pe - > m_Angle = an > > ANGLETOFINESHIFT ;
pe - > m_xSpeed = FixedMul ( pe - > m_Speed , finecosine [ pe - > m_Angle ] ) ;
pe - > m_ySpeed = FixedMul ( pe - > m_Speed , finesine [ pe - > m_Angle ] ) ;
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 ( ) ;
}
an = an + ANGLE_180 ; // Reverse the angle.
}
return pe ! = NULL ; // Return true if something started moving.
}
//==========================================================================
//
// DMovePolyTo :: Tick
//
//==========================================================================
void DMovePolyTo : : Tick ( )
{
FPolyObj * poly = PO_GetPolyobj ( m_PolyObj ) ;
if ( poly ! = NULL )
{
if ( poly - > MovePolyobj ( m_xSpeed , m_ySpeed ) )
{
int absSpeed = abs ( m_Speed ) ;
m_Dist - = absSpeed ;
if ( m_Dist < = 0 )
{
SN_StopSequence ( poly ) ;
Destroy ( ) ;
}
else if ( m_Dist < absSpeed )
{
m_Speed = m_Dist * ( m_Speed < 0 ? - 1 : 1 ) ;
m_xSpeed = m_xTarget - poly - > StartSpot . x ;
m_ySpeed = m_yTarget - poly - > StartSpot . y ;
}
}
}
}
//==========================================================================
//
// EV_MovePolyTo
//
//==========================================================================
bool EV_MovePolyTo ( line_t * line , int polyNum , int speed , fixed_t targx , fixed_t targy , bool overRide )
{
DMovePolyTo * pe = NULL ;
FPolyObj * poly ;
2016-03-10 19:44:53 +00:00
DVector2 dist ;
2016-03-01 15:47:10 +00:00
double distlen ;
if ( ( poly = PO_GetPolyobj ( polyNum ) ) = = NULL )
{
Printf ( " EV_MovePolyTo: Invalid polyobj num: %d \n " , polyNum ) ;
return false ;
}
FPolyMirrorIterator it ( poly ) ;
dist . X = targx - poly - > StartSpot . x ;
dist . Y = targy - poly - > StartSpot . y ;
distlen = dist . MakeUnit ( ) ;
while ( ( poly = it . NextMirror ( ) ) ! = NULL )
{
if ( poly - > specialdata ! = NULL & & ! overRide )
{ // poly is already in motion
break ;
}
pe = new DMovePolyTo ( poly - > tag ) ;
poly - > specialdata = pe ;
pe - > m_Dist = xs_RoundToInt ( distlen ) ;
pe - > m_Speed = speed ;
pe - > m_xSpeed = xs_RoundToInt ( speed * dist . X ) ;
pe - > m_ySpeed = xs_RoundToInt ( speed * dist . Y ) ;
pe - > m_xTarget = xs_RoundToInt ( poly - > StartSpot . x + distlen * dist . X ) ;
pe - > m_yTarget = xs_RoundToInt ( poly - > StartSpot . y + distlen * dist . Y ) ;
if ( ( pe - > m_Dist / pe - > m_Speed ) < = 2 )
{
pe - > StopInterpolation ( ) ;
}
dist = - dist ; // reverse the direction
}
return pe ! = NULL ; // Return true if something started moving.
}
//==========================================================================
//
// T_PolyDoor
//
//==========================================================================
void DPolyDoor : : Tick ( )
{
int absSpeed ;
FPolyObj * poly = PO_GetPolyobj ( m_PolyObj ) ;
if ( poly = = NULL ) return ;
if ( m_Tics )
{
if ( ! - - m_Tics )
{
SN_StartSequence ( poly , poly - > seqType , SEQ_DOOR , m_Close ) ;
}
return ;
}
switch ( m_Type )
{
case PODOOR_SLIDE :
if ( m_Dist < = 0 | | poly - > MovePolyobj ( m_xSpeed , m_ySpeed ) )
{
absSpeed = abs ( m_Speed ) ;
m_Dist - = absSpeed ;
if ( m_Dist < = 0 )
{
SN_StopSequence ( poly ) ;
if ( ! m_Close )
{
m_Dist = m_TotalDist ;
m_Close = true ;
m_Tics = m_WaitTics ;
m_Direction = ( ANGLE_MAX > > ANGLETOFINESHIFT ) - m_Direction ;
m_xSpeed = - m_xSpeed ;
m_ySpeed = - m_ySpeed ;
}
else
{
Destroy ( ) ;
}
}
}
else
{
if ( poly - > crush | | ! m_Close )
{ // continue moving if the poly is a crusher, or is opening
return ;
}
else
{ // open back up
m_Dist = m_TotalDist - m_Dist ;
m_Direction = ( ANGLE_MAX > > ANGLETOFINESHIFT ) -
m_Direction ;
m_xSpeed = - m_xSpeed ;
m_ySpeed = - m_ySpeed ;
m_Close = false ;
SN_StartSequence ( poly , poly - > seqType , SEQ_DOOR , 0 ) ;
}
}
break ;
case PODOOR_SWING :
if ( m_Dist < = 0 | | poly - > RotatePolyobj ( m_Speed ) )
{
absSpeed = abs ( m_Speed ) ;
m_Dist - = absSpeed ;
if ( m_Dist < = 0 )
{
SN_StopSequence ( poly ) ;
if ( ! m_Close )
{
m_Dist = m_TotalDist ;
m_Close = true ;
m_Tics = m_WaitTics ;
m_Speed = - m_Speed ;
}
else
{
Destroy ( ) ;
}
}
}
else
{
if ( poly - > crush | | ! m_Close )
{ // 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 ( poly , poly - > seqType , SEQ_DOOR , 0 ) ;
}
}
break ;
default :
break ;
}
}
//==========================================================================
//
// EV_OpenPolyDoor
//
//==========================================================================
bool EV_OpenPolyDoor ( line_t * line , int polyNum , int speed , angle_t angle ,
int delay , int distance , podoortype_t type )
{
DPolyDoor * pd = NULL ;
FPolyObj * poly ;
int swingdir = 1 ; // ADD: PODOOR_SWINGL, PODOOR_SWINGR
if ( ( poly = PO_GetPolyobj ( polyNum ) ) = = NULL )
{
Printf ( " EV_OpenPolyDoor: Invalid polyobj num: %d \n " , polyNum ) ;
return false ;
}
FPolyMirrorIterator it ( poly ) ;
while ( ( poly = it . NextMirror ( ) ) ! = NULL )
{
if ( poly - > specialdata ! = NULL )
{ // poly is already moving
break ;
}
pd = new DPolyDoor ( poly - > tag , type ) ;
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 > > ANGLETOFINESHIFT ;
pd - > m_xSpeed = FixedMul ( pd - > m_Speed , finecosine [ pd - > m_Direction ] ) ;
pd - > m_ySpeed = FixedMul ( pd - > m_Speed , finesine [ pd - > m_Direction ] ) ;
SN_StartSequence ( poly , poly - > seqType , SEQ_DOOR , 0 ) ;
angle + = ANGLE_180 ; // reverse the angle
}
else if ( type = = PODOOR_SWING )
{
pd - > m_WaitTics = delay ;
pd - > m_Direction = swingdir ;
pd - > m_Speed = ( speed * pd - > m_Direction * ( ANGLE_90 / 64 ) ) > > 3 ;
pd - > m_Dist = pd - > m_TotalDist = angle ;
SN_StartSequence ( poly , poly - > seqType , SEQ_DOOR , 0 ) ;
swingdir = - swingdir ; // reverse the direction
}
}
return pd ! = NULL ; // Return true if something started moving.
}
//==========================================================================
//
// EV_StopPoly
//
//==========================================================================
bool EV_StopPoly ( int polynum )
{
FPolyObj * poly ;
if ( NULL ! = ( poly = PO_GetPolyobj ( polynum ) ) )
{
if ( poly - > specialdata ! = NULL )
{
poly - > specialdata - > Stop ( ) ;
}
return true ;
}
return false ;
}
// ===== Higher Level Poly Interface code =====
//==========================================================================
//
// PO_GetPolyobj
//
//==========================================================================
FPolyObj * PO_GetPolyobj ( int polyNum )
{
int i ;
for ( i = 0 ; i < po_NumPolyobjs ; i + + )
{
if ( polyobjs [ i ] . tag = = polyNum )
{
return & polyobjs [ i ] ;
}
}
return NULL ;
}
//==========================================================================
//
//
//
//==========================================================================
FPolyObj : : FPolyObj ( )
{
StartSpot . x = StartSpot . y = 0 ;
angle = 0 ;
tag = 0 ;
memset ( bbox , 0 , sizeof ( bbox ) ) ;
validcount = 0 ;
crush = 0 ;
bHurtOnTouch = false ;
seqType = 0 ;
size = 0 ;
subsectorlinks = NULL ;
specialdata = NULL ;
interpolation = NULL ;
}
//==========================================================================
//
// GetPolyobjMirror
//
//==========================================================================
int FPolyObj : : GetMirror ( )
{
return MirrorNum ;
}
//==========================================================================
//
// ThrustMobj
//
//==========================================================================
void FPolyObj : : ThrustMobj ( AActor * actor , side_t * side )
{
2016-03-19 23:54:18 +00:00
DAngle thrustAngle ;
2016-03-01 15:47:10 +00:00
DPolyAction * pe ;
int force ;
if ( ! ( actor - > flags & MF_SHOOTABLE ) & & ! actor - > player )
{
return ;
}
vertex_t * v1 = side - > V1 ( ) ;
vertex_t * v2 = side - > V2 ( ) ;
2016-03-19 23:54:18 +00:00
thrustAngle = VecToAngle ( v2 - > x - v1 - > x , v2 - > y - v1 - > y ) - 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 ( ) > > 8 ;
}
else
{
force = pe - > GetSpeed ( ) > > 3 ;
}
if ( force < FRACUNIT )
{
force = FRACUNIT ;
}
else if ( force > 4 * FRACUNIT )
{
force = 4 * FRACUNIT ;
}
}
else
{
force = FRACUNIT ;
}
2016-03-19 23:54:18 +00:00
DVector2 thrust = thrustAngle . ToVector ( FIXED2FLOAT ( force ) ) ;
actor - > Vel + = thrust ;
2016-03-01 15:47:10 +00:00
if ( crush )
{
2016-03-19 23:54:18 +00:00
fixedvec2 pos = actor - > Vec2Offset ( FLOAT2FIXED ( thrust . X ) , FLOAT2FIXED ( thrust . Y ) ) ;
2016-03-01 15:47:10 +00:00
if ( bHurtOnTouch | | ! P_CheckMove ( actor , pos . x , pos . y ) )
{
int newdam = P_DamageMobj ( actor , NULL , NULL , crush , NAME_Crush ) ;
P_TraceBleed ( newdam > 0 ? newdam : crush , actor ) ;
}
}
if ( level . flags2 & LEVEL2_POLYGRIND ) actor - > Grind ( false ) ; // crush corpses that get caught in a polyobject's way
}
//==========================================================================
//
// UpdateSegBBox
//
//==========================================================================
void FPolyObj : : UpdateBBox ( )
{
for ( unsigned i = 0 ; i < Linedefs . Size ( ) ; i + + )
{
line_t * line = Linedefs [ i ] ;
if ( line - > v1 - > x < line - > v2 - > x )
{
line - > bbox [ BOXLEFT ] = line - > v1 - > x ;
line - > bbox [ BOXRIGHT ] = line - > v2 - > x ;
}
else
{
line - > bbox [ BOXLEFT ] = line - > v2 - > x ;
line - > bbox [ BOXRIGHT ] = line - > v1 - > x ;
}
if ( line - > v1 - > y < line - > v2 - > y )
{
line - > bbox [ BOXBOTTOM ] = line - > v1 - > y ;
line - > bbox [ BOXTOP ] = line - > v2 - > y ;
}
else
{
line - > bbox [ BOXBOTTOM ] = line - > v2 - > y ;
line - > bbox [ BOXTOP ] = line - > v1 - > y ;
}
// Update the line's slopetype
line - > dx = line - > v2 - > x - line - > v1 - > x ;
line - > dy = line - > v2 - > y - line - > v1 - > y ;
}
CalcCenter ( ) ;
}
void FPolyObj : : CalcCenter ( )
{
SQWORD cx = 0 , cy = 0 ;
for ( unsigned i = 0 ; i < Vertices . Size ( ) ; i + + )
{
cx + = Vertices [ i ] - > x ;
cy + = Vertices [ i ] - > y ;
}
CenterSpot . x = ( fixed_t ) ( cx / Vertices . Size ( ) ) ;
CenterSpot . y = ( fixed_t ) ( cy / Vertices . Size ( ) ) ;
}
//==========================================================================
//
// PO_MovePolyobj
//
//==========================================================================
bool FPolyObj : : MovePolyobj ( int x , int y , bool force )
{
FBoundingBox oldbounds = Bounds ;
UnLinkPolyobj ( ) ;
DoMovePolyobj ( x , y ) ;
if ( ! force )
{
bool blocked = false ;
for ( unsigned i = 0 ; i < Sidedefs . Size ( ) ; i + + )
{
if ( CheckMobjBlocking ( Sidedefs [ i ] ) )
{
blocked = true ;
}
}
if ( blocked )
{
DoMovePolyobj ( - x , - y ) ;
LinkPolyobj ( ) ;
return false ;
}
}
StartSpot . x + = x ;
StartSpot . y + = y ;
CenterSpot . x + = x ;
CenterSpot . y + = y ;
LinkPolyobj ( ) ;
ClearSubsectorLinks ( ) ;
RecalcActorFloorCeil ( Bounds | oldbounds ) ;
return true ;
}
//==========================================================================
//
// DoMovePolyobj
//
//==========================================================================
void FPolyObj : : DoMovePolyobj ( int x , int y )
{
for ( unsigned i = 0 ; i < Vertices . Size ( ) ; i + + )
{
Vertices [ i ] - > x + = x ;
Vertices [ i ] - > y + = y ;
PrevPts [ i ] . x + = x ;
PrevPts [ i ] . y + = y ;
}
for ( unsigned i = 0 ; i < Linedefs . Size ( ) ; i + + )
{
Linedefs [ i ] - > bbox [ BOXTOP ] + = y ;
Linedefs [ i ] - > bbox [ BOXBOTTOM ] + = y ;
Linedefs [ i ] - > bbox [ BOXLEFT ] + = x ;
Linedefs [ i ] - > bbox [ BOXRIGHT ] + = x ;
}
}
//==========================================================================
//
// RotatePt
//
//==========================================================================
2016-03-16 11:41:26 +00:00
static void RotatePt ( DAngle an , fixed_t * x , fixed_t * y , fixed_t startSpotX , fixed_t startSpotY )
2016-03-01 15:47:10 +00:00
{
fixed_t tr_x = * x ;
fixed_t tr_y = * y ;
2016-03-16 11:41:26 +00:00
double s = an . Sin ( ) ;
double c = an . Cos ( ) ;
* x = ( xs_CRoundToInt ( tr_x * c - tr_y * s ) & 0xfffffe00 ) + startSpotX ;
2016-03-16 23:07:37 +00:00
* y = ( xs_CRoundToInt ( tr_x * s + tr_y * c ) & 0xfffffe00 ) + startSpotY ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// PO_RotatePolyobj
//
//==========================================================================
bool FPolyObj : : RotatePolyobj ( angle_t angle , bool fromsave )
{
2016-03-16 11:41:26 +00:00
DAngle an ;
2016-03-01 15:47:10 +00:00
bool blocked ;
FBoundingBox oldbounds = Bounds ;
2016-03-16 11:41:26 +00:00
an = ANGLE2DBL ( this - > angle + angle ) ;
2016-03-01 15:47:10 +00:00
UnLinkPolyobj ( ) ;
for ( unsigned i = 0 ; i < Vertices . Size ( ) ; i + + )
{
PrevPts [ i ] . x = Vertices [ i ] - > x ;
PrevPts [ i ] . y = Vertices [ i ] - > y ;
Vertices [ i ] - > x = OriginalPts [ i ] . x ;
Vertices [ i ] - > y = OriginalPts [ i ] . y ;
RotatePt ( an , & Vertices [ i ] - > x , & Vertices [ i ] - > y , StartSpot . x , StartSpot . y ) ;
}
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 ] - > x = PrevPts [ i ] . x ;
Vertices [ i ] - > y = PrevPts [ i ] . y ;
}
UpdateBBox ( ) ;
LinkPolyobj ( ) ;
return false ;
}
}
this - > angle + = angle ;
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 * bmapwidth ;
for ( i = bbox [ BOXLEFT ] ; i < = bbox [ BOXRIGHT ] ; i + + )
{
if ( i > = 0 & & i < bmapwidth & & j > = 0 & & j < bmapheight )
{
link = PolyBlockMap [ index + i ] ;
while ( link ! = NULL & & link - > polyobj ! = this )
{
link = link - > next ;
}
if ( link = = NULL )
{ // polyobj not located in the link cell
continue ;
}
link - > polyobj = NULL ;
}
}
}
}
//==========================================================================
//
// 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 ;
ld = sd - > linedef ;
top = GetSafeBlockY ( ld - > bbox [ BOXTOP ] - bmaporgy ) ;
bottom = GetSafeBlockY ( ld - > bbox [ BOXBOTTOM ] - bmaporgy ) ;
left = GetSafeBlockX ( ld - > bbox [ BOXLEFT ] - bmaporgx ) ;
right = GetSafeBlockX ( ld - > bbox [ BOXRIGHT ] - bmaporgx ) ;
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 = blocklinks [ j + i ] ; block ! = NULL ; block = block - > NextActor )
{
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 ;
2016-03-25 17:19:54 +00:00
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 ! = NULL & &
! ( ld - > flags & ( ML_BLOCKING | ML_BLOCKEVERYTHING ) )
& & ! ( ld - > flags & ML_BLOCK_PLAYERS & & mobj - > player )
& & ! ( 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 ) & &
2016-03-25 17:19:54 +00:00
( 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 ;
}
2016-03-20 14:04:13 +00:00
FBoundingBox box ( mobj - > _f_X ( ) , mobj - > _f_Y ( ) , mobj - > _f_radius ( ) ) ;
2016-03-01 15:47:10 +00:00
if ( box . Right ( ) < = ld - > bbox [ BOXLEFT ]
| | box . Left ( ) > = ld - > bbox [ BOXRIGHT ]
| | box . Top ( ) < = ld - > bbox [ BOXBOTTOM ]
| | box . Bottom ( ) > = ld - > bbox [ BOXTOP ] )
{
continue ;
}
if ( box . BoxOnLineSide ( ld ) ! = - 1 )
{
continue ;
}
// 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 ] ! = NULL )
{
2016-03-25 17:19:54 +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 & & (
2016-03-19 23:54:18 +00:00
mobj - > _f_Z ( ) < ld - > sidedef [ ! side ] - > sector - > GetSecPlane ( sector_t : : floor ) . ZatPoint ( mobj ) | |
mobj - > _f_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 ;
// calculate the polyobj bbox
Bounds . ClearBox ( ) ;
for ( unsigned i = 0 ; i < Sidedefs . Size ( ) ; i + + )
{
vertex_t * vt ;
vt = Sidedefs [ i ] - > linedef - > v1 ;
Bounds . AddToBox ( vt - > x , vt - > y ) ;
vt = Sidedefs [ i ] - > linedef - > v2 ;
Bounds . AddToBox ( vt - > x , vt - > y ) ;
}
bbox [ BOXRIGHT ] = GetSafeBlockX ( Bounds . Right ( ) - bmaporgx ) ;
bbox [ BOXLEFT ] = GetSafeBlockX ( Bounds . Left ( ) - bmaporgx ) ;
bbox [ BOXTOP ] = GetSafeBlockY ( Bounds . Top ( ) - bmaporgy ) ;
bbox [ BOXBOTTOM ] = GetSafeBlockY ( Bounds . Bottom ( ) - bmaporgy ) ;
// 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 = & PolyBlockMap [ j + i ] ;
if ( ! ( * link ) )
{ // Create a new link at the current block cell
* link = new polyblock_t ;
( * link ) - > next = NULL ;
( * link ) - > prev = NULL ;
( * link ) - > polyobj = this ;
continue ;
}
else
{
tempLink = * link ;
while ( tempLink - > next ! = NULL & & tempLink - > polyobj ! = NULL )
{
tempLink = tempLink - > next ;
}
}
if ( tempLink - > polyobj = = NULL )
{
tempLink - > polyobj = this ;
continue ;
}
else
{
tempLink - > next = new polyblock_t ;
tempLink - > next - > next = NULL ;
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 ( bounds ) ;
AActor * actor ;
while ( ( actor = it . Next ( ) ) ! = NULL )
{
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 ( fixed_t fx , fixed_t fy , fixed_t & ox , fixed_t & oy , side_t * * side ) const
{
unsigned int i ;
double x = fx , y = fy ;
double bestdist = HUGE_VAL ;
double bestx = 0 , besty = 0 ;
side_t * bestline = NULL ;
for ( i = 0 ; i < Sidedefs . Size ( ) ; + + i )
{
vertex_t * v1 = Sidedefs [ i ] - > V1 ( ) ;
vertex_t * v2 = Sidedefs [ i ] - > V2 ( ) ;
double a = v2 - > x - v1 - > x ;
double b = v2 - > y - v1 - > y ;
double den = a * a + b * b ;
double ix , iy , dist ;
if ( den = = 0 )
{ // Line is actually a point!
ix = v1 - > x ;
iy = v1 - > y ;
}
else
{
double num = ( x - v1 - > x ) * a + ( y - v1 - > y ) * b ;
double u = num / den ;
if ( u < = 0 )
{
ix = v1 - > x ;
iy = v1 - > y ;
}
else if ( u > = 1 )
{
ix = v2 - > x ;
iy = v2 - > y ;
}
else
{
ix = v1 - > x + u * a ;
iy = v1 - > y + u * b ;
}
}
a = ( ix - x ) ;
b = ( iy - y ) ;
dist = a * a + b * b ;
if ( dist < bestdist )
{
bestdist = dist ;
bestx = ix ;
besty = iy ;
bestline = Sidedefs [ i ] ;
}
}
ox = fixed_t ( bestx ) ;
oy = fixed_t ( besty ) ;
if ( side ! = NULL )
{
* side = bestline ;
}
}
//==========================================================================
//
// InitBlockMap
//
//==========================================================================
static void InitBlockMap ( void )
{
int i ;
PolyBlockMap = new polyblock_t * [ bmapwidth * bmapheight ] ;
memset ( PolyBlockMap , 0 , bmapwidth * bmapheight * sizeof ( polyblock_t * ) ) ;
for ( i = 0 ; i < po_NumPolyobjs ; i + + )
{
polyobjs [ i ] . LinkPolyobj ( ) ;
}
}
//==========================================================================
//
// InitSideLists [RH]
//
// Group sides by vertex and collect side that are known to belong to a
// polyobject so that they can be initialized fast.
//==========================================================================
static void InitSideLists ( )
{
for ( int i = 0 ; i < numsides ; + + i )
{
if ( sides [ i ] . linedef ! = NULL & &
( sides [ i ] . linedef - > special = = Polyobj_StartLine | |
sides [ i ] . linedef - > special = = Polyobj_ExplicitLine ) )
{
KnownPolySides . Push ( i ) ;
}
}
}
//==========================================================================
//
// KillSideLists [RH]
//
//==========================================================================
static void KillSideLists ( )
{
KnownPolySides . Clear ( ) ;
KnownPolySides . ShrinkToFit ( ) ;
}
//==========================================================================
//
// AddPolyVert
//
// Helper function for IterFindPolySides()
//
//==========================================================================
static void AddPolyVert ( TArray < DWORD > & vnum , DWORD vert )
{
for ( unsigned int i = vnum . Size ( ) - 1 ; i - - ! = 0 ; )
{
if ( vnum [ i ] = = vert )
{ // Already in the set. No need to add it.
return ;
}
}
vnum . Push ( vert ) ;
}
//==========================================================================
//
// IterFindPolySides
//
// Beginning with the first vertex of the starting side, for each vertex
// in vnum, add all the sides that use it as a first vertex to the polyobj,
// and add all their second vertices to vnum. This continues until there
// are no new vertices in vnum.
//
//==========================================================================
static void IterFindPolySides ( FPolyObj * po , side_t * side )
{
static TArray < DWORD > vnum ;
unsigned int vnumat ;
assert ( sidetemp ! = NULL ) ;
vnum . Clear ( ) ;
vnum . Push ( DWORD ( side - > V1 ( ) - vertexes ) ) ;
vnumat = 0 ;
while ( vnum . Size ( ) ! = vnumat )
{
DWORD sidenum = sidetemp [ vnum [ vnumat + + ] ] . b . first ;
while ( sidenum ! = NO_SIDE )
{
po - > Sidedefs . Push ( & sides [ sidenum ] ) ;
AddPolyVert ( vnum , DWORD ( sides [ sidenum ] . V2 ( ) - vertexes ) ) ;
sidenum = sidetemp [ sidenum ] . b . next ;
}
}
}
//==========================================================================
//
// SpawnPolyobj
//
//==========================================================================
static int STACK_ARGS posicmp ( const void * a , const void * b )
{
return ( * ( const side_t * * ) a ) - > linedef - > args [ 1 ] - ( * ( const side_t * * ) b ) - > linedef - > args [ 1 ] ;
}
static void SpawnPolyobj ( int index , int tag , int type )
{
unsigned int ii ;
int i ;
FPolyObj * po = & polyobjs [ index ] ;
for ( ii = 0 ; ii < KnownPolySides . Size ( ) ; + + ii )
{
i = KnownPolySides [ ii ] ;
if ( i < 0 )
{
continue ;
}
side_t * sd = & sides [ i ] ;
if ( sd - > linedef - > special = = Polyobj_StartLine & &
sd - > linedef - > args [ 0 ] = = tag )
{
if ( po - > Sidedefs . Size ( ) > 0 )
{
I_Error ( " SpawnPolyobj: Polyobj %d already spawned. \n " , tag ) ;
}
sd - > linedef - > special = 0 ;
sd - > linedef - > args [ 0 ] = 0 ;
IterFindPolySides ( & polyobjs [ index ] , sd ) ;
po - > MirrorNum = sd - > linedef - > args [ 1 ] ;
po - > crush = ( type ! = SMT_PolySpawn ) ? 3 : 0 ;
po - > bHurtOnTouch = ( type = = SMT_PolySpawnHurt ) ;
po - > tag = tag ;
po - > seqType = sd - > linedef - > args [ 2 ] ;
if ( po - > seqType < 0 | | po - > seqType > 63 )
{
po - > seqType = 0 ;
}
break ;
}
}
if ( po - > Sidedefs . Size ( ) = = 0 )
{
// didn't find a polyobj through PO_LINE_START
TArray < side_t * > polySideList ;
unsigned int psIndexOld ;
psIndexOld = po - > Sidedefs . Size ( ) ;
for ( ii = 0 ; ii < KnownPolySides . Size ( ) ; + + ii )
{
i = KnownPolySides [ ii ] ;
if ( i > = 0 & &
sides [ i ] . linedef - > special = = Polyobj_ExplicitLine & &
sides [ i ] . linedef - > args [ 0 ] = = tag )
{
if ( ! sides [ i ] . linedef - > args [ 1 ] )
{
I_Error ( " SpawnPolyobj: Explicit line missing order number in poly %d, linedef %d. \n " , tag , int ( sides [ i ] . linedef - lines ) ) ;
}
po - > Sidedefs . Push ( & sides [ i ] ) ;
}
}
qsort ( & po - > Sidedefs [ 0 ] , po - > Sidedefs . Size ( ) , sizeof ( po - > Sidedefs [ 0 ] ) , posicmp ) ;
if ( po - > Sidedefs . Size ( ) > 0 )
{
po - > crush = ( type ! = SMT_PolySpawn ) ? 3 : 0 ;
po - > bHurtOnTouch = ( type = = SMT_PolySpawnHurt ) ;
po - > tag = tag ;
po - > seqType = po - > Sidedefs [ 0 ] - > linedef - > args [ 3 ] ;
po - > MirrorNum = po - > Sidedefs [ 0 ] - > linedef - > args [ 2 ] ;
}
else
I_Error ( " SpawnPolyobj: Poly %d does not exist \n " , tag ) ;
}
validcount + + ;
for ( unsigned int i = 0 ; i < po - > Sidedefs . Size ( ) ; i + + )
{
line_t * l = po - > Sidedefs [ i ] - > linedef ;
if ( l - > validcount ! = validcount )
{
l - > validcount = validcount ;
po - > Linedefs . Push ( l ) ;
vertex_t * v = l - > v1 ;
int j ;
for ( j = po - > Vertices . Size ( ) - 1 ; j > = 0 ; j - - )
{
if ( po - > Vertices [ j ] = = v ) break ;
}
if ( j < 0 ) po - > Vertices . Push ( v ) ;
v = l - > v2 ;
for ( j = po - > Vertices . Size ( ) - 1 ; j > = 0 ; j - - )
{
if ( po - > Vertices [ j ] = = v ) break ;
}
if ( j < 0 ) po - > Vertices . Push ( v ) ;
}
}
po - > Sidedefs . ShrinkToFit ( ) ;
po - > Linedefs . ShrinkToFit ( ) ;
po - > Vertices . ShrinkToFit ( ) ;
}
//==========================================================================
//
// TranslateToStartSpot
//
//==========================================================================
static void TranslateToStartSpot ( int tag , int originX , int originY )
{
FPolyObj * po ;
int deltaX ;
int deltaY ;
po = NULL ;
for ( int i = 0 ; i < po_NumPolyobjs ; i + + )
{
if ( polyobjs [ i ] . tag = = tag )
{
po = & polyobjs [ i ] ;
break ;
}
}
if ( po = = NULL )
{ // didn't match the tag with a polyobj tag
I_Error ( " TranslateToStartSpot: Unable to match polyobj tag: %d \n " , tag ) ;
}
if ( po - > Sidedefs . Size ( ) = = 0 )
{
I_Error ( " TranslateToStartSpot: Anchor point located without a StartSpot point: %d \n " , tag ) ;
}
po - > OriginalPts . Resize ( po - > Sidedefs . Size ( ) ) ;
po - > PrevPts . Resize ( po - > Sidedefs . Size ( ) ) ;
deltaX = originX - po - > StartSpot . x ;
deltaY = originY - po - > StartSpot . y ;
for ( unsigned i = 0 ; i < po - > Sidedefs . Size ( ) ; i + + )
{
po - > Sidedefs [ i ] - > Flags | = WALLF_POLYOBJ ;
}
for ( unsigned i = 0 ; i < po - > Linedefs . Size ( ) ; i + + )
{
po - > Linedefs [ i ] - > bbox [ BOXTOP ] - = deltaY ;
po - > Linedefs [ i ] - > bbox [ BOXBOTTOM ] - = deltaY ;
po - > Linedefs [ i ] - > bbox [ BOXLEFT ] - = deltaX ;
po - > Linedefs [ i ] - > bbox [ BOXRIGHT ] - = deltaX ;
}
for ( unsigned i = 0 ; i < po - > Vertices . Size ( ) ; i + + )
{
po - > Vertices [ i ] - > x - = deltaX ;
po - > Vertices [ i ] - > y - = deltaY ;
po - > OriginalPts [ i ] . x = po - > Vertices [ i ] - > x - po - > StartSpot . x ;
po - > OriginalPts [ i ] . y = po - > Vertices [ i ] - > y - po - > StartSpot . y ;
}
po - > CalcCenter ( ) ;
// For compatibility purposes
po - > CenterSubsector = R_PointInSubsector ( po - > CenterSpot . x , po - > CenterSpot . y ) ;
}
//==========================================================================
//
// PO_Init
//
//==========================================================================
void PO_Init ( void )
{
// [RH] Hexen found the polyobject-related things by reloading the map's
// THINGS lump here and scanning through it. I have P_SpawnMapThing()
// record those things instead, so that in here we simply need to
// look at the polyspawns list.
polyspawns_t * polyspawn , * * prev ;
int polyIndex ;
// [RH] Make this faster
InitSideLists ( ) ;
polyobjs = new FPolyObj [ po_NumPolyobjs ] ;
polyIndex = 0 ; // index polyobj number
// Find the startSpot points, and spawn each polyobj
for ( polyspawn = polyspawns , prev = & polyspawns ; polyspawn ; )
{
// 9301 (3001) = no crush, 9302 (3002) = crushing, 9303 = hurting touch
if ( polyspawn - > type > = SMT_PolySpawn & & polyspawn - > type < = SMT_PolySpawnHurt )
{
// Polyobj StartSpot Pt.
polyobjs [ polyIndex ] . StartSpot . x = polyspawn - > x ;
polyobjs [ polyIndex ] . StartSpot . y = polyspawn - > y ;
SpawnPolyobj ( polyIndex , polyspawn - > angle , polyspawn - > type ) ;
polyIndex + + ;
* prev = polyspawn - > next ;
delete polyspawn ;
polyspawn = * prev ;
}
else
{
prev = & polyspawn - > next ;
polyspawn = polyspawn - > next ;
}
}
for ( polyspawn = polyspawns ; polyspawn ; )
{
polyspawns_t * next = polyspawn - > next ;
if ( polyspawn - > type = = SMT_PolyAnchor )
{
// Polyobj Anchor Pt.
TranslateToStartSpot ( polyspawn - > angle , polyspawn - > x , polyspawn - > y ) ;
}
delete polyspawn ;
polyspawn = next ;
}
polyspawns = NULL ;
// check for a startspot without an anchor point
for ( polyIndex = 0 ; polyIndex < po_NumPolyobjs ; polyIndex + + )
{
if ( polyobjs [ polyIndex ] . OriginalPts . Size ( ) = = 0 )
{
I_Error ( " PO_Init: StartSpot located without an Anchor point: %d \n " ,
polyobjs [ polyIndex ] . tag ) ;
}
}
InitBlockMap ( ) ;
// [RH] Don't need the seg lists anymore
KillSideLists ( ) ;
for ( int i = 0 ; i < numnodes ; i + + )
{
node_t * no = & nodes [ i ] ;
double fdx = ( double ) no - > dx ;
double fdy = ( double ) no - > dy ;
2016-03-11 14:45:47 +00:00
no - > len = ( float ) g_sqrt ( fdx * fdx + fdy * fdy ) ;
2016-03-01 15:47:10 +00:00
}
// mark all subsectors which have a seg belonging to a polyobj
// These ones should not be rendered on the textured automap.
for ( int i = 0 ; i < numsubsectors ; i + + )
{
subsector_t * ss = & subsectors [ i ] ;
for ( DWORD j = 0 ; j < ss - > numlines ; j + + )
{
if ( ss - > firstline [ j ] . sidedef ! = NULL & &
ss - > firstline [ j ] . sidedef - > Flags & WALLF_POLYOBJ )
{
ss - > flags | = SSECF_POLYORG ;
break ;
}
}
}
}
//==========================================================================
//
// PO_Busy
//
//==========================================================================
bool PO_Busy ( int polyobj )
{
FPolyObj * poly ;
poly = PO_GetPolyobj ( polyobj ) ;
return ( poly ! = NULL & & poly - > specialdata ! = NULL ) ;
}
//==========================================================================
//
//
//
//==========================================================================
void FPolyObj : : ClearSubsectorLinks ( )
{
while ( subsectorlinks ! = NULL )
{
assert ( subsectorlinks - > state = = 1337 ) ;
FPolyNode * next = subsectorlinks - > snext ;
if ( subsectorlinks - > pnext ! = NULL )
{
assert ( subsectorlinks - > pnext - > state = = 1337 ) ;
subsectorlinks - > pnext - > pprev = subsectorlinks - > pprev ;
}
if ( subsectorlinks - > pprev ! = NULL )
{
assert ( subsectorlinks - > pprev - > state = = 1337 ) ;
subsectorlinks - > pprev - > pnext = subsectorlinks - > pnext ;
}
else
{
subsectorlinks - > subsector - > polys = subsectorlinks - > pnext ;
}
if ( subsectorlinks - > subsector - > BSP ! = NULL )
{
subsectorlinks - > subsector - > BSP - > bDirty = true ;
}
subsectorlinks - > state = - 1 ;
delete subsectorlinks ;
subsectorlinks = next ;
}
subsectorlinks = NULL ;
}
void FPolyObj : : ClearAllSubsectorLinks ( )
{
for ( int i = 0 ; i < po_NumPolyobjs ; i + + )
{
polyobjs [ i ] . ClearSubsectorLinks ( ) ;
}
ReleaseAllPolyNodes ( ) ;
}
//==========================================================================
//
// GetIntersection
//
// adapted from P_InterceptVector
//
//==========================================================================
static bool GetIntersection ( FPolySeg * seg , node_t * bsp , FPolyVertex * v )
{
double frac ;
double num ;
double den ;
double v2x = ( double ) seg - > v1 . x ;
double v2y = ( double ) seg - > v1 . y ;
double v2dx = ( double ) ( seg - > v2 . x - seg - > v1 . x ) ;
double v2dy = ( double ) ( seg - > v2 . y - seg - > v1 . y ) ;
double v1x = ( double ) bsp - > x ;
double v1y = ( double ) bsp - > y ;
double v1dx = ( double ) bsp - > dx ;
double v1dy = ( double ) bsp - > dy ;
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 - > x = xs_RoundToInt ( v2x + frac * v2dx ) ;
v - > y = xs_RoundToInt ( v2y + frac * v2dy ) ;
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 ( double ( - node - > dy ) * ( vt - > x - node - > x ) + double ( node - > dx ) * ( vt - > y - node - > y ) ) / node - > len ;
}
//==========================================================================
//
// AddToBBox
//
//==========================================================================
static void AddToBBox ( fixed_t child [ 4 ] , fixed_t parent [ 4 ] )
{
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
//
//==========================================================================
static void AddToBBox ( FPolyVertex * v , fixed_t bbox [ 4 ] )
{
if ( v - > x < bbox [ BOXLEFT ] )
{
bbox [ BOXLEFT ] = v - > x ;
}
if ( v - > x > bbox [ BOXRIGHT ] )
{
bbox [ BOXRIGHT ] = v - > x ;
}
if ( v - > y < bbox [ BOXBOTTOM ] )
{
bbox [ BOXBOTTOM ] = v - > y ;
}
if ( v - > y > bbox [ BOXTOP ] )
{
bbox [ BOXTOP ] = v - > y ;
}
}
//==========================================================================
//
// SplitPoly
//
//==========================================================================
static void SplitPoly ( FPolyNode * pnode , void * node , fixed_t bbox [ 4 ] )
{
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 . x , pnode - > poly - > CenterSpot . y , bsp ) ;
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 . x , seg - > v2 . y , bsp ) ;
lists [ side ] . Push ( * seg ) ;
}
}
else if ( dist_v2 < = POLY_EPSILON )
{
int side = R_PointOnSide ( seg - > v1 . x , seg - > v1 . y , bsp ) ;
lists [ side ] . Push ( * seg ) ;
}
else
{
int side1 = R_PointOnSide ( seg - > v1 . x , seg - > v1 . y , bsp ) ;
int side2 = R_PointOnSide ( seg - > v2 . x , seg - > v2 . y , bsp ) ;
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 * ) ( ( BYTE * ) node - 1 ) ;
// Link node to subsector
pnode - > pnext = sub - > polys ;
if ( pnode - > pnext ! = NULL )
{
assert ( pnode - > pnext - > state = = 1337 ) ;
pnode - > pnext - > pprev = pnode ;
}
pnode - > pprev = NULL ;
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 ) ;
fixed_t subbbox [ 4 ] = { FIXED_MIN , FIXED_MAX , FIXED_MAX , FIXED_MIN } ;
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.
fixed_t dummybbox [ 4 ] = { 0 } ;
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 ( ! ( i_compatflags & COMPATF_POLYOBJ ) )
{
SplitPoly ( node , nodes + numnodes - 1 , dummybbox ) ;
}
else
{
subsector_t * sub = CenterSubsector ;
// Link node to subsector
node - > pnext = sub - > polys ;
if ( node - > pnext ! = NULL )
{
assert ( node - > pnext - > state = = 1337 ) ;
node - > pnext - > pprev = node ;
}
node - > pprev = NULL ;
sub - > polys = node ;
// link node to polyobject
node - > snext = node - > poly - > subsectorlinks ;
node - > poly - > subsectorlinks = node ;
node - > subsector = sub ;
}
}
//==========================================================================
//
//
//
//==========================================================================
void PO_LinkToSubsectors ( )
{
for ( int i = 0 ; i < po_NumPolyobjs ; i + + )
{
if ( polyobjs [ i ] . subsectorlinks = = NULL )
{
polyobjs [ i ] . CreateSubsectorLinks ( ) ;
}
}
}
//==========================================================================
//
// NewPolyNode
//
//==========================================================================
static FPolyNode * NewPolyNode ( )
{
FPolyNode * node ;
if ( FreePolyNodes ! = NULL )
{
node = FreePolyNodes ;
FreePolyNodes = node - > pnext ;
}
else
{
node = new FPolyNode ;
}
node - > state = 1337 ;
node - > poly = NULL ;
node - > pnext = NULL ;
node - > pprev = NULL ;
node - > subsector = NULL ;
node - > snext = NULL ;
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 ! = NULL ; node = next )
{
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 ! = NULL )
{
UsedPolys [ 0 ] = poly - > tag ;
NumUsedPolys = 1 ;
}
else
{
NumUsedPolys = 0 ;
}
}
//==========================================================================
//
// FPolyMirrorIterator :: NextMirror
//
// Returns the polyobject that mirrors the current one, or NULL if there
// is no mirroring polyobject, or there is a mirroring polyobject but it was
// already returned.
//
//==========================================================================
FPolyObj * FPolyMirrorIterator : : NextMirror ( )
{
FPolyObj * poly = CurPoly , * nextpoly ;
if ( poly = = NULL )
{
return NULL ;
}
// Do the work to decide which polyobject to return the next time this
// function is called.
int mirror = poly - > GetMirror ( ) , i ;
nextpoly = NULL ;
// 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 = PO_GetPolyobj ( mirror ) ;
if ( nextpoly = = NULL )
{
Printf ( " Invalid mirror polyobj num %d for polyobj num %d \n " , mirror , UsedPolys [ i - 1 ] ) ;
}
}
}
CurPoly = nextpoly ;
return poly ;
}