2018-11-16 07:06:01 +00:00
//-----------------------------------------------------------------------------
2016-03-01 15:47:10 +00:00
//
2017-04-17 11:33:19 +00:00
// Copyright 1993-1996 id Software
// Copyright 1994-1996 Raven Software
// Copyright 1998-1998 Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
// Copyright 1999-2016 Randy Heit
// Copyright 2002-2017 Christoph Oelckers
2016-03-01 15:47:10 +00:00
//
2017-04-17 11:33:19 +00:00
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
2016-03-01 15:47:10 +00:00
//
2017-04-17 11:33:19 +00:00
// This program is distributed in the hope that it will be useful,
2016-03-01 15:47:10 +00:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2017-04-17 11:33:19 +00:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
2016-03-01 15:47:10 +00:00
//
2017-04-17 11:33:19 +00:00
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//-----------------------------------------------------------------------------
2016-03-01 15:47:10 +00:00
//
// DESCRIPTION:
// Moving object handling. Spawn functions.
//
//-----------------------------------------------------------------------------
2017-04-17 11:33:19 +00:00
/* For code that originates from ZDoom the following applies:
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
* * Redistribution and use in source and binary forms , with or without
* * modification , are permitted provided that the following conditions
* * are met :
* *
* * 1. Redistributions of source code must retain the above copyright
* * notice , this list of conditions and the following disclaimer .
* * 2. Redistributions in binary form must reproduce the above copyright
* * notice , this list of conditions and the following disclaimer in the
* * documentation and / or other materials provided with the distribution .
* * 3. The name of the author may not be used to endorse or promote products
* * derived from this software without specific prior written permission .
* *
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* * IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* * INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* * NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* * DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* * THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* * ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* * THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
2016-03-01 15:47:10 +00:00
// HEADER FILES ------------------------------------------------------------
2016-03-22 17:06:08 +00:00
# include <float.h>
2016-03-01 15:47:10 +00:00
# include "templates.h"
2019-01-31 18:38:04 +00:00
2016-03-01 15:47:10 +00:00
# include "m_random.h"
# include "doomdef.h"
# include "p_local.h"
# include "p_maputl.h"
# include "p_lnspec.h"
# include "p_effect.h"
# include "p_terrain.h"
# include "hu_stuff.h"
# include "v_video.h"
# include "c_dispatch.h"
# include "b_bot.h" //Added by MC:
# include "a_sharedglobal.h"
# include "gi.h"
# include "sbar.h"
# include "p_acs.h"
# include "cmdlib.h"
# include "decallib.h"
# include "a_keys.h"
# include "p_conversation.h"
# include "g_game.h"
# include "teaminfo.h"
# include "r_sky.h"
# include "d_event.h"
# include "p_enemy.h"
# include "gstrings.h"
# include "r_renderer.h"
# include "po_man.h"
# include "p_spec.h"
# include "p_checkposition.h"
2016-09-19 08:34:54 +00:00
# include "serializer.h"
2016-09-24 15:26:21 +00:00
# include "r_utility.h"
2016-10-12 17:22:33 +00:00
# include "thingdef.h"
2016-11-27 09:40:43 +00:00
# include "d_player.h"
2017-01-08 17:45:30 +00:00
# include "g_levellocals.h"
2017-01-15 00:02:38 +00:00
# include "a_morph.h"
2017-01-30 07:10:33 +00:00
# include "events.h"
2017-03-10 01:22:42 +00:00
# include "actorinlines.h"
2018-01-04 22:41:57 +00:00
# include "a_dynlight.h"
2018-12-27 08:44:49 +00:00
# include "fragglescript/t_fs.h"
2016-03-01 15:47:10 +00:00
// MACROS ------------------------------------------------------------------
2016-03-19 23:54:18 +00:00
# define WATER_SINK_FACTOR 0.125
# define WATER_SINK_SMALL_FACTOR 0.25
# define WATER_SINK_SPEED 0.5
# define WATER_JUMP_SPEED 3.5
2016-03-01 15:47:10 +00:00
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void PlayerLandedOnThing ( AActor * mo , AActor * onmobj ) ;
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
EXTERN_CVAR ( Int , cl_rockettrails )
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static FRandom pr_explodemissile ( " ExplodeMissile " ) ;
FRandom pr_bounce ( " Bounce " ) ;
static FRandom pr_reflect ( " Reflect " ) ;
static FRandom pr_nightmarerespawn ( " NightmareRespawn " ) ;
static FRandom pr_botspawnmobj ( " BotSpawnActor " ) ;
static FRandom pr_spawnmapthing ( " SpawnMapThing " ) ;
static FRandom pr_spawnpuff ( " SpawnPuff " ) ;
static FRandom pr_spawnblood ( " SpawnBlood " ) ;
static FRandom pr_splatter ( " BloodSplatter " ) ;
static FRandom pr_takedamage ( " TakeDamage " ) ;
static FRandom pr_splat ( " FAxeSplatter " ) ;
static FRandom pr_ripperblood ( " RipperBlood " ) ;
static FRandom pr_chunk ( " Chunk " ) ;
static FRandom pr_checkmissilespawn ( " CheckMissileSpawn " ) ;
static FRandom pr_spawnmissile ( " SpawnMissile " ) ;
static FRandom pr_missiledamage ( " MissileDamage " ) ;
static FRandom pr_multiclasschoice ( " MultiClassChoice " ) ;
static FRandom pr_rockettrail ( " RocketTrail " ) ;
static FRandom pr_uniquetid ( " UniqueTID " ) ;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
FRandom pr_spawnmobj ( " SpawnActor " ) ;
CUSTOM_CVAR ( Float , sv_gravity , 800.f , CVAR_SERVERINFO | CVAR_NOSAVE )
{
2019-01-28 01:41:29 +00:00
for ( auto Level : AllLevels ( ) )
{
Level - > gravity = self ;
}
2016-03-01 15:47:10 +00:00
}
CVAR ( Bool , cl_missiledecals , true , CVAR_ARCHIVE )
CVAR ( Bool , addrocketexplosion , false , CVAR_ARCHIVE )
CVAR ( Int , cl_pufftype , 0 , CVAR_ARCHIVE ) ;
CVAR ( Int , cl_bloodtype , 0 , CVAR_ARCHIVE ) ;
// CODE --------------------------------------------------------------------
2016-11-24 20:36:02 +00:00
IMPLEMENT_CLASS ( AActor , false , true )
2016-11-05 16:08:54 +00:00
IMPLEMENT_POINTERS_START ( AActor )
IMPLEMENT_POINTER ( target )
IMPLEMENT_POINTER ( lastenemy )
IMPLEMENT_POINTER ( tracer )
IMPLEMENT_POINTER ( goal )
IMPLEMENT_POINTER ( LastLookActor )
IMPLEMENT_POINTER ( Inventory )
IMPLEMENT_POINTER ( LastHeard )
IMPLEMENT_POINTER ( master )
IMPLEMENT_POINTER ( Poisoner )
IMPLEMENT_POINTER ( alternative )
IMPLEMENT_POINTERS_END
2016-03-01 15:47:10 +00:00
AActor : : ~ AActor ( )
{
// Please avoid calling the destructor directly (or through delete)!
// Use Destroy() instead.
}
2016-11-24 19:02:44 +00:00
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// AActor :: Serialize
//
//==========================================================================
2016-09-19 08:34:54 +00:00
# define A(a,b) ((a), (b), def->b)
void AActor : : Serialize ( FSerializer & arc )
2016-03-01 15:47:10 +00:00
{
2016-09-19 08:34:54 +00:00
AActor * def = GetDefault ( ) ;
2016-03-19 23:54:18 +00:00
Super : : Serialize ( arc ) ;
2016-03-01 15:47:10 +00:00
2016-09-19 08:34:54 +00:00
arc
. Sprite ( " sprite " , sprite , & def - > sprite )
A ( " pos " , __Pos )
A ( " angles " , Angles )
A ( " frame " , frame )
A ( " scale " , Scale )
A ( " renderstyle " , RenderStyle )
A ( " renderflags " , renderflags )
A ( " picnum " , picnum )
A ( " floorpic " , floorpic )
A ( " ceilingpic " , ceilingpic )
A ( " tidtohate " , TIDtoHate )
A ( " lastlookpn " , LastLookPlayerNumber )
( " lastlookactor " , LastLookActor )
A ( " effects " , effects )
2017-01-13 18:29:54 +00:00
A ( " fountaincolor " , fountaincolor )
2016-09-19 08:34:54 +00:00
A ( " alpha " , Alpha )
A ( " fillcolor " , fillcolor )
A ( " sector " , Sector )
A ( " floorz " , floorz )
A ( " ceilingz " , ceilingz )
A ( " dropoffz " , dropoffz )
A ( " floorsector " , floorsector )
A ( " ceilingsector " , ceilingsector )
A ( " radius " , radius )
2016-12-25 13:35:35 +00:00
A ( " renderradius " , renderradius )
2016-09-19 08:34:54 +00:00
A ( " height " , Height )
A ( " ppassheight " , projectilepassheight )
A ( " vel " , Vel )
A ( " tics " , tics )
A ( " state " , state )
A ( " damage " , DamageVal )
. Terrain ( " floorterrain " , floorterrain , & def - > floorterrain )
A ( " projectilekickback " , projectileKickback )
A ( " flags " , flags )
A ( " flags2 " , flags2 )
A ( " flags3 " , flags3 )
A ( " flags4 " , flags4 )
A ( " flags5 " , flags5 )
A ( " flags6 " , flags6 )
A ( " flags7 " , flags7 )
2017-05-14 16:26:31 +00:00
A ( " flags8 " , flags8 )
2016-09-19 08:34:54 +00:00
A ( " weaponspecial " , weaponspecial )
A ( " special1 " , special1 )
A ( " special2 " , special2 )
A ( " specialf1 " , specialf1 )
A ( " specialf2 " , specialf2 )
A ( " health " , health )
A ( " movedir " , movedir )
A ( " visdir " , visdir )
A ( " movecount " , movecount )
A ( " strafecount " , strafecount )
( " target " , target )
( " lastenemy " , lastenemy )
( " lastheard " , LastHeard )
A ( " reactiontime " , reactiontime )
A ( " threshold " , threshold )
A ( " player " , player )
A ( " spawnpoint " , SpawnPoint )
A ( " spawnangle " , SpawnAngle )
A ( " starthealth " , StartHealth )
A ( " skillrespawncount " , skillrespawncount )
( " tracer " , tracer )
A ( " floorclip " , Floorclip )
A ( " tid " , tid )
A ( " special " , special )
. Args ( " args " , args , def - > args , special )
A ( " accuracy " , accuracy )
A ( " stamina " , stamina )
( " goal " , goal )
A ( " waterlevel " , waterlevel )
2017-04-22 13:52:24 +00:00
A ( " boomwaterlevel " , boomwaterlevel )
2016-09-19 08:34:54 +00:00
A ( " minmissilechance " , MinMissileChance )
A ( " spawnflags " , SpawnFlags )
( " inventory " , Inventory )
A ( " inventoryid " , InventoryID )
A ( " floatbobphase " , FloatBobPhase )
2017-06-03 15:42:57 +00:00
A ( " floatbobstrength " , FloatBobStrength )
2016-09-19 08:34:54 +00:00
A ( " translation " , Translation )
2017-03-02 09:26:23 +00:00
A ( " bloodcolor " , BloodColor )
A ( " bloodtranslation " , BloodTranslation )
2016-09-19 08:34:54 +00:00
A ( " seesound " , SeeSound )
A ( " attacksound " , AttackSound )
A ( " paimsound " , PainSound )
A ( " deathsound " , DeathSound )
A ( " activesound " , ActiveSound )
A ( " usesound " , UseSound )
A ( " bouncesound " , BounceSound )
A ( " wallbouncesound " , WallBounceSound )
A ( " crushpainsound " , CrushPainSound )
A ( " speed " , Speed )
A ( " floatspeed " , FloatSpeed )
A ( " mass " , Mass )
A ( " painchance " , PainChance )
A ( " spawnstate " , SpawnState )
A ( " seestate " , SeeState )
A ( " meleestate " , MeleeState )
A ( " missilestate " , MissileState )
A ( " maxdropoffheight " , MaxDropOffHeight )
A ( " maxstepheight " , MaxStepHeight )
A ( " bounceflags " , BounceFlags )
A ( " bouncefactor " , bouncefactor )
A ( " wallbouncefactor " , wallbouncefactor )
A ( " bouncecount " , bouncecount )
A ( " maxtargetrange " , maxtargetrange )
A ( " meleethreshold " , meleethreshold )
A ( " meleerange " , meleerange )
A ( " damagetype " , DamageType )
A ( " damagetypereceived " , DamageTypeReceived )
A ( " paintype " , PainType )
A ( " deathtype " , DeathType )
A ( " gravity " , Gravity )
A ( " fastchasestrafecount " , FastChaseStrafeCount )
( " master " , master )
A ( " smokecounter " , smokecounter )
( " blockingmobj " , BlockingMobj )
A ( " blockingline " , BlockingLine )
2018-11-04 04:53:37 +00:00
A ( " blocking3dfloor " , Blocking3DFloor )
2018-10-31 19:56:01 +00:00
A ( " blockingceiling " , BlockingCeiling )
A ( " blockingfloor " , BlockingFloor )
2016-09-19 08:34:54 +00:00
A ( " visibletoteam " , VisibleToTeam )
A ( " pushfactor " , pushfactor )
A ( " species " , Species )
A ( " score " , Score )
A ( " designatedteam " , DesignatedTeam )
A ( " lastpush " , lastpush )
2018-04-21 10:12:12 +00:00
A ( " activationtype " , activationtype )
2016-09-19 08:34:54 +00:00
A ( " lastbump " , lastbump )
A ( " painthreshold " , PainThreshold )
A ( " damagefactor " , DamageFactor )
A ( " damagemultiply " , DamageMultiply )
A ( " waveindexxy " , WeaveIndexXY )
A ( " weaveindexz " , WeaveIndexZ )
A ( " pdmgreceived " , PoisonDamageReceived )
A ( " pdurreceived " , PoisonDurationReceived )
A ( " ppreceived " , PoisonPeriodReceived )
( " poisoner " , Poisoner )
A ( " posiondamage " , PoisonDamage )
A ( " poisonduration " , PoisonDuration )
A ( " poisonperiod " , PoisonPeriod )
A ( " poisondamagetype " , PoisonDamageType )
A ( " poisondmgtypereceived " , PoisonDamageTypeReceived )
A ( " conversationroot " , ConversationRoot )
A ( " conversation " , Conversation )
A ( " friendplayer " , FriendPlayer )
A ( " telefogsourcetype " , TeleFogSourceType )
A ( " telefogdesttype " , TeleFogDestType )
A ( " ripperlevel " , RipperLevel )
A ( " riplevelmin " , RipLevelMin )
A ( " riplevelmax " , RipLevelMax )
A ( " devthreshold " , DefThreshold )
A ( " spriteangle " , SpriteAngle )
A ( " spriterotation " , SpriteRotation )
( " alternative " , alternative )
2017-02-28 11:11:25 +00:00
A ( " cameraheight " , CameraHeight )
2017-08-28 02:07:32 +00:00
A ( " camerafov " , CameraFOV )
2016-09-18 20:07:08 +00:00
A ( " tag " , Tag )
A ( " visiblestartangle " , VisibleStartAngle )
A ( " visibleendangle " , VisibleEndAngle )
A ( " visiblestartpitch " , VisibleStartPitch )
2017-02-28 11:47:44 +00:00
A ( " visibleendpitch " , VisibleEndPitch )
2017-02-28 11:56:35 +00:00
A ( " woundhealth " , WoundHealth )
2017-02-28 11:47:44 +00:00
A ( " rdfactor " , RadiusDamageFactor )
A ( " selfdamagefactor " , SelfDamageFactor )
2017-07-29 17:59:36 +00:00
A ( " stealthalpha " , StealthAlpha )
A ( " renderhidden " , RenderHidden )
2018-11-04 17:30:40 +00:00
A ( " renderrequired " , RenderRequired )
A ( " friendlyseeblocks " , friendlyseeblocks )
2018-11-16 07:06:01 +00:00
A ( " spawntime " , SpawnTime )
2018-12-24 09:18:25 +00:00
A ( " spawnorder " , SpawnOrder )
2018-11-16 07:06:01 +00:00
A ( " friction " , Friction ) ;
2016-09-19 08:34:54 +00:00
}
2016-03-01 15:47:10 +00:00
2016-09-19 08:34:54 +00:00
# undef A
2016-03-01 15:47:10 +00:00
2016-09-19 08:34:54 +00:00
//==========================================================================
//
// This must be done after the world is set up.
//
//==========================================================================
2016-09-01 18:49:58 +00:00
2016-09-19 08:34:54 +00:00
void AActor : : PostSerialize ( )
{
2016-12-26 10:58:08 +00:00
touching_sectorlist = nullptr ;
touching_rendersectors = nullptr ;
2016-12-25 21:40:26 +00:00
LinkToWorld ( nullptr , false , Sector ) ;
2016-03-01 15:47:10 +00:00
2016-09-19 08:34:54 +00:00
AddToHash ( ) ;
if ( player )
2016-03-01 15:47:10 +00:00
{
2016-09-19 08:34:54 +00:00
if ( playeringame [ player - players ] & &
player - > cls ! = NULL & &
! ( flags4 & MF4_NOSKIN ) & &
state - > sprite = = GetDefaultByType ( player - > cls ) - > SpawnState - > sprite )
{ // Give player back the skin
2017-02-17 20:51:23 +00:00
sprite = Skins [ player - > userinfo . GetSkin ( ) ] . sprite ;
2016-09-19 08:34:54 +00:00
}
if ( Speed = = 0 )
2016-03-01 15:47:10 +00:00
{
2016-09-19 08:34:54 +00:00
Speed = GetDefault ( ) - > Speed ;
2016-03-01 15:47:10 +00:00
}
}
2016-09-19 08:34:54 +00:00
ClearInterpolation ( ) ;
UpdateWaterLevel ( false ) ;
2016-03-01 15:47:10 +00:00
}
2016-09-19 08:34:54 +00:00
2016-03-01 15:47:10 +00:00
AActor & AActor : : operator = ( const AActor & other )
{
2017-03-08 14:20:00 +00:00
memcpy ( & snext , & other . snext , ( uint8_t * ) & this [ 1 ] - ( uint8_t * ) & snext ) ;
2016-03-01 15:47:10 +00:00
return * this ;
}
//==========================================================================
//
// AActor::InStateSequence
//
// Checks whether the current state is in a contiguous sequence that
// starts with basestate
//
//==========================================================================
2018-12-18 19:38:25 +00:00
static int InStateSequence ( FState * newstate , FState * basestate )
2016-03-01 15:47:10 +00:00
{
if ( basestate = = NULL ) return false ;
FState * thisstate = basestate ;
do
{
if ( newstate = = thisstate ) return true ;
basestate = thisstate ;
thisstate = thisstate - > GetNextState ( ) ;
}
while ( thisstate = = basestate + 1 ) ;
return false ;
}
2018-12-18 23:09:36 +00:00
DEFINE_ACTION_FUNCTION_NATIVE ( AActor , InStateSequence , InStateSequence )
2016-11-30 00:25:51 +00:00
{
2018-12-18 19:38:25 +00:00
PARAM_PROLOGUE ;
2016-11-30 00:25:51 +00:00
PARAM_POINTER ( newstate , FState ) ;
PARAM_POINTER ( basestate , FState ) ;
2018-12-18 19:38:25 +00:00
ACTION_RETURN_BOOL ( InStateSequence ( newstate , basestate ) ) ;
}
2018-12-18 23:09:36 +00:00
DEFINE_ACTION_FUNCTION_NATIVE ( FState , InStateSequence , InStateSequence )
2018-12-18 19:38:25 +00:00
{
PARAM_SELF_STRUCT_PROLOGUE ( FState ) ;
PARAM_POINTER ( basestate , FState ) ;
ACTION_RETURN_BOOL ( InStateSequence ( self , basestate ) ) ;
2016-11-30 00:25:51 +00:00
}
2018-12-02 15:53:12 +00:00
bool AActor : : IsMapActor ( )
{
// [SP] Don't remove owned inventory objects.
2018-12-02 23:43:01 +00:00
return ( ! IsKindOf ( NAME_Inventory ) | | GC : : ReadBarrier ( PointerVar < AActor > ( NAME_Owner ) ) = = nullptr ) ;
2018-12-02 15:53:12 +00:00
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// AActor::GetTics
//
// Get the actual duration of the next state
// We are using a state flag now to indicate a state that should be
// accelerated in Fast mode or slowed in Slow mode.
//
//==========================================================================
int AActor : : GetTics ( FState * newstate )
{
int tics = newstate - > GetTics ( ) ;
2016-11-21 20:34:34 +00:00
if ( isFast ( ) & & newstate - > GetFast ( ) )
2016-03-01 15:47:10 +00:00
{
return tics - ( tics > > 1 ) ;
}
2016-11-21 20:34:34 +00:00
else if ( isSlow ( ) & & newstate - > GetSlow ( ) )
2016-03-01 15:47:10 +00:00
{
return tics < < 1 ;
}
return tics ;
}
//==========================================================================
//
// AActor::SetState
//
// Returns true if the mobj is still present.
//
//==========================================================================
bool AActor : : SetState ( FState * newstate , bool nofunction )
{
if ( debugfile & & player & & ( player - > cheats & CF_PREDICTING ) )
fprintf ( debugfile , " for pl %td: SetState while predicting! \n " , player - players ) ;
do
{
if ( newstate = = NULL )
{
state = NULL ;
Destroy ( ) ;
return false ;
}
int prevsprite , newsprite ;
if ( state ! = NULL )
{
prevsprite = state - > sprite ;
}
else
{
prevsprite = - 1 ;
}
2016-11-14 23:18:57 +00:00
if ( ! ( newstate - > UseFlags & SUF_ACTOR ) )
{
2017-04-13 07:17:11 +00:00
Printf ( TEXTCOLOR_RED " State %s in %s not flagged for use as an actor sprite \n " , FState : : StaticGetStateName ( newstate ) . GetChars ( ) , GetClass ( ) - > TypeName . GetChars ( ) ) ;
2016-11-14 23:18:57 +00:00
state = nullptr ;
Destroy ( ) ;
return false ;
}
2016-03-01 15:47:10 +00:00
state = newstate ;
tics = GetTics ( newstate ) ;
renderflags = ( renderflags & ~ RF_FULLBRIGHT ) | ActorRenderFlags : : FromInt ( newstate - > GetFullbright ( ) ) ;
newsprite = newstate - > sprite ;
if ( newsprite ! = SPR_FIXED )
{ // okay to change sprite and/or frame
if ( ! newstate - > GetSameFrame ( ) )
{ // okay to change frame
frame = newstate - > GetFrame ( ) ;
}
if ( newsprite ! = SPR_NOCHANGE )
{ // okay to change sprite
if ( ! ( flags4 & MF4_NOSKIN ) & & newsprite = = SpawnState - > sprite )
{ // [RH] If the new sprite is the same as the original sprite, and
// this actor is attached to a player, use the player's skin's
// sprite. If a player is not attached, do not change the sprite
// unless it is different from the previous state's sprite; a
// player may have been attached, died, and respawned elsewhere,
// and we do not want to lose the skin on the body. If it wasn't
// for Dehacked, I would move sprite changing out of the states
// altogether, since actors rarely change their sprites after
// spawning.
2017-02-17 20:51:23 +00:00
if ( player ! = NULL & & Skins . Size ( ) > 0 )
2016-03-01 15:47:10 +00:00
{
2017-02-17 20:51:23 +00:00
sprite = Skins [ player - > userinfo . GetSkin ( ) ] . sprite ;
2016-03-01 15:47:10 +00:00
}
else if ( newsprite ! = prevsprite )
{
sprite = newsprite ;
}
}
else
{
sprite = newsprite ;
}
}
}
if ( ! nofunction )
{
FState * returned_state ;
2016-06-16 14:11:00 +00:00
FStateParamInfo stp = { newstate , STATE_Actor , PSP_WEAPON } ;
if ( newstate - > CallAction ( this , this , & stp , & returned_state ) )
2016-03-01 15:47:10 +00:00
{
// Check whether the called action function resulted in destroying the actor
if ( ObjectFlags & OF_EuthanizeMe )
{
return false ;
}
if ( returned_state ! = NULL )
{ // The action was an A_Jump-style function that wants to change the next state.
newstate = returned_state ;
tics = 0 ; // make sure we loop and set the new state properly
continue ;
}
}
}
newstate = newstate - > GetNextState ( ) ;
} while ( tics = = 0 ) ;
2018-04-03 22:21:25 +00:00
SetDynamicLights ( ) ;
2016-03-01 15:47:10 +00:00
return true ;
}
2016-10-26 09:30:30 +00:00
DEFINE_ACTION_FUNCTION ( AActor , SetState )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2016-11-14 13:12:27 +00:00
PARAM_POINTER ( state , FState ) ;
2018-11-17 09:03:40 +00:00
PARAM_BOOL ( nofunction ) ;
2016-10-26 09:30:30 +00:00
ACTION_RETURN_BOOL ( self - > SetState ( state , nofunction ) ) ;
} ;
2017-03-14 10:44:21 +00:00
2016-03-01 15:47:10 +00:00
//============================================================================
//
// AActor :: DestroyAllInventory
//
//============================================================================
void AActor : : DestroyAllInventory ( )
{
2018-12-04 16:11:36 +00:00
AActor * inv = Inventory ;
2016-10-06 06:50:46 +00:00
if ( inv ! = nullptr )
2016-03-01 15:47:10 +00:00
{
2018-12-04 16:11:36 +00:00
TArray < AActor * > toDelete ;
2016-10-06 06:50:46 +00:00
// Delete the list in a two stage approach.
// This is necessary because an item may destroy another item (e.g. sister weapons)
// which would break the list and leave parts of it undestroyed, maybe doing bad things later.
while ( inv ! = nullptr )
{
toDelete . Push ( inv ) ;
2018-12-03 23:41:39 +00:00
auto item = inv - > Inventory ;
2016-10-06 06:50:46 +00:00
inv - > Inventory = nullptr ;
2018-12-02 23:43:01 +00:00
inv - > PointerVar < AActor > ( NAME_Owner ) = nullptr ;
2016-10-06 06:50:46 +00:00
inv = item ;
}
for ( auto p : toDelete )
{
// the item may already have been deleted by another one, so check this here to avoid problems.
if ( ! ( p - > ObjectFlags & OF_EuthanizeMe ) )
{
p - > Destroy ( ) ;
}
}
2016-03-01 15:47:10 +00:00
}
}
2018-11-24 19:58:33 +00:00
DEFINE_ACTION_FUNCTION ( AActor , DestroyAllInventory )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
self - > DestroyAllInventory ( ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
//============================================================================
//
// AActor :: UseInventory
//
// Attempts to use an item. If the use succeeds, one copy of the item is
// removed from the inventory. If all copies are removed, then the item is
// destroyed.
//
//============================================================================
2018-12-04 16:11:36 +00:00
bool AActor : : UseInventory ( AActor * item )
2016-03-01 15:47:10 +00:00
{
2018-12-01 16:17:08 +00:00
IFVIRTUAL ( AActor , UseInventory )
2016-03-01 15:47:10 +00:00
{
2018-12-01 16:17:08 +00:00
VMValue params [ ] = { this , item } ;
int retval = 0 ;
2018-11-24 21:03:56 +00:00
VMReturn ret ( & retval ) ;
VMCall ( func , params , 2 , & ret , 1 ) ;
2018-12-01 16:17:08 +00:00
return ! ! retval ;
2016-03-01 15:47:10 +00:00
}
2018-12-01 16:18:26 +00:00
return false ;
2016-11-24 12:45:43 +00:00
}
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// AActor :: DropInventory
//
// Removes a single copy of an item and throws it out in front of the actor.
//
//===========================================================================
2018-12-04 16:11:36 +00:00
AActor * AActor : : DropInventory ( AActor * item , int amt )
2016-03-01 15:47:10 +00:00
{
2018-12-01 16:18:26 +00:00
IFVM ( Actor , DropInventory )
2018-09-16 13:05:57 +00:00
{
2018-12-01 16:18:26 +00:00
VMValue params [ ] = { this , item , amt } ;
2018-12-04 16:11:36 +00:00
AActor * retval = 0 ;
2018-12-01 16:18:26 +00:00
VMReturn ret ( ( void * * ) & retval ) ;
VMCall ( func , params , 3 , & ret , 1 ) ;
return retval ;
2018-09-16 13:05:57 +00:00
}
2018-12-01 16:18:26 +00:00
return nullptr ;
2016-11-28 18:42:26 +00:00
}
2016-03-01 15:47:10 +00:00
//============================================================================
//
// AActor :: FindInventory
//
//============================================================================
2018-12-04 16:11:36 +00:00
AActor * AActor : : FindInventory ( PClassActor * type , bool subclass )
2016-03-01 15:47:10 +00:00
{
2018-12-04 16:11:36 +00:00
AActor * item ;
2016-03-01 15:47:10 +00:00
if ( type = = NULL )
{
return NULL ;
}
for ( item = Inventory ; item ! = NULL ; item = item - > Inventory )
{
if ( ! subclass )
{
if ( item - > GetClass ( ) = = type )
{
break ;
}
}
else
{
if ( item - > IsKindOf ( type ) )
{
break ;
}
}
}
return item ;
}
2018-12-04 16:11:36 +00:00
AActor * AActor : : FindInventory ( FName type , bool subclass )
2016-03-01 15:47:10 +00:00
{
2017-01-18 21:57:47 +00:00
return FindInventory ( PClass : : FindActor ( type ) , subclass ) ;
2016-03-01 15:47:10 +00:00
}
2016-11-19 00:23:56 +00:00
DEFINE_ACTION_FUNCTION ( AActor , FindInventory )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2018-12-04 16:11:36 +00:00
PARAM_CLASS ( type , AActor ) ;
2018-11-17 09:03:40 +00:00
PARAM_BOOL ( subclass ) ;
2016-11-19 00:23:56 +00:00
ACTION_RETURN_OBJECT ( self - > FindInventory ( type , subclass ) ) ;
}
2016-03-01 15:47:10 +00:00
//============================================================================
//
// AActor :: GiveInventoryType
//
//============================================================================
2018-12-04 16:11:36 +00:00
AActor * AActor : : GiveInventoryType ( PClassActor * type )
2016-03-01 15:47:10 +00:00
{
2018-12-03 23:41:39 +00:00
if ( type ! = nullptr )
2016-03-01 15:47:10 +00:00
{
2018-12-04 16:11:36 +00:00
auto item = Spawn ( type ) ;
2018-11-29 23:06:20 +00:00
if ( ! CallTryPickup ( item , this ) )
2016-03-01 15:47:10 +00:00
{
item - > Destroy ( ) ;
2018-12-03 23:41:39 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
2018-12-03 23:41:39 +00:00
return item ;
2016-03-01 15:47:10 +00:00
}
2018-12-03 23:41:39 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
2016-11-25 15:05:03 +00:00
DEFINE_ACTION_FUNCTION ( AActor , GiveInventoryType )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2018-12-04 16:11:36 +00:00
PARAM_CLASS ( type , AActor ) ;
2016-11-25 15:05:03 +00:00
ACTION_RETURN_OBJECT ( self - > GiveInventoryType ( type ) ) ;
}
2016-03-01 15:47:10 +00:00
//============================================================================
//
// AActor :: ClearInventory
//
// Clears the inventory of a single actor.
//
//============================================================================
void AActor : : ClearInventory ( )
{
2018-12-02 14:24:44 +00:00
IFVIRTUAL ( AActor , ClearInventory )
2016-03-01 15:47:10 +00:00
{
2018-12-02 14:24:44 +00:00
VMValue params [ ] = { this } ;
VMCall ( func , params , 1 , nullptr , 0 ) ;
2016-03-01 15:47:10 +00:00
}
}
//============================================================================
//
// AActor :: CopyFriendliness
//
// Makes this actor hate (or like) the same things another actor does.
//
//============================================================================
void AActor : : CopyFriendliness ( AActor * other , bool changeTarget , bool resetHealth )
{
2019-01-27 15:08:22 +00:00
Level - > total_monsters - = CountsAsKill ( ) ;
2016-03-01 15:47:10 +00:00
TIDtoHate = other - > TIDtoHate ;
LastLookActor = other - > LastLookActor ;
LastLookPlayerNumber = other - > LastLookPlayerNumber ;
flags = ( flags & ~ MF_FRIENDLY ) | ( other - > flags & MF_FRIENDLY ) ;
flags3 = ( flags3 & ~ ( MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS ) ) | ( other - > flags3 & ( MF3_NOSIGHTCHECK | MF3_HUNTPLAYERS ) ) ;
flags4 = ( flags4 & ~ ( MF4_NOHATEPLAYERS | MF4_BOSSSPAWNED ) ) | ( other - > flags4 & ( MF4_NOHATEPLAYERS | MF4_BOSSSPAWNED ) ) ;
FriendPlayer = other - > FriendPlayer ;
DesignatedTeam = other - > DesignatedTeam ;
if ( changeTarget & & other - > target ! = NULL & & ! ( other - > target - > flags3 & MF3_NOTARGET ) & & ! ( other - > target - > flags7 & MF7_NEVERTARGET ) )
{
// LastHeard must be set as well so that A_Look can react to the new target if called
LastHeard = target = other - > target ;
}
if ( resetHealth ) health = SpawnHealth ( ) ;
2019-01-27 15:08:22 +00:00
Level - > total_monsters + = CountsAsKill ( ) ;
2016-03-01 15:47:10 +00:00
}
2016-11-03 12:38:40 +00:00
DEFINE_ACTION_FUNCTION ( AActor , CopyFriendliness )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2016-12-02 11:06:49 +00:00
PARAM_OBJECT_NOT_NULL ( other , AActor ) ;
2018-11-17 09:03:40 +00:00
PARAM_BOOL ( changetarget ) ;
PARAM_BOOL ( resethealth ) ;
2016-11-03 12:38:40 +00:00
self - > CopyFriendliness ( other , changetarget , resethealth ) ;
return 0 ;
}
2017-04-14 11:31:58 +00:00
//---------------------------------------------------------------------------
//
// FUNC P_GetRealMaxHealth
//
// Taken out of P_GiveBody so that the bot code can also use it to decide
// whether to pick up an item or not.
//
//---------------------------------------------------------------------------
2019-01-03 21:05:49 +00:00
int P_GetRealMaxHealth ( AActor * actor , int max )
2017-04-14 11:31:58 +00:00
{
// Max is 0 by default, preserving default behavior for P_GiveBody()
// calls while supporting health pickups.
auto player = actor - > player ;
if ( max < = 0 )
{
max = actor - > GetMaxHealth ( true ) ;
// [MH] First step in predictable generic morph effects
if ( player - > morphTics )
{
if ( player - > MorphStyle & MORPH_FULLHEALTH )
{
if ( ! ( player - > MorphStyle & MORPH_ADDSTAMINA ) )
{
2019-01-03 17:01:58 +00:00
max - = actor - > stamina + actor - > IntVar ( NAME_BonusHealth ) ;
2017-04-14 11:31:58 +00:00
}
}
else // old health behaviour
{
max = MAXMORPHHEALTH ;
if ( player - > MorphStyle & MORPH_ADDSTAMINA )
{
2019-01-03 17:01:58 +00:00
max + = actor - > stamina + actor - > IntVar ( NAME_BonusHealth ) ;
2017-04-14 11:31:58 +00:00
}
}
}
}
else
{
// Bonus health should be added on top of the item's limit.
if ( player - > morphTics = = 0 | | ( player - > MorphStyle & MORPH_ADDSTAMINA ) )
{
2019-01-03 17:01:58 +00:00
max + = actor - > IntVar ( NAME_BonusHealth ) ;
2017-04-14 11:31:58 +00:00
}
}
return max ;
}
2017-01-15 00:02:38 +00:00
//---------------------------------------------------------------------------
//
// FUNC P_GiveBody
//
// Returns false if the body isn't needed at all.
//
//---------------------------------------------------------------------------
bool P_GiveBody ( AActor * actor , int num , int max )
{
if ( actor - > health < = 0 | | ( actor - > player ! = NULL & & actor - > player - > playerstate = = PST_DEAD ) )
{ // Do not heal dead things.
return false ;
}
player_t * player = actor - > player ;
num = clamp ( num , - 65536 , 65536 ) ; // prevent overflows for bad values
if ( player ! = NULL )
{
2017-04-14 11:31:58 +00:00
max = P_GetRealMaxHealth ( player - > mo , max ) ; // do not pass voodoo dolls in here.
// [RH] For Strife: A negative value sets you up with a percentage of your full health.
2017-01-15 00:02:38 +00:00
if ( num < 0 )
{
num = max * - num / 100 ;
if ( player - > health < num )
{
player - > health = num ;
actor - > health = num ;
return true ;
}
}
else if ( num > 0 )
{
if ( player - > health < max )
{
num = int ( num * G_SkillProperty ( SKILLP_HealthFactor ) ) ;
if ( num < 1 ) num = 1 ;
player - > health + = num ;
if ( player - > health > max )
{
player - > health = max ;
}
actor - > health = player - > health ;
return true ;
}
}
}
else
{
// Parameter value for max is ignored on monsters, preserving original
2017-01-15 15:55:30 +00:00
// behaviour of health as well as on existing calls to P_GiveBody().
2017-01-15 00:02:38 +00:00
max = actor - > SpawnHealth ( ) ;
if ( num < 0 )
{
num = max * - num / 100 ;
if ( actor - > health < num )
{
actor - > health = num ;
return true ;
}
}
else if ( actor - > health < max )
{
actor - > health + = num ;
if ( actor - > health > max )
{
actor - > health = max ;
}
return true ;
}
}
return false ;
}
DEFINE_ACTION_FUNCTION ( AActor , GiveBody )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_INT ( num ) ;
2018-11-17 09:03:40 +00:00
PARAM_INT ( max ) ;
2017-01-15 00:02:38 +00:00
ACTION_RETURN_BOOL ( P_GiveBody ( self , num , max ) ) ;
}
2016-03-01 15:47:10 +00:00
//============================================================================
//
// AActor :: CheckLocalView
//
// Returns true if this actor is local for the player. Here, local means the
// player is either looking out this actor's eyes, or this actor is the player
// and the player is looking out the eyes of something non-"sentient."
//
//============================================================================
2019-01-31 02:29:25 +00:00
bool AActor : : CheckLocalView ( ) const
2016-03-01 15:47:10 +00:00
{
2019-02-01 16:02:10 +00:00
auto p = Level - > GetConsolePlayer ( ) ;
if ( p = = nullptr )
{
return false ;
}
2019-01-30 00:15:32 +00:00
if ( p - > camera = = this )
2016-03-01 15:47:10 +00:00
{
return true ;
}
2019-01-30 00:15:32 +00:00
if ( p - > mo ! = this | | p - > camera = = nullptr )
2016-03-01 15:47:10 +00:00
{
return false ;
}
2019-01-30 00:15:32 +00:00
if ( p - > camera - > player = = NULL & &
! ( p - > camera - > flags3 & MF3_ISMONSTER ) )
2016-03-01 15:47:10 +00:00
{
return true ;
}
return false ;
}
2016-11-29 11:17:05 +00:00
DEFINE_ACTION_FUNCTION ( AActor , CheckLocalView )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_INT ( cp ) ;
2019-01-31 02:29:25 +00:00
ACTION_RETURN_BOOL ( self - > CheckLocalView ( ) ) ;
2016-11-29 11:17:05 +00:00
}
2016-09-18 20:07:08 +00:00
//============================================================================
//
// AActor :: IsInsideVisibleAngles
//
// Returns true if this actor is within viewing angle/pitch visibility.
//
//============================================================================
bool AActor : : IsInsideVisibleAngles ( ) const
{
// Don't bother masking if not wanted.
if ( ! ( renderflags & RF_MASKROTATION ) )
return true ;
2019-02-01 16:02:10 +00:00
auto p = Level - > GetConsolePlayer ( ) ;
if ( p = = nullptr | | p - > camera = = nullptr )
2019-01-30 00:15:32 +00:00
return true ;
2016-09-24 15:26:21 +00:00
DAngle anglestart = VisibleStartAngle ;
DAngle angleend = VisibleEndAngle ;
DAngle pitchstart = VisibleStartPitch ;
DAngle pitchend = VisibleEndPitch ;
2016-09-18 20:07:08 +00:00
if ( anglestart > angleend )
{
DAngle temp = anglestart ;
anglestart = angleend ;
angleend = temp ;
}
2016-09-24 07:42:35 +00:00
if ( pitchstart > pitchend )
2016-09-18 20:07:08 +00:00
{
DAngle temp = pitchstart ;
pitchstart = pitchend ;
pitchend = temp ;
}
2016-09-24 15:26:21 +00:00
2016-09-18 20:07:08 +00:00
2019-02-01 16:02:10 +00:00
AActor * mo = p - > camera ;
2016-09-18 20:07:08 +00:00
2016-09-24 15:26:21 +00:00
if ( mo ! = nullptr )
2016-09-18 20:07:08 +00:00
{
2016-09-24 15:26:21 +00:00
2017-03-11 22:28:07 +00:00
DVector3 diffang = r_viewpoint . Pos - Pos ( ) ;
2016-09-18 20:07:08 +00:00
DAngle to = diffang . Angle ( ) ;
if ( ! ( renderflags & RF_ABSMASKANGLE ) )
to = deltaangle ( Angles . Yaw , to ) ;
2016-09-24 15:26:21 +00:00
if ( ( to > = anglestart & & to < = angleend ) )
2016-09-18 20:07:08 +00:00
{
to = diffang . Pitch ( ) ;
if ( ! ( renderflags & RF_ABSMASKPITCH ) )
to = deltaangle ( Angles . Pitch , to ) ;
return ! ! ( to > = pitchstart & & to < = pitchend ) ;
}
else return false ;
}
return true ;
}
2016-03-01 15:47:10 +00:00
//============================================================================
//
// AActor :: IsVisibleToPlayer
//
// Returns true if this actor should be seen by the console player.
//
2019-02-01 11:31:08 +00:00
// Not that even for secondary maps this must check the real player!
//
2016-03-01 15:47:10 +00:00
//============================================================================
bool AActor : : IsVisibleToPlayer ( ) const
{
2019-02-01 16:02:10 +00:00
auto p = Level - > GetConsolePlayer ( ) ;
2016-03-01 15:47:10 +00:00
// [BB] Safety check. This should never be NULL. Nevertheless, we return true to leave the default ZDoom behavior unaltered.
2019-02-01 16:02:10 +00:00
if ( p = = nullptr | | p - > camera = = nullptr )
2016-03-01 15:47:10 +00:00
return true ;
if ( VisibleToTeam ! = 0 & & teamplay & &
2019-02-01 16:02:10 +00:00
( signed ) ( VisibleToTeam - 1 ) ! = p - > userinfo . GetTeam ( ) )
2016-03-01 15:47:10 +00:00
return false ;
2017-04-12 07:55:27 +00:00
auto & vis = GetInfo ( ) - > VisibleToPlayerClass ;
if ( vis . Size ( ) = = 0 ) return true ; // early out for the most common case.
2019-02-01 16:02:10 +00:00
const player_t * pPlayer = p - > camera - > player ;
2016-03-01 15:47:10 +00:00
2017-04-11 22:07:41 +00:00
if ( pPlayer )
2016-03-01 15:47:10 +00:00
{
2017-04-12 07:55:27 +00:00
for ( auto cls : vis )
2016-03-01 15:47:10 +00:00
{
if ( cls & & pPlayer - > mo - > GetClass ( ) - > IsDescendantOf ( cls ) )
{
2017-04-11 22:07:41 +00:00
return true ;
2016-03-01 15:47:10 +00:00
}
}
2017-04-11 22:07:41 +00:00
return false ;
2016-03-01 15:47:10 +00:00
}
// [BB] Passed all checks.
return true ;
}
//============================================================================
//
// AActor :: ConversationAnimation
//
// Plays a conversation-related animation:
// 0 = greeting
// 1 = "yes"
// 2 = "no"
//
//============================================================================
void AActor : : ConversationAnimation ( int animnum )
{
FState * state = NULL ;
switch ( animnum )
{
case 0 :
state = FindState ( NAME_Greetings ) ;
break ;
case 1 :
state = FindState ( NAME_Yes ) ;
break ;
case 2 :
state = FindState ( NAME_No ) ;
break ;
}
if ( state ! = NULL ) SetState ( state ) ;
}
//============================================================================
//
// AActor :: Touch
//
// Something just touched this actor. Normally used only for inventory items,
// but some Strife monsters also use it.
//
//============================================================================
void AActor : : Touch ( AActor * toucher )
{
}
2016-11-28 11:55:33 +00:00
DEFINE_ACTION_FUNCTION ( AActor , Touch )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2016-12-02 11:06:49 +00:00
PARAM_OBJECT_NOT_NULL ( toucher , AActor ) ;
2016-11-28 11:55:33 +00:00
self - > Touch ( toucher ) ;
return 0 ;
}
void AActor : : CallTouch ( AActor * toucher )
{
IFVIRTUAL ( AActor , Touch )
{
VMValue params [ 2 ] = { ( DObject * ) this , toucher } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2016-11-28 11:55:33 +00:00
}
else Touch ( toucher ) ;
}
2016-03-01 15:47:10 +00:00
//============================================================================
//
// AActor :: Grind
//
// Handles the an actor being crushed by a door, crusher or polyobject.
// Originally part of P_DoCrunch(), it has been made into its own actor
// function so that it could be called from a polyobject without hassle.
// Bool items is true if it should destroy() dropped items, false otherwise.
//============================================================================
bool AActor : : Grind ( bool items )
{
// crunch bodies to giblets
if ( ( flags & MF_CORPSE ) & & ! ( flags3 & MF3_DONTGIB ) & & ( health < = 0 ) )
{
FState * state = FindState ( NAME_Crush ) ;
// In Heretic and Chex Quest we don't change the actor's sprite, just its size.
if ( state = = NULL & & gameinfo . dontcrunchcorpses )
{
flags & = ~ MF_SOLID ;
flags3 | = MF3_DONTGIB ;
2016-03-20 19:55:06 +00:00
Height = 0 ;
2016-03-20 14:04:13 +00:00
radius = 0 ;
2016-03-01 15:47:10 +00:00
return false ;
}
bool isgeneric = false ;
// ZDoom behavior differs from standard as crushed corpses cannot be raised.
// The reason for the change was originally because of a problem with players,
// see rh_log entry for February 21, 1999. Don't know if it is still relevant.
if ( state = = NULL // Only use the default crushed state if:
& & ! ( flags & MF_NOBLOOD ) // 1. the monster bleeeds,
2019-01-29 18:28:22 +00:00
& & ( Level - > i_compatflags & COMPATF_CORPSEGIBS ) // 2. the compat setting is on,
2016-03-01 15:47:10 +00:00
& & player = = NULL ) // 3. and the thing isn't a player.
{
isgeneric = true ;
state = FindState ( NAME_GenericCrush ) ;
if ( state ! = NULL & & ( sprites [ state - > sprite ] . numframes < = 0 ) )
state = NULL ; // If one of these tests fails, do not use that state.
}
if ( state ! = NULL & & ! ( flags & MF_ICECORPSE ) )
{
if ( this - > flags4 & MF4_BOSSDEATH )
{
A_BossDeath ( this ) ;
}
flags & = ~ MF_SOLID ;
flags3 | = MF3_DONTGIB ;
2016-03-20 19:55:06 +00:00
Height = 0 ;
2016-03-20 14:04:13 +00:00
radius = 0 ;
2016-03-01 15:47:10 +00:00
SetState ( state ) ;
if ( isgeneric ) // Not a custom crush state, so colorize it appropriately.
{
S_Sound ( this , CHAN_BODY , " misc/fallingsplat " , 1 , ATTN_IDLE ) ;
2017-03-02 09:26:23 +00:00
Translation = BloodTranslation ;
2016-03-01 15:47:10 +00:00
}
return false ;
}
if ( ! ( flags & MF_NOBLOOD ) )
{
if ( this - > flags4 & MF4_BOSSDEATH )
{
A_BossDeath ( this ) ;
}
PClassActor * i = PClass : : FindActor ( " RealGibs " ) ;
if ( i ! = NULL )
{
i = i - > GetReplacement ( ) ;
const AActor * defaults = GetDefaultByType ( i ) ;
if ( defaults - > SpawnState = = NULL | |
sprites [ defaults - > SpawnState - > sprite ] . numframes = = 0 )
{
i = NULL ;
}
}
if ( i = = NULL )
{
// if there's no gib sprite don't crunch it.
flags & = ~ MF_SOLID ;
flags3 | = MF3_DONTGIB ;
2016-03-20 19:55:06 +00:00
Height = 0 ;
2016-03-20 14:04:13 +00:00
radius = 0 ;
2016-03-01 15:47:10 +00:00
return false ;
}
AActor * gib = Spawn ( i , Pos ( ) , ALLOW_REPLACE ) ;
if ( gib ! = NULL )
{
gib - > RenderStyle = RenderStyle ;
2016-03-21 11:18:46 +00:00
gib - > Alpha = Alpha ;
2016-03-20 19:55:06 +00:00
gib - > Height = 0 ;
2016-03-01 15:47:10 +00:00
gib - > radius = 0 ;
2017-03-02 09:26:23 +00:00
gib - > Translation = BloodTranslation ;
2016-03-01 15:47:10 +00:00
}
S_Sound ( this , CHAN_BODY , " misc/fallingsplat " , 1 , ATTN_IDLE ) ;
}
if ( flags & MF_ICECORPSE )
{
tics = 1 ;
2016-03-19 23:54:18 +00:00
Vel . Zero ( ) ;
2016-03-01 15:47:10 +00:00
}
else if ( player )
{
flags | = MF_NOCLIP ;
flags3 | = MF3_DONTGIB ;
renderflags | = RF_INVISIBLE ;
}
else
{
Destroy ( ) ;
}
return false ; // keep checking
}
// killough 11/98: kill touchy things immediately
if ( flags6 & MF6_TOUCHY & & ( flags6 & MF6_ARMED | | IsSentient ( ) ) )
{
flags6 & = ~ MF6_ARMED ; // Disarm
P_DamageMobj ( this , NULL , NULL , health , NAME_Crush , DMG_FORCED ) ; // kill object
return true ; // keep checking
}
if ( ! ( flags & MF_SOLID ) | | ( flags & MF_NOCLIP ) )
{
return false ;
}
if ( ! ( flags & MF_SHOOTABLE ) )
{
return false ; // assume it is bloody gibs or something
}
return true ;
}
2018-11-29 20:49:13 +00:00
DEFINE_ACTION_FUNCTION ( AActor , Grind )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_BOOL ( items ) ;
ACTION_RETURN_BOOL ( self - > Grind ( items ) ) ;
}
bool AActor : : CallGrind ( bool items )
{
IFVIRTUAL ( AActor , Grind )
{
VMValue params [ ] = { ( DObject * ) this , items } ;
int retv ;
VMReturn ret ( & retv ) ;
VMCall ( func , params , 2 , & ret , 1 ) ;
return ! ! retv ;
}
return Grind ( items ) ;
}
2016-03-01 15:47:10 +00:00
//============================================================================
//
// AActor :: Massacre
//
// Called by the massacre cheat to kill monsters. Returns true if the monster
// was killed and false if it was already dead.
//============================================================================
bool AActor : : Massacre ( )
{
int prevhealth ;
if ( health > 0 )
{
2016-10-01 10:08:07 +00:00
auto f = flags ;
auto f2 = flags2 ;
2016-03-01 15:47:10 +00:00
flags | = MF_SHOOTABLE ;
flags2 & = ~ ( MF2_DORMANT | MF2_INVULNERABLE ) ;
do
{
prevhealth = health ;
P_DamageMobj ( this , NULL , NULL , TELEFRAG_DAMAGE , NAME_Massacre ) ;
}
while ( health ! = prevhealth & & health > 0 ) ; //abort if the actor wasn't hurt.
2016-10-01 10:08:07 +00:00
if ( health > 0 )
{
// restore flags if this did not kill the monster.
flags = f ;
flags2 = f2 ;
}
2016-03-01 15:47:10 +00:00
return health < = 0 ;
}
return false ;
}
//----------------------------------------------------------------------------
//
// PROC P_ExplodeMissile
//
//----------------------------------------------------------------------------
2017-02-25 19:45:28 +00:00
void P_ExplodeMissile ( AActor * mo , line_t * line , AActor * target , bool onsky )
2016-03-01 15:47:10 +00:00
{
2018-10-28 22:08:38 +00:00
// [ZZ] line damage callback
if ( line )
{
2018-11-04 04:53:37 +00:00
P_ProjectileHitLinedef ( mo , line ) ;
2018-10-28 22:08:38 +00:00
}
2016-03-01 15:47:10 +00:00
if ( mo - > flags3 & MF3_EXPLOCOUNT )
{
if ( + + mo - > threshold < mo - > DefThreshold )
{
return ;
}
}
2016-03-19 23:54:18 +00:00
mo - > Vel . Zero ( ) ;
2016-03-01 15:47:10 +00:00
mo - > effects = 0 ; // [RH]
mo - > flags & = ~ MF_SHOOTABLE ;
2017-01-27 16:21:22 +00:00
FState * nextstate = nullptr ;
2016-03-01 15:47:10 +00:00
2017-01-27 16:21:22 +00:00
if ( target ! = nullptr )
2016-03-01 15:47:10 +00:00
{
if ( mo - > flags7 & MF7_HITTARGET ) mo - > target = target ;
if ( mo - > flags7 & MF7_HITMASTER ) mo - > master = target ;
if ( mo - > flags7 & MF7_HITTRACER ) mo - > tracer = target ;
2017-01-27 16:21:22 +00:00
if ( ( target - > flags & ( MF_SHOOTABLE | MF_CORPSE ) ) | | ( target - > flags6 & MF6_KILLED ) )
{
if ( target - > flags & MF_NOBLOOD ) nextstate = mo - > FindState ( NAME_Crash ) ;
if ( nextstate = = NULL ) nextstate = mo - > FindState ( NAME_Death , NAME_Extreme ) ;
}
2016-03-01 15:47:10 +00:00
}
if ( nextstate = = NULL ) nextstate = mo - > FindState ( NAME_Death ) ;
2017-02-25 19:45:28 +00:00
if ( onsky | | ( line ! = NULL & & line - > special = = Line_Horizon ) )
2016-03-01 15:47:10 +00:00
{
2017-02-25 19:45:28 +00:00
if ( ! ( mo - > flags3 & MF3_SKYEXPLODE ) )
{
// [RH] Don't explode missiles on horizon lines.
mo - > Destroy ( ) ;
return ;
}
nextstate = mo - > FindState ( NAME_Death , NAME_Sky ) ;
2016-03-01 15:47:10 +00:00
}
if ( line ! = NULL & & cl_missiledecals )
{
2016-03-27 22:55:57 +00:00
DVector3 pos = mo - > PosRelative ( line ) ;
int side = P_PointOnLineSidePrecise ( pos , line ) ;
2016-03-01 15:47:10 +00:00
if ( line - > sidedef [ side ] = = NULL )
side ^ = 1 ;
if ( line - > sidedef [ side ] ! = NULL )
{
FDecalBase * base = mo - > DecalGenerator ;
if ( base ! = NULL )
{
// Find the nearest point on the line, and stick a decal there
2016-03-27 22:55:57 +00:00
DVector3 linepos ;
2016-03-28 08:01:24 +00:00
double den , frac ;
2016-03-01 15:47:10 +00:00
2016-03-27 22:55:57 +00:00
den = line - > Delta ( ) . LengthSquared ( ) ;
2016-03-01 15:47:10 +00:00
if ( den ! = 0 )
{
2016-03-31 19:13:32 +00:00
frac = clamp < double > ( ( mo - > Pos ( ) . XY ( ) - line - > v1 - > fPos ( ) ) | line - > Delta ( ) , 0 , den ) / den ;
2016-03-01 15:47:10 +00:00
2016-03-31 19:13:32 +00:00
linepos = DVector3 ( line - > v1 - > fPos ( ) + line - > Delta ( ) * frac , pos . Z ) ;
2016-03-01 15:47:10 +00:00
F3DFloor * ffloor = NULL ;
if ( line - > sidedef [ side ^ 1 ] ! = NULL )
{
sector_t * backsector = line - > sidedef [ side ^ 1 ] - > sector ;
extsector_t : : xfloor & xf = backsector - > e - > XFloor ;
// find a 3D-floor to stick to
for ( unsigned int i = 0 ; i < xf . ffloors . Size ( ) ; i + + )
{
F3DFloor * rover = xf . ffloors [ i ] ;
if ( ( rover - > flags & ( FF_EXISTS | FF_SOLID | FF_RENDERSIDES ) ) = = ( FF_EXISTS | FF_SOLID | FF_RENDERSIDES ) )
{
2016-03-27 22:55:57 +00:00
if ( pos . Z < = rover - > top . plane - > ZatPoint ( linepos ) & & pos . Z > = rover - > bottom . plane - > ZatPoint ( linepos ) )
2016-03-01 15:47:10 +00:00
{
2016-03-27 22:55:57 +00:00
ffloor = rover ;
2016-03-01 15:47:10 +00:00
break ;
}
}
}
}
2019-01-27 00:49:20 +00:00
DImpactDecal : : StaticCreate ( mo - > Level , base - > GetDecal ( ) , linepos , line - > sidedef [ side ] , ffloor ) ;
2016-03-01 15:47:10 +00:00
}
}
}
}
2017-01-12 21:49:18 +00:00
// play the sound before changing the state, so that AActor::OnDestroy can call S_RelinkSounds on it and the death state can override it.
2016-03-01 15:47:10 +00:00
if ( mo - > DeathSound )
{
S_Sound ( mo , CHAN_VOICE , mo - > DeathSound , 1 ,
( mo - > flags3 & MF3_FULLVOLDEATH ) ? ATTN_NONE : ATTN_NORM ) ;
}
mo - > SetState ( nextstate ) ;
if ( ! ( mo - > ObjectFlags & OF_EuthanizeMe ) )
{
// The rest only applies if the missile actor still exists.
// [RH] Change render style of exploding rockets
if ( mo - > flags5 & MF5_DEHEXPLOSION )
{
if ( deh . ExplosionStyle = = 255 )
{
if ( addrocketexplosion )
{
mo - > RenderStyle = STYLE_Add ;
2016-03-21 11:18:46 +00:00
mo - > Alpha = 1. ;
2016-03-01 15:47:10 +00:00
}
else
{
mo - > RenderStyle = STYLE_Translucent ;
2016-03-21 11:18:46 +00:00
mo - > Alpha = 0.6666 ;
2016-03-01 15:47:10 +00:00
}
}
else
{
mo - > RenderStyle = ERenderStyle ( deh . ExplosionStyle ) ;
2016-03-21 11:18:46 +00:00
mo - > Alpha = deh . ExplosionAlpha ;
2016-03-01 15:47:10 +00:00
}
}
if ( mo - > flags4 & MF4_RANDOMIZE )
{
mo - > tics - = ( pr_explodemissile ( ) & 3 ) * TICRATE / 35 ;
if ( mo - > tics < 1 )
mo - > tics = 1 ;
}
mo - > flags & = ~ MF_MISSILE ;
}
}
2016-11-27 21:14:18 +00:00
DEFINE_ACTION_FUNCTION ( AActor , ExplodeMissile )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2018-11-17 09:03:40 +00:00
PARAM_POINTER ( line , line_t ) ;
PARAM_OBJECT ( target , AActor ) ;
2016-11-27 21:14:18 +00:00
P_ExplodeMissile ( self , line , target ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
void AActor : : PlayBounceSound ( bool onfloor )
{
if ( ! onfloor & & ( BounceFlags & BOUNCE_NoWallSound ) )
{
return ;
}
if ( ! ( BounceFlags & BOUNCE_Quiet ) )
{
if ( BounceFlags & BOUNCE_UseSeeSound )
{
S_Sound ( this , CHAN_VOICE , SeeSound , 1 , ATTN_IDLE ) ;
}
else if ( onfloor | | WallBounceSound < = 0 )
{
S_Sound ( this , CHAN_VOICE , BounceSound , 1 , ATTN_IDLE ) ;
}
else
{
S_Sound ( this , CHAN_VOICE , WallBounceSound , 1 , ATTN_IDLE ) ;
}
}
}
//----------------------------------------------------------------------------
//
// PROC P_FloorBounceMissile
//
// Returns true if the missile was destroyed
//----------------------------------------------------------------------------
bool AActor : : FloorBounceMissile ( secplane_t & plane )
{
2018-11-05 15:56:49 +00:00
// [ZZ] if bouncing missile hits a damageable sector(plane), it dies
if ( P_ProjectileHitPlane ( this , - 1 ) & & bouncecount > 0 )
{
Vel . Zero ( ) ;
Speed = 0 ;
bouncecount = 0 ;
if ( flags & MF_MISSILE )
P_ExplodeMissile ( this , nullptr , nullptr ) ;
else CallDie ( nullptr , nullptr ) ;
return true ;
}
2016-03-27 22:55:57 +00:00
if ( Z ( ) < = floorz & & P_HitFloor ( this ) )
2016-03-01 15:47:10 +00:00
{
// Landed in some sort of liquid
if ( BounceFlags & BOUNCE_ExplodeOnWater )
{
if ( flags & MF_MISSILE )
P_ExplodeMissile ( this , NULL , NULL ) ;
else
2016-11-26 00:14:47 +00:00
CallDie ( NULL , NULL ) ;
2016-03-01 15:47:10 +00:00
return true ;
}
if ( ! ( BounceFlags & BOUNCE_CanBounceWater ) )
{
Destroy ( ) ;
return true ;
}
}
2018-11-30 10:22:34 +00:00
bool onsky ;
2016-03-29 10:40:41 +00:00
if ( plane . fC ( ) < 0 )
2016-03-01 15:47:10 +00:00
{ // on ceiling
if ( ! ( BounceFlags & BOUNCE_Ceilings ) )
return true ;
2018-11-30 10:22:34 +00:00
onsky = ceilingpic = = skyflatnum ;
2016-03-01 15:47:10 +00:00
}
else
{ // on floor
if ( ! ( BounceFlags & BOUNCE_Floors ) )
return true ;
2018-11-30 10:22:34 +00:00
onsky = floorpic = = skyflatnum ;
}
if ( onsky & & ( BounceFlags & BOUNCE_NotOnSky ) )
{
Destroy ( ) ;
return true ;
2016-03-01 15:47:10 +00:00
}
// The amount of bounces is limited
if ( bouncecount > 0 & & - - bouncecount = = 0 )
{
if ( flags & MF_MISSILE )
P_ExplodeMissile ( this , NULL , NULL ) ;
else
2016-11-26 00:14:47 +00:00
CallDie ( NULL , NULL ) ;
2016-03-01 15:47:10 +00:00
return true ;
}
2016-03-19 23:54:18 +00:00
double dot = ( Vel | plane . Normal ( ) ) * 2 ;
2016-03-01 15:47:10 +00:00
if ( BounceFlags & ( BOUNCE_HereticType | BOUNCE_MBF ) )
{
2016-03-19 23:54:18 +00:00
Vel - = plane . Normal ( ) * dot ;
2016-03-16 11:41:26 +00:00
AngleFromVel ( ) ;
2016-03-01 15:47:10 +00:00
if ( ! ( BounceFlags & BOUNCE_MBF ) ) // Heretic projectiles die, MBF projectiles don't.
{
flags | = MF_INBOUNCE ;
SetState ( FindState ( NAME_Death ) ) ;
flags & = ~ MF_INBOUNCE ;
return false ;
}
2016-03-24 19:43:35 +00:00
else Vel . Z * = 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
Vel = ( Vel - plane . Normal ( ) * dot ) * bouncefactor ;
2016-03-16 11:41:26 +00:00
AngleFromVel ( ) ;
2016-03-01 15:47:10 +00:00
}
PlayBounceSound ( true ) ;
// Set bounce state
if ( BounceFlags & BOUNCE_UseBounceState )
{
2018-08-18 23:14:15 +00:00
FName names [ 2 ] = { NAME_Bounce , plane . fC ( ) < 0 ? NAME_Ceiling : NAME_Floor } ;
FState * bouncestate = FindState ( 2 , names ) ;
if ( bouncestate ! = nullptr )
2016-03-01 15:47:10 +00:00
{
SetState ( bouncestate ) ;
}
}
if ( BounceFlags & BOUNCE_MBF ) // Bring it to rest below a certain speed
{
2016-03-19 23:54:18 +00:00
if ( fabs ( Vel . Z ) < Mass * GetGravity ( ) / 64 )
Vel . Z = 0 ;
2016-03-01 15:47:10 +00:00
}
else if ( BounceFlags & ( BOUNCE_AutoOff | BOUNCE_AutoOffFloorOnly ) )
{
2016-03-29 10:40:41 +00:00
if ( plane . fC ( ) > 0 | | ( BounceFlags & BOUNCE_AutoOff ) )
2016-03-01 15:47:10 +00:00
{
// AutoOff only works when bouncing off a floor, not a ceiling (or in compatibility mode.)
2016-03-19 23:54:18 +00:00
if ( ! ( flags & MF_NOGRAVITY ) & & ( Vel . Z < 3 ) )
2016-03-01 15:47:10 +00:00
BounceFlags & = ~ BOUNCE_TypeMask ;
}
}
return false ;
}
//----------------------------------------------------------------------------
//
// FUNC P_FaceMobj
//
// Returns 1 if 'source' needs to turn clockwise, or 0 if 'source' needs
// to turn counter clockwise. 'delta' is set to the amount 'source'
// needs to turn.
//
//----------------------------------------------------------------------------
2016-03-16 11:41:26 +00:00
int P_FaceMobj ( AActor * source , AActor * target , DAngle * delta )
2016-03-01 15:47:10 +00:00
{
2016-03-16 11:41:26 +00:00
DAngle diff ;
2016-03-01 15:47:10 +00:00
2016-03-16 23:07:37 +00:00
diff = deltaangle ( source - > Angles . Yaw , source - > AngleTo ( target ) ) ;
2016-03-16 11:41:26 +00:00
if ( diff > 0 )
2016-03-01 15:47:10 +00:00
{
2016-03-16 11:41:26 +00:00
* delta = diff ;
2016-03-16 23:07:37 +00:00
return 1 ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-16 11:41:26 +00:00
* delta = - diff ;
2016-03-16 23:07:37 +00:00
return 0 ;
2016-03-01 15:47:10 +00:00
}
}
//----------------------------------------------------------------------------
//
// CanSeek
//
// Checks if a seeker missile can home in on its target
//
//----------------------------------------------------------------------------
bool AActor : : CanSeek ( AActor * target ) const
{
if ( target - > flags5 & MF5_CANTSEEK ) return false ;
if ( ( flags2 & MF2_DONTSEEKINVISIBLE ) & &
( ( target - > flags & MF_SHADOW ) | |
( target - > renderflags & RF_INVISIBLE ) | |
2016-03-21 11:18:46 +00:00
! target - > RenderStyle . IsVisible ( target - > Alpha )
2016-03-01 15:47:10 +00:00
)
) return false ;
return true ;
}
2016-11-07 22:16:25 +00:00
DEFINE_ACTION_FUNCTION ( AActor , CanSeek )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2016-12-02 11:06:49 +00:00
PARAM_OBJECT_NOT_NULL ( target , AActor ) ;
2016-11-07 22:16:25 +00:00
ACTION_RETURN_BOOL ( self - > CanSeek ( target ) ) ;
}
2016-03-01 15:47:10 +00:00
//----------------------------------------------------------------------------
//
// FUNC P_SeekerMissile
//
// The missile's tracer field must be the target. Returns true if
// target was tracked, false if not.
//
//----------------------------------------------------------------------------
2016-03-21 21:20:10 +00:00
bool P_SeekerMissile ( AActor * actor , double thresh , double turnMax , bool precise , bool usecurspeed )
2016-03-01 15:47:10 +00:00
{
int dir ;
2016-03-16 11:41:26 +00:00
DAngle delta ;
2016-03-01 15:47:10 +00:00
AActor * target ;
2016-03-19 23:54:18 +00:00
double speed ;
2016-03-01 15:47:10 +00:00
2016-03-16 11:41:26 +00:00
speed = ! usecurspeed ? actor - > Speed : actor - > VelToSpeed ( ) ;
2016-03-01 15:47:10 +00:00
target = actor - > tracer ;
if ( target = = NULL | | ! actor - > CanSeek ( target ) )
{
return false ;
}
if ( ! ( target - > flags & MF_SHOOTABLE ) )
{ // Target died
2019-01-07 08:14:52 +00:00
actor - > tracer = nullptr ;
2016-03-01 15:47:10 +00:00
return false ;
}
if ( speed = = 0 )
{ // Technically, we're not seeking since our speed is 0, but the target *is* seekable.
return true ;
}
dir = P_FaceMobj ( actor , target , & delta ) ;
if ( delta > thresh )
{
2016-03-16 11:41:26 +00:00
delta / = 2 ;
2016-03-01 15:47:10 +00:00
if ( delta > turnMax )
{
delta = turnMax ;
}
}
if ( dir )
{ // Turn clockwise
2016-03-16 11:41:26 +00:00
actor - > Angles . Yaw + = delta ;
2016-03-01 15:47:10 +00:00
}
else
{ // Turn counter clockwise
2016-03-16 11:41:26 +00:00
actor - > Angles . Yaw - = delta ;
2016-03-01 15:47:10 +00:00
}
if ( ! precise )
{
2016-03-16 11:41:26 +00:00
actor - > VelFromAngle ( speed ) ;
2016-03-01 15:47:10 +00:00
if ( ! ( actor - > flags3 & ( MF3_FLOORHUGGER | MF3_CEILINGHUGGER ) ) )
{
if ( actor - > Top ( ) < target - > Z ( ) | |
target - > Top ( ) < actor - > Z ( ) )
{ // Need to seek vertically
2016-03-19 23:54:18 +00:00
actor - > Vel . Z = ( target - > Center ( ) - actor - > Center ( ) ) / actor - > DistanceBySpeed ( target , speed ) ;
2016-03-01 15:47:10 +00:00
}
}
}
else
{
2016-03-16 21:29:35 +00:00
DAngle pitch = 0. ;
2016-03-01 15:47:10 +00:00
if ( ! ( actor - > flags3 & ( MF3_FLOORHUGGER | MF3_CEILINGHUGGER ) ) )
{ // Need to seek vertically
2016-03-22 17:06:08 +00:00
double dist = MAX ( 1. , actor - > Distance2D ( target ) ) ;
2016-03-01 15:47:10 +00:00
// Aim at a player's eyes and at the middle of the actor for everything else.
2016-03-22 17:06:08 +00:00
double aimheight = target - > Height / 2 ;
2019-01-03 17:01:58 +00:00
if ( target - > player )
2016-03-01 15:47:10 +00:00
{
2019-01-03 17:01:58 +00:00
aimheight = target - > player - > DefaultViewHeight ( ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-22 17:06:08 +00:00
pitch = DVector2 ( dist , target - > Z ( ) + aimheight - actor - > Center ( ) ) . Angle ( ) ;
2016-03-01 15:47:10 +00:00
}
2016-04-04 21:07:21 +00:00
actor - > Vel3DFromAngle ( - pitch , speed ) ;
2016-03-01 15:47:10 +00:00
}
return true ;
}
//
// P_XYMovement
//
// Returns the actor's old floorz.
//
2016-03-19 23:54:18 +00:00
# define STOPSPEED (0x1000 / 65536.)
2016-03-27 22:55:57 +00:00
# define CARRYSTOPSPEED ((0x1000*32 / 3) / 65536.)
2016-03-01 15:47:10 +00:00
2016-03-27 22:55:57 +00:00
double P_XYMovement ( AActor * mo , DVector2 scroll )
2016-03-01 15:47:10 +00:00
{
static int pushtime = 0 ;
2016-03-27 22:55:57 +00:00
bool bForceSlide = ! scroll . isZero ( ) ;
DVector2 ptry ;
2016-03-01 15:47:10 +00:00
player_t * player ;
2016-03-27 22:55:57 +00:00
DVector2 move ;
2016-03-01 15:47:10 +00:00
const secplane_t * walkplane ;
2016-03-20 00:25:47 +00:00
static const double windTab [ 3 ] = { 5 / 32. , 10 / 32. , 25 / 32. } ;
2016-03-01 15:47:10 +00:00
int steps , step , totalsteps ;
2016-03-27 22:55:57 +00:00
DVector2 start ;
double Oldfloorz = mo - > floorz ;
double oldz = mo - > Z ( ) ;
2016-03-01 15:47:10 +00:00
2016-03-19 23:54:18 +00:00
double maxmove = ( mo - > waterlevel < 1 ) | | ( mo - > flags & MF_MISSILE ) | |
2016-03-21 23:06:58 +00:00
( mo - > player & & mo - > player - > crouchoffset < - 10 ) ? MAXMOVE : MAXMOVE / 4 ;
2016-03-01 15:47:10 +00:00
if ( mo - > flags2 & MF2_WINDTHRUST & & mo - > waterlevel < 2 & & ! ( mo - > flags & MF_NOCLIP ) )
{
int special = mo - > Sector - > special ;
switch ( special )
{
case 40 : case 41 : case 42 : // Wind_East
2016-03-20 00:25:47 +00:00
mo - > Thrust ( 0. , windTab [ special - 40 ] ) ;
2016-03-01 15:47:10 +00:00
break ;
case 43 : case 44 : case 45 : // Wind_North
2016-03-20 00:25:47 +00:00
mo - > Thrust ( 90. , windTab [ special - 43 ] ) ;
2016-03-01 15:47:10 +00:00
break ;
case 46 : case 47 : case 48 : // Wind_South
2016-03-20 00:25:47 +00:00
mo - > Thrust ( 270. , windTab [ special - 46 ] ) ;
2016-03-01 15:47:10 +00:00
break ;
case 49 : case 50 : case 51 : // Wind_West
2016-03-20 00:25:47 +00:00
mo - > Thrust ( 180. , windTab [ special - 49 ] ) ;
2016-03-01 15:47:10 +00:00
break ;
}
}
// [RH] No need to clamp these now. However, wall running needs it so
// that large thrusts can't propel an actor through a wall, because wall
// running depends on the player's original movement continuing even after
// it gets blocked.
2018-11-17 23:34:27 +00:00
//
// [anon] When friction is turned off, turn off the crouching and water
// speed caps as well, since it is a sort of friction, and the modders
// most likely want to deal with that themselves.
2019-01-29 18:28:22 +00:00
if ( ( mo - > player ! = NULL & & ( mo - > Level - > i_compatflags & COMPATF_WALLRUN ) ) | | ( ( mo - > waterlevel > = 1 | |
2018-11-17 23:34:27 +00:00
( mo - > player ! = NULL & & mo - > player - > crouchfactor < 0.75 ) ) & & ! ( mo - > flags8 & MF8_NOFRICTION ) ) )
2016-03-01 15:47:10 +00:00
{
// preserve the direction instead of clamping x and y independently.
2016-03-19 23:54:18 +00:00
double cx = mo - > Vel . X = = 0 ? 1. : clamp ( mo - > Vel . X , - maxmove , maxmove ) / mo - > Vel . X ;
double cy = mo - > Vel . Y = = 0 ? 1. : clamp ( mo - > Vel . Y , - maxmove , maxmove ) / mo - > Vel . Y ;
double fac = MIN ( cx , cy ) ;
2016-03-01 15:47:10 +00:00
2016-03-19 23:54:18 +00:00
mo - > Vel . X * = fac ;
mo - > Vel . Y * = fac ;
2016-03-01 15:47:10 +00:00
}
2016-08-31 07:18:59 +00:00
const double VELOCITY_THRESHOLD = 5000 ; // don't let it move faster than this. Fixed point overflowed at 32768 but that's too much to make this safe.
if ( mo - > Vel . LengthSquared ( ) > = VELOCITY_THRESHOLD * VELOCITY_THRESHOLD )
{
mo - > Vel . MakeResize ( VELOCITY_THRESHOLD ) ;
}
2016-03-27 22:55:57 +00:00
move = mo - > Vel ;
2016-03-01 15:47:10 +00:00
// [RH] Carrying sectors didn't work with low speeds in BOOM. This is
// because BOOM relied on the speed being fast enough to accumulate
// despite friction. If the speed is too low, then its movement will get
// cancelled, and it won't accumulate to the desired speed.
mo - > flags4 & = ~ MF4_SCROLLMOVE ;
2016-03-27 22:55:57 +00:00
if ( fabs ( scroll . X ) > CARRYSTOPSPEED )
2016-03-01 15:47:10 +00:00
{
2016-03-27 22:55:57 +00:00
scroll . X * = CARRYFACTOR ;
mo - > Vel . X + = scroll . X ;
2016-03-01 15:47:10 +00:00
mo - > flags4 | = MF4_SCROLLMOVE ;
}
2016-03-27 22:55:57 +00:00
if ( fabs ( scroll . Y ) > CARRYSTOPSPEED )
2016-03-01 15:47:10 +00:00
{
2016-03-27 22:55:57 +00:00
scroll . Y * = CARRYFACTOR ;
mo - > Vel . Y + = scroll . Y ;
2016-03-01 15:47:10 +00:00
mo - > flags4 | = MF4_SCROLLMOVE ;
}
2016-03-27 22:55:57 +00:00
move + = scroll ;
2016-03-01 15:47:10 +00:00
2016-03-27 22:55:57 +00:00
if ( move . isZero ( ) )
2016-03-01 15:47:10 +00:00
{
if ( mo - > flags & MF_SKULLFLY )
{
// the skull slammed into something
mo - > flags & = ~ MF_SKULLFLY ;
2016-03-19 23:54:18 +00:00
mo - > Vel . Zero ( ) ;
2016-03-01 15:47:10 +00:00
if ( ! ( mo - > flags2 & MF2_DORMANT ) )
{
if ( mo - > SeeState ! = NULL ) mo - > SetState ( mo - > SeeState ) ;
else mo - > SetIdle ( ) ;
}
else
{
mo - > SetIdle ( ) ;
mo - > tics = - 1 ;
}
}
2016-03-27 22:55:57 +00:00
return Oldfloorz ;
2016-03-01 15:47:10 +00:00
}
player = mo - > player ;
// [RH] Adjust player movement on sloped floors
2016-03-27 22:55:57 +00:00
DVector2 startmove = move ;
walkplane = P_CheckSlopeWalk ( mo , move ) ;
2016-03-01 15:47:10 +00:00
// [RH] Take smaller steps when moving faster than the object's size permits.
// Moving as fast as the object's "diameter" is bad because it could skip
// some lines because the actor could land such that it is just touching the
// line. For Doom to detect that the line is there, it needs to actually cut
// through the actor.
{
2016-03-27 22:55:57 +00:00
double maxmove = mo - > radius - 1 ;
2016-03-01 15:47:10 +00:00
if ( maxmove < = 0 )
2016-03-27 22:55:57 +00:00
{ // gibs can have radius 0, so don't divide by zero below!
maxmove = MAXMOVE ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 22:55:57 +00:00
const double xspeed = fabs ( move . X ) ;
const double yspeed = fabs ( move . Y ) ;
2016-03-01 15:47:10 +00:00
steps = 1 ;
if ( xspeed > yspeed )
{
if ( xspeed > maxmove )
{
2016-03-27 22:55:57 +00:00
steps = int ( 1 + xspeed / maxmove ) ;
2016-03-01 15:47:10 +00:00
}
}
else
{
if ( yspeed > maxmove )
{
2016-03-27 22:55:57 +00:00
steps = int ( 1 + yspeed / maxmove ) ;
2016-03-01 15:47:10 +00:00
}
}
}
// P_SlideMove needs to know the step size before P_CheckSlopeWalk
// because it also calls P_CheckSlopeWalk on its clipped steps.
2016-03-27 22:55:57 +00:00
DVector2 onestep = startmove / steps ;
2016-03-01 15:47:10 +00:00
2016-03-27 22:55:57 +00:00
start = mo - > Pos ( ) ;
2016-03-01 15:47:10 +00:00
step = 1 ;
totalsteps = steps ;
// [RH] Instead of doing ripping damage each step, do it each tic.
// This makes it compatible with Heretic and Hexen, which only did
// one step for their missiles with ripping damage (excluding those
// that don't use P_XYMovement). It's also more intuitive since it
// makes the damage done dependant on the amount of time the projectile
// spends inside a target rather than on the projectile's size. The
// last actor ripped through is recorded so that if the projectile
// passes through more than one actor this tic, each one takes damage
// and not just the first one.
pushtime + + ;
FCheckPosition tm ( ! ! ( mo - > flags2 & MF2_RIP ) ) ;
2016-03-27 22:55:57 +00:00
DAngle oldangle = mo - > Angles . Yaw ;
2016-03-01 15:47:10 +00:00
do
{
2019-01-29 18:28:22 +00:00
if ( mo - > Level - > i_compatflags & COMPATF_WALLRUN ) pushtime + + ;
2016-03-01 15:47:10 +00:00
tm . PushTime = pushtime ;
2016-03-27 22:55:57 +00:00
ptry = start + move * step / steps ;
2016-03-01 15:47:10 +00:00
2016-03-27 22:55:57 +00:00
DVector2 startvel = mo - > Vel ;
2016-03-01 15:47:10 +00:00
2016-03-27 22:55:57 +00:00
// killough 3/15/98: Allow objects to drop off
// [RH] If walking on a slope, stay on the slope
if ( ! P_TryMove ( mo , ptry , true , walkplane , tm ) )
2016-03-01 15:47:10 +00:00
{
// blocked move
AActor * BlockingMobj = mo - > BlockingMobj ;
line_t * BlockingLine = mo - > BlockingLine ;
2018-10-31 19:56:01 +00:00
// [ZZ]
if ( ! BlockingLine & & ! BlockingMobj ) // hit floor or ceiling while XY movement - sector actions
{
int hitpart = - 1 ;
sector_t * hitsector = nullptr ;
secplane_t * hitplane = nullptr ;
if ( tm . ceilingsector & & mo - > Z ( ) + mo - > Height > tm . ceilingsector - > ceilingplane . ZatPoint ( tm . pos . XY ( ) ) )
mo - > BlockingCeiling = tm . ceilingsector ;
if ( tm . floorsector & & mo - > Z ( ) < tm . floorsector - > floorplane . ZatPoint ( tm . pos . XY ( ) ) )
mo - > BlockingFloor = tm . floorsector ;
// the following two only set the appropriate field - to avoid issues caused by running actions right in the middle of XY movement
P_CheckFor3DFloorHit ( mo , mo - > floorz , false ) ;
P_CheckFor3DCeilingHit ( mo , mo - > ceilingz , false ) ;
}
2016-03-01 15:47:10 +00:00
if ( ! ( mo - > flags & MF_MISSILE ) & & ( mo - > BounceFlags & BOUNCE_MBF )
& & ( BlockingMobj ! = NULL ? P_BounceActor ( mo , BlockingMobj , false ) : P_BounceWall ( mo ) ) )
{
// Do nothing, relevant actions already done in the condition.
// This allows to avoid setting velocities to 0 in the final else of this series.
}
else if ( ( mo - > flags2 & ( MF2_SLIDE | MF2_BLASTED ) | | bForceSlide ) & & ! ( mo - > flags & MF_MISSILE ) )
{ // try to slide along it
if ( BlockingMobj = = NULL )
{ // slide against wall
if ( BlockingLine ! = NULL & &
mo - > player & & mo - > waterlevel & & mo - > waterlevel < 3 & &
( mo - > player - > cmd . ucmd . forwardmove | mo - > player - > cmd . ucmd . sidemove ) & &
mo - > BlockingLine - > sidedef [ 1 ] ! = NULL )
{
2016-03-19 23:54:18 +00:00
mo - > Vel . Z = WATER_JUMP_SPEED ;
2016-03-01 15:47:10 +00:00
}
// If the blocked move executed any push specials that changed the
// actor's velocity, do not attempt to slide.
2016-03-27 22:55:57 +00:00
if ( mo - > Vel . XY ( ) = = startvel )
2016-03-01 15:47:10 +00:00
{
2019-01-29 18:28:22 +00:00
if ( player & & ( mo - > Level - > i_compatflags & COMPATF_WALLRUN ) )
2016-03-01 15:47:10 +00:00
{
// [RH] Here is the key to wall running: The move is clipped using its full speed.
// If the move is done a second time (because it was too fast for one move), it
// is still clipped against the wall at its full speed, so you effectively
// execute two moves in one tic.
2016-03-27 11:29:58 +00:00
P_SlideMove ( mo , mo - > Vel , 1 ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-27 22:55:57 +00:00
P_SlideMove ( mo , onestep , totalsteps ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 22:55:57 +00:00
if ( mo - > Vel . XY ( ) . isZero ( ) )
2016-03-01 15:47:10 +00:00
{
steps = 0 ;
}
else
{
2019-01-29 18:28:22 +00:00
if ( ! player | | ! ( mo - > Level - > i_compatflags & COMPATF_WALLRUN ) )
2016-03-01 15:47:10 +00:00
{
2016-03-27 22:55:57 +00:00
move = mo - > Vel ;
onestep = move / steps ;
P_CheckSlopeWalk ( mo , move ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 22:55:57 +00:00
start = mo - > Pos ( ) . XY ( ) - move * step / steps ;
2016-03-01 15:47:10 +00:00
}
}
else
{
steps = 0 ;
}
}
else
{ // slide against another actor
2016-03-27 22:55:57 +00:00
DVector2 t ;
t . X = 0 , t . Y = onestep . Y ;
walkplane = P_CheckSlopeWalk ( mo , t ) ;
if ( P_TryMove ( mo , mo - > Pos ( ) + t , true , walkplane , tm ) )
2016-03-01 15:47:10 +00:00
{
2016-03-19 23:54:18 +00:00
mo - > Vel . X = 0 ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-27 22:55:57 +00:00
t . X = onestep . X , t . Y = 0 ;
walkplane = P_CheckSlopeWalk ( mo , t ) ;
if ( P_TryMove ( mo , mo - > Pos ( ) + t , true , walkplane , tm ) )
2016-03-01 15:47:10 +00:00
{
2016-03-19 23:54:18 +00:00
mo - > Vel . Y = 0 ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-19 23:54:18 +00:00
mo - > Vel . X = mo - > Vel . Y = 0 ;
2016-03-01 15:47:10 +00:00
}
}
if ( player & & player - > mo = = mo )
{
2016-03-19 23:54:18 +00:00
if ( mo - > Vel . X = = 0 )
player - > Vel . X = 0 ;
if ( mo - > Vel . Y = = 0 )
player - > Vel . Y = 0 ;
2016-03-01 15:47:10 +00:00
}
steps = 0 ;
}
}
else if ( mo - > flags & MF_MISSILE )
{
steps = 0 ;
if ( BlockingMobj )
{
if ( mo - > BounceFlags & BOUNCE_Actors )
{
// Bounce test and code moved to P_BounceActor
if ( ! P_BounceActor ( mo , BlockingMobj , false ) )
{ // Struck a player/creature
P_ExplodeMissile ( mo , NULL , BlockingMobj ) ;
}
2016-03-27 22:55:57 +00:00
return Oldfloorz ;
2016-03-01 15:47:10 +00:00
}
}
else
{
// Struck a wall
if ( P_BounceWall ( mo ) )
{
mo - > PlayBounceSound ( false ) ;
2016-03-27 22:55:57 +00:00
return Oldfloorz ;
2016-03-01 15:47:10 +00:00
}
}
if ( BlockingMobj & & ( BlockingMobj - > flags2 & MF2_REFLECTIVE ) )
{
bool seeker = ( mo - > flags2 & MF2_SEEKERMISSILE ) ? true : false ;
// Don't change the angle if there's THRUREFLECT on the monster.
if ( ! ( BlockingMobj - > flags7 & MF7_THRUREFLECT ) )
{
2016-03-16 23:07:37 +00:00
DAngle angle = BlockingMobj - > AngleTo ( mo ) ;
2016-03-01 15:47:10 +00:00
bool dontReflect = ( mo - > AdjustReflectionAngle ( BlockingMobj , angle ) ) ;
// Change angle for deflection/reflection
if ( ! dontReflect )
{
bool tg = ( mo - > target ! = NULL ) ;
bool blockingtg = ( BlockingMobj - > target ! = NULL ) ;
if ( ( BlockingMobj - > flags7 & MF7_AIMREFLECT ) & & ( tg | blockingtg ) )
{
AActor * origin = tg ? mo - > target : BlockingMobj - > target ;
//dest->x - source->x
2016-03-19 23:54:18 +00:00
DVector3 vect = mo - > Vec3To ( origin ) ;
2016-03-20 19:55:06 +00:00
vect . Z + = origin - > Height / 2 ;
2016-03-19 23:54:18 +00:00
mo - > Vel = vect . Resized ( mo - > Speed ) ;
2016-03-01 15:47:10 +00:00
}
else
{
if ( ( BlockingMobj - > flags7 & MF7_MIRRORREFLECT ) & & ( tg | blockingtg ) )
{
2016-03-16 11:41:26 +00:00
mo - > Angles . Yaw + = 180. ;
2016-03-19 23:54:18 +00:00
mo - > Vel * = - .5 ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-16 11:41:26 +00:00
mo - > Angles . Yaw = angle ;
mo - > VelFromAngle ( mo - > Speed / 2 ) ;
2016-03-19 23:54:18 +00:00
mo - > Vel . Z * = - .5 ;
2016-03-01 15:47:10 +00:00
}
}
}
else
{
goto explode ;
}
}
if ( mo - > flags2 & MF2_SEEKERMISSILE )
{
mo - > tracer = mo - > target ;
}
mo - > target = BlockingMobj ;
2016-03-27 22:55:57 +00:00
return Oldfloorz ;
2016-03-01 15:47:10 +00:00
}
explode :
// explode a missile
2017-02-25 19:45:28 +00:00
bool onsky = false ;
2018-11-30 10:22:34 +00:00
if ( tm . ceilingline & & tm . ceilingline - > hitSkyWall ( mo ) )
2018-10-28 22:08:38 +00:00
{
if ( ! ( mo - > flags3 & MF3_SKYEXPLODE ) )
2016-03-01 15:47:10 +00:00
{
2018-10-28 22:08:38 +00:00
// Hack to prevent missiles exploding against the sky.
// Does not handle sky floors.
mo - > Destroy ( ) ;
return Oldfloorz ;
}
else onsky = true ;
}
// [RH] Don't explode on horizon lines.
if ( mo - > BlockingLine ! = NULL & & mo - > BlockingLine - > special = = Line_Horizon )
{
if ( ! ( mo - > flags3 & MF3_SKYEXPLODE ) )
{
mo - > Destroy ( ) ;
return Oldfloorz ;
2016-03-01 15:47:10 +00:00
}
2018-10-28 22:08:38 +00:00
else onsky = true ;
}
2018-10-31 19:56:01 +00:00
if ( mo - > BlockingCeiling ) // hit floor or ceiling while XY movement
2018-10-28 22:08:38 +00:00
{
2018-11-04 04:53:37 +00:00
P_ProjectileHitPlane ( mo , SECPART_Ceiling ) ;
2018-10-31 19:56:01 +00:00
}
if ( mo - > BlockingFloor )
{
2018-11-04 04:53:37 +00:00
P_ProjectileHitPlane ( mo , SECPART_Floor ) ;
2018-10-28 22:08:38 +00:00
}
2017-02-25 19:45:28 +00:00
P_ExplodeMissile ( mo , mo - > BlockingLine , BlockingMobj , onsky ) ;
2016-03-27 22:55:57 +00:00
return Oldfloorz ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-19 23:54:18 +00:00
mo - > Vel . X = mo - > Vel . Y = 0 ;
2016-03-01 15:47:10 +00:00
steps = 0 ;
}
}
else
{
2016-03-27 22:55:57 +00:00
if ( mo - > Pos ( ) . XY ( ) ! = ptry )
2016-03-01 15:47:10 +00:00
{
// If the new position does not match the desired position, the player
2016-05-20 11:10:42 +00:00
// must have gone through a teleporter or portal.
2016-03-19 23:54:18 +00:00
if ( mo - > Vel . X = = 0 & & mo - > Vel . Y = = 0 )
2016-03-01 15:47:10 +00:00
{
2016-05-20 11:10:42 +00:00
// Stop moving right now if it was a regular teleporter.
2016-03-01 15:47:10 +00:00
step = steps ;
}
else
{
2016-05-20 11:10:42 +00:00
// It was a portal, line-to-line or fogless teleporter, so the move should continue.
// For that we need to adjust the start point, and the movement vector.
2016-03-27 22:55:57 +00:00
DAngle anglediff = deltaangle ( oldangle , mo - > Angles . Yaw ) ;
2016-03-09 11:49:49 +00:00
if ( anglediff ! = 0 )
{
2016-03-27 22:55:57 +00:00
move = move . Rotated ( anglediff ) ;
2016-05-20 11:10:42 +00:00
oldangle = mo - > Angles . Yaw ;
2016-03-09 11:49:49 +00:00
}
2016-03-27 22:55:57 +00:00
start = mo - > Pos ( ) - move * step / steps ;
2016-03-01 15:47:10 +00:00
}
}
}
} while ( + + step < = steps ) ;
// Friction
if ( player & & player - > mo = = mo & & player - > cheats & CF_NOVELOCITY )
{ // debug option for no sliding at all
2016-03-19 23:54:18 +00:00
mo - > Vel . X = mo - > Vel . Y = 0 ;
player - > Vel . X = player - > Vel . Y = 0 ;
2016-03-27 22:55:57 +00:00
return Oldfloorz ;
2016-03-01 15:47:10 +00:00
}
2018-11-17 23:34:27 +00:00
if ( mo - > flags & ( MF_MISSILE | MF_SKULLFLY ) | | mo - > flags8 & MF8_NOFRICTION )
2016-03-01 15:47:10 +00:00
{ // no friction for missiles
2016-03-27 22:55:57 +00:00
return Oldfloorz ;
2016-03-01 15:47:10 +00:00
}
2016-03-20 18:52:35 +00:00
if ( mo - > Z ( ) > mo - > floorz & & ! ( mo - > flags2 & MF2_ONMOBJ ) & &
2016-03-01 15:47:10 +00:00
! mo - > IsNoClip2 ( ) & &
( ! ( mo - > flags2 & MF2_FLY ) | | ! ( mo - > flags & MF_NOGRAVITY ) ) & &
! mo - > waterlevel )
{ // [RH] Friction when falling is available for larger aircontrols
2019-01-27 15:08:22 +00:00
auto airfriction = mo - > Level - > airfriction ;
if ( player ! = NULL & & airfriction ! = 1. )
2016-03-01 15:47:10 +00:00
{
2019-01-27 15:08:22 +00:00
mo - > Vel . X * = airfriction ;
mo - > Vel . Y * = airfriction ;
2016-03-01 15:47:10 +00:00
if ( player - > mo = = mo ) // Not voodoo dolls
{
2019-01-27 15:08:22 +00:00
player - > Vel . X * = airfriction ;
player - > Vel . Y * = airfriction ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-27 22:55:57 +00:00
return Oldfloorz ;
2016-03-01 15:47:10 +00:00
}
// killough 8/11/98: add bouncers
// killough 9/15/98: add objects falling off ledges
// killough 11/98: only include bouncers hanging off ledges
2016-03-23 11:21:52 +00:00
if ( ( mo - > flags & MF_CORPSE ) | | ( mo - > BounceFlags & BOUNCE_MBF & & mo - > Z ( ) > mo - > dropoffz ) | | ( mo - > flags6 & MF6_FALLING ) )
2016-03-01 15:47:10 +00:00
{ // Don't stop sliding if halfway off a step with some velocity
2016-03-23 11:21:52 +00:00
if ( fabs ( mo - > Vel . X ) > 0.25 | | fabs ( mo - > Vel . Y ) > 0.25 )
2016-03-01 15:47:10 +00:00
{
2016-03-30 07:41:46 +00:00
if ( mo - > floorz > mo - > Sector - > floorplane . ZatPoint ( mo ) )
2016-03-01 15:47:10 +00:00
{
2016-03-23 11:21:52 +00:00
if ( mo - > dropoffz ! = mo - > floorz ) // 3DMidtex or other special cases that must be excluded
2016-03-01 15:47:10 +00:00
{
unsigned i ;
for ( i = 0 ; i < mo - > Sector - > e - > XFloor . ffloors . Size ( ) ; i + + )
{
// Sliding around on 3D floors looks extremely bad so
// if the floor comes from one in the current sector stop sliding the corpse!
F3DFloor * rover = mo - > Sector - > e - > XFloor . ffloors [ i ] ;
if ( ! ( rover - > flags & FF_EXISTS ) ) continue ;
2016-03-30 07:41:46 +00:00
if ( rover - > flags & FF_SOLID & & rover - > top . plane - > ZatPoint ( mo ) = = mo - > floorz ) break ;
2016-03-01 15:47:10 +00:00
}
if ( i = = mo - > Sector - > e - > XFloor . ffloors . Size ( ) )
2016-03-27 22:55:57 +00:00
return Oldfloorz ;
2016-03-01 15:47:10 +00:00
}
}
}
}
// killough 11/98:
// Stop voodoo dolls that have come to rest, despite any
// moving corresponding player:
2016-03-19 23:54:18 +00:00
if ( fabs ( mo - > Vel . X ) < STOPSPEED & & fabs ( mo - > Vel . Y ) < STOPSPEED
2016-03-01 15:47:10 +00:00
& & ( ! player | | ( player - > mo ! = mo )
| | ! ( player - > cmd . ucmd . forwardmove | player - > cmd . ucmd . sidemove ) ) )
{
// if in a walking frame, stop moving
// killough 10/98:
// Don't affect main player when voodoo dolls stop:
if ( player & & player - > mo = = mo & & ! ( player - > cheats & CF_PREDICTING ) )
{
2019-01-03 12:58:53 +00:00
PlayIdle ( player - > mo ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-19 23:54:18 +00:00
mo - > Vel . X = mo - > Vel . Y = 0 ;
2016-03-01 15:47:10 +00:00
mo - > flags4 & = ~ MF4_SCROLLMOVE ;
// killough 10/98: kill any bobbing velocity too (except in voodoo dolls)
if ( player & & player - > mo = = mo )
2016-03-19 23:54:18 +00:00
player - > Vel . X = player - > Vel . Y = 0 ;
2016-03-01 15:47:10 +00:00
}
else
{
// phares 3/17/98
// Friction will have been adjusted by friction thinkers for icy
// or muddy floors. Otherwise it was never touched and
// remained set at ORIG_FRICTION
//
// killough 8/28/98: removed inefficient thinker algorithm,
// instead using touching_sectorlist in P_GetFriction() to
// determine friction (and thus only when it is needed).
//
// killough 10/98: changed to work with new bobbing method.
// Reducing player velocity is no longer needed to reduce
// bobbing, so ice works much better now.
2016-03-24 21:50:03 +00:00
double friction = P_GetFriction ( mo , NULL ) ;
2016-03-01 15:47:10 +00:00
2016-03-19 23:54:18 +00:00
mo - > Vel . X * = friction ;
mo - > Vel . Y * = friction ;
2016-03-01 15:47:10 +00:00
// killough 10/98: Always decrease player bobbing by ORIG_FRICTION.
// This prevents problems with bobbing on ice, where it was not being
// reduced fast enough, leading to all sorts of kludges being developed.
if ( player & & player - > mo = = mo ) // Not voodoo dolls
{
2016-03-24 21:50:03 +00:00
player - > Vel . X * = ORIG_FRICTION ;
player - > Vel . Y * = ORIG_FRICTION ;
2016-03-19 23:54:18 +00:00
}
// Don't let the velocity become less than the smallest representable fixed point value.
if ( fabs ( mo - > Vel . X ) < MinVel ) mo - > Vel . X = 0 ;
if ( fabs ( mo - > Vel . Y ) < MinVel ) mo - > Vel . Y = 0 ;
if ( player & & player - > mo = = mo ) // Not voodoo dolls
{
if ( fabs ( player - > Vel . X ) < MinVel ) player - > Vel . X = 0 ;
if ( fabs ( player - > Vel . Y ) < MinVel ) player - > Vel . Y = 0 ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-27 22:55:57 +00:00
return Oldfloorz ;
2016-03-01 15:47:10 +00:00
}
// Move this to p_inter ***
void P_MonsterFallingDamage ( AActor * mo )
{
int damage ;
2016-03-28 08:01:24 +00:00
double vel ;
2016-03-01 15:47:10 +00:00
2019-01-27 15:08:22 +00:00
if ( ! ( mo - > Level - > flags2 & LEVEL2_MONSTERFALLINGDAMAGE ) )
2016-03-01 15:47:10 +00:00
return ;
if ( mo - > floorsector - > Flags & SECF_NOFALLINGDAMAGE )
return ;
2016-03-28 08:01:24 +00:00
vel = fabs ( mo - > Vel . Z ) ;
if ( vel > 35 )
2016-03-01 15:47:10 +00:00
{ // automatic death
damage = TELEFRAG_DAMAGE ;
}
else
{
2016-03-28 08:01:24 +00:00
damage = int ( ( vel - 23 ) * 6 ) ;
2016-03-01 15:47:10 +00:00
}
damage = TELEFRAG_DAMAGE ; // always kill 'em
P_DamageMobj ( mo , NULL , NULL , damage , NAME_Falling ) ;
}
//
// P_ZMovement
//
2016-03-28 08:01:24 +00:00
void P_ZMovement ( AActor * mo , double oldfloorz )
2016-03-01 15:47:10 +00:00
{
2016-03-28 08:01:24 +00:00
double dist ;
double delta ;
double oldz = mo - > Z ( ) ;
2016-03-19 23:54:18 +00:00
double grav = mo - > GetGravity ( ) ;
2016-03-01 15:47:10 +00:00
//
// check for smooth step up
//
2016-03-20 18:52:35 +00:00
if ( mo - > player & & mo - > player - > mo = = mo & & mo - > Z ( ) < mo - > floorz )
2016-03-01 15:47:10 +00:00
{
2016-03-22 17:06:08 +00:00
mo - > player - > viewheight - = mo - > floorz - mo - > Z ( ) ;
2016-03-01 15:47:10 +00:00
mo - > player - > deltaviewheight = mo - > player - > GetDeltaViewHeight ( ) ;
}
2016-03-19 23:54:18 +00:00
mo - > AddZ ( mo - > Vel . Z ) ;
2016-03-01 15:47:10 +00:00
//
// apply gravity
//
2016-03-20 18:52:35 +00:00
if ( mo - > Z ( ) > mo - > floorz & & ! ( mo - > flags & MF_NOGRAVITY ) )
2016-03-01 15:47:10 +00:00
{
2016-03-19 23:54:18 +00:00
double startvelz = mo - > Vel . Z ;
2016-03-01 15:47:10 +00:00
if ( mo - > waterlevel = = 0 | | ( mo - > player & &
! ( mo - > player - > cmd . ucmd . forwardmove | mo - > player - > cmd . ucmd . sidemove ) ) )
{
// [RH] Double gravity only if running off a ledge. Coming down from
// an upward thrust (e.g. a jump) should not double it.
2016-03-28 08:01:24 +00:00
if ( mo - > Vel . Z = = 0 & & oldfloorz > mo - > floorz & & mo - > Z ( ) = = oldfloorz )
2016-03-01 15:47:10 +00:00
{
2016-03-19 23:54:18 +00:00
mo - > Vel . Z - = grav + grav ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-19 23:54:18 +00:00
mo - > Vel . Z - = grav ;
2016-03-01 15:47:10 +00:00
}
}
if ( mo - > player = = NULL )
{
if ( mo - > waterlevel > = 1 )
{
2016-03-19 23:54:18 +00:00
double sinkspeed ;
2016-03-01 15:47:10 +00:00
if ( ( mo - > flags & MF_SPECIAL ) & & ! ( mo - > flags3 & MF3_ISMONSTER ) )
{ // Pickup items don't sink if placed and drop slowly if dropped
sinkspeed = ( mo - > flags & MF_DROPPED ) ? - WATER_SINK_SPEED / 8 : 0 ;
}
else
{
sinkspeed = - WATER_SINK_SPEED ;
// If it's not a player, scale sinkspeed by its mass, with
// 100 being equivalent to a player.
if ( mo - > player = = NULL )
{
2016-03-19 23:54:18 +00:00
sinkspeed = sinkspeed * clamp ( mo - > Mass , 1 , 4000 ) / 100 ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-19 23:54:18 +00:00
if ( mo - > Vel . Z < sinkspeed )
2016-03-01 15:47:10 +00:00
{ // Dropping too fast, so slow down toward sinkspeed.
2016-03-19 23:54:18 +00:00
mo - > Vel . Z - = MAX ( sinkspeed * 2 , - 8. ) ;
if ( mo - > Vel . Z > sinkspeed )
2016-03-01 15:47:10 +00:00
{
2016-03-19 23:54:18 +00:00
mo - > Vel . Z = sinkspeed ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-19 23:54:18 +00:00
else if ( mo - > Vel . Z > sinkspeed )
2016-03-01 15:47:10 +00:00
{ // Dropping too slow/going up, so trend toward sinkspeed.
2016-03-19 23:54:18 +00:00
mo - > Vel . Z = startvelz + MAX ( sinkspeed / 3 , - 8. ) ;
if ( mo - > Vel . Z < sinkspeed )
2016-03-01 15:47:10 +00:00
{
2016-03-19 23:54:18 +00:00
mo - > Vel . Z = sinkspeed ;
2016-03-01 15:47:10 +00:00
}
}
}
}
else
{
if ( mo - > waterlevel > 1 )
{
2016-03-19 23:54:18 +00:00
double sinkspeed = - WATER_SINK_SPEED ;
2016-03-01 15:47:10 +00:00
2016-03-19 23:54:18 +00:00
if ( mo - > Vel . Z < sinkspeed )
2016-03-01 15:47:10 +00:00
{
2016-03-19 23:54:18 +00:00
mo - > Vel . Z = ( startvelz < sinkspeed ) ? startvelz : sinkspeed ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-19 23:54:18 +00:00
mo - > Vel . Z = startvelz + ( ( mo - > Vel . Z - startvelz ) *
2016-03-01 15:47:10 +00:00
( mo - > waterlevel = = 1 ? WATER_SINK_SMALL_FACTOR : WATER_SINK_FACTOR ) ) ;
}
}
}
}
// Hexen compatibility handling for floatbobbing. Ugh...
// Hexen yanked all items to the floor, except those being spawned at map start in the air.
// Those were kept at their original height.
// Do this only if the item was actually spawned by the map above ground to avoid problems.
2019-01-29 18:28:22 +00:00
if ( mo - > specialf1 > 0 & & ( mo - > flags2 & MF2_FLOATBOB ) & & ( mo - > Level - > ib_compatflags & BCOMPATF_FLOATBOB ) )
2016-03-01 15:47:10 +00:00
{
2016-03-20 20:51:09 +00:00
mo - > SetZ ( mo - > floorz + mo - > specialf1 ) ;
2016-03-01 15:47:10 +00:00
}
//
// adjust height
//
if ( ( mo - > flags & MF_FLOAT ) & & ! ( mo - > flags2 & MF2_DORMANT ) & & mo - > target )
{ // float down towards target if too close
if ( ! ( mo - > flags & ( MF_SKULLFLY | MF_INFLOAT ) ) )
{
2016-03-28 08:01:24 +00:00
dist = mo - > Distance2D ( mo - > target ) ;
delta = ( mo - > target - > Center ( ) ) - mo - > Z ( ) ;
2016-03-01 15:47:10 +00:00
if ( delta < 0 & & dist < - ( delta * 3 ) )
2016-03-28 08:01:24 +00:00
mo - > AddZ ( - mo - > FloatSpeed ) ;
2016-03-01 15:47:10 +00:00
else if ( delta > 0 & & dist < ( delta * 3 ) )
2016-03-28 08:01:24 +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 ) )
2016-03-01 15:47:10 +00:00
{
if ( ! mo - > IsNoClip2 ( ) )
{
2019-01-27 15:08:22 +00:00
mo - > AddZ ( DAngle ( 360 / 80.f * mo - > Level - > maptime ) . Sin ( ) / 8 ) ;
2016-03-01 15:47:10 +00:00
}
2018-12-27 00:19:50 +00:00
if ( ! ( mo - > flags8 & MF8_NOFRICTION ) )
{
mo - > Vel . Z * = FRICTION_FLY ;
}
2016-03-01 15:47:10 +00:00
}
2018-12-27 00:19:50 +00:00
if ( mo - > waterlevel & & ! ( mo - > flags & MF_NOGRAVITY ) & & ! ( mo - > flags8 & MF8_NOFRICTION ) )
2016-03-01 15:47:10 +00:00
{
2016-03-24 21:50:03 +00:00
double friction = - 1 ;
2016-03-19 23:54:18 +00:00
// Check 3D floors -- might be the source of the waterlevel
for ( auto rover : mo - > Sector - > e - > XFloor . ffloors )
{
if ( ! ( rover - > flags & FF_EXISTS ) ) continue ;
if ( ! ( rover - > flags & FF_SWIMMABLE ) ) continue ;
2016-03-30 07:41:46 +00:00
if ( mo - > Z ( ) > = rover - > top . plane - > ZatPoint ( mo ) | |
mo - > Center ( ) < rover - > bottom . plane - > ZatPoint ( mo ) )
2016-03-19 23:54:18 +00:00
continue ;
friction = rover - > model - > GetFriction ( rover - > top . isceiling ) ;
break ;
}
2016-03-24 21:50:03 +00:00
if ( friction < 0 )
2016-03-19 23:54:18 +00:00
friction = mo - > Sector - > GetFriction ( ) ; // get real friction, even if from a terrain definition
2016-03-24 21:50:03 +00:00
mo - > Vel . Z * = friction ;
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
if ( ( ! mo - > player | | ! ( mo - > player - > cheats & CF_PREDICTING ) ) & &
mo - > Sector - > SecActTarget ! = NULL & &
2016-03-30 07:41:46 +00:00
mo - > Sector - > floorplane . ZatPoint ( mo ) = = mo - > floorz )
2016-03-01 15:47:10 +00:00
{ // [RH] Let the sector do something to the actor
2018-10-31 19:56:01 +00:00
mo - > Sector - > TriggerSectorActions ( mo , SECSPAC_HitFloor ) ;
2016-03-01 15:47:10 +00:00
}
2018-10-31 19:56:01 +00:00
P_CheckFor3DFloorHit ( mo , mo - > floorz , true ) ;
2016-03-01 15:47:10 +00:00
// [RH] Need to recheck this because the sector action might have
// teleported the actor so it is no longer below the floor.
2016-03-20 18:52:35 +00:00
if ( mo - > Z ( ) < = mo - > floorz )
2016-03-01 15:47:10 +00:00
{
2018-10-31 19:56:01 +00:00
mo - > BlockingFloor = mo - > Sector ;
2016-03-01 15:47:10 +00:00
if ( ( mo - > flags & MF_MISSILE ) & & ! ( mo - > flags & MF_NOCLIP ) )
{
2016-03-20 18:52:35 +00:00
mo - > SetZ ( mo - > floorz ) ;
2016-03-01 15:47:10 +00:00
if ( mo - > BounceFlags & BOUNCE_Floors )
{
mo - > FloorBounceMissile ( mo - > floorsector - > floorplane ) ;
/* if (!(mo->flags6 & MF6_CANJUMP)) */ return ;
}
else if ( mo - > flags3 & MF3_NOEXPLODEFLOOR )
{
P_HitFloor ( mo ) ;
2016-03-19 23:54:18 +00:00
mo - > Vel . Z = 0 ;
2016-03-01 15:47:10 +00:00
return ;
}
2017-02-27 09:50:21 +00:00
else if ( ( mo - > flags3 & MF3_FLOORHUGGER ) & & ! ( mo - > flags5 & MF5_NODROPOFF ) )
2016-03-01 15:47:10 +00:00
{ // Floor huggers can go up steps
return ;
}
else
{
2017-02-25 19:45:28 +00:00
bool onsky = false ;
if ( mo - > floorpic = = skyflatnum )
2016-03-01 15:47:10 +00:00
{
2017-02-25 19:45:28 +00:00
if ( ! ( mo - > flags3 & MF3_SKYEXPLODE ) )
{
// [RH] Just remove the missile without exploding it
// if this is a sky floor.
mo - > Destroy ( ) ;
return ;
}
else onsky = true ;
2016-03-01 15:47:10 +00:00
}
P_HitFloor ( mo ) ;
2018-10-28 22:08:38 +00:00
// hit floor: direct damage callback
2018-11-04 04:53:37 +00:00
P_ProjectileHitPlane ( mo , SECPART_Floor ) ;
2017-02-25 19:45:28 +00:00
P_ExplodeMissile ( mo , NULL , NULL , onsky ) ;
2016-03-01 15:47:10 +00:00
return ;
}
}
2016-03-28 08:01:24 +00:00
else if ( mo - > BounceFlags & BOUNCE_MBF & & mo - > Vel . Z ) // check for MBF-like bounce on non-missiles
2016-03-01 15:47:10 +00:00
{
mo - > FloorBounceMissile ( mo - > floorsector - > floorplane ) ;
}
if ( mo - > flags3 & MF3_ISMONSTER ) // Blasted mobj falling
{
2016-03-28 08:01:24 +00:00
if ( mo - > Vel . Z < - 23 )
2016-03-01 15:47:10 +00:00
{
P_MonsterFallingDamage ( mo ) ;
}
}
2016-03-20 18:52:35 +00:00
mo - > SetZ ( mo - > floorz ) ;
2016-03-28 08:01:24 +00:00
if ( mo - > Vel . Z < 0 )
2016-03-01 15:47:10 +00:00
{
2016-03-28 08:01:24 +00:00
const double minvel = - 8 ; // landing speed from a jump with normal gravity
2016-03-01 15:47:10 +00:00
// Spawn splashes, etc.
P_HitFloor ( mo ) ;
2016-03-28 08:01:24 +00:00
if ( mo - > DamageType = = NAME_Ice & & mo - > Vel . Z < minvel )
2016-03-01 15:47:10 +00:00
{
mo - > tics = 1 ;
2016-03-19 23:54:18 +00:00
mo - > Vel . Zero ( ) ;
2016-03-01 15:47:10 +00:00
return ;
}
// Let the actor do something special for hitting the floor
2016-11-24 12:45:43 +00:00
if ( mo - > flags7 & MF7_SMASHABLE )
{
P_DamageMobj ( mo , nullptr , nullptr , mo - > health , NAME_Smash ) ;
}
2016-03-01 15:47:10 +00:00
if ( mo - > player )
{
2016-03-28 08:01:24 +00:00
if ( mo - > player - > jumpTics < 0 | | mo - > Vel . Z < minvel )
2016-03-01 15:47:10 +00:00
{ // delay any jumping for a short while
mo - > player - > jumpTics = 7 ;
}
2016-03-28 08:01:24 +00:00
if ( mo - > Vel . Z < minvel & & ! ( mo - > flags & MF_NOGRAVITY ) )
2016-03-01 15:47:10 +00:00
{
// Squat down.
// Decrease viewheight for a moment after hitting the ground (hard),
// and utter appropriate sound.
PlayerLandedOnThing ( mo , NULL ) ;
}
}
2016-03-19 23:54:18 +00:00
mo - > Vel . Z = 0 ;
2016-03-01 15:47:10 +00:00
}
if ( mo - > flags & MF_SKULLFLY )
{ // The skull slammed into something
2016-03-19 23:54:18 +00:00
mo - > Vel . Z = - mo - > Vel . Z ;
2016-03-01 15:47:10 +00:00
}
mo - > Crash ( ) ;
}
}
if ( mo - > flags2 & MF2_FLOORCLIP )
{
mo - > AdjustFloorClip ( ) ;
}
2016-03-20 12:32:53 +00:00
if ( mo - > Top ( ) > mo - > ceilingz )
2016-03-01 15:47:10 +00:00
{ // hit the ceiling
if ( ( ! mo - > player | | ! ( mo - > player - > cheats & CF_PREDICTING ) ) & &
mo - > Sector - > SecActTarget ! = NULL & &
2016-03-30 07:41:46 +00:00
mo - > Sector - > ceilingplane . ZatPoint ( mo ) = = mo - > ceilingz )
2016-03-01 15:47:10 +00:00
{ // [RH] Let the sector do something to the actor
2017-01-13 00:34:43 +00:00
mo - > Sector - > TriggerSectorActions ( mo , SECSPAC_HitCeiling ) ;
2016-03-01 15:47:10 +00:00
}
2018-10-31 19:56:01 +00:00
P_CheckFor3DCeilingHit ( mo , mo - > ceilingz , true ) ;
2016-03-01 15:47:10 +00:00
// [RH] Need to recheck this because the sector action might have
// teleported the actor so it is no longer above the ceiling.
2016-03-20 12:32:53 +00:00
if ( mo - > Top ( ) > mo - > ceilingz )
2016-03-01 15:47:10 +00:00
{
2018-10-31 19:56:01 +00:00
mo - > BlockingCeiling = mo - > Sector ;
2016-03-20 19:55:06 +00:00
mo - > SetZ ( mo - > ceilingz - mo - > Height ) ;
2016-03-01 15:47:10 +00:00
if ( mo - > BounceFlags & BOUNCE_Ceilings )
{ // ceiling bounce
mo - > FloorBounceMissile ( mo - > ceilingsector - > ceilingplane ) ;
/*if (!(mo->flags6 & MF6_CANJUMP))*/ return ;
}
if ( mo - > flags & MF_SKULLFLY )
{ // the skull slammed into something
2016-03-19 23:54:18 +00:00
mo - > Vel . Z = - mo - > Vel . Z ;
2016-03-01 15:47:10 +00:00
}
2016-03-19 23:54:18 +00:00
if ( mo - > Vel . Z > 0 )
mo - > Vel . Z = 0 ;
2016-03-01 15:47:10 +00:00
if ( ( mo - > flags & MF_MISSILE ) & & ! ( mo - > flags & MF_NOCLIP ) )
{
if ( mo - > flags3 & MF3_CEILINGHUGGER )
{
return ;
}
2017-02-25 19:45:28 +00:00
bool onsky = false ;
if ( mo - > ceilingpic = = skyflatnum )
2016-03-01 15:47:10 +00:00
{
2017-02-25 19:45:28 +00:00
if ( ! ( mo - > flags3 & MF3_SKYEXPLODE ) )
{
mo - > Destroy ( ) ;
return ;
}
else onsky = true ;
2016-03-01 15:47:10 +00:00
}
2018-10-28 22:08:38 +00:00
// hit ceiling: direct damage callback
2018-11-04 04:53:37 +00:00
P_ProjectileHitPlane ( mo , SECPART_Ceiling ) ;
2017-02-25 19:45:28 +00:00
P_ExplodeMissile ( mo , NULL , NULL , onsky ) ;
2016-03-01 15:47:10 +00:00
return ;
}
}
}
2016-03-28 08:01:24 +00:00
P_CheckFakeFloorTriggers ( mo , oldz ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-27 20:35:58 +00:00
void P_CheckFakeFloorTriggers ( AActor * mo , double oldz , bool oldz_has_viewheight )
2016-03-01 15:47:10 +00:00
{
if ( mo - > player & & ( mo - > player - > cheats & CF_PREDICTING ) )
{
return ;
}
sector_t * sec = mo - > Sector ;
assert ( sec ! = NULL ) ;
if ( sec = = NULL )
{
return ;
}
if ( sec - > heightsec ! = NULL & & sec - > SecActTarget ! = NULL )
{
sector_t * hs = sec - > heightsec ;
2016-03-30 07:41:46 +00:00
double waterz = hs - > floorplane . ZatPoint ( mo ) ;
2016-03-22 17:06:08 +00:00
double newz ;
double viewheight ;
2016-03-01 15:47:10 +00:00
if ( mo - > player ! = NULL )
{
viewheight = mo - > player - > viewheight ;
}
else
{
2016-03-22 17:06:08 +00:00
viewheight = mo - > Height ;
2016-03-01 15:47:10 +00:00
}
2016-03-22 17:06:08 +00:00
if ( oldz > waterz & & mo - > Z ( ) < = waterz )
2016-03-01 15:47:10 +00:00
{ // Feet hit fake floor
2017-01-13 00:34:43 +00:00
sec - > TriggerSectorActions ( mo , SECSPAC_HitFakeFloor ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-22 17:06:08 +00:00
newz = mo - > Z ( ) + viewheight ;
2016-03-01 15:47:10 +00:00
if ( ! oldz_has_viewheight )
{
oldz + = viewheight ;
}
if ( oldz < = waterz & & newz > waterz )
{ // View went above fake floor
2017-01-13 00:34:43 +00:00
sec - > TriggerSectorActions ( mo , SECSPAC_EyesSurface ) ;
2016-03-01 15:47:10 +00:00
}
else if ( oldz > waterz & & newz < = waterz )
{ // View went below fake floor
2017-01-13 00:34:43 +00:00
sec - > TriggerSectorActions ( mo , SECSPAC_EyesDive ) ;
2016-03-01 15:47:10 +00:00
}
2018-05-01 09:29:29 +00:00
if ( ! ( hs - > MoreFlags & SECMF_FAKEFLOORONLY ) )
2016-03-01 15:47:10 +00:00
{
2016-03-30 07:41:46 +00:00
waterz = hs - > ceilingplane . ZatPoint ( mo ) ;
2016-03-01 15:47:10 +00:00
if ( oldz < = waterz & & newz > waterz )
{ // View went above fake ceiling
2017-01-13 00:34:43 +00:00
sec - > TriggerSectorActions ( mo , SECSPAC_EyesAboveC ) ;
2016-03-01 15:47:10 +00:00
}
else if ( oldz > waterz & & newz < = waterz )
{ // View went below fake ceiling
2017-01-13 00:34:43 +00:00
sec - > TriggerSectorActions ( mo , SECSPAC_EyesBelowC ) ;
2016-03-01 15:47:10 +00:00
}
}
}
}
2017-04-30 20:16:32 +00:00
DEFINE_ACTION_FUNCTION ( AActor , CheckFakeFloorTriggers )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_FLOAT ( oldz ) ;
2018-11-17 09:03:40 +00:00
PARAM_BOOL ( oldz_has_viewh ) ;
2017-04-30 20:16:32 +00:00
P_CheckFakeFloorTriggers ( self , oldz , oldz_has_viewh ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// PlayerLandedOnThing
//
//===========================================================================
static void PlayerLandedOnThing ( AActor * mo , AActor * onmobj )
{
bool grunted ;
if ( ! mo - > player )
return ;
if ( mo - > player - > mo = = mo )
{
2016-03-22 11:42:27 +00:00
mo - > player - > deltaviewheight = mo - > Vel . Z / 8. ;
2016-03-01 15:47:10 +00:00
}
if ( mo - > player - > cheats & CF_PREDICTING )
return ;
P_FallingDamage ( mo ) ;
// [RH] only make noise if alive
if ( ! mo - > player - > morphTics & & mo - > health > 0 )
{
grunted = false ;
// Why should this number vary by gravity?
2019-01-03 17:01:58 +00:00
if ( mo - > health > 0 & & mo - > Vel . Z < - mo - > player - > mo - > FloatVar ( NAME_GruntSpeed ) )
2016-03-01 15:47:10 +00:00
{
S_Sound ( mo , CHAN_VOICE , " *grunt " , 1 , ATTN_NORM ) ;
grunted = true ;
}
if ( onmobj ! = NULL | | ! Terrains [ P_GetThingFloorType ( mo ) ] . IsLiquid )
{
if ( ! grunted | | ! S_AreSoundsEquivalent ( mo , " *grunt " , " *land " ) )
{
S_Sound ( mo , CHAN_AUTO , " *land " , 1 , ATTN_NORM ) ;
}
}
}
// mo->player->centering = true;
}
//
// P_NightmareRespawn
//
void P_NightmareRespawn ( AActor * mobj )
{
2016-03-22 23:53:09 +00:00
double z ;
2016-03-01 15:47:10 +00:00
AActor * mo ;
AActor * info = mobj - > GetDefault ( ) ;
mobj - > skillrespawncount + + ;
// spawn the new monster (assume the spawn will be good)
if ( info - > flags & MF_SPAWNCEILING )
z = ONCEILINGZ ;
else if ( info - > flags2 & MF2_SPAWNFLOAT )
z = FLOATRANDZ ;
else
z = ONFLOORZ ;
// spawn it
2016-03-23 09:42:41 +00:00
mo = AActor : : StaticSpawn ( mobj - > GetClass ( ) , DVector3 ( mobj - > SpawnPoint . X , mobj - > SpawnPoint . Y , z ) , NO_REPLACE , true ) ;
2017-02-05 20:43:16 +00:00
mo - > health = mobj - > SpawnHealth ( ) ;
2016-03-01 15:47:10 +00:00
if ( z = = ONFLOORZ )
{
2016-03-22 23:53:09 +00:00
mo - > AddZ ( mobj - > SpawnPoint . Z ) ;
2016-03-20 18:52:35 +00:00
if ( mo - > Z ( ) < mo - > floorz )
2016-03-01 15:47:10 +00:00
{ // Do not respawn monsters in the floor, even if that's where they
// started. The initial P_ZMovement() call would have put them on
// the floor right away, but we need them on the floor now so we
// can use P_CheckPosition() properly.
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
{
2016-03-20 19:55:06 +00:00
mo - > SetZ ( mo - > ceilingz - mo - > Height ) ;
2016-03-01 15:47:10 +00:00
}
}
else if ( z = = ONCEILINGZ )
{
2016-03-22 23:53:09 +00:00
mo - > AddZ ( - mobj - > SpawnPoint . Z ) ;
2016-03-01 15:47:10 +00:00
}
// If there are 3D floors, we need to find floor/ceiling again.
P_FindFloorCeiling ( mo , FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT ) ;
if ( z = = ONFLOORZ )
{
2016-03-20 18:52:35 +00:00
if ( mo - > Z ( ) < mo - > floorz )
2016-03-01 15:47:10 +00:00
{ // Do not respawn monsters in the floor, even if that's where they
// started. The initial P_ZMovement() call would have put them on
// the floor right away, but we need them on the floor now so we
// can use P_CheckPosition() properly.
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
{ // Do the same for the ceiling.
2016-03-20 19:55:06 +00:00
mo - > SetZ ( mo - > ceilingz - mo - > Height ) ;
2016-03-01 15:47:10 +00:00
}
}
// something is occupying its position?
2016-03-22 23:53:09 +00:00
if ( ! P_CheckPosition ( mo , mo - > Pos ( ) , true ) )
2016-03-01 15:47:10 +00:00
{
//[GrafZahl] MF_COUNTKILL still needs to be checked here.
mo - > ClearCounters ( ) ;
mo - > Destroy ( ) ;
return ; // no respawn
}
2016-03-23 09:42:41 +00:00
z = mo - > Z ( ) ;
2016-03-01 15:47:10 +00:00
// inherit attributes from deceased one
2016-03-22 23:53:09 +00:00
mo - > SpawnPoint = mobj - > SpawnPoint ;
2016-03-01 15:47:10 +00:00
mo - > SpawnAngle = mobj - > SpawnAngle ;
mo - > SpawnFlags = mobj - > SpawnFlags & ~ MTF_DORMANT ; // It wasn't dormant when it died, so it's not dormant now, either.
2016-03-16 21:29:35 +00:00
mo - > Angles . Yaw = ( double ) mobj - > SpawnAngle ;
2016-03-01 15:47:10 +00:00
mo - > HandleSpawnFlags ( ) ;
mo - > reactiontime = 18 ;
mo - > CopyFriendliness ( mobj , false ) ;
mo - > Translation = mobj - > Translation ;
mo - > skillrespawncount = mobj - > skillrespawncount ;
2016-03-25 15:25:25 +00:00
mo - > Prev . Z = z ; // Do not interpolate Z position if we changed it since spawning.
2016-03-01 15:47:10 +00:00
// spawn a teleport fog at old spot because of removal of the body?
2016-06-06 08:48:40 +00:00
P_SpawnTeleportFog ( mobj , mobj - > Pos ( ) , true , true ) ;
2016-03-01 15:47:10 +00:00
// spawn a teleport fog at the new spot
2016-06-06 08:48:40 +00:00
P_SpawnTeleportFog ( mobj , DVector3 ( mobj - > SpawnPoint , z ) , false , true ) ;
2016-03-01 15:47:10 +00:00
// remove the old monster
mobj - > Destroy ( ) ;
}
//
// P_AddMobjToHash
//
// Inserts an mobj into the correct chain based on its tid.
// If its tid is 0, this function does nothing.
//
void AActor : : AddToHash ( )
{
if ( tid = = 0 )
{
iprev = NULL ;
inext = NULL ;
return ;
}
else
{
int hash = TIDHASH ( tid ) ;
2019-01-27 15:08:22 +00:00
auto & slot = Level - > TIDHash [ hash ] ;
2016-03-01 15:47:10 +00:00
2019-01-24 18:28:40 +00:00
inext = slot ;
iprev = & slot ;
slot = this ;
2016-03-01 15:47:10 +00:00
if ( inext )
{
inext - > iprev = & inext ;
}
}
}
//
// P_RemoveMobjFromHash
//
// Removes an mobj from its hash chain.
//
void AActor : : RemoveFromHash ( )
{
if ( tid ! = 0 & & iprev )
{
* iprev = inext ;
if ( inext )
{
inext - > iprev = iprev ;
}
iprev = NULL ;
inext = NULL ;
}
tid = 0 ;
}
//==========================================================================
//
// P_IsTIDUsed
//
// Returns true if there is at least one actor with the specified TID
// (dead or alive).
//
//==========================================================================
2019-01-24 18:28:40 +00:00
bool FLevelLocals : : IsTIDUsed ( int tid )
2016-03-01 15:47:10 +00:00
{
2019-01-24 18:28:40 +00:00
AActor * probe = TIDHash [ tid & 127 ] ;
2016-03-01 15:47:10 +00:00
while ( probe ! = NULL )
{
if ( probe - > tid = = tid )
{
return true ;
}
probe = probe - > inext ;
}
return false ;
}
//==========================================================================
//
// P_FindUniqueTID
//
// Returns an unused TID. If start_tid is 0, then a random TID will be
// chosen. Otherwise, it will perform a linear search starting from
// start_tid. If limit is non-0, then it will not check more than <limit>
// number of TIDs. Returns 0 if no suitable TID was found.
//
//==========================================================================
2019-01-24 18:28:40 +00:00
int FLevelLocals : : FindUniqueTID ( int start_tid , int limit )
2016-03-01 15:47:10 +00:00
{
int tid ;
if ( start_tid ! = 0 )
{ // Do a linear search.
if ( start_tid > INT_MAX - limit + 1 )
{ // If 'limit+start_tid-1' overflows, clamp 'limit' to INT_MAX
limit = INT_MAX ;
}
else
{
limit + = start_tid - 1 ;
}
for ( tid = start_tid ; tid < = limit ; + + tid )
{
2019-01-24 18:28:40 +00:00
if ( tid ! = 0 & & ! IsTIDUsed ( tid ) )
2016-03-01 15:47:10 +00:00
{
return tid ;
}
}
// Nothing free found.
return 0 ;
}
// Do a random search. To try and be a bit more performant, this
// actually does several linear searches. In the case of *very*
// dense TID usage, this could potentially perform worse than doing
// a complete linear scan starting at 1. However, you would need
// to use an absolutely ridiculous number of actors before this
// becomes a real concern.
if ( limit = = 0 )
{
limit = INT_MAX ;
}
for ( int i = 0 ; i < limit ; i + = 5 )
{
// Use a positive starting TID.
tid = pr_uniquetid . GenRand32 ( ) & INT_MAX ;
2019-01-24 18:28:40 +00:00
tid = FindUniqueTID ( tid = = 0 ? 1 : tid , 5 ) ;
2016-03-01 15:47:10 +00:00
if ( tid ! = 0 )
{
return tid ;
}
}
// Nothing free found.
return 0 ;
}
2016-11-30 00:25:51 +00:00
2016-03-01 15:47:10 +00:00
CCMD ( utid )
{
2019-01-28 01:41:29 +00:00
for ( auto Level : AllLevels ( ) )
{
Printf ( " %s, %d \n " , Level - > MapName . GetChars ( ) , Level - > FindUniqueTID ( argv . argc ( ) > 1 ? atoi ( argv [ 1 ] ) : 0 ,
( argv . argc ( ) > 2 & & atoi ( argv [ 2 ] ) > = 0 ) ? atoi ( argv [ 2 ] ) : 0 ) ) ;
}
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// AActor :: GetMissileDamage
//
// If the actor's damage amount is an expression, evaluate it and return
// the result. Otherwise, return ((random() & mask) + add) * damage.
//
//==========================================================================
int AActor : : GetMissileDamage ( int mask , int add )
{
2016-09-19 01:36:51 +00:00
if ( DamageVal > = 0 )
2016-03-01 15:47:10 +00:00
{
2016-09-19 01:36:51 +00:00
if ( mask = = 0 )
{
return add * DamageVal ;
}
else
{
return ( ( pr_missiledamage ( ) & mask ) + add ) * DamageVal ;
}
}
if ( DamageFunc = = nullptr )
{
// This should never happen
assert ( false & & " No damage function found " ) ;
2016-03-01 15:47:10 +00:00
return 0 ;
}
VMValue param = this ;
2016-11-06 09:28:01 +00:00
VMReturn result ;
2016-03-01 15:47:10 +00:00
2016-11-06 09:28:01 +00:00
int amount ;
2016-03-01 15:47:10 +00:00
2016-11-06 09:28:01 +00:00
result . IntAt ( & amount ) ;
2016-03-01 15:47:10 +00:00
2017-04-12 23:12:04 +00:00
if ( VMCall ( DamageFunc , & param , 1 , & result , 1 ) < 1 )
2016-03-01 15:47:10 +00:00
{ // No results
return 0 ;
}
2016-09-19 01:36:51 +00:00
return amount ;
2016-03-01 15:47:10 +00:00
}
void AActor : : Howl ( )
{
2017-03-02 17:40:01 +00:00
FSoundID howl = SoundVar ( NAME_HowlSound ) ;
2016-03-01 15:47:10 +00:00
if ( ! S_IsActorPlayingSomething ( this , CHAN_BODY , howl ) )
{
S_Sound ( this , CHAN_BODY , howl , 1 , ATTN_NORM ) ;
}
}
2016-11-26 12:18:48 +00:00
DEFINE_ACTION_FUNCTION ( AActor , Howl )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
self - > Howl ( ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
bool AActor : : Slam ( AActor * thing )
{
flags & = ~ MF_SKULLFLY ;
2016-03-19 23:54:18 +00:00
Vel . Zero ( ) ;
2016-03-01 15:47:10 +00:00
if ( health > 0 )
{
if ( ! ( flags2 & MF2_DORMANT ) )
{
int dam = GetMissileDamage ( 7 , 1 ) ;
int newdam = P_DamageMobj ( thing , this , this , dam , NAME_Melee ) ;
P_TraceBleed ( newdam > 0 ? newdam : dam , thing , this ) ;
// The charging monster may have died by the target's actions here.
if ( health > 0 )
{
if ( SeeState ! = NULL ) SetState ( SeeState ) ;
else SetIdle ( ) ;
}
}
else
{
SetIdle ( ) ;
tics = - 1 ;
}
}
return false ; // stop moving
}
2016-11-26 00:14:47 +00:00
DEFINE_ACTION_FUNCTION ( AActor , Slam )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_OBJECT ( thing , AActor ) ;
ACTION_RETURN_BOOL ( self - > Slam ( thing ) ) ;
}
bool AActor : : CallSlam ( AActor * thing )
{
IFVIRTUAL ( AActor , Slam )
{
VMValue params [ 2 ] = { ( DObject * ) this , thing } ;
VMReturn ret ;
int retval ;
ret . IntAt ( & retval ) ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , & ret , 1 ) ;
2016-11-26 00:14:47 +00:00
return ! ! retval ;
}
else return Slam ( thing ) ;
}
2016-11-27 20:41:04 +00:00
// This virtual method only exists on the script side.
2016-03-01 15:47:10 +00:00
int AActor : : SpecialMissileHit ( AActor * victim )
{
2016-11-27 20:41:04 +00:00
IFVIRTUAL ( AActor , SpecialMissileHit )
{
VMValue params [ 2 ] = { ( DObject * ) this , victim } ;
VMReturn ret ;
int retval ;
ret . IntAt ( & retval ) ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , & ret , 1 ) ;
2016-11-27 20:41:04 +00:00
return retval ;
}
else return - 1 ;
2016-03-01 15:47:10 +00:00
}
2016-03-16 11:41:26 +00:00
bool AActor : : AdjustReflectionAngle ( AActor * thing , DAngle & angle )
2016-03-01 15:47:10 +00:00
{
if ( flags2 & MF2_DONTREFLECT ) return true ;
if ( thing - > flags7 & MF7_THRUREFLECT ) return false ;
// Change angle for reflection
if ( thing - > flags4 & MF4_SHIELDREFLECT )
{
2016-03-16 11:41:26 +00:00
// Shield reflection (from the Centaur)
2016-03-21 21:20:10 +00:00
if ( absangle ( angle , thing - > Angles . Yaw ) > 45 )
2016-03-01 15:47:10 +00:00
return true ; // Let missile explode
2016-11-26 12:18:48 +00:00
if ( thing - > flags7 & MF7_NOSHIELDREFLECT ) return true ;
2016-03-01 15:47:10 +00:00
if ( pr_reflect ( ) < 128 )
2016-03-16 11:41:26 +00:00
angle + = 45 ;
2016-03-01 15:47:10 +00:00
else
2016-03-16 11:41:26 +00:00
angle - = 45 ;
2016-03-01 15:47:10 +00:00
}
else if ( thing - > flags4 & MF4_DEFLECT )
{
// deflect (like the Heresiarch)
if ( pr_reflect ( ) < 128 )
2016-03-16 11:41:26 +00:00
angle + = 45 ;
2016-03-01 15:47:10 +00:00
else
2016-03-16 11:41:26 +00:00
angle - = 45 ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-16 11:41:26 +00:00
angle + = ( ( pr_reflect ( ) % 16 ) - 8 ) ;
2016-03-01 15:47:10 +00:00
}
//Always check for AIMREFLECT, no matter what else is checked above.
if ( thing - > flags7 & MF7_AIMREFLECT )
{
if ( this - > target ! = NULL )
{
A_Face ( this , this - > target ) ;
}
else if ( thing - > target ! = NULL )
{
A_Face ( this , thing - > target ) ;
}
}
return false ;
}
2017-01-16 00:16:11 +00:00
int AActor : : AbsorbDamage ( int damage , FName dmgtype )
{
2018-12-04 16:00:48 +00:00
for ( AActor * item = Inventory ; item ! = nullptr ; item = item - > Inventory )
2017-01-16 00:16:11 +00:00
{
2018-12-04 16:00:48 +00:00
IFVIRTUALPTRNAME ( item , NAME_Inventory , AbsorbDamage )
2017-01-16 00:16:11 +00:00
{
2017-01-16 09:23:26 +00:00
VMValue params [ 4 ] = { item , damage , dmgtype . GetIndex ( ) , & damage } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 4 , nullptr , 0 ) ;
2017-01-16 00:16:11 +00:00
}
}
return damage ;
}
2017-01-16 19:33:41 +00:00
void AActor : : AlterWeaponSprite ( visstyle_t * vis )
{
int changed = 0 ;
2018-12-04 16:00:48 +00:00
TArray < AActor * > items ;
2017-01-16 19:33:41 +00:00
// This needs to go backwards through the items but the list has no backlinks.
2018-12-04 16:00:48 +00:00
for ( AActor * item = Inventory ; item ! = nullptr ; item = item - > Inventory )
2017-01-16 19:33:41 +00:00
{
items . Push ( item ) ;
}
for ( int i = items . Size ( ) - 1 ; i > = 0 ; i - - )
{
2018-12-04 16:00:48 +00:00
IFVIRTUALPTRNAME ( items [ i ] , NAME_Inventory , AlterWeaponSprite )
2017-01-16 19:33:41 +00:00
{
VMValue params [ 3 ] = { items [ i ] , vis , & changed } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 3 , nullptr , 0 ) ;
2017-01-16 19:33:41 +00:00
}
}
}
2016-03-01 15:47:10 +00:00
void AActor : : PlayActiveSound ( )
{
if ( ActiveSound & & ! S_IsActorPlayingSomething ( this , CHAN_VOICE , - 1 ) )
{
S_Sound ( this , CHAN_VOICE , ActiveSound , 1 ,
( flags3 & MF3_FULLVOLACTIVE ) ? ATTN_NONE : ATTN_IDLE ) ;
}
}
2016-11-12 18:16:47 +00:00
DEFINE_ACTION_FUNCTION ( AActor , PlayActiveSound )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
self - > PlayActiveSound ( ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
bool AActor : : IsOkayToAttack ( AActor * link )
{
// Standard things to eliminate: an actor shouldn't attack itself,
// or a non-shootable, dormant, non-player-and-non-monster actor.
if ( link = = this ) return false ;
if ( ! ( link - > player | | ( link - > flags3 & MF3_ISMONSTER ) ) ) return false ;
if ( ! ( link - > flags & MF_SHOOTABLE ) ) return false ;
if ( link - > flags2 & MF2_DORMANT ) return false ;
2017-05-09 07:57:38 +00:00
if ( link - > flags7 & MF7_NEVERTARGET ) return false ; // NEVERTARGET means just that.
2016-03-01 15:47:10 +00:00
// An actor shouldn't attack friendly actors. The reference depends
// on the type of actor: for a player's actor, itself; for a projectile,
// its target; and for a summoned minion, its tracer.
2017-05-09 07:57:38 +00:00
AActor * Friend ;
if ( flags5 & MF5_SUMMONEDMONSTER ) Friend = tracer ;
2016-03-01 15:47:10 +00:00
else if ( flags2 & MF2_SEEKERMISSILE ) Friend = target ;
2019-01-30 00:15:32 +00:00
else if ( ( flags & MF_FRIENDLY ) & & FriendPlayer ) Friend = Level - > Players [ FriendPlayer - 1 ] - > mo ;
2017-05-09 07:57:38 +00:00
else Friend = this ;
2016-03-01 15:47:10 +00:00
// Friend checks
if ( link = = Friend ) return false ;
if ( Friend = = NULL ) return false ;
if ( Friend - > IsFriend ( link ) ) return false ;
if ( ( link - > flags5 & MF5_SUMMONEDMONSTER ) // No attack against minions on the same side
& & ( link - > tracer = = Friend ) ) return false ;
if ( multiplayer & & ! deathmatch // No attack against fellow players in coop
& & link - > player & & Friend - > player ) return false ;
if ( ( ( flags & link - > flags ) & MF_FRIENDLY ) // No friendly infighting amongst minions
& & IsFriend ( link ) ) return false ;
// Now that all the actor checks are made, the line of sight can be checked
if ( P_CheckSight ( this , link ) )
{
// AMageStaffFX2::IsOkayToAttack had an extra check here, generalized with a flag,
// to only allow the check to succeed if the enemy was in a ~84<38> FOV of the player
if ( flags3 & MF3_SCREENSEEKER )
{
2016-03-25 23:34:56 +00:00
DAngle angle = absangle ( Friend - > AngleTo ( link ) , Friend - > Angles . Yaw ) ;
if ( angle < 30 * ( 256. / 360. ) )
2016-03-01 15:47:10 +00:00
{
return true ;
}
}
// Other actors are not concerned by this check
else return true ;
}
// The sight check was failed, or the angle wasn't right for a screenseeker
return false ;
}
2017-03-08 14:20:00 +00:00
void AActor : : SetShade ( uint32_t rgb )
2016-03-01 15:47:10 +00:00
{
PalEntry * entry = ( PalEntry * ) & rgb ;
2017-06-04 18:42:03 +00:00
fillcolor = ( rgb & 0xffffff ) | ( ColorMatcher . Pick ( entry - > r , entry - > g , entry - > b ) < < 24 ) ;
2016-03-01 15:47:10 +00:00
}
void AActor : : SetShade ( int r , int g , int b )
{
fillcolor = MAKEARGB ( ColorMatcher . Pick ( r , g , b ) , r , g , b ) ;
}
2016-11-30 00:25:51 +00:00
DEFINE_ACTION_FUNCTION ( AActor , SetShade )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_INT ( color ) ;
self - > SetShade ( color ) ;
return 0 ;
}
2016-03-16 11:41:26 +00:00
void AActor : : SetPitch ( DAngle p , bool interpolate , bool forceclamp )
2016-03-01 15:47:10 +00:00
{
if ( player ! = NULL | | forceclamp )
{ // clamp the pitch we set
2016-03-16 11:41:26 +00:00
DAngle min , max ;
2016-03-01 15:47:10 +00:00
if ( player ! = NULL )
{
min = player - > MinPitch ;
max = player - > MaxPitch ;
}
else
{
2016-03-16 11:41:26 +00:00
min = - 89. ;
max = 89. ;
2016-03-01 15:47:10 +00:00
}
2016-03-16 11:41:26 +00:00
p = clamp ( p , min , max ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-16 11:41:26 +00:00
if ( p ! = Angles . Pitch )
2016-03-01 15:47:10 +00:00
{
2016-03-16 11:41:26 +00:00
Angles . Pitch = p ;
2016-03-01 15:47:10 +00:00
if ( player ! = NULL & & interpolate )
{
player - > cheats | = CF_INTERPVIEW ;
}
}
}
2016-03-16 11:41:26 +00:00
void AActor : : SetAngle ( DAngle ang , bool interpolate )
2016-03-01 15:47:10 +00:00
{
2016-03-16 11:41:26 +00:00
if ( ang ! = Angles . Yaw )
2016-03-01 15:47:10 +00:00
{
2016-03-16 11:41:26 +00:00
Angles . Yaw = ang ;
2016-03-01 15:47:10 +00:00
if ( player ! = NULL & & interpolate )
{
player - > cheats | = CF_INTERPVIEW ;
}
}
}
2016-03-16 11:41:26 +00:00
void AActor : : SetRoll ( DAngle r , bool interpolate )
2016-03-01 15:47:10 +00:00
{
2016-03-16 11:41:26 +00:00
if ( r ! = Angles . Roll )
2016-03-01 15:47:10 +00:00
{
2016-03-16 11:41:26 +00:00
Angles . Roll = r ;
2016-03-01 15:47:10 +00:00
if ( player ! = NULL & & interpolate )
{
player - > cheats | = CF_INTERPVIEW ;
}
}
}
2017-02-28 11:11:25 +00:00
PClassActor * AActor : : GetBloodType ( int type ) const
{
IFVIRTUAL ( AActor , GetBloodType )
{
VMValue params [ ] = { ( DObject * ) this , type } ;
PClassActor * res ;
VMReturn ret ( ( void * * ) & res ) ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , countof ( params ) , & ret , 1 ) ;
2017-02-28 11:11:25 +00:00
return res ;
}
return nullptr ;
}
2016-03-01 15:47:10 +00:00
2016-03-28 08:01:24 +00:00
DVector3 AActor : : GetPortalTransition ( double byoffset , sector_t * * pSec )
2016-03-01 15:47:10 +00:00
{
bool moved = false ;
sector_t * sec = Sector ;
2016-03-28 08:01:24 +00:00
double testz = Z ( ) + byoffset ;
DVector3 pos = Pos ( ) ;
2016-03-01 15:47:10 +00:00
while ( ! sec - > PortalBlocksMovement ( sector_t : : ceiling ) )
{
2016-05-20 11:10:42 +00:00
if ( testz > = sec - > GetPortalPlaneZ ( sector_t : : ceiling ) )
2016-03-01 15:47:10 +00:00
{
2016-04-19 09:35:28 +00:00
pos = PosRelative ( sec - > GetOppositePortalGroup ( sector_t : : ceiling ) ) ;
2019-01-29 00:30:41 +00:00
sec = Level - > PointInSector ( pos ) ;
2016-03-01 15:47:10 +00:00
moved = true ;
}
else break ;
}
if ( ! moved )
{
while ( ! sec - > PortalBlocksMovement ( sector_t : : floor ) )
{
2016-05-20 11:10:42 +00:00
if ( testz < sec - > GetPortalPlaneZ ( sector_t : : floor ) )
2016-03-01 15:47:10 +00:00
{
2016-04-19 09:35:28 +00:00
pos = PosRelative ( sec - > GetOppositePortalGroup ( sector_t : : floor ) ) ;
2019-01-29 00:30:41 +00:00
sec = Level - > PointInSector ( pos ) ;
2016-03-01 15:47:10 +00:00
}
else break ;
}
}
2016-03-07 20:58:34 +00:00
if ( pSec ) * pSec = sec ;
2016-03-01 15:47:10 +00:00
return pos ;
}
void AActor : : CheckPortalTransition ( bool islinked )
{
bool moved = false ;
2016-12-25 21:40:26 +00:00
FLinkContext ctx ;
2016-03-01 15:47:10 +00:00
while ( ! Sector - > PortalBlocksMovement ( sector_t : : ceiling ) )
{
2016-05-20 11:10:42 +00:00
if ( Z ( ) > = Sector - > GetPortalPlaneZ ( sector_t : : ceiling ) )
2016-03-01 15:47:10 +00:00
{
2016-03-25 15:25:25 +00:00
DVector3 oldpos = Pos ( ) ;
2016-12-25 21:40:26 +00:00
if ( islinked & & ! moved ) UnlinkFromWorld ( & ctx ) ;
2016-04-19 09:35:28 +00:00
SetXYZ ( PosRelative ( Sector - > GetOppositePortalGroup ( sector_t : : ceiling ) ) ) ;
2017-02-19 20:35:06 +00:00
Prev + = Pos ( ) - oldpos ;
2019-01-29 00:30:41 +00:00
Sector = Level - > PointInSector ( Pos ( ) ) ;
2016-03-01 15:47:10 +00:00
PrevPortalGroup = Sector - > PortalGroup ;
moved = true ;
}
else break ;
}
if ( ! moved )
{
while ( ! Sector - > PortalBlocksMovement ( sector_t : : floor ) )
{
2016-04-19 09:35:28 +00:00
double portalz = Sector - > GetPortalPlaneZ ( sector_t : : floor ) ;
if ( Z ( ) < portalz & & floorz < portalz )
2016-03-01 15:47:10 +00:00
{
2016-03-25 15:25:25 +00:00
DVector3 oldpos = Pos ( ) ;
2016-12-25 21:40:26 +00:00
if ( islinked & & ! moved ) UnlinkFromWorld ( & ctx ) ;
2016-04-19 09:35:28 +00:00
SetXYZ ( PosRelative ( Sector - > GetOppositePortalGroup ( sector_t : : floor ) ) ) ;
2017-02-19 20:35:06 +00:00
Prev + = Pos ( ) - oldpos ;
2019-01-29 00:30:41 +00:00
Sector = Level - > PointInSector ( Pos ( ) ) ;
2016-03-01 15:47:10 +00:00
PrevPortalGroup = Sector - > PortalGroup ;
moved = true ;
}
else break ;
}
}
2016-12-25 21:40:26 +00:00
if ( islinked & & moved ) LinkToWorld ( & ctx ) ;
2016-03-01 15:47:10 +00:00
}
2017-08-12 11:55:33 +00:00
DEFINE_ACTION_FUNCTION ( AActor , CheckPortalTransition )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2018-11-17 09:03:40 +00:00
PARAM_BOOL ( linked ) ;
2017-08-12 11:55:33 +00:00
self - > CheckPortalTransition ( linked ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
//
// P_MobjThinker
//
void AActor : : Tick ( )
{
// [RH] Data for Heretic/Hexen scrolling sectors
2017-03-08 14:20:00 +00:00
static const int8_t HexenCompatSpeeds [ ] = { - 25 , 0 , - 10 , - 5 , 0 , 5 , 10 , 0 , 25 } ;
static const int8_t HexenScrollies [ 24 ] [ 2 ] =
2016-03-01 15:47:10 +00:00
{
{ 0 , 1 } , { 0 , 2 } , { 0 , 4 } ,
{ - 1 , 0 } , { - 2 , 0 } , { - 4 , 0 } ,
{ 0 , - 1 } , { 0 , - 2 } , { 0 , - 4 } ,
{ 1 , 0 } , { 2 , 0 } , { 4 , 0 } ,
{ 1 , 1 } , { 2 , 2 } , { 4 , 4 } ,
{ - 1 , 1 } , { - 2 , 2 } , { - 4 , 4 } ,
{ - 1 , - 1 } , { - 2 , - 2 } , { - 4 , - 4 } ,
{ 1 , - 1 } , { 2 , - 2 } , { 4 , - 4 }
} ;
2017-03-08 14:20:00 +00:00
static const uint8_t HereticScrollDirs [ 4 ] = { 6 , 9 , 1 , 4 } ;
static const uint8_t HereticSpeedMuls [ 5 ] = { 5 , 10 , 25 , 30 , 35 } ;
2016-03-01 15:47:10 +00:00
AActor * onmo ;
//assert (state != NULL);
if ( state = = NULL )
{
2016-03-19 23:54:18 +00:00
Printf ( " Actor of type %s at (%f,%f) left without a state \n " , GetClass ( ) - > TypeName . GetChars ( ) , X ( ) , Y ( ) ) ;
2016-03-01 15:47:10 +00:00
Destroy ( ) ;
return ;
}
if ( flags5 & MF5_NOINTERACTION )
{
// only do the minimally necessary things here to save time:
// Check the time freezer
// apply velocity
// ensure that the actor is not linked into the blockmap
2019-01-28 19:15:48 +00:00
//Added by MC: Freeze mode.
if ( isFrozen ( ) )
2016-03-01 15:47:10 +00:00
{
2019-01-28 19:15:48 +00:00
// Boss cubes shouldn't be accelerated by timefreeze
if ( flags6 & MF6_BOSSCUBE )
2016-03-01 15:47:10 +00:00
{
2019-01-28 19:15:48 +00:00
special2 + + ;
2016-03-01 15:47:10 +00:00
}
2019-01-28 19:15:48 +00:00
return ;
2016-03-01 15:47:10 +00:00
}
2016-11-15 17:38:03 +00:00
if ( ! Vel . isZero ( ) | | ! ( flags & MF_NOBLOCKMAP ) )
{
2016-12-25 21:40:26 +00:00
FLinkContext ctx ;
UnlinkFromWorld ( & ctx ) ;
2016-11-15 17:38:03 +00:00
flags | = MF_NOBLOCKMAP ;
SetXYZ ( Vec3Offset ( Vel ) ) ;
CheckPortalTransition ( false ) ;
2016-12-25 21:40:26 +00:00
LinkToWorld ( & ctx ) ;
2016-11-15 17:38:03 +00:00
}
2017-05-19 19:36:32 +00:00
flags8 & = ~ MF8_INSCROLLSEC ;
2016-03-01 15:47:10 +00:00
}
else
{
2017-09-02 05:57:03 +00:00
if ( ! player | | ! ( player - > cheats & CF_PREDICTING ) )
2016-03-01 15:47:10 +00:00
{
2017-09-02 05:57:03 +00:00
// Handle powerup effects here so that the order is controlled
// by the order in the inventory, not the order in the thinker table
2018-12-04 16:00:48 +00:00
AActor * item = Inventory ;
2017-09-02 05:57:03 +00:00
2018-12-02 23:43:01 +00:00
while ( item ! = NULL )
2017-01-19 19:56:31 +00:00
{
2018-12-04 16:00:48 +00:00
IFVIRTUALPTRNAME ( item , NAME_Inventory , DoEffect )
2017-09-02 05:57:03 +00:00
{
VMValue params [ 1 ] = { item } ;
VMCall ( func , params , 1 , nullptr , 0 ) ;
}
item = item - > Inventory ;
2017-01-19 19:56:31 +00:00
}
2016-03-01 15:47:10 +00:00
}
if ( flags & MF_UNMORPHED )
{
return ;
}
2019-01-28 19:15:48 +00:00
if ( isFrozen ( ) )
2016-03-01 15:47:10 +00:00
{
// Boss cubes shouldn't be accelerated by timefreeze
if ( flags6 & MF6_BOSSCUBE )
{
special2 + + ;
}
2019-01-28 19:15:48 +00:00
return ;
2016-03-01 15:47:10 +00:00
}
if ( effects & FX_ROCKET )
{
if ( + + smokecounter = = 4 )
{
// add some smoke behind the rocket
smokecounter = 0 ;
2016-03-23 09:42:41 +00:00
AActor * th = Spawn ( " RocketSmokeTrail " , Vec3Offset ( - Vel ) , ALLOW_REPLACE ) ;
2016-03-01 15:47:10 +00:00
if ( th )
{
th - > tics - = pr_rockettrail ( ) & 3 ;
if ( th - > tics < 1 ) th - > tics = 1 ;
if ( ! ( cl_rockettrails & 2 ) ) th - > renderflags | = RF_INVISIBLE ;
}
}
}
else if ( effects & FX_GRENADE )
{
if ( + + smokecounter = = 8 )
{
smokecounter = 0 ;
2016-03-20 19:55:06 +00:00
DAngle moveangle = Vel . Angle ( ) ;
double xo = - moveangle . Cos ( ) * radius * 2 + pr_rockettrail ( ) / 64. ;
double yo = - moveangle . Sin ( ) * radius * 2 + pr_rockettrail ( ) / 64. ;
double zo = - Height * Vel . Z / 8. + Height * ( 2 / 3. ) ;
AActor * th = Spawn ( " GrenadeSmokeTrail " , Vec3Offset ( xo , yo , zo ) , ALLOW_REPLACE ) ;
2016-03-01 15:47:10 +00:00
if ( th )
{
th - > tics - = pr_rockettrail ( ) & 3 ;
if ( th - > tics < 1 ) th - > tics = 1 ;
if ( ! ( cl_rockettrails & 2 ) ) th - > renderflags | = RF_INVISIBLE ;
}
}
}
2016-03-26 00:13:36 +00:00
double oldz = Z ( ) ;
2016-03-01 15:47:10 +00:00
// [RH] Give the pain elemental vertical friction
// This used to be in APainElemental::Tick but in order to use
// A_PainAttack with other monsters it has to be here
if ( flags4 & MF4_VFRICTION )
{
if ( health > 0 )
{
2016-03-28 08:01:24 +00:00
if ( fabs ( Vel . Z ) < 0.25 )
2016-03-01 15:47:10 +00:00
{
2016-03-19 23:54:18 +00:00
Vel . Z = 0 ;
2016-03-01 15:47:10 +00:00
flags4 & = ~ MF4_VFRICTION ;
}
else
{
2016-03-19 23:54:18 +00:00
Vel . Z * = ( 0xe800 / 65536. ) ;
2016-03-01 15:47:10 +00:00
}
}
}
// [RH] Pulse in and out of visibility
if ( effects & FX_VISIBILITYPULSE )
{
if ( visdir > 0 )
{
2016-03-21 11:18:46 +00:00
Alpha + = 1 / 32. ;
if ( Alpha > = 1. )
2016-03-01 15:47:10 +00:00
{
2016-03-21 11:18:46 +00:00
Alpha = 1. ;
2016-03-01 15:47:10 +00:00
visdir = - 1 ;
}
}
else
{
2016-03-21 11:18:46 +00:00
Alpha - = 1 / 32. ;
if ( Alpha < = 0.25 )
2016-03-01 15:47:10 +00:00
{
2016-03-21 11:18:46 +00:00
Alpha = 0.25 ;
2016-03-01 15:47:10 +00:00
visdir = 1 ;
}
}
}
else if ( flags & MF_STEALTH )
{
// [RH] Fade a stealth monster in and out of visibility
RenderStyle . Flags & = ~ STYLEF_Alpha1 ;
if ( visdir > 0 )
{
2016-03-21 11:18:46 +00:00
Alpha + = 2. / TICRATE ;
if ( Alpha > 1. )
2016-03-01 15:47:10 +00:00
{
2016-03-21 11:18:46 +00:00
Alpha = 1. ;
2016-03-01 15:47:10 +00:00
visdir = 0 ;
}
}
else if ( visdir < 0 )
{
2016-03-21 11:18:46 +00:00
Alpha - = 1.5 / TICRATE ;
2017-02-28 11:47:44 +00:00
if ( Alpha < StealthAlpha )
2016-03-01 15:47:10 +00:00
{
2017-02-28 11:47:44 +00:00
Alpha = StealthAlpha ;
2016-03-01 15:47:10 +00:00
visdir = 0 ;
}
}
}
2019-01-30 00:38:18 +00:00
if ( Level - > BotInfo . botnum & & ! demoplayback & &
2016-03-01 15:47:10 +00:00
( ( flags & ( MF_SPECIAL | MF_MISSILE ) ) | | ( flags3 & MF3_ISMONSTER ) ) )
{
2019-01-30 00:38:18 +00:00
Level - > BotInfo . BotTick ( this ) ;
2016-03-01 15:47:10 +00:00
}
// [RH] Consider carrying sectors here
2016-03-28 08:01:24 +00:00
DVector2 cumm ( 0 , 0 ) ;
2017-05-19 14:31:44 +00:00
2019-01-27 15:08:22 +00:00
if ( ( ( ( flags8 & MF8_INSCROLLSEC ) & & Level - > Scrolls . Size ( ) > 0 ) | | player ! = NULL ) & & ! ( flags & MF_NOCLIP ) & & ! ( flags & MF_NOSECTOR ) )
2016-03-01 15:47:10 +00:00
{
2016-03-28 08:01:24 +00:00
double height , waterheight ; // killough 4/4/98: add waterheight
2016-03-01 15:47:10 +00:00
const msecnode_t * node ;
int countx , county ;
2017-05-19 14:31:44 +00:00
// Clear the flag for the next frame.
2017-05-19 19:36:32 +00:00
flags8 & = ~ MF8_INSCROLLSEC ;
2017-05-19 14:31:44 +00:00
2016-03-01 15:47:10 +00:00
// killough 3/7/98: Carry things on floor
// killough 3/20/98: use new sector list which reflects true members
// killough 3/27/98: fix carrier bug
// killough 4/4/98: Underwater, carry things even w/o gravity
// Move objects only if on floor or underwater,
// non-floating, and clipped.
countx = county = 0 ;
for ( node = touching_sectorlist ; node ; node = node - > m_tnext )
{
sector_t * sec = node - > m_sector ;
2016-03-28 08:01:24 +00:00
DVector2 scrollv ;
2016-03-01 15:47:10 +00:00
2019-01-27 15:08:22 +00:00
if ( Level - > Scrolls . Size ( ) > unsigned ( sec - > Index ( ) ) )
2016-03-01 15:47:10 +00:00
{
2019-01-27 15:08:22 +00:00
scrollv = Level - > Scrolls [ sec - > Index ( ) ] ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-28 08:01:24 +00:00
scrollv . Zero ( ) ;
2016-03-01 15:47:10 +00:00
}
if ( player ! = NULL )
{
int scrolltype = sec - > special ;
if ( scrolltype > = Scroll_North_Slow & &
scrolltype < = Scroll_SouthWest_Fast )
{ // Hexen scroll special
scrolltype - = Scroll_North_Slow ;
2019-01-29 18:28:22 +00:00
if ( Level - > i_compatflags & COMPATF_RAVENSCROLL )
2016-03-01 15:47:10 +00:00
{
2016-03-28 08:01:24 +00:00
scrollv . X - = HexenCompatSpeeds [ HexenScrollies [ scrolltype ] [ 0 ] + 4 ] * ( 1. / ( 32 * CARRYFACTOR ) ) ;
scrollv . Y + = HexenCompatSpeeds [ HexenScrollies [ scrolltype ] [ 1 ] + 4 ] * ( 1. / ( 32 * CARRYFACTOR ) ) ;
2016-03-01 15:47:10 +00:00
}
else
{
// Use speeds that actually match the scrolling textures!
2016-03-28 08:01:24 +00:00
scrollv . X - = HexenScrollies [ scrolltype ] [ 0 ] * 0.5 ;
scrollv . Y + = HexenScrollies [ scrolltype ] [ 1 ] * 0.5 ;
2016-03-01 15:47:10 +00:00
}
}
else if ( scrolltype > = Carry_East5 & &
scrolltype < = Carry_West35 )
{ // Heretic scroll special
scrolltype - = Carry_East5 ;
2017-03-08 14:20:00 +00:00
uint8_t dir = HereticScrollDirs [ scrolltype / 5 ] ;
2016-03-28 08:01:24 +00:00
double carryspeed = HereticSpeedMuls [ scrolltype % 5 ] * ( 1. / ( 32 * CARRYFACTOR ) ) ;
2019-01-29 18:28:22 +00:00
if ( scrolltype < 5 & & ! ( Level - > i_compatflags & COMPATF_RAVENSCROLL ) )
2016-03-01 15:47:10 +00:00
{
// Use speeds that actually match the scrolling textures!
2016-05-24 08:01:57 +00:00
carryspeed = ( 1 < < ( ( scrolltype % 5 ) + 15 ) ) / 65536. ;
2016-03-01 15:47:10 +00:00
}
2016-03-28 08:01:24 +00:00
scrollv . X + = carryspeed * ( ( dir & 3 ) - 1 ) ;
scrollv . Y + = carryspeed * ( ( ( dir & 12 ) > > 2 ) - 1 ) ;
2016-03-01 15:47:10 +00:00
}
else if ( scrolltype = = dScroll_EastLavaDamage )
{ // Special Heretic scroll special
2019-01-29 18:28:22 +00:00
if ( Level - > i_compatflags & COMPATF_RAVENSCROLL )
2016-03-01 15:47:10 +00:00
{
2016-03-28 08:01:24 +00:00
scrollv . X + = 28. / ( 32 * CARRYFACTOR ) ;
2016-03-01 15:47:10 +00:00
}
else
{
// Use a speed that actually matches the scrolling texture!
2016-03-28 08:01:24 +00:00
scrollv . X + = 12. / ( 32 * CARRYFACTOR ) ;
2016-03-01 15:47:10 +00:00
}
}
else if ( scrolltype = = Scroll_StrifeCurrent )
{ // Strife scroll special
2019-01-27 15:08:22 +00:00
int anglespeed = Level - > GetFirstSectorTag ( sec ) - 100 ;
2016-03-28 08:01:24 +00:00
double carryspeed = ( anglespeed % 10 ) / ( 16 * CARRYFACTOR ) ;
DAngle angle = ( ( anglespeed / 10 ) * 45. ) ;
scrollv + = angle . ToVector ( carryspeed ) ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-28 08:01:24 +00:00
if ( scrollv . isZero ( ) )
2016-03-01 15:47:10 +00:00
{
continue ;
}
sector_t * heightsec = sec - > GetHeightSec ( ) ;
if ( flags & MF_NOGRAVITY & & heightsec = = NULL )
{
continue ;
}
2016-03-28 08:01:24 +00:00
DVector3 pos = PosRelative ( sec ) ;
2016-03-01 15:47:10 +00:00
height = sec - > floorplane . ZatPoint ( pos ) ;
2016-03-28 19:04:46 +00:00
double height2 = sec - > floorplane . ZatPoint ( this ) ;
if ( isAbove ( height ) )
2016-03-01 15:47:10 +00:00
{
if ( heightsec = = NULL )
{
continue ;
}
waterheight = heightsec - > floorplane . ZatPoint ( pos ) ;
2016-03-28 08:01:24 +00:00
if ( waterheight > height & & Z ( ) > = waterheight )
2016-03-01 15:47:10 +00:00
{
continue ;
}
}
2016-03-28 08:01:24 +00:00
cumm + = scrollv ;
if ( scrollv . X ) countx + + ;
if ( scrollv . Y ) county + + ;
2016-03-01 15:47:10 +00:00
}
// Some levels designed with Boom in mind actually want things to accelerate
// at neighboring scrolling sector boundaries. But it is only important for
// non-player objects.
2019-01-29 18:28:22 +00:00
if ( player ! = NULL | | ! ( Level - > i_compatflags & COMPATF_BOOMSCROLL ) )
2016-03-01 15:47:10 +00:00
{
if ( countx > 1 )
{
2016-03-28 08:01:24 +00:00
cumm . X / = countx ;
2016-03-01 15:47:10 +00:00
}
if ( county > 1 )
{
2016-03-28 08:01:24 +00:00
cumm . Y / = county ;
2016-03-01 15:47:10 +00:00
}
}
}
// [RH] If standing on a steep slope, fall down it
if ( ( flags & MF_SOLID ) & & ! ( flags & ( MF_NOCLIP | MF_NOGRAVITY ) ) & &
! ( flags & MF_NOBLOCKMAP ) & &
2016-03-20 18:52:35 +00:00
Vel . Z < = 0 & &
floorz = = Z ( ) )
2016-03-01 15:47:10 +00:00
{
secplane_t floorplane ;
// Check 3D floors as well
2016-03-26 11:36:15 +00:00
floorplane = P_FindFloorPlane ( floorsector , PosAtZ ( floorz ) ) ;
2016-03-01 15:47:10 +00:00
2016-03-30 07:41:46 +00:00
if ( floorplane . fC ( ) < STEEPSLOPE & &
2016-03-28 08:01:24 +00:00
floorplane . ZatPoint ( PosRelative ( floorsector ) ) < = floorz )
2016-03-01 15:47:10 +00:00
{
const msecnode_t * node ;
bool dopush = true ;
2016-03-30 07:41:46 +00:00
if ( floorplane . fC ( ) > STEEPSLOPE * 2 / 3 )
2016-03-01 15:47:10 +00:00
{
for ( node = touching_sectorlist ; node ; node = node - > m_tnext )
{
const 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-26 00:13:36 +00:00
if ( floorplane . ZatPoint ( PosRelative ( node - > m_sector ) ) > = Z ( ) - MaxStepHeight )
2016-03-01 15:47:10 +00:00
{
dopush = false ;
break ;
}
}
}
}
if ( dopush )
{
2016-03-19 23:54:18 +00:00
Vel + = floorplane . Normal ( ) . XY ( ) ;
2016-03-01 15:47:10 +00:00
}
}
}
// [RH] Missiles moving perfectly vertical need some X/Y movement, or they
// won't hurt anything. Don't do this if damage is 0! That way, you can
// still have missiles that go straight up and down through actors without
// damaging anything.
// (for backwards compatibility this must check for lack of damage function, not for zero damage!)
2016-09-19 01:36:51 +00:00
if ( ( flags & MF_MISSILE ) & & Vel . X = = 0 & & Vel . Y = = 0 & & ! IsZeroDamage ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-19 23:54:18 +00:00
Vel . X = MinVel ;
2016-03-01 15:47:10 +00:00
}
// Handle X and Y velocities
2018-10-31 19:56:01 +00:00
BlockingMobj = nullptr ;
sector_t * oldBlockingCeiling = BlockingCeiling ;
sector_t * oldBlockingFloor = BlockingFloor ;
2018-11-04 04:53:37 +00:00
Blocking3DFloor = nullptr ;
2018-10-31 19:56:01 +00:00
BlockingFloor = nullptr ;
BlockingCeiling = nullptr ;
2016-03-28 08:01:24 +00:00
double oldfloorz = P_XYMovement ( this , cumm ) ;
2016-03-01 15:47:10 +00:00
if ( ObjectFlags & OF_EuthanizeMe )
{ // actor was destroyed
return ;
}
2018-10-31 19:56:01 +00:00
// [ZZ] trigger hit floor/hit ceiling actions from XY movement
if ( BlockingFloor & & BlockingFloor ! = oldBlockingFloor & & ( ! player | | ! ( player - > cheats & CF_PREDICTING ) ) & & BlockingFloor - > SecActTarget )
BlockingFloor - > TriggerSectorActions ( this , SECSPAC_HitFloor ) ;
if ( BlockingCeiling & & BlockingCeiling ! = oldBlockingCeiling & & ( ! player | | ! ( player - > cheats & CF_PREDICTING ) ) & & BlockingCeiling - > SecActTarget )
BlockingCeiling - > TriggerSectorActions ( this , SECSPAC_HitCeiling ) ;
2016-03-19 23:54:18 +00:00
if ( Vel . X = = 0 & & Vel . Y = = 0 ) // Actors at rest
2016-03-01 15:47:10 +00:00
{
if ( flags2 & MF2_BLASTED )
{ // Reset to not blasted when velocities are gone
flags2 & = ~ MF2_BLASTED ;
}
if ( ( flags6 & MF6_TOUCHY ) & & ! IsSentient ( ) )
{ // Arm a mine which has come to rest
flags6 | = MF6_ARMED ;
}
}
2016-03-20 18:52:35 +00:00
if ( Vel . Z ! = 0 | | BlockingMobj | | Z ( ) ! = floorz )
2016-03-01 15:47:10 +00:00
{ // Handle Z velocity and gravity
2019-01-29 18:28:22 +00:00
if ( ( ( flags2 & MF2_PASSMOBJ ) | | ( flags & MF_SPECIAL ) ) & & ! ( Level - > i_compatflags & COMPATF_NO_PASSMOBJ ) )
2016-03-01 15:47:10 +00:00
{
if ( ! ( onmo = P_CheckOnmobj ( this ) ) )
{
P_ZMovement ( this , oldfloorz ) ;
flags2 & = ~ MF2_ONMOBJ ;
}
else
{
if ( player )
{
2019-01-27 15:08:22 +00:00
if ( Vel . Z < Level - > gravity * Sector - > gravity * ( - 1. / 100 ) // -655.36f)
2016-03-01 15:47:10 +00:00
& & ! ( flags & MF_NOGRAVITY ) )
{
PlayerLandedOnThing ( this , onmo ) ;
}
}
2016-03-22 11:42:27 +00:00
if ( onmo - > Top ( ) - Z ( ) < = MaxStepHeight )
2016-03-01 15:47:10 +00:00
{
if ( player & & player - > mo = = this )
{
2016-03-22 17:06:08 +00:00
player - > viewheight - = onmo - > Top ( ) - Z ( ) ;
2016-03-22 11:42:27 +00:00
double deltaview = player - > GetDeltaViewHeight ( ) ;
2016-03-01 15:47:10 +00:00
if ( deltaview > player - > deltaviewheight )
{
player - > deltaviewheight = deltaview ;
}
}
2016-03-22 17:06:08 +00:00
SetZ ( onmo - > Top ( ) ) ;
2016-03-01 15:47:10 +00:00
}
// Check for MF6_BUMPSPECIAL
// By default, only players can activate things by bumping into them
// We trigger specials as long as we are on top of it and not just when
// we land on it. This could be considered as gravity making us continually
// bump into it, but it also avoids having to worry about walking on to
// something without dropping and not triggering anything.
if ( ( onmo - > flags6 & MF6_BUMPSPECIAL ) & & ( ( player ! = NULL )
| | ( ( onmo - > activationtype & THINGSPEC_MonsterTrigger ) & & ( flags3 & MF3_ISMONSTER ) )
| | ( ( onmo - > activationtype & THINGSPEC_MissileTrigger ) & & ( flags & MF_MISSILE ) )
2019-01-27 15:08:22 +00:00
) & & ( Level - > maptime > onmo - > lastbump ) ) // Leave the bumper enough time to go away
2016-03-01 15:47:10 +00:00
{
if ( player = = NULL | | ! ( player - > cheats & CF_PREDICTING ) )
{
if ( P_ActivateThingSpecial ( onmo , this ) )
2019-01-27 15:08:22 +00:00
onmo - > lastbump = Level - > maptime + TICRATE ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-28 08:01:24 +00:00
if ( Vel . Z ! = 0 & & ( BounceFlags & BOUNCE_Actors ) )
2016-03-01 15:47:10 +00:00
{
P_BounceActor ( this , onmo , true ) ;
}
else
{
flags2 | = MF2_ONMOBJ ;
2016-03-19 23:54:18 +00:00
Vel . Z = 0 ;
2016-03-01 15:47:10 +00:00
Crash ( ) ;
}
}
}
else
{
P_ZMovement ( this , oldfloorz ) ;
}
if ( ObjectFlags & OF_EuthanizeMe )
return ; // actor was destroyed
}
2016-03-20 18:52:35 +00:00
else if ( Z ( ) < = floorz )
2016-03-01 15:47:10 +00:00
{
Crash ( ) ;
2016-10-16 02:14:34 +00:00
if ( ObjectFlags & OF_EuthanizeMe )
return ; // actor was destroyed
2016-03-01 15:47:10 +00:00
}
CheckPortalTransition ( true ) ;
2016-03-26 00:13:36 +00:00
UpdateWaterLevel ( ) ;
2016-03-01 15:47:10 +00:00
// [RH] Don't advance if predicting a player
if ( player & & ( player - > cheats & CF_PREDICTING ) )
{
return ;
}
// Check for poison damage, but only once per PoisonPeriod tics (or once per second if none).
2019-01-27 15:08:22 +00:00
if ( PoisonDurationReceived & & ( Level - > time % ( PoisonPeriodReceived ? PoisonPeriodReceived : TICRATE ) = = 0 ) )
2016-03-01 15:47:10 +00:00
{
P_DamageMobj ( this , NULL , Poisoner , PoisonDamageReceived , PoisonDamageTypeReceived ? PoisonDamageTypeReceived : ( FName ) NAME_Poison , 0 ) ;
- - PoisonDurationReceived ;
// Must clear damage when duration is done, otherwise it
// could be added to with ADDITIVEPOISONDAMAGE.
if ( ! PoisonDurationReceived ) PoisonDamageReceived = 0 ;
}
}
assert ( state ! = NULL ) ;
if ( state = = NULL )
{
Destroy ( ) ;
return ;
}
if ( ! CheckNoDelay ( ) )
return ; // freed itself
// cycle through states, calling action functions at transitions
2016-04-17 21:48:04 +00:00
UpdateRenderSectorList ( ) ;
2016-03-01 15:47:10 +00:00
if ( tics ! = - 1 )
{
// [RH] Use tics <= 0 instead of == 0 so that spawnstates
// of 0 tics work as expected.
if ( - - tics < = 0 )
{
if ( ! SetState ( state - > GetNextState ( ) ) )
return ; // freed itself
}
}
2017-02-26 19:06:16 +00:00
if ( tics = = - 1 | | state - > GetCanRaise ( ) )
2016-03-01 15:47:10 +00:00
{
int respawn_monsters = G_SkillProperty ( SKILLP_Respawn ) ;
// check for nightmare respawn
if ( ! ( flags5 & MF5_ALWAYSRESPAWN ) )
{
if ( ! respawn_monsters | | ! ( flags3 & MF3_ISMONSTER ) | | ( flags2 & MF2_DORMANT ) | | ( flags5 & MF5_NEVERRESPAWN ) )
return ;
int limit = G_SkillProperty ( SKILLP_RespawnLimit ) ;
if ( limit > 0 & & skillrespawncount > = limit )
return ;
}
movecount + + ;
if ( movecount < respawn_monsters )
return ;
2019-01-27 15:08:22 +00:00
if ( Level - > time & 31 )
2016-03-01 15:47:10 +00:00
return ;
if ( pr_nightmarerespawn ( ) > 4 )
return ;
P_NightmareRespawn ( this ) ;
}
}
//==========================================================================
//
// AActor :: CheckNoDelay
//
//==========================================================================
bool AActor : : CheckNoDelay ( )
{
if ( ( flags7 & MF7_HANDLENODELAY ) & & ! ( flags2 & MF2_DORMANT ) )
{
flags7 & = ~ MF7_HANDLENODELAY ;
if ( state - > GetNoDelay ( ) )
{
// For immediately spawned objects with the NoDelay flag set for their
// Spawn state, explicitly call the current state's function.
FState * newstate ;
2016-06-16 14:11:00 +00:00
FStateParamInfo stp = { state , STATE_Actor , PSP_WEAPON } ;
if ( state - > CallAction ( this , this , & stp , & newstate ) )
2016-03-01 15:47:10 +00:00
{
if ( ObjectFlags & OF_EuthanizeMe )
{
return false ; // freed itself
}
if ( newstate ! = NULL )
{
return SetState ( newstate ) ;
}
}
}
}
return true ;
}
2017-01-12 23:35:56 +00:00
DEFINE_ACTION_FUNCTION ( AActor , CheckNoDelay )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
ACTION_RETURN_BOOL ( self - > CheckNoDelay ( ) ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// AActor :: CheckSectorTransition
//
// Fire off some sector triggers if the actor has changed sectors.
//
//==========================================================================
void AActor : : CheckSectorTransition ( sector_t * oldsec )
{
if ( oldsec ! = Sector )
{
if ( oldsec - > SecActTarget ! = NULL )
{
2017-01-13 00:34:43 +00:00
oldsec - > TriggerSectorActions ( this , SECSPAC_Exit ) ;
2016-03-01 15:47:10 +00:00
}
if ( Sector - > SecActTarget ! = NULL )
{
int act = SECSPAC_Enter ;
2016-03-30 07:41:46 +00:00
if ( Z ( ) < = Sector - > floorplane . ZatPoint ( this ) )
2016-03-01 15:47:10 +00:00
{
act | = SECSPAC_HitFloor ;
}
2016-03-30 07:41:46 +00:00
if ( Top ( ) > = Sector - > ceilingplane . ZatPoint ( this ) )
2016-03-01 15:47:10 +00:00
{
act | = SECSPAC_HitCeiling ;
}
2016-03-30 07:41:46 +00:00
if ( Sector - > heightsec ! = NULL & & Z ( ) = = Sector - > heightsec - > floorplane . ZatPoint ( this ) )
2016-03-01 15:47:10 +00:00
{
act | = SECSPAC_HitFakeFloor ;
}
2017-01-13 00:34:43 +00:00
Sector - > TriggerSectorActions ( this , act ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-20 18:52:35 +00:00
if ( Z ( ) = = floorz )
2016-03-01 15:47:10 +00:00
{
2018-10-31 19:56:01 +00:00
P_CheckFor3DFloorHit ( this , Z ( ) , true ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-20 12:32:53 +00:00
if ( Top ( ) = = ceilingz )
2016-03-01 15:47:10 +00:00
{
2018-10-31 19:56:01 +00:00
P_CheckFor3DCeilingHit ( this , Top ( ) , true ) ;
2016-03-01 15:47:10 +00:00
}
}
}
//==========================================================================
//
2017-02-14 19:11:30 +00:00
// AActor::SplashCheck
2016-03-01 15:47:10 +00:00
//
// Returns true if actor should splash
//
//==========================================================================
2017-02-14 19:11:30 +00:00
void AActor : : SplashCheck ( )
2016-03-01 15:47:10 +00:00
{
2016-03-22 17:06:08 +00:00
double fh = - FLT_MAX ;
2017-02-14 19:11:30 +00:00
bool reset = false ;
2016-03-01 15:47:10 +00:00
waterlevel = 0 ;
if ( Sector = = NULL )
{
2017-02-14 19:11:30 +00:00
return ;
2016-03-01 15:47:10 +00:00
}
2018-05-01 09:29:29 +00:00
if ( Sector - > MoreFlags & SECMF_UNDERWATER ) // intentionally not SECMF_UNDERWATERMASK
2016-03-01 15:47:10 +00:00
{
waterlevel = 3 ;
}
else
{
const sector_t * hsec = Sector - > GetHeightSec ( ) ;
if ( hsec ! = NULL )
{
2017-02-14 19:11:30 +00:00
fh = hsec - > floorplane . ZatPoint ( this ) ;
2018-05-01 09:29:29 +00:00
//if (hsec->MoreFlags & SECMF_UNDERWATERMASK) // also check Boom-style non-swimmable sectors
2016-03-01 15:47:10 +00:00
{
2016-03-22 17:06:08 +00:00
if ( Z ( ) < fh )
2016-03-01 15:47:10 +00:00
{
waterlevel = 1 ;
2016-03-22 17:06:08 +00:00
if ( Center ( ) < fh )
2016-03-01 15:47:10 +00:00
{
waterlevel = 2 ;
2016-03-22 17:06:08 +00:00
if ( ( player & & Z ( ) + player - > viewheight < = fh ) | |
( Top ( ) < = fh ) )
2016-03-01 15:47:10 +00:00
{
waterlevel = 3 ;
}
}
}
2018-05-01 09:29:29 +00:00
else if ( ! ( hsec - > MoreFlags & SECMF_FAKEFLOORONLY ) & & ( Top ( ) > hsec - > ceilingplane . ZatPoint ( this ) ) )
2016-03-01 15:47:10 +00:00
{
waterlevel = 3 ;
}
else
{
waterlevel = 0 ;
}
}
}
else
{
// Check 3D floors as well!
2017-02-14 19:11:30 +00:00
for ( auto rover : Sector - > e - > XFloor . ffloors )
2016-03-01 15:47:10 +00:00
{
if ( ! ( rover - > flags & FF_EXISTS ) ) continue ;
2017-01-29 17:46:35 +00:00
if ( rover - > flags & FF_SOLID ) continue ;
2016-03-01 15:47:10 +00:00
2017-02-14 19:11:30 +00:00
bool reset = ! ( rover - > flags & FF_SWIMMABLE ) ;
2017-01-29 17:46:35 +00:00
if ( reset & & rover - > alpha = = 0 ) continue ;
2017-02-14 19:11:30 +00:00
double ff_bottom = rover - > bottom . plane - > ZatPoint ( this ) ;
double ff_top = rover - > top . plane - > ZatPoint ( this ) ;
2016-03-01 15:47:10 +00:00
2017-02-14 19:11:30 +00:00
if ( ff_top < = Z ( ) | | ff_bottom > ( Center ( ) ) ) continue ;
fh = ff_top ;
2016-03-22 17:06:08 +00:00
if ( Z ( ) < fh )
2016-03-01 15:47:10 +00:00
{
waterlevel = 1 ;
2016-03-22 17:06:08 +00:00
if ( Center ( ) < fh )
2016-03-01 15:47:10 +00:00
{
waterlevel = 2 ;
2016-03-22 17:06:08 +00:00
if ( ( player & & Z ( ) + player - > viewheight < = fh ) | |
( Top ( ) < = fh ) )
2016-03-01 15:47:10 +00:00
{
waterlevel = 3 ;
}
}
}
break ;
}
}
}
2017-02-14 19:11:30 +00:00
2016-03-01 15:47:10 +00:00
// some additional checks to make deep sectors like Boom's splash without setting
// the water flags.
2017-02-14 19:11:30 +00:00
if ( boomwaterlevel = = 0 & & waterlevel ! = 0 )
2016-03-01 15:47:10 +00:00
{
2016-03-23 12:31:12 +00:00
P_HitWater ( this , Sector , PosAtZ ( fh ) , true ) ;
2016-03-01 15:47:10 +00:00
}
boomwaterlevel = waterlevel ;
2017-02-14 19:11:30 +00:00
return ;
}
//==========================================================================
//
// AActor::UpdateWaterLevel
//
// Returns true if actor should splash
//
//==========================================================================
bool AActor : : UpdateWaterLevel ( bool dosplash )
{
if ( dosplash ) SplashCheck ( ) ;
double fh = - FLT_MAX ;
bool reset = false ;
2019-01-03 13:35:17 +00:00
int oldlevel = waterlevel ;
2017-02-14 19:11:30 +00:00
waterlevel = 0 ;
2019-01-03 13:35:17 +00:00
if ( Sector ! = nullptr )
2016-03-01 15:47:10 +00:00
{
2019-01-03 13:35:17 +00:00
if ( Sector - > MoreFlags & SECMF_UNDERWATER ) // intentionally not SECMF_UNDERWATERMASK
2017-02-14 19:11:30 +00:00
{
2019-01-03 13:35:17 +00:00
waterlevel = 3 ;
}
else
{
const sector_t * hsec = Sector - > GetHeightSec ( ) ;
if ( hsec ! = NULL )
2017-02-14 19:11:30 +00:00
{
2019-01-03 13:35:17 +00:00
fh = hsec - > floorplane . ZatPoint ( this ) ;
if ( hsec - > MoreFlags & SECMF_UNDERWATERMASK ) // also check Boom-style non-swimmable sectors
2017-02-14 19:11:30 +00:00
{
2019-01-03 13:35:17 +00:00
if ( Z ( ) < fh )
2017-02-14 19:11:30 +00:00
{
2019-01-03 13:35:17 +00:00
waterlevel = 1 ;
if ( Center ( ) < fh )
2017-02-14 19:11:30 +00:00
{
2019-01-03 13:35:17 +00:00
waterlevel = 2 ;
if ( ( player & & Z ( ) + player - > viewheight < = fh ) | |
( Top ( ) < = fh ) )
{
waterlevel = 3 ;
}
2017-02-14 19:11:30 +00:00
}
}
2019-01-03 13:35:17 +00:00
else if ( ! ( hsec - > MoreFlags & SECMF_FAKEFLOORONLY ) & & ( Top ( ) > hsec - > ceilingplane . ZatPoint ( this ) ) )
{
waterlevel = 3 ;
}
else
{
waterlevel = 0 ;
}
2017-02-14 19:11:30 +00:00
}
}
2019-01-03 13:35:17 +00:00
else
2017-02-14 19:11:30 +00:00
{
2019-01-03 13:35:17 +00:00
// Check 3D floors as well!
for ( auto rover : Sector - > e - > XFloor . ffloors )
{
if ( ! ( rover - > flags & FF_EXISTS ) ) continue ;
if ( rover - > flags & FF_SOLID ) continue ;
if ( ! ( rover - > flags & FF_SWIMMABLE ) ) continue ;
2017-02-14 19:11:30 +00:00
2019-01-03 13:35:17 +00:00
double ff_bottom = rover - > bottom . plane - > ZatPoint ( this ) ;
double ff_top = rover - > top . plane - > ZatPoint ( this ) ;
2017-02-14 19:11:30 +00:00
2019-01-03 13:35:17 +00:00
if ( ff_top < = Z ( ) | | ff_bottom > ( Center ( ) ) ) continue ;
2017-02-14 19:11:30 +00:00
2019-01-03 13:35:17 +00:00
fh = ff_top ;
if ( Z ( ) < fh )
2017-02-14 19:11:30 +00:00
{
2019-01-03 13:35:17 +00:00
waterlevel = 1 ;
if ( Center ( ) < fh )
2017-02-14 19:11:30 +00:00
{
2019-01-03 13:35:17 +00:00
waterlevel = 2 ;
if ( ( player & & Z ( ) + player - > viewheight < = fh ) | |
( Top ( ) < = fh ) )
{
waterlevel = 3 ;
}
2017-02-14 19:11:30 +00:00
}
}
2019-01-03 13:35:17 +00:00
break ;
2017-02-14 19:11:30 +00:00
}
2019-01-03 13:35:17 +00:00
}
}
2017-02-14 19:11:30 +00:00
2019-01-03 13:35:17 +00:00
// Play surfacing and diving sounds, as appropriate.
if ( player ! = nullptr )
{
if ( oldlevel < 3 & & waterlevel = = 3 )
{
// Our head just went under.
S_Sound ( this , CHAN_VOICE , " *dive " , 1 , ATTN_NORM ) ;
}
else if ( oldlevel = = 3 & & waterlevel < 3 )
{
// Our head just came up.
2019-01-27 20:59:19 +00:00
if ( player - > air_finished > Level - > time )
2019-01-03 13:35:17 +00:00
{
// We hadn't run out of air yet.
S_Sound ( this , CHAN_VOICE , " *surface " , 1 , ATTN_NORM ) ;
}
// If we were running out of air, then ResetAirSupply() will play *gasp.
2017-02-14 19:11:30 +00:00
}
}
2016-03-01 15:47:10 +00:00
}
return false ; // we did the splash ourselves
}
2017-01-12 23:35:56 +00:00
DEFINE_ACTION_FUNCTION ( AActor , UpdateWaterLevel )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2018-11-17 09:03:40 +00:00
PARAM_BOOL ( splash ) ;
2017-01-12 23:35:56 +00:00
ACTION_RETURN_BOOL ( self - > UpdateWaterLevel ( splash ) ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// P_SpawnMobj
//
//==========================================================================
2019-01-27 20:59:19 +00:00
void ConstructActor ( AActor * actor , const DVector3 & pos , bool SpawningMapThing )
2016-03-01 15:47:10 +00:00
{
2019-01-27 20:59:19 +00:00
auto Level = actor - > Level ;
actor - > SpawnTime = Level - > totaltime ;
actor - > SpawnOrder = Level - > spawnindex + + ;
2016-03-01 15:47:10 +00:00
// Set default dialogue
2019-01-27 20:59:19 +00:00
actor - > ConversationRoot = Level - > GetConversation ( actor - > GetClass ( ) - > TypeName ) ;
2016-03-01 15:47:10 +00:00
if ( actor - > ConversationRoot ! = - 1 )
{
2019-01-27 20:59:19 +00:00
actor - > Conversation = Level - > StrifeDialogues [ actor - > ConversationRoot ] ;
2016-03-01 15:47:10 +00:00
}
else
{
actor - > Conversation = NULL ;
}
2016-03-23 09:42:41 +00:00
actor - > SetXYZ ( pos ) ;
2016-04-17 21:48:04 +00:00
actor - > OldRenderPos = { FLT_MAX , FLT_MAX , FLT_MAX } ;
2016-03-01 15:47:10 +00:00
actor - > picnum . SetInvalid ( ) ;
actor - > health = actor - > SpawnHealth ( ) ;
// Actors with zero gravity need the NOGRAVITY flag set.
2016-03-20 23:51:19 +00:00
if ( actor - > Gravity = = 0 ) actor - > flags | = MF_NOGRAVITY ;
2016-03-01 15:47:10 +00:00
2019-01-30 00:38:18 +00:00
FRandom & rng = Level - > BotInfo . m_Thinking ? pr_botspawnmobj : pr_spawnmobj ;
2016-03-01 15:47:10 +00:00
if ( actor - > isFast ( ) & & actor - > flags3 & MF3_ISMONSTER )
actor - > reactiontime = 0 ;
if ( actor - > flags3 & MF3_ISMONSTER )
{
actor - > LastLookPlayerNumber = rng ( ) % MAXPLAYERS ;
actor - > TIDtoHate = 0 ;
}
// Set the state, but do not use SetState, because action
// routines can't be called yet. If the spawnstate has an action
// routine, it will not be called.
FState * st = actor - > SpawnState ;
actor - > state = st ;
actor - > tics = st - > GetTics ( ) ;
actor - > sprite = st - > sprite ;
actor - > frame = st - > GetFrame ( ) ;
actor - > renderflags = ( actor - > renderflags & ~ RF_FULLBRIGHT ) | ActorRenderFlags : : FromInt ( st - > GetFullbright ( ) ) ;
2016-12-26 10:58:08 +00:00
actor - > touching_sectorlist = nullptr ; // NULL head of sector list // phares 3/13/98
actor - > touching_rendersectors = nullptr ;
2017-02-28 11:47:44 +00:00
if ( G_SkillProperty ( SKILLP_FastMonsters ) )
{
double f = actor - > FloatVar ( NAME_FastSpeed ) ;
if ( f > = 0 ) actor - > Speed = f ;
}
2016-03-01 15:47:10 +00:00
// set subsector and/or block links
2016-12-25 21:40:26 +00:00
actor - > LinkToWorld ( nullptr , SpawningMapThing ) ;
2016-03-01 15:47:10 +00:00
actor - > ClearInterpolation ( ) ;
2016-03-23 11:21:52 +00:00
actor - > dropoffz = actor - > floorz = actor - > Sector - > floorplane . ZatPoint ( pos ) ;
2016-03-23 09:42:41 +00:00
actor - > ceilingz = actor - > Sector - > ceilingplane . ZatPoint ( pos ) ;
2016-03-01 15:47:10 +00:00
// The z-coordinate needs to be set once before calling P_FindFloorCeiling
// For FLOATRANDZ just use the floor here.
2016-03-23 09:42:41 +00:00
if ( pos . Z = = ONFLOORZ | | pos . Z = = FLOATRANDZ )
2016-03-01 15:47:10 +00:00
{
2016-03-20 18:52:35 +00:00
actor - > SetZ ( actor - > floorz ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-23 09:42:41 +00:00
else if ( pos . Z = = ONCEILINGZ )
2016-03-01 15:47:10 +00:00
{
2016-03-20 19:55:06 +00:00
actor - > SetZ ( actor - > ceilingz - actor - > Height ) ;
2016-03-01 15:47:10 +00:00
}
2019-01-27 20:59:19 +00:00
if ( SpawningMapThing | | ! actor - > IsKindOf ( NAME_PlayerPawn ) )
2016-03-01 15:47:10 +00:00
{
// Check if there's something solid to stand on between the current position and the
// current sector's floor. For map spawns this must be delayed until after setting the
// z-coordinate.
if ( ! SpawningMapThing )
{
P_FindFloorCeiling ( actor , FFCF_ONLYSPAWNPOS ) ;
}
else
{
actor - > floorsector = actor - > Sector ;
actor - > floorpic = actor - > floorsector - > GetTexture ( sector_t : : floor ) ;
actor - > floorterrain = actor - > floorsector - > GetTerrain ( sector_t : : floor ) ;
actor - > ceilingsector = actor - > Sector ;
actor - > ceilingpic = actor - > ceilingsector - > GetTexture ( sector_t : : ceiling ) ;
}
}
else if ( ! ( actor - > flags5 & MF5_NOINTERACTION ) )
{
P_FindFloorCeiling ( actor ) ;
}
else
{
actor - > floorpic = actor - > Sector - > GetTexture ( sector_t : : floor ) ;
actor - > floorterrain = actor - > Sector - > GetTerrain ( sector_t : : floor ) ;
actor - > floorsector = actor - > Sector ;
actor - > ceilingpic = actor - > Sector - > GetTexture ( sector_t : : ceiling ) ;
actor - > ceilingsector = actor - > Sector ;
}
2016-03-23 09:42:41 +00:00
actor - > SpawnPoint . X = pos . X ;
actor - > SpawnPoint . Y = pos . Y ;
// do not copy Z!
2016-03-01 15:47:10 +00:00
2016-03-23 09:42:41 +00:00
if ( pos . Z = = ONFLOORZ )
2016-03-01 15:47:10 +00:00
{
2016-03-20 18:52:35 +00:00
actor - > SetZ ( actor - > floorz ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-23 09:42:41 +00:00
else if ( pos . Z = = ONCEILINGZ )
2016-03-01 15:47:10 +00:00
{
2016-03-20 19:55:06 +00:00
actor - > SetZ ( actor - > ceilingz - actor - > Height ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-23 09:42:41 +00:00
else if ( pos . Z = = FLOATRANDZ )
2016-03-01 15:47:10 +00:00
{
2016-03-20 19:55:06 +00:00
double space = actor - > ceilingz - actor - > Height - actor - > floorz ;
2016-03-20 18:52:35 +00:00
if ( space > 48 )
2016-03-01 15:47:10 +00:00
{
2016-03-20 18:52:35 +00:00
space - = 40 ;
actor - > SetZ ( space * rng ( ) / 256. + actor - > floorz + 40 ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-20 18:52:35 +00:00
actor - > SetZ ( actor - > floorz ) ;
2016-03-01 15:47:10 +00:00
}
}
else
{
2016-03-30 07:41:46 +00:00
actor - > SpawnPoint . Z = ( actor - > Z ( ) - actor - > Sector - > floorplane . ZatPoint ( actor ) ) ;
2016-03-01 15:47:10 +00:00
}
2017-03-08 14:20:00 +00:00
if ( actor - > FloatBobPhase = = ( uint8_t ) - 1 ) actor - > FloatBobPhase = rng ( ) ; // Don't make everything bob in sync (unless deliberately told to do)
2016-03-01 15:47:10 +00:00
if ( actor - > flags2 & MF2_FLOORCLIP )
{
actor - > AdjustFloorClip ( ) ;
}
else
{
2016-03-20 22:42:27 +00:00
actor - > Floorclip = 0 ;
2016-03-01 15:47:10 +00:00
}
2016-03-26 00:13:36 +00:00
actor - > UpdateWaterLevel ( false ) ;
2016-03-01 15:47:10 +00:00
if ( ! SpawningMapThing )
{
2016-11-24 20:36:02 +00:00
actor - > CallBeginPlay ( ) ;
2016-03-01 15:47:10 +00:00
if ( actor - > ObjectFlags & OF_EuthanizeMe )
{
2019-01-27 20:59:19 +00:00
return ;
2016-03-01 15:47:10 +00:00
}
}
2019-01-27 20:59:19 +00:00
if ( Level - > flags & LEVEL_NOALLIES & & ! actor - > player )
2016-03-01 15:47:10 +00:00
{
actor - > flags & = ~ MF_FRIENDLY ;
}
// [RH] Count monsters whenever they are spawned.
if ( actor - > CountsAsKill ( ) )
{
2019-01-27 20:59:19 +00:00
Level - > total_monsters + + ;
2016-03-01 15:47:10 +00:00
}
// [RH] Same, for items
if ( actor - > flags & MF_COUNTITEM )
{
2019-01-27 20:59:19 +00:00
Level - > total_items + + ;
2016-03-01 15:47:10 +00:00
}
// And for secrets
if ( actor - > flags5 & MF5_COUNTSECRET )
{
2019-01-27 20:59:19 +00:00
Level - > total_secrets + + ;
2016-03-01 15:47:10 +00:00
}
2017-05-19 14:31:44 +00:00
// force scroller check in the first tic.
actor - > flags8 | = MF8_INSCROLLSEC ;
2019-01-27 20:59:19 +00:00
}
AActor * AActor : : StaticSpawn ( PClassActor * type , const DVector3 & pos , replace_t allowreplacement , bool SpawningMapThing )
{
if ( type = = NULL )
{
I_Error ( " Tried to spawn a class-less actor \n " ) ;
}
if ( allowreplacement )
{
type = type - > GetReplacement ( ) ;
}
AActor * actor ;
actor = static_cast < AActor * > ( level . CreateThinker ( type ) ) ;
ConstructActor ( actor , pos , SpawningMapThing ) ;
2016-03-01 15:47:10 +00:00
return actor ;
}
2016-10-30 13:00:11 +00:00
DEFINE_ACTION_FUNCTION ( AActor , Spawn )
{
PARAM_PROLOGUE ;
2016-12-02 11:06:49 +00:00
PARAM_CLASS_NOT_NULL ( type , AActor ) ;
2018-11-17 09:03:40 +00:00
PARAM_FLOAT ( x ) ;
PARAM_FLOAT ( y ) ;
PARAM_FLOAT ( z ) ;
PARAM_INT ( flags ) ;
2016-10-30 13:00:11 +00:00
ACTION_RETURN_OBJECT ( AActor : : StaticSpawn ( type , DVector3 ( x , y , z ) , replace_t ( flags ) ) ) ;
}
2016-03-23 09:42:41 +00:00
PClassActor * ClassForSpawn ( FName classname )
2016-03-01 15:47:10 +00:00
{
2016-03-23 09:42:41 +00:00
PClass * cls = PClass : : FindClass ( classname ) ;
if ( cls = = NULL )
2016-03-01 15:47:10 +00:00
{
I_Error ( " Attempt to spawn actor of unknown type '%s' \n " , classname . GetChars ( ) ) ;
}
2017-04-12 08:20:58 +00:00
if ( ! cls - > IsDescendantOf ( RUNTIME_CLASS ( AActor ) ) )
2016-03-01 15:47:10 +00:00
{
I_Error ( " Attempt to spawn non-actor of type '%s' \n " , classname . GetChars ( ) ) ;
}
2016-03-23 09:42:41 +00:00
return static_cast < PClassActor * > ( cls ) ;
2016-03-01 15:47:10 +00:00
}
void AActor : : LevelSpawned ( )
{
if ( tics > 0 & & ! ( flags4 & MF4_SYNCHRONIZED ) )
{
tics = 1 + ( pr_spawnmapthing ( ) % tics ) ;
}
// [RH] Clear MF_DROPPED flag if the default version doesn't have it set.
2018-12-04 16:11:36 +00:00
// (Inventory.BeginPlay() makes all inventory items spawn with it set.)
2016-03-01 15:47:10 +00:00
if ( ! ( GetDefault ( ) - > flags & MF_DROPPED ) )
{
flags & = ~ MF_DROPPED ;
}
HandleSpawnFlags ( ) ;
}
void AActor : : HandleSpawnFlags ( )
{
if ( SpawnFlags & MTF_AMBUSH )
{
flags | = MF_AMBUSH ;
}
if ( SpawnFlags & MTF_DORMANT )
{
2016-11-24 20:36:02 +00:00
CallDeactivate ( NULL ) ;
2016-03-01 15:47:10 +00:00
}
if ( SpawnFlags & MTF_STANDSTILL )
{
flags4 | = MF4_STANDSTILL ;
}
if ( SpawnFlags & MTF_FRIENDLY )
{
flags | = MF_FRIENDLY ;
// Friendlies don't count as kills!
if ( flags & MF_COUNTKILL )
{
flags & = ~ MF_COUNTKILL ;
2019-01-27 20:59:19 +00:00
Level - > total_monsters - - ;
2016-03-01 15:47:10 +00:00
}
}
if ( SpawnFlags & MTF_SHADOW )
{
flags | = MF_SHADOW ;
RenderStyle = STYLE_Translucent ;
2016-03-21 11:18:46 +00:00
Alpha = 0.25 ;
2016-03-01 15:47:10 +00:00
}
else if ( SpawnFlags & MTF_ALTSHADOW )
{
RenderStyle = STYLE_None ;
}
if ( SpawnFlags & MTF_SECRET )
{
if ( ! ( flags5 & MF5_COUNTSECRET ) )
{
//Printf("Secret %s in sector %i!\n", GetTag(), Sector->sectornum);
flags5 | = MF5_COUNTSECRET ;
2019-01-27 20:59:19 +00:00
Level - > total_secrets + + ;
2016-03-01 15:47:10 +00:00
}
}
}
2017-01-14 01:05:52 +00:00
DEFINE_ACTION_FUNCTION ( AActor , HandleSpawnFlags )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
self - > HandleSpawnFlags ( ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
void AActor : : BeginPlay ( )
{
// If the actor is spawned with the dormant flag set, clear it, and use
// the normal deactivation logic to make it properly dormant.
if ( flags2 & MF2_DORMANT )
{
flags2 & = ~ MF2_DORMANT ;
2016-11-24 20:36:02 +00:00
CallDeactivate ( NULL ) ;
2016-03-01 15:47:10 +00:00
}
}
2016-11-21 13:59:17 +00:00
DEFINE_ACTION_FUNCTION ( AActor , BeginPlay )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
self - > BeginPlay ( ) ;
return 0 ;
}
2016-11-24 20:36:02 +00:00
void AActor : : CallBeginPlay ( )
{
IFVIRTUAL ( AActor , BeginPlay )
{
// Without the type cast this picks the 'void *' assignment...
VMValue params [ 1 ] = { ( DObject * ) this } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 1 , nullptr , 0 ) ;
2016-11-24 20:36:02 +00:00
}
else BeginPlay ( ) ;
}
2016-11-21 13:59:17 +00:00
2016-03-01 15:47:10 +00:00
void AActor : : PostBeginPlay ( )
{
2017-03-12 15:56:00 +00:00
SetDynamicLights ( ) ;
2016-03-16 11:41:26 +00:00
PrevAngles = Angles ;
2016-03-01 15:47:10 +00:00
flags7 | = MF7_HANDLENODELAY ;
}
2017-01-30 07:10:33 +00:00
void AActor : : CallPostBeginPlay ( )
{
Super : : CallPostBeginPlay ( ) ;
E_WorldThingSpawned ( this ) ;
}
2016-03-01 15:47:10 +00:00
bool AActor : : isFast ( )
{
if ( flags5 & MF5_ALWAYSFAST ) return true ;
if ( flags5 & MF5_NEVERFAST ) return false ;
return ! ! G_SkillProperty ( SKILLP_FastMonsters ) ;
}
bool AActor : : isSlow ( )
{
return ! ! G_SkillProperty ( SKILLP_SlowMonsters ) ;
}
2016-11-24 20:36:02 +00:00
//===========================================================================
//
// Activate
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void AActor : : Activate ( AActor * activator )
{
if ( ( flags3 & MF3_ISMONSTER ) & & ( health > 0 | | ( flags & MF_ICECORPSE ) ) )
{
if ( flags2 & MF2_DORMANT )
{
flags2 & = ~ MF2_DORMANT ;
FState * state = FindState ( NAME_Active ) ;
if ( state ! = NULL )
{
SetState ( state ) ;
}
else
{
tics = 1 ;
}
}
}
}
2016-11-21 13:59:17 +00:00
DEFINE_ACTION_FUNCTION ( AActor , Activate )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_OBJECT ( activator , AActor ) ;
self - > Activate ( activator ) ;
return 0 ;
}
2016-11-24 20:36:02 +00:00
void AActor : : CallActivate ( AActor * activator )
{
IFVIRTUAL ( AActor , Activate )
{
// Without the type cast this picks the 'void *' assignment...
VMValue params [ 2 ] = { ( DObject * ) this , ( DObject * ) activator } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2016-11-24 20:36:02 +00:00
}
else Activate ( activator ) ;
}
//===========================================================================
//
// Deactivate
//
//===========================================================================
2016-03-01 15:47:10 +00:00
void AActor : : Deactivate ( AActor * activator )
{
if ( ( flags3 & MF3_ISMONSTER ) & & ( health > 0 | | ( flags & MF_ICECORPSE ) ) )
{
if ( ! ( flags2 & MF2_DORMANT ) )
{
flags2 | = MF2_DORMANT ;
FState * state = FindState ( NAME_Inactive ) ;
if ( state ! = NULL )
{
SetState ( state ) ;
}
else
{
tics = - 1 ;
}
}
}
}
2016-11-21 13:59:17 +00:00
DEFINE_ACTION_FUNCTION ( AActor , Deactivate )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_OBJECT ( activator , AActor ) ;
self - > Deactivate ( activator ) ;
return 0 ;
}
2016-11-24 20:36:02 +00:00
void AActor : : CallDeactivate ( AActor * activator )
{
IFVIRTUAL ( AActor , Deactivate )
{
// Without the type cast this picks the 'void *' assignment...
VMValue params [ 2 ] = { ( DObject * ) this , ( DObject * ) activator } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 2 , nullptr , 0 ) ;
2016-11-24 20:36:02 +00:00
}
else Deactivate ( activator ) ;
}
2016-04-17 21:48:04 +00:00
2016-11-24 20:36:02 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
//
2016-11-24 20:36:02 +00:00
// Destroy
2016-03-01 15:47:10 +00:00
//
2016-11-24 20:36:02 +00:00
//===========================================================================
2016-03-01 15:47:10 +00:00
2017-01-12 21:49:18 +00:00
void AActor : : OnDestroy ( )
2016-03-01 15:47:10 +00:00
{
2017-01-31 00:15:57 +00:00
// [ZZ] call destroy event hook.
// note that this differs from ThingSpawned in that you can actually override OnDestroy to avoid calling the hook.
// but you can't really do that without utterly breaking the game, so it's ok.
// note: if OnDestroy is ever made optional, E_WorldThingDestroyed should still be called for ANY thing.
E_WorldThingDestroyed ( this ) ;
2019-01-01 18:35:55 +00:00
DeleteAttachedLights ( ) ;
2016-04-17 21:48:04 +00:00
ClearRenderSectorList ( ) ;
2016-05-11 07:58:03 +00:00
ClearRenderLineList ( ) ;
2016-04-17 21:48:04 +00:00
2016-03-01 15:47:10 +00:00
// [RH] Destroy any inventory this actor is carrying
DestroyAllInventory ( ) ;
// [RH] Unlink from tid chain
RemoveFromHash ( ) ;
// unlink from sector and block lists
2016-12-25 21:40:26 +00:00
UnlinkFromWorld ( nullptr ) ;
2016-03-01 15:47:10 +00:00
flags | = MF_NOSECTOR | MF_NOBLOCKMAP ;
// Transform any playing sound into positioned, non-actor sounds.
S_RelinkSound ( this , NULL ) ;
2017-01-12 21:49:18 +00:00
Super : : OnDestroy ( ) ;
2016-03-01 15:47:10 +00:00
}
//===========================================================================
//
// AdjustFloorClip
//
//===========================================================================
void AActor : : AdjustFloorClip ( )
{
if ( flags3 & MF3_SPECIALFLOORCLIP )
{
return ;
}
2016-03-26 19:59:35 +00:00
double oldclip = Floorclip ;
2016-03-20 22:42:27 +00:00
double shallowestclip = INT_MAX ;
2016-03-01 15:47:10 +00:00
const msecnode_t * m ;
// possibly standing on a 3D-floor
2016-03-30 07:41:46 +00:00
if ( Sector - > e - > XFloor . ffloors . Size ( ) & & Z ( ) > Sector - > floorplane . ZatPoint ( this ) ) Floorclip = 0 ;
2016-03-01 15:47:10 +00:00
// [RH] clip based on shallowest floor player is standing on
// If the sector has a deep water effect, then let that effect
// do the floorclipping instead of the terrain type.
for ( m = touching_sectorlist ; m ; m = m - > m_tnext )
{
2016-03-28 10:03:07 +00:00
DVector3 pos = PosRelative ( m - > m_sector ) ;
2016-03-01 15:47:10 +00:00
sector_t * hsec = m - > m_sector - > GetHeightSec ( ) ;
2016-03-28 10:03:07 +00:00
if ( hsec = = NULL & & m - > m_sector - > floorplane . ZatPoint ( pos ) = = Z ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-20 22:42:27 +00:00
double clip = Terrains [ m - > m_sector - > GetTerrain ( sector_t : : floor ) ] . FootClip ;
2016-03-01 15:47:10 +00:00
if ( clip < shallowestclip )
{
shallowestclip = clip ;
}
}
}
2016-03-20 22:42:27 +00:00
if ( shallowestclip = = INT_MAX )
2016-03-01 15:47:10 +00:00
{
2016-03-20 22:42:27 +00:00
Floorclip = 0 ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-20 22:42:27 +00:00
Floorclip = shallowestclip ;
2016-03-01 15:47:10 +00:00
}
2016-03-20 22:42:27 +00:00
if ( player & & player - > mo = = this & & oldclip ! = Floorclip )
2016-03-01 15:47:10 +00:00
{
2016-03-22 17:06:08 +00:00
player - > viewheight - = ( oldclip - Floorclip ) ;
2016-03-01 15:47:10 +00:00
player - > deltaviewheight = player - > GetDeltaViewHeight ( ) ;
}
}
2016-11-01 15:32:47 +00:00
DEFINE_ACTION_FUNCTION ( AActor , AdjustFloorClip )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
self - > AdjustFloorClip ( ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
//
// P_SpawnPlayer
// Called when a player is spawned on the level.
// Most of the player structure stays unchanged between levels.
//
EXTERN_CVAR ( Bool , chasedemo )
2016-10-23 10:06:59 +00:00
EXTERN_CVAR ( Bool , sv_singleplayerrespawn )
2017-02-28 10:23:40 +00:00
EXTERN_CVAR ( Float , fov )
2016-03-01 15:47:10 +00:00
extern bool demonew ;
2019-01-27 15:08:22 +00:00
AActor * FLevelLocals : : SpawnPlayer ( FPlayerStart * mthing , int playernum , int flags )
2016-03-01 15:47:10 +00:00
{
player_t * p ;
2019-01-03 21:05:49 +00:00
AActor * mobj , * oldactor ;
2017-03-08 14:20:00 +00:00
uint8_t state ;
2016-03-23 09:42:41 +00:00
DVector3 spawn ;
2016-03-16 11:41:26 +00:00
DAngle SpawnAngle ;
2016-03-01 15:47:10 +00:00
2016-03-09 03:42:24 +00:00
if ( mthing = = NULL )
{
return NULL ;
}
2016-03-01 15:47:10 +00:00
// not playing?
2019-02-01 16:02:10 +00:00
if ( ( unsigned ) playernum > = ( unsigned ) MAXPLAYERS | | ! PlayerInGame ( playernum ) )
2016-03-01 15:47:10 +00:00
return NULL ;
// Old lerp data needs to go
if ( playernum = = consoleplayer )
{
P_PredictionLerpReset ( ) ;
}
2019-02-01 16:02:10 +00:00
p = Players [ playernum ] ;
2016-03-01 15:47:10 +00:00
if ( p - > cls = = NULL )
{
// [GRB] Pick a class from player class list
if ( PlayerClasses . Size ( ) > 1 )
{
int type ;
if ( ! deathmatch | | ! multiplayer )
{
type = SinglePlayerClass [ playernum ] ;
}
else
{
type = p - > userinfo . GetPlayerClassNum ( ) ;
if ( type < 0 )
{
type = pr_multiclasschoice ( ) % PlayerClasses . Size ( ) ;
}
}
p - > CurrentPlayerClass = type ;
}
else
{
p - > CurrentPlayerClass = 0 ;
}
p - > cls = PlayerClasses [ p - > CurrentPlayerClass ] . Type ;
}
if ( ( dmflags2 & DF2_SAME_SPAWN_SPOT ) & &
( p - > playerstate = = PST_REBORN ) & &
( deathmatch = = false ) & &
( gameaction ! = ga_worlddone ) & &
( p - > mo ! = NULL ) & &
( ! ( p - > mo - > Sector - > Flags & SECF_NORESPAWN ) ) & &
( NULL ! = p - > attacker ) & & // don't respawn on damaging floors
( p - > mo - > Sector - > damageamount < TELEFRAG_DAMAGE ) ) // this really should be a bit smarter...
{
2016-03-23 09:42:41 +00:00
spawn = p - > mo - > Pos ( ) ;
2016-03-16 11:41:26 +00:00
SpawnAngle = p - > mo - > Angles . Yaw ;
2016-03-01 15:47:10 +00:00
}
else
{
2016-03-23 09:42:41 +00:00
spawn . X = mthing - > pos . X ;
spawn . Y = mthing - > pos . Y ;
2016-03-01 15:47:10 +00:00
2016-03-16 11:41:26 +00:00
// Allow full angular precision
2016-03-16 21:29:35 +00:00
SpawnAngle = ( double ) mthing - > angle ;
2016-03-01 15:47:10 +00:00
if ( i_compatflags2 & COMPATF2_BADANGLES )
{
2016-03-16 11:41:26 +00:00
SpawnAngle + = 0.01 ;
2016-03-01 15:47:10 +00:00
}
if ( GetDefaultByType ( p - > cls ) - > flags & MF_SPAWNCEILING )
2016-03-23 09:42:41 +00:00
spawn . Z = ONCEILINGZ ;
2016-03-01 15:47:10 +00:00
else if ( GetDefaultByType ( p - > cls ) - > flags2 & MF2_SPAWNFLOAT )
2016-03-23 09:42:41 +00:00
spawn . Z = FLOATRANDZ ;
2016-03-01 15:47:10 +00:00
else
2016-03-23 09:42:41 +00:00
spawn . Z = ONFLOORZ ;
2016-03-01 15:47:10 +00:00
}
2019-01-03 21:05:49 +00:00
mobj = Spawn ( p - > cls , spawn , NO_REPLACE ) ;
2016-03-01 15:47:10 +00:00
2019-01-27 15:08:22 +00:00
if ( flags & LEVEL_USEPLAYERSTARTZ )
2016-03-01 15:47:10 +00:00
{
2016-03-23 09:42:41 +00:00
if ( spawn . Z = = ONFLOORZ )
2016-03-21 23:06:58 +00:00
mobj - > AddZ ( mthing - > pos . Z ) ;
2016-03-23 09:42:41 +00:00
else if ( spawn . Z = = ONCEILINGZ )
2016-03-21 23:06:58 +00:00
mobj - > AddZ ( - mthing - > pos . Z ) ;
2016-03-01 15:47:10 +00:00
P_FindFloorCeiling ( mobj , FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT ) ;
}
mobj - > FriendPlayer = playernum + 1 ; // [RH] players are their own friends
oldactor = p - > mo ;
p - > mo = mobj ;
mobj - > player = p ;
state = p - > playerstate ;
if ( state = = PST_REBORN | | state = = PST_ENTER )
{
2019-01-28 01:44:05 +00:00
PlayerReborn ( playernum ) ;
2016-03-01 15:47:10 +00:00
}
else if ( oldactor ! = NULL & & oldactor - > player = = p & & ! ( flags & SPF_TEMPPLAYER ) )
{
// Move the voodoo doll's inventory to the new player.
2019-01-02 23:35:56 +00:00
IFVM ( Actor , ObtainInventory )
{
VMValue params [ ] = { mobj , oldactor } ;
VMCall ( func , params , 2 , nullptr , 0 ) ;
}
2019-01-27 15:08:22 +00:00
Behaviors . StopMyScripts ( oldactor ) ; // cancel all ENTER/RESPAWN scripts for the voodoo doll
2016-03-01 15:47:10 +00:00
}
// [GRB] Reset skin
2017-02-17 20:51:23 +00:00
p - > userinfo . SkinNumChanged ( R_FindSkin ( Skins [ p - > userinfo . GetSkin ( ) ] . Name , p - > CurrentPlayerClass ) ) ;
2016-03-01 15:47:10 +00:00
if ( ! ( mobj - > flags2 & MF2_DONTTRANSLATE ) )
{
// [RH] Be sure the player has the right translation
R_BuildPlayerTranslation ( playernum ) ;
// [RH] set color translations for player sprites
mobj - > Translation = TRANSLATION ( TRANSLATION_Players , playernum ) ;
}
2016-03-16 11:41:26 +00:00
mobj - > Angles . Yaw = SpawnAngle ;
2016-04-25 18:27:27 +00:00
mobj - > Angles . Pitch = mobj - > Angles . Roll = 0. ;
2016-03-01 15:47:10 +00:00
mobj - > health = p - > health ;
// [RH] Set player sprite based on skin
if ( ! ( mobj - > flags4 & MF4_NOSKIN ) )
{
2017-02-17 20:51:23 +00:00
mobj - > sprite = Skins [ p - > userinfo . GetSkin ( ) ] . sprite ;
2016-03-01 15:47:10 +00:00
}
2017-02-28 10:23:40 +00:00
p - > DesiredFOV = p - > FOV = fov ;
2016-03-01 15:47:10 +00:00
p - > camera = p - > mo ;
p - > playerstate = PST_LIVE ;
p - > refire = 0 ;
p - > damagecount = 0 ;
p - > bonuscount = 0 ;
p - > morphTics = 0 ;
p - > MorphedPlayerClass = 0 ;
p - > MorphStyle = 0 ;
p - > MorphExitFlash = NULL ;
p - > extralight = 0 ;
p - > fixedcolormap = NOFIXEDCOLORMAP ;
p - > fixedlightlevel = - 1 ;
2019-01-03 17:01:58 +00:00
p - > viewheight = p - > DefaultViewHeight ( ) ;
2016-03-01 15:47:10 +00:00
p - > inconsistant = 0 ;
2019-01-07 08:14:52 +00:00
p - > attacker = nullptr ;
2016-03-01 15:47:10 +00:00
p - > spreecount = 0 ;
p - > multicount = 0 ;
p - > lastkilltime = 0 ;
p - > BlendR = p - > BlendG = p - > BlendB = p - > BlendA = 0.f ;
p - > Uncrouch ( ) ;
2016-03-16 21:29:35 +00:00
p - > MinPitch = p - > MaxPitch = 0. ; // will be filled in by PostBeginPlay()/netcode
2019-01-07 08:14:52 +00:00
p - > MUSINFOactor = nullptr ;
2016-03-01 15:47:10 +00:00
p - > MUSINFOtics = - 1 ;
2016-03-19 23:54:18 +00:00
p - > Vel . Zero ( ) ; // killough 10/98: initialize bobbing to 0.
2016-03-01 15:47:10 +00:00
2019-01-03 21:05:49 +00:00
IFVIRTUALPTRNAME ( p - > mo , NAME_PlayerPawn , ResetAirSupply )
2019-01-03 12:04:48 +00:00
{
VMValue params [ ] = { p - > mo , false } ;
VMCall ( func , params , 2 , nullptr , 0 ) ;
}
2016-03-01 15:47:10 +00:00
for ( int ii = 0 ; ii < MAXPLAYERS ; + + ii )
{
2019-01-30 00:15:32 +00:00
if ( PlayerInGame ( ii ) & & Players [ ii ] - > camera = = oldactor )
2016-03-01 15:47:10 +00:00
{
2019-01-30 00:15:32 +00:00
Players [ ii ] - > camera = mobj ;
2016-03-01 15:47:10 +00:00
}
}
// [RH] Allow chasecam for demo watching
if ( ( demoplayback | | demonew ) & & chasedemo )
p - > cheats = CF_CHASECAM ;
// setup gun psprite
if ( ! ( flags & SPF_TEMPPLAYER ) )
{ // This can also start a script so don't do it for the dummy player.
P_SetupPsprites ( p , ! ! ( flags & SPF_WEAPONFULLYUP ) ) ;
}
if ( deathmatch )
{ // Give all cards in death match mode.
2019-01-03 21:05:49 +00:00
IFVIRTUALPTRNAME ( p - > mo , NAME_PlayerPawn , GiveDeathmatchInventory )
2019-01-03 12:58:53 +00:00
{
VMValue params [ 1 ] = { p - > mo } ;
VMCall ( func , params , 1 , nullptr , 0 ) ;
}
2016-03-01 15:47:10 +00:00
}
2019-01-27 15:08:22 +00:00
else if ( ( multiplayer | | ( flags2 & LEVEL2_ALLOWRESPAWN ) | | sv_singleplayerrespawn | |
2017-12-28 05:53:30 +00:00
! ! G_SkillProperty ( SKILLP_PlayerRespawn ) ) & & state = = PST_REBORN & & oldactor ! = NULL )
2016-03-01 15:47:10 +00:00
{ // Special inventory handling for respawning in coop
2018-11-24 19:58:33 +00:00
IFVM ( PlayerPawn , FilterCoopRespawnInventory )
{
VMValue params [ ] = { p - > mo , oldactor } ;
VMCall ( func , params , 2 , nullptr , 0 ) ;
}
2016-03-01 15:47:10 +00:00
}
if ( oldactor ! = NULL )
{ // Remove any inventory left from the old actor. Coop handles
// it above, but the other modes don't.
oldactor - > DestroyAllInventory ( ) ;
}
// [BC] Handle temporary invulnerability when respawned
2017-01-17 23:11:04 +00:00
if ( state = = PST_REBORN | | state = = PST_ENTER )
{
2019-01-03 21:05:49 +00:00
IFVIRTUALPTRNAME ( p - > mo , NAME_PlayerPawn , OnRespawn )
2017-01-17 23:11:04 +00:00
{
VMValue param = p - > mo ;
2017-04-12 23:12:04 +00:00
VMCall ( func , & param , 1 , nullptr , 0 ) ;
2017-01-17 23:11:04 +00:00
}
2016-03-01 15:47:10 +00:00
}
if ( StatusBar ! = NULL & & ( playernum = = consoleplayer | | StatusBar - > GetPlayer ( ) = = playernum ) )
{
2017-03-22 16:29:13 +00:00
StatusBar - > AttachToPlayer ( p ) ;
2016-03-01 15:47:10 +00:00
}
if ( multiplayer )
{
2016-06-06 08:48:40 +00:00
P_SpawnTeleportFog ( mobj , mobj - > Vec3Angle ( 20. , mobj - > Angles . Yaw , 0. ) , false , true ) ;
2016-03-01 15:47:10 +00:00
}
// "Fix" for one of the starts on exec.wad MAP01: If you start inside the ceiling,
// drop down below it, even if that means sinking into the floor.
2016-03-20 12:32:53 +00:00
if ( mobj - > Top ( ) > mobj - > ceilingz )
2016-03-01 15:47:10 +00:00
{
2016-03-20 19:55:06 +00:00
mobj - > SetZ ( mobj - > ceilingz - mobj - > Height , false ) ;
2016-03-01 15:47:10 +00:00
}
// [BC] Do script stuff
if ( ! ( flags & SPF_TEMPPLAYER ) )
{
if ( state = = PST_ENTER | | ( state = = PST_LIVE & & ! savegamerestore ) )
{
2019-01-27 15:08:22 +00:00
Behaviors . StartTypedScripts ( SCRIPT_Enter , p - > mo , true ) ;
2016-03-01 15:47:10 +00:00
}
else if ( state = = PST_REBORN )
{
assert ( oldactor ! = NULL ) ;
// before relocating all pointers to the player all sound targets
// pointing to the old actor have to be NULLed. Otherwise all
// monsters who last targeted this player will wake up immediately
// after the player has respawned.
AActor * th ;
2019-01-27 15:15:32 +00:00
auto it = GetThinkerIterator < AActor > ( ) ;
2016-03-01 15:47:10 +00:00
while ( ( th = it . Next ( ) ) )
{
2019-01-07 08:14:52 +00:00
if ( th - > LastHeard = = oldactor ) th - > LastHeard = nullptr ;
2016-03-01 15:47:10 +00:00
}
2019-01-27 15:08:22 +00:00
for ( auto & sec : sectors )
2016-03-01 15:47:10 +00:00
{
2017-01-07 18:32:24 +00:00
if ( sec . SoundTarget = = oldactor ) sec . SoundTarget = nullptr ;
2016-03-01 15:47:10 +00:00
}
DObject : : StaticPointerSubstitution ( oldactor , p - > mo ) ;
2018-11-19 17:13:23 +00:00
2017-03-03 17:53:11 +00:00
E_PlayerRespawned ( int ( p - players ) ) ;
2019-01-27 15:08:22 +00:00
Behaviors . StartTypedScripts ( SCRIPT_Respawn , p - > mo , true ) ;
2016-03-01 15:47:10 +00:00
}
}
return mobj ;
}
//
// P_SpawnMapThing
// The fields of the mapthing should
// already be in host byte order.
//
// [RH] position is used to weed out unwanted start spots
2019-01-27 15:08:22 +00:00
AActor * FLevelLocals : : SpawnMapThing ( FMapThing * mthing , int position )
2016-03-01 15:47:10 +00:00
{
PClassActor * i ;
int mask ;
AActor * mobj ;
if ( mthing - > EdNum = = 0 | | mthing - > EdNum = = - 1 )
return NULL ;
// find which type to spawn
FDoomEdEntry * mentry = mthing - > info ;
if ( mentry = = NULL )
{
// [RH] Don't die if the map tries to spawn an unknown thing
2016-03-23 11:21:52 +00:00
Printf ( " Unknown type %i at (%.1f, %.1f) \n " ,
mthing - > EdNum , mthing - > pos . X , mthing - > pos . Y ) ;
2016-03-01 15:47:10 +00:00
mentry = DoomEdMap . CheckKey ( 0 ) ;
if ( mentry = = NULL ) // we need a valid entry for the rest of this function so if we can't find a default, let's exit right away.
{
return NULL ;
}
}
if ( mentry - > Type = = NULL & & mentry - > Special < = 0 )
{
// has been explicitly set to not spawning anything.
return NULL ;
}
// copy args to mapthing so that we have them in one place for the rest of this function
if ( mentry - > ArgsDefined > 0 )
{
if ( mentry - > Type ! = NULL ) mthing - > special = mentry - > Special ;
memcpy ( mthing - > args , mentry - > Args , sizeof ( mthing - > args [ 0 ] ) * mentry - > ArgsDefined ) ;
}
int pnum = - 1 ;
if ( mentry - > Type = = NULL )
{
switch ( mentry - > Special )
{
case SMT_DeathmatchStart :
{
// count deathmatch start positions
FPlayerStart start ( mthing , 0 ) ;
2019-01-27 15:08:22 +00:00
deathmatchstarts . Push ( start ) ;
2016-03-01 15:47:10 +00:00
return NULL ;
}
case SMT_PolyAnchor :
case SMT_PolySpawn :
case SMT_PolySpawnCrush :
case SMT_PolySpawnHurt :
2018-12-28 09:08:39 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
case SMT_Player1Start :
case SMT_Player2Start :
case SMT_Player3Start :
case SMT_Player4Start :
case SMT_Player5Start :
case SMT_Player6Start :
case SMT_Player7Start :
case SMT_Player8Start :
pnum = mentry - > Special - SMT_Player1Start ;
break ;
// Sound sequence override will be handled later
default :
break ;
}
}
2019-01-27 15:08:22 +00:00
if ( pnum = = - 1 | | ( flags & LEVEL_FILTERSTARTS ) )
2016-03-01 15:47:10 +00:00
{
// check for appropriate game type
if ( deathmatch )
{
mask = MTF_DEATHMATCH ;
}
else if ( multiplayer )
{
mask = MTF_COOPERATIVE ;
}
else
{
mask = MTF_SINGLE ;
}
if ( ! ( mthing - > flags & mask ) )
{
return NULL ;
}
mask = G_SkillProperty ( SKILLP_SpawnFilter ) ;
2017-05-01 18:44:41 +00:00
if ( ! ( mthing - > SkillFilter & mask ) & & ! mentry - > NoSkillFlags )
2016-03-01 15:47:10 +00:00
{
return NULL ;
}
// Check class spawn masks. Now with player classes available
// this is enabled for all games.
if ( ! multiplayer )
{ // Single player
2019-02-01 16:02:10 +00:00
auto p = GetConsolePlayer ( ) ;
if ( p )
{
int spawnmask = p - > GetSpawnClass ( ) ;
if ( spawnmask ! = 0 & & ( mthing - > ClassFilter & spawnmask ) = = 0 )
{ // Not for current class
return nullptr ;
}
2016-03-01 15:47:10 +00:00
}
}
else if ( ! deathmatch )
{ // Cooperative
mask = 0 ;
for ( int i = 0 ; i < MAXPLAYERS ; i + + )
{
2019-02-01 16:02:10 +00:00
if ( PlayerInGame ( i ) )
2016-03-01 15:47:10 +00:00
{
2019-02-01 16:02:10 +00:00
int spawnmask = Players [ i ] - > GetSpawnClass ( ) ;
2016-03-01 15:47:10 +00:00
if ( spawnmask ! = 0 )
mask | = spawnmask ;
else
mask = - 1 ;
}
}
if ( mask ! = - 1 & & ( mthing - > ClassFilter & mask ) = = 0 )
{
return NULL ;
}
}
}
if ( pnum ! = - 1 )
{
// [RH] Only spawn spots that match position.
if ( mthing - > args [ 0 ] ! = position )
return NULL ;
// save spots for respawning in network games
FPlayerStart start ( mthing , pnum + 1 ) ;
2019-01-27 15:08:22 +00:00
playerstarts [ pnum ] = start ;
if ( flags2 & LEVEL2_RANDOMPLAYERSTARTS )
2016-03-07 16:53:51 +00:00
{ // When using random player starts, all starts count
2019-01-27 15:08:22 +00:00
AllPlayerStarts . Push ( start ) ;
2016-03-07 16:53:51 +00:00
}
else
{ // When not using random player starts, later single player
// starts should override earlier ones, since the earlier
// ones are for voodoo dolls and not likely to be ideal for
// spawning regular players.
unsigned i ;
2019-01-27 15:08:22 +00:00
for ( i = 0 ; i < AllPlayerStarts . Size ( ) ; + + i )
2016-03-07 16:53:51 +00:00
{
2019-01-27 15:08:22 +00:00
if ( AllPlayerStarts [ i ] . type = = pnum + 1 )
2016-03-07 16:53:51 +00:00
{
2019-01-27 15:08:22 +00:00
AllPlayerStarts [ i ] = start ;
2016-03-07 16:53:51 +00:00
break ;
}
}
2019-01-27 15:08:22 +00:00
if ( i = = AllPlayerStarts . Size ( ) )
2016-03-07 16:53:51 +00:00
{
2019-01-27 15:08:22 +00:00
AllPlayerStarts . Push ( start ) ;
2016-03-07 16:53:51 +00:00
}
}
2019-01-27 15:08:22 +00:00
if ( ! deathmatch & & ! ( flags2 & LEVEL2_RANDOMPLAYERSTARTS ) )
2016-03-01 15:47:10 +00:00
{
2019-01-28 02:02:25 +00:00
return SpawnPlayer ( & start , pnum , ( flags2 & LEVEL2_PRERAISEWEAPON ) ? SPF_WEAPONFULLYUP : 0 ) ;
2016-03-01 15:47:10 +00:00
}
return NULL ;
}
// [RH] sound sequence overriders
if ( mentry - > Type = = NULL & & mentry - > Special = = SMT_SSeqOverride )
{
int type = mthing - > args [ 0 ] ;
if ( type = = 255 ) type = - 1 ;
if ( type > 63 )
{
Printf ( " Sound sequence %d out of range \n " , type ) ;
}
else
{
2019-01-29 00:30:41 +00:00
PointInSector ( mthing - > pos ) - > seqType = type ;
2016-03-01 15:47:10 +00:00
}
return NULL ;
}
// [RH] If the thing's corresponding sprite has no frames, also map
// it to the unknown thing.
// Handle decorate replacements explicitly here
// to check for missing frames in the replacement object.
i = mentry - > Type - > GetReplacement ( ) ;
const AActor * defaults = GetDefaultByType ( i ) ;
if ( defaults - > SpawnState = = NULL | |
sprites [ defaults - > SpawnState - > sprite ] . numframes = = 0 )
{
// We don't load mods for shareware games so we'll just ignore
// missing actors. Heretic needs this since the shareware includes
// the retail weapons in Deathmatch.
if ( gameinfo . flags & GI_SHAREWARE )
return NULL ;
2016-03-23 11:21:52 +00:00
Printf ( " %s at (%.1f, %.1f) has no frames \n " ,
i - > TypeName . GetChars ( ) , mthing - > pos . X , mthing - > pos . Y ) ;
2016-03-01 15:47:10 +00:00
i = PClass : : FindActor ( " Unknown " ) ;
2017-04-12 08:20:58 +00:00
assert ( i - > IsDescendantOf ( RUNTIME_CLASS ( AActor ) ) ) ;
2016-03-01 15:47:10 +00:00
}
const AActor * info = GetDefaultByType ( i ) ;
// don't spawn keycards and players in deathmatch
if ( deathmatch & & info - > flags & MF_NOTDMATCH )
return NULL ;
// [RH] don't spawn extra weapons in coop if so desired
if ( multiplayer & & ! deathmatch & & ( dmflags & DF_NO_COOP_WEAPON_SPAWN ) )
{
if ( GetDefaultByType ( i ) - > flags7 & MF7_WEAPONSPAWN )
{
if ( ( mthing - > flags & ( MTF_DEATHMATCH | MTF_SINGLE ) ) = = MTF_DEATHMATCH )
return NULL ;
}
}
// don't spawn any monsters if -nomonsters
2019-01-27 15:08:22 +00:00
if ( ( ( flags2 & LEVEL2_NOMONSTERS ) | | ( dmflags & DF_NO_MONSTERS ) ) & & info - > flags3 & MF3_ISMONSTER )
2016-03-01 15:47:10 +00:00
{
return NULL ;
}
2018-12-02 17:04:44 +00:00
auto it = GetDefaultByType ( i ) ;
2017-02-28 20:45:47 +00:00
2018-12-02 17:04:44 +00:00
IFVIRTUALPTR ( it , AActor , ShouldSpawn )
{
int ret ;
VMValue param = it ;
VMReturn rett ( & ret ) ;
VMCall ( func , & param , 1 , & rett , 1 ) ;
if ( ! ret ) return nullptr ;
2016-03-01 15:47:10 +00:00
}
// spawn it
2016-03-23 11:21:52 +00:00
double sz ;
2016-03-01 15:47:10 +00:00
if ( info - > flags & MF_SPAWNCEILING )
2016-03-23 11:21:52 +00:00
sz = ONCEILINGZ ;
2016-03-01 15:47:10 +00:00
else if ( info - > flags2 & MF2_SPAWNFLOAT )
2016-03-23 11:21:52 +00:00
sz = FLOATRANDZ ;
2016-03-01 15:47:10 +00:00
else
2016-03-23 11:21:52 +00:00
sz = ONFLOORZ ;
2016-03-01 15:47:10 +00:00
2016-03-23 11:21:52 +00:00
mobj = AActor : : StaticSpawn ( i , DVector3 ( mthing - > pos , sz ) , NO_REPLACE , true ) ;
2016-03-01 15:47:10 +00:00
2016-03-23 11:21:52 +00:00
if ( sz = = ONFLOORZ )
2016-03-01 15:47:10 +00:00
{
2016-03-23 11:21:52 +00:00
mobj - > AddZ ( mthing - > pos . Z ) ;
2019-01-29 18:28:22 +00:00
if ( ( mobj - > flags2 & MF2_FLOATBOB ) & & ( mobj - > Level - > ib_compatflags & BCOMPATF_FLOATBOB ) )
2016-03-01 15:47:10 +00:00
{
2016-03-23 11:21:52 +00:00
mobj - > specialf1 = mthing - > pos . Z ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-23 11:21:52 +00:00
else if ( sz = = ONCEILINGZ )
mobj - > AddZ ( - mthing - > pos . Z ) ;
2016-03-01 15:47:10 +00:00
2016-03-23 11:21:52 +00:00
mobj - > SpawnPoint = mthing - > pos ;
2016-03-01 15:47:10 +00:00
mobj - > SpawnAngle = mthing - > angle ;
mobj - > SpawnFlags = mthing - > flags ;
2018-01-09 20:48:19 +00:00
if ( mthing - > friendlyseeblocks > 0 )
mobj - > friendlyseeblocks = mthing - > friendlyseeblocks ;
2016-03-01 15:47:10 +00:00
if ( mthing - > FloatbobPhase > = 0 & & mthing - > FloatbobPhase < 64 ) mobj - > FloatBobPhase = mthing - > FloatbobPhase ;
2016-03-20 23:51:19 +00:00
if ( mthing - > Gravity < 0 ) mobj - > Gravity = - mthing - > Gravity ;
else if ( mthing - > Gravity > 0 ) mobj - > Gravity * = mthing - > Gravity ;
2016-11-23 13:32:18 +00:00
else
{
mobj - > flags | = MF_NOGRAVITY ;
mobj - > Gravity = 0 ;
}
2016-03-01 15:47:10 +00:00
// For Hexen floatbob 'compatibility' we do not really want to alter the floorz.
2019-01-29 18:28:22 +00:00
if ( mobj - > specialf1 = = 0 | | ! ( mobj - > flags2 & MF2_FLOATBOB ) | | ! ( mobj - > Level - > ib_compatflags & BCOMPATF_FLOATBOB ) )
2016-03-01 15:47:10 +00:00
{
P_FindFloorCeiling ( mobj , FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT ) ;
}
// if the actor got args defined either in DECORATE or MAPINFO we must ignore the map's properties.
if ( ! ( mobj - > flags2 & MF2_ARGSDEFINED ) )
{
// [RH] Set the thing's special
mobj - > special = mthing - > special ;
for ( int j = 0 ; j < 5 ; j + + ) mobj - > args [ j ] = mthing - > args [ j ] ;
}
// [RH] Add ThingID to mobj and link it in with the others
mobj - > tid = mthing - > thingid ;
mobj - > AddToHash ( ) ;
2016-03-16 21:29:35 +00:00
mobj - > PrevAngles . Yaw = mobj - > Angles . Yaw = ( double ) mthing - > angle ;
2016-03-01 15:47:10 +00:00
// Check if this actor's mapthing has a conversation defined
if ( mthing - > Conversation > 0 )
{
// Make sure that this does not partially overwrite the default dialogue settings.
2019-01-27 15:08:22 +00:00
int root = GetConversation ( mthing - > Conversation ) ;
2016-03-01 15:47:10 +00:00
if ( root ! = - 1 )
{
mobj - > ConversationRoot = root ;
2019-01-27 15:08:22 +00:00
mobj - > Conversation = StrifeDialogues [ mobj - > ConversationRoot ] ;
2016-03-01 15:47:10 +00:00
}
}
// Set various UDMF options
2016-03-21 11:18:46 +00:00
if ( mthing - > Alpha > = 0 )
mobj - > Alpha = mthing - > Alpha ;
2016-03-01 15:47:10 +00:00
if ( mthing - > RenderStyle ! = STYLE_Count )
mobj - > RenderStyle = ( ERenderStyle ) mthing - > RenderStyle ;
2016-03-20 11:13:00 +00:00
if ( mthing - > Scale . X ! = 0 )
mobj - > Scale . X = mthing - > Scale . X * mobj - > Scale . X ;
if ( mthing - > Scale . Y ! = 0 )
2016-04-08 14:52:42 +00:00
mobj - > Scale . Y = mthing - > Scale . Y * mobj - > Scale . Y ;
2016-03-01 15:47:10 +00:00
if ( mthing - > pitch )
2016-03-16 21:29:35 +00:00
mobj - > Angles . Pitch = ( double ) mthing - > pitch ;
2016-03-01 15:47:10 +00:00
if ( mthing - > roll )
2016-04-25 18:27:27 +00:00
mobj - > Angles . Roll = ( double ) mthing - > roll ;
2016-03-01 15:47:10 +00:00
if ( mthing - > score )
mobj - > Score = mthing - > score ;
if ( mthing - > fillcolor )
2017-06-04 18:42:03 +00:00
mobj - > fillcolor = ( mthing - > fillcolor & 0xffffff ) | ( ColorMatcher . Pick ( ( mthing - > fillcolor & 0xff0000 ) > > 16 ,
( mthing - > fillcolor & 0xff00 ) > > 8 , ( mthing - > fillcolor & 0xff ) ) < < 24 ) ;
2016-03-01 15:47:10 +00:00
2018-01-20 21:41:28 +00:00
// allow color strings for lights and reshuffle the args for spot lights
2019-01-01 18:35:55 +00:00
if ( i - > IsDescendantOf ( NAME_DynamicLight ) )
2018-01-04 22:41:57 +00:00
{
2018-01-21 08:10:04 +00:00
if ( mthing - > arg0str ! = NAME_None )
2018-01-20 21:41:28 +00:00
{
PalEntry color = V_GetColor ( nullptr , mthing - > arg0str ) ;
2019-01-01 18:35:55 +00:00
mobj - > args [ 0 ] = color . r ;
mobj - > args [ 1 ] = color . g ;
mobj - > args [ 2 ] = color . b ;
2018-01-20 21:41:28 +00:00
}
2019-01-01 18:35:55 +00:00
else if ( mobj - > IntVar ( NAME_lightflags ) & LF_SPOT )
2018-01-20 21:41:28 +00:00
{
2019-01-01 18:35:55 +00:00
mobj - > args [ 0 ] = RPART ( mthing - > args [ 0 ] ) ;
mobj - > args [ 1 ] = GPART ( mthing - > args [ 0 ] ) ;
mobj - > args [ 2 ] = BPART ( mthing - > args [ 0 ] ) ;
2018-01-20 21:41:28 +00:00
}
2019-01-01 18:35:55 +00:00
if ( mobj - > IntVar ( NAME_lightflags ) & LF_SPOT )
2018-01-20 21:41:28 +00:00
{
2019-01-01 18:35:55 +00:00
mobj - > AngleVar ( NAME_SpotInnerAngle ) = double ( mthing - > args [ 1 ] ) ;
mobj - > AngleVar ( NAME_SpotOuterAngle ) = double ( mthing - > args [ 2 ] ) ;
2018-01-20 21:41:28 +00:00
}
2018-01-04 22:41:57 +00:00
}
2016-11-24 20:36:02 +00:00
mobj - > CallBeginPlay ( ) ;
2016-03-01 15:47:10 +00:00
if ( ! ( mobj - > ObjectFlags & OF_EuthanizeMe ) )
{
mobj - > LevelSpawned ( ) ;
}
2017-02-27 23:59:09 +00:00
if ( mthing - > Health > 0 )
mobj - > health = int ( mobj - > health * mthing - > Health ) ;
2016-03-01 15:47:10 +00:00
else
2017-02-27 23:59:09 +00:00
mobj - > health = - int ( mthing - > Health ) ;
if ( mthing - > Health = = 0 )
2016-11-26 00:14:47 +00:00
mobj - > CallDie ( NULL , NULL ) ;
2017-02-27 23:59:09 +00:00
else if ( mthing - > Health ! = 1 )
2016-03-01 15:47:10 +00:00
mobj - > StartHealth = mobj - > health ;
return mobj ;
}
2018-12-27 08:44:49 +00:00
//===========================================================================
//
// SpawnMapThing
//
//===========================================================================
CVAR ( Bool , dumpspawnedthings , false , 0 )
2019-01-27 15:08:22 +00:00
AActor * FLevelLocals : : SpawnMapThing ( int index , FMapThing * mt , int position )
2018-12-27 08:44:49 +00:00
{
2019-01-27 15:08:22 +00:00
AActor * spawned = SpawnMapThing ( mt , position ) ;
2018-12-27 08:44:49 +00:00
if ( dumpspawnedthings )
{
Printf ( " %5d: (%5f, %5f, %5f), doomednum = %5d, flags = %04x, type = %s \n " ,
index , mt - > pos . X , mt - > pos . Y , mt - > pos . Z , mt - > EdNum , mt - > flags ,
spawned ? spawned - > GetClass ( ) - > TypeName . GetChars ( ) : " (none) " ) ;
}
2019-01-27 15:08:22 +00:00
T_AddSpawnedThing ( this , spawned ) ;
2018-12-27 08:44:49 +00:00
return spawned ;
}
2016-03-01 15:47:10 +00:00
//
// GAME SPAWN FUNCTIONS
//
//
// P_SpawnPuff
//
2016-04-10 08:57:48 +00:00
AActor * P_SpawnPuff ( AActor * source , PClassActor * pufftype , const DVector3 & pos1 , DAngle hitdir , DAngle particledir , int updown , int flags , AActor * vict )
2016-03-01 15:47:10 +00:00
{
AActor * puff ;
2016-04-10 08:57:48 +00:00
DVector3 pos = pos1 ;
2016-03-01 15:47:10 +00:00
2016-12-02 11:06:49 +00:00
if ( pufftype = = nullptr ) return nullptr ;
2016-04-10 08:57:48 +00:00
if ( ! ( flags & PF_NORANDOMZ ) ) pos . Z + = pr_spawnpuff . Random2 ( ) / 64. ;
puff = Spawn ( pufftype , pos , ALLOW_REPLACE ) ;
2016-03-01 15:47:10 +00:00
if ( puff = = NULL ) return NULL ;
if ( ( puff - > flags4 & MF4_RANDOMIZE ) & & puff - > tics > 0 )
{
puff - > tics - = pr_spawnpuff ( ) & 3 ;
if ( puff - > tics < 1 )
puff - > tics = 1 ;
}
//Moved puff creation and target/master/tracer setting to here.
if ( puff & & vict )
{
if ( puff - > flags7 & MF7_HITTARGET ) puff - > target = vict ;
if ( puff - > flags7 & MF7_HITMASTER ) puff - > master = vict ;
if ( puff - > flags7 & MF7_HITTRACER ) puff - > tracer = vict ;
}
// [BB] If the puff came from a player, set the target of the puff to this player.
if ( puff & & ( puff - > flags5 & MF5_PUFFGETSOWNER ) )
puff - > target = source ;
2016-03-06 20:58:36 +00:00
// Angle is the opposite of the hit direction (i.e. the puff faces the source.)
2016-03-23 12:31:12 +00:00
puff - > Angles . Yaw = hitdir + 180 ;
2016-03-01 15:47:10 +00:00
// If a puff has a crash state and an actor was not hit,
// it will enter the crash state. This is used by the StrifeSpark
// and BlasterPuff.
FState * crashstate ;
2018-11-02 05:14:02 +00:00
if ( ( flags & PF_HITSKY ) & & ( crashstate = puff - > FindState ( NAME_Death , NAME_Sky , true ) ) ! = NULL )
{
puff - > SetState ( crashstate ) ;
}
else if ( ! ( flags & PF_HITTHING ) & & ( crashstate = puff - > FindState ( NAME_Crash ) ) ! = NULL )
2016-03-01 15:47:10 +00:00
{
puff - > SetState ( crashstate ) ;
}
else if ( ( flags & PF_HITTHINGBLEED ) & & ( crashstate = puff - > FindState ( NAME_Death , NAME_Extreme , true ) ) ! = NULL )
{
puff - > SetState ( crashstate ) ;
}
else if ( ( flags & PF_MELEERANGE ) & & puff - > MeleeState ! = NULL )
{
// handle the hard coded state jump of Doom's bullet puff
// in a more flexible manner.
puff - > SetState ( puff - > MeleeState ) ;
}
if ( ! ( flags & PF_TEMPORARY ) )
{
if ( cl_pufftype & & updown ! = 3 & & ( puff - > flags4 & MF4_ALLOWPARTICLES ) )
{
2019-01-29 00:09:02 +00:00
P_DrawSplash2 ( source - > Level , 32 , pos , particledir , updown , 1 ) ;
2017-02-27 18:51:37 +00:00
if ( cl_pufftype = = 1 ) puff - > renderflags | = RF_INVISIBLE ;
2016-03-01 15:47:10 +00:00
}
if ( ( flags & PF_HITTHING ) & & puff - > SeeSound )
{ // Hit thing sound
S_Sound ( puff , CHAN_BODY , puff - > SeeSound , 1 , ATTN_NORM ) ;
}
else if ( puff - > AttackSound )
{
S_Sound ( puff , CHAN_BODY , puff - > AttackSound , 1 , ATTN_NORM ) ;
}
}
return puff ;
}
2016-11-07 22:16:25 +00:00
DEFINE_ACTION_FUNCTION ( AActor , SpawnPuff )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_CLASS ( pufftype , AActor ) ;
PARAM_FLOAT ( x ) ;
PARAM_FLOAT ( y ) ;
PARAM_FLOAT ( z ) ;
PARAM_ANGLE ( hitdir ) ;
PARAM_ANGLE ( particledir ) ;
PARAM_INT ( updown ) ;
2018-11-17 09:03:40 +00:00
PARAM_INT ( flags ) ;
PARAM_OBJECT ( victim , AActor ) ;
2016-11-07 22:16:25 +00:00
ACTION_RETURN_OBJECT ( P_SpawnPuff ( self , pufftype , DVector3 ( x , y , z ) , hitdir , particledir , updown , flags , victim ) ) ;
}
2016-03-01 15:47:10 +00:00
//---------------------------------------------------------------------------
//
// P_SpawnBlood
//
//---------------------------------------------------------------------------
2016-04-07 12:14:44 +00:00
void P_SpawnBlood ( const DVector3 & pos1 , DAngle dir , int damage , AActor * originator )
2016-03-01 15:47:10 +00:00
{
AActor * th ;
PClassActor * bloodcls = originator - > GetBloodType ( ) ;
2016-04-07 12:14:44 +00:00
DVector3 pos = pos1 ;
pos . Z + = pr_spawnblood . Random2 ( ) / 64. ;
2016-03-01 15:47:10 +00:00
int bloodtype = cl_bloodtype ;
if ( bloodcls ! = NULL & & ! ( GetDefaultByType ( bloodcls ) - > flags4 & MF4_ALLOWPARTICLES ) )
bloodtype = 0 ;
if ( bloodcls ! = NULL )
{
2016-04-07 12:14:44 +00:00
th = Spawn ( bloodcls , pos , NO_REPLACE ) ; // GetBloodType already performed the replacement
2016-03-19 23:54:18 +00:00
th - > Vel . Z = 2 ;
2016-03-28 14:22:21 +00:00
th - > Angles . Yaw = dir ;
2016-03-01 15:47:10 +00:00
// [NG] Applying PUFFGETSOWNER to the blood will make it target the owner
if ( th - > flags5 & MF5_PUFFGETSOWNER ) th - > target = originator ;
if ( gameinfo . gametype & GAME_DoomChex )
{
th - > tics - = pr_spawnblood ( ) & 3 ;
if ( th - > tics < 1 )
th - > tics = 1 ;
}
// colorize the blood
2017-03-02 09:26:23 +00:00
if ( ! ( th - > flags2 & MF2_DONTTRANSLATE ) )
2016-03-01 15:47:10 +00:00
{
2017-03-02 09:26:23 +00:00
th - > Translation = originator - > BloodTranslation ;
2016-03-01 15:47:10 +00:00
}
// Moved out of the blood actor so that replacing blood is easier
if ( gameinfo . gametype & GAME_DoomStrifeChex )
{
if ( gameinfo . gametype = = GAME_Strife )
{
if ( damage > 13 )
{
FState * state = th - > FindState ( NAME_Spray ) ;
if ( state ! = NULL )
{
th - > SetState ( state ) ;
goto statedone ;
}
}
else damage + = 2 ;
}
int advance = 0 ;
if ( damage < = 12 & & damage > = 9 )
{
advance = 1 ;
}
else if ( damage < 9 )
{
advance = 2 ;
}
PClassActor * cls = th - > GetClass ( ) ;
while ( cls ! = RUNTIME_CLASS ( AActor ) )
{
int checked_advance = advance ;
if ( cls - > OwnsState ( th - > SpawnState ) )
{
for ( ; checked_advance > 0 ; - - checked_advance )
{
// [RH] Do not set to a state we do not own.
if ( cls - > OwnsState ( th - > SpawnState + checked_advance ) )
{
th - > SetState ( th - > SpawnState + checked_advance ) ;
goto statedone ;
}
}
}
// We can safely assume the ParentClass is of type PClassActor
// since we stop when we see the Actor base class.
cls = static_cast < PClassActor * > ( cls - > ParentClass ) ;
}
}
statedone :
if ( ! ( bloodtype < = 1 ) ) th - > renderflags | = RF_INVISIBLE ;
}
if ( bloodtype > = 1 )
2019-01-29 00:09:02 +00:00
P_DrawSplash2 ( originator - > Level , 40 , pos , dir , 2 , originator - > BloodColor ) ;
2016-03-01 15:47:10 +00:00
}
2016-11-30 00:25:51 +00:00
DEFINE_ACTION_FUNCTION ( AActor , SpawnBlood )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_FLOAT ( x ) ;
PARAM_FLOAT ( y ) ;
PARAM_FLOAT ( z ) ;
PARAM_ANGLE ( dir ) ;
PARAM_INT ( damage ) ;
P_SpawnBlood ( DVector3 ( x , y , z ) , dir , damage , self ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
//---------------------------------------------------------------------------
//
// PROC P_BloodSplatter
//
//---------------------------------------------------------------------------
2016-03-23 12:31:12 +00:00
void P_BloodSplatter ( const DVector3 & pos , AActor * originator , DAngle hitangle )
2016-03-01 15:47:10 +00:00
{
PClassActor * bloodcls = originator - > GetBloodType ( 1 ) ;
int bloodtype = cl_bloodtype ;
if ( bloodcls ! = NULL & & ! ( GetDefaultByType ( bloodcls ) - > flags4 & MF4_ALLOWPARTICLES ) )
bloodtype = 0 ;
if ( bloodcls ! = NULL )
{
AActor * mo ;
2016-03-06 20:58:36 +00:00
mo = Spawn ( bloodcls , pos , NO_REPLACE ) ; // GetBloodType already performed the replacement
2016-03-01 15:47:10 +00:00
mo - > target = originator ;
2016-03-19 23:54:18 +00:00
mo - > Vel . X = pr_splatter . Random2 ( ) / 64. ;
mo - > Vel . Y = pr_splatter . Random2 ( ) / 64. ;
mo - > Vel . Z = 3 ;
2016-03-01 15:47:10 +00:00
// colorize the blood!
2017-03-02 09:26:23 +00:00
if ( ! ( mo - > flags2 & MF2_DONTTRANSLATE ) )
2016-03-01 15:47:10 +00:00
{
2017-03-02 09:26:23 +00:00
mo - > Translation = originator - > BloodTranslation ;
2016-03-01 15:47:10 +00:00
}
if ( ! ( bloodtype < = 1 ) ) mo - > renderflags | = RF_INVISIBLE ;
}
if ( bloodtype > = 1 )
{
2019-01-29 00:09:02 +00:00
P_DrawSplash2 ( originator - > Level , 40 , pos , hitangle - 180. , 2 , originator - > BloodColor ) ;
2016-03-01 15:47:10 +00:00
}
}
//===========================================================================
//
// P_BloodSplatter2
//
//===========================================================================
2016-03-23 12:31:12 +00:00
void P_BloodSplatter2 ( const DVector3 & pos , AActor * originator , DAngle hitangle )
2016-03-01 15:47:10 +00:00
{
PClassActor * bloodcls = originator - > GetBloodType ( 2 ) ;
int bloodtype = cl_bloodtype ;
if ( bloodcls ! = NULL & & ! ( GetDefaultByType ( bloodcls ) - > flags4 & MF4_ALLOWPARTICLES ) )
bloodtype = 0 ;
2016-04-10 08:57:48 +00:00
DVector2 add ;
add . X = ( pr_splat ( ) - 128 ) / 32. ;
add . Y = ( pr_splat ( ) - 128 ) / 32. ;
2016-03-01 15:47:10 +00:00
if ( bloodcls ! = NULL )
{
AActor * mo ;
2016-03-23 12:31:12 +00:00
mo = Spawn ( bloodcls , pos + add , NO_REPLACE ) ; // GetBloodType already performed the replacement
2016-03-01 15:47:10 +00:00
mo - > target = originator ;
// colorize the blood!
2017-03-02 09:26:23 +00:00
if ( ! ( mo - > flags2 & MF2_DONTTRANSLATE ) )
2016-03-01 15:47:10 +00:00
{
2017-03-02 09:26:23 +00:00
mo - > Translation = originator - > BloodTranslation ;
2016-03-01 15:47:10 +00:00
}
if ( ! ( bloodtype < = 1 ) ) mo - > renderflags | = RF_INVISIBLE ;
}
if ( bloodtype > = 1 )
{
2019-01-29 00:09:02 +00:00
P_DrawSplash2 ( originator - > Level , 40 , pos + add , hitangle - 180. , 2 , originator - > BloodColor ) ;
2016-03-01 15:47:10 +00:00
}
}
2016-11-30 00:25:51 +00:00
DEFINE_ACTION_FUNCTION ( AActor , BloodSplatter )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_FLOAT ( x ) ;
PARAM_FLOAT ( y ) ;
PARAM_FLOAT ( z ) ;
PARAM_ANGLE ( dir ) ;
2018-11-17 09:03:40 +00:00
PARAM_BOOL ( axe ) ;
2016-11-30 00:25:51 +00:00
if ( axe ) P_BloodSplatter2 ( DVector3 ( x , y , z ) , self , dir ) ;
else P_BloodSplatter ( DVector3 ( x , y , z ) , self , dir ) ;
return 0 ;
}
2016-03-01 15:47:10 +00:00
//---------------------------------------------------------------------------
//
// PROC P_RipperBlood
//
//---------------------------------------------------------------------------
void P_RipperBlood ( AActor * mo , AActor * bleeder )
{
PClassActor * bloodcls = bleeder - > GetBloodType ( ) ;
2016-03-23 09:42:41 +00:00
double xo = pr_ripperblood . Random2 ( ) / 16. ;
double yo = pr_ripperblood . Random2 ( ) / 16. ;
double zo = pr_ripperblood . Random2 ( ) / 16. ;
DVector3 pos = mo - > Vec3Offset ( xo , yo , zo ) ;
2016-03-01 15:47:10 +00:00
int bloodtype = cl_bloodtype ;
if ( bloodcls ! = NULL & & ! ( GetDefaultByType ( bloodcls ) - > flags4 & MF4_ALLOWPARTICLES ) )
bloodtype = 0 ;
if ( bloodcls ! = NULL )
{
AActor * th ;
th = Spawn ( bloodcls , pos , NO_REPLACE ) ; // GetBloodType already performed the replacement
// [NG] Applying PUFFGETSOWNER to the blood will make it target the owner
if ( th - > flags5 & MF5_PUFFGETSOWNER ) th - > target = bleeder ;
if ( gameinfo . gametype = = GAME_Heretic )
th - > flags | = MF_NOGRAVITY ;
2016-03-19 23:54:18 +00:00
th - > Vel . X = mo - > Vel . X / 2 ;
th - > Vel . Y = mo - > Vel . Y / 2 ;
2016-03-01 15:47:10 +00:00
th - > tics + = pr_ripperblood ( ) & 3 ;
// colorize the blood!
2017-03-02 09:26:23 +00:00
if ( ! ( th - > flags2 & MF2_DONTTRANSLATE ) )
2016-03-01 15:47:10 +00:00
{
2017-03-02 09:26:23 +00:00
th - > Translation = bleeder - > BloodTranslation ;
2016-03-01 15:47:10 +00:00
}
if ( ! ( bloodtype < = 1 ) ) th - > renderflags | = RF_INVISIBLE ;
}
if ( bloodtype > = 1 )
{
2019-01-29 00:09:02 +00:00
P_DrawSplash2 ( bleeder - > Level , 28 , pos , bleeder - > AngleTo ( mo ) + 180. , 0 , bleeder - > BloodColor ) ;
2016-03-01 15:47:10 +00:00
}
}
//---------------------------------------------------------------------------
//
// FUNC P_GetThingFloorType
//
//---------------------------------------------------------------------------
int P_GetThingFloorType ( AActor * thing )
{
if ( thing - > floorterrain > = 0 )
{
return thing - > floorterrain ;
}
else
{
return thing - > Sector - > GetTerrain ( sector_t : : floor ) ;
}
}
2016-11-27 23:49:10 +00:00
2016-03-01 15:47:10 +00:00
//---------------------------------------------------------------------------
//
// FUNC P_HitWater
//
// Returns true if hit liquid and splashed, false if not.
//---------------------------------------------------------------------------
2016-03-23 12:31:12 +00:00
bool P_HitWater ( AActor * thing , sector_t * sec , const DVector3 & pos , bool checkabove , bool alert , bool force )
2016-03-01 15:47:10 +00:00
{
if ( thing - > player & & ( thing - > player - > cheats & CF_PREDICTING ) )
return false ;
AActor * mo = NULL ;
FSplashDef * splash ;
int terrainnum ;
sector_t * hsec = NULL ;
// don't splash above the object
if ( checkabove )
{
2016-03-23 12:31:12 +00:00
double compare_z = thing - > Center ( ) ;
2016-03-01 15:47:10 +00:00
// Missiles are typically small and fast, so they might
// end up submerged by the move that calls P_HitWater.
if ( thing - > flags & MF_MISSILE )
2016-03-23 12:31:12 +00:00
compare_z - = thing - > Vel . Z ;
if ( pos . Z > compare_z )
2016-03-01 15:47:10 +00:00
return false ;
}
#if 0 // needs some rethinking before activation
// This avoids spawning splashes on invisible self referencing sectors.
// For network consistency do this only in single player though because
// it is not guaranteed that all players have GL nodes loaded.
if ( ! multiplayer & & thing - > subsector - > sector ! = thing - > subsector - > render_sector )
{
2016-03-23 12:31:12 +00:00
double zs = thing - > subsector - > sector - > floorplane . ZatPoint ( pos ) ;
double zr = thing - > subsector - > render_sector - > floorplane . ZatPoint ( pos ) ;
2016-03-01 15:47:10 +00:00
2016-03-23 12:31:12 +00:00
if ( zs > zr & & thing - > Z ( ) > = zs ) return false ;
2016-03-01 15:47:10 +00:00
}
# endif
// 'force' means, we want this sector's terrain, no matter what.
if ( ! force )
{
for ( unsigned int i = 0 ; i < sec - > e - > XFloor . ffloors . Size ( ) ; i + + )
{
2016-03-23 12:31:12 +00:00
F3DFloor * rover = sec - > e - > XFloor . ffloors [ i ] ;
if ( ! ( rover - > flags & FF_EXISTS ) ) continue ;
double planez = rover - > top . plane - > ZatPoint ( pos ) ;
if ( pos . Z > planez - 0.5 & & pos . Z < planez + 0.5 ) // allow minor imprecisions
2016-03-01 15:47:10 +00:00
{
2017-01-29 17:46:35 +00:00
if ( ( rover - > flags & ( FF_SOLID | FF_SWIMMABLE ) ) | | rover - > alpha > 0 )
2016-03-23 12:31:12 +00:00
{
terrainnum = rover - > model - > GetTerrain ( rover - > top . isceiling ) ;
goto foundone ;
}
2016-03-01 15:47:10 +00:00
}
2016-03-23 12:31:12 +00:00
planez = rover - > bottom . plane - > ZatPoint ( pos ) ;
if ( planez < pos . Z & & ! ( planez < thing - > floorz ) ) return false ;
2016-03-01 15:47:10 +00:00
}
}
hsec = sec - > GetHeightSec ( ) ;
2018-05-01 09:29:29 +00:00
if ( force | | hsec = = NULL | | ! ( hsec - > MoreFlags & SECMF_CLIPFAKEPLANES ) )
2016-03-01 15:47:10 +00:00
{
terrainnum = sec - > GetTerrain ( sector_t : : floor ) ;
}
else
{
terrainnum = hsec - > GetTerrain ( sector_t : : floor ) ;
}
foundone :
int splashnum = Terrains [ terrainnum ] . Splash ;
bool smallsplash = false ;
const secplane_t * plane ;
if ( splashnum = = - 1 )
return Terrains [ terrainnum ] . IsLiquid ;
// don't splash when touching an underwater floor
2016-03-23 12:31:12 +00:00
if ( thing - > waterlevel > = 1 & & pos . Z < = thing - > floorz ) return Terrains [ terrainnum ] . IsLiquid ;
2016-03-01 15:47:10 +00:00
plane = hsec ! = NULL ? & sec - > heightsec - > floorplane : & sec - > floorplane ;
// Don't splash for living things with small vertical velocities.
// There are levels where the constant splashing from the monsters gets extremely annoying
2016-03-23 12:31:12 +00:00
if ( ( ( thing - > flags3 & MF3_ISMONSTER | | thing - > player ) & & thing - > Vel . Z > = - 6 ) & & ! force )
2016-03-01 15:47:10 +00:00
return Terrains [ terrainnum ] . IsLiquid ;
splash = & Splashes [ splashnum ] ;
// Small splash for small masses
if ( thing - > Mass < 10 )
smallsplash = true ;
2018-06-02 10:10:06 +00:00
if ( ! ( thing - > flags3 & MF3_DONTSPLASH ) )
2016-03-01 15:47:10 +00:00
{
2018-06-02 10:10:06 +00:00
if ( smallsplash & & splash - > SmallSplash )
2016-03-01 15:47:10 +00:00
{
2018-06-02 10:10:06 +00:00
mo = Spawn ( splash - > SmallSplash , pos , ALLOW_REPLACE ) ;
if ( mo ) mo - > Floorclip + = splash - > SmallSplashClip ;
}
else
{
if ( splash - > SplashChunk )
{
mo = Spawn ( splash - > SplashChunk , pos , ALLOW_REPLACE ) ;
mo - > target = thing ;
if ( splash - > ChunkXVelShift ! = 255 )
{
mo - > Vel . X = ( pr_chunk . Random2 ( ) < < splash - > ChunkXVelShift ) / 65536. ;
}
if ( splash - > ChunkYVelShift ! = 255 )
{
mo - > Vel . Y = ( pr_chunk . Random2 ( ) < < splash - > ChunkYVelShift ) / 65536. ;
}
mo - > Vel . Z = splash - > ChunkBaseZVel + ( pr_chunk ( ) < < splash - > ChunkZVelShift ) / 65536. ;
}
if ( splash - > SplashBase )
2016-03-01 15:47:10 +00:00
{
2018-06-02 10:10:06 +00:00
mo = Spawn ( splash - > SplashBase , pos , ALLOW_REPLACE ) ;
2016-03-01 15:47:10 +00:00
}
2018-06-02 10:10:06 +00:00
if ( thing - > player & & ! splash - > NoAlert & & alert )
2016-03-01 15:47:10 +00:00
{
2018-06-02 10:10:06 +00:00
P_NoiseAlert ( thing , thing , true ) ;
2016-03-01 15:47:10 +00:00
}
}
2018-06-02 10:10:06 +00:00
if ( mo )
2016-03-01 15:47:10 +00:00
{
2018-06-02 10:10:06 +00:00
S_Sound ( mo , CHAN_ITEM , smallsplash ?
splash - > SmallSplashSound : splash - > NormalSplashSound ,
1 , ATTN_IDLE ) ;
2016-03-01 15:47:10 +00:00
}
2018-06-02 10:10:06 +00:00
else
2016-03-01 15:47:10 +00:00
{
2019-01-27 18:32:38 +00:00
S_Sound ( thing - > Level , pos , CHAN_ITEM , smallsplash ?
2018-06-02 10:10:06 +00:00
splash - > SmallSplashSound : splash - > NormalSplashSound ,
1 , ATTN_IDLE ) ;
2016-03-01 15:47:10 +00:00
}
}
// Don't let deep water eat missiles
return plane = = & sec - > floorplane ? Terrains [ terrainnum ] . IsLiquid : false ;
}
2016-11-30 00:25:51 +00:00
DEFINE_ACTION_FUNCTION ( AActor , HitWater )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2016-12-02 11:06:49 +00:00
PARAM_POINTER_NOT_NULL ( sec , sector_t ) ;
2016-11-30 00:25:51 +00:00
PARAM_FLOAT ( x ) ;
PARAM_FLOAT ( y ) ;
PARAM_FLOAT ( z ) ;
2018-11-17 09:03:40 +00:00
PARAM_BOOL ( checkabove ) ;
PARAM_BOOL ( alert ) ;
PARAM_BOOL ( force ) ;
2016-11-30 00:25:51 +00:00
ACTION_RETURN_BOOL ( P_HitWater ( self , sec , DVector3 ( x , y , z ) , checkabove , alert , force ) ) ;
}
2016-03-01 15:47:10 +00:00
//---------------------------------------------------------------------------
//
// FUNC P_HitFloor
//
// Returns true if hit liquid and splashed, false if not.
//---------------------------------------------------------------------------
bool P_HitFloor ( AActor * thing )
{
const msecnode_t * m ;
// killough 11/98: touchy objects explode on impact
// Allow very short drops to be safe, so that a touchy can be summoned without exploding.
2016-03-28 08:01:24 +00:00
if ( thing - > flags6 & MF6_TOUCHY & & ( ( thing - > flags6 & MF6_ARMED ) | | thing - > IsSentient ( ) ) & & thing - > Vel . Z < - 5 )
2016-03-01 15:47:10 +00:00
{
thing - > flags6 & = ~ MF6_ARMED ; // Disarm
P_DamageMobj ( thing , NULL , NULL , thing - > health , NAME_Crush , DMG_FORCED ) ; // kill object
return false ;
}
// don't splash if landing on the edge above water/lava/etc....
2016-03-28 10:03:07 +00:00
DVector3 pos ;
2016-03-01 15:47:10 +00:00
for ( m = thing - > touching_sectorlist ; m ; m = m - > m_tnext )
{
2016-03-28 10:03:07 +00:00
pos = thing - > PosRelative ( m - > m_sector ) ;
if ( thing - > Z ( ) = = m - > m_sector - > floorplane . ZatPoint ( pos ) )
2016-03-01 15:47:10 +00:00
{
break ;
}
// Check 3D floors
for ( unsigned int i = 0 ; i < m - > m_sector - > e - > XFloor . ffloors . Size ( ) ; i + + )
{
F3DFloor * rover = m - > m_sector - > e - > XFloor . ffloors [ i ] ;
if ( ! ( rover - > flags & FF_EXISTS ) ) continue ;
if ( rover - > flags & ( FF_SOLID | FF_SWIMMABLE ) )
{
2016-03-28 10:03:07 +00:00
if ( rover - > top . plane - > ZatPoint ( pos ) = = thing - > Z ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-06 20:58:36 +00:00
return P_HitWater ( thing , m - > m_sector , pos ) ;
2016-03-01 15:47:10 +00:00
}
}
}
}
if ( m = = NULL | | m - > m_sector - > GetHeightSec ( ) ! = NULL )
{
return false ;
}
2016-03-06 20:58:36 +00:00
return P_HitWater ( thing , m - > m_sector , pos ) ;
2016-03-01 15:47:10 +00:00
}
2016-11-24 12:45:43 +00:00
DEFINE_ACTION_FUNCTION ( AActor , HitFloor )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
ACTION_RETURN_BOOL ( P_HitFloor ( self ) ) ;
}
2016-03-01 15:47:10 +00:00
//---------------------------------------------------------------------------
//
// FUNC P_CheckMissileSpawn
//
// Returns true if the missile is at a valid spawn point, otherwise
// explodes it and returns false.
//
//---------------------------------------------------------------------------
2016-03-20 14:04:13 +00:00
bool P_CheckMissileSpawn ( AActor * th , double maxdist )
2016-03-01 15:47:10 +00:00
{
// [RH] Don't decrement tics if they are already less than 1
if ( ( th - > flags4 & MF4_RANDOMIZE ) & & th - > tics > 0 )
{
th - > tics - = pr_checkmissilespawn ( ) & 3 ;
if ( th - > tics < 1 )
th - > tics = 1 ;
}
2017-07-20 16:32:54 +00:00
DVector3 newpos = { 0 , 0 , 0 } ;
2017-06-07 20:40:54 +00:00
2016-03-01 15:47:10 +00:00
if ( maxdist > 0 )
{
// move a little forward so an angle can be computed if it immediately explodes
2016-03-20 14:04:13 +00:00
DVector3 advance = th - > Vel ;
double maxsquared = maxdist * maxdist ;
2016-03-01 15:47:10 +00:00
// Keep halving the advance vector until we get something less than maxdist
// units away, since we still want to spawn the missile inside the shooter.
do
{
advance * = 0.5f ;
}
2016-03-20 14:04:13 +00:00
while ( advance . XY ( ) . LengthSquared ( ) > = maxsquared ) ;
2017-06-07 20:40:54 +00:00
newpos + = advance ;
2016-03-01 15:47:10 +00:00
}
2017-07-20 16:32:54 +00:00
newpos = th - > Vec3Offset ( newpos ) ;
th - > SetXYZ ( newpos ) ;
2019-01-29 00:30:41 +00:00
th - > Sector = th - > Level - > PointInSector ( th - > Pos ( ) ) ;
2017-07-20 16:32:54 +00:00
2016-03-01 15:47:10 +00:00
FCheckPosition tm ( ! ! ( th - > flags2 & MF2_RIP ) ) ;
// killough 8/12/98: for non-missile objects (e.g. grenades)
//
// [GZ] MBF excludes non-missile objects from the P_TryMove test
// and subsequent potential P_ExplodeMissile call. That is because
// in MBF, a projectile is not an actor with the MF_MISSILE flag
// but an actor with either or both the MF_MISSILE and MF_BOUNCES
// flags, and a grenade is identified by not having MF_MISSILE.
// Killough wanted grenades not to explode directly when spawned,
// therefore they can be fired safely even when humping a wall as
// they will then just drop on the floor at their shooter's feet.
//
// However, ZDoom does allow non-missiles to be shot as well, so
// Killough's check for non-missiles is inadequate here. So let's
// replace it by a check for non-missile and MBF bounce type.
// This should allow MBF behavior where relevant without altering
// established ZDoom behavior for crazy stuff like a cacodemon cannon.
bool MBFGrenade = ( ! ( th - > flags & MF_MISSILE ) | | ( th - > BounceFlags & BOUNCE_MBF ) ) ;
// killough 3/15/98: no dropoff (really = don't care for missiles)
2017-06-07 20:40:54 +00:00
auto oldf2 = th - > flags2 ;
2017-06-08 07:00:03 +00:00
th - > flags2 & = ~ ( MF2_MCROSS | MF2_PCROSS ) ; // The following check is not supposed to activate missile triggers.
2017-06-07 20:40:54 +00:00
if ( ! ( P_TryMove ( th , newpos , false , NULL , tm , true ) ) )
2016-03-01 15:47:10 +00:00
{
// [RH] Don't explode ripping missiles that spawn inside something
if ( th - > BlockingMobj = = NULL | | ! ( th - > flags2 & MF2_RIP ) | | ( th - > BlockingMobj - > flags5 & MF5_DONTRIP ) )
{
2016-12-24 13:46:34 +00:00
// If this is a monster spawned by A_SpawnProjectile subtract it from the counter.
2016-03-01 15:47:10 +00:00
th - > ClearCounters ( ) ;
// [RH] Don't explode missiles that spawn on top of horizon lines
if ( th - > BlockingLine ! = NULL & & th - > BlockingLine - > special = = Line_Horizon )
{
th - > Destroy ( ) ;
}
else if ( MBFGrenade & & th - > BlockingLine ! = NULL )
{
P_BounceWall ( th ) ;
}
else
{
P_ExplodeMissile ( th , NULL , th - > BlockingMobj ) ;
}
return false ;
}
}
2017-06-07 20:40:54 +00:00
th - > flags2 = oldf2 ;
2016-03-01 15:47:10 +00:00
th - > ClearInterpolation ( ) ;
return true ;
}
2016-11-18 20:34:06 +00:00
DEFINE_ACTION_FUNCTION ( AActor , CheckMissileSpawn )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_FLOAT ( add ) ;
ACTION_RETURN_BOOL ( P_CheckMissileSpawn ( self , add ) ) ;
}
2016-03-01 15:47:10 +00:00
//---------------------------------------------------------------------------
//
// FUNC P_PlaySpawnSound
//
// Plays a missiles spawn sound. Location depends on the
// MF_SPAWNSOUNDSOURCE flag.
//
//---------------------------------------------------------------------------
void P_PlaySpawnSound ( AActor * missile , AActor * spawner )
{
if ( missile - > SeeSound ! = 0 )
{
if ( ! ( missile - > flags & MF_SPAWNSOUNDSOURCE ) )
{
S_Sound ( missile , CHAN_VOICE , missile - > SeeSound , 1 , ATTN_NORM ) ;
}
else if ( spawner ! = NULL )
{
S_Sound ( spawner , CHAN_WEAPON , missile - > SeeSound , 1 , ATTN_NORM ) ;
}
else
{
// If there is no spawner use the spawn position.
// But not in a silenced sector.
if ( ! ( missile - > Sector - > Flags & SECF_SILENT ) )
2019-01-27 18:32:38 +00:00
S_Sound ( missile - > Level , missile - > Pos ( ) , CHAN_WEAPON , missile - > SeeSound , 1 , ATTN_NORM ) ;
2016-03-01 15:47:10 +00:00
}
}
}
2016-11-30 00:25:51 +00:00
DEFINE_ACTION_FUNCTION ( AActor , PlaySpawnSound )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2016-12-02 11:06:49 +00:00
PARAM_OBJECT_NOT_NULL ( missile , AActor ) ;
2016-11-30 00:25:51 +00:00
P_PlaySpawnSound ( missile , self ) ;
return 0 ;
}
2018-12-04 23:21:16 +00:00
double GetDefaultSpeed ( PClassActor * type )
2016-03-01 15:47:10 +00:00
{
if ( type = = NULL )
return 0 ;
2017-02-28 11:47:44 +00:00
auto def = GetDefaultByType ( type ) ;
if ( G_SkillProperty ( SKILLP_FastMonsters ) )
{
double f = def - > FloatVar ( NAME_FastSpeed ) ;
if ( f > = 0 ) return f ;
}
return def - > Speed ;
2016-03-01 15:47:10 +00:00
}
//---------------------------------------------------------------------------
//
// FUNC P_SpawnMissile
//
// Returns NULL if the missile exploded immediately, otherwise returns
// a mobj_t pointer to the missile.
//
//---------------------------------------------------------------------------
2016-03-28 10:03:07 +00:00
AActor * P_SpawnMissileXYZ ( DVector3 pos , AActor * source , AActor * dest , PClassActor * type , bool checkspawn , AActor * owner )
2016-03-01 15:47:10 +00:00
{
2016-12-02 11:06:49 +00:00
if ( source = = nullptr | | type = = nullptr )
2016-03-01 15:47:10 +00:00
{
2016-12-02 11:06:49 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
if ( dest = = NULL )
{
Printf ( " P_SpawnMissilyXYZ: Tried to shoot %s from %s with no dest \n " ,
type - > TypeName . GetChars ( ) , source - > GetClass ( ) - > TypeName . GetChars ( ) ) ;
return NULL ;
}
2016-03-28 10:03:07 +00:00
if ( pos . Z ! = ONFLOORZ & & pos . Z ! = ONCEILINGZ )
2016-03-01 15:47:10 +00:00
{
2016-03-28 10:03:07 +00:00
pos . Z - = source - > Floorclip ;
2016-03-01 15:47:10 +00:00
}
2016-03-23 09:42:41 +00:00
AActor * th = Spawn ( type , pos , ALLOW_REPLACE ) ;
2016-03-01 15:47:10 +00:00
P_PlaySpawnSound ( th , source ) ;
// record missile's originator
if ( owner = = NULL ) owner = source ;
th - > target = owner ;
2016-03-19 23:54:18 +00:00
double speed = th - > Speed ;
2016-03-01 15:47:10 +00:00
// [RH]
// Hexen calculates the missile velocity based on the source's location.
// Would it be more useful to base it on the actual position of the
// missile?
// Answer: No, because this way, you can set up sets of parallel missiles.
2016-03-19 23:54:18 +00:00
DVector3 velocity = source - > Vec3To ( dest ) ;
2016-03-01 15:47:10 +00:00
// Floor and ceiling huggers should never have a vertical component to their velocity
if ( th - > flags3 & ( MF3_FLOORHUGGER | MF3_CEILINGHUGGER ) )
{
velocity . Z = 0 ;
}
// [RH] Adjust the trajectory if the missile will go over the target's head.
2016-03-28 10:03:07 +00:00
else if ( pos . Z - source - > Z ( ) > = dest - > Height )
2016-03-01 15:47:10 +00:00
{
2016-03-28 10:03:07 +00:00
velocity . Z + = ( dest - > Height - pos . Z + source - > Z ( ) ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-19 23:54:18 +00:00
th - > Vel = velocity . Resized ( speed ) ;
2016-03-01 15:47:10 +00:00
// invisible target: rotate velocity vector in 2D
// [RC] Now monsters can aim at invisible player as if they were fully visible.
if ( dest - > flags & MF_SHADOW & & ! ( source - > flags6 & MF6_SEEINVISIBLE ) )
{
2016-03-19 23:54:18 +00:00
DAngle an = pr_spawnmissile . Random2 ( ) * ( 22.5 / 256 ) ;
double c = an . Cos ( ) ;
double s = an . Sin ( ) ;
2016-03-01 15:47:10 +00:00
2016-03-19 23:54:18 +00:00
double newx = th - > Vel . X * c - th - > Vel . Y * s ;
double newy = th - > Vel . X * s + th - > Vel . Y * c ;
th - > Vel . X = newx ;
th - > Vel . Y = newy ;
2016-03-01 15:47:10 +00:00
}
2016-03-16 11:41:26 +00:00
th - > AngleFromVel ( ) ;
2016-03-01 15:47:10 +00:00
if ( th - > flags4 & MF4_SPECTRAL )
{
th - > SetFriendPlayer ( owner - > player ) ;
}
return ( ! checkspawn | | P_CheckMissileSpawn ( th , source - > radius ) ) ? th : NULL ;
}
2016-11-26 23:41:06 +00:00
DEFINE_ACTION_FUNCTION ( AActor , SpawnMissileXYZ )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_FLOAT ( x ) ;
PARAM_FLOAT ( y ) ;
PARAM_FLOAT ( z ) ;
2016-12-02 11:06:49 +00:00
PARAM_OBJECT_NOT_NULL ( dest , AActor ) ;
2016-11-26 23:41:06 +00:00
PARAM_CLASS ( type , AActor ) ;
2018-11-17 09:03:40 +00:00
PARAM_BOOL ( check ) ;
PARAM_OBJECT ( owner , AActor ) ;
2016-11-26 23:41:06 +00:00
ACTION_RETURN_OBJECT ( P_SpawnMissileXYZ ( DVector3 ( x , y , z ) , self , dest , type , check , owner ) ) ;
}
2016-12-02 11:06:49 +00:00
AActor * P_SpawnMissile ( AActor * source , AActor * dest , PClassActor * type , AActor * owner )
{
if ( source = = nullptr )
{
return nullptr ;
}
return P_SpawnMissileXYZ ( source - > PosPlusZ ( 32 + source - > GetBobOffset ( ) ) , source , dest , type , true , owner ) ;
}
DEFINE_ACTION_FUNCTION ( AActor , SpawnMissile )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_OBJECT_NOT_NULL ( dest , AActor ) ;
PARAM_CLASS ( type , AActor ) ;
2018-11-17 09:03:40 +00:00
PARAM_OBJECT ( owner , AActor ) ;
2016-12-02 11:06:49 +00:00
ACTION_RETURN_OBJECT ( P_SpawnMissile ( self , dest , type , owner ) ) ;
}
AActor * P_SpawnMissileZ ( AActor * source , double z , AActor * dest , PClassActor * type )
{
if ( source = = nullptr )
{
return nullptr ;
}
return P_SpawnMissileXYZ ( source - > PosAtZ ( z ) , source , dest , type ) ;
}
DEFINE_ACTION_FUNCTION ( AActor , SpawnMissileZ )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_FLOAT ( z ) ;
PARAM_OBJECT_NOT_NULL ( dest , AActor ) ;
PARAM_CLASS ( type , AActor ) ;
ACTION_RETURN_OBJECT ( P_SpawnMissileZ ( self , z , dest , type ) ) ;
}
2016-11-26 23:41:06 +00:00
2016-03-01 15:47:10 +00:00
AActor * P_OldSpawnMissile ( AActor * source , AActor * owner , AActor * dest , PClassActor * type )
{
2016-12-02 11:06:49 +00:00
if ( source = = nullptr | | type = = nullptr )
2016-03-01 15:47:10 +00:00
{
2016-12-02 11:06:49 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
2016-03-23 09:42:41 +00:00
AActor * th = Spawn ( type , source - > PosPlusZ ( 32. ) , ALLOW_REPLACE ) ;
2016-03-01 15:47:10 +00:00
P_PlaySpawnSound ( th , source ) ;
th - > target = owner ; // record missile's originator
2016-03-16 23:07:37 +00:00
th - > Angles . Yaw = source - > AngleTo ( dest ) ;
2016-03-16 11:41:26 +00:00
th - > VelFromAngle ( ) ;
2016-03-01 15:47:10 +00:00
2016-03-19 23:54:18 +00:00
double dist = source - > DistanceBySpeed ( dest , MAX ( 1. , th - > Speed ) ) ;
th - > Vel . Z = ( dest - > Z ( ) - source - > Z ( ) ) / dist ;
2016-03-01 15:47:10 +00:00
if ( th - > flags4 & MF4_SPECTRAL )
{
th - > SetFriendPlayer ( owner - > player ) ;
}
P_CheckMissileSpawn ( th , source - > radius ) ;
return th ;
}
2016-10-30 13:00:11 +00:00
DEFINE_ACTION_FUNCTION ( AActor , OldSpawnMissile )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2016-12-02 11:06:49 +00:00
PARAM_OBJECT_NOT_NULL ( dest , AActor ) ;
2016-10-30 13:00:11 +00:00
PARAM_CLASS ( type , AActor ) ;
2018-11-17 09:03:40 +00:00
PARAM_OBJECT ( owner , AActor ) ;
2016-10-30 13:00:11 +00:00
ACTION_RETURN_OBJECT ( P_OldSpawnMissile ( self , owner , dest , type ) ) ;
}
2016-03-01 15:47:10 +00:00
//---------------------------------------------------------------------------
//
// FUNC P_SpawnMissileAngle
//
// Returns NULL if the missile exploded immediately, otherwise returns
// a mobj_t pointer to the missile.
//
//---------------------------------------------------------------------------
2016-03-28 10:03:07 +00:00
AActor * P_SpawnMissileAngle ( AActor * source , PClassActor * type , DAngle angle , double vz )
2016-03-01 15:47:10 +00:00
{
2016-12-02 11:06:49 +00:00
if ( source = = nullptr | | type = = nullptr )
2016-03-01 15:47:10 +00:00
{
return NULL ;
}
2016-03-28 10:03:07 +00:00
return P_SpawnMissileAngleZSpeed ( source , source - > Z ( ) + 32 + source - > GetBobOffset ( ) , type , angle , vz , GetDefaultSpeed ( type ) ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-28 10:03:07 +00:00
AActor * P_SpawnMissileAngleZ ( AActor * source , double z , PClassActor * type , DAngle angle , double vz )
2016-03-01 15:47:10 +00:00
{
2016-12-02 11:06:49 +00:00
if ( type = = nullptr )
{
return nullptr ;
}
2016-03-28 10:03:07 +00:00
return P_SpawnMissileAngleZSpeed ( source , z , type , angle , vz , GetDefaultSpeed ( type ) ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-28 10:03:07 +00:00
AActor * P_SpawnMissileZAimed ( AActor * source , double z , AActor * dest , PClassActor * type )
2016-03-01 15:47:10 +00:00
{
2016-12-02 11:06:49 +00:00
if ( source = = nullptr | | type = = nullptr )
2016-03-01 15:47:10 +00:00
{
2016-12-02 11:06:49 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
2016-03-28 08:01:24 +00:00
DAngle an ;
double dist ;
double speed ;
double vz ;
2016-03-01 15:47:10 +00:00
2016-03-28 08:01:24 +00:00
an = source - > Angles . Yaw ;
2016-03-01 15:47:10 +00:00
if ( dest - > flags & MF_SHADOW )
{
2016-03-28 08:01:24 +00:00
an + = pr_spawnmissile . Random2 ( ) * ( 16. / 360. ) ;
2016-03-01 15:47:10 +00:00
}
2016-03-28 08:01:24 +00:00
dist = source - > Distance2D ( dest ) ;
2016-03-28 14:22:21 +00:00
speed = GetDefaultSpeed ( type ) ;
2016-03-01 15:47:10 +00:00
dist / = speed ;
2016-03-28 08:01:24 +00:00
vz = dist ! = 0 ? ( dest - > Z ( ) - source - > Z ( ) ) / dist : speed ;
2016-03-12 13:11:43 +00:00
return P_SpawnMissileAngleZSpeed ( source , z , type , an , vz , speed ) ;
2016-03-01 15:47:10 +00:00
}
2016-11-28 17:36:13 +00:00
DEFINE_ACTION_FUNCTION ( AActor , SpawnMissileZAimed )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_FLOAT ( z ) ;
2016-12-02 11:06:49 +00:00
PARAM_OBJECT_NOT_NULL ( dest , AActor ) ;
2016-11-28 17:36:13 +00:00
PARAM_CLASS ( type , AActor ) ;
ACTION_RETURN_OBJECT ( P_SpawnMissileZAimed ( self , z , dest , type ) ) ;
}
2016-03-01 15:47:10 +00:00
//---------------------------------------------------------------------------
//
2016-03-28 10:03:07 +00:00
// FUNC P_SpawnMissileAngleZSpeed
2016-03-01 15:47:10 +00:00
//
// Returns NULL if the missile exploded immediately, otherwise returns
// a mobj_t pointer to the missile.
//
//---------------------------------------------------------------------------
2016-03-28 10:03:07 +00:00
AActor * P_SpawnMissileAngleZSpeed ( AActor * source , double z ,
PClassActor * type , DAngle angle , double vz , double speed , AActor * owner , bool checkspawn )
2016-03-01 15:47:10 +00:00
{
2016-12-02 11:06:49 +00:00
if ( source = = nullptr | | type = = nullptr )
2016-03-01 15:47:10 +00:00
{
2016-12-02 11:06:49 +00:00
return nullptr ;
2016-03-01 15:47:10 +00:00
}
AActor * mo ;
if ( z ! = ONFLOORZ & & z ! = ONCEILINGZ )
{
2016-03-28 10:03:07 +00:00
z - = source - > Floorclip ;
2016-03-01 15:47:10 +00:00
}
2016-03-28 10:03:07 +00:00
mo = Spawn ( type , source - > PosAtZ ( z ) , ALLOW_REPLACE ) ;
2016-03-01 15:47:10 +00:00
P_PlaySpawnSound ( mo , source ) ;
if ( owner = = NULL ) owner = source ;
mo - > target = owner ;
2016-03-28 10:03:07 +00:00
mo - > Angles . Yaw = angle ;
mo - > VelFromAngle ( speed ) ;
mo - > Vel . Z = vz ;
2016-03-01 15:47:10 +00:00
if ( mo - > flags4 & MF4_SPECTRAL )
{
mo - > SetFriendPlayer ( owner - > player ) ;
}
return ( ! checkspawn | | P_CheckMissileSpawn ( mo , source - > radius ) ) ? mo : NULL ;
}
2016-11-11 22:32:13 +00:00
DEFINE_ACTION_FUNCTION ( AActor , SpawnMissileAngleZSpeed )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_FLOAT ( z ) ;
PARAM_CLASS ( type , AActor ) ;
PARAM_ANGLE ( angle ) ;
PARAM_FLOAT ( vz ) ;
PARAM_FLOAT ( speed ) ;
2018-11-17 09:03:40 +00:00
PARAM_OBJECT ( owner , AActor ) ;
PARAM_BOOL ( checkspawn ) ;
2016-11-11 22:32:13 +00:00
ACTION_RETURN_OBJECT ( P_SpawnMissileAngleZSpeed ( self , z , type , angle , vz , speed , owner , checkspawn ) ) ;
}
2016-11-28 10:52:03 +00:00
AActor * P_SpawnSubMissile ( AActor * source , PClassActor * type , AActor * target )
{
AActor * other = Spawn ( type , source - > Pos ( ) , ALLOW_REPLACE ) ;
2016-12-02 11:06:49 +00:00
if ( source = = nullptr | | type = = nullptr )
2016-11-28 10:52:03 +00:00
{
2016-12-02 11:06:49 +00:00
return nullptr ;
2016-11-28 10:52:03 +00:00
}
other - > target = target ;
other - > Angles . Yaw = source - > Angles . Yaw ;
other - > VelFromAngle ( ) ;
if ( other - > flags4 & MF4_SPECTRAL )
{
if ( source - > flags & MF_MISSILE & & source - > flags4 & MF4_SPECTRAL )
{
other - > FriendPlayer = source - > FriendPlayer ;
}
else
{
other - > SetFriendPlayer ( target - > player ) ;
}
}
if ( P_CheckMissileSpawn ( other , source - > radius ) )
{
DAngle pitch = P_AimLineAttack ( source , source - > Angles . Yaw , 1024. ) ;
other - > Vel . Z = - other - > Speed * pitch . Sin ( ) ;
return other ;
}
return NULL ;
}
DEFINE_ACTION_FUNCTION ( AActor , SpawnSubMissile )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_CLASS ( cls , AActor ) ;
2016-12-02 11:06:49 +00:00
PARAM_OBJECT_NOT_NULL ( target , AActor ) ;
2016-11-28 10:52:03 +00:00
ACTION_RETURN_OBJECT ( P_SpawnSubMissile ( self , cls , target ) ) ;
}
2016-03-01 15:47:10 +00:00
/*
= = = = = = = = = = = = = = = =
=
= P_SpawnPlayerMissile
=
= Tries to aim at a nearby monster
= = = = = = = = = = = = = = = =
*/
2016-03-21 23:30:56 +00:00
AActor * P_SpawnPlayerMissile ( AActor * source , double x , double y , double z ,
2016-03-17 10:38:56 +00:00
PClassActor * type , DAngle angle , FTranslatedLineTarget * pLineTarget , AActor * * pMissileActor ,
2016-03-01 15:47:10 +00:00
bool nofreeaim , bool noautoaim , int aimflags )
{
2016-12-02 11:06:49 +00:00
if ( source = = nullptr | | type = = nullptr )
{
return nullptr ;
}
2016-04-07 11:11:23 +00:00
static const double angdiff [ 3 ] = { - 5.625 , 5.625 , 0 } ;
2016-03-17 10:38:56 +00:00
DAngle an = angle ;
DAngle pitch ;
2016-03-01 15:47:10 +00:00
FTranslatedLineTarget scratch ;
AActor * defaultobject = GetDefaultByType ( type ) ;
2016-03-17 10:38:56 +00:00
DAngle vrange = nofreeaim ? 35. : 0. ;
2016-03-01 15:47:10 +00:00
if ( ! pLineTarget ) pLineTarget = & scratch ;
2018-11-25 08:29:12 +00:00
if ( ! ( aimflags & ALF_NOWEAPONCHECK ) & & source - > player & & source - > player - > ReadyWeapon & & ( ( source - > player - > ReadyWeapon - > IntVar ( NAME_WeaponFlags ) & WIF_NOAUTOAIM ) | | noautoaim ) )
2016-03-01 15:47:10 +00:00
{
// Keep exactly the same angle and pitch as the player's own aim
an = angle ;
2016-03-17 10:38:56 +00:00
pitch = source - > Angles . Pitch ;
2016-03-01 15:47:10 +00:00
pLineTarget - > linetarget = NULL ;
}
else // see which target is to be aimed at
{
// [XA] If MaxTargetRange is defined in the spawned projectile, use this as the
// maximum range for the P_AimLineAttack call later; this allows MaxTargetRange
// to function as a "maximum tracer-acquisition range" for seeker missiles.
2016-03-24 19:56:59 +00:00
double linetargetrange = defaultobject - > maxtargetrange > 0 ? defaultobject - > maxtargetrange * 64 : 16 * 64. ;
2016-03-01 15:47:10 +00:00
int i = 2 ;
do
{
an = angle + angdiff [ i ] ;
pitch = P_AimLineAttack ( source , an , linetargetrange , pLineTarget , vrange , aimflags ) ;
if ( source - > player ! = NULL & &
! nofreeaim & &
2019-01-27 15:08:22 +00:00
source - > Level - > IsFreelookAllowed ( ) & &
2016-03-17 10:38:56 +00:00
source - > player - > userinfo . GetAimDist ( ) < = 0.5 )
2016-03-01 15:47:10 +00:00
{
break ;
}
} while ( pLineTarget - > linetarget = = NULL & & - - i > = 0 ) ;
if ( pLineTarget - > linetarget = = NULL )
{
an = angle ;
2019-01-27 15:08:22 +00:00
if ( nofreeaim | | ! source - > Level - > IsFreelookAllowed ( ) )
2016-03-01 15:47:10 +00:00
{
2016-03-17 10:38:56 +00:00
pitch = 0. ;
2016-03-01 15:47:10 +00:00
}
}
}
if ( z ! = ONFLOORZ & & z ! = ONCEILINGZ )
{
// Doom spawns missiles 4 units lower than hitscan attacks for players.
2019-01-05 14:41:31 +00:00
z + = source - > Center ( ) - source - > Floorclip + source - > AttackOffset ( - 4 ) ;
2016-03-01 15:47:10 +00:00
// Do not fire beneath the floor.
2016-03-21 23:30:56 +00:00
if ( z < source - > floorz )
2016-03-01 15:47:10 +00:00
{
2016-03-21 23:30:56 +00:00
z = source - > floorz ;
2016-03-01 15:47:10 +00:00
}
}
2016-03-21 23:30:56 +00:00
DVector3 pos = source - > Vec2OffsetZ ( x , y , z ) ;
AActor * MissileActor = Spawn ( type , pos , ALLOW_REPLACE ) ;
2016-03-01 15:47:10 +00:00
if ( pMissileActor ) * pMissileActor = MissileActor ;
P_PlaySpawnSound ( MissileActor , source ) ;
MissileActor - > target = source ;
2016-03-17 10:38:56 +00:00
MissileActor - > Angles . Yaw = an ;
2016-03-16 11:41:26 +00:00
if ( MissileActor - > flags3 & ( MF3_FLOORHUGGER | MF3_CEILINGHUGGER ) )
{
MissileActor - > VelFromAngle ( ) ;
}
else
2016-03-01 15:47:10 +00:00
{
2016-03-17 10:38:56 +00:00
MissileActor - > Vel3DFromAngle ( pitch , MissileActor - > Speed ) ;
2016-03-01 15:47:10 +00:00
}
if ( MissileActor - > flags4 & MF4_SPECTRAL )
{
MissileActor - > SetFriendPlayer ( source - > player ) ;
}
if ( P_CheckMissileSpawn ( MissileActor , source - > radius ) )
{
return MissileActor ;
}
return NULL ;
}
2016-11-20 00:11:01 +00:00
DEFINE_ACTION_FUNCTION ( AActor , SpawnPlayerMissile )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_CLASS ( type , AActor ) ;
2018-11-17 09:03:40 +00:00
PARAM_ANGLE ( angle ) ;
PARAM_FLOAT ( x ) ;
PARAM_FLOAT ( y ) ;
PARAM_FLOAT ( z ) ;
2018-11-20 15:29:51 +00:00
PARAM_OUTPOINTER ( lt , FTranslatedLineTarget ) ;
2018-11-17 09:03:40 +00:00
PARAM_BOOL ( nofreeaim ) ;
PARAM_BOOL ( noautoaim ) ;
PARAM_INT ( aimflags ) ;
2016-11-20 00:11:01 +00:00
AActor * missileactor ;
2018-11-16 14:25:37 +00:00
if ( angle = = 1e37 ) angle = self - > Angles . Yaw ;
2016-11-20 00:11:01 +00:00
AActor * misl = P_SpawnPlayerMissile ( self , x , y , z , type , angle , lt , & missileactor , nofreeaim , noautoaim , aimflags ) ;
2017-04-10 13:17:39 +00:00
if ( numret > 0 ) ret [ 0 ] . SetObject ( misl ) ;
if ( numret > 1 ) ret [ 1 ] . SetObject ( missileactor ) , numret = 2 ;
2016-11-20 00:11:01 +00:00
return numret ;
}
2016-03-01 15:47:10 +00:00
int AActor : : GetTeam ( )
{
if ( player )
{
return player - > userinfo . GetTeam ( ) ;
}
int myTeam = DesignatedTeam ;
// Check for monsters that belong to a player on the team but aren't part of the team themselves.
if ( myTeam = = TEAM_NONE & & FriendPlayer ! = 0 )
{
myTeam = players [ FriendPlayer - 1 ] . userinfo . GetTeam ( ) ;
}
return myTeam ;
}
bool AActor : : IsTeammate ( AActor * other )
{
if ( ! other )
{
return false ;
}
else if ( ! deathmatch & & player & & other - > player )
{
2017-03-10 19:00:58 +00:00
return ( ! ( ( flags ^ other - > flags ) & MF_FRIENDLY ) ) ;
2016-03-01 15:47:10 +00:00
}
else if ( teamplay )
{
int myTeam = GetTeam ( ) ;
int otherTeam = other - > GetTeam ( ) ;
return ( myTeam ! = TEAM_NONE & & myTeam = = otherTeam ) ;
}
return false ;
}
//==========================================================================
//
// AActor :: GetSpecies
//
// Species is defined as the lowest base class that is a monster
// with no non-monster class in between. If the actor specifies an explicit
// species (i.e. not 'None'), that is used. This is virtualized, so special
// monsters can change this behavior if they like.
//
//==========================================================================
FName AActor : : GetSpecies ( )
{
if ( Species ! = NAME_None )
{
return Species ;
}
PClassActor * thistype = GetClass ( ) ;
if ( GetDefaultByType ( thistype ) - > flags3 & MF3_ISMONSTER )
{
while ( thistype - > ParentClass )
{
if ( GetDefaultByType ( thistype - > ParentClass ) - > flags3 & MF3_ISMONSTER )
thistype = static_cast < PClassActor * > ( thistype - > ParentClass ) ;
else
break ;
}
}
return Species = thistype - > TypeName ; // [GZ] Speeds up future calls.
}
//==========================================================================
//
// AActor :: IsFriend
//
// Checks if two monsters have to be considered friendly.
//
//==========================================================================
bool AActor : : IsFriend ( AActor * other )
{
if ( flags & other - > flags & MF_FRIENDLY )
{
if ( deathmatch & & teamplay )
return IsTeammate ( other ) | |
( FriendPlayer ! = 0 & & other - > FriendPlayer ! = 0 & &
2019-01-30 00:15:32 +00:00
Level - > Players [ FriendPlayer - 1 ] - > mo - > IsTeammate ( Level - > Players [ other - > FriendPlayer - 1 ] - > mo ) ) ;
2016-03-01 15:47:10 +00:00
return ! deathmatch | |
FriendPlayer = = other - > FriendPlayer | |
FriendPlayer = = 0 | |
other - > FriendPlayer = = 0 | |
2019-01-30 00:15:32 +00:00
Level - > Players [ FriendPlayer - 1 ] - > mo - > IsTeammate ( Level - > Players [ other - > FriendPlayer - 1 ] - > mo ) ;
2016-03-01 15:47:10 +00:00
}
2017-03-10 17:47:52 +00:00
// [SP] If friendly flags match, then they are on the same team.
/*if (!((flags ^ other->flags) & MF_FRIENDLY))
return true ; */
2016-03-01 15:47:10 +00:00
return false ;
}
//==========================================================================
//
// AActor :: IsHostile
//
// Checks if two monsters have to be considered hostile under any circumstances
//
//==========================================================================
bool AActor : : IsHostile ( AActor * other )
{
// Both monsters are non-friendlies so hostilities depend on infighting settings
if ( ! ( ( flags | other - > flags ) & MF_FRIENDLY ) ) return false ;
// Both monsters are friendly and belong to the same player if applicable.
if ( flags & other - > flags & MF_FRIENDLY )
{
if ( deathmatch & & teamplay )
return ! IsTeammate ( other ) & &
! ( FriendPlayer ! = 0 & & other - > FriendPlayer ! = 0 & &
2019-01-30 00:15:32 +00:00
Level - > Players [ FriendPlayer - 1 ] - > mo - > IsTeammate ( Level - > Players [ other - > FriendPlayer - 1 ] - > mo ) ) ;
2016-03-01 15:47:10 +00:00
return deathmatch & &
FriendPlayer ! = other - > FriendPlayer & &
FriendPlayer ! = 0 & &
other - > FriendPlayer ! = 0 & &
2019-01-30 00:15:32 +00:00
! Level - > Players [ FriendPlayer - 1 ] - > mo - > IsTeammate ( Level - > Players [ other - > FriendPlayer - 1 ] - > mo ) ;
2016-03-01 15:47:10 +00:00
}
return true ;
}
2016-11-24 20:36:02 +00:00
//==========================================================================
//
// AActor :: DoSpecialDamage
//
// override this for special damage effects.
//
//==========================================================================
2016-03-01 15:47:10 +00:00
int AActor : : DoSpecialDamage ( AActor * target , int damage , FName damagetype )
{
if ( target - > player & & target - > player - > mo = = target & & damage < 1000 & &
( target - > player - > cheats & CF_GODMODE | | target - > player - > cheats & CF_GODMODE2 ) )
{
return - 1 ;
}
else
{
if ( target - > player )
{
// Only do this for old style poison damage.
if ( PoisonDamage > 0 & & PoisonDuration = = INT_MIN )
{
P_PoisonPlayer ( target - > player , this , this - > target , PoisonDamage ) ;
damage > > = 1 ;
}
}
return damage ;
}
}
2016-11-23 20:26:59 +00:00
DEFINE_ACTION_FUNCTION ( AActor , DoSpecialDamage )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
2016-12-02 11:06:49 +00:00
PARAM_OBJECT_NOT_NULL ( target , AActor ) ;
2016-11-23 20:26:59 +00:00
PARAM_INT ( damage ) ;
PARAM_NAME ( damagetype ) ;
ACTION_RETURN_INT ( self - > DoSpecialDamage ( target , damage , damagetype ) ) ;
}
2016-11-24 20:36:02 +00:00
int AActor : : CallDoSpecialDamage ( AActor * target , int damage , FName damagetype )
{
IFVIRTUAL ( AActor , DoSpecialDamage )
{
// Without the type cast this picks the 'void *' assignment...
VMValue params [ 4 ] = { ( DObject * ) this , ( DObject * ) target , damage , damagetype . GetIndex ( ) } ;
VMReturn ret ;
int retval ;
ret . IntAt ( & retval ) ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 4 , & ret , 1 ) ;
2016-11-24 20:36:02 +00:00
return retval ;
}
else return DoSpecialDamage ( target , damage , damagetype ) ;
}
//==========================================================================
//
// AActor :: TakeSpecialDamage
//
//==========================================================================
2016-11-23 20:26:59 +00:00
2016-03-01 15:47:10 +00:00
int AActor : : TakeSpecialDamage ( AActor * inflictor , AActor * source , int damage , FName damagetype )
{
FState * death ;
// If the actor does not have a corresponding death state, then it does not take damage.
// Note that DeathState matches every kind of damagetype, so an actor has that, it can
// be hurt with any type of damage. Exception: Massacre damage always succeeds, because
// it needs to work.
// Always kill if there is a regular death state or no death states at all.
if ( FindState ( NAME_Death ) ! = NULL | | ! HasSpecialDeathStates ( ) | | damagetype = = NAME_Massacre )
{
return damage ;
}
if ( inflictor & & inflictor - > DeathType ! = NAME_None )
damagetype = inflictor - > DeathType ;
if ( damagetype = = NAME_Ice )
{
death = FindState ( NAME_Death , NAME_Ice , true ) ;
if ( death = = NULL & & ! deh . NoAutofreeze & & ! ( flags4 & MF4_NOICEDEATH ) & &
( player | | ( flags3 & MF3_ISMONSTER ) ) )
{
death = FindState ( NAME_GenericFreezeDeath ) ;
}
}
else
{
death = FindState ( NAME_Death , damagetype ) ;
}
return ( death = = NULL ) ? - 1 : damage ;
}
2016-11-28 23:16:30 +00:00
DEFINE_ACTION_FUNCTION ( AActor , TakeSpecialDamage )
{
PARAM_SELF_PROLOGUE ( AActor ) ;
PARAM_OBJECT ( inflictor , AActor ) ;
PARAM_OBJECT ( source , AActor ) ;
PARAM_INT ( damage ) ;
PARAM_NAME ( damagetype ) ;
ACTION_RETURN_INT ( self - > TakeSpecialDamage ( inflictor , source , damage , damagetype ) ) ;
}
2018-12-04 23:21:16 +00:00
2016-11-28 23:16:30 +00:00
int AActor : : CallTakeSpecialDamage ( AActor * inflictor , AActor * source , int damage , FName damagetype )
{
IFVIRTUAL ( AActor , TakeSpecialDamage )
{
VMValue params [ 5 ] = { ( DObject * ) this , inflictor , source , damage , damagetype . GetIndex ( ) } ;
VMReturn ret ;
int retval ;
ret . IntAt ( & retval ) ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 5 , & ret , 1 ) ;
2016-11-28 23:16:30 +00:00
return retval ;
}
else return TakeSpecialDamage ( inflictor , source , damage , damagetype ) ;
}
2016-03-01 15:47:10 +00:00
void AActor : : Crash ( )
{
// [RC] Weird that this forces the Crash state regardless of flag.
if ( ! ( flags6 & MF6_DONTCORPSE ) )
{
if ( ( ( flags & MF_CORPSE ) | | ( flags6 & MF6_KILLED ) ) & &
! ( flags3 & MF3_CRASHED ) & &
! ( flags & MF_ICECORPSE ) )
{
FState * crashstate = NULL ;
2017-02-28 12:40:46 +00:00
int gibh = GetGibHealth ( ) ;
2016-03-01 15:47:10 +00:00
if ( DamageType ! = NAME_None )
{
2017-02-28 12:40:46 +00:00
if ( health < gibh )
2016-03-01 15:47:10 +00:00
{ // Extreme death
FName labels [ ] = { NAME_Crash , NAME_Extreme , DamageType } ;
crashstate = FindState ( 3 , labels , true ) ;
}
if ( crashstate = = NULL )
{ // Normal death
crashstate = FindState ( NAME_Crash , DamageType , true ) ;
}
}
if ( crashstate = = NULL )
{
2017-02-28 12:40:46 +00:00
if ( health < gibh )
2016-03-01 15:47:10 +00:00
{ // Extreme death
crashstate = FindState ( NAME_Crash , NAME_Extreme ) ;
}
else
{ // Normal death
crashstate = FindState ( NAME_Crash ) ;
}
}
if ( crashstate ! = NULL ) SetState ( crashstate ) ;
// Set MF3_CRASHED regardless of the presence of a crash state
// so this code doesn't have to be executed repeatedly.
flags3 | = MF3_CRASHED ;
}
}
}
void AActor : : SetIdle ( bool nofunction )
{
FState * idle = FindState ( NAME_Idle ) ;
if ( idle = = NULL ) idle = SpawnState ;
SetState ( idle , nofunction ) ;
}
2016-11-26 00:14:47 +00:00
2016-03-01 15:47:10 +00:00
int AActor : : SpawnHealth ( ) const
{
int defhealth = StartHealth ? StartHealth : GetDefault ( ) - > health ;
if ( ! ( flags3 & MF3_ISMONSTER ) | | defhealth = = 0 )
{
return defhealth ;
}
else if ( flags & MF_FRIENDLY )
{
2016-03-24 00:46:11 +00:00
int adj = int ( defhealth * G_SkillProperty ( SKILLP_FriendlyHealth ) ) ;
2016-03-01 15:47:10 +00:00
return ( adj < = 0 ) ? 1 : adj ;
}
else
{
2016-03-24 00:46:11 +00:00
int adj = int ( defhealth * G_SkillProperty ( SKILLP_MonsterHealth ) ) ;
2016-03-01 15:47:10 +00:00
return ( adj < = 0 ) ? 1 : adj ;
}
}
2019-01-03 12:58:53 +00:00
int AActor : : GetMaxHealth ( bool withupgrades ) const
{
int ret = 100 ;
IFVIRTUAL ( AActor , GetMaxHealth )
{
VMValue param [ ] = { const_cast < AActor * > ( this ) , withupgrades } ;
VMReturn r ( & ret ) ;
VMCall ( func , param , 2 , & r , 1 ) ;
}
return ret ;
}
2016-03-01 15:47:10 +00:00
FState * AActor : : GetRaiseState ( )
{
if ( ! ( flags & MF_CORPSE ) )
{
return NULL ; // not a monster
}
if ( tics ! = - 1 & & // not lying still yet
! state - > GetCanRaise ( ) ) // or not ready to be raised yet
{
return NULL ;
}
2019-01-03 21:05:49 +00:00
if ( IsKindOf ( NAME_PlayerPawn ) )
2016-03-01 15:47:10 +00:00
{
return NULL ; // do not resurrect players
}
return FindState ( NAME_Raise ) ;
}
void AActor : : Revive ( )
{
AActor * info = GetDefault ( ) ;
2018-01-29 17:18:31 +00:00
FLinkContext ctx ;
bool flagchange = ( flags & ( MF_NOBLOCKMAP | MF_NOSECTOR ) ) ! = ( info - > flags & ( MF_NOBLOCKMAP | MF_NOSECTOR ) ) ;
if ( flagchange ) UnlinkFromWorld ( & ctx ) ;
2016-03-01 15:47:10 +00:00
flags = info - > flags ;
2018-01-29 17:18:31 +00:00
if ( flagchange ) LinkToWorld ( & ctx ) ;
2016-03-01 15:47:10 +00:00
flags2 = info - > flags2 ;
flags3 = info - > flags3 ;
flags4 = info - > flags4 ;
flags5 = info - > flags5 ;
flags6 = info - > flags6 ;
flags7 = info - > flags7 ;
2016-08-04 15:14:31 +00:00
if ( SpawnFlags & MTF_FRIENDLY ) flags | = MF_FRIENDLY ;
2016-03-01 15:47:10 +00:00
DamageType = info - > DamageType ;
health = SpawnHealth ( ) ;
2019-01-07 08:14:52 +00:00
target = nullptr ;
lastenemy = nullptr ;
2016-03-01 15:47:10 +00:00
// [RH] If it's a monster, it gets to count as another kill
if ( CountsAsKill ( ) )
{
2019-01-27 20:59:19 +00:00
Level - > total_monsters + + ;
2016-03-01 15:47:10 +00:00
}
2017-01-31 02:11:09 +00:00
// [ZZ] resurrect hook
E_WorldThingRevived ( this ) ;
2016-03-01 15:47:10 +00:00
}
int AActor : : GetGibHealth ( ) const
{
2017-02-28 12:40:46 +00:00
IFVIRTUAL ( AActor , GetGibHealth )
2016-03-01 15:47:10 +00:00
{
2017-02-28 12:40:46 +00:00
VMValue params [ ] = { ( DObject * ) this } ;
int h ;
VMReturn ret ( & h ) ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 1 , & ret , 1 ) ;
2017-02-28 12:40:46 +00:00
return h ;
2016-03-01 15:47:10 +00:00
}
2017-02-28 12:40:46 +00:00
return - SpawnHealth ( ) ;
2016-03-01 15:47:10 +00:00
}
2016-11-30 00:25:51 +00:00
2016-03-01 15:47:10 +00:00
// killough 11/98:
// Whether an object is "sentient" or not. Used for environmental influences.
// (left precisely the same as MBF even though it doesn't make much sense.)
bool AActor : : IsSentient ( ) const
{
return health > 0 & & SeeState ! = NULL ;
}
FSharedStringArena AActor : : mStringPropertyData ;
const char * AActor : : GetTag ( const char * def ) const
{
if ( Tag ! = NULL )
{
const char * tag = Tag - > GetChars ( ) ;
if ( tag [ 0 ] = = ' $ ' )
{
return GStrings ( tag + 1 ) ;
}
else
{
return tag ;
}
}
else if ( def )
{
return def ;
}
else
{
return GetClass ( ) - > TypeName . GetChars ( ) ;
}
}
void AActor : : SetTag ( const char * def )
{
2016-11-30 00:25:51 +00:00
if ( def = = NULL | | * def = = 0 ) Tag = nullptr ;
else Tag = mStringPropertyData . Alloc ( def ) ;
}
2016-03-01 15:47:10 +00:00
void AActor : : ClearCounters ( )
{
if ( CountsAsKill ( ) & & health > 0 )
{
2019-01-27 15:08:22 +00:00
Level - > total_monsters - - ;
2016-03-01 15:47:10 +00:00
flags & = ~ MF_COUNTKILL ;
}
// Same, for items
if ( flags & MF_COUNTITEM )
{
2019-01-27 15:08:22 +00:00
Level - > total_items - - ;
2016-03-01 15:47:10 +00:00
flags & = ~ MF_COUNTITEM ;
}
// And finally for secrets
if ( flags5 & MF5_COUNTSECRET )
{
2019-01-27 15:08:22 +00:00
Level - > total_secrets - - ;
2016-03-01 15:47:10 +00:00
flags5 & = ~ MF5_COUNTSECRET ;
}
}
2016-11-27 10:59:47 +00:00
int AActor : : GetModifiedDamage ( FName damagetype , int damage , bool passive )
{
2017-01-01 22:11:48 +00:00
auto inv = Inventory ;
while ( inv ! = nullptr )
{
2018-12-04 16:11:36 +00:00
IFVIRTUALPTRNAME ( inv , NAME_Inventory , ModifyDamage )
2017-01-19 19:56:31 +00:00
{
VMValue params [ 5 ] = { ( DObject * ) inv , damage , int ( damagetype ) , & damage , passive } ;
2017-04-12 23:12:04 +00:00
VMCall ( func , params , 5 , nullptr , 0 ) ;
2017-01-19 19:56:31 +00:00
}
2017-01-01 22:11:48 +00:00
inv = inv - > Inventory ;
}
2016-11-27 10:59:47 +00:00
return damage ;
}
2016-03-21 23:06:58 +00:00
int AActor : : ApplyDamageFactor ( FName damagetype , int damage ) const
{
damage = int ( damage * DamageFactor ) ;
if ( damage > 0 )
{
2017-04-11 22:07:41 +00:00
damage = DamageTypeDefinition : : ApplyMobjDamageFactor ( damage , damagetype , & GetInfo ( ) - > DamageFactors ) ;
2016-03-21 23:06:58 +00:00
}
return damage ;
}
2016-11-25 15:05:03 +00:00
void AActor : : SetTranslation ( FName trname )
2016-10-02 12:09:45 +00:00
{
2016-11-25 15:05:03 +00:00
// There is no constant for the empty name...
if ( trname . GetChars ( ) [ 0 ] = = 0 )
2016-10-02 12:09:45 +00:00
{
// an empty string resets to the default
Translation = GetDefault ( ) - > Translation ;
return ;
}
int tnum = R_FindCustomTranslation ( trname ) ;
if ( tnum > = 0 )
{
Translation = tnum ;
}
// silently ignore if the name does not exist, this would create some insane message spam otherwise.
}
2016-11-30 16:15:01 +00:00
//---------------------------------------------------------------------------
//
// PROP A_RestoreSpecialPosition
//
//---------------------------------------------------------------------------
static FRandom pr_restore ( " RestorePos " ) ;
void AActor : : RestoreSpecialPosition ( )
{
// Move item back to its original location
DVector2 sp = SpawnPoint ;
2016-12-25 21:40:26 +00:00
FLinkContext ctx ;
UnlinkFromWorld ( & ctx ) ;
2016-11-30 16:15:01 +00:00
SetXY ( sp ) ;
2016-12-25 21:40:26 +00:00
LinkToWorld ( & ctx , true ) ;
2016-11-30 16:15:01 +00:00
SetZ ( Sector - > floorplane . ZatPoint ( sp ) ) ;
P_FindFloorCeiling ( this , FFCF_ONLYSPAWNPOS | FFCF_NOPORTALS ) ; // no portal checks here so that things get spawned in this sector.
if ( flags & MF_SPAWNCEILING )
{
SetZ ( ceilingz - Height - SpawnPoint . Z ) ;
}
else if ( flags2 & MF2_SPAWNFLOAT )
{
double space = ceilingz - Height - floorz ;
if ( space > 48 )
{
space - = 40 ;
SetZ ( ( space * pr_restore ( ) ) / 256. + floorz + 40 ) ;
}
else
{
SetZ ( floorz ) ;
}
}
else
{
SetZ ( SpawnPoint . Z + floorz ) ;
}
// Redo floor/ceiling check, in case of 3D floors and portals
P_FindFloorCeiling ( this , FFCF_SAMESECTOR | FFCF_ONLY3DFLOORS | FFCF_3DRESTRICT ) ;
if ( Z ( ) < floorz )
{ // Do not reappear under the floor, even if that's where we were for the
// initial spawn.
SetZ ( floorz ) ;
}
if ( ( flags & MF_SOLID ) & & ( Top ( ) > ceilingz ) )
{ // Do the same for the ceiling.
SetZ ( ceilingz - Height ) ;
}
// Do not interpolate from the position the actor was at when it was
// picked up, in case that is different from where it is now.
ClearInterpolation ( ) ;
}
2016-03-01 15:47:10 +00:00
//----------------------------------------------------------------------------
//
// DropItem handling
//
//----------------------------------------------------------------------------
2016-11-05 16:08:54 +00:00
2017-02-08 19:37:22 +00:00
DEFINE_FIELD ( FDropItem , Next )
DEFINE_FIELD ( FDropItem , Name )
DEFINE_FIELD ( FDropItem , Probability )
DEFINE_FIELD ( FDropItem , Amount )
2016-11-03 23:19:36 +00:00
2016-03-01 15:47:10 +00:00
void PrintMiscActorInfo ( AActor * query )
{
if ( query )
{
int flagi ;
int querystyle = STYLE_Count ;
for ( int style = STYLE_None ; style < STYLE_Count ; + + style )
{ // Check for a legacy render style that matches.
if ( LegacyRenderStyles [ style ] = = query - > RenderStyle )
{
querystyle = style ;
break ;
}
}
static const char * renderstyles [ ] = { " None " , " Normal " , " Fuzzy " , " SoulTrans " ,
" OptFuzzy " , " Stencil " , " Translucent " , " Add " , " Shaded " , " TranslucentStencil " ,
" Shadow " , " Subtract " , " AddStencil " , " AddShaded " } ;
FLineSpecial * spec = P_GetLineSpecialInfo ( query - > special ) ;
Printf ( " %s @ %p has the following flags: \n flags: %x " , query - > GetTag ( ) , query , query - > flags . GetValue ( ) ) ;
for ( flagi = 0 ; flagi < = 31 ; flagi + + )
if ( query - > flags & ActorFlags : : FromInt ( 1 < < flagi ) ) Printf ( " %s " , FLAG_NAME ( 1 < < flagi , flags ) ) ;
Printf ( " \n flags2: %x " , query - > flags2 . GetValue ( ) ) ;
for ( flagi = 0 ; flagi < = 31 ; flagi + + )
if ( query - > flags2 & ActorFlags2 : : FromInt ( 1 < < flagi ) ) Printf ( " %s " , FLAG_NAME ( 1 < < flagi , flags2 ) ) ;
Printf ( " \n flags3: %x " , query - > flags3 . GetValue ( ) ) ;
for ( flagi = 0 ; flagi < = 31 ; flagi + + )
if ( query - > flags3 & ActorFlags3 : : FromInt ( 1 < < flagi ) ) Printf ( " %s " , FLAG_NAME ( 1 < < flagi , flags3 ) ) ;
Printf ( " \n flags4: %x " , query - > flags4 . GetValue ( ) ) ;
for ( flagi = 0 ; flagi < = 31 ; flagi + + )
if ( query - > flags4 & ActorFlags4 : : FromInt ( 1 < < flagi ) ) Printf ( " %s " , FLAG_NAME ( 1 < < flagi , flags4 ) ) ;
Printf ( " \n flags5: %x " , query - > flags5 . GetValue ( ) ) ;
for ( flagi = 0 ; flagi < = 31 ; flagi + + )
if ( query - > flags5 & ActorFlags5 : : FromInt ( 1 < < flagi ) ) Printf ( " %s " , FLAG_NAME ( 1 < < flagi , flags5 ) ) ;
Printf ( " \n flags6: %x " , query - > flags6 . GetValue ( ) ) ;
for ( flagi = 0 ; flagi < = 31 ; flagi + + )
if ( query - > flags6 & ActorFlags6 : : FromInt ( 1 < < flagi ) ) Printf ( " %s " , FLAG_NAME ( 1 < < flagi , flags6 ) ) ;
Printf ( " \n flags7: %x " , query - > flags7 . GetValue ( ) ) ;
for ( flagi = 0 ; flagi < = 31 ; flagi + + )
if ( query - > flags7 & ActorFlags7 : : FromInt ( 1 < < flagi ) ) Printf ( " %s " , FLAG_NAME ( 1 < < flagi , flags7 ) ) ;
2017-05-14 16:26:31 +00:00
Printf ( " \n flags8: %x " , query - > flags8 . GetValue ( ) ) ;
for ( flagi = 0 ; flagi < = 31 ; flagi + + )
if ( query - > flags8 & ActorFlags8 : : FromInt ( 1 < < flagi ) ) Printf ( " %s " , FLAG_NAME ( 1 < < flagi , flags8 ) ) ;
2016-03-01 15:47:10 +00:00
Printf ( " \n Bounce flags: %x \n Bounce factors: f:%f, w:%f " ,
2016-03-24 19:43:35 +00:00
query - > BounceFlags . GetValue ( ) , query - > bouncefactor ,
query - > wallbouncefactor ) ;
2016-03-01 15:47:10 +00:00
/*for (flagi = 0; flagi < 31; flagi++)
if ( query - > BounceFlags & 1 < < flagi ) Printf ( " %s " , flagnamesb [ flagi ] ) ; */
Printf ( " \n Render style = %i:%s, alpha %f \n Render flags: %x " ,
2018-12-04 16:00:48 +00:00
querystyle , ( ( unsigned ) querystyle < countof ( renderstyles ) ? renderstyles [ querystyle ] : " Custom " ) ,
2016-03-21 11:18:46 +00:00
query - > Alpha , query - > renderflags . GetValue ( ) ) ;
2016-03-01 15:47:10 +00:00
/*for (flagi = 0; flagi < 31; flagi++)
if ( query - > renderflags & 1 < < flagi ) Printf ( " %s " , flagnamesr [ flagi ] ) ; */
Printf ( " \n Special+args: %s(%i, %i, %i, %i, %i) \n special1: %i, special2: %i. " ,
( spec ? spec - > name : " None " ) ,
query - > args [ 0 ] , query - > args [ 1 ] , query - > args [ 2 ] , query - > args [ 3 ] ,
query - > args [ 4 ] , query - > special1 , query - > special2 ) ;
Printf ( " \n TID: %d " , query - > tid ) ;
2017-02-28 12:40:46 +00:00
Printf ( " \n Coord= x: %f, y: %f, z:%f, floor:%f, ceiling:%f, height= %f " ,
2016-03-19 23:54:18 +00:00
query - > X ( ) , query - > Y ( ) , query - > Z ( ) ,
2017-02-28 12:40:46 +00:00
query - > floorz , query - > ceilingz , query - > Height ) ;
2016-03-01 15:47:10 +00:00
Printf ( " \n Speed= %f, velocity= x:%f, y:%f, z:%f, combined:%f. \n " ,
2016-03-19 23:54:18 +00:00
query - > Speed , query - > Vel . X , query - > Vel . Y , query - > Vel . Z , query - > Vel . Length ( ) ) ;
2016-12-02 00:33:18 +00:00
Printf ( " Scale: x:%f, y:%f \n " , query - > Scale . X , query - > Scale . Y ) ;
2018-01-09 20:48:19 +00:00
Printf ( " FriendlySeeBlocks: %d \n " , query - > friendlyseeblocks ) ;
2018-03-18 09:23:24 +00:00
Printf ( " Target: %s \n " , query - > target ? query - > target - > GetClass ( ) - > TypeName . GetChars ( ) : " - " ) ;
Printf ( " Last enemy: %s \n " , query - > lastenemy ? query - > lastenemy - > GetClass ( ) - > TypeName . GetChars ( ) : " - " ) ;
2016-03-01 15:47:10 +00:00
}
}