// BLANKART
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file  p_spec.h
/// \brief Implements special effects:
///        Texture animation, height or lighting changes
///        according to adjacent sectors, respective
///        utility functions, etc.

#ifndef __P_SPEC__
#define __P_SPEC__

#ifdef __cplusplus
extern "C" {
#endif
	
extern mobj_t *skyboxmo[2]; // current skybox mobjs: 0 = viewpoint, 1 = centerpoint

extern mobj_t *skyboxviewpnts[16]; // array of MT_SKYBOX viewpoint mobjs
extern mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs

void P_InitSkyboxPoint(mobj_t *mobj, mapthing_t *mthing);

/** Animated texture descriptor
  * This keeps track of an animated texture or an animated flat.
  * \sa P_UpdateSpecials, P_InitPicAnims, animdef_t
  */
typedef struct
{
	INT32 picnum;    ///< The end flat number
	INT32 basepic;   ///< The start flat number
	INT32 numpics;   ///< Number of frames in the animation
	tic_t speed;     ///< Number of tics for which each frame is shown
} anim_t;

extern anim_t *lastanim;
extern anim_t *anims;

typedef struct
{
	UINT32 count;
	thinker_t **thinkers;
} thinkerlist_t;

/** Animated texture definition.
  * Used for loading an ANIMDEFS lump from a wad.
  *
  * Animations are defined by the first and last frame (i.e., flat or texture).
  * The animation sequence uses all flats between the start and end entry, in
  * the order found in the wad.
  *
  * \sa anim_t
  */
typedef struct
{
	SINT8 istexture; ///< True for a texture, false for a flat.
	char endname[9]; ///< Name of the last frame, null-terminated.
	char startname[9]; ///< Name of the first frame, null-terminated.
	INT32 speed; ///< Number of tics for which each frame is shown.
} ATTRPACK animdef_t;

// Amount (dx, dy) vector linedef is shifted right to get scroll amount
#define SCROLL_SHIFT 5

typedef enum
{
	TMM_DOUBLESIZE      = 1,
	TMM_SILENT          = 1<<1,
	TMM_ALLOWYAWCONTROL = 1<<2,
	TMM_SWING           = 1<<3,
	TMM_MACELINKS       = 1<<4,
	TMM_CENTERLINK      = 1<<5,
	TMM_CLIP            = 1<<6,
	TMM_ALWAYSTHINK     = 1<<7,
} textmapmaceflags_t;

typedef enum
{
	TMDA_BOTTOMOFFSET = 1,
	TMDA_BOTTOM       = 1<<1,
	TMDA_MIDDLE       = 1<<2,
	TMDA_TOP          = 1<<3,
} textmapdronealignment_t;

typedef enum
{
	TMSF_RETRACTED  = 1,
	TMSF_INTANGIBLE = 1<<1,
} textmapspikeflags_t;

typedef enum
{
	TMWPF_DISABLED   = 1,
	TMWPF_SHORTCUT   = 1<<1,
	TMWPF_NORESPAWN  = 1<<2,
	TMWPF_FINISHLINE = 1<<3,
} textmapwaypointflags_t;

typedef enum
{
	TMFF_AIMLESS    = 1,
	TMFF_STATIONARY = 1<<1,
	TMFF_HOP        = 1<<2,
} textmapflickyflags_t;

typedef enum
{
	TMFH_NOFLAME = 1,
	TMFH_CORONA  = 1<<1,
} textmapflameholderflags_t;

typedef enum
{
	TMSPR_NOGRAVITY   = 1,
	TMSPR_ROTATEEXTRA = 1<<1,
	TMSPR_FORCEANGLE  = 1<<2,
} textmapspringflags_t;

typedef enum
{
	TMF_INVISIBLE       = 1,
	TMF_NODISTANCECHECK = 1<<1,
} textmapfanflags_t;

typedef enum
{
	TMGD_BACK  = 0,
	TMGD_RIGHT = 1,
	TMGD_LEFT  = 2,
} textmapguarddirection_t;

typedef enum
{
	TMNI_BONUSONLY = 1,
	TMNI_REVEAL    = 1<<1,
} textmapnightsitem_t;

typedef enum
{
	TMP_NORMAL    = 0,
	TMP_SLIDE     = 1,
	TMP_IMMOVABLE = 2,
	TMP_CLASSIC   = 3,
} textmappushabletype_t;

typedef enum
{
	TMMR_SAME   = 0,
	TMMR_WEAK   = 1,
	TMMR_STRONG = 2,
} textmapmonitorrespawn_t;

typedef enum
{
	TMF_GRAYSCALE = 1,
	TMF_SKIPINTRO = 1<<1,
} textmapfangflags_t;

typedef enum
{
	TMB_NODEATHFLING = 1,
	TMB_BARRIER      = 1<<1,
} textmapbrakflags_t;

typedef enum
{
	TMICF_INVERTTIMEATTACK = 1,
	TMICF_INVERTSIZE       = 1<<1,
} textmapitemcapsuleflags_t;

typedef enum
{
	TMMA_WARN = 1,
	TMMA_FLIP = 2,
} textmapmayarrow_t;

typedef enum
{
	TMEF_SKIPTALLY    = 1,
	TMEF_EMERALDCHECK = 1<<1,
} textmapexitflags_t;

typedef enum
{
	TMSP_NOTELEPORT = 1,
	TMSP_FORCESPIN  = 1<<1,
} textmapspeedpadflags_t;

//FOF flags
typedef enum
{
	TMFA_NOPLANES    = 1,
	TMFA_NOSIDES     = 1<<1,
	TMFA_INSIDES     = 1<<2,
	TMFA_ONLYINSIDES = 1<<3,
	TMFA_NOSHADE     = 1<<4,
	TMFA_SPLAT       = 1<<5,
} textmapfofappearance_t;

typedef enum
{
	TMFT_INTANGIBLETOP    = 1,
	TMFT_INTANGIBLEBOTTOM = 1<<1,
	TMFT_DONTBLOCKPLAYER  = 1<<2,
	TMFT_VISIBLEFROMINSIDE = (TMFT_INTANGIBLETOP|TMFT_INTANGIBLEBOTTOM|TMFT_DONTBLOCKPLAYER),
	TMFT_DONTBLOCKOTHERS  = 1<<3,
	TMFT_INTANGIBLE       = (TMFT_DONTBLOCKPLAYER|TMFT_DONTBLOCKOTHERS),
} textmapfoftangibility_t;

typedef enum
{
	TMFW_NOSIDES      = 1,
	TMFW_DOUBLESHADOW = 1<<1,
	TMFW_COLORMAPONLY = 1<<2,
	TMFW_NORIPPLE     = 1<<3,
	TMFW_GOOWATER     = 1<<4,
	TMFW_SPLAT        = 1<<5,
} textmapfofwater_t;

typedef enum
{
	TMFB_REVERSE  = 1,
	TMFB_SPINDASH = 1<<1,
	TMFB_DYNAMIC  = 1<<2,
} textmapfofbobbing_t;

typedef enum
{
	TMFC_NOSHADE     = 1,
	TMFC_NORETURN    = 1<<1,
	TMFC_AIRBOB      = 1<<2,
	TMFC_FLOATBOB    = 1<<3,
	TMFC_SPLAT       = 1<<4,
} textmapfofcrumbling_t;

typedef enum
{
	TMFR_REVERSE  = 1,
	TMFR_SPINDASH = 1<<1,
} textmapfofrising_t;

typedef enum
{
	TMFM_BRICK     = 1,
	TMFM_INVISIBLE = 1<<1,
} textmapfofmario_t;

typedef enum
{
	TMFB_TOUCH,
	TMFB_SPIN,
	TMFB_REGULAR,
	TMFB_STRONG,
} textmapfofbusttype_t;

typedef enum
{
	TMFB_PUSHABLES   = 1,
	TMFB_EXECUTOR    = 1<<1,
	TMFB_ONLYBOTTOM  = 1<<2,
	TMFB_SPLAT       = 1<<3,
} textmapfofbustflags_t;

typedef enum
{
	TMFL_NOBOSSES = 1,
	TMFL_SPLAT    = 1<<1,
} textmapfoflaserflags_t;

typedef enum
{
	TMT_CONTINUOUS           = 0,
	TMT_ONCE                 = 1,
	TMT_EACHTIMEMASK         = TMT_ONCE,
	TMT_EACHTIMEENTER        = 2,
	TMT_EACHTIMEENTERANDEXIT = 3,
} textmaptriggertype_t;

typedef enum
{
	TMXT_CONTINUOUS           = 0,
	TMXT_EACHTIMEMASK         = TMXT_CONTINUOUS,
	TMXT_EACHTIMEENTER        = 1,
	TMXT_EACHTIMEENTERANDEXIT = 2,
} textmapxtriggertype_t;

typedef enum
{
	TMF_HASALL        = 0,
	TMF_HASANY        = 1,
	TMF_HASEXACTLY    = 2,
	TMF_DOESNTHAVEALL = 3,
	TMF_DOESNTHAVEANY = 4,
} textmapflagcheck_t;

typedef enum
{
	TMT_RED  = 0,
	TMT_BLUE = 1,
} textmapteam_t;

typedef enum
{
	TMC_EQUAL = 0,
	TMC_LTE   = 1,
	TMC_GTE   = 2,
} textmapcomparison_t;

typedef enum
{
	TMNP_FASTEST   = 0,
	TMNP_SLOWEST   = 1,
	TMNP_TRIGGERER = 2,
} textmapnightsplayer_t;

typedef enum
{
	TMN_ALWAYS       = 0,
	TMN_FROMNONIGHTS = 1,
	TMN_FROMNIGHTS   = 2,
} textmapnighterizeoptions_t;

typedef enum
{
	TMN_BONUSLAPS       = 1,
	TMN_LEVELCOMPLETION = 1<<2,
} textmapnightserizeflags_t;

typedef enum
{
	TMD_ALWAYS         = 0,
	TMD_NOBODYNIGHTS   = 1,
	TMD_SOMEBODYNIGHTS = 2,
} textmapdenighterizeoptions_t;

typedef enum
{
	TMS_IFENOUGH    = 0,
	TMS_IFNOTENOUGH = 1,
	TMS_ALWAYS      = 2,
} textmapspherescheck_t;

typedef enum
{
	TMI_BONUSLAPS = 1,
	TMI_ENTER     = 1<<2,
} textmapideyacaptureflags_t;

typedef enum
{
	TMP_FLOOR = 0,
	TMP_CEILING = 1,
	TMP_BOTH = 2,
} textmapplanes_t;

typedef enum
{
	TMT_ADD          = 0,
	TMT_REMOVE       = 1,
	TMT_REPLACEFIRST = 2,
} textmaptagoptions_t;

typedef enum
{
	TMT_SILENT       = 1,
	TMT_KEEPANGLE    = 1<<1,
	TMT_KEEPMOMENTUM = 1<<2,
	TMT_RELATIVE     = 1<<3,
} textmapteleportflags_t;

typedef enum
{
	TMM_ALLPLAYERS = 1,
	TMM_OFFSET = 1<<1,
	TMM_FADE = 1<<2,
	TMM_NORELOAD = 1<<3,
	TMM_FORCERESET = 1<<4,
	TMM_NOLOOP = 1<<5,
	TMM_NOCREDIT = 1<<6,
} textmapmusicflags_t;

typedef enum
{
	TMSS_TRIGGERMOBJ   = 0,
	TMSS_TRIGGERSECTOR = 1,
	TMSS_NOWHERE       = 2,
	TMSS_TAGGEDSECTOR  = 3,
} textmapsoundsource_t;

typedef enum
{
	TMSL_EVERYONE     = 0,
	TMSL_TRIGGERER    = 1,
	TMSL_TAGGEDSECTOR = 2,
} textmapsoundlistener_t;

typedef enum
{
	TML_SECTOR  = 0,
	TML_FLOOR   = 1,
	TML_CEILING = 2,
} textmaplightareas_t;

typedef enum
{
	TMLC_NOSECTOR  = 1,
	TMLC_NOFLOOR   = 1<<1,
	TMLC_NOCEILING = 1<<2,
} textmaplightcopyflags_t;

typedef enum
{
	TMF_RELATIVE = 1,
	TMF_OVERRIDE = 1<<1,
	TMF_TICBASED = 1<<2,
} textmapfadeflags_t;

typedef enum
{
	TMB_USETARGET = 1,
	TMB_SYNC      = 1<<1,
} textmapblinkinglightflags_t;

typedef enum
{
	TMFR_NORETURN  = 1,
	TMFR_CHECKFLAG = 1<<1,
} textmapfofrespawnflags_t;

typedef enum
{
	TMST_RELATIVE          = 1,
	TMST_DONTDOTRANSLUCENT = 1<<1,
} textmapsettranslucencyflags_t;

typedef enum
{
	TMFT_RELATIVE          = 1,
	TMFT_OVERRIDE          = 1<<1,
	TMFT_TICBASED          = 1<<2,
	TMFT_IGNORECOLLISION   = 1<<3,
	TMFT_GHOSTFADE         = 1<<4,
	TMFT_DONTDOTRANSLUCENT = 1<<5,
	TMFT_DONTDOEXISTS      = 1<<6,
	TMFT_DONTDOLIGHTING    = 1<<7,
	TMFT_DONTDOCOLORMAP    = 1<<8,
	TMFT_USEEXACTALPHA     = 1<<9,
} textmapfadetranslucencyflags_t;

typedef enum
{
	TMS_VIEWPOINT   = 0,
	TMS_CENTERPOINT = 1,
	TMS_BOTH        = 2,
} textmapskybox_t;

typedef enum
{
	TMP_CLOSE          = 1,
	TMP_RUNPOSTEXEC    = 1<<1,
	TMP_CALLBYNAME     = 1<<2,
	TMP_KEEPCONTROLS   = 1<<3,
	TMP_KEEPREALTIME   = 1<<4,
	//TMP_ALLPLAYERS     = 1<<5,
	//TMP_FREEZETHINKERS = 1<<6,
} textmappromptflags_t;

typedef enum
{
	TMF_NOCHANGE = 0,
	TMF_ADD      = 1,
	TMF_REMOVE   = 2,
} textmapsetflagflags_t;

typedef enum
{
	TMSD_FRONT = 0,
	TMSD_BACK = 1,
	TMSD_FRONTBACK = 2,
} textmapsides_t;

typedef enum
{
	TMS_SCROLLCARRY = 0,
	TMS_SCROLLONLY = 1,
	TMS_CARRYONLY = 2,
} textmapscroll_t;

typedef enum
{
	TMST_REGULAR = 0,
	TMST_ACCELERATIVE = 1,
	TMST_DISPLACEMENT = 2,
	TMST_TYPEMASK = 3,
	TMST_NONEXCLUSIVE = 4,
} textmapscrolltype_t;

typedef enum
{
	TMPF_SLIDE = 1,
	TMPF_NONEXCLUSIVE = 1<<1,
} textmappusherflags_t;

typedef enum
{
	TMPP_NOZFADE      = 1,
	TMPP_PUSHZ        = 1<<1,
	TMPP_NONEXCLUSIVE = 1<<2,
} textmappointpushflags_t;

typedef enum
{
	TMB_TRANSLUCENT     = 0,
	TMB_ADD             = 1,
	TMB_SUBTRACT        = 2,
	TMB_REVERSESUBTRACT = 3,
	TMB_MODULATE        = 4,
} textmapblendmodes_t;

typedef enum
{
	TMCFF_FLIP       = 1,
	TMCFF_SINGLEUSE  = 1<<1,
	TMCFF_NEEDSECTOR = 1<<2,
	TMCFF_NOSPREAD   = 1<<3,
} textmapcrossfinishflags_t;

typedef enum
{
	TMCRF_FRONTONLY = 1,
} textmapcrossrespawnflags_t;

typedef enum
{
	TMBOT_NORUBBERBAND = 1,
	TMBOT_NOCONTROL    = 1<<1,
	TMBOT_FORCEDIR     = 1<<2,
	TMBOT_AIRDROP      = 1<<3,
} textmapbotcontroller_t;

typedef enum
{
	TMLOOP_ALPHA = 0,
	TMLOOP_BETA  = 1,
} textmaploopendpointtype_t;

// GETSECSPECIAL (specialval, section)
//
// Pulls out the special # from a particular section.
//
#define GETSECSPECIAL(i,j) ((i >> ((j-1)*4))&15)

// This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize.
#define MAXFLATSIZE (2048<<FRACBITS)

// Something that should've been done long ago???
// We won't be using epicenter or radius anytime soon so I don't think it's worth it yet.
void P_StartQuake(fixed_t intensity, tic_t time);

// at game start
void P_InitPicAnims(void);

// at map load
void P_InitSpecials(void);
void P_ApplyFlatAlignment(sector_t* sector, angle_t flatangle, fixed_t xoffs, fixed_t yoffs, boolean floor, boolean ceiling);
fixed_t P_GetSectorGravityFactor(sector_t *sec);
void P_SpawnSpecials(boolean fromnetsave);
void P_SpawnSpecialsThatRequireObjects(boolean fromnetsave);

// every tic
void P_UpdateSpecials(void);
sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number);
sector_t *P_MobjTouchingSectorSpecialFlag(mobj_t *mo, sectorspecialflags_t flag);
sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 number);
sector_t *P_PlayerTouchingSectorSpecialFlag(player_t *player, sectorspecialflags_t flag);
void P_PlayerInSpecialSector(player_t *player);
void P_CheckMobjTrigger(mobj_t *mobj, boolean pushable);
void P_CheckMobjTouchingSectorActions(mobj_t *mobj, boolean continuous, boolean sectorchanged);
sector_t *P_FindPlayerTrigger(player_t *player, line_t *sourceline);
boolean P_IsPlayerValid(size_t playernum);
boolean P_CanPlayerTrigger(size_t playernum);
void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *roversector);
sector_t *P_ThingOnSpecial3DFloor(mobj_t *mo);
void P_CheckSectorTransitionalEffects(mobj_t *thing, sector_t *prevsec, boolean wasgrounded);

fixed_t P_FindLowestFloorSurrounding(sector_t *sec);
fixed_t P_FindHighestFloorSurrounding(sector_t *sec);

fixed_t P_FindNextHighestFloor(sector_t *sec, fixed_t currentheight);
fixed_t P_FindNextLowestFloor(sector_t *sec, fixed_t currentheight);

fixed_t P_FindLowestCeilingSurrounding(sector_t *sec);
fixed_t P_FindHighestCeilingSurrounding(sector_t *sec);

INT32 P_FindMinSurroundingLight(sector_t *sector, INT32 max);

void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing);
void P_PushSpecialLine(line_t *line, mobj_t *thing);
void P_ActivateThingSpecial(mobj_t *mo, mobj_t *source);

mobj_t* P_FindObjectTypeFromTag(mobjtype_t type, mtag_t tag);

//
// Special activation info
//
struct activator_t
{
	mobj_t *mo;
	line_t *line;
	UINT8 side;
	sector_t *sector;
	polyobj_t *po;
	boolean fromLineSpecial; // Backwards compat for ACS
};

boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, char **stringargs);
boolean P_CanActivateSpecial(INT16 special);

boolean P_AllowFriction(mobj_t *mobj);

void P_SetupSignExit(player_t *player);

boolean P_IsMobjTouchingSectorPlane(mobj_t *mo, sector_t *sec);
boolean P_IsMobjTouching3DFloor(mobj_t *mo, ffloor_t *ffloor, sector_t *sec);
boolean P_IsMobjTouchingPolyobj(mobj_t *mo, polyobj_t *po, sector_t *polysec);

void P_SwitchWeather(preciptype_t newWeather);

boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller);
void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller);
void P_RunNightserizeExecutors(mobj_t *actor);
void P_RunDeNightserizeExecutors(mobj_t *actor);
void P_RunNightsLapExecutors(mobj_t *actor);
void P_RunNightsCapsuleTouchExecutors(mobj_t *actor, boolean entering, boolean enoughspheres);

UINT16 P_GetFFloorID(ffloor_t *fflr);
ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id);

// Use this when you don't know the type of your thinker data struct but need to access its thinker.
struct thinkerdata_t
{
	thinker_t thinker;
};

//
// P_LIGHTS
//
/** Fire flicker action structure.
  */
struct fireflicker_t
{
	thinker_t thinker; ///< The thinker in use for the effect.
	sector_t *sector;  ///< The sector where action is taking place.
	INT32 count;
	INT32 resetcount;
	INT16 maxlight;    ///< The brightest light level to use.
	INT16 minlight;    ///< The darkest light level to use.
};

struct lightflash_t
{
	thinker_t thinker;
	sector_t *sector;
	INT32 maxlight;
	INT32 minlight;
};

/** Laser block thinker.
  */
struct laserthink_t
{
	thinker_t thinker; ///< Thinker structure for laser.
	INT16 tag;
	line_t *sourceline;
	UINT8 nobosses;
};

/** Strobe light action structure..
  */
struct strobe_t
{
	thinker_t thinker; ///< The thinker in use for the effect.
	sector_t *sector;  ///< The sector where the action is taking place.
	INT32 count;
	INT16 minlight;    ///< The minimum light level to use.
	INT16 maxlight;    ///< The maximum light level to use.
	INT32 darktime;    ///< How INT32 to use minlight.
	INT32 brighttime;  ///< How INT32 to use maxlight.
};

struct glow_t
{
	thinker_t thinker;
	sector_t *sector;
	INT16 minlight;
	INT16 maxlight;
	INT16 direction;
	INT16 speed;
};

/** Thinker struct for fading lights.
  */
struct lightlevel_t
{
	thinker_t thinker;		///< Thinker in use for the effect.
	sector_t *sector;		///< Sector where action is taking place.
	INT16 sourcelevel;		///< Light level we're fading from.
	INT16 destlevel;		///< Light level we're fading to.

	fixed_t fixedcurlevel;	///< Fixed point for current light level.
	fixed_t fixedpertic;	///< Fixed point for increment per tic.
	// The reason for those two above to be fixed point is to deal with decimal values that would otherwise get trimmed away.
	INT32 timer;			///< Internal timer.
};

#define GLOWSPEED 8
#define STROBEBRIGHT 5
#define FASTDARK 15
#define SLOWDARK 35

void P_RemoveLighting(sector_t *sector);

void T_FireFlicker(fireflicker_t *flick);
fireflicker_t *P_SpawnAdjustableFireFlicker(sector_t *sector, INT16 lighta, INT16 lightb, INT32 length);
void T_LightningFlash(lightflash_t *flash);
void T_StrobeFlash(strobe_t *flash);

void P_SpawnLightningFlash(sector_t *sector);
strobe_t * P_SpawnAdjustableStrobeFlash(sector_t *sector, INT16 lighta, INT16 lightb, INT32 darktime, INT32 brighttime, boolean inSync);

void T_Glow(glow_t *g);
glow_t *P_SpawnAdjustableGlowingLight(sector_t *sector, INT16 lighta, INT16 lightb, INT32 length);

void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean ticbased);
void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased, boolean force, boolean relative);
void T_LightFade(lightlevel_t *ll);

typedef enum
{
	floor_special,
	ceiling_special,
	lighting_special,
} special_e;

//
// P_CEILNG
//
typedef enum
{
	raiseToHighest,
	lowerToLowestFast,

	instantRaise, // instant-move for ceilings

	crushAndRaise,
	raiseAndCrush,
	crushCeilOnce,
	crushBothOnce,

	moveCeilingByFrontSector,
	instantMoveCeilingByFrontSector,

	moveCeilingByDistance,

	bounceCeiling,
	bounceCeilingCrush,
} ceiling_e;

/** Ceiling movement structure.
  */
struct ceiling_t
{
	thinker_t thinker;    ///< Thinker for the type of movement.
	ceiling_e type;       ///< Type of movement.
	sector_t *sector;     ///< Sector where the action is taking place.
	fixed_t bottomheight; ///< The lowest height to move to.
	fixed_t topheight;    ///< The highest height to move to.
	fixed_t speed;        ///< Ceiling speed.
	fixed_t delay;
	fixed_t delaytimer;
	UINT8 crush;           ///< Whether to crush things or not.

	INT32 texture;        ///< The number of a flat to use when done.
	INT32 direction;      ///< 1 = up, 0 = waiting, -1 = down.

	// ID
	INT16 tag;            ///< Tag of linedef executor to run when movement is done.
	fixed_t origspeed;    ///< The original, "real" speed.
	INT32 sourceline;     ///< Index of the source linedef

	fixed_t crushHeight;  ///< Crusher height
	fixed_t crushSpeed;   ///< Crusher speed
	fixed_t returnHeight; ///< Crusher return height
	fixed_t returnSpeed;  ///< Crusher return speed
};

#define CEILSPEED (FRACUNIT)

INT32 EV_DoCeiling(mtag_t tag, line_t *line, ceiling_e type);
INT32 EV_DoCeilingOLD(mtag_t tag, line_t *line, ceiling_e type);
void T_MoveCeiling(ceiling_t *ceiling);

boolean EV_DoRaiseCeilingToHighest(mtag_t tag);
boolean EV_DoLowerCeilingToLowestFast(mtag_t tag);
boolean EV_DoInstantRaiseCeiling(mtag_t tag);
boolean EV_DoMoveCeilingByHeight(mtag_t tag, fixed_t height, fixed_t speed, mtag_t chain, INT32 texture);
boolean EV_DoInstantMoveCeilingByHeight(mtag_t tag, fixed_t height, INT32 texture);
boolean EV_DoMoveCeilingByDistance(mtag_t tag, fixed_t distance, fixed_t speed, boolean instant);
boolean EV_DoBounceCeiling(mtag_t tag, boolean crush, fixed_t crushHeight, fixed_t crushSpeed, fixed_t returnHeight, fixed_t returnSpeed, INT32 delayInit, INT32 delay);

INT32 EV_DoCrush(mtag_t tag, line_t *line, ceiling_e type);
INT32 EV_DoCrushOLD(mtag_t tag, line_t *line, ceiling_e type);
void T_CrushCeiling(ceiling_t *ceiling);

boolean EV_DoRaiseAndCrushCeiling(mtag_t tag, fixed_t speed, fixed_t returnSpeed);
boolean EV_DoCrushBothOnce(mtag_t tag, fixed_t speed);
boolean EV_DoCrushCeilingOnce(mtag_t tag, fixed_t speed);

//
// P_FLOOR
//
typedef enum
{
	// raise floor to next highest surrounding floor
	raiseFloorToNearestFast,

	// move the floor down instantly
	instantLower,

	moveFloorByFrontSector,
	instantMoveFloorByFrontSector,

	moveFloorByDistance,

	bounceFloor,
	bounceFloorCrush,

	crushFloorOnce,
} floor_e;

typedef enum
{
	elevateUp,
	elevateDown,
	elevateContinuous,
	elevateBounce,
	elevateHighest,
	bridgeFall,
} elevator_e;

struct floormove_t
{
	thinker_t thinker;
	floor_e type;
	UINT8 crush;
	sector_t *sector;
	INT32 direction;
	INT32 texture;
	fixed_t floordestheight;
	fixed_t speed;
	fixed_t origspeed;
	fixed_t delay;
	fixed_t delaytimer;
	INT16 tag;
	INT32 sourceline;
	fixed_t crushHeight;
	fixed_t crushSpeed;
	fixed_t returnHeight;
	fixed_t returnSpeed;
};

struct elevator_t
{
	thinker_t thinker;
	elevator_e type;
	sector_t *sector;
	sector_t *actionsector; // The sector the rover action is taking place in.
	INT32 direction;
	fixed_t floordestheight;
	fixed_t ceilingdestheight;
	fixed_t speed;
	fixed_t origspeed;
	fixed_t low;
	fixed_t high;
	fixed_t distance;
	fixed_t delay;
	fixed_t delaytimer;
	fixed_t floorwasheight; // Height the floor WAS at
	fixed_t ceilingwasheight; // Height the ceiling WAS at
};

typedef enum
{
	CF_RETURN   = 1,    // Return after crumbling
	CF_FLOATBOB = 1<<1, // Float on water
	CF_REVERSE  = 1<<2, // Reverse gravity
} crumbleflag_t;

struct crumble_t
{
	thinker_t thinker;
	line_t *sourceline;
	sector_t *sector;
	sector_t *actionsector; // The sector the rover action is taking place in.
	player_t *player; // Player who initiated the thinker (used for airbob)
	INT32 direction;
	INT32 origalpha;
	INT32 timer;
	fixed_t speed;
	fixed_t floorwasheight; // Height the floor WAS at
	fixed_t ceilingwasheight; // Height the ceiling WAS at
	UINT8 flags;
};

struct noenemies_t
{
	thinker_t thinker;
	line_t *sourceline; // Source line of the thinker
};

struct continuousfall_t
{
	thinker_t thinker;
	sector_t *sector;
	fixed_t speed;
	INT32 direction;
	fixed_t floorstartheight;
	fixed_t ceilingstartheight;
	fixed_t destheight;
};

struct bouncecheese_t
{
	thinker_t thinker;
	line_t *sourceline;
	sector_t *sector;
	fixed_t speed;
	fixed_t distance;
	fixed_t floorwasheight;
	fixed_t ceilingwasheight;
	boolean low;
};

struct mariothink_t
{
	thinker_t thinker;
	sector_t *sector;
	fixed_t speed;
	INT32 direction;
	fixed_t floorstartheight;
	fixed_t ceilingstartheight;
	INT16 tag;
};

struct mariocheck_t
{
	thinker_t thinker;
	line_t *sourceline;
	sector_t *sector;
};

struct thwomp_t
{
	thinker_t thinker;
	line_t *sourceline;
	sector_t *sector;
	fixed_t crushspeed;
	fixed_t retractspeed;
	INT32 direction;
	fixed_t floorstartheight;
	fixed_t ceilingstartheight;
	INT32 delay;
	INT16 tag;
	UINT16 sound;
	INT32 initDelay;
};

struct floatthink_t
{
	thinker_t thinker;
	line_t *sourceline;
	sector_t *sector;
	INT16 tag;
};

struct eachtime_t
{
	thinker_t thinker;
	line_t *sourceline; // Source line of the thinker
	boolean playersInArea[MAXPLAYERS];
	boolean triggerOnExit;
};

typedef enum
{
	RF_REVERSE  = 1,    //Lower when stood on
	//free = 1<<1,
	RF_DYNAMIC  = 1<<2, //Dynamically sinking platform
} raiseflag_t;

struct raise_t
{
	thinker_t thinker;
	INT16 tag;
	sector_t *sector;
	fixed_t ceilingbottom;
	fixed_t ceilingtop;
	fixed_t basespeed;
	fixed_t extraspeed; //For dynamically sinking platform
	UINT8 shaketimer; //For dynamically sinking platform
	UINT8 flags;
};

#define ELEVATORSPEED (FRACUNIT*4)
#define FLOORSPEED (FRACUNIT)

typedef enum
{
	ok,
	crushed,
	pastdest
} result_e;

result_e T_MovePlane(sector_t *sector, fixed_t speed, fixed_t dest, boolean crush,
	boolean ceiling, INT32 direction);

void EV_DoFloor(mtag_t tag, line_t *line, floor_e floortype);
void EV_DoFloorOLD(mtag_t tag, line_t *line, floor_e floortype);
void EV_DoRaiseFloorToNearestFast(mtag_t tag);
void EV_DoInstantLowerFloor(mtag_t tag);
void EV_DoInstantMoveFloorByHeight(mtag_t tag, fixed_t height, INT32 texture);
void EV_DoMoveFloorByHeight(mtag_t tag, fixed_t height, fixed_t speed, mtag_t chain, INT32 texture);
void EV_DoMoveFloorByDistance(mtag_t tag, fixed_t distance, fixed_t speed, boolean instant);
void EV_DoBounceFloor(mtag_t tag, boolean crush, fixed_t crushHeight, fixed_t crushSpeed, fixed_t returnHeight, fixed_t returnSpeed, INT32 delayInit, INT32 delay);
void EV_DoCrushFloorOnce(mtag_t tag, fixed_t speed);

void EV_DoElevator(mtag_t tag, line_t *line, elevator_e elevtype);
void EV_DoElevateDown(mtag_t tag);
void EV_DoElevateUp(mtag_t tag);
void EV_DoElevateHighest(mtag_t tag);
void EV_DoContinuousElevator(mtag_t tag, fixed_t speed, INT32 delayInit, INT32 delay, boolean lowFirst);
void EV_DoBridgeFall(mtag_t tag);

void EV_CrumbleChain(sector_t *sec, ffloor_t *rover);
void EV_BounceSector(sector_t *sector, fixed_t momz, line_t *sourceline);

// Some other special 3dfloor types
INT32 EV_StartCrumble(sector_t *sector, ffloor_t *rover,
	boolean floating, player_t *player, fixed_t origalpha, boolean crumblereturn);

void EV_DoContinuousFall(sector_t *sec, sector_t *backsector, fixed_t spd, boolean backwards);

void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher);

void T_MoveFloor(void *thinker);

void T_MoveElevator(void *thinker);
void T_ContinuousFalling(void *thinker);
void T_BounceCheese(void *thinker);
void T_StartCrumble(void *thinker);
void T_MarioBlock(void *thinker);
void T_FloatSector(void *thinker);
void T_MarioBlockChecker(void *thinker);
void T_ThwompSector(void *thinker);
void T_NoEnemiesSector(void *thinker);
void T_EachTimeThinker(void *thinker);
void T_CameraScanner(void *thinker);
void T_RaiseSector(void *thinker);

struct executor_t
{
	thinker_t thinker; // Thinker for linedef executor delay
	line_t *line;      // Pointer to line that is waiting.
	mobj_t *caller;    // Pointer to calling mobj
	sector_t *sector;  // Pointer to triggering sector
	INT32 timer;       // Delay timer
};

void T_ExecutorDelay(void *thinker);

/** Generalized scroller.
  */
struct scroll_t
{
	thinker_t thinker;   ///< Thinker structure for scrolling.
	fixed_t dx, dy;      ///< (dx,dy) scroll speeds.
	INT32 affectee;      ///< Number of affected sidedef or sector.
	INT32 control;       ///< Control sector (-1 if none) used to control scrolling.
	fixed_t last_height; ///< Last known height of control sector.
	fixed_t vdx, vdy;    ///< Accumulated velocity if accelerative.
	INT32 accel;         ///< Whether it's accelerative.
	INT32 exclusive;     ///< If a conveyor, same property as in pusher_t
	/** Types of generalized scrollers.
	*/
	enum
	{
		sc_side,         ///< Scroll wall texture on a sidedef.
		sc_floor,        ///< Scroll floor.
		sc_ceiling,      ///< Scroll ceiling.
		sc_carry,        ///< Carry objects on floor.
		sc_carry_ceiling,///< Carry objects on ceiling (for 3Dfloor conveyors).
	} type;
};

void T_Scroll(void *thinker);
void T_LaserFlash(void *thinker);

/** Friction for ice/sludge effects.
  */
struct friction_t
{
	thinker_t thinker;   ///< Thinker structure for friction.
	INT32 friction;      ///< Friction value, 0xe800 = normal.
	INT32 movefactor;    ///< Inertia factor when adding to momentum, FRACUNIT = normal.
	INT32 affectee;      ///< Number of affected sector.
	INT32 referrer;      ///< If roverfriction == true, then this will contain the sector # of the control sector where the effect was applied.
	UINT8 roverfriction;  ///< flag for whether friction originated from a FOF or not
};

// Friction defines.
#define ORIG_FRICTION          (62914) ///< Original value.

void T_Friction(void *thinker);

typedef enum
{
	p_wind,        ///< Wind.
	p_current,     ///< Current.
} pushertype_e;

// Model for pushers for push/pull effects
struct pusher_t
{
	thinker_t thinker;  ///< Thinker structure for pusher effect.
	pushertype_e type;  ///< Type of pusher effect.
	fixed_t x_mag;      ///< X strength.
	fixed_t y_mag;      ///< Y strength.
	fixed_t z_mag;      ///< Z strength.
	INT32 affectee;     ///< Number of affected sector.
	UINT8 roverpusher;  ///< flag for whether pusher originated from a FOF or not
	INT32 referrer;     ///< If roverpusher == true, then this will contain the sector # of the control sector where the effect was applied.
	INT32 exclusive;    /// < Once this affect has been applied to a mobj, no other pushers may affect it.
	INT32 slider;       /// < Should the player go into an uncontrollable slide?
};

// Model for disappearing/reappearing FOFs
struct disappear_t
{
	thinker_t thinker;  ///< Thinker structure for effect.
	tic_t appeartime;   ///< Tics to be appeared for
	tic_t disappeartime;///< Tics to be disappeared for
	tic_t offset;       ///< Time to wait until thinker starts
	tic_t timer;        ///< Timer between states
	INT32 affectee;     ///< Number of affected line
	INT32 sourceline;   ///< Number of source line
	INT32 exists;       ///< Exists toggle
};

void T_Disappear(void *thinker);

// Model for fading FOFs
struct fade_t
{
	thinker_t thinker;  ///< Thinker structure for effect.
	ffloor_t *rover;    ///< Target ffloor
	extracolormap_t *dest_exc; ///< Colormap to fade to
	UINT32 sectornum;    ///< Number of ffloor target sector
	UINT32 ffloornum;    ///< Number of ffloor of target sector
	INT32 alpha;        ///< Internal alpha counter
	INT16 sourcevalue;  ///< Transparency value to fade from
	INT16 destvalue;    ///< Transparency value to fade to
	INT16 destlightlevel; ///< Light level to fade to
	INT16 speed;        ///< Speed to fade by
	boolean ticbased;    ///< Tic-based logic toggle
	INT32 timer;        ///< Timer for tic-based logic
	boolean doexists;   ///< Handle FOF_EXISTS
	boolean dotranslucent; ///< Handle FOF_TRANSLUCENT
	boolean dolighting; ///< Handle shadows and light blocks
	boolean docolormap; ///< Handle colormaps
	boolean docollision; ///< Handle interactive flags
	boolean doghostfade; ///< No interactive flags during fading
	boolean exactalpha; ///< Use exact alpha values (opengl)
};

void T_Fade(void *thinker);

// Model for fading colormaps

struct fadecolormap_t
{
	thinker_t thinker;          ///< Thinker structure for effect.
	sector_t *sector;           ///< Sector where action is taking place.
	extracolormap_t *source_exc;
	extracolormap_t *dest_exc;
	boolean ticbased;           ///< Tic-based timing
	INT32 duration;             ///< Total duration for tic-based logic (OR: speed increment)
	INT32 timer;                ///< Timer for tic-based logic (OR: internal speed counter)
};

void T_FadeColormap(void *thinker);

// Prototype function for pushers
void T_Pusher(void *thinker);

// Plane displacement
struct planedisplace_t
{
	thinker_t thinker;   ///< Thinker structure for plane displacement effect.
	INT32 affectee;      ///< Number of affected sector.
	INT32 control;       ///< Control sector used to control plane positions.
	fixed_t last_height; ///< Last known height of control sector.
	fixed_t speed;       ///< Plane movement speed.
	UINT8 reverse;       ///< Move in reverse direction to control sector?
	/** Types of plane displacement effects.
	*/
	enum
	{
		pd_floor,        ///< Displace floor.
		pd_ceiling,      ///< Displace ceiling.
		pd_both,         ///< Displace both floor AND ceiling.
	} type;
};

void T_PlaneDisplace(void *thinker);

void P_CalcHeight(player_t *player);

/* line specials */
enum
{
	LT_SLOPE_ANCHORS             = 777,

	// binary converter
	LT_SLOPE_ANCHORS_OLD_FLOOR   = 777,
	LT_SLOPE_ANCHORS_OLD_CEILING = 778,
	LT_SLOPE_ANCHORS_OLD         = 779,
};

#ifdef __cplusplus
} // extern "C"
#endif

#endif
