2016-03-28 15:27:55 +00:00
//-----------------------------------------------------------------------------
//
2017-04-17 11:33:19 +00:00
// Copyright 1998-1998 Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
// Copyright 1999-2016 Randy Heit
// Copyright 2002-2016 Christoph Oelckers
2016-03-28 15:27:55 +00:00
//
2017-04-17 11:33:19 +00:00
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
2016-03-28 15:27:55 +00:00
//
2017-04-17 11:33:19 +00:00
// This program is distributed in the hope that it will be useful,
2016-03-28 15:27:55 +00:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2017-04-17 11:33:19 +00:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
2016-03-28 15:27:55 +00:00
//
2017-04-17 11:33:19 +00:00
//-----------------------------------------------------------------------------
2016-03-28 15:27:55 +00:00
//
// DESCRIPTION:
// Initializes and implements BOOM linedef triggers for
// Scrollers/Conveyors
//
//-----------------------------------------------------------------------------
2017-04-17 11:33:19 +00:00
/* For code that originates from ZDoom the following applies:
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
* * 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 .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
2016-03-28 15:27:55 +00:00
# include <stdlib.h>
# include "actor.h"
# include "p_spec.h"
2016-09-19 22:41:22 +00:00
# include "serializer.h"
2016-03-28 15:27:55 +00:00
# include "p_lnspec.h"
# include "r_data/r_interpolate.h"
2017-01-08 17:45:30 +00:00
# include "g_levellocals.h"
2019-01-23 23:22:18 +00:00
# include "maploader/maploader.h"
2019-01-24 19:27:34 +00:00
# include "p_spec_thinkers.h"
2016-03-28 15:27:55 +00:00
2016-11-24 20:36:02 +00:00
IMPLEMENT_CLASS ( DScroller , false , true )
2016-03-28 15:27:55 +00:00
2016-11-05 16:08:54 +00:00
IMPLEMENT_POINTERS_START ( DScroller )
IMPLEMENT_POINTER ( m_Interpolations [ 0 ] )
IMPLEMENT_POINTER ( m_Interpolations [ 1 ] )
IMPLEMENT_POINTER ( m_Interpolations [ 2 ] )
IMPLEMENT_POINTERS_END
2016-03-28 15:27:55 +00:00
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
EScrollPos operator & ( EScrollPos one , EScrollPos two )
{
return EScrollPos ( int ( one ) & int ( two ) ) ;
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
2016-09-19 22:41:22 +00:00
void DScroller : : Serialize ( FSerializer & arc )
2016-03-28 15:27:55 +00:00
{
Super : : Serialize ( arc ) ;
2016-09-19 22:41:22 +00:00
arc . Enum ( " type " , m_Type )
( " dx " , m_dx )
( " dy " , m_dy )
2019-01-06 11:41:02 +00:00
( " sector " , m_Sector )
( " side " , m_Side )
( " control " , m_Controller )
2016-09-19 22:41:22 +00:00
( " lastheight " , m_LastHeight )
( " vdx " , m_vdx )
( " vdy " , m_vdy )
( " accel " , m_Accel )
. Enum ( " parts " , m_Parts )
. Array ( " interpolations " , m_Interpolations , 3 ) ;
2016-03-28 15:27:55 +00:00
}
//-----------------------------------------------------------------------------
//
// [RH] Compensate for rotated sector textures by rotating the scrolling
// in the opposite direction.
//
//-----------------------------------------------------------------------------
2016-03-28 19:04:46 +00:00
static void RotationComp ( const sector_t * sec , int which , double dx , double dy , double & tdx , double & tdy )
2016-03-28 15:27:55 +00:00
{
2016-04-23 11:40:02 +00:00
DAngle an = sec - > GetAngle ( which ) ;
2016-03-28 15:27:55 +00:00
if ( an = = 0 )
{
tdx = dx ;
tdy = dy ;
}
else
{
2016-08-14 19:55:20 +00:00
double ca = - an . Cos ( ) ;
double sa = - an . Sin ( ) ;
2016-03-28 19:04:46 +00:00
tdx = dx * ca - dy * sa ;
tdy = dy * ca + dx * sa ;
2016-03-28 15:27:55 +00:00
}
}
//-----------------------------------------------------------------------------
//
// killough 2/28/98:
//
// This function, with the help of r_plane.c and r_bsp.c, supports generalized
// scrolling floors and walls, with optional mobj-carrying properties, e.g.
// conveyor belts, rivers, etc. A linedef with a special type affects all
// tagged sectors the same way, by creating scrolling and/or object-carrying
// properties. Multiple linedefs may be used on the same sector and are
// cumulative, although the special case of scrolling a floor and carrying
// things on it, requires only one linedef. The linedef's direction determines
// the scrolling direction, and the linedef's length determines the scrolling
// speed. This was designed so that an edge around the sector could be used to
// control the direction of the sector's scrolling, which is usually what is
// desired.
//
// Process the active scrollers.
//
// This is the main scrolling code
// killough 3/7/98
//
//-----------------------------------------------------------------------------
void DScroller : : Tick ( )
{
2016-03-28 19:04:46 +00:00
double dx = m_dx , dy = m_dy , tdx , tdy ;
2016-03-28 15:27:55 +00:00
2019-01-06 11:41:02 +00:00
if ( m_Controller ! = nullptr )
2016-03-28 15:27:55 +00:00
{ // compute scroll amounts based on a sector's height changes
2019-01-06 11:41:02 +00:00
double height = m_Controller - > CenterFloor ( ) + m_Controller - > CenterCeiling ( ) ;
2016-03-28 19:04:46 +00:00
double delta = height - m_LastHeight ;
2016-03-28 15:27:55 +00:00
m_LastHeight = height ;
2016-03-28 19:04:46 +00:00
dx * = delta ;
dy * = delta ;
2016-03-28 15:27:55 +00:00
}
// killough 3/14/98: Add acceleration
if ( m_Accel )
{
m_vdx = dx + = m_vdx ;
m_vdy = dy + = m_vdy ;
}
2016-03-28 19:04:46 +00:00
if ( dx = = 0 & & dy = = 0 )
2016-03-28 15:27:55 +00:00
return ;
switch ( m_Type )
{
case EScroll : : sc_side : // killough 3/7/98: Scroll wall texture
if ( m_Parts & EScrollPos : : scw_top )
{
2019-01-06 11:41:02 +00:00
m_Side - > AddTextureXOffset ( side_t : : top , dx ) ;
m_Side - > AddTextureYOffset ( side_t : : top , dy ) ;
2016-03-28 15:27:55 +00:00
}
2019-01-06 11:41:02 +00:00
if ( m_Parts & EScrollPos : : scw_mid & & ( m_Side - > linedef - > backsector = = nullptr | |
! ( m_Side - > linedef - > flags & ML_3DMIDTEX ) ) )
2016-03-28 15:27:55 +00:00
{
2019-01-06 11:41:02 +00:00
m_Side - > AddTextureXOffset ( side_t : : mid , dx ) ;
m_Side - > AddTextureYOffset ( side_t : : mid , dy ) ;
2016-03-28 15:27:55 +00:00
}
if ( m_Parts & EScrollPos : : scw_bottom )
{
2019-01-06 11:41:02 +00:00
m_Side - > AddTextureXOffset ( side_t : : bottom , dx ) ;
m_Side - > AddTextureYOffset ( side_t : : bottom , dy ) ;
2016-03-28 15:27:55 +00:00
}
break ;
case EScroll : : sc_floor : // killough 3/7/98: Scroll floor texture
2019-01-06 11:41:02 +00:00
RotationComp ( m_Sector , sector_t : : floor , dx , dy , tdx , tdy ) ;
m_Sector - > AddXOffset ( sector_t : : floor , tdx ) ;
m_Sector - > AddYOffset ( sector_t : : floor , tdy ) ;
2016-03-28 15:27:55 +00:00
break ;
case EScroll : : sc_ceiling : // killough 3/7/98: Scroll ceiling texture
2019-01-06 11:41:02 +00:00
RotationComp ( m_Sector , sector_t : : ceiling , dx , dy , tdx , tdy ) ;
m_Sector - > AddXOffset ( sector_t : : ceiling , tdx ) ;
m_Sector - > AddYOffset ( sector_t : : ceiling , tdy ) ;
2016-03-28 15:27:55 +00:00
break ;
// [RH] Don't actually carry anything here. That happens later.
case EScroll : : sc_carry :
2019-01-06 11:41:02 +00:00
level . Scrolls [ m_Sector - > Index ( ) ] + = { dx , dy } ;
2017-05-19 14:31:44 +00:00
// mark all potentially affected things here so that the very expensive calculation loop in AActor::Tick does not need to run for actors which do not touch a scrolling sector.
2019-01-06 11:41:02 +00:00
for ( auto n = m_Sector - > touching_thinglist ; n ; n = n - > m_snext )
2017-05-19 14:31:44 +00:00
{
n - > m_thing - > flags8 | = MF8_INSCROLLSEC ;
}
2016-03-28 15:27:55 +00:00
break ;
case EScroll : : sc_carry_ceiling : // to be added later
break ;
}
}
//-----------------------------------------------------------------------------
//
// Add_Scroller()
//
// Add a generalized scroller to the thinker list.
//
// type: the enumerated type of scrolling: floor, ceiling, floor carrier,
// wall, floor carrier & scroller
//
// (dx,dy): the direction and speed of the scrolling or its acceleration
//
// control: the sector whose heights control this scroller's effect
// remotely, or -1 if no control sector
//
// affectee: the index of the affected object (sector or sidedef)
//
// accel: non-zero if this is an accelerative effect
//
//-----------------------------------------------------------------------------
2019-01-06 11:41:02 +00:00
DScroller : : DScroller ( EScroll type , double dx , double dy , sector_t * ctrl , sector_t * sec , side_t * side , int accel , EScrollPos scrollpos )
2016-03-28 15:27:55 +00:00
: DThinker ( STAT_SCROLLER )
{
m_Type = type ;
m_dx = dx ;
m_dy = dy ;
m_Accel = accel ;
m_Parts = scrollpos ;
m_vdx = m_vdy = 0 ;
2016-09-28 09:58:12 +00:00
m_LastHeight = 0 ;
2019-01-06 11:41:02 +00:00
if ( ( m_Controller = ctrl ) ! = nullptr )
2017-01-07 18:32:24 +00:00
{
2019-01-06 11:41:02 +00:00
m_LastHeight = m_Controller - > CenterFloor ( ) + m_Controller - > CenterCeiling ( ) ;
2017-01-07 18:32:24 +00:00
}
2019-01-06 11:41:02 +00:00
m_Side = side ;
m_Sector = sec ;
m_Interpolations [ 0 ] = m_Interpolations [ 1 ] = m_Interpolations [ 2 ] = nullptr ;
2016-03-28 15:27:55 +00:00
switch ( type )
{
case EScroll : : sc_carry :
2019-01-06 11:41:02 +00:00
assert ( sec ! = nullptr ) ;
level . AddScroller ( sec - > Index ( ) ) ;
2016-03-28 15:27:55 +00:00
break ;
case EScroll : : sc_side :
2019-01-06 11:41:02 +00:00
assert ( side ! = nullptr ) ;
side - > Flags | = WALLF_NOAUTODECALS ;
2016-03-28 15:27:55 +00:00
if ( m_Parts & EScrollPos : : scw_top )
{
2019-01-06 11:41:02 +00:00
m_Interpolations [ 0 ] = m_Side - > SetInterpolation ( side_t : : top ) ;
2016-03-28 15:27:55 +00:00
}
2019-01-06 11:41:02 +00:00
if ( m_Parts & EScrollPos : : scw_mid & & ( m_Side - > linedef - > backsector = = nullptr | |
! ( m_Side - > linedef - > flags & ML_3DMIDTEX ) ) )
2016-03-28 15:27:55 +00:00
{
2019-01-06 11:41:02 +00:00
m_Interpolations [ 1 ] = m_Side - > SetInterpolation ( side_t : : mid ) ;
2016-03-28 15:27:55 +00:00
}
if ( m_Parts & EScrollPos : : scw_bottom )
{
2019-01-06 11:41:02 +00:00
m_Interpolations [ 2 ] = m_Side - > SetInterpolation ( side_t : : bottom ) ;
2016-03-28 15:27:55 +00:00
}
break ;
case EScroll : : sc_floor :
2019-01-06 11:41:02 +00:00
assert ( sec ! = nullptr ) ;
m_Interpolations [ 0 ] = m_Sector - > SetInterpolation ( sector_t : : FloorScroll , false ) ;
2016-03-28 15:27:55 +00:00
break ;
case EScroll : : sc_ceiling :
2019-01-06 11:41:02 +00:00
assert ( sec ! = nullptr ) ;
m_Interpolations [ 0 ] = m_Sector - > SetInterpolation ( sector_t : : CeilingScroll , false ) ;
2016-03-28 15:27:55 +00:00
break ;
default :
break ;
}
}
2017-01-12 21:49:18 +00:00
void DScroller : : OnDestroy ( )
2016-03-28 15:27:55 +00:00
{
for ( int i = 0 ; i < 3 ; i + + )
{
2019-01-06 11:41:02 +00:00
if ( m_Interpolations [ i ] ! = nullptr )
2016-03-28 15:27:55 +00:00
{
m_Interpolations [ i ] - > DelRef ( ) ;
2019-01-06 11:41:02 +00:00
m_Interpolations [ i ] = nullptr ;
2016-03-28 15:27:55 +00:00
}
}
2017-01-12 21:49:18 +00:00
Super : : OnDestroy ( ) ;
2016-03-28 15:27:55 +00:00
}
//-----------------------------------------------------------------------------
//
// Adds wall scroller. Scroll amount is rotated with respect to wall's
// linedef first, so that scrolling towards the wall in a perpendicular
// direction is translated into vertical motion, while scrolling along
// the wall in a parallel direction is translated into horizontal motion.
//
// killough 5/25/98: cleaned up arithmetic to avoid drift due to roundoff
//
//-----------------------------------------------------------------------------
2019-01-06 11:41:02 +00:00
DScroller : : DScroller ( double dx , double dy , const line_t * l , sector_t * control , int accel , EScrollPos scrollpos )
2016-03-28 15:27:55 +00:00
: DThinker ( STAT_SCROLLER )
{
2016-03-28 19:04:46 +00:00
double x = fabs ( l - > Delta ( ) . X ) , y = fabs ( l - > Delta ( ) . Y ) , d ;
if ( y > x ) d = x , x = y , y = d ;
d = x / g_sin ( g_atan2 ( y , x ) + M_PI / 2 ) ;
2016-08-14 20:10:44 +00:00
x = - ( dy * l - > Delta ( ) . Y + dx * l - > Delta ( ) . X ) / d ;
y = - ( dx * l - > Delta ( ) . Y - dy * l - > Delta ( ) . X ) / d ;
2016-03-28 15:27:55 +00:00
m_Type = EScroll : : sc_side ;
m_dx = x ;
m_dy = y ;
m_vdx = m_vdy = 0 ;
m_Accel = accel ;
m_Parts = scrollpos ;
2016-09-28 09:58:12 +00:00
m_LastHeight = 0 ;
2019-01-06 11:41:02 +00:00
if ( ( m_Controller = control ) ! = nullptr )
{
m_LastHeight = m_Controller - > CenterFloor ( ) + m_Controller - > CenterCeiling ( ) ;
}
m_Sector = nullptr ;
m_Side = l - > sidedef [ 0 ] ;
m_Side - > Flags | = WALLF_NOAUTODECALS ;
m_Interpolations [ 0 ] = m_Interpolations [ 1 ] = m_Interpolations [ 2 ] = nullptr ;
2016-03-28 15:27:55 +00:00
if ( m_Parts & EScrollPos : : scw_top )
{
2019-01-06 11:41:02 +00:00
m_Interpolations [ 0 ] = m_Side - > SetInterpolation ( side_t : : top ) ;
2016-03-28 15:27:55 +00:00
}
2019-01-06 11:41:02 +00:00
if ( m_Parts & EScrollPos : : scw_mid & & ( m_Side - > linedef - > backsector = = nullptr | |
! ( m_Side - > linedef - > flags & ML_3DMIDTEX ) ) )
2016-03-28 15:27:55 +00:00
{
2019-01-06 11:41:02 +00:00
m_Interpolations [ 1 ] = m_Side - > SetInterpolation ( side_t : : mid ) ;
2016-03-28 15:27:55 +00:00
}
if ( m_Parts & EScrollPos : : scw_bottom )
{
2019-01-06 11:41:02 +00:00
m_Interpolations [ 2 ] = m_Side - > SetInterpolation ( side_t : : bottom ) ;
2016-03-28 15:27:55 +00:00
}
}
//-----------------------------------------------------------------------------
//
// Modify a wall scroller
//
//-----------------------------------------------------------------------------
2019-01-06 11:41:02 +00:00
void SetWallScroller ( FLevelLocals * Level , int id , int sidechoice , double dx , double dy , EScrollPos Where )
2016-03-28 15:27:55 +00:00
{
Where = Where & scw_all ;
if ( Where = = 0 ) return ;
2016-03-28 19:04:46 +00:00
if ( dx = = 0 & & dy = = 0 )
2016-03-28 15:27:55 +00:00
{
// Special case: Remove the scroller, because the deltas are both 0.
TThinkerIterator < DScroller > iterator ( STAT_SCROLLER ) ;
DScroller * scroller ;
while ( ( scroller = iterator . Next ( ) ) )
{
2019-01-06 11:41:02 +00:00
auto wall = scroller - > GetWall ( ) ;
2016-03-28 15:27:55 +00:00
2019-01-24 00:40:09 +00:00
if ( wall ! = nullptr & & Level - > LineHasId ( wall - > linedef , id ) & & wall - > linedef - > sidedef [ sidechoice ] = = wall & & Where = = scroller - > GetScrollParts ( ) )
2016-03-28 15:27:55 +00:00
{
scroller - > Destroy ( ) ;
}
}
}
else
{
// Find scrollers already attached to the matching walls, and change
// their rates.
2019-01-06 11:41:02 +00:00
TArray < DScroller * > Collection ;
2016-03-28 15:27:55 +00:00
{
TThinkerIterator < DScroller > iterator ( STAT_SCROLLER ) ;
2019-01-06 11:41:02 +00:00
DScroller * scroll ;
2016-03-28 15:27:55 +00:00
2019-01-06 11:41:02 +00:00
while ( ( scroll = iterator . Next ( ) ) )
2016-03-28 15:27:55 +00:00
{
2019-01-06 11:41:02 +00:00
auto wall = scroll - > GetWall ( ) ;
if ( wall ! = nullptr )
2016-03-28 15:27:55 +00:00
{
2019-01-06 11:41:02 +00:00
auto line = wall - > linedef ;
2019-01-24 00:40:09 +00:00
if ( Level - > LineHasId ( line , id ) & & line - > sidedef [ sidechoice ] = = wall & & Where = = scroll - > GetScrollParts ( ) )
2019-01-06 11:41:02 +00:00
{
scroll - > SetRate ( dx , dy ) ;
Collection . Push ( scroll ) ;
}
2016-03-28 15:27:55 +00:00
}
}
}
size_t numcollected = Collection . Size ( ) ;
int linenum ;
// Now create scrollers for any walls that don't already have them.
2019-01-24 00:40:09 +00:00
auto itr = Level - > GetLineIdIterator ( id ) ;
2016-03-28 15:27:55 +00:00
while ( ( linenum = itr . Next ( ) ) > = 0 )
{
2019-01-06 11:41:02 +00:00
side_t * side = Level - > lines [ linenum ] . sidedef [ sidechoice ] ;
if ( side ! = nullptr )
2016-03-28 15:27:55 +00:00
{
2019-01-06 11:41:02 +00:00
if ( Collection . FindEx ( [ = ] ( const DScroller * element ) { return element - > GetWall ( ) = = side ; } ) = = Collection . Size ( ) )
2016-03-28 15:27:55 +00:00
{
2019-01-06 11:41:02 +00:00
Create < DScroller > ( EScroll : : sc_side , dx , dy , nullptr , nullptr , side , 0 , Where ) ;
2016-03-28 15:27:55 +00:00
}
}
}
}
}
2019-01-06 11:41:02 +00:00
void SetScroller ( FLevelLocals * Level , int tag , EScroll type , double dx , double dy )
2016-03-28 15:27:55 +00:00
{
TThinkerIterator < DScroller > iterator ( STAT_SCROLLER ) ;
DScroller * scroller ;
int i ;
// Check if there is already a scroller for this tag
// If at least one sector with this tag is scrolling, then they all are.
// If the deltas are both 0, we don't remove the scroller, because a
// displacement/accelerative scroller might have been set up, and there's
// no way to create one after the level is fully loaded.
i = 0 ;
while ( ( scroller = iterator . Next ( ) ) )
{
if ( scroller - > IsType ( type ) )
{
2019-01-23 23:43:43 +00:00
if ( Level - > SectorHasTag ( scroller - > GetSector ( ) , tag ) )
2016-03-28 15:27:55 +00:00
{
i + + ;
scroller - > SetRate ( dx , dy ) ;
}
}
}
2016-03-28 19:04:46 +00:00
if ( i > 0 | | ( dx = = 0 & & dy = = 0 ) )
2016-03-28 15:27:55 +00:00
{
return ;
}
// Need to create scrollers for the sector(s)
2019-01-23 23:43:43 +00:00
auto itr = Level - > GetSectorTagIterator ( tag ) ;
2016-03-28 15:27:55 +00:00
while ( ( i = itr . Next ( ) ) > = 0 )
{
2019-01-06 11:41:02 +00:00
Create < DScroller > ( type , dx , dy , nullptr , & Level - > sectors [ i ] , nullptr , 0 ) ;
2016-03-28 15:27:55 +00:00
}
}