// Emacs style mode select	 -*- C++ -*- 
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// DESCRIPTION:
//		Play functions, animation, global header.
//
//-----------------------------------------------------------------------------


#ifndef __P_LOCAL__
#define __P_LOCAL__

#include <float.h>
#include "doomtype.h"
#include "vectors.h"
#include "dobject.h"
// [BB] New #includes.
#include "po_man.h"
#include "d_player.h"
#include "d_event.h"

const double NO_VALUE = FLT_MAX;

class player_t;
class AActor;
struct FPlayerStart;
class PClassActor;
class APlayerPawn;
struct line_t;
struct sector_t;
struct msecnode_t;
struct portnode_t;
struct secplane_t;
struct FCheckPosition;
struct FTranslatedLineTarget;
struct FLinePortal;

#include <stdlib.h>

#define STEEPSLOPE		(46342/65536.)	// [RH] Minimum floorplane.c value for walking

#define BONUSADD		6

// Inspired by Maes
extern int bmapnegx;
extern int bmapnegy;

//#define GRAVITY 		FRACUNIT
#define MAXMOVE 		(30.)

#define TALKRANGE		(128.)
#define USERANGE		(64.)

#define DEFMELEERANGE		(64.)
#define SAWRANGE		(64.+(1./65536.))	// use meleerange + 1 so the puff doesn't skip the flash (i.e. plays all states)
#define MISSILERANGE	(32*64.)
#define PLAYERMISSILERANGE	(8192.)	// [RH] New MISSILERANGE for players

// follow a player exlusively for 3 seconds
// No longer used.
// #define BASETHRESHOLD	100


//
// P_PSPR
//
void P_SetupPsprites (player_t* curplayer, bool startweaponup);
void P_DropWeapon (player_t* player);


//
// P_USER
//
void	P_FallingDamage (AActor *ent);
bool	PLAYER_Responder( event_t *pEvent );
void	P_PlayerThink (player_t *player);
/*
void	P_PredictPlayer (player_t *player);
void	P_UnPredictPlayer ();
void	P_PredictionLerpReset();
*/

//
// P_MOBJ
//

#define SPF_TEMPPLAYER		1	// spawning a short-lived dummy player
#define SPF_WEAPONFULLYUP	2	// spawn with weapon already raised
#define SPF_CLIENTUPDATE	4	// [BB]

APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags=0);

int P_FaceMobj (AActor *source, AActor *target, DAngle *delta);
bool P_SeekerMissile (AActor *actor, double thresh, double turnMax, bool precise = false, bool usecurspeed=false);

enum EPuffFlags
{
	PF_HITTHING = 1,
	PF_MELEERANGE = 2,
	PF_TEMPORARY = 4,
	PF_HITTHINGBLEED = 8,
	PF_NORANDOMZ = 16
};

// [BC] Added bTellClientToSpawn.
AActor *P_SpawnPuff(AActor *source, PClassActor *pufftype, const DVector3 &pos, DAngle hitdir, DAngle particledir, int updown, int flags = 0, AActor *vict = NULL, bool bTellClientToSpawn = true);
void	P_SpawnBlood (const DVector3 &pos, DAngle angle, int damage, AActor *originator);
void	P_BloodSplatter (const DVector3 &pos, AActor *originator, DAngle hitangle);
void	P_BloodSplatter2 (const DVector3 &pos, AActor *originator, DAngle hitangle);
void	P_RipperBlood (AActor *mo, AActor *bleeder);
int		P_GetThingFloorType (AActor *thing);
void	P_ExplodeMissile (AActor *missile, line_t *explodeline, AActor *target);

AActor *P_OldSpawnMissile(AActor *source, AActor *owner, AActor *dest, PClassActor *type);
AActor *P_SpawnMissile (AActor* source, AActor* dest, PClassActor *type, AActor* owner = NULL, const bool bSpawnOnClient = false ); // [BB] Added bSpawnOnClient.
AActor *P_SpawnMissileZ(AActor* source, double z, AActor* dest, PClassActor *type, const bool bSpawnOnClient = false); // [BB] Added bSpawnOnClient.
AActor *P_SpawnMissileXYZ(DVector3 pos, AActor *source, AActor *dest, PClassActor *type, bool checkspawn = true, AActor *owner = NULL, const bool bSpawnOnClient = false); // [BB] Added bSpawnOnClient.
AActor *P_SpawnMissileAngle(AActor *source, PClassActor *type, DAngle angle, double vz, const bool bSpawnOnClient = false); // [BB] Added bSpawnOnClient.
AActor *P_SpawnMissileAngleZ(AActor *source, double z, PClassActor *type, DAngle angle, double vz, const bool bSpawnOnClient = false); // [BB] Added bSpawnOnClient.
AActor *P_SpawnMissileAngleZSpeed(AActor *source, double z, PClassActor *type, DAngle angle, double vz, double speed, AActor *owner = NULL, bool checkspawn = true, const bool bSpawnOnClient = false); // [BB] Added bSpawnOnClient.
AActor *P_SpawnMissileZAimed(AActor *source, double z, AActor *dest, PClassActor *type, const bool bSpawnOnClient = false); // [BB] Added bSpawnOnClient.


AActor *P_SpawnPlayerMissile (AActor* source, PClassActor *type);
AActor *P_SpawnPlayerMissile (AActor *source, PClassActor *type, DAngle angle, bool bSpawnSound = true );

AActor *P_SpawnPlayerMissile (AActor *source, double x, double y, double z, PClassActor *type, DAngle angle, 
							  FTranslatedLineTarget *pLineTarget = NULL, AActor **MissileActor = NULL, bool nofreeaim = false, bool noautoaim = false, int aimflags = 0, bool bSpawnSound = true, bool bSpawnOnClient = true);

// [BB]
inline void P_SpawnPlayerMissileWithPossibleSpread (AActor* source, PClassActor *type)
{
	P_SpawnPlayerMissile ( source, type );

	// [BB] Apply spread.
	if ( source && source->player && source->player->cheats2 & CF2_SPREAD )
	{
		P_SpawnPlayerMissile( source, type, source->Angles.Yaw + 15., false );
		P_SpawnPlayerMissile( source, type, source->Angles.Yaw - 15., false );
	}
}

// [BB]
inline void P_SpawnPlayerMissileWithPossibleSpread (AActor* source, PClassActor *type, DAngle angle)
{
	P_SpawnPlayerMissile ( source, type, angle );

	// [BB] Apply spread.
	if ( source && source->player && source->player->cheats2 & CF2_SPREAD )
	{
		P_SpawnPlayerMissile( source, type, angle + 15., false );
		P_SpawnPlayerMissile( source, type, angle - 15., false );
	}
}


void P_CheckFakeFloorTriggers(AActor *mo, double oldz, bool oldz_has_viewheight = false);

AActor *P_SpawnSubMissile (AActor *source, PClassActor *type, AActor *target);	// Strife uses it


//
// [RH] P_THINGS
//
extern FClassMap SpawnableThings;
extern FClassMap StrifeTypes;

bool	P_Thing_Spawn (int tid, AActor *source, int type, DAngle angle, bool fog, int newtid);
bool	P_Thing_Projectile (int tid, AActor *source, int type, const char * type_name, DAngle angle,
			double speed, double vspeed, int dest, AActor *forcedest, int gravity, int newtid,
			bool leadTarget);

bool	P_MoveThing(AActor *source, const DVector3 &pos, bool fog);
bool	P_Thing_Move (int tid, AActor *source, int mapspot, bool fog);
int		P_Thing_Damage (int tid, AActor *whofor0, int amount, FName type);
void	P_Thing_SetVelocity(AActor *actor, const DVector3 &vec, bool add, bool setbob);
void P_RemoveThing(AActor * actor);
bool P_Thing_Raise(AActor *thing, AActor *raiser, bool byClient = false); // [BB/EP] Added 'byClient'.
bool P_Thing_CanRaise(AActor *thing);
PClassActor *P_GetSpawnableType(int spawnnum);
void InitSpawnablesFromMapinfo();
int P_Thing_CheckInputNum(player_t *p, int inputnum);
int P_Thing_Warp(AActor *caller, AActor *reference, double xofs, double yofs, double zofs, DAngle angle, int flags, double heightoffset, double radiusoffset, DAngle pitch);
int P_Thing_CheckProximity(AActor *self, PClass *classname, double distance, int count, int flags, int ptr, bool counting = false);

enum
{
	// These are the original inputs sent by the player.
	INPUT_OLDBUTTONS,
	INPUT_BUTTONS,
	INPUT_PITCH,
	INPUT_YAW,
	INPUT_ROLL,
	INPUT_FORWARDMOVE,
	INPUT_SIDEMOVE,
	INPUT_UPMOVE,

	// These are the inputs, as modified by P_PlayerThink().
	// Most of the time, these will match the original inputs, but
	// they can be different if a player is frozen or using a
	// chainsaw.
	MODINPUT_OLDBUTTONS,
	MODINPUT_BUTTONS,
	MODINPUT_PITCH,
	MODINPUT_YAW,
	MODINPUT_ROLL,
	MODINPUT_FORWARDMOVE,
	MODINPUT_SIDEMOVE,
	MODINPUT_UPMOVE
};
enum CPXF
{
	CPXF_ANCESTOR = 1 << 0,
	CPXF_LESSOREQUAL = 1 << 1,
	CPXF_NOZ = 1 << 2,
	CPXF_COUNTDEAD = 1 << 3,
	CPXF_DEADONLY = 1 << 4,
	CPXF_EXACT = 1 << 5,
	CPXF_SETTARGET = 1 << 6,
	CPXF_SETMASTER = 1 << 7,
	CPXF_SETTRACER = 1 << 8,
	CPXF_FARTHEST = 1 << 9,
	CPXF_CLOSEST = 1 << 10,
	CPXF_SETONPTR = 1 << 11,
	CPXF_CHECKSIGHT = 1 << 12,
};

enum WARPF
{
	WARPF_ABSOLUTEOFFSET = 0x1,
	WARPF_ABSOLUTEANGLE  = 0x2,
	WARPF_USECALLERANGLE = 0x4,

	WARPF_NOCHECKPOSITION = 0x8,

	WARPF_INTERPOLATE       = 0x10,
	WARPF_WARPINTERPOLATION = 0x20,
	WARPF_COPYINTERPOLATION = 0x40,

	WARPF_STOP             = 0x80,
	WARPF_TOFLOOR          = 0x100,
	WARPF_TESTONLY         = 0x200,
	WARPF_ABSOLUTEPOSITION = 0x400,
	WARPF_BOB              = 0x800,
	WARPF_MOVEPTR          = 0x1000,
	WARPF_USEPTR           = 0x2000,
	WARPF_USETID           = 0x2000,
	WARPF_COPYVELOCITY		= 0x4000,
	WARPF_COPYPITCH			= 0x8000,
};

enum PCM
{
	PCM_DROPOFF =		1,
	PCM_NOACTORS =		1 << 1,
	PCM_NOLINES =		1 << 2,
};


AActor *P_BlockmapSearch (AActor *mo, int distance, AActor *(*check)(AActor*, int, void *), void *params = NULL);
AActor *P_RoughMonsterSearch (AActor *mo, int distance, bool onlyseekable=false, bool frontonly = false);

//
// P_MAP
//


struct spechit_t
{
	line_t *line;
	DVector2 Oldrefpos;
	DVector2 Refpos;
};

extern TArray<spechit_t> spechit;
extern TArray<spechit_t> portalhit;


bool	P_TestMobjLocation (AActor *mobj);
bool	P_TestMobjZ (AActor *mobj, bool quick=true, AActor **pOnmobj = NULL);
bool P_CheckPosition(AActor *thing, const DVector2 &pos, bool actorsonly = false);
bool P_CheckPosition(AActor *thing, const DVector2 &pos, FCheckPosition &tm, bool actorsonly = false);
AActor	*P_CheckOnmobj (AActor *thing);
void	P_FakeZMovement (AActor *mo);
bool	P_TryMove(AActor* thing, const DVector2 &pos, int dropoff, const secplane_t * onfloor, FCheckPosition &tm, bool missileCheck = false);
bool	P_TryMove(AActor* thing, const DVector2 &pos, int dropoff, const secplane_t * onfloor = NULL, bool missilecheck = false);

bool	P_CheckMove(AActor *thing, const DVector2 &pos, int flags = 0);
bool	P_OldTryMove (AActor* thing, fixed_t x, fixed_t y, bool dropoff, bool onfloor = false);
void	P_ApplyTorque(AActor *mo);

bool	P_TeleportMove(AActor* thing, const DVector3 &pos, bool telefrag, bool modifyactor = true);	// [RH] Added z and telefrag parameters

void	P_PlayerStartStomp (AActor *actor, bool mononly=false);		// [RH] Stomp on things for a newly spawned player
void	P_SlideMove (AActor* mo, const DVector2 &pos, int numsteps);
void	P_OldSlideMove (AActor* mo);
bool	P_BounceWall (AActor *mo);
bool	P_BounceActor (AActor *mo, AActor *BlockingMobj, bool ontop);
bool	P_CheckSight (AActor *t1, AActor *t2, int flags=0);

enum ESightFlags
{
	SF_IGNOREVISIBILITY=1,
	SF_SEEPASTSHOOTABLELINES=2,
	SF_SEEPASTBLOCKEVERYTHING=4,
	SF_IGNOREWATERBOUNDARY=8
};

void	P_ResetSightCounters (bool full);
void	P_ResetSpawnCounters( void ); // [BC]
bool	P_TalkFacing (AActor *player);
void	P_UseLines (player_t* player);
bool	P_UsePuzzleItem (AActor *actor, int itemType);

enum
{
	FFCF_ONLYSPAWNPOS = 1,
	FFCF_SAMESECTOR = 2,
	FFCF_ONLY3DFLOORS = 4,	// includes 3D midtexes
	FFCF_3DRESTRICT = 8,	// ignore 3D midtexes and floors whose floorz are above thing's z
	FFCF_NOPORTALS = 16,	// ignore portals (considers them impassable.)
	FFCF_NOFLOOR = 32,
	FFCF_NOCEILING = 64,
	FFCF_RESTRICTEDPORTAL = 128,	// current values in the iterator's return are through a restricted portal type (i.e. some features are blocked.)
	FFCF_NODROPOFF = 256,			// Caller does not need a dropoff (saves some time when checking portals)
	FFCF_INCLUDE3DFLOORS = 512,	// [BB] Includes 3D floors even if FFCF_3DRESTRICT is set. Workaround for client side spawning.
};
void	P_FindFloorCeiling (AActor *actor, int flags=0);

bool	P_ChangeSector (sector_t* sector, int crunch, double amt, int floorOrCeil, bool isreset, bool instant = false);

DAngle P_AimLineAttack(AActor *t1, DAngle angle, double distance, FTranslatedLineTarget *pLineTarget = NULL, DAngle vrange = 0., int flags = 0, AActor *target = NULL, AActor *friender = NULL);

enum	// P_AimLineAttack flags
{
	ALF_FORCENOSMART = 1,
	ALF_CHECK3D = 2,
	ALF_CHECKNONSHOOTABLE = 4,
	ALF_CHECKCONVERSATION = 8,
	ALF_NOFRIENDS = 16,
	ALF_PORTALRESTRICT = 32,	// only work through portals with a global offset (to be used for stuff that cannot remember the calculated FTranslatedLineTarget info)

	// [Proteh] Don't use backwards reconciliation
	ALF_NOUNLAGGED = 64
};

enum	// P_LineAttack flags
{
	LAF_ISMELEEATTACK = 1,
	LAF_NORANDOMPUFFZ = 2,
	LAF_NOIMPACTDECAL = 4,
	LAF_NOINTERACT =	8,
	LAF_TARGETISSOURCE = 16,
};

AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, PClassActor *pufftype, int flags = 0, FTranslatedLineTarget *victim = NULL, int *actualdamage = NULL);
AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, DAngle pitch, int damage, FName damageType, FName pufftype, int flags = 0, FTranslatedLineTarget *victim = NULL, int *actualdamage = NULL);

void	P_TraceBleed(int damage, const DVector3 &pos, AActor *target, DAngle angle, DAngle pitch);
void	P_TraceBleed(int damage, AActor *target, DAngle angle, DAngle pitch);

void	P_TraceBleed (int damage, AActor *target, AActor *missile);		// missile version
void	P_TraceBleed(int damage, FTranslatedLineTarget *t, AActor *puff);		// hitscan version
void	P_TraceBleed (int damage, AActor *target);		// random direction version
bool	P_HitFloor (AActor *thing);
bool	P_HitWater (AActor *thing, sector_t *sec, const DVector3 &pos, bool checkabove = false, bool alert = true, bool force = false);
void	P_CheckSplash(AActor *self, double distance);

struct FRailParams
{
	AActor *source = nullptr;
	int damage = 0;
	double offset_xy = 0;
	double offset_z = 0;
	int color1 = 0, color2 = 0;
	double maxdiff = 0;
	int flags = 0;
	PClassActor *puff = nullptr;
	DAngle angleoffset = 0.;
	DAngle pitchoffset = 0.;
	double distance = 8192;
	int duration = 0;
	double sparsity = 1.0;
	double drift = 1.0;
	PClassActor *spawnclass = nullptr;
	int SpiralOffset = 270;
	int limit = 0;
};	// [RH] Shoot a railgun

void P_RailAttack(FRailParams *params);
void P_RailAttackWithPossibleSpread (FRailParams *params);	// [BB] Shoot a railgun with spread applied if necessary

enum	// P_RailAttack / A_RailAttack / A_CustomRailgun / P_DrawRailTrail flags
{	
	RAF_SILENT = 1,
	RAF_NOPIERCE = 2,
	RAF_EXPLICITANGLE = 4,
	RAF_FULLBRIGHT = 8,
	RAF_CENTERZ = 16,
	RAF_NORANDOMPUFFZ = 32,
};


bool	P_CheckMissileSpawn(AActor *missile, double maxdist, bool bExplode = true);

void	P_PlaySpawnSound(AActor *missile, AActor *spawner);

// [RH] Position the chasecam
void	P_AimCamera (AActor *t1, DVector3 &, DAngle &, sector_t *&sec, bool &unlinked);

// [RH] Means of death
enum
{
	RADF_HURTSOURCE = 1,
	RADF_NOIMPACTDAMAGE = 2,
	RADF_SOURCEISSPOT = 4,
	RADF_NODAMAGE = 8,
	RADF_THRUSTZ = 16,
};
int	P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance, 
						FName damageType, int flags, int fulldamagedistance=0);

void	P_DelSeclist(msecnode_t *, msecnode_t *sector_t::*seclisthead);
void	P_DelSeclist(portnode_t *, portnode_t *FLinePortal::*seclisthead);

template<class nodetype, class linktype>
nodetype *P_AddSecnode(linktype *s, AActor *thing, nodetype *nextnode, nodetype *&sec_thinglist);

template<class nodetype, class linktype>
nodetype* P_DelSecnode(nodetype *, nodetype *linktype::*head);

msecnode_t *P_CreateSecNodeList(AActor *thing, double radius, msecnode_t *sector_list, msecnode_t *sector_t::*seclisthead);
double	P_GetMoveFactor(const AActor *mo, double *frictionp);	// phares  3/6/98
double		P_GetFriction(const AActor *mo, double *frictionfactor);

// [RH] 
const secplane_t * P_CheckSlopeWalk(AActor *actor, DVector2 &move);

// [TP]
bool P_CheckUnblock ( AActor *pActor1, AActor *pActor2 );

//
// P_SETUP
//
extern BYTE*			rejectmatrix;	// for fast sight rejection



//
// P_INTER
//
void P_TouchSpecialThing (AActor *special, AActor *toucher);
int  P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags=0, DAngle angle = 0.);
void P_PoisonMobj (AActor *target, AActor *inflictor, AActor *source, int damage, int duration, int period, FName type);
bool P_GiveBody (AActor *actor, int num, int max=0);
bool P_PoisonPlayer (player_t *player, AActor *poisoner, AActor *source, int poison);
void P_PoisonDamage (player_t *player, AActor *source, int damage, bool playPainSound);

enum EDmgFlags
{
	DMG_NO_ARMOR = 1,
	DMG_INFLICTOR_IS_PUFF = 2,
	DMG_THRUSTLESS = 4,
	DMG_FORCED = 8,
	DMG_NO_FACTOR = 16,
	DMG_PLAYERATTACK = 32,
	DMG_FOILINVUL = 64,
	DMG_FOILBUDDHA = 128,
	DMG_NO_PROTECT = 256,
	DMG_USEANGLE = 512,
};


//
// P_SPEC
//
bool P_AlignFlat (int linenum, int side, int fc);

// [BB] Moved here from po_man.cpp
class DRotatePoly : public DPolyAction
{
	DECLARE_CLASS (DRotatePoly, DPolyAction)
public:
	DRotatePoly (int polyNum);
	void Tick ();
	void UpdateToClient( ULONG ulClient );

private:
	DRotatePoly ();

	friend bool EV_RotatePoly (line_t *line, int polyNum, int speed, int byteAngle, int direction, bool overRide);
};


class DMovePoly : public DPolyAction
{
	DECLARE_CLASS (DMovePoly, DPolyAction)
public:
	DMovePoly (int polyNum);
	void Serialize(FSerializer &arc);
	void Tick ();
	virtual void UpdateToClient( ULONG ulClient ); // [WS] This needs to be virtual.

	DAngle	GetAngle( void );
	void	SetAngle( DAngle angle );

	const DVector2& GetSpeedV( void );
	void	SetSpeedV( const DVector2 &speedV );
protected:
	DMovePoly ();
	DAngle m_Angle;
	DVector2 m_Speedv;

	friend bool EV_MovePoly(line_t *line, int polyNum, double speed, DAngle angle, double dist, bool overRide);
};

class DMovePolyTo : public DPolyAction
{
	DECLARE_CLASS(DMovePolyTo, DPolyAction)
public:
	DMovePolyTo(int polyNum);
	void Serialize(FSerializer &arc);
	void Tick();
protected:
	DMovePolyTo();
	DVector2 m_Speedv;
	DVector2 m_Target;

	friend bool EV_MovePolyTo(line_t *line, int polyNum, double speed, const DVector2 &pos, bool overRide);
};


class DPolyDoor : public DMovePoly
{
	DECLARE_CLASS (DPolyDoor, DMovePoly)
public:
	DPolyDoor (int polyNum, podoortype_t type);
	void Serialize(FSerializer &arc);
	void Tick ();
	void UpdateToClient( ULONG ulClient );

	DAngle	GetDirection( void );
	void	SetDirection( DAngle direction );

	double	GetTotalDist( void );
	void	SetTotalDist( double dist );

	bool	GetClose( void );
	void	SetClose( bool bClose );

protected:
	DAngle m_Direction;
	double m_TotalDist;
	int m_Tics;
	int m_WaitTics;
	podoortype_t m_Type;
	bool m_Close;

	friend bool EV_OpenPolyDoor(line_t *line, int polyNum, double speed, DAngle angle, int delay, double distance, podoortype_t type);
private:
	DPolyDoor ();
};

#endif	// __P_LOCAL__
