2016-02-15 11:35:40 +00:00
/*
* * portals . cpp
* * Everything that has to do with portals ( both of the line and sector variety )
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 2016 ZZYZX
* * Copyright 2016 Christoph Oelckers
* * All rights reserved .
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
* * There is no code here that is directly taken from Eternity
* * although some similarities may be inevitable because it has to
* * implement the same concepts .
*/
2016-01-10 03:45:26 +00:00
# include "portal.h"
# include "p_local.h"
# include "p_lnspec.h"
# include "r_bsp.h"
# include "r_segs.h"
# include "c_cvars.h"
# include "m_bbox.h"
# include "p_tags.h"
2016-02-05 22:15:56 +00:00
# include "farchive.h"
2016-02-06 04:16:35 +00:00
# include "v_text.h"
2016-02-14 17:16:59 +00:00
# include "a_sharedglobal.h"
# include "i_system.h"
# include "c_dispatch.h"
2016-02-15 01:14:34 +00:00
# include "p_maputl.h"
# include "p_spec.h"
2016-02-15 12:40:31 +00:00
# include "p_checkposition.h"
2016-01-10 03:45:26 +00:00
// simulation recurions maximum
CVAR ( Int , sv_portal_recursions , 4 , CVAR_ARCHIVE | CVAR_SERVERINFO )
2016-02-14 17:16:59 +00:00
FDisplacementTable Displacements ;
2016-02-05 22:15:56 +00:00
TArray < FLinePortal > linePortals ;
2016-02-14 23:53:59 +00:00
TArray < FLinePortal * > linkedPortals ; // only the linked portals, this is used to speed up looking for them in P_CollectConnectedGroups.
2016-02-05 22:15:56 +00:00
2016-02-16 11:51:10 +00:00
//============================================================================
//
// This is used to mark processed portals for some collection functions.
//
//============================================================================
struct FPortalBits
{
TArray < DWORD > data ;
void setSize ( int num )
{
data . Resize ( ( num + 31 ) / 32 ) ;
clear ( ) ;
}
void clear ( )
{
memset ( & data [ 0 ] , 0 , data . Size ( ) * sizeof ( DWORD ) ) ;
}
void setBit ( int group )
{
data [ group > > 5 ] | = ( 1 < < ( group & 31 ) ) ;
}
int getBit ( int group )
{
return data [ group > > 5 ] & ( 1 < < ( group & 31 ) ) ;
}
} ;
2016-02-05 22:15:56 +00:00
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Save a line portal for savegames.
//
//============================================================================
2016-02-05 22:15:56 +00:00
FArchive & operator < < ( FArchive & arc , FLinePortal & port )
{
arc < < port . mOrigin
< < port . mDestination
< < port . mXDisplacement
< < port . mYDisplacement
< < port . mType
< < port . mFlags
< < port . mDefFlags
< < port . mAlign ;
return arc ;
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// finds the destination for a line portal for spawning
//
//============================================================================
2016-02-06 20:16:57 +00:00
static line_t * FindDestination ( line_t * src , int tag )
{
if ( tag )
{
int lineno = - 1 ;
FLineIdIterator it ( tag ) ;
while ( ( lineno = it . Next ( ) ) > = 0 )
{
if ( & lines [ lineno ] ! = src )
{
return & lines [ lineno ] ;
}
}
}
return NULL ;
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Spawns a single line portal
//
//============================================================================
2016-02-05 22:15:56 +00:00
void P_SpawnLinePortal ( line_t * line )
{
// portal destination is special argument #0
line_t * dst = NULL ;
if ( line - > args [ 2 ] > = PORTT_VISUAL & & line - > args [ 2 ] < = PORTT_LINKED )
{
2016-02-06 20:16:57 +00:00
dst = FindDestination ( line , line - > args [ 0 ] ) ;
2016-02-05 22:15:56 +00:00
line - > portalindex = linePortals . Reserve ( 1 ) ;
FLinePortal * port = & linePortals . Last ( ) ;
memset ( port , 0 , sizeof ( FLinePortal ) ) ;
port - > mOrigin = line ;
port - > mDestination = dst ;
port - > mType = BYTE ( line - > args [ 2 ] ) ; // range check is done above.
2016-02-06 04:16:35 +00:00
if ( port - > mType = = PORTT_LINKED )
{
// Linked portals have no z-offset ever.
port - > mAlign = PORG_ABSOLUTE ;
}
else
{
port - > mAlign = BYTE ( line - > args [ 3 ] > = PORG_ABSOLUTE & & line - > args [ 3 ] < = PORG_CEILING ? line - > args [ 3 ] : PORG_ABSOLUTE ) ;
if ( port - > mType = = PORTT_INTERACTIVE )
{
// Due to the way z is often handled, these pose a major issue for parts of the code that needs to transparently handle interactive portals.
Printf ( TEXTCOLOR_RED " Warning: z-offsetting not allowed for interactive portals. Changing line %d to teleport-portal! \n " , int ( line - lines ) ) ;
port - > mType = PORTT_TELEPORT ;
}
}
2016-02-05 22:15:56 +00:00
if ( port - > mDestination ! = NULL )
{
port - > mDefFlags = port - > mType = = PORTT_VISUAL ? PORTF_VISIBLE : port - > mType = = PORTT_TELEPORT ? PORTF_TYPETELEPORT : PORTF_TYPEINTERACTIVE ;
}
}
else if ( line - > args [ 2 ] = = PORTT_LINKEDEE & & line - > args [ 0 ] = = 0 )
{
// EE-style portals require that the first line ID is identical and the first arg of the two linked linedefs are 0 and 1 respectively.
int mytag = tagManager . GetFirstLineID ( line ) ;
for ( int i = 0 ; i < numlines ; i + + )
{
if ( tagManager . GetFirstLineID ( & lines [ i ] ) = = mytag & & lines [ i ] . args [ 0 ] = = 1 )
{
line - > portalindex = linePortals . Reserve ( 1 ) ;
FLinePortal * port = & linePortals . Last ( ) ;
memset ( port , 0 , sizeof ( FLinePortal ) ) ;
port - > mOrigin = line ;
port - > mDestination = & lines [ i ] ;
port - > mType = PORTT_LINKED ;
port - > mAlign = PORG_ABSOLUTE ;
port - > mDefFlags = PORTF_TYPEINTERACTIVE ;
2016-02-05 23:21:44 +00:00
// we need to create the backlink here, too.
lines [ i ] . portalindex = linePortals . Reserve ( 1 ) ;
port = & linePortals . Last ( ) ;
memset ( port , 0 , sizeof ( FLinePortal ) ) ;
port - > mOrigin = & lines [ i ] ;
port - > mDestination = line ;
port - > mType = PORTT_LINKED ;
port - > mAlign = PORG_ABSOLUTE ;
port - > mDefFlags = PORTF_TYPEINTERACTIVE ;
2016-02-05 22:15:56 +00:00
}
}
}
else
{
// undefined type
return ;
}
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Update a line portal's state after all have been spawned
//
//============================================================================
2016-02-05 22:15:56 +00:00
void P_UpdatePortal ( FLinePortal * port )
{
if ( port - > mDestination = = NULL )
{
// Portal has no destination: switch it off
port - > mFlags = 0 ;
}
else if ( port - > mDestination - > getPortalDestination ( ) ! = port - > mOrigin )
{
//portal doesn't link back. This will be a simple teleporter portal.
port - > mFlags = port - > mDefFlags & ~ PORTF_INTERACTIVE ;
if ( port - > mType = = PORTT_LINKED )
{
// this is illegal. Demote the type to TELEPORT
port - > mType = PORTT_TELEPORT ;
port - > mDefFlags & = ~ PORTF_INTERACTIVE ;
}
}
else
{
port - > mFlags = port - > mDefFlags ;
2016-02-14 17:16:59 +00:00
if ( port - > mType = = PORTT_LINKED )
{
if ( linePortals [ port - > mDestination - > portalindex ] . mType ! = PORTT_LINKED )
{
port - > mType = PORTT_INTERACTIVE ; // linked portals must be two-way.
}
else
{
port - > mXDisplacement = port - > mDestination - > v2 - > x - port - > mOrigin - > v1 - > x ;
port - > mYDisplacement = port - > mDestination - > v2 - > y - port - > mOrigin - > v1 - > y ;
}
}
}
2016-02-05 22:15:56 +00:00
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Collect a separate list of linked portals so that these can be
// processed faster without the simpler types interfering.
//
//============================================================================
2016-02-14 23:53:59 +00:00
void P_CollectLinkedPortals ( )
{
linkedPortals . Clear ( ) ;
for ( unsigned i = 0 ; i < linePortals . Size ( ) ; i + + )
{
FLinePortal * port = & linePortals [ i ] ;
if ( port - > mType = = PORTT_LINKED )
{
linkedPortals . Push ( port ) ;
}
}
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Post-process all line portals
//
//============================================================================
2016-02-05 22:15:56 +00:00
void P_FinalizePortals ( )
{
for ( unsigned i = 0 ; i < linePortals . Size ( ) ; i + + )
{
FLinePortal * port = & linePortals [ i ] ;
P_UpdatePortal ( port ) ;
}
2016-02-14 23:53:59 +00:00
P_CollectLinkedPortals ( ) ;
2016-02-05 22:15:56 +00:00
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Change the destination of a portal
//
//============================================================================
2016-02-06 20:16:57 +00:00
static bool ChangePortalLine ( line_t * line , int destid )
{
if ( line - > portalindex > = linePortals . Size ( ) ) return false ;
FLinePortal * port = & linePortals [ line - > portalindex ] ;
if ( port - > mType = = PORTT_LINKED ) return false ; // linked portals cannot be changed.
2016-02-06 23:30:27 +00:00
if ( destid = = 0 ) port - > mDestination = NULL ;
2016-02-06 20:16:57 +00:00
port - > mDestination = FindDestination ( line , destid ) ;
2016-02-06 23:30:27 +00:00
if ( port - > mDestination = = NULL )
{
port - > mFlags = 0 ;
}
else if ( port - > mType = = PORTT_INTERACTIVE )
2016-02-06 20:16:57 +00:00
{
FLinePortal * portd = & linePortals [ port - > mDestination - > portalindex ] ;
if ( portd ! = NULL & & portd - > mType = = PORTT_INTERACTIVE & & portd - > mDestination = = line )
{
// this is a 2-way interactive portal
port - > mFlags = port - > mDefFlags | PORTF_INTERACTIVE ;
portd - > mFlags = portd - > mDefFlags | PORTF_INTERACTIVE ;
}
else
{
port - > mFlags = port - > mDefFlags ;
portd - > mFlags = portd - > mDefFlags ;
}
}
return true ;
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Change the destination of a group of portals
//
//============================================================================
2016-02-06 20:16:57 +00:00
bool P_ChangePortal ( line_t * ln , int thisid , int destid )
{
int lineno ;
if ( thisid = = 0 ) return ChangePortalLine ( ln , destid ) ;
FLineIdIterator it ( thisid ) ;
bool res = false ;
while ( ( lineno = it . Next ( ) ) > = 0 )
{
res | = ChangePortalLine ( & lines [ lineno ] , destid ) ;
}
return res ;
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Calculate the intersection between two lines.
2016-01-10 03:45:26 +00:00
// [ZZ] lots of floats here to avoid overflowing a lot
2016-02-15 11:35:40 +00:00
//
//============================================================================
2016-02-05 15:58:57 +00:00
bool P_IntersectLines ( fixed_t o1x , fixed_t o1y , fixed_t p1x , fixed_t p1y ,
2016-01-10 03:45:26 +00:00
fixed_t o2x , fixed_t o2y , fixed_t p2x , fixed_t p2y ,
fixed_t & rx , fixed_t & ry )
{
2016-02-05 15:58:57 +00:00
double xx = FIXED2DBL ( o2x ) - FIXED2DBL ( o1x ) ;
double xy = FIXED2DBL ( o2y ) - FIXED2DBL ( o1y ) ;
2016-01-10 03:45:26 +00:00
2016-02-05 15:58:57 +00:00
double d1x = FIXED2DBL ( p1x ) - FIXED2DBL ( o1x ) ;
double d1y = FIXED2DBL ( p1y ) - FIXED2DBL ( o1y ) ;
2016-01-10 03:45:26 +00:00
if ( d1x > d1y )
{
d1y = d1y / d1x * 32767.0f ;
d1x = 32767.0 ;
}
else
{
d1x = d1x / d1y * 32767.0f ;
d1y = 32767.0 ;
}
2016-02-05 15:58:57 +00:00
double d2x = FIXED2DBL ( p2x ) - FIXED2DBL ( o2x ) ;
double d2y = FIXED2DBL ( p2y ) - FIXED2DBL ( o2y ) ;
2016-01-10 03:45:26 +00:00
2016-02-05 15:58:57 +00:00
double cross = d1x * d2y - d1y * d2x ;
2016-01-10 03:45:26 +00:00
if ( fabs ( cross ) < 1e-8 )
return false ;
2016-02-05 15:58:57 +00:00
double t1 = ( xx * d2y - xy * d2x ) / cross ;
2016-01-10 03:45:26 +00:00
rx = o1x + FLOAT2FIXED ( d1x * t1 ) ;
ry = o1y + FLOAT2FIXED ( d1y * t1 ) ;
return true ;
}
2016-01-10 03:22:33 +00:00
inline int P_PointOnLineSideExplicit ( fixed_t x , fixed_t y , fixed_t x1 , fixed_t y1 , fixed_t x2 , fixed_t y2 )
{
return DMulScale32 ( y - y1 , x2 - x1 , x1 - x , y2 - y1 ) > 0 ;
2016-01-10 03:45:26 +00:00
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// check if this line is between portal and the viewer. clip away if it is.
// (this may need some fixing)
//
//============================================================================
2016-01-10 03:45:26 +00:00
bool P_ClipLineToPortal ( line_t * line , line_t * portal , fixed_t viewx , fixed_t viewy , bool partial , bool samebehind )
{
2016-02-05 15:58:57 +00:00
bool behind1 = ! ! P_PointOnLineSidePrecise ( line - > v1 - > x , line - > v1 - > y , portal ) ;
bool behind2 = ! ! P_PointOnLineSidePrecise ( line - > v2 - > x , line - > v2 - > y , portal ) ;
2016-01-10 03:45:26 +00:00
// [ZZ] update 16.12.2014: if a vertex equals to one of portal's vertices, it's treated as being behind the portal.
// this is required in order to clip away diagonal lines around the portal (example: 1-sided triangle shape with a mirror on it's side)
if ( ( line - > v1 - > x = = portal - > v1 - > x & & line - > v1 - > y = = portal - > v1 - > y ) | |
( line - > v1 - > x = = portal - > v2 - > x & & line - > v1 - > y = = portal - > v2 - > y ) )
behind1 = samebehind ;
if ( ( line - > v2 - > x = = portal - > v1 - > x & & line - > v2 - > y = = portal - > v1 - > y ) | |
( line - > v2 - > x = = portal - > v2 - > x & & line - > v2 - > y = = portal - > v2 - > y ) )
behind2 = samebehind ;
if ( behind1 & & behind2 )
{
// line is behind the portal plane. now check if it's in front of two view plane borders (i.e. if it will get in the way of rendering)
fixed_t dummyx , dummyy ;
2016-02-05 15:58:57 +00:00
bool infront1 = P_IntersectLines ( line - > v1 - > x , line - > v1 - > y , line - > v2 - > x , line - > v2 - > y , viewx , viewy , portal - > v1 - > x , portal - > v1 - > y , dummyx , dummyy ) ;
bool infront2 = P_IntersectLines ( line - > v1 - > x , line - > v1 - > y , line - > v2 - > x , line - > v2 - > y , viewx , viewy , portal - > v2 - > x , portal - > v2 - > y , dummyx , dummyy ) ;
2016-01-10 03:45:26 +00:00
if ( infront1 & & infront2 )
return true ;
}
return false ;
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Translates a coordinate by a portal's displacement
//
//============================================================================
2016-01-10 03:45:26 +00:00
void P_TranslatePortalXY ( line_t * src , line_t * dst , fixed_t & x , fixed_t & y )
{
if ( ! src | | ! dst )
return ;
fixed_t nposx , nposy ; // offsets from line
// Get the angle between the two linedefs, for rotating
// orientation and velocity. Rotate 180 degrees, and flip
// the position across the exit linedef, if reversed.
angle_t angle =
R_PointToAngle2 ( 0 , 0 , dst - > dx , dst - > dy ) -
R_PointToAngle2 ( 0 , 0 , src - > dx , src - > dy ) ;
angle + = ANGLE_180 ;
// Sine, cosine of angle adjustment
fixed_t s = finesine [ angle > > ANGLETOFINESHIFT ] ;
fixed_t c = finecosine [ angle > > ANGLETOFINESHIFT ] ;
fixed_t tx , ty ;
nposx = x - src - > v1 - > x ;
nposy = y - src - > v1 - > y ;
// Rotate position along normal to match exit linedef
tx = FixedMul ( nposx , c ) - FixedMul ( nposy , s ) ;
ty = FixedMul ( nposy , c ) + FixedMul ( nposx , s ) ;
tx + = dst - > v2 - > x ;
ty + = dst - > v2 - > y ;
x = tx ;
y = ty ;
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Translates a velocity vector by a portal's displacement
//
//============================================================================
2016-01-10 03:45:26 +00:00
void P_TranslatePortalVXVY ( line_t * src , line_t * dst , fixed_t & vx , fixed_t & vy )
{
angle_t angle =
R_PointToAngle2 ( 0 , 0 , dst - > dx , dst - > dy ) -
R_PointToAngle2 ( 0 , 0 , src - > dx , src - > dy ) ;
angle + = ANGLE_180 ;
// Sine, cosine of angle adjustment
fixed_t s = finesine [ angle > > ANGLETOFINESHIFT ] ;
fixed_t c = finecosine [ angle > > ANGLETOFINESHIFT ] ;
fixed_t orig_velx = vx ;
fixed_t orig_vely = vy ;
vx = FixedMul ( orig_velx , c ) - FixedMul ( orig_vely , s ) ;
vy = FixedMul ( orig_vely , c ) + FixedMul ( orig_velx , s ) ;
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Translates an angle by a portal's displacement
//
//============================================================================
2016-01-10 03:45:26 +00:00
void P_TranslatePortalAngle ( line_t * src , line_t * dst , angle_t & angle )
{
if ( ! src | | ! dst )
return ;
// Get the angle between the two linedefs, for rotating
// orientation and velocity. Rotate 180 degrees, and flip
// the position across the exit linedef, if reversed.
angle_t xangle =
R_PointToAngle2 ( 0 , 0 , dst - > dx , dst - > dy ) -
R_PointToAngle2 ( 0 , 0 , src - > dx , src - > dy ) ;
xangle + = ANGLE_180 ;
angle + = xangle ;
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// Translates a z-coordinate by a portal's displacement
//
//============================================================================
2016-01-10 03:45:26 +00:00
void P_TranslatePortalZ ( line_t * src , line_t * dst , fixed_t & z )
{
2016-02-05 22:15:56 +00:00
// args[2] = 0 - no adjustment
2016-01-10 03:45:26 +00:00
// args[2] = 1 - adjust by floor difference
// args[2] = 2 - adjust by ceiling difference
2016-02-05 22:15:56 +00:00
switch ( src - > getPortalAlignment ( ) )
2016-01-10 03:45:26 +00:00
{
2016-02-05 22:15:56 +00:00
case PORG_FLOOR :
2016-01-10 03:45:26 +00:00
z = z - src - > frontsector - > floorplane . ZatPoint ( src - > v1 - > x , src - > v1 - > y ) + dst - > frontsector - > floorplane . ZatPoint ( dst - > v2 - > x , dst - > v2 - > y ) ;
2016-02-05 22:15:56 +00:00
return ;
case PORG_CEILING :
2016-01-10 03:45:26 +00:00
z = z - src - > frontsector - > ceilingplane . ZatPoint ( src - > v1 - > x , src - > v1 - > y ) + dst - > frontsector - > ceilingplane . ZatPoint ( dst - > v2 - > x , dst - > v2 - > y ) ;
2016-02-05 22:15:56 +00:00
return ;
default :
return ;
2016-01-10 03:45:26 +00:00
}
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
2016-01-10 03:45:26 +00:00
// calculate shortest distance from a point (x,y) to a linedef
2016-02-15 11:35:40 +00:00
//
//============================================================================
2016-01-10 03:45:26 +00:00
fixed_t P_PointLineDistance ( line_t * line , fixed_t x , fixed_t y )
{
angle_t angle = R_PointToAngle2 ( 0 , 0 , line - > dx , line - > dy ) ;
angle + = ANGLE_180 ;
fixed_t dx = line - > v1 - > x - x ;
fixed_t dy = line - > v1 - > y - y ;
fixed_t s = finesine [ angle > > ANGLETOFINESHIFT ] ;
fixed_t c = finecosine [ angle > > ANGLETOFINESHIFT ] ;
fixed_t d2x = FixedMul ( dx , c ) - FixedMul ( dy , s ) ;
return abs ( d2x ) ;
}
void P_NormalizeVXVY ( fixed_t & vx , fixed_t & vy )
{
2016-02-05 15:58:57 +00:00
double _vx = FIXED2DBL ( vx ) ;
double _vy = FIXED2DBL ( vy ) ;
double len = sqrt ( _vx * _vx + _vy * _vy ) ;
2016-01-10 03:45:26 +00:00
vx = FLOAT2FIXED ( _vx / len ) ;
vy = FLOAT2FIXED ( _vy / len ) ;
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
2016-01-10 03:45:26 +00:00
// portal tracer code
2016-02-15 11:35:40 +00:00
//
//============================================================================
2016-01-10 03:45:26 +00:00
PortalTracer : : PortalTracer ( fixed_t startx , fixed_t starty , fixed_t endx , fixed_t endy )
{
this - > startx = startx ;
this - > starty = starty ;
this - > endx = endx ;
this - > endy = endy ;
intx = endx ;
inty = endy ;
intxIn = intx ;
intyIn = inty ;
z = 0 ;
angle = 0 ;
depth = 0 ;
frac = 0 ;
in = NULL ;
out = NULL ;
vx = 0 ;
vy = 0 ;
}
bool PortalTracer : : TraceStep ( )
{
if ( depth > sv_portal_recursions )
return false ;
this - > in = NULL ;
this - > out = NULL ;
this - > vx = 0 ;
this - > vy = 0 ;
int oDepth = depth ;
fixed_t dirx = endx - startx ;
fixed_t diry = endy - starty ;
P_NormalizeVXVY ( dirx , diry ) ;
dirx = 0 ;
diry = 0 ;
FPathTraverse it ( startx - dirx , starty - diry , endx + dirx , endy + diry , PT_ADDLINES | PT_COMPATIBLE ) ;
intercept_t * in ;
while ( ( in = it . Next ( ) ) )
{
line_t * li ;
if ( in - > isaline )
{
li = in - > d . line ;
2016-02-05 15:14:45 +00:00
if ( li - > isLinePortal ( ) )
2016-01-10 03:45:26 +00:00
{
if ( P_PointOnLineSide ( startx - dirx , starty - diry , li ) )
continue ; // we're at the back side of this line
2016-02-05 15:14:45 +00:00
line_t * out = li - > getPortalDestination ( ) ;
2016-01-10 03:45:26 +00:00
this - > in = li ;
this - > out = out ;
// we only know that we crossed it, but we also need to know WHERE we crossed it
fixed_t vx = it . Trace ( ) . dx ;
fixed_t vy = it . Trace ( ) . dy ;
fixed_t x = it . Trace ( ) . x + FixedMul ( vx , in - > frac ) ;
fixed_t y = it . Trace ( ) . y + FixedMul ( vy , in - > frac ) ;
P_NormalizeVXVY ( vx , vy ) ;
this - > vx = vx ;
this - > vy = vy ;
// teleport our trace
if ( ! out - > backsector )
{
intx = x + vx ;
inty = y + vy ;
}
else
{
intx = x - vx ;
inty = y - vy ;
}
//P_TranslateCoordinatesAndAngle(li, out, startx, starty, noangle);
//P_TranslateCoordinatesAndAngle(li, out, endx, endy, angle);
//if (hdeltaZ)
// P_TranslateZ(li, out, deltaZ);
//P_TranslateCoordinatesAndAngle(li, out, vx, vy, noangle);
P_TranslatePortalXY ( li , out , startx , starty ) ;
P_TranslatePortalVXVY ( li , out , this - > vx , this - > vy ) ;
intxIn = intx ;
intyIn = inty ;
P_TranslatePortalXY ( li , out , intx , inty ) ;
P_TranslatePortalXY ( li , out , endx , endy ) ;
P_TranslatePortalAngle ( li , out , angle ) ;
P_TranslatePortalZ ( li , out , z ) ;
frac + = in - > frac ;
depth + + ;
break ; // breaks to outer loop
}
if ( ! ( li - > flags & ML_TWOSIDED ) | | ( li - > flags & ML_BLOCKEVERYTHING ) )
return false ; // stop tracing, 2D blocking line
}
}
2016-02-05 15:58:57 +00:00
//Printf("returning %d; vx = %.2f; vy = %.2f\n", (oDepth != depth), FIXED2DBL(this->vx), FIXED2DBL(this->vy));
2016-01-10 03:45:26 +00:00
return ( oDepth ! = depth ) ; // if a portal has been found, return false
}
2016-02-14 17:16:59 +00:00
//============================================================================
//
// CollectSectors
//
// Collects all sectors that are connected to any sector belonging to a portal
// because they all will need the same displacement values
//
//============================================================================
static bool CollectSectors ( int groupid , sector_t * origin )
{
if ( origin - > PortalGroup ! = 0 ) return false ; // already processed
origin - > PortalGroup = groupid ;
TArray < sector_t * > list ( 16 ) ;
list . Push ( origin ) ;
for ( unsigned i = 0 ; i < list . Size ( ) ; i + + )
{
sector_t * sec = list [ i ] ;
for ( int j = 0 ; j < sec - > linecount ; j + + )
{
line_t * line = sec - > lines [ j ] ;
sector_t * other = line - > frontsector = = sec ? line - > backsector : line - > frontsector ;
if ( other ! = NULL & & other ! = sec & & other - > PortalGroup ! = groupid )
{
other - > PortalGroup = groupid ;
list . Push ( other ) ;
}
}
}
return true ;
}
//============================================================================
//
// AddDisplacementForPortal
//
// Adds the displacement for one portal to the displacement array
// (one version for sector to sector plane, one for line to line portals)
//
2016-02-15 11:35:40 +00:00
// Note: Despite the similarities to Eternity's equivalent this is
// original code!
//
2016-02-14 17:16:59 +00:00
//============================================================================
static void AddDisplacementForPortal ( AStackPoint * portal )
{
int thisgroup = portal - > Mate - > Sector - > PortalGroup ;
int othergroup = portal - > Sector - > PortalGroup ;
if ( thisgroup = = othergroup )
{
Printf ( " Portal between sectors %d and %d has both sides in same group and will be disabled \n " , portal - > Sector - > sectornum , portal - > Mate - > Sector - > sectornum ) ;
portal - > special1 = portal - > Mate - > special1 = SKYBOX_PORTAL ;
return ;
}
if ( thisgroup < = 0 | | thisgroup > = Displacements . size | | othergroup < = 0 | | othergroup > = Displacements . size )
{
Printf ( " Portal between sectors %d and %d has invalid group and will be disabled \n " , portal - > Sector - > sectornum , portal - > Mate - > Sector - > sectornum ) ;
portal - > special1 = portal - > Mate - > special1 = SKYBOX_PORTAL ;
return ;
}
FDisplacement & disp = Displacements ( thisgroup , othergroup ) ;
if ( ! disp . isSet )
{
2016-02-16 15:40:53 +00:00
disp . pos . x = portal - > scaleX ;
disp . pos . y = portal - > scaleY ;
2016-02-14 17:16:59 +00:00
disp . isSet = true ;
}
else
{
2016-02-16 15:40:53 +00:00
if ( disp . pos . x ! = portal - > scaleX | | disp . pos . y ! = portal - > scaleY )
2016-02-14 17:16:59 +00:00
{
Printf ( " Portal between sectors %d and %d has displacement mismatch and will be disabled \n " , portal - > Sector - > sectornum , portal - > Mate - > Sector - > sectornum ) ;
portal - > special1 = portal - > Mate - > special1 = SKYBOX_PORTAL ;
return ;
}
}
}
static void AddDisplacementForPortal ( FLinePortal * portal )
{
int thisgroup = portal - > mOrigin - > frontsector - > PortalGroup ;
int othergroup = portal - > mDestination - > frontsector - > PortalGroup ;
if ( thisgroup = = othergroup )
{
Printf ( " Portal between lines %d and %d has both sides in same group \n " , int ( portal - > mOrigin - lines ) , int ( portal - > mDestination - lines ) ) ;
portal - > mType = linePortals [ portal - > mDestination - > portalindex ] . mType = PORTT_TELEPORT ;
return ;
}
if ( thisgroup < = 0 | | thisgroup > = Displacements . size | | othergroup < = 0 | | othergroup > = Displacements . size )
{
Printf ( " Portal between lines %d and %d has invalid group \n " , int ( portal - > mOrigin - lines ) , int ( portal - > mDestination - lines ) ) ;
portal - > mType = linePortals [ portal - > mDestination - > portalindex ] . mType = PORTT_TELEPORT ;
return ;
}
FDisplacement & disp = Displacements ( thisgroup , othergroup ) ;
if ( ! disp . isSet )
{
2016-02-16 15:40:53 +00:00
disp . pos . x = portal - > mXDisplacement ;
disp . pos . y = portal - > mYDisplacement ;
2016-02-14 17:16:59 +00:00
disp . isSet = true ;
}
else
{
2016-02-16 15:40:53 +00:00
if ( disp . pos . x ! = portal - > mXDisplacement | | disp . pos . y ! = portal - > mYDisplacement )
2016-02-14 17:16:59 +00:00
{
Printf ( " Portal between lines %d and %d has displacement mismatch \n " , int ( portal - > mOrigin - lines ) , int ( portal - > mDestination - lines ) ) ;
portal - > mType = linePortals [ portal - > mDestination - > portalindex ] . mType = PORTT_TELEPORT ;
return ;
}
}
}
//============================================================================
//
// ConnectGroups
//
// Do the indirect connections. This loop will run until it cannot find any new connections
//
//============================================================================
static bool ConnectGroups ( )
{
// Now
BYTE indirect = 1 ;
bool bogus = false ;
bool changed ;
do
{
changed = false ;
for ( int x = 1 ; x < Displacements . size ; x + + )
{
for ( int y = 1 ; y < Displacements . size ; y + + )
{
FDisplacement & dispxy = Displacements ( x , y ) ;
if ( dispxy . isSet )
{
for ( int z = 1 ; z < Displacements . size ; z + + )
{
FDisplacement & dispyz = Displacements ( y , z ) ;
if ( dispyz . isSet )
{
FDisplacement & dispxz = Displacements ( x , z ) ;
if ( dispxz . isSet )
{
2016-02-16 15:40:53 +00:00
if ( dispxy . pos . x + dispyz . pos . x ! = dispxz . pos . x | | dispxy . pos . y + dispyz . pos . y ! = dispxz . pos . y )
2016-02-14 17:16:59 +00:00
{
bogus = true ;
}
}
else
{
2016-02-16 15:40:53 +00:00
dispxz . pos = dispxy . pos + dispyz . pos ;
2016-02-14 17:16:59 +00:00
dispxz . isSet = true ;
dispxz . indirect = indirect ;
changed = true ;
}
}
}
}
}
}
indirect + + ;
} while ( changed ) ;
return bogus ;
}
//============================================================================
//
// P_CreateLinkedPortals
//
// Creates the data structures needed for linked portals
// Removes portals from sloped sectors (as they cannot work on them)
// Group all sectors connected to one side of the portal
// Caclculate displacements between all created groups.
//
// Portals with the same offset but different anchors will not be merged.
//
//============================================================================
2016-02-14 12:12:03 +00:00
void P_CreateLinkedPortals ( )
{
2016-02-14 17:16:59 +00:00
TThinkerIterator < AStackPoint > it ;
AStackPoint * mo ;
TArray < AStackPoint * > orgs ;
int id = 0 ;
bool bogus = false ;
while ( ( mo = it . Next ( ) ) )
{
if ( mo - > special1 = = SKYBOX_LINKEDPORTAL )
{
if ( mo - > Mate ! = NULL )
{
orgs . Push ( mo ) ;
mo - > reactiontime = + + id ;
}
else
{
// this should never happen, but if it does, the portal needs to be removed
mo - > Destroy ( ) ;
}
}
}
if ( orgs . Size ( ) = = 0 )
{
return ;
}
for ( int i = 0 ; i < numsectors ; i + + )
{
for ( int j = 0 ; j < 2 ; j + + )
{
ASkyViewpoint * box = sectors [ i ] . SkyBoxes [ j ] ;
if ( box ! = NULL & & box - > special1 = = SKYBOX_LINKEDPORTAL )
{
secplane_t & plane = j = = 0 ? sectors [ i ] . floorplane : sectors [ i ] . ceilingplane ;
if ( plane . a | | plane . b )
{
// The engine cannot deal with portals on a sloped plane.
sectors [ i ] . SkyBoxes [ j ] = NULL ;
Printf ( " Portal on %s of sector %d is sloped and will be disabled \n " , j = = 0 ? " floor " : " ceiling " , i ) ;
}
}
}
}
// Group all sectors, starting at each portal origin.
id = 1 ;
for ( unsigned i = 0 ; i < orgs . Size ( ) ; i + + )
{
if ( CollectSectors ( id , orgs [ i ] - > Sector ) ) id + + ;
if ( CollectSectors ( id , orgs [ i ] - > Mate - > Sector ) ) id + + ;
}
for ( unsigned i = 0 ; i < linePortals . Size ( ) ; i + + )
{
if ( linePortals [ i ] . mType = = PORTT_LINKED )
{
if ( CollectSectors ( id , linePortals [ i ] . mOrigin - > frontsector ) ) id + + ;
if ( CollectSectors ( id , linePortals [ i ] . mDestination - > frontsector ) ) id + + ;
}
}
Displacements . Create ( id ) ;
// Check for leftover sectors that connect to a portal
for ( int i = 0 ; i < numsectors ; i + + )
{
for ( int j = 0 ; j < 2 ; j + + )
{
ASkyViewpoint * box = sectors [ i ] . SkyBoxes [ j ] ;
if ( box ! = NULL )
{
2016-02-15 11:35:40 +00:00
if ( box - > special1 = = SKYBOX_LINKEDPORTAL & & sectors [ i ] . PortalGroup = = 0 )
2016-02-14 17:16:59 +00:00
{
2016-02-15 11:35:40 +00:00
// Note: the linked actor will be on the other side of the portal.
// To get this side's group we will have to look at the mate object.
CollectSectors ( box - > Mate - > Sector - > PortalGroup , & sectors [ i ] ) ;
// We cannot process the backlink here because all we can access is the anchor object
// If necessary that will have to be done for the other side's portal.
2016-02-14 17:16:59 +00:00
}
}
}
}
for ( unsigned i = 0 ; i < orgs . Size ( ) ; i + + )
{
AddDisplacementForPortal ( orgs [ i ] ) ;
}
for ( unsigned i = 0 ; i < linePortals . Size ( ) ; i + + )
{
if ( linePortals [ i ] . mType = = PORTT_LINKED )
{
AddDisplacementForPortal ( & linePortals [ i ] ) ;
}
}
for ( int x = 1 ; x < Displacements . size ; x + + )
{
for ( int y = x + 1 ; y < Displacements . size ; y + + )
{
FDisplacement & dispxy = Displacements ( x , y ) ;
FDisplacement & dispyx = Displacements ( y , x ) ;
if ( dispxy . isSet & & dispyx . isSet & &
2016-02-16 15:40:53 +00:00
( dispxy . pos . x ! = - dispyx . pos . x | | dispxy . pos . y ! = - dispyx . pos . y ) )
2016-02-14 17:16:59 +00:00
{
2016-02-15 11:35:40 +00:00
int sec1 = - 1 , sec2 = - 1 ;
for ( int i = 0 ; i < numsectors & & ( sec1 = = - 1 | | sec2 = = - 1 ) ; i + + )
{
if ( sec1 = = - 1 & & sectors [ i ] . PortalGroup = = x ) sec1 = i ;
if ( sec2 = = - 1 & & sectors [ i ] . PortalGroup = = y ) sec2 = i ;
}
Printf ( " Link offset mismatch between sectors %d and %d \n " , sec1 , sec2 ) ;
2016-02-14 17:16:59 +00:00
bogus = true ;
}
2016-02-15 20:49:46 +00:00
// mark everything that connects to a one-sided line
for ( int i = 0 ; i < numlines ; i + + )
{
if ( lines [ i ] . backsector = = NULL & & lines [ i ] . frontsector - > PortalGroup = = 0 )
{
CollectSectors ( - 1 , lines [ i ] . frontsector ) ;
}
}
// and now print a message for everything that still wasn't processed.
for ( int i = 0 ; i < numsectors ; i + + )
{
if ( sectors [ i ] . PortalGroup = = 0 )
{
Printf ( " Unable to assign sector %d to any group. Possibly self-referencing \n " , i ) ;
}
else if ( sectors [ i ] . PortalGroup = = - 1 ) sectors [ i ] . PortalGroup = 0 ;
}
2016-02-14 17:16:59 +00:00
}
}
bogus | = ConnectGroups ( ) ;
if ( bogus )
{
// todo: disable all portals whose offsets do not match the associated groups
}
// reject would just get in the way when checking sight through portals.
if ( rejectmatrix ! = NULL )
{
delete [ ] rejectmatrix ;
rejectmatrix = NULL ;
}
// finally we must flag all planes which are obstructed by the sector's own ceiling or floor.
for ( int i = 0 ; i < numsectors ; i + + )
{
sectors [ i ] . CheckPortalPlane ( sector_t : : floor ) ;
sectors [ i ] . CheckPortalPlane ( sector_t : : ceiling ) ;
}
//BuildBlockmap();
}
2016-02-14 23:53:59 +00:00
//============================================================================
//
// Collect all portal groups this actor would occupy at the given position
// This is used to determine which parts of the map need to be checked.
//
//============================================================================
2016-02-16 11:51:10 +00:00
bool P_CollectConnectedGroups ( AActor * actor , fixed_t newx , fixed_t newy , FPortalGroupArray & out )
2016-02-14 23:53:59 +00:00
{
2016-02-16 11:51:10 +00:00
// Keep this temporary work stuff static. This function can never be called recursively
// and this would have to be reallocated for each call otherwise.
static FPortalBits processMask ;
static TArray < FLinePortal * > foundPortals ;
2016-02-14 23:53:59 +00:00
bool retval = false ;
2016-02-16 11:51:10 +00:00
if ( linkedPortals . Size ( ) = = 0 )
2016-02-14 23:53:59 +00:00
{
2016-02-16 11:51:10 +00:00
// If there are no portals, all sectors are in group 0.
out . Add ( 0 ) ;
2016-02-14 23:53:59 +00:00
return false ;
}
2016-02-16 11:51:10 +00:00
processMask . setSize ( linkedPortals . Size ( ) ) ;
processMask . clear ( ) ;
foundPortals . Clear ( ) ;
2016-02-14 23:53:59 +00:00
int thisgroup = actor - > Sector - > PortalGroup ;
2016-02-16 11:51:10 +00:00
processMask . setBit ( thisgroup ) ;
out . Add ( thisgroup ) ;
for ( unsigned i = 0 ; i < linkedPortals . Size ( ) ; i + + )
2016-02-14 23:53:59 +00:00
{
2016-02-16 11:51:10 +00:00
line_t * ld = linkedPortals [ i ] - > mOrigin ;
2016-02-14 23:53:59 +00:00
int othergroup = ld - > frontsector - > PortalGroup ;
FDisplacement & disp = Displacements ( thisgroup , othergroup ) ;
if ( ! disp . isSet ) continue ; // no connection.
2016-02-16 15:40:53 +00:00
FBoundingBox box ( newx + disp . pos . x , newy + disp . pos . y , actor - > radius ) ;
2016-02-14 23:53:59 +00:00
if ( box . Right ( ) < = ld - > bbox [ BOXLEFT ]
| | box . Left ( ) > = ld - > bbox [ BOXRIGHT ]
| | box . Top ( ) < = ld - > bbox [ BOXBOTTOM ]
| | box . Bottom ( ) > = ld - > bbox [ BOXTOP ] )
continue ; // not touched
2016-02-16 11:51:10 +00:00
if ( box . BoxOnLineSide ( linkedPortals [ i ] - > mOrigin ) ! = - 1 ) continue ; // not touched
foundPortals . Push ( linkedPortals [ i ] ) ;
2016-02-14 23:53:59 +00:00
}
bool foundone = true ;
while ( foundone )
{
foundone = false ;
for ( int i = foundPortals . Size ( ) - 1 ; i > = 0 ; i - - )
{
2016-02-16 11:51:10 +00:00
if ( processMask . getBit ( foundPortals [ i ] - > mOrigin - > frontsector - > PortalGroup ) & &
! processMask . getBit ( foundPortals [ i ] - > mDestination - > frontsector - > PortalGroup ) )
2016-02-14 23:53:59 +00:00
{
2016-02-16 11:51:10 +00:00
processMask . setBit ( foundPortals [ i ] - > mDestination - > frontsector - > PortalGroup ) ;
out . Add ( foundPortals [ i ] - > mDestination - > frontsector - > PortalGroup ) ;
2016-02-14 23:53:59 +00:00
foundone = true ;
retval = true ;
foundPortals . Delete ( i ) ;
}
}
}
sector_t * sec = P_PointInSector ( newx , newy ) ;
sector_t * wsec = sec ;
while ( ! wsec - > PortalBlocksMovement ( sector_t : : ceiling ) & & actor - > Top ( ) > wsec - > SkyBoxes [ sector_t : : ceiling ] - > threshold )
{
sector_t * othersec = wsec - > SkyBoxes [ sector_t : : ceiling ] - > Sector ;
FDisplacement & disp = Displacements ( actor - > Sector - > PortalGroup , othersec - > PortalGroup ) ;
2016-02-16 15:40:53 +00:00
fixed_t dx = newx + disp . pos . x ;
fixed_t dy = newx + disp . pos . y ;
2016-02-16 11:51:10 +00:00
processMask . setBit ( othersec - > PortalGroup ) ;
out . Add ( othersec - > PortalGroup ) ;
wsec = P_PointInSector ( dx , dy ) ; // get upper sector at the exact spot we want to check and repeat
2016-02-14 23:53:59 +00:00
retval = true ;
}
wsec = sec ;
while ( ! wsec - > PortalBlocksMovement ( sector_t : : floor ) & & actor - > Z ( ) < wsec - > SkyBoxes [ sector_t : : floor ] - > threshold )
{
sector_t * othersec = wsec - > SkyBoxes [ sector_t : : ceiling ] - > Sector ;
FDisplacement & disp = Displacements ( actor - > Sector - > PortalGroup , othersec - > PortalGroup ) ;
2016-02-16 15:40:53 +00:00
fixed_t dx = newx + disp . pos . x ;
fixed_t dy = newx + disp . pos . y ;
2016-02-16 11:51:10 +00:00
processMask . setBit ( othersec - > PortalGroup ) ;
out . Add ( othersec - > PortalGroup ) ;
wsec = P_PointInSector ( dx , dy ) ; // get lower sector at the exact spot we want to check and repeat
2016-02-14 23:53:59 +00:00
retval = true ;
}
return retval ;
}
2016-02-15 11:35:40 +00:00
//============================================================================
//
// print the group link table to the console
//
//============================================================================
2016-02-14 17:16:59 +00:00
CCMD ( dumplinktable )
{
for ( int x = 1 ; x < Displacements . size ; x + + )
{
for ( int y = 1 ; y < Displacements . size ; y + + )
{
FDisplacement & disp = Displacements ( x , y ) ;
2016-02-16 15:40:53 +00:00
Printf ( " %c%c(%6d, %6d) " , TEXTCOLOR_ESCAPE , ' C ' + disp . indirect , disp . pos . x > > FRACBITS , disp . pos . y > > FRACBITS ) ;
2016-02-14 17:16:59 +00:00
}
Printf ( " \n " ) ;
}
2016-02-14 12:12:03 +00:00
}
2016-02-14 17:16:59 +00:00
2016-02-14 23:53:59 +00:00