2014-03-15 16:59:03 +00:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
2021-05-07 15:45:56 +00:00
// Copyright (C) 1999-2021 by Sonic Team Junior.
2014-03-15 16:59:03 +00:00
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file p_spec.c
/// \brief Implements special effects:
/// Texture animation, height or lighting changes
/// according to adjacent sectors, respective
/// utility functions, etc.
/// Line Tag handling. Line and Sector triggers.
# include "doomdef.h"
# include "g_game.h"
# include "p_local.h"
# include "p_setup.h" // levelflats for flat animation
# include "r_data.h"
2020-01-07 15:35:10 +00:00
# include "r_textures.h"
2014-03-15 16:59:03 +00:00
# include "m_random.h"
# include "p_mobj.h"
# include "i_system.h"
# include "s_sound.h"
# include "w_wad.h"
# include "z_zone.h"
# include "r_main.h" //Two extra includes.
# include "r_sky.h"
# include "p_polyobj.h"
2015-10-10 16:57:35 +00:00
# include "p_slopes.h"
2014-03-15 16:59:03 +00:00
# include "hu_stuff.h"
2018-03-26 22:53:09 +00:00
# include "v_video.h" // V_AUTOFADEOUT|V_ALLOWLOWERCASE
2014-03-15 16:59:03 +00:00
# include "m_misc.h"
# include "m_cond.h" //unlock triggers
2020-11-17 12:14:45 +00:00
# include "lua_hook.h" // LUA_HookLinedefExecute
2018-11-03 14:54:47 +00:00
# include "f_finale.h" // control text prompt
2020-03-09 13:54:56 +00:00
# include "r_skins.h" // skins
2014-03-15 16:59:03 +00:00
# ifdef HW3SOUND
# include "hardware/hw3sound.h"
# endif
// Not sure if this is necessary, but it was in w_wad.c, so I'm putting it here too -Shadow Hog
# include <errno.h>
2017-04-26 17:16:01 +00:00
mobj_t * skyboxmo [ 2 ] ; // current skybox mobjs: 0 = viewpoint, 1 = centerpoint
mobj_t * skyboxviewpnts [ 16 ] ; // array of MT_SKYBOX viewpoint mobjs
mobj_t * skyboxcenterpnts [ 16 ] ; // array of MT_SKYBOX centerpoint mobjs
2014-03-15 16:59:03 +00:00
// Amount (dx, dy) vector linedef is shifted right to get scroll amount
# define SCROLL_SHIFT 5
/** Animated texture descriptor
* This keeps track of an animated texture or an animated flat .
* \ sa P_UpdateSpecials , P_InitPicAnims , animdef_t
*/
typedef struct
{
SINT8 istexture ; ///< ::true for a texture, ::false for a flat
INT32 picnum ; ///< The end flat number
INT32 basepic ; ///< The start flat number
INT32 numpics ; ///< Number of frames in the animation
tic_t speed ; ///< Number of tics for which each frame is shown
} anim_t ;
# if defined(_MSC_VER)
# pragma pack(1)
# endif
/** Animated texture definition.
2017-10-07 22:18:25 +00:00
* Used for loading an ANIMDEFS lump from a wad .
2014-03-15 16:59:03 +00:00
*
* Animations are defined by the first and last frame ( i . e . , flat or texture ) .
* The animation sequence uses all flats between the start and end entry , in
* the order found in the wad .
*
* \ sa anim_t
*/
typedef struct
{
SINT8 istexture ; ///< True for a texture, false for a flat.
char endname [ 9 ] ; ///< Name of the last frame, null-terminated.
char startname [ 9 ] ; ///< Name of the first frame, null-terminated.
INT32 speed ; ///< Number of tics for which each frame is shown.
} ATTRPACK animdef_t ;
# if defined(_MSC_VER)
# pragma pack()
# endif
typedef struct
{
UINT32 count ;
thinker_t * * thinkers ;
} thinkerlist_t ;
static void P_SpawnScrollers ( void ) ;
static void P_SpawnFriction ( void ) ;
static void P_SpawnPushers ( void ) ;
static void Add_Pusher ( pushertype_e type , fixed_t x_mag , fixed_t y_mag , mobj_t * source , INT32 affectee , INT32 referrer , INT32 exclusive , INT32 slider ) ; //SoM: 3/9/2000
static void Add_MasterDisappearer ( tic_t appeartime , tic_t disappeartime , tic_t offset , INT32 line , INT32 sourceline ) ;
2018-08-18 09:55:49 +00:00
static void P_ResetFakeFloorFader ( ffloor_t * rover , fade_t * data , boolean finalize ) ;
# define P_RemoveFakeFloorFader(l) P_ResetFakeFloorFader(l, NULL, false);
2018-09-11 14:28:24 +00:00
static boolean P_FadeFakeFloor ( ffloor_t * rover , INT16 sourcevalue , INT16 destvalue , INT16 speed , boolean ticbased , INT32 * timer ,
2018-09-12 20:57:35 +00:00
boolean doexists , boolean dotranslucent , boolean dolighting , boolean docolormap ,
boolean docollision , boolean doghostfade , boolean exactalpha ) ;
2018-08-17 19:13:05 +00:00
static void P_AddFakeFloorFader ( ffloor_t * rover , size_t sectornum , size_t ffloornum ,
2018-09-10 13:48:24 +00:00
INT16 destvalue , INT16 speed , boolean ticbased , boolean relative ,
2018-09-12 20:57:35 +00:00
boolean doexists , boolean dotranslucent , boolean dolighting , boolean docolormap ,
boolean docollision , boolean doghostfade , boolean exactalpha ) ;
static void P_ResetColormapFader ( sector_t * sector ) ;
static void Add_ColormapFader ( sector_t * sector , extracolormap_t * source_exc , extracolormap_t * dest_exc ,
2018-09-13 14:38:15 +00:00
boolean ticbased , INT32 duration ) ;
2014-03-15 16:59:03 +00:00
static void P_AddBlockThinker ( sector_t * sec , line_t * sourceline ) ;
2020-04-17 22:26:49 +00:00
static void P_AddFloatThinker ( sector_t * sec , UINT16 tag , line_t * sourceline ) ;
2016-03-30 18:05:07 +00:00
//static void P_AddBridgeThinker(line_t *sourceline, sector_t *sec);
2014-03-15 16:59:03 +00:00
static void P_AddFakeFloorsByLine ( size_t line , ffloortype_e ffloorflags , thinkerlist_t * secthinkers ) ;
2014-11-12 00:55:07 +00:00
static void P_ProcessLineSpecial ( line_t * line , mobj_t * mo , sector_t * callsec ) ;
2014-03-15 16:59:03 +00:00
static void Add_Friction ( INT32 friction , INT32 movefactor , INT32 affectee , INT32 referrer ) ;
2017-04-11 21:33:04 +00:00
static void P_AddPlaneDisplaceThinker ( INT32 type , fixed_t speed , INT32 control , INT32 affectee , UINT8 reverse ) ;
2014-03-15 16:59:03 +00:00
//SoM: 3/7/2000: New sturcture without limits.
static anim_t * lastanim ;
static anim_t * anims = NULL ; /// \todo free leak
static size_t maxanims ;
// Animating line specials
// Init animated textures
// - now called at level loading P_SetupLevel()
static animdef_t * animdefs = NULL ;
2019-11-09 16:23:38 +00:00
// Increase the size of animdefs to make room for a new animation definition
static void GrowAnimDefs ( void )
{
maxanims + + ;
animdefs = ( animdef_t * ) Z_Realloc ( animdefs , sizeof ( animdef_t ) * ( maxanims + 1 ) , PU_STATIC , NULL ) ;
}
2014-03-15 16:59:03 +00:00
// A prototype; here instead of p_spec.h, so they're "private"
2016-10-19 07:17:36 +00:00
void P_ParseANIMDEFSLump ( INT32 wadNum , UINT16 lumpnum ) ;
void P_ParseAnimationDefintion ( SINT8 istexture ) ;
2014-03-15 16:59:03 +00:00
/** Sets up texture and flat animations.
*
2017-10-07 22:18:25 +00:00
* Converts an : : animdef_t array loaded from a lump into
2014-03-15 16:59:03 +00:00
* : : anim_t format .
*
* Issues an error if any animation cycles are invalid .
*
* \ sa P_FindAnimatedFlat , P_SetupLevelFlatAnims
2016-10-19 07:17:36 +00:00
* \ author Steven McGranahan ( original ) , Shadow Hog ( had to rewrite it to handle multiple WADs ) , JTE ( had to rewrite it to handle multiple WADs _correctly_ )
2014-03-15 16:59:03 +00:00
*/
void P_InitPicAnims ( void )
{
// Init animation
INT32 w ; // WAD
2016-10-19 07:17:36 +00:00
size_t i ;
I_Assert ( animdefs = = NULL ) ;
2017-11-03 03:13:13 +00:00
2017-10-07 22:18:25 +00:00
maxanims = 0 ;
2014-03-15 16:59:03 +00:00
2017-10-26 15:58:18 +00:00
for ( w = numwadfiles - 1 ; w > = 0 ; w - - )
2014-03-15 16:59:03 +00:00
{
2017-10-26 15:58:18 +00:00
UINT16 animdefsLumpNum ;
// Find ANIMDEFS lump in the WAD
animdefsLumpNum = W_CheckNumForNamePwad ( " ANIMDEFS " , w , 0 ) ;
while ( animdefsLumpNum ! = INT16_MAX )
2014-03-15 16:59:03 +00:00
{
2017-10-26 15:58:18 +00:00
P_ParseANIMDEFSLump ( w , animdefsLumpNum ) ;
animdefsLumpNum = W_CheckNumForNamePwad ( " ANIMDEFS " , ( UINT16 ) w , animdefsLumpNum + 1 ) ;
2014-03-15 16:59:03 +00:00
}
}
2017-11-03 03:13:13 +00:00
2017-10-07 22:18:25 +00:00
// Define the last one
animdefs [ maxanims ] . istexture = - 1 ;
strncpy ( animdefs [ maxanims ] . endname , " " , 9 ) ;
strncpy ( animdefs [ maxanims ] . startname , " " , 9 ) ;
animdefs [ maxanims ] . speed = 0 ;
2014-03-15 16:59:03 +00:00
if ( anims )
free ( anims ) ;
anims = ( anim_t * ) malloc ( sizeof ( * anims ) * ( maxanims + 1 ) ) ;
if ( ! anims )
2017-10-07 22:18:25 +00:00
I_Error ( " Not enough free memory for ANIMDEFS data " ) ;
2014-03-15 16:59:03 +00:00
lastanim = anims ;
for ( i = 0 ; animdefs [ i ] . istexture ! = - 1 ; i + + )
{
if ( animdefs [ i ] . istexture )
{
if ( R_CheckTextureNumForName ( animdefs [ i ] . startname ) = = - 1 )
continue ;
lastanim - > picnum = R_TextureNumForName ( animdefs [ i ] . endname ) ;
lastanim - > basepic = R_TextureNumForName ( animdefs [ i ] . startname ) ;
}
else
{
if ( ( W_CheckNumForName ( animdefs [ i ] . startname ) ) = = LUMPERROR )
continue ;
2019-05-21 03:28:52 +00:00
lastanim - > picnum = R_GetFlatNumForName ( animdefs [ i ] . endname ) ;
lastanim - > basepic = R_GetFlatNumForName ( animdefs [ i ] . startname ) ;
2014-03-15 16:59:03 +00:00
}
lastanim - > istexture = animdefs [ i ] . istexture ;
lastanim - > numpics = lastanim - > picnum - lastanim - > basepic + 1 ;
if ( lastanim - > numpics < 2 )
{
free ( anims ) ;
I_Error ( " P_InitPicAnims: bad cycle from %s to %s " ,
animdefs [ i ] . startname , animdefs [ i ] . endname ) ;
}
2017-10-07 22:18:25 +00:00
lastanim - > speed = LONG ( animdefs [ i ] . speed ) ;
2014-03-15 16:59:03 +00:00
lastanim + + ;
}
lastanim - > istexture = - 1 ;
R_ClearTextureNumCache ( false ) ;
2016-10-19 07:17:36 +00:00
// Clear animdefs now that we're done with it.
// We'll only be using anims from now on.
2017-10-07 22:18:25 +00:00
Z_Free ( animdefs ) ;
2016-10-19 07:17:36 +00:00
animdefs = NULL ;
2014-03-15 16:59:03 +00:00
}
2016-10-19 07:17:36 +00:00
void P_ParseANIMDEFSLump ( INT32 wadNum , UINT16 lumpnum )
2014-03-15 16:59:03 +00:00
{
char * animdefsLump ;
size_t animdefsLumpLength ;
char * animdefsText ;
char * animdefsToken ;
2016-10-19 07:17:36 +00:00
char * p ;
2014-03-15 16:59:03 +00:00
// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
// need to make a space of memory where I can ensure that it will terminate
// correctly. Start by loading the relevant data from the WAD.
animdefsLump = ( char * ) W_CacheLumpNumPwad ( wadNum , lumpnum , PU_STATIC ) ;
// If that didn't exist, we have nothing to do here.
if ( animdefsLump = = NULL ) return ;
// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
animdefsLumpLength = W_LumpLengthPwad ( wadNum , lumpnum ) ;
animdefsText = ( char * ) Z_Malloc ( ( animdefsLumpLength + 1 ) * sizeof ( char ) , PU_STATIC , NULL ) ;
// Now move the contents of the lump into this new location.
memmove ( animdefsText , animdefsLump , animdefsLumpLength ) ;
// Make damn well sure the last character in our new memory location is \0.
animdefsText [ animdefsLumpLength ] = ' \0 ' ;
// Finally, free up the memory from the first data load, because we really
// don't need it.
Z_Free ( animdefsLump ) ;
// Now, let's start parsing this thing
2016-10-19 07:17:36 +00:00
p = animdefsText ;
animdefsToken = M_GetToken ( p ) ;
2014-03-15 16:59:03 +00:00
while ( animdefsToken ! = NULL )
{
if ( stricmp ( animdefsToken , " TEXTURE " ) = = 0 )
{
Z_Free ( animdefsToken ) ;
2016-10-19 07:17:36 +00:00
P_ParseAnimationDefintion ( 1 ) ;
2014-03-15 16:59:03 +00:00
}
else if ( stricmp ( animdefsToken , " FLAT " ) = = 0 )
{
Z_Free ( animdefsToken ) ;
2016-10-19 07:17:36 +00:00
P_ParseAnimationDefintion ( 0 ) ;
2014-03-15 16:59:03 +00:00
}
else if ( stricmp ( animdefsToken , " OSCILLATE " ) = = 0 )
{
// This probably came off the tail of an earlier definition. It's technically legal syntax, but we don't support it.
I_Error ( " Error parsing ANIMDEFS lump: Animation definitions utilizing \" OSCILLATE \" (the animation plays in reverse when it reaches the end) are not supported by SRB2 " ) ;
}
else
{
I_Error ( " Error parsing ANIMDEFS lump: Expected \" TEXTURE \" or \" FLAT \" , got \" %s \" " , animdefsToken ) ;
}
2016-10-19 07:17:36 +00:00
// parse next line
while ( * p ! = ' \0 ' & & * p ! = ' \n ' ) + + p ;
if ( * p = = ' \n ' ) + + p ;
animdefsToken = M_GetToken ( p ) ;
2014-03-15 16:59:03 +00:00
}
Z_Free ( animdefsToken ) ;
Z_Free ( ( void * ) animdefsText ) ;
}
2016-10-19 07:17:36 +00:00
void P_ParseAnimationDefintion ( SINT8 istexture )
2014-03-15 16:59:03 +00:00
{
char * animdefsToken ;
2016-05-22 04:44:12 +00:00
size_t animdefsTokenLength ;
2014-03-15 16:59:03 +00:00
char * endPos ;
INT32 animSpeed ;
2016-10-19 07:17:36 +00:00
size_t i ;
2014-03-15 16:59:03 +00:00
// Startname
animdefsToken = M_GetToken ( NULL ) ;
if ( animdefsToken = = NULL )
{
I_Error ( " Error parsing ANIMDEFS lump: Unexpected end of file where start texture/flat name should be " ) ;
}
if ( stricmp ( animdefsToken , " OPTIONAL " ) = = 0 )
{
// This is meaningful to ZDoom - it tells the program NOT to bomb out
// if the textures can't be found - but it's useless in SRB2, so we'll
// just smile, nod, and carry on
Z_Free ( animdefsToken ) ;
animdefsToken = M_GetToken ( NULL ) ;
if ( animdefsToken = = NULL )
{
I_Error ( " Error parsing ANIMDEFS lump: Unexpected end of file where start texture/flat name should be " ) ;
}
else if ( stricmp ( animdefsToken , " RANGE " ) = = 0 )
{
// Oh. Um. Apparently "OPTIONAL" is a texture name. Naughty.
// I should probably handle this more gracefully, but right now
// I can't be bothered; especially since ZDoom doesn't handle this
// condition at all.
I_Error ( " Error parsing ANIMDEFS lump: \" OPTIONAL \" is a keyword; you cannot use it as the startname of an animation " ) ;
}
}
animdefsTokenLength = strlen ( animdefsToken ) ;
if ( animdefsTokenLength > 8 )
{
I_Error ( " Error parsing ANIMDEFS lump: lump name \" %s \" exceeds 8 characters " , animdefsToken ) ;
}
2016-10-19 07:17:36 +00:00
// Search for existing animdef
for ( i = 0 ; i < maxanims ; i + + )
2017-06-25 17:17:05 +00:00
if ( animdefs [ i ] . istexture = = istexture // Check if it's the same type!
& & stricmp ( animdefsToken , animdefs [ i ] . startname ) = = 0 )
2016-10-19 07:17:36 +00:00
{
//CONS_Alert(CONS_NOTICE, "Duplicate animation: %s\n", animdefsToken);
// If we weren't parsing in reverse order, we would `break` here and parse the new data into the existing slot we found.
// Instead, we're just going to skip parsing the rest of this line entirely.
Z_Free ( animdefsToken ) ;
return ;
}
// Not found
if ( i = = maxanims )
{
// Increase the size to make room for the new animation definition
2019-11-09 16:23:38 +00:00
GrowAnimDefs ( ) ;
2016-10-19 07:17:36 +00:00
strncpy ( animdefs [ i ] . startname , animdefsToken , 9 ) ;
}
// animdefs[i].startname is now set to animdefsToken either way.
2014-03-15 16:59:03 +00:00
Z_Free ( animdefsToken ) ;
2016-10-19 07:17:36 +00:00
// set texture type
animdefs [ i ] . istexture = istexture ;
2014-03-15 16:59:03 +00:00
// "RANGE"
animdefsToken = M_GetToken ( NULL ) ;
if ( animdefsToken = = NULL )
{
2016-10-19 07:17:36 +00:00
I_Error ( " Error parsing ANIMDEFS lump: Unexpected end of file where \" RANGE \" after \" %s \" 's startname should be " , animdefs [ i ] . startname ) ;
2014-03-15 16:59:03 +00:00
}
if ( stricmp ( animdefsToken , " ALLOWDECALS " ) = = 0 )
{
// Another ZDoom keyword, ho-hum. Skip it, move on to the next token.
Z_Free ( animdefsToken ) ;
animdefsToken = M_GetToken ( NULL ) ;
}
if ( stricmp ( animdefsToken , " PIC " ) = = 0 )
{
// This is technically legitimate ANIMDEFS syntax, but SRB2 doesn't support it.
I_Error ( " Error parsing ANIMDEFS lump: Animation definitions utilizing \" PIC \" (specific frames instead of a consecutive range) are not supported by SRB2 " ) ;
}
if ( stricmp ( animdefsToken , " RANGE " ) ! = 0 )
{
2016-10-19 07:17:36 +00:00
I_Error ( " Error parsing ANIMDEFS lump: Expected \" RANGE \" after \" %s \" 's startname, got \" %s \" " , animdefs [ i ] . startname , animdefsToken ) ;
2014-03-15 16:59:03 +00:00
}
Z_Free ( animdefsToken ) ;
// Endname
animdefsToken = M_GetToken ( NULL ) ;
if ( animdefsToken = = NULL )
{
2016-10-19 07:17:36 +00:00
I_Error ( " Error parsing ANIMDEFS lump: Unexpected end of file where \" %s \" 's end texture/flat name should be " , animdefs [ i ] . startname ) ;
2014-03-15 16:59:03 +00:00
}
animdefsTokenLength = strlen ( animdefsToken ) ;
if ( animdefsTokenLength > 8 )
{
I_Error ( " Error parsing ANIMDEFS lump: lump name \" %s \" exceeds 8 characters " , animdefsToken ) ;
}
2016-10-19 07:17:36 +00:00
strncpy ( animdefs [ i ] . endname , animdefsToken , 9 ) ;
2014-03-15 16:59:03 +00:00
Z_Free ( animdefsToken ) ;
// "TICS"
animdefsToken = M_GetToken ( NULL ) ;
if ( animdefsToken = = NULL )
{
2016-10-19 07:17:36 +00:00
I_Error ( " Error parsing ANIMDEFS lump: Unexpected end of file where \" %s \" 's \" TICS \" should be " , animdefs [ i ] . startname ) ;
2014-03-15 16:59:03 +00:00
}
if ( stricmp ( animdefsToken , " RAND " ) = = 0 )
{
// This is technically legitimate ANIMDEFS syntax, but SRB2 doesn't support it.
I_Error ( " Error parsing ANIMDEFS lump: Animation definitions utilizing \" RAND \" (random duration per frame) are not supported by SRB2 " ) ;
}
if ( stricmp ( animdefsToken , " TICS " ) ! = 0 )
{
2016-10-19 07:17:36 +00:00
I_Error ( " Error parsing ANIMDEFS lump: Expected \" TICS \" in animation definition for \" %s \" , got \" %s \" " , animdefs [ i ] . startname , animdefsToken ) ;
2014-03-15 16:59:03 +00:00
}
Z_Free ( animdefsToken ) ;
// Speed
animdefsToken = M_GetToken ( NULL ) ;
if ( animdefsToken = = NULL )
{
2016-10-19 07:17:36 +00:00
I_Error ( " Error parsing ANIMDEFS lump: Unexpected end of file where \" %s \" 's animation speed should be " , animdefs [ i ] . startname ) ;
2014-03-15 16:59:03 +00:00
}
endPos = NULL ;
# ifndef AVOID_ERRNO
errno = 0 ;
# endif
animSpeed = strtol ( animdefsToken , & endPos , 10 ) ;
if ( endPos = = animdefsToken // Empty string
| | * endPos ! = ' \0 ' // Not end of string
# ifndef AVOID_ERRNO
| | errno = = ERANGE // Number out-of-range
# endif
| | animSpeed < 0 ) // Number is not positive
{
2016-10-19 07:17:36 +00:00
I_Error ( " Error parsing ANIMDEFS lump: Expected a positive integer for \" %s \" 's animation speed, got \" %s \" " , animdefs [ i ] . startname , animdefsToken ) ;
2014-03-15 16:59:03 +00:00
}
2016-10-19 07:17:36 +00:00
animdefs [ i ] . speed = animSpeed ;
2014-03-15 16:59:03 +00:00
Z_Free ( animdefsToken ) ;
2019-11-09 16:23:38 +00:00
# ifdef WALLFLATS
// hehe... uhh.....
if ( ! istexture )
{
GrowAnimDefs ( ) ;
M_Memcpy ( & animdefs [ maxanims - 1 ] , & animdefs [ i ] , sizeof ( animdef_t ) ) ;
animdefs [ maxanims - 1 ] . istexture = 1 ;
}
# endif
}
2014-03-15 16:59:03 +00:00
/** Checks for flats in levelflats that are part of a flat animation sequence
* and sets them up for animation .
*
* \ param animnum Index into : : anims to find flats for .
* \ sa P_SetupLevelFlatAnims
*/
static inline void P_FindAnimatedFlat ( INT32 animnum )
{
size_t i ;
lumpnum_t startflatnum , endflatnum ;
levelflat_t * foundflats ;
foundflats = levelflats ;
startflatnum = anims [ animnum ] . basepic ;
endflatnum = anims [ animnum ] . picnum ;
// note: high word of lumpnum is the wad number
if ( ( startflatnum > > 16 ) ! = ( endflatnum > > 16 ) )
I_Error ( " AnimatedFlat start %s not in same wad as end %s \n " ,
animdefs [ animnum ] . startname , animdefs [ animnum ] . endname ) ;
//
// now search through the levelflats if this anim flat sequence is used
//
for ( i = 0 ; i < numlevelflats ; i + + , foundflats + + )
{
// is that levelflat from the flat anim sequence ?
2019-11-09 04:45:00 +00:00
if ( ( anims [ animnum ] . istexture ) & & ( foundflats - > type = = LEVELFLAT_TEXTURE )
2019-10-21 03:21:22 +00:00
& & ( ( UINT16 ) foundflats - > u . texture . num > = startflatnum & & ( UINT16 ) foundflats - > u . texture . num < = endflatnum ) )
2019-05-21 18:24:26 +00:00
{
2019-10-21 03:21:22 +00:00
foundflats - > u . texture . basenum = startflatnum ;
foundflats - > animseq = foundflats - > u . texture . num - startflatnum ;
2019-05-21 18:24:26 +00:00
foundflats - > numpics = endflatnum - startflatnum + 1 ;
foundflats - > speed = anims [ animnum ] . speed ;
CONS_Debug ( DBG_SETUP , " animflat: #%03d name:%.8s animseq:%d numpics:%d speed:%d \n " ,
atoi ( sizeu1 ( i ) ) , foundflats - > name , foundflats - > animseq ,
foundflats - > numpics , foundflats - > speed ) ;
}
2019-11-09 22:32:06 +00:00
else if ( ( ! anims [ animnum ] . istexture ) & & ( foundflats - > type = = LEVELFLAT_FLAT )
& & ( foundflats - > u . flat . lumpnum > = startflatnum & & foundflats - > u . flat . lumpnum < = endflatnum ) )
2014-03-15 16:59:03 +00:00
{
2019-10-21 03:21:22 +00:00
foundflats - > u . flat . baselumpnum = startflatnum ;
foundflats - > animseq = foundflats - > u . flat . lumpnum - startflatnum ;
2014-03-15 16:59:03 +00:00
foundflats - > numpics = endflatnum - startflatnum + 1 ;
foundflats - > speed = anims [ animnum ] . speed ;
CONS_Debug ( DBG_SETUP , " animflat: #%03d name:%.8s animseq:%d numpics:%d speed:%d \n " ,
atoi ( sizeu1 ( i ) ) , foundflats - > name , foundflats - > animseq ,
foundflats - > numpics , foundflats - > speed ) ;
}
}
}
/** Sets up all flats used in a level.
*
* \ sa P_InitPicAnims , P_FindAnimatedFlat
*/
void P_SetupLevelFlatAnims ( void )
{
INT32 i ;
// the original game flat anim sequences
for ( i = 0 ; anims [ i ] . istexture ! = - 1 ; i + + )
2019-05-21 18:24:26 +00:00
P_FindAnimatedFlat ( i ) ;
2014-03-15 16:59:03 +00:00
}
//
// UTILITIES
//
2016-03-30 18:05:07 +00:00
#if 0
2014-03-15 16:59:03 +00:00
/** Gets a side from a sector line.
*
* \ param currentSector Sector the line is in .
* \ param line Index of the line within the sector .
* \ param side 0 for front , 1 for back .
* \ return Pointer to the side_t of the side you want .
* \ sa getSector , twoSided , getNextSector
*/
static inline side_t * getSide ( INT32 currentSector , INT32 line , INT32 side )
{
return & sides [ ( sectors [ currentSector ] . lines [ line ] ) - > sidenum [ side ] ] ;
}
/** Gets a sector from a sector line.
*
* \ param currentSector Sector the line is in .
* \ param line Index of the line within the sector .
* \ param side 0 for front , 1 for back .
* \ return Pointer to the : : sector_t of the sector on that side .
* \ sa getSide , twoSided , getNextSector
*/
static inline sector_t * getSector ( INT32 currentSector , INT32 line , INT32 side )
{
return sides [ ( sectors [ currentSector ] . lines [ line ] ) - > sidenum [ side ] ] . sector ;
}
/** Determines whether a sector line is two-sided.
* Uses the Boom method , checking if the line ' s back side is set to - 1 , rather
* than looking for : : ML_TWOSIDED .
*
* \ param sector The sector .
* \ param line Line index within the sector .
* \ return 1 if the sector is two - sided , 0 otherwise .
* \ sa getSide , getSector , getNextSector
*/
static inline boolean twoSided ( INT32 sector , INT32 line )
{
return ( sectors [ sector ] . lines [ line ] ) - > sidenum [ 1 ] ! = 0xffff ;
}
2016-03-30 18:05:07 +00:00
# endif
2014-03-15 16:59:03 +00:00
/** Finds sector next to current.
*
* \ param line Pointer to the line to cross .
* \ param sec Pointer to the current sector .
* \ return Pointer to a : : sector_t of the adjacent sector , or NULL if the line
* is one - sided .
* \ sa getSide , getSector , twoSided
* \ author Steven McGranahan
*/
static sector_t * getNextSector ( line_t * line , sector_t * sec )
{
if ( line - > frontsector = = sec )
{
if ( line - > backsector ! = sec )
return line - > backsector ;
else
return NULL ;
}
return line - > frontsector ;
}
/** Finds lowest floor in adjacent sectors.
*
* \ param sec Sector to start in .
* \ return Lowest floor height in an adjacent sector .
* \ sa P_FindHighestFloorSurrounding , P_FindNextLowestFloor ,
* P_FindLowestCeilingSurrounding
*/
fixed_t P_FindLowestFloorSurrounding ( sector_t * sec )
{
size_t i ;
line_t * check ;
sector_t * other ;
fixed_t floorh ;
floorh = sec - > floorheight ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
check = sec - > lines [ i ] ;
other = getNextSector ( check , sec ) ;
if ( ! other )
continue ;
if ( other - > floorheight < floorh )
floorh = other - > floorheight ;
}
return floorh ;
}
/** Finds highest floor in adjacent sectors.
*
* \ param sec Sector to start in .
* \ return Highest floor height in an adjacent sector .
* \ sa P_FindLowestFloorSurrounding , P_FindNextHighestFloor ,
* P_FindHighestCeilingSurrounding
*/
fixed_t P_FindHighestFloorSurrounding ( sector_t * sec )
{
size_t i ;
line_t * check ;
sector_t * other ;
fixed_t floorh = - 500 * FRACUNIT ;
INT32 foundsector = 0 ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
check = sec - > lines [ i ] ;
other = getNextSector ( check , sec ) ;
if ( ! other )
continue ;
if ( other - > floorheight > floorh | | ! foundsector )
floorh = other - > floorheight ;
if ( ! foundsector )
foundsector = 1 ;
}
return floorh ;
}
/** Finds next highest floor in adjacent sectors.
*
* \ param sec Sector to start in .
* \ param currentheight Height to start at .
* \ return Next highest floor height in an adjacent sector , or currentheight
* if there are none higher .
* \ sa P_FindHighestFloorSurrounding , P_FindNextLowestFloor ,
* P_FindNextHighestCeiling
* \ author Lee Killough
*/
fixed_t P_FindNextHighestFloor ( sector_t * sec , fixed_t currentheight )
{
sector_t * other ;
size_t i ;
fixed_t height ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
other = getNextSector ( sec - > lines [ i ] , sec ) ;
if ( other & & other - > floorheight > currentheight )
{
height = other - > floorheight ;
while ( + + i < sec - > linecount )
{
other = getNextSector ( sec - > lines [ i ] , sec ) ;
if ( other & &
other - > floorheight < height & &
other - > floorheight > currentheight )
height = other - > floorheight ;
}
return height ;
}
}
return currentheight ;
}
////////////////////////////////////////////////////
// SoM: Start new Boom functions
////////////////////////////////////////////////////
/** Finds next lowest floor in adjacent sectors.
*
* \ param sec Sector to start in .
* \ param currentheight Height to start at .
* \ return Next lowest floor height in an adjacent sector , or currentheight
* if there are none lower .
* \ sa P_FindLowestFloorSurrounding , P_FindNextHighestFloor ,
* P_FindNextLowestCeiling
* \ author Lee Killough
*/
fixed_t P_FindNextLowestFloor ( sector_t * sec , fixed_t currentheight )
{
sector_t * other ;
size_t i ;
fixed_t height ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
other = getNextSector ( sec - > lines [ i ] , sec ) ;
if ( other & & other - > floorheight < currentheight )
{
height = other - > floorheight ;
while ( + + i < sec - > linecount )
{
other = getNextSector ( sec - > lines [ i ] , sec ) ;
if ( other & & other - > floorheight > height
& & other - > floorheight < currentheight )
height = other - > floorheight ;
}
return height ;
}
}
return currentheight ;
}
#if 0
/** Finds next lowest ceiling in adjacent sectors.
*
* \ param sec Sector to start in .
* \ param currentheight Height to start at .
* \ return Next lowest ceiling height in an adjacent sector , or currentheight
* if there are none lower .
* \ sa P_FindLowestCeilingSurrounding , P_FindNextHighestCeiling ,
* P_FindNextLowestFloor
* \ author Lee Killough
*/
static fixed_t P_FindNextLowestCeiling ( sector_t * sec , fixed_t currentheight )
{
sector_t * other ;
size_t i ;
fixed_t height ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
other = getNextSector ( sec - > lines [ i ] , sec ) ;
if ( other & & other - > ceilingheight < currentheight )
{
height = other - > ceilingheight ;
while ( + + i < sec - > linecount )
{
other = getNextSector ( sec - > lines [ i ] , sec ) ;
if ( other & & other - > ceilingheight > height
& & other - > ceilingheight < currentheight )
height = other - > ceilingheight ;
}
return height ;
}
}
return currentheight ;
}
/** Finds next highest ceiling in adjacent sectors.
*
* \ param sec Sector to start in .
* \ param currentheight Height to start at .
* \ return Next highest ceiling height in an adjacent sector , or currentheight
* if there are none higher .
* \ sa P_FindHighestCeilingSurrounding , P_FindNextLowestCeiling ,
* P_FindNextHighestFloor
* \ author Lee Killough
*/
static fixed_t P_FindNextHighestCeiling ( sector_t * sec , fixed_t currentheight )
{
sector_t * other ;
size_t i ;
fixed_t height ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
other = getNextSector ( sec - > lines [ i ] , sec ) ;
if ( other & & other - > ceilingheight > currentheight )
{
height = other - > ceilingheight ;
while ( + + i < sec - > linecount )
{
other = getNextSector ( sec - > lines [ i ] , sec ) ;
if ( other & & other - > ceilingheight < height
& & other - > ceilingheight > currentheight )
height = other - > ceilingheight ;
}
return height ;
}
}
return currentheight ;
}
# endif
////////////////////////////
// End New Boom functions
////////////////////////////
/** Finds lowest ceiling in adjacent sectors.
*
* \ param sec Sector to start in .
* \ return Lowest ceiling height in an adjacent sector .
* \ sa P_FindHighestCeilingSurrounding , P_FindNextLowestCeiling ,
* P_FindLowestFloorSurrounding
*/
fixed_t P_FindLowestCeilingSurrounding ( sector_t * sec )
{
size_t i ;
line_t * check ;
sector_t * other ;
fixed_t height = 32000 * FRACUNIT ; //SoM: 3/7/2000: Remove ovf
INT32 foundsector = 0 ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
check = sec - > lines [ i ] ;
other = getNextSector ( check , sec ) ;
if ( ! other )
continue ;
if ( other - > ceilingheight < height | | ! foundsector )
height = other - > ceilingheight ;
if ( ! foundsector )
foundsector = 1 ;
}
return height ;
}
/** Finds Highest ceiling in adjacent sectors.
*
* \ param sec Sector to start in .
* \ return Highest ceiling height in an adjacent sector .
* \ sa P_FindLowestCeilingSurrounding , P_FindNextHighestCeiling ,
* P_FindHighestFloorSurrounding
*/
fixed_t P_FindHighestCeilingSurrounding ( sector_t * sec )
{
size_t i ;
line_t * check ;
sector_t * other ;
fixed_t height = 0 ;
INT32 foundsector = 0 ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
check = sec - > lines [ i ] ;
other = getNextSector ( check , sec ) ;
if ( ! other )
continue ;
if ( other - > ceilingheight > height | | ! foundsector )
height = other - > ceilingheight ;
if ( ! foundsector )
foundsector = 1 ;
}
return height ;
}
#if 0
//SoM: 3/7/2000: UTILS.....
//
// P_FindShortestTextureAround()
//
// Passed a sector number, returns the shortest lower texture on a
// linedef bounding the sector.
//
//
static fixed_t P_FindShortestTextureAround ( INT32 secnum )
{
fixed_t minsize = 32000 < < FRACBITS ;
side_t * side ;
size_t i ;
sector_t * sec = & sectors [ secnum ] ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
if ( twoSided ( secnum , i ) )
{
side = getSide ( secnum , i , 0 ) ;
if ( side - > bottomtexture > 0 )
if ( textureheight [ side - > bottomtexture ] < minsize )
minsize = textureheight [ side - > bottomtexture ] ;
side = getSide ( secnum , i , 1 ) ;
if ( side - > bottomtexture > 0 )
if ( textureheight [ side - > bottomtexture ] < minsize )
minsize = textureheight [ side - > bottomtexture ] ;
}
}
return minsize ;
}
//SoM: 3/7/2000: Stuff.... (can you tell I'm getting tired? It's 12 : 30!)
//
// P_FindShortestUpperAround()
//
// Passed a sector number, returns the shortest upper texture on a
// linedef bounding the sector.
//
//
static fixed_t P_FindShortestUpperAround ( INT32 secnum )
{
fixed_t minsize = 32000 < < FRACBITS ;
side_t * side ;
size_t i ;
sector_t * sec = & sectors [ secnum ] ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
if ( twoSided ( secnum , i ) )
{
side = getSide ( secnum , i , 0 ) ;
if ( side - > toptexture > 0 )
if ( textureheight [ side - > toptexture ] < minsize )
minsize = textureheight [ side - > toptexture ] ;
side = getSide ( secnum , i , 1 ) ;
if ( side - > toptexture > 0 )
if ( textureheight [ side - > toptexture ] < minsize )
minsize = textureheight [ side - > toptexture ] ;
}
}
return minsize ;
}
//SoM: 3/7/2000
//
// P_FindModelFloorSector()
//
// Passed a floor height and a sector number, return a pointer to a
// a sector with that floor height across the lowest numbered two sided
// line surrounding the sector.
//
// Note: If no sector at that height bounds the sector passed, return NULL
//
//
static sector_t * P_FindModelFloorSector ( fixed_t floordestheight , INT32 secnum )
{
size_t i ;
sector_t * sec = & sectors [ secnum ] ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
if ( twoSided ( secnum , i ) )
{
if ( getSide ( secnum , i , 0 ) - > sector - sectors = = secnum )
sec = getSector ( secnum , i , 1 ) ;
else
sec = getSector ( secnum , i , 0 ) ;
if ( sec - > floorheight = = floordestheight )
return sec ;
}
}
return NULL ;
}
//
// P_FindModelCeilingSector()
//
// Passed a ceiling height and a sector number, return a pointer to a
// a sector with that ceiling height across the lowest numbered two sided
// line surrounding the sector.
//
// Note: If no sector at that height bounds the sector passed, return NULL
//
static sector_t * P_FindModelCeilingSector ( fixed_t ceildestheight , INT32 secnum )
{
size_t i ;
sector_t * sec = & sectors [ secnum ] ;
for ( i = 0 ; i < sec - > linecount ; i + + )
{
if ( twoSided ( secnum , i ) )
{
if ( getSide ( secnum , i , 0 ) - > sector - sectors = = secnum )
sec = getSector ( secnum , i , 1 ) ;
else
sec = getSector ( secnum , i , 0 ) ;
if ( sec - > ceilingheight = = ceildestheight )
return sec ;
}
}
return NULL ;
}
# endif
// Parses arguments for parameterized polyobject door types
static boolean PolyDoor ( line_t * line )
{
polydoordata_t pdd ;
2020-04-17 20:26:04 +00:00
pdd . polyObjNum = Tag_FGet ( & line - > tags ) ; // polyobject id
2014-03-15 16:59:03 +00:00
switch ( line - > special )
{
case 480 : // Polyobj_DoorSlide
pdd . doorType = POLY_DOOR_SLIDE ;
pdd . speed = sides [ line - > sidenum [ 0 ] ] . textureoffset / 8 ;
pdd . angle = R_PointToAngle2 ( line - > v1 - > x , line - > v1 - > y , line - > v2 - > x , line - > v2 - > y ) ; // angle of motion
pdd . distance = sides [ line - > sidenum [ 0 ] ] . rowoffset ;
if ( line - > sidenum [ 1 ] ! = 0xffff )
pdd . delay = sides [ line - > sidenum [ 1 ] ] . textureoffset > > FRACBITS ; // delay in tics
else
pdd . delay = 0 ;
break ;
case 481 : // Polyobj_DoorSwing
pdd . doorType = POLY_DOOR_SWING ;
pdd . speed = sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ; // angular speed
pdd . distance = sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ; // angular distance
if ( line - > sidenum [ 1 ] ! = 0xffff )
pdd . delay = sides [ line - > sidenum [ 1 ] ] . textureoffset > > FRACBITS ; // delay in tics
else
pdd . delay = 0 ;
break ;
default :
return 0 ; // ???
}
return EV_DoPolyDoor ( & pdd ) ;
}
// Parses arguments for parameterized polyobject move specials
static boolean PolyMove ( line_t * line )
{
polymovedata_t pmd ;
2020-04-17 20:26:04 +00:00
pmd . polyObjNum = Tag_FGet ( & line - > tags ) ;
2014-03-15 16:59:03 +00:00
pmd . speed = sides [ line - > sidenum [ 0 ] ] . textureoffset / 8 ;
pmd . angle = R_PointToAngle2 ( line - > v1 - > x , line - > v1 - > y , line - > v2 - > x , line - > v2 - > y ) ;
pmd . distance = sides [ line - > sidenum [ 0 ] ] . rowoffset ;
pmd . overRide = ( line - > special = = 483 ) ; // Polyobj_OR_Move
return EV_DoPolyObjMove ( & pmd ) ;
}
// Makes a polyobject invisible and intangible
// If NOCLIMB is ticked, the polyobject will still be tangible, just not visible.
static void PolyInvisible ( line_t * line )
{
2020-04-17 20:26:04 +00:00
INT32 polyObjNum = Tag_FGet ( & line - > tags ) ;
2014-03-15 16:59:03 +00:00
polyobj_t * po ;
if ( ! ( po = Polyobj_GetForNum ( polyObjNum ) ) )
{
CONS_Debug ( DBG_POLYOBJ , " PolyInvisible: bad polyobj %d \n " , polyObjNum ) ;
return ;
}
// don't allow line actions to affect bad polyobjects
if ( po - > isBad )
return ;
if ( ! ( line - > flags & ML_NOCLIMB ) )
po - > flags & = ~ POF_SOLID ;
po - > flags | = POF_NOSPECIALS ;
po - > flags & = ~ POF_RENDERALL ;
}
// Makes a polyobject visible and tangible
// If NOCLIMB is ticked, the polyobject will not be tangible, just visible.
static void PolyVisible ( line_t * line )
{
2020-04-17 20:26:04 +00:00
INT32 polyObjNum = Tag_FGet ( & line - > tags ) ;
2014-03-15 16:59:03 +00:00
polyobj_t * po ;
if ( ! ( po = Polyobj_GetForNum ( polyObjNum ) ) )
{
CONS_Debug ( DBG_POLYOBJ , " PolyVisible: bad polyobj %d \n " , polyObjNum ) ;
return ;
}
// don't allow line actions to affect bad polyobjects
if ( po - > isBad )
return ;
if ( ! ( line - > flags & ML_NOCLIMB ) )
po - > flags | = POF_SOLID ;
po - > flags & = ~ POF_NOSPECIALS ;
2018-09-13 01:58:20 +00:00
po - > flags | = ( po - > spawnflags & POF_RENDERALL ) ;
2014-03-15 16:59:03 +00:00
}
2020-05-04 08:01:44 +00:00
2014-03-15 16:59:03 +00:00
// Sets the translucency of a polyobject
// Frontsector floor / 100 = translevel
static void PolyTranslucency ( line_t * line )
{
2020-04-17 20:26:04 +00:00
INT32 polyObjNum = Tag_FGet ( & line - > tags ) ;
2014-03-15 16:59:03 +00:00
polyobj_t * po ;
2020-05-04 08:29:35 +00:00
INT32 value ;
2014-03-15 16:59:03 +00:00
if ( ! ( po = Polyobj_GetForNum ( polyObjNum ) ) )
{
CONS_Debug ( DBG_POLYOBJ , " EV_DoPolyObjWaypoint: bad polyobj %d \n " , polyObjNum ) ;
return ;
}
// don't allow line actions to affect bad polyobjects
if ( po - > isBad )
return ;
2020-05-04 08:29:35 +00:00
// If Front X Offset is specified, use that. Else, use floorheight.
value = ( sides [ line - > sidenum [ 0 ] ] . textureoffset ? sides [ line - > sidenum [ 0 ] ] . textureoffset : line - > frontsector - > floorheight ) > > FRACBITS ;
// If DONTPEGBOTTOM, specify raw translucency value. Else, take it out of 1000.
if ( ! ( line - > flags & ML_DONTPEGBOTTOM ) )
value / = 100 ;
2018-09-13 04:04:50 +00:00
if ( line - > flags & ML_EFFECT3 ) // relative calc
2020-05-04 08:29:35 +00:00
po - > translucency + = value ;
2018-09-13 04:04:50 +00:00
else
2020-05-04 08:29:35 +00:00
po - > translucency = value ;
po - > translucency = max ( min ( po - > translucency , NUMTRANSMAPS ) , 0 ) ;
2014-03-15 16:59:03 +00:00
}
2018-09-07 19:27:18 +00:00
// Makes a polyobject translucency fade and applies tangibility
2018-09-09 05:08:02 +00:00
static boolean PolyFade ( line_t * line )
2018-09-07 19:27:18 +00:00
{
2020-04-17 20:26:04 +00:00
INT32 polyObjNum = Tag_FGet ( & line - > tags ) ;
2018-09-07 19:27:18 +00:00
polyobj_t * po ;
2018-09-15 04:56:27 +00:00
polyfadedata_t pfd ;
2020-05-04 08:29:35 +00:00
INT32 value ;
2018-09-07 19:27:18 +00:00
if ( ! ( po = Polyobj_GetForNum ( polyObjNum ) ) )
{
2018-09-18 11:06:59 +00:00
CONS_Debug ( DBG_POLYOBJ , " PolyFade: bad polyobj %d \n " , polyObjNum ) ;
2018-09-09 05:08:02 +00:00
return 0 ;
2018-09-07 19:27:18 +00:00
}
// don't allow line actions to affect bad polyobjects
if ( po - > isBad )
2018-09-09 05:08:02 +00:00
return 0 ;
2018-09-07 19:27:18 +00:00
2018-09-18 11:06:59 +00:00
// Prevent continuous execs from interfering on an existing fade
if ( ! ( line - > flags & ML_EFFECT5 )
& & po - > thinker
& & po - > thinker - > function . acp1 = = ( actionf_p1 ) T_PolyObjFade )
{
CONS_Debug ( DBG_POLYOBJ , " Line type 492 Executor: Fade PolyObject thinker already exists \n " ) ;
return 0 ;
}
2018-09-13 04:04:50 +00:00
pfd . polyObjNum = polyObjNum ;
2020-05-04 08:29:35 +00:00
// If Front X Offset is specified, use that. Else, use floorheight.
value = ( sides [ line - > sidenum [ 0 ] ] . textureoffset ? sides [ line - > sidenum [ 0 ] ] . textureoffset : line - > frontsector - > floorheight ) > > FRACBITS ;
// If DONTPEGBOTTOM, specify raw translucency value. Else, take it out of 1000.
if ( ! ( line - > flags & ML_DONTPEGBOTTOM ) )
value / = 100 ;
2018-09-13 04:26:24 +00:00
if ( line - > flags & ML_EFFECT3 ) // relative calc
2020-05-04 08:29:35 +00:00
pfd . destvalue = po - > translucency + value ;
2018-09-13 04:04:50 +00:00
else
2020-05-04 08:29:35 +00:00
pfd . destvalue = value ;
pfd . destvalue = max ( min ( pfd . destvalue , NUMTRANSMAPS ) , 0 ) ;
2018-09-13 04:04:50 +00:00
2018-09-09 05:08:02 +00:00
// already equal, nothing to do
2018-09-13 04:04:50 +00:00
if ( po - > translucency = = pfd . destvalue )
2018-09-09 05:08:02 +00:00
return 1 ;
2018-09-07 19:27:18 +00:00
2018-09-13 04:04:50 +00:00
pfd . docollision = ! ( line - > flags & ML_BOUNCY ) ; // do not handle collision flags
pfd . doghostfade = ( line - > flags & ML_EFFECT1 ) ; // do ghost fade (no collision flags during fade)
pfd . ticbased = ( line - > flags & ML_EFFECT4 ) ; // Speed = Tic Duration
// allow Back Y Offset to be consistent with other fade specials
pfd . speed = ( line - > sidenum [ 1 ] ! = 0xFFFF & & ! sides [ line - > sidenum [ 0 ] ] . rowoffset ) ?
abs ( sides [ line - > sidenum [ 1 ] ] . rowoffset > > FRACBITS )
: abs ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ;
2018-09-07 19:27:18 +00:00
return EV_DoPolyObjFade ( & pfd ) ;
2014-03-15 16:59:03 +00:00
}
// Parses arguments for parameterized polyobject waypoint movement
static boolean PolyWaypoint ( line_t * line )
{
polywaypointdata_t pwd ;
2020-04-17 20:26:04 +00:00
pwd . polyObjNum = Tag_FGet ( & line - > tags ) ;
2014-03-15 16:59:03 +00:00
pwd . speed = sides [ line - > sidenum [ 0 ] ] . textureoffset / 8 ;
pwd . sequence = sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ; // Sequence #
2020-05-13 14:21:47 +00:00
// Behavior after reaching the last waypoint?
if ( line - > flags & ML_EFFECT3 )
pwd . returnbehavior = PWR_WRAP ; // Wrap back to first waypoint
else if ( line - > flags & ML_EFFECT2 )
pwd . returnbehavior = PWR_COMEBACK ; // Go through sequence in reverse
else
pwd . returnbehavior = PWR_STOP ; // Stop
2020-05-16 07:49:30 +00:00
// Flags
pwd . flags = 0 ;
if ( line - > flags & ML_EFFECT1 )
pwd . flags | = PWF_REVERSE ;
if ( line - > flags & ML_EFFECT4 )
pwd . flags | = PWF_LOOP ;
2014-03-15 16:59:03 +00:00
return EV_DoPolyObjWaypoint ( & pwd ) ;
}
// Parses arguments for parameterized polyobject rotate specials
static boolean PolyRotate ( line_t * line )
{
polyrotdata_t prd ;
2020-04-17 20:26:04 +00:00
prd . polyObjNum = Tag_FGet ( & line - > tags ) ;
2014-03-15 16:59:03 +00:00
prd . speed = sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ; // angular speed
prd . distance = sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ; // angular distance
// Polyobj_(OR_)RotateRight have dir == -1
prd . direction = ( line - > special = = 484 | | line - > special = = 485 ) ? - 1 : 1 ;
// Polyobj_OR types have override set to true
prd . overRide = ( line - > special = = 485 | | line - > special = = 487 ) ;
2014-11-12 00:55:07 +00:00
if ( line - > flags & ML_NOCLIMB )
prd . turnobjs = 0 ;
else if ( line - > flags & ML_EFFECT4 )
prd . turnobjs = 2 ;
else
prd . turnobjs = 1 ;
2014-03-15 16:59:03 +00:00
return EV_DoPolyObjRotate ( & prd ) ;
}
2020-05-04 07:58:27 +00:00
// Parses arguments for polyobject flag waving special
static boolean PolyFlag ( line_t * line )
{
polyflagdata_t pfd ;
2020-07-10 14:51:11 +00:00
pfd . polyObjNum = Tag_FGet ( & line - > tags ) ;
2020-05-04 07:58:27 +00:00
pfd . speed = P_AproxDistance ( line - > dx , line - > dy ) > > FRACBITS ;
pfd . angle = R_PointToAngle2 ( line - > v1 - > x , line - > v1 - > y , line - > v2 - > x , line - > v2 - > y ) > > ANGLETOFINESHIFT ;
pfd . momx = sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ;
return EV_DoPolyObjFlag ( & pfd ) ;
}
2014-11-12 00:55:07 +00:00
// Parses arguments for parameterized polyobject move-by-sector-heights specials
static boolean PolyDisplace ( line_t * line )
{
polydisplacedata_t pdd ;
2020-04-17 20:26:04 +00:00
pdd . polyObjNum = Tag_FGet ( & line - > tags ) ;
2014-11-12 00:55:07 +00:00
pdd . controlSector = line - > frontsector ;
pdd . dx = line - > dx > > 8 ;
pdd . dy = line - > dy > > 8 ;
return EV_DoPolyObjDisplace ( & pdd ) ;
}
2019-04-05 09:50:59 +00:00
2020-05-04 08:01:44 +00:00
// Parses arguments for parameterized polyobject rotate-by-sector-heights specials
2019-04-05 09:50:59 +00:00
static boolean PolyRotDisplace ( line_t * line )
{
polyrotdisplacedata_t pdd ;
fixed_t anginter , distinter ;
2020-04-17 20:26:04 +00:00
pdd . polyObjNum = Tag_FGet ( & line - > tags ) ;
2019-04-05 09:50:59 +00:00
pdd . controlSector = line - > frontsector ;
// Rotate 'anginter' interval for each 'distinter' interval from the control sector.
// Use default values if not provided as fallback.
anginter = sides [ line - > sidenum [ 0 ] ] . rowoffset ? sides [ line - > sidenum [ 0 ] ] . rowoffset : 90 * FRACUNIT ;
distinter = sides [ line - > sidenum [ 0 ] ] . textureoffset ? sides [ line - > sidenum [ 0 ] ] . textureoffset : 128 * FRACUNIT ;
pdd . rotscale = FixedDiv ( anginter , distinter ) ;
// Same behavior as other rotators when carrying things.
if ( line - > flags & ML_NOCLIMB )
pdd . turnobjs = 0 ;
else if ( line - > flags & ML_EFFECT4 )
pdd . turnobjs = 2 ;
else
pdd . turnobjs = 1 ;
return EV_DoPolyObjRotDisplace ( & pdd ) ;
}
2014-03-15 16:59:03 +00:00
2018-03-31 14:11:27 +00:00
//
// P_RunNightserizeExecutors
//
void P_RunNightserizeExecutors ( mobj_t * actor )
{
size_t i ;
for ( i = 0 ; i < numlines ; i + + )
{
if ( lines [ i ] . special = = 323 | | lines [ i ] . special = = 324 )
P_RunTriggerLinedef ( & lines [ i ] , actor , NULL ) ;
}
}
//
// P_RunDeNightserizeExecutors
//
void P_RunDeNightserizeExecutors ( mobj_t * actor )
{
size_t i ;
for ( i = 0 ; i < numlines ; i + + )
{
if ( lines [ i ] . special = = 325 | | lines [ i ] . special = = 326 )
P_RunTriggerLinedef ( & lines [ i ] , actor , NULL ) ;
}
}
//
// P_RunNightsLapExecutors
//
void P_RunNightsLapExecutors ( mobj_t * actor )
{
size_t i ;
for ( i = 0 ; i < numlines ; i + + )
{
if ( lines [ i ] . special = = 327 | | lines [ i ] . special = = 328 )
P_RunTriggerLinedef ( & lines [ i ] , actor , NULL ) ;
}
}
2018-04-01 12:23:59 +00:00
//
2018-08-14 16:00:37 +00:00
// P_RunNightsCapsuleTouchExecutors
2018-04-01 12:23:59 +00:00
//
2018-08-14 16:00:37 +00:00
void P_RunNightsCapsuleTouchExecutors ( mobj_t * actor , boolean entering , boolean enoughspheres )
2018-04-01 12:23:59 +00:00
{
size_t i ;
for ( i = 0 ; i < numlines ; i + + )
{
2018-08-14 14:48:50 +00:00
if ( ( lines [ i ] . special = = 329 | | lines [ i ] . special = = 330 )
2018-08-14 16:00:37 +00:00
& & ( ( entering & & ( lines [ i ] . flags & ML_TFERLINE ) )
| | ( ! entering & & ! ( lines [ i ] . flags & ML_TFERLINE ) ) )
& & ( ( lines [ i ] . flags & ML_DONTPEGTOP )
| | ( enoughspheres & & ! ( lines [ i ] . flags & ML_BOUNCY ) )
| | ( ! enoughspheres & & ( lines [ i ] . flags & ML_BOUNCY ) ) ) )
2018-04-01 12:23:59 +00:00
P_RunTriggerLinedef ( & lines [ i ] , actor , NULL ) ;
}
}
2014-03-15 16:59:03 +00:00
/** Finds minimum light from an adjacent sector.
*
* \ param sector Sector to start in .
* \ param max Maximum value to return .
* \ return Minimum light value from an adjacent sector , or max if the minimum
* light value is greater than max .
*/
INT32 P_FindMinSurroundingLight ( sector_t * sector , INT32 max )
{
size_t i ;
INT32 min = max ;
line_t * line ;
sector_t * check ;
for ( i = 0 ; i < sector - > linecount ; i + + )
{
line = sector - > lines [ i ] ;
check = getNextSector ( line , sector ) ;
if ( ! check )
continue ;
if ( check - > lightlevel < min )
min = check - > lightlevel ;
}
return min ;
}
void T_ExecutorDelay ( executor_t * e )
{
if ( - - e - > timer < = 0 )
{
if ( e - > caller & & P_MobjWasRemoved ( e - > caller ) ) // If the mobj died while we were delaying
P_SetTarget ( & e - > caller , NULL ) ; // Call with no mobj!
2014-11-12 00:55:07 +00:00
P_ProcessLineSpecial ( e - > line , e - > caller , e - > sector ) ;
2014-03-15 16:59:03 +00:00
P_SetTarget ( & e - > caller , NULL ) ; // Let the mobj know it can be removed now.
P_RemoveThinker ( & e - > thinker ) ;
}
}
2014-11-12 00:55:07 +00:00
static void P_AddExecutorDelay ( line_t * line , mobj_t * mobj , sector_t * sector )
2014-03-15 16:59:03 +00:00
{
executor_t * e ;
2020-05-03 18:41:37 +00:00
INT32 delay ;
2014-03-15 16:59:03 +00:00
2020-05-03 18:41:37 +00:00
if ( udmf )
delay = line - > executordelay ;
else
{
if ( ! line - > backsector )
I_Error ( " P_AddExecutorDelay: Line has no backsector! \n " ) ;
delay = ( line - > backsector - > ceilingheight > > FRACBITS ) + ( line - > backsector - > floorheight > > FRACBITS ) ;
}
2014-03-15 16:59:03 +00:00
e = Z_Calloc ( sizeof ( * e ) , PU_LEVSPEC , NULL ) ;
e - > thinker . function . acp1 = ( actionf_p1 ) T_ExecutorDelay ;
e - > line = line ;
2014-11-12 00:55:07 +00:00
e - > sector = sector ;
2020-05-03 18:41:37 +00:00
e - > timer = delay ;
2014-03-15 16:59:03 +00:00
P_SetTarget ( & e - > caller , mobj ) ; // Use P_SetTarget to make sure the mobj doesn't get freed while we're delaying.
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & e - > thinker ) ;
2014-03-15 16:59:03 +00:00
}
2018-04-02 12:11:28 +00:00
/** Used by P_RunTriggerLinedef to check a NiGHTS trigger linedef's conditions
*
* \ param triggerline Trigger linedef to check conditions for ; should NEVER be NULL .
* \ param actor Object initiating the action ; should not be NULL .
* \ sa P_RunTriggerLinedef
*/
static boolean P_CheckNightsTriggerLine ( line_t * triggerline , mobj_t * actor )
{
INT16 specialtype = triggerline - > special ;
size_t i ;
UINT8 inputmare = max ( 0 , min ( 255 , sides [ triggerline - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ) ;
UINT8 inputlap = max ( 0 , min ( 255 , sides [ triggerline - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ) ;
boolean ltemare = triggerline - > flags & ML_NOCLIMB ;
boolean gtemare = triggerline - > flags & ML_BLOCKMONSTERS ;
boolean ltelap = triggerline - > flags & ML_EFFECT1 ;
boolean gtelap = triggerline - > flags & ML_EFFECT2 ;
boolean lapfrombonustime = triggerline - > flags & ML_EFFECT3 ;
boolean perglobalinverse = triggerline - > flags & ML_DONTPEGBOTTOM ;
boolean perglobal = ! ( triggerline - > flags & ML_EFFECT4 ) & & ! perglobalinverse ;
boolean donomares = triggerline - > flags & ML_BOUNCY ; // nightserize: run at end of level (no mares)
boolean fromnonights = triggerline - > flags & ML_TFERLINE ; // nightserize: from non-nights // denightserize: all players no nights
2018-04-07 11:33:07 +00:00
boolean fromnights = triggerline - > flags & ML_DONTPEGTOP ; // nightserize: from nights // denightserize: >0 players are nights
2018-04-02 12:11:28 +00:00
UINT8 currentmare = UINT8_MAX ;
UINT8 currentlap = UINT8_MAX ;
// Do early returns for Nightserize
if ( specialtype > = 323 & & specialtype < = 324 )
{
// run only when no mares are found
if ( donomares & & P_FindLowestMare ( ) ! = UINT8_MAX )
return false ;
2018-04-02 12:13:53 +00:00
// run only if there is a mare present
if ( ! donomares & & P_FindLowestMare ( ) = = UINT8_MAX )
return false ;
2018-04-02 12:11:28 +00:00
// run only if player is nightserizing from non-nights
if ( fromnonights )
{
if ( ! actor - > player )
return false ;
else if ( actor - > player - > powers [ pw_carry ] = = CR_NIGHTSMODE )
return false ;
}
2018-04-07 11:33:07 +00:00
// run only if player is nightserizing from nights
else if ( fromnights )
{
if ( ! actor - > player )
return false ;
else if ( actor - > player - > powers [ pw_carry ] ! = CR_NIGHTSMODE )
return false ;
}
2018-04-02 12:11:28 +00:00
}
// Get current mare and lap (and check early return for DeNightserize)
2018-08-14 14:48:50 +00:00
if ( perglobal | | perglobalinverse
| | ( specialtype > = 325 & & specialtype < = 326 & & ( fromnonights | | fromnights ) ) )
2018-04-02 12:11:28 +00:00
{
2018-04-07 11:33:07 +00:00
UINT8 playersarenights = 0 ;
2018-04-02 12:11:28 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
2018-09-04 16:04:24 +00:00
UINT8 lap ;
2018-04-02 12:11:28 +00:00
if ( ! playeringame [ i ] | | players [ i ] . spectator )
continue ;
// denightserize: run only if all players are not nights
if ( specialtype > = 325 & & specialtype < = 326 & & fromnonights
& & players [ i ] . powers [ pw_carry ] = = CR_NIGHTSMODE )
return false ;
2018-04-07 11:33:07 +00:00
// count number of nights players for denightserize return
if ( specialtype > = 325 & & specialtype < = 326 & & fromnights
& & players [ i ] . powers [ pw_carry ] = = CR_NIGHTSMODE )
playersarenights + + ;
2018-09-04 16:04:24 +00:00
lap = lapfrombonustime ? players [ i ] . marebonuslap : players [ i ] . marelap ;
2018-04-02 12:11:28 +00:00
// get highest mare/lap of players
if ( perglobal )
{
if ( players [ i ] . mare > currentmare | | currentmare = = UINT8_MAX )
{
currentmare = players [ i ] . mare ;
currentlap = UINT8_MAX ;
}
2018-08-14 14:48:50 +00:00
if ( players [ i ] . mare = = currentmare
2018-04-02 12:11:28 +00:00
& & ( lap > currentlap | | currentlap = = UINT8_MAX ) )
currentlap = lap ;
}
// get lowest mare/lap of players
else if ( perglobalinverse )
{
if ( players [ i ] . mare < currentmare | | currentmare = = UINT8_MAX )
{
currentmare = players [ i ] . mare ;
currentlap = UINT8_MAX ;
}
2018-08-14 14:48:50 +00:00
if ( players [ i ] . mare = = currentmare
2018-04-02 12:11:28 +00:00
& & ( lap < currentlap | | currentlap = = UINT8_MAX ) )
currentlap = lap ;
}
}
2018-04-07 11:33:07 +00:00
// denightserize: run only if >0 players are nights
if ( specialtype > = 325 & & specialtype < = 326 & & fromnights
& & playersarenights < 1 )
return false ;
2018-04-02 12:11:28 +00:00
}
// get current mare/lap from triggering player
2018-04-02 12:13:53 +00:00
else if ( ! perglobal & & ! perglobalinverse )
2018-04-02 12:11:28 +00:00
{
if ( ! actor - > player )
return false ;
currentmare = actor - > player - > mare ;
currentlap = lapfrombonustime ? actor - > player - > marebonuslap : actor - > player - > marelap ;
}
2018-08-14 20:08:21 +00:00
if ( lapfrombonustime & & ! currentlap )
return false ; // special case: player->marebonuslap is 0 until passing through on bonus time. Don't trigger lines looking for inputlap 0.
2018-04-02 12:11:28 +00:00
// Compare current mare/lap to input mare/lap based on rules
if ( ! ( specialtype > = 323 & & specialtype < = 324 & & donomares ) // don't return false if donomares and we got this far
& & ( ( ltemare & & currentmare > inputmare )
| | ( gtemare & & currentmare < inputmare )
| | ( ! ltemare & & ! gtemare & & currentmare ! = inputmare )
| | ( ltelap & & currentlap > inputlap )
| | ( gtelap & & currentlap < inputlap )
| | ( ! ltelap & & ! gtelap & & currentlap ! = inputlap ) )
)
return false ;
return true ;
}
2014-08-04 03:49:33 +00:00
/** Used by P_LinedefExecute to check a trigger linedef's conditions
* The linedef executor specials in the trigger linedef ' s sector are run if all conditions are met .
* Return false cancels P_LinedefExecute , this happens if a condition is not met .
2014-03-15 16:59:03 +00:00
*
2014-08-04 03:49:33 +00:00
* \ param triggerline Trigger linedef to check conditions for ; should NEVER be NULL .
2014-03-15 16:59:03 +00:00
* \ param actor Object initiating the action ; should not be NULL .
* \ param caller Sector in which the action was started . May be NULL .
2014-08-04 03:49:33 +00:00
* \ sa P_ProcessLineSpecial , P_LinedefExecute
2014-03-15 16:59:03 +00:00
*/
2014-08-04 03:49:33 +00:00
boolean P_RunTriggerLinedef ( line_t * triggerline , mobj_t * actor , sector_t * caller )
2014-03-15 16:59:03 +00:00
{
sector_t * ctlsector ;
2014-08-04 03:49:33 +00:00
fixed_t dist = P_AproxDistance ( triggerline - > dx , triggerline - > dy ) > > FRACBITS ;
size_t i , linecnt , sectori ;
INT16 specialtype = triggerline - > special ;
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
/////////////////////////////////////////////////
// Distance-checking/sector trigger conditions //
/////////////////////////////////////////////////
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
// Linetypes 303 and 304 require a specific
// number, or minimum or maximum, of rings.
if ( specialtype = = 303 | | specialtype = = 304 )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
fixed_t rings = 0 ;
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
// With the passuse flag, count all player's
// rings.
if ( triggerline - > flags & ML_EFFECT4 )
{
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( ! playeringame [ i ] | | players [ i ] . spectator )
continue ;
2014-03-15 16:59:03 +00:00
2018-08-14 15:10:10 +00:00
if ( ! players [ i ] . mo | | ( ( maptol & TOL_NIGHTS ) ? players [ i ] . spheres : players [ i ] . rings ) < = 0 )
2014-08-04 03:49:33 +00:00
continue ;
2014-03-15 16:59:03 +00:00
2018-08-14 15:10:10 +00:00
rings + = ( maptol & TOL_NIGHTS ) ? players [ i ] . spheres : players [ i ] . rings ;
2014-08-04 03:49:33 +00:00
}
}
else
{
if ( ! ( actor & & actor - > player ) )
return false ; // no player to count rings from here, sorry
2014-03-15 16:59:03 +00:00
2018-08-14 15:10:10 +00:00
rings = ( maptol & TOL_NIGHTS ) ? actor - > player - > spheres : actor - > player - > rings ;
2014-08-04 03:49:33 +00:00
}
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
if ( triggerline - > flags & ML_NOCLIMB )
{
if ( rings > dist )
return false ;
}
else if ( triggerline - > flags & ML_BLOCKMONSTERS )
{
if ( rings < dist )
return false ;
}
else
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
if ( rings ! = dist )
return false ;
}
}
else if ( specialtype > = 314 & & specialtype < = 315 )
{
msecnode_t * node ;
mobj_t * mo ;
INT32 numpush = 0 ;
INT32 numneeded = dist ;
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
if ( ! caller )
return false ; // we need a calling sector to find pushables in, silly!
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
// Count the pushables in this sector
node = caller - > touching_thinglist ; // things touching this sector
while ( node )
{
mo = node - > m_thing ;
2020-01-08 22:36:29 +00:00
if ( ( mo - > flags & MF_PUSHABLE ) | | ( ( mo - > info - > flags & MF_PUSHABLE ) & & mo - > fuse ) )
2014-08-04 03:49:33 +00:00
numpush + + ;
2016-06-09 13:16:02 +00:00
node = node - > m_thinglist_next ;
2014-08-04 03:49:33 +00:00
}
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
if ( triggerline - > flags & ML_NOCLIMB ) // Need at least or more
{
if ( numpush < numneeded )
return false ;
}
else if ( triggerline - > flags & ML_EFFECT4 ) // Need less than
{
if ( numpush > = numneeded )
return false ;
}
else // Need exact
{
if ( numpush ! = numneeded )
return false ;
}
}
else if ( caller )
{
if ( GETSECSPECIAL ( caller - > special , 2 ) = = 6 )
{
if ( ! ( ALL7EMERALDS ( emeralds ) ) )
return false ;
}
else if ( GETSECSPECIAL ( caller - > special , 2 ) = = 7 )
{
UINT8 mare ;
if ( ! ( maptol & TOL_NIGHTS ) )
return false ;
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
mare = P_FindLowestMare ( ) ;
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
if ( triggerline - > flags & ML_NOCLIMB )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
if ( ! ( mare < = dist ) )
return false ;
2014-03-15 16:59:03 +00:00
}
2014-08-04 03:49:33 +00:00
else if ( triggerline - > flags & ML_BLOCKMONSTERS )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
if ( ! ( mare > = dist ) )
return false ;
2014-03-15 16:59:03 +00:00
}
else
{
2014-08-04 03:49:33 +00:00
if ( ! ( mare = = dist ) )
return false ;
2014-03-15 16:59:03 +00:00
}
}
2014-08-04 03:49:33 +00:00
// If we were not triggered by a sector type especially for the purpose,
// a Linedef Executor linedef trigger is not handling sector triggers properly, return.
2014-03-15 16:59:03 +00:00
2014-11-12 00:55:07 +00:00
else if ( ( ! GETSECSPECIAL ( caller - > special , 2 ) | | GETSECSPECIAL ( caller - > special , 2 ) > 7 ) & & ( specialtype > 322 ) )
2014-08-04 03:49:33 +00:00
{
CONS_Alert ( CONS_WARNING ,
M_GetText ( " Linedef executor trigger isn't handling sector triggers properly! \n specialtype = %d, if you are not a dev, report this warning instance \n along with the wad that caused it! \n " ) ,
specialtype ) ;
return false ;
}
}
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
//////////////////////////////////////
// Miscellaneous trigger conditions //
//////////////////////////////////////
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
switch ( specialtype )
{
case 305 : // continuous
case 306 : // each time
case 307 : // once
2017-07-24 16:53:18 +00:00
if ( ! ( actor & & actor - > player & & actor - > player - > charability = = dist / 10 ) )
2014-08-04 03:49:33 +00:00
return false ;
break ;
case 309 : // continuous
case 310 : // each time
// Only red team members can activate this.
if ( ! ( actor & & actor - > player & & actor - > player - > ctfteam = = 1 ) )
return false ;
break ;
case 311 : // continuous
case 312 : // each time
// Only blue team members can activate this.
if ( ! ( actor & & actor - > player & & actor - > player - > ctfteam = = 2 ) )
return false ;
break ;
case 317 : // continuous
case 318 : // once
{ // Unlockable triggers required
INT32 trigid = ( INT32 ) ( sides [ triggerline - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
if ( ( modifiedgame & & ! savemoddata ) | | ( netgame | | multiplayer ) )
return false ;
else if ( trigid < 0 | | trigid > 31 ) // limited by 32 bit variable
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
CONS_Debug ( DBG_GAMELOGIC , " Unlockable trigger (sidedef %hu): bad trigger ID %d \n " , triggerline - > sidenum [ 0 ] , trigid ) ;
return false ;
2014-03-15 16:59:03 +00:00
}
2014-08-04 03:49:33 +00:00
else if ( ! ( unlocktriggers & ( 1 < < trigid ) ) )
return false ;
}
break ;
case 319 : // continuous
case 320 : // once
{ // An unlockable itself must be unlocked!
INT32 unlockid = ( INT32 ) ( sides [ triggerline - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
if ( ( modifiedgame & & ! savemoddata ) | | ( netgame | | multiplayer ) )
return false ;
else if ( unlockid < 0 | | unlockid > = MAXUNLOCKABLES ) // limited by unlockable count
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
CONS_Debug ( DBG_GAMELOGIC , " Unlockable check (sidedef %hu): bad unlockable ID %d \n " , triggerline - > sidenum [ 0 ] , unlockid ) ;
return false ;
2014-03-15 16:59:03 +00:00
}
2014-08-04 03:49:33 +00:00
else if ( ! ( unlockables [ unlockid - 1 ] . unlocked ) )
return false ;
2014-03-15 16:59:03 +00:00
}
2014-08-04 03:49:33 +00:00
break ;
2014-11-12 00:55:07 +00:00
case 321 : // continuous
case 322 : // each time
// decrement calls left before triggering
if ( triggerline - > callcount > 0 )
{
if ( - - triggerline - > callcount > 0 )
return false ;
}
break ;
2018-04-01 12:23:59 +00:00
case 323 : // nightserize - each time
case 324 : // nightserize - once
case 325 : // denightserize - each time
case 326 : // denightserize - once
case 327 : // nights lap - each time
case 328 : // nights lap - once
2018-08-14 18:57:58 +00:00
case 329 : // nights egg capsule touch - each time
case 330 : // nights egg capsule touch - once
2018-04-02 12:11:28 +00:00
if ( ! P_CheckNightsTriggerLine ( triggerline , actor ) )
return false ;
2018-03-31 14:11:27 +00:00
break ;
2019-07-03 07:19:29 +00:00
case 331 : // continuous
case 332 : // each time
case 333 : // once
if ( ! ( actor & & actor - > player & & ( ( stricmp ( triggerline - > text , skins [ actor - > player - > skin ] . name ) = = 0 ) ^ ( ( triggerline - > flags & ML_NOCLIMB ) = = ML_NOCLIMB ) ) ) )
return false ;
break ;
2020-01-11 23:40:57 +00:00
case 334 : // object dye - continuous
case 335 : // object dye - each time
case 336 : // object dye - once
{
INT32 triggercolor = ( INT32 ) sides [ triggerline - > sidenum [ 0 ] ] . toptexture ;
2020-08-25 02:53:21 +00:00
UINT16 color = ( actor - > player ? actor - > player - > powers [ pw_dye ] : actor - > color ) ;
2020-01-11 23:40:57 +00:00
boolean invert = ( triggerline - > flags & ML_NOCLIMB ? true : false ) ;
2020-04-12 13:16:04 +00:00
2020-01-11 23:40:57 +00:00
if ( invert ^ ( triggercolor ! = color ) )
return false ;
}
2014-08-04 03:49:33 +00:00
default :
break ;
}
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
/////////////////////////////////
// Processing linedef specials //
/////////////////////////////////
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
ctlsector = triggerline - > frontsector ;
sectori = ( size_t ) ( ctlsector - sectors ) ;
linecnt = ctlsector - > linecount ;
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
if ( triggerline - > flags & ML_EFFECT5 ) // disregard order for efficiency
{
for ( i = 0 ; i < linecnt ; i + + )
if ( ctlsector - > lines [ i ] - > special > = 400
& & ctlsector - > lines [ i ] - > special < 500 )
2014-03-15 16:59:03 +00:00
{
2020-05-03 18:41:37 +00:00
if ( ctlsector - > lines [ i ] - > executordelay )
2014-11-12 00:55:07 +00:00
P_AddExecutorDelay ( ctlsector - > lines [ i ] , actor , caller ) ;
2014-08-04 03:49:33 +00:00
else
2014-11-12 00:55:07 +00:00
P_ProcessLineSpecial ( ctlsector - > lines [ i ] , actor , caller ) ;
2014-03-15 16:59:03 +00:00
}
2014-08-04 03:49:33 +00:00
}
else // walk around the sector in a defined order
{
boolean backwards = false ;
size_t j , masterlineindex = ( size_t ) - 1 ;
2014-03-15 16:59:03 +00:00
2014-08-04 03:49:33 +00:00
for ( i = 0 ; i < linecnt ; i + + )
if ( ctlsector - > lines [ i ] = = triggerline )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
masterlineindex = i ;
break ;
2014-03-15 16:59:03 +00:00
}
2014-08-04 03:49:33 +00:00
# ifdef PARANOIA
if ( masterlineindex = = ( size_t ) - 1 )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
const size_t li = ( size_t ) ( ctlsector - > lines [ i ] - lines ) ;
I_Error ( " Line %s isn't linked into its front sector " , sizeu1 ( li ) ) ;
2014-03-15 16:59:03 +00:00
}
# endif
2014-08-04 03:49:33 +00:00
// i == masterlineindex
for ( ; ; )
{
if ( backwards ) // v2 to v1
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
for ( j = 0 ; j < linecnt ; j + + )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
if ( i = = j )
continue ;
if ( ctlsector - > lines [ i ] - > v1 = = ctlsector - > lines [ j ] - > v2 )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
i = j ;
break ;
2014-03-15 16:59:03 +00:00
}
2014-08-04 03:49:33 +00:00
if ( ctlsector - > lines [ i ] - > v1 = = ctlsector - > lines [ j ] - > v1 )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
i = j ;
backwards = false ;
break ;
2014-03-15 16:59:03 +00:00
}
}
2014-08-04 03:49:33 +00:00
if ( j = = linecnt )
{
const size_t vertexei = ( size_t ) ( ctlsector - > lines [ i ] - > v1 - vertexes ) ;
CONS_Debug ( DBG_GAMELOGIC , " Warning: Sector %s is not closed at vertex %s (%d, %d) \n " ,
sizeu1 ( sectori ) , sizeu2 ( vertexei ) , ctlsector - > lines [ i ] - > v1 - > x , ctlsector - > lines [ i ] - > v1 - > y ) ;
return false ; // abort
}
}
else // v1 to v2
{
for ( j = 0 ; j < linecnt ; j + + )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
if ( i = = j )
continue ;
if ( ctlsector - > lines [ i ] - > v2 = = ctlsector - > lines [ j ] - > v1 )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
i = j ;
break ;
2014-03-15 16:59:03 +00:00
}
2014-08-04 03:49:33 +00:00
if ( ctlsector - > lines [ i ] - > v2 = = ctlsector - > lines [ j ] - > v2 )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
i = j ;
backwards = true ;
break ;
2014-03-15 16:59:03 +00:00
}
}
2014-08-04 03:49:33 +00:00
if ( j = = linecnt )
2014-03-15 16:59:03 +00:00
{
2014-08-04 03:49:33 +00:00
const size_t vertexei = ( size_t ) ( ctlsector - > lines [ i ] - > v1 - vertexes ) ;
CONS_Debug ( DBG_GAMELOGIC , " Warning: Sector %s is not closed at vertex %s (%d, %d) \n " ,
sizeu1 ( sectori ) , sizeu2 ( vertexei ) , ctlsector - > lines [ i ] - > v2 - > x , ctlsector - > lines [ i ] - > v2 - > y ) ;
return false ; // abort
2014-03-15 16:59:03 +00:00
}
}
2014-08-04 03:49:33 +00:00
if ( i = = masterlineindex )
break ;
if ( ctlsector - > lines [ i ] - > special > = 400
& & ctlsector - > lines [ i ] - > special < 500 )
{
2020-05-03 18:41:37 +00:00
if ( ctlsector - > lines [ i ] - > executordelay )
2014-11-12 00:55:07 +00:00
P_AddExecutorDelay ( ctlsector - > lines [ i ] , actor , caller ) ;
2014-08-04 03:49:33 +00:00
else
2014-11-12 00:55:07 +00:00
P_ProcessLineSpecial ( ctlsector - > lines [ i ] , actor , caller ) ;
2014-08-04 03:49:33 +00:00
}
2014-03-15 16:59:03 +00:00
}
}
2014-08-04 03:49:33 +00:00
2014-11-12 00:55:07 +00:00
// "Trigger on X calls" linedefs reset if noclimb is set
if ( ( specialtype = = 321 | | specialtype = = 322 ) & & triggerline - > flags & ML_NOCLIMB )
triggerline - > callcount = sides [ triggerline - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ;
else
2014-08-27 03:56:30 +00:00
// These special types work only once
if ( specialtype = = 302 // Once
| | specialtype = = 304 // Ring count - Once
| | specialtype = = 307 // Character ability - Once
| | specialtype = = 308 // Race only - Once
2016-03-09 09:24:13 +00:00
| | specialtype = = 313 // No More Enemies - Once
2014-08-27 03:56:30 +00:00
| | specialtype = = 315 // No of pushables - Once
| | specialtype = = 318 // Unlockable trigger - Once
| | specialtype = = 320 // Unlockable - Once
2014-11-12 00:55:07 +00:00
| | specialtype = = 321 | | specialtype = = 322 // Trigger on X calls - Continuous + Each Time
2018-04-01 12:23:59 +00:00
| | specialtype = = 324 // Nightserize - Once
| | specialtype = = 326 // DeNightserize - Once
| | specialtype = = 328 // Nights lap - Once
| | specialtype = = 330 // Nights Bonus Time - Once
2019-07-03 07:19:29 +00:00
| | specialtype = = 333 // Skin - Once
2020-01-11 23:40:57 +00:00
| | specialtype = = 336 // Dye - Once
2014-08-27 03:56:30 +00:00
| | specialtype = = 399 ) // Level Load
2014-08-04 03:49:33 +00:00
triggerline - > special = 0 ; // Clear it out
return true ;
}
/** Runs a linedef executor.
* Can be called by :
* - a player moving into a special sector or FOF .
* - a pushable object moving into a special sector or FOF .
* - a ceiling or floor movement from a previous linedef executor finishing .
* - any object in a state with the A_LinedefExecute ( ) action .
*
* \ param tag Tag of the linedef executor to run .
* \ param actor Object initiating the action ; should not be NULL .
* \ param caller Sector in which the action was started . May be NULL .
* \ sa P_ProcessLineSpecial , P_RunTriggerLinedef
* \ author Graue < graue @ oceanbase . org >
*/
void P_LinedefExecute ( INT16 tag , mobj_t * actor , sector_t * caller )
{
size_t masterline ;
CONS_Debug ( DBG_GAMELOGIC , " P_LinedefExecute: Executing trigger linedefs of tag %d \n " , tag ) ;
I_Assert ( ! actor | | ! P_MobjWasRemoved ( actor ) ) ; // If actor is there, it must be valid.
for ( masterline = 0 ; masterline < numlines ; masterline + + )
{
2020-04-17 16:15:25 +00:00
if ( Tag_FGet ( & lines [ masterline ] . tags ) ! = tag )
2014-08-04 03:49:33 +00:00
continue ;
2014-08-27 03:56:30 +00:00
// "No More Enemies" and "Level Load" take care of themselves.
2014-08-04 03:49:33 +00:00
if ( lines [ masterline ] . special = = 313
2014-08-27 03:56:30 +00:00
| | lines [ masterline ] . special = = 399
// Each-time executors handle themselves, too
| | lines [ masterline ] . special = = 301 // Each time
| | lines [ masterline ] . special = = 306 // Character ability - Each time
| | lines [ masterline ] . special = = 310 // CTF Red team - Each time
2014-11-12 00:55:07 +00:00
| | lines [ masterline ] . special = = 312 // CTF Blue team - Each time
2019-07-03 07:19:29 +00:00
| | lines [ masterline ] . special = = 322 // Trigger on X calls - Each Time
2020-01-11 23:40:57 +00:00
| | lines [ masterline ] . special = = 332 // Skin - Each time
| | lines [ masterline ] . special = = 335 ) // Dye - Each time
2014-08-04 03:49:33 +00:00
continue ;
if ( lines [ masterline ] . special < 300
| | lines [ masterline ] . special > 399 )
continue ;
if ( ! P_RunTriggerLinedef ( & lines [ masterline ] , actor , caller ) )
return ; // cancel P_LinedefExecute if function returns false
}
2014-03-15 16:59:03 +00:00
}
//
// P_SwitchWeather
//
// Switches the weather!
//
void P_SwitchWeather ( INT32 weathernum )
{
boolean purge = false ;
INT32 swap = 0 ;
switch ( weathernum )
{
case PRECIP_NONE : // None
if ( curWeather = = PRECIP_NONE )
return ; // Nothing to do.
purge = true ;
break ;
case PRECIP_STORM : // Storm
case PRECIP_STORM_NOSTRIKES : // Storm w/ no lightning
case PRECIP_RAIN : // Rain
if ( curWeather = = PRECIP_SNOW | | curWeather = = PRECIP_BLANK | | curWeather = = PRECIP_STORM_NORAIN )
swap = PRECIP_RAIN ;
break ;
case PRECIP_SNOW : // Snow
if ( curWeather = = PRECIP_SNOW )
return ; // Nothing to do.
if ( curWeather = = PRECIP_RAIN | | curWeather = = PRECIP_STORM | | curWeather = = PRECIP_STORM_NOSTRIKES | | curWeather = = PRECIP_BLANK | | curWeather = = PRECIP_STORM_NORAIN )
swap = PRECIP_SNOW ; // Need to delete the other precips.
break ;
case PRECIP_STORM_NORAIN : // Storm w/o rain
if ( curWeather = = PRECIP_SNOW
| | curWeather = = PRECIP_STORM
| | curWeather = = PRECIP_STORM_NOSTRIKES
| | curWeather = = PRECIP_RAIN
| | curWeather = = PRECIP_BLANK )
swap = PRECIP_STORM_NORAIN ;
else if ( curWeather = = PRECIP_STORM_NORAIN )
return ;
break ;
case PRECIP_BLANK :
if ( curWeather = = PRECIP_SNOW
| | curWeather = = PRECIP_STORM
| | curWeather = = PRECIP_STORM_NOSTRIKES
| | curWeather = = PRECIP_RAIN )
swap = PRECIP_BLANK ;
else if ( curWeather = = PRECIP_STORM_NORAIN )
swap = PRECIP_BLANK ;
else if ( curWeather = = PRECIP_BLANK )
return ;
break ;
default :
CONS_Debug ( DBG_GAMELOGIC , " P_SwitchWeather: Unknown weather type %d. \n " , weathernum ) ;
break ;
}
if ( purge )
{
thinker_t * think ;
precipmobj_t * precipmobj ;
2019-07-12 23:42:03 +00:00
for ( think = thlist [ THINK_PRECIP ] . next ; think ! = & thlist [ THINK_PRECIP ] ; think = think - > next )
2014-03-15 16:59:03 +00:00
{
2018-10-07 14:00:58 +00:00
if ( think - > function . acp1 ! = ( actionf_p1 ) P_NullPrecipThinker )
2014-03-15 16:59:03 +00:00
continue ; // not a precipmobj thinker
precipmobj = ( precipmobj_t * ) think ;
P_RemovePrecipMobj ( precipmobj ) ;
}
}
else if ( swap & & ! ( ( swap = = PRECIP_BLANK & & curWeather = = PRECIP_STORM_NORAIN ) | | ( swap = = PRECIP_STORM_NORAIN & & curWeather = = PRECIP_BLANK ) ) ) // Rather than respawn all that crap, reuse it!
{
thinker_t * think ;
precipmobj_t * precipmobj ;
state_t * st ;
2019-07-12 23:42:03 +00:00
for ( think = thlist [ THINK_PRECIP ] . next ; think ! = & thlist [ THINK_PRECIP ] ; think = think - > next )
2014-03-15 16:59:03 +00:00
{
2018-10-07 14:00:58 +00:00
if ( think - > function . acp1 ! = ( actionf_p1 ) P_NullPrecipThinker )
continue ; // not a precipmobj thinker
precipmobj = ( precipmobj_t * ) think ;
2014-03-15 16:59:03 +00:00
if ( swap = = PRECIP_RAIN ) // Snow To Rain
{
precipmobj - > flags = mobjinfo [ MT_RAIN ] . flags ;
st = & states [ mobjinfo [ MT_RAIN ] . spawnstate ] ;
precipmobj - > state = st ;
precipmobj - > tics = st - > tics ;
precipmobj - > sprite = st - > sprite ;
precipmobj - > frame = st - > frame ;
precipmobj - > momz = mobjinfo [ MT_RAIN ] . speed ;
precipmobj - > precipflags & = ~ PCF_INVISIBLE ;
2018-10-07 14:00:58 +00:00
precipmobj - > precipflags | = PCF_RAIN ;
//think->function.acp1 = (actionf_p1)P_RainThinker;
2014-03-15 16:59:03 +00:00
}
else if ( swap = = PRECIP_SNOW ) // Rain To Snow
{
INT32 z ;
precipmobj - > flags = mobjinfo [ MT_SNOWFLAKE ] . flags ;
2016-03-27 14:33:15 +00:00
z = M_RandomByte ( ) ;
2014-03-15 16:59:03 +00:00
if ( z < 64 )
z = 2 ;
else if ( z < 144 )
z = 1 ;
else
z = 0 ;
st = & states [ mobjinfo [ MT_SNOWFLAKE ] . spawnstate + z ] ;
precipmobj - > state = st ;
precipmobj - > tics = st - > tics ;
precipmobj - > sprite = st - > sprite ;
precipmobj - > frame = st - > frame ;
precipmobj - > momz = mobjinfo [ MT_SNOWFLAKE ] . speed ;
2018-10-07 14:00:58 +00:00
precipmobj - > precipflags & = ~ ( PCF_INVISIBLE | PCF_RAIN ) ;
2014-03-15 16:59:03 +00:00
2018-10-07 14:00:58 +00:00
//think->function.acp1 = (actionf_p1)P_SnowThinker;
2014-03-15 16:59:03 +00:00
}
else if ( swap = = PRECIP_BLANK | | swap = = PRECIP_STORM_NORAIN ) // Remove precip, but keep it around for reuse.
{
2018-10-07 14:00:58 +00:00
//think->function.acp1 = (actionf_p1)P_NullPrecipThinker;
2014-03-15 16:59:03 +00:00
precipmobj - > precipflags | = PCF_INVISIBLE ;
}
}
}
switch ( weathernum )
{
case PRECIP_SNOW : // snow
curWeather = PRECIP_SNOW ;
if ( ! swap )
P_SpawnPrecipitation ( ) ;
break ;
case PRECIP_RAIN : // rain
{
boolean dontspawn = false ;
if ( curWeather = = PRECIP_RAIN | | curWeather = = PRECIP_STORM | | curWeather = = PRECIP_STORM_NOSTRIKES )
dontspawn = true ;
curWeather = PRECIP_RAIN ;
if ( ! dontspawn & & ! swap )
P_SpawnPrecipitation ( ) ;
break ;
}
case PRECIP_STORM : // storm
{
boolean dontspawn = false ;
if ( curWeather = = PRECIP_RAIN | | curWeather = = PRECIP_STORM | | curWeather = = PRECIP_STORM_NOSTRIKES )
dontspawn = true ;
curWeather = PRECIP_STORM ;
if ( ! dontspawn & & ! swap )
P_SpawnPrecipitation ( ) ;
break ;
}
case PRECIP_STORM_NOSTRIKES : // storm w/o lightning
{
boolean dontspawn = false ;
if ( curWeather = = PRECIP_RAIN | | curWeather = = PRECIP_STORM | | curWeather = = PRECIP_STORM_NOSTRIKES )
dontspawn = true ;
curWeather = PRECIP_STORM_NOSTRIKES ;
if ( ! dontspawn & & ! swap )
P_SpawnPrecipitation ( ) ;
break ;
}
case PRECIP_STORM_NORAIN : // storm w/o rain
curWeather = PRECIP_STORM_NORAIN ;
if ( ! swap )
P_SpawnPrecipitation ( ) ;
break ;
case PRECIP_BLANK :
curWeather = PRECIP_BLANK ;
if ( ! swap )
P_SpawnPrecipitation ( ) ;
break ;
default :
curWeather = PRECIP_NONE ;
break ;
}
}
/** Gets an object.
*
* \ param type Object type to look for .
* \ param s Sector number to look in .
* \ return Pointer to the first : : type found in the sector .
* \ sa P_GetPushThing
*/
static mobj_t * P_GetObjectTypeInSectorNum ( mobjtype_t type , size_t s )
{
sector_t * sec = sectors + s ;
mobj_t * thing = sec - > thinglist ;
while ( thing )
{
if ( thing - > type = = type )
return thing ;
thing = thing - > snext ;
}
return NULL ;
}
/** Processes the line special triggered by an object.
*
* \ param line Line with the special command on it .
* \ param mo mobj that triggered the line . Must be valid and non - NULL .
2014-11-12 00:55:07 +00:00
* \ param callsec sector in which action was initiated ; this can be NULL .
* Because of the A_LinedefExecute ( ) action , even if non - NULL ,
* this sector might not have the same tag as the linedef executor ,
* and it might not have the linedef executor sector type .
2014-03-15 16:59:03 +00:00
* \ todo Handle mo being NULL gracefully . T_MoveFloor ( ) and T_MoveCeiling ( )
* don ' t have an object to pass .
* \ todo Split up into multiple functions .
* \ sa P_LinedefExecute
* \ author Graue < graue @ oceanbase . org >
*/
2014-11-12 00:55:07 +00:00
static void P_ProcessLineSpecial ( line_t * line , mobj_t * mo , sector_t * callsec )
2014-03-15 16:59:03 +00:00
{
INT32 secnum = - 1 ;
mobj_t * bot = NULL ;
2020-04-17 16:15:25 +00:00
mtag_t tag = Tag_FGet ( & line - > tags ) ;
2014-03-15 16:59:03 +00:00
I_Assert ( ! mo | | ! P_MobjWasRemoved ( mo ) ) ; // If mo is there, mo must be valid!
if ( mo & & mo - > player & & botingame )
bot = players [ secondarydisplayplayer ] . mo ;
// note: only commands with linedef types >= 400 && < 500 can be used
switch ( line - > special )
{
case 400 : // Set tagged sector's floor height/pic
EV_DoFloor ( line , instantMoveFloorByFrontSector ) ;
break ;
case 401 : // Set tagged sector's ceiling height/pic
EV_DoCeiling ( line , instantMoveCeilingByFrontSector ) ;
break ;
case 402 : // Set tagged sector's light level
{
INT16 newlightlevel ;
INT32 newfloorlightsec , newceilinglightsec ;
newlightlevel = line - > frontsector - > lightlevel ;
newfloorlightsec = line - > frontsector - > floorlightsec ;
newceilinglightsec = line - > frontsector - > ceilinglightsec ;
// act on all sectors with the same tag as the triggering linedef
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , secnum )
2014-03-15 16:59:03 +00:00
{
if ( sectors [ secnum ] . lightingdata )
{
// Stop the lighting madness going on in this sector!
P_RemoveThinker ( & ( ( elevator_t * ) sectors [ secnum ] . lightingdata ) - > thinker ) ;
sectors [ secnum ] . lightingdata = NULL ;
// No, it's not an elevator_t, but any struct with a thinker_t named
// 'thinker' at the beginning will do here. (We don't know what it
// actually is: could be lightlevel_t, fireflicker_t, glow_t, etc.)
}
sectors [ secnum ] . lightlevel = newlightlevel ;
sectors [ secnum ] . floorlightsec = newfloorlightsec ;
sectors [ secnum ] . ceilinglightsec = newceilinglightsec ;
}
}
break ;
case 403 : // Move floor, linelen = speed, frontsector floor = dest height
EV_DoFloor ( line , moveFloorByFrontSector ) ;
break ;
case 404 : // Move ceiling, linelen = speed, frontsector ceiling = dest height
EV_DoCeiling ( line , moveCeilingByFrontSector ) ;
break ;
case 405 : // Move floor by front side texture offsets, offset x = speed, offset y = amount to raise/lower
EV_DoFloor ( line , moveFloorByFrontTexture ) ;
break ;
case 407 : // Move ceiling by front side texture offsets, offset x = speed, offset y = amount to raise/lower
EV_DoCeiling ( line , moveCeilingByFrontTexture ) ;
break ;
/* case 405: // Lower floor by line, dx = speed, dy = amount to lower
EV_DoFloor ( line , lowerFloorByLine ) ;
break ;
case 406 : // Raise floor by line, dx = speed, dy = amount to raise
EV_DoFloor ( line , raiseFloorByLine ) ;
break ;
case 407 : // Lower ceiling by line, dx = speed, dy = amount to lower
EV_DoCeiling ( line , lowerCeilingByLine ) ;
break ;
case 408 : // Raise ceiling by line, dx = speed, dy = amount to raise
EV_DoCeiling ( line , raiseCeilingByLine ) ;
break ; */
case 409 : // Change tagged sectors' tag
2014-08-04 03:49:33 +00:00
// (formerly "Change calling sectors' tag", but behavior was changed)
2014-03-15 16:59:03 +00:00
{
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , secnum )
2020-04-17 20:26:04 +00:00
Tag_SectorFSet ( secnum , ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ) ;
2014-03-15 16:59:03 +00:00
break ;
}
case 410 : // Change front sector's tag
2020-04-17 20:26:04 +00:00
Tag_SectorFSet ( ( UINT32 ) ( line - > frontsector - sectors ) , ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ) ;
2014-03-15 16:59:03 +00:00
break ;
case 411 : // Stop floor/ceiling movement in tagged sector(s)
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , secnum )
2014-03-15 16:59:03 +00:00
{
if ( sectors [ secnum ] . floordata )
{
if ( sectors [ secnum ] . floordata = = sectors [ secnum ] . ceilingdata ) // elevator
{
P_RemoveThinker ( & ( ( elevator_t * ) sectors [ secnum ] . floordata ) - > thinker ) ;
sectors [ secnum ] . floordata = sectors [ secnum ] . ceilingdata = NULL ;
sectors [ secnum ] . floorspeed = sectors [ secnum ] . ceilspeed = 0 ;
}
else // floormove
{
P_RemoveThinker ( & ( ( floormove_t * ) sectors [ secnum ] . floordata ) - > thinker ) ;
sectors [ secnum ] . floordata = NULL ;
sectors [ secnum ] . floorspeed = 0 ;
}
}
if ( sectors [ secnum ] . ceilingdata ) // ceiling
{
P_RemoveThinker ( & ( ( ceiling_t * ) sectors [ secnum ] . ceilingdata ) - > thinker ) ;
sectors [ secnum ] . ceilingdata = NULL ;
sectors [ secnum ] . ceilspeed = 0 ;
}
}
break ;
case 412 : // Teleport the player or thing
{
mobj_t * dest ;
if ( ! mo ) // nothing to teleport
return ;
if ( line - > flags & ML_EFFECT3 ) // Relative silent teleport
{
fixed_t x , y , z ;
x = sides [ line - > sidenum [ 0 ] ] . textureoffset ;
y = sides [ line - > sidenum [ 0 ] ] . rowoffset ;
z = line - > frontsector - > ceilingheight ;
P_UnsetThingPosition ( mo ) ;
mo - > x + = x ;
mo - > y + = y ;
mo - > z + = z ;
P_SetThingPosition ( mo ) ;
if ( mo - > player )
{
if ( bot ) // This might put poor Tails in a wall if he's too far behind! D: But okay, whatever! >:3
P_TeleportMove ( bot , bot - > x + x , bot - > y + y , bot - > z + z ) ;
if ( splitscreen & & mo - > player = = & players [ secondarydisplayplayer ] & & camera2 . chase )
{
camera2 . x + = x ;
camera2 . y + = y ;
camera2 . z + = z ;
camera2 . subsector = R_PointInSubsector ( camera2 . x , camera2 . y ) ;
}
else if ( camera . chase & & mo - > player = = & players [ displayplayer ] )
{
camera . x + = x ;
camera . y + = y ;
camera . z + = z ;
camera . subsector = R_PointInSubsector ( camera . x , camera . y ) ;
}
}
}
else
{
2020-04-17 16:15:25 +00:00
if ( ( secnum = Tag_Iterate_Sectors ( tag , 0 ) ) < 0 )
2014-03-15 16:59:03 +00:00
return ;
dest = P_GetObjectTypeInSectorNum ( MT_TELEPORTMAN , secnum ) ;
if ( ! dest )
return ;
if ( bot )
P_Teleport ( bot , dest - > x , dest - > y , dest - > z , ( line - > flags & ML_NOCLIMB ) ? mo - > angle : dest - > angle , ( line - > flags & ML_BLOCKMONSTERS ) = = 0 , ( line - > flags & ML_EFFECT4 ) = = ML_EFFECT4 ) ;
if ( line - > flags & ML_BLOCKMONSTERS )
P_Teleport ( mo , dest - > x , dest - > y , dest - > z , ( line - > flags & ML_NOCLIMB ) ? mo - > angle : dest - > angle , false , ( line - > flags & ML_EFFECT4 ) = = ML_EFFECT4 ) ;
else
{
P_Teleport ( mo , dest - > x , dest - > y , dest - > z , ( line - > flags & ML_NOCLIMB ) ? mo - > angle : dest - > angle , true , ( line - > flags & ML_EFFECT4 ) = = ML_EFFECT4 ) ;
// Play the 'bowrwoosh!' sound
S_StartSound ( dest , sfx_mixup ) ;
}
}
}
break ;
case 413 : // Change music
// console player only unless NOCLIMB is set
2018-11-18 06:53:18 +00:00
if ( ( line - > flags & ML_NOCLIMB ) | | ( mo & & mo - > player & & P_IsLocalPlayer ( mo - > player ) ) | | titlemapinaction )
2014-03-15 16:59:03 +00:00
{
2018-09-18 19:28:57 +00:00
boolean musicsame = ( ! sides [ line - > sidenum [ 0 ] ] . text [ 0 ] | | ! strnicmp ( sides [ line - > sidenum [ 0 ] ] . text , S_MusicName ( ) , 7 ) ) ;
UINT16 tracknum = ( UINT16 ) max ( sides [ line - > sidenum [ 0 ] ] . bottomtexture , 0 ) ;
INT32 position = ( INT32 ) max ( sides [ line - > sidenum [ 0 ] ] . midtexture , 0 ) ;
UINT32 prefadems = ( UINT32 ) max ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS , 0 ) ;
UINT32 postfadems = ( UINT32 ) max ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS , 0 ) ;
UINT8 fadetarget = ( UINT8 ) max ( ( line - > sidenum [ 1 ] ! = 0xffff ) ? sides [ line - > sidenum [ 1 ] ] . textureoffset > > FRACBITS : 0 , 0 ) ;
2019-03-13 23:44:24 +00:00
INT16 fadesource = ( INT16 ) max ( ( line - > sidenum [ 1 ] ! = 0xffff ) ? sides [ line - > sidenum [ 1 ] ] . rowoffset > > FRACBITS : - 1 , - 1 ) ;
2018-09-18 19:28:57 +00:00
2019-03-13 23:44:24 +00:00
// Seek offset from current song position
2018-09-18 19:28:57 +00:00
if ( line - > flags & ML_EFFECT1 )
{
// adjust for loop point if subtracting
if ( position < 0 & & S_GetMusicLength ( ) & &
S_GetMusicPosition ( ) > S_GetMusicLoopPoint ( ) & &
S_GetMusicPosition ( ) + position < S_GetMusicLoopPoint ( ) )
position = max ( S_GetMusicLength ( ) - ( S_GetMusicLoopPoint ( ) - ( S_GetMusicPosition ( ) + position ) ) , 0 ) ;
else
position = max ( S_GetMusicPosition ( ) + position , 0 ) ;
}
2014-03-15 16:59:03 +00:00
2019-03-13 23:44:24 +00:00
// Fade current music to target volume (if music won't be changed)
2018-09-18 19:28:57 +00:00
if ( ( line - > flags & ML_EFFECT2 ) & & fadetarget & & musicsame )
{
2019-03-13 23:44:24 +00:00
// 0 fadesource means fade from current volume.
// meaning that we can't specify volume 0 as the source volume -- this starts at 1.
if ( ! fadesource )
fadesource = - 1 ;
2016-01-08 03:48:20 +00:00
2018-09-18 19:28:57 +00:00
if ( ! postfadems )
S_SetInternalMusicVolume ( fadetarget ) ;
else
S_FadeMusicFromVolume ( fadetarget , fadesource , postfadems ) ;
2014-03-15 16:59:03 +00:00
2018-09-18 19:28:57 +00:00
if ( position )
S_SetMusicPosition ( position ) ;
}
2019-03-13 23:44:24 +00:00
// Change the music and apply position/fade operations
2018-09-18 19:28:57 +00:00
else
{
strncpy ( mapmusname , sides [ line - > sidenum [ 0 ] ] . text , 7 ) ;
mapmusname [ 6 ] = 0 ;
mapmusflags = tracknum & MUSIC_TRACKMASK ;
if ( ! ( line - > flags & ML_BLOCKMONSTERS ) )
mapmusflags | = MUSIC_RELOADRESET ;
if ( line - > flags & ML_BOUNCY )
mapmusflags | = MUSIC_FORCERESET ;
mapmusposition = position ;
2014-03-15 16:59:03 +00:00
2018-09-18 19:28:57 +00:00
S_ChangeMusicEx ( mapmusname , mapmusflags , ! ( line - > flags & ML_EFFECT4 ) , position ,
! ( line - > flags & ML_EFFECT2 ) ? prefadems : 0 ,
! ( line - > flags & ML_EFFECT2 ) ? postfadems : 0 ) ;
2018-09-18 14:22:17 +00:00
2018-09-18 19:28:57 +00:00
if ( ( line - > flags & ML_EFFECT2 ) & & fadetarget )
{
if ( ! postfadems )
S_SetInternalMusicVolume ( fadetarget ) ;
else
S_FadeMusicFromVolume ( fadetarget , fadesource , postfadems ) ;
}
}
2014-03-15 16:59:03 +00:00
// Except, you can use the ML_BLOCKMONSTERS flag to change this behavior.
2016-01-08 03:48:20 +00:00
// if (mapmusflags & MUSIC_RELOADRESET) then it will reset the music in G_PlayerReborn.
2014-03-15 16:59:03 +00:00
}
break ;
case 414 : // Play SFX
{
2017-06-07 17:24:06 +00:00
INT32 sfxnum ;
2014-03-15 16:59:03 +00:00
2016-06-04 23:23:20 +00:00
sfxnum = sides [ line - > sidenum [ 0 ] ] . toptexture ;
2014-03-15 16:59:03 +00:00
2017-06-07 17:24:06 +00:00
if ( sfxnum = = sfx_None )
return ; // Do nothing!
if ( sfxnum < sfx_None | | sfxnum > = NUMSFX )
2014-03-15 16:59:03 +00:00
{
2017-06-07 17:24:06 +00:00
CONS_Debug ( DBG_GAMELOGIC , " Line type 414 Executor: sfx number %d is invalid! \n " , sfxnum ) ;
return ;
2014-03-15 16:59:03 +00:00
}
2019-10-14 14:30:20 +00:00
2020-04-17 16:15:25 +00:00
if ( tag ! = 0 ) // Do special stuff only if a non-zero linedef tag is set
2014-03-15 16:59:03 +00:00
{
2019-11-25 19:30:06 +00:00
// Play sounds from tagged sectors' origins.
2017-06-07 17:24:06 +00:00
if ( line - > flags & ML_EFFECT5 ) // Repeat Midtexture
{
// Additionally play the sound from tagged sectors' soundorgs
sector_t * sec ;
2014-03-15 16:59:03 +00:00
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , secnum )
2017-06-07 17:24:06 +00:00
{
sec = & sectors [ secnum ] ;
S_StartSound ( & sec - > soundorg , sfxnum ) ;
}
}
2019-11-25 19:30:06 +00:00
// Play the sound without origin for anyone, as long as they're inside tagged areas.
else
2014-03-15 16:59:03 +00:00
{
2019-11-28 20:10:57 +00:00
UINT8 i = 0 ;
2019-11-25 19:30:06 +00:00
mobj_t * camobj = players [ displayplayer ] . mo ;
2017-06-07 17:24:06 +00:00
ffloor_t * rover ;
boolean foundit = false ;
2014-03-15 16:59:03 +00:00
2019-11-25 19:30:06 +00:00
for ( i = 0 ; i < 2 ; camobj = players [ secondarydisplayplayer ] . mo , i + + )
2017-06-07 17:24:06 +00:00
{
2019-11-25 19:30:06 +00:00
if ( ! camobj )
2017-06-07 17:24:06 +00:00
continue ;
2014-03-15 16:59:03 +00:00
2020-04-17 20:26:04 +00:00
if ( foundit | | Tag_Find ( & camobj - > subsector - > sector - > tags , tag ) )
2019-11-25 19:30:06 +00:00
{
foundit = true ;
break ;
}
2014-03-15 16:59:03 +00:00
2019-11-25 19:30:06 +00:00
// Only trigger if mobj is touching the tag
for ( rover = camobj - > subsector - > sector - > ffloors ; rover ; rover = rover - > next )
{
2020-04-17 20:26:04 +00:00
if ( ! Tag_Find ( & rover - > master - > frontsector - > tags , tag ) )
2019-11-25 19:30:06 +00:00
continue ;
2014-03-15 16:59:03 +00:00
2019-11-25 19:30:06 +00:00
if ( camobj - > z > P_GetSpecialTopZ ( camobj , sectors + rover - > secnum , camobj - > subsector - > sector ) )
continue ;
2014-03-15 16:59:03 +00:00
2019-11-25 19:30:06 +00:00
if ( camobj - > z + camobj - > height < P_GetSpecialBottomZ ( camobj , sectors + rover - > secnum , camobj - > subsector - > sector ) )
continue ;
2014-03-15 16:59:03 +00:00
2019-11-25 19:30:06 +00:00
foundit = true ;
break ;
}
}
2014-03-15 16:59:03 +00:00
2019-11-25 19:30:06 +00:00
if ( foundit )
S_StartSound ( NULL , sfxnum ) ;
2017-06-07 17:24:06 +00:00
}
2014-03-15 16:59:03 +00:00
}
2019-10-14 14:30:20 +00:00
else
2014-03-15 16:59:03 +00:00
{
2019-10-14 14:30:20 +00:00
if ( line - > flags & ML_NOCLIMB )
{
// play the sound from nowhere, but only if display player triggered it
if ( mo & & mo - > player & & ( mo - > player = = & players [ displayplayer ] | | mo - > player = = & players [ secondarydisplayplayer ] ) )
S_StartSound ( NULL , sfxnum ) ;
}
else if ( line - > flags & ML_EFFECT4 )
{
// play the sound from nowhere
2014-03-15 16:59:03 +00:00
S_StartSound ( NULL , sfxnum ) ;
2019-10-14 14:30:20 +00:00
}
else if ( line - > flags & ML_BLOCKMONSTERS )
{
// play the sound from calling sector's soundorg
if ( callsec )
S_StartSound ( & callsec - > soundorg , sfxnum ) ;
else if ( mo )
S_StartSound ( & mo - > subsector - > sector - > soundorg , sfxnum ) ;
}
2014-03-15 16:59:03 +00:00
else if ( mo )
2019-10-14 14:30:20 +00:00
{
// play the sound from mobj that triggered it
S_StartSound ( mo , sfxnum ) ;
}
2014-03-15 16:59:03 +00:00
}
}
break ;
case 415 : // Run a script
if ( cv_runscripts . value )
{
INT32 scrnum ;
lumpnum_t lumpnum ;
char newname [ 9 ] ;
strcpy ( newname , G_BuildMapName ( gamemap ) ) ;
newname [ 0 ] = ' S ' ;
newname [ 1 ] = ' C ' ;
newname [ 2 ] = ' R ' ;
scrnum = sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ;
if ( scrnum < 0 | | scrnum > 999 )
{
scrnum = 0 ;
newname [ 5 ] = newname [ 6 ] = newname [ 7 ] = ' 0 ' ;
}
else
{
newname [ 5 ] = ( char ) ( ' 0 ' + ( char ) ( ( scrnum / 100 ) ) ) ;
newname [ 6 ] = ( char ) ( ' 0 ' + ( char ) ( ( scrnum % 100 ) / 10 ) ) ;
newname [ 7 ] = ( char ) ( ' 0 ' + ( char ) ( scrnum % 10 ) ) ;
}
newname [ 8 ] = ' \0 ' ;
lumpnum = W_CheckNumForName ( newname ) ;
if ( lumpnum = = LUMPERROR | | W_LumpLength ( lumpnum ) = = 0 )
{
CONS_Debug ( DBG_SETUP , " SOC Error: script lump %s not found/not valid. \n " , newname ) ;
}
else
COM_BufInsertText ( W_CacheLumpNum ( lumpnum , PU_CACHE ) ) ;
}
break ;
case 416 : // Spawn adjustable fire flicker
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , secnum )
2014-03-15 16:59:03 +00:00
{
if ( line - > flags & ML_NOCLIMB & & line - > backsector )
{
// Use front sector for min light level, back sector for max.
// This is tricky because P_SpawnAdjustableFireFlicker expects
// the maxsector (second argument) to also be the target
// sector, so we have to do some light level twiddling.
fireflicker_t * flick ;
INT16 reallightlevel = sectors [ secnum ] . lightlevel ;
sectors [ secnum ] . lightlevel = line - > backsector - > lightlevel ;
flick = P_SpawnAdjustableFireFlicker ( line - > frontsector , & sectors [ secnum ] ,
P_AproxDistance ( line - > dx , line - > dy ) > > FRACBITS ) ;
// Make sure the starting light level is in range.
if ( reallightlevel < flick - > minlight )
reallightlevel = ( INT16 ) flick - > minlight ;
else if ( reallightlevel > flick - > maxlight )
reallightlevel = ( INT16 ) flick - > maxlight ;
sectors [ secnum ] . lightlevel = reallightlevel ;
}
else
{
// Use front sector for min, target sector for max,
// the same way linetype 61 does it.
P_SpawnAdjustableFireFlicker ( line - > frontsector , & sectors [ secnum ] ,
P_AproxDistance ( line - > dx , line - > dy ) > > FRACBITS ) ;
}
}
break ;
case 417 : // Spawn adjustable glowing light
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , secnum )
2014-03-15 16:59:03 +00:00
{
if ( line - > flags & ML_NOCLIMB & & line - > backsector )
{
// Use front sector for min light level, back sector for max.
// This is tricky because P_SpawnAdjustableGlowingLight expects
// the maxsector (second argument) to also be the target
// sector, so we have to do some light level twiddling.
glow_t * glow ;
INT16 reallightlevel = sectors [ secnum ] . lightlevel ;
sectors [ secnum ] . lightlevel = line - > backsector - > lightlevel ;
glow = P_SpawnAdjustableGlowingLight ( line - > frontsector , & sectors [ secnum ] ,
P_AproxDistance ( line - > dx , line - > dy ) > > FRACBITS ) ;
// Make sure the starting light level is in range.
if ( reallightlevel < glow - > minlight )
reallightlevel = ( INT16 ) glow - > minlight ;
else if ( reallightlevel > glow - > maxlight )
reallightlevel = ( INT16 ) glow - > maxlight ;
sectors [ secnum ] . lightlevel = reallightlevel ;
}
else
{
// Use front sector for min, target sector for max,
// the same way linetype 602 does it.
P_SpawnAdjustableGlowingLight ( line - > frontsector , & sectors [ secnum ] ,
P_AproxDistance ( line - > dx , line - > dy ) > > FRACBITS ) ;
}
}
break ;
case 418 : // Spawn adjustable strobe flash (unsynchronized)
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , secnum )
2014-03-15 16:59:03 +00:00
{
if ( line - > flags & ML_NOCLIMB & & line - > backsector )
{
// Use front sector for min light level, back sector for max.
// This is tricky because P_SpawnAdjustableGlowingLight expects
// the maxsector (second argument) to also be the target
// sector, so we have to do some light level twiddling.
strobe_t * flash ;
INT16 reallightlevel = sectors [ secnum ] . lightlevel ;
sectors [ secnum ] . lightlevel = line - > backsector - > lightlevel ;
flash = P_SpawnAdjustableStrobeFlash ( line - > frontsector , & sectors [ secnum ] ,
abs ( line - > dx ) > > FRACBITS , abs ( line - > dy ) > > FRACBITS , false ) ;
// Make sure the starting light level is in range.
if ( reallightlevel < flash - > minlight )
reallightlevel = ( INT16 ) flash - > minlight ;
else if ( reallightlevel > flash - > maxlight )
reallightlevel = ( INT16 ) flash - > maxlight ;
sectors [ secnum ] . lightlevel = reallightlevel ;
}
else
{
// Use front sector for min, target sector for max,
// the same way linetype 602 does it.
P_SpawnAdjustableStrobeFlash ( line - > frontsector , & sectors [ secnum ] ,
abs ( line - > dx ) > > FRACBITS , abs ( line - > dy ) > > FRACBITS , false ) ;
}
}
break ;
case 419 : // Spawn adjustable strobe flash (synchronized)
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , secnum )
2014-03-15 16:59:03 +00:00
{
if ( line - > flags & ML_NOCLIMB & & line - > backsector )
{
// Use front sector for min light level, back sector for max.
// This is tricky because P_SpawnAdjustableGlowingLight expects
// the maxsector (second argument) to also be the target
// sector, so we have to do some light level twiddling.
strobe_t * flash ;
INT16 reallightlevel = sectors [ secnum ] . lightlevel ;
sectors [ secnum ] . lightlevel = line - > backsector - > lightlevel ;
flash = P_SpawnAdjustableStrobeFlash ( line - > frontsector , & sectors [ secnum ] ,
abs ( line - > dx ) > > FRACBITS , abs ( line - > dy ) > > FRACBITS , true ) ;
// Make sure the starting light level is in range.
if ( reallightlevel < flash - > minlight )
reallightlevel = ( INT16 ) flash - > minlight ;
else if ( reallightlevel > flash - > maxlight )
reallightlevel = ( INT16 ) flash - > maxlight ;
sectors [ secnum ] . lightlevel = reallightlevel ;
}
else
{
// Use front sector for min, target sector for max,
// the same way linetype 602 does it.
P_SpawnAdjustableStrobeFlash ( line - > frontsector , & sectors [ secnum ] ,
abs ( line - > dx ) > > FRACBITS , abs ( line - > dy ) > > FRACBITS , true ) ;
}
}
break ;
case 420 : // Fade light levels in tagged sectors to new value
2020-04-17 16:15:25 +00:00
P_FadeLight ( tag ,
2018-09-13 01:32:12 +00:00
( line - > flags & ML_DONTPEGBOTTOM ) ? max ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS , 0 ) : line - > frontsector - > lightlevel ,
// failsafe: if user specifies Back Y Offset and NOT Front Y Offset, use the Back Offset
// to be consistent with other light and fade specials
( line - > flags & ML_DONTPEGBOTTOM ) ?
( ( line - > sidenum [ 1 ] ! = 0xFFFF & & ! ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ) ?
max ( min ( sides [ line - > sidenum [ 1 ] ] . rowoffset > > FRACBITS , 255 ) , 0 )
: max ( min ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS , 255 ) , 0 ) )
2018-09-13 01:47:53 +00:00
: abs ( P_AproxDistance ( line - > dx , line - > dy ) ) > > FRACBITS ,
2018-09-18 10:27:30 +00:00
( line - > flags & ML_EFFECT4 ) ,
( line - > flags & ML_EFFECT5 ) ) ;
2014-03-15 16:59:03 +00:00
break ;
case 421 : // Stop lighting effect in tagged sectors
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , secnum )
2014-03-15 16:59:03 +00:00
if ( sectors [ secnum ] . lightingdata )
{
P_RemoveThinker ( & ( ( elevator_t * ) sectors [ secnum ] . lightingdata ) - > thinker ) ;
sectors [ secnum ] . lightingdata = NULL ;
}
break ;
case 422 : // Cut away to another view
{
mobj_t * altview ;
2018-11-18 06:53:18 +00:00
if ( ( ! mo | | ! mo - > player ) & & ! titlemapinaction ) // only players have views, and title screens
2014-03-15 16:59:03 +00:00
return ;
2020-04-17 16:15:25 +00:00
if ( ( secnum = Tag_Iterate_Sectors ( tag , 0 ) ) < 0 )
2014-03-15 16:59:03 +00:00
return ;
altview = P_GetObjectTypeInSectorNum ( MT_ALTVIEWMAN , secnum ) ;
if ( ! altview )
return ;
2018-11-18 06:53:18 +00:00
// If titlemap, set the camera ref for title's thinker
// This is not revoked until overwritten; awayviewtics is ignored
if ( titlemapinaction )
titlemapcameraref = altview ;
2020-06-21 08:06:42 +00:00
else
{
P_SetTarget ( & mo - > player - > awayviewmobj , altview ) ;
mo - > player - > awayviewtics = P_AproxDistance ( line - > dx , line - > dy ) > > FRACBITS ;
2018-11-18 06:53:18 +00:00
}
2014-03-15 16:59:03 +00:00
if ( line - > flags & ML_NOCLIMB ) // lets you specify a vertical angle
{
INT32 aim ;
aim = sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ;
2020-06-22 09:14:42 +00:00
aim = ( aim + 360 ) % 360 ;
2014-03-15 16:59:03 +00:00
aim * = ( ANGLE_90 > > 8 ) ;
aim / = 90 ;
aim < < = 8 ;
2020-06-21 08:06:42 +00:00
if ( titlemapinaction )
titlemapcameraref - > cusval = ( angle_t ) aim ;
else
mo - > player - > awayviewaiming = ( angle_t ) aim ;
2014-03-15 16:59:03 +00:00
}
else
2020-06-21 08:06:42 +00:00
{
// straight ahead
2020-06-22 04:21:42 +00:00
if ( ! titlemapinaction )
2020-06-21 08:06:42 +00:00
mo - > player - > awayviewaiming = 0 ;
2020-06-22 04:21:42 +00:00
// don't do cusval cause that's annoying
2020-06-21 08:06:42 +00:00
}
2014-03-15 16:59:03 +00:00
}
break ;
case 423 : // Change Sky
if ( ( mo & & mo - > player & & P_IsLocalPlayer ( mo - > player ) ) | | ( line - > flags & ML_NOCLIMB ) )
P_SetupLevelSky ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS , ( line - > flags & ML_NOCLIMB ) ) ;
break ;
case 424 : // Change Weather
if ( line - > flags & ML_NOCLIMB )
{
globalweather = ( UINT8 ) ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
P_SwitchWeather ( globalweather ) ;
}
else if ( mo & & mo - > player & & P_IsLocalPlayer ( mo - > player ) )
P_SwitchWeather ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
break ;
case 425 : // Calls P_SetMobjState on calling mobj
if ( mo & & ! mo - > player )
P_SetMobjState ( mo , sides [ line - > sidenum [ 0 ] ] . toptexture ) ; //P_AproxDistance(line->dx, line->dy)>>FRACBITS);
break ;
case 426 : // Moves the mobj to its sector's soundorg and on the floor, and stops it
if ( ! mo )
return ;
if ( line - > flags & ML_NOCLIMB )
{
P_UnsetThingPosition ( mo ) ;
mo - > x = mo - > subsector - > sector - > soundorg . x ;
mo - > y = mo - > subsector - > sector - > soundorg . y ;
mo - > z = mo - > floorz ;
P_SetThingPosition ( mo ) ;
}
mo - > momx = mo - > momy = mo - > momz = 1 ;
mo - > pmomz = 0 ;
if ( mo - > player )
{
mo - > player - > rmomx = mo - > player - > rmomy = 1 ;
mo - > player - > cmomx = mo - > player - > cmomy = 0 ;
P_ResetPlayer ( mo - > player ) ;
P_SetPlayerMobjState ( mo , S_PLAY_STND ) ;
// Reset bot too.
if ( bot ) {
if ( line - > flags & ML_NOCLIMB )
P_TeleportMove ( bot , mo - > x , mo - > y , mo - > z ) ;
bot - > momx = bot - > momy = bot - > momz = 1 ;
bot - > pmomz = 0 ;
bot - > player - > rmomx = bot - > player - > rmomy = 1 ;
bot - > player - > cmomx = bot - > player - > cmomy = 0 ;
P_ResetPlayer ( bot - > player ) ;
P_SetPlayerMobjState ( bot , S_PLAY_STND ) ;
}
}
break ;
case 427 : // Awards points if the mobj is a player
if ( mo & & mo - > player )
P_AddPlayerScore ( mo - > player , sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
break ;
case 428 : // Start floating platform movement
EV_DoElevator ( line , elevateContinuous , true ) ;
break ;
case 429 : // Crush Ceiling Down Once
EV_DoCrush ( line , crushCeilOnce ) ;
break ;
case 430 : // Crush Floor Up Once
EV_DoFloor ( line , crushFloorOnce ) ;
break ;
case 431 : // Crush Floor & Ceiling to middle Once
EV_DoCrush ( line , crushBothOnce ) ;
break ;
case 432 : // Enable 2D Mode (Disable if noclimb)
2019-08-20 19:31:28 +00:00
if ( mo & & mo - > player )
2014-03-15 16:59:03 +00:00
{
if ( line - > flags & ML_NOCLIMB )
mo - > flags2 & = ~ MF2_TWOD ;
else
mo - > flags2 | = MF2_TWOD ;
// Copy effect to bot if necessary
// (Teleport them to you so they don't break it.)
if ( bot & & ( bot - > flags2 & MF2_TWOD ) ! = ( mo - > flags2 & MF2_TWOD ) ) {
bot - > flags2 = ( bot - > flags2 & ~ MF2_TWOD ) | ( mo - > flags2 & MF2_TWOD ) ;
P_TeleportMove ( bot , mo - > x , mo - > y , mo - > z ) ;
}
}
break ;
case 433 : // Flip gravity (Flop gravity if noclimb) Works on pushables, too!
if ( line - > flags & ML_NOCLIMB )
mo - > flags2 & = ~ MF2_OBJECTFLIP ;
else
mo - > flags2 | = MF2_OBJECTFLIP ;
if ( bot )
bot - > flags2 = ( bot - > flags2 & ~ MF2_OBJECTFLIP ) | ( mo - > flags2 & MF2_OBJECTFLIP ) ;
break ;
case 434 : // Custom Power
2019-08-20 19:31:28 +00:00
if ( mo & & mo - > player )
2014-03-15 16:59:03 +00:00
{
mobj_t * dummy = P_SpawnMobj ( mo - > x , mo - > y , mo - > z , MT_NULL ) ;
var1 = sides [ line - > sidenum [ 0 ] ] . toptexture ; //(line->dx>>FRACBITS)-1;
if ( line - > sidenum [ 1 ] ! = 0xffff & & line - > flags & ML_BLOCKMONSTERS ) // read power from back sidedef
var2 = sides [ line - > sidenum [ 1 ] ] . toptexture ;
else if ( line - > flags & ML_NOCLIMB ) // 'Infinite'
var2 = UINT16_MAX ;
else
var2 = sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ;
P_SetTarget ( & dummy - > target , mo ) ;
A_CustomPower ( dummy ) ;
if ( bot ) {
P_SetTarget ( & dummy - > target , bot ) ;
A_CustomPower ( dummy ) ;
}
P_RemoveMobj ( dummy ) ;
}
break ;
case 435 : // Change scroller direction
{
scroll_t * scroller ;
thinker_t * th ;
2019-04-20 20:37:19 +00:00
for ( th = thlist [ THINK_MAIN ] . next ; th ! = & thlist [ THINK_MAIN ] ; th = th - > next )
2014-03-15 16:59:03 +00:00
{
if ( th - > function . acp1 ! = ( actionf_p1 ) T_Scroll )
continue ;
scroller = ( scroll_t * ) th ;
2020-04-17 16:15:25 +00:00
if ( ! Tag_Find ( & sectors [ scroller - > affectee ] . tags , tag ) )
2014-03-15 16:59:03 +00:00
continue ;
scroller - > dx = FixedMul ( line - > dx > > SCROLL_SHIFT , CARRYFACTOR ) ;
scroller - > dy = FixedMul ( line - > dy > > SCROLL_SHIFT , CARRYFACTOR ) ;
}
}
break ;
case 436 : // Shatter block remotely
{
INT16 sectag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
INT16 foftag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ;
sector_t * sec ; // Sector that the FOF is visible in
2018-09-20 00:11:30 +00:00
ffloor_t * rover ; // FOF that we are going to crumble
boolean foundrover = false ; // for debug, "Can't find a FOF" message
2014-03-15 16:59:03 +00:00
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( sectag , secnum )
2014-03-15 16:59:03 +00:00
{
sec = sectors + secnum ;
if ( ! sec - > ffloors )
{
CONS_Debug ( DBG_GAMELOGIC , " Line type 436 Executor: Target sector #%d has no FOFs. \n " , secnum ) ;
return ;
}
for ( rover = sec - > ffloors ; rover ; rover = rover - > next )
{
2020-04-17 20:26:04 +00:00
if ( Tag_Find ( & rover - > master - > frontsector - > tags , foftag ) )
2018-09-19 21:49:03 +00:00
{
2018-09-20 00:11:30 +00:00
foundrover = true ;
2018-09-19 21:49:03 +00:00
EV_CrumbleChain ( sec , rover ) ;
}
2014-03-15 16:59:03 +00:00
}
2018-09-19 21:49:03 +00:00
if ( ! foundrover )
2014-03-15 16:59:03 +00:00
{
CONS_Debug ( DBG_GAMELOGIC , " Line type 436 Executor: Can't find a FOF control sector with tag %d \n " , foftag ) ;
return ;
}
}
}
break ;
case 437 : // Disable Player Controls
2019-08-20 19:31:28 +00:00
if ( mo & & mo - > player )
2014-03-15 16:59:03 +00:00
{
UINT16 fractime = ( UINT16 ) ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
if ( fractime < 1 )
fractime = 1 ; //instantly wears off upon leaving
if ( line - > flags & ML_NOCLIMB )
fractime | = 1 < < 15 ; //more crazy &ing, as if music stuff wasn't enough
mo - > player - > powers [ pw_nocontrol ] = fractime ;
if ( bot )
bot - > player - > powers [ pw_nocontrol ] = fractime ;
}
break ;
case 438 : // Set player scale
if ( mo )
{
mo - > destscale = FixedDiv ( P_AproxDistance ( line - > dx , line - > dy ) , 100 < < FRACBITS ) ;
if ( mo - > destscale < FRACUNIT / 100 )
mo - > destscale = FRACUNIT / 100 ;
if ( mo - > player & & bot )
bot - > destscale = mo - > destscale ;
}
break ;
case 439 : // Set texture
{
size_t linenum ;
side_t * set = & sides [ line - > sidenum [ 0 ] ] , * this ;
boolean always = ! ( line - > flags & ML_NOCLIMB ) ; // If noclimb: Only change mid texture if mid texture already exists on tagged lines, etc.
2020-04-17 16:15:25 +00:00
2014-03-15 16:59:03 +00:00
for ( linenum = 0 ; linenum < numlines ; linenum + + )
{
if ( lines [ linenum ] . special = = 439 )
continue ; // Don't override other set texture lines!
2020-04-17 16:15:25 +00:00
if ( ! Tag_Find ( & lines [ linenum ] . tags , tag ) )
2014-03-15 16:59:03 +00:00
continue ; // Find tagged lines
// Front side
this = & sides [ lines [ linenum ] . sidenum [ 0 ] ] ;
if ( always | | this - > toptexture ) this - > toptexture = set - > toptexture ;
if ( always | | this - > midtexture ) this - > midtexture = set - > midtexture ;
if ( always | | this - > bottomtexture ) this - > bottomtexture = set - > bottomtexture ;
if ( lines [ linenum ] . sidenum [ 1 ] = = 0xffff )
continue ; // One-sided stops here.
// Back side
this = & sides [ lines [ linenum ] . sidenum [ 1 ] ] ;
if ( always | | this - > toptexture ) this - > toptexture = set - > toptexture ;
if ( always | | this - > midtexture ) this - > midtexture = set - > midtexture ;
if ( always | | this - > bottomtexture ) this - > bottomtexture = set - > bottomtexture ;
}
}
break ;
case 440 : // Play race countdown and start Metal Sonic
if ( ! metalrecording & & ! metalplayback )
G_DoPlayMetal ( ) ;
break ;
case 441 : // Trigger unlockable
if ( ( ! modifiedgame | | savemoddata ) & & ! ( netgame | | multiplayer ) )
{
INT32 trigid = ( INT32 ) ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
if ( trigid < 0 | | trigid > 31 ) // limited by 32 bit variable
CONS_Debug ( DBG_GAMELOGIC , " Unlockable trigger (sidedef %hu): bad trigger ID %d \n " , line - > sidenum [ 0 ] , trigid ) ;
else
{
unlocktriggers | = 1 < < trigid ;
// Unlocked something?
if ( M_UpdateUnlockablesAndExtraEmblems ( ) )
{
2018-06-03 21:41:54 +00:00
S_StartSound ( NULL , sfx_s3k68 ) ;
2014-03-15 16:59:03 +00:00
G_SaveGameData ( ) ; // only save if unlocked something
}
}
}
// Execute one time only
line - > special = 0 ;
break ;
case 442 : // Calls P_SetMobjState on mobjs of a given type in the tagged sectors
{
const mobjtype_t type = ( mobjtype_t ) sides [ line - > sidenum [ 0 ] ] . toptexture ;
statenum_t state = NUMSTATES ;
sector_t * sec ;
mobj_t * thing ;
if ( line - > sidenum [ 1 ] ! = 0xffff )
state = ( statenum_t ) sides [ line - > sidenum [ 1 ] ] . toptexture ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , secnum )
2014-03-15 16:59:03 +00:00
{
2014-03-18 17:56:54 +00:00
boolean tryagain ;
2014-03-15 16:59:03 +00:00
sec = sectors + secnum ;
2014-03-18 17:56:54 +00:00
do {
tryagain = false ;
for ( thing = sec - > thinglist ; thing ; thing = thing - > snext )
if ( thing - > type = = type )
{
if ( state ! = NUMSTATES )
{
if ( ! P_SetMobjState ( thing , state ) ) // set state to specific state
{ // mobj was removed
tryagain = true ; // snext is corrupt, we'll have to start over.
break ;
}
}
else if ( ! P_SetMobjState ( thing , thing - > state - > nextstate ) ) // set state to nextstate
{ // mobj was removed
tryagain = true ; // snext is corrupt, we'll have to start over.
break ;
}
}
} while ( tryagain ) ;
2014-03-15 16:59:03 +00:00
}
break ;
}
case 443 : // Calls a named Lua function
2020-01-11 14:38:50 +00:00
if ( line - > stringargs [ 0 ] )
2020-11-17 12:14:45 +00:00
LUA_HookLinedefExecute ( line , mo , callsec ) ;
2017-04-01 19:16:48 +00:00
else
2020-01-11 14:38:50 +00:00
CONS_Alert ( CONS_WARNING , " Linedef %s is missing the hook name of the Lua function to call! (This should be given in arg0str) \n " , sizeu1 ( line - lines ) ) ;
2014-03-15 16:59:03 +00:00
break ;
case 444 : // Earthquake camera
{
quake . intensity = sides [ line - > sidenum [ 0 ] ] . textureoffset ;
quake . radius = sides [ line - > sidenum [ 0 ] ] . rowoffset ;
quake . time = P_AproxDistance ( line - > dx , line - > dy ) > > FRACBITS ;
quake . epicenter = NULL ; /// \todo
// reasonable defaults.
if ( ! quake . intensity )
quake . intensity = 8 < < FRACBITS ;
if ( ! quake . radius )
quake . radius = 512 < < FRACBITS ;
break ;
}
case 445 : // Force block disappear remotely (reappear if noclimb)
{
INT16 sectag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
INT16 foftag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ;
sector_t * sec ; // Sector that the FOF is visible (or not visible) in
2018-09-20 00:11:30 +00:00
ffloor_t * rover ; // FOF to vanish/un-vanish
boolean foundrover = false ; // for debug, "Can't find a FOF" message
2016-02-26 18:11:12 +00:00
ffloortype_e oldflags ; // store FOF's old flags
2014-03-15 16:59:03 +00:00
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( sectag , secnum )
2014-03-15 16:59:03 +00:00
{
sec = sectors + secnum ;
if ( ! sec - > ffloors )
{
CONS_Debug ( DBG_GAMELOGIC , " Line type 445 Executor: Target sector #%d has no FOFs. \n " , secnum ) ;
return ;
}
for ( rover = sec - > ffloors ; rover ; rover = rover - > next )
{
2020-04-17 20:26:04 +00:00
if ( Tag_Find ( & rover - > master - > frontsector - > tags , foftag ) )
2018-09-19 21:49:03 +00:00
{
2018-09-20 00:11:30 +00:00
foundrover = true ;
2018-09-19 21:49:03 +00:00
oldflags = rover - > flags ;
// Abracadabra!
if ( line - > flags & ML_NOCLIMB )
rover - > flags | = FF_EXISTS ;
else
rover - > flags & = ~ FF_EXISTS ;
// if flags changed, reset sector's light list
if ( rover - > flags ! = oldflags )
2019-07-04 13:43:18 +00:00
{
2018-09-19 21:49:03 +00:00
sec - > moved = true ;
2019-07-04 13:43:18 +00:00
P_RecalcPrecipInSector ( sec ) ;
}
2018-09-19 21:49:03 +00:00
}
2014-03-15 16:59:03 +00:00
}
2018-09-19 21:49:03 +00:00
if ( ! foundrover )
2014-03-15 16:59:03 +00:00
{
CONS_Debug ( DBG_GAMELOGIC , " Line type 445 Executor: Can't find a FOF control sector with tag %d \n " , foftag ) ;
return ;
}
}
}
break ;
2016-06-21 21:04:58 +00:00
case 446 : // Make block fall remotely (acts like FF_CRUMBLE)
{
INT16 sectag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
INT16 foftag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ;
sector_t * sec ; // Sector that the FOF is visible in
2018-09-20 00:11:30 +00:00
ffloor_t * rover ; // FOF that we are going to make fall down
boolean foundrover = false ; // for debug, "Can't find a FOF" message
2016-06-21 21:04:58 +00:00
player_t * player = NULL ; // player that caused FOF to fall
boolean respawn = true ; // should the fallen FOF respawn?
if ( mo ) // NULL check
player = mo - > player ;
if ( line - > flags & ML_NOCLIMB ) // don't respawn!
respawn = false ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( sectag , secnum )
2016-06-21 21:04:58 +00:00
{
sec = sectors + secnum ;
if ( ! sec - > ffloors )
{
CONS_Debug ( DBG_GAMELOGIC , " Line type 446 Executor: Target sector #%d has no FOFs. \n " , secnum ) ;
return ;
}
for ( rover = sec - > ffloors ; rover ; rover = rover - > next )
{
2020-04-17 20:26:04 +00:00
if ( Tag_Find ( & rover - > master - > frontsector - > tags , foftag ) )
2018-09-19 21:49:03 +00:00
{
2018-09-20 00:11:30 +00:00
foundrover = true ;
2018-09-19 21:49:03 +00:00
if ( line - > flags & ML_BLOCKMONSTERS ) // FOF flags determine respawn ability instead?
respawn = ! ( rover - > flags & FF_NORETURN ) ^ ! ! ( line - > flags & ML_NOCLIMB ) ; // no climb inverts
EV_StartCrumble ( rover - > master - > frontsector , rover , ( rover - > flags & FF_FLOATBOB ) , player , rover - > alpha , respawn ) ;
}
2016-06-21 21:04:58 +00:00
}
2018-09-19 21:49:03 +00:00
if ( ! foundrover )
2016-06-21 21:04:58 +00:00
{
CONS_Debug ( DBG_GAMELOGIC , " Line type 446 Executor: Can't find a FOF control sector with tag %d \n " , foftag ) ;
return ;
}
}
}
break ;
2018-06-14 20:51:21 +00:00
case 447 : // Change colormap of tagged sectors!
// Basically this special applies a colormap to the tagged sectors, just like 606 (the colormap linedef)
// Except it is activated by linedef executor, not level load
// This could even override existing colormaps I believe
// -- Monster Iestyn 14/06/18
2020-03-20 10:19:30 +00:00
{
extracolormap_t * source ;
if ( ! udmf )
source = sides [ line - > sidenum [ 0 ] ] . colormap_data ;
else
2018-09-11 20:50:35 +00:00
{
2020-03-20 10:19:30 +00:00
if ( ! line - > args [ 1 ] )
source = line - > frontsector - > extra_colormap ;
else
2018-09-12 11:31:10 +00:00
{
2020-04-18 12:04:15 +00:00
INT32 sourcesec = Tag_Iterate_Sectors ( line - > args [ 1 ] , 0 ) ;
2020-03-20 10:19:30 +00:00
if ( sourcesec = = - 1 )
2018-09-12 11:31:10 +00:00
{
2020-03-20 10:19:30 +00:00
CONS_Debug ( DBG_GAMELOGIC , " Line type 447 Executor: Can't find sector with source colormap (tag %d)! \n " , line - > args [ 1 ] ) ;
return ;
2018-09-12 11:31:10 +00:00
}
2020-03-20 10:19:30 +00:00
source = sectors [ sourcesec ] . extra_colormap ;
2018-09-12 11:31:10 +00:00
}
2020-03-20 10:19:30 +00:00
}
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( line - > args [ 0 ] , secnum )
2020-03-20 10:19:30 +00:00
{
2020-03-20 11:19:02 +00:00
if ( sectors [ secnum ] . colormap_protected )
continue ;
2020-03-20 10:19:30 +00:00
P_ResetColormapFader ( & sectors [ secnum ] ) ;
2020-04-18 15:15:25 +00:00
if ( line - > args [ 2 ] & TMCF_RELATIVE )
2018-09-12 11:38:51 +00:00
{
2020-03-20 10:19:30 +00:00
extracolormap_t * target = ( ! udmf & & ( line - > flags & ML_TFERLINE ) & & line - > sidenum [ 1 ] ! = 0xFFFF ) ?
sides [ line - > sidenum [ 1 ] ] . colormap_data : sectors [ secnum ] . extra_colormap ; // use back colormap instead of target sector
extracolormap_t * exc = R_AddColormaps (
target ,
source ,
2020-04-18 15:15:25 +00:00
line - > args [ 2 ] & TMCF_SUBLIGHTR ,
line - > args [ 2 ] & TMCF_SUBLIGHTG ,
line - > args [ 2 ] & TMCF_SUBLIGHTB ,
line - > args [ 2 ] & TMCF_SUBLIGHTA ,
line - > args [ 2 ] & TMCF_SUBFADER ,
line - > args [ 2 ] & TMCF_SUBFADEG ,
line - > args [ 2 ] & TMCF_SUBFADEB ,
line - > args [ 2 ] & TMCF_SUBFADEA ,
line - > args [ 2 ] & TMCF_SUBFADESTART ,
line - > args [ 2 ] & TMCF_SUBFADEEND ,
line - > args [ 2 ] & TMCF_IGNOREFLAGS ,
2020-03-20 10:19:30 +00:00
false ) ;
2018-09-12 11:38:51 +00:00
2018-09-12 12:06:44 +00:00
if ( ! ( sectors [ secnum ] . extra_colormap = R_GetColormapFromList ( exc ) ) )
{
exc - > colormap = R_CreateLightTable ( exc ) ;
R_AddColormapToList ( exc ) ;
sectors [ secnum ] . extra_colormap = exc ;
2018-09-12 11:38:51 +00:00
}
else
2018-09-12 12:06:44 +00:00
Z_Free ( exc ) ;
2018-09-12 11:38:51 +00:00
}
2018-09-12 12:06:44 +00:00
else
2020-03-20 10:19:30 +00:00
sectors [ secnum ] . extra_colormap = source ;
2018-09-11 20:50:35 +00:00
}
2018-06-14 20:51:21 +00:00
break ;
2020-03-20 10:19:30 +00:00
}
2017-04-26 17:16:01 +00:00
case 448 : // Change skybox viewpoint/centerpoint
if ( ( mo & & mo - > player & & P_IsLocalPlayer ( mo - > player ) ) | | ( line - > flags & ML_NOCLIMB ) )
{
INT32 viewid = sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ;
INT32 centerid = sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ;
2017-05-01 18:34:53 +00:00
if ( ( line - > flags & ( ML_EFFECT4 | ML_BLOCKMONSTERS ) ) = = ML_EFFECT4 ) // Solid Midtexture is on but Block Enemies is off?
2017-04-26 17:16:01 +00:00
{
2017-05-01 18:34:53 +00:00
CONS_Alert ( CONS_WARNING ,
M_GetText ( " Skybox switch linedef (tag %d) doesn't have anything to do. \n Consider changing the linedef's flag configuration or removing it entirely. \n " ) ,
2020-04-17 16:15:25 +00:00
tag ) ;
2017-04-26 17:16:01 +00:00
}
2017-05-01 18:34:53 +00:00
else
2017-04-26 17:16:01 +00:00
{
2017-05-01 18:34:53 +00:00
// set viewpoint mobj
if ( ! ( line - > flags & ML_EFFECT4 ) ) // Solid Midtexture turns off viewpoint setting
{
if ( viewid > = 0 & & viewid < 16 )
skyboxmo [ 0 ] = skyboxviewpnts [ viewid ] ;
else
skyboxmo [ 0 ] = NULL ;
}
// set centerpoint mobj
if ( line - > flags & ML_BLOCKMONSTERS ) // Block Enemies turns ON centerpoint setting
{
if ( centerid > = 0 & & centerid < 16 )
skyboxmo [ 1 ] = skyboxcenterpnts [ centerid ] ;
else
skyboxmo [ 1 ] = NULL ;
}
2017-04-26 17:16:01 +00:00
}
CONS_Debug ( DBG_GAMELOGIC , " Line type 448 Executor: viewid = %d, centerid = %d, viewpoint? = %s, centerpoint? = %s \n " ,
viewid ,
centerid ,
( ( line - > flags & ML_EFFECT4 ) ? " no " : " yes " ) ,
( ( line - > flags & ML_BLOCKMONSTERS ) ? " yes " : " no " ) ) ;
}
break ;
2019-07-31 22:17:17 +00:00
case 449 : // Enable bosses with parameter
{
INT32 bossid = sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ;
if ( bossid & ~ 15 ) // if any bits other than first 16 are set
{
CONS_Alert ( CONS_WARNING ,
M_GetText ( " Boss enable linedef (tag %d) has an invalid texture x offset. \n Consider changing it or removing it entirely. \n " ) ,
2020-04-17 16:15:25 +00:00
tag ) ;
2019-07-31 22:17:17 +00:00
break ;
}
if ( line - > flags & ML_NOCLIMB )
{
bossdisabled | = ( 1 < < bossid ) ;
CONS_Debug ( DBG_GAMELOGIC , " Line type 449 Executor: bossid disabled = %d " , bossid ) ;
}
else
{
bossdisabled & = ~ ( 1 < < bossid ) ;
CONS_Debug ( DBG_GAMELOGIC , " Line type 449 Executor: bossid enabled = %d " , bossid ) ;
}
break ;
}
2014-03-15 16:59:03 +00:00
case 450 : // Execute Linedef Executor - for recursion
2020-04-17 16:15:25 +00:00
P_LinedefExecute ( tag , mo , NULL ) ;
2014-03-15 16:59:03 +00:00
break ;
case 451 : // Execute Random Linedef Executor
{
INT32 rvalue1 = sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ;
INT32 rvalue2 = sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ;
INT32 result ;
if ( rvalue1 < = rvalue2 )
result = P_RandomRange ( rvalue1 , rvalue2 ) ;
else
result = P_RandomRange ( rvalue2 , rvalue1 ) ;
P_LinedefExecute ( ( INT16 ) result , mo , NULL ) ;
break ;
}
2018-09-10 14:09:02 +00:00
case 452 : // Set FOF alpha
{
2018-09-10 14:11:27 +00:00
INT16 destvalue = line - > sidenum [ 1 ] ! = 0xffff ?
( INT16 ) ( sides [ line - > sidenum [ 1 ] ] . textureoffset > > FRACBITS ) : ( INT16 ) ( P_AproxDistance ( line - > dx , line - > dy ) > > FRACBITS ) ;
2018-09-10 14:09:02 +00:00
INT16 sectag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
INT16 foftag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ;
sector_t * sec ; // Sector that the FOF is visible in
2018-09-20 00:11:30 +00:00
ffloor_t * rover ; // FOF that we are going to operate
boolean foundrover = false ; // for debug, "Can't find a FOF" message
2018-09-10 14:09:02 +00:00
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( sectag , secnum )
2018-09-10 14:09:02 +00:00
{
sec = sectors + secnum ;
if ( ! sec - > ffloors )
{
CONS_Debug ( DBG_GAMELOGIC , " Line type 452 Executor: Target sector #%d has no FOFs. \n " , secnum ) ;
return ;
}
for ( rover = sec - > ffloors ; rover ; rover = rover - > next )
{
2020-04-17 20:26:04 +00:00
if ( Tag_Find ( & rover - > master - > frontsector - > tags , foftag ) )
2018-09-19 21:49:03 +00:00
{
2018-09-20 00:11:30 +00:00
foundrover = true ;
2018-09-19 21:49:03 +00:00
// If fading an invisible FOF whose render flags we did not yet set,
// initialize its alpha to 1
// for relative alpha calc
if ( ! ( line - > flags & ML_NOCLIMB ) & & // do translucent
( rover - > spawnflags & FF_NOSHADE ) & & // do not include light blocks, which don't set FF_NOSHADE
! ( rover - > spawnflags & FF_RENDERSIDES ) & &
! ( rover - > spawnflags & FF_RENDERPLANES ) & &
! ( rover - > flags & FF_RENDERALL ) )
rover - > alpha = 1 ;
P_RemoveFakeFloorFader ( rover ) ;
P_FadeFakeFloor ( rover ,
rover - > alpha ,
max ( 1 , min ( 256 , ( line - > flags & ML_EFFECT3 ) ? rover - > alpha + destvalue : destvalue ) ) ,
0 , // set alpha immediately
false , NULL , // tic-based logic
false , // do not handle FF_EXISTS
! ( line - > flags & ML_NOCLIMB ) , // handle FF_TRANSLUCENT
false , // do not handle lighting
false , // do not handle colormap
false , // do not handle collision
false , // do not do ghost fade (no collision during fade)
true ) ; // use exact alpha values (for opengl)
}
2018-09-10 14:09:02 +00:00
}
2018-09-19 21:49:03 +00:00
if ( ! foundrover )
2018-09-10 14:09:02 +00:00
{
CONS_Debug ( DBG_GAMELOGIC , " Line type 452 Executor: Can't find a FOF control sector with tag %d \n " , foftag ) ;
return ;
}
}
break ;
}
case 453 : // Fade FOF
2018-03-30 20:17:35 +00:00
{
2018-09-10 14:11:27 +00:00
INT16 destvalue = line - > sidenum [ 1 ] ! = 0xffff ?
2018-09-10 13:49:52 +00:00
( INT16 ) ( sides [ line - > sidenum [ 1 ] ] . textureoffset > > FRACBITS ) : ( INT16 ) ( line - > dx > > FRACBITS ) ;
2018-09-10 14:11:27 +00:00
INT16 speed = line - > sidenum [ 1 ] ! = 0xffff ?
2018-09-18 01:03:51 +00:00
( INT16 ) ( abs ( sides [ line - > sidenum [ 1 ] ] . rowoffset > > FRACBITS ) ) : ( INT16 ) ( abs ( line - > dy ) > > FRACBITS ) ;
2018-08-17 04:32:20 +00:00
INT16 sectag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
INT16 foftag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ;
sector_t * sec ; // Sector that the FOF is visible in
2018-09-20 00:11:30 +00:00
ffloor_t * rover ; // FOF that we are going to operate
boolean foundrover = false ; // for debug, "Can't find a FOF" message
2018-08-17 06:28:52 +00:00
size_t j = 0 ; // sec->ffloors is saved as ffloor #0, ss->ffloors->next is #1, etc
2018-08-17 04:32:20 +00:00
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( sectag , secnum )
2018-08-17 04:32:20 +00:00
{
sec = sectors + secnum ;
if ( ! sec - > ffloors )
{
2018-09-10 14:09:02 +00:00
CONS_Debug ( DBG_GAMELOGIC , " Line type 453 Executor: Target sector #%d has no FOFs. \n " , secnum ) ;
2018-08-17 04:32:20 +00:00
return ;
}
for ( rover = sec - > ffloors ; rover ; rover = rover - > next )
{
2020-04-17 20:26:04 +00:00
if ( Tag_Find ( & rover - > master - > frontsector - > tags , foftag ) )
2018-09-19 21:49:03 +00:00
{
2018-09-20 00:11:30 +00:00
foundrover = true ;
2018-09-19 21:49:03 +00:00
// Prevent continuous execs from interfering on an existing fade
if ( ! ( line - > flags & ML_EFFECT5 )
& & rover - > fadingdata )
//&& ((fade_t*)rover->fadingdata)->timer > (ticbased ? 2 : speed*2))
{
CONS_Debug ( DBG_GAMELOGIC , " Line type 453 Executor: Fade FOF thinker already exists, timer: %d \n " , ( ( fade_t * ) rover - > fadingdata ) - > timer ) ;
continue ;
}
if ( speed > 0 )
P_AddFakeFloorFader ( rover , secnum , j ,
destvalue ,
speed ,
( line - > flags & ML_EFFECT4 ) , // tic-based logic
( line - > flags & ML_EFFECT3 ) , // Relative destvalue
! ( line - > flags & ML_BLOCKMONSTERS ) , // do not handle FF_EXISTS
! ( line - > flags & ML_NOCLIMB ) , // do not handle FF_TRANSLUCENT
! ( line - > flags & ML_EFFECT2 ) , // do not handle lighting
! ( line - > flags & ML_EFFECT2 ) , // do not handle colormap (ran out of flags)
! ( line - > flags & ML_BOUNCY ) , // do not handle collision
( line - > flags & ML_EFFECT1 ) , // do ghost fade (no collision during fade)
( line - > flags & ML_TFERLINE ) ) ; // use exact alpha values (for opengl)
else
{
// If fading an invisible FOF whose render flags we did not yet set,
// initialize its alpha to 1
// for relative alpha calc
if ( ! ( line - > flags & ML_NOCLIMB ) & & // do translucent
( rover - > spawnflags & FF_NOSHADE ) & & // do not include light blocks, which don't set FF_NOSHADE
! ( rover - > spawnflags & FF_RENDERSIDES ) & &
! ( rover - > spawnflags & FF_RENDERPLANES ) & &
! ( rover - > flags & FF_RENDERALL ) )
rover - > alpha = 1 ;
P_RemoveFakeFloorFader ( rover ) ;
P_FadeFakeFloor ( rover ,
rover - > alpha ,
max ( 1 , min ( 256 , ( line - > flags & ML_EFFECT3 ) ? rover - > alpha + destvalue : destvalue ) ) ,
0 , // set alpha immediately
false , NULL , // tic-based logic
! ( line - > flags & ML_BLOCKMONSTERS ) , // do not handle FF_EXISTS
! ( line - > flags & ML_NOCLIMB ) , // do not handle FF_TRANSLUCENT
! ( line - > flags & ML_EFFECT2 ) , // do not handle lighting
! ( line - > flags & ML_EFFECT2 ) , // do not handle colormap (ran out of flags)
! ( line - > flags & ML_BOUNCY ) , // do not handle collision
( line - > flags & ML_EFFECT1 ) , // do ghost fade (no collision during fade)
( line - > flags & ML_TFERLINE ) ) ; // use exact alpha values (for opengl)
}
}
2018-08-17 06:28:52 +00:00
j + + ;
2018-08-17 04:32:20 +00:00
}
2018-09-19 21:49:03 +00:00
if ( ! foundrover )
2018-08-17 04:32:20 +00:00
{
2018-09-10 14:09:02 +00:00
CONS_Debug ( DBG_GAMELOGIC , " Line type 453 Executor: Can't find a FOF control sector with tag %d \n " , foftag ) ;
2018-08-17 04:32:20 +00:00
return ;
}
}
2018-03-30 20:17:35 +00:00
break ;
}
2018-08-14 02:10:16 +00:00
2018-09-10 14:09:02 +00:00
case 454 : // Stop fading FOF
2018-03-31 05:15:24 +00:00
{
2018-08-17 04:32:20 +00:00
INT16 sectag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
INT16 foftag = ( INT16 ) ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ;
sector_t * sec ; // Sector that the FOF is visible in
2018-09-20 00:11:30 +00:00
ffloor_t * rover ; // FOF that we are going to operate
boolean foundrover = false ; // for debug, "Can't find a FOF" message
2018-08-17 04:32:20 +00:00
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( sectag , secnum )
2018-08-17 04:32:20 +00:00
{
sec = sectors + secnum ;
if ( ! sec - > ffloors )
{
2018-09-10 14:09:02 +00:00
CONS_Debug ( DBG_GAMELOGIC , " Line type 454 Executor: Target sector #%d has no FOFs. \n " , secnum ) ;
2018-08-17 04:32:20 +00:00
return ;
}
for ( rover = sec - > ffloors ; rover ; rover = rover - > next )
{
2020-04-17 20:26:04 +00:00
if ( Tag_Find ( & rover - > master - > frontsector - > tags , foftag ) )
2018-09-19 21:49:03 +00:00
{
2018-09-20 00:11:30 +00:00
foundrover = true ;
2018-09-19 21:49:03 +00:00
P_ResetFakeFloorFader ( rover , NULL ,
! ( line - > flags & ML_BLOCKMONSTERS ) ) ; // do not finalize collision flags
}
2018-08-17 04:32:20 +00:00
}
2018-09-19 21:49:03 +00:00
if ( ! foundrover )
2018-08-17 04:32:20 +00:00
{
2018-09-10 14:09:02 +00:00
CONS_Debug ( DBG_GAMELOGIC , " Line type 454 Executor: Can't find a FOF control sector with tag %d \n " , foftag ) ;
2018-08-17 04:32:20 +00:00
return ;
}
}
2018-03-30 20:17:35 +00:00
break ;
2018-03-31 05:15:24 +00:00
}
2018-03-30 20:17:35 +00:00
2018-09-12 13:06:38 +00:00
case 455 : // Fade colormap
2020-03-20 10:19:30 +00:00
{
extracolormap_t * dest ;
if ( ! udmf )
dest = sides [ line - > sidenum [ 0 ] ] . colormap_data ;
else
{
if ( ! line - > args [ 1 ] )
dest = line - > frontsector - > extra_colormap ;
else
{
2020-04-18 12:04:15 +00:00
INT32 destsec = Tag_Iterate_Sectors ( line - > args [ 1 ] , 0 ) ;
2020-03-20 10:19:30 +00:00
if ( destsec = = - 1 )
{
CONS_Debug ( DBG_GAMELOGIC , " Line type 455 Executor: Can't find sector with destination colormap (tag %d)! \n " , line - > args [ 1 ] ) ;
return ;
}
dest = sectors [ destsec ] . extra_colormap ;
}
}
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( line - > args [ 0 ] , secnum )
2018-09-12 13:06:38 +00:00
{
extracolormap_t * source_exc , * dest_exc , * exc ;
2020-03-20 11:19:02 +00:00
if ( sectors [ secnum ] . colormap_protected )
continue ;
2020-03-20 10:19:30 +00:00
// Don't interrupt ongoing fade
2020-04-18 15:15:25 +00:00
if ( ! ( line - > args [ 3 ] & TMCF_OVERRIDE )
2018-09-18 01:16:27 +00:00
& & sectors [ secnum ] . fadecolormapdata )
2018-09-18 09:45:28 +00:00
//&& ((fadecolormap_t*)sectors[secnum].fadecolormapdata)->timer > (ticbased ? 2 : speed*2))
{
2018-09-18 11:07:30 +00:00
CONS_Debug ( DBG_GAMELOGIC , " Line type 455 Executor: Fade color thinker already exists, timer: %d \n " , ( ( fadecolormap_t * ) sectors [ secnum ] . fadecolormapdata ) - > timer ) ;
2018-09-18 01:16:27 +00:00
continue ;
2018-09-18 09:45:28 +00:00
}
2018-09-18 01:16:27 +00:00
2020-03-20 10:19:30 +00:00
if ( ! udmf & & ( line - > flags & ML_TFERLINE ) ) // use back colormap instead of target sector
2018-09-13 00:52:05 +00:00
sectors [ secnum ] . extra_colormap = ( line - > sidenum [ 1 ] ! = 0xFFFF ) ?
2020-03-20 10:19:30 +00:00
sides [ line - > sidenum [ 1 ] ] . colormap_data : NULL ;
2018-09-12 15:21:49 +00:00
exc = sectors [ secnum ] . extra_colormap ;
2018-09-12 13:06:38 +00:00
2020-04-18 15:15:25 +00:00
if ( ! ( line - > args [ 3 ] & TMCF_FROMBLACK ) // Override fade from default rgba
2020-03-20 10:19:30 +00:00
& & ! R_CheckDefaultColormap ( dest , true , false , false )
2018-09-12 13:06:38 +00:00
& & R_CheckDefaultColormap ( exc , true , false , false ) )
{
exc = R_CopyColormap ( exc , false ) ;
2020-03-20 10:19:30 +00:00
exc - > rgba = R_GetRgbaRGB ( dest - > rgba ) + R_PutRgbaA ( R_GetRgbaA ( exc - > rgba ) ) ;
//exc->fadergba = R_GetRgbaRGB(dest->rgba) + R_PutRgbaA(R_GetRgbaA(exc->fadergba));
2018-09-12 13:06:38 +00:00
if ( ! ( source_exc = R_GetColormapFromList ( exc ) ) )
{
exc - > colormap = R_CreateLightTable ( exc ) ;
R_AddColormapToList ( exc ) ;
source_exc = exc ;
}
else
Z_Free ( exc ) ;
2018-09-12 15:11:22 +00:00
sectors [ secnum ] . extra_colormap = source_exc ;
2018-09-12 13:06:38 +00:00
}
else
2018-09-12 15:11:22 +00:00
source_exc = exc ? exc : R_GetDefaultColormap ( ) ;
2018-09-12 13:06:38 +00:00
2020-04-18 15:15:25 +00:00
if ( line - > args [ 3 ] & TMCF_RELATIVE )
2018-09-12 13:06:38 +00:00
{
exc = R_AddColormaps (
source_exc ,
2020-03-20 10:19:30 +00:00
dest ,
2020-04-18 15:15:25 +00:00
line - > args [ 3 ] & TMCF_SUBLIGHTR ,
line - > args [ 3 ] & TMCF_SUBLIGHTG ,
line - > args [ 3 ] & TMCF_SUBLIGHTB ,
line - > args [ 3 ] & TMCF_SUBLIGHTA ,
line - > args [ 3 ] & TMCF_SUBFADER ,
line - > args [ 3 ] & TMCF_SUBFADEG ,
line - > args [ 3 ] & TMCF_SUBFADEB ,
line - > args [ 3 ] & TMCF_SUBFADEA ,
line - > args [ 3 ] & TMCF_SUBFADESTART ,
line - > args [ 3 ] & TMCF_SUBFADEEND ,
line - > args [ 3 ] & TMCF_IGNOREFLAGS ,
2018-09-12 13:06:38 +00:00
false ) ;
}
else
2020-03-20 10:19:30 +00:00
exc = R_CopyColormap ( dest , false ) ;
2018-09-12 13:06:38 +00:00
if ( ! ( dest_exc = R_GetColormapFromList ( exc ) ) )
{
exc - > colormap = R_CreateLightTable ( exc ) ;
R_AddColormapToList ( exc ) ;
dest_exc = exc ;
}
else
Z_Free ( exc ) ;
2020-03-20 10:19:30 +00:00
Add_ColormapFader ( & sectors [ secnum ] , source_exc , dest_exc , true , // tic-based timing
line - > args [ 2 ] ) ;
2018-09-12 13:06:38 +00:00
}
break ;
2020-03-20 10:19:30 +00:00
}
2018-09-12 13:06:38 +00:00
case 456 : // Stop fade colormap
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( line - > args [ 0 ] , secnum )
2018-09-12 13:06:38 +00:00
P_ResetColormapFader ( & sectors [ secnum ] ) ;
2018-09-12 13:42:51 +00:00
break ;
2018-09-12 13:06:38 +00:00
2018-11-04 21:28:25 +00:00
case 457 : // Track mobj angle to point
if ( mo )
{
2018-11-14 15:13:39 +00:00
INT32 failureangle = FixedAngle ( ( min ( max ( abs ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) , 0 ) , 360 ) ) * FRACUNIT ) ;
2018-11-04 21:28:25 +00:00
INT32 failuredelay = abs ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ;
INT32 failureexectag = line - > sidenum [ 1 ] ! = 0xffff ?
( INT32 ) ( sides [ line - > sidenum [ 1 ] ] . textureoffset > > FRACBITS ) : 0 ;
boolean persist = ( line - > flags & ML_EFFECT2 ) ;
mobj_t * anchormo ;
2020-04-17 16:15:25 +00:00
if ( ( secnum = Tag_Iterate_Sectors ( tag , 0 ) ) < 0 )
2018-11-04 21:28:25 +00:00
return ;
anchormo = P_GetObjectTypeInSectorNum ( MT_ANGLEMAN , secnum ) ;
if ( ! anchormo )
return ;
mo - > eflags | = MFE_TRACERANGLE ;
P_SetTarget ( & mo - > tracer , anchormo ) ;
mo - > lastlook = persist ; // don't disable behavior after first failure
mo - > extravalue1 = failureangle ; // angle to exceed for failure state
mo - > extravalue2 = failureexectag ; // exec tag for failure state (angle is not within range)
mo - > cusval = mo - > cvmem = failuredelay ; // cusval = tics to allow failure before line trigger; cvmem = decrement timer
}
break ;
case 458 : // Stop tracking mobj angle to point
2018-11-04 21:48:18 +00:00
if ( mo & & ( mo - > eflags & MFE_TRACERANGLE ) )
2018-11-04 21:28:25 +00:00
{
mo - > eflags & = ~ MFE_TRACERANGLE ;
P_SetTarget ( & mo - > tracer , NULL ) ;
mo - > lastlook = mo - > cvmem = mo - > cusval = mo - > extravalue1 = mo - > extravalue2 = 0 ;
}
break ;
2018-11-04 21:45:29 +00:00
case 459 : // Control Text Prompt
// console player only unless NOCLIMB is set
2018-11-10 20:34:56 +00:00
if ( mo & & mo - > player & & P_IsLocalPlayer ( mo - > player ) & & ( ! bot | | bot ! = mo ) )
2018-11-04 21:45:29 +00:00
{
INT32 promptnum = max ( 0 , ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) - 1 ) ;
INT32 pagenum = max ( 0 , ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) - 1 ) ;
2020-04-17 16:15:25 +00:00
INT32 postexectag = abs ( ( line - > sidenum [ 1 ] ! = 0xFFFF ) ? sides [ line - > sidenum [ 1 ] ] . textureoffset > > FRACBITS : tag ) ;
2018-11-04 21:45:29 +00:00
boolean closetextprompt = ( line - > flags & ML_BLOCKMONSTERS ) ;
2018-11-10 20:34:56 +00:00
//boolean allplayers = (line->flags & ML_NOCLIMB);
2018-11-04 21:45:29 +00:00
boolean runpostexec = ( line - > flags & ML_EFFECT1 ) ;
boolean blockcontrols = ! ( line - > flags & ML_EFFECT2 ) ;
boolean freezerealtime = ! ( line - > flags & ML_EFFECT3 ) ;
//boolean freezethinkers = (line->flags & ML_EFFECT4);
2018-11-10 14:32:53 +00:00
boolean callbynamedtag = ( line - > flags & ML_TFERLINE ) ;
2018-11-04 21:45:29 +00:00
if ( closetextprompt )
2018-11-05 03:22:47 +00:00
F_EndTextPrompt ( false , false ) ;
2018-11-04 21:45:29 +00:00
else
2018-11-13 05:13:36 +00:00
{
if ( callbynamedtag & & sides [ line - > sidenum [ 0 ] ] . text & & sides [ line - > sidenum [ 0 ] ] . text [ 0 ] )
F_GetPromptPageByNamedTag ( sides [ line - > sidenum [ 0 ] ] . text , & promptnum , & pagenum ) ;
2018-11-04 21:45:29 +00:00
F_StartTextPrompt ( promptnum , pagenum , mo , runpostexec ? postexectag : 0 , blockcontrols , freezerealtime ) ;
2018-11-13 05:13:36 +00:00
}
2018-11-04 21:45:29 +00:00
}
break ;
2019-07-19 02:42:46 +00:00
case 460 : // Award rings
{
2019-07-25 01:18:07 +00:00
INT16 rings = ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ) ;
2019-08-03 12:04:02 +00:00
INT32 delay = ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS ) ;
2019-07-25 01:18:07 +00:00
if ( mo & & mo - > player )
2019-07-19 02:42:46 +00:00
{
2019-08-03 12:04:02 +00:00
if ( delay < = 0 | | ! ( leveltime % delay ) )
2019-07-19 02:42:46 +00:00
P_GivePlayerRings ( mo - > player , rings ) ;
}
}
2018-11-04 21:45:29 +00:00
break ;
2019-08-29 05:57:58 +00:00
case 461 : // Spawns an object on the map based on texture offsets
{
const mobjtype_t type = ( mobjtype_t ) ( sides [ line - > sidenum [ 0 ] ] . toptexture ) ;
2019-09-08 21:14:47 +00:00
mobj_t * mobj ;
2019-08-29 05:57:58 +00:00
fixed_t x , y , z ;
x = sides [ line - > sidenum [ 0 ] ] . textureoffset ;
y = sides [ line - > sidenum [ 0 ] ] . rowoffset ;
z = line - > frontsector - > floorheight ;
if ( line - > flags & ML_NOCLIMB ) // If noclimb is set, spawn randomly within a range
{
2019-08-30 03:56:15 +00:00
if ( line - > sidenum [ 1 ] ! = 0xffff ) // Make sure the linedef has a back side
{
x = P_RandomRange ( sides [ line - > sidenum [ 0 ] ] . textureoffset > > FRACBITS , sides [ line - > sidenum [ 1 ] ] . textureoffset > > FRACBITS ) < < FRACBITS ;
y = P_RandomRange ( sides [ line - > sidenum [ 0 ] ] . rowoffset > > FRACBITS , sides [ line - > sidenum [ 1 ] ] . rowoffset > > FRACBITS ) < < FRACBITS ;
z = P_RandomRange ( line - > frontsector - > floorheight > > FRACBITS , line - > frontsector - > ceilingheight > > FRACBITS ) < < FRACBITS ;
}
else
{
CONS_Alert ( CONS_WARNING , " Linedef Type %d - Spawn Object: Linedef is set for random range but has no back side. \n " , line - > special ) ;
break ;
}
2019-08-29 05:57:58 +00:00
}
2019-09-08 21:14:47 +00:00
mobj = P_SpawnMobj ( x , y , z , type ) ;
2019-08-30 03:56:15 +00:00
if ( mobj )
2020-04-13 15:10:25 +00:00
{
if ( line - > flags & ML_EFFECT1 )
mobj - > angle = R_PointToAngle2 ( line - > v1 - > x , line - > v1 - > y , line - > v2 - > x , line - > v2 - > y ) ;
2019-08-30 03:56:15 +00:00
CONS_Debug ( DBG_GAMELOGIC , " Linedef Type %d - Spawn Object: %d spawned at (%d, %d, %d) \n " , line - > special , mobj - > type , mobj - > x > > FRACBITS , mobj - > y > > FRACBITS , mobj - > z > > FRACBITS ) ; //TODO: Convert mobj->type to a string somehow.
2020-04-13 15:10:25 +00:00
}
2019-08-30 03:56:15 +00:00
else
CONS_Alert ( CONS_ERROR , " Linedef Type %d - Spawn Object: Object did not spawn! \n " , line - > special ) ;
2019-08-29 05:57:58 +00:00
}
break ;
2019-11-08 15:47:12 +00:00
case 462 : // Stop clock (and end level in record attack)
if ( G_PlatformGametype ( ) )
{
stoppedclock = true ;
CONS_Debug ( DBG_GAMELOGIC , " Clock stopped! \n " ) ;
if ( modeattacking )
{
UINT8 i ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( ! playeringame [ i ] )
continue ;
P_DoPlayerExit ( & players [ i ] ) ;
}
}
}
break ;
2020-04-12 13:16:04 +00:00
2020-01-11 23:40:57 +00:00
case 463 : // Dye object
{
INT32 color = sides [ line - > sidenum [ 0 ] ] . toptexture ;
2020-04-12 13:16:04 +00:00
2020-01-11 23:40:57 +00:00
if ( mo )
{
2020-05-24 00:29:07 +00:00
if ( color < 0 | | color > = numskincolors )
2020-01-11 23:40:57 +00:00
return ;
2020-04-12 13:16:04 +00:00
2020-01-11 23:40:57 +00:00
var1 = 0 ;
var2 = color ;
A_Dye ( mo ) ;
}
}
break ;
2020-04-12 13:16:04 +00:00
2020-05-03 13:22:13 +00:00
case 464 : // Trigger Egg Capsule
{
thinker_t * th ;
mobj_t * mo2 ;
// Find the center of the Eggtrap and release all the pretty animals!
// The chimps are my friends.. heeheeheheehehee..... - LouisJM
for ( th = thlist [ THINK_MOBJ ] . next ; th ! = & thlist [ THINK_MOBJ ] ; th = th - > next )
{
if ( th - > function . acp1 = = ( actionf_p1 ) P_RemoveThinkerDelayed )
continue ;
mo2 = ( mobj_t * ) th ;
if ( mo2 - > type ! = MT_EGGTRAP )
continue ;
if ( ! mo2 - > spawnpoint )
continue ;
2020-07-10 14:51:11 +00:00
if ( mo2 - > spawnpoint - > angle ! = tag )
2020-05-03 13:22:13 +00:00
continue ;
P_KillMobj ( mo2 , NULL , mo , 0 ) ;
}
if ( ! ( line - > flags & ML_NOCLIMB ) )
{
INT32 i ;
// Mark all players with the time to exit thingy!
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( ! playeringame [ i ] )
continue ;
P_DoPlayerExit ( & players [ i ] ) ;
}
}
}
break ;
2020-05-03 19:03:15 +00:00
case 465 : // Set linedef executor delay
{
INT32 linenum ;
if ( ! udmf )
break ;
2021-02-11 12:24:20 +00:00
TAG_ITER_LINES ( line - > args [ 0 ] , linenum )
2020-05-03 19:03:15 +00:00
{
if ( line - > args [ 2 ] )
lines [ linenum ] . executordelay + = line - > args [ 1 ] ;
else
lines [ linenum ] . executordelay = line - > args [ 1 ] ;
}
}
break ;
2021-04-19 21:20:34 +00:00
case 466 : // Set level failure state
{
if ( line - > flags & ML_NOCLIMB )
{
stagefailed = false ;
CONS_Debug ( DBG_GAMELOGIC , " Stage can be completed successfully! \n " ) ;
}
else
{
stagefailed = true ;
CONS_Debug ( DBG_GAMELOGIC , " Stage will end in failure... \n " ) ;
}
}
break ;
2014-03-15 16:59:03 +00:00
case 480 : // Polyobj_DoorSlide
case 481 : // Polyobj_DoorSwing
PolyDoor ( line ) ;
break ;
case 482 : // Polyobj_Move
case 483 : // Polyobj_OR_Move
PolyMove ( line ) ;
break ;
case 484 : // Polyobj_RotateRight
case 485 : // Polyobj_OR_RotateRight
case 486 : // Polyobj_RotateLeft
case 487 : // Polyobj_OR_RotateLeft
PolyRotate ( line ) ;
break ;
case 488 : // Polyobj_Waypoint
PolyWaypoint ( line ) ;
break ;
case 489 :
PolyInvisible ( line ) ;
break ;
case 490 :
PolyVisible ( line ) ;
break ;
case 491 :
PolyTranslucency ( line ) ;
break ;
2018-09-07 19:27:18 +00:00
case 492 :
PolyFade ( line ) ;
break ;
2014-03-15 16:59:03 +00:00
default :
break ;
}
}
//
// P_SetupSignExit
//
// Finds the exit sign in the current sector and
// sets its target to the player who passed the map.
//
void P_SetupSignExit ( player_t * player )
{
mobj_t * thing ;
msecnode_t * node = player - > mo - > subsector - > sector - > touching_thinglist ; // things touching this sector
thinker_t * think ;
INT32 numfound = 0 ;
2016-06-09 13:16:02 +00:00
for ( ; node ; node = node - > m_thinglist_next )
2014-03-15 16:59:03 +00:00
{
thing = node - > m_thing ;
if ( thing - > type ! = MT_SIGN )
continue ;
2019-12-05 04:36:25 +00:00
if ( ! numfound
& & ! ( player - > mo - > target & & player - > mo - > target - > type = = MT_SIGN )
2020-02-29 04:51:45 +00:00
& & ! ( ( gametyperules & GTR_FRIENDLY ) & & ( netgame | | multiplayer ) & & cv_exitmove . value ) )
2019-12-05 04:36:25 +00:00
P_SetTarget ( & player - > mo - > target , thing ) ;
2019-11-08 16:47:51 +00:00
2014-03-15 16:59:03 +00:00
if ( thing - > state ! = & states [ thing - > info - > spawnstate ] )
continue ;
P_SetTarget ( & thing - > target , player - > mo ) ;
2019-11-04 08:28:44 +00:00
P_SetObjectMomZ ( thing , 12 * FRACUNIT , false ) ;
2019-11-04 17:33:09 +00:00
P_SetMobjState ( thing , S_SIGNSPIN1 ) ;
2014-03-15 16:59:03 +00:00
if ( thing - > info - > seesound )
S_StartSound ( thing , thing - > info - > seesound ) ;
+ + numfound ;
}
if ( numfound )
return ;
// didn't find any signposts in the exit sector.
// spin all signposts in the level then.
2019-04-20 21:29:20 +00:00
for ( think = thlist [ THINK_MOBJ ] . next ; think ! = & thlist [ THINK_MOBJ ] ; think = think - > next )
2014-03-15 16:59:03 +00:00
{
2019-07-12 23:42:03 +00:00
if ( think - > function . acp1 = = ( actionf_p1 ) P_RemoveThinkerDelayed )
continue ;
2014-03-15 16:59:03 +00:00
thing = ( mobj_t * ) think ;
if ( thing - > type ! = MT_SIGN )
continue ;
2019-12-05 04:36:25 +00:00
if ( ! numfound
& & ! ( player - > mo - > target & & player - > mo - > target - > type = = MT_SIGN )
2020-02-29 04:51:45 +00:00
& & ! ( ( gametyperules & GTR_FRIENDLY ) & & ( netgame | | multiplayer ) & & cv_exitmove . value ) )
2019-12-05 04:36:25 +00:00
P_SetTarget ( & player - > mo - > target , thing ) ;
2019-11-08 16:47:51 +00:00
2014-03-15 16:59:03 +00:00
if ( thing - > state ! = & states [ thing - > info - > spawnstate ] )
continue ;
P_SetTarget ( & thing - > target , player - > mo ) ;
2019-11-04 08:28:44 +00:00
P_SetObjectMomZ ( thing , 12 * FRACUNIT , false ) ;
2019-11-04 17:33:09 +00:00
P_SetMobjState ( thing , S_SIGNSPIN1 ) ;
2014-03-15 16:59:03 +00:00
if ( thing - > info - > seesound )
S_StartSound ( thing , thing - > info - > seesound ) ;
+ + numfound ;
}
}
//
// P_IsFlagAtBase
//
// Checks to see if a flag is at its base.
//
2014-05-26 02:41:05 +00:00
boolean P_IsFlagAtBase ( mobjtype_t flag )
2014-03-15 16:59:03 +00:00
{
thinker_t * think ;
mobj_t * mo ;
2019-07-12 23:42:03 +00:00
INT32 specialnum = ( flag = = MT_REDFLAG ) ? 3 : 4 ;
2014-03-15 16:59:03 +00:00
2019-04-20 21:29:20 +00:00
for ( think = thlist [ THINK_MOBJ ] . next ; think ! = & thlist [ THINK_MOBJ ] ; think = think - > next )
2014-03-15 16:59:03 +00:00
{
2019-07-12 23:42:03 +00:00
if ( think - > function . acp1 = = ( actionf_p1 ) P_RemoveThinkerDelayed )
continue ;
2014-03-15 16:59:03 +00:00
mo = ( mobj_t * ) think ;
if ( mo - > type ! = flag )
continue ;
if ( GETSECSPECIAL ( mo - > subsector - > sector - > special , 4 ) = = specialnum )
return true ;
else if ( mo - > subsector - > sector - > ffloors ) // Check the 3D floors
{
ffloor_t * rover ;
for ( rover = mo - > subsector - > sector - > ffloors ; rover ; rover = rover - > next )
{
if ( ! ( rover - > flags & FF_EXISTS ) )
continue ;
if ( GETSECSPECIAL ( rover - > master - > frontsector - > special , 4 ) ! = specialnum )
continue ;
2019-07-12 23:42:03 +00:00
if ( ! ( mo - > z < = P_GetSpecialTopZ ( mo , sectors + rover - > secnum , mo - > subsector - > sector )
& & mo - > z > = P_GetSpecialBottomZ ( mo , sectors + rover - > secnum , mo - > subsector - > sector ) ) )
continue ;
return true ;
2014-03-15 16:59:03 +00:00
}
}
}
return false ;
}
//
// P_PlayerTouchingSectorSpecial
//
// Replaces the old player->specialsector.
// This allows a player to touch more than
// one sector at a time, if necessary.
//
// Returns a pointer to the first sector of
// the particular type that it finds.
// Returns NULL if it doesn't find it.
//
sector_t * P_PlayerTouchingSectorSpecial ( player_t * player , INT32 section , INT32 number )
{
msecnode_t * node ;
ffloor_t * rover ;
if ( ! player - > mo )
return NULL ;
// Check default case first
if ( GETSECSPECIAL ( player - > mo - > subsector - > sector - > special , section ) = = number )
return player - > mo - > subsector - > sector ;
// Hmm.. maybe there's a FOF that has it...
for ( rover = player - > mo - > subsector - > sector - > ffloors ; rover ; rover = rover - > next )
{
2016-05-31 20:31:29 +00:00
fixed_t topheight , bottomheight ;
2014-03-15 16:59:03 +00:00
if ( GETSECSPECIAL ( rover - > master - > frontsector - > special , section ) ! = number )
continue ;
if ( ! ( rover - > flags & FF_EXISTS ) )
continue ;
2016-05-31 20:31:29 +00:00
topheight = P_GetSpecialTopZ ( player - > mo , sectors + rover - > secnum , player - > mo - > subsector - > sector ) ;
bottomheight = P_GetSpecialBottomZ ( player - > mo , sectors + rover - > secnum , player - > mo - > subsector - > sector ) ;
2014-03-15 16:59:03 +00:00
// Check the 3D floor's type...
if ( rover - > flags & FF_BLOCKPLAYER )
{
2019-10-06 13:34:25 +00:00
boolean floorallowed = ( ( rover - > master - > frontsector - > flags & SF_FLIPSPECIAL_FLOOR ) & & ( ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ! ( player - > mo - > eflags & MFE_VERTICALFLIP ) ) & & ( player - > mo - > z = = topheight ) ) ;
boolean ceilingallowed = ( ( rover - > master - > frontsector - > flags & SF_FLIPSPECIAL_CEILING ) & & ( ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ( player - > mo - > eflags & MFE_VERTICALFLIP ) ) & & ( player - > mo - > z + player - > mo - > height = = bottomheight ) ) ;
2014-03-15 16:59:03 +00:00
// Thing must be on top of the floor to be affected...
2019-10-06 13:34:25 +00:00
if ( ! ( floorallowed | | ceilingallowed ) )
continue ;
2014-03-15 16:59:03 +00:00
}
else
{
// Water and DEATH FOG!!! heh
2016-05-31 20:31:29 +00:00
if ( player - > mo - > z > topheight | | ( player - > mo - > z + player - > mo - > height ) < bottomheight )
2014-03-15 16:59:03 +00:00
continue ;
}
// This FOF has the special we're looking for!
return rover - > master - > frontsector ;
}
2016-06-09 13:56:24 +00:00
for ( node = player - > mo - > touching_sectorlist ; node ; node = node - > m_sectorlist_next )
2014-03-15 16:59:03 +00:00
{
if ( GETSECSPECIAL ( node - > m_sector - > special , section ) = = number )
{
// This sector has the special we're looking for, but
// are we allowed to touch it?
if ( node - > m_sector = = player - > mo - > subsector - > sector
| | ( node - > m_sector - > flags & SF_TRIGGERSPECIAL_TOUCH ) )
return node - > m_sector ;
}
// Hmm.. maybe there's a FOF that has it...
for ( rover = node - > m_sector - > ffloors ; rover ; rover = rover - > next )
{
2016-05-31 20:31:29 +00:00
fixed_t topheight , bottomheight ;
2014-03-15 16:59:03 +00:00
if ( GETSECSPECIAL ( rover - > master - > frontsector - > special , section ) ! = number )
continue ;
if ( ! ( rover - > flags & FF_EXISTS ) )
continue ;
2016-05-31 20:31:29 +00:00
topheight = P_GetSpecialTopZ ( player - > mo , sectors + rover - > secnum , player - > mo - > subsector - > sector ) ;
bottomheight = P_GetSpecialBottomZ ( player - > mo , sectors + rover - > secnum , player - > mo - > subsector - > sector ) ;
2014-03-15 16:59:03 +00:00
// Check the 3D floor's type...
if ( rover - > flags & FF_BLOCKPLAYER )
{
2019-10-06 13:34:25 +00:00
boolean floorallowed = ( ( rover - > master - > frontsector - > flags & SF_FLIPSPECIAL_FLOOR ) & & ( ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ! ( player - > mo - > eflags & MFE_VERTICALFLIP ) ) & & ( player - > mo - > z = = topheight ) ) ;
boolean ceilingallowed = ( ( rover - > master - > frontsector - > flags & SF_FLIPSPECIAL_CEILING ) & & ( ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ( player - > mo - > eflags & MFE_VERTICALFLIP ) ) & & ( player - > mo - > z + player - > mo - > height = = bottomheight ) ) ;
2014-03-15 16:59:03 +00:00
// Thing must be on top of the floor to be affected...
2019-10-06 13:34:25 +00:00
if ( ! ( floorallowed | | ceilingallowed ) )
continue ;
2014-03-15 16:59:03 +00:00
}
else
{
// Water and DEATH FOG!!! heh
2016-05-31 20:31:29 +00:00
if ( player - > mo - > z > topheight | | ( player - > mo - > z + player - > mo - > height ) < bottomheight )
2014-03-15 16:59:03 +00:00
continue ;
}
// This FOF has the special we're looking for, but are we allowed to touch it?
if ( node - > m_sector = = player - > mo - > subsector - > sector
| | ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_TOUCH ) )
return rover - > master - > frontsector ;
}
}
return NULL ;
}
//
2014-08-27 03:56:30 +00:00
// P_ThingIsOnThe3DFloor
2014-03-15 16:59:03 +00:00
//
2014-08-27 03:56:30 +00:00
// This checks whether the mobj is on/in the FOF we want it to be at
// Needed for the "All players" trigger sector specials only
2014-03-15 16:59:03 +00:00
//
2014-08-27 03:56:30 +00:00
static boolean P_ThingIsOnThe3DFloor ( mobj_t * mo , sector_t * sector , sector_t * targetsec )
{
ffloor_t * rover ;
2015-05-24 17:53:30 +00:00
fixed_t top , bottom ;
2014-08-27 03:56:30 +00:00
if ( ! mo - > player ) // should NEVER happen
return false ;
if ( ! targetsec - > ffloors ) // also should NEVER happen
return false ;
for ( rover = targetsec - > ffloors ; rover ; rover = rover - > next )
{
if ( rover - > master - > frontsector ! = sector )
continue ;
// we're assuming the FOF existed when the first player touched it
//if (!(rover->flags & FF_EXISTS))
// return false;
2015-05-24 17:53:30 +00:00
top = P_GetSpecialTopZ ( mo , sector , targetsec ) ;
bottom = P_GetSpecialBottomZ ( mo , sector , targetsec ) ;
2014-08-27 03:56:30 +00:00
// Check the 3D floor's type...
if ( rover - > flags & FF_BLOCKPLAYER )
{
2019-10-06 13:34:25 +00:00
boolean floorallowed = ( ( rover - > master - > frontsector - > flags & SF_FLIPSPECIAL_FLOOR ) & & ( ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ! ( mo - > eflags & MFE_VERTICALFLIP ) ) & & ( mo - > z = = top ) ) ;
boolean ceilingallowed = ( ( rover - > master - > frontsector - > flags & SF_FLIPSPECIAL_CEILING ) & & ( ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ( mo - > eflags & MFE_VERTICALFLIP ) ) & & ( mo - > z + mo - > height = = bottom ) ) ;
2014-08-27 03:56:30 +00:00
// Thing must be on top of the floor to be affected...
2019-10-06 13:34:25 +00:00
if ( ! ( floorallowed | | ceilingallowed ) )
continue ;
2014-08-27 03:56:30 +00:00
}
else
{
// Water and intangible FOFs
2015-05-24 17:53:30 +00:00
if ( mo - > z > top | | ( mo - > z + mo - > height ) < bottom )
2014-08-27 03:56:30 +00:00
return false ;
}
return true ;
}
return false ;
}
//
// P_MobjReadyToTrigger
//
// Is player standing on the sector's "ground"?
//
2017-06-07 17:24:06 +00:00
static boolean P_MobjReadyToTrigger ( mobj_t * mo , sector_t * sec )
2014-08-27 03:56:30 +00:00
{
2019-10-06 13:34:25 +00:00
boolean floorallowed = ( ( sec - > flags & SF_FLIPSPECIAL_FLOOR ) & & ( ( sec - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ! ( mo - > eflags & MFE_VERTICALFLIP ) ) & & ( mo - > z = = P_GetSpecialBottomZ ( mo , sec , sec ) ) ) ;
boolean ceilingallowed = ( ( sec - > flags & SF_FLIPSPECIAL_CEILING ) & & ( ( sec - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ( mo - > eflags & MFE_VERTICALFLIP ) ) & & ( mo - > z + mo - > height = = P_GetSpecialTopZ ( mo , sec , sec ) ) ) ;
// Thing must be on top of the floor to be affected...
return ( floorallowed | | ceilingallowed ) ;
2014-08-27 03:56:30 +00:00
}
2014-03-15 16:59:03 +00:00
/** Applies a sector special to a player.
*
* \ param player Player in the sector .
* \ param sector Sector with the special .
* \ param roversector If ! NULL , sector is actually an FOF ; otherwise , sector
* is being physically contacted by the player .
* \ todo Split up into multiple functions .
* \ sa P_PlayerInSpecialSector , P_PlayerOnSpecial3DFloor
*/
void P_ProcessSpecialSector ( player_t * player , sector_t * sector , sector_t * roversector )
{
INT32 i = 0 ;
INT32 section1 , section2 , section3 , section4 ;
INT32 special ;
2020-04-17 16:15:25 +00:00
mtag_t sectag = Tag_FGet ( & sector - > tags ) ;
2014-03-15 16:59:03 +00:00
section1 = GETSECSPECIAL ( sector - > special , 1 ) ;
section2 = GETSECSPECIAL ( sector - > special , 2 ) ;
section3 = GETSECSPECIAL ( sector - > special , 3 ) ;
section4 = GETSECSPECIAL ( sector - > special , 4 ) ;
// Ignore spectators
if ( player - > spectator )
return ;
// Ignore dead players.
// If this strange phenomenon could be potentially used in levels,
// TODO: modify this to accommodate for it.
2020-09-20 02:19:53 +00:00
if ( player - > playerstate ! = PST_LIVE )
2014-03-15 16:59:03 +00:00
return ;
// Conveyor stuff
if ( section3 = = 2 | | section3 = = 4 )
player - > onconveyor = section3 ;
special = section1 ;
// Process Section 1
switch ( special )
{
case 1 : // Damage (Generic)
2014-08-27 03:56:30 +00:00
if ( roversector | | P_MobjReadyToTrigger ( player - > mo , sector ) )
2015-02-13 16:15:58 +00:00
P_DamageMobj ( player - > mo , NULL , NULL , 1 , 0 ) ;
2014-03-15 16:59:03 +00:00
break ;
case 2 : // Damage (Water)
2017-03-14 18:11:17 +00:00
if ( ( roversector | | P_MobjReadyToTrigger ( player - > mo , sector ) ) & & ( player - > powers [ pw_underwater ] | | player - > powers [ pw_carry ] = = CR_NIGHTSMODE ) )
2015-02-13 16:15:58 +00:00
P_DamageMobj ( player - > mo , NULL , NULL , 1 , DMG_WATER ) ;
2014-03-15 16:59:03 +00:00
break ;
case 3 : // Damage (Fire)
2015-02-13 16:15:58 +00:00
if ( roversector | | P_MobjReadyToTrigger ( player - > mo , sector ) )
P_DamageMobj ( player - > mo , NULL , NULL , 1 , DMG_FIRE ) ;
2014-03-15 16:59:03 +00:00
break ;
case 4 : // Damage (Electrical)
2015-02-13 16:15:58 +00:00
if ( roversector | | P_MobjReadyToTrigger ( player - > mo , sector ) )
P_DamageMobj ( player - > mo , NULL , NULL , 1 , DMG_ELECTRIC ) ;
2014-03-15 16:59:03 +00:00
break ;
case 5 : // Spikes
2020-04-17 20:54:35 +00:00
if ( roversector | | P_MobjReadyToTrigger ( player - > mo , sector ) )
P_DamageMobj ( player - > mo , NULL , NULL , 1 , DMG_SPIKE ) ;
2014-03-15 16:59:03 +00:00
break ;
case 6 : // Death Pit (Camera Mod)
case 7 : // Death Pit (No Camera Mod)
2014-08-27 03:56:30 +00:00
if ( roversector | | P_MobjReadyToTrigger ( player - > mo , sector ) )
2020-01-22 02:20:27 +00:00
{
if ( player - > quittime )
G_MovePlayerToSpawnOrStarpost ( player - players ) ;
else
P_DamageMobj ( player - > mo , NULL , NULL , 1 , DMG_DEATHPIT ) ;
}
2014-03-15 16:59:03 +00:00
break ;
case 8 : // Instant Kill
2020-01-22 02:20:27 +00:00
if ( player - > quittime )
G_MovePlayerToSpawnOrStarpost ( player - players ) ;
else
P_DamageMobj ( player - > mo , NULL , NULL , 1 , DMG_INSTAKILL ) ;
2014-03-15 16:59:03 +00:00
break ;
case 9 : // Ring Drainer (Floor Touch)
case 10 : // Ring Drainer (No Floor Touch)
2015-08-15 20:07:16 +00:00
if ( leveltime % ( TICRATE / 2 ) = = 0 & & player - > rings > 0 )
2014-03-15 16:59:03 +00:00
{
2015-08-15 20:07:16 +00:00
player - > rings - - ;
2020-12-11 18:34:30 +00:00
S_StartSound ( player - > mo , sfx_antiri ) ;
2014-03-15 16:59:03 +00:00
}
break ;
2017-08-06 21:10:29 +00:00
case 11 : // Special Stage Damage
if ( player - > exiting | | player - > bot ) // Don't do anything for bots or players who have just finished
2014-03-15 16:59:03 +00:00
break ;
2018-06-03 21:41:54 +00:00
if ( ! ( player - > powers [ pw_shield ] | | player - > spheres > 0 ) ) // Don't do anything if no shield or spheres anyway
2014-03-15 16:59:03 +00:00
break ;
2017-08-06 21:10:29 +00:00
P_SpecialStageDamage ( player , NULL , NULL ) ;
2014-03-15 16:59:03 +00:00
break ;
case 12 : // Space Countdown
2016-10-13 14:13:33 +00:00
if ( ! ( player - > powers [ pw_shield ] & SH_PROTECTWATER ) & & ! player - > powers [ pw_spacetime ] )
2014-03-15 16:59:03 +00:00
player - > powers [ pw_spacetime ] = spacetimetics + 1 ;
break ;
2014-08-04 03:49:33 +00:00
case 13 : // Ramp Sector (Increase step-up/down)
2014-03-15 16:59:03 +00:00
case 14 : // Non-Ramp Sector (Don't step-down)
case 15 : // Bouncy Sector (FOF Control Only)
break ;
}
special = section2 ;
// Process Section 2
switch ( special )
{
case 1 : // Trigger Linedef Exec (Pushable Objects)
break ;
case 2 : // Linedef executor requires all players present+doesn't require touching floor
case 3 : // Linedef executor requires all players present
2014-08-27 03:56:30 +00:00
/// \todo check continues for proper splitscreen support?
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
2019-11-20 14:34:19 +00:00
{
if ( ! playeringame [ i ] )
continue ;
if ( ! players [ i ] . mo )
continue ;
if ( players [ i ] . spectator )
continue ;
if ( players [ i ] . bot )
continue ;
2020-02-29 03:57:22 +00:00
if ( G_CoopGametype ( ) & & players [ i ] . lives < = 0 )
2019-11-20 14:34:19 +00:00
continue ;
if ( roversector )
2014-08-27 03:56:30 +00:00
{
2019-11-20 14:34:19 +00:00
if ( sector - > flags & SF_TRIGGERSPECIAL_TOUCH )
2014-08-27 03:56:30 +00:00
{
2019-11-20 14:34:19 +00:00
msecnode_t * node ;
for ( node = players [ i ] . mo - > touching_sectorlist ; node ; node = node - > m_sectorlist_next )
2016-10-31 14:03:53 +00:00
{
2019-11-20 14:34:19 +00:00
if ( P_ThingIsOnThe3DFloor ( players [ i ] . mo , sector , node - > m_sector ) )
break ;
2016-10-31 14:03:53 +00:00
}
2019-11-20 14:34:19 +00:00
if ( ! node )
2014-08-27 03:56:30 +00:00
goto DoneSection2 ;
}
2019-11-20 14:34:19 +00:00
else if ( players [ i ] . mo - > subsector & & ! P_ThingIsOnThe3DFloor ( players [ i ] . mo , sector , players [ i ] . mo - > subsector - > sector ) ) // this function handles basically everything for us lmao
goto DoneSection2 ;
}
else
{
if ( players [ i ] . mo - > subsector - > sector = = sector )
;
else if ( sector - > flags & SF_TRIGGERSPECIAL_TOUCH )
2014-08-27 03:56:30 +00:00
{
2019-11-20 14:34:19 +00:00
msecnode_t * node ;
for ( node = players [ i ] . mo - > touching_sectorlist ; node ; node = node - > m_sectorlist_next )
2016-10-31 14:03:53 +00:00
{
2019-11-20 14:34:19 +00:00
if ( node - > m_sector = = sector )
break ;
2016-10-31 14:03:53 +00:00
}
2019-11-20 14:34:19 +00:00
if ( ! node )
2014-08-27 03:56:30 +00:00
goto DoneSection2 ;
}
2019-11-20 14:34:19 +00:00
else
goto DoneSection2 ;
if ( special = = 3 & & ! P_MobjReadyToTrigger ( players [ i ] . mo , sector ) )
goto DoneSection2 ;
2014-08-27 03:56:30 +00:00
}
2019-11-20 14:34:19 +00:00
}
2017-09-28 13:39:47 +00:00
/* FALLTHRU */
2014-03-15 16:59:03 +00:00
case 4 : // Linedef executor that doesn't require touching floor
case 5 : // Linedef executor
case 6 : // Linedef executor (7 Emeralds)
case 7 : // Linedef executor (NiGHTS Mare)
if ( ! player - > bot )
2020-04-17 16:15:25 +00:00
P_LinedefExecute ( sectag , player - > mo , sector ) ;
2014-03-15 16:59:03 +00:00
break ;
case 8 : // Tells pushable things to check FOFs
break ;
case 9 : // Egg trap capsule
{
thinker_t * th ;
mobj_t * mo2 ;
line_t junk ;
if ( player - > bot | | sector - > ceilingdata | | sector - > floordata )
return ;
// Find the center of the Eggtrap and release all the pretty animals!
// The chimps are my friends.. heeheeheheehehee..... - LouisJM
2019-04-20 21:29:20 +00:00
for ( th = thlist [ THINK_MOBJ ] . next ; th ! = & thlist [ THINK_MOBJ ] ; th = th - > next )
2014-03-15 16:59:03 +00:00
{
2019-07-12 23:42:03 +00:00
if ( th - > function . acp1 = = ( actionf_p1 ) P_RemoveThinkerDelayed )
continue ;
2014-03-15 16:59:03 +00:00
mo2 = ( mobj_t * ) th ;
2019-07-12 23:42:03 +00:00
if ( mo2 - > type ! = MT_EGGTRAP )
continue ;
P_KillMobj ( mo2 , NULL , player - > mo , 0 ) ;
2014-03-15 16:59:03 +00:00
}
// clear the special so you can't push the button twice.
sector - > special = 0 ;
2020-12-03 20:50:10 +00:00
// Initialize my junk
junk . tags . tags = NULL ;
junk . tags . count = 0 ;
2014-03-15 16:59:03 +00:00
// Move the button down
2020-12-03 20:50:10 +00:00
Tag_FSet ( & junk . tags , LE_CAPSULE0 ) ;
2014-03-15 16:59:03 +00:00
EV_DoElevator ( & junk , elevateDown , false ) ;
// Open the top FOF
2020-12-03 20:50:10 +00:00
Tag_FSet ( & junk . tags , LE_CAPSULE1 ) ;
2014-03-15 16:59:03 +00:00
EV_DoFloor ( & junk , raiseFloorToNearestFast ) ;
// Open the bottom FOF
2020-12-03 20:50:10 +00:00
Tag_FSet ( & junk . tags , LE_CAPSULE2 ) ;
2014-03-15 16:59:03 +00:00
EV_DoCeiling ( & junk , lowerToLowestFast ) ;
// Mark all players with the time to exit thingy!
for ( i = 0 ; i < MAXPLAYERS ; i + + )
2019-11-08 15:47:12 +00:00
{
if ( ! playeringame [ i ] )
continue ;
2014-03-15 16:59:03 +00:00
P_DoPlayerExit ( & players [ i ] ) ;
2019-11-08 15:47:12 +00:00
}
2014-03-15 16:59:03 +00:00
break ;
}
case 10 : // Special Stage Time/Rings
case 11 : // Custom Gravity
break ;
case 12 : // Lua sector special
break ;
}
DoneSection2 :
special = section3 ;
// Process Section 3
switch ( special )
{
2016-06-04 23:23:20 +00:00
case 1 : // Unused
2014-03-15 16:59:03 +00:00
case 2 : // Wind/Current
2016-06-04 23:23:20 +00:00
case 3 : // Unused
2014-03-15 16:59:03 +00:00
case 4 : // Conveyor Belt
break ;
2016-06-04 23:23:20 +00:00
case 5 : // Speed pad
2014-03-15 16:59:03 +00:00
if ( player - > powers [ pw_flashing ] ! = 0 & & player - > powers [ pw_flashing ] < TICRATE / 2 )
break ;
2020-04-17 16:15:25 +00:00
i = Tag_FindLineSpecial ( 4 , sectag ) ;
2014-03-15 16:59:03 +00:00
if ( i ! = - 1 )
{
angle_t lineangle ;
fixed_t linespeed ;
2016-07-09 16:36:24 +00:00
fixed_t sfxnum ;
2014-03-15 16:59:03 +00:00
lineangle = R_PointToAngle2 ( lines [ i ] . v1 - > x , lines [ i ] . v1 - > y , lines [ i ] . v2 - > x , lines [ i ] . v2 - > y ) ;
2016-06-04 23:23:20 +00:00
linespeed = sides [ lines [ i ] . sidenum [ 0 ] ] . textureoffset ;
if ( linespeed = = 0 )
{
2020-04-17 16:15:25 +00:00
CONS_Debug ( DBG_GAMELOGIC , " ERROR: Speed pad (tag %d) at zero speed. \n " , sectag ) ;
2016-06-04 23:23:20 +00:00
break ;
}
2014-03-15 16:59:03 +00:00
2019-06-19 23:28:45 +00:00
player - > mo - > angle = player - > drawangle = lineangle ;
2014-03-15 16:59:03 +00:00
2019-12-30 21:05:24 +00:00
if ( ! demoplayback | | P_ControlStyle ( player ) = = CS_LMAOGALOG )
2020-05-28 09:03:35 +00:00
P_SetPlayerAngle ( player , player - > mo - > angle ) ;
2014-03-15 16:59:03 +00:00
if ( ! ( lines [ i ] . flags & ML_EFFECT4 ) )
{
P_UnsetThingPosition ( player - > mo ) ;
if ( roversector ) // make FOF speed pads work
{
player - > mo - > x = roversector - > soundorg . x ;
player - > mo - > y = roversector - > soundorg . y ;
}
else
{
player - > mo - > x = sector - > soundorg . x ;
player - > mo - > y = sector - > soundorg . y ;
}
P_SetThingPosition ( player - > mo ) ;
}
P_InstaThrust ( player - > mo , player - > mo - > angle , linespeed ) ;
2020-11-18 17:49:17 +00:00
if ( lines [ i ] . flags & ML_EFFECT5 ) // Roll!
2014-03-15 16:59:03 +00:00
{
if ( ! ( player - > pflags & PF_SPINNING ) )
player - > pflags | = PF_SPINNING ;
2017-03-11 02:09:01 +00:00
P_SetPlayerMobjState ( player - > mo , S_PLAY_ROLL ) ;
2014-03-15 16:59:03 +00:00
}
player - > powers [ pw_flashing ] = TICRATE / 3 ;
2016-06-04 23:23:20 +00:00
2016-07-09 16:36:24 +00:00
sfxnum = sides [ lines [ i ] . sidenum [ 0 ] ] . toptexture ;
2016-06-04 23:23:20 +00:00
if ( ! sfxnum )
sfxnum = sfx_spdpad ;
S_StartSound ( player - > mo , sfxnum ) ;
2014-03-15 16:59:03 +00:00
}
break ;
2016-06-04 23:23:20 +00:00
case 6 : // Unused
case 7 : // Unused
case 8 : // Unused
case 9 : // Unused
case 10 : // Unused
case 11 : // Unused
case 12 : // Unused
case 13 : // Unused
case 14 : // Unused
case 15 : // Unused
2014-03-15 16:59:03 +00:00
break ;
}
special = section4 ;
// Process Section 4
switch ( special )
{
case 1 : // Starpost Activator
{
mobj_t * post = P_GetObjectTypeInSectorNum ( MT_STARPOST , sector - sectors ) ;
if ( ! post )
break ;
2020-01-25 05:54:54 +00:00
P_TouchStarPost ( post , player , false ) ;
2014-03-15 16:59:03 +00:00
break ;
}
case 2 : // Special stage GOAL sector / Exit Sector / CTF Flag Return
2019-12-18 19:52:05 +00:00
if ( player - > bot | | ! ( gametyperules & GTR_ALLOWEXIT ) )
2014-03-15 16:59:03 +00:00
break ;
2018-06-03 21:41:54 +00:00
if ( ! ( maptol & TOL_NIGHTS ) & & G_IsSpecialStage ( gamemap ) & & player - > nightstime > 6 )
2019-08-02 19:02:55 +00:00
{
2018-06-03 21:41:54 +00:00
player - > nightstime = 6 ; // Just let P_Ticker take care of the rest.
2019-08-02 19:02:55 +00:00
return ;
}
2014-03-15 16:59:03 +00:00
// Exit (for FOF exits; others are handled in P_PlayerThink in p_user.c)
{
INT32 lineindex ;
2019-11-15 14:35:28 +00:00
P_DoPlayerFinish ( player ) ;
2014-03-15 16:59:03 +00:00
P_SetupSignExit ( player ) ;
// important: use sector->tag on next line instead of player->mo->subsector->tag
// this part is different from in P_PlayerThink, this is what was causing
// FOF custom exits not to work.
2020-04-17 16:15:25 +00:00
lineindex = Tag_FindLineSpecial ( 2 , sectag ) ;
2014-03-15 16:59:03 +00:00
2020-02-29 03:57:22 +00:00
if ( G_CoopGametype ( ) & & lineindex ! = - 1 ) // Custom exit!
2014-03-15 16:59:03 +00:00
{
// Special goodies with the block monsters flag depending on emeralds collected
if ( ( lines [ lineindex ] . flags & ML_BLOCKMONSTERS ) & & ALL7EMERALDS ( emeralds ) )
nextmapoverride = ( INT16 ) ( lines [ lineindex ] . frontsector - > ceilingheight > > FRACBITS ) ;
else
nextmapoverride = ( INT16 ) ( lines [ lineindex ] . frontsector - > floorheight > > FRACBITS ) ;
if ( lines [ lineindex ] . flags & ML_NOCLIMB )
2019-07-29 13:55:36 +00:00
skipstats = 1 ;
2014-03-15 16:59:03 +00:00
}
}
break ;
case 3 : // Red Team's Base
2019-12-18 14:43:50 +00:00
if ( ( gametyperules & GTR_TEAMFLAGS ) & & P_IsObjectOnGround ( player - > mo ) )
2014-03-15 16:59:03 +00:00
{
if ( player - > ctfteam = = 1 & & ( player - > gotflag & GF_BLUEFLAG ) )
{
mobj_t * mo ;
// Make sure the red team still has their own
// flag at their base so they can score.
if ( ! P_IsFlagAtBase ( MT_REDFLAG ) )
break ;
2018-03-26 22:53:09 +00:00
HU_SetCEchoFlags ( V_AUTOFADEOUT | V_ALLOWLOWERCASE ) ;
2014-03-15 16:59:03 +00:00
HU_SetCEchoDuration ( 5 ) ;
2020-12-08 21:56:45 +00:00
HU_DoCEcho ( va ( M_GetText ( " \205 %s \200 \\ CAPTURED THE \204 BLUE FLAG \200 . \\ \\ \\ \\ " ) , player_names [ player - players ] ) ) ;
2014-03-15 16:59:03 +00:00
2016-04-07 02:55:58 +00:00
if ( splitscreen | | players [ consoleplayer ] . ctfteam = = 1 )
2014-03-15 16:59:03 +00:00
S_StartSound ( NULL , sfx_flgcap ) ;
else if ( players [ consoleplayer ] . ctfteam = = 2 )
S_StartSound ( NULL , sfx_lose ) ;
mo = P_SpawnMobj ( player - > mo - > x , player - > mo - > y , player - > mo - > z , MT_BLUEFLAG ) ;
player - > gotflag & = ~ GF_BLUEFLAG ;
mo - > flags & = ~ MF_SPECIAL ;
mo - > fuse = TICRATE ;
mo - > spawnpoint = bflagpoint ;
mo - > flags2 | = MF2_JUSTATTACKED ;
redscore + = 1 ;
P_AddPlayerScore ( player , 250 ) ;
}
}
break ;
case 4 : // Blue Team's Base
2019-12-18 14:43:50 +00:00
if ( ( gametyperules & GTR_TEAMFLAGS ) & & P_IsObjectOnGround ( player - > mo ) )
2014-03-15 16:59:03 +00:00
{
if ( player - > ctfteam = = 2 & & ( player - > gotflag & GF_REDFLAG ) )
{
mobj_t * mo ;
// Make sure the blue team still has their own
// flag at their base so they can score.
if ( ! P_IsFlagAtBase ( MT_BLUEFLAG ) )
break ;
2018-03-26 22:53:09 +00:00
HU_SetCEchoFlags ( V_AUTOFADEOUT | V_ALLOWLOWERCASE ) ;
2014-03-15 16:59:03 +00:00
HU_SetCEchoDuration ( 5 ) ;
2020-12-08 21:56:45 +00:00
HU_DoCEcho ( va ( M_GetText ( " \204 %s \200 \\ CAPTURED THE \205 RED FLAG \200 . \\ \\ \\ \\ " ) , player_names [ player - players ] ) ) ;
2014-03-15 16:59:03 +00:00
2016-04-07 02:55:58 +00:00
if ( splitscreen | | players [ consoleplayer ] . ctfteam = = 2 )
2014-03-15 16:59:03 +00:00
S_StartSound ( NULL , sfx_flgcap ) ;
else if ( players [ consoleplayer ] . ctfteam = = 1 )
S_StartSound ( NULL , sfx_lose ) ;
mo = P_SpawnMobj ( player - > mo - > x , player - > mo - > y , player - > mo - > z , MT_REDFLAG ) ;
player - > gotflag & = ~ GF_REDFLAG ;
mo - > flags & = ~ MF_SPECIAL ;
mo - > fuse = TICRATE ;
mo - > spawnpoint = rflagpoint ;
mo - > flags2 | = MF2_JUSTATTACKED ;
bluescore + = 1 ;
P_AddPlayerScore ( player , 250 ) ;
}
}
break ;
case 5 : // Fan sector
player - > mo - > momz + = mobjinfo [ MT_FAN ] . mass / 4 ;
if ( player - > mo - > momz > mobjinfo [ MT_FAN ] . mass )
player - > mo - > momz = mobjinfo [ MT_FAN ] . mass ;
P_ResetPlayer ( player ) ;
if ( player - > panim ! = PA_FALL )
2015-01-22 15:23:45 +00:00
P_SetPlayerMobjState ( player - > mo , S_PLAY_FALL ) ;
2014-03-15 16:59:03 +00:00
break ;
case 6 : // Super Sonic transformer
if ( player - > mo - > health > 0 & & ! player - > bot & & ( player - > charflags & SF_SUPER ) & & ! player - > powers [ pw_super ] & & ALL7EMERALDS ( emeralds ) )
P_DoSuperTransformation ( player , true ) ;
break ;
case 7 : // Make player spin
2020-11-18 18:27:09 +00:00
if ( ! ( player - > pflags & PF_SPINNING ) )
2014-03-15 16:59:03 +00:00
{
player - > pflags | = PF_SPINNING ;
2017-03-11 02:09:01 +00:00
P_SetPlayerMobjState ( player - > mo , S_PLAY_ROLL ) ;
2014-03-15 16:59:03 +00:00
S_StartAttackSound ( player - > mo , sfx_spin ) ;
if ( abs ( player - > rmomx ) < FixedMul ( 5 * FRACUNIT , player - > mo - > scale )
& & abs ( player - > rmomy ) < FixedMul ( 5 * FRACUNIT , player - > mo - > scale ) )
P_InstaThrust ( player - > mo , player - > mo - > angle , FixedMul ( 10 * FRACUNIT , player - > mo - > scale ) ) ;
}
break ;
case 8 : // Zoom Tube Start
{
INT32 sequence ;
fixed_t speed ;
INT32 lineindex ;
mobj_t * waypoint = NULL ;
angle_t an ;
2016-09-23 22:48:48 +00:00
if ( player - > mo - > tracer & & player - > mo - > tracer - > type = = MT_TUBEWAYPOINT & & player - > powers [ pw_carry ] = = CR_ZOOMTUBE )
2014-03-15 16:59:03 +00:00
break ;
2020-06-18 21:09:29 +00:00
if ( player - > powers [ pw_ignorelatch ] & ( 1 < < 15 ) )
break ;
2014-03-15 16:59:03 +00:00
// Find line #3 tagged to this sector
2020-04-17 16:15:25 +00:00
lineindex = Tag_FindLineSpecial ( 3 , sectag ) ;
2014-03-15 16:59:03 +00:00
if ( lineindex = = - 1 )
{
CONS_Debug ( DBG_GAMELOGIC , " ERROR: Sector special %d missing line special #3. \n " , sector - > special ) ;
break ;
}
// Grab speed and sequence values
2016-06-03 00:30:07 +00:00
speed = abs ( sides [ lines [ lineindex ] . sidenum [ 0 ] ] . textureoffset ) / 8 ;
sequence = abs ( sides [ lines [ lineindex ] . sidenum [ 0 ] ] . rowoffset ) > > FRACBITS ;
if ( speed = = 0 )
{
CONS_Debug ( DBG_GAMELOGIC , " ERROR: Waypoint sequence %d at zero speed. \n " , sequence ) ;
break ;
}
2014-03-15 16:59:03 +00:00
2020-05-12 21:50:30 +00:00
waypoint = P_GetFirstWaypoint ( sequence ) ;
2014-03-15 16:59:03 +00:00
if ( ! waypoint )
{
CONS_Debug ( DBG_GAMELOGIC , " ERROR: FIRST WAYPOINT IN SEQUENCE %d NOT FOUND. \n " , sequence ) ;
break ;
}
else
{
CONS_Debug ( DBG_GAMELOGIC , " Waypoint %d found in sequence %d - speed = %d \n " , waypoint - > health , sequence , speed ) ;
}
an = R_PointToAngle2 ( player - > mo - > x , player - > mo - > y , waypoint - > x , waypoint - > y ) - player - > mo - > angle ;
if ( an > ANGLE_90 & & an < ANGLE_270 & & ! ( lines [ lineindex ] . flags & ML_EFFECT4 ) )
break ; // behind back
P_SetTarget ( & player - > mo - > tracer , waypoint ) ;
2016-09-23 22:48:48 +00:00
player - > powers [ pw_carry ] = CR_ZOOMTUBE ;
2014-03-15 16:59:03 +00:00
player - > speed = speed ;
player - > pflags | = PF_SPINNING ;
2017-09-15 19:34:46 +00:00
player - > pflags & = ~ ( PF_JUMPED | PF_NOJUMPDAMAGE | PF_GLIDING | PF_BOUNCING | PF_SLIDING | PF_CANCARRY ) ;
2014-03-15 16:59:03 +00:00
player - > climbing = 0 ;
2017-03-11 02:09:01 +00:00
if ( player - > mo - > state - states ! = S_PLAY_ROLL )
2014-03-15 16:59:03 +00:00
{
2017-03-11 02:09:01 +00:00
P_SetPlayerMobjState ( player - > mo , S_PLAY_ROLL ) ;
2014-03-15 16:59:03 +00:00
S_StartSound ( player - > mo , sfx_spin ) ;
}
}
break ;
case 9 : // Zoom Tube End
{
INT32 sequence ;
fixed_t speed ;
INT32 lineindex ;
mobj_t * waypoint = NULL ;
angle_t an ;
2016-09-23 22:48:48 +00:00
if ( player - > mo - > tracer & & player - > mo - > tracer - > type = = MT_TUBEWAYPOINT & & player - > powers [ pw_carry ] = = CR_ZOOMTUBE )
2014-03-15 16:59:03 +00:00
break ;
2020-06-18 21:09:29 +00:00
if ( player - > powers [ pw_ignorelatch ] & ( 1 < < 15 ) )
break ;
2014-03-15 16:59:03 +00:00
// Find line #3 tagged to this sector
2020-04-17 16:15:25 +00:00
lineindex = Tag_FindLineSpecial ( 3 , sectag ) ;
2014-03-15 16:59:03 +00:00
if ( lineindex = = - 1 )
{
CONS_Debug ( DBG_GAMELOGIC , " ERROR: Sector special %d missing line special #3. \n " , sector - > special ) ;
break ;
}
// Grab speed and sequence values
2016-06-03 00:30:07 +00:00
speed = - abs ( sides [ lines [ lineindex ] . sidenum [ 0 ] ] . textureoffset ) / 8 ; // Negative means reverse
sequence = abs ( sides [ lines [ lineindex ] . sidenum [ 0 ] ] . rowoffset ) > > FRACBITS ;
if ( speed = = 0 )
{
CONS_Debug ( DBG_GAMELOGIC , " ERROR: Waypoint sequence %d at zero speed. \n " , sequence ) ;
break ;
}
2014-03-15 16:59:03 +00:00
2020-05-12 21:50:30 +00:00
waypoint = P_GetLastWaypoint ( sequence ) ;
2014-03-15 16:59:03 +00:00
if ( ! waypoint )
{
CONS_Debug ( DBG_GAMELOGIC , " ERROR: LAST WAYPOINT IN SEQUENCE %d NOT FOUND. \n " , sequence ) ;
break ;
}
else
{
CONS_Debug ( DBG_GAMELOGIC , " Waypoint %d found in sequence %d - speed = %d \n " , waypoint - > health , sequence , speed ) ;
}
an = R_PointToAngle2 ( player - > mo - > x , player - > mo - > y , waypoint - > x , waypoint - > y ) - player - > mo - > angle ;
if ( an > ANGLE_90 & & an < ANGLE_270 & & ! ( lines [ lineindex ] . flags & ML_EFFECT4 ) )
break ; // behind back
P_SetTarget ( & player - > mo - > tracer , waypoint ) ;
2016-09-23 22:48:48 +00:00
player - > powers [ pw_carry ] = CR_ZOOMTUBE ;
2014-03-15 16:59:03 +00:00
player - > speed = speed ;
player - > pflags | = PF_SPINNING ;
2017-09-15 19:34:46 +00:00
player - > pflags & = ~ ( PF_JUMPED | PF_NOJUMPDAMAGE | PF_GLIDING | PF_BOUNCING | PF_SLIDING | PF_CANCARRY ) ;
2016-06-05 01:20:26 +00:00
player - > climbing = 0 ;
2014-03-15 16:59:03 +00:00
2017-03-11 02:09:01 +00:00
if ( player - > mo - > state - states ! = S_PLAY_ROLL )
2014-03-15 16:59:03 +00:00
{
2017-03-11 02:09:01 +00:00
P_SetPlayerMobjState ( player - > mo , S_PLAY_ROLL ) ;
2014-03-15 16:59:03 +00:00
S_StartSound ( player - > mo , sfx_spin ) ;
}
}
break ;
case 10 : // Finish Line
2020-02-29 02:47:38 +00:00
if ( ( ( gametyperules & ( GTR_RACE | GTR_LIVES ) ) = = GTR_RACE ) & & ! player - > exiting )
2014-03-15 16:59:03 +00:00
{
if ( player - > starpostnum = = numstarposts ) // Must have touched all the starposts
{
player - > laps + + ;
2017-03-14 18:11:17 +00:00
if ( player - > powers [ pw_carry ] = = CR_NIGHTSMODE )
2014-03-15 16:59:03 +00:00
player - > drillmeter + = 48 * 20 ;
2014-03-21 18:42:55 +00:00
if ( player - > laps > = ( UINT8 ) cv_numlaps . value )
2014-03-15 16:59:03 +00:00
CONS_Printf ( M_GetText ( " %s has finished the race. \n " ) , player_names [ player - players ] ) ;
2020-12-07 22:46:05 +00:00
else if ( player - > laps = = ( UINT8 ) cv_numlaps . value - 1 )
2020-12-08 18:05:06 +00:00
CONS_Printf ( M_GetText ( " %s started the \205 final lap \200 ! \n " ) , player_names [ player - players ] ) ;
2014-03-15 16:59:03 +00:00
else
2014-03-21 18:42:55 +00:00
CONS_Printf ( M_GetText ( " %s started lap %u \n " ) , player_names [ player - players ] , ( UINT32 ) player - > laps + 1 ) ;
2014-03-15 16:59:03 +00:00
// Reset starposts (checkpoints) info
2019-09-07 21:11:33 +00:00
player - > starpostscale = player - > starpostangle = player - > starposttime = player - > starpostnum = 0 ;
2014-03-15 16:59:03 +00:00
player - > starpostx = player - > starposty = player - > starpostz = 0 ;
P_ResetStarposts ( ) ;
// Play the starpost sound for 'consistency'
S_StartSound ( player - > mo , sfx_strpst ) ;
}
else if ( player - > starpostnum )
{
// blatant reuse of a variable that's normally unused in circuit
if ( ! player - > tossdelay )
S_StartSound ( player - > mo , sfx_lose ) ;
player - > tossdelay = 3 ;
}
if ( player - > laps > = ( unsigned ) cv_numlaps . value )
{
if ( P_IsLocalPlayer ( player ) )
{
HU_SetCEchoFlags ( 0 ) ;
HU_SetCEchoDuration ( 5 ) ;
HU_DoCEcho ( " FINISHED! " ) ;
}
P_DoPlayerExit ( player ) ;
}
}
break ;
case 11 : // Rope hang
{
INT32 sequence ;
fixed_t speed ;
INT32 lineindex ;
mobj_t * waypointmid = NULL ;
mobj_t * waypointhigh = NULL ;
mobj_t * waypointlow = NULL ;
mobj_t * closest = NULL ;
2020-01-04 17:05:03 +00:00
vector3_t p , line [ 2 ] , resulthigh , resultlow ;
2014-03-15 16:59:03 +00:00
2016-09-23 22:48:48 +00:00
if ( player - > mo - > tracer & & player - > mo - > tracer - > type = = MT_TUBEWAYPOINT & & player - > powers [ pw_carry ] = = CR_ROPEHANG )
2014-03-15 16:59:03 +00:00
break ;
2020-06-18 21:09:29 +00:00
if ( player - > powers [ pw_ignorelatch ] & ( 1 < < 15 ) )
break ;
2014-03-15 16:59:03 +00:00
if ( player - > mo - > momz > 0 )
break ;
2020-06-06 09:36:34 +00:00
if ( player - > cmd . buttons & BT_SPIN )
2014-03-15 16:59:03 +00:00
break ;
if ( ! ( player - > pflags & PF_SLIDING ) & & player - > mo - > state = = & states [ player - > mo - > info - > painstate ] )
break ;
if ( player - > exiting )
break ;
//initialize resulthigh and resultlow with 0
memset ( & resultlow , 0x00 , sizeof ( resultlow ) ) ;
memset ( & resulthigh , 0x00 , sizeof ( resulthigh ) ) ;
// Find line #11 tagged to this sector
2020-04-17 16:15:25 +00:00
lineindex = Tag_FindLineSpecial ( 11 , sectag ) ;
2014-03-15 16:59:03 +00:00
if ( lineindex = = - 1 )
{
CONS_Debug ( DBG_GAMELOGIC , " ERROR: Sector special %d missing line special #11. \n " , sector - > special ) ;
break ;
}
// Grab speed and sequence values
2016-06-03 00:30:07 +00:00
speed = abs ( sides [ lines [ lineindex ] . sidenum [ 0 ] ] . textureoffset ) / 8 ;
sequence = abs ( sides [ lines [ lineindex ] . sidenum [ 0 ] ] . rowoffset ) > > FRACBITS ;
if ( speed = = 0 )
{
CONS_Debug ( DBG_GAMELOGIC , " ERROR: Waypoint sequence %d at zero speed. \n " , sequence ) ;
break ;
}
2014-03-15 16:59:03 +00:00
// Find the closest waypoint
// Find the preceding waypoint
// Find the proceeding waypoint
// Determine the closest spot on the line between the three waypoints
// Put player at that location.
2020-05-12 21:50:30 +00:00
waypointmid = P_GetClosestWaypoint ( sequence , player - > mo ) ;
2014-03-15 16:59:03 +00:00
2020-05-12 21:50:30 +00:00
if ( ! waypointmid )
2014-03-15 16:59:03 +00:00
{
CONS_Debug ( DBG_GAMELOGIC , " ERROR: WAYPOINT(S) IN SEQUENCE %d NOT FOUND. \n " , sequence ) ;
break ;
}
2020-05-12 21:50:30 +00:00
waypointlow = P_GetPreviousWaypoint ( waypointmid , true ) ;
waypointhigh = P_GetNextWaypoint ( waypointmid , true ) ;
2014-03-15 16:59:03 +00:00
CONS_Debug ( DBG_GAMELOGIC , " WaypointMid: %d; WaypointLow: %d; WaypointHigh: %d \n " ,
waypointmid - > health , waypointlow ? waypointlow - > health : - 1 , waypointhigh ? waypointhigh - > health : - 1 ) ;
// Now we have three waypoints... the closest one we're near, and the one that comes before, and after.
// Next, we need to find the closest point on the line between each set, and determine which one we're
// closest to.
2020-01-04 17:05:03 +00:00
p . x = player - > mo - > x ;
p . y = player - > mo - > y ;
p . z = player - > mo - > z ;
2014-03-15 16:59:03 +00:00
// Waypointmid and Waypointlow:
if ( waypointlow )
{
2020-01-04 17:05:03 +00:00
line [ 0 ] . x = waypointmid - > x ;
line [ 0 ] . y = waypointmid - > y ;
line [ 0 ] . z = waypointmid - > z ;
line [ 1 ] . x = waypointlow - > x ;
line [ 1 ] . y = waypointlow - > y ;
line [ 1 ] . z = waypointlow - > z ;
P_ClosestPointOnLine3D ( & p , line , & resultlow ) ;
2014-03-15 16:59:03 +00:00
}
// Waypointmid and Waypointhigh:
if ( waypointhigh )
{
2020-01-04 17:05:03 +00:00
line [ 0 ] . x = waypointmid - > x ;
line [ 0 ] . y = waypointmid - > y ;
line [ 0 ] . z = waypointmid - > z ;
line [ 1 ] . x = waypointhigh - > x ;
line [ 1 ] . y = waypointhigh - > y ;
line [ 1 ] . z = waypointhigh - > z ;
P_ClosestPointOnLine3D ( & p , line , & resulthigh ) ;
2014-03-15 16:59:03 +00:00
}
2014-08-04 03:49:33 +00:00
// 3D support now available. Disregard the previous notice here. -Red
2014-03-15 16:59:03 +00:00
P_UnsetThingPosition ( player - > mo ) ;
P_ResetPlayer ( player ) ;
player - > mo - > momx = player - > mo - > momy = player - > mo - > momz = 0 ;
if ( lines [ lineindex ] . flags & ML_EFFECT1 ) // Don't wrap
{
2020-05-12 21:50:30 +00:00
mobj_t * highest = P_GetLastWaypoint ( sequence ) ;
2014-03-15 16:59:03 +00:00
highest - > flags | = MF_SLIDEME ;
}
2014-08-04 03:49:33 +00:00
// Changing the conditions on these ifs to fix issues with snapping to the wrong spot -Red
if ( ( lines [ lineindex ] . flags & ML_EFFECT1 ) & & waypointmid - > health = = 0 )
{
closest = waypointhigh ;
player - > mo - > x = resulthigh . x ;
player - > mo - > y = resulthigh . y ;
player - > mo - > z = resulthigh . z - P_GetPlayerHeight ( player ) ;
}
2020-05-12 21:50:30 +00:00
else if ( ( lines [ lineindex ] . flags & ML_EFFECT1 ) & & waypointmid - > health = = numwaypoints [ sequence ] - 1 )
2014-08-04 03:49:33 +00:00
{
closest = waypointmid ;
player - > mo - > x = resultlow . x ;
player - > mo - > y = resultlow . y ;
player - > mo - > z = resultlow . z - P_GetPlayerHeight ( player ) ;
}
2014-03-15 16:59:03 +00:00
else
{
if ( P_AproxDistance ( P_AproxDistance ( player - > mo - > x - resultlow . x , player - > mo - > y - resultlow . y ) ,
player - > mo - > z - resultlow . z ) < P_AproxDistance ( P_AproxDistance ( player - > mo - > x - resulthigh . x ,
player - > mo - > y - resulthigh . y ) , player - > mo - > z - resulthigh . z ) )
{
// Line between Mid and Low is closer
closest = waypointmid ;
player - > mo - > x = resultlow . x ;
player - > mo - > y = resultlow . y ;
player - > mo - > z = resultlow . z - P_GetPlayerHeight ( player ) ;
}
else
{
// Line between Mid and High is closer
closest = waypointhigh ;
player - > mo - > x = resulthigh . x ;
player - > mo - > y = resulthigh . y ;
player - > mo - > z = resulthigh . z - P_GetPlayerHeight ( player ) ;
}
}
P_SetTarget ( & player - > mo - > tracer , closest ) ;
2016-09-23 22:48:48 +00:00
player - > powers [ pw_carry ] = CR_ROPEHANG ;
2014-03-15 16:59:03 +00:00
// Option for static ropes.
if ( lines [ lineindex ] . flags & ML_NOCLIMB )
player - > speed = 0 ;
else
player - > speed = speed ;
S_StartSound ( player - > mo , sfx_s3k4a ) ;
2017-09-15 19:34:46 +00:00
player - > pflags & = ~ ( PF_JUMPED | PF_NOJUMPDAMAGE | PF_GLIDING | PF_BOUNCING | PF_SLIDING | PF_CANCARRY ) ;
2014-03-15 16:59:03 +00:00
player - > climbing = 0 ;
P_SetThingPosition ( player - > mo ) ;
2015-01-22 15:23:45 +00:00
P_SetPlayerMobjState ( player - > mo , S_PLAY_RIDE ) ;
2014-03-15 16:59:03 +00:00
}
break ;
case 12 : // Camera noclip
case 13 : // Unused
case 14 : // Unused
case 15 : // Unused
break ;
}
}
/** Checks if an object is standing on or is inside a special 3D floor.
* If so , the sector is returned .
*
* \ param mo Object to check .
* \ return Pointer to the sector with a special type , or NULL if no special 3 D
* floors are being contacted .
* \ sa P_PlayerOnSpecial3DFloor
*/
sector_t * P_ThingOnSpecial3DFloor ( mobj_t * mo )
{
sector_t * sector ;
ffloor_t * rover ;
2016-10-31 00:02:38 +00:00
fixed_t topheight , bottomheight ;
2014-03-15 16:59:03 +00:00
sector = mo - > subsector - > sector ;
if ( ! sector - > ffloors )
return NULL ;
for ( rover = sector - > ffloors ; rover ; rover = rover - > next )
{
if ( ! rover - > master - > frontsector - > special )
continue ;
if ( ! ( rover - > flags & FF_EXISTS ) )
continue ;
2016-05-31 20:31:29 +00:00
topheight = P_GetSpecialTopZ ( mo , sectors + rover - > secnum , sector ) ;
bottomheight = P_GetSpecialBottomZ ( mo , sectors + rover - > secnum , sector ) ;
2014-03-15 16:59:03 +00:00
// Check the 3D floor's type...
if ( ( ( rover - > flags & FF_BLOCKPLAYER ) & & mo - > player )
| | ( ( rover - > flags & FF_BLOCKOTHERS ) & & ! mo - > player ) )
{
2019-10-06 13:34:25 +00:00
boolean floorallowed = ( ( rover - > master - > frontsector - > flags & SF_FLIPSPECIAL_FLOOR ) & & ( ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ! ( mo - > eflags & MFE_VERTICALFLIP ) ) & & ( mo - > z = = topheight ) ) ;
boolean ceilingallowed = ( ( rover - > master - > frontsector - > flags & SF_FLIPSPECIAL_CEILING ) & & ( ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ( mo - > eflags & MFE_VERTICALFLIP ) ) & & ( mo - > z + mo - > height = = bottomheight ) ) ;
2014-03-15 16:59:03 +00:00
// Thing must be on top of the floor to be affected...
2019-10-06 13:34:25 +00:00
if ( ! ( floorallowed | | ceilingallowed ) )
continue ;
2014-03-15 16:59:03 +00:00
}
else
{
// Water and intangible FOFs
2016-05-31 20:31:29 +00:00
if ( mo - > z > topheight | | ( mo - > z + mo - > height ) < bottomheight )
2014-03-15 16:59:03 +00:00
continue ;
}
return rover - > master - > frontsector ;
}
return NULL ;
}
2016-10-31 00:02:38 +00:00
# define TELEPORTED (player->mo->subsector->sector != originalsector)
2014-03-15 16:59:03 +00:00
/** Checks if a player is standing on or is inside a 3D floor (e.g. water) and
* applies any specials .
*
* \ param player Player to check .
* \ sa P_ThingOnSpecial3DFloor , P_PlayerInSpecialSector
*/
static void P_PlayerOnSpecial3DFloor ( player_t * player , sector_t * sector )
{
2016-10-31 14:03:53 +00:00
sector_t * originalsector = player - > mo - > subsector - > sector ;
2014-03-15 16:59:03 +00:00
ffloor_t * rover ;
2016-10-31 00:02:38 +00:00
fixed_t topheight , bottomheight ;
2014-03-15 16:59:03 +00:00
for ( rover = sector - > ffloors ; rover ; rover = rover - > next )
{
if ( ! rover - > master - > frontsector - > special )
continue ;
if ( ! ( rover - > flags & FF_EXISTS ) )
continue ;
2016-05-31 20:31:29 +00:00
topheight = P_GetSpecialTopZ ( player - > mo , sectors + rover - > secnum , sector ) ;
bottomheight = P_GetSpecialBottomZ ( player - > mo , sectors + rover - > secnum , sector ) ;
2014-03-15 16:59:03 +00:00
// Check the 3D floor's type...
if ( rover - > flags & FF_BLOCKPLAYER )
{
2019-10-06 13:34:25 +00:00
boolean floorallowed = ( ( rover - > master - > frontsector - > flags & SF_FLIPSPECIAL_FLOOR ) & & ( ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ! ( player - > mo - > eflags & MFE_VERTICALFLIP ) ) & & ( player - > mo - > z = = topheight ) ) ;
boolean ceilingallowed = ( ( rover - > master - > frontsector - > flags & SF_FLIPSPECIAL_CEILING ) & & ( ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ( player - > mo - > eflags & MFE_VERTICALFLIP ) ) & & ( player - > mo - > z + player - > mo - > height = = bottomheight ) ) ;
2014-03-15 16:59:03 +00:00
// Thing must be on top of the floor to be affected...
2019-10-06 13:34:25 +00:00
if ( ! ( floorallowed | | ceilingallowed ) )
continue ;
2014-03-15 16:59:03 +00:00
}
else
{
// Water and DEATH FOG!!! heh
2016-05-31 20:31:29 +00:00
if ( player - > mo - > z > topheight | | ( player - > mo - > z + player - > mo - > height ) < bottomheight )
2014-03-15 16:59:03 +00:00
continue ;
}
// This FOF has the special we're looking for, but are we allowed to touch it?
if ( sector = = player - > mo - > subsector - > sector
| | ( rover - > master - > frontsector - > flags & SF_TRIGGERSPECIAL_TOUCH ) )
2016-10-31 00:02:38 +00:00
{
2014-03-15 16:59:03 +00:00
P_ProcessSpecialSector ( player , rover - > master - > frontsector , sector ) ;
2016-10-31 00:02:38 +00:00
if TELEPORTED return ;
}
2014-03-15 16:59:03 +00:00
}
// Allow sector specials to be applied to polyobjects!
if ( player - > mo - > subsector - > polyList )
{
polyobj_t * po = player - > mo - > subsector - > polyList ;
sector_t * polysec ;
boolean touching = false ;
boolean inside = false ;
2016-10-31 00:02:38 +00:00
while ( po )
2014-03-15 16:59:03 +00:00
{
if ( po - > flags & POF_NOSPECIALS )
{
po = ( polyobj_t * ) ( po - > link . next ) ;
continue ;
}
polysec = po - > lines [ 0 ] - > backsector ;
if ( ( polysec - > flags & SF_TRIGGERSPECIAL_TOUCH ) )
touching = P_MobjTouchingPolyobj ( po , player - > mo ) ;
else
touching = false ;
inside = P_MobjInsidePolyobj ( po , player - > mo ) ;
if ( ! ( inside | | touching ) )
{
po = ( polyobj_t * ) ( po - > link . next ) ;
continue ;
}
// We're inside it! Yess...
if ( ! polysec - > special )
{
po = ( polyobj_t * ) ( po - > link . next ) ;
continue ;
}
if ( ! ( po - > flags & POF_TESTHEIGHT ) ) // Don't do height checking
2019-10-06 13:34:25 +00:00
;
2014-03-15 16:59:03 +00:00
else if ( po - > flags & POF_SOLID )
{
2019-10-06 13:34:25 +00:00
boolean floorallowed = ( ( polysec - > flags & SF_FLIPSPECIAL_FLOOR ) & & ( ( polysec - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ! ( player - > mo - > eflags & MFE_VERTICALFLIP ) ) & & ( player - > mo - > z = = polysec - > ceilingheight ) ) ;
boolean ceilingallowed = ( ( polysec - > flags & SF_FLIPSPECIAL_CEILING ) & & ( ( polysec - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ( player - > mo - > eflags & MFE_VERTICALFLIP ) ) & & ( player - > mo - > z + player - > mo - > height = = polysec - > floorheight ) ) ;
2014-03-15 16:59:03 +00:00
// Thing must be on top of the floor to be affected...
2019-10-06 13:34:25 +00:00
if ( ! ( floorallowed | | ceilingallowed ) )
2014-03-15 16:59:03 +00:00
{
2019-10-06 13:34:25 +00:00
po = ( polyobj_t * ) ( po - > link . next ) ;
continue ;
2014-03-15 16:59:03 +00:00
}
}
else
{
// Water and DEATH FOG!!! heh
if ( player - > mo - > z > polysec - > ceilingheight | | ( player - > mo - > z + player - > mo - > height ) < polysec - > floorheight )
{
po = ( polyobj_t * ) ( po - > link . next ) ;
continue ;
}
}
P_ProcessSpecialSector ( player , polysec , sector ) ;
2016-10-31 00:02:38 +00:00
if TELEPORTED return ;
2014-03-15 16:59:03 +00:00
po = ( polyobj_t * ) ( po - > link . next ) ;
}
}
}
# define VDOORSPEED (FRACUNIT*2)
//
// P_RunSpecialSectorCheck
//
// Helper function to P_PlayerInSpecialSector
//
static void P_RunSpecialSectorCheck ( player_t * player , sector_t * sector )
{
boolean nofloorneeded = false ;
2015-05-23 01:57:58 +00:00
fixed_t f_affectpoint , c_affectpoint ;
2014-03-15 16:59:03 +00:00
if ( ! sector - > special ) // nothing special, exit
return ;
if ( GETSECSPECIAL ( sector - > special , 2 ) = = 9 ) // Egg trap capsule -- should only be for 3dFloors!
return ;
// The list of specials that activate without floor touch
// Check Section 1
switch ( GETSECSPECIAL ( sector - > special , 1 ) )
{
case 2 : // Damage (water)
case 8 : // Instant kill
case 10 : // Ring drainer that doesn't require floor touch
case 12 : // Space countdown
nofloorneeded = true ;
break ;
}
// Check Section 2
switch ( GETSECSPECIAL ( sector - > special , 2 ) )
{
case 2 : // Linedef executor (All players needed)
case 4 : // Linedef executor
case 6 : // Linedef executor (7 Emeralds)
case 7 : // Linedef executor (NiGHTS Mare)
nofloorneeded = true ;
break ;
}
// Check Section 3
/* switch(GETSECSPECIAL(sector->special, 3))
{
} */
// Check Section 4
switch ( GETSECSPECIAL ( sector - > special , 4 ) )
{
case 2 : // Level Exit / GOAL Sector / Flag Return
2018-06-03 21:41:54 +00:00
if ( ! ( maptol & TOL_NIGHTS ) & & G_IsSpecialStage ( gamemap ) )
2014-03-15 16:59:03 +00:00
{
// Special stage GOAL sector
// requires touching floor.
break ;
}
2017-09-28 13:39:47 +00:00
/* FALLTHRU */
2014-03-15 16:59:03 +00:00
case 1 : // Starpost activator
case 5 : // Fan sector
case 6 : // Super Sonic Transform
case 8 : // Zoom Tube Start
case 9 : // Zoom Tube End
case 10 : // Finish line
nofloorneeded = true ;
break ;
}
if ( nofloorneeded )
{
P_ProcessSpecialSector ( player , sector , NULL ) ;
return ;
}
2015-05-24 17:53:30 +00:00
f_affectpoint = P_GetSpecialBottomZ ( player - > mo , sector , sector ) ;
c_affectpoint = P_GetSpecialTopZ ( player - > mo , sector , sector ) ;
2014-11-19 00:45:57 +00:00
2019-10-06 13:34:25 +00:00
{
boolean floorallowed = ( ( sector - > flags & SF_FLIPSPECIAL_FLOOR ) & & ( ( sector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ! ( player - > mo - > eflags & MFE_VERTICALFLIP ) ) & & ( player - > mo - > z = = f_affectpoint ) ) ;
boolean ceilingallowed = ( ( sector - > flags & SF_FLIPSPECIAL_CEILING ) & & ( ( sector - > flags & SF_TRIGGERSPECIAL_HEADBUMP ) | | ( player - > mo - > eflags & MFE_VERTICALFLIP ) ) & & ( player - > mo - > z + player - > mo - > height = = c_affectpoint ) ) ;
// Thing must be on top of the floor to be affected...
if ( ! ( floorallowed | | ceilingallowed ) )
return ;
}
2014-03-15 16:59:03 +00:00
P_ProcessSpecialSector ( player , sector , NULL ) ;
}
/** Checks if the player is in a special sector or FOF and apply any specials.
*
* \ param player Player to check .
* \ sa P_PlayerOnSpecial3DFloor , P_ProcessSpecialSector
*/
void P_PlayerInSpecialSector ( player_t * player )
{
2016-10-31 00:02:38 +00:00
sector_t * originalsector ;
sector_t * loopsector ;
2014-03-15 16:59:03 +00:00
msecnode_t * node ;
if ( ! player - > mo )
return ;
2016-10-31 00:02:38 +00:00
originalsector = player - > mo - > subsector - > sector ;
2014-03-15 16:59:03 +00:00
2016-10-31 14:03:53 +00:00
P_PlayerOnSpecial3DFloor ( player , originalsector ) ; // Handle FOFs first.
2016-10-31 00:02:38 +00:00
if TELEPORTED return ;
2014-03-15 16:59:03 +00:00
2016-10-31 00:02:38 +00:00
P_RunSpecialSectorCheck ( player , originalsector ) ;
if TELEPORTED return ;
2014-03-15 16:59:03 +00:00
2016-10-31 00:02:38 +00:00
// Iterate through touching_sectorlist for SF_TRIGGERSPECIAL_TOUCH
2016-06-09 13:56:24 +00:00
for ( node = player - > mo - > touching_sectorlist ; node ; node = node - > m_sectorlist_next )
2014-03-15 16:59:03 +00:00
{
2016-10-31 00:02:38 +00:00
loopsector = node - > m_sector ;
2014-03-15 16:59:03 +00:00
2016-10-31 00:02:38 +00:00
if ( loopsector = = originalsector ) // Don't duplicate
2014-03-15 16:59:03 +00:00
continue ;
// Check 3D floors...
2016-10-31 14:03:53 +00:00
P_PlayerOnSpecial3DFloor ( player , loopsector ) ;
2016-10-31 00:02:38 +00:00
if TELEPORTED return ;
2014-03-15 16:59:03 +00:00
2016-10-31 00:02:38 +00:00
if ( ! ( loopsector - > flags & SF_TRIGGERSPECIAL_TOUCH ) )
continue ;
P_RunSpecialSectorCheck ( player , loopsector ) ;
if TELEPORTED return ;
2014-03-15 16:59:03 +00:00
}
}
2016-10-31 00:02:38 +00:00
# undef TELEPORTED
2014-03-15 16:59:03 +00:00
/** Animate planes, scroll walls, etc. and keeps track of level timelimit and exits if time is up.
*
2015-10-11 20:01:04 +00:00
* \ sa P_CheckTimeLimit , P_CheckPointLimit
2014-03-15 16:59:03 +00:00
*/
void P_UpdateSpecials ( void )
{
anim_t * anim ;
2015-10-11 20:01:04 +00:00
INT32 i ;
2014-03-15 16:59:03 +00:00
INT32 pic ;
size_t j ;
levelflat_t * foundflats ; // for flat animation
// LEVEL TIMER
2015-10-11 20:01:04 +00:00
P_CheckTimeLimit ( ) ;
2014-03-15 16:59:03 +00:00
// POINT LIMIT
P_CheckPointLimit ( ) ;
// ANIMATE TEXTURES
for ( anim = anims ; anim < lastanim ; anim + + )
{
2016-01-23 18:59:17 +00:00
for ( i = 0 ; i < anim - > numpics ; i + + )
2014-03-15 16:59:03 +00:00
{
pic = anim - > basepic + ( ( leveltime / anim - > speed + i ) % anim - > numpics ) ;
if ( anim - > istexture )
2016-01-23 18:59:17 +00:00
texturetranslation [ anim - > basepic + i ] = pic ;
2014-03-15 16:59:03 +00:00
}
}
// ANIMATE FLATS
/// \todo do not check the non-animate flat.. link the animated ones?
/// \note its faster than the original anywaysince it animates only
/// flats used in the level, and there's usually very few of them
foundflats = levelflats ;
for ( j = 0 ; j < numlevelflats ; j + + , foundflats + + )
{
if ( foundflats - > speed ) // it is an animated flat
{
2019-05-21 18:24:26 +00:00
// update the levelflat texture number
2019-11-09 22:32:06 +00:00
if ( ( foundflats - > type = = LEVELFLAT_TEXTURE ) & & ( foundflats - > u . texture . basenum ! = - 1 ) )
2019-10-21 03:21:22 +00:00
foundflats - > u . texture . num = foundflats - > u . texture . basenum + ( ( leveltime / foundflats - > speed + foundflats - > animseq ) % foundflats - > numpics ) ;
2014-03-15 16:59:03 +00:00
// update the levelflat lump number
2019-11-09 02:42:15 +00:00
else if ( ( foundflats - > type = = LEVELFLAT_FLAT ) & & ( foundflats - > u . flat . baselumpnum ! = LUMPERROR ) )
2019-10-21 03:21:22 +00:00
foundflats - > u . flat . lumpnum = foundflats - > u . flat . baselumpnum + ( ( leveltime / foundflats - > speed + foundflats - > animseq ) % foundflats - > numpics ) ;
2014-03-15 16:59:03 +00:00
}
}
}
2020-04-24 21:29:41 +00:00
//
// Floor over floors (FOFs), 3Dfloors, 3Dblocks, fake floors (ffloors), rovers, or whatever you want to call them
//
/** Gets the ID number for a 3Dfloor in its target sector.
*
* \ param fflr The 3 Dfloor we want an ID for .
* \ return ID of 3 Dfloor in target sector . Note that the first FOF ' s ID is 0. UINT16_MAX is given if invalid .
* \ sa P_GetFFloorByID
*/
UINT16 P_GetFFloorID ( ffloor_t * fflr )
{
ffloor_t * rover ;
sector_t * sec ;
UINT16 i = 0 ;
2020-04-25 15:52:11 +00:00
if ( ! fflr )
2020-04-24 21:29:41 +00:00
return UINT16_MAX ;
2020-04-25 15:53:52 +00:00
sec = fflr - > target ;
2020-04-24 21:29:41 +00:00
if ( ! sec - > ffloors )
return UINT16_MAX ;
for ( rover = sec - > ffloors ; rover ; rover = rover - > next , i + + )
if ( rover = = fflr )
return i ;
return UINT16_MAX ;
}
2016-07-10 16:58:54 +00:00
/** Gets a 3Dfloor by control sector.
*
* \ param sec Target sector .
* \ param sec2 Control sector .
* \ return Pointer to found 3 Dfloor , or NULL .
* \ sa P_GetFFloorByID
*/
2015-01-01 19:50:31 +00:00
static inline ffloor_t * P_GetFFloorBySec ( sector_t * sec , sector_t * sec2 )
{
ffloor_t * rover ;
if ( ! sec - > ffloors )
return NULL ;
for ( rover = sec - > ffloors ; rover ; rover = rover - > next )
if ( rover - > secnum = = ( size_t ) ( sec2 - sectors ) )
return rover ;
return NULL ;
}
2016-07-10 16:58:54 +00:00
/** Gets a 3Dfloor by ID number.
*
* \ param sec Target sector .
2016-10-25 15:15:17 +00:00
* \ param id ID of 3 Dfloor in target sector . Note that the first FOF ' s ID is 0.
2016-07-10 16:58:54 +00:00
* \ return Pointer to found 3 Dfloor , or NULL .
2020-04-24 21:29:41 +00:00
* \ sa P_GetFFloorBySec , P_GetFFloorID
2016-07-10 16:58:54 +00:00
*/
ffloor_t * P_GetFFloorByID ( sector_t * sec , UINT16 id )
{
ffloor_t * rover ;
UINT16 i = 0 ;
if ( ! sec - > ffloors )
return NULL ;
for ( rover = sec - > ffloors ; rover ; rover = rover - > next )
if ( i + + = = id )
return rover ;
return NULL ;
}
2014-03-15 16:59:03 +00:00
/** Adds a newly formed 3Dfloor structure to a sector's ffloors list.
*
* \ param sec Target sector .
2019-07-09 20:15:12 +00:00
* \ param fflr Newly formed 3 Dfloor structure .
2014-03-15 16:59:03 +00:00
* \ sa P_AddFakeFloor
*/
2019-07-09 20:15:12 +00:00
static inline void P_AddFFloorToList ( sector_t * sec , ffloor_t * fflr )
2014-03-15 16:59:03 +00:00
{
ffloor_t * rover ;
if ( ! sec - > ffloors )
{
2019-07-09 20:15:12 +00:00
sec - > ffloors = fflr ;
fflr - > next = 0 ;
fflr - > prev = 0 ;
2014-03-15 16:59:03 +00:00
return ;
}
for ( rover = sec - > ffloors ; rover - > next ; rover = rover - > next ) ;
2019-07-09 20:15:12 +00:00
rover - > next = fflr ;
fflr - > prev = rover ;
fflr - > next = 0 ;
2014-03-15 16:59:03 +00:00
}
/** Adds a 3Dfloor.
*
* \ param sec Target sector .
* \ param sec2 Control sector .
* \ param master Control linedef .
* \ param flags Options affecting this 3 Dfloor .
* \ param secthinkers List of relevant thinkers sorted by sector . May be NULL .
* \ return Pointer to the new 3 Dfloor .
* \ sa P_AddFFloor , P_AddFakeFloorsByLine , P_SpawnSpecials
*/
static ffloor_t * P_AddFakeFloor ( sector_t * sec , sector_t * sec2 , line_t * master , ffloortype_e flags , thinkerlist_t * secthinkers )
{
2019-07-09 20:15:12 +00:00
ffloor_t * fflr ;
2014-03-15 16:59:03 +00:00
thinker_t * th ;
friction_t * f ;
pusher_t * p ;
size_t sec2num ;
size_t i ;
if ( sec = = sec2 )
2015-01-01 19:50:31 +00:00
return NULL ; //Don't need a fake floor on a control sector.
2019-07-09 20:15:12 +00:00
if ( ( fflr = ( P_GetFFloorBySec ( sec , sec2 ) ) ) )
return fflr ; // If this ffloor already exists, return it
2014-03-15 16:59:03 +00:00
if ( sec2 - > ceilingheight < sec2 - > floorheight )
{
fixed_t tempceiling = sec2 - > ceilingheight ;
//flip the sector around and print an error instead of crashing 12.1.08 -Inuyasha
2020-11-10 12:11:42 +00:00
CONS_Alert ( CONS_ERROR , M_GetText ( " FOF (line %s) has a top height below its bottom. \n " ) , sizeu1 ( master - lines ) ) ;
2014-03-15 16:59:03 +00:00
sec2 - > ceilingheight = sec2 - > floorheight ;
sec2 - > floorheight = tempceiling ;
}
if ( sec2 - > numattached = = 0 )
{
sec2 - > attached = Z_Malloc ( sizeof ( * sec2 - > attached ) * sec2 - > maxattached , PU_STATIC , NULL ) ;
sec2 - > attachedsolid = Z_Malloc ( sizeof ( * sec2 - > attachedsolid ) * sec2 - > maxattached , PU_STATIC , NULL ) ;
sec2 - > attached [ 0 ] = sec - sectors ;
sec2 - > numattached = 1 ;
sec2 - > attachedsolid [ 0 ] = ( flags & FF_SOLID ) ;
}
else
{
for ( i = 0 ; i < sec2 - > numattached ; i + + )
if ( sec2 - > attached [ i ] = = ( size_t ) ( sec - sectors ) )
return NULL ;
if ( sec2 - > numattached > = sec2 - > maxattached )
{
sec2 - > maxattached * = 2 ;
sec2 - > attached = Z_Realloc ( sec2 - > attached , sizeof ( * sec2 - > attached ) * sec2 - > maxattached , PU_STATIC , NULL ) ;
sec2 - > attachedsolid = Z_Realloc ( sec2 - > attachedsolid , sizeof ( * sec2 - > attachedsolid ) * sec2 - > maxattached , PU_STATIC , NULL ) ;
}
sec2 - > attached [ sec2 - > numattached ] = sec - sectors ;
sec2 - > attachedsolid [ sec2 - > numattached ] = ( flags & FF_SOLID ) ;
sec2 - > numattached + + ;
}
// Add the floor
2019-07-09 20:15:12 +00:00
fflr = Z_Calloc ( sizeof ( * fflr ) , PU_LEVEL , NULL ) ;
fflr - > secnum = sec2 - sectors ;
fflr - > target = sec ;
fflr - > bottomheight = & sec2 - > floorheight ;
fflr - > bottompic = & sec2 - > floorpic ;
fflr - > bottomxoffs = & sec2 - > floor_xoffs ;
fflr - > bottomyoffs = & sec2 - > floor_yoffs ;
fflr - > bottomangle = & sec2 - > floorpic_angle ;
2014-03-15 16:59:03 +00:00
// Add the ceiling
2019-07-09 20:15:12 +00:00
fflr - > topheight = & sec2 - > ceilingheight ;
fflr - > toppic = & sec2 - > ceilingpic ;
fflr - > toplightlevel = & sec2 - > lightlevel ;
fflr - > topxoffs = & sec2 - > ceiling_xoffs ;
fflr - > topyoffs = & sec2 - > ceiling_yoffs ;
fflr - > topangle = & sec2 - > ceilingpic_angle ;
2014-03-15 16:59:03 +00:00
2015-05-17 03:32:12 +00:00
// Add slopes
2019-07-09 20:15:12 +00:00
fflr - > t_slope = & sec2 - > c_slope ;
fflr - > b_slope = & sec2 - > f_slope ;
2019-06-17 17:46:51 +00:00
// mark the target sector as having slopes, if the FOF has any of its own
// (this fixes FOF slopes glitching initially at level load in software mode)
if ( sec2 - > hasslope )
sec - > hasslope = true ;
2015-05-17 03:32:12 +00:00
2014-03-15 16:59:03 +00:00
if ( ( flags & FF_SOLID ) & & ( master - > flags & ML_EFFECT1 ) ) // Block player only
flags & = ~ FF_BLOCKOTHERS ;
if ( ( flags & FF_SOLID ) & & ( master - > flags & ML_EFFECT2 ) ) // Block all BUT player
flags & = ~ FF_BLOCKPLAYER ;
2019-07-09 20:15:12 +00:00
fflr - > spawnflags = fflr - > flags = flags ;
fflr - > master = master ;
fflr - > norender = INFTICS ;
fflr - > fadingdata = NULL ;
2014-03-15 16:59:03 +00:00
// Scan the thinkers to check for special conditions applying to this FOF.
// If we have thinkers sorted by sector, just check the relevant ones;
// otherwise, check them all. Apologies for the ugly loop...
sec2num = sec2 - sectors ;
// Just initialise both of these to placate the compiler.
i = 0 ;
2019-04-20 20:37:19 +00:00
th = thlist [ THINK_MAIN ] . next ;
2014-03-15 16:59:03 +00:00
for ( ; ; )
{
if ( secthinkers )
{
if ( i < secthinkers [ sec2num ] . count )
th = secthinkers [ sec2num ] . thinkers [ i ] ;
else break ;
}
2019-04-20 20:37:19 +00:00
else if ( th = = & thlist [ THINK_MAIN ] )
2014-03-15 16:59:03 +00:00
break ;
// Should this FOF have friction?
2020-04-17 20:54:35 +00:00
if ( th - > function . acp1 = = ( actionf_p1 ) T_Friction )
2014-03-15 16:59:03 +00:00
{
f = ( friction_t * ) th ;
if ( f - > affectee = = ( INT32 ) sec2num )
Add_Friction ( f - > friction , f - > movefactor , ( INT32 ) ( sec - sectors ) , f - > affectee ) ;
}
// Should this FOF have wind/current/pusher?
else if ( th - > function . acp1 = = ( actionf_p1 ) T_Pusher )
{
p = ( pusher_t * ) th ;
if ( p - > affectee = = ( INT32 ) sec2num )
Add_Pusher ( p - > type , p - > x_mag < < FRACBITS , p - > y_mag < < FRACBITS , p - > source , ( INT32 ) ( sec - sectors ) , p - > affectee , p - > exclusive , p - > slider ) ;
}
if ( secthinkers ) i + + ;
else th = th - > next ;
}
if ( flags & FF_TRANSLUCENT )
{
if ( sides [ master - > sidenum [ 0 ] ] . toptexture > 0 )
2019-07-09 20:15:12 +00:00
fflr - > alpha = sides [ master - > sidenum [ 0 ] ] . toptexture ; // for future reference, "#0" is 1, and "#255" is 256. Be warned
2014-03-15 16:59:03 +00:00
else
2019-07-09 20:15:12 +00:00
fflr - > alpha = 0x80 ;
2014-03-15 16:59:03 +00:00
}
else
2019-07-09 20:15:12 +00:00
fflr - > alpha = 0xff ;
2014-03-15 16:59:03 +00:00
2019-07-09 20:15:12 +00:00
fflr - > spawnalpha = fflr - > alpha ; // save for netgames
2014-11-12 00:55:07 +00:00
2014-03-15 16:59:03 +00:00
if ( flags & FF_QUICKSAND )
CheckForQuicksand = true ;
if ( ( flags & FF_BUSTUP ) | | ( flags & FF_SHATTER ) | | ( flags & FF_SPINBUST ) )
CheckForBustableBlocks = true ;
if ( ( flags & FF_MARIO ) )
{
2016-11-22 16:21:30 +00:00
if ( ! ( flags & FF_SHATTERBOTTOM ) ) // Don't change the textures of a brick block, just a question block
2016-06-04 19:02:11 +00:00
P_AddBlockThinker ( sec2 , master ) ;
2014-03-15 16:59:03 +00:00
CheckForMarioBlocks = true ;
}
if ( ( flags & FF_CRUMBLE ) )
2020-04-17 21:54:37 +00:00
sec2 - > crumblestate = CRUMBLE_WAIT ;
2014-03-15 16:59:03 +00:00
if ( ( flags & FF_FLOATBOB ) )
{
2020-04-17 20:26:04 +00:00
P_AddFloatThinker ( sec2 , Tag_FGet ( & master - > tags ) , master ) ;
2014-03-15 16:59:03 +00:00
CheckForFloatBob = true ;
}
2019-07-09 20:15:12 +00:00
P_AddFFloorToList ( sec , fflr ) ;
2014-03-15 16:59:03 +00:00
2019-07-09 20:15:12 +00:00
return fflr ;
2014-03-15 16:59:03 +00:00
}
//
// SPECIAL SPAWNING
//
/** Adds a float thinker.
* Float thinkers cause solid 3 Dfloors to float on water .
*
* \ param sec Control sector .
* \ param actionsector Target sector .
* \ sa P_SpawnSpecials , T_FloatSector
* \ author SSNTails < http : //www.ssntails.org>
*/
2020-04-17 22:26:49 +00:00
static void P_AddFloatThinker ( sector_t * sec , UINT16 tag , line_t * sourceline )
2014-03-15 16:59:03 +00:00
{
2020-04-17 22:26:49 +00:00
floatthink_t * floater ;
2014-03-15 16:59:03 +00:00
// create and initialize new thinker
floater = Z_Calloc ( sizeof ( * floater ) , PU_LEVSPEC , NULL ) ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & floater - > thinker ) ;
2014-03-15 16:59:03 +00:00
floater - > thinker . function . acp1 = ( actionf_p1 ) T_FloatSector ;
floater - > sector = sec ;
2020-04-17 22:26:49 +00:00
floater - > tag = ( INT16 ) tag ;
2014-03-15 16:59:03 +00:00
floater - > sourceline = sourceline ;
}
2017-01-05 20:29:48 +00:00
/**
* Adds a plane displacement thinker .
* Whenever the " control " sector moves ,
* the " affectee " sector ' s floor or ceiling plane moves too !
*
* \ param speed Rate of movement relative to control sector
* \ param control Control sector .
* \ param affectee Target sector .
2017-04-11 21:33:04 +00:00
* \ param reverse Reverse direction ?
2017-01-05 20:29:48 +00:00
* \ sa P_SpawnSpecials , T_PlaneDisplace
* \ author Monster Iestyn
*/
2017-04-11 21:33:04 +00:00
static void P_AddPlaneDisplaceThinker ( INT32 type , fixed_t speed , INT32 control , INT32 affectee , UINT8 reverse )
2017-01-05 20:29:48 +00:00
{
planedisplace_t * displace ;
// create and initialize new displacement thinker
displace = Z_Calloc ( sizeof ( * displace ) , PU_LEVSPEC , NULL ) ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & displace - > thinker ) ;
2017-01-05 20:29:48 +00:00
displace - > thinker . function . acp1 = ( actionf_p1 ) T_PlaneDisplace ;
displace - > affectee = affectee ;
displace - > control = control ;
displace - > last_height = sectors [ control ] . floorheight ;
displace - > speed = speed ;
displace - > type = type ;
2017-04-11 21:33:04 +00:00
displace - > reverse = reverse ;
2017-01-05 20:29:48 +00:00
}
2014-03-15 16:59:03 +00:00
/** Adds a Mario block thinker, which changes the block's texture between blank
* and ? depending on whether it has contents .
* Needed in case objects respawn inside .
*
* \ param sec Control sector .
* \ param actionsector Target sector .
* \ param sourceline Control linedef .
* \ sa P_SpawnSpecials , T_MarioBlockChecker
* \ author SSNTails < http : //www.ssntails.org>
*/
static void P_AddBlockThinker ( sector_t * sec , line_t * sourceline )
{
2020-04-18 09:05:58 +00:00
mariocheck_t * block ;
2014-03-15 16:59:03 +00:00
// create and initialize new elevator thinker
block = Z_Calloc ( sizeof ( * block ) , PU_LEVSPEC , NULL ) ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & block - > thinker ) ;
2014-03-15 16:59:03 +00:00
block - > thinker . function . acp1 = ( actionf_p1 ) T_MarioBlockChecker ;
block - > sourceline = sourceline ;
block - > sector = sec ;
}
/** Adds a raise thinker.
* A raise thinker checks to see if the
* player is standing on its 3 D Floor ,
* and if so , raises the platform towards
* it ' s destination . Otherwise , it lowers
* to the lowest nearby height if not
* there already .
*
* \ param sec Control sector .
* \ sa P_SpawnSpecials , T_RaiseSector
* \ author SSNTails < http : //www.ssntails.org>
*/
2020-04-27 10:54:08 +00:00
static void P_AddRaiseThinker ( sector_t * sec , INT16 tag , fixed_t speed , fixed_t ceilingtop , fixed_t ceilingbottom , boolean lower , boolean spindash )
2014-03-15 16:59:03 +00:00
{
2020-04-17 07:58:53 +00:00
raise_t * raise ;
2014-03-15 16:59:03 +00:00
raise = Z_Calloc ( sizeof ( * raise ) , PU_LEVSPEC , NULL ) ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & raise - > thinker ) ;
2014-03-15 16:59:03 +00:00
raise - > thinker . function . acp1 = ( actionf_p1 ) T_RaiseSector ;
2020-04-27 10:54:08 +00:00
raise - > tag = tag ;
2014-03-15 16:59:03 +00:00
raise - > sector = sec ;
2020-04-27 09:19:07 +00:00
raise - > ceilingtop = ceilingtop ;
raise - > ceilingbottom = ceilingbottom ;
2014-03-15 16:59:03 +00:00
2020-05-01 10:44:39 +00:00
raise - > basespeed = speed ;
2014-03-15 16:59:03 +00:00
2020-04-17 09:11:36 +00:00
if ( lower )
2020-04-17 07:58:53 +00:00
raise - > flags | = RF_REVERSE ;
2020-04-17 09:11:36 +00:00
if ( spindash )
2020-04-17 07:58:53 +00:00
raise - > flags | = RF_SPINDASH ;
2014-03-15 16:59:03 +00:00
}
2020-04-27 10:54:08 +00:00
static void P_AddAirbob ( sector_t * sec , INT16 tag , fixed_t dist , boolean raise , boolean spindash , boolean dynamic )
2014-03-15 16:59:03 +00:00
{
2020-04-17 07:58:53 +00:00
raise_t * airbob ;
2014-03-15 16:59:03 +00:00
airbob = Z_Calloc ( sizeof ( * airbob ) , PU_LEVSPEC , NULL ) ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & airbob - > thinker ) ;
2014-03-15 16:59:03 +00:00
airbob - > thinker . function . acp1 = ( actionf_p1 ) T_RaiseSector ;
2020-04-27 10:54:08 +00:00
airbob - > tag = tag ;
2014-03-15 16:59:03 +00:00
airbob - > sector = sec ;
2020-04-17 07:58:53 +00:00
airbob - > ceilingtop = sec - > ceilingheight ;
2020-04-17 09:13:13 +00:00
airbob - > ceilingbottom = sec - > ceilingheight - dist ;
2014-03-15 16:59:03 +00:00
2020-04-17 07:58:53 +00:00
airbob - > basespeed = FRACUNIT ;
2019-11-07 22:56:42 +00:00
2020-04-17 09:11:36 +00:00
if ( ! raise )
2020-04-17 07:58:53 +00:00
airbob - > flags | = RF_REVERSE ;
2020-04-17 09:11:36 +00:00
if ( spindash )
2020-04-17 07:58:53 +00:00
airbob - > flags | = RF_SPINDASH ;
if ( dynamic )
airbob - > flags | = RF_DYNAMIC ;
2014-03-15 16:59:03 +00:00
}
/** Adds a thwomp thinker.
* Even thwomps need to think !
*
* \ param sec Control sector .
* \ sa P_SpawnSpecials , T_ThwompSector
* \ author SSNTails < http : //www.ssntails.org>
*/
2020-11-10 12:10:01 +00:00
static inline void P_AddThwompThinker ( sector_t * sec , line_t * sourceline , fixed_t crushspeed , fixed_t retractspeed , UINT16 sound )
2014-03-15 16:59:03 +00:00
{
2020-04-18 08:26:03 +00:00
thwomp_t * thwomp ;
2014-03-15 16:59:03 +00:00
// You *probably* already have a thwomp in this sector. If you've combined it with something
// else that uses the floordata/ceilingdata, you must be weird.
if ( sec - > floordata | | sec - > ceilingdata )
return ;
// create and initialize new elevator thinker
thwomp = Z_Calloc ( sizeof ( * thwomp ) , PU_LEVSPEC , NULL ) ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & thwomp - > thinker ) ;
2014-03-15 16:59:03 +00:00
thwomp - > thinker . function . acp1 = ( actionf_p1 ) T_ThwompSector ;
// set up the fields according to the type of elevator action
2020-04-18 08:26:03 +00:00
thwomp - > sourceline = sourceline ;
2014-03-15 16:59:03 +00:00
thwomp - > sector = sec ;
2020-04-27 11:01:31 +00:00
thwomp - > crushspeed = crushspeed ;
thwomp - > retractspeed = retractspeed ;
2014-03-15 16:59:03 +00:00
thwomp - > direction = 0 ;
2020-04-18 08:26:03 +00:00
thwomp - > floorstartheight = sec - > floorheight ;
thwomp - > ceilingstartheight = sec - > ceilingheight ;
thwomp - > delay = 1 ;
2020-04-25 08:47:45 +00:00
thwomp - > tag = Tag_FGet ( & sourceline - > tags ) ;
2020-04-27 11:01:31 +00:00
thwomp - > sound = sound ;
2020-04-18 08:26:03 +00:00
sec - > floordata = thwomp ;
sec - > ceilingdata = thwomp ;
// Start with 'resting' texture
sides [ sourceline - > sidenum [ 0 ] ] . midtexture = sides [ sourceline - > sidenum [ 0 ] ] . bottomtexture ;
2014-03-15 16:59:03 +00:00
}
/** Adds a thinker which checks if any MF_ENEMY objects with health are in the defined area.
* If not , a linedef executor is run once .
*
* \ param sourceline Control linedef .
* \ sa P_SpawnSpecials , T_NoEnemiesSector
* \ author SSNTails < http : //www.ssntails.org>
*/
2020-04-18 07:21:04 +00:00
static inline void P_AddNoEnemiesThinker ( line_t * sourceline )
2014-03-15 16:59:03 +00:00
{
2020-04-18 07:21:04 +00:00
noenemies_t * nobaddies ;
2014-03-15 16:59:03 +00:00
// create and initialize new thinker
nobaddies = Z_Calloc ( sizeof ( * nobaddies ) , PU_LEVSPEC , NULL ) ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & nobaddies - > thinker ) ;
2014-03-15 16:59:03 +00:00
nobaddies - > thinker . function . acp1 = ( actionf_p1 ) T_NoEnemiesSector ;
nobaddies - > sourceline = sourceline ;
}
/** Adds a thinker for Each-Time linedef executors. A linedef executor is run
* only when a player enters the area and doesn ' t run again until they re - enter .
*
* \ param sourceline Control linedef .
* \ sa P_SpawnSpecials , T_EachTimeThinker
* \ author SSNTails < http : //www.ssntails.org>
*/
2020-04-17 12:00:48 +00:00
static void P_AddEachTimeThinker ( line_t * sourceline )
2014-03-15 16:59:03 +00:00
{
2020-04-17 12:00:48 +00:00
eachtime_t * eachtime ;
2014-03-15 16:59:03 +00:00
// create and initialize new thinker
eachtime = Z_Calloc ( sizeof ( * eachtime ) , PU_LEVSPEC , NULL ) ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & eachtime - > thinker ) ;
2014-03-15 16:59:03 +00:00
eachtime - > thinker . function . acp1 = ( actionf_p1 ) T_EachTimeThinker ;
eachtime - > sourceline = sourceline ;
2020-04-17 12:00:48 +00:00
eachtime - > triggerOnExit = ! ! ( sourceline - > flags & ML_BOUNCY ) ;
2014-03-15 16:59:03 +00:00
}
/** Adds a camera scanner.
*
* \ param sourcesec Control sector .
* \ param actionsector Target sector .
* \ param angle Angle of the source line .
* \ sa P_SpawnSpecials , T_CameraScanner
* \ author SSNTails < http : //www.ssntails.org>
*/
static inline void P_AddCameraScanner ( sector_t * sourcesec , sector_t * actionsector , angle_t angle )
{
elevator_t * elevator ; // Why not? LOL
2020-04-26 16:42:31 +00:00
CONS_Alert ( CONS_WARNING , M_GetText ( " Detected a camera scanner effect (linedef type 5). This effect is deprecated and will be removed in the future! \n " ) ) ;
2014-03-15 16:59:03 +00:00
// create and initialize new elevator thinker
elevator = Z_Calloc ( sizeof ( * elevator ) , PU_LEVSPEC , NULL ) ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & elevator - > thinker ) ;
2014-03-15 16:59:03 +00:00
elevator - > thinker . function . acp1 = ( actionf_p1 ) T_CameraScanner ;
elevator - > type = elevateBounce ;
// set up the fields according to the type of elevator action
elevator - > sector = sourcesec ;
elevator - > actionsector = actionsector ;
elevator - > distance = FixedInt ( AngleFixed ( angle ) ) ;
}
/** Flashes a laser block.
*
* \ param flash Thinker structure for this laser .
2020-05-02 08:03:16 +00:00
* \ sa P_AddLaserThinker
2014-03-15 16:59:03 +00:00
* \ author SSNTails < http : //www.ssntails.org>
*/
void T_LaserFlash ( laserthink_t * flash )
{
msecnode_t * node ;
mobj_t * thing ;
2020-05-02 08:03:16 +00:00
INT32 s ;
ffloor_t * fflr ;
sector_t * sector ;
sector_t * sourcesec = flash - > sourceline - > frontsector ;
2015-05-24 17:53:30 +00:00
fixed_t top , bottom ;
2014-03-15 16:59:03 +00:00
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( flash - > tag , s )
2020-05-02 08:03:16 +00:00
{
sector = & sectors [ s ] ;
for ( fflr = sector - > ffloors ; fflr ; fflr = fflr - > next )
{
if ( fflr - > master ! = flash - > sourceline )
continue ;
2014-03-15 16:59:03 +00:00
2020-05-02 08:03:16 +00:00
if ( ! ( fflr - > flags & FF_EXISTS ) )
break ;
2014-03-15 16:59:03 +00:00
2020-05-02 08:03:16 +00:00
if ( leveltime & 2 )
//fflr->flags |= FF_RENDERALL;
fflr - > alpha = 0xB0 ;
else
//fflr->flags &= ~FF_RENDERALL;
fflr - > alpha = 0x90 ;
2014-03-15 16:59:03 +00:00
2020-05-18 14:14:05 +00:00
top = P_GetFFloorTopZAt ( fflr , sector - > soundorg . x , sector - > soundorg . y ) ;
bottom = P_GetFFloorBottomZAt ( fflr , sector - > soundorg . x , sector - > soundorg . y ) ;
2020-05-02 08:03:16 +00:00
sector - > soundorg . z = ( top + bottom ) / 2 ;
S_StartSound ( & sector - > soundorg , sfx_laser ) ;
2014-03-15 16:59:03 +00:00
2020-05-02 08:03:16 +00:00
// Seek out objects to DESTROY! MUAHAHHAHAHAA!!!*cough*
for ( node = sector - > touching_thinglist ; node & & node - > m_thing ; node = node - > m_thinglist_next )
{
thing = node - > m_thing ;
2014-03-15 16:59:03 +00:00
2020-05-02 08:03:16 +00:00
if ( flash - > nobosses & & thing - > flags & MF_BOSS )
continue ; // Don't hurt bosses
2014-03-15 16:59:03 +00:00
2020-05-02 08:03:16 +00:00
// Don't endlessly kill egg guard shields (or anything else for that matter)
if ( thing - > health < = 0 )
continue ;
2017-01-07 18:02:55 +00:00
2020-05-02 08:03:16 +00:00
top = P_GetSpecialTopZ ( thing , sourcesec , sector ) ;
bottom = P_GetSpecialBottomZ ( thing , sourcesec , sector ) ;
2015-05-24 17:53:30 +00:00
2020-05-02 08:03:16 +00:00
if ( thing - > z > = top
| | thing - > z + thing - > height < = bottom )
continue ;
2014-03-15 16:59:03 +00:00
2020-05-02 08:03:16 +00:00
if ( thing - > flags & MF_SHOOTABLE )
P_DamageMobj ( thing , NULL , NULL , 1 , 0 ) ;
else if ( thing - > type = = MT_EGGSHIELD )
P_KillMobj ( thing , NULL , NULL , 0 ) ;
}
2014-03-15 16:59:03 +00:00
2020-05-02 08:03:16 +00:00
break ;
}
2014-03-15 16:59:03 +00:00
}
}
2020-05-02 08:03:16 +00:00
static inline void P_AddLaserThinker ( INT16 tag , line_t * line , boolean nobosses )
2014-03-15 16:59:03 +00:00
{
2020-05-02 08:03:16 +00:00
laserthink_t * flash = Z_Calloc ( sizeof ( * flash ) , PU_LEVSPEC , NULL ) ;
2014-03-15 16:59:03 +00:00
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & flash - > thinker ) ;
2014-03-15 16:59:03 +00:00
flash - > thinker . function . acp1 = ( actionf_p1 ) T_LaserFlash ;
2020-05-02 08:03:16 +00:00
flash - > tag = tag ;
2014-03-15 16:59:03 +00:00
flash - > sourceline = line ;
2020-04-27 11:09:57 +00:00
flash - > nobosses = nobosses ;
2014-03-15 16:59:03 +00:00
}
//
// P_RunLevelLoadExecutors
//
// After loading/spawning all other specials
// and items, execute these.
//
static void P_RunLevelLoadExecutors ( void )
{
size_t i ;
for ( i = 0 ; i < numlines ; i + + )
{
if ( lines [ i ] . special = = 399 )
2014-08-27 03:56:30 +00:00
P_RunTriggerLinedef ( & lines [ i ] , NULL , NULL ) ;
2014-03-15 16:59:03 +00:00
}
}
2017-04-24 19:33:39 +00:00
/** Before things are loaded, initialises certain stuff in case they're needed
2020-01-10 18:56:29 +00:00
* by P_SpawnSlopes or P_LoadThings . This was split off from
2017-04-24 19:33:39 +00:00
* P_SpawnSpecials , in case you couldn ' t tell .
*
2020-05-04 20:40:49 +00:00
* \ sa P_SpawnSpecials
2017-04-24 19:33:39 +00:00
* \ author Monster Iestyn
*/
void P_InitSpecials ( void )
{
// Set the default gravity. Custom gravity overrides this setting.
2020-05-03 16:33:18 +00:00
gravity = mapheaderinfo [ gamemap - 1 ] - > gravity ;
2017-04-24 19:33:39 +00:00
// Defaults in case levels don't have them set.
2020-05-03 15:56:49 +00:00
sstimer = mapheaderinfo [ gamemap - 1 ] - > sstimer * TICRATE + 6 ;
ssspheres = mapheaderinfo [ gamemap - 1 ] - > ssspheres ;
2017-04-24 19:33:39 +00:00
CheckForBustableBlocks = CheckForBouncySector = CheckForQuicksand = CheckForMarioBlocks = CheckForFloatBob = CheckForReverseGravity = false ;
// Set curWeather
switch ( mapheaderinfo [ gamemap - 1 ] - > weather )
{
case PRECIP_SNOW : // snow
case PRECIP_RAIN : // rain
case PRECIP_STORM : // storm
case PRECIP_STORM_NORAIN : // storm w/o rain
case PRECIP_STORM_NOSTRIKES : // storm w/o lightning
curWeather = mapheaderinfo [ gamemap - 1 ] - > weather ;
break ;
default : // blank/none
2017-04-24 19:43:58 +00:00
curWeather = PRECIP_NONE ;
2017-04-24 19:33:39 +00:00
break ;
}
// Set globalweather
globalweather = mapheaderinfo [ gamemap - 1 ] - > weather ;
}
2018-05-15 07:39:52 +00:00
static void P_ApplyFlatAlignment ( line_t * master , sector_t * sector , angle_t flatangle , fixed_t xoffs , fixed_t yoffs )
2018-05-14 17:34:22 +00:00
{
2019-07-02 22:10:22 +00:00
if ( ! ( master - > flags & ML_NETONLY ) ) // Modify floor flat alignment unless ML_NETONLY flag is set
2018-05-14 17:34:22 +00:00
{
2019-12-23 11:42:38 +00:00
sector - > floorpic_angle = flatangle ;
2018-05-14 17:34:22 +00:00
sector - > floor_xoffs + = xoffs ;
sector - > floor_yoffs + = yoffs ;
}
2019-07-02 22:10:22 +00:00
if ( ! ( master - > flags & ML_NONET ) ) // Modify ceiling flat alignment unless ML_NONET flag is set
2018-05-14 17:34:22 +00:00
{
2019-12-23 11:42:38 +00:00
sector - > ceilingpic_angle = flatangle ;
2018-05-14 17:34:22 +00:00
sector - > ceiling_xoffs + = xoffs ;
sector - > ceiling_yoffs + = yoffs ;
}
}
2014-03-15 16:59:03 +00:00
/** After the map has loaded, scans for specials that spawn 3Dfloors and
* thinkers .
*
* \ todo Split up into multiple functions .
* \ todo Get rid of all the magic numbers .
* \ todo Potentially use ' fromnetsave ' to stop any new thinkers from being created
* as they ' ll just be erased by UnArchiveThinkers .
* \ sa P_SpawnPrecipitation , P_SpawnFriction , P_SpawnPushers , P_SpawnScrollers
*/
2019-12-28 11:48:32 +00:00
void P_SpawnSpecials ( boolean fromnetsave )
2014-03-15 16:59:03 +00:00
{
sector_t * sector ;
size_t i ;
INT32 j ;
thinkerlist_t * secthinkers ;
thinker_t * th ;
2015-01-01 19:50:31 +00:00
// This used to be used, and *should* be used in the future,
// but currently isn't.
( void ) fromnetsave ;
2019-08-03 11:09:18 +00:00
// yep, we do this here - "bossdisabled" is considered an apparatus of specials.
bossdisabled = 0 ;
2019-11-08 15:47:12 +00:00
stoppedclock = false ;
2015-01-01 19:50:31 +00:00
2014-03-15 16:59:03 +00:00
// Init special SECTORs.
sector = sectors ;
for ( i = 0 ; i < numsectors ; i + + , sector + + )
{
if ( ! sector - > special )
continue ;
// Process Section 1
switch ( GETSECSPECIAL ( sector - > special , 1 ) )
{
case 5 : // Spikes
2020-04-18 13:17:25 +00:00
//Terrible hack to replace an even worse hack:
//Spike damage automatically sets SF_TRIGGERSPECIAL_TOUCH.
//Yes, this also affects other specials on the same sector. Sorry.
sector - > flags | = SF_TRIGGERSPECIAL_TOUCH ;
2014-03-15 16:59:03 +00:00
break ;
case 15 : // Bouncy sector
CheckForBouncySector = true ;
break ;
}
// Process Section 2
switch ( GETSECSPECIAL ( sector - > special , 2 ) )
{
case 10 : // Time for special stage
sstimer = ( sector - > floorheight > > FRACBITS ) * TICRATE + 6 ; // Time to finish
2018-06-03 21:41:54 +00:00
ssspheres = sector - > ceilingheight > > FRACBITS ; // Ring count for special stage
2014-03-15 16:59:03 +00:00
break ;
case 11 : // Custom global gravity!
gravity = sector - > floorheight / 1000 ;
break ;
}
// Process Section 3
/* switch(GETSECSPECIAL(player->specialsector, 3))
{
} */
// Process Section 4
switch ( GETSECSPECIAL ( sector - > special , 4 ) )
{
case 10 : // Circuit finish line
2020-02-29 02:47:38 +00:00
if ( ( gametyperules & ( GTR_RACE | GTR_LIVES ) ) = = GTR_RACE )
2014-03-15 16:59:03 +00:00
circuitmap = true ;
break ;
}
}
P_SpawnScrollers ( ) ; // Add generalized scrollers
P_SpawnFriction ( ) ; // Friction model using linedefs
P_SpawnPushers ( ) ; // Pusher model using linedefs
// Look for thinkers that affect FOFs, and sort them by sector
secthinkers = Z_Calloc ( numsectors * sizeof ( thinkerlist_t ) , PU_STATIC , NULL ) ;
// Firstly, find out how many there are in each sector
2019-04-20 20:37:19 +00:00
for ( th = thlist [ THINK_MAIN ] . next ; th ! = & thlist [ THINK_MAIN ] ; th = th - > next )
2014-03-15 16:59:03 +00:00
{
2020-04-17 20:54:35 +00:00
if ( th - > function . acp1 = = ( actionf_p1 ) T_Friction )
2014-03-15 16:59:03 +00:00
secthinkers [ ( ( friction_t * ) th ) - > affectee ] . count + + ;
else if ( th - > function . acp1 = = ( actionf_p1 ) T_Pusher )
secthinkers [ ( ( pusher_t * ) th ) - > affectee ] . count + + ;
}
// Allocate each list, and then zero the count so we can use it to track
// the end of the list as we add the thinkers
for ( i = 0 ; i < numsectors ; i + + )
if ( secthinkers [ i ] . count > 0 )
{
secthinkers [ i ] . thinkers = Z_Malloc ( secthinkers [ i ] . count * sizeof ( thinker_t * ) , PU_STATIC , NULL ) ;
secthinkers [ i ] . count = 0 ;
}
// Finally, populate the lists.
2019-04-20 20:37:19 +00:00
for ( th = thlist [ THINK_MAIN ] . next ; th ! = & thlist [ THINK_MAIN ] ; th = th - > next )
2014-03-15 16:59:03 +00:00
{
size_t secnum = ( size_t ) - 1 ;
2020-04-17 20:54:35 +00:00
if ( th - > function . acp1 = = ( actionf_p1 ) T_Friction )
2014-03-15 16:59:03 +00:00
secnum = ( ( friction_t * ) th ) - > affectee ;
else if ( th - > function . acp1 = = ( actionf_p1 ) T_Pusher )
secnum = ( ( pusher_t * ) th ) - > affectee ;
if ( secnum ! = ( size_t ) - 1 )
secthinkers [ secnum ] . thinkers [ secthinkers [ secnum ] . count + + ] = th ;
}
// Init line EFFECTs
for ( i = 0 ; i < numlines ; i + + )
{
2020-04-17 08:52:12 +00:00
mtag_t tag = Tag_FGet ( & lines [ i ] . tags ) ;
2019-07-02 22:10:22 +00:00
if ( lines [ i ] . special ! = 7 ) // This is a hack. I can at least hope nobody wants to prevent flat alignment in netgames...
2014-03-15 16:59:03 +00:00
{
2016-05-30 21:44:23 +00:00
// set line specials to 0 here too, same reason as above
if ( netgame | | multiplayer )
2014-03-15 16:59:03 +00:00
{
2019-07-08 20:56:00 +00:00
if ( lines [ i ] . flags & ML_NONET )
2019-07-02 22:10:22 +00:00
{
lines [ i ] . special = 0 ;
continue ;
}
2014-03-15 16:59:03 +00:00
}
2019-07-08 20:56:00 +00:00
else if ( lines [ i ] . flags & ML_NETONLY )
2014-03-15 16:59:03 +00:00
{
lines [ i ] . special = 0 ;
continue ;
}
}
switch ( lines [ i ] . special )
{
INT32 s ;
size_t sec ;
ffloortype_e ffloorflags ;
case 1 : // Definable gravity per sector
sec = sides [ * lines [ i ] . sidenum ] . sector - sectors ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
{
sectors [ s ] . gravity = & sectors [ sec ] . floorheight ; // This allows it to change in realtime!
if ( lines [ i ] . flags & ML_NOCLIMB )
sectors [ s ] . verticalflip = true ;
else
sectors [ s ] . verticalflip = false ;
CheckForReverseGravity = sectors [ s ] . verticalflip ;
}
break ;
case 2 : // Custom exit
break ;
case 3 : // Zoom Tube Parameters
break ;
case 4 : // Speed pad (combines with sector special Section3:5 or Section3:6)
break ;
case 5 : // Change camera info
sec = sides [ * lines [ i ] . sidenum ] . sector - sectors ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
P_AddCameraScanner ( & sectors [ sec ] , & sectors [ s ] , R_PointToAngle2 ( lines [ i ] . v2 - > x , lines [ i ] . v2 - > y , lines [ i ] . v1 - > x , lines [ i ] . v1 - > y ) ) ;
break ;
2016-05-30 21:44:23 +00:00
case 7 : // Flat alignment - redone by toast
2019-07-02 22:10:22 +00:00
if ( ( lines [ i ] . flags & ( ML_NETONLY | ML_NONET ) ) ! = ( ML_NETONLY | ML_NONET ) ) // If you can do something...
2014-03-15 16:59:03 +00:00
{
2016-05-30 21:44:23 +00:00
angle_t flatangle = InvAngle ( R_PointToAngle2 ( lines [ i ] . v1 - > x , lines [ i ] . v1 - > y , lines [ i ] . v2 - > x , lines [ i ] . v2 - > y ) ) ;
fixed_t xoffs ;
fixed_t yoffs ;
2016-06-03 01:24:45 +00:00
2019-07-02 22:10:22 +00:00
if ( lines [ i ] . flags & ML_EFFECT6 ) // Set offset through x and y texture offsets if ML_EFFECT6 flag is set
2014-03-15 16:59:03 +00:00
{
2016-05-30 21:44:23 +00:00
xoffs = sides [ lines [ i ] . sidenum [ 0 ] ] . textureoffset ;
yoffs = sides [ lines [ i ] . sidenum [ 0 ] ] . rowoffset ;
2014-03-15 16:59:03 +00:00
}
2016-05-30 21:44:23 +00:00
else // Otherwise, set calculated offsets such that line's v1 is the apparent origin
2014-03-15 16:59:03 +00:00
{
2017-03-30 20:57:36 +00:00
xoffs = - lines [ i ] . v1 - > x ;
2017-03-30 20:57:36 +00:00
yoffs = lines [ i ] . v1 - > y ;
2014-03-15 16:59:03 +00:00
}
2016-06-03 01:24:45 +00:00
2018-05-14 17:34:22 +00:00
//If no tag is given, apply to front sector
2020-04-17 08:52:12 +00:00
if ( tag = = 0 )
2018-05-14 17:34:22 +00:00
P_ApplyFlatAlignment ( lines + i , lines [ i ] . frontsector , flatangle , xoffs , yoffs ) ;
else
2014-03-15 16:59:03 +00:00
{
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2018-05-14 17:34:22 +00:00
P_ApplyFlatAlignment ( lines + i , sectors + s , flatangle , xoffs , yoffs ) ;
2014-03-15 16:59:03 +00:00
}
}
2016-05-29 19:47:08 +00:00
else // Otherwise, print a helpful warning. Can I do no less?
CONS_Alert ( CONS_WARNING ,
2016-05-30 21:44:23 +00:00
M_GetText ( " Flat alignment linedef (tag %d) doesn't have anything to do. \n Consider changing the linedef's flag configuration or removing it entirely. \n " ) ,
2020-04-17 08:52:12 +00:00
tag ) ;
2014-03-15 16:59:03 +00:00
break ;
case 8 : // Sector Parameters
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
{
if ( lines [ i ] . flags & ML_NOCLIMB )
{
sectors [ s ] . flags & = ~ SF_FLIPSPECIAL_FLOOR ;
sectors [ s ] . flags | = SF_FLIPSPECIAL_CEILING ;
}
else if ( lines [ i ] . flags & ML_EFFECT4 )
sectors [ s ] . flags | = SF_FLIPSPECIAL_BOTH ;
if ( lines [ i ] . flags & ML_EFFECT3 )
sectors [ s ] . flags | = SF_TRIGGERSPECIAL_TOUCH ;
2019-10-06 13:34:25 +00:00
if ( lines [ i ] . flags & ML_EFFECT2 )
sectors [ s ] . flags | = SF_TRIGGERSPECIAL_HEADBUMP ;
if ( lines [ i ] . flags & ML_EFFECT1 )
sectors [ s ] . flags | = SF_INVERTPRECIP ;
2014-08-04 03:49:33 +00:00
if ( lines [ i ] . frontsector & & GETSECSPECIAL ( lines [ i ] . frontsector - > special , 4 ) = = 12 )
sectors [ s ] . camsec = sides [ * lines [ i ] . sidenum ] . sector - sectors ;
2014-03-15 16:59:03 +00:00
}
break ;
case 9 : // Chain Parameters
break ;
case 10 : // Vertical culling plane for sprites and FOFs
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
sectors [ s ] . cullheight = & lines [ i ] ; // This allows it to change in realtime!
break ;
case 50 : // Insta-Lower Sector
EV_DoFloor ( & lines [ i ] , instantLower ) ;
break ;
case 51 : // Instant raise for ceilings
EV_DoCeiling ( & lines [ i ] , instantRaise ) ;
break ;
case 52 : // Continuously Falling sector
EV_DoContinuousFall ( lines [ i ] . frontsector , lines [ i ] . backsector , P_AproxDistance ( lines [ i ] . dx , lines [ i ] . dy ) , ( lines [ i ] . flags & ML_NOCLIMB ) ) ;
break ;
case 53 : // New super cool and awesome moving floor and ceiling type
case 54 : // New super cool and awesome moving floor type
if ( lines [ i ] . backsector )
EV_DoFloor ( & lines [ i ] , bounceFloor ) ;
if ( lines [ i ] . special = = 54 )
break ;
2017-09-28 13:39:47 +00:00
/* FALLTHRU */
2014-03-15 16:59:03 +00:00
case 55 : // New super cool and awesome moving ceiling type
if ( lines [ i ] . backsector )
EV_DoCeiling ( & lines [ i ] , bounceCeiling ) ;
break ;
case 56 : // New super cool and awesome moving floor and ceiling crush type
case 57 : // New super cool and awesome moving floor crush type
if ( lines [ i ] . backsector )
EV_DoFloor ( & lines [ i ] , bounceFloorCrush ) ;
if ( lines [ i ] . special = = 57 )
2017-09-28 13:39:47 +00:00
break ; //only move the floor
/* FALLTHRU */
2014-03-15 16:59:03 +00:00
case 58 : // New super cool and awesome moving ceiling crush type
if ( lines [ i ] . backsector )
EV_DoCeiling ( & lines [ i ] , bounceCeilingCrush ) ;
break ;
case 59 : // Activate floating platform
EV_DoElevator ( & lines [ i ] , elevateContinuous , false ) ;
break ;
case 60 : // Floating platform with adjustable speed
EV_DoElevator ( & lines [ i ] , elevateContinuous , true ) ;
break ;
case 61 : // Crusher!
EV_DoCrush ( & lines [ i ] , crushAndRaise ) ;
break ;
case 62 : // Crusher (up and then down)!
EV_DoCrush ( & lines [ i ] , fastCrushAndRaise ) ;
break ;
case 63 : // support for drawn heights coming from different sector
sec = sides [ * lines [ i ] . sidenum ] . sector - sectors ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
sectors [ s ] . heightsec = ( INT32 ) sec ;
break ;
case 64 : // Appearing/Disappearing FOF option
if ( lines [ i ] . flags & ML_BLOCKMONSTERS ) { // Find FOFs by control sector tag
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
for ( j = 0 ; ( unsigned ) j < sectors [ s ] . linecount ; j + + )
if ( sectors [ s ] . lines [ j ] - > special > = 100 & & sectors [ s ] . lines [ j ] - > special < 300 )
Add_MasterDisappearer ( abs ( lines [ i ] . dx > > FRACBITS ) , abs ( lines [ i ] . dy > > FRACBITS ) , abs ( sides [ lines [ i ] . sidenum [ 0 ] ] . sector - > floorheight > > FRACBITS ) , ( INT32 ) ( sectors [ s ] . lines [ j ] - lines ) , ( INT32 ) i ) ;
} else // Find FOFs by effect sector tag
2020-04-13 11:10:38 +00:00
{
2021-02-11 12:24:20 +00:00
TAG_ITER_LINES ( tag , s )
2014-03-15 16:59:03 +00:00
{
if ( ( size_t ) s = = i )
continue ;
2020-04-17 20:26:04 +00:00
if ( Tag_Find ( & sides [ lines [ s ] . sidenum [ 0 ] ] . sector - > tags , Tag_FGet ( & sides [ lines [ i ] . sidenum [ 0 ] ] . sector - > tags ) ) )
2014-03-15 16:59:03 +00:00
Add_MasterDisappearer ( abs ( lines [ i ] . dx > > FRACBITS ) , abs ( lines [ i ] . dy > > FRACBITS ) , abs ( sides [ lines [ i ] . sidenum [ 0 ] ] . sector - > floorheight > > FRACBITS ) , s , ( INT32 ) i ) ;
}
2020-04-13 11:10:38 +00:00
}
2014-03-15 16:59:03 +00:00
break ;
2016-12-13 22:42:47 +00:00
case 66 : // Displace floor by front sector
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2017-04-11 21:33:04 +00:00
P_AddPlaneDisplaceThinker ( pd_floor , P_AproxDistance ( lines [ i ] . dx , lines [ i ] . dy ) > > 8 , sides [ lines [ i ] . sidenum [ 0 ] ] . sector - sectors , s , ! ! ( lines [ i ] . flags & ML_NOCLIMB ) ) ;
2016-12-13 22:42:47 +00:00
break ;
case 67 : // Displace ceiling by front sector
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2017-04-11 21:33:04 +00:00
P_AddPlaneDisplaceThinker ( pd_ceiling , P_AproxDistance ( lines [ i ] . dx , lines [ i ] . dy ) > > 8 , sides [ lines [ i ] . sidenum [ 0 ] ] . sector - sectors , s , ! ! ( lines [ i ] . flags & ML_NOCLIMB ) ) ;
2016-12-13 22:42:47 +00:00
break ;
case 68 : // Displace both floor AND ceiling by front sector
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2017-04-11 21:33:04 +00:00
P_AddPlaneDisplaceThinker ( pd_both , P_AproxDistance ( lines [ i ] . dx , lines [ i ] . dy ) > > 8 , sides [ lines [ i ] . sidenum [ 0 ] ] . sector - sectors , s , ! ! ( lines [ i ] . flags & ML_NOCLIMB ) ) ;
2016-12-13 22:42:47 +00:00
break ;
2014-03-15 16:59:03 +00:00
case 100 : // FOF (solid, opaque, shadows)
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL , secthinkers ) ;
break ;
case 101 : // FOF (solid, opaque, no shadows)
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_NOSHADE | FF_CUTLEVEL , secthinkers ) ;
break ;
case 102 : // TL block: FOF (solid, translucent)
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_NOSHADE | FF_TRANSLUCENT | FF_EXTRA | FF_CUTEXTRA ;
// Draw the 'insides' of the block too
if ( lines [ i ] . flags & ML_NOCLIMB )
{
2016-06-06 17:30:58 +00:00
ffloorflags | = FF_CUTLEVEL | FF_BOTHPLANES | FF_ALLSIDES ;
ffloorflags & = ~ ( FF_EXTRA | FF_CUTEXTRA ) ;
2014-03-15 16:59:03 +00:00
}
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 103 : // Solid FOF with no floor/ceiling (quite possibly useless)
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERSIDES | FF_NOSHADE | FF_CUTLEVEL , secthinkers ) ;
break ;
case 104 : // 3D Floor type that doesn't draw sides
// If line has no-climb set, give it shadows, otherwise don't
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERPLANES | FF_CUTLEVEL ;
if ( ! ( lines [ i ] . flags & ML_NOCLIMB ) )
ffloorflags | = FF_NOSHADE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 105 : // FOF (solid, invisible)
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_NOSHADE , secthinkers ) ;
break ;
case 120 : // Opaque water
ffloorflags = FF_EXISTS | FF_RENDERALL | FF_SWIMMABLE | FF_BOTHPLANES | FF_ALLSIDES | FF_CUTEXTRA | FF_EXTRA | FF_CUTSPRITES ;
if ( lines [ i ] . flags & ML_NOCLIMB )
ffloorflags | = FF_DOUBLESHADOW ;
if ( lines [ i ] . flags & ML_EFFECT4 )
ffloorflags | = FF_COLORMAPONLY ;
if ( lines [ i ] . flags & ML_EFFECT5 )
ffloorflags | = FF_RIPPLE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 121 : // TL water
ffloorflags = FF_EXISTS | FF_RENDERALL | FF_TRANSLUCENT | FF_SWIMMABLE | FF_BOTHPLANES | FF_ALLSIDES | FF_CUTEXTRA | FF_EXTRA | FF_CUTSPRITES ;
if ( lines [ i ] . flags & ML_NOCLIMB )
ffloorflags | = FF_DOUBLESHADOW ;
if ( lines [ i ] . flags & ML_EFFECT4 )
ffloorflags | = FF_COLORMAPONLY ;
if ( lines [ i ] . flags & ML_EFFECT5 )
ffloorflags | = FF_RIPPLE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 122 : // Opaque water, no sides
ffloorflags = FF_EXISTS | FF_RENDERPLANES | FF_SWIMMABLE | FF_BOTHPLANES | FF_CUTEXTRA | FF_EXTRA | FF_CUTSPRITES ;
if ( lines [ i ] . flags & ML_NOCLIMB )
ffloorflags | = FF_DOUBLESHADOW ;
if ( lines [ i ] . flags & ML_EFFECT4 )
ffloorflags | = FF_COLORMAPONLY ;
if ( lines [ i ] . flags & ML_EFFECT5 )
ffloorflags | = FF_RIPPLE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 123 : // TL water, no sides
ffloorflags = FF_EXISTS | FF_RENDERPLANES | FF_TRANSLUCENT | FF_SWIMMABLE | FF_BOTHPLANES | FF_CUTEXTRA | FF_EXTRA | FF_CUTSPRITES ;
if ( lines [ i ] . flags & ML_NOCLIMB )
ffloorflags | = FF_DOUBLESHADOW ;
if ( lines [ i ] . flags & ML_EFFECT4 )
ffloorflags | = FF_COLORMAPONLY ;
if ( lines [ i ] . flags & ML_EFFECT5 )
ffloorflags | = FF_RIPPLE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 124 : // goo water
ffloorflags = FF_EXISTS | FF_RENDERALL | FF_TRANSLUCENT | FF_SWIMMABLE | FF_GOOWATER | FF_BOTHPLANES | FF_ALLSIDES | FF_CUTEXTRA | FF_EXTRA | FF_CUTSPRITES ;
if ( lines [ i ] . flags & ML_NOCLIMB )
ffloorflags | = FF_DOUBLESHADOW ;
if ( lines [ i ] . flags & ML_EFFECT4 )
ffloorflags | = FF_COLORMAPONLY ;
if ( lines [ i ] . flags & ML_EFFECT5 )
ffloorflags | = FF_RIPPLE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 125 : // goo water, no sides
ffloorflags = FF_EXISTS | FF_RENDERPLANES | FF_TRANSLUCENT | FF_SWIMMABLE | FF_GOOWATER | FF_BOTHPLANES | FF_CUTEXTRA | FF_EXTRA | FF_CUTSPRITES ;
if ( lines [ i ] . flags & ML_NOCLIMB )
ffloorflags | = FF_DOUBLESHADOW ;
if ( lines [ i ] . flags & ML_EFFECT4 )
ffloorflags | = FF_COLORMAPONLY ;
if ( lines [ i ] . flags & ML_EFFECT5 )
ffloorflags | = FF_RIPPLE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 140 : // 'Platform' - You can jump up through it
// If line has no-climb set, don't give it shadows, otherwise do
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_PLATFORM | FF_BOTHPLANES | FF_ALLSIDES ;
if ( lines [ i ] . flags & ML_NOCLIMB )
ffloorflags | = FF_NOSHADE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 141 : // Translucent "platform"
// If line has no-climb set, don't give it shadows, otherwise do
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_PLATFORM | FF_TRANSLUCENT | FF_EXTRA | FF_CUTEXTRA ;
if ( lines [ i ] . flags & ML_NOCLIMB )
ffloorflags | = FF_NOSHADE ;
// Draw the 'insides' of the block too
if ( lines [ i ] . flags & ML_EFFECT2 )
{
2016-06-06 17:30:58 +00:00
ffloorflags | = FF_CUTLEVEL | FF_BOTHPLANES | FF_ALLSIDES ;
ffloorflags & = ~ ( FF_EXTRA | FF_CUTEXTRA ) ;
2014-03-15 16:59:03 +00:00
}
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 142 : // Translucent "platform" with no sides
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERPLANES | FF_TRANSLUCENT | FF_PLATFORM | FF_EXTRA | FF_CUTEXTRA ;
if ( lines [ i ] . flags & ML_NOCLIMB ) // shade it unless no-climb
ffloorflags | = FF_NOSHADE ;
// Draw the 'insides' of the block too
if ( lines [ i ] . flags & ML_EFFECT2 )
{
2016-06-06 17:30:58 +00:00
ffloorflags | = FF_CUTLEVEL | FF_BOTHPLANES | FF_ALLSIDES ;
ffloorflags & = ~ ( FF_EXTRA | FF_CUTEXTRA ) ;
2014-03-15 16:59:03 +00:00
}
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 143 : // 'Reverse platform' - You fall through it
// If line has no-climb set, don't give it shadows, otherwise do
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_REVERSEPLATFORM | FF_BOTHPLANES | FF_ALLSIDES ;
if ( lines [ i ] . flags & ML_NOCLIMB )
ffloorflags | = FF_NOSHADE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 144 : // Translucent "reverse platform"
// If line has no-climb set, don't give it shadows, otherwise do
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_REVERSEPLATFORM | FF_TRANSLUCENT | FF_EXTRA | FF_CUTEXTRA ;
if ( lines [ i ] . flags & ML_NOCLIMB )
ffloorflags | = FF_NOSHADE ;
// Draw the 'insides' of the block too
if ( lines [ i ] . flags & ML_EFFECT2 )
{
2016-06-06 17:30:58 +00:00
ffloorflags | = FF_CUTLEVEL | FF_BOTHPLANES | FF_ALLSIDES ;
ffloorflags & = ~ ( FF_EXTRA | FF_CUTEXTRA ) ;
2014-03-15 16:59:03 +00:00
}
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 145 : // Translucent "reverse platform" with no sides
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERPLANES | FF_TRANSLUCENT | FF_REVERSEPLATFORM | FF_EXTRA | FF_CUTEXTRA ;
if ( lines [ i ] . flags & ML_NOCLIMB ) // shade it unless no-climb
ffloorflags | = FF_NOSHADE ;
// Draw the 'insides' of the block too
if ( lines [ i ] . flags & ML_EFFECT2 )
{
2016-06-06 17:30:58 +00:00
ffloorflags | = FF_CUTLEVEL | FF_BOTHPLANES | FF_ALLSIDES ;
ffloorflags & = ~ ( FF_EXTRA | FF_CUTEXTRA ) ;
2014-03-15 16:59:03 +00:00
}
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 146 : // Intangible floor/ceiling with solid sides (fences/hoops maybe?)
2020-01-17 03:34:31 +00:00
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERSIDES | FF_ALLSIDES | FF_INTANGIBLEFLATS , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
case 150 : // Air bobbing platform
case 151 : // Adjustable air bobbing platform
2020-04-17 09:11:36 +00:00
{
fixed_t dist = ( lines [ i ] . special = = 150 ) ? 16 * FRACUNIT : P_AproxDistance ( lines [ i ] . dx , lines [ i ] . dy ) ;
2014-03-15 16:59:03 +00:00
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL , secthinkers ) ;
2020-07-10 14:51:11 +00:00
P_AddAirbob ( lines [ i ] . frontsector , tag , dist , false , ! ! ( lines [ i ] . flags & ML_NOCLIMB ) , false ) ;
2014-03-15 16:59:03 +00:00
break ;
2020-04-17 09:11:36 +00:00
}
2014-03-15 16:59:03 +00:00
case 152 : // Adjustable air bobbing platform in reverse
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL , secthinkers ) ;
2020-07-10 14:51:11 +00:00
P_AddAirbob ( lines [ i ] . frontsector , tag , P_AproxDistance ( lines [ i ] . dx , lines [ i ] . dy ) , true , ! ! ( lines [ i ] . flags & ML_NOCLIMB ) , false ) ;
2019-10-11 20:23:11 +00:00
break ;
case 153 : // Dynamic Sinking Platform
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL , secthinkers ) ;
2020-07-10 14:51:11 +00:00
P_AddAirbob ( lines [ i ] . frontsector , tag , P_AproxDistance ( lines [ i ] . dx , lines [ i ] . dy ) , false , ! ! ( lines [ i ] . flags & ML_NOCLIMB ) , true ) ;
2014-03-15 16:59:03 +00:00
break ;
case 160 : // Float/bob platform
2015-01-01 19:50:31 +00:00
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL | FF_FLOATBOB , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
case 170 : // Crumbling platform
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL | FF_CRUMBLE , secthinkers ) ;
break ;
case 171 : // Crumbling platform that will not return
P_AddFakeFloorsByLine ( i ,
FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL | FF_CRUMBLE | FF_NORETURN , secthinkers ) ;
break ;
case 172 : // "Platform" that crumbles and returns
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_PLATFORM | FF_CRUMBLE | FF_BOTHPLANES | FF_ALLSIDES ;
if ( lines [ i ] . flags & ML_NOCLIMB ) // shade it unless no-climb
ffloorflags | = FF_NOSHADE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 173 : // "Platform" that crumbles and doesn't return
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_PLATFORM | FF_CRUMBLE | FF_NORETURN | FF_BOTHPLANES | FF_ALLSIDES ;
if ( lines [ i ] . flags & ML_NOCLIMB ) // shade it unless no-climb
ffloorflags | = FF_NOSHADE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 174 : // Translucent "platform" that crumbles and returns
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL | FF_PLATFORM | FF_CRUMBLE | FF_TRANSLUCENT | FF_BOTHPLANES | FF_ALLSIDES ;
if ( lines [ i ] . flags & ML_NOCLIMB ) // shade it unless no-climb
ffloorflags | = FF_NOSHADE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 175 : // Translucent "platform" that crumbles and doesn't return
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL | FF_PLATFORM | FF_CRUMBLE | FF_NORETURN | FF_TRANSLUCENT | FF_BOTHPLANES | FF_ALLSIDES ;
if ( lines [ i ] . flags & ML_NOCLIMB ) // shade it unless no-climb
ffloorflags | = FF_NOSHADE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 176 : // Air bobbing platform that will crumble and bob on the water when it falls and hits
2015-01-01 19:50:31 +00:00
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_FLOATBOB | FF_CRUMBLE , secthinkers ) ;
2020-07-10 14:51:11 +00:00
P_AddAirbob ( lines [ i ] . frontsector , tag , 16 * FRACUNIT , false , ! ! ( lines [ i ] . flags & ML_NOCLIMB ) , false ) ;
2014-03-15 16:59:03 +00:00
break ;
case 177 : // Air bobbing platform that will crumble and bob on
// the water when it falls and hits, then never return
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL | FF_FLOATBOB | FF_CRUMBLE | FF_NORETURN , secthinkers ) ;
2020-07-10 14:51:11 +00:00
P_AddAirbob ( lines [ i ] . frontsector , tag , 16 * FRACUNIT , false , ! ! ( lines [ i ] . flags & ML_NOCLIMB ) , false ) ;
2014-03-15 16:59:03 +00:00
break ;
case 178 : // Crumbling platform that will float when it hits water
2015-01-01 19:50:31 +00:00
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CRUMBLE | FF_FLOATBOB , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
case 179 : // Crumbling platform that will float when it hits water, but not return
2015-01-01 19:50:31 +00:00
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL | FF_CRUMBLE | FF_FLOATBOB | FF_NORETURN , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
case 180 : // Air bobbing platform that will crumble
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL | FF_CRUMBLE , secthinkers ) ;
2020-07-10 14:51:11 +00:00
P_AddAirbob ( lines [ i ] . frontsector , tag , 16 * FRACUNIT , false , ! ! ( lines [ i ] . flags & ML_NOCLIMB ) , false ) ;
2014-03-15 16:59:03 +00:00
break ;
case 190 : // Rising Platform FOF (solid, opaque, shadows)
case 191 : // Rising Platform FOF (solid, opaque, no shadows)
case 192 : // Rising Platform TL block: FOF (solid, translucent)
case 193 : // Rising Platform FOF (solid, invisible)
case 194 : // Rising Platform 'Platform' - You can jump up through it
case 195 : // Rising Platform Translucent "platform"
2020-04-27 09:19:07 +00:00
{
fixed_t speed = FixedDiv ( P_AproxDistance ( lines [ i ] . dx , lines [ i ] . dy ) , 4 * FRACUNIT ) ;
fixed_t ceilingtop = P_FindHighestCeilingSurrounding ( lines [ i ] . frontsector ) ;
fixed_t ceilingbottom = P_FindLowestCeilingSurrounding ( lines [ i ] . frontsector ) ;
ffloorflags = FF_EXISTS | FF_SOLID ;
if ( lines [ i ] . special ! = 193 )
ffloorflags | = FF_RENDERALL ;
if ( lines [ i ] . special < = 191 )
ffloorflags | = FF_CUTLEVEL ;
if ( lines [ i ] . special = = 192 | | lines [ i ] . special = = 195 )
ffloorflags | = FF_TRANSLUCENT | FF_EXTRA | FF_CUTEXTRA ;
if ( lines [ i ] . special > = 194 )
ffloorflags | = FF_PLATFORM | FF_BOTHPLANES | FF_ALLSIDES ;
if ( lines [ i ] . special ! = 190 & & ( lines [ i ] . special < = 193 | | lines [ i ] . flags & ML_NOCLIMB ) )
2014-03-15 16:59:03 +00:00
ffloorflags | = FF_NOSHADE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
2020-04-27 09:19:07 +00:00
2020-07-10 14:51:11 +00:00
P_AddRaiseThinker ( lines [ i ] . frontsector , tag , speed , ceilingtop , ceilingbottom , ! ! ( lines [ i ] . flags & ML_BLOCKMONSTERS ) , ! ! ( lines [ i ] . flags & ML_NOCLIMB ) ) ;
2014-03-15 16:59:03 +00:00
break ;
2020-04-27 09:19:07 +00:00
}
2014-03-15 16:59:03 +00:00
case 200 : // Double light effect
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_CUTSPRITES | FF_DOUBLESHADOW , secthinkers ) ;
break ;
case 201 : // Light effect
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_CUTSPRITES , secthinkers ) ;
break ;
case 202 : // Fog
2020-03-25 07:34:52 +00:00
ffloorflags = FF_EXISTS | FF_RENDERALL | FF_FOG | FF_INVERTPLANES | FF_INVERTSIDES | FF_CUTEXTRA | FF_EXTRA | FF_DOUBLESHADOW | FF_CUTSPRITES ;
2014-03-15 16:59:03 +00:00
sec = sides [ * lines [ i ] . sidenum ] . sector - sectors ;
2020-02-16 19:19:24 +00:00
// SoM: Because it's fog, check for an extra colormap and set the fog flag...
2014-03-15 16:59:03 +00:00
if ( sectors [ sec ] . extra_colormap )
2020-02-16 19:19:24 +00:00
sectors [ sec ] . extra_colormap - > flags = CMF_FOG ;
2015-01-01 19:50:31 +00:00
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
case 220 : // Like opaque water, but not swimmable. (Good for snow effect on FOFs)
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_RENDERALL | FF_BOTHPLANES | FF_ALLSIDES | FF_CUTEXTRA | FF_EXTRA | FF_CUTSPRITES , secthinkers ) ;
break ;
case 221 : // FOF (intangible, translucent)
// If line has no-climb set, give it shadows, otherwise don't
ffloorflags = FF_EXISTS | FF_RENDERALL | FF_TRANSLUCENT | FF_EXTRA | FF_CUTEXTRA | FF_CUTSPRITES ;
if ( ! ( lines [ i ] . flags & ML_NOCLIMB ) )
ffloorflags | = FF_NOSHADE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 222 : // FOF with no floor/ceiling (good for GFZGRASS effect on FOFs)
// If line has no-climb set, give it shadows, otherwise don't
ffloorflags = FF_EXISTS | FF_RENDERSIDES | FF_ALLSIDES ;
if ( ! ( lines [ i ] . flags & ML_NOCLIMB ) )
ffloorflags | = FF_NOSHADE | FF_CUTSPRITES ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
break ;
case 223 : // FOF (intangible, invisible) - for combining specials in a sector
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_NOSHADE , secthinkers ) ;
break ;
case 250 : // Mario Block
2016-06-06 17:30:58 +00:00
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL | FF_MARIO ;
if ( lines [ i ] . flags & ML_NOCLIMB )
ffloorflags | = FF_SHATTERBOTTOM ;
if ( lines [ i ] . flags & ML_EFFECT1 )
ffloorflags & = ~ ( FF_SOLID | FF_RENDERALL | FF_CUTLEVEL ) ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
case 251 : // A THWOMP!
2020-04-27 11:01:31 +00:00
{
fixed_t crushspeed = ( lines [ i ] . flags & ML_EFFECT5 ) ? lines [ i ] . dy > > 3 : 10 * FRACUNIT ;
fixed_t retractspeed = ( lines [ i ] . flags & ML_EFFECT5 ) ? lines [ i ] . dx > > 3 : 2 * FRACUNIT ;
UINT16 sound = ( lines [ i ] . flags & ML_EFFECT4 ) ? sides [ lines [ i ] . sidenum [ 0 ] ] . textureoffset > > FRACBITS : sfx_thwomp ;
2020-11-10 12:10:01 +00:00
P_AddThwompThinker ( lines [ i ] . frontsector , & lines [ i ] , crushspeed , retractspeed , sound ) ;
2020-05-02 07:15:34 +00:00
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_CUTLEVEL , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
2020-04-27 11:01:31 +00:00
}
2014-03-15 16:59:03 +00:00
case 252 : // Shatter block (breaks when touched)
2019-10-14 18:15:03 +00:00
ffloorflags = FF_EXISTS | FF_BLOCKOTHERS | FF_RENDERALL | FF_BUSTUP | FF_SHATTER ;
2014-03-15 16:59:03 +00:00
if ( lines [ i ] . flags & ML_NOCLIMB )
2019-10-14 18:15:03 +00:00
ffloorflags | = FF_BLOCKPLAYER | FF_SHATTERBOTTOM ;
2016-06-06 17:30:58 +00:00
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
case 253 : // Translucent shatter block (see 76)
2019-10-14 18:15:03 +00:00
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_BLOCKOTHERS | FF_RENDERALL | FF_BUSTUP | FF_SHATTER | FF_TRANSLUCENT , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
case 254 : // Bustable block
2016-06-06 17:30:58 +00:00
ffloorflags = FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_BUSTUP ;
2014-03-15 16:59:03 +00:00
if ( lines [ i ] . flags & ML_NOCLIMB )
2019-10-19 20:20:48 +00:00
ffloorflags | = FF_STRONGBUST ;
2016-06-06 17:30:58 +00:00
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
case 255 : // Spin bust block (breaks when jumped or spun downwards onto)
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_BUSTUP | FF_SPINBUST , secthinkers ) ;
break ;
case 256 : // Translucent spin bust block (see 78)
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_SOLID | FF_RENDERALL | FF_BUSTUP | FF_SPINBUST | FF_TRANSLUCENT , secthinkers ) ;
break ;
case 257 : // Quicksand
2016-06-06 17:30:58 +00:00
ffloorflags = FF_EXISTS | FF_QUICKSAND | FF_RENDERALL | FF_ALLSIDES | FF_CUTSPRITES ;
2014-03-15 16:59:03 +00:00
if ( lines [ i ] . flags & ML_EFFECT5 )
2016-06-06 17:30:58 +00:00
ffloorflags | = FF_RIPPLE ;
P_AddFakeFloorsByLine ( i , ffloorflags , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
case 258 : // Laser block
2020-07-10 14:51:11 +00:00
P_AddLaserThinker ( tag , lines + i , ! ! ( lines [ i ] . flags & ML_EFFECT1 ) ) ;
2020-05-02 08:03:16 +00:00
P_AddFakeFloorsByLine ( i , FF_EXISTS | FF_RENDERALL | FF_NOSHADE | FF_EXTRA | FF_CUTEXTRA | FF_TRANSLUCENT , secthinkers ) ;
2014-03-15 16:59:03 +00:00
break ;
2019-12-29 11:01:41 +00:00
case 259 : // Custom FOF
2014-03-15 16:59:03 +00:00
if ( lines [ i ] . sidenum [ 1 ] ! = 0xffff )
{
2019-12-29 11:01:41 +00:00
ffloortype_e fofflags = sides [ lines [ i ] . sidenum [ 1 ] ] . toptexture ;
P_AddFakeFloorsByLine ( i , fofflags , secthinkers ) ;
2014-03-15 16:59:03 +00:00
}
else
2020-04-17 08:52:12 +00:00
I_Error ( " Custom FOF (tag %d) found without a linedef back side! " , tag ) ;
2014-03-15 16:59:03 +00:00
break ;
case 300 : // Linedef executor (combines with sector special 974/975) and commands
case 302 :
case 303 :
case 304 :
// Charability linedef executors
case 305 :
case 307 :
break ;
case 308 : // Race-only linedef executor. Triggers once.
2019-12-18 04:54:45 +00:00
if ( ! ( gametyperules & GTR_RACE ) )
2014-03-15 16:59:03 +00:00
lines [ i ] . special = 0 ;
break ;
// Linedef executor triggers for CTF teams.
case 309 :
case 311 :
2019-12-18 14:43:50 +00:00
if ( ! ( gametyperules & GTR_TEAMFLAGS ) )
2014-03-15 16:59:03 +00:00
lines [ i ] . special = 0 ;
break ;
// Each time executors
case 306 :
case 301 :
case 310 :
case 312 :
2019-07-03 07:19:29 +00:00
case 332 :
2020-01-11 23:40:57 +00:00
case 335 :
2020-04-17 12:00:48 +00:00
P_AddEachTimeThinker ( & lines [ i ] ) ;
2014-03-15 16:59:03 +00:00
break ;
// No More Enemies Linedef Exec
case 313 :
2020-04-18 07:21:04 +00:00
P_AddNoEnemiesThinker ( & lines [ i ] ) ;
2014-03-15 16:59:03 +00:00
break ;
// Pushable linedef executors (count # of pushables)
case 314 :
case 315 :
break ;
// Unlock trigger executors
case 317 :
case 318 :
break ;
2014-08-04 03:49:33 +00:00
case 319 :
case 320 :
break ;
2014-03-15 16:59:03 +00:00
2014-11-12 00:55:07 +00:00
// Trigger on X calls
case 321 :
case 322 :
if ( lines [ i ] . flags & ML_NOCLIMB & & sides [ lines [ i ] . sidenum [ 0 ] ] . rowoffset > 0 ) // optional "starting" count
lines [ i ] . callcount = sides [ lines [ i ] . sidenum [ 0 ] ] . rowoffset > > FRACBITS ;
else
lines [ i ] . callcount = sides [ lines [ i ] . sidenum [ 0 ] ] . textureoffset > > FRACBITS ;
if ( lines [ i ] . special = = 322 ) // Each time
2020-04-17 12:00:48 +00:00
P_AddEachTimeThinker ( & lines [ i ] ) ;
2014-11-12 00:55:07 +00:00
break ;
2018-03-30 18:22:59 +00:00
// NiGHTS trigger executors
case 323 :
case 324 :
case 325 :
2018-04-01 12:23:59 +00:00
case 326 :
case 327 :
case 328 :
case 329 :
case 330 :
2018-03-30 18:22:59 +00:00
break ;
2019-07-03 07:19:29 +00:00
// Skin trigger executors
case 331 :
case 333 :
break ;
2020-04-12 13:16:04 +00:00
2020-01-11 23:40:57 +00:00
// Object dye executors
case 334 :
case 336 :
break ;
2019-07-03 07:19:29 +00:00
2014-03-15 16:59:03 +00:00
case 399 : // Linedef execute on map load
// This is handled in P_RunLevelLoadExecutors.
break ;
case 400 :
case 401 :
case 402 :
case 403 :
case 404 :
case 405 :
case 406 :
case 407 :
case 408 :
case 409 :
case 410 :
case 411 :
case 412 :
case 413 :
case 414 :
case 415 :
case 416 :
case 417 :
case 418 :
case 419 :
case 420 :
case 421 :
case 422 :
case 423 :
case 424 :
case 425 :
case 426 :
case 427 :
case 428 :
case 429 :
case 430 :
case 431 :
break ;
2019-07-31 22:17:17 +00:00
case 449 : // Enable bosses with parameter
{
INT32 bossid = sides [ * lines [ i ] . sidenum ] . textureoffset > > FRACBITS ;
if ( bossid & ~ 15 ) // if any bits other than first 16 are set
{
CONS_Alert ( CONS_WARNING ,
M_GetText ( " Boss enable linedef (tag %d) has an invalid texture x offset. \n Consider changing it or removing it entirely. \n " ) ,
2020-04-17 08:52:12 +00:00
tag ) ;
2019-07-31 22:17:17 +00:00
break ;
}
if ( ! ( lines [ i ] . flags & ML_NOCLIMB ) )
{
bossdisabled | = ( 1 < < bossid ) ; // gotta disable in the first place to enable
CONS_Debug ( DBG_GAMELOGIC , " Line type 449 spawn effect: bossid disabled = %d " , bossid ) ;
}
break ;
}
2014-03-15 16:59:03 +00:00
// 500 is used for a scroller
// 501 is used for a scroller
// 502 is used for a scroller
// 503 is used for a scroller
// 504 is used for a scroller
// 505 is used for a scroller
2020-05-19 21:39:35 +00:00
// 506 is used for a scroller
// 507 is used for a scroller
// 508 is used for a scroller
2014-03-15 16:59:03 +00:00
// 510 is used for a scroller
// 511 is used for a scroller
// 512 is used for a scroller
// 513 is used for a scroller
// 514 is used for a scroller
// 515 is used for a scroller
// 520 is used for a scroller
// 521 is used for a scroller
// 522 is used for a scroller
// 523 is used for a scroller
// 524 is used for a scroller
// 525 is used for a scroller
// 530 is used for a scroller
// 531 is used for a scroller
// 532 is used for a scroller
// 533 is used for a scroller
// 534 is used for a scroller
// 535 is used for a scroller
// 540 is used for friction
// 541 is used for wind
// 542 is used for upwards wind
// 543 is used for downwards wind
// 544 is used for current
// 545 is used for upwards current
// 546 is used for downwards current
// 547 is used for push/pull
case 600 : // floor lighting independently (e.g. lava)
sec = sides [ * lines [ i ] . sidenum ] . sector - sectors ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
sectors [ s ] . floorlightsec = ( INT32 ) sec ;
break ;
case 601 : // ceiling lighting independently
sec = sides [ * lines [ i ] . sidenum ] . sector - sectors ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
sectors [ s ] . ceilinglightsec = ( INT32 ) sec ;
break ;
case 602 : // Adjustable pulsating light
sec = sides [ * lines [ i ] . sidenum ] . sector - sectors ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
P_SpawnAdjustableGlowingLight ( & sectors [ sec ] , & sectors [ s ] ,
P_AproxDistance ( lines [ i ] . dx , lines [ i ] . dy ) > > FRACBITS ) ;
break ;
case 603 : // Adjustable flickering light
sec = sides [ * lines [ i ] . sidenum ] . sector - sectors ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
P_SpawnAdjustableFireFlicker ( & sectors [ sec ] , & sectors [ s ] ,
P_AproxDistance ( lines [ i ] . dx , lines [ i ] . dy ) > > FRACBITS ) ;
break ;
case 604 : // Adjustable Blinking Light (unsynchronized)
sec = sides [ * lines [ i ] . sidenum ] . sector - sectors ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
P_SpawnAdjustableStrobeFlash ( & sectors [ sec ] , & sectors [ s ] ,
abs ( lines [ i ] . dx ) > > FRACBITS , abs ( lines [ i ] . dy ) > > FRACBITS , false ) ;
break ;
case 605 : // Adjustable Blinking Light (synchronized)
sec = sides [ * lines [ i ] . sidenum ] . sector - sectors ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
P_SpawnAdjustableStrobeFlash ( & sectors [ sec ] , & sectors [ s ] ,
abs ( lines [ i ] . dx ) > > FRACBITS , abs ( lines [ i ] . dy ) > > FRACBITS , true ) ;
break ;
case 606 : // HACK! Copy colormaps. Just plain colormaps.
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( lines [ i ] . args [ 0 ] , s )
2020-03-20 10:19:30 +00:00
{
extracolormap_t * exc ;
2020-03-20 11:19:02 +00:00
if ( sectors [ s ] . colormap_protected )
continue ;
2020-03-20 10:19:30 +00:00
if ( ! udmf )
exc = sides [ lines [ i ] . sidenum [ 0 ] ] . colormap_data ;
else
{
if ( ! lines [ i ] . args [ 1 ] )
exc = lines [ i ] . frontsector - > extra_colormap ;
else
{
2020-04-18 12:04:15 +00:00
INT32 sourcesec = Tag_Iterate_Sectors ( lines [ i ] . args [ 1 ] , 0 ) ;
2020-03-20 10:19:30 +00:00
if ( sourcesec = = - 1 )
{
CONS_Debug ( DBG_GAMELOGIC , " Line type 606: Can't find sector with source colormap (tag %d)! \n " , lines [ i ] . args [ 1 ] ) ;
return ;
}
exc = sectors [ sourcesec ] . extra_colormap ;
}
}
sectors [ s ] . extra_colormap = sectors [ s ] . spawn_extra_colormap = exc ;
}
2014-03-15 16:59:03 +00:00
break ;
default :
break ;
}
}
// Allocate each list
for ( i = 0 ; i < numsectors ; i + + )
if ( secthinkers [ i ] . thinkers )
Z_Free ( secthinkers [ i ] . thinkers ) ;
Z_Free ( secthinkers ) ;
// haleyjd 02/20/06: spawn polyobjects
Polyobj_InitLevel ( ) ;
for ( i = 0 ; i < numlines ; i + + )
{
switch ( lines [ i ] . special )
{
case 30 : // Polyobj_Flag
2020-05-04 07:58:27 +00:00
PolyFlag ( & lines [ i ] ) ;
2014-03-15 16:59:03 +00:00
break ;
2014-11-12 00:55:07 +00:00
case 31 : // Polyobj_Displace
PolyDisplace ( & lines [ i ] ) ;
break ;
2019-04-05 09:50:59 +00:00
case 32 : // Polyobj_RotDisplace
PolyRotDisplace ( & lines [ i ] ) ;
break ;
2014-03-15 16:59:03 +00:00
}
}
P_RunLevelLoadExecutors ( ) ;
}
/** Adds 3Dfloors as appropriate based on a common control linedef.
*
* \ param line Control linedef to use .
* \ param ffloorflags 3 Dfloor flags to use .
* \ param secthkiners Lists of thinkers sorted by sector . May be NULL .
* \ sa P_SpawnSpecials , P_AddFakeFloor
* \ author Graue < graue @ oceanbase . org >
*/
static void P_AddFakeFloorsByLine ( size_t line , ffloortype_e ffloorflags , thinkerlist_t * secthinkers )
{
INT32 s ;
2020-04-17 16:15:25 +00:00
mtag_t tag = Tag_FGet ( & lines [ line ] . tags ) ;
2014-03-15 16:59:03 +00:00
size_t sec = sides [ * lines [ line ] . sidenum ] . sector - sectors ;
2020-04-12 16:52:47 +00:00
line_t * li = lines + line ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2020-04-12 16:52:47 +00:00
P_AddFakeFloor ( & sectors [ s ] , & sectors [ sec ] , li , ffloorflags , secthinkers ) ;
2014-03-15 16:59:03 +00:00
}
/*
SoM : 3 / 8 / 2000 : General scrolling functions .
T_Scroll ,
Add_Scroller ,
Add_WallScroller ,
P_SpawnScrollers
*/
2014-11-12 00:55:07 +00:00
// helper function for T_Scroll
static void P_DoScrollMove ( mobj_t * thing , fixed_t dx , fixed_t dy , INT32 exclusive )
{
fixed_t fuckaj = 0 ; // Nov 05 14:12:08 <+MonsterIestyn> I've heard of explicitly defined variables but this is ridiculous
if ( thing - > player )
{
if ( ! ( dx | dy ) )
{
thing - > player - > cmomx = 0 ;
thing - > player - > cmomy = 0 ;
}
else
{
thing - > player - > cmomx + = dx ;
thing - > player - > cmomy + = dy ;
thing - > player - > cmomx = FixedMul ( thing - > player - > cmomx , 0xe800 ) ;
thing - > player - > cmomy = FixedMul ( thing - > player - > cmomy , 0xe800 ) ;
}
}
if ( thing - > player & & ( thing - > player - > pflags & PF_SPINNING ) & & ( thing - > player - > rmomx | | thing - > player - > rmomy ) & & ! ( thing - > player - > pflags & PF_STARTDASH ) )
fuckaj = FixedDiv ( 549 * ORIG_FRICTION , 500 * FRACUNIT ) ;
else if ( thing - > friction ! = ORIG_FRICTION )
fuckaj = thing - > friction ;
if ( fuckaj ) {
// refactor thrust for new friction
dx = FixedDiv ( dx , CARRYFACTOR ) ;
dy = FixedDiv ( dy , CARRYFACTOR ) ;
dx = FixedMul ( dx , FRACUNIT - fuckaj ) ;
dy = FixedMul ( dy , FRACUNIT - fuckaj ) ;
}
thing - > momx + = dx ;
thing - > momy + = dy ;
if ( exclusive )
2016-03-09 09:30:52 +00:00
thing - > eflags | = MFE_PUSHED ;
2014-11-12 00:55:07 +00:00
}
2014-03-15 16:59:03 +00:00
/** Processes an active scroller.
* 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 an
* edge around a sector can be used to control the direction of the sector ' s
* scrolling , which is usually what is desired .
*
* \ param s Thinker for the scroller to process .
* \ todo Split up into multiple functions .
* \ todo Use attached lists to make : : sc_carry_ceiling case faster and
* cleaner .
* \ sa Add_Scroller , Add_WallScroller , P_SpawnScrollers
* \ author Steven McGranahan
* \ author Graue < graue @ oceanbase . org >
*/
void T_Scroll ( scroll_t * s )
{
fixed_t dx = s - > dx , dy = s - > dy ;
boolean is3dblock = false ;
if ( s - > control ! = - 1 )
{ // compute scroll amounts based on a sector's height changes
fixed_t height = sectors [ s - > control ] . floorheight +
sectors [ s - > control ] . ceilingheight ;
fixed_t delta = height - s - > last_height ;
s - > last_height = height ;
dx = FixedMul ( dx , delta ) ;
dy = FixedMul ( dy , delta ) ;
}
if ( s - > accel )
{
s - > vdx = dx + = s - > vdx ;
s - > vdy = dy + = s - > vdy ;
}
// if (!(dx | dy)) // no-op if both (x,y) offsets 0
// return;
switch ( s - > type )
{
side_t * side ;
sector_t * sec ;
fixed_t height ;
msecnode_t * node ;
mobj_t * thing ;
line_t * line ;
size_t i ;
INT32 sect ;
2018-05-21 19:02:30 +00:00
ffloor_t * rover ;
2014-03-15 16:59:03 +00:00
case sc_side : // scroll wall texture
side = sides + s - > affectee ;
side - > textureoffset + = dx ;
side - > rowoffset + = dy ;
break ;
case sc_floor : // scroll floor texture
sec = sectors + s - > affectee ;
sec - > floor_xoffs + = dx ;
sec - > floor_yoffs + = dy ;
break ;
case sc_ceiling : // scroll ceiling texture
sec = sectors + s - > affectee ;
sec - > ceiling_xoffs + = dx ;
sec - > ceiling_yoffs + = dy ;
break ;
case sc_carry :
sec = sectors + s - > affectee ;
height = sec - > floorheight ;
// sec is the control sector, find the real sector(s) to use
for ( i = 0 ; i < sec - > linecount ; i + + )
{
line = sec - > lines [ i ] ;
if ( line - > special < 100 | | line - > special > = 300 )
is3dblock = false ;
else
is3dblock = true ;
if ( ! is3dblock )
continue ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( Tag_FGet ( & line - > tags ) , sect )
2014-03-15 16:59:03 +00:00
{
sector_t * psec ;
psec = sectors + sect ;
2018-05-21 19:02:30 +00:00
// Find the FOF corresponding to the control linedef
for ( rover = psec - > ffloors ; rover ; rover = rover - > next )
{
if ( rover - > master = = sec - > lines [ i ] )
break ;
}
if ( ! rover ) // This should be impossible, but don't complain if it is the case somehow
continue ;
if ( ! ( rover - > flags & FF_EXISTS ) ) // If the FOF does not "exist", we pretend that nobody's there
continue ;
2016-06-09 13:16:02 +00:00
for ( node = psec - > touching_thinglist ; node ; node = node - > m_thinglist_next )
2014-03-15 16:59:03 +00:00
{
thing = node - > m_thing ;
2016-03-09 09:30:52 +00:00
if ( thing - > eflags & MFE_PUSHED ) // Already pushed this tic by an exclusive pusher.
2014-03-15 16:59:03 +00:00
continue ;
2015-05-24 17:53:30 +00:00
height = P_GetSpecialBottomZ ( thing , sec , psec ) ;
2014-03-15 16:59:03 +00:00
if ( ! ( thing - > flags & MF_NOCLIP ) ) // Thing must be clipped
if ( ! ( thing - > flags & MF_NOGRAVITY | | thing - > z + thing - > height ! = height ) ) // Thing must a) be non-floating and have z+height == height
{
// Move objects only if on floor
// non-floating, and clipped.
2014-11-12 00:55:07 +00:00
P_DoScrollMove ( thing , dx , dy , s - > exclusive ) ;
2014-03-15 16:59:03 +00:00
}
} // end of for loop through touching_thinglist
} // end of loop through sectors
}
if ( ! is3dblock )
{
2016-06-09 13:16:02 +00:00
for ( node = sec - > touching_thinglist ; node ; node = node - > m_thinglist_next )
2014-03-15 16:59:03 +00:00
{
thing = node - > m_thing ;
2016-03-09 09:30:52 +00:00
if ( thing - > eflags & MFE_PUSHED )
2014-03-15 16:59:03 +00:00
continue ;
2015-05-24 17:53:30 +00:00
height = P_GetSpecialBottomZ ( thing , sec , sec ) ;
2014-11-12 00:55:07 +00:00
if ( ! ( thing - > flags & MF_NOCLIP ) & &
2014-03-15 16:59:03 +00:00
( ! ( thing - > flags & MF_NOGRAVITY | | thing - > z > height ) ) )
{
// Move objects only if on floor or underwater,
// non-floating, and clipped.
2014-11-12 00:55:07 +00:00
P_DoScrollMove ( thing , dx , dy , s - > exclusive ) ;
2014-03-15 16:59:03 +00:00
}
}
}
break ;
case sc_carry_ceiling : // carry on ceiling (FOF scrolling)
sec = sectors + s - > affectee ;
height = sec - > ceilingheight ;
// sec is the control sector, find the real sector(s) to use
for ( i = 0 ; i < sec - > linecount ; i + + )
{
line = sec - > lines [ i ] ;
if ( line - > special < 100 | | line - > special > = 300 )
is3dblock = false ;
else
is3dblock = true ;
if ( ! is3dblock )
continue ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( Tag_FGet ( & line - > tags ) , sect )
2014-03-15 16:59:03 +00:00
{
sector_t * psec ;
psec = sectors + sect ;
2018-05-21 19:02:30 +00:00
// Find the FOF corresponding to the control linedef
for ( rover = psec - > ffloors ; rover ; rover = rover - > next )
{
if ( rover - > master = = sec - > lines [ i ] )
break ;
}
if ( ! rover ) // This should be impossible, but don't complain if it is the case somehow
continue ;
if ( ! ( rover - > flags & FF_EXISTS ) ) // If the FOF does not "exist", we pretend that nobody's there
continue ;
2016-06-09 13:16:02 +00:00
for ( node = psec - > touching_thinglist ; node ; node = node - > m_thinglist_next )
2014-03-15 16:59:03 +00:00
{
thing = node - > m_thing ;
2016-03-09 09:30:52 +00:00
if ( thing - > eflags & MFE_PUSHED )
2014-03-15 16:59:03 +00:00
continue ;
2015-05-24 17:53:30 +00:00
height = P_GetSpecialTopZ ( thing , sec , psec ) ;
2014-03-15 16:59:03 +00:00
if ( ! ( thing - > flags & MF_NOCLIP ) ) // Thing must be clipped
if ( ! ( thing - > flags & MF_NOGRAVITY | | thing - > z ! = height ) ) // Thing must a) be non-floating and have z == height
{
// Move objects only if on floor or underwater,
// non-floating, and clipped.
2014-11-12 00:55:07 +00:00
P_DoScrollMove ( thing , dx , dy , s - > exclusive ) ;
2014-03-15 16:59:03 +00:00
}
} // end of for loop through touching_thinglist
} // end of loop through sectors
}
if ( ! is3dblock )
{
2016-06-09 13:16:02 +00:00
for ( node = sec - > touching_thinglist ; node ; node = node - > m_thinglist_next )
2014-03-15 16:59:03 +00:00
{
thing = node - > m_thing ;
2016-03-09 09:30:52 +00:00
if ( thing - > eflags & MFE_PUSHED )
2014-03-15 16:59:03 +00:00
continue ;
2015-05-24 17:53:30 +00:00
height = P_GetSpecialTopZ ( thing , sec , sec ) ;
2014-11-12 00:55:07 +00:00
if ( ! ( thing - > flags & MF_NOCLIP ) & &
2014-08-04 03:49:33 +00:00
( ! ( thing - > flags & MF_NOGRAVITY | | thing - > z + thing - > height < height ) ) )
2014-03-15 16:59:03 +00:00
{
// Move objects only if on floor or underwater,
// non-floating, and clipped.
2014-11-12 00:55:07 +00:00
P_DoScrollMove ( thing , dx , dy , s - > exclusive ) ;
2014-03-15 16:59:03 +00:00
}
}
}
break ; // end of sc_carry_ceiling
} // end of switch
}
/** Adds a generalized scroller to the thinker list.
*
* \ param type The enumerated type of scrolling .
* \ param dx x speed of scrolling or its acceleration .
* \ param dy y speed of scrolling or its acceleration .
* \ param control Sector whose heights control this scroller ' s effect
* remotely , or - 1 if there is no control sector .
* \ param affectee Index of the affected object , sector or sidedef .
* \ param accel Nonzero for an accelerative effect .
* \ sa Add_WallScroller , P_SpawnScrollers , T_Scroll
*/
static void Add_Scroller ( INT32 type , fixed_t dx , fixed_t dy , INT32 control , INT32 affectee , INT32 accel , INT32 exclusive )
{
scroll_t * s = Z_Calloc ( sizeof * s , PU_LEVSPEC , NULL ) ;
s - > thinker . function . acp1 = ( actionf_p1 ) T_Scroll ;
s - > type = type ;
s - > dx = dx ;
s - > dy = dy ;
s - > accel = accel ;
s - > exclusive = exclusive ;
s - > vdx = s - > vdy = 0 ;
if ( ( s - > control = control ) ! = - 1 )
s - > last_height = sectors [ control ] . floorheight + sectors [ control ] . ceilingheight ;
s - > affectee = affectee ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & s - > thinker ) ;
2014-03-15 16:59:03 +00:00
}
/** Initializes the scrollers.
*
* \ todo Get rid of all the magic numbers .
* \ sa P_SpawnSpecials , Add_Scroller , Add_WallScroller
*/
static void P_SpawnScrollers ( void )
{
size_t i ;
line_t * l = lines ;
2020-04-17 16:15:25 +00:00
mtag_t tag ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < numlines ; i + + , l + + )
{
2019-06-04 17:34:59 +00:00
fixed_t dx = l - > dx > > SCROLL_SHIFT ; // direction and speed of scrolling
fixed_t dy = l - > dy > > SCROLL_SHIFT ;
2014-03-15 16:59:03 +00:00
INT32 control = - 1 , accel = 0 ; // no control sector or acceleration
INT32 special = l - > special ;
2020-04-17 16:15:25 +00:00
tag = Tag_FGet ( & l - > tags ) ;
2014-03-15 16:59:03 +00:00
// These types are same as the ones they get set to except that the
// first side's sector's heights cause scrolling when they change, and
// this linedef controls the direction and speed of the scrolling. The
// most complicated linedef since donuts, but powerful :)
if ( special = = 515 | | special = = 512 | | special = = 522 | | special = = 532 | | special = = 504 ) // displacement scrollers
{
special - = 2 ;
control = ( INT32 ) ( sides [ * l - > sidenum ] . sector - sectors ) ;
}
else if ( special = = 514 | | special = = 511 | | special = = 521 | | special = = 531 | | special = = 503 ) // accelerative scrollers
{
special - - ;
accel = 1 ;
control = ( INT32 ) ( sides [ * l - > sidenum ] . sector - sectors ) ;
}
else if ( special = = 535 | | special = = 525 ) // displacement scrollers
{
special - = 2 ;
control = ( INT32 ) ( sides [ * l - > sidenum ] . sector - sectors ) ;
}
else if ( special = = 534 | | special = = 524 ) // accelerative scrollers
{
accel = 1 ;
special - - ;
control = ( INT32 ) ( sides [ * l - > sidenum ] . sector - sectors ) ;
}
switch ( special )
{
register INT32 s ;
case 513 : // scroll effect ceiling
case 533 : // scroll and carry objects on ceiling
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2019-06-04 17:34:59 +00:00
Add_Scroller ( sc_ceiling , - dx , dy , control , s , accel , l - > flags & ML_NOCLIMB ) ;
2014-03-15 16:59:03 +00:00
if ( special ! = 533 )
break ;
2017-09-28 13:39:47 +00:00
/* FALLTHRU */
2014-03-15 16:59:03 +00:00
case 523 : // carry objects on ceiling
dx = FixedMul ( dx , CARRYFACTOR ) ;
dy = FixedMul ( dy , CARRYFACTOR ) ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2019-06-04 17:34:59 +00:00
Add_Scroller ( sc_carry_ceiling , dx , dy , control , s , accel , l - > flags & ML_NOCLIMB ) ;
2014-03-15 16:59:03 +00:00
break ;
case 510 : // scroll effect floor
case 530 : // scroll and carry objects on floor
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2019-06-04 17:34:59 +00:00
Add_Scroller ( sc_floor , - dx , dy , control , s , accel , l - > flags & ML_NOCLIMB ) ;
2014-03-15 16:59:03 +00:00
if ( special ! = 530 )
break ;
2017-09-28 13:39:47 +00:00
/* FALLTHRU */
2014-03-15 16:59:03 +00:00
case 520 : // carry objects on floor
dx = FixedMul ( dx , CARRYFACTOR ) ;
dy = FixedMul ( dy , CARRYFACTOR ) ;
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2019-06-04 17:34:59 +00:00
Add_Scroller ( sc_carry , dx , dy , control , s , accel , l - > flags & ML_NOCLIMB ) ;
2014-03-15 16:59:03 +00:00
break ;
// scroll wall according to linedef
// (same direction and speed as scrolling floors)
case 502 :
2020-04-13 11:10:38 +00:00
{
2021-02-11 12:24:20 +00:00
TAG_ITER_LINES ( tag , s )
2014-03-15 16:59:03 +00:00
if ( s ! = ( INT32 ) i )
2020-05-19 21:39:35 +00:00
{
if ( l - > flags & ML_EFFECT2 ) // use texture offsets instead
{
dx = sides [ l - > sidenum [ 0 ] ] . textureoffset ;
dy = sides [ l - > sidenum [ 0 ] ] . rowoffset ;
}
if ( l - > flags & ML_EFFECT3 )
{
if ( lines [ s ] . sidenum [ 1 ] ! = 0xffff )
Add_Scroller ( sc_side , dx , dy , control , lines [ s ] . sidenum [ 1 ] , accel , 0 ) ;
}
else
Add_Scroller ( sc_side , dx , dy , control , lines [ s ] . sidenum [ 0 ] , accel , 0 ) ;
}
2014-03-15 16:59:03 +00:00
break ;
2020-04-13 11:10:38 +00:00
}
2014-03-15 16:59:03 +00:00
case 505 :
s = lines [ i ] . sidenum [ 0 ] ;
Add_Scroller ( sc_side , - sides [ s ] . textureoffset , sides [ s ] . rowoffset , - 1 , s , accel , 0 ) ;
break ;
case 506 :
s = lines [ i ] . sidenum [ 1 ] ;
if ( s ! = 0xffff )
Add_Scroller ( sc_side , - sides [ s ] . textureoffset , sides [ s ] . rowoffset , - 1 , lines [ i ] . sidenum [ 0 ] , accel , 0 ) ;
else
2020-05-19 21:39:35 +00:00
CONS_Debug ( DBG_GAMELOGIC , " Line special 506 (line #%s) missing back side! \n " , sizeu1 ( i ) ) ;
break ;
case 507 :
s = lines [ i ] . sidenum [ 0 ] ;
if ( lines [ i ] . sidenum [ 1 ] ! = 0xffff )
Add_Scroller ( sc_side , - sides [ s ] . textureoffset , sides [ s ] . rowoffset , - 1 , lines [ i ] . sidenum [ 1 ] , accel , 0 ) ;
else
CONS_Debug ( DBG_GAMELOGIC , " Line special 507 (line #%s) missing back side! \n " , sizeu1 ( i ) ) ;
break ;
case 508 :
s = lines [ i ] . sidenum [ 1 ] ;
if ( s ! = 0xffff )
Add_Scroller ( sc_side , - sides [ s ] . textureoffset , sides [ s ] . rowoffset , - 1 , s , accel , 0 ) ;
else
CONS_Debug ( DBG_GAMELOGIC , " Line special 508 (line #%s) missing back side! \n " , sizeu1 ( i ) ) ;
2014-03-15 16:59:03 +00:00
break ;
case 500 : // scroll first side
Add_Scroller ( sc_side , FRACUNIT , 0 , - 1 , lines [ i ] . sidenum [ 0 ] , accel , 0 ) ;
break ;
case 501 : // jff 1/30/98 2-way scroll
Add_Scroller ( sc_side , - FRACUNIT , 0 , - 1 , lines [ i ] . sidenum [ 0 ] , accel , 0 ) ;
break ;
}
}
}
/** Adds master appear/disappear thinker.
*
* \ param appeartime tics to be existent
* \ param disappeartime tics to be nonexistent
* \ param sector pointer to control sector
*/
static void Add_MasterDisappearer ( tic_t appeartime , tic_t disappeartime , tic_t offset , INT32 line , INT32 sourceline )
{
disappear_t * d = Z_Malloc ( sizeof * d , PU_LEVSPEC , NULL ) ;
d - > thinker . function . acp1 = ( actionf_p1 ) T_Disappear ;
d - > appeartime = appeartime ;
d - > disappeartime = disappeartime ;
d - > offset = offset ;
d - > affectee = line ;
d - > sourceline = sourceline ;
d - > exists = true ;
d - > timer = 1 ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & d - > thinker ) ;
2014-03-15 16:59:03 +00:00
}
/** Makes a FOF appear/disappear
*
* \ param d Disappear thinker .
* \ sa Add_MasterDisappearer
*/
void T_Disappear ( disappear_t * d )
{
if ( d - > offset & & ! d - > exists )
{
d - > offset - - ;
return ;
}
if ( - - d - > timer < = 0 )
{
ffloor_t * rover ;
register INT32 s ;
2020-04-17 16:15:25 +00:00
mtag_t afftag = Tag_FGet ( & lines [ d - > affectee ] . tags ) ;
2014-03-15 16:59:03 +00:00
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( afftag , s )
2014-03-15 16:59:03 +00:00
{
for ( rover = sectors [ s ] . ffloors ; rover ; rover = rover - > next )
{
if ( rover - > master ! = & lines [ d - > affectee ] )
continue ;
if ( d - > exists )
rover - > flags & = ~ FF_EXISTS ;
else
{
rover - > flags | = FF_EXISTS ;
if ( ! ( lines [ d - > sourceline ] . flags & ML_NOCLIMB ) )
{
2020-03-22 14:17:16 +00:00
sectors [ s ] . soundorg . z = P_GetFFloorTopZAt ( rover , sectors [ s ] . soundorg . x , sectors [ s ] . soundorg . y ) ;
2014-03-15 16:59:03 +00:00
S_StartSound ( & sectors [ s ] . soundorg , sfx_appear ) ;
}
}
}
sectors [ s ] . moved = true ;
2019-07-04 13:43:18 +00:00
P_RecalcPrecipInSector ( & sectors [ s ] ) ;
2014-03-15 16:59:03 +00:00
}
if ( d - > exists )
{
d - > timer = d - > disappeartime ;
d - > exists = false ;
}
else
{
d - > timer = d - > appeartime ;
d - > exists = true ;
}
}
}
2018-03-31 05:15:24 +00:00
/** Removes fadingdata from FOF control sector
2018-08-14 02:10:16 +00:00
*
2018-03-31 05:15:24 +00:00
* \ param line line to search for target faders
* \ param data pointer to set new fadingdata to . Can be NULL to erase .
*/
2018-08-18 09:55:49 +00:00
static void P_ResetFakeFloorFader ( ffloor_t * rover , fade_t * data , boolean finalize )
2018-03-31 05:15:24 +00:00
{
2018-08-18 09:55:49 +00:00
fade_t * fadingdata = ( fade_t * ) rover - > fadingdata ;
2018-03-31 05:15:24 +00:00
// find any existing thinkers and remove them, then replace with new data
2018-08-18 09:55:49 +00:00
if ( fadingdata ! = data )
2018-03-31 05:15:24 +00:00
{
2018-09-15 06:24:44 +00:00
if ( fadingdata )
2018-08-18 09:31:41 +00:00
{
2018-08-18 09:55:49 +00:00
if ( finalize )
P_FadeFakeFloor ( rover ,
2018-09-11 14:28:24 +00:00
fadingdata - > sourcevalue ,
2018-08-18 09:55:49 +00:00
fadingdata - > alpha > = fadingdata - > destvalue ?
fadingdata - > alpha - 1 : // trigger fade-out finish
fadingdata - > alpha + 1 , // trigger fade-in finish
0 ,
2018-09-10 00:45:12 +00:00
fadingdata - > ticbased ,
& fadingdata - > timer ,
2018-08-18 09:55:49 +00:00
fadingdata - > doexists ,
fadingdata - > dotranslucent ,
2018-09-09 00:41:45 +00:00
fadingdata - > dolighting ,
2018-09-12 20:57:35 +00:00
fadingdata - > docolormap ,
2018-09-12 16:40:56 +00:00
fadingdata - > docollision ,
2018-08-18 09:55:49 +00:00
fadingdata - > doghostfade ,
fadingdata - > exactalpha ) ;
rover - > alpha = fadingdata - > alpha ;
2018-09-09 03:44:29 +00:00
if ( fadingdata - > dolighting )
P_RemoveLighting ( & sectors [ rover - > secnum ] ) ;
2018-09-12 20:57:35 +00:00
if ( fadingdata - > docolormap )
P_ResetColormapFader ( & sectors [ rover - > secnum ] ) ;
2018-08-18 09:55:49 +00:00
P_RemoveThinker ( & fadingdata - > thinker ) ;
2018-08-18 09:31:41 +00:00
}
2018-03-31 05:15:24 +00:00
2018-08-17 06:28:52 +00:00
rover - > fadingdata = data ;
2018-03-31 05:15:24 +00:00
}
}
2018-09-11 14:28:24 +00:00
static boolean P_FadeFakeFloor ( ffloor_t * rover , INT16 sourcevalue , INT16 destvalue , INT16 speed , boolean ticbased , INT32 * timer ,
2018-09-12 20:57:35 +00:00
boolean doexists , boolean dotranslucent , boolean dolighting , boolean docolormap ,
boolean docollision , boolean doghostfade , boolean exactalpha )
2018-04-07 11:09:04 +00:00
{
2018-08-17 18:49:33 +00:00
boolean stillfading = false ;
2018-08-17 20:27:32 +00:00
INT32 alpha ;
fade_t * fadingdata = ( fade_t * ) rover - > fadingdata ;
2018-09-15 05:04:15 +00:00
( void ) docolormap ; // *shrug* maybe we can use this in the future. For now, let's be consistent with our other function params
2018-08-17 20:27:32 +00:00
2018-09-08 05:33:12 +00:00
if ( rover - > master - > special = = 258 ) // Laser block
return false ;
2018-09-08 13:31:23 +00:00
// If fading an invisible FOF whose render flags we did not yet set,
// initialize its alpha to 1
if ( dotranslucent & &
2018-09-09 00:06:48 +00:00
( rover - > spawnflags & FF_NOSHADE ) & & // do not include light blocks, which don't set FF_NOSHADE
2018-09-10 02:14:24 +00:00
! ( rover - > flags & FF_FOG ) & & // do not include fog
2018-09-08 13:31:23 +00:00
! ( rover - > spawnflags & FF_RENDERSIDES ) & &
! ( rover - > spawnflags & FF_RENDERPLANES ) & &
! ( rover - > flags & FF_RENDERALL ) )
rover - > alpha = 1 ;
2018-08-17 20:27:32 +00:00
if ( fadingdata )
alpha = fadingdata - > alpha ;
else
alpha = rover - > alpha ;
2018-04-07 11:09:04 +00:00
2018-08-17 18:49:33 +00:00
// routines specific to fade in and fade out
2018-09-09 23:57:41 +00:00
if ( ! ticbased & & alpha = = destvalue )
2018-08-17 18:49:33 +00:00
return stillfading ;
2018-08-17 20:27:32 +00:00
else if ( alpha > destvalue ) // fade out
2018-04-07 11:09:04 +00:00
{
// finish fading out
2018-09-09 23:31:32 +00:00
if ( speed < 1 | | ( ! ticbased & & alpha - speed < = destvalue + speed ) | |
2018-09-10 00:45:12 +00:00
( ticbased & & ( - - ( * timer ) < = 0 | | alpha < = destvalue ) ) )
2018-04-07 11:09:04 +00:00
{
2018-08-17 20:27:32 +00:00
alpha = destvalue ;
2018-04-07 11:09:04 +00:00
2018-08-17 19:13:05 +00:00
if ( docollision )
2018-04-07 11:09:04 +00:00
{
2018-08-17 18:49:33 +00:00
if ( rover - > spawnflags & FF_SOLID )
rover - > flags & = ~ FF_SOLID ;
if ( rover - > spawnflags & FF_SWIMMABLE )
rover - > flags & = ~ FF_SWIMMABLE ;
if ( rover - > spawnflags & FF_QUICKSAND )
rover - > flags & = ~ FF_QUICKSAND ;
if ( rover - > spawnflags & FF_BUSTUP )
rover - > flags & = ~ FF_BUSTUP ;
2018-09-08 05:12:34 +00:00
if ( rover - > spawnflags & FF_MARIO )
rover - > flags & = ~ FF_MARIO ;
2018-04-07 11:09:04 +00:00
}
}
else // continue fading out
{
2018-09-09 23:31:32 +00:00
if ( ! ticbased )
alpha - = speed ;
2018-09-11 14:28:24 +00:00
else
{
INT16 delta = abs ( destvalue - sourcevalue ) ;
fixed_t factor = min ( FixedDiv ( speed - ( * timer ) , speed ) , 1 * FRACUNIT ) ;
alpha = max ( min ( alpha , sourcevalue - ( INT16 ) FixedMul ( delta , factor ) ) , destvalue ) ;
}
2018-08-17 18:49:33 +00:00
stillfading = true ;
2018-04-07 11:09:04 +00:00
}
}
else // fade in
{
// finish fading in
2018-09-09 23:31:32 +00:00
if ( speed < 1 | | ( ! ticbased & & alpha + speed > = destvalue - speed ) | |
2018-09-11 14:28:24 +00:00
( ticbased & & ( - - ( * timer ) < = 0 | | alpha > = destvalue ) ) )
2018-04-07 11:09:04 +00:00
{
2018-08-17 20:27:32 +00:00
alpha = destvalue ;
2018-08-14 02:10:16 +00:00
2018-08-17 19:13:05 +00:00
if ( docollision )
2018-04-07 11:09:04 +00:00
{
2018-08-17 18:49:33 +00:00
if ( rover - > spawnflags & FF_SOLID )
rover - > flags | = FF_SOLID ;
if ( rover - > spawnflags & FF_SWIMMABLE )
rover - > flags | = FF_SWIMMABLE ;
if ( rover - > spawnflags & FF_QUICKSAND )
rover - > flags | = FF_QUICKSAND ;
if ( rover - > spawnflags & FF_BUSTUP )
rover - > flags | = FF_BUSTUP ;
2018-09-08 05:12:34 +00:00
if ( rover - > spawnflags & FF_MARIO )
rover - > flags | = FF_MARIO ;
2018-04-07 11:09:04 +00:00
}
}
else // continue fading in
{
2018-09-09 23:31:32 +00:00
if ( ! ticbased )
alpha + = speed ;
2018-09-11 14:28:24 +00:00
else
{
INT16 delta = abs ( destvalue - sourcevalue ) ;
fixed_t factor = min ( FixedDiv ( speed - ( * timer ) , speed ) , 1 * FRACUNIT ) ;
alpha = min ( max ( alpha , sourcevalue + ( INT16 ) FixedMul ( delta , factor ) ) , destvalue ) ;
}
2018-08-17 18:49:33 +00:00
stillfading = true ;
}
}
2018-08-14 02:10:16 +00:00
2018-08-17 18:49:33 +00:00
// routines common to both fade in and fade out
if ( ! stillfading )
{
2018-09-08 05:23:15 +00:00
if ( doexists & & ! ( rover - > spawnflags & FF_BUSTUP ) )
2018-08-17 18:49:33 +00:00
{
2018-08-17 20:27:32 +00:00
if ( alpha < = 1 )
2018-08-17 18:49:33 +00:00
rover - > flags & = ~ FF_EXISTS ;
else
2018-04-07 11:09:04 +00:00
rover - > flags | = FF_EXISTS ;
2018-09-09 00:49:32 +00:00
// Re-render lighting at end of fade
if ( dolighting & & ! ( rover - > spawnflags & FF_NOSHADE ) & & ! ( rover - > flags & FF_EXISTS ) )
rover - > target - > moved = true ;
2018-08-17 18:49:33 +00:00
}
2018-04-07 11:09:04 +00:00
2018-09-10 02:14:24 +00:00
if ( dotranslucent & & ! ( rover - > flags & FF_FOG ) )
2018-08-17 18:49:33 +00:00
{
2018-08-17 20:27:32 +00:00
if ( alpha > = 256 )
2018-04-07 11:09:04 +00:00
{
2018-09-08 12:46:56 +00:00
if ( ! ( rover - > flags & FF_CUTSOLIDS ) & &
( rover - > spawnflags & FF_CUTSOLIDS ) )
{
rover - > flags | = FF_CUTSOLIDS ;
rover - > target - > moved = true ;
}
2018-09-08 13:08:32 +00:00
2018-08-17 18:49:33 +00:00
rover - > flags & = ~ FF_TRANSLUCENT ;
}
else
{
rover - > flags | = FF_TRANSLUCENT ;
2018-09-08 13:08:32 +00:00
2018-09-08 12:46:56 +00:00
if ( ( rover - > flags & FF_CUTSOLIDS ) & &
( rover - > spawnflags & FF_CUTSOLIDS ) )
{
rover - > flags & = ~ FF_CUTSOLIDS ;
rover - > target - > moved = true ;
}
2018-04-07 11:09:04 +00:00
}
2018-09-08 13:08:32 +00:00
2018-09-09 00:06:48 +00:00
if ( ( rover - > spawnflags & FF_NOSHADE ) & & // do not include light blocks, which don't set FF_NOSHADE
! ( rover - > spawnflags & FF_RENDERSIDES ) & &
2018-09-08 13:08:32 +00:00
! ( rover - > spawnflags & FF_RENDERPLANES ) )
{
if ( rover - > alpha > 1 )
rover - > flags | = FF_RENDERALL ;
else
rover - > flags & = ~ FF_RENDERALL ;
}
2018-08-17 18:49:33 +00:00
}
}
else
{
2018-09-08 05:23:15 +00:00
if ( doexists & & ! ( rover - > spawnflags & FF_BUSTUP ) )
2018-09-09 00:49:32 +00:00
{
// Re-render lighting if we haven't yet set FF_EXISTS (beginning of fade)
if ( dolighting & & ! ( rover - > spawnflags & FF_NOSHADE ) & & ! ( rover - > flags & FF_EXISTS ) )
rover - > target - > moved = true ;
2018-08-17 18:49:33 +00:00
rover - > flags | = FF_EXISTS ;
2018-09-09 00:49:32 +00:00
}
2018-08-17 18:49:33 +00:00
2018-09-10 02:14:24 +00:00
if ( dotranslucent & & ! ( rover - > flags & FF_FOG ) )
2018-08-17 18:49:33 +00:00
{
rover - > flags | = FF_TRANSLUCENT ;
2018-09-08 13:08:32 +00:00
2018-09-08 12:46:56 +00:00
if ( ( rover - > flags & FF_CUTSOLIDS ) & &
( rover - > spawnflags & FF_CUTSOLIDS ) )
{
rover - > flags & = ~ FF_CUTSOLIDS ;
rover - > target - > moved = true ;
}
2018-09-08 13:08:32 +00:00
2018-09-09 00:06:48 +00:00
if ( ( rover - > spawnflags & FF_NOSHADE ) & & // do not include light blocks, which don't set FF_NOSHADE
! ( rover - > spawnflags & FF_RENDERSIDES ) & &
2018-09-08 13:08:32 +00:00
! ( rover - > spawnflags & FF_RENDERPLANES ) )
rover - > flags | = FF_RENDERALL ;
2018-08-17 18:49:33 +00:00
}
2018-08-17 19:13:05 +00:00
if ( docollision )
2018-08-17 18:49:33 +00:00
{
2018-08-17 19:59:34 +00:00
if ( doghostfade ) // remove collision flags during fade
2018-08-17 18:49:33 +00:00
{
if ( rover - > spawnflags & FF_SOLID )
rover - > flags & = ~ FF_SOLID ;
if ( rover - > spawnflags & FF_SWIMMABLE )
rover - > flags & = ~ FF_SWIMMABLE ;
if ( rover - > spawnflags & FF_QUICKSAND )
rover - > flags & = ~ FF_QUICKSAND ;
if ( rover - > spawnflags & FF_BUSTUP )
rover - > flags & = ~ FF_BUSTUP ;
2018-09-08 05:12:34 +00:00
if ( rover - > spawnflags & FF_MARIO )
rover - > flags & = ~ FF_MARIO ;
2018-08-17 18:49:33 +00:00
}
2018-08-17 19:59:34 +00:00
else // keep collision during fade
2018-08-17 18:49:33 +00:00
{
if ( rover - > spawnflags & FF_SOLID )
rover - > flags | = FF_SOLID ;
if ( rover - > spawnflags & FF_SWIMMABLE )
rover - > flags | = FF_SWIMMABLE ;
if ( rover - > spawnflags & FF_QUICKSAND )
rover - > flags | = FF_QUICKSAND ;
if ( rover - > spawnflags & FF_BUSTUP )
rover - > flags | = FF_BUSTUP ;
2018-09-08 05:12:34 +00:00
if ( rover - > spawnflags & FF_MARIO )
rover - > flags | = FF_MARIO ;
2018-08-17 18:49:33 +00:00
}
2018-04-07 11:09:04 +00:00
}
}
2018-09-10 02:14:24 +00:00
if ( ! ( rover - > flags & FF_FOG ) ) // don't set FOG alpha
2018-08-17 20:27:32 +00:00
{
2018-09-10 02:14:24 +00:00
if ( ! stillfading | | exactalpha )
rover - > alpha = alpha ;
else // clamp fadingdata->alpha to software's alpha levels
{
if ( alpha < 12 )
rover - > alpha = destvalue < 12 ? destvalue : 1 ; // Don't even draw it
else if ( alpha < 38 )
rover - > alpha = destvalue > = 12 & & destvalue < 38 ? destvalue : 25 ;
else if ( alpha < 64 )
rover - > alpha = destvalue > = 38 & & destvalue < 64 ? destvalue : 51 ;
else if ( alpha < 89 )
rover - > alpha = destvalue > = 64 & & destvalue < 89 ? destvalue : 76 ;
else if ( alpha < 115 )
rover - > alpha = destvalue > = 89 & & destvalue < 115 ? destvalue : 102 ;
else if ( alpha < 140 )
rover - > alpha = destvalue > = 115 & & destvalue < 140 ? destvalue : 128 ;
else if ( alpha < 166 )
rover - > alpha = destvalue > = 140 & & destvalue < 166 ? destvalue : 154 ;
else if ( alpha < 192 )
rover - > alpha = destvalue > = 166 & & destvalue < 192 ? destvalue : 179 ;
else if ( alpha < 217 )
rover - > alpha = destvalue > = 192 & & destvalue < 217 ? destvalue : 204 ;
else if ( alpha < 243 )
rover - > alpha = destvalue > = 217 & & destvalue < 243 ? destvalue : 230 ;
else // Opaque
rover - > alpha = destvalue > = 243 ? destvalue : 256 ;
}
2018-08-17 20:27:32 +00:00
}
if ( fadingdata )
fadingdata - > alpha = alpha ;
2018-08-17 18:49:33 +00:00
return stillfading ;
2018-04-07 11:09:04 +00:00
}
2018-08-17 19:13:05 +00:00
/** Adds fake floor fader thinker.
2018-03-30 19:27:37 +00:00
*
2018-08-17 19:13:05 +00:00
* \ param destvalue transparency value to fade to
* \ param speed speed to fade by
2018-09-09 23:31:32 +00:00
* \ param ticbased tic - based logic , speed = duration
2018-09-10 13:48:24 +00:00
* \ param relative Destvalue is relative to rover - > alpha
2018-08-17 19:13:05 +00:00
* \ param doexists handle FF_EXISTS
* \ param dotranslucent handle FF_TRANSLUCENT
2018-09-09 19:41:30 +00:00
* \ param dolighting fade FOF light
2018-08-17 19:13:05 +00:00
* \ param docollision handle interactive flags
* \ param doghostfade no interactive flags during fading
* \ param exactalpha use exact alpha values ( opengl )
2018-03-30 19:27:37 +00:00
*/
2018-08-17 19:13:05 +00:00
static void P_AddFakeFloorFader ( ffloor_t * rover , size_t sectornum , size_t ffloornum ,
2018-09-10 13:48:24 +00:00
INT16 destvalue , INT16 speed , boolean ticbased , boolean relative ,
2018-09-12 20:57:35 +00:00
boolean doexists , boolean dotranslucent , boolean dolighting , boolean docolormap ,
boolean docollision , boolean doghostfade , boolean exactalpha )
2018-03-30 19:27:37 +00:00
{
2018-09-15 05:04:15 +00:00
fade_t * d ;
2018-09-10 13:48:24 +00:00
// If fading an invisible FOF whose render flags we did not yet set,
// initialize its alpha to 1
if ( dotranslucent & &
( rover - > spawnflags & FF_NOSHADE ) & & // do not include light blocks, which don't set FF_NOSHADE
! ( rover - > spawnflags & FF_RENDERSIDES ) & &
! ( rover - > spawnflags & FF_RENDERPLANES ) & &
! ( rover - > flags & FF_RENDERALL ) )
rover - > alpha = 1 ;
2018-09-09 12:10:45 +00:00
// already equal, nothing to do
2018-09-10 13:48:24 +00:00
if ( rover - > alpha = = max ( 1 , min ( 256 , relative ? rover - > alpha + destvalue : destvalue ) ) )
2018-09-09 12:10:45 +00:00
return ;
2018-09-15 05:04:15 +00:00
d = Z_Malloc ( sizeof * d , PU_LEVSPEC , NULL ) ;
2018-03-30 19:27:37 +00:00
2018-03-30 20:17:35 +00:00
d - > thinker . function . acp1 = ( actionf_p1 ) T_Fade ;
2018-08-17 06:28:52 +00:00
d - > rover = rover ;
2018-09-10 03:52:36 +00:00
d - > sectornum = ( UINT32 ) sectornum ;
d - > ffloornum = ( UINT32 ) ffloornum ;
2018-09-08 13:31:23 +00:00
2018-09-11 14:28:24 +00:00
d - > alpha = d - > sourcevalue = rover - > alpha ;
2019-07-09 20:15:12 +00:00
d - > destvalue = max ( 1 , min ( 256 , relative ? rover - > alpha + destvalue : destvalue ) ) ; // rover->alpha is 1-256
2018-09-09 23:31:32 +00:00
if ( ticbased )
{
2018-09-10 00:45:12 +00:00
d - > ticbased = true ;
2018-09-11 14:28:24 +00:00
d - > timer = d - > speed = abs ( speed ) ; // use d->speed as total duration
2018-09-09 23:31:32 +00:00
}
else
{
2018-09-10 00:45:12 +00:00
d - > ticbased = false ;
2018-09-11 14:28:24 +00:00
d - > speed = max ( 1 , speed ) ; // minimum speed 1/tic // if speed < 1, alpha is set immediately in thinker
2018-09-10 00:45:12 +00:00
d - > timer = - 1 ;
2018-09-09 23:31:32 +00:00
}
2018-04-07 03:14:48 +00:00
d - > doexists = doexists ;
d - > dotranslucent = dotranslucent ;
2018-09-09 00:41:45 +00:00
d - > dolighting = dolighting ;
2018-09-12 20:57:35 +00:00
d - > docolormap = docolormap ;
2018-09-09 21:19:49 +00:00
d - > docollision = docollision ;
2018-08-14 14:18:08 +00:00
d - > doghostfade = doghostfade ;
2018-08-17 19:13:05 +00:00
d - > exactalpha = exactalpha ;
2018-03-31 03:29:41 +00:00
2018-03-31 05:15:24 +00:00
// find any existing thinkers and remove them, then replace with new data
2018-08-18 09:55:49 +00:00
P_ResetFakeFloorFader ( rover , d , false ) ;
2018-03-31 05:15:24 +00:00
2018-09-09 03:44:29 +00:00
// Set a separate thinker for shadow fading
if ( dolighting & & ! ( rover - > flags & FF_NOSHADE ) )
2018-09-09 19:41:30 +00:00
{
UINT16 lightdelta = abs ( sectors [ rover - > secnum ] . spawn_lightlevel - rover - > target - > lightlevel ) ;
2018-09-09 23:44:35 +00:00
fixed_t alphapercent = min ( FixedDiv ( d - > destvalue , rover - > spawnalpha ) , 1 * FRACUNIT ) ; // don't make darker than spawn_lightlevel
2018-09-09 19:41:30 +00:00
fixed_t adjustedlightdelta = FixedMul ( lightdelta , alphapercent ) ;
if ( rover - > target - > lightlevel > = sectors [ rover - > secnum ] . spawn_lightlevel ) // fading out, get lighter
2018-09-09 21:19:49 +00:00
d - > destlightlevel = rover - > target - > lightlevel - adjustedlightdelta ;
2018-09-09 19:41:30 +00:00
else // fading in, get darker
2018-09-09 21:19:49 +00:00
d - > destlightlevel = rover - > target - > lightlevel + adjustedlightdelta ;
2018-09-09 19:41:30 +00:00
2018-09-09 03:44:29 +00:00
P_FadeLightBySector ( & sectors [ rover - > secnum ] ,
2018-09-09 21:19:49 +00:00
d - > destlightlevel ,
2018-09-10 00:45:12 +00:00
ticbased ? d - > timer :
2018-09-09 23:31:32 +00:00
FixedFloor ( FixedDiv ( abs ( d - > destvalue - d - > alpha ) , d - > speed ) ) / FRACUNIT ,
2018-09-09 19:41:30 +00:00
true ) ;
}
2018-09-09 21:19:49 +00:00
else
d - > destlightlevel = - 1 ;
2018-09-09 03:44:29 +00:00
2018-09-12 20:57:35 +00:00
// Set a separate thinker for colormap fading
2020-03-20 11:19:02 +00:00
if ( docolormap & & ! ( rover - > flags & FF_NOSHADE ) & & sectors [ rover - > secnum ] . spawn_extra_colormap & & ! sectors [ rover - > secnum ] . colormap_protected )
2018-09-12 20:57:35 +00:00
{
extracolormap_t * dest_exc ,
* source_exc = sectors [ rover - > secnum ] . extra_colormap ? sectors [ rover - > secnum ] . extra_colormap : R_GetDefaultColormap ( ) ;
INT32 colordelta = R_GetRgbaA ( sectors [ rover - > secnum ] . spawn_extra_colormap - > rgba ) ; // alpha is from 0
fixed_t alphapercent = min ( FixedDiv ( d - > destvalue , rover - > spawnalpha ) , 1 * FRACUNIT ) ; // don't make darker than spawn_lightlevel
fixed_t adjustedcolordelta = FixedMul ( colordelta , alphapercent ) ;
INT32 coloralpha ;
coloralpha = adjustedcolordelta ;
dest_exc = R_CopyColormap ( sectors [ rover - > secnum ] . spawn_extra_colormap , false ) ;
dest_exc - > rgba = R_GetRgbaRGB ( dest_exc - > rgba ) + R_PutRgbaA ( coloralpha ) ;
if ( ! ( d - > dest_exc = R_GetColormapFromList ( dest_exc ) ) )
{
dest_exc - > colormap = R_CreateLightTable ( dest_exc ) ;
R_AddColormapToList ( dest_exc ) ;
d - > dest_exc = dest_exc ;
}
else
Z_Free ( dest_exc ) ;
// If fading from 0, set source_exc rgb same to dest_exc
if ( ! R_CheckDefaultColormap ( d - > dest_exc , true , false , false )
& & R_CheckDefaultColormap ( source_exc , true , false , false ) )
{
extracolormap_t * exc = R_CopyColormap ( source_exc , false ) ;
exc - > rgba = R_GetRgbaRGB ( d - > dest_exc - > rgba ) + R_PutRgbaA ( R_GetRgbaA ( source_exc - > rgba ) ) ;
exc - > fadergba = R_GetRgbaRGB ( d - > dest_exc - > rgba ) + R_PutRgbaA ( R_GetRgbaA ( source_exc - > fadergba ) ) ;
if ( ! ( source_exc = R_GetColormapFromList ( exc ) ) )
{
exc - > colormap = R_CreateLightTable ( exc ) ;
R_AddColormapToList ( exc ) ;
source_exc = exc ;
}
else
Z_Free ( exc ) ;
}
2018-09-16 00:34:19 +00:00
Add_ColormapFader ( & sectors [ rover - > secnum ] , source_exc , d - > dest_exc , true ,
2018-09-12 20:57:35 +00:00
ticbased ? d - > timer :
FixedFloor ( FixedDiv ( abs ( d - > destvalue - d - > alpha ) , d - > speed ) ) / FRACUNIT ) ;
}
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & d - > thinker ) ;
2018-03-30 19:27:37 +00:00
}
/** Makes a FOF fade
*
* \ param d Fade thinker .
2018-08-17 19:13:05 +00:00
* \ sa P_AddFakeFloorFader
2018-03-30 19:27:37 +00:00
*/
void T_Fade ( fade_t * d )
{
2018-09-11 14:28:24 +00:00
if ( d - > rover & & ! P_FadeFakeFloor ( d - > rover , d - > sourcevalue , d - > destvalue , d - > speed , d - > ticbased , & d - > timer ,
2018-09-12 20:57:35 +00:00
d - > doexists , d - > dotranslucent , d - > dolighting , d - > docolormap , d - > docollision , d - > doghostfade , d - > exactalpha ) )
2018-09-09 21:19:49 +00:00
{
// Finalize lighting, copypasta from P_AddFakeFloorFader
if ( d - > dolighting & & ! ( d - > rover - > flags & FF_NOSHADE ) & & d - > destlightlevel > - 1 )
sectors [ d - > rover - > secnum ] . lightlevel = d - > destlightlevel ;
2018-09-12 20:57:35 +00:00
// Finalize colormap
if ( d - > docolormap & & ! ( d - > rover - > flags & FF_NOSHADE ) & & sectors [ d - > rover - > secnum ] . spawn_extra_colormap )
sectors [ d - > rover - > secnum ] . extra_colormap = d - > dest_exc ;
2018-08-17 19:13:05 +00:00
P_RemoveFakeFloorFader ( d - > rover ) ;
2018-09-09 21:19:49 +00:00
}
2018-03-30 19:27:37 +00:00
}
2018-09-12 13:06:38 +00:00
static void P_ResetColormapFader ( sector_t * sector )
{
if ( sector - > fadecolormapdata )
{
// The thinker is the first member in all the action structs,
// so just let the thinker get freed, and that will free the whole
// structure.
P_RemoveThinker ( & ( ( elevator_t * ) sector - > fadecolormapdata ) - > thinker ) ;
sector - > fadecolormapdata = NULL ;
}
}
static void Add_ColormapFader ( sector_t * sector , extracolormap_t * source_exc , extracolormap_t * dest_exc ,
2018-09-13 14:38:15 +00:00
boolean ticbased , INT32 duration )
2018-09-12 13:06:38 +00:00
{
2018-09-15 04:51:55 +00:00
fadecolormap_t * d ;
2018-09-12 13:06:38 +00:00
P_ResetColormapFader ( sector ) ;
// nothing to do, set immediately
if ( ! duration | | R_CheckEqualColormaps ( source_exc , dest_exc , true , true , true ) )
{
sector - > extra_colormap = dest_exc ;
return ;
}
2018-09-15 04:51:55 +00:00
d = Z_Malloc ( sizeof * d , PU_LEVSPEC , NULL ) ;
2018-09-12 13:06:38 +00:00
d - > thinker . function . acp1 = ( actionf_p1 ) T_FadeColormap ;
d - > sector = sector ;
d - > source_exc = source_exc ;
d - > dest_exc = dest_exc ;
2018-09-13 14:38:15 +00:00
if ( ticbased )
{
d - > ticbased = true ;
d - > duration = d - > timer = duration ;
}
else
{
d - > ticbased = false ;
d - > timer = 256 ;
d - > duration = duration ; // use as speed
}
2018-09-12 13:06:38 +00:00
sector - > fadecolormapdata = d ;
2019-04-20 21:29:20 +00:00
P_AddThinker ( THINK_MAIN , & d - > thinker ) ;
2018-09-12 13:06:38 +00:00
}
void T_FadeColormap ( fadecolormap_t * d )
{
2018-09-13 14:38:15 +00:00
if ( ( d - > ticbased & & - - d - > timer < = 0 )
| | ( ! d - > ticbased & & ( d - > timer - = d - > duration ) < = 0 ) ) // d->duration used as speed decrement
2018-09-12 13:06:38 +00:00
{
d - > sector - > extra_colormap = d - > dest_exc ;
P_ResetColormapFader ( d - > sector ) ;
}
else
{
extracolormap_t * exc ;
2018-09-13 14:38:15 +00:00
INT32 duration = d - > ticbased ? d - > duration : 256 ;
fixed_t factor = min ( FixedDiv ( duration - d - > timer , duration ) , 1 * FRACUNIT ) ;
2020-02-16 19:19:24 +00:00
INT16 cr , cg , cb , ca , fadestart , fadeend , flags ;
2018-09-12 13:06:38 +00:00
INT32 rgba , fadergba ;
2018-09-12 15:11:22 +00:00
// NULL failsafes (or intentionally set to signify default)
if ( ! d - > sector - > extra_colormap )
d - > sector - > extra_colormap = R_GetDefaultColormap ( ) ;
if ( ! d - > source_exc )
d - > source_exc = R_GetDefaultColormap ( ) ;
if ( ! d - > dest_exc )
d - > dest_exc = R_GetDefaultColormap ( ) ;
2018-09-12 13:06:38 +00:00
// For each var (rgba + fadergba + params = 11 vars), we apply
// percentage fading: currentval = sourceval + (delta * percent of duration elapsed)
// delta is negative when fading out (destval is lower)
// max/min are used to ensure progressive calcs don't go backwards and to cap values to dest.
# define APPLYFADE(dest, src, cur) (\
( dest - src < 0 ) ? \
max ( \
min ( cur , \
2018-09-12 15:33:44 +00:00
src + ( INT16 ) FixedMul ( dest - src , factor ) ) , \
2018-09-12 13:06:38 +00:00
dest ) \
: ( dest - src > 0 ) ? \
min ( \
max ( cur , \
2018-09-12 15:33:44 +00:00
src + ( INT16 ) FixedMul ( dest - src , factor ) ) , \
2018-09-12 13:06:38 +00:00
dest ) \
: \
dest \
)
cr = APPLYFADE ( R_GetRgbaR ( d - > dest_exc - > rgba ) , R_GetRgbaR ( d - > source_exc - > rgba ) , R_GetRgbaR ( d - > sector - > extra_colormap - > rgba ) ) ;
cg = APPLYFADE ( R_GetRgbaG ( d - > dest_exc - > rgba ) , R_GetRgbaG ( d - > source_exc - > rgba ) , R_GetRgbaG ( d - > sector - > extra_colormap - > rgba ) ) ;
cb = APPLYFADE ( R_GetRgbaB ( d - > dest_exc - > rgba ) , R_GetRgbaB ( d - > source_exc - > rgba ) , R_GetRgbaB ( d - > sector - > extra_colormap - > rgba ) ) ;
ca = APPLYFADE ( R_GetRgbaA ( d - > dest_exc - > rgba ) , R_GetRgbaA ( d - > source_exc - > rgba ) , R_GetRgbaA ( d - > sector - > extra_colormap - > rgba ) ) ;
rgba = R_PutRgbaRGBA ( cr , cg , cb , ca ) ;
cr = APPLYFADE ( R_GetRgbaR ( d - > dest_exc - > fadergba ) , R_GetRgbaR ( d - > source_exc - > fadergba ) , R_GetRgbaR ( d - > sector - > extra_colormap - > fadergba ) ) ;
cg = APPLYFADE ( R_GetRgbaG ( d - > dest_exc - > fadergba ) , R_GetRgbaG ( d - > source_exc - > fadergba ) , R_GetRgbaG ( d - > sector - > extra_colormap - > fadergba ) ) ;
cb = APPLYFADE ( R_GetRgbaB ( d - > dest_exc - > fadergba ) , R_GetRgbaB ( d - > source_exc - > fadergba ) , R_GetRgbaB ( d - > sector - > extra_colormap - > fadergba ) ) ;
ca = APPLYFADE ( R_GetRgbaA ( d - > dest_exc - > fadergba ) , R_GetRgbaA ( d - > source_exc - > fadergba ) , R_GetRgbaA ( d - > sector - > extra_colormap - > fadergba ) ) ;
fadergba = R_PutRgbaRGBA ( cr , cg , cb , ca ) ;
fadestart = APPLYFADE ( d - > dest_exc - > fadestart , d - > source_exc - > fadestart , d - > sector - > extra_colormap - > fadestart ) ;
fadeend = APPLYFADE ( d - > dest_exc - > fadeend , d - > source_exc - > fadeend , d - > sector - > extra_colormap - > fadeend ) ;
2020-02-16 19:19:24 +00:00
flags = abs ( factor ) > FRACUNIT / 2 ? d - > dest_exc - > flags : d - > source_exc - > flags ; // set new flags halfway through fade
2018-09-12 13:06:38 +00:00
# undef APPLYFADE
//////////////////
// setup new colormap
//////////////////
2020-02-16 19:19:24 +00:00
if ( ! ( d - > sector - > extra_colormap = R_GetColormapFromListByValues ( rgba , fadergba , fadestart , fadeend , flags ) ) )
2018-09-12 13:06:38 +00:00
{
exc = R_CreateDefaultColormap ( false ) ;
exc - > fadestart = fadestart ;
exc - > fadeend = fadeend ;
2020-02-16 19:19:24 +00:00
exc - > flags = flags ;
2018-09-12 13:06:38 +00:00
exc - > rgba = rgba ;
exc - > fadergba = fadergba ;
exc - > colormap = R_CreateLightTable ( exc ) ;
R_AddColormapToList ( exc ) ;
d - > sector - > extra_colormap = exc ;
}
}
}
2014-03-15 16:59:03 +00:00
/*
SoM : 3 / 8 / 2000 : Friction functions start .
Add_Friction ,
T_Friction ,
P_SpawnFriction
*/
/** Adds friction thinker.
*
* \ param friction Friction value , 0xe800 is normal .
* \ param affectee Target sector .
* \ param roverfriction FOF or not
* \ sa T_Friction , P_SpawnFriction
*/
static void Add_Friction ( INT32 friction , INT32 movefactor , INT32 affectee , INT32 referrer )
{
friction_t * f = Z_Calloc ( sizeof * f , PU_LEVSPEC , NULL ) ;
f - > thinker . function . acp1 = ( actionf_p1 ) T_Friction ;
f - > friction = friction ;
f - > movefactor = movefactor ;
f - > affectee = affectee ;
if ( referrer ! = - 1 )
{
f - > roverfriction = true ;
f - > referrer = referrer ;
}
else
f - > roverfriction = false ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & f - > thinker ) ;
2014-03-15 16:59:03 +00:00
}
/** Applies friction to all things in a sector.
*
* \ param f Friction thinker .
* \ sa Add_Friction
*/
void T_Friction ( friction_t * f )
{
2015-10-10 16:57:35 +00:00
sector_t * sec , * referrer = NULL ;
2014-03-15 16:59:03 +00:00
mobj_t * thing ;
msecnode_t * node ;
sec = sectors + f - > affectee ;
2016-06-02 21:46:27 +00:00
// Get FOF control sector
2014-03-15 16:59:03 +00:00
if ( f - > roverfriction )
2015-05-24 17:53:30 +00:00
referrer = sectors + f - > referrer ;
2014-03-15 16:59:03 +00:00
// Assign the friction value to players on the floor, non-floating,
// and clipped. Normally the object's friction value is kept at
// ORIG_FRICTION and this thinker changes it for icy or muddy floors.
// When the object is straddling sectors with the same
// floorheight that have different frictions, use the lowest
// friction value (muddy has precedence over icy).
node = sec - > touching_thinglist ; // things touching this sector
while ( node )
{
thing = node - > m_thing ;
// apparently, all I had to do was comment out part of the next line and
// friction works for all mobj's
// (or at least MF_PUSHABLEs, which is all I care about anyway)
if ( ! ( thing - > flags & ( MF_NOGRAVITY | MF_NOCLIP ) ) & & thing - > z = = thing - > floorz )
{
if ( f - > roverfriction )
{
2015-05-24 17:53:30 +00:00
if ( thing - > floorz ! = P_GetSpecialTopZ ( thing , referrer , sec ) )
2014-03-15 16:59:03 +00:00
{
2016-06-09 13:16:02 +00:00
node = node - > m_thinglist_next ;
2014-03-15 16:59:03 +00:00
continue ;
}
if ( ( thing - > friction = = ORIG_FRICTION ) // normal friction?
| | ( f - > friction < thing - > friction ) )
{
thing - > friction = f - > friction ;
2016-06-03 16:26:50 +00:00
if ( thing - > player )
2016-08-09 23:05:46 +00:00
thing - > movefactor = f - > movefactor ;
2014-03-15 16:59:03 +00:00
}
}
2015-05-24 17:53:30 +00:00
else if ( P_GetSpecialBottomZ ( thing , sec , sec ) = = thing - > floorz & & ( thing - > friction = = ORIG_FRICTION // normal friction?
2014-03-15 16:59:03 +00:00
| | f - > friction < thing - > friction ) )
{
thing - > friction = f - > friction ;
2016-06-03 16:26:50 +00:00
if ( thing - > player )
2016-08-09 23:05:46 +00:00
thing - > movefactor = f - > movefactor ;
2014-03-15 16:59:03 +00:00
}
}
2016-06-09 13:16:02 +00:00
node = node - > m_thinglist_next ;
2014-03-15 16:59:03 +00:00
}
}
/** Spawns all friction effects.
*
* \ sa P_SpawnSpecials , Add_Friction
*/
static void P_SpawnFriction ( void )
{
size_t i ;
line_t * l = lines ;
2020-04-17 16:15:25 +00:00
mtag_t tag ;
2014-03-15 16:59:03 +00:00
register INT32 s ;
2016-06-02 21:46:27 +00:00
fixed_t strength ; // frontside texture offset controls magnitude
2014-03-15 16:59:03 +00:00
fixed_t friction ; // friction value to be applied during movement
INT32 movefactor ; // applied to each player move to simulate inertia
for ( i = 0 ; i < numlines ; i + + , l + + )
if ( l - > special = = 540 )
{
2020-04-17 16:15:25 +00:00
tag = Tag_FGet ( & l - > tags ) ;
2016-06-02 21:46:27 +00:00
strength = sides [ l - > sidenum [ 0 ] ] . textureoffset > > FRACBITS ;
if ( strength > 0 ) // sludge
strength = strength * 2 ; // otherwise, the maximum sludginess value is +967...
2016-06-02 22:30:50 +00:00
// The following might seem odd. At the time of movement,
// the move distance is multiplied by 'friction/0x10000', so a
// higher friction value actually means 'less friction'.
2016-06-02 21:46:27 +00:00
friction = ORIG_FRICTION - ( 0x1EB8 * strength ) / 0x80 ; // ORIG_FRICTION is 0xE800
2014-03-15 16:59:03 +00:00
if ( friction > FRACUNIT )
friction = FRACUNIT ;
if ( friction < 0 )
friction = 0 ;
2016-08-09 23:05:46 +00:00
movefactor = FixedDiv ( ORIG_FRICTION , friction ) ;
if ( movefactor < FRACUNIT )
movefactor = 8 * movefactor - 7 * FRACUNIT ;
2014-03-15 16:59:03 +00:00
else
2016-08-09 23:05:46 +00:00
movefactor = FRACUNIT ;
2014-03-15 16:59:03 +00:00
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
Add_Friction ( friction , movefactor , s , - 1 ) ;
}
}
/*
SoM : 3 / 8 / 2000 : Push / Pull / Wind / Current functions .
Add_Pusher ,
PIT_PushThing ,
T_Pusher ,
P_GetPushThing ,
P_SpawnPushers
*/
# define PUSH_FACTOR 7
/** Adds a pusher.
*
* \ param type Type of push / pull effect .
* \ param x_mag X magnitude .
* \ param y_mag Y magnitude .
* \ param source For a point pusher / puller , the source object .
* \ param affectee Target sector .
* \ param referrer What sector set it
* \ sa T_Pusher , P_GetPushThing , P_SpawnPushers
*/
static void Add_Pusher ( pushertype_e type , fixed_t x_mag , fixed_t y_mag , mobj_t * source , INT32 affectee , INT32 referrer , INT32 exclusive , INT32 slider )
{
pusher_t * p = Z_Calloc ( sizeof * p , PU_LEVSPEC , NULL ) ;
p - > thinker . function . acp1 = ( actionf_p1 ) T_Pusher ;
p - > source = source ;
p - > type = type ;
p - > x_mag = x_mag > > FRACBITS ;
p - > y_mag = y_mag > > FRACBITS ;
p - > exclusive = exclusive ;
p - > slider = slider ;
if ( referrer ! = - 1 )
{
p - > roverpusher = true ;
p - > referrer = referrer ;
}
else
p - > roverpusher = false ;
// "The right triangle of the square of the length of the hypotenuse is equal to the sum of the squares of the lengths of the other two sides."
// "Bah! Stupid brains! Don't you know anything besides the Pythagorean Theorem?" - Earthworm Jim
if ( type = = p_downcurrent | | type = = p_upcurrent | | type = = p_upwind | | type = = p_downwind )
p - > magnitude = P_AproxDistance ( p - > x_mag , p - > y_mag ) < < ( FRACBITS - PUSH_FACTOR ) ;
else
p - > magnitude = P_AproxDistance ( p - > x_mag , p - > y_mag ) ;
if ( source ) // point source exist?
{
// where force goes to zero
if ( type = = p_push )
p - > radius = AngleFixed ( source - > angle ) ;
else
p - > radius = ( p - > magnitude ) < < ( FRACBITS + 1 ) ;
p - > x = p - > source - > x ;
p - > y = p - > source - > y ;
p - > z = p - > source - > z ;
}
p - > affectee = affectee ;
2019-04-20 20:37:19 +00:00
P_AddThinker ( THINK_MAIN , & p - > thinker ) ;
2014-03-15 16:59:03 +00:00
}
// PIT_PushThing determines the angle and magnitude of the effect.
// The object's x and y momentum values are changed.
static pusher_t * tmpusher ; // pusher structure for blockmap searches
/** Applies a point pusher/puller to a thing.
*
* \ param thing Thing to be pushed .
* \ return True if the thing was pushed .
* \ todo Make a more robust P_BlockThingsIterator ( ) so the hidden parameter
* : : tmpusher won ' t need to be used .
* \ sa T_Pusher
*/
static inline boolean PIT_PushThing ( mobj_t * thing )
{
2016-03-09 09:30:52 +00:00
if ( thing - > eflags & MFE_PUSHED )
2014-03-15 16:59:03 +00:00
return false ;
2016-09-23 22:48:48 +00:00
if ( thing - > player & & thing - > player - > powers [ pw_carry ] = = CR_ROPEHANG )
2014-03-15 16:59:03 +00:00
return false ;
2021-02-22 01:32:00 +00:00
if ( ! tmpusher - > source )
return false ;
2014-03-15 16:59:03 +00:00
// Allow this to affect pushable objects at some point?
2017-03-14 18:11:17 +00:00
if ( thing - > player & & ( ! ( thing - > flags & ( MF_NOGRAVITY | MF_NOCLIP ) ) | | thing - > player - > powers [ pw_carry ] = = CR_NIGHTSMODE ) )
2014-03-15 16:59:03 +00:00
{
INT32 dist ;
INT32 speed ;
INT32 sx , sy , sz ;
sx = tmpusher - > x ;
sy = tmpusher - > y ;
sz = tmpusher - > z ;
// don't fade wrt Z if health & 2 (mapthing has multi flag)
if ( tmpusher - > source - > health & 2 )
dist = P_AproxDistance ( thing - > x - sx , thing - > y - sy ) ;
else
{
// Make sure the Z is in range
if ( thing - > z < sz - tmpusher - > radius | | thing - > z > sz + tmpusher - > radius )
return false ;
dist = P_AproxDistance ( P_AproxDistance ( thing - > x - sx , thing - > y - sy ) ,
thing - > z - sz ) ;
}
speed = ( tmpusher - > magnitude - ( ( dist > > FRACBITS ) > > 1 ) ) < < ( FRACBITS - PUSH_FACTOR - 1 ) ;
// If speed <= 0, you're outside the effective radius. You also have
// to be able to see the push/pull source point.
// Written with bits and pieces of P_HomingAttack
if ( ( speed > 0 ) & & ( P_CheckSight ( thing , tmpusher - > source ) ) )
{
2017-03-14 18:11:17 +00:00
if ( thing - > player - > powers [ pw_carry ] ! = CR_NIGHTSMODE )
2014-03-15 16:59:03 +00:00
{
// only push wrt Z if health & 1 (mapthing has ambush flag)
if ( tmpusher - > source - > health & 1 )
{
fixed_t tmpmomx , tmpmomy , tmpmomz ;
tmpmomx = FixedMul ( FixedDiv ( sx - thing - > x , dist ) , speed ) ;
tmpmomy = FixedMul ( FixedDiv ( sy - thing - > y , dist ) , speed ) ;
tmpmomz = FixedMul ( FixedDiv ( sz - thing - > z , dist ) , speed ) ;
if ( tmpusher - > source - > type = = MT_PUSH ) // away!
{
tmpmomx * = - 1 ;
tmpmomy * = - 1 ;
tmpmomz * = - 1 ;
}
thing - > momx + = tmpmomx ;
thing - > momy + = tmpmomy ;
thing - > momz + = tmpmomz ;
if ( thing - > player )
{
thing - > player - > cmomx + = tmpmomx ;
thing - > player - > cmomy + = tmpmomy ;
thing - > player - > cmomx = FixedMul ( thing - > player - > cmomx , 0xe800 ) ;
thing - > player - > cmomy = FixedMul ( thing - > player - > cmomy , 0xe800 ) ;
}
}
else
{
angle_t pushangle ;
pushangle = R_PointToAngle2 ( thing - > x , thing - > y , sx , sy ) ;
if ( tmpusher - > source - > type = = MT_PUSH )
pushangle + = ANGLE_180 ; // away
pushangle > > = ANGLETOFINESHIFT ;
thing - > momx + = FixedMul ( speed , FINECOSINE ( pushangle ) ) ;
thing - > momy + = FixedMul ( speed , FINESINE ( pushangle ) ) ;
if ( thing - > player )
{
thing - > player - > cmomx + = FixedMul ( speed , FINECOSINE ( pushangle ) ) ;
thing - > player - > cmomy + = FixedMul ( speed , FINESINE ( pushangle ) ) ;
thing - > player - > cmomx = FixedMul ( thing - > player - > cmomx , 0xe800 ) ;
thing - > player - > cmomy = FixedMul ( thing - > player - > cmomy , 0xe800 ) ;
}
}
}
else
{
//NiGHTS-specific handling.
//By default, pushes and pulls only affect the Z-axis.
//By having the ambush flag, it affects the X-axis.
//By having the object special flag, it affects the Y-axis.
fixed_t tmpmomx , tmpmomy , tmpmomz ;
if ( tmpusher - > source - > health & 1 )
tmpmomx = FixedMul ( FixedDiv ( sx - thing - > x , dist ) , speed ) ;
else
tmpmomx = 0 ;
if ( tmpusher - > source - > health & 2 )
tmpmomy = FixedMul ( FixedDiv ( sy - thing - > y , dist ) , speed ) ;
else
tmpmomy = 0 ;
tmpmomz = FixedMul ( FixedDiv ( sz - thing - > z , dist ) , speed ) ;
if ( tmpusher - > source - > type = = MT_PUSH ) // away!
{
tmpmomx * = - 1 ;
tmpmomy * = - 1 ;
tmpmomz * = - 1 ;
}
thing - > momx + = tmpmomx ;
thing - > momy + = tmpmomy ;
thing - > momz + = tmpmomz ;
if ( thing - > player )
{
thing - > player - > cmomx + = tmpmomx ;
thing - > player - > cmomy + = tmpmomy ;
thing - > player - > cmomx = FixedMul ( thing - > player - > cmomx , 0xe800 ) ;
thing - > player - > cmomy = FixedMul ( thing - > player - > cmomy , 0xe800 ) ;
}
}
}
}
if ( tmpusher - > exclusive )
2016-03-09 09:30:52 +00:00
thing - > eflags | = MFE_PUSHED ;
2014-03-15 16:59:03 +00:00
return true ;
}
/** Applies a pusher to all affected objects.
*
* \ param p Thinker for the pusher effect .
* \ todo Split up into multiple functions .
* \ sa Add_Pusher , PIT_PushThing
*/
void T_Pusher ( pusher_t * p )
{
2015-10-10 16:57:35 +00:00
sector_t * sec , * referrer = NULL ;
2014-03-15 16:59:03 +00:00
mobj_t * thing ;
msecnode_t * node ;
INT32 xspeed = 0 , yspeed = 0 ;
INT32 xl , xh , yl , yh , bx , by ;
INT32 radius ;
//INT32 ht = 0;
boolean inFOF ;
boolean touching ;
boolean moved ;
xspeed = yspeed = 0 ;
sec = sectors + p - > affectee ;
// Be sure the special sector type is still turned on. If so, proceed.
// Else, bail out; the sector type has been changed on us.
if ( p - > roverpusher )
{
2015-05-24 17:53:30 +00:00
referrer = & sectors [ p - > referrer ] ;
2014-03-15 16:59:03 +00:00
2016-06-02 21:46:27 +00:00
if ( GETSECSPECIAL ( referrer - > special , 3 ) ! = 2 )
2015-05-24 17:53:30 +00:00
return ;
2014-03-15 16:59:03 +00:00
}
2016-06-02 21:46:27 +00:00
else if ( GETSECSPECIAL ( sec - > special , 3 ) ! = 2 )
2014-03-15 16:59:03 +00:00
return ;
// For constant pushers (wind/current) there are 3 situations:
//
// 1) Affected Thing is above the floor.
//
// Apply the full force if wind, no force if current.
//
// 2) Affected Thing is on the ground.
//
// Apply half force if wind, full force if current.
//
// 3) Affected Thing is below the ground (underwater effect).
//
// Apply no force if wind, full force if current.
//
// Apply the effect to clipped players only for now.
//
// In Phase II, you can apply these effects to Things other than players.
if ( p - > type = = p_push )
{
// Seek out all pushable things within the force radius of this
// point pusher. Crosses sectors, so use blockmap.
tmpusher = p ; // MT_PUSH/MT_PULL point source
radius = p - > radius ; // where force goes to zero
tmbbox [ BOXTOP ] = p - > y + radius ;
tmbbox [ BOXBOTTOM ] = p - > y - radius ;
tmbbox [ BOXRIGHT ] = p - > x + radius ;
tmbbox [ BOXLEFT ] = p - > x - radius ;
2015-04-09 01:50:49 +00:00
xl = ( unsigned ) ( tmbbox [ BOXLEFT ] - bmaporgx - MAXRADIUS ) > > MAPBLOCKSHIFT ;
xh = ( unsigned ) ( tmbbox [ BOXRIGHT ] - bmaporgx + MAXRADIUS ) > > MAPBLOCKSHIFT ;
yl = ( unsigned ) ( tmbbox [ BOXBOTTOM ] - bmaporgy - MAXRADIUS ) > > MAPBLOCKSHIFT ;
yh = ( unsigned ) ( tmbbox [ BOXTOP ] - bmaporgy + MAXRADIUS ) > > MAPBLOCKSHIFT ;
2014-03-15 16:59:03 +00:00
for ( bx = xl ; bx < = xh ; bx + + )
for ( by = yl ; by < = yh ; by + + )
P_BlockThingsIterator ( bx , by , PIT_PushThing ) ;
return ;
}
// constant pushers p_wind and p_current
node = sec - > touching_thinglist ; // things touching this sector
2016-06-09 13:16:02 +00:00
for ( ; node ; node = node - > m_thinglist_next )
2014-03-15 16:59:03 +00:00
{
thing = node - > m_thing ;
if ( thing - > flags & ( MF_NOGRAVITY | MF_NOCLIP )
& & ! ( thing - > type = = MT_SMALLBUBBLE
2014-11-12 00:55:07 +00:00
| | thing - > type = = MT_MEDIUMBUBBLE
| | thing - > type = = MT_EXTRALARGEBUBBLE ) )
2014-03-15 16:59:03 +00:00
continue ;
2020-01-08 22:36:29 +00:00
if ( ! ( ( thing - > flags & MF_PUSHABLE ) | | ( ( thing - > info - > flags & MF_PUSHABLE ) & & thing - > fuse ) )
& & ! ( thing - > type = = MT_PLAYER
2014-11-12 00:55:07 +00:00
| | thing - > type = = MT_SMALLBUBBLE
| | thing - > type = = MT_MEDIUMBUBBLE
| | thing - > type = = MT_EXTRALARGEBUBBLE
| | thing - > type = = MT_LITTLETUMBLEWEED
| | thing - > type = = MT_BIGTUMBLEWEED ) )
2014-03-15 16:59:03 +00:00
continue ;
2016-03-09 09:30:52 +00:00
if ( thing - > eflags & MFE_PUSHED )
2014-03-15 16:59:03 +00:00
continue ;
2016-09-23 22:48:48 +00:00
if ( thing - > player & & thing - > player - > powers [ pw_carry ] = = CR_ROPEHANG )
2014-03-15 16:59:03 +00:00
continue ;
if ( thing - > player & & ( thing - > state = = & states [ thing - > info - > painstate ] ) & & ( thing - > player - > powers [ pw_flashing ] > ( flashingtics / 4 ) * 3 & & thing - > player - > powers [ pw_flashing ] < = flashingtics ) )
continue ;
inFOF = touching = moved = false ;
// Find the area that the 'thing' is in
if ( p - > roverpusher )
{
2015-05-24 17:53:30 +00:00
fixed_t top , bottom ;
2014-03-15 16:59:03 +00:00
2015-05-24 17:53:30 +00:00
top = P_GetSpecialTopZ ( thing , referrer , sec ) ;
bottom = P_GetSpecialBottomZ ( thing , referrer , sec ) ;
2014-03-15 16:59:03 +00:00
if ( thing - > eflags & MFE_VERTICALFLIP )
{
2015-05-24 17:53:30 +00:00
if ( bottom > thing - > z + thing - > height
| | top < ( thing - > z + ( thing - > height > > 1 ) ) )
2014-03-15 16:59:03 +00:00
continue ;
2015-05-24 17:53:30 +00:00
if ( thing - > z < bottom )
2014-03-15 16:59:03 +00:00
touching = true ;
2015-05-24 17:53:30 +00:00
if ( thing - > z + ( thing - > height > > 1 ) > bottom )
2014-03-15 16:59:03 +00:00
inFOF = true ;
}
else
{
2016-09-08 21:55:11 +00:00
if ( top < thing - > z | | bottom > ( thing - > z + ( thing - > height > > 1 ) ) )
2014-03-15 16:59:03 +00:00
continue ;
2015-05-24 17:53:30 +00:00
if ( thing - > z + thing - > height > top )
2014-03-15 16:59:03 +00:00
touching = true ;
2015-05-24 17:53:30 +00:00
if ( thing - > z + ( thing - > height > > 1 ) < top )
2014-03-15 16:59:03 +00:00
inFOF = true ;
}
}
else // Treat the entire sector as one big FOF
{
2015-05-24 17:53:30 +00:00
if ( thing - > z = = P_GetSpecialBottomZ ( thing , sec , sec ) )
2014-03-15 16:59:03 +00:00
touching = true ;
else if ( p - > type ! = p_current )
inFOF = true ;
}
if ( ! touching & & ! inFOF ) // Object is out of range of effect
continue ;
if ( p - > type = = p_wind )
{
if ( touching ) // on ground
{
xspeed = ( p - > x_mag ) > > 1 ; // half force
yspeed = ( p - > y_mag ) > > 1 ;
moved = true ;
}
else if ( inFOF )
{
xspeed = ( p - > x_mag ) ; // full force
yspeed = ( p - > y_mag ) ;
moved = true ;
}
}
else if ( p - > type = = p_upwind )
{
if ( touching ) // on ground
{
thing - > momz + = ( p - > magnitude ) > > 1 ;
moved = true ;
}
else if ( inFOF )
{
thing - > momz + = p - > magnitude ;
moved = true ;
}
}
else if ( p - > type = = p_downwind )
{
if ( touching ) // on ground
{
thing - > momz - = ( p - > magnitude ) > > 1 ;
moved = true ;
}
else if ( inFOF )
{
thing - > momz - = p - > magnitude ;
moved = true ;
}
}
else // p_current
{
if ( ! touching & & ! inFOF ) // Not in water at all
xspeed = yspeed = 0 ; // no force
else // underwater / touching water
{
if ( p - > type = = p_upcurrent )
thing - > momz + = p - > magnitude ;
else if ( p - > type = = p_downcurrent )
thing - > momz - = p - > magnitude ;
else
{
xspeed = p - > x_mag ; // full force
yspeed = p - > y_mag ;
}
moved = true ;
}
}
if ( p - > type ! = p_downcurrent & & p - > type ! = p_upcurrent
& & p - > type ! = p_upwind & & p - > type ! = p_downwind )
{
thing - > momx + = xspeed < < ( FRACBITS - PUSH_FACTOR ) ;
thing - > momy + = yspeed < < ( FRACBITS - PUSH_FACTOR ) ;
if ( thing - > player )
{
thing - > player - > cmomx + = xspeed < < ( FRACBITS - PUSH_FACTOR ) ;
thing - > player - > cmomy + = yspeed < < ( FRACBITS - PUSH_FACTOR ) ;
thing - > player - > cmomx = FixedMul ( thing - > player - > cmomx , ORIG_FRICTION ) ;
thing - > player - > cmomy = FixedMul ( thing - > player - > cmomy , ORIG_FRICTION ) ;
}
// Tumbleweeds bounce a bit...
if ( thing - > type = = MT_LITTLETUMBLEWEED | | thing - > type = = MT_BIGTUMBLEWEED )
thing - > momz + = P_AproxDistance ( xspeed < < ( FRACBITS - PUSH_FACTOR ) , yspeed < < ( FRACBITS - PUSH_FACTOR ) ) > > 2 ;
}
if ( moved )
{
if ( p - > slider & & thing - > player )
{
2017-03-18 21:06:06 +00:00
pflags_t jumped = ( thing - > player - > pflags & ( PF_JUMPED | PF_NOJUMPDAMAGE ) ) ;
2014-03-15 16:59:03 +00:00
P_ResetPlayer ( thing - > player ) ;
if ( jumped )
2017-03-18 21:06:06 +00:00
thing - > player - > pflags | = jumped ;
2014-03-15 16:59:03 +00:00
thing - > player - > pflags | = PF_SLIDING ;
thing - > angle = R_PointToAngle2 ( 0 , 0 , xspeed < < ( FRACBITS - PUSH_FACTOR ) , yspeed < < ( FRACBITS - PUSH_FACTOR ) ) ;
2019-12-30 21:05:24 +00:00
if ( ! demoplayback | | P_ControlStyle ( thing - > player ) = = CS_LMAOGALOG )
2014-03-15 16:59:03 +00:00
{
2020-05-28 09:03:35 +00:00
angle_t angle = thing - > player - > angleturn < < 16 ;
if ( thing - > angle - angle > ANGLE_180 )
P_SetPlayerAngle ( thing - > player , angle - ( angle - thing - > angle ) / 8 ) ;
else
P_SetPlayerAngle ( thing - > player , angle + ( thing - > angle - angle ) / 8 ) ;
//P_SetPlayerAngle(thing->player, thing->angle);
2014-03-15 16:59:03 +00:00
}
}
if ( p - > exclusive )
2016-03-09 09:30:52 +00:00
thing - > eflags | = MFE_PUSHED ;
2014-03-15 16:59:03 +00:00
}
}
}
/** Gets a push/pull object.
*
* \ param s Sector number to look in .
* \ return Pointer to the first : : MT_PUSH or : : MT_PULL object found in the
* sector .
* \ sa P_GetTeleportDestThing , P_GetStarpostThing , P_GetAltViewThing
*/
mobj_t * P_GetPushThing ( UINT32 s )
{
mobj_t * thing ;
sector_t * sec ;
sec = sectors + s ;
thing = sec - > thinglist ;
while ( thing )
{
switch ( thing - > type )
{
case MT_PUSH :
case MT_PULL :
return thing ;
default :
break ;
}
thing = thing - > snext ;
}
return NULL ;
}
/** Spawns pushers.
*
* \ todo Remove magic numbers .
* \ sa P_SpawnSpecials , Add_Pusher
*/
static void P_SpawnPushers ( void )
{
size_t i ;
line_t * l = lines ;
2020-04-17 16:15:25 +00:00
mtag_t tag ;
2014-03-15 16:59:03 +00:00
register INT32 s ;
mobj_t * thing ;
for ( i = 0 ; i < numlines ; i + + , l + + )
2020-04-17 16:15:25 +00:00
{
tag = Tag_FGet ( & l - > tags ) ;
2014-03-15 16:59:03 +00:00
switch ( l - > special )
{
2019-06-04 17:34:59 +00:00
case 541 : // wind
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2019-06-04 17:34:59 +00:00
Add_Pusher ( p_wind , l - > dx , l - > dy , NULL , s , - 1 , l - > flags & ML_NOCLIMB , l - > flags & ML_EFFECT4 ) ;
break ;
case 544 : // current
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2019-06-04 17:34:59 +00:00
Add_Pusher ( p_current , l - > dx , l - > dy , NULL , s , - 1 , l - > flags & ML_NOCLIMB , l - > flags & ML_EFFECT4 ) ;
break ;
case 547 : // push/pull
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2014-03-15 16:59:03 +00:00
{
2019-06-04 17:34:59 +00:00
thing = P_GetPushThing ( s ) ;
if ( thing ) // No MT_P* means no effect
2014-03-15 16:59:03 +00:00
Add_Pusher ( p_push , l - > dx , l - > dy , thing , s , - 1 , l - > flags & ML_NOCLIMB , l - > flags & ML_EFFECT4 ) ;
}
2019-06-04 17:34:59 +00:00
break ;
case 545 : // current up
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2019-06-04 17:34:59 +00:00
Add_Pusher ( p_upcurrent , l - > dx , l - > dy , NULL , s , - 1 , l - > flags & ML_NOCLIMB , l - > flags & ML_EFFECT4 ) ;
break ;
case 546 : // current down
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2019-06-04 17:34:59 +00:00
Add_Pusher ( p_downcurrent , l - > dx , l - > dy , NULL , s , - 1 , l - > flags & ML_NOCLIMB , l - > flags & ML_EFFECT4 ) ;
break ;
case 542 : // wind up
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2019-06-04 17:34:59 +00:00
Add_Pusher ( p_upwind , l - > dx , l - > dy , NULL , s , - 1 , l - > flags & ML_NOCLIMB , l - > flags & ML_EFFECT4 ) ;
break ;
case 543 : // wind down
2021-02-11 12:24:20 +00:00
TAG_ITER_SECTORS ( tag , s )
2019-06-04 17:34:59 +00:00
Add_Pusher ( p_downwind , l - > dx , l - > dy , NULL , s , - 1 , l - > flags & ML_NOCLIMB , l - > flags & ML_EFFECT4 ) ;
break ;
2014-03-15 16:59:03 +00:00
}
2020-04-17 16:15:25 +00:00
}
2019-08-07 16:39:51 +00:00
}