2016-03-01 15:47:10 +00:00
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
// Movement, collision handling.
// Shooting and aiming.
//
//-----------------------------------------------------------------------------
# include <stdlib.h>
# include <math.h>
# include "templates.h"
# include "m_bbox.h"
# include "m_random.h"
# include "i_system.h"
# include "c_dispatch.h"
2016-03-11 14:45:47 +00:00
# include "math/cmath.h"
2016-03-01 15:47:10 +00:00
# include "doomdef.h"
# include "p_local.h"
# include "p_spec.h"
# include "d_player.h"
# include "p_maputl.h"
# include "p_lnspec.h"
# include "p_effect.h"
# include "p_terrain.h"
# include "p_trace.h"
# include "p_checkposition.h"
# include "r_utility.h"
2016-04-21 20:59:07 +00:00
# include "p_blockmap.h"
2016-04-27 10:37:38 +00:00
# include "p_3dmidtex.h"
2016-03-01 15:47:10 +00:00
# include "s_sound.h"
# include "decallib.h"
// State.
# include "doomstat.h"
# include "r_state.h"
# include "gi.h"
# include "a_sharedglobal.h"
# include "p_conversation.h"
# include "r_data/r_translate.h"
# include "g_level.h"
# include "r_sky.h"
CVAR ( Bool , cl_bloodsplats , true , CVAR_ARCHIVE )
CVAR ( Int , sv_smartaim , 0 , CVAR_ARCHIVE | CVAR_SERVERINFO )
CVAR ( Bool , cl_doautoaim , false , CVAR_ARCHIVE )
2016-03-26 22:19:38 +00:00
static void CheckForPushSpecial ( line_t * line , int side , AActor * mobj , DVector2 * posforwindowcheck = NULL ) ;
2016-03-01 15:47:10 +00:00
static void SpawnShootDecal ( AActor * t1 , const FTraceResults & trace ) ;
2016-03-06 20:58:36 +00:00
static void SpawnDeepSplash ( AActor * t1 , const FTraceResults & trace , AActor * puff ) ;
2016-03-01 15:47:10 +00:00
static FRandom pr_tracebleed ( " TraceBleed " ) ;
static FRandom pr_checkthing ( " CheckThing " ) ;
static FRandom pr_lineattack ( " LineAttack " ) ;
static FRandom pr_crunch ( " DoCrunch " ) ;
// keep track of special lines as they are hit,
// but don't process them until the move is proven valid
TArray < spechit_t > spechit ;
TArray < spechit_t > portalhit ;
// Temporary holder for thing_sectorlist threads
msecnode_t * sector_list = NULL ; // phares 3/16/98
//==========================================================================
//
// FindRefPoint
//
// Finds the point on the line closest to the given coordinate
//
//==========================================================================
2016-03-26 22:19:38 +00:00
static DVector2 FindRefPoint ( line_t * ld , const DVector2 & pos )
2016-03-01 15:47:10 +00:00
{
// If there's any chance of slopes getting in the way we need to get a proper refpoint, otherwise we can save the work.
// Slopes can get in here when:
// - the actual sector planes are sloped
// - there's 3D floors in this sector
// - there's a crossable floor portal (for which the dropoff needs to be calculated within P_LineOpening, and the lower sector can easily have slopes)
2016-03-26 22:19:38 +00:00
//
// Todo: check if this bootload of checks even helps or if it adds more than it saves
//
if ( ld - > frontsector - > floorplane . isSlope ( ) | |
ld - > backsector - > floorplane . isSlope ( ) | |
ld - > frontsector - > ceilingplane . isSlope ( ) | |
ld - > backsector - > ceilingplane . isSlope ( ) | |
ld - > backsector - > e - > XFloor . ffloors . Size ( ) ! = 0 | |
ld - > frontsector - > e - > XFloor . ffloors . Size ( ) ! = 0 | |
! ld - > frontsector - > PortalBlocksMovement ( sector_t : : ceiling ) | |
2016-03-01 15:47:10 +00:00
! ld - > frontsector - > PortalBlocksMovement ( sector_t : : floor ) )
{
2016-03-26 22:19:38 +00:00
2016-03-31 19:13:32 +00:00
DVector2 v1 = ld - > v1 - > fPos ( ) ;
2016-03-26 22:19:38 +00:00
DVector2 d = ld - > Delta ( ) ;
double r = clamp ( ( ( pos . X - v1 . X ) * d . X + ( pos . Y - v1 . Y ) * d . Y ) / ( d . X * d . X + d . Y * d . Y ) , 0. , 1. ) ;
return v1 + d * r ;
2016-03-01 15:47:10 +00:00
}
return pos ;
}
//==========================================================================
//
// PIT_FindFloorCeiling
//
// only3d set means to only check against 3D floors and midtexes.
//
//==========================================================================
bool ffcf_verbose ;
static bool PIT_FindFloorCeiling ( FMultiBlockLinesIterator & mit , FMultiBlockLinesIterator : : CheckResult & cres , const FBoundingBox & box , FCheckPosition & tmf , int flags )
{
line_t * ld = cres . line ;
2016-03-26 22:19:38 +00:00
if ( ! box . inRange ( ld ) | | box . BoxOnLineSide ( ld ) ! = - 1 )
2016-03-01 15:47:10 +00:00
return true ;
// A line has been hit
if ( ffcf_verbose )
{
Printf ( " Hit line %d at position %f,%f, group %d \n " ,
2016-03-26 22:19:38 +00:00
int ( ld - lines ) , cres . Position . X , cres . Position . Y , ld - > frontsector - > PortalGroup ) ;
2016-03-01 15:47:10 +00:00
}
if ( ! ld - > backsector )
{ // One sided line
return true ;
}
2016-03-26 22:19:38 +00:00
DVector2 refpoint = FindRefPoint ( ld , cres . Position ) ;
2016-03-01 15:47:10 +00:00
FLineOpening open ;
2016-03-26 22:19:38 +00:00
P_LineOpening ( open , tmf . thing , ld , refpoint , & cres . Position , flags ) ;
2016-03-01 15:47:10 +00:00
// adjust floor / ceiling heights
if ( ! ( flags & FFCF_NOCEILING ) )
{
2016-03-25 17:19:54 +00:00
if ( open . top < tmf . ceilingz )
2016-03-01 15:47:10 +00:00
{
tmf . ceilingz = open . top ;
2016-03-06 20:58:36 +00:00
if ( open . topsec ! = NULL ) tmf . ceilingsector = open . topsec ;
2016-03-25 17:19:54 +00:00
if ( ffcf_verbose ) Printf ( " Adjust ceilingz to %f \n " , open . top ) ;
2016-03-01 15:47:10 +00:00
mit . StopUp ( ) ;
}
}
if ( ! ( flags & FFCF_NOFLOOR ) )
{
2016-03-25 17:19:54 +00:00
if ( open . bottom > tmf . floorz )
2016-03-01 15:47:10 +00:00
{
2016-03-25 17:19:54 +00:00
tmf . floorz = open . bottom ;
2016-03-01 15:47:10 +00:00
if ( open . bottomsec ! = NULL ) tmf . floorsector = open . bottomsec ;
tmf . touchmidtex = open . touchmidtex ;
tmf . abovemidtex = open . abovemidtex ;
2016-03-25 17:19:54 +00:00
if ( ffcf_verbose ) Printf ( " Adjust floorz to %f \n " , open . bottom ) ;
2016-03-23 11:21:52 +00:00
if ( tmf . floorz > tmf . dropoffz + tmf . thing - > MaxDropOffHeight ) mit . StopDown ( ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-25 17:19:54 +00:00
else if ( open . bottom = = tmf . floorz )
2016-03-01 15:47:10 +00:00
{
tmf . touchmidtex | = open . touchmidtex ;
tmf . abovemidtex | = open . abovemidtex ;
}
2016-03-25 17:19:54 +00:00
if ( open . lowfloor < tmf . dropoffz & & open . lowfloor > LINEOPEN_MIN )
2016-03-01 15:47:10 +00:00
{
2016-03-25 17:19:54 +00:00
tmf . dropoffz = open . lowfloor ;
if ( ffcf_verbose ) Printf ( " Adjust dropoffz to %f \n " , open . bottom ) ;
2016-03-23 11:21:52 +00:00
if ( tmf . floorz > tmf . dropoffz + tmf . thing - > MaxDropOffHeight ) mit . StopDown ( ) ;
2016-03-01 15:47:10 +00:00
}
}
return true ;
}
//==========================================================================
//
// calculates the actual floor and ceiling position at a given
// coordinate. Traverses through portals unless being told not to.
//
//==========================================================================
void P_GetFloorCeilingZ ( FCheckPosition & tmf , int flags )
{
2016-03-26 22:19:38 +00:00
sector_t * sec = ( ! ( flags & FFCF_SAMESECTOR ) | | tmf . thing - > Sector = = NULL ) ? P_PointInSector ( tmf . pos ) : tmf . sector ;
2016-03-01 15:47:10 +00:00
F3DFloor * ffc , * fff ;
2016-03-26 22:19:38 +00:00
tmf . ceilingz = sec - > NextHighestCeilingAt ( tmf . pos . X , tmf . pos . Y , tmf . pos . Z , tmf . pos . Z + tmf . thing - > Height , flags , & tmf . ceilingsector , & ffc ) ;
tmf . floorz = tmf . dropoffz = sec - > NextLowestFloorAt ( tmf . pos . X , tmf . pos . Y , tmf . pos . Z , flags , tmf . thing - > MaxStepHeight , & tmf . floorsector , & fff ) ;
2016-03-01 15:47:10 +00:00
if ( fff )
{
tmf . floorpic = * fff - > top . texture ;
tmf . floorterrain = fff - > model - > GetTerrain ( fff - > top . isceiling ) ;
}
else
{
tmf . floorpic = tmf . floorsector - > GetTexture ( sector_t : : floor ) ;
tmf . floorterrain = tmf . floorsector - > GetTerrain ( sector_t : : floor ) ;
}
tmf . ceilingpic = ffc ? * ffc - > bottom . texture : tmf . ceilingsector - > GetTexture ( sector_t : : ceiling ) ;
tmf . sector = sec ;
}
//==========================================================================
//
// P_FindFloorCeiling
//
//==========================================================================
void P_FindFloorCeiling ( AActor * actor , int flags )
{
FCheckPosition tmf ;
tmf . thing = actor ;
2016-03-26 22:19:38 +00:00
tmf . pos = actor - > Pos ( ) ;
2016-03-01 15:47:10 +00:00
if ( flags & FFCF_ONLYSPAWNPOS )
{
flags | = FFCF_3DRESTRICT ;
}
if ( flags & FFCF_SAMESECTOR )
{
tmf . sector = actor - > Sector ;
}
P_GetFloorCeilingZ ( tmf , flags ) ;
assert ( tmf . thing - > Sector ! = NULL ) ;
actor - > floorz = tmf . floorz ;
actor - > dropoffz = tmf . dropoffz ;
actor - > ceilingz = tmf . ceilingz ;
actor - > floorpic = tmf . floorpic ;
actor - > floorterrain = tmf . floorterrain ;
actor - > floorsector = tmf . floorsector ;
actor - > ceilingpic = tmf . ceilingpic ;
actor - > ceilingsector = tmf . ceilingsector ;
2016-03-20 18:52:35 +00:00
if ( ffcf_verbose ) Printf ( " Starting with ceilingz = %f, floorz = %f \n " , tmf . ceilingz , tmf . floorz ) ;
2016-03-01 15:47:10 +00:00
tmf . touchmidtex = false ;
tmf . abovemidtex = false ;
validcount + + ;
FPortalGroupArray grouplist ;
FMultiBlockLinesIterator mit ( grouplist , actor ) ;
FMultiBlockLinesIterator : : CheckResult cres ;
// if we already have a valid floor/ceiling sector within the current sector,
// we do not need to iterate through plane portals to find a floor or ceiling.
if ( actor - > floorsector = = actor - > Sector ) mit . StopDown ( ) ;
if ( actor - > ceilingsector = = actor - > Sector ) mit . StopUp ( ) ;
while ( ( mit . Next ( & cres ) ) )
{
PIT_FindFloorCeiling ( mit , cres , mit . Box ( ) , tmf , flags | cres . portalflags ) ;
}
2016-03-23 11:21:52 +00:00
if ( tmf . touchmidtex ) tmf . dropoffz = tmf . floorz ;
2016-03-01 15:47:10 +00:00
2016-03-20 18:52:35 +00:00
bool usetmf = ! ( flags & FFCF_ONLYSPAWNPOS ) | | ( tmf . abovemidtex & & ( tmf . floorz < = actor - > Z ( ) ) ) ;
2016-03-01 15:47:10 +00:00
// when actual floor or ceiling are beyond a portal plane we also need to use the result of the blockmap iterator, regardless of the flags being specified.
if ( usetmf | | tmf . floorsector - > PortalGroup ! = actor - > Sector - > PortalGroup )
{
actor - > floorz = tmf . floorz ;
actor - > dropoffz = tmf . dropoffz ;
actor - > floorpic = tmf . floorpic ;
actor - > floorterrain = tmf . floorterrain ;
actor - > floorsector = tmf . floorsector ;
}
if ( usetmf | | tmf . ceilingsector - > PortalGroup ! = actor - > Sector - > PortalGroup )
{
actor - > ceilingz = tmf . ceilingz ;
actor - > ceilingpic = tmf . ceilingpic ;
actor - > ceilingsector = tmf . ceilingsector ;
}
}
2016-11-30 00:25:51 +00:00
DEFINE_ACTION_FUNCTION ( AActor , FindFloorCeiling )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_INT_DEF ( flags ) ;
P_FindFloorCeiling ( self , flags ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
// Debug CCMD for checking errors in the MultiBlockLinesIterator (needs to be removed when this code is complete)
CCMD ( ffcf )
{
ffcf_verbose = true ;
P_FindFloorCeiling ( players [ 0 ] . mo , 0 ) ;
ffcf_verbose = false ;
}
//==========================================================================
//
// TELEPORT MOVE
//
//
// P_TeleportMove
//
// [RH] Added telefrag parameter: When true, anything in the spawn spot
// will always be telefragged, and the move will be successful.
// Added z parameter. Originally, the thing's z was set *after* the
// move was made, so the height checking I added for 1.13 could
// potentially erroneously indicate the move was okay if the thing
// was being teleported between two non-overlapping height ranges.
//
//==========================================================================
2016-03-26 22:19:38 +00:00
bool P_TeleportMove ( AActor * thing , const DVector3 & pos , bool telefrag , bool modifyactor )
2016-03-01 15:47:10 +00:00
{
FCheckPosition tmf ;
sector_t * oldsec = thing - > Sector ;
// kill anything occupying the position
// The base floor/ceiling is from the subsector that contains the point.
// Any contacted lines the step closer together will adjust them.
tmf . thing = thing ;
2016-03-26 22:19:38 +00:00
tmf . pos = pos ;
2016-03-01 15:47:10 +00:00
tmf . touchmidtex = false ;
tmf . abovemidtex = false ;
P_GetFloorCeilingZ ( tmf , 0 ) ;
2016-04-04 12:16:43 +00:00
spechit . Clear ( ) ; // this is needed so that no more specials get activated after crossing a teleporter.
2016-03-01 15:47:10 +00:00
bool StompAlwaysFrags = ( ( thing - > flags2 & MF2_TELESTOMP ) | | ( level . flags & LEVEL_MONSTERSTELEFRAG ) | | telefrag ) & & ! ( thing - > flags7 & MF7_NOTELESTOMP ) ;
// P_LineOpening requires the thing's z to be the destination z in order to work.
2016-03-26 22:19:38 +00:00
double savedz = thing - > Z ( ) ;
thing - > SetZ ( pos . Z ) ;
sector_t * sector = P_PointInSector ( pos ) ;
2016-03-01 15:47:10 +00:00
FPortalGroupArray grouplist ;
2016-03-26 22:19:38 +00:00
FMultiBlockLinesIterator mit ( grouplist , pos . X , pos . Y , pos . Z , thing - > Height , thing - > radius , sector ) ;
2016-03-01 15:47:10 +00:00
FMultiBlockLinesIterator : : CheckResult cres ;
while ( mit . Next ( & cres ) )
{
PIT_FindFloorCeiling ( mit , cres , mit . Box ( ) , tmf , 0 ) ;
}
2016-03-26 22:19:38 +00:00
thing - > SetZ ( savedz ) ;
2016-03-01 15:47:10 +00:00
2016-03-23 11:21:52 +00:00
if ( tmf . touchmidtex ) tmf . dropoffz = tmf . floorz ;
2016-03-01 15:47:10 +00:00
2016-03-26 22:19:38 +00:00
FMultiBlockThingsIterator mit2 ( grouplist , pos . X , pos . Y , pos . Z , thing - > Height , thing - > radius , false , sector ) ;
2016-03-01 15:47:10 +00:00
FMultiBlockThingsIterator : : CheckResult cres2 ;
while ( mit2 . Next ( & cres2 ) )
{
AActor * th = cres2 . thing ;
if ( ! ( th - > flags & MF_SHOOTABLE ) )
continue ;
// don't clip against self
if ( th = = thing )
continue ;
2016-03-26 22:19:38 +00:00
double blockdist = th - > radius + tmf . thing - > radius ;
if ( fabs ( th - > X ( ) - cres2 . Position . X ) > = blockdist | | fabs ( th - > Y ( ) - cres2 . Position . Y ) > = blockdist )
2016-03-01 15:47:10 +00:00
continue ;
if ( ( th - > flags2 | tmf . thing - > flags2 ) & MF2_THRUACTORS )
continue ;
if ( tmf . thing - > flags6 & MF6_THRUSPECIES & & tmf . thing - > GetSpecies ( ) = = th - > GetSpecies ( ) )
continue ;
// [RH] Z-Check
// But not if not MF2_PASSMOBJ or MF3_DONTOVERLAP are set!
// Otherwise those things would get stuck inside each other.
if ( ( thing - > flags2 & MF2_PASSMOBJ | | th - > flags4 & MF4_ACTLIKEBRIDGE ) & & ! ( i_compatflags & COMPATF_NO_PASSMOBJ ) )
{
if ( ! ( th - > flags3 & thing - > flags3 & MF3_DONTOVERLAP ) )
{
2016-03-26 22:19:38 +00:00
if ( pos . Z > th - > Top ( ) | | // overhead
pos . Z + thing - > Height < th - > Z ( ) ) // underneath
2016-03-01 15:47:10 +00:00
continue ;
}
}
// monsters don't stomp things except on boss level
// [RH] Some Heretic/Hexen monsters can telestomp
// ... and some items can never be telefragged while others will be telefragged by everything that teleports upon them.
if ( ( StompAlwaysFrags & & ! ( th - > flags6 & MF6_NOTELEFRAG ) ) | | ( th - > flags7 & MF7_ALWAYSTELEFRAG ) )
{
// Don't actually damage if predicting a teleport
if ( thing - > player = = NULL | | ! ( thing - > player - > cheats & CF_PREDICTING ) )
P_DamageMobj ( th , thing , thing , TELEFRAG_DAMAGE , NAME_Telefrag , DMG_THRUSTLESS ) ;
continue ;
}
return false ;
}
if ( modifyactor )
{
// the move is ok, so link the thing into its new position
2016-03-26 22:19:38 +00:00
thing - > SetOrigin ( pos , false ) ;
2016-03-01 15:47:10 +00:00
thing - > floorz = tmf . floorz ;
thing - > ceilingz = tmf . ceilingz ;
thing - > floorsector = tmf . floorsector ;
thing - > floorpic = tmf . floorpic ;
thing - > floorterrain = tmf . floorterrain ;
thing - > ceilingsector = tmf . ceilingsector ;
thing - > ceilingpic = tmf . ceilingpic ;
thing - > dropoffz = tmf . dropoffz ; // killough 11/98
thing - > BlockingLine = NULL ;
if ( thing - > flags2 & MF2_FLOORCLIP )
{
thing - > AdjustFloorClip ( ) ;
}
if ( thing = = players [ consoleplayer ] . camera )
{
R_ResetViewInterpolation ( ) ;
}
// If this teleport was caused by a move, P_TryMove() will handle the
// sector transition messages better than we can here.
2016-08-07 20:03:36 +00:00
// This needs to be compatibility optioned because some older maps exploited this missing feature.
if ( ! ( thing - > flags6 & MF6_INTRYMOVE ) & & ! ( i_compatflags2 & COMPATF2_TELEPORT ) )
2016-03-01 15:47:10 +00:00
{
thing - > CheckSectorTransition ( oldsec ) ;
}
}
return true ;
}
2016-11-03 12:38:40 +00:00
DEFINE_ACTION_FUNCTION ( AActor , TeleportMove )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_FLOAT ( x ) ;
PARAM_FLOAT ( y ) ;
PARAM_FLOAT ( z ) ;
PARAM_BOOL ( telefrag ) ;
PARAM_BOOL_DEF ( modify ) ;
ACTION_RETURN_BOOL ( P_TeleportMove ( self , DVector3 ( x , y , z ) , telefrag , modify ) ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// [RH] P_PlayerStartStomp
//
// Like P_TeleportMove, but it doesn't move anything, and only monsters and other
// players get telefragged.
//
//==========================================================================
void P_PlayerStartStomp ( AActor * actor , bool mononly )
{
FPortalGroupArray grouplist ;
FMultiBlockThingsIterator mit ( grouplist , actor ) ;
FMultiBlockThingsIterator : : CheckResult cres ;
while ( ( mit . Next ( & cres ) ) )
{
AActor * th = cres . thing ;
if ( ! ( th - > flags & MF_SHOOTABLE ) )
continue ;
// don't clip against self, and don't kill your own voodoo dolls
if ( th = = actor | | ( th - > player = = actor - > player & & th - > player ! = NULL ) )
continue ;
2016-03-26 22:19:38 +00:00
double blockdist = th - > radius + actor - > radius ;
if ( fabs ( th - > X ( ) - cres . Position . X ) > = blockdist | | fabs ( th - > Y ( ) - cres . Position . Y ) > = blockdist )
2016-03-01 15:47:10 +00:00
continue ;
// only kill monsters and other players
if ( th - > player = = NULL & & ! ( th - > flags3 & MF3_ISMONSTER ) )
continue ;
if ( th - > player ! = NULL & & mononly )
continue ;
if ( actor - > Z ( ) > th - > Top ( ) )
continue ; // overhead
if ( actor - > Top ( ) < th - > Z ( ) )
continue ; // underneath
P_DamageMobj ( th , actor , actor , TELEFRAG_DAMAGE , NAME_Telefrag ) ;
}
}
//==========================================================================
//
// killough 8/28/98:
//
// P_GetFriction()
//
// Returns the friction associated with a particular mobj.
//
//==========================================================================
2016-03-24 21:50:03 +00:00
double P_GetFriction ( const AActor * mo , double * frictionfactor )
2016-03-01 15:47:10 +00:00
{
2016-03-24 21:50:03 +00:00
double friction = ORIG_FRICTION ;
double movefactor = ORIG_FRICTION_FACTOR ;
double newfriction ;
double newmf ;
2016-03-01 15:47:10 +00:00
const msecnode_t * m ;
sector_t * sec ;
if ( mo - > IsNoClip2 ( ) )
{
// The default values are fine for noclip2 mode
}
else if ( mo - > flags2 & MF2_FLY & & mo - > flags & MF_NOGRAVITY )
{
friction = FRICTION_FLY ;
}
else if ( ( ! ( mo - > flags & MF_NOGRAVITY ) & & mo - > waterlevel > 1 ) | |
2016-03-20 18:52:35 +00:00
( mo - > waterlevel = = 1 & & mo - > Z ( ) > mo - > floorz + 6 ) )
2016-03-01 15:47:10 +00:00
{
2016-03-19 23:54:18 +00:00
friction = mo - > Sector - > GetFriction ( sector_t : : floor , & movefactor ) ;
2016-03-24 21:50:03 +00:00
movefactor * = 0.5 ;
2016-03-01 15:47:10 +00:00
2016-03-19 23:54:18 +00:00
// Check 3D floors -- might be the source of the waterlevel
for ( unsigned i = 0 ; i < mo - > Sector - > e - > XFloor . ffloors . Size ( ) ; i + + )
{
F3DFloor * rover = mo - > Sector - > e - > XFloor . ffloors [ i ] ;
if ( ! ( rover - > flags & FF_EXISTS ) ) continue ;
if ( ! ( rover - > flags & FF_SWIMMABLE ) ) continue ;
2016-03-01 15:47:10 +00:00
2016-03-30 07:41:46 +00:00
if ( mo - > Z ( ) > rover - > top . plane - > ZatPoint ( mo ) | |
mo - > Z ( ) < rover - > bottom . plane - > ZatPoint ( mo ) )
2016-03-19 23:54:18 +00:00
continue ;
2016-03-01 15:47:10 +00:00
2016-03-19 23:54:18 +00:00
newfriction = rover - > model - > GetFriction ( rover - > top . isceiling , & newmf ) ;
if ( newfriction < friction | | friction = = ORIG_FRICTION )
{
friction = newfriction ;
2016-03-24 21:50:03 +00:00
movefactor = newmf * 0.5 ;
2016-03-01 15:47:10 +00:00
}
2016-03-19 23:54:18 +00:00
}
2016-03-01 15:47:10 +00:00
}
else if ( var_friction & & ! ( mo - > flags & ( MF_NOCLIP | MF_NOGRAVITY ) ) )
{ // When the object is straddling sectors with the same
// floor height that have different frictions, use the lowest
// friction value (muddy has precedence over icy).
for ( m = mo - > touching_sectorlist ; m ; m = m - > m_tnext )
{
sec = m - > m_sector ;
2016-03-26 22:19:38 +00:00
DVector3 pos = mo - > PosRelative ( sec ) ;
2016-03-01 15:47:10 +00:00
// 3D floors must be checked, too
for ( unsigned i = 0 ; i < sec - > e - > XFloor . ffloors . Size ( ) ; i + + )
{
F3DFloor * rover = sec - > e - > XFloor . ffloors [ i ] ;
if ( ! ( rover - > flags & FF_EXISTS ) ) continue ;
if ( rover - > flags & FF_SOLID )
{
// Must be standing on a solid floor
2016-03-30 08:08:06 +00:00
if ( ! mo - > isAtZ ( rover - > top . plane - > ZatPoint ( pos ) ) ) continue ;
2016-03-01 15:47:10 +00:00
}
else if ( rover - > flags & FF_SWIMMABLE )
{
// Or on or inside a swimmable floor (e.g. in shallow water)
2016-03-26 22:19:38 +00:00
if ( mo - > Z ( ) > rover - > top . plane - > ZatPoint ( pos ) | |
( mo - > Top ( ) ) < rover - > bottom . plane - > ZatPoint ( pos ) )
2016-03-01 15:47:10 +00:00
continue ;
}
else
continue ;
2016-03-19 23:54:18 +00:00
newfriction = rover - > model - > GetFriction ( rover - > top . isceiling , & newmf ) ;
2016-03-01 15:47:10 +00:00
if ( newfriction < friction | | friction = = ORIG_FRICTION )
{
friction = newfriction ;
2016-03-30 08:08:06 +00:00
movefactor = newmf ;
2016-03-01 15:47:10 +00:00
}
}
if ( ! ( sec - > Flags & SECF_FRICTION ) & &
Terrains [ sec - > GetTerrain ( sector_t : : floor ) ] . Friction = = 0 )
{
continue ;
}
2016-03-19 23:54:18 +00:00
newfriction = sec - > GetFriction ( sector_t : : floor , & newmf ) ;
2016-03-01 15:47:10 +00:00
if ( ( newfriction < friction | | friction = = ORIG_FRICTION ) & &
2016-03-26 22:19:38 +00:00
( mo - > Z ( ) < = sec - > floorplane . ZatPoint ( pos ) | |
2016-03-01 15:47:10 +00:00
( sec - > GetHeightSec ( ) ! = NULL & &
2016-03-26 22:19:38 +00:00
mo - > Z ( ) < = sec - > heightsec - > floorplane . ZatPoint ( pos ) ) ) )
2016-03-01 15:47:10 +00:00
{
friction = newfriction ;
2016-03-19 23:54:18 +00:00
movefactor = newmf ;
2016-03-01 15:47:10 +00:00
}
}
}
2016-03-24 21:50:03 +00:00
if ( mo - > Friction ! = 1 )
2016-03-01 15:47:10 +00:00
{
2016-03-24 21:50:03 +00:00
friction = clamp ( ( friction * mo - > Friction ) , 0. , 1. ) ;
2016-03-01 15:47:10 +00:00
movefactor = FrictionToMoveFactor ( friction ) ;
}
if ( frictionfactor )
* frictionfactor = movefactor ;
return friction ;
}
//==========================================================================
//
// phares 3/19/98
// P_GetMoveFactor() returns the value by which the x,y
// movements are multiplied to add to player movement.
//
// killough 8/28/98: rewritten
//
//==========================================================================
2016-03-24 21:50:03 +00:00
double P_GetMoveFactor ( const AActor * mo , double * frictionp )
2016-03-01 15:47:10 +00:00
{
2016-03-24 21:50:03 +00:00
double movefactor , friction ;
2016-03-01 15:47:10 +00:00
// If the floor is icy or muddy, it's harder to get moving. This is where
// the different friction factors are applied to 'trying to move'. In
// p_mobj.c, the friction factors are applied as you coast and slow down.
if ( ( friction = P_GetFriction ( mo , & movefactor ) ) < ORIG_FRICTION )
{
// phares 3/11/98: you start off slowly, then increase as
// you get better footing
2016-03-19 23:54:18 +00:00
double velocity = mo - > VelXYToSpeed ( ) ;
2016-03-01 15:47:10 +00:00
2016-03-19 23:54:18 +00:00
if ( velocity > MORE_FRICTION_VELOCITY * 4 )
2016-03-24 21:50:03 +00:00
movefactor * = 8 ;
2016-03-19 23:54:18 +00:00
else if ( velocity > MORE_FRICTION_VELOCITY * 2 )
2016-03-24 21:50:03 +00:00
movefactor * = 4 ;
2016-03-01 15:47:10 +00:00
else if ( velocity > MORE_FRICTION_VELOCITY )
2016-03-24 21:50:03 +00:00
movefactor * = 2 ;
2016-03-01 15:47:10 +00:00
}
if ( frictionp )
* frictionp = friction ;
return movefactor ;
}
2016-11-30 00:25:51 +00:00
DEFINE_ACTION_FUNCTION ( AActor , GetFriction )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
double friction , movefactor = P_GetMoveFactor ( self , & friction ) ;
if ( numret > 1 )
{
numret = 2 ;
ret [ 1 ] . SetFloat ( movefactor ) ;
}
if ( numret > 0 )
{
ret [ 0 ] . SetFloat ( friction ) ;
}
return numret ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// Checks if the line intersects with the actor
// returns
// - 1 when above/below
// - 0 when intersecting
// - -1 when outside the portal
//
2016-04-19 09:35:28 +00:00
// Note that this check is done from the 'other' side of the portal
// so plane names seem to be inverted.
//
2016-03-01 15:47:10 +00:00
//==========================================================================
static int LineIsAbove ( line_t * line , AActor * actor )
{
2016-04-19 09:35:28 +00:00
if ( line - > frontsector - > PortalBlocksMovement ( sector_t : : floor ) ) return - 1 ;
return line - > frontsector - > GetPortalPlaneZ ( sector_t : : floor ) > = actor - > Top ( ) ;
2016-03-01 15:47:10 +00:00
}
static int LineIsBelow ( line_t * line , AActor * actor )
{
2016-04-19 09:35:28 +00:00
if ( line - > frontsector - > PortalBlocksMovement ( sector_t : : ceiling ) ) return - 1 ;
return line - > frontsector - > GetPortalPlaneZ ( sector_t : : ceiling ) < = actor - > Z ( ) ;
2016-03-01 15:47:10 +00:00
}
//
// MOVEMENT ITERATOR FUNCTIONS
//
//==========================================================================
//
//
// PIT_CheckLine
2016-03-27 20:49:59 +00:00
// Adjusts tmfloorz and tmceilingz as lines are contacted
2016-03-01 15:47:10 +00:00
//
//
//==========================================================================
static // killough 3/26/98: make static
bool PIT_CheckLine ( FMultiBlockLinesIterator & mit , FMultiBlockLinesIterator : : CheckResult & cres , const FBoundingBox & box , FCheckPosition & tm )
{
line_t * ld = cres . line ;
bool rail = false ;
2016-03-26 22:19:38 +00:00
if ( ! box . inRange ( ld ) | | box . BoxOnLineSide ( ld ) ! = - 1 )
2016-03-01 15:47:10 +00:00
return true ;
// A line has been hit
/*
=
= The moving thing ' s destination position will cross the given line .
= If this should not be allowed , return false .
= If the line is special , keep track of it to process later if the move
= is proven ok . NOTE : specials are NOT sorted by order , so two special lines
= that are only 8 pixels apart could be crossed in either order .
*/
if ( ! ld - > backsector )
{ // One sided line
// Needed for polyobject portals.
2016-04-07 15:13:56 +00:00
if ( cres . line - > isLinePortal ( ) & & ( cres . line - > sidedef [ 0 ] - > Flags & WALLF_POLYOBJ ) )
2016-03-01 15:47:10 +00:00
{
spechit_t spec ;
spec . line = ld ;
2016-03-26 22:19:38 +00:00
spec . Refpos = cres . Position ;
spec . Oldrefpos = tm . thing - > PosRelative ( ld ) ;
2016-03-01 15:47:10 +00:00
portalhit . Push ( spec ) ;
return true ;
}
if ( ( ( cres . portalflags & FFCF_NOFLOOR ) & & LineIsAbove ( cres . line , tm . thing ) ! = 0 ) | |
( ( cres . portalflags & FFCF_NOCEILING ) & & LineIsBelow ( cres . line , tm . thing ) ! = 0 ) )
{
// this blocking line is in a different vertical layer and does not intersect with the actor that is being checked.
// Since a one-sided line does not have an opening there's nothing left to do about it.
return true ;
}
2016-05-20 11:10:42 +00:00
// check if the actor can step through the ceiling portal. In this case one-sided lines in the current area should not block
if ( ! cres . line - > frontsector - > PortalBlocksMovement ( sector_t : : ceiling ) )
{
double portz = cres . line - > frontsector - > GetPortalPlaneZ ( sector_t : : ceiling ) ;
if ( tm . thing - > Z ( ) < portz & & tm . thing - > Z ( ) + tm . thing - > MaxStepHeight > = portz & & tm . floorz < portz )
{
tm . floorz = portz ;
tm . floorsector = cres . line - > frontsector ;
tm . floorpic = cres . line - > sidedef [ 0 ] - > GetTexture ( side_t : : mid ) ;
tm . floorterrain = 0 ;
tm . portalstep = true ;
return true ;
}
}
2016-03-01 15:47:10 +00:00
if ( tm . thing - > flags2 & MF2_BLASTED )
{
P_DamageMobj ( tm . thing , NULL , NULL , tm . thing - > Mass > > 5 , NAME_Melee ) ;
}
tm . thing - > BlockingLine = ld ;
CheckForPushSpecial ( ld , 0 , tm . thing ) ;
return false ;
}
// MBF bouncers are treated as missiles here.
bool Projectile = ( tm . thing - > flags & MF_MISSILE | | tm . thing - > BounceFlags & BOUNCE_MBF ) ;
// MBF considers that friendly monsters are not blocked by monster-blocking lines.
// This is added here as a compatibility option. Note that monsters that are dehacked
// into being friendly with the MBF flag automatically gain MF3_NOBLOCKMONST, so this
// just optionally generalizes the behavior to other friendly monsters.
bool NotBlocked = ( ( tm . thing - > flags3 & MF3_NOBLOCKMONST )
| | ( ( i_compatflags & COMPATF_NOBLOCKFRIENDS ) & & ( tm . thing - > flags & MF_FRIENDLY ) ) ) ;
if ( ! ( Projectile ) | | ( ld - > flags & ( ML_BLOCKEVERYTHING | ML_BLOCKPROJECTILE ) ) )
{
if ( ld - > flags & ML_RAILING )
{
rail = true ;
}
else if ( ( ld - > flags & ( ML_BLOCKING | ML_BLOCKEVERYTHING ) ) | | // explicitly blocking everything
( ! ( NotBlocked ) & & ( ld - > flags & ML_BLOCKMONSTERS ) ) | | // block monsters only
( tm . thing - > player ! = NULL & & ( ld - > flags & ML_BLOCK_PLAYERS ) ) | | // block players
( ( Projectile ) & & ( ld - > flags & ML_BLOCKPROJECTILE ) ) | | // block projectiles
( ( tm . thing - > flags & MF_FLOAT ) & & ( ld - > flags & ML_BLOCK_FLOATERS ) ) ) // block floaters
{
if ( cres . portalflags & FFCF_NOFLOOR )
{
int state = LineIsAbove ( cres . line , tm . thing ) ;
if ( state = = - 1 ) return true ;
if ( state = = 1 )
{
// the line should not block but we should set the ceilingz to the portal boundary so that we can't float up into that line.
2016-04-19 09:35:28 +00:00
double portalz = cres . line - > frontsector - > GetPortalPlaneZ ( sector_t : : floor ) ;
2016-03-01 15:47:10 +00:00
if ( portalz < tm . ceilingz )
{
tm . ceilingz = portalz ;
tm . ceilingsector = cres . line - > frontsector ;
}
return true ;
}
}
else if ( cres . portalflags & FFCF_NOCEILING )
{
// same, but for downward portals
int state = LineIsBelow ( cres . line , tm . thing ) ;
if ( state = = - 1 ) return true ;
if ( state = = 1 )
{
2016-04-19 09:35:28 +00:00
double portalz = cres . line - > frontsector - > GetPortalPlaneZ ( sector_t : : ceiling ) ;
2016-03-01 15:47:10 +00:00
if ( portalz > tm . floorz )
{
tm . floorz = portalz ;
tm . floorsector = cres . line - > frontsector ;
tm . floorterrain = 0 ;
}
return true ;
}
}
else
{
if ( tm . thing - > flags2 & MF2_BLASTED )
{
P_DamageMobj ( tm . thing , NULL , NULL , tm . thing - > Mass > > 5 , NAME_Melee ) ;
}
tm . thing - > BlockingLine = ld ;
// Calculate line side based on the actor's original position, not the new one.
2016-03-26 22:19:38 +00:00
CheckForPushSpecial ( ld , P_PointOnLineSide ( cres . Position , ld ) , tm . thing ) ;
2016-03-01 15:47:10 +00:00
return false ;
}
}
}
2016-03-26 22:19:38 +00:00
DVector2 ref = FindRefPoint ( ld , cres . Position ) ;
2016-03-01 15:47:10 +00:00
FLineOpening open ;
2016-03-26 22:19:38 +00:00
P_LineOpening ( open , tm . thing , ld , ref , & cres . Position , cres . portalflags ) ;
2016-03-01 15:47:10 +00:00
// [RH] Steep sectors count as dropoffs, if the actor touches the boundary between a steep slope and something else
if ( ! ( tm . thing - > flags & MF_DROPOFF ) & &
! ( tm . thing - > flags & ( MF_NOGRAVITY | MF_NOCLIP ) ) )
{
2016-03-30 07:41:46 +00:00
if ( ( open . frontfloorplane . fC ( ) < STEEPSLOPE ) ! = ( open . backfloorplane . fC ( ) < STEEPSLOPE ) )
2016-03-01 15:47:10 +00:00
{
// on the boundary of a steep slope
return false ;
}
}
// If the floor planes on both sides match we should recalculate open.bottom at the actual position we are checking
// This is to avoid bumpy movement when crossing a linedef with the same slope on both sides.
2016-11-23 13:27:08 +00:00
// This should never narrow down the opening, though, only widen it.
2016-03-25 17:19:54 +00:00
if ( open . frontfloorplane = = open . backfloorplane & & open . bottom > LINEOPEN_MIN )
2016-03-01 15:47:10 +00:00
{
2016-11-23 13:27:08 +00:00
auto newopen = open . frontfloorplane . ZatPoint ( cres . Position ) ;
if ( newopen < open . bottom ) open . bottom = newopen ;
2016-03-01 15:47:10 +00:00
}
if ( rail & &
// Eww! Gross! This check means the rail only exists if you stand on the
// high side of the rail. So if you're walking on the low side of the rail,
// it's possible to get stuck in the rail until you jump out. Unfortunately,
// there is an area on Strife MAP04 that requires this behavior. Still, it's
// better than Strife's handling of rails, which lets you jump into rails
// from either side. How long until somebody reports this as a bug and I'm
// forced to say, "It's not a bug. It's a feature?" Ugh.
( ! ( level . flags2 & LEVEL2_RAILINGHACK ) | |
2016-03-26 22:19:38 +00:00
open . bottom = = tm . thing - > Sector - > floorplane . ZatPoint ( ref ) ) )
2016-03-01 15:47:10 +00:00
{
2016-03-25 17:19:54 +00:00
open . bottom + = 32 ;
2016-03-01 15:47:10 +00:00
}
// adjust floor / ceiling heights
if ( ! ( cres . portalflags & FFCF_NOCEILING ) )
{
2016-03-25 17:19:54 +00:00
if ( open . top < tm . ceilingz )
2016-03-01 15:47:10 +00:00
{
2016-03-25 17:19:54 +00:00
tm . ceilingz = open . top ;
2016-03-01 15:47:10 +00:00
tm . ceilingsector = open . topsec ;
tm . ceilingpic = open . ceilingpic ;
tm . ceilingline = ld ;
tm . thing - > BlockingLine = ld ;
}
}
if ( ! ( cres . portalflags & FFCF_NOFLOOR ) )
{
2016-03-25 17:19:54 +00:00
if ( open . bottom > tm . floorz )
2016-03-01 15:47:10 +00:00
{
2016-03-25 17:19:54 +00:00
tm . floorz = open . bottom ;
2016-03-01 15:47:10 +00:00
tm . floorsector = open . bottomsec ;
tm . floorpic = open . floorpic ;
tm . floorterrain = open . floorterrain ;
tm . touchmidtex = open . touchmidtex ;
tm . abovemidtex = open . abovemidtex ;
tm . thing - > BlockingLine = ld ;
}
2016-03-25 17:19:54 +00:00
else if ( open . bottom = = tm . floorz )
2016-03-01 15:47:10 +00:00
{
tm . touchmidtex | = open . touchmidtex ;
tm . abovemidtex | = open . abovemidtex ;
}
2016-03-25 17:19:54 +00:00
if ( open . lowfloor < tm . dropoffz )
2016-03-01 15:47:10 +00:00
{
2016-03-25 17:19:54 +00:00
tm . dropoffz = open . lowfloor ;
2016-03-01 15:47:10 +00:00
}
}
// if contacted a special line, add it to the list
spechit_t spec ;
if ( ld - > special )
{
spec . line = ld ;
2016-03-26 22:19:38 +00:00
spec . Refpos = cres . Position ;
spec . Oldrefpos = tm . thing - > PosRelative ( ld ) ;
2016-03-01 15:47:10 +00:00
spechit . Push ( spec ) ;
}
2016-03-13 20:55:47 +00:00
if ( ld - > isLinePortal ( ) )
2016-03-01 15:47:10 +00:00
{
spec . line = ld ;
2016-03-26 22:19:38 +00:00
spec . Refpos = cres . Position ;
spec . Oldrefpos = tm . thing - > PosRelative ( ld ) ;
2016-03-01 15:47:10 +00:00
portalhit . Push ( spec ) ;
}
return true ;
}
//==========================================================================
//
//
// PIT_CheckPortal
// This checks the destination side of a non-static line portal
// We cannot run a full P_CheckPosition there because it'd set
// multiple fields to values that can cause problems in other
// parts of the code
//
// What this does is starting a separate BlockLinesIterator
// and only taking the absolutely necessary information
// (i.e. floor and ceiling height plus terrain)
//
//
//==========================================================================
static bool PIT_CheckPortal ( FMultiBlockLinesIterator & mit , FMultiBlockLinesIterator : : CheckResult cres , const FBoundingBox & box , FCheckPosition & tm )
{
// if in another vertical section let's just ignore it.
if ( cres . portalflags & ( FFCF_NOCEILING | FFCF_NOFLOOR ) ) return false ;
2016-03-26 22:19:38 +00:00
if ( ! box . inRange ( cres . line ) | | box . BoxOnLineSide ( cres . line ) ! = - 1 )
2016-03-01 15:47:10 +00:00
return false ;
line_t * lp = cres . line - > getPortalDestination ( ) ;
2016-03-27 00:06:54 +00:00
double zofs = 0 ;
2016-03-01 15:47:10 +00:00
2016-03-27 00:06:54 +00:00
P_TranslatePortalXY ( cres . line , cres . Position . X , cres . Position . Y ) ;
P_TranslatePortalZ ( cres . line , zofs ) ;
2016-03-01 15:47:10 +00:00
// fudge a bit with the portal line so that this gets included in the checks that normally only get run on two-sided lines
sector_t * sec = lp - > backsector ;
if ( lp - > backsector = = NULL ) lp - > backsector = lp - > frontsector ;
2016-03-25 17:19:54 +00:00
tm . thing - > AddZ ( zofs ) ;
2016-03-01 15:47:10 +00:00
2016-03-26 22:19:38 +00:00
FBoundingBox pbox ( cres . Position . X , cres . Position . Y , tm . thing - > radius ) ;
2016-03-01 15:47:10 +00:00
FBlockLinesIterator it ( pbox ) ;
bool ret = false ;
line_t * ld ;
// Check all lines at the destination
while ( ( ld = it . Next ( ) ) )
{
2016-03-26 22:19:38 +00:00
if ( ! pbox . inRange ( ld ) | | pbox . BoxOnLineSide ( ld ) ! = - 1 )
2016-03-01 15:47:10 +00:00
continue ;
if ( ld - > backsector = = NULL )
continue ;
2016-03-26 22:19:38 +00:00
DVector2 ref = FindRefPoint ( ld , cres . Position ) ;
2016-03-01 15:47:10 +00:00
FLineOpening open ;
2016-03-26 22:19:38 +00:00
P_LineOpening ( open , tm . thing , ld , ref , & cres . Position , 0 ) ;
2016-03-01 15:47:10 +00:00
// adjust floor / ceiling heights
2016-03-25 17:19:54 +00:00
if ( open . top - zofs < tm . ceilingz )
2016-03-01 15:47:10 +00:00
{
2016-03-25 17:19:54 +00:00
tm . ceilingz = open . top - zofs ;
2016-03-01 15:47:10 +00:00
tm . ceilingpic = open . ceilingpic ;
/*
tm . ceilingsector = open . topsec ;
tm . ceilingline = ld ;
tm . thing - > BlockingLine = ld ;
*/
ret = true ;
}
2016-03-25 17:19:54 +00:00
if ( open . bottom - zofs > tm . floorz )
2016-03-01 15:47:10 +00:00
{
2016-03-25 17:19:54 +00:00
tm . floorz = open . bottom - zofs ;
2016-03-01 15:47:10 +00:00
tm . floorpic = open . floorpic ;
tm . floorterrain = open . floorterrain ;
/*
tm . floorsector = open . bottomsec ;
tm . touchmidtex = open . touchmidtex ;
tm . abovemidtex = open . abovemidtex ;
tm . thing - > BlockingLine = ld ;
*/
ret = true ;
}
2016-03-25 17:19:54 +00:00
if ( open . lowfloor - zofs < tm . dropoffz )
tm . dropoffz = open . lowfloor - zofs ;
2016-03-01 15:47:10 +00:00
}
2016-03-25 17:19:54 +00:00
tm . thing - > AddZ ( - zofs ) ;
2016-03-01 15:47:10 +00:00
lp - > backsector = sec ;
return ret ;
}
//==========================================================================
//
// Isolated to keep the code readable and fix the logic
//
//==========================================================================
static bool CheckRipLevel ( AActor * victim , AActor * projectile )
{
if ( victim - > RipLevelMin > 0 & & projectile - > RipperLevel < victim - > RipLevelMin ) return false ;
if ( victim - > RipLevelMax > 0 & & projectile - > RipperLevel > victim - > RipLevelMax ) return false ;
return true ;
}
//==========================================================================
//
// Isolated to keep the code readable and allow reuse in other attacks
//
//==========================================================================
static bool CanAttackHurt ( AActor * victim , AActor * shooter )
{
// players are never subject to infighting settings and are always allowed
// to harm / be harmed by anything.
if ( ! victim - > player & & ! shooter - > player )
{
int infight = G_SkillProperty ( SKILLP_Infight ) ;
if ( infight < 0 )
{
// -1: Monsters cannot hurt each other, but make exceptions for
// friendliness and hate status.
if ( shooter - > flags & MF_SHOOTABLE )
{
// Question: Should monsters be allowed to shoot barrels in this mode?
// The old code does not.
if ( victim - > flags3 & MF3_ISMONSTER )
{
// Monsters that are clearly hostile can always hurt each other
if ( ! victim - > IsHostile ( shooter ) )
{
// The same if the shooter hates the target
if ( victim - > tid = = 0 | | shooter - > TIDtoHate ! = victim - > tid )
{
return false ;
}
}
}
}
}
else if ( infight = = 0 )
{
// 0: Monsters cannot hurt same species except
// cases where they are clearly supposed to do that
if ( victim - > IsFriend ( shooter ) )
{
// Friends never harm each other, unless the shooter has the HARMFRIENDS set.
if ( ! ( shooter - > flags7 & MF7_HARMFRIENDS ) ) return false ;
}
else
{
if ( victim - > TIDtoHate ! = 0 & & victim - > TIDtoHate = = shooter - > TIDtoHate )
{
// [RH] Don't hurt monsters that hate the same victim as you do
return false ;
}
if ( victim - > GetSpecies ( ) = = shooter - > GetSpecies ( ) & & ! ( victim - > flags6 & MF6_DOHARMSPECIES ) )
{
// Don't hurt same species or any relative -
// but only if the target isn't one's hostile.
if ( ! victim - > IsHostile ( shooter ) )
{
// Allow hurting monsters the shooter hates.
if ( victim - > tid = = 0 | | shooter - > TIDtoHate ! = victim - > tid )
{
return false ;
}
}
}
}
}
// else if (infight==1) every shot hurts anything - no further tests needed
}
return true ;
}
//==========================================================================
//
// PIT_CheckThing
//
//==========================================================================
bool PIT_CheckThing ( FMultiBlockThingsIterator & it , FMultiBlockThingsIterator : : CheckResult & cres , const FBoundingBox & box , FCheckPosition & tm )
{
AActor * thing = cres . thing ;
2016-03-27 00:06:54 +00:00
double topz ;
2016-03-01 15:47:10 +00:00
bool solid ;
int damage ;
// don't clip against self
if ( thing = = tm . thing )
return true ;
if ( ! ( ( thing - > flags & ( MF_SOLID | MF_SPECIAL | MF_SHOOTABLE ) ) | | thing - > flags6 & MF6_TOUCHY ) )
return true ; // can't hit thing
2016-03-27 00:06:54 +00:00
double blockdist = thing - > radius + tm . thing - > radius ;
if ( fabs ( thing - > X ( ) - cres . Position . X ) > = blockdist | | fabs ( thing - > Y ( ) - cres . Position . Y ) > = blockdist )
2016-03-01 15:47:10 +00:00
return true ;
if ( ( thing - > flags2 | tm . thing - > flags2 ) & MF2_THRUACTORS )
return true ;
if ( ( tm . thing - > flags6 & MF6_THRUSPECIES ) & & ( tm . thing - > GetSpecies ( ) = = thing - > GetSpecies ( ) ) )
return true ;
tm . thing - > BlockingMobj = thing ;
2016-03-27 00:06:54 +00:00
topz = thing - > Top ( ) ;
2016-03-01 15:47:10 +00:00
// Both things overlap in x or y direction
bool unblocking = false ;
// walking on other actors and unblocking is too messy through restricted portal types so disable it.
if ( ! ( cres . portalflags & FFCF_RESTRICTEDPORTAL ) )
{
if ( ! ( i_compatflags & COMPATF_NO_PASSMOBJ ) & & ! ( tm . thing - > flags & ( MF_FLOAT | MF_MISSILE | MF_SKULLFLY | MF_NOGRAVITY ) ) & &
( thing - > flags & MF_SOLID ) & & ( thing - > flags4 & MF4_ACTLIKEBRIDGE ) )
{
// [RH] Let monsters walk on actors as well as floors
if ( ( tm . thing - > flags3 & MF3_ISMONSTER ) & &
2016-03-23 11:21:52 +00:00
topz > = tm . floorz & & topz < = tm . thing - > Z ( ) + tm . thing - > MaxStepHeight )
2016-03-01 15:47:10 +00:00
{
// The commented-out if is an attempt to prevent monsters from walking off a
// thing further than they would walk off a ledge. I can't think of an easy
// way to do this, so I restrict them to only walking on bridges instead.
// Uncommenting the if here makes it almost impossible for them to walk on
// anything, bridge or otherwise.
2016-03-20 18:52:35 +00:00
// if (abs(thing->x - tmx) <= thing->radius &&
// abs(thing->y - tmy) <= thing->radius)
2016-03-01 15:47:10 +00:00
{
tm . stepthing = thing ;
2016-03-27 00:06:54 +00:00
tm . floorz = topz ;
2016-03-01 15:47:10 +00:00
}
}
}
if ( ( ( tm . FromPMove | | tm . thing - > player ! = NULL ) & & thing - > flags & MF_SOLID ) )
{
2016-03-27 00:06:54 +00:00
DVector3 oldpos = tm . thing - > PosRelative ( thing ) ;
2016-03-01 15:47:10 +00:00
// Both actors already overlap. To prevent them from remaining stuck allow the move if it
// takes them further apart or the move does not change the position (when called from P_ChangeSector.)
2016-05-14 13:05:06 +00:00
if ( tm . pos . X = = tm . thing - > X ( ) & & tm . pos . Y = = tm . thing - > Y ( ) )
2016-03-01 15:47:10 +00:00
{
unblocking = true ;
}
2016-03-27 00:06:54 +00:00
else if ( fabs ( thing - > X ( ) - oldpos . X ) < ( thing - > radius + tm . thing - > radius ) & &
fabs ( thing - > Y ( ) - oldpos . Y ) < ( thing - > radius + tm . thing - > radius ) )
2016-03-01 15:47:10 +00:00
{
2016-03-27 00:06:54 +00:00
double newdist = thing - > Distance2D ( cres . Position . X , cres . Position . Y ) ;
double olddist = thing - > Distance2D ( oldpos . X , oldpos . Y ) ;
2016-03-01 15:47:10 +00:00
if ( newdist > olddist )
{
// unblock only if there's already a vertical overlap (or both actors are flagged not to overlap)
2016-03-27 00:06:54 +00:00
unblocking = ( tm . thing - > Top ( ) > thing - > Z ( ) & & tm . thing - > Z ( ) < topz ) | | ( tm . thing - > flags3 & thing - > flags3 & MF3_DONTOVERLAP ) ;
2016-03-01 15:47:10 +00:00
}
}
}
}
// [RH] If the other thing is a bridge, then treat the moving thing as if it had MF2_PASSMOBJ, so
// you can use a scrolling floor to move scenery items underneath a bridge.
if ( ( tm . thing - > flags2 & MF2_PASSMOBJ | | thing - > flags4 & MF4_ACTLIKEBRIDGE ) & & ! ( i_compatflags & COMPATF_NO_PASSMOBJ ) )
{ // check if a mobj passed over/under another object
if ( ! ( tm . thing - > flags & MF_MISSILE ) | |
! ( tm . thing - > flags2 & MF2_RIP ) | |
( thing - > flags5 & MF5_DONTRIP ) | |
( ( tm . thing - > flags6 & MF6_NOBOSSRIP ) & & ( thing - > flags2 & MF2_BOSS ) ) )
{
if ( tm . thing - > flags3 & thing - > flags3 & MF3_DONTOVERLAP )
{ // Some things prefer not to overlap each other, if possible
return unblocking ;
}
2016-03-27 00:06:54 +00:00
if ( ( tm . thing - > Z ( ) > = topz ) | | ( tm . thing - > Top ( ) < = thing - > Z ( ) ) )
2016-03-01 15:47:10 +00:00
return true ;
}
}
if ( tm . thing - > player = = NULL | | ! ( tm . thing - > player - > cheats & CF_PREDICTING ) )
{
// touchy object is alive, toucher is solid
if ( thing - > flags6 & MF6_TOUCHY & & tm . thing - > flags & MF_SOLID & & thing - > health > 0 & &
// Thing is an armed mine or a sentient thing
( thing - > flags6 & MF6_ARMED | | thing - > IsSentient ( ) ) & &
// either different classes or players
( thing - > player | | thing - > GetClass ( ) ! = tm . thing - > GetClass ( ) ) & &
// or different species if DONTHARMSPECIES
( ! ( thing - > flags6 & MF6_DONTHARMSPECIES ) | | thing - > GetSpecies ( ) ! = tm . thing - > GetSpecies ( ) ) & &
// touches vertically
2016-03-27 00:06:54 +00:00
topz > = tm . thing - > Z ( ) & & tm . thing - > Top ( ) > = thing - > Z ( ) & &
2016-03-01 15:47:10 +00:00
// prevents lost souls from exploding when fired by pain elementals
( thing - > master ! = tm . thing & & tm . thing - > master ! = thing ) )
// Difference with MBF: MBF hardcodes the LS/PE check and lets actors of the same species
// but different classes trigger the touchiness, but that seems less straightforwards.
{
thing - > flags6 & = ~ MF6_ARMED ; // Disarm
P_DamageMobj ( thing , NULL , NULL , thing - > health , NAME_None , DMG_FORCED ) ; // kill object
return true ;
}
// Check for MF6_BUMPSPECIAL
// By default, only players can activate things by bumping into them
if ( ( thing - > flags6 & MF6_BUMPSPECIAL ) & & ( ( tm . thing - > player ! = NULL )
| | ( ( thing - > activationtype & THINGSPEC_MonsterTrigger ) & & ( tm . thing - > flags3 & MF3_ISMONSTER ) )
| | ( ( thing - > activationtype & THINGSPEC_MissileTrigger ) & & ( tm . thing - > flags & MF_MISSILE ) )
) & & ( level . maptime > thing - > lastbump ) ) // Leave the bumper enough time to go away
{
if ( P_ActivateThingSpecial ( thing , tm . thing ) )
thing - > lastbump = level . maptime + TICRATE ;
}
}
// Check for skulls slamming into things
if ( tm . thing - > flags & MF_SKULLFLY )
{
2016-11-26 00:14:47 +00:00
bool res = tm . thing - > CallSlam ( tm . thing - > BlockingMobj ) ;
2016-03-01 15:47:10 +00:00
tm . thing - > BlockingMobj = NULL ;
return res ;
}
// [ED850] Player Prediction ends here. There is nothing else they could/should do.
if ( tm . thing - > player ! = NULL & & ( tm . thing - > player - > cheats & CF_PREDICTING ) )
{
solid = ( thing - > flags & MF_SOLID ) & &
! ( thing - > flags & MF_NOCLIP ) & &
( ( tm . thing - > flags & MF_SOLID ) | | ( tm . thing - > flags6 & MF6_BLOCKEDBYSOLIDACTORS ) ) ;
return ! solid | | unblocking ;
}
// Check for blasted thing running into another
if ( ( tm . thing - > flags2 & MF2_BLASTED ) & & ( thing - > flags & MF_SHOOTABLE ) )
{
if ( ! ( thing - > flags2 & MF2_BOSS ) & & ( thing - > flags3 & MF3_ISMONSTER ) & & ! ( thing - > flags3 & MF3_DONTBLAST ) )
{
// ideally this should take the mass factor into account
2016-03-19 23:54:18 +00:00
thing - > Vel + = tm . thing - > Vel . XY ( ) ;
2016-11-27 15:22:23 +00:00
if ( fabs ( thing - > Vel . X ) + fabs ( thing - > Vel . Y ) > 3. )
2016-03-01 15:47:10 +00:00
{
int newdam ;
damage = ( tm . thing - > Mass / 100 ) + 1 ;
newdam = P_DamageMobj ( thing , tm . thing , tm . thing , damage , tm . thing - > DamageType ) ;
P_TraceBleed ( newdam > 0 ? newdam : damage , thing , tm . thing ) ;
damage = ( thing - > Mass / 100 ) + 1 ;
newdam = P_DamageMobj ( tm . thing , thing , thing , damage > > 2 , tm . thing - > DamageType ) ;
P_TraceBleed ( newdam > 0 ? newdam : damage , tm . thing , thing ) ;
}
return false ;
}
}
// Check for missile or non-solid MBF bouncer
if ( tm . thing - > flags & MF_MISSILE | | ( ( tm . thing - > BounceFlags & BOUNCE_MBF ) & & ! ( tm . thing - > flags & MF_SOLID ) ) )
{
// Check for a non-shootable mobj
if ( thing - > flags2 & MF2_NONSHOOTABLE )
{
return true ;
}
// Check for passing through a ghost
if ( ( thing - > flags3 & MF3_GHOST ) & & ( tm . thing - > flags2 & MF2_THRUGHOST ) )
{
return true ;
}
if ( ( tm . thing - > flags6 & MF6_MTHRUSPECIES )
& & tm . thing - > target // NULL pointer check
& & ( tm . thing - > target - > GetSpecies ( ) = = thing - > GetSpecies ( ) ) )
return true ;
// Check for rippers passing through corpses
if ( ( thing - > flags & MF_CORPSE ) & & ( tm . thing - > flags2 & MF2_RIP ) & & ! ( thing - > flags & MF_SHOOTABLE ) )
{
return true ;
}
2016-03-20 19:55:06 +00:00
double clipheight ;
2016-03-01 15:47:10 +00:00
if ( thing - > projectilepassheight > 0 )
{
clipheight = thing - > projectilepassheight ;
}
else if ( thing - > projectilepassheight < 0 & & ( i_compatflags & COMPATF_MISSILECLIP ) )
{
clipheight = - thing - > projectilepassheight ;
}
else
{
2016-03-20 19:55:06 +00:00
clipheight = thing - > Height ;
2016-03-01 15:47:10 +00:00
}
// Check if it went over / under
2016-03-20 19:55:06 +00:00
if ( tm . thing - > Z ( ) > thing - > Z ( ) + clipheight )
2016-03-01 15:47:10 +00:00
{ // Over thing
return true ;
}
if ( tm . thing - > Top ( ) < thing - > Z ( ) )
{ // Under thing
return true ;
}
// [RH] What is the point of this check, again? In Hexen, it is unconditional,
// but here we only do it if the missile's damage is 0.
// MBF bouncer might have a non-0 damage value, but they must not deal damage on impact either.
2016-09-19 01:36:51 +00:00
if ( ( tm . thing - > BounceFlags & BOUNCE_Actors ) & & ( tm . thing - > IsZeroDamage ( ) | | ! ( tm . thing - > flags & MF_MISSILE ) ) )
2016-03-01 15:47:10 +00:00
{
return ( tm . thing - > target = = thing | | ! ( thing - > flags & MF_SOLID ) ) ;
}
switch ( tm . thing - > SpecialMissileHit ( thing ) )
{
case 0 : return false ;
case 1 : return true ;
default : break ;
}
// [RH] Extend DeHacked infighting to allow for monsters
// to never fight each other
if ( tm . thing - > target ! = NULL )
{
if ( thing = = tm . thing - > target )
{ // Don't missile self
return true ;
}
if ( ! CanAttackHurt ( thing , tm . thing - > target ) )
{
return false ;
}
}
if ( ! ( thing - > flags & MF_SHOOTABLE ) )
{ // Didn't do any damage
return ! ( thing - > flags & MF_SOLID ) ;
}
if ( ( thing - > flags4 & MF4_SPECTRAL ) & & ! ( tm . thing - > flags4 & MF4_SPECTRAL ) )
{
return true ;
}
if ( ( tm . DoRipping & & ! ( thing - > flags5 & MF5_DONTRIP ) ) & & CheckRipLevel ( thing , tm . thing ) )
{
if ( ! ( tm . thing - > flags6 & MF6_NOBOSSRIP ) | | ! ( thing - > flags2 & MF2_BOSS ) )
{
bool * check = tm . LastRipped . CheckKey ( thing ) ;
if ( check = = NULL | | ! * check )
{
tm . LastRipped [ thing ] = true ;
if ( ! ( thing - > flags & MF_NOBLOOD ) & &
! ( thing - > flags2 & MF2_REFLECTIVE ) & &
! ( tm . thing - > flags3 & MF3_BLOODLESSIMPACT ) & &
! ( thing - > flags2 & ( MF2_INVULNERABLE | MF2_DORMANT ) ) )
{ // Ok to spawn blood
P_RipperBlood ( tm . thing , thing ) ;
}
S_Sound ( tm . thing , CHAN_BODY , " misc/ripslop " , 1 , ATTN_IDLE ) ;
// Do poisoning (if using new style poison)
if ( tm . thing - > PoisonDamage > 0 & & tm . thing - > PoisonDuration ! = INT_MIN )
{
P_PoisonMobj ( thing , tm . thing , tm . thing - > target , tm . thing - > PoisonDamage , tm . thing - > PoisonDuration , tm . thing - > PoisonPeriod , tm . thing - > PoisonDamageType ) ;
}
damage = tm . thing - > GetMissileDamage ( 3 , 2 ) ;
int newdam = P_DamageMobj ( thing , tm . thing , tm . thing - > target , damage , tm . thing - > DamageType ) ;
if ( ! ( tm . thing - > flags3 & MF3_BLOODLESSIMPACT ) )
{
P_TraceBleed ( newdam > 0 ? newdam : damage , thing , tm . thing ) ;
}
if ( thing - > flags2 & MF2_PUSHABLE
& & ! ( tm . thing - > flags2 & MF2_CANNOTPUSH ) )
{ // Push thing
if ( thing - > lastpush ! = tm . PushTime )
{
2016-03-24 20:31:04 +00:00
thing - > Vel + = tm . thing - > Vel . XY ( ) * thing - > pushfactor ;
2016-03-01 15:47:10 +00:00
thing - > lastpush = tm . PushTime ;
}
}
}
spechit . Clear ( ) ;
return true ;
}
}
// Do poisoning (if using new style poison)
if ( tm . thing - > PoisonDamage > 0 & & tm . thing - > PoisonDuration ! = INT_MIN )
{
P_PoisonMobj ( thing , tm . thing , tm . thing - > target , tm . thing - > PoisonDamage , tm . thing - > PoisonDuration , tm . thing - > PoisonPeriod , tm . thing - > PoisonDamageType ) ;
}
// Do damage
damage = tm . thing - > GetMissileDamage ( ( tm . thing - > flags4 & MF4_STRIFEDAMAGE ) ? 3 : 7 , 1 ) ;
if ( ( damage > 0 ) | | ( tm . thing - > flags6 & MF6_FORCEPAIN ) | | ( tm . thing - > flags7 & MF7_CAUSEPAIN ) )
{
int newdam = P_DamageMobj ( thing , tm . thing , tm . thing - > target , damage , tm . thing - > DamageType ) ;
if ( damage > 0 )
{
if ( ( tm . thing - > flags5 & MF5_BLOODSPLATTER ) & &
! ( thing - > flags & MF_NOBLOOD ) & &
! ( thing - > flags2 & MF2_REFLECTIVE ) & &
! ( thing - > flags2 & ( MF2_INVULNERABLE | MF2_DORMANT ) ) & &
! ( tm . thing - > flags3 & MF3_BLOODLESSIMPACT ) & &
( pr_checkthing ( ) < 192 ) )
{
2016-03-23 12:31:12 +00:00
P_BloodSplatter ( tm . thing - > Pos ( ) , thing , tm . thing - > AngleTo ( thing ) ) ;
2016-03-01 15:47:10 +00:00
}
if ( ! ( tm . thing - > flags3 & MF3_BLOODLESSIMPACT ) )
{
P_TraceBleed ( newdam > 0 ? newdam : damage , thing , tm . thing ) ;
}
}
}
else
{
P_GiveBody ( thing , - damage ) ;
}
if ( ( thing - > flags7 & MF7_THRUREFLECT ) & & ( thing - > flags2 & MF2_REFLECTIVE ) & & ( tm . thing - > flags & MF_MISSILE ) )
{
if ( tm . thing - > flags2 & MF2_SEEKERMISSILE )
{
tm . thing - > tracer = tm . thing - > target ;
}
tm . thing - > target = thing ;
return true ;
}
return false ; // don't traverse any more
}
if ( thing - > flags2 & MF2_PUSHABLE & & ! ( tm . thing - > flags2 & MF2_CANNOTPUSH ) )
{ // Push thing
if ( thing - > lastpush ! = tm . PushTime )
{
2016-03-24 20:31:04 +00:00
thing - > Vel + = tm . thing - > Vel . XY ( ) * thing - > pushfactor ;
2016-03-01 15:47:10 +00:00
thing - > lastpush = tm . PushTime ;
}
}
solid = ( thing - > flags & MF_SOLID ) & &
! ( thing - > flags & MF_NOCLIP ) & &
( ( tm . thing - > flags & MF_SOLID ) | | ( tm . thing - > flags6 & MF6_BLOCKEDBYSOLIDACTORS ) ) ;
// Check for special pickup
if ( ( thing - > flags & MF_SPECIAL ) & & ( tm . thing - > flags & MF_PICKUP )
// [RH] The next condition is to compensate for the extra height
// that gets added by P_CheckPosition() so that you cannot pick
// up things that are above your true height.
2016-03-22 11:42:27 +00:00
& & thing - > Z ( ) < tm . thing - > Top ( ) - tm . thing - > MaxStepHeight )
2016-03-01 15:47:10 +00:00
{ // Can be picked up by tmthing
P_TouchSpecialThing ( thing , tm . thing ) ; // can remove thing
}
// killough 3/16/98: Allow non-solid moving objects to move through solid
// ones, by allowing the moving thing (tmthing) to move if it's non-solid,
// despite another solid thing being in the way.
// killough 4/11/98: Treat no-clipping things as not blocking
return ! solid | | unblocking ;
// return !(thing->flags & MF_SOLID); // old code -- killough
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
MOVEMENT CLIPPING
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
//==========================================================================
//
// P_CheckPosition
// This is purely informative, nothing is modified
// (except things picked up and missile damage applied).
//
// in:
// a AActor (can be valid or invalid)
// a position to be checked
// (doesn't need to be related to the AActor->x,y)
//
// during:
// special things are touched if MF_PICKUP
// early out on solid lines?
//
// out:
// newsubsec
2016-03-27 11:29:58 +00:00
// floorz
2016-03-01 15:47:10 +00:00
// ceilingz
// tmdropoffz = the lowest point contacted (monsters won't move to a dropoff)
// speciallines[]
// numspeciallines
// AActor *BlockingMobj = pointer to thing that blocked position (NULL if not
// blocked, or blocked by a line).
//
//==========================================================================
2016-03-27 00:06:54 +00:00
bool P_CheckPosition ( AActor * thing , const DVector2 & pos , FCheckPosition & tm , bool actorsonly )
2016-03-01 15:47:10 +00:00
{
sector_t * newsec ;
AActor * thingblocker ;
2016-03-20 19:55:06 +00:00
double realHeight = thing - > Height ;
2016-03-01 15:47:10 +00:00
tm . thing = thing ;
2016-03-27 00:06:54 +00:00
tm . pos . X = pos . X ;
tm . pos . Y = pos . Y ;
2016-03-26 22:19:38 +00:00
tm . pos . Z = thing - > Z ( ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 00:06:54 +00:00
newsec = tm . sector = P_PointInSector ( pos ) ;
2016-03-01 15:47:10 +00:00
tm . ceilingline = thing - > BlockingLine = NULL ;
// Retrieve the base floor / ceiling from the target location.
// Any contacted lines the step closer together will adjust them.
if ( ! thing - > IsNoClip2 ( ) )
{
2016-04-27 00:13:35 +00:00
if ( ! newsec - > PortalBlocksMovement ( sector_t : : ceiling ) | | ! newsec - > PortalBlocksMovement ( sector_t : : floor ) )
{
// Use P_GetFloorCeilingZ only if there's portals to consider. Its logic is subtly different than what is needed here for 3D floors.
P_GetFloorCeilingZ ( tm , FFCF_SAMESECTOR ) ;
}
else
{
tm . floorz = tm . dropoffz = newsec - > floorplane . ZatPoint ( pos ) ;
tm . floorpic = newsec - > GetTexture ( sector_t : : floor ) ;
tm . ceilingz = newsec - > ceilingplane . ZatPoint ( pos ) ;
tm . ceilingpic = newsec - > GetTexture ( sector_t : : ceiling ) ;
tm . floorsector = tm . ceilingsector = newsec ;
2016-04-27 10:08:22 +00:00
tm . floorterrain = newsec - > GetTerrain ( sector_t : : floor ) ;
2016-04-27 00:13:35 +00:00
}
F3DFloor * rover ;
double thingtop = thing - > Height > 0 ? thing - > Top ( ) : thing - > Z ( ) + 1 ;
for ( unsigned i = 0 ; i < newsec - > e - > XFloor . ffloors . Size ( ) ; i + + )
{
rover = newsec - > e - > XFloor . ffloors [ i ] ;
if ( ! ( rover - > flags & FF_SOLID ) | | ! ( rover - > flags & FF_EXISTS ) ) continue ;
double ff_bottom = rover - > bottom . plane - > ZatPoint ( pos ) ;
double ff_top = rover - > top . plane - > ZatPoint ( pos ) ;
double delta1 = thing - > Z ( ) - ( ff_bottom + ( ( ff_top - ff_bottom ) / 2 ) ) ;
double delta2 = thingtop - ( ff_bottom + ( ( ff_top - ff_bottom ) / 2 ) ) ;
if ( ff_top > tm . floorz & & fabs ( delta1 ) < fabs ( delta2 ) )
{
tm . floorz = tm . dropoffz = ff_top ;
tm . floorpic = * rover - > top . texture ;
2016-04-27 10:08:22 +00:00
tm . floorterrain = rover - > model - > GetTerrain ( rover - > top . isceiling ) ;
2016-04-27 00:13:35 +00:00
}
2016-07-30 07:15:13 +00:00
if ( ff_bottom < tm . ceilingz & & fabs ( delta1 ) > = fabs ( delta2 ) )
2016-04-27 00:13:35 +00:00
{
tm . ceilingz = ff_bottom ;
tm . ceilingpic = * rover - > bottom . texture ;
}
}
2016-03-01 15:47:10 +00:00
}
else
{
// With noclip2, we must ignore 3D floors and go right to the uppermost ceiling and lowermost floor.
2016-03-27 00:06:54 +00:00
tm . floorz = tm . dropoffz = newsec - > LowestFloorAt ( pos , & tm . floorsector ) ;
tm . ceilingz = newsec - > HighestCeilingAt ( pos , & tm . ceilingsector ) ;
2016-03-01 15:47:10 +00:00
tm . floorpic = tm . floorsector - > GetTexture ( sector_t : : floor ) ;
tm . floorterrain = tm . floorsector - > GetTerrain ( sector_t : : floor ) ;
tm . ceilingpic = tm . ceilingsector - > GetTexture ( sector_t : : ceiling ) ;
}
tm . touchmidtex = false ;
tm . abovemidtex = false ;
validcount + + ;
if ( ( thing - > flags & MF_NOCLIP ) & & ! ( thing - > flags & MF_SKULLFLY ) )
return true ;
// Check things first, possibly picking things up.
thing - > BlockingMobj = NULL ;
thingblocker = NULL ;
if ( thing - > player )
{ // [RH] Fake taller height to catch stepping up into things.
2016-03-22 11:42:27 +00:00
thing - > Height = realHeight + thing - > MaxStepHeight ;
2016-03-01 15:47:10 +00:00
}
tm . stepthing = NULL ;
2016-03-27 00:06:54 +00:00
FBoundingBox box ( pos . X , pos . Y , thing - > radius ) ;
2016-03-01 15:47:10 +00:00
FPortalGroupArray pcheck ;
2016-03-27 00:06:54 +00:00
FMultiBlockThingsIterator it2 ( pcheck , pos . X , pos . Y , thing - > Z ( ) , thing - > Height , thing - > radius , false , newsec ) ;
2016-03-01 15:47:10 +00:00
FMultiBlockThingsIterator : : CheckResult tcres ;
while ( ( it2 . Next ( & tcres ) ) )
{
if ( ! PIT_CheckThing ( it2 , tcres , it2 . Box ( ) , tm ) )
{ // [RH] If a thing can be stepped up on, we need to continue checking
// other things in the blocks and see if we hit something that is
// definitely blocking. Otherwise, we need to check the lines, or we
// could end up stuck inside a wall.
AActor * BlockingMobj = thing - > BlockingMobj ;
// If this blocks through a restricted line portal, it will always completely block.
if ( BlockingMobj = = NULL | | ( i_compatflags & COMPATF_NO_PASSMOBJ ) | | ( tcres . portalflags & FFCF_RESTRICTEDPORTAL ) )
{ // Thing slammed into something; don't let it move now.
2016-03-20 19:55:06 +00:00
thing - > Height = realHeight ;
2016-03-01 15:47:10 +00:00
return false ;
}
else if ( ! BlockingMobj - > player & & ! ( thing - > flags & ( MF_FLOAT | MF_MISSILE | MF_SKULLFLY ) ) & &
2016-03-22 11:42:27 +00:00
BlockingMobj - > Top ( ) - thing - > Z ( ) < = thing - > MaxStepHeight )
2016-03-01 15:47:10 +00:00
{
if ( thingblocker = = NULL | |
2016-03-22 11:42:27 +00:00
BlockingMobj - > Z ( ) > thingblocker - > Z ( ) )
2016-03-01 15:47:10 +00:00
{
thingblocker = BlockingMobj ;
}
thing - > BlockingMobj = NULL ;
}
else if ( thing - > player & &
2016-03-22 11:42:27 +00:00
thing - > Top ( ) - BlockingMobj - > Z ( ) < = thing - > MaxStepHeight )
2016-03-01 15:47:10 +00:00
{
if ( thingblocker )
{ // There is something to step up on. Return this thing as
// the blocker so that we don't step up.
2016-03-20 19:55:06 +00:00
thing - > Height = realHeight ;
2016-03-01 15:47:10 +00:00
return false ;
}
// Nothing is blocking us, but this actor potentially could
// if there is something else to step on.
thing - > BlockingMobj = NULL ;
}
else
{ // Definitely blocking
2016-03-20 19:55:06 +00:00
thing - > Height = realHeight ;
2016-03-01 15:47:10 +00:00
return false ;
}
}
}
// check lines
// [RH] We need to increment validcount again, because a function above may
// have already set some lines to equal the current validcount.
//
// Specifically, when DehackedPickup spawns a new item in its TryPickup()
// function, that new actor will set the lines around it to match validcount
// when it links itself into the world. If we just leave validcount alone,
// that will give the player the freedom to walk through walls at will near
// a pickup they cannot get, because their validcount will prevent them from
// being considered for collision with the player.
validcount + + ;
thing - > BlockingMobj = NULL ;
2016-03-20 19:55:06 +00:00
thing - > Height = realHeight ;
2016-03-01 15:47:10 +00:00
if ( actorsonly | | ( thing - > flags & MF_NOCLIP ) )
return ( thing - > BlockingMobj = thingblocker ) = = NULL ;
spechit . Clear ( ) ;
portalhit . Clear ( ) ;
2016-03-27 00:06:54 +00:00
FMultiBlockLinesIterator it ( pcheck , pos . X , pos . Y , thing - > Z ( ) , thing - > Height , thing - > radius , newsec ) ;
2016-03-01 15:47:10 +00:00
FMultiBlockLinesIterator : : CheckResult lcres ;
2016-03-27 00:06:54 +00:00
double thingdropoffz = tm . floorz ;
2016-03-01 15:47:10 +00:00
//bool onthing = (thingdropoffz != tmdropoffz);
2016-03-23 11:21:52 +00:00
tm . floorz = tm . dropoffz ;
2016-03-01 15:47:10 +00:00
bool good = true ;
while ( it . Next ( & lcres ) )
{
bool thisresult = PIT_CheckLine ( it , lcres , it . Box ( ) , tm ) ;
good & = thisresult ;
if ( thisresult )
{
FLinePortal * port = lcres . line - > getPortal ( ) ;
if ( port ! = NULL & & port - > mFlags & PORTF_PASSABLE & & port - > mType ! = PORTT_LINKED )
{
// Checking the other side of the portal completely is too costly,
// but checking the portal's destination line is necessary to
// retrieve the proper sector heights on the other side.
if ( PIT_CheckPortal ( it , lcres , it . Box ( ) , tm ) )
{
tm . thing - > BlockingLine = lcres . line ;
}
}
}
}
if ( ! good )
{
return false ;
}
2016-03-20 19:55:06 +00:00
if ( tm . ceilingz - tm . floorz < thing - > Height )
2016-03-01 15:47:10 +00:00
{
return false ;
}
if ( tm . touchmidtex )
{
2016-03-23 11:21:52 +00:00
tm . dropoffz = tm . floorz ;
2016-03-01 15:47:10 +00:00
}
else if ( tm . stepthing ! = NULL )
{
2016-03-27 00:06:54 +00:00
tm . dropoffz = thingdropoffz ;
2016-03-01 15:47:10 +00:00
}
return ( thing - > BlockingMobj = thingblocker ) = = NULL ;
}
2016-03-27 00:06:54 +00:00
bool P_CheckPosition ( AActor * thing , const DVector2 & pos , bool actorsonly )
2016-03-01 15:47:10 +00:00
{
FCheckPosition tm ;
2016-03-27 00:06:54 +00:00
return P_CheckPosition ( thing , pos , tm , actorsonly ) ;
2016-03-01 15:47:10 +00:00
}
2016-11-18 20:34:06 +00:00
DEFINE_ACTION_FUNCTION ( AActor , CheckPosition )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_FLOAT ( x ) ;
PARAM_FLOAT ( y ) ;
PARAM_BOOL_DEF ( actorsonly ) ;
ACTION_RETURN_BOOL ( P_CheckPosition ( self , DVector2 ( x , y ) , actorsonly ) ) ;
}
2016-03-01 15:47:10 +00:00
//----------------------------------------------------------------------------
//
// FUNC P_TestMobjLocation
//
// Returns true if the mobj is not blocked by anything at its current
// location, otherwise returns false.
//
//----------------------------------------------------------------------------
bool P_TestMobjLocation ( AActor * mobj )
{
ActorFlags flags ;
flags = mobj - > flags ;
mobj - > flags & = ~ MF_PICKUP ;
2016-03-20 18:52:35 +00:00
if ( P_CheckPosition ( mobj , mobj - > Pos ( ) ) )
2016-03-01 15:47:10 +00:00
{ // XY is ok, now check Z
mobj - > flags = flags ;
2016-03-20 18:52:35 +00:00
if ( ( mobj - > Z ( ) < mobj - > floorz ) | | ( mobj - > Top ( ) > mobj - > ceilingz ) )
2016-03-01 15:47:10 +00:00
{ // Bad Z
return false ;
}
return true ;
}
mobj - > flags = flags ;
return false ;
}
2016-11-24 00:23:35 +00:00
DEFINE_ACTION_FUNCTION ( AActor , TestMobjLocation )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
ACTION_RETURN_BOOL ( P_TestMobjLocation ( self ) ) ;
}
2016-03-01 15:47:10 +00:00
//=============================================================================
//
// P_CheckOnmobj(AActor *thing)
//
// Checks if the new Z position is legal
//=============================================================================
AActor * P_CheckOnmobj ( AActor * thing )
{
2016-03-27 11:29:58 +00:00
double oldz ;
2016-03-01 15:47:10 +00:00
bool good ;
AActor * onmobj ;
2016-03-27 11:29:58 +00:00
oldz = thing - > Z ( ) ;
2016-03-01 15:47:10 +00:00
P_FakeZMovement ( thing ) ;
good = P_TestMobjZ ( thing , false , & onmobj ) ;
2016-03-27 11:29:58 +00:00
thing - > SetZ ( oldz ) ;
2016-03-01 15:47:10 +00:00
return good ? NULL : onmobj ;
}
//=============================================================================
//
// P_TestMobjZ
//
//=============================================================================
bool P_TestMobjZ ( AActor * actor , bool quick , AActor * * pOnmobj )
{
AActor * onmobj = NULL ;
if ( actor - > flags & MF_NOCLIP )
{
if ( pOnmobj ) * pOnmobj = NULL ;
return true ;
}
FPortalGroupArray check ;
FMultiBlockThingsIterator it ( check , actor , - 1 , true ) ;
FMultiBlockThingsIterator : : CheckResult cres ;
while ( it . Next ( & cres ) )
{
AActor * thing = cres . thing ;
2016-03-27 11:29:58 +00:00
double blockdist = thing - > radius + actor - > radius ;
if ( fabs ( thing - > X ( ) - cres . Position . X ) > = blockdist | | fabs ( thing - > Y ( ) - cres . Position . Y ) > = blockdist )
2016-03-01 15:47:10 +00:00
{
continue ;
}
if ( ( actor - > flags2 | thing - > flags2 ) & MF2_THRUACTORS )
{
continue ;
}
if ( ( actor - > flags6 & MF6_THRUSPECIES ) & & ( thing - > GetSpecies ( ) = = actor - > GetSpecies ( ) ) )
{
continue ;
}
if ( ! ( thing - > flags & MF_SOLID ) )
{ // Can't hit thing
continue ;
}
if ( thing - > flags & ( MF_SPECIAL | MF_NOCLIP ) )
{ // [RH] Specials and noclippers don't block moves
continue ;
}
if ( thing - > flags & ( MF_CORPSE ) )
{ // Corpses need a few more checks
if ( ! ( actor - > flags & MF_ICECORPSE ) )
continue ;
}
if ( ! ( thing - > flags4 & MF4_ACTLIKEBRIDGE ) & & ( actor - > flags & MF_SPECIAL ) )
{ // [RH] Only bridges block pickup items
continue ;
}
if ( thing = = actor )
{ // Don't clip against self
continue ;
}
if ( ( actor - > flags & MF_MISSILE ) & & ( thing = = actor - > target ) )
{ // Don't clip against whoever shot the missile.
continue ;
}
if ( actor - > Z ( ) > thing - > Top ( ) )
{ // over thing
continue ;
}
else if ( actor - > Top ( ) < = thing - > Z ( ) )
{ // under thing
continue ;
}
2016-03-27 11:29:58 +00:00
else if ( ! quick & & onmobj ! = NULL & & thing - > Top ( ) < onmobj - > Top ( ) )
2016-03-01 15:47:10 +00:00
{ // something higher is in the way
continue ;
}
onmobj = thing ;
if ( quick ) break ;
}
if ( pOnmobj ) * pOnmobj = onmobj ;
return onmobj = = NULL ;
}
2016-11-30 00:25:51 +00:00
DEFINE_ACTION_FUNCTION ( AActor , TestMobjZ )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_BOOL_DEF ( quick ) ;
AActor * on = nullptr ; ;
bool retv = P_TestMobjZ ( self , quick , & on ) ;
if ( numret > 1 )
{
numret = 2 ;
ret [ 1 ] . SetPointer ( on , ATAG_OBJECT ) ;
}
if ( numret > 0 )
{
ret [ 0 ] . SetInt ( retv ) ;
}
return numret ;
}
2016-03-01 15:47:10 +00:00
//=============================================================================
//
// P_FakeZMovement
//
// Fake the zmovement so that we can check if a move is legal
//=============================================================================
void P_FakeZMovement ( AActor * mo )
{
//
// adjust height
//
2016-03-27 11:29:58 +00:00
mo - > AddZ ( mo - > Vel . Z ) ;
2016-03-01 15:47:10 +00:00
if ( ( mo - > flags & MF_FLOAT ) & & mo - > target )
{ // float down towards target if too close
if ( ! ( mo - > flags & MF_SKULLFLY ) & & ! ( mo - > flags & MF_INFLOAT ) )
{
2016-03-27 11:29:58 +00:00
double dist = mo - > Distance2D ( mo - > target ) ;
double delta = mo - > target - > Center ( ) - mo - > Z ( ) ;
2016-03-01 15:47:10 +00:00
if ( delta < 0 & & dist < - ( delta * 3 ) )
2016-03-27 11:29:58 +00:00
mo - > AddZ ( - mo - > FloatSpeed ) ;
2016-03-01 15:47:10 +00:00
else if ( delta > 0 & & dist < ( delta * 3 ) )
2016-03-27 11:29:58 +00:00
mo - > AddZ ( mo - > FloatSpeed ) ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-20 18:52:35 +00:00
if ( mo - > player & & mo - > flags & MF_NOGRAVITY & & ( mo - > Z ( ) > mo - > floorz ) & & ! mo - > IsNoClip2 ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
mo - > AddZ ( DAngle ( 4.5 * level . maptime ) . Sin ( ) ) ;
2016-03-01 15:47:10 +00:00
}
//
// clip movement
//
2016-03-20 18:52:35 +00:00
if ( mo - > Z ( ) < = mo - > floorz )
2016-03-01 15:47:10 +00:00
{ // hit the floor
2016-03-20 18:52:35 +00:00
mo - > SetZ ( mo - > floorz ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-20 12:32:53 +00:00
if ( mo - > Top ( ) > mo - > ceilingz )
2016-03-01 15:47:10 +00:00
{ // hit the ceiling
2016-03-20 19:55:06 +00:00
mo - > SetZ ( mo - > ceilingz - mo - > Height ) ;
2016-03-01 15:47:10 +00:00
}
}
//===========================================================================
//
// CheckForPushSpecial
//
//===========================================================================
2016-03-26 22:19:38 +00:00
static void CheckForPushSpecial ( line_t * line , int side , AActor * mobj , DVector2 * posforwindowcheck )
2016-03-01 15:47:10 +00:00
{
if ( line - > special & & ! ( mobj - > flags6 & MF6_NOTRIGGER ) )
{
2016-08-09 18:15:13 +00:00
if ( posforwindowcheck & & ! ( i_compatflags2 & COMPATF2_PUSHWINDOW ) & & line - > backsector ! = NULL )
2016-03-01 15:47:10 +00:00
{ // Make sure this line actually blocks us and is not a window
// or similar construct we are standing inside of.
2016-03-26 22:19:38 +00:00
DVector3 pos = mobj - > PosRelative ( line ) ;
double fzt = line - > frontsector - > ceilingplane . ZatPoint ( * posforwindowcheck ) ;
double fzb = line - > frontsector - > floorplane . ZatPoint ( * posforwindowcheck ) ;
double bzt = line - > backsector - > ceilingplane . ZatPoint ( * posforwindowcheck ) ;
double bzb = line - > backsector - > floorplane . ZatPoint ( * posforwindowcheck ) ;
if ( fzt > = mobj - > Top ( ) & & bzt > = mobj - > Top ( ) & &
fzb < = mobj - > Z ( ) & & bzb < = mobj - > Z ( ) )
2016-03-01 15:47:10 +00:00
{
2016-04-27 10:37:38 +00:00
if ( line - > flags & ML_3DMIDTEX )
{
double top , bot ;
P_GetMidTexturePosition ( line , side , & top , & bot ) ;
if ( bot < mobj - > Top ( ) & & top > mobj - > Z ( ) )
{
goto isblocking ;
}
}
2016-03-01 15:47:10 +00:00
// we must also check if some 3D floor in the backsector may be blocking
2016-03-26 22:19:38 +00:00
for ( auto rover : line - > backsector - > e - > XFloor . ffloors )
2016-03-01 15:47:10 +00:00
{
if ( ! ( rover - > flags & FF_SOLID ) | | ! ( rover - > flags & FF_EXISTS ) ) continue ;
2016-03-26 22:19:38 +00:00
double ff_bottom = rover - > bottom . plane - > ZatPoint ( * posforwindowcheck ) ;
double ff_top = rover - > top . plane - > ZatPoint ( * posforwindowcheck ) ;
2016-03-01 15:47:10 +00:00
2016-03-26 22:19:38 +00:00
if ( ff_bottom < mobj - > Top ( ) & & ff_top > mobj - > Z ( ) )
2016-03-01 15:47:10 +00:00
{
goto isblocking ;
}
}
return ;
}
}
isblocking :
if ( mobj - > flags2 & MF2_PUSHWALL )
{
P_ActivateLine ( line , mobj , side , SPAC_Push ) ;
}
else if ( mobj - > flags2 & MF2_IMPACT )
{
if ( ( level . flags2 & LEVEL2_MISSILESACTIVATEIMPACT ) | |
! ( mobj - > flags & MF_MISSILE ) | |
( mobj - > target = = NULL ) )
{
P_ActivateLine ( line , mobj , side , SPAC_Impact ) ;
}
else
{
P_ActivateLine ( line , mobj - > target , side , SPAC_Impact ) ;
}
}
}
}
//==========================================================================
//
// P_TryMove
// Attempt to move to a new position,
// crossing special lines unless MF_TELEPORT is set.
//
//==========================================================================
2016-03-27 11:29:58 +00:00
bool P_TryMove ( AActor * thing , const DVector2 & pos ,
2016-03-01 15:47:10 +00:00
int dropoff , // killough 3/15/98: allow dropoff as option
const secplane_t * onfloor , // [RH] Let P_TryMove keep the thing on the floor
FCheckPosition & tm ,
bool missileCheck ) // [GZ] Fired missiles ignore the drop-off test
{
sector_t * oldsector ;
2016-03-27 11:29:58 +00:00
double oldz ;
2016-03-01 15:47:10 +00:00
int side ;
int oldside ;
sector_t * oldsec = thing - > Sector ; // [RH] for sector actions
sector_t * newsec ;
tm . floatok = false ;
2016-05-20 11:10:42 +00:00
tm . portalstep = false ;
2016-03-27 11:29:58 +00:00
oldz = thing - > Z ( ) ;
2016-03-01 15:47:10 +00:00
if ( onfloor )
{
2016-03-27 11:29:58 +00:00
thing - > SetZ ( onfloor - > ZatPoint ( pos ) ) ;
2016-03-01 15:47:10 +00:00
}
thing - > flags6 | = MF6_INTRYMOVE ;
2016-03-27 00:06:54 +00:00
if ( ! P_CheckPosition ( thing , pos , tm ) )
2016-03-01 15:47:10 +00:00
{
AActor * BlockingMobj = thing - > BlockingMobj ;
// Solid wall or thing
if ( ! BlockingMobj | | BlockingMobj - > player | | ! thing - > player )
{
goto pushline ;
}
else
{
if ( BlockingMobj - > player | | ! thing - > player )
{
goto pushline ;
}
2016-03-22 11:42:27 +00:00
else if ( BlockingMobj - > Top ( ) - thing - > Z ( ) > thing - > MaxStepHeight
2016-03-27 11:29:58 +00:00
| | ( BlockingMobj - > Sector - > ceilingplane . ZatPoint ( pos ) - ( BlockingMobj - > Top ( ) ) < thing - > Height )
2016-03-20 19:55:06 +00:00
| | ( tm . ceilingz - ( BlockingMobj - > Top ( ) ) < thing - > Height ) )
2016-03-01 15:47:10 +00:00
{
goto pushline ;
}
}
if ( ! ( tm . thing - > flags2 & MF2_PASSMOBJ ) | | ( i_compatflags & COMPATF_NO_PASSMOBJ ) )
{
2016-03-27 11:29:58 +00:00
thing - > SetZ ( oldz ) ;
2016-03-01 15:47:10 +00:00
thing - > flags6 & = ~ MF6_INTRYMOVE ;
return false ;
}
}
if ( thing - > flags3 & MF3_FLOORHUGGER )
{
2016-03-20 18:52:35 +00:00
thing - > SetZ ( tm . floorz ) ;
2016-03-01 15:47:10 +00:00
}
else if ( thing - > flags3 & MF3_CEILINGHUGGER )
{
2016-03-20 19:55:06 +00:00
thing - > SetZ ( tm . ceilingz - thing - > Height ) ;
2016-03-01 15:47:10 +00:00
}
if ( onfloor & & tm . floorsector = = thing - > floorsector )
{
2016-03-20 18:52:35 +00:00
thing - > SetZ ( tm . floorz ) ;
2016-03-01 15:47:10 +00:00
}
if ( ! ( thing - > flags & MF_NOCLIP ) )
{
2016-03-20 19:55:06 +00:00
if ( tm . ceilingz - tm . floorz < thing - > Height )
2016-03-01 15:47:10 +00:00
{
goto pushline ; // doesn't fit
}
tm . floatok = true ;
if ( ! ( thing - > flags & MF_TELEPORT )
2016-03-20 12:32:53 +00:00
& & tm . ceilingz < thing - > Top ( )
2016-03-01 15:47:10 +00:00
& & ! ( thing - > flags3 & MF3_CEILINGHUGGER )
& & ( ! ( thing - > flags2 & MF2_FLY ) | | ! ( thing - > flags & MF_NOGRAVITY ) ) )
{
goto pushline ; // mobj must lower itself to fit
}
if ( thing - > flags2 & MF2_FLY & & thing - > flags & MF_NOGRAVITY )
{
# if 1
2016-03-20 12:32:53 +00:00
if ( thing - > Top ( ) > tm . ceilingz )
2016-03-01 15:47:10 +00:00
goto pushline ;
# else
// When flying, slide up or down blocking lines until the actor
// is not blocked.
2016-03-20 12:32:53 +00:00
if ( thing - > Top ( ) > tm . ceilingz )
2016-03-01 15:47:10 +00:00
{
2016-03-20 18:52:35 +00:00
thing - > Vel . Z = - 8 ;
2016-03-01 15:47:10 +00:00
goto pushline ;
}
2016-03-23 11:21:52 +00:00
else if ( thing - > Z ( ) < tm . floorz & & tm . floorz - tm . dropoffz > thing - > MaxDropOffHeight )
2016-03-01 15:47:10 +00:00
{
2016-03-20 18:52:35 +00:00
thing - > Vel . Z = 8 ;
2016-03-01 15:47:10 +00:00
goto pushline ;
}
# endif
}
if ( ! ( thing - > flags & MF_TELEPORT ) & & ! ( thing - > flags3 & MF3_FLOORHUGGER ) )
{
2016-03-20 18:52:35 +00:00
if ( ( thing - > flags & MF_MISSILE ) & & ! ( thing - > flags6 & MF6_STEPMISSILE ) & & tm . floorz > thing - > Z ( ) )
2016-03-01 15:47:10 +00:00
{ // [RH] Don't let normal missiles climb steps
goto pushline ;
}
2016-03-22 11:42:27 +00:00
if ( tm . floorz - thing - > Z ( ) > thing - > MaxStepHeight )
2016-03-01 15:47:10 +00:00
{ // too big a step up
goto pushline ;
}
2016-03-20 18:52:35 +00:00
else if ( thing - > Z ( ) < tm . floorz )
2016-03-01 15:47:10 +00:00
{ // [RH] Check to make sure there's nothing in the way for the step up
2016-03-20 18:52:35 +00:00
double savedz = thing - > Z ( ) ;
2016-03-01 15:47:10 +00:00
bool good ;
2016-03-20 18:52:35 +00:00
thing - > SetZ ( tm . floorz ) ;
2016-03-01 15:47:10 +00:00
good = P_TestMobjZ ( thing ) ;
2016-03-20 18:52:35 +00:00
thing - > SetZ ( savedz ) ;
2016-03-01 15:47:10 +00:00
if ( ! good )
{
goto pushline ;
}
if ( thing - > flags6 & MF6_STEPMISSILE )
{
2016-03-20 18:52:35 +00:00
thing - > SetZ ( tm . floorz ) ;
2016-03-01 15:47:10 +00:00
// If moving down, cancel vertical component of the velocity
2016-03-20 18:52:35 +00:00
if ( thing - > Vel . Z < 0 )
2016-03-01 15:47:10 +00:00
{
// If it's a bouncer, let it bounce off its new floor, too.
if ( thing - > BounceFlags & BOUNCE_Floors )
{
thing - > FloorBounceMissile ( tm . floorsector - > floorplane ) ;
}
else
{
2016-03-19 23:54:18 +00:00
thing - > Vel . Z = 0 ;
2016-03-01 15:47:10 +00:00
}
}
}
}
}
// compatibility check: Doom originally did not allow monsters to cross dropoffs at all.
// If the compatibility flag is on, only allow this when the velocity comes from a scroller
if ( ( i_compatflags & COMPATF_CROSSDROPOFF ) & & ! ( thing - > flags4 & MF4_SCROLLMOVE ) )
{
dropoff = false ;
}
if ( dropoff = = 2 & & // large jump down (e.g. dogs)
2016-03-23 11:21:52 +00:00
( tm . floorz - tm . dropoffz > 128. | | thing - > target = = NULL | | thing - > target - > Z ( ) > tm . dropoffz ) )
2016-03-01 15:47:10 +00:00
{
dropoff = false ;
}
// killough 3/15/98: Allow certain objects to drop off
if ( ( ! dropoff & & ! ( thing - > flags & ( MF_DROPOFF | MF_FLOAT | MF_MISSILE ) ) ) | | ( thing - > flags5 & MF5_NODROPOFF ) )
{
if ( ! ( thing - > flags5 & MF5_AVOIDINGDROPOFF ) )
{
2016-03-20 18:52:35 +00:00
double floorz = tm . floorz ;
2016-03-01 15:47:10 +00:00
// [RH] If the thing is standing on something, use its current z as the floorz.
// This is so that it does not walk off of things onto a drop off.
if ( thing - > flags2 & MF2_ONMOBJ )
{
2016-03-20 18:52:35 +00:00
floorz = MAX ( thing - > Z ( ) , tm . floorz ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-23 11:21:52 +00:00
if ( floorz - tm . dropoffz > thing - > MaxDropOffHeight & &
2016-03-01 15:47:10 +00:00
! ( thing - > flags2 & MF2_BLASTED ) & & ! missileCheck )
{ // Can't move over a dropoff unless it's been blasted
// [GZ] Or missile-spawned
2016-03-27 11:29:58 +00:00
thing - > SetZ ( oldz ) ;
2016-03-01 15:47:10 +00:00
thing - > flags6 & = ~ MF6_INTRYMOVE ;
return false ;
}
}
else
{
// special logic to move a monster off a dropoff
// this intentionally does not check for standing on things.
2016-03-23 11:21:52 +00:00
if ( thing - > floorz - tm . floorz > thing - > MaxDropOffHeight | |
2016-03-01 15:47:10 +00:00
thing - > dropoffz - tm . dropoffz > thing - > MaxDropOffHeight )
{
thing - > flags6 & = ~ MF6_INTRYMOVE ;
return false ;
}
}
}
if ( thing - > flags2 & MF2_CANTLEAVEFLOORPIC
& & ( tm . floorpic ! = thing - > floorpic
2016-03-20 18:52:35 +00:00
| | tm . floorz - thing - > Z ( ) ! = 0 ) )
2016-03-01 15:47:10 +00:00
{ // must stay within a sector of a certain floor type
2016-03-27 11:29:58 +00:00
thing - > SetZ ( oldz ) ;
2016-03-01 15:47:10 +00:00
thing - > flags6 & = ~ MF6_INTRYMOVE ;
return false ;
}
//Added by MC: To prevent bot from getting into dangerous sectors.
if ( thing - > player & & thing - > player - > Bot ! = NULL & & thing - > flags & MF_SHOOTABLE )
{
if ( tm . sector ! = thing - > Sector
& & bglobal . IsDangerous ( tm . sector ) )
{
thing - > player - > Bot - > prev = thing - > player - > Bot - > dest ;
thing - > player - > Bot - > dest = NULL ;
2016-03-19 23:54:18 +00:00
thing - > Vel . X = thing - > Vel . Y = 0 ;
2016-03-27 11:29:58 +00:00
thing - > SetZ ( oldz ) ;
2016-03-01 15:47:10 +00:00
thing - > flags6 & = ~ MF6_INTRYMOVE ;
return false ;
}
}
}
// [RH] Check status of eyes against fake floor/ceiling in case
// it slopes or the player's eyes are bobbing in and out.
bool oldAboveFakeFloor , oldAboveFakeCeiling ;
2016-04-02 21:01:32 +00:00
double viewheight ;
// pacify GCC
viewheight = thing - > player ? thing - > player - > viewheight : thing - > Height / 2 ;
oldAboveFakeFloor = oldAboveFakeCeiling = false ;
2016-03-01 15:47:10 +00:00
if ( oldsec - > heightsec )
{
2016-03-30 09:25:02 +00:00
double eyez = oldz + viewheight ;
2016-03-01 15:47:10 +00:00
oldAboveFakeFloor = eyez > oldsec - > heightsec - > floorplane . ZatPoint ( thing ) ;
oldAboveFakeCeiling = eyez > oldsec - > heightsec - > ceilingplane . ZatPoint ( thing ) ;
}
// Borrowed from MBF:
if ( thing - > BounceFlags & BOUNCE_MBF & & // killough 8/13/98
! ( thing - > flags & ( MF_MISSILE | MF_NOGRAVITY ) ) & &
2016-03-20 18:52:35 +00:00
! thing - > IsSentient ( ) & & tm . floorz - thing - > Z ( ) > 16 )
2016-03-01 15:47:10 +00:00
{ // too big a step up for MBF bouncers under gravity
thing - > flags6 & = ~ MF6_INTRYMOVE ;
return false ;
}
// Check for crossed portals
bool portalcrossed ;
portalcrossed = false ;
while ( true )
{
2016-03-27 11:29:58 +00:00
double bestfrac = 1.1 ;
2016-03-01 15:47:10 +00:00
spechit_t besthit ;
2016-03-12 16:43:36 +00:00
int besthitnum ;
2016-03-01 15:47:10 +00:00
// find the portal nearest to the crossing actor
2016-03-12 16:43:36 +00:00
for ( unsigned i = 0 ; i < portalhit . Size ( ) ; i + + )
2016-03-01 15:47:10 +00:00
{
2016-03-12 16:43:36 +00:00
auto & spec = portalhit [ i ] ;
2016-03-01 15:47:10 +00:00
line_t * ld = spec . line ;
if ( ld - > frontsector - > PortalGroup ! = thing - > Sector - > PortalGroup ) continue ; // must be in the same group to be considered valid.
// see if the line was crossed
2016-03-26 22:19:38 +00:00
oldside = P_PointOnLineSide ( spec . Oldrefpos , ld ) ;
side = P_PointOnLineSide ( spec . Refpos , ld ) ;
2016-03-01 15:47:10 +00:00
if ( oldside = = 0 & & side = = 1 )
{
2016-03-26 22:19:38 +00:00
divline_t dl2 = { ld - > v1 - > fX ( ) , ld - > v1 - > fY ( ) , ld - > Delta ( ) . X , ld - > Delta ( ) . Y } ;
divline_t dl1 = { spec . Oldrefpos . X , spec . Oldrefpos . Y , spec . Refpos . X - spec . Oldrefpos . X , spec . Refpos . Y - spec . Oldrefpos . Y } ;
2016-03-27 11:29:58 +00:00
double frac = P_InterceptVector ( & dl1 , & dl2 ) ;
2016-03-01 15:47:10 +00:00
if ( frac < bestfrac )
{
besthit = spec ;
bestfrac = frac ;
2016-03-12 16:43:36 +00:00
besthitnum = i ;
2016-03-01 15:47:10 +00:00
}
}
}
2016-03-27 11:29:58 +00:00
if ( bestfrac < 1.1 )
2016-03-01 15:47:10 +00:00
{
2016-03-12 16:43:36 +00:00
portalhit . Delete ( besthitnum ) ;
2016-03-01 15:47:10 +00:00
line_t * ld = besthit . line ;
FLinePortal * port = ld - > getPortal ( ) ;
if ( port - > mType = = PORTT_LINKED )
{
thing - > UnlinkFromWorld ( ) ;
2016-03-27 11:29:58 +00:00
thing - > SetXY ( tm . pos + port - > mDisplacement ) ;
thing - > Prev + = port - > mDisplacement ;
2016-03-01 15:47:10 +00:00
thing - > LinkToWorld ( ) ;
P_FindFloorCeiling ( thing ) ;
portalcrossed = true ;
2016-05-20 11:10:42 +00:00
tm . portalstep = false ;
2016-03-01 15:47:10 +00:00
}
else if ( ! portalcrossed )
{
2016-03-27 00:06:54 +00:00
DVector3 pos ( tm . pos , thing - > Z ( ) ) ;
DVector3 oldthingpos = thing - > Pos ( ) ;
DVector2 thingpos = oldthingpos ;
2016-03-01 15:47:10 +00:00
2016-03-27 00:06:54 +00:00
P_TranslatePortalXY ( ld , pos . X , pos . Y ) ;
P_TranslatePortalXY ( ld , thingpos . X , thingpos . Y ) ;
P_TranslatePortalZ ( ld , pos . Z ) ;
thing - > SetXYZ ( thingpos . X , thingpos . Y , pos . Z ) ;
if ( ! P_CheckPosition ( thing , pos , true ) ) // check if some actor blocks us on the other side. (No line checks, because of the mess that'd create.)
2016-03-01 15:47:10 +00:00
{
thing - > SetXYZ ( oldthingpos ) ;
thing - > flags6 & = ~ MF6_INTRYMOVE ;
return false ;
}
thing - > UnlinkFromWorld ( ) ;
thing - > SetXYZ ( pos ) ;
2016-03-19 23:54:18 +00:00
P_TranslatePortalVXVY ( ld , thing - > Vel . X , thing - > Vel . Y ) ;
2016-03-16 11:41:26 +00:00
P_TranslatePortalAngle ( ld , thing - > Angles . Yaw ) ;
2016-03-01 15:47:10 +00:00
thing - > LinkToWorld ( ) ;
P_FindFloorCeiling ( thing ) ;
thing - > ClearInterpolation ( ) ;
portalcrossed = true ;
2016-05-20 11:10:42 +00:00
tm . portalstep = false ;
2016-03-01 15:47:10 +00:00
}
// if this is the current camera we need to store the point where the portal was crossed and the exit
// so that the renderer can properly calculate an interpolated position along the movement path.
if ( thing = = players [ consoleplayer ] . camera )
{
2016-04-02 07:58:56 +00:00
divline_t dl1 = { besthit . Oldrefpos . X , besthit . Oldrefpos . Y , besthit . Refpos . X - besthit . Oldrefpos . X , besthit . Refpos . Y - besthit . Oldrefpos . Y } ;
2016-03-27 11:29:58 +00:00
DVector3a hit = { { dl1 . x + dl1 . dx * bestfrac , dl1 . y + dl1 . dy * bestfrac , 0. } , 0. } ;
2016-03-01 15:47:10 +00:00
R_AddInterpolationPoint ( hit ) ;
if ( port - > mType = = PORTT_LINKED )
{
2016-03-27 11:29:58 +00:00
hit . pos . X + = port - > mDisplacement . X ;
hit . pos . Y + = port - > mDisplacement . Y ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-27 11:29:58 +00:00
P_TranslatePortalXY ( ld , hit . pos . X , hit . pos . Y ) ;
P_TranslatePortalZ ( ld , hit . pos . Z ) ;
players [ consoleplayer ] . viewz + = hit . pos . Z ; // needs to be done here because otherwise the renderer will not catch the change.
2016-03-03 10:58:04 +00:00
P_TranslatePortalAngle ( ld , hit . angle ) ;
2016-03-01 15:47:10 +00:00
}
R_AddInterpolationPoint ( hit ) ;
}
2016-03-12 16:43:36 +00:00
if ( port - > mType = = PORTT_LINKED )
{
continue ;
2016-03-14 23:16:13 +00:00
}
2016-03-01 15:47:10 +00:00
}
break ;
}
if ( ! portalcrossed )
{
// the move is ok, so link the thing into its new position
thing - > UnlinkFromWorld ( ) ;
oldsector = thing - > Sector ;
thing - > floorz = tm . floorz ;
2016-03-20 12:32:53 +00:00
thing - > ceilingz = tm . ceilingz ;
2016-03-01 15:47:10 +00:00
thing - > dropoffz = tm . dropoffz ; // killough 11/98: keep track of dropoffs
thing - > floorpic = tm . floorpic ;
thing - > floorterrain = tm . floorterrain ;
thing - > floorsector = tm . floorsector ;
thing - > ceilingpic = tm . ceilingpic ;
thing - > ceilingsector = tm . ceilingsector ;
2016-03-27 11:29:58 +00:00
thing - > SetXY ( pos ) ;
2016-03-01 15:47:10 +00:00
thing - > LinkToWorld ( ) ;
}
if ( thing - > flags2 & MF2_FLOORCLIP )
{
thing - > AdjustFloorClip ( ) ;
}
// if any special lines were hit, do the effect
if ( ! ( thing - > flags & ( MF_TELEPORT | MF_NOCLIP ) ) )
{
spechit_t spec ;
2016-03-26 22:19:38 +00:00
DVector2 lastpos = thing - > Pos ( ) ;
2016-03-01 15:47:10 +00:00
while ( spechit . Pop ( spec ) )
{
line_t * ld = spec . line ;
// see if the line was crossed
2016-03-09 10:44:01 +00:00
2016-04-04 12:19:34 +00:00
side = P_PointOnLineSide ( spec . Refpos , ld ) ;
2016-03-26 22:19:38 +00:00
oldside = P_PointOnLineSide ( spec . Oldrefpos , ld ) ;
2016-03-01 15:47:10 +00:00
if ( side ! = oldside & & ld - > special & & ! ( thing - > flags6 & MF6_NOTRIGGER ) )
{
if ( thing - > player & & ( thing - > player - > cheats & CF_PREDICTING ) )
{
P_PredictLine ( ld , thing , oldside , SPAC_Cross ) ;
}
else if ( thing - > player )
{
P_ActivateLine ( ld , thing , oldside , SPAC_Cross ) ;
}
else if ( thing - > flags2 & MF2_MCROSS )
{
P_ActivateLine ( ld , thing , oldside , SPAC_MCross ) ;
}
else if ( thing - > flags2 & MF2_PCROSS )
{
P_ActivateLine ( ld , thing , oldside , SPAC_PCross ) ;
}
else if ( ( ld - > special = = Teleport | |
ld - > special = = Teleport_NoFog | |
ld - > special = = Teleport_Line ) )
{ // [RH] Just a little hack for BOOM compatibility
P_ActivateLine ( ld , thing , oldside , SPAC_MCross ) ;
}
else
{
P_ActivateLine ( ld , thing , oldside , SPAC_AnyCross ) ;
}
}
}
}
// [RH] Don't activate anything if just predicting
if ( thing - > player & & ( thing - > player - > cheats & CF_PREDICTING ) )
{
thing - > flags6 & = ~ MF6_INTRYMOVE ;
return true ;
}
// [RH] Check for crossing fake floor/ceiling
newsec = thing - > Sector ;
if ( newsec - > heightsec & & oldsec - > heightsec & & newsec - > SecActTarget )
{
const sector_t * hs = newsec - > heightsec ;
2016-03-30 09:25:02 +00:00
double eyez = thing - > Z ( ) + viewheight ;
2016-03-27 11:29:58 +00:00
double fakez = hs - > floorplane . ZatPoint ( pos ) ;
2016-03-01 15:47:10 +00:00
if ( ! oldAboveFakeFloor & & eyez > fakez )
{ // View went above fake floor
newsec - > SecActTarget - > TriggerAction ( thing , SECSPAC_EyesSurface ) ;
}
else if ( oldAboveFakeFloor & & eyez < = fakez )
{ // View went below fake floor
newsec - > SecActTarget - > TriggerAction ( thing , SECSPAC_EyesDive ) ;
}
if ( ! ( hs - > MoreFlags & SECF_FAKEFLOORONLY ) )
{
2016-03-27 11:29:58 +00:00
fakez = hs - > ceilingplane . ZatPoint ( pos ) ;
2016-03-01 15:47:10 +00:00
if ( ! oldAboveFakeCeiling & & eyez > fakez )
{ // View went above fake ceiling
newsec - > SecActTarget - > TriggerAction ( thing , SECSPAC_EyesAboveC ) ;
}
else if ( oldAboveFakeCeiling & & eyez < = fakez )
{ // View went below fake ceiling
newsec - > SecActTarget - > TriggerAction ( thing , SECSPAC_EyesBelowC ) ;
}
}
}
2016-05-20 11:10:42 +00:00
// If the actor stepped through a ceiling portal we need to reacquire the actual position info after the transition
if ( tm . portalstep )
{
DVector3 oldpos = thing - > Pos ( ) ;
thing - > UnlinkFromWorld ( ) ;
thing - > SetXYZ ( thing - > PosRelative ( thing - > Sector - > GetOppositePortalGroup ( sector_t : : ceiling ) ) ) ;
thing - > Prev = thing - > Pos ( ) - oldpos ;
thing - > Sector = P_PointInSector ( thing - > Pos ( ) ) ;
thing - > PrevPortalGroup = thing - > Sector - > PortalGroup ;
thing - > LinkToWorld ( ) ;
P_FindFloorCeiling ( thing ) ;
}
2016-03-01 15:47:10 +00:00
// [RH] If changing sectors, trigger transitions
thing - > CheckSectorTransition ( oldsec ) ;
thing - > flags6 & = ~ MF6_INTRYMOVE ;
return true ;
pushline :
thing - > flags6 & = ~ MF6_INTRYMOVE ;
// [RH] Don't activate anything if just predicting
if ( thing - > player & & ( thing - > player - > cheats & CF_PREDICTING ) )
{
return false ;
}
2016-03-27 11:29:58 +00:00
thing - > SetZ ( oldz ) ;
2016-03-01 15:47:10 +00:00
if ( ! ( thing - > flags & ( MF_TELEPORT | MF_NOCLIP ) ) )
{
int numSpecHitTemp ;
if ( tm . thing - > flags2 & MF2_BLASTED )
{
P_DamageMobj ( tm . thing , NULL , NULL , tm . thing - > Mass > > 5 , NAME_Melee ) ;
}
numSpecHitTemp = ( int ) spechit . Size ( ) ;
while ( numSpecHitTemp > 0 )
{
// see which lines were pushed
spechit_t & spec = spechit [ - - numSpecHitTemp ] ;
2016-03-26 22:19:38 +00:00
side = P_PointOnLineSide ( spec . Refpos , spec . line ) ;
CheckForPushSpecial ( spec . line , side , thing , & spec . Refpos ) ;
2016-03-01 15:47:10 +00:00
}
}
return false ;
}
2016-03-27 11:29:58 +00:00
bool P_TryMove ( AActor * thing , const DVector2 & pos ,
2016-03-01 15:47:10 +00:00
int dropoff , // killough 3/15/98: allow dropoff as option
const secplane_t * onfloor ) // [RH] Let P_TryMove keep the thing on the floor
{
FCheckPosition tm ;
2016-03-27 11:29:58 +00:00
return P_TryMove ( thing , pos , dropoff , onfloor , tm ) ;
2016-03-01 15:47:10 +00:00
}
2016-11-16 17:47:45 +00:00
DEFINE_ACTION_FUNCTION ( AActor , TryMove )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2016-11-16 18:29:06 +00:00
PARAM_FLOAT ( x ) ;
PARAM_FLOAT ( y ) ;
2016-11-16 17:47:45 +00:00
PARAM_INT ( dropoff ) ;
ACTION_RETURN_BOOL ( P_TryMove ( self , DVector2 ( x , y ) , dropoff ) ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// P_CheckMove
// Similar to P_TryMove but doesn't actually move the actor. Used for polyobject crushing
//
//==========================================================================
2016-04-10 16:51:40 +00:00
bool P_CheckMove ( AActor * thing , const DVector2 & pos , int flags )
2016-03-01 15:47:10 +00:00
{
FCheckPosition tm ;
2016-03-27 11:29:58 +00:00
double newz = thing - > Z ( ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 00:06:54 +00:00
if ( ! P_CheckPosition ( thing , pos , tm ) )
2016-03-01 15:47:10 +00:00
{
2016-04-10 16:51:40 +00:00
// Ignore PCM_DROPOFF. Not necessary here: a little later it is.
if ( ! flags | | ( ! ( flags & PCM_NOACTORS ) & & ! ( flags & PCM_NOLINES ) ) )
{
return false ;
}
if ( ! ( flags & PCM_NOACTORS ) & & thing - > BlockingMobj )
{
return false ;
}
if ( ! ( flags & PCM_NOLINES ) & & thing - > BlockingLine )
{
return false ;
}
2016-03-01 15:47:10 +00:00
}
if ( thing - > flags3 & MF3_FLOORHUGGER )
{
2016-03-27 11:29:58 +00:00
newz = tm . floorz ;
2016-03-01 15:47:10 +00:00
}
else if ( thing - > flags3 & MF3_CEILINGHUGGER )
{
2016-03-27 11:29:58 +00:00
newz = tm . ceilingz - thing - > Height ;
2016-03-01 15:47:10 +00:00
}
if ( ! ( thing - > flags & MF_NOCLIP ) )
{
2016-03-20 19:55:06 +00:00
if ( tm . ceilingz - tm . floorz < thing - > Height )
2016-03-01 15:47:10 +00:00
{
return false ;
}
if ( ! ( thing - > flags & MF_TELEPORT )
2016-03-20 19:55:06 +00:00
& & tm . ceilingz - newz < thing - > Height
2016-03-01 15:47:10 +00:00
& & ! ( thing - > flags3 & MF3_CEILINGHUGGER )
& & ( ! ( thing - > flags2 & MF2_FLY ) | | ! ( thing - > flags & MF_NOGRAVITY ) ) )
{
return false ;
}
if ( thing - > flags2 & MF2_FLY & & thing - > flags & MF_NOGRAVITY )
{
2016-03-20 12:32:53 +00:00
if ( thing - > Top ( ) > tm . ceilingz )
2016-03-01 15:47:10 +00:00
return false ;
}
if ( ! ( thing - > flags & MF_TELEPORT ) & & ! ( thing - > flags3 & MF3_FLOORHUGGER ) )
{
2016-03-27 11:29:58 +00:00
if ( tm . floorz - newz > thing - > MaxStepHeight )
2016-03-01 15:47:10 +00:00
{ // too big a step up
return false ;
}
2016-03-27 11:29:58 +00:00
else if ( ( thing - > flags & MF_MISSILE ) & & ! ( thing - > flags6 & MF6_STEPMISSILE ) & & tm . floorz > newz )
2016-03-01 15:47:10 +00:00
{ // [RH] Don't let normal missiles climb steps
return false ;
}
2016-03-27 11:29:58 +00:00
else if ( newz < tm . floorz )
2016-03-01 15:47:10 +00:00
{ // [RH] Check to make sure there's nothing in the way for the step up
2016-03-27 11:29:58 +00:00
double savedz = thing - > Z ( ) ;
thing - > SetZ ( newz = tm . floorz ) ;
2016-03-01 15:47:10 +00:00
bool good = P_TestMobjZ ( thing ) ;
2016-03-27 11:29:58 +00:00
thing - > SetZ ( savedz ) ;
2016-03-01 15:47:10 +00:00
if ( ! good )
{
return false ;
}
}
2016-05-11 08:09:13 +00:00
else if ( ( flags & PCM_DROPOFF ) & & ! ( thing - > flags & ( MF_FLOAT | MF_DROPOFF ) ) )
2016-04-09 19:44:17 +00:00
{
2016-05-11 17:39:08 +00:00
if ( newz - tm . dropoffz > thing - > MaxDropOffHeight )
2016-04-09 19:44:17 +00:00
{
return false ;
}
}
2016-03-01 15:47:10 +00:00
}
if ( thing - > flags2 & MF2_CANTLEAVEFLOORPIC
& & ( tm . floorpic ! = thing - > floorpic
2016-03-27 11:29:58 +00:00
| | tm . floorz - newz ! = 0 ) )
2016-03-01 15:47:10 +00:00
{ // must stay within a sector of a certain floor type
return false ;
}
}
return true ;
}
//==========================================================================
//
// SLIDE MOVE
// Allows the player to slide along any angled walls.
//
//==========================================================================
struct FSlide
{
2016-03-27 11:29:58 +00:00
double bestSlidefrac ;
double secondSlidefrac ;
2016-03-01 15:47:10 +00:00
line_t * bestslideline ;
line_t * secondslideline ;
AActor * slidemo ;
2016-03-27 11:29:58 +00:00
DVector2 tmmove ;
2016-03-01 15:47:10 +00:00
void HitSlideLine ( line_t * ld ) ;
2016-03-27 11:29:58 +00:00
void SlideTraverse ( const DVector2 & start , const DVector2 & end ) ;
void SlideMove ( AActor * mo , DVector2 tryp , int numsteps ) ;
2016-03-01 15:47:10 +00:00
// The bouncing code uses the same data structure
2016-03-27 11:29:58 +00:00
bool BounceTraverse ( const DVector2 & start , const DVector2 & end ) ;
2016-03-01 15:47:10 +00:00
bool BounceWall ( AActor * mo ) ;
} ;
//==========================================================================
//
// P_HitSlideLine
// Adjusts the xmove / ymove
// so that the next move will slide along the wall.
// If the floor is icy, then you can bounce off a wall. // phares
//
//==========================================================================
void FSlide : : HitSlideLine ( line_t * ld )
{
int side ;
2016-03-27 11:29:58 +00:00
DAngle lineangle ;
DAngle moveangle ;
DAngle deltaangle ;
2016-03-01 15:47:10 +00:00
2016-03-27 11:29:58 +00:00
double movelen ;
2016-03-01 15:47:10 +00:00
bool icyfloor ; // is floor icy? // phares
// |
// Under icy conditions, if the angle of approach to the wall // V
// is more than 45 degrees, then you'll bounce and lose half
// your velocity. If less than 45 degrees, you'll slide along
// the wall. 45 is arbitrary and is believable.
// Check for the special cases of horz or vert walls.
// killough 10/98: only bounce if hit hard (prevents wobbling)
icyfloor =
2016-03-27 11:29:58 +00:00
tmmove . LengthSquared ( ) > 4 * 4 & &
2016-03-01 15:47:10 +00:00
var_friction & & // killough 8/28/98: calc friction on demand
2016-03-20 18:52:35 +00:00
slidemo - > Z ( ) < = slidemo - > floorz & &
2016-03-01 15:47:10 +00:00
P_GetFriction ( slidemo , NULL ) > ORIG_FRICTION ;
2016-03-27 11:29:58 +00:00
if ( ld - > Delta ( ) . X = = 0 )
2016-03-01 15:47:10 +00:00
{ // ST_VERTICAL
2016-03-27 11:29:58 +00:00
if ( icyfloor & & ( fabs ( tmmove . X ) > fabs ( tmmove . Y ) ) )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
tmmove . X = - tmmove . X / 2 ;
tmmove . Y / = 2 ; // absorb half the velocity
2016-03-01 15:47:10 +00:00
if ( slidemo - > player & & slidemo - > health > 0 & & ! ( slidemo - > player - > cheats & CF_PREDICTING ) )
{
S_Sound ( slidemo , CHAN_VOICE , " *grunt " , 1 , ATTN_IDLE ) ; // oooff!// ^
}
} // |
else // phares
2016-03-27 11:29:58 +00:00
tmmove . X = 0 ; // no more movement in the X direction
2016-03-01 15:47:10 +00:00
return ;
}
2016-03-27 11:29:58 +00:00
if ( ld - > Delta ( ) . Y = = 0 )
2016-03-01 15:47:10 +00:00
{ // ST_HORIZONTAL
2016-03-27 11:29:58 +00:00
if ( icyfloor & & ( fabs ( tmmove . Y ) > fabs ( tmmove . X ) ) )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
tmmove . X / = 2 ; // absorb half the velocity
tmmove . Y = - tmmove . Y / 2 ;
2016-03-01 15:47:10 +00:00
if ( slidemo - > player & & slidemo - > health > 0 & & ! ( slidemo - > player - > cheats & CF_PREDICTING ) )
{
S_Sound ( slidemo , CHAN_VOICE , " *grunt " , 1 , ATTN_IDLE ) ; // oooff!
}
}
else
2016-03-27 11:29:58 +00:00
tmmove . Y = 0 ; // no more movement in the Y direction
2016-03-01 15:47:10 +00:00
return ;
}
// The wall is angled. Bounce if the angle of approach is // phares
// less than 45 degrees. // phares
2016-03-27 11:29:58 +00:00
DVector3 pos = slidemo - > PosRelative ( ld ) ;
side = P_PointOnLineSide ( pos , ld ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 11:29:58 +00:00
lineangle = ld - > Delta ( ) . Angle ( ) ;
2016-03-01 15:47:10 +00:00
if ( side = = 1 )
2016-03-27 11:29:58 +00:00
lineangle + = 180. ;
2016-03-01 15:47:10 +00:00
2016-03-27 11:29:58 +00:00
moveangle = tmmove . Angle ( ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 11:29:58 +00:00
// prevents sudden path reversal due to rounding error | // phares
2016-03-27 20:49:59 +00:00
moveangle + = 3600 / 65536. * 65536. ; // Boom added 10 to the angle here.
2016-03-27 11:29:58 +00:00
deltaangle = : : deltaangle ( lineangle , moveangle ) ; // V
movelen = tmmove . Length ( ) ;
if ( icyfloor & & ( deltaangle > 45 ) & & ( deltaangle < 135 ) )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
moveangle = : : deltaangle ( deltaangle , lineangle ) ;
2016-03-01 15:47:10 +00:00
movelen / = 2 ; // absorb
if ( slidemo - > player & & slidemo - > health > 0 & & ! ( slidemo - > player - > cheats & CF_PREDICTING ) )
{
S_Sound ( slidemo , CHAN_VOICE , " *grunt " , 1 , ATTN_IDLE ) ; // oooff!
}
2016-03-27 11:29:58 +00:00
tmmove = moveangle . ToVector ( movelen ) ;
}
else
{
// The compatibility option that used to be here had to be removed because
// with floating point math it was no longer possible to reproduce.
#if 0
// with full precision this should work now. Needs some testing
if ( deltaangle < 0 ) deltaangle + = 180. ;
tmmove = lineangle . ToVector ( movelen * deltaangle . Cos ( ) ) ;
# else
divline_t dll , dlv ;
double inter1 , inter2 , inter3 ;
2016-03-01 15:47:10 +00:00
2016-03-27 11:29:58 +00:00
P_MakeDivline ( ld , & dll ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 11:29:58 +00:00
dlv . x = pos . X ;
dlv . y = pos . Y ;
dlv . dx = dll . dy ;
dlv . dy = - dll . dx ;
2016-03-01 15:47:10 +00:00
2016-03-27 11:29:58 +00:00
inter1 = P_InterceptVector ( & dll , & dlv ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 11:29:58 +00:00
dlv . dx = tmmove . X ;
dlv . dy = tmmove . Y ;
inter2 = P_InterceptVector ( & dll , & dlv ) ;
inter3 = P_InterceptVector ( & dlv , & dll ) ;
if ( inter3 ! = 0 )
{
tmmove . X = ( inter2 - inter1 ) * dll . dx / inter3 ;
tmmove . Y = ( inter2 - inter1 ) * dll . dy / inter3 ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-27 11:29:58 +00:00
tmmove . Zero ( ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 11:29:58 +00:00
# endif
2016-03-01 15:47:10 +00:00
} // phares
}
//==========================================================================
//
// PTR_SlideTraverse
//
//==========================================================================
2016-03-27 11:29:58 +00:00
void FSlide : : SlideTraverse ( const DVector2 & start , const DVector2 & end )
2016-03-01 15:47:10 +00:00
{
FLineOpening open ;
2016-03-27 11:29:58 +00:00
FPathTraverse it ( start . X , start . Y , end . X , end . Y , PT_ADDLINES ) ;
2016-03-01 15:47:10 +00:00
intercept_t * in ;
while ( ( in = it . Next ( ) ) )
{
line_t * li ;
if ( ! in - > isaline )
{
// should never happen
Printf ( " PTR_SlideTraverse: not a line? " ) ;
continue ;
}
li = in - > d . line ;
if ( ! ( li - > flags & ML_TWOSIDED ) | | ! li - > backsector )
{
2016-03-27 11:29:58 +00:00
DVector3 pos = slidemo - > PosRelative ( li ) ;
if ( P_PointOnLineSide ( pos , li ) )
2016-03-01 15:47:10 +00:00
{
// don't hit the back side
continue ;
}
goto isblocking ;
}
if ( li - > flags & ( ML_BLOCKING | ML_BLOCKEVERYTHING ) )
{
goto isblocking ;
}
if ( li - > flags & ML_BLOCK_PLAYERS & & slidemo - > player ! = NULL )
{
goto isblocking ;
}
if ( li - > flags & ML_BLOCKMONSTERS & & ! ( ( slidemo - > flags3 & MF3_NOBLOCKMONST )
| | ( ( i_compatflags & COMPATF_NOBLOCKFRIENDS ) & & ( slidemo - > flags & MF_FRIENDLY ) ) ) )
{
goto isblocking ;
}
// set openrange, opentop, openbottom
2016-03-27 11:29:58 +00:00
P_LineOpening ( open , slidemo , li , it . InterceptPoint ( in ) ) ;
2016-03-01 15:47:10 +00:00
2016-03-25 17:19:54 +00:00
if ( open . range < slidemo - > Height )
2016-03-01 15:47:10 +00:00
goto isblocking ; // doesn't fit
2016-03-25 17:19:54 +00:00
if ( open . top < slidemo - > Top ( ) )
2016-03-01 15:47:10 +00:00
goto isblocking ; // mobj is too high
2016-03-25 17:19:54 +00:00
if ( open . bottom - slidemo - > Z ( ) > slidemo - > MaxStepHeight )
2016-03-01 15:47:10 +00:00
{
goto isblocking ; // too big a step up
}
2016-03-25 17:19:54 +00:00
else if ( slidemo - > Z ( ) < open . bottom )
2016-03-01 15:47:10 +00:00
{ // [RH] Check to make sure there's nothing in the way for the step up
2016-03-25 17:19:54 +00:00
double savedz = slidemo - > Z ( ) ;
slidemo - > SetZ ( open . bottom ) ;
2016-03-01 15:47:10 +00:00
bool good = P_TestMobjZ ( slidemo ) ;
2016-03-25 17:19:54 +00:00
slidemo - > SetZ ( savedz ) ;
2016-03-01 15:47:10 +00:00
if ( ! good )
{
goto isblocking ;
}
}
// this line doesn't block movement
continue ;
// the line does block movement,
// see if it is closer than best so far
isblocking :
2016-03-31 14:52:25 +00:00
if ( in - > frac < bestSlidefrac )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
secondSlidefrac = bestSlidefrac ;
2016-03-01 15:47:10 +00:00
secondslideline = bestslideline ;
2016-03-31 14:52:25 +00:00
bestSlidefrac = in - > frac ;
2016-03-01 15:47:10 +00:00
bestslideline = li ;
}
return ; // stop
}
}
//==========================================================================
//
// P_SlideMove
//
2016-03-12 13:11:43 +00:00
// The vel.x / vel.y move is bad, so try to slide along a wall.
2016-03-01 15:47:10 +00:00
//
// Find the first line hit, move flush to it, and slide along it
//
// This is a kludgy mess.
//
//==========================================================================
2016-03-27 11:29:58 +00:00
void FSlide : : SlideMove ( AActor * mo , DVector2 tryp , int numsteps )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
DVector2 lead ;
DVector2 trail ;
DVector2 newpos ;
DVector2 move ;
2016-03-01 15:47:10 +00:00
const secplane_t * walkplane ;
int hitcount ;
hitcount = 3 ;
slidemo = mo ;
if ( mo - > player & & mo - > player - > mo = = mo & & mo - > reactiontime > 0 )
return ; // player coming right out of a teleporter.
retry :
if ( ! - - hitcount )
goto stairstep ; // don't loop forever
// trace along the three leading corners
2016-03-27 11:29:58 +00:00
if ( tryp . X > 0 )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
lead . X = mo - > X ( ) + mo - > radius ;
trail . X = mo - > X ( ) - mo - > radius ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-27 11:29:58 +00:00
lead . X = mo - > X ( ) - mo - > radius ;
trail . X = mo - > X ( ) + mo - > radius ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 11:29:58 +00:00
if ( tryp . Y > 0 )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
lead . Y = mo - > Y ( ) + mo - > radius ;
trail . Y = mo - > Y ( ) - mo - > radius ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-27 11:29:58 +00:00
lead . Y = mo - > Y ( ) - mo - > radius ;
trail . Y = mo - > Y ( ) + mo - > radius ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 11:29:58 +00:00
bestSlidefrac = 1.01 ;
2016-03-01 15:47:10 +00:00
2016-03-27 11:29:58 +00:00
SlideTraverse ( lead , lead + tryp ) ;
SlideTraverse ( DVector2 ( trail . X , lead . Y ) , tryp + DVector2 ( trail . X , lead . Y ) ) ;
SlideTraverse ( DVector2 ( lead . X , trail . Y ) , tryp + DVector2 ( lead . X , trail . Y ) ) ;
2016-03-01 15:47:10 +00:00
// move up to the wall
2016-03-27 11:29:58 +00:00
if ( bestSlidefrac > 1 )
2016-03-01 15:47:10 +00:00
{
// the move must have hit the middle, so stairstep
stairstep :
// killough 3/15/98: Allow objects to drop off ledges
2016-03-27 11:29:58 +00:00
move = { 0 , tryp . Y } ;
walkplane = P_CheckSlopeWalk ( mo , move ) ;
if ( ! P_TryMove ( mo , mo - > Pos ( ) + move , true , walkplane ) )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
move = { tryp . X , 0 } ;
walkplane = P_CheckSlopeWalk ( mo , move ) ;
P_TryMove ( mo , mo - > Pos ( ) + move , true , walkplane ) ;
2016-03-01 15:47:10 +00:00
}
return ;
}
// fudge a bit to make sure it doesn't hit
2016-03-27 20:49:59 +00:00
bestSlidefrac - = 1. / 32 ;
2016-03-27 11:29:58 +00:00
if ( bestSlidefrac > 0 )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
newpos = tryp * bestSlidefrac ;
2016-03-01 15:47:10 +00:00
// [BL] We need to abandon this function if we end up going through a teleporter
2016-03-27 11:29:58 +00:00
const DVector2 startvel = mo - > Vel . XY ( ) ;
2016-03-01 15:47:10 +00:00
// killough 3/15/98: Allow objects to drop off ledges
2016-03-27 11:29:58 +00:00
if ( ! P_TryMove ( mo , mo - > Pos ( ) + newpos , true ) )
2016-03-01 15:47:10 +00:00
goto stairstep ;
2016-03-27 11:29:58 +00:00
if ( mo - > Vel . XY ( ) ! = startvel )
2016-03-01 15:47:10 +00:00
return ;
}
// Now continue along the wall.
2016-03-27 11:29:58 +00:00
bestSlidefrac = 1. - ( bestSlidefrac + 1. / 32 ) ; // remainder
if ( bestSlidefrac > 1 )
bestSlidefrac = 1 ;
else if ( bestSlidefrac < = 0 )
2016-03-01 15:47:10 +00:00
return ;
2016-03-27 11:29:58 +00:00
tryp = tmmove = tryp * bestSlidefrac ;
2016-03-01 15:47:10 +00:00
HitSlideLine ( bestslideline ) ; // clip the moves
2016-03-27 11:29:58 +00:00
mo - > Vel . X = tmmove . X * numsteps ;
mo - > Vel . Y = tmmove . Y * numsteps ;
2016-03-01 15:47:10 +00:00
// killough 10/98: affect the bobbing the same way (but not voodoo dolls)
if ( mo - > player & & mo - > player - > mo = = mo )
{
2016-03-19 23:54:18 +00:00
if ( fabs ( mo - > player - > Vel . X ) > fabs ( mo - > Vel . X ) )
mo - > player - > Vel . X = mo - > Vel . X ;
if ( fabs ( mo - > player - > Vel . Y ) > fabs ( mo - > Vel . Y ) )
mo - > player - > Vel . Y = mo - > Vel . Y ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 11:29:58 +00:00
walkplane = P_CheckSlopeWalk ( mo , tmmove ) ;
2016-03-01 15:47:10 +00:00
// killough 3/15/98: Allow objects to drop off ledges
2016-03-27 11:29:58 +00:00
if ( ! P_TryMove ( mo , mo - > Pos ( ) + tmmove , true , walkplane ) )
2016-03-01 15:47:10 +00:00
{
goto retry ;
}
}
2016-03-27 11:29:58 +00:00
void P_SlideMove ( AActor * mo , const DVector2 & pos , int numsteps )
2016-03-01 15:47:10 +00:00
{
FSlide slide ;
2016-03-27 11:29:58 +00:00
slide . SlideMove ( mo , pos , numsteps ) ;
2016-03-01 15:47:10 +00:00
}
//============================================================================
//
// P_CheckSlopeWalk
//
//============================================================================
2016-03-27 12:07:35 +00:00
const secplane_t * P_CheckSlopeWalk ( AActor * actor , DVector2 & move )
2016-03-01 15:47:10 +00:00
{
static secplane_t copyplane ;
if ( actor - > flags & MF_NOGRAVITY )
{
return NULL ;
}
2016-03-27 12:07:35 +00:00
DVector3 pos = actor - > PosRelative ( actor - > floorsector ) ;
2016-03-01 15:47:10 +00:00
const secplane_t * plane = & actor - > floorsector - > floorplane ;
2016-03-27 12:07:35 +00:00
double planezhere = plane - > ZatPoint ( pos ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 12:07:35 +00:00
for ( auto rover : actor - > floorsector - > e - > XFloor . ffloors )
2016-03-01 15:47:10 +00:00
{
if ( ! ( rover - > flags & FF_SOLID ) | | ! ( rover - > flags & FF_EXISTS ) ) continue ;
2016-03-27 12:07:35 +00:00
double thisplanez = rover - > top . plane - > ZatPoint ( pos ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 12:07:35 +00:00
if ( thisplanez > planezhere & & thisplanez < = actor - > Z ( ) + actor - > MaxStepHeight )
2016-03-01 15:47:10 +00:00
{
copyplane = * rover - > top . plane ;
2016-03-29 10:40:41 +00:00
if ( copyplane . fC ( ) < 0 ) copyplane . FlipVert ( ) ;
2016-03-01 15:47:10 +00:00
plane = & copyplane ;
planezhere = thisplanez ;
}
}
if ( actor - > floorsector ! = actor - > Sector )
{
2016-03-27 12:07:35 +00:00
for ( auto rover : actor - > Sector - > e - > XFloor . ffloors )
2016-03-01 15:47:10 +00:00
{
if ( ! ( rover - > flags & FF_SOLID ) | | ! ( rover - > flags & FF_EXISTS ) ) continue ;
2016-03-30 07:41:46 +00:00
double thisplanez = rover - > top . plane - > ZatPoint ( actor ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 12:07:35 +00:00
if ( thisplanez > planezhere & & thisplanez < = actor - > Z ( ) + actor - > MaxStepHeight )
2016-03-01 15:47:10 +00:00
{
copyplane = * rover - > top . plane ;
2016-03-29 10:40:41 +00:00
if ( copyplane . fC ( ) < 0 ) copyplane . FlipVert ( ) ;
2016-03-01 15:47:10 +00:00
plane = & copyplane ;
planezhere = thisplanez ;
}
}
}
if ( actor - > floorsector ! = actor - > Sector )
{
// this additional check prevents sliding on sloped dropoffs
2016-03-27 12:07:35 +00:00
if ( planezhere > actor - > floorz + 4 )
2016-03-01 15:47:10 +00:00
return NULL ;
}
2016-03-27 12:07:35 +00:00
if ( actor - > Z ( ) - planezhere > 1 )
2016-03-01 15:47:10 +00:00
{ // not on floor
return NULL ;
}
2016-03-27 12:07:35 +00:00
if ( plane - > isSlope ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-27 12:07:35 +00:00
DVector2 dest ;
double t ;
2016-03-01 15:47:10 +00:00
2016-03-27 12:07:35 +00:00
dest = actor - > Pos ( ) + move ;
2016-04-03 17:46:00 +00:00
t = ( plane - > Normal ( ) | DVector3 ( dest , actor - > Z ( ) ) ) + plane - > fD ( ) ;
2016-03-01 15:47:10 +00:00
if ( t < 0 )
{ // Desired location is behind (below) the plane
// (i.e. Walking up the plane)
2016-03-31 07:23:14 +00:00
if ( plane - > fC ( ) < STEEPSLOPE )
2016-03-01 15:47:10 +00:00
{ // Can't climb up slopes of ~45 degrees or more
if ( actor - > flags & MF_NOCLIP )
{
return ( actor - > floorsector = = actor - > Sector ) ? plane : NULL ;
}
else
{
const msecnode_t * node ;
bool dopush = true ;
2016-03-30 07:41:46 +00:00
if ( plane - > fC ( ) > STEEPSLOPE * 2 / 3 )
2016-03-01 15:47:10 +00:00
{
for ( node = actor - > touching_sectorlist ; node ; node = node - > m_tnext )
{
sector_t * sec = node - > m_sector ;
2016-03-30 07:41:46 +00:00
if ( sec - > floorplane . fC ( ) > = STEEPSLOPE )
2016-03-01 15:47:10 +00:00
{
2016-03-27 12:07:35 +00:00
DVector3 pos = actor - > PosRelative ( sec ) + move ;
2016-03-01 15:47:10 +00:00
2016-03-27 12:07:35 +00:00
if ( sec - > floorplane . ZatPoint ( pos ) > = actor - > Z ( ) - actor - > MaxStepHeight )
2016-03-01 15:47:10 +00:00
{
dopush = false ;
break ;
}
}
}
}
if ( dopush )
{
2016-04-03 17:46:00 +00:00
move = plane - > Normal ( ) * 2 ;
actor - > Vel . X = move . X ;
actor - > Vel . Y = move . Y ;
2016-03-01 15:47:10 +00:00
}
return ( actor - > floorsector = = actor - > Sector ) ? plane : NULL ;
}
}
// Slide the desired location along the plane's normal
// so that it lies on the plane's surface
2016-04-03 17:46:00 +00:00
dest - = plane - > Normal ( ) * t ;
2016-03-27 12:07:35 +00:00
move = dest - actor - > Pos ( ) . XY ( ) ;
2016-03-01 15:47:10 +00:00
return ( actor - > floorsector = = actor - > Sector ) ? plane : NULL ;
}
else if ( t > 0 )
{ // Desired location is in front of (above) the plane
2016-03-28 19:04:46 +00:00
if ( actor - > isAtZ ( planezhere ) ) // it is very important not to be too precise here.
2016-03-27 12:07:35 +00:00
{
// Actor's current spot is on/in the plane, so walk down it
2016-03-01 15:47:10 +00:00
// Same principle as walking up, except reversed
2016-04-03 17:46:00 +00:00
dest + = plane - > Normal ( ) * t ;
2016-03-27 12:07:35 +00:00
move = dest - actor - > Pos ( ) . XY ( ) ;
2016-03-01 15:47:10 +00:00
return ( actor - > floorsector = = actor - > Sector ) ? plane : NULL ;
}
}
}
return NULL ;
}
//============================================================================
//
// PTR_BounceTraverse
//
//============================================================================
2016-03-27 11:29:58 +00:00
bool FSlide : : BounceTraverse ( const DVector2 & start , const DVector2 & end )
2016-03-01 15:47:10 +00:00
{
FLineOpening open ;
2016-03-27 11:29:58 +00:00
FPathTraverse it ( start . X , start . Y , end . X , end . Y , PT_ADDLINES ) ;
2016-03-01 15:47:10 +00:00
intercept_t * in ;
while ( ( in = it . Next ( ) ) )
{
line_t * li ;
if ( ! in - > isaline )
{
Printf ( " PTR_BounceTraverse: not a line? " ) ;
continue ;
}
li = in - > d . line ;
assert ( ( ( size_t ) li - ( size_t ) lines ) % sizeof ( line_t ) = = 0 ) ;
if ( li - > flags & ML_BLOCKEVERYTHING )
{
goto bounceblocking ;
}
if ( ! ( li - > flags & ML_TWOSIDED ) | | ! li - > backsector )
{
2016-03-27 20:49:59 +00:00
if ( P_PointOnLineSide ( slidemo - > Pos ( ) , li ) )
2016-03-01 15:47:10 +00:00
continue ; // don't hit the back side
goto bounceblocking ;
}
2016-03-27 20:49:59 +00:00
P_LineOpening ( open , slidemo , li , it . InterceptPoint ( in ) ) ; // set openrange, opentop, openbottom
2016-03-25 17:19:54 +00:00
if ( open . range < slidemo - > Height )
2016-03-01 15:47:10 +00:00
goto bounceblocking ; // doesn't fit
2016-03-25 17:19:54 +00:00
if ( open . top < slidemo - > Top ( ) )
2016-03-01 15:47:10 +00:00
goto bounceblocking ; // mobj is too high
2016-03-25 17:19:54 +00:00
if ( open . bottom > slidemo - > Z ( ) )
2016-03-01 15:47:10 +00:00
goto bounceblocking ; // mobj is too low
continue ; // this line doesn't block movement
// the line does block movement, see if it is closer than best so far
bounceblocking :
2016-03-31 14:52:25 +00:00
if ( in - > frac < bestSlidefrac )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
secondSlidefrac = bestSlidefrac ;
2016-03-01 15:47:10 +00:00
secondslideline = bestslideline ;
2016-03-31 14:52:25 +00:00
bestSlidefrac = in - > frac ;
2016-03-01 15:47:10 +00:00
bestslideline = li ;
}
return false ; // stop
}
return true ;
}
//============================================================================
//
// P_BounceWall
//
//============================================================================
bool FSlide : : BounceWall ( AActor * mo )
{
2016-03-27 11:29:58 +00:00
DVector2 lead ;
2016-03-01 15:47:10 +00:00
int side ;
2016-03-27 11:29:58 +00:00
DAngle lineangle , moveangle , deltaangle ;
double movelen ;
2016-03-01 15:47:10 +00:00
line_t * line ;
if ( ! ( mo - > BounceFlags & BOUNCE_Walls ) )
{
return false ;
}
slidemo = mo ;
//
// trace along the three leading corners
//
2016-03-19 23:54:18 +00:00
if ( mo - > Vel . X > 0 )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
lead . X = mo - > X ( ) + mo - > radius ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-27 11:29:58 +00:00
lead . X = mo - > X ( ) - mo - > radius ;
2016-03-01 15:47:10 +00:00
}
2016-03-19 23:54:18 +00:00
if ( mo - > Vel . Y > 0 )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
lead . Y = mo - > Y ( ) + mo - > radius ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-27 11:29:58 +00:00
lead . Y = mo - > Y ( ) - mo - > radius ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 11:29:58 +00:00
bestSlidefrac = 1.01 ;
2016-03-01 15:47:10 +00:00
bestslideline = mo - > BlockingLine ;
2016-03-27 11:29:58 +00:00
if ( BounceTraverse ( lead , lead + mo - > Vel . XY ( ) ) & & mo - > BlockingLine = = NULL )
2016-03-01 15:47:10 +00:00
{ // Could not find a wall, so bounce off the floor/ceiling instead.
2016-03-20 18:52:35 +00:00
double floordist = mo - > Z ( ) - mo - > floorz ;
double ceildist = mo - > ceilingz - mo - > Z ( ) ;
2016-03-01 15:47:10 +00:00
if ( floordist < = ceildist )
{
mo - > FloorBounceMissile ( mo - > Sector - > floorplane ) ;
return true ;
}
else
{
mo - > FloorBounceMissile ( mo - > Sector - > ceilingplane ) ;
return true ;
}
}
line = bestslideline ;
if ( line - > special = = Line_Horizon )
{
mo - > SeeSound = 0 ; // it might make a sound otherwise
mo - > Destroy ( ) ;
return true ;
}
// The amount of bounces is limited
if ( mo - > bouncecount > 0 & & - - mo - > bouncecount = = 0 )
{
if ( mo - > flags & MF_MISSILE )
P_ExplodeMissile ( mo , line , NULL ) ;
else
2016-11-26 00:14:47 +00:00
mo - > CallDie ( NULL , NULL ) ;
2016-03-01 15:47:10 +00:00
return true ;
}
2016-03-27 11:29:58 +00:00
side = P_PointOnLineSide ( mo - > Pos ( ) , line ) ;
lineangle = line - > Delta ( ) . Angle ( ) ;
2016-03-01 15:47:10 +00:00
if ( side = = 1 )
{
2016-03-27 11:29:58 +00:00
lineangle + = 180 ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 11:29:58 +00:00
moveangle = mo - > Vel . Angle ( ) ;
deltaangle = ( lineangle * 2 ) - moveangle ;
mo - > Angles . Yaw = deltaangle ;
2016-03-01 15:47:10 +00:00
2016-03-27 11:29:58 +00:00
movelen = mo - > Vel . XY ( ) . Length ( ) * mo - > wallbouncefactor ;
2016-03-01 15:47:10 +00:00
2016-03-27 11:29:58 +00:00
FBoundingBox box ( mo - > X ( ) , mo - > Y ( ) , mo - > radius ) ;
2016-03-01 15:47:10 +00:00
if ( box . BoxOnLineSide ( line ) = = - 1 )
{
2016-03-27 11:29:58 +00:00
DVector2 ofs = deltaangle . ToVector ( mo - > radius ) ;
DVector3 pos = mo - > Vec3Offset ( ofs . X , ofs . Y , 0. ) ;
2016-03-01 15:47:10 +00:00
mo - > SetOrigin ( pos , true ) ;
}
2016-03-27 11:29:58 +00:00
if ( movelen < 1 )
2016-03-01 15:47:10 +00:00
{
2016-03-27 11:29:58 +00:00
movelen = 2 ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 11:29:58 +00:00
DVector2 vel = deltaangle . ToVector ( movelen ) ;
mo - > Vel . X = vel . X ;
mo - > Vel . Y = vel . Y ;
2016-03-01 15:47:10 +00:00
if ( mo - > BounceFlags & BOUNCE_UseBounceState )
{
FState * bouncestate = mo - > FindState ( NAME_Bounce , NAME_Wall ) ;
if ( bouncestate ! = NULL )
{
mo - > SetState ( bouncestate ) ;
}
}
return true ;
}
bool P_BounceWall ( AActor * mo )
{
FSlide slide ;
return slide . BounceWall ( mo ) ;
}
//==========================================================================
//
//
//
//==========================================================================
extern FRandom pr_bounce ;
bool P_BounceActor ( AActor * mo , AActor * BlockingMobj , bool ontop )
{
//Don't go through all of this if the actor is reflective and wants things to pass through them.
if ( BlockingMobj & & ( ( BlockingMobj - > flags2 & MF2_REFLECTIVE ) & & ( BlockingMobj - > flags7 & MF7_THRUREFLECT ) ) ) return true ;
if ( mo & & BlockingMobj & & ( ( mo - > BounceFlags & BOUNCE_AllActors )
| | ( ( mo - > flags & MF_MISSILE ) & & ( ! ( mo - > flags2 & MF2_RIP )
| | ( BlockingMobj - > flags5 & MF5_DONTRIP )
| | ( ( mo - > flags6 & MF6_NOBOSSRIP ) & & ( BlockingMobj - > flags2 & MF2_BOSS ) ) ) & & ( BlockingMobj - > flags2 & MF2_REFLECTIVE ) )
| | ( ( BlockingMobj - > player = = NULL ) & & ( ! ( BlockingMobj - > flags3 & MF3_ISMONSTER ) ) ) ) )
{
if ( mo - > bouncecount > 0 & & - - mo - > bouncecount = = 0 ) return false ;
if ( mo - > flags7 & MF7_HITTARGET ) mo - > target = BlockingMobj ;
if ( mo - > flags7 & MF7_HITMASTER ) mo - > master = BlockingMobj ;
if ( mo - > flags7 & MF7_HITTRACER ) mo - > tracer = BlockingMobj ;
if ( ! ontop )
{
2016-03-16 23:07:37 +00:00
DAngle angle = BlockingMobj - > AngleTo ( mo ) + ( ( pr_bounce ( ) % 16 ) - 8 ) ;
2016-03-24 19:43:35 +00:00
double speed = mo - > VelXYToSpeed ( ) * mo - > wallbouncefactor ; // [GZ] was 0.75, using wallbouncefactor seems more consistent
2016-03-27 20:49:59 +00:00
mo - > Angles . Yaw = angle ;
2016-03-16 11:41:26 +00:00
mo - > VelFromAngle ( speed ) ;
2016-03-01 15:47:10 +00:00
mo - > PlayBounceSound ( true ) ;
if ( mo - > BounceFlags & BOUNCE_UseBounceState )
{
FName names [ ] = { NAME_Bounce , NAME_Actor , NAME_Creature } ;
FState * bouncestate ;
int count = 2 ;
if ( ( BlockingMobj - > flags & MF_SHOOTABLE ) & & ! ( BlockingMobj - > flags & MF_NOBLOOD ) )
{
count = 3 ;
}
bouncestate = mo - > FindState ( count , names ) ;
if ( bouncestate ! = NULL )
{
mo - > SetState ( bouncestate ) ;
}
}
}
else
{
2016-03-19 23:54:18 +00:00
double dot = mo - > Vel . Z ;
2016-03-01 15:47:10 +00:00
if ( mo - > BounceFlags & ( BOUNCE_HereticType | BOUNCE_MBF ) )
{
2016-03-19 23:54:18 +00:00
mo - > Vel . Z - = 2. / dot ;
2016-03-01 15:47:10 +00:00
if ( ! ( mo - > BounceFlags & BOUNCE_MBF ) ) // Heretic projectiles die, MBF projectiles don't.
{
mo - > flags | = MF_INBOUNCE ;
mo - > SetState ( mo - > FindState ( NAME_Death ) ) ;
mo - > flags & = ~ MF_INBOUNCE ;
return false ;
}
else
{
2016-03-24 19:43:35 +00:00
mo - > Vel . Z * = mo - > bouncefactor ;
2016-03-01 15:47:10 +00:00
}
}
else // Don't run through this for MBF-style bounces
{
// The reflected velocity keeps only about 70% of its original speed
2016-03-24 19:43:35 +00:00
mo - > Vel . Z = ( mo - > Vel . Z - 2. / dot ) * mo - > bouncefactor ;
2016-03-01 15:47:10 +00:00
}
mo - > PlayBounceSound ( true ) ;
if ( mo - > BounceFlags & BOUNCE_MBF ) // Bring it to rest below a certain speed
{
2016-03-19 23:54:18 +00:00
if ( fabs ( mo - > Vel . Z ) < mo - > Mass * mo - > GetGravity ( ) / 64 )
mo - > Vel . Z = 0 ;
2016-03-01 15:47:10 +00:00
}
else if ( mo - > BounceFlags & ( BOUNCE_AutoOff | BOUNCE_AutoOffFloorOnly ) )
{
2016-03-19 23:54:18 +00:00
if ( ! ( mo - > flags & MF_NOGRAVITY ) & & ( mo - > Vel . Z < 3. ) )
2016-03-01 15:47:10 +00:00
mo - > BounceFlags & = ~ BOUNCE_TypeMask ;
}
}
return true ;
}
return false ;
}
//============================================================================
//
// Aiming
//
//============================================================================
2016-03-03 12:31:21 +00:00
CVAR ( Bool , aimdebug , false , 0 )
2016-03-02 19:44:02 +00:00
struct AimTarget : public FTranslatedLineTarget
{
2016-03-27 15:58:18 +00:00
DAngle pitch ;
double frac ;
2016-03-02 19:44:02 +00:00
void Clear ( )
{
memset ( this , 0 , sizeof ( * this ) ) ;
2016-03-27 15:58:18 +00:00
frac = FLT_MAX ;
2016-03-02 19:44:02 +00:00
}
} ;
2016-03-01 15:47:10 +00:00
struct aim_t
{
2016-03-02 19:44:02 +00:00
enum
{
aim_up = 1 ,
aim_down = 2
} ;
2016-03-27 15:58:18 +00:00
DAngle aimpitch ;
double attackrange ;
double shootz ; // Height if not aiming up or down
double limitz ; // height limit for portals to avoid bad setups
2016-03-01 15:47:10 +00:00
AActor * shootthing ;
AActor * friender ; // actor to check friendliness again
2016-03-02 19:44:02 +00:00
AActor * aimtarget ; // if we want to aim at precisely this target.
2016-03-01 15:47:10 +00:00
2016-03-27 15:58:18 +00:00
DAngle toppitch , bottompitch ;
2016-03-02 19:44:02 +00:00
AimTarget linetarget ;
AimTarget thing_friend , thing_other ;
2016-03-01 15:47:10 +00:00
int flags ;
sector_t * lastsector ;
secplane_t * lastfloorplane ;
secplane_t * lastceilingplane ;
2016-03-02 19:44:02 +00:00
int aimdir ;
2016-03-27 15:58:18 +00:00
DVector3 startpos ;
DVector2 aimtrace ;
double startfrac ;
2016-03-02 19:44:02 +00:00
2016-03-01 15:47:10 +00:00
bool crossedffloors ;
2016-03-02 19:44:02 +00:00
bool unlinked ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
// Creates a clone of this structure with the basic info copied.
aim_t Clone ( )
{
aim_t cloned ;
cloned . aimtrace = aimtrace ;
cloned . aimpitch = aimpitch ;
cloned . aimtarget = aimtarget ;
cloned . attackrange = attackrange ;
cloned . shootthing = shootthing ;
cloned . friender = friender ;
cloned . shootz = shootz ;
cloned . unlinked = unlinked ;
cloned . flags = flags ;
return cloned ;
}
//============================================================================
//
// SetResult
//
//============================================================================
2016-03-01 15:47:10 +00:00
2016-03-27 15:58:18 +00:00
void SetResult ( AimTarget & res , double frac , AActor * th , DAngle pitch )
2016-03-02 19:44:02 +00:00
{
if ( res . frac > frac )
{
res . linetarget = th ;
res . pitch = pitch ;
2016-03-27 15:58:18 +00:00
res . angleFromSource = ( th - > Pos ( ) - startpos ) . Angle ( ) ;
2016-03-02 19:44:02 +00:00
res . unlinked = unlinked ;
res . frac = frac ;
}
}
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
void SetResult ( AimTarget & res , AimTarget & set )
{
if ( res . frac > set . frac )
{
res = set ;
}
}
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
//============================================================================
//
// Result
//
//============================================================================
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
AimTarget * Result ( )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
AimTarget * result = & linetarget ;
if ( result - > linetarget = = NULL )
{
if ( thing_other . linetarget ! = NULL )
{
result = & thing_other ;
}
else if ( thing_friend . linetarget ! = NULL )
{
result = & thing_friend ;
}
}
return result ;
}
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
//============================================================================
//
// AimTraverse3DFloors
//
//============================================================================
2016-03-27 15:58:18 +00:00
bool AimTraverse3DFloors ( const divline_t & trace , intercept_t * in , int frontflag , int * planestocheck )
2016-03-02 19:44:02 +00:00
{
sector_t * nextsector ;
secplane_t * nexttopplane , * nextbottomplane ;
line_t * li = in - > d . line ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
nextsector = NULL ;
nexttopplane = nextbottomplane = NULL ;
* planestocheck = aimdir ;
if ( li - > backsector = = NULL ) return true ; // shouldn't really happen but crashed once for me...
if ( li - > frontsector - > e - > XFloor . ffloors . Size ( ) | | li - > backsector - > e - > XFloor . ffloors . Size ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
F3DFloor * rover ;
2016-03-27 15:58:18 +00:00
DAngle highpitch , lowpitch ;
2016-03-02 19:44:02 +00:00
2016-03-31 14:52:25 +00:00
double trX = trace . x + trace . dx * in - > frac ;
double trY = trace . y + trace . dy * in - > frac ;
double dist = attackrange * in - > frac ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
// 3D floor check. This is not 100% accurate but normally sufficient when
// combined with a final sight check
for ( int i = 1 ; i < = 2 ; i + + )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
sector_t * s = i = = 1 ? li - > frontsector : li - > backsector ;
for ( unsigned k = 0 ; k < s - > e - > XFloor . ffloors . Size ( ) ; k + + )
{
crossedffloors = true ;
rover = s - > e - > XFloor . ffloors [ k ] ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
if ( ( rover - > flags & FF_SHOOTTHROUGH ) | | ! ( rover - > flags & FF_EXISTS ) ) continue ;
2016-03-01 15:47:10 +00:00
2016-03-27 15:58:18 +00:00
double ff_bottom = rover - > bottom . plane - > ZatPoint ( trX , trY ) ;
double ff_top = rover - > top . plane - > ZatPoint ( trX , trY ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 15:58:18 +00:00
highpitch = - VecToAngle ( dist , ff_top - shootz ) ;
lowpitch = - VecToAngle ( dist , ff_bottom - shootz ) ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
if ( highpitch < = toppitch )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
// blocks completely
if ( lowpitch > = bottompitch ) return false ;
// blocks upper edge of view
if ( lowpitch > toppitch )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
toppitch = lowpitch ;
if ( frontflag ! = i - 1 )
{
nexttopplane = rover - > bottom . plane ;
* planestocheck & = ~ aim_up ;
}
2016-03-01 15:47:10 +00:00
}
}
2016-03-02 19:44:02 +00:00
else if ( lowpitch > = bottompitch )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
// blocks lower edge of view
2016-03-27 15:58:18 +00:00
if ( highpitch < bottompitch )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
bottompitch = highpitch ;
if ( frontflag ! = i - 1 )
{
nextbottomplane = rover - > top . plane ;
* planestocheck & = ~ aim_down ;
}
2016-03-01 15:47:10 +00:00
}
}
2016-03-02 19:44:02 +00:00
// trace is leaving a sector with a 3d-floor
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
if ( frontflag = = i - 1 )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
if ( s = = lastsector )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
// upper slope intersects with this 3d-floor
if ( rover - > bottom . plane = = lastceilingplane & & lowpitch > toppitch )
{
toppitch = lowpitch ;
}
// lower slope intersects with this 3d-floor
if ( rover - > top . plane = = lastfloorplane & & highpitch < bottompitch )
{
bottompitch = highpitch ;
}
2016-03-01 15:47:10 +00:00
}
}
2016-03-02 19:44:02 +00:00
if ( toppitch > = bottompitch ) return false ; // stop
2016-03-01 15:47:10 +00:00
}
}
}
2016-03-02 19:44:02 +00:00
lastsector = nextsector ;
lastceilingplane = nexttopplane ;
lastfloorplane = nextbottomplane ;
return true ;
}
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
//============================================================================
//
// traverses a sector portal
//
//============================================================================
2016-03-01 15:47:10 +00:00
2016-03-27 15:58:18 +00:00
void EnterSectorPortal ( int position , double frac , sector_t * entersec , DAngle newtoppitch , DAngle newbottompitch )
2016-03-02 19:44:02 +00:00
{
2016-04-19 09:35:28 +00:00
double portalz = entersec - > GetPortalPlaneZ ( position ) ;
2016-03-06 20:58:36 +00:00
2016-04-19 09:35:28 +00:00
if ( position = = sector_t : : ceiling & & portalz < limitz ) return ;
else if ( position = = sector_t : : floor & & portalz > limitz ) return ;
2016-03-02 19:44:02 +00:00
aim_t newtrace = Clone ( ) ;
newtrace . toppitch = newtoppitch ;
newtrace . bottompitch = newbottompitch ;
newtrace . aimdir = position = = sector_t : : ceiling ? aim_t : : aim_up : aim_t : : aim_down ;
2016-04-19 09:35:28 +00:00
newtrace . startpos = startpos + entersec - > GetPortalDisplacement ( position ) ;
2016-03-27 15:58:18 +00:00
newtrace . startfrac = frac + 1. / attackrange ; // this is to skip the transition line to the portal which would produce a bogus opening
newtrace . lastsector = P_PointInSector ( newtrace . startpos + aimtrace * newtrace . startfrac ) ;
2016-04-19 09:35:28 +00:00
newtrace . limitz = portalz ;
2016-03-03 12:31:21 +00:00
if ( aimdebug )
Printf ( " -----Entering %s portal from sector %d to sector %d \n " , position ? " ceiling " : " floor " , lastsector - > sectornum , newtrace . lastsector - > sectornum ) ;
2016-03-02 19:44:02 +00:00
newtrace . AimTraverse ( ) ;
SetResult ( linetarget , newtrace . linetarget ) ;
SetResult ( thing_friend , newtrace . thing_friend ) ;
SetResult ( thing_other , newtrace . thing_other ) ;
2016-03-03 12:31:21 +00:00
if ( aimdebug )
Printf ( " -----Exiting %s portal \n " , position ? " ceiling " : " floor " ) ;
2016-03-02 19:44:02 +00:00
}
2016-03-02 23:40:48 +00:00
//============================================================================
//
// traverses a line portal
//
//============================================================================
2016-03-27 15:58:18 +00:00
void EnterLinePortal ( line_t * li , double frac )
2016-03-02 23:40:48 +00:00
{
aim_t newtrace = Clone ( ) ;
FLinePortal * port = li - > getPortal ( ) ;
2016-03-03 19:29:43 +00:00
if ( port - > mType ! = PORTT_LINKED & & ( flags & ALF_PORTALRESTRICT ) ) return ;
2016-03-02 23:40:48 +00:00
newtrace . toppitch = toppitch ;
newtrace . bottompitch = bottompitch ;
newtrace . aimdir = aimdir ;
newtrace . unlinked = ( port - > mType ! = PORTT_LINKED ) ;
newtrace . startpos = startpos ;
newtrace . aimtrace = aimtrace ;
2016-03-27 15:58:18 +00:00
P_TranslatePortalXY ( li , newtrace . startpos . X , newtrace . startpos . Y ) ;
P_TranslatePortalZ ( li , newtrace . startpos . Z ) ;
P_TranslatePortalVXVY ( li , newtrace . aimtrace . X , newtrace . aimtrace . Y ) ;
2016-03-02 23:40:48 +00:00
2016-03-27 15:58:18 +00:00
newtrace . startfrac = frac + 1 / attackrange ; // this is to skip the transition line to the portal which would produce a bogus opening
2016-03-02 23:40:48 +00:00
2016-03-27 15:58:18 +00:00
DVector2 pos = newtrace . startpos + newtrace . aimtrace * newtrace . startfrac ;
2016-03-02 23:40:48 +00:00
2016-03-27 15:58:18 +00:00
newtrace . lastsector = P_PointInSector ( pos ) ;
2016-03-03 10:58:04 +00:00
P_TranslatePortalZ ( li , limitz ) ;
2016-03-03 12:31:21 +00:00
if ( aimdebug )
Printf ( " -----Entering line portal from sector %d to sector %d \n " , lastsector - > sectornum , newtrace . lastsector - > sectornum ) ;
2016-03-02 23:40:48 +00:00
newtrace . AimTraverse ( ) ;
SetResult ( linetarget , newtrace . linetarget ) ;
SetResult ( thing_friend , newtrace . thing_friend ) ;
SetResult ( thing_other , newtrace . thing_other ) ;
}
2016-03-02 19:44:02 +00:00
//============================================================================
//
// PTR_AimTraverse
// Sets linetaget and aimpitch when a target is aimed at.
//
//============================================================================
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
void AimTraverse ( )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
// for smart aiming
linetarget . Clear ( ) ;
thing_friend . Clear ( ) ;
thing_other . Clear ( ) ;
crossedffloors = lastsector - > e - > XFloor . ffloors . Size ( ) ! = 0 ;
lastfloorplane = lastceilingplane = NULL ;
// check the initial sector for 3D-floors and portals
bool ceilingportalstate = ( aimdir & aim_t : : aim_up ) & & toppitch < 0 & & ! lastsector - > PortalBlocksMovement ( sector_t : : ceiling ) ;
bool floorportalstate = ( aimdir & aim_t : : aim_down ) & & bottompitch > 0 & & ! lastsector - > PortalBlocksMovement ( sector_t : : floor ) ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
for ( auto rover : lastsector - > e - > XFloor . ffloors )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
if ( ( rover - > flags & FF_SHOOTTHROUGH ) | | ! ( rover - > flags & FF_EXISTS ) ) continue ;
2016-03-01 15:47:10 +00:00
2016-03-27 15:58:18 +00:00
double bottomz = rover - > bottom . plane - > ZatPoint ( startpos ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 15:58:18 +00:00
if ( bottomz > = startpos . Z + shootthing - > Height )
2016-03-02 19:44:02 +00:00
{
lastceilingplane = rover - > bottom . plane ;
// no ceiling portal if below a 3D floor
ceilingportalstate = false ;
}
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
bottomz = rover - > top . plane - > ZatPoint ( startpos ) ;
2016-03-27 15:58:18 +00:00
if ( bottomz < = startpos . Z )
2016-03-02 19:44:02 +00:00
{
lastfloorplane = rover - > top . plane ;
// no floor portal if above a 3D floor
floorportalstate = false ;
}
}
2016-03-27 15:58:18 +00:00
if ( ceilingportalstate ) EnterSectorPortal ( sector_t : : ceiling , 0 , lastsector , toppitch , MIN < DAngle > ( 0. , bottompitch ) ) ;
if ( floorportalstate ) EnterSectorPortal ( sector_t : : floor , 0 , lastsector , MAX < DAngle > ( 0. , toppitch ) , bottompitch ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 15:58:18 +00:00
FPathTraverse it ( startpos . X , startpos . Y , aimtrace . X , aimtrace . Y , PT_ADDLINES | PT_ADDTHINGS | PT_COMPATIBLE | PT_DELTA , startfrac ) ;
2016-03-02 19:44:02 +00:00
intercept_t * in ;
2016-03-01 15:47:10 +00:00
2016-03-03 12:31:21 +00:00
if ( aimdebug )
2016-03-13 14:14:08 +00:00
Printf ( " Start AimTraverse, start = %f,%f,%f, vect = %f,%f \n " ,
2016-03-27 15:58:18 +00:00
startpos . X / 65536. , startpos . Y / 65536. , startpos . Z / 65536. ,
aimtrace . X / 65536. , aimtrace . Y / 65536. ) ;
2016-03-03 12:31:21 +00:00
2016-03-02 19:44:02 +00:00
while ( ( in = it . Next ( ) ) )
{
line_t * li ;
AActor * th ;
2016-03-27 15:58:18 +00:00
DAngle pitch ;
DAngle thingtoppitch ;
DAngle thingbottompitch ;
double dist ;
DAngle thingpitch ;
2016-03-01 15:47:10 +00:00
2016-03-31 14:52:25 +00:00
if ( linetarget . linetarget ! = NULL & & in - > frac > linetarget . frac ) return ; // we already found something better in another portal section.
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
if ( in - > isaline )
{
li = in - > d . line ;
2016-03-27 15:58:18 +00:00
int frontflag = P_PointOnLineSidePrecise ( startpos , li ) ;
2016-03-01 15:47:10 +00:00
2016-03-03 12:31:21 +00:00
if ( aimdebug )
2016-03-27 15:58:18 +00:00
Printf ( " Found line %d: ___toppitch = %f, ___bottompitch = %f \n " , int ( li - lines ) , toppitch . Degrees , bottompitch . Degrees ) ;
2016-03-01 15:47:10 +00:00
2016-03-02 23:40:48 +00:00
if ( li - > isLinePortal ( ) & & frontflag = = 0 )
{
2016-03-31 14:52:25 +00:00
EnterLinePortal ( li , in - > frac ) ;
2016-03-02 23:40:48 +00:00
return ;
}
2016-03-02 19:44:02 +00:00
if ( ! ( li - > flags & ML_TWOSIDED ) | | ( li - > flags & ML_BLOCKEVERYTHING ) )
return ; // stop
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
// Crosses a two sided line.
// A two sided line will restrict the possible target ranges.
FLineOpening open ;
2016-03-27 15:58:18 +00:00
P_LineOpening ( open , NULL , li , it . InterceptPoint ( in ) , ( DVector2 * ) nullptr , FFCF_NODROPOFF ) ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
// The following code assumes that portals on the front of the line have already been processed.
2016-03-25 17:19:54 +00:00
if ( open . range < = 0 | | open . bottom > = open . top )
2016-03-02 19:44:02 +00:00
return ;
2016-03-31 14:52:25 +00:00
dist = attackrange * in - > frac ;
2016-03-02 19:44:02 +00:00
2016-03-25 17:19:54 +00:00
if ( open . bottom ! = LINEOPEN_MIN )
2016-03-01 15:47:10 +00:00
{
2016-03-27 15:58:18 +00:00
pitch = - VecToAngle ( dist , open . bottom - shootz ) ;
2016-03-02 19:44:02 +00:00
if ( pitch < bottompitch ) bottompitch = pitch ;
}
2016-03-25 17:19:54 +00:00
if ( open . top ! = LINEOPEN_MAX )
2016-03-02 19:44:02 +00:00
{
2016-03-27 15:58:18 +00:00
pitch = - VecToAngle ( dist , open . top - shootz ) ;
2016-03-02 19:44:02 +00:00
if ( pitch > toppitch ) toppitch = pitch ;
2016-03-01 15:47:10 +00:00
}
2016-03-02 19:44:02 +00:00
if ( toppitch > = bottompitch )
return ;
int planestocheck ;
2016-03-27 15:58:18 +00:00
if ( ! AimTraverse3DFloors ( it . Trace ( ) , in , frontflag , & planestocheck ) )
2016-03-02 19:44:02 +00:00
return ;
2016-03-03 12:31:21 +00:00
if ( aimdebug )
2016-03-27 15:58:18 +00:00
Printf ( " After line %d: toppitch = %f, bottompitch = %f, planestocheck = %d \n " , int ( li - lines ) , toppitch . Degrees , bottompitch . Degrees , planestocheck ) ;
2016-03-02 19:44:02 +00:00
sector_t * entersec = frontflag ? li - > frontsector : li - > backsector ;
sector_t * exitsec = frontflag ? li - > backsector : li - > frontsector ;
2016-03-02 21:26:47 +00:00
lastsector = entersec ;
2016-03-02 19:44:02 +00:00
// check portal in backsector when aiming up/downward is possible, the line doesn't have portals on both sides and there's actually a portal in the backsector
2016-03-25 17:19:54 +00:00
if ( ( planestocheck & aim_up ) & & toppitch < 0 & & open . top ! = LINEOPEN_MAX & & ! entersec - > PortalBlocksMovement ( sector_t : : ceiling ) )
2016-03-02 19:44:02 +00:00
{
2016-03-31 14:52:25 +00:00
EnterSectorPortal ( sector_t : : ceiling , in - > frac , entersec , toppitch , MIN < DAngle > ( 0. , bottompitch ) ) ;
2016-03-02 19:44:02 +00:00
}
2016-03-25 17:19:54 +00:00
if ( ( planestocheck & aim_down ) & & bottompitch > 0 & & open . bottom ! = LINEOPEN_MIN & & ! entersec - > PortalBlocksMovement ( sector_t : : floor ) )
2016-03-02 19:44:02 +00:00
{
2016-03-31 14:52:25 +00:00
EnterSectorPortal ( sector_t : : floor , in - > frac , entersec , MAX < DAngle > ( 0. , toppitch ) , bottompitch ) ;
2016-03-02 19:44:02 +00:00
}
continue ; // shot continues
2016-03-01 15:47:10 +00:00
}
2016-03-02 19:44:02 +00:00
// shoot a thing
th = in - > d . thing ;
if ( th = = shootthing )
continue ; // can't shoot self
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
if ( aimtarget ! = NULL & & th ! = aimtarget )
continue ; // only care about target, and you're not it
// If we want to start a conversation anything that has one should be
// found, regardless of other settings.
if ( ! ( flags & ALF_CHECKCONVERSATION ) | | th - > Conversation = = NULL )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
if ( ! ( flags & ALF_CHECKNONSHOOTABLE ) ) // For info CCMD, ignore stuff about GHOST and SHOOTABLE flags
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
if ( ! ( th - > flags & MF_SHOOTABLE ) )
continue ; // corpse or something
// check for physical attacks on a ghost
if ( ( th - > flags3 & MF3_GHOST ) & &
shootthing - > player & & // [RH] Be sure shootthing is a player
shootthing - > player - > ReadyWeapon & &
( shootthing - > player - > ReadyWeapon - > flags2 & MF2_THRUGHOST ) )
{
continue ;
}
2016-03-01 15:47:10 +00:00
}
}
2016-03-31 14:52:25 +00:00
dist = attackrange * in - > frac ;
2016-03-02 19:44:02 +00:00
// Don't autoaim certain special actors
if ( ! cl_doautoaim & & th - > flags6 & MF6_NOTAUTOAIMED )
{
continue ;
}
// we must do one last check whether the trace has crossed a 3D floor
if ( lastsector = = th - > Sector & & th - > Sector - > e - > XFloor . ffloors . Size ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
if ( lastceilingplane )
2016-03-01 15:47:10 +00:00
{
2016-03-30 07:41:46 +00:00
double ff_top = lastceilingplane - > ZatPoint ( th ) ;
2016-03-27 15:58:18 +00:00
DAngle pitch = - VecToAngle ( dist , ff_top - shootz ) ;
2016-03-02 19:44:02 +00:00
// upper slope intersects with this 3d-floor
if ( pitch > toppitch )
{
toppitch = pitch ;
}
}
if ( lastfloorplane )
{
2016-03-30 07:41:46 +00:00
double ff_bottom = lastfloorplane - > ZatPoint ( th ) ;
2016-03-27 15:58:18 +00:00
DAngle pitch = - VecToAngle ( dist , ff_bottom - shootz ) ;
2016-03-02 19:44:02 +00:00
// lower slope intersects with this 3d-floor
if ( pitch < bottompitch )
{
bottompitch = pitch ;
}
2016-03-01 15:47:10 +00:00
}
}
2016-03-02 19:44:02 +00:00
// check angles to see if the thing can be aimed at
2016-03-01 15:47:10 +00:00
2016-03-27 15:58:18 +00:00
thingtoppitch = - VecToAngle ( dist , th - > Top ( ) - shootz ) ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
if ( thingtoppitch > bottompitch )
continue ; // shot over the thing
2016-03-01 15:47:10 +00:00
2016-03-27 15:58:18 +00:00
thingbottompitch = - VecToAngle ( dist , th - > Z ( ) - shootz ) ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
if ( thingbottompitch < toppitch )
continue ; // shot under the thing
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
if ( crossedffloors )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
// if 3D floors were in the way do an extra visibility check for safety
2016-03-02 23:40:48 +00:00
if ( ! unlinked & & ! P_CheckSight ( shootthing , th , SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY ) )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
// the thing can't be seen so we can safely exclude its range from our aiming field
if ( thingtoppitch < toppitch )
{
if ( thingbottompitch > toppitch ) toppitch = thingbottompitch ;
}
else if ( thingbottompitch > bottompitch )
{
if ( thingtoppitch < bottompitch ) bottompitch = thingtoppitch ;
}
if ( toppitch < bottompitch ) continue ;
else return ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-02 19:44:02 +00:00
// this thing can be hit!
if ( thingtoppitch < toppitch )
thingtoppitch = toppitch ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
if ( thingbottompitch > bottompitch )
thingbottompitch = bottompitch ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
thingpitch = thingtoppitch / 2 + thingbottompitch / 2 ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
if ( flags & ALF_CHECK3D )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
// We need to do a 3D distance check here because this is nearly always used in
// combination with P_LineAttack. P_LineAttack uses 3D distance but FPathTraverse
// only 2D. This causes some problems with Hexen's weapons that use different
// attack modes based on distance to target
2016-03-27 15:58:18 +00:00
double cosine = thingpitch . Cos ( ) ;
2016-03-02 19:44:02 +00:00
if ( cosine ! = 0 )
2016-03-01 15:47:10 +00:00
{
2016-03-27 15:58:18 +00:00
double tracelen = DVector2 ( it . Trace ( ) . dx , it . Trace ( ) . dy ) . Length ( ) ;
2016-03-31 14:52:25 +00:00
double d3 = tracelen * in - > frac / cosine ;
2016-03-02 19:44:02 +00:00
if ( d3 > attackrange )
{
return ;
}
2016-03-01 15:47:10 +00:00
}
}
2016-03-02 19:44:02 +00:00
if ( ( flags & ALF_NOFRIENDS ) & & th - > IsFriend ( friender ) & & aimtarget = = NULL )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
continue ;
2016-03-01 15:47:10 +00:00
}
2016-03-02 19:44:02 +00:00
else if ( sv_smartaim ! = 0 & & ! ( flags & ALF_FORCENOSMART ) & & aimtarget = = NULL )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
// try to be a little smarter about what to aim at!
// In particular avoid autoaiming at friends and barrels.
if ( th - > IsFriend ( friender ) )
2016-03-01 15:47:10 +00:00
{
2016-03-02 19:44:02 +00:00
if ( sv_smartaim < 2 )
{
// friends don't aim at friends (except players), at least not first
2016-03-05 00:23:27 +00:00
if ( aimdebug )
2016-03-19 23:54:18 +00:00
Printf ( " Hit friend %s at %f,%f,%f \n " , th - > GetClass ( ) - > TypeName . GetChars ( ) , th - > X ( ) , th - > Y ( ) , th - > Z ( ) ) ;
2016-03-31 14:52:25 +00:00
SetResult ( thing_friend , in - > frac , th , thingpitch ) ;
2016-03-02 19:44:02 +00:00
}
}
else if ( ! ( th - > flags3 & MF3_ISMONSTER ) & & th - > player = = NULL )
{
if ( sv_smartaim < 3 )
{
// don't autoaim at barrels and other shootable stuff unless no monsters have been found
2016-03-03 12:31:21 +00:00
if ( aimdebug )
2016-03-19 23:54:18 +00:00
Printf ( " Hit other %s at %f,%f,%f \n " , th - > GetClass ( ) - > TypeName . GetChars ( ) , th - > X ( ) , th - > Y ( ) , th - > Z ( ) ) ;
2016-03-31 14:52:25 +00:00
SetResult ( thing_other , in - > frac , th , thingpitch ) ;
2016-03-02 19:44:02 +00:00
}
}
else
{
2016-03-03 12:31:21 +00:00
if ( aimdebug )
2016-03-19 23:54:18 +00:00
Printf ( " Hit target %s at %f,%f,%f \n " , th - > GetClass ( ) - > TypeName . GetChars ( ) , th - > X ( ) , th - > Y ( ) , th - > Z ( ) ) ;
2016-03-31 14:52:25 +00:00
SetResult ( linetarget , in - > frac , th , thingpitch ) ;
2016-03-02 19:44:02 +00:00
return ;
2016-03-01 15:47:10 +00:00
}
}
else
{
2016-03-03 12:31:21 +00:00
if ( aimdebug )
2016-03-19 23:54:18 +00:00
Printf ( " Hit target %s at %f,%f,%f \n " , th - > GetClass ( ) - > TypeName . GetChars ( ) , th - > X ( ) , th - > Y ( ) , th - > Z ( ) ) ;
2016-03-31 14:52:25 +00:00
SetResult ( linetarget , in - > frac , th , thingpitch ) ;
2016-03-01 15:47:10 +00:00
return ;
}
}
}
2016-03-02 19:44:02 +00:00
} ;
2016-03-01 15:47:10 +00:00
//============================================================================
//
// P_AimLineAttack
//
//============================================================================
2016-03-17 10:38:56 +00:00
DAngle P_AimLineAttack ( AActor * t1 , DAngle angle , double distance , FTranslatedLineTarget * pLineTarget , DAngle vrange ,
2016-03-01 15:47:10 +00:00
int flags , AActor * target , AActor * friender )
{
2016-03-27 15:58:18 +00:00
double shootz = t1 - > Center ( ) - t1 - > Floorclip ;
2016-03-01 15:47:10 +00:00
if ( t1 - > player ! = NULL )
{
2016-03-27 15:58:18 +00:00
shootz + = t1 - > player - > mo - > AttackZOffset * t1 - > player - > crouchfactor ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-27 15:58:18 +00:00
shootz + = 8 ;
2016-03-01 15:47:10 +00:00
}
// can't shoot outside view angles
if ( vrange = = 0 )
{
if ( t1 - > player = = NULL | | ! level . IsFreelookAllowed ( ) )
{
2016-03-17 10:38:56 +00:00
vrange = 35. ;
2016-03-01 15:47:10 +00:00
}
else
{
// [BB] Disable autoaim on weapons with WIF_NOAUTOAIM.
AWeapon * weapon = t1 - > player - > ReadyWeapon ;
if ( weapon & & ( weapon - > WeaponFlags & WIF_NOAUTOAIM ) )
{
2016-03-17 10:38:56 +00:00
vrange = 0.5 ;
2016-03-01 15:47:10 +00:00
}
else
{
// 35 degrees is approximately what Doom used. You cannot have a
// vrange of 0 degrees, because then toppitch and bottompitch will
// be equal, and PTR_AimTraverse will never find anything to shoot at
// if it crosses a line.
2016-03-17 10:38:56 +00:00
vrange = clamp ( t1 - > player - > userinfo . GetAimDist ( ) , 0.5 , 35. ) ;
2016-03-01 15:47:10 +00:00
}
}
}
2016-03-02 19:44:02 +00:00
aim_t aim ;
aim . flags = flags ;
aim . shootthing = t1 ;
aim . friender = ( friender = = NULL ) ? t1 : friender ;
aim . aimdir = aim_t : : aim_up | aim_t : : aim_down ;
2016-03-27 15:58:18 +00:00
aim . startpos = t1 - > Pos ( ) ;
aim . aimtrace = angle . ToVector ( distance ) ;
2016-03-02 19:44:02 +00:00
aim . limitz = aim . shootz = shootz ;
2016-03-27 15:58:18 +00:00
aim . toppitch = t1 - > Angles . Pitch - vrange ;
aim . bottompitch = t1 - > Angles . Pitch + vrange ;
aim . attackrange = distance ;
aim . aimpitch = t1 - > Angles . Pitch ;
2016-03-01 15:47:10 +00:00
aim . lastsector = t1 - > Sector ;
2016-03-02 19:44:02 +00:00
aim . startfrac = 0 ;
aim . unlinked = false ;
aim . aimtarget = target ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
aim . AimTraverse ( ) ;
2016-03-01 15:47:10 +00:00
2016-03-02 19:44:02 +00:00
AimTarget * result = aim . Result ( ) ;
2016-03-01 15:47:10 +00:00
if ( pLineTarget )
{
2016-03-02 19:44:02 +00:00
* pLineTarget = * result ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 15:58:18 +00:00
return result - > linetarget ? result - > pitch : t1 - > Angles . Pitch ;
2016-03-01 15:47:10 +00:00
}
2016-10-26 09:30:30 +00:00
DEFINE_ACTION_FUNCTION ( AActor , AimLineAttack )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_ANGLE ( angle ) ;
PARAM_FLOAT ( distance ) ;
2016-10-27 15:47:46 +00:00
PARAM_POINTER_DEF ( pLineTarget , FTranslatedLineTarget ) ;
PARAM_ANGLE_DEF ( vrange ) ;
PARAM_INT_DEF ( flags ) ;
PARAM_OBJECT_DEF ( target , AActor ) ;
PARAM_OBJECT_DEF ( friender , AActor ) ;
2016-10-26 09:30:30 +00:00
ACTION_RETURN_FLOAT ( P_AimLineAttack ( self , angle , distance , pLineTarget , vrange , flags , target , friender ) . Degrees ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
2016-03-06 20:58:36 +00:00
// Helper stuff for P_LineAttack
2016-03-01 15:47:10 +00:00
//
//==========================================================================
struct Origin
{
AActor * Caller ;
2016-04-23 15:16:37 +00:00
FNameNoInit PuffSpecies ;
2016-03-01 15:47:10 +00:00
bool hitGhosts ;
2016-04-23 15:16:37 +00:00
bool MThruSpecies ;
bool ThruSpecies ;
bool ThruActors ;
2016-03-01 15:47:10 +00:00
} ;
static ETraceStatus CheckForActor ( FTraceResults & res , void * userdata )
{
if ( res . HitType ! = TRACE_HitActor )
{
return TRACE_Stop ;
}
Origin * data = ( Origin * ) userdata ;
2016-04-23 15:16:37 +00:00
// Skip actors if the puff has:
// 1. THRUACTORS or SPECTRAL
// 2. MTHRUSPECIES on puff and the shooter has same species as the hit actor
// 3. THRUSPECIES on puff and the puff has same species as the hit actor
// 4. THRUGHOST on puff and the GHOST flag on the hit actor
2016-03-01 15:47:10 +00:00
2016-04-23 15:16:37 +00:00
if ( ( data - > ThruActors ) | | ( res . Actor - > flags4 & MF4_SPECTRAL ) | |
( data - > MThruSpecies & & res . Actor - > GetSpecies ( ) = = data - > Caller - > GetSpecies ( ) ) | |
( data - > ThruSpecies & & res . Actor - > GetSpecies ( ) = = data - > PuffSpecies ) | |
( data - > hitGhosts & & res . Actor - > flags3 & MF3_GHOST ) )
2016-03-01 15:47:10 +00:00
{
return TRACE_Skip ;
}
return TRACE_Stop ;
}
//==========================================================================
//
// P_LineAttack
//
// if damage == 0, it is just a test trace that will leave linetarget set
//
//==========================================================================
2016-03-17 10:38:56 +00:00
AActor * P_LineAttack ( AActor * t1 , DAngle angle , double distance ,
DAngle pitch , int damage , FName damageType , PClassActor * pufftype , int flags , FTranslatedLineTarget * victim , int * actualdamage )
2016-03-01 15:47:10 +00:00
{
2016-06-23 00:34:14 +00:00
bool nointeract = ! ! ( flags & LAF_NOINTERACT ) ;
2016-03-27 18:58:01 +00:00
DVector3 direction ;
double shootz ;
2016-03-01 15:47:10 +00:00
FTraceResults trace ;
Origin TData ;
TData . Caller = t1 ;
bool killPuff = false ;
AActor * puff = NULL ;
int pflag = 0 ;
int puffFlags = ( flags & LAF_ISMELEEATTACK ) ? PF_MELEERANGE : 0 ;
2016-05-07 18:10:08 +00:00
bool spawnSky = false ;
2016-03-01 15:47:10 +00:00
if ( flags & LAF_NORANDOMPUFFZ )
puffFlags | = PF_NORANDOMZ ;
if ( victim ! = NULL )
{
memset ( victim , 0 , sizeof ( * victim ) ) ;
}
if ( actualdamage ! = NULL )
{
* actualdamage = 0 ;
}
2016-03-17 10:38:56 +00:00
double pc = pitch . Cos ( ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 18:58:01 +00:00
direction = { pc * angle . Cos ( ) , pc * angle . Sin ( ) , - pitch . Sin ( ) } ;
shootz = t1 - > Center ( ) - t1 - > Floorclip ;
2016-03-01 15:47:10 +00:00
if ( t1 - > player ! = NULL )
{
2016-03-27 18:58:01 +00:00
shootz + = t1 - > player - > mo - > AttackZOffset * t1 - > player - > crouchfactor ;
2016-03-01 15:47:10 +00:00
if ( damageType = = NAME_Melee | | damageType = = NAME_Hitscan )
{
// this is coming from a weapon attack function which needs to transfer information to the obituary code,
// We need to preserve this info from the damage type because the actual damage type can get overridden by the puff
pflag = DMG_PLAYERATTACK ;
}
}
else
{
2016-03-27 18:58:01 +00:00
shootz + = 8 ;
2016-03-01 15:47:10 +00:00
}
// We need to check the defaults of the replacement here
AActor * puffDefaults = GetDefaultByType ( pufftype - > GetReplacement ( ) ) ;
2016-04-23 15:16:37 +00:00
2016-03-01 15:47:10 +00:00
TData . hitGhosts = ( t1 - > player ! = NULL & &
t1 - > player - > ReadyWeapon ! = NULL & &
( t1 - > player - > ReadyWeapon - > flags2 & MF2_THRUGHOST ) ) | |
( puffDefaults & & ( puffDefaults - > flags2 & MF2_THRUGHOST ) ) ;
2016-05-07 18:10:08 +00:00
spawnSky = ( puffDefaults & & ( puffDefaults - > flags3 & MF3_SKYEXPLODE ) ) ;
2016-04-23 15:16:37 +00:00
TData . MThruSpecies = ( puffDefaults & & ( puffDefaults - > flags6 & MF6_MTHRUSPECIES ) ) ;
TData . PuffSpecies = NAME_None ;
2016-03-01 15:47:10 +00:00
2016-04-23 15:16:37 +00:00
// [MC] To prevent possible mod breakage, this flag is pretty much necessary.
// Somewhere, someone is relying on these to spawn on actors and move through them.
if ( ( puffDefaults - > flags7 & MF7_ALLOWTHRUFLAGS ) )
{
TData . ThruSpecies = ( puffDefaults & & ( puffDefaults - > flags6 & MF6_THRUSPECIES ) ) ;
TData . ThruActors = ( puffDefaults & & ( puffDefaults - > flags2 & MF2_THRUACTORS ) ) ;
// [MC] Because this is a one-hit trace event, we need to spawn the puff, get the species
// and destroy it. Assume there is no species unless tempuff isn't NULL. We cannot get
// a proper species the same way as puffDefaults flags it appears...
AActor * tempuff = NULL ;
if ( pufftype ! = NULL )
tempuff = Spawn ( pufftype , t1 - > Pos ( ) , ALLOW_REPLACE ) ;
if ( tempuff ! = NULL )
{
TData . PuffSpecies = tempuff - > GetSpecies ( ) ;
tempuff - > Destroy ( ) ;
}
}
else
{
TData . ThruSpecies = false ;
TData . ThruActors = false ;
}
2016-03-01 15:47:10 +00:00
// if the puff uses a non-standard damage type, this will override default, hitscan and melee damage type.
// All other explicitly passed damage types (currenty only MDK) will be preserved.
if ( ( damageType = = NAME_None | | damageType = = NAME_Melee | | damageType = = NAME_Hitscan ) & &
puffDefaults ! = NULL & & puffDefaults - > DamageType ! = NAME_None )
{
damageType = puffDefaults - > DamageType ;
}
int tflags ;
2016-06-19 15:59:00 +00:00
if ( nointeract | | ( puffDefaults & & puffDefaults - > flags6 & MF6_NOTRIGGER ) ) tflags = TRACE_NoSky ;
2016-03-01 15:47:10 +00:00
else tflags = TRACE_NoSky | TRACE_Impact ;
2016-03-27 18:58:01 +00:00
if ( ! Trace ( t1 - > PosAtZ ( shootz ) , t1 - > Sector , direction , distance , MF_SHOOTABLE ,
ML_BLOCKEVERYTHING | ML_BLOCKHITSCAN , t1 , trace , tflags , CheckForActor , & TData ) )
2016-03-01 15:47:10 +00:00
{ // hit nothing
2016-06-19 15:59:00 +00:00
if ( ! nointeract & & puffDefaults & & puffDefaults - > ActiveSound )
2016-03-01 15:47:10 +00:00
{ // Play miss sound
S_Sound ( t1 , CHAN_WEAPON , puffDefaults - > ActiveSound , 1 , ATTN_NORM ) ;
}
2016-06-19 15:59:00 +00:00
// [MC] LAF_NOINTERACT guarantees puff spawning and returns it directly to the calling function.
// No damage caused, no sounds played, no blood splatters.
if ( nointeract | | ( puffDefaults & & puffDefaults - > flags3 & MF3_ALWAYSPUFF ) )
2016-03-01 15:47:10 +00:00
{ // Spawn the puff anyway
2016-03-23 17:07:04 +00:00
puff = P_SpawnPuff ( t1 , pufftype , trace . HitPos , trace . SrcAngleFromTarget , trace . SrcAngleFromTarget , 2 , puffFlags ) ;
2016-06-19 15:59:00 +00:00
if ( nointeract )
{
return puff ;
}
2016-03-01 15:47:10 +00:00
}
else
{
2016-06-19 15:59:00 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
}
else
{
if ( trace . HitType ! = TRACE_HitActor )
{
// position a bit closer for puffs
2016-06-19 15:59:00 +00:00
if ( nointeract | | trace . HitType ! = TRACE_HitWall | | ( ( trace . Line - > special ! = Line_Horizon ) | | spawnSky ) )
2016-03-01 15:47:10 +00:00
{
2016-03-23 17:07:04 +00:00
DVector2 pos = P_GetOffsetPosition ( trace . HitPos . X , trace . HitPos . Y , - trace . HitVector . X * 4 , - trace . HitVector . Y * 4 ) ;
puff = P_SpawnPuff ( t1 , pufftype , DVector3 ( pos , trace . HitPos . Z - trace . HitVector . Z * 4 ) , trace . SrcAngleFromTarget ,
trace . SrcAngleFromTarget - 90 , 0 , puffFlags ) ;
2016-03-20 14:04:13 +00:00
puff - > radius = 1 / 65536. ;
2016-06-19 15:59:00 +00:00
if ( nointeract )
{
return puff ;
}
2016-03-01 15:47:10 +00:00
}
// [RH] Spawn a decal
2016-03-06 20:58:36 +00:00
if ( trace . HitType = = TRACE_HitWall & & trace . Line - > special ! = Line_Horizon & & ! trace . Line - > isVisualPortal ( ) & & ! ( flags & LAF_NOIMPACTDECAL ) & & ! ( puffDefaults - > flags7 & MF7_NODECAL ) )
2016-03-01 15:47:10 +00:00
{
// [TN] If the actor or weapon has a decal defined, use that one.
if ( t1 - > DecalGenerator ! = NULL | |
( t1 - > player ! = NULL & & t1 - > player - > ReadyWeapon ! = NULL & & t1 - > player - > ReadyWeapon - > DecalGenerator ! = NULL ) )
{
// [ZK] If puff has FORCEDECAL set, do not use the weapon's decal
if ( puffDefaults - > flags7 & MF7_FORCEDECAL & & puff ! = NULL & & puff - > DecalGenerator )
SpawnShootDecal ( puff , trace ) ;
else
SpawnShootDecal ( t1 , trace ) ;
}
// Else, look if the bulletpuff has a decal defined.
else if ( puff ! = NULL & & puff - > DecalGenerator )
{
SpawnShootDecal ( puff , trace ) ;
}
else
{
SpawnShootDecal ( t1 , trace ) ;
}
}
else if ( puff ! = NULL & &
trace . CrossedWater = = NULL & &
trace . Sector - > heightsec = = NULL & &
trace . HitType = = TRACE_HitFloor )
{
2016-03-06 20:58:36 +00:00
P_HitWater ( puff , trace . Sector , trace . HitPos ) ;
2016-03-01 15:47:10 +00:00
}
}
else
{
// Hit a thing, so it could be either a puff or blood
2016-03-23 17:07:04 +00:00
DVector3 bleedpos = trace . HitPos ;
2016-03-01 15:47:10 +00:00
// position a bit closer for puffs/blood if using compatibility mode.
2016-03-06 20:58:36 +00:00
if ( i_compatflags & COMPATF_HITSCAN )
{
2016-03-23 17:07:04 +00:00
DVector2 ofs = P_GetOffsetPosition ( bleedpos . X , bleedpos . Y , - 10 * trace . HitVector . X , - 10 * trace . HitVector . Y ) ;
bleedpos . X = ofs . X ;
bleedpos . Y = ofs . Y ;
bleedpos . Z - = - 10 * trace . HitVector . Z ;
2016-03-06 20:58:36 +00:00
}
2016-03-01 15:47:10 +00:00
// Spawn bullet puffs or blood spots, depending on target type.
2016-06-19 15:59:00 +00:00
if ( nointeract | | ( puffDefaults & & puffDefaults - > flags3 & MF3_PUFFONACTORS ) | |
2016-03-01 15:47:10 +00:00
( trace . Actor - > flags & MF_NOBLOOD ) | |
( trace . Actor - > flags2 & ( MF2_INVULNERABLE | MF2_DORMANT ) ) )
{
if ( ! ( trace . Actor - > flags & MF_NOBLOOD ) )
puffFlags | = PF_HITTHINGBLEED ;
// We must pass the unreplaced puff type here
2016-03-23 17:07:04 +00:00
puff = P_SpawnPuff ( t1 , pufftype , bleedpos , trace . SrcAngleFromTarget , trace . SrcAngleFromTarget - 90 , 2 , puffFlags | PF_HITTHING , trace . Actor ) ;
2016-06-19 15:59:00 +00:00
if ( nointeract )
{
return puff ;
}
2016-03-01 15:47:10 +00:00
}
// Allow puffs to inflict poison damage, so that hitscans can poison, too.
if ( puffDefaults ! = NULL & & puffDefaults - > PoisonDamage > 0 & & puffDefaults - > PoisonDuration ! = INT_MIN )
{
P_PoisonMobj ( trace . Actor , puff ? puff : t1 , t1 , puffDefaults - > PoisonDamage , puffDefaults - > PoisonDuration , puffDefaults - > PoisonPeriod , puffDefaults - > PoisonDamageType ) ;
}
// [GZ] If MF6_FORCEPAIN is set, we need to call P_DamageMobj even if damage is 0!
// Note: The puff may not yet be spawned here so we must check the class defaults, not the actor.
int newdam = damage ;
if ( damage | | ( puffDefaults ! = NULL & & ( ( puffDefaults - > flags6 & MF6_FORCEPAIN ) | | ( puffDefaults - > flags7 & MF7_CAUSEPAIN ) ) ) )
{
int dmgflags = DMG_INFLICTOR_IS_PUFF | pflag ;
// Allow MF5_PIERCEARMOR on a weapon as well.
if ( t1 - > player ! = NULL & & ( dmgflags & DMG_PLAYERATTACK ) & & t1 - > player - > ReadyWeapon ! = NULL & &
t1 - > player - > ReadyWeapon - > flags5 & MF5_PIERCEARMOR )
{
dmgflags | = DMG_NO_ARMOR ;
}
if ( puff = = NULL )
{
// Since the puff is the damage inflictor we need it here
// regardless of whether it is displayed or not.
2016-03-23 17:07:04 +00:00
puff = P_SpawnPuff ( t1 , pufftype , bleedpos , 0. , 0. , 2 , puffFlags | PF_HITTHING | PF_TEMPORARY ) ;
2016-03-01 15:47:10 +00:00
killPuff = true ;
}
2016-03-23 17:07:04 +00:00
newdam = P_DamageMobj ( trace . Actor , puff ? puff : t1 , t1 , damage , damageType , dmgflags | DMG_USEANGLE , trace . SrcAngleFromTarget ) ;
2016-03-01 15:47:10 +00:00
if ( actualdamage ! = NULL )
{
* actualdamage = newdam ;
}
}
if ( ! ( puffDefaults ! = NULL & & puffDefaults - > flags3 & MF3_BLOODLESSIMPACT ) )
{
2016-06-19 15:59:00 +00:00
bool bloodsplatter = ( t1 - > flags5 & MF5_BLOODSPLATTER ) | |
( t1 - > player ! = nullptr & & t1 - > player - > ReadyWeapon ! = nullptr & &
( t1 - > player - > ReadyWeapon - > WeaponFlags & WIF_AXEBLOOD ) ) ;
bool axeBlood = ( t1 - > player ! = nullptr & &
t1 - > player - > ReadyWeapon ! = nullptr & &
( t1 - > player - > ReadyWeapon - > WeaponFlags & WIF_AXEBLOOD ) ) ;
2016-03-01 15:47:10 +00:00
if ( ! bloodsplatter & & ! axeBlood & &
! ( trace . Actor - > flags & MF_NOBLOOD ) & &
! ( trace . Actor - > flags2 & ( MF2_INVULNERABLE | MF2_DORMANT ) ) )
{
2016-03-23 17:07:04 +00:00
P_SpawnBlood ( bleedpos , trace . SrcAngleFromTarget , newdam > 0 ? newdam : damage , trace . Actor ) ;
2016-03-01 15:47:10 +00:00
}
if ( damage )
{
if ( bloodsplatter | | axeBlood )
{
if ( ! ( trace . Actor - > flags & MF_NOBLOOD ) & &
! ( trace . Actor - > flags2 & ( MF2_INVULNERABLE | MF2_DORMANT ) ) )
{
if ( axeBlood )
{
2016-03-23 17:07:04 +00:00
P_BloodSplatter2 ( bleedpos , trace . Actor , trace . SrcAngleFromTarget ) ;
2016-03-01 15:47:10 +00:00
}
if ( pr_lineattack ( ) < 192 )
{
2016-03-23 17:07:04 +00:00
P_BloodSplatter ( bleedpos , trace . Actor , trace . SrcAngleFromTarget ) ;
2016-03-01 15:47:10 +00:00
}
}
}
// [RH] Stick blood to walls
2016-03-27 18:58:01 +00:00
P_TraceBleed ( newdam > 0 ? newdam : damage , trace . HitPos , trace . Actor , trace . SrcAngleFromTarget , pitch ) ;
2016-03-01 15:47:10 +00:00
}
}
if ( victim ! = NULL )
{
victim - > linetarget = trace . Actor ;
2016-03-23 17:07:04 +00:00
victim - > angleFromSource = trace . SrcAngleFromTarget ;
2016-03-06 20:58:36 +00:00
victim - > unlinked = trace . unlinked ;
2016-03-01 15:47:10 +00:00
}
}
if ( trace . Crossed3DWater | | trace . CrossedWater )
{
if ( puff = = NULL )
{ // Spawn puff just to get a mass for the splash
2016-03-23 17:07:04 +00:00
puff = P_SpawnPuff ( t1 , pufftype , trace . HitPos , 0. , 0. , 2 , puffFlags | PF_HITTHING | PF_TEMPORARY ) ;
2016-03-01 15:47:10 +00:00
killPuff = true ;
}
2016-03-06 20:58:36 +00:00
SpawnDeepSplash ( t1 , trace , puff ) ;
2016-03-01 15:47:10 +00:00
}
}
if ( killPuff & & puff ! = NULL )
{
puff - > Destroy ( ) ;
puff = NULL ;
}
return puff ;
}
2016-03-17 10:38:56 +00:00
AActor * P_LineAttack ( AActor * t1 , DAngle angle , double distance ,
DAngle pitch , int damage , FName damageType , FName pufftype , int flags , FTranslatedLineTarget * victim , int * actualdamage )
2016-03-01 15:47:10 +00:00
{
PClassActor * type = PClass : : FindActor ( pufftype ) ;
if ( type = = NULL )
{
if ( victim ! = NULL )
{
memset ( victim , 0 , sizeof ( * victim ) ) ;
}
Printf ( " Attempt to spawn unknown actor type '%s' \n " , pufftype . GetChars ( ) ) ;
return NULL ;
}
else
{
return P_LineAttack ( t1 , angle , distance , pitch , damage , damageType , type , flags , victim , actualdamage ) ;
}
}
2016-10-26 09:30:30 +00:00
DEFINE_ACTION_FUNCTION ( AActor , LineAttack )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_ANGLE ( angle ) ;
PARAM_FLOAT ( distance ) ;
PARAM_ANGLE ( pitch ) ;
PARAM_INT ( damage ) ;
PARAM_NAME ( damageType ) ;
PARAM_CLASS ( puffType , AActor ) ;
2016-10-27 15:47:46 +00:00
PARAM_INT_DEF ( flags ) ;
PARAM_POINTER_DEF ( victim , FTranslatedLineTarget ) ;
2016-10-26 09:30:30 +00:00
int acdmg ;
2016-12-02 11:06:49 +00:00
if ( puffType = = nullptr ) puffType = PClass : : FindActor ( " BulletPuff " ) ; // P_LineAttack does not work without a puff to take info from.
2016-10-26 09:30:30 +00:00
auto puff = P_LineAttack ( self , angle , distance , pitch , damage , damageType , puffType , flags , victim , & acdmg ) ;
if ( numret > 0 ) ret [ 0 ] . SetPointer ( puff , ATAG_OBJECT ) ;
if ( numret > 1 ) ret [ 1 ] . SetInt ( acdmg ) , numret = 2 ;
return numret ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// P_LinePickActor
//
//==========================================================================
2016-03-27 18:58:01 +00:00
AActor * P_LinePickActor ( AActor * t1 , DAngle angle , double distance , DAngle pitch , ActorFlags actorMask , DWORD wallMask )
2016-03-01 15:47:10 +00:00
{
2016-03-27 18:58:01 +00:00
DVector3 direction ;
double shootz ;
2016-03-01 15:47:10 +00:00
2016-03-27 18:58:01 +00:00
double pc = pitch . Cos ( ) ;
direction = { pc * angle . Cos ( ) , pc * angle . Sin ( ) , - pitch . Sin ( ) } ;
shootz = t1 - > Center ( ) - t1 - > Floorclip ;
2016-03-01 15:47:10 +00:00
if ( t1 - > player ! = NULL )
{
2016-03-27 18:58:01 +00:00
shootz + = t1 - > player - > mo - > AttackZOffset * t1 - > player - > crouchfactor ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-27 18:58:01 +00:00
shootz + = 8 ;
2016-03-01 15:47:10 +00:00
}
FTraceResults trace ;
Origin TData ;
TData . Caller = t1 ;
TData . hitGhosts = true ;
2016-08-09 08:11:13 +00:00
TData . MThruSpecies = false ;
TData . ThruActors = false ;
TData . ThruSpecies = false ;
2016-03-01 15:47:10 +00:00
2016-03-27 18:58:01 +00:00
if ( Trace ( t1 - > PosAtZ ( shootz ) , t1 - > Sector , direction , distance ,
2016-03-03 15:33:42 +00:00
actorMask , wallMask , t1 , trace , TRACE_NoSky | TRACE_PortalRestrict , CheckForActor , & TData ) )
2016-03-01 15:47:10 +00:00
{
if ( trace . HitType = = TRACE_HitActor )
{
return trace . Actor ;
}
}
return NULL ;
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-27 18:58:01 +00:00
void P_TraceBleed ( int damage , const DVector3 & pos , AActor * actor , DAngle angle , DAngle pitch )
2016-03-01 15:47:10 +00:00
{
if ( ! cl_bloodsplats )
return ;
const char * bloodType = " BloodSplat " ;
int count ;
2016-03-27 18:58:01 +00:00
double noise ;
2016-03-01 15:47:10 +00:00
if ( ( actor - > flags & MF_NOBLOOD ) | |
( actor - > flags5 & MF5_NOBLOODDECALS ) | |
( actor - > flags2 & ( MF2_INVULNERABLE | MF2_DORMANT ) ) | |
( actor - > player & & actor - > player - > cheats & CF_GODMODE ) )
{
return ;
}
2016-03-27 18:58:01 +00:00
2016-03-01 15:47:10 +00:00
if ( damage < 15 )
{ // For low damages, there is a chance to not spray blood at all
if ( damage < = 10 )
{
if ( pr_tracebleed ( ) < 160 )
{
return ;
}
}
count = 1 ;
2016-03-27 18:58:01 +00:00
noise = 11.25 / 256. ;
2016-03-01 15:47:10 +00:00
}
else if ( damage < 25 )
{
count = 2 ;
2016-03-27 18:58:01 +00:00
noise = 22.5 / 256. ;
2016-03-01 15:47:10 +00:00
}
else
{ // For high damages, there is a chance to spray just one big glob of blood
if ( pr_tracebleed ( ) < 24 )
{
bloodType = " BloodSmear " ;
count = 1 ;
2016-03-27 18:58:01 +00:00
noise = 45. / 256. ;
2016-03-01 15:47:10 +00:00
}
else
{
count = 3 ;
2016-03-27 18:58:01 +00:00
noise = 45. / 256. ;
2016-03-01 15:47:10 +00:00
}
}
for ( ; count ; - - count )
{
FTraceResults bleedtrace ;
2016-03-27 18:58:01 +00:00
DAngle bleedang = angle + ( pr_tracebleed ( ) - 128 ) * noise ;
DAngle bleedpitch = pitch + ( pr_tracebleed ( ) - 128 ) * noise ;
double cosp = bleedpitch . Cos ( ) ;
DVector3 vdir = DVector3 ( cosp * bleedang . Cos ( ) , cosp * bleedang . Sin ( ) , - bleedpitch . Sin ( ) ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 20:49:59 +00:00
if ( Trace ( pos , actor - > Sector , vdir , 172 , 0 , ML_BLOCKEVERYTHING , actor , bleedtrace , TRACE_NoSky ) )
2016-03-01 15:47:10 +00:00
{
if ( bleedtrace . HitType = = TRACE_HitWall )
{
PalEntry bloodcolor = actor - > GetBloodColor ( ) ;
if ( bloodcolor ! = 0 )
{
bloodcolor . r > > = 1 ; // the full color is too bright for blood decals
bloodcolor . g > > = 1 ;
bloodcolor . b > > = 1 ;
bloodcolor . a = 1 ;
}
2016-03-23 17:07:04 +00:00
DImpactDecal : : StaticCreate ( bloodType , bleedtrace . HitPos ,
2016-03-06 20:58:36 +00:00
bleedtrace . Line - > sidedef [ bleedtrace . Side ] , bleedtrace . ffloor , bloodcolor ) ;
2016-03-01 15:47:10 +00:00
}
}
}
}
2016-03-27 18:58:01 +00:00
void P_TraceBleed ( int damage , AActor * target , DAngle angle , DAngle pitch )
2016-03-01 15:47:10 +00:00
{
2016-03-27 18:58:01 +00:00
P_TraceBleed ( damage , target - > PosPlusZ ( target - > Height / 2 ) , target , angle , pitch ) ;
2016-03-01 15:47:10 +00:00
}
2016-11-26 00:14:47 +00:00
DEFINE_ACTION_FUNCTION ( AActor , TraceBleedAngle )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_INT ( damage ) ;
PARAM_FLOAT ( angle ) ;
PARAM_FLOAT ( pitch ) ;
P_TraceBleed ( damage , self , angle , pitch ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
//
//
//==========================================================================
void P_TraceBleed ( int damage , AActor * target , AActor * missile )
{
2016-03-25 23:34:56 +00:00
DAngle pitch ;
2016-03-01 15:47:10 +00:00
if ( target = = NULL | | missile - > flags3 & MF3_BLOODLESSIMPACT )
{
return ;
}
2016-03-19 23:54:18 +00:00
if ( missile - > Vel . Z ! = 0 )
2016-03-01 15:47:10 +00:00
{
double aim ;
2016-03-27 18:58:01 +00:00
aim = g_atan ( missile - > Vel . Z / target - > Distance2D ( missile ) ) ;
2016-03-30 14:30:22 +00:00
pitch = - DAngle : : ToDegrees ( aim ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-25 23:34:56 +00:00
pitch = 0. ;
2016-03-01 15:47:10 +00:00
}
2016-03-25 23:34:56 +00:00
P_TraceBleed ( damage , target - > PosPlusZ ( target - > Height / 2 ) , target , missile - > AngleTo ( target ) , pitch ) ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
void P_TraceBleed ( int damage , FTranslatedLineTarget * t , AActor * puff )
{
if ( t - > linetarget = = NULL | | puff - > flags3 & MF3_BLOODLESSIMPACT )
{
return ;
}
2016-03-27 18:58:01 +00:00
DAngle pitch = ( pr_tracebleed ( ) - 128 ) * ( 360 / 65536. ) ;
P_TraceBleed ( damage , t - > linetarget - > PosPlusZ ( t - > linetarget - > Height / 2 ) , t - > linetarget , t - > angleFromSource , pitch ) ;
2016-03-01 15:47:10 +00:00
}
2016-11-20 11:27:26 +00:00
DEFINE_ACTION_FUNCTION ( _FTranslatedLineTarget , TraceBleed )
{
PARAM_SELF_STRUCT_PROLOGUE ( FTranslatedLineTarget ) ;
PARAM_INT ( damage ) ;
2016-12-02 11:06:49 +00:00
PARAM_OBJECT_NOT_NULL ( missile , AActor ) ;
2016-11-20 11:27:26 +00:00
P_TraceBleed ( damage , self , missile ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
//
//
//==========================================================================
void P_TraceBleed ( int damage , AActor * target )
{
if ( target ! = NULL )
{
2016-03-27 18:58:01 +00:00
DAngle angle = pr_tracebleed ( ) * ( 360 / 256. ) ;
DAngle pitch = ( pr_tracebleed ( ) - 128 ) * ( 360 / 65536. ) ;
P_TraceBleed ( damage , target - > PosPlusZ ( target - > Height / 2 ) , target , angle , pitch ) ;
2016-03-01 15:47:10 +00:00
}
}
2016-11-29 17:42:48 +00:00
DEFINE_ACTION_FUNCTION ( AActor , TraceBleed )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_INT ( damage ) ;
PARAM_OBJECT ( missile , AActor ) ;
if ( missile ) P_TraceBleed ( damage , self , missile ) ;
else P_TraceBleed ( damage , self ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// [RH] Rail gun stuffage
//
//==========================================================================
struct SRailHit
{
AActor * HitActor ;
2016-03-23 17:07:04 +00:00
DVector3 HitPos ;
DAngle HitAngle ;
2016-03-01 15:47:10 +00:00
} ;
struct RailData
{
AActor * Caller ;
TArray < SRailHit > RailHits ;
2016-04-09 10:09:06 +00:00
TArray < SPortalHit > PortalHits ;
2016-04-23 15:16:37 +00:00
FNameNoInit PuffSpecies ;
2016-03-01 15:47:10 +00:00
bool StopAtOne ;
bool StopAtInvul ;
2016-04-23 15:16:37 +00:00
bool ThruGhosts ;
2016-03-01 15:47:10 +00:00
bool ThruSpecies ;
2016-04-23 15:16:37 +00:00
bool MThruSpecies ;
bool ThruActors ;
2016-06-14 23:20:43 +00:00
int limit ;
int count ;
2016-03-01 15:47:10 +00:00
} ;
static ETraceStatus ProcessRailHit ( FTraceResults & res , void * userdata )
{
RailData * data = ( RailData * ) userdata ;
2016-04-09 10:09:06 +00:00
if ( res . HitType = = TRACE_CrossingPortal )
{
SPortalHit newhit ;
newhit . HitPos = res . HitPos ;
newhit . ContPos = res . SrcFromTarget ;
newhit . OutDir = res . HitVector ;
data - > PortalHits . Push ( newhit ) ;
return TRACE_Continue ;
}
2016-03-01 15:47:10 +00:00
if ( res . HitType ! = TRACE_HitActor )
{
return TRACE_Stop ;
}
// Invulnerable things completely block the shot
if ( data - > StopAtInvul & & res . Actor - > flags2 & MF2_INVULNERABLE )
{
return TRACE_Stop ;
}
2016-04-23 15:16:37 +00:00
// Skip actors if the puff has:
// 1. THRUACTORS (This one did NOT include a check for spectral)
// 2. MTHRUSPECIES on puff and the shooter has same species as the hit actor
// 3. THRUSPECIES on puff and the puff has same species as the hit actor
// 4. THRUGHOST on puff and the GHOST flag on the hit actor
if ( ( data - > ThruActors ) | |
( data - > MThruSpecies & & res . Actor - > GetSpecies ( ) = = data - > Caller - > GetSpecies ( ) ) | |
( data - > ThruSpecies & & res . Actor - > GetSpecies ( ) = = data - > PuffSpecies ) | |
( data - > ThruGhosts & & res . Actor - > flags3 & MF3_GHOST ) )
2016-03-01 15:47:10 +00:00
{
return TRACE_Skip ;
}
// Save this thing for damaging later, and continue the trace
SRailHit newhit ;
newhit . HitActor = res . Actor ;
2016-03-06 20:58:36 +00:00
newhit . HitPos = res . HitPos ;
2016-03-23 17:07:04 +00:00
newhit . HitAngle = res . SrcAngleFromTarget ;
2016-03-06 20:58:36 +00:00
if ( i_compatflags & COMPATF_HITSCAN )
{
2016-03-23 17:07:04 +00:00
DVector2 ofs = P_GetOffsetPosition ( newhit . HitPos . X , newhit . HitPos . Y , - 10 * res . HitVector . X , - 10 * res . HitVector . Y ) ;
newhit . HitPos . X = ofs . X ;
newhit . HitPos . Y = ofs . Y ;
newhit . HitPos . Z - = - 10 * res . HitVector . Z ;
2016-03-06 20:58:36 +00:00
}
2016-03-01 15:47:10 +00:00
data - > RailHits . Push ( newhit ) ;
2016-06-14 23:20:43 +00:00
if ( data - > limit )
{
data - > count + + ;
}
return ( data - > StopAtOne | | ( data - > limit & & ( data - > count > = data - > limit ) ) ) ? TRACE_Stop : TRACE_Continue ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-24 23:59:14 +00:00
void P_RailAttack ( FRailParams * p )
2016-03-01 15:47:10 +00:00
{
2016-03-24 23:59:14 +00:00
DVector3 start ;
2016-03-01 15:47:10 +00:00
FTraceResults trace ;
2016-03-24 23:59:14 +00:00
PClassActor * puffclass = p - > puff ;
2016-03-01 15:47:10 +00:00
if ( puffclass = = NULL )
{
puffclass = PClass : : FindActor ( NAME_BulletPuff ) ;
}
2016-03-24 23:59:14 +00:00
AActor * source = p - > source ;
DAngle pitch = - source - > Angles . Pitch + p - > pitchoffset ;
DAngle angle = source - > Angles . Yaw + p - > angleoffset ;
2016-03-01 15:47:10 +00:00
2016-03-24 23:59:14 +00:00
DVector3 vec ( DRotator ( pitch , angle , angle ) ) ;
double shootz = source - > Center ( ) - source - > FloatSpeed + p - > offset_z ;
2016-03-01 15:47:10 +00:00
2016-03-24 23:59:14 +00:00
if ( ! ( p - > flags & RAF_CENTERZ ) )
2016-03-01 15:47:10 +00:00
{
if ( source - > player ! = NULL )
{
2016-03-24 23:59:14 +00:00
shootz + = source - > player - > mo - > AttackZOffset * source - > player - > crouchfactor ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-24 23:59:14 +00:00
shootz + = 8 ;
2016-03-01 15:47:10 +00:00
}
}
2016-07-22 08:35:44 +00:00
int puffflags = 0 ;
if ( p - > flags & RAF_NORANDOMPUFFZ )
{
puffflags | = PF_NORANDOMZ ;
}
2016-03-24 23:59:14 +00:00
DVector2 xy = source - > Vec2Angle ( p - > offset_xy , angle - 90. ) ;
2016-03-01 15:47:10 +00:00
RailData rail_data ;
rail_data . Caller = source ;
2016-06-14 23:20:43 +00:00
rail_data . limit = p - > limit ;
rail_data . count = 0 ;
2016-03-24 23:59:14 +00:00
rail_data . StopAtOne = ! ! ( p - > flags & RAF_NOPIERCE ) ;
start . X = xy . X ;
start . Y = xy . Y ;
start . Z = shootz ;
2016-03-01 15:47:10 +00:00
int flags ;
assert ( puffclass ! = NULL ) ; // Because we set it to a default above
AActor * puffDefaults = GetDefaultByType ( puffclass - > GetReplacement ( ) ) ; //Contains all the flags such as FOILINVUL, etc.
2016-04-09 10:09:06 +00:00
// disabled because not complete yet.
2016-04-11 08:46:30 +00:00
flags = ( puffDefaults - > flags6 & MF6_NOTRIGGER ) ? TRACE_ReportPortals : TRACE_PCross | TRACE_Impact | TRACE_ReportPortals ;
2016-03-01 15:47:10 +00:00
rail_data . StopAtInvul = ( puffDefaults - > flags3 & MF3_FOILINVUL ) ? false : true ;
2016-04-23 15:16:37 +00:00
rail_data . MThruSpecies = ( ( puffDefaults - > flags6 & MF6_MTHRUSPECIES ) ) ? true : false ;
// Prevent mod breakage as somewhere, someone is relying on these to spawn on an actor
// and move through them...
if ( ( puffDefaults - > flags7 & MF7_ALLOWTHRUFLAGS ) )
{
rail_data . ThruGhosts = ! ! ( puffDefaults - > flags2 & MF2_THRUGHOST ) ;
rail_data . ThruSpecies = ! ! ( puffDefaults - > flags6 & MF6_THRUSPECIES ) ;
rail_data . ThruActors = ! ! ( puffDefaults - > flags2 & MF2_THRUACTORS ) ;
}
else
{
rail_data . ThruGhosts = false ;
rail_data . MThruSpecies = false ;
rail_data . ThruActors = false ;
}
// used as damage inflictor
AActor * thepuff = NULL ;
if ( puffclass ! = NULL ) thepuff = Spawn ( puffclass , source - > Pos ( ) , ALLOW_REPLACE ) ;
rail_data . PuffSpecies = ( thepuff ! = NULL ) ? thepuff - > GetSpecies ( ) : NAME_None ;
2016-04-11 08:46:30 +00:00
2016-03-24 23:59:14 +00:00
Trace ( start , source - > Sector , vec , p - > distance , MF_SHOOTABLE , ML_BLOCKEVERYTHING , source , trace , flags , ProcessRailHit , & rail_data ) ;
2016-03-01 15:47:10 +00:00
// Hurt anything the trace hit
unsigned int i ;
FName damagetype = ( puffDefaults = = NULL | | puffDefaults - > DamageType = = NAME_None ) ? FName ( NAME_Railgun ) : puffDefaults - > DamageType ;
for ( i = 0 ; i < rail_data . RailHits . Size ( ) ; i + + )
{
bool spawnpuff ;
bool bleed = false ;
2016-07-22 08:35:44 +00:00
int actorpuffflags = puffflags | PF_HITTHING ;
2016-03-01 15:47:10 +00:00
AActor * hitactor = rail_data . RailHits [ i ] . HitActor ;
2016-03-23 17:07:04 +00:00
DVector3 & hitpos = rail_data . RailHits [ i ] . HitPos ;
DAngle hitangle = rail_data . RailHits [ i ] . HitAngle ;
2016-03-01 15:47:10 +00:00
if ( ( hitactor - > flags & MF_NOBLOOD ) | |
( hitactor - > flags2 & MF2_DORMANT | | ( ( hitactor - > flags2 & MF2_INVULNERABLE ) & & ! ( puffDefaults - > flags3 & MF3_FOILINVUL ) ) ) )
{
spawnpuff = ( puffclass ! = NULL ) ;
}
else
{
spawnpuff = ( puffclass ! = NULL & & puffDefaults - > flags3 & MF3_ALWAYSPUFF ) ;
2016-07-22 08:35:44 +00:00
actorpuffflags | = PF_HITTHINGBLEED ; // [XA] Allow for puffs to jump to XDeath state.
2016-03-01 15:47:10 +00:00
if ( ! ( puffDefaults - > flags3 & MF3_BLOODLESSIMPACT ) )
{
bleed = true ;
}
}
if ( spawnpuff )
{
2016-07-22 08:35:44 +00:00
P_SpawnPuff ( source , puffclass , hitpos , hitangle , hitangle - 90 , 1 , actorpuffflags , hitactor ) ;
2016-03-01 15:47:10 +00:00
}
int dmgFlagPass = DMG_INFLICTOR_IS_PUFF ;
if ( puffDefaults ! = NULL ) // is this even possible?
{
if ( puffDefaults - > PoisonDamage > 0 & & puffDefaults - > PoisonDuration ! = INT_MIN )
{
P_PoisonMobj ( hitactor , thepuff ? thepuff : source , source , puffDefaults - > PoisonDamage , puffDefaults - > PoisonDuration , puffDefaults - > PoisonPeriod , puffDefaults - > PoisonDamageType ) ;
}
if ( puffDefaults - > flags3 & MF3_FOILINVUL ) dmgFlagPass | = DMG_FOILINVUL ;
if ( puffDefaults - > flags7 & MF7_FOILBUDDHA ) dmgFlagPass | = DMG_FOILBUDDHA ;
}
2016-03-24 23:59:14 +00:00
int newdam = P_DamageMobj ( hitactor , thepuff ? thepuff : source , source , p - > damage , damagetype , dmgFlagPass | DMG_USEANGLE , hitangle ) ;
2016-03-01 15:47:10 +00:00
if ( bleed )
{
2016-03-24 23:59:14 +00:00
P_SpawnBlood ( hitpos , hitangle , newdam > 0 ? newdam : p - > damage , hitactor ) ;
2016-03-27 20:49:59 +00:00
P_TraceBleed ( newdam > 0 ? newdam : p - > damage , hitpos , hitactor , hitangle , pitch ) ;
2016-03-01 15:47:10 +00:00
}
}
// Spawn a decal or puff at the point where the trace ended.
if ( trace . HitType = = TRACE_HitWall )
{
AActor * puff = NULL ;
if ( puffclass ! = NULL & & puffDefaults - > flags3 & MF3_ALWAYSPUFF )
{
2016-07-22 08:35:44 +00:00
puff = P_SpawnPuff ( source , puffclass , trace . HitPos , trace . SrcAngleFromTarget , trace . SrcAngleFromTarget - 90 , 1 , puffflags ) ;
2016-03-01 15:47:10 +00:00
if ( puff & & ( trace . Line ! = NULL ) & & ( trace . Line - > special = = Line_Horizon ) & & ! ( puff - > flags3 & MF3_SKYEXPLODE ) )
puff - > Destroy ( ) ;
}
if ( puff ! = NULL & & puffDefaults - > flags7 & MF7_FORCEDECAL & & puff - > DecalGenerator )
SpawnShootDecal ( puff , trace ) ;
else
SpawnShootDecal ( source , trace ) ;
}
if ( trace . HitType = = TRACE_HitFloor | | trace . HitType = = TRACE_HitCeiling )
{
AActor * puff = NULL ;
if ( puffclass ! = NULL & & puffDefaults - > flags3 & MF3_ALWAYSPUFF )
{
2016-07-22 08:35:44 +00:00
puff = P_SpawnPuff ( source , puffclass , trace . HitPos , trace . SrcAngleFromTarget , trace . SrcAngleFromTarget - 90 , 1 , puffflags ) ;
2016-03-01 15:47:10 +00:00
if ( puff & & ! ( puff - > flags3 & MF3_SKYEXPLODE ) & &
( ( ( trace . HitType = = TRACE_HitFloor ) & & ( puff - > floorpic = = skyflatnum ) ) | |
( ( trace . HitType = = TRACE_HitCeiling ) & & ( puff - > ceilingpic = = skyflatnum ) ) ) )
{
puff - > Destroy ( ) ;
}
}
}
if ( thepuff ! = NULL )
{
2016-03-06 20:58:36 +00:00
if ( trace . Crossed3DWater | | trace . CrossedWater )
2016-03-01 15:47:10 +00:00
{
2016-03-06 20:58:36 +00:00
SpawnDeepSplash ( source , trace , thepuff ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-06 20:58:36 +00:00
else if ( trace . HitType = = TRACE_HitFloor & & trace . Sector - > heightsec = = NULL )
2016-03-01 15:47:10 +00:00
{
2016-03-06 20:58:36 +00:00
P_HitWater ( thepuff , trace . Sector , trace . HitPos ) ;
2016-03-01 15:47:10 +00:00
}
thepuff - > Destroy ( ) ;
}
// Draw the slug's trail.
2016-04-23 13:19:59 +00:00
P_DrawRailTrail ( source , rail_data . PortalHits , p - > color1 , p - > color2 , p - > maxdiff , p - > flags , p - > spawnclass , angle , p - > duration , p - > sparsity , p - > drift , p - > SpiralOffset , pitch ) ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// [RH] P_AimCamera
//
//==========================================================================
CVAR ( Float , chase_height , - 8.f , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
CVAR ( Float , chase_dist , 90.f , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2016-04-16 20:54:47 +00:00
void P_AimCamera ( AActor * t1 , DVector3 & campos , DAngle & camangle , sector_t * & CameraSector , bool & unlinked )
2016-03-01 15:47:10 +00:00
{
2016-03-27 18:58:01 +00:00
double distance = clamp < double > ( chase_dist , 0 , 30000 ) ;
DAngle angle = t1 - > Angles . Yaw - 180 ;
DAngle pitch = t1 - > Angles . Pitch ;
2016-03-01 15:47:10 +00:00
FTraceResults trace ;
2016-03-27 18:58:01 +00:00
DVector3 vvec ;
double sz ;
2016-03-01 15:47:10 +00:00
2016-03-27 18:58:01 +00:00
double pc = pitch . Cos ( ) ;
2016-03-23 17:07:04 +00:00
2016-04-07 10:44:46 +00:00
vvec = { pc * angle . Cos ( ) , pc * angle . Sin ( ) , pitch . Sin ( ) } ;
2016-03-27 18:58:01 +00:00
sz = t1 - > Top ( ) - t1 - > Floorclip + clamp < double > ( chase_height , - 1000 , 1000 ) ;
2016-03-01 15:47:10 +00:00
2016-03-27 18:58:01 +00:00
if ( Trace ( t1 - > PosAtZ ( sz ) , t1 - > Sector , vvec , distance , 0 , 0 , NULL , trace ) & &
2016-03-23 17:07:04 +00:00
trace . Distance > 10 )
2016-03-01 15:47:10 +00:00
{
// Position camera slightly in front of hit thing
2016-03-27 18:58:01 +00:00
campos = t1 - > PosAtZ ( sz ) + vvec * ( trace . Distance - 5 ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-05-11 11:57:49 +00:00
campos = trace . HitPos - trace . HitVector * 1 / 256. ;
2016-03-01 15:47:10 +00:00
}
CameraSector = trace . Sector ;
2016-03-06 12:10:42 +00:00
unlinked = trace . unlinked ;
2016-04-16 20:54:47 +00:00
camangle = trace . SrcAngleFromTarget - 180. ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// P_TalkFacing
//
// Looks for something within 5.625 degrees left or right of the player
// to talk to.
//
//==========================================================================
bool P_TalkFacing ( AActor * player )
{
2016-03-17 10:38:56 +00:00
static const double angleofs [ ] = { 0 , 90. / 16 , - 90. / 16 } ;
2016-03-01 15:47:10 +00:00
FTranslatedLineTarget t ;
2016-03-17 10:38:56 +00:00
for ( double angle : angleofs )
2016-03-01 15:47:10 +00:00
{
2016-03-17 10:38:56 +00:00
P_AimLineAttack ( player , player - > Angles . Yaw + angle , TALKRANGE , & t , 35. , ALF_FORCENOSMART | ALF_CHECKCONVERSATION | ALF_PORTALRESTRICT ) ;
2016-03-01 15:47:10 +00:00
if ( t . linetarget ! = NULL )
{
if ( t . linetarget - > health > 0 & & // Dead things can't talk.
2016-03-16 10:36:42 +00:00
! ( t . linetarget - > flags4 & MF4_INCOMBAT ) & & // Fighting things don't talk either.
2016-03-01 15:47:10 +00:00
t . linetarget - > Conversation ! = NULL )
{
// Give the NPC a chance to play a brief animation
t . linetarget - > ConversationAnimation ( 0 ) ;
P_StartConversation ( t . linetarget , player , true , true ) ;
return true ;
}
return false ;
}
}
return false ;
}
//==========================================================================
//
// USE LINES
//
//==========================================================================
2016-03-27 18:58:01 +00:00
bool P_UseTraverse ( AActor * usething , const DVector2 & start , const DVector2 & end , bool & foundline )
2016-03-01 15:47:10 +00:00
{
2016-03-27 18:58:01 +00:00
FPathTraverse it ( start . X , start . Y , end . X , end . Y , PT_ADDLINES | PT_ADDTHINGS ) ;
2016-03-01 15:47:10 +00:00
intercept_t * in ;
2016-03-27 18:58:01 +00:00
DVector3 xpos = { start . X , start . Y , usething - > Z ( ) } ;
2016-03-01 15:47:10 +00:00
while ( ( in = it . Next ( ) ) )
{
// [RH] Check for things to talk with or use a puzzle item on
if ( ! in - > isaline )
{
if ( usething = = in - > d . thing )
continue ;
// Check thing
// Check for puzzle item use or USESPECIAL flag
// Extended to use the same activationtype mechanism as BUMPSPECIAL does
if ( in - > d . thing - > flags5 & MF5_USESPECIAL | | in - > d . thing - > special = = UsePuzzleItem )
{
if ( P_ActivateThingSpecial ( in - > d . thing , usething ) )
return true ;
}
continue ;
}
2016-03-27 18:58:01 +00:00
2016-03-01 15:47:10 +00:00
if ( it . PortalRelocate ( in , PT_ADDLINES | PT_ADDTHINGS , & xpos ) )
{
continue ;
}
FLineOpening open ;
if ( in - > d . line - > special = = 0 | | ! ( in - > d . line - > activation & ( SPAC_Use | SPAC_UseThrough | SPAC_UseBack ) ) )
{
blocked :
if ( in - > d . line - > flags & ( ML_BLOCKEVERYTHING | ML_BLOCKUSE ) )
{
open . range = 0 ;
}
else
{
2016-03-27 18:58:01 +00:00
P_LineOpening ( open , NULL , in - > d . line , it . InterceptPoint ( in ) ) ;
2016-03-01 15:47:10 +00:00
}
if ( open . range < = 0 | |
( in - > d . line - > special ! = 0 & & ( i_compatflags & COMPATF_USEBLOCKING ) ) )
{
// [RH] Give sector a chance to intercept the use
sector_t * sec ;
sec = usething - > Sector ;
if ( sec - > SecActTarget & & sec - > SecActTarget - > TriggerAction ( usething , SECSPAC_Use ) )
{
return true ;
}
2016-03-27 18:58:01 +00:00
sec = P_PointOnLineSide ( xpos , in - > d . line ) = = 0 ?
2016-03-01 15:47:10 +00:00
in - > d . line - > frontsector : in - > d . line - > backsector ;
if ( sec ! = NULL & & sec - > SecActTarget & &
sec - > SecActTarget - > TriggerAction ( usething , SECSPAC_UseWall ) )
{
return true ;
}
if ( usething - > player )
{
S_Sound ( usething , CHAN_VOICE , " *usefail " , 1 , ATTN_IDLE ) ;
}
return true ; // can't use through a wall
}
foundline = true ;
continue ; // not a special line, but keep checking
}
2016-03-27 18:58:01 +00:00
if ( P_PointOnLineSide ( xpos , in - > d . line ) = = 1 )
2016-03-01 15:47:10 +00:00
{
if ( ! ( in - > d . line - > activation & SPAC_UseBack ) )
{
// [RH] continue traversal for two-sided lines
//return in->d.line->backsector != NULL; // don't use back side
goto blocked ; // do a proper check for back sides of triggers
}
else
{
P_ActivateLine ( in - > d . line , usething , 1 , SPAC_UseBack , & xpos ) ;
return true ;
}
}
else
{
if ( ( in - > d . line - > activation & ( SPAC_Use | SPAC_UseThrough | SPAC_UseBack ) ) = = SPAC_UseBack )
{
goto blocked ; // Line cannot be used from front side so treat it as a non-trigger line
}
P_ActivateLine ( in - > d . line , usething , 0 , SPAC_Use , & xpos ) ;
//WAS can't use more than one special line in a row
//jff 3/21/98 NOW multiple use allowed with enabling line flag
//[RH] And now I've changed it again. If the line is of type
// SPAC_USE, then it eats the use. Everything else passes
// it through, including SPAC_USETHROUGH.
if ( i_compatflags & COMPATF_USEBLOCKING )
{
if ( in - > d . line - > activation & SPAC_UseThrough ) continue ;
else return true ;
}
else
{
if ( ! ( in - > d . line - > activation & SPAC_Use ) ) continue ;
else return true ;
}
}
}
return false ;
}
//==========================================================================
//
// Returns false if a "oof" sound should be made because of a blocking
// linedef. Makes 2s middles which are impassable, as well as 2s uppers
// and lowers which block the player, cause the sound effect when the
// player tries to activate them. Specials are excluded, although it is
// assumed that all special linedefs within reach have been considered
// and rejected already (see P_UseLines).
//
// by Lee Killough
//
//==========================================================================
2016-03-27 18:58:01 +00:00
bool P_NoWayTraverse ( AActor * usething , const DVector2 & start , const DVector2 & end )
2016-03-01 15:47:10 +00:00
{
2016-03-27 18:58:01 +00:00
FPathTraverse it ( start . X , start . Y , end . X , end . Y , PT_ADDLINES ) ;
2016-03-01 15:47:10 +00:00
intercept_t * in ;
while ( ( in = it . Next ( ) ) )
{
line_t * ld = in - > d . line ;
FLineOpening open ;
// [GrafZahl] de-obfuscated. Was I the only one who was unable to make sense out of
// this convoluted mess?
if ( ld - > special ) continue ;
if ( ld - > isLinePortal ( ) ) return false ;
if ( ld - > flags & ( ML_BLOCKING | ML_BLOCKEVERYTHING | ML_BLOCK_PLAYERS ) ) return true ;
2016-03-27 18:58:01 +00:00
P_LineOpening ( open , NULL , ld , it . InterceptPoint ( in ) ) ;
2016-03-01 15:47:10 +00:00
if ( open . range < = 0 | |
2016-03-25 17:19:54 +00:00
open . bottom > usething - > Z ( ) + usething - > MaxStepHeight | |
open . top < usething - > Top ( ) ) return true ;
2016-03-01 15:47:10 +00:00
}
return false ;
}
//==========================================================================
//
// P_UseLines
//
// Looks for special lines in front of the player to activate
//
//==========================================================================
void P_UseLines ( player_t * player )
{
bool foundline = false ;
// If the player is transitioning a portal, use the group that is at its vertical center.
2016-03-27 18:58:01 +00:00
DVector2 start = player - > mo - > GetPortalTransition ( player - > mo - > Height / 2 ) ;
2016-03-01 15:47:10 +00:00
// [NS] Now queries the Player's UseRange.
2016-03-27 18:58:01 +00:00
DVector2 end = start + player - > mo - > Angles . Yaw . ToVector ( player - > mo - > UseRange ) ;
2016-03-01 15:47:10 +00:00
// old code:
// This added test makes the "oof" sound work on 2s lines -- killough:
2016-03-27 18:58:01 +00:00
if ( ! P_UseTraverse ( player - > mo , start , end , foundline ) )
2016-03-01 15:47:10 +00:00
{ // [RH] Give sector a chance to eat the use
sector_t * sec = player - > mo - > Sector ;
int spac = SECSPAC_Use ;
if ( foundline ) spac | = SECSPAC_UseWall ;
if ( ( ! sec - > SecActTarget | | ! sec - > SecActTarget - > TriggerAction ( player - > mo , spac ) ) & &
2016-03-27 18:58:01 +00:00
P_NoWayTraverse ( player - > mo , start , end ) )
2016-03-01 15:47:10 +00:00
{
S_Sound ( player - > mo , CHAN_VOICE , " *usefail " , 1 , ATTN_IDLE ) ;
}
}
}
//==========================================================================
//
// P_UsePuzzleItem
//
// Returns true if the puzzle item was used on a line or a thing.
//
//==========================================================================
bool P_UsePuzzleItem ( AActor * PuzzleItemUser , int PuzzleItemType )
{
2016-03-27 18:58:01 +00:00
DVector2 start ;
DVector2 end ;
double usedist ;
2016-03-01 15:47:10 +00:00
// [NS] If it's a Player, get their UseRange.
if ( PuzzleItemUser - > player )
2016-03-27 18:58:01 +00:00
usedist = PuzzleItemUser - > player - > mo - > UseRange ;
2016-03-01 15:47:10 +00:00
else
usedist = USERANGE ;
2016-03-27 18:58:01 +00:00
start = PuzzleItemUser - > GetPortalTransition ( PuzzleItemUser - > Height / 2 ) ;
end = PuzzleItemUser - > Angles . Yaw . ToVector ( usedist ) ;
2016-03-01 15:47:10 +00:00
2016-04-11 20:10:46 +00:00
FPathTraverse it ( start . X , start . Y , end . X , end . Y , PT_DELTA | PT_ADDLINES | PT_ADDTHINGS ) ;
2016-03-01 15:47:10 +00:00
intercept_t * in ;
while ( ( in = it . Next ( ) ) )
{
AActor * mobj ;
FLineOpening open ;
if ( in - > isaline )
{ // Check line
if ( in - > d . line - > special ! = UsePuzzleItem )
{
2016-03-27 18:58:01 +00:00
P_LineOpening ( open , NULL , in - > d . line , it . InterceptPoint ( in ) ) ;
2016-03-01 15:47:10 +00:00
if ( open . range < = 0 )
{
return false ; // can't use through a wall
}
continue ;
}
2016-03-27 18:58:01 +00:00
if ( P_PointOnLineSide ( PuzzleItemUser - > Pos ( ) , in - > d . line ) = = 1 )
2016-03-01 15:47:10 +00:00
{ // Don't use back sides
return false ;
}
if ( PuzzleItemType ! = in - > d . line - > args [ 0 ] )
{ // Item type doesn't match
return false ;
}
int args [ 3 ] = { in - > d . line - > args [ 2 ] , in - > d . line - > args [ 3 ] , in - > d . line - > args [ 4 ] } ;
P_StartScript ( PuzzleItemUser , in - > d . line , in - > d . line - > args [ 1 ] , NULL , args , 3 , ACS_ALWAYS ) ;
in - > d . line - > special = 0 ;
return true ;
}
// Check thing
mobj = in - > d . thing ;
if ( mobj - > special ! = UsePuzzleItem )
{ // Wrong special
continue ;
}
if ( PuzzleItemType ! = mobj - > args [ 0 ] )
{ // Item type doesn't match
continue ;
}
int args [ 3 ] = { mobj - > args [ 2 ] , mobj - > args [ 3 ] , mobj - > args [ 4 ] } ;
P_StartScript ( PuzzleItemUser , NULL , mobj - > args [ 1 ] , NULL , args , 3 , ACS_ALWAYS ) ;
mobj - > special = 0 ;
return true ;
}
return false ;
}
//==========================================================================
//
// RADIUS ATTACK
//
//
//==========================================================================
// [RH] Damage scale to apply to thing that shot the missile.
static float selfthrustscale ;
CUSTOM_CVAR ( Float , splashfactor , 1.f , CVAR_SERVERINFO )
{
if ( self < = 0.f )
self = 1.f ;
else
selfthrustscale = 1.f / self ;
}
//==========================================================================
//
// P_RadiusAttack
// Source is the creature that caused the explosion at spot.
//
//==========================================================================
2016-08-08 18:01:43 +00:00
int P_RadiusAttack ( AActor * bombspot , AActor * bombsource , int bombdamage , int bombdistance , FName bombmod ,
2016-03-01 15:47:10 +00:00
int flags , int fulldamagedistance )
{
if ( bombdistance < = 0 )
2016-08-08 18:01:43 +00:00
return 0 ;
2016-03-01 15:47:10 +00:00
fulldamagedistance = clamp < int > ( fulldamagedistance , 0 , bombdistance - 1 ) ;
2016-03-27 19:11:17 +00:00
double bombdistancefloat = 1. / ( double ) ( bombdistance - fulldamagedistance ) ;
2016-03-01 15:47:10 +00:00
double bombdamagefloat = ( double ) bombdamage ;
2016-03-08 13:41:37 +00:00
FPortalGroupArray grouplist ( FPortalGroupArray : : PGA_Full3d ) ;
2016-04-04 14:51:25 +00:00
FMultiBlockThingsIterator it ( grouplist , bombspot - > X ( ) , bombspot - > Y ( ) , bombspot - > Z ( ) - bombdistance , bombspot - > Height + bombdistance * 2 , bombdistance , false , bombspot - > Sector ) ;
2016-03-07 00:02:48 +00:00
FMultiBlockThingsIterator : : CheckResult cres ;
2016-03-01 15:47:10 +00:00
if ( flags & RADF_SOURCEISSPOT )
{ // The source is actually the same as the spot, even if that wasn't what we received.
bombsource = bombspot ;
}
2016-08-08 18:01:43 +00:00
int count = 0 ;
2016-03-07 00:02:48 +00:00
while ( ( it . Next ( & cres ) ) )
2016-03-01 15:47:10 +00:00
{
2016-03-07 00:02:48 +00:00
AActor * thing = cres . thing ;
2016-03-27 20:49:59 +00:00
// Vulnerable actors can be damaged by radius attacks even if not shootable
2016-03-01 15:47:10 +00:00
// Used to emulate MBF's vulnerability of non-missile bouncers to explosions.
if ( ! ( ( thing - > flags & MF_SHOOTABLE ) | | ( thing - > flags6 & MF6_VULNERABLE ) ) )
continue ;
// Boss spider and cyborg and Heretic's ep >= 2 bosses
// take no damage from concussion.
if ( thing - > flags3 & MF3_NORADIUSDMG & & ! ( bombspot - > flags4 & MF4_FORCERADIUSDMG ) )
continue ;
if ( ! ( flags & RADF_HURTSOURCE ) & & ( thing = = bombsource | | thing = = bombspot ) )
{ // don't damage the source of the explosion
continue ;
}
// a much needed option: monsters that fire explosive projectiles cannot
// be hurt by projectiles fired by a monster of the same type.
// Controlled by the DONTHARMCLASS and DONTHARMSPECIES flags.
if ( ( bombsource & & ! thing - > player ) // code common to both checks
& & ( // Class check first
( ( bombsource - > flags4 & MF4_DONTHARMCLASS ) & & ( thing - > GetClass ( ) = = bombsource - > GetClass ( ) ) )
| | // Nigh-identical species check second
( ( bombsource - > flags6 & MF6_DONTHARMSPECIES ) & & ( thing - > GetSpecies ( ) = = bombsource - > GetSpecies ( ) ) )
)
) continue ;
// Barrels always use the original code, since this makes
// them far too "active." BossBrains also use the old code
// because some user levels require they have a height of 16,
// which can make them near impossible to hit with the new code.
if ( ( flags & RADF_NODAMAGE ) | | ! ( ( bombspot - > flags5 | thing - > flags5 ) & MF5_OLDRADIUSDMG ) )
{
// [RH] New code. The bounding box only covers the
// height of the thing and not the height of the map.
double points ;
double len ;
2016-03-27 19:11:17 +00:00
double dx , dy ;
2016-03-01 15:47:10 +00:00
double boxradius ;
2016-03-27 19:11:17 +00:00
DVector2 vec = bombspot - > Vec2To ( thing ) ;
dx = fabs ( vec . X ) ;
dy = fabs ( vec . Y ) ;
boxradius = thing - > radius ;
2016-03-01 15:47:10 +00:00
// The damage pattern is square, not circular.
len = double ( dx > dy ? dx : dy ) ;
2016-03-27 19:11:17 +00:00
if ( bombspot - > Z ( ) < thing - > Z ( ) | | bombspot - > Z ( ) > = thing - > Top ( ) )
2016-03-01 15:47:10 +00:00
{
double dz ;
2016-03-27 19:11:17 +00:00
if ( bombspot - > Z ( ) > thing - > Z ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-27 19:11:17 +00:00
dz = double ( bombspot - > Z ( ) - thing - > Top ( ) ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-27 19:11:17 +00:00
dz = double ( thing - > Z ( ) - bombspot - > Z ( ) ) ;
2016-03-01 15:47:10 +00:00
}
if ( len < = boxradius )
{
len = dz ;
}
else
{
len - = boxradius ;
2016-03-11 14:45:47 +00:00
len = g_sqrt ( len * len + dz * dz ) ;
2016-03-01 15:47:10 +00:00
}
}
else
{
len - = boxradius ;
if ( len < 0.f )
len = 0.f ;
}
len = clamp < double > ( len - ( double ) fulldamagedistance , 0 , len ) ;
2016-03-27 19:11:17 +00:00
points = bombdamagefloat * ( 1. - len * bombdistancefloat ) ;
2016-03-01 15:47:10 +00:00
if ( thing = = bombsource )
{
points = points * splashfactor ;
}
2016-03-24 20:31:04 +00:00
points * = thing - > GetClass ( ) - > RDFactor ;
2016-03-01 15:47:10 +00:00
2016-04-21 08:51:41 +00:00
// points and bombdamage should be the same sign (the double cast of 'points' is needed to prevent overflows and incorrect values slipping through.)
if ( ( ( ( double ) int ( points ) * bombdamage ) > 0 ) & & P_CheckSight ( thing , bombspot , SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY ) )
2016-03-01 15:47:10 +00:00
{ // OK to damage; target is in direct path
2016-03-12 13:11:43 +00:00
double vz ;
2016-03-01 15:47:10 +00:00
double thrust ;
int damage = abs ( ( int ) points ) ;
int newdam = damage ;
if ( ! ( flags & RADF_NODAMAGE ) )
2016-08-08 18:01:43 +00:00
{
//[MC] Don't count actors saved by buddha if already at 1 health.
int prehealth = thing - > health ;
2016-03-01 15:47:10 +00:00
newdam = P_DamageMobj ( thing , bombspot , bombsource , damage , bombmod ) ;
2016-08-08 18:01:43 +00:00
if ( thing - > health < prehealth ) count + + ;
}
2016-03-01 15:47:10 +00:00
else if ( thing - > player = = NULL & & ( ! ( flags & RADF_NOIMPACTDAMAGE ) & & ! ( thing - > flags7 & MF7_DONTTHRUST ) ) )
thing - > flags2 | = MF2_BLASTED ;
if ( ! ( thing - > flags & MF_ICECORPSE ) )
{
if ( ! ( flags & RADF_NODAMAGE ) & & ! ( bombspot - > flags3 & MF3_BLOODLESSIMPACT ) )
P_TraceBleed ( newdam > 0 ? newdam : damage , thing , bombspot ) ;
if ( ( flags & RADF_NODAMAGE ) | | ! ( bombspot - > flags2 & MF2_NODMGTHRUST ) )
{
if ( bombsource = = NULL | | ! ( bombsource - > flags2 & MF2_NODMGTHRUST ) )
{
if ( ! ( thing - > flags7 & MF7_DONTTHRUST ) )
{
2016-03-19 23:54:18 +00:00
thrust = points * 0.5 / ( double ) thing - > Mass ;
2016-03-01 15:47:10 +00:00
if ( bombsource = = thing )
{
thrust * = selfthrustscale ;
}
2016-03-19 23:54:18 +00:00
vz = ( thing - > Center ( ) - bombspot - > Z ( ) ) * thrust ;
2016-03-01 15:47:10 +00:00
if ( bombsource ! = thing )
{
2016-03-19 23:54:18 +00:00
vz * = 0.5 ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-19 23:54:18 +00:00
vz * = 0.8 ;
2016-03-01 15:47:10 +00:00
}
2016-03-19 23:54:18 +00:00
thing - > Thrust ( bombspot - > AngleTo ( thing ) , thrust ) ;
2016-05-11 11:59:11 +00:00
if ( ! ( flags & RADF_NODAMAGE ) | | ( flags & RADF_THRUSTZ ) )
2016-03-19 23:54:18 +00:00
thing - > Vel . Z + = vz ; // this really doesn't work well
2016-03-01 15:47:10 +00:00
}
}
}
}
}
}
else
{
// [RH] Old code just for barrels
2016-03-27 19:11:17 +00:00
double dx , dy , dist ;
2016-03-01 15:47:10 +00:00
2016-03-27 19:11:17 +00:00
DVector2 vec = bombspot - > Vec2To ( thing ) ;
dx = fabs ( vec . X ) ;
dy = fabs ( vec . Y ) ;
2016-03-01 15:47:10 +00:00
dist = dx > dy ? dx : dy ;
2016-03-27 19:11:17 +00:00
dist - = thing - > radius ;
2016-03-01 15:47:10 +00:00
if ( dist < 0 )
dist = 0 ;
if ( dist > = bombdistance )
continue ; // out of range
if ( P_CheckSight ( thing , bombspot , SF_IGNOREVISIBILITY | SF_IGNOREWATERBOUNDARY ) )
{ // OK to damage; target is in direct path
2016-03-27 19:11:17 +00:00
dist = clamp < double > ( dist - fulldamagedistance , 0 , dist ) ;
int damage = Scale ( bombdamage , bombdistance - int ( dist ) , bombdistance ) ;
2016-03-01 15:47:10 +00:00
2016-03-24 20:31:04 +00:00
double factor = splashfactor * thing - > GetClass ( ) - > RDFactor ;
damage = int ( damage * factor ) ;
2016-03-01 15:47:10 +00:00
if ( damage > 0 )
{
2016-08-08 18:01:43 +00:00
//[MC] Don't count actors saved by buddha if already at 1 health.
int prehealth = thing - > health ;
2016-03-01 15:47:10 +00:00
int newdam = P_DamageMobj ( thing , bombspot , bombsource , damage , bombmod ) ;
P_TraceBleed ( newdam > 0 ? newdam : damage , thing , bombspot ) ;
2016-08-08 18:01:43 +00:00
if ( thing - > health < prehealth ) count + + ;
2016-03-01 15:47:10 +00:00
}
}
}
}
2016-08-08 18:01:43 +00:00
return count ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// SECTOR HEIGHT CHANGING
// After modifying a sector's floor or ceiling height,
// call this routine to adjust the positions
// of all things that touch the sector.
//
// If anything doesn't fit anymore, true will be returned.
//
// [RH] If crushchange is non-negative, they will take the
// specified amount of damage as they are being crushed.
// If crushchange is negative, you should set the sector
// height back the way it was and call P_ChangeSector()
// again to undo the changes.
// Note that this is very different from the original
// true/false usage of crushchange! If you want regular
// DOOM crushing behavior set crushchange to 10 or -1
// if no crushing is desired.
//
//==========================================================================
struct FChangePosition
{
sector_t * sector ;
2016-03-27 20:35:58 +00:00
double moveamt ;
2016-03-01 15:47:10 +00:00
int crushchange ;
bool nofit ;
bool movemidtex ;
} ;
TArray < AActor * > intersectors ;
EXTERN_CVAR ( Int , cl_bloodtype )
//=============================================================================
//
// P_AdjustFloorCeil
//
//=============================================================================
bool P_AdjustFloorCeil ( AActor * thing , FChangePosition * cpos )
{
ActorFlags2 flags2 = thing - > flags2 & MF2_PASSMOBJ ;
FCheckPosition tm ;
if ( ( thing - > flags2 & MF2_PASSMOBJ ) & & ( thing - > flags3 & MF3_ISMONSTER ) )
{
tm . FromPMove = true ;
}
if ( cpos - > movemidtex )
{
// From Eternity:
// ALL things must be treated as PASSMOBJ when moving
// 3DMidTex lines, otherwise you get stuck in them.
thing - > flags2 | = MF2_PASSMOBJ ;
}
2016-03-27 00:06:54 +00:00
bool isgood = P_CheckPosition ( thing , thing - > Pos ( ) , tm ) ;
2016-07-04 20:36:27 +00:00
if ( ! ( thing - > flags4 & MF4_ACTLIKEBRIDGE ) )
{
thing - > floorz = tm . floorz ;
thing - > ceilingz = tm . ceilingz ;
thing - > dropoffz = tm . dropoffz ; // killough 11/98: remember dropoffs
thing - > floorpic = tm . floorpic ;
thing - > floorterrain = tm . floorterrain ;
thing - > floorsector = tm . floorsector ;
thing - > ceilingpic = tm . ceilingpic ;
thing - > ceilingsector = tm . ceilingsector ;
}
else
{
// Bridges only keep the info at their spawn position
// This is necessary to prevent moving sectors from altering the bridge's z-position.
// The bridge should remain at its current z, even if the sector change would cause
// floorz or ceilingz to be changed in a way that would make P_ZMovement adjust the bridge.
P_FindFloorCeiling ( thing , FFCF_ONLYSPAWNPOS ) ;
}
2016-03-01 15:47:10 +00:00
// restore the PASSMOBJ flag but leave the other flags alone.
thing - > flags2 = ( thing - > flags2 & ~ MF2_PASSMOBJ ) | flags2 ;
return isgood ;
}
//=============================================================================
//
// P_FindAboveIntersectors
//
//=============================================================================
void P_FindAboveIntersectors ( AActor * actor )
{
if ( actor - > flags & MF_NOCLIP )
return ;
if ( ! ( actor - > flags & MF_SOLID ) )
return ;
2016-03-06 23:17:52 +00:00
FPortalGroupArray check ;
FMultiBlockThingsIterator it ( check , actor ) ;
FMultiBlockThingsIterator : : CheckResult cres ;
while ( it . Next ( & cres ) )
2016-03-01 15:47:10 +00:00
{
2016-03-06 23:17:52 +00:00
AActor * thing = cres . thing ;
2016-03-27 20:35:58 +00:00
double blockdist = actor - > radius + thing - > radius ;
if ( fabs ( thing - > X ( ) - cres . Position . X ) > = blockdist | | fabs ( thing - > Y ( ) - cres . Position . Y ) > = blockdist )
2016-03-01 15:47:10 +00:00
continue ;
2016-03-06 23:17:52 +00:00
2016-03-01 15:47:10 +00:00
if ( ! ( thing - > flags & MF_SOLID ) )
{ // Can't hit thing
continue ;
}
if ( thing - > flags & ( MF_SPECIAL ) )
{ // [RH] Corpses and specials don't block moves
continue ;
}
if ( thing - > flags & ( MF_CORPSE ) )
{ // Corpses need a few more checks
if ( ! ( actor - > flags & MF_ICECORPSE ) )
continue ;
}
if ( thing = = actor )
{ // Don't clip against self
continue ;
}
if ( ! ( ( thing - > flags2 | actor - > flags2 ) & MF2_PASSMOBJ ) & & ! ( ( thing - > flags3 | actor - > flags3 ) & MF3_ISMONSTER ) )
{
// Don't bother if both things don't have MF2_PASSMOBJ set and aren't monsters.
// These things would always block each other which in nearly every situation is
// not what is wanted here.
continue ;
}
2016-05-14 13:05:06 +00:00
if ( thing - > Z ( ) > actor - > Z ( ) & &
2016-03-01 15:47:10 +00:00
thing - > Z ( ) < = actor - > Top ( ) )
{ // Thing intersects above the base
intersectors . Push ( thing ) ;
}
}
}
//=============================================================================
//
// P_FindBelowIntersectors
//
//=============================================================================
void P_FindBelowIntersectors ( AActor * actor )
{
if ( actor - > flags & MF_NOCLIP )
return ;
if ( ! ( actor - > flags & MF_SOLID ) )
return ;
2016-03-06 23:17:52 +00:00
FPortalGroupArray check ;
FMultiBlockThingsIterator it ( check , actor ) ;
FMultiBlockThingsIterator : : CheckResult cres ;
while ( it . Next ( & cres ) )
2016-03-01 15:47:10 +00:00
{
2016-03-06 23:17:52 +00:00
AActor * thing = cres . thing ;
2016-03-27 20:35:58 +00:00
double blockdist = actor - > radius + thing - > radius ;
if ( fabs ( thing - > X ( ) - cres . Position . X ) > = blockdist | | fabs ( thing - > Y ( ) - cres . Position . Y ) > = blockdist )
2016-03-01 15:47:10 +00:00
continue ;
2016-03-06 23:17:52 +00:00
2016-03-01 15:47:10 +00:00
if ( ! ( thing - > flags & MF_SOLID ) )
{ // Can't hit thing
continue ;
}
if ( thing - > flags & ( MF_SPECIAL ) )
{ // [RH] Corpses and specials don't block moves
continue ;
}
if ( thing - > flags & ( MF_CORPSE ) )
{ // Corpses need a few more checks
if ( ! ( actor - > flags & MF_ICECORPSE ) )
continue ;
}
if ( thing = = actor )
{ // Don't clip against self
continue ;
}
if ( ! ( ( thing - > flags2 | actor - > flags2 ) & MF2_PASSMOBJ ) & & ! ( ( thing - > flags3 | actor - > flags3 ) & MF3_ISMONSTER ) )
{
// Don't bother if both things don't have MF2_PASSMOBJ set and aren't monsters.
// These things would always block each other which in nearly every situation is
// not what is wanted here.
continue ;
}
if ( thing - > Top ( ) < = actor - > Top ( ) & &
thing - > Top ( ) > actor - > Z ( ) )
{ // Thing intersects below the base
intersectors . Push ( thing ) ;
}
}
}
//=============================================================================
//
// P_DoCrunch
//
//=============================================================================
void P_DoCrunch ( AActor * thing , FChangePosition * cpos )
{
if ( ! ( thing & & thing - > Grind ( true ) & & cpos ) ) return ;
cpos - > nofit = true ;
if ( ( cpos - > crushchange > 0 ) & & ! ( level . maptime & 3 ) )
{
int newdam = P_DamageMobj ( thing , NULL , NULL , cpos - > crushchange , NAME_Crush ) ;
// spray blood in a random direction
if ( ! ( thing - > flags2 & ( MF2_INVULNERABLE | MF2_DORMANT ) ) )
{
if ( ! ( thing - > flags & MF_NOBLOOD ) )
{
PalEntry bloodcolor = thing - > GetBloodColor ( ) ;
PClassActor * bloodcls = thing - > GetBloodType ( ) ;
P_TraceBleed ( newdam > 0 ? newdam : cpos - > crushchange , thing ) ;
if ( bloodcls ! = NULL )
{
AActor * mo ;
2016-03-23 09:42:41 +00:00
mo = Spawn ( bloodcls , thing - > PosPlusZ ( thing - > Height / 2 ) , ALLOW_REPLACE ) ;
2016-03-01 15:47:10 +00:00
2016-03-19 23:54:18 +00:00
mo - > Vel . X = pr_crunch . Random2 ( ) / 16. ;
mo - > Vel . Y = pr_crunch . Random2 ( ) / 16. ;
2016-03-01 15:47:10 +00:00
if ( bloodcolor ! = 0 & & ! ( mo - > flags2 & MF2_DONTTRANSLATE ) )
{
mo - > Translation = TRANSLATION ( TRANSLATION_Blood , bloodcolor . a ) ;
}
if ( ! ( cl_bloodtype < = 1 ) ) mo - > renderflags | = RF_INVISIBLE ;
}
2016-03-23 12:31:12 +00:00
DAngle an = ( M_Random ( ) - 128 ) * ( 360. / 256 ) ;
2016-03-01 15:47:10 +00:00
if ( cl_bloodtype > = 1 )
{
2016-03-23 12:31:12 +00:00
P_DrawSplash2 ( 32 , thing - > PosPlusZ ( thing - > Height / 2 ) , an , 2 , bloodcolor ) ;
2016-03-01 15:47:10 +00:00
}
}
if ( thing - > CrushPainSound ! = 0 & & ! S_GetSoundPlayingInfo ( thing , thing - > CrushPainSound ) )
{
S_Sound ( thing , CHAN_VOICE , thing - > CrushPainSound , 1.f , ATTN_NORM ) ;
}
}
}
// keep checking (crush other things)
return ;
}
//=============================================================================
//
// P_PushUp
//
// Returns 0 if thing fits, 1 if ceiling got in the way, or 2 if something
// above it didn't fit.
//=============================================================================
int P_PushUp ( AActor * thing , FChangePosition * cpos )
{
unsigned int firstintersect = intersectors . Size ( ) ;
unsigned int lastintersect ;
int mymass = thing - > Mass ;
2016-03-20 12:32:53 +00:00
if ( thing - > Top ( ) > thing - > ceilingz )
2016-03-01 15:47:10 +00:00
{
return 1 ;
}
// [GZ] Skip thing intersect test for THRUACTORS things.
if ( thing - > flags2 & MF2_THRUACTORS )
return 0 ;
P_FindAboveIntersectors ( thing ) ;
lastintersect = intersectors . Size ( ) ;
for ( ; firstintersect < lastintersect ; firstintersect + + )
{
AActor * intersect = intersectors [ firstintersect ] ;
// [GZ] Skip this iteration for THRUSPECIES things
// Should there be MF2_THRUGHOST / MF3_GHOST checks there too for consistency?
// Or would that risk breaking established behavior? THRUGHOST, like MTHRUSPECIES,
// is normally for projectiles which would have exploded by now anyway...
if ( thing - > flags6 & MF6_THRUSPECIES & & thing - > GetSpecies ( ) = = intersect - > GetSpecies ( ) )
continue ;
if ( ( thing - > flags & MF_MISSILE ) & & ( intersect - > flags2 & MF2_REFLECTIVE ) & & ( intersect - > flags7 & MF7_THRUREFLECT ) )
continue ;
if ( ! ( intersect - > flags2 & MF2_PASSMOBJ ) | |
( ! ( intersect - > flags3 & MF3_ISMONSTER ) & & intersect - > Mass > mymass ) | |
( intersect - > flags4 & MF4_ACTLIKEBRIDGE )
)
{
// Can't push bridges or things more massive than ourself
return 2 ;
}
2016-03-27 20:35:58 +00:00
double oldz = intersect - > Z ( ) ;
2016-03-01 15:47:10 +00:00
P_AdjustFloorCeil ( intersect , cpos ) ;
2016-03-27 20:35:58 +00:00
intersect - > SetZ ( thing - > Top ( ) + 1 / 65536. ) ;
2016-03-01 15:47:10 +00:00
if ( P_PushUp ( intersect , cpos ) )
{ // Move blocked
P_DoCrunch ( intersect , cpos ) ;
2016-03-27 20:35:58 +00:00
intersect - > SetZ ( oldz ) ;
2016-03-01 15:47:10 +00:00
return 2 ;
}
2016-04-17 21:48:04 +00:00
intersect - > UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-06 23:17:52 +00:00
thing - > CheckPortalTransition ( true ) ;
2016-03-01 15:47:10 +00:00
return 0 ;
}
//=============================================================================
//
// P_PushDown
//
// Returns 0 if thing fits, 1 if floor got in the way, or 2 if something
// below it didn't fit.
//=============================================================================
int P_PushDown ( AActor * thing , FChangePosition * cpos )
{
unsigned int firstintersect = intersectors . Size ( ) ;
unsigned int lastintersect ;
int mymass = thing - > Mass ;
2016-03-20 18:52:35 +00:00
if ( thing - > Z ( ) < = thing - > floorz )
2016-03-01 15:47:10 +00:00
{
return 1 ;
}
P_FindBelowIntersectors ( thing ) ;
lastintersect = intersectors . Size ( ) ;
for ( ; firstintersect < lastintersect ; firstintersect + + )
{
AActor * intersect = intersectors [ firstintersect ] ;
if ( ! ( intersect - > flags2 & MF2_PASSMOBJ ) | |
( ! ( intersect - > flags3 & MF3_ISMONSTER ) & & intersect - > Mass > mymass ) | |
( intersect - > flags4 & MF4_ACTLIKEBRIDGE )
)
{
// Can't push bridges or things more massive than ourself
return 2 ;
}
2016-03-27 20:35:58 +00:00
double oldz = intersect - > Z ( ) ;
2016-03-01 15:47:10 +00:00
P_AdjustFloorCeil ( intersect , cpos ) ;
2016-03-27 20:35:58 +00:00
if ( oldz > thing - > Z ( ) - intersect - > Height )
2016-03-01 15:47:10 +00:00
{ // Only push things down, not up.
2016-03-27 20:35:58 +00:00
intersect - > SetZ ( thing - > Z ( ) - intersect - > Height ) ;
2016-03-01 15:47:10 +00:00
if ( P_PushDown ( intersect , cpos ) )
{ // Move blocked
P_DoCrunch ( intersect , cpos ) ;
2016-03-27 20:35:58 +00:00
intersect - > SetZ ( oldz ) ;
2016-03-01 15:47:10 +00:00
return 2 ;
}
2016-04-17 21:48:04 +00:00
intersect - > UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-06 23:17:52 +00:00
thing - > CheckPortalTransition ( true ) ;
2016-03-01 15:47:10 +00:00
return 0 ;
}
//=============================================================================
//
// PIT_FloorDrop
//
//=============================================================================
void PIT_FloorDrop ( AActor * thing , FChangePosition * cpos )
{
2016-03-27 20:35:58 +00:00
double oldfloorz = thing - > floorz ;
double oldz = thing - > Z ( ) ;
2016-03-01 15:47:10 +00:00
P_AdjustFloorCeil ( thing , cpos ) ;
2016-03-27 20:35:58 +00:00
if ( oldfloorz = = thing - > floorz ) return ;
2016-03-01 15:47:10 +00:00
if ( thing - > flags4 & MF4_ACTLIKEBRIDGE ) return ; // do not move bridge things
2016-03-27 20:35:58 +00:00
if ( thing - > Vel . Z = = 0 & &
2016-03-01 15:47:10 +00:00
( ! ( thing - > flags & MF_NOGRAVITY ) | |
2016-03-27 20:35:58 +00:00
( thing - > Z ( ) = = oldfloorz & & ! ( thing - > flags & MF_NOLIFTDROP ) ) ) )
2016-03-01 15:47:10 +00:00
{
if ( ( thing - > flags & MF_NOGRAVITY ) | | ( thing - > flags5 & MF5_MOVEWITHSECTOR ) | |
2016-03-27 20:35:58 +00:00
( ( ( cpos - > sector - > Flags & SECF_FLOORDROP ) | | cpos - > moveamt < 9 )
& & thing - > Z ( ) - thing - > floorz < = cpos - > moveamt ) )
2016-03-01 15:47:10 +00:00
{
2016-03-20 18:52:35 +00:00
thing - > SetZ ( thing - > floorz ) ;
2016-03-01 15:47:10 +00:00
P_CheckFakeFloorTriggers ( thing , oldz ) ;
2016-04-17 21:48:04 +00:00
thing - > UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-27 20:35:58 +00:00
else if ( ( thing - > Z ( ) ! = oldfloorz & & ! ( thing - > flags & MF_NOLIFTDROP ) ) )
2016-03-01 15:47:10 +00:00
{
if ( ( thing - > flags & MF_NOGRAVITY ) & & ( thing - > flags6 & MF6_RELATIVETOFLOOR ) )
{
2016-03-27 20:35:58 +00:00
thing - > AddZ ( - oldfloorz + thing - > floorz ) ;
2016-03-01 15:47:10 +00:00
P_CheckFakeFloorTriggers ( thing , oldz ) ;
2016-04-17 21:48:04 +00:00
thing - > UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
}
}
if ( thing - > player & & thing - > player - > mo = = thing )
{
2016-03-27 20:35:58 +00:00
thing - > player - > viewz + = thing - > Z ( ) - oldz ;
2016-03-01 15:47:10 +00:00
}
}
//=============================================================================
//
// PIT_FloorRaise
//
//=============================================================================
void PIT_FloorRaise ( AActor * thing , FChangePosition * cpos )
{
2016-03-27 20:35:58 +00:00
double oldfloorz = thing - > floorz ;
double oldz = thing - > Z ( ) ;
2016-03-01 15:47:10 +00:00
P_AdjustFloorCeil ( thing , cpos ) ;
2016-03-27 20:35:58 +00:00
if ( oldfloorz = = thing - > floorz ) return ;
2016-03-01 15:47:10 +00:00
// Move things intersecting the floor up
2016-03-20 18:52:35 +00:00
if ( thing - > Z ( ) < = thing - > floorz )
2016-03-01 15:47:10 +00:00
{
if ( thing - > flags4 & MF4_ACTLIKEBRIDGE )
{
cpos - > nofit = true ;
return ; // do not move bridge things
}
intersectors . Clear ( ) ;
2016-03-20 18:52:35 +00:00
thing - > SetZ ( thing - > floorz ) ;
2016-03-01 15:47:10 +00:00
}
else
{
if ( ( thing - > flags & MF_NOGRAVITY ) & & ( thing - > flags6 & MF6_RELATIVETOFLOOR ) )
{
intersectors . Clear ( ) ;
2016-03-27 20:35:58 +00:00
thing - > AddZ ( - oldfloorz + thing - > floorz ) ;
2016-03-01 15:47:10 +00:00
}
else return ;
}
switch ( P_PushUp ( thing , cpos ) )
{
default :
P_CheckFakeFloorTriggers ( thing , oldz ) ;
2016-04-17 21:48:04 +00:00
thing - > UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
break ;
case 1 :
P_DoCrunch ( thing , cpos ) ;
P_CheckFakeFloorTriggers ( thing , oldz ) ;
2016-04-17 21:48:04 +00:00
thing - > UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
break ;
case 2 :
P_DoCrunch ( thing , cpos ) ;
2016-03-27 20:35:58 +00:00
thing - > SetZ ( oldz ) ;
2016-03-01 15:47:10 +00:00
break ;
}
if ( thing - > player & & thing - > player - > mo = = thing )
{
2016-03-27 20:35:58 +00:00
thing - > player - > viewz + = thing - > Z ( ) - oldz ;
2016-03-01 15:47:10 +00:00
}
}
//=============================================================================
//
// PIT_CeilingLower
//
//=============================================================================
void PIT_CeilingLower ( AActor * thing , FChangePosition * cpos )
{
bool onfloor ;
2016-03-27 20:35:58 +00:00
double oldz = thing - > Z ( ) ;
2016-03-01 15:47:10 +00:00
2016-03-20 18:52:35 +00:00
onfloor = thing - > Z ( ) < = thing - > floorz ;
2016-03-01 15:47:10 +00:00
P_AdjustFloorCeil ( thing , cpos ) ;
2016-03-20 12:32:53 +00:00
if ( thing - > Top ( ) > thing - > ceilingz )
2016-03-01 15:47:10 +00:00
{
if ( thing - > flags4 & MF4_ACTLIKEBRIDGE )
{
cpos - > nofit = true ;
return ; // do not move bridge things
}
intersectors . Clear ( ) ;
2016-03-20 19:55:06 +00:00
if ( thing - > ceilingz - thing - > Height > = thing - > floorz )
2016-03-01 15:47:10 +00:00
{
2016-03-20 19:55:06 +00:00
thing - > SetZ ( thing - > ceilingz - thing - > Height ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-20 18:52:35 +00:00
thing - > SetZ ( thing - > floorz ) ;
2016-03-01 15:47:10 +00:00
}
2016-04-17 21:48:04 +00:00
thing - > UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
switch ( P_PushDown ( thing , cpos ) )
{
case 2 :
// intentional fall-through
case 1 :
if ( onfloor )
2016-03-20 18:52:35 +00:00
thing - > SetZ ( thing - > floorz ) ;
2016-03-01 15:47:10 +00:00
P_DoCrunch ( thing , cpos ) ;
P_CheckFakeFloorTriggers ( thing , oldz ) ;
2016-04-17 21:48:04 +00:00
thing - > UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
break ;
default :
P_CheckFakeFloorTriggers ( thing , oldz ) ;
2016-04-17 21:48:04 +00:00
thing - > UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
break ;
}
}
if ( thing - > player & & thing - > player - > mo = = thing )
{
2016-03-27 20:35:58 +00:00
thing - > player - > viewz + = thing - > Z ( ) - oldz ;
2016-03-01 15:47:10 +00:00
}
}
//=============================================================================
//
// PIT_CeilingRaise
//
//=============================================================================
void PIT_CeilingRaise ( AActor * thing , FChangePosition * cpos )
{
bool isgood = P_AdjustFloorCeil ( thing , cpos ) ;
2016-03-27 20:35:58 +00:00
double oldz = thing - > Z ( ) ;
2016-03-01 15:47:10 +00:00
if ( thing - > flags4 & MF4_ACTLIKEBRIDGE ) return ; // do not move bridge things
// For DOOM compatibility, only move things that are inside the floor.
// (or something else?) Things marked as hanging from the ceiling will
// stay where they are.
2016-03-27 20:35:58 +00:00
if ( thing - > Z ( ) < thing - > floorz & &
thing - > Top ( ) > = thing - > ceilingz - cpos - > moveamt & &
2016-03-01 15:47:10 +00:00
! ( thing - > flags & MF_NOLIFTDROP ) )
{
2016-03-20 18:52:35 +00:00
thing - > SetZ ( thing - > floorz ) ;
2016-03-20 12:32:53 +00:00
if ( thing - > Top ( ) > thing - > ceilingz )
2016-03-01 15:47:10 +00:00
{
2016-03-20 19:55:06 +00:00
thing - > SetZ ( thing - > ceilingz - thing - > Height ) ;
2016-03-01 15:47:10 +00:00
}
P_CheckFakeFloorTriggers ( thing , oldz ) ;
2016-04-17 21:48:04 +00:00
thing - > UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-20 12:32:53 +00:00
else if ( ( thing - > flags2 & MF2_PASSMOBJ ) & & ! isgood & & thing - > Top ( ) < thing - > ceilingz )
2016-03-01 15:47:10 +00:00
{
AActor * onmobj ;
2016-03-20 12:32:53 +00:00
if ( ! P_TestMobjZ ( thing , true , & onmobj ) & & onmobj - > Z ( ) < = thing - > Z ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-20 19:55:06 +00:00
thing - > SetZ ( MIN ( thing - > ceilingz - thing - > Height , onmobj - > Top ( ) ) ) ;
2016-04-17 21:48:04 +00:00
thing - > UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
}
}
if ( thing - > player & & thing - > player - > mo = = thing )
{
2016-03-27 20:35:58 +00:00
thing - > player - > viewz + = thing - > Z ( ) - oldz ;
2016-03-01 15:47:10 +00:00
}
}
//=============================================================================
//
// P_ChangeSector [RH] Was P_CheckSector in BOOM
//
// jff 3/19/98 added to just check monsters on the periphery
// of a moving sector instead of all in bounding box of the
// sector. Both more accurate and faster.
//
//=============================================================================
2016-03-27 20:35:58 +00:00
bool P_ChangeSector ( sector_t * sector , int crunch , double amt , int floorOrCeil , bool isreset )
2016-03-01 15:47:10 +00:00
{
FChangePosition cpos ;
void ( * iterator ) ( AActor * , FChangePosition * ) ;
void ( * iterator2 ) ( AActor * , FChangePosition * ) = NULL ;
msecnode_t * n ;
cpos . nofit = false ;
cpos . crushchange = crunch ;
2016-03-27 20:35:58 +00:00
cpos . moveamt = fabs ( amt ) ;
2016-03-01 15:47:10 +00:00
cpos . movemidtex = false ;
cpos . sector = sector ;
// Also process all sectors that have 3D floors transferred from the
// changed sector.
if ( sector - > e - > XFloor . attached . Size ( ) & & floorOrCeil ! = 2 )
{
unsigned i ;
sector_t * sec ;
// Use different functions for the four different types of sector movement.
// for 3D-floors the meaning of floor and ceiling is inverted!!!
if ( floorOrCeil = = 1 )
{
iterator = ( amt > = 0 ) ? PIT_FloorRaise : PIT_FloorDrop ;
}
else
{
iterator = ( amt > = 0 ) ? PIT_CeilingRaise : PIT_CeilingLower ;
}
for ( i = 0 ; i < sector - > e - > XFloor . attached . Size ( ) ; i + + )
{
sec = sector - > e - > XFloor . attached [ i ] ;
P_Recalculate3DFloors ( sec ) ; // Must recalculate the 3d floor and light lists
// no thing checks for attached sectors because of heightsec
if ( sec - > heightsec = = sector ) continue ;
for ( n = sec - > touching_thinglist ; n ; n = n - > m_snext ) n - > visited = false ;
do
{
for ( n = sec - > touching_thinglist ; n ; n = n - > m_snext )
{
if ( ! n - > visited )
{
n - > visited = true ;
if ( ! ( n - > m_thing - > flags & MF_NOBLOCKMAP ) | | //jff 4/7/98 don't do these
( n - > m_thing - > flags5 & MF5_MOVEWITHSECTOR ) )
{
iterator ( n - > m_thing , & cpos ) ;
}
break ;
}
}
} while ( n ) ;
sec - > CheckPortalPlane ( ! floorOrCeil ) ;
}
}
P_Recalculate3DFloors ( sector ) ; // Must recalculate the 3d floor and light lists
// [RH] Use different functions for the four different types of sector
// movement.
switch ( floorOrCeil )
{
case 0 :
// floor
iterator = ( amt < 0 ) ? PIT_FloorDrop : PIT_FloorRaise ;
break ;
case 1 :
// ceiling
iterator = ( amt < 0 ) ? PIT_CeilingLower : PIT_CeilingRaise ;
break ;
case 2 :
// 3dmidtex
// This must check both floor and ceiling
iterator = ( amt < 0 ) ? PIT_FloorDrop : PIT_FloorRaise ;
iterator2 = ( amt < 0 ) ? PIT_CeilingLower : PIT_CeilingRaise ;
cpos . movemidtex = true ;
break ;
default :
// invalid
assert ( floorOrCeil > 0 & & floorOrCeil < 2 ) ;
return false ;
}
// killough 4/4/98: scan list front-to-back until empty or exhausted,
// restarting from beginning after each thing is processed. Avoids
// crashes, and is sure to examine all things in the sector, and only
// the things which are in the sector, until a steady-state is reached.
// Things can arbitrarily be inserted and removed and it won't mess up.
//
// killough 4/7/98: simplified to avoid using complicated counter
// Mark all things invalid
for ( n = sector - > touching_thinglist ; n ; n = n - > m_snext )
n - > visited = false ;
do
{
for ( n = sector - > touching_thinglist ; n ; n = n - > m_snext ) // go through list
{
if ( ! n - > visited ) // unprocessed thing found
{
n - > visited = true ; // mark thing as processed
if ( ! ( n - > m_thing - > flags & MF_NOBLOCKMAP ) | | //jff 4/7/98 don't do these
( n - > m_thing - > flags5 & MF5_MOVEWITHSECTOR ) )
{
iterator ( n - > m_thing , & cpos ) ; // process it
if ( iterator2 ! = NULL ) iterator2 ( n - > m_thing , & cpos ) ;
}
break ; // exit and start over
}
}
} while ( n ) ; // repeat from scratch until all things left are marked valid
if ( floorOrCeil ! = 2 ) sector - > CheckPortalPlane ( floorOrCeil ) ; // check for portal obstructions after everything is done.
if ( ! cpos . nofit & & ! isreset /* && sector->MoreFlags & (SECF_UNDERWATERMASK)*/ )
{
// If this is a control sector for a deep water transfer, all actors in affected
// sectors need to have their waterlevel information updated and if applicable,
// execute appropriate sector actions.
// Only check if the sector move was successful.
TArray < sector_t * > & secs = sector - > e - > FakeFloor . Sectors ;
for ( unsigned i = 0 ; i < secs . Size ( ) ; i + + )
{
sector_t * s = secs [ i ] ;
for ( n = s - > touching_thinglist ; n ; n = n - > m_snext )
n - > visited = false ;
do
{
for ( n = s - > touching_thinglist ; n ; n = n - > m_snext ) // go through list
{
if ( ! n - > visited & & n - > m_thing - > Sector = = s ) // unprocessed thing found
{
n - > visited = true ; // mark thing as processed
2016-03-26 00:13:36 +00:00
n - > m_thing - > UpdateWaterLevel ( false ) ;
2016-03-27 20:35:58 +00:00
P_CheckFakeFloorTriggers ( n - > m_thing , n - > m_thing - > Z ( ) - amt ) ;
2016-03-01 15:47:10 +00:00
}
}
} while ( n ) ; // repeat from scratch until all things left are marked valid
}
}
return cpos . nofit ;
}
//=============================================================================
// phares 3/21/98
//
// Maintain a freelist of msecnode_t's to reduce memory allocs and frees.
//=============================================================================
msecnode_t * headsecnode = NULL ;
//=============================================================================
//
// P_GetSecnode
//
// Retrieve a node from the freelist. The calling routine
// should make sure it sets all fields properly.
//
//=============================================================================
msecnode_t * P_GetSecnode ( )
{
msecnode_t * node ;
if ( headsecnode )
{
node = headsecnode ;
headsecnode = headsecnode - > m_snext ;
}
else
{
node = ( msecnode_t * ) M_Malloc ( sizeof ( * node ) ) ;
}
return node ;
}
//=============================================================================
//
// P_PutSecnode
//
// Returns a node to the freelist.
//
//=============================================================================
void P_PutSecnode ( msecnode_t * node )
{
node - > m_snext = headsecnode ;
headsecnode = node ;
}
//=============================================================================
// phares 3/16/98
//
// P_AddSecnode
//
// Searches the current list to see if this sector is
// already there. If not, it adds a sector node at the head of the list of
// sectors this object appears in. This is called when creating a list of
// nodes that will get linked in later. Returns a pointer to the new node.
//
//=============================================================================
2016-04-17 21:48:04 +00:00
msecnode_t * P_AddSecnode ( sector_t * s , AActor * thing , msecnode_t * nextnode , msecnode_t * & sec_thinglist )
2016-03-01 15:47:10 +00:00
{
msecnode_t * node ;
if ( s = = 0 )
{
I_FatalError ( " AddSecnode of 0 for %s \n " , thing - > GetClass ( ) - > TypeName . GetChars ( ) ) ;
}
node = nextnode ;
while ( node )
{
if ( node - > m_sector = = s ) // Already have a node for this sector?
{
node - > m_thing = thing ; // Yes. Setting m_thing says 'keep it'.
return nextnode ;
}
node = node - > m_tnext ;
}
// Couldn't find an existing node for this sector. Add one at the head
// of the list.
node = P_GetSecnode ( ) ;
// killough 4/4/98, 4/7/98: mark new nodes unvisited.
node - > visited = 0 ;
node - > m_sector = s ; // sector
node - > m_thing = thing ; // mobj
node - > m_tprev = NULL ; // prev node on Thing thread
node - > m_tnext = nextnode ; // next node on Thing thread
if ( nextnode )
nextnode - > m_tprev = node ; // set back link on Thing
// Add new node at head of sector thread starting at s->touching_thinglist
node - > m_sprev = NULL ; // prev node on sector thread
2016-04-17 21:48:04 +00:00
node - > m_snext = sec_thinglist ; // next node on sector thread
if ( sec_thinglist )
2016-03-01 15:47:10 +00:00
node - > m_snext - > m_sprev = node ;
2016-04-17 21:48:04 +00:00
sec_thinglist = node ;
2016-03-01 15:47:10 +00:00
return node ;
}
//=============================================================================
//
// P_DelSecnode
//
// Deletes a sector node from the list of
// sectors this object appears in. Returns a pointer to the next node
// on the linked list, or NULL.
//
//=============================================================================
2016-04-17 21:48:04 +00:00
msecnode_t * P_DelSecnode ( msecnode_t * node , msecnode_t * sector_t : : * listhead )
2016-03-01 15:47:10 +00:00
{
msecnode_t * tp ; // prev node on thing thread
msecnode_t * tn ; // next node on thing thread
msecnode_t * sp ; // prev node on sector thread
msecnode_t * sn ; // next node on sector thread
if ( node )
{
// Unlink from the Thing thread. The Thing thread begins at
// sector_list and not from AActor->touching_sectorlist.
tp = node - > m_tprev ;
tn = node - > m_tnext ;
if ( tp )
tp - > m_tnext = tn ;
if ( tn )
tn - > m_tprev = tp ;
// Unlink from the sector thread. This thread begins at
// sector_t->touching_thinglist.
sp = node - > m_sprev ;
sn = node - > m_snext ;
if ( sp )
sp - > m_snext = sn ;
else
2016-04-17 21:48:04 +00:00
node - > m_sector - > * listhead = sn ;
2016-03-01 15:47:10 +00:00
if ( sn )
sn - > m_sprev = sp ;
// Return this node to the freelist
P_PutSecnode ( node ) ;
return tn ;
}
return NULL ;
} // phares 3/13/98
//=============================================================================
//
// P_DelSector_List
//
// Deletes the sector_list and NULLs it.
//
//=============================================================================
void P_DelSector_List ( )
{
if ( sector_list ! = NULL )
{
P_DelSeclist ( sector_list ) ;
sector_list = NULL ;
}
}
//=============================================================================
//
// P_DelSeclist
//
// Delete an entire sector list
//
//=============================================================================
void P_DelSeclist ( msecnode_t * node )
{
while ( node )
2016-04-17 21:48:04 +00:00
node = P_DelSecnode ( node , & sector_t : : touching_thinglist ) ;
2016-03-01 15:47:10 +00:00
}
//=============================================================================
// phares 3/14/98
//
// P_CreateSecNodeList
//
// Alters/creates the sector_list that shows what sectors the object resides in
//
//=============================================================================
2016-03-27 20:49:59 +00:00
void P_CreateSecNodeList ( AActor * thing )
2016-03-01 15:47:10 +00:00
{
msecnode_t * node ;
// First, clear out the existing m_thing fields. As each node is
// added or verified as needed, m_thing will be set properly. When
// finished, delete all nodes where m_thing is still NULL. These
// represent the sectors the Thing has vacated.
node = sector_list ;
while ( node )
{
node - > m_thing = NULL ;
node = node - > m_tnext ;
}
2016-04-17 22:02:07 +00:00
FBoundingBox box ( thing - > X ( ) , thing - > Y ( ) , thing - > radius ) ;
FBlockLinesIterator it ( box ) ;
line_t * ld ;
2016-03-01 15:47:10 +00:00
2016-04-17 22:02:07 +00:00
while ( ( ld = it . Next ( ) ) )
2016-03-01 15:47:10 +00:00
{
2016-04-17 22:02:07 +00:00
if ( ! box . inRange ( ld ) | | box . BoxOnLineSide ( ld ) ! = - 1 )
2016-03-01 15:47:10 +00:00
continue ;
// This line crosses through the object.
// Collect the sector(s) from the line and add to the
// sector_list you're examining. If the Thing ends up being
// allowed to move to this position, then the sector_list
// will be attached to the Thing's AActor at touching_sectorlist.
2016-04-17 21:48:04 +00:00
sector_list = P_AddSecnode ( ld - > frontsector , thing , sector_list , ld - > frontsector - > touching_thinglist ) ;
2016-03-01 15:47:10 +00:00
// Don't assume all lines are 2-sided, since some Things
2016-03-25 17:19:54 +00:00
// like MT_TFOG are allowed regardless of whether their radius takes
2016-03-01 15:47:10 +00:00
// them beyond an impassable linedef.
// killough 3/27/98, 4/4/98:
// Use sidedefs instead of 2s flag to determine two-sidedness.
if ( ld - > backsector )
2016-04-17 21:48:04 +00:00
sector_list = P_AddSecnode ( ld - > backsector , thing , sector_list , ld - > backsector - > touching_thinglist ) ;
2016-03-01 15:47:10 +00:00
}
// Add the sector of the (x,y) point to sector_list.
2016-04-17 21:48:04 +00:00
sector_list = P_AddSecnode ( thing - > Sector , thing , sector_list , thing - > Sector - > touching_thinglist ) ;
2016-03-01 15:47:10 +00:00
// Now delete any nodes that won't be used. These are the ones where
// m_thing is still NULL.
node = sector_list ;
while ( node )
{
if ( node - > m_thing = = NULL )
{
if ( node = = sector_list )
sector_list = node - > m_tnext ;
2016-04-17 21:48:04 +00:00
node = P_DelSecnode ( node , & sector_t : : touching_thinglist ) ;
2016-03-01 15:47:10 +00:00
}
else
{
node = node - > m_tnext ;
}
}
}
2016-04-17 21:48:04 +00:00
2016-04-21 20:59:07 +00:00
//=============================================================================
//
// P_DelPortalnode
//
// Same for line portal nodes
//
//=============================================================================
portnode_t * P_DelPortalnode ( portnode_t * node )
{
portnode_t * tp ; // prev node on thing thread
portnode_t * tn ; // next node on thing thread
portnode_t * sp ; // prev node on sector thread
portnode_t * sn ; // next node on sector thread
if ( node )
{
// Unlink from the Thing thread. The Thing thread begins at
// sector_list and not from AActor->touching_sectorlist.
tp = node - > m_tprev ;
tn = node - > m_tnext ;
if ( tp )
tp - > m_tnext = tn ;
if ( tn )
tn - > m_tprev = tp ;
// Unlink from the sector thread. This thread begins at
// sector_t->touching_thinglist.
sp = node - > m_sprev ;
sn = node - > m_snext ;
if ( sp )
sp - > m_snext = sn ;
else
node - > m_portal - > render_thinglist = sn ;
if ( sn )
sn - > m_sprev = sp ;
// Return this node to the freelist (use the same one as for msecnodes, since both types are the same size.)
P_PutSecnode ( reinterpret_cast < msecnode_t * > ( node ) ) ;
return tn ;
}
return NULL ;
}
//=============================================================================
//
// P_AddPortalnode
//
//=============================================================================
portnode_t * P_AddPortalnode ( FLinePortal * s , AActor * thing , portnode_t * nextnode )
{
portnode_t * node ;
if ( s = = 0 )
{
I_FatalError ( " AddSecnode of 0 for %s \n " , thing - > GetClass ( ) - > TypeName . GetChars ( ) ) ;
}
node = reinterpret_cast < portnode_t * > ( P_GetSecnode ( ) ) ;
// killough 4/4/98, 4/7/98: mark new nodes unvisited.
node - > visited = 0 ;
node - > m_portal = s ; // portal
node - > m_thing = thing ; // mobj
node - > m_tprev = NULL ; // prev node on Thing thread
node - > m_tnext = nextnode ; // next node on Thing thread
if ( nextnode )
nextnode - > m_tprev = node ; // set back link on Thing
// Add new node at head of portal thread starting at s->touching_thinglist
node - > m_sprev = NULL ; // prev node on portal thread
node - > m_snext = s - > render_thinglist ; // next node on portal thread
if ( s - > render_thinglist )
node - > m_snext - > m_sprev = node ;
s - > render_thinglist = node ;
return node ;
}
2016-04-17 21:48:04 +00:00
//==========================================================================
//
// Handle the lists used to render actors from other portal areas
//
//==========================================================================
void AActor : : UpdateRenderSectorList ( )
{
static const double SPRITE_SPACE = 64. ;
2016-04-17 23:35:37 +00:00
if ( Pos ( ) ! = OldRenderPos & & ! ( flags & MF_NOSECTOR ) )
2016-04-17 21:48:04 +00:00
{
2016-04-21 20:59:07 +00:00
// Only check if the map contains line portals
ClearRenderLineList ( ) ;
if ( PortalBlockmap . containsLines & & Pos ( ) . XY ( ) ! = OldRenderPos . XY ( ) )
{
int bx = GetBlockX ( X ( ) ) ;
2016-04-29 21:14:04 +00:00
int by = GetBlockY ( Y ( ) ) ;
2016-04-21 20:59:07 +00:00
FBoundingBox bb ( X ( ) , Y ( ) , MIN ( radius * 1.5 , 128. ) ) ; // Don't go further than 128 map units, even for large actors
// Are there any portals near the actor's position?
if ( bx > = 0 & & by > = 0 & & bx < bmapwidth & & by < bmapheight & & PortalBlockmap ( bx , by ) . neighborContainsLines )
{
// Go through the entire list. In most cases this is faster than setting up a blockmap iterator
for ( auto & p : linePortals )
{
if ( p . mType = = PORTT_VISUAL ) continue ;
if ( bb . inRange ( p . mOrigin ) & & bb . BoxOnLineSide ( p . mOrigin ) )
{
render_portallist = P_AddPortalnode ( & p , this , render_portallist ) ;
}
}
}
}
2016-04-17 21:48:04 +00:00
sector_t * sec = Sector ;
double lasth = - FLT_MAX ;
ClearRenderSectorList ( ) ;
while ( ! sec - > PortalBlocksMovement ( sector_t : : ceiling ) )
{
2016-04-19 09:35:28 +00:00
double planeh = sec - > GetPortalPlaneZ ( sector_t : : ceiling ) ;
2016-05-24 11:53:29 +00:00
if ( planeh < = lasth ) break ; // broken setup.
2016-04-17 21:48:04 +00:00
if ( Top ( ) + SPRITE_SPACE < planeh ) break ;
lasth = planeh ;
2016-04-19 09:35:28 +00:00
DVector2 newpos = Pos ( ) + sec - > GetPortalDisplacement ( sector_t : : ceiling ) ;
2016-04-17 21:48:04 +00:00
sec = P_PointInSector ( newpos ) ;
render_sectorlist = P_AddSecnode ( sec , this , render_sectorlist , sec - > render_thinglist ) ;
}
lasth = FLT_MAX ;
while ( ! sec - > PortalBlocksMovement ( sector_t : : floor ) )
{
2016-04-19 09:35:28 +00:00
double planeh = sec - > GetPortalPlaneZ ( sector_t : : floor ) ;
2016-05-24 11:53:29 +00:00
if ( planeh > = lasth ) break ; // broken setup.
2016-04-17 21:48:04 +00:00
if ( Z ( ) - SPRITE_SPACE > planeh ) break ;
lasth = planeh ;
2016-04-19 09:35:28 +00:00
DVector2 newpos = Pos ( ) + sec - > GetPortalDisplacement ( sector_t : : floor ) ;
2016-04-17 21:48:04 +00:00
sec = P_PointInSector ( newpos ) ;
render_sectorlist = P_AddSecnode ( sec , this , render_sectorlist , sec - > render_thinglist ) ;
}
}
}
void AActor : : ClearRenderSectorList ( )
{
msecnode_t * node = render_sectorlist ;
while ( node )
node = P_DelSecnode ( node , & sector_t : : render_thinglist ) ;
render_sectorlist = NULL ;
}
2016-04-21 20:59:07 +00:00
void AActor : : ClearRenderLineList ( )
{
portnode_t * node = render_portallist ;
while ( node )
node = P_DelPortalnode ( node ) ;
render_portallist = NULL ;
}
2016-04-17 21:48:04 +00:00
2016-03-01 15:47:10 +00:00
//==========================================================================
//
//
//
//==========================================================================
void SpawnShootDecal ( AActor * t1 , const FTraceResults & trace )
{
FDecalBase * decalbase = NULL ;
if ( t1 - > player ! = NULL & & t1 - > player - > ReadyWeapon ! = NULL )
{
decalbase = t1 - > player - > ReadyWeapon - > GetDefault ( ) - > DecalGenerator ;
}
else
{
decalbase = t1 - > DecalGenerator ;
}
if ( decalbase ! = NULL )
{
DImpactDecal : : StaticCreate ( decalbase - > GetDecal ( ) ,
2016-03-23 17:07:04 +00:00
trace . HitPos , trace . Line - > sidedef [ trace . Side ] , trace . ffloor ) ;
2016-03-01 15:47:10 +00:00
}
}
//==========================================================================
//
//
//
//==========================================================================
2016-03-06 20:58:36 +00:00
static void SpawnDeepSplash ( AActor * t1 , const FTraceResults & trace , AActor * puff )
2016-03-01 15:47:10 +00:00
{
2016-03-23 17:07:04 +00:00
const DVector3 * hitpos ;
2016-03-06 20:58:36 +00:00
if ( trace . Crossed3DWater )
{
hitpos = & trace . Crossed3DWaterPos ;
}
2016-03-01 15:47:10 +00:00
else if ( trace . CrossedWater & & trace . CrossedWater - > heightsec )
{
2016-03-06 20:58:36 +00:00
hitpos = & trace . CrossedWaterPos ;
2016-03-01 15:47:10 +00:00
}
2016-03-06 20:58:36 +00:00
else return ;
2016-03-23 17:07:04 +00:00
P_HitWater ( puff ! = NULL ? puff : t1 , P_PointInSector ( * hitpos ) , * hitpos ) ;
2016-03-01 15:47:10 +00:00
}
//=============================================================================
//
// P_ActivateThingSpecial
//
// Handles the code for things activated by death, USESPECIAL or BUMPSPECIAL
//
//=============================================================================
bool P_ActivateThingSpecial ( AActor * thing , AActor * trigger , bool death )
{
bool res = false ;
// Target switching mechanism
if ( thing - > activationtype & THINGSPEC_ThingTargets ) thing - > target = trigger ;
if ( thing - > activationtype & THINGSPEC_TriggerTargets ) trigger - > target = thing ;
// State change mechanism. The thing needs to be not dead and to have at least one of the relevant flags
if ( ! death & & ( thing - > activationtype & ( THINGSPEC_Activate | THINGSPEC_Deactivate | THINGSPEC_Switch ) ) )
{
// If a switchable thing does not know whether it should be activated
// or deactivated, the default is to activate it.
if ( ( thing - > activationtype & THINGSPEC_Switch )
& & ! ( thing - > activationtype & ( THINGSPEC_Activate | THINGSPEC_Deactivate ) ) )
{
thing - > activationtype | = THINGSPEC_Activate ;
}
// Can it be activated?
if ( thing - > activationtype & THINGSPEC_Activate )
{
thing - > activationtype & = ~ THINGSPEC_Activate ; // Clear flag
if ( thing - > activationtype & THINGSPEC_Switch ) // Set other flag if switching
thing - > activationtype | = THINGSPEC_Deactivate ;
2016-11-24 20:36:02 +00:00
thing - > CallActivate ( trigger ) ;
2016-03-01 15:47:10 +00:00
res = true ;
}
// If not, can it be deactivated?
else if ( thing - > activationtype & THINGSPEC_Deactivate )
{
thing - > activationtype & = ~ THINGSPEC_Deactivate ; // Clear flag
if ( thing - > activationtype & THINGSPEC_Switch ) // Set other flag if switching
thing - > activationtype | = THINGSPEC_Activate ;
2016-11-24 20:36:02 +00:00
thing - > CallDeactivate ( trigger ) ;
2016-03-01 15:47:10 +00:00
res = true ;
}
}
// Run the special, if any
if ( thing - > special )
{
res = ! ! P_ExecuteSpecial ( thing - > special , NULL ,
// TriggerActs overrides the level flag, which only concerns thing activated by death
( ( ( death & & level . flags & LEVEL_ACTOWNSPECIAL & & ! ( thing - > activationtype & THINGSPEC_TriggerActs ) )
| | ( thing - > activationtype & THINGSPEC_ThingActs ) ) // Who triggers?
? thing : trigger ) ,
false , thing - > args [ 0 ] , thing - > args [ 1 ] , thing - > args [ 2 ] , thing - > args [ 3 ] , thing - > args [ 4 ] ) ;
// Clears the special if it was run on thing's death or if flag is set.
if ( death | | ( thing - > activationtype & THINGSPEC_ClearSpecial & & res ) ) thing - > special = 0 ;
}
// Returns the result
return res ;
}