// 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:
//		Refresh/rendering module, shared data struct definitions.
//
//-----------------------------------------------------------------------------


#ifndef __R_DEFS_H__
#define __R_DEFS_H__

#include "doomdef.h"
#include "templates.h"
#include "memarena.h"

// Some more or less basic data types
// we depend on.
#include "m_fixed.h"

// We rely on the thinker data struct
// to handle sound origins in sectors.
// SECTORS do store MObjs anyway.
#include "actor.h"
struct FLightNode;
struct FGLSection;
struct seg_t;

#include "dthinker.h"

#define MAXWIDTH 5760
#define MAXHEIGHT 3600

const WORD NO_INDEX = 0xffffu;
const DWORD NO_SIDE = 0xffffffffu;

// Silhouette, needed for clipping Segs (mainly)
// and sprites representing things.
enum
{
	SIL_NONE,
	SIL_BOTTOM,
	SIL_TOP,
	SIL_BOTH
};

extern size_t MaxDrawSegs;


//
// INTERNAL MAP TYPES
//	used by play and refresh
//

//
// Your plain vanilla vertex.
// Note: transformed values not buffered locally,
//	like some DOOM-alikes ("wt", "WebView") did.
//
enum
{
	VERTEXFLAG_ZCeilingEnabled = 0x01,
	VERTEXFLAG_ZFloorEnabled   = 0x02
};
struct vertexdata_t
{
	fixed_t zCeiling, zFloor;
	DWORD flags;
};
struct vertex_t
{
	fixed_t x, y;

	float fx, fy;		// Floating point coordinates of this vertex (excluding polyoblect translation!)
	angle_t viewangle;	// precalculated angle for clipping
	int angletime;		// recalculation time for view angle
	bool dirty;			// something has changed and needs to be recalculated
	int numheights;
	int numsectors;
	sector_t ** sectors;
	float * heightlist;

	vertex_t()
	{
		x = y = 0;
		fx = fy = 0;
		angletime = 0;
		viewangle = 0;
		dirty = true;
		numheights = numsectors = 0;
		sectors = NULL;
		heightlist = NULL;
	}

	bool operator== (const vertex_t &other)
	{
		return x == other.x && y == other.y;
	}

	bool operator!= (const vertex_t &other)
	{
		return x != other.x || y != other.y;
	}

	void clear()
	{
		x = y = 0;
	}

	angle_t GetClipAngle();
};

// Forward of LineDefs, for Sectors.
struct line_t;

class player_t;
class FScanner;
class FBitmap;
struct FCopyInfo;
class DInterpolation;
class FArchive;

enum
{
	UDMF_Line,
	UDMF_Side,
	UDMF_Sector,
	UDMF_Thing
};


struct FUDMFKey
{
	enum
	{
		UDMF_Int,
		UDMF_Float,
		UDMF_String
	};

	FName Key;
	int Type;
	int IntVal;
	double FloatVal;
	FString StringVal;

	FUDMFKey()
	{
	}

	FUDMFKey& operator =(int val)
	{
		Type = UDMF_Int;
		IntVal = val;
		FloatVal = val;
		StringVal = "";
		return *this;
	}

	FUDMFKey& operator =(double val)
	{
		Type = UDMF_Float;
		IntVal = int(val);
		FloatVal = val;
		StringVal = "";
		return *this;
	}

	FUDMFKey& operator =(const FString &val)
	{
		Type = UDMF_String;
		IntVal = strtol(val, NULL, 0);
		FloatVal = strtod(val, NULL);
		StringVal = val;
		return *this;
	}

};

class FUDMFKeys : public TArray<FUDMFKey>
{
public:
	void Sort();
	FUDMFKey *Find(FName key);
};

//
// The SECTORS record, at runtime.
// Stores things/mobjs.
//
class DSectorEffect;
struct sector_t;
struct line_t;
struct FRemapTable;

enum
{
	SECSPAC_Enter		= 1,	// Trigger when player enters
	SECSPAC_Exit		= 2,	// Trigger when player exits
	SECSPAC_HitFloor	= 4,	// Trigger when player hits floor
	SECSPAC_HitCeiling	= 8,	// Trigger when player hits ceiling
	SECSPAC_Use			= 16,	// Trigger when player uses
	SECSPAC_UseWall		= 32,	// Trigger when player uses a wall
	SECSPAC_EyesDive	= 64,	// Trigger when player eyes go below fake floor
	SECSPAC_EyesSurface = 128,	// Trigger when player eyes go above fake floor
	SECSPAC_EyesBelowC	= 256,	// Trigger when player eyes go below fake ceiling
	SECSPAC_EyesAboveC	= 512,	// Trigger when player eyes go above fake ceiling
	SECSPAC_HitFakeFloor= 1024,	// Trigger when player hits fake floor
};

class ASectorAction : public AActor
{
	DECLARE_CLASS (ASectorAction, AActor)
public:
	void Destroy ();
	void BeginPlay ();
	void Activate (AActor *source);
	void Deactivate (AActor *source);
	virtual bool TriggerAction (AActor *triggerer, int activationType);
	// [BB] Added PrepareForHiding ();
	virtual void PrepareForHiding ();
protected:
	bool CheckTrigger (AActor *triggerer) const;
	// [BB] Added RemoveFromSectorActionsList ();
	void RemoveFromSectorActionsList ();
};

class ASkyViewpoint;

struct secplane_t
{
	// the plane is defined as a*x + b*y + c*z + d = 0
	// ic is 1/c, for faster Z calculations

	fixed_t a, b, c, d, ic;

	// [Spleen] Store the old D's of the plane for unlagged support
	fixed_t		unlaggedD[UNLAGGEDTICS];
	fixed_t		restoreD;

	// Returns < 0 : behind; == 0 : on; > 0 : in front
	int PointOnSide (fixed_t x, fixed_t y, fixed_t z) const
	{
		return TMulScale16(a,x, b,y, c,z) + d;
	}

	// Returns the value of z at (0,0) This is used by the 3D floor code which does not handle slopes
	fixed_t Zat0 () const
	{
		return ic < 0 ? d : -d;
	}

	// Returns the value of z at (x,y)
	fixed_t ZatPoint (fixed_t x, fixed_t y) const
	{
		return FixedMul (ic, -d - DMulScale16 (a, x, b, y));
	}

	// Returns the value of z at (x,y) as a double
	double ZatPoint (double x, double y) const
	{
		return (d + a*x + b*y) * ic / (-65536.0 * 65536.0);
	}

	// Returns the value of z at vertex v
	fixed_t ZatPoint (const vertex_t *v) const
	{
		return FixedMul (ic, -d - DMulScale16 (a, v->x, b, v->y));
	}

	// Returns the value of z at (x,y) if d is equal to dist
	fixed_t ZatPointDist (fixed_t x, fixed_t y, fixed_t dist) const
	{
		return FixedMul (ic, -dist - DMulScale16 (a, x, b, y));
	}

	// Returns the value of z at vertex v if d is equal to dist
	fixed_t ZatPointDist (const vertex_t *v, fixed_t dist)
	{
		return FixedMul (ic, -dist - DMulScale16 (a, v->x, b, v->y));
	}

	// Flips the plane's vertical orientiation, so that if it pointed up,
	// it will point down, and vice versa.
	void FlipVert ()
	{
		a = -a;
		b = -b;
		c = -c;
		d = -d;
		ic = -ic;
	}

	// Returns true if 2 planes are the same
	bool operator== (const secplane_t &other) const
	{
		return a == other.a && b == other.b && c == other.c && d == other.d;
	}

	// Returns true if 2 planes are different
	bool operator!= (const secplane_t &other) const
	{
		return a != other.a || b != other.b || c != other.c || d != other.d;
	}

	// Moves a plane up/down by hdiff units
	void ChangeHeight (fixed_t hdiff)
	{
		d = d - FixedMul (hdiff, c);
	}

	// Moves a plane up/down by hdiff units
	fixed_t GetChangedHeight (fixed_t hdiff)
	{
		return d - FixedMul (hdiff, c);
	}

	// Returns how much this plane's height would change if d were set to oldd
	fixed_t HeightDiff (fixed_t oldd) const
	{
		return FixedMul (oldd - d, ic);
	}

	// Returns how much this plane's height would change if d were set to oldd
	fixed_t HeightDiff (fixed_t oldd, fixed_t newd) const
	{
		return FixedMul (oldd - newd, ic);
	}

	fixed_t PointToDist (fixed_t x, fixed_t y, fixed_t z) const
	{
		return -TMulScale16 (a, x, y, b, z, c);
	}

	fixed_t PointToDist (const vertex_t *v, fixed_t z) const
	{
		return -TMulScale16 (a, v->x, b, v->y, z, c);
	}

	bool CopyPlaneIfValid (secplane_t *dest, const secplane_t *opp) const;

};

FArchive &operator<< (FArchive &arc, secplane_t &plane);


#include "p_3dfloors.h"
struct subsector_t;
struct sector_t;
struct side_t;
extern bool gl_plane_reflection_i;
struct FPortal;

// Ceiling/floor flags
enum
{
	PLANEF_ABSLIGHTING	= 1,	// floor/ceiling light is absolute, not relative
	PLANEF_BLOCKED		= 2,	// can not be moved anymore.
	PLANEF_ADDITIVE		= 4,	// rendered additive
	PLANEF_SPRINGPAD		= 8,	// [BC] Floor bounces actors up at the same velocity they landed on it with.	
};

// Internal sector flags
enum
{
	SECF_FAKEFLOORONLY	= 2,	// when used as heightsec in R_FakeFlat, only copies floor
	SECF_CLIPFAKEPLANES = 4,	// as a heightsec, clip planes to target sector's planes
	SECF_NOFAKELIGHT	= 8,	// heightsec does not change lighting
	SECF_IGNOREHEIGHTSEC= 16,	// heightsec is only for triggering sector actions
	SECF_UNDERWATER		= 32,	// sector is underwater
	SECF_FORCEDUNDERWATER= 64,	// sector is forced to be underwater
	SECF_UNDERWATERMASK	= 32+64,
	SECF_DRAWN			= 128,	// sector has been drawn at least once
	SECF_HIDDEN			= 256,	// Do not draw on textured automap
	SECF_NOFLOORSKYBOX	= 512,	// force use of regular sky 
	SECF_NOCEILINGSKYBOX	= 1024,	// force use of regular sky 
	SECF_RETURNZONE		= 2048,	// [BC] Flags should be immediately returned if they're dropped within this sector (lava sectors, unreachable sectors, etc.).
};

enum
{
	SECF_SILENT			= 1,	// actors in sector make no noise
	SECF_NOFALLINGDAMAGE= 2,	// No falling damage in this sector
	SECF_FLOORDROP		= 4,	// all actors standing on this floor will remain on it when it lowers very fast.
	SECF_NORESPAWN		= 8,	// players can not respawn in this sector
};

enum
{
	PL_SKYFLAT = 0x40000000
};

struct FDynamicColormap;


struct FLinkedSector
{
	sector_t *Sector;
	int Type;
};


// this substructure contains a few sector properties that are stored in dynamic arrays
// These must not be copied by R_FakeFlat etc. or bad things will happen.
struct extsector_t
{
	// Boom sector transfer information
	struct fakefloor
	{
		TArray<sector_t *> Sectors;
	} FakeFloor;

	// 3DMIDTEX information
	struct midtex
	{
		struct plane
		{
			TArray<sector_t *> AttachedSectors;		// all sectors containing 3dMidtex lines attached to this sector
			TArray<line_t *> AttachedLines;			// all 3dMidtex lines attached to this sector
			fixed_t MoveDistance;					// [Dusk] how far from the inital position is the plane at?
		} Floor, Ceiling;
	} Midtex;

	// Linked sector information
	struct linked
	{
		struct plane
		{
			TArray<FLinkedSector> Sectors;
		} Floor, Ceiling;
	} Linked;

	// 3D floors
	struct xfloor
	{
		TDeletingArray<F3DFloor *>		ffloors;		// 3D floors in this sector
		TArray<lightlist_t>				lightlist;		// 3D light list
		TArray<sector_t*>				attached;		// 3D floors attached to this sector
	} XFloor;
	
	void Serialize(FArchive &arc);
};

struct FTransform
{
	// killough 3/7/98: floor and ceiling texture offsets
	fixed_t xoffs, yoffs;

	// [RH] floor and ceiling texture scales
	fixed_t xscale, yscale;

	// [RH] floor and ceiling texture rotation
	angle_t angle;

	// base values
	fixed_t base_angle, base_yoffs;
};

struct sector_t
{
	// Member functions
	bool IsLinked(sector_t *other, bool ceiling) const;
	fixed_t FindLowestFloorSurrounding (vertex_t **v) const;
	fixed_t FindHighestFloorSurrounding (vertex_t **v) const;
	fixed_t FindNextHighestFloor (vertex_t **v) const;
	fixed_t FindNextLowestFloor (vertex_t **v) const;
	fixed_t FindLowestCeilingSurrounding (vertex_t **v) const;			// jff 2/04/98
	fixed_t FindHighestCeilingSurrounding (vertex_t **v) const;			// jff 2/04/98
	fixed_t FindNextLowestCeiling (vertex_t **v) const;					// jff 2/04/98
	fixed_t FindNextHighestCeiling (vertex_t **v) const;				// jff 2/04/98
	fixed_t FindShortestTextureAround () const;							// jff 2/04/98
	fixed_t FindShortestUpperAround () const;							// jff 2/04/98
	sector_t *FindModelFloorSector (fixed_t floordestheight) const;		// jff 2/04/98
	sector_t *FindModelCeilingSector (fixed_t floordestheight) const;	// jff 2/04/98
	int FindMinSurroundingLight (int max) const;
	sector_t *NextSpecialSector (int type, sector_t *prev) const;		// [RH]
	fixed_t FindLowestCeilingPoint (vertex_t **v) const;
	fixed_t FindHighestFloorPoint (vertex_t **v) const;
	void AdjustFloorClip () const;
	// [BB] Added bInformClients and bExecuteOnClient.
	void SetColor(int r, int g, int b, int desat, bool bInformClients = true, bool bExecuteOnClient = false);
	// [BB] Added bInformClients and bExecuteOnClient.
	void SetFade(int r, int g, int b, bool bInformClients = true, bool bExecuteOnClient = false);
	void ClosestPoint(fixed_t x, fixed_t y, fixed_t &ox, fixed_t &oy) const;
	int GetFloorLight () const;
	int GetCeilingLight () const;
	sector_t *GetHeightSec() const;

	DInterpolation *SetInterpolation(int position, bool attach);
	void StopInterpolation(int position);

	ASkyViewpoint *GetSkyBox(int which);

	enum
	{
		floor,
		ceiling
	};

	struct splane
	{
		FTransform xform;
		int Flags;
		int Light;
		fixed_t alpha;
		FTextureID Texture;
		fixed_t TexZ;
	};


	splane planes[2];

	void SetXOffset(int pos, fixed_t o)
	{
		planes[pos].xform.xoffs = o;
	}

	void AddXOffset(int pos, fixed_t o)
	{
		planes[pos].xform.xoffs += o;
	}

	fixed_t GetXOffset(int pos) const
	{
		return planes[pos].xform.xoffs;
	}

	void SetYOffset(int pos, fixed_t o)
	{
		planes[pos].xform.yoffs = o;
	}

	void AddYOffset(int pos, fixed_t o)
	{
		planes[pos].xform.yoffs += o;
	}

	fixed_t GetYOffset(int pos, bool addbase = true) const
	{
		if (!addbase)
		{
			return planes[pos].xform.yoffs;
		}
		else
		{
			return planes[pos].xform.yoffs + planes[pos].xform.base_yoffs;
		}
	}

	void SetXScale(int pos, fixed_t o)
	{
		planes[pos].xform.xscale = o;
	}

	fixed_t GetXScale(int pos) const
	{
		return planes[pos].xform.xscale;
	}

	void SetYScale(int pos, fixed_t o)
	{
		planes[pos].xform.yscale = o;
	}

	fixed_t GetYScale(int pos) const
	{
		return planes[pos].xform.yscale;
	}

	void SetAngle(int pos, angle_t o)
	{
		planes[pos].xform.angle = o;
	}

	angle_t GetAngle(int pos, bool addbase = true) const
	{
		if (!addbase)
		{
			return planes[pos].xform.angle;
		}
		else
		{
			return planes[pos].xform.angle + planes[pos].xform.base_angle;
		}
	}

	void SetBase(int pos, fixed_t y, angle_t o)
	{
		planes[pos].xform.base_yoffs = y;
		planes[pos].xform.base_angle = o;
	}

	void SetAlpha(int pos, fixed_t o)
	{
		planes[pos].alpha = o;
	}

	fixed_t GetAlpha(int pos) const
	{
		return planes[pos].alpha;
	}

	int GetFlags(int pos) const 
	{
		return planes[pos].Flags;
	}

	void ChangeFlags(int pos, int And, int Or)
	{
		planes[pos].Flags &= ~And;
		planes[pos].Flags |= Or;
	}

	int GetPlaneLight(int pos) const 
	{
		return planes[pos].Light;
	}

	void SetPlaneLight(int pos, int level)
	{
		planes[pos].Light = level;
	}

	FTextureID GetTexture(int pos) const
	{
		return planes[pos].Texture;
	}

	void SetTexture(int pos, FTextureID tex, bool floorclip = true)
	{
		FTextureID old = planes[pos].Texture;
		planes[pos].Texture = tex;
		if (floorclip && pos == floor && tex != old) AdjustFloorClip();
	}

	fixed_t GetPlaneTexZ(int pos) const
	{
		return planes[pos].TexZ;
	}

	void SetPlaneTexZ(int pos, fixed_t val)
	{
		planes[pos].TexZ = val;
	}

	void ChangePlaneTexZ(int pos, fixed_t val)
	{
		planes[pos].TexZ += val;
	}

	static inline short ClampLight(int level)
	{
		return (short)clamp(level, SHRT_MIN, SHRT_MAX);
	}

	void ChangeLightLevel(int newval)
	{
		lightlevel = ClampLight(lightlevel + newval);
	}

	void SetLightLevel(int newval)
	{
		lightlevel = ClampLight(newval);
	}

	int GetLightLevel() const
	{
		return lightlevel;
	}

	secplane_t &GetSecPlane(int pos)
	{
		return pos == floor? floorplane:ceilingplane;
	}

	bool PlaneMoving(int pos);


	// Member variables
	fixed_t		CenterFloor () const { return floorplane.ZatPoint (soundorg[0], soundorg[1]); }
	fixed_t		CenterCeiling () const { return ceilingplane.ZatPoint (soundorg[0], soundorg[1]); }

	// [RH] store floor and ceiling planes instead of heights
	secplane_t	floorplane, ceilingplane;

	// [RH] give floor and ceiling even more properties
	FDynamicColormap *ColorMap;	// [RH] Per-sector colormap


	TObjPtr<AActor> SoundTarget;

	short		special;
	short		tag;
	short		lightlevel;
	short		seqType;		// this sector's sound sequence

	int			nexttag,firsttag;	// killough 1/30/98: improves searches for tags.

	int			sky;
	FNameNoInit	SeqName;		// Sound sequence name. Setting seqType non-negative will override this.

	fixed_t		soundorg[2];	// origin for any sounds played by the sector
	int 		validcount;		// if == validcount, already checked
	AActor* 	thinglist;		// list of mobjs in sector

	// killough 8/28/98: friction is a sector property, not an mobj property.
	// these fields used to be in AActor, but presented performance problems
	// when processed as mobj properties. Fix is to make them sector properties.
	fixed_t		friction, movefactor;

	// thinker_t for reversable actions
	TObjPtr<DSectorEffect> floordata;			// jff 2/22/98 make thinkers on
	TObjPtr<DSectorEffect> ceilingdata;			// floors, ceilings, lighting,
	TObjPtr<DSectorEffect> lightingdata;		// independent of one another

	enum
	{
		CeilingMove,
		FloorMove,
		CeilingScroll,
		FloorScroll
	};
	TObjPtr<DInterpolation> interpolations[4];

	BYTE 		soundtraversed;	// 0 = untraversed, 1,2 = sndlines -1
	// jff 2/26/98 lockout machinery for stairbuilding
	SBYTE stairlock;	// -2 on first locked -1 after thinker done 0 normally
	SWORD prevsec;		// -1 or number of sector for previous step
	SWORD nextsec;		// -1 or number of next step sector

	short linecount;
	struct line_t **lines;		// [linecount] size

	// killough 3/7/98: support flat heights drawn at another sector's heights
	sector_t *heightsec;		// other sector, or NULL if no other sector

	DWORD bottommap, midmap, topmap;	// killough 4/4/98: dynamic colormaps
										// [RH] these can also be blend values if
										//		the alpha mask is non-zero

	// list of mobjs that are at least partially in the sector
	// thinglist is a subset of touching_thinglist
	struct msecnode_t *touching_thinglist;				// phares 3/14/98

	float gravity;		// [RH] Sector gravity (1.0 is normal)
	short damage;		// [RH] Damage to do while standing on floor
	short mod;			// [RH] Means-of-death for applied damage

	WORD ZoneNumber;	// [RH] Zone this sector belongs to
	WORD MoreFlags;		// [RH] Internal sector flags
	DWORD Flags;		// Sector flags

	// [RH] Action specials for sectors. Like Skull Tag, but more
	// flexible in a Bloody way. SecActTarget forms a list of actors
	// joined by their tracer fields. When a potential sector action
	// occurs, SecActTarget's TriggerAction method is called.
	TObjPtr<ASectorAction> SecActTarget;

	// [RH] The sky box to render for this sector. NULL means use a
	// regular sky.
	TObjPtr<ASkyViewpoint> FloorSkyBox, CeilingSkyBox;

	short						secretsector;		//jff 2/16/98 remembers if sector WAS secret (automap)
	int							sectornum;			// for comparing sector copies

	extsector_t	*				e;		// This stores data that requires construction/destruction. Such data must not be copied by R_FakeFlat.

	// GL only stuff starts here
	float						reflect[2];

	int							dirtyframe[3];		// last frame this sector was marked dirty
	bool						dirty;				// marked for recalculation
	bool						transdoor;			// For transparent door hacks
	fixed_t						transdoorheight;	// for transparent door hacks
	int							subsectorcount;		// list of subsectors
	subsector_t **				subsectors;
	FPortal *					portals[2];			// floor and ceiling portals

	enum
	{
		vbo_fakefloor = floor+2,
		vbo_fakeceiling = ceiling+2,
	};

	int				vboindex[4];	// VBO indices of the 4 planes this sector uses during rendering
	fixed_t			vboheight[2];	// Last calculated height for the 2 planes of this actual sector
	int				vbocount[2];	// Total count of vertices belonging to this sector's planes

	float GetReflect(int pos) { return gl_plane_reflection_i? reflect[pos] : 0; }
	bool VBOHeightcheck(int pos) const { return vboheight[pos] == GetPlaneTexZ(pos); }

	enum
	{
		INVALIDATE_PLANES = 1,
		INVALIDATE_OTHER = 2
	};

	// [BC] Is this sector a floor or ceiling?
	int		floorOrCeiling;

	// [BC] Has the height changed during the course of the level?
	bool		bCeilingHeightChange;
	bool		bFloorHeightChange;
	secplane_t	SavedCeilingPlane;
	secplane_t	SavedFloorPlane;
	fixed_t		SavedCeilingTexZ;
	fixed_t		SavedFloorTexZ;

	// [BC] Has the flat changed?
	bool		bFlatChange;
	FTextureID	SavedFloorPic;
	FTextureID	SavedCeilingPic;

	// [BC] Has the light level changed?
	bool	bLightChange;
	short	SavedLightLevel;

	// [BC] Backup other numberous elements for resetting the map.
	FDynamicColormap	*SavedColorMap;
	float				SavedGravity;
	fixed_t				SavedFloorXOffset;
	fixed_t				SavedFloorYOffset;
	fixed_t				SavedCeilingXOffset;
	fixed_t				SavedCeilingYOffset;
	fixed_t				SavedFloorXScale;
	fixed_t				SavedFloorYScale;
	fixed_t				SavedCeilingXScale;
	fixed_t				SavedCeilingYScale;
	angle_t				SavedFloorAngle;
	angle_t				SavedCeilingAngle;
	fixed_t				SavedBaseFloorAngle;
	fixed_t				SavedBaseFloorYOffset;
	fixed_t				SavedBaseCeilingAngle;
	fixed_t				SavedBaseCeilingYOffset;
	fixed_t				SavedFriction;
	fixed_t				SavedMoveFactor;
	short				SavedSpecial;
	short				SavedDamage;
	short				SavedMOD;
	float				SavedCeilingReflect;
	float				SavedFloorReflect;

};

FArchive &operator<< (FArchive &arc, sector_t::splane &p);


struct ReverbContainer;
struct zone_t
{
	ReverbContainer *Environment;
};


//
// The SideDef.
//

class DBaseDecal;

enum
{
	WALLF_ABSLIGHTING	 = 1,	// Light is absolute instead of relative
	WALLF_NOAUTODECALS	 = 2,	// Do not attach impact decals to this wall
	WALLF_NOFAKECONTRAST = 4,	// Don't do fake contrast for this wall in side_t::GetLightLevel
	WALLF_SMOOTHLIGHTING = 8,   // Similar to autocontrast but applies to all angles.
	WALLF_CLIP_MIDTEX	 = 16,	// Like the line counterpart, but only for this side.
	WALLF_WRAP_MIDTEX	 = 32,	// Like the line counterpart, but only for this side.
	WALLF_POLYOBJ		 = 64,	// This wall belongs to a polyobject.
	WALLF_LIGHT_FOG      = 128,	// This wall's Light is used even in fog.
};

struct side_t
{
	enum ETexpart
	{
		top=0,
		mid=1,
		bottom=2
	};
	struct part
	{
		fixed_t xoffset;
		fixed_t yoffset;
		fixed_t xscale;
		fixed_t yscale;
		FTextureID texture;
		TObjPtr<DInterpolation> interpolation;
		//int Light;

		// [AK] Saved properties for when a map resets, or when we need to give updates
		// to new clients connecting.
		fixed_t SavedXOffset;
		fixed_t SavedYOffset;
		fixed_t SavedXScale;
		fixed_t SavedYScale;
		FTextureID SavedTexture;
	};

	sector_t*	sector;			// Sector the SideDef is facing.
	DBaseDecal*	AttachedDecals;	// [RH] Decals bound to the wall
	part		textures[3];
	line_t		*linedef;
	//DWORD		linenum;
	DWORD		LeftSide, RightSide;	// [RH] Group walls into loops
	WORD		TexelLength;
	SWORD		Light;
	BYTE		Flags;
	int			Index;		// needed to access custom UDMF fields which are stored in loading order.

	// [BC] Saved flags for when a map resets, or when we need to give updates
	// to new clients connecting.
	BYTE		SavedFlags;

	int GetLightLevel (bool foggy, int baselight, bool noabsolute=false, int *pfakecontrast_usedbygzdoom=NULL) const;

	void SetLight(SWORD l)
	{
		Light = l;
	}

	FTextureID GetTexture(int which) const
	{
		return textures[which].texture;
	}
	void SetTexture(int which, FTextureID tex)
	{
		textures[which].texture = tex;
	}

	void SetTextureXOffset(int which, fixed_t offset)
	{
		textures[which].xoffset = offset;
	}
	void SetTextureXOffset(fixed_t offset)
	{
		textures[top].xoffset =
		textures[mid].xoffset =
		textures[bottom].xoffset = offset;
	}
	fixed_t GetTextureXOffset(int which) const
	{
		return textures[which].xoffset;
	}
	void AddTextureXOffset(int which, fixed_t delta)
	{
		textures[which].xoffset += delta;
	}

	void SetTextureYOffset(int which, fixed_t offset)
	{
		textures[which].yoffset = offset;
	}
	void SetTextureYOffset(fixed_t offset)
	{
		textures[top].yoffset =
		textures[mid].yoffset =
		textures[bottom].yoffset = offset;
	}
	fixed_t GetTextureYOffset(int which) const
	{
		return textures[which].yoffset;
	}
	void AddTextureYOffset(int which, fixed_t delta)
	{
		textures[which].yoffset += delta;
	}

	void SetTextureXScale(int which, fixed_t scale)
	{
		textures[which].xscale = scale == 0 ? FRACUNIT : scale;
	}
	void SetTextureXScale(fixed_t scale)
	{
		textures[top].xscale = textures[mid].xscale = textures[bottom].xscale = scale == 0 ? FRACUNIT : scale;
	}
	fixed_t GetTextureXScale(int which) const
	{
		return textures[which].xscale;
	}
	void MultiplyTextureXScale(int which, fixed_t delta)
	{
		textures[which].xscale = FixedMul(textures[which].xscale, delta);
	}


	void SetTextureYScale(int which, fixed_t scale)
	{
		textures[which].yscale = scale == 0 ? FRACUNIT : scale;
	}
	void SetTextureYScale(fixed_t scale)
	{
		textures[top].yscale = textures[mid].yscale = textures[bottom].yscale = scale == 0 ? FRACUNIT : scale;
	}
	fixed_t GetTextureYScale(int which) const
	{
		return textures[which].yscale;
	}
	void MultiplyTextureYScale(int which, fixed_t delta)
	{
		textures[which].yscale = FixedMul(textures[which].yscale, delta);
	}

	DInterpolation *SetInterpolation(int position);
	void StopInterpolation(int position);

	vertex_t *V1() const;
	vertex_t *V2() const;

	//For GL
	FLightNode * lighthead[2];				// all blended lights that may affect this wall

	seg_t **segs;	// all segs belonging to this sidedef in ascending order. Used for precise rendering
	int numsegs;

};

FArchive &operator<< (FArchive &arc, side_t::part &p);

//
// Move clipping aid for LineDefs.
//
enum slopetype_t
{
	ST_HORIZONTAL,
	ST_VERTICAL,
	ST_POSITIVE,
	ST_NEGATIVE
};

#define	TEXCHANGE_FRONTTOP		1
#define	TEXCHANGE_FRONTMEDIUM	2
#define	TEXCHANGE_FRONTBOTTOM	4
#define	TEXCHANGE_BACKTOP		8
#define	TEXCHANGE_BACKMEDIUM	16
#define	TEXCHANGE_BACKBOTTOM	32
struct line_t
{
	vertex_t	*v1, *v2;	// vertices, from v1 to v2
	fixed_t 	dx, dy;		// precalculated v2 - v1 for side checking
	DWORD		flags;
	DWORD		activation;	// activation type
	int			special;
	fixed_t		Alpha;		// <--- translucency (0=invisibile, FRACUNIT=opaque)
	int			id;			// <--- same as tag or set with Line_SetIdentification
	int			args[5];	// <--- hexen-style arguments (expanded to ZDoom's full width)
	int			firstid, nextid;
	side_t		*sidedef[2];
	//DWORD		sidenum[2];	// sidenum[1] will be NO_SIDE if one sided
	fixed_t		bbox[4];	// bounding box, for the extent of the LineDef.
	slopetype_t	slopetype;	// To aid move clipping.
	sector_t	*frontsector, *backsector;
	int 		validcount;	// if == validcount, already checked
	int			locknumber;	// [Dusk] lock number for special
	// [BC] Have any of this line's textures been changed during the course of the level?
	// [EP] TODO: remove the 'ul' prefix from this variable, it isn't ULONG anymore
	unsigned int ulTexChangeFlags;

	// [BC] Saved properties for when a map resets, or when we need to give updates
	// to new clients connecting.
	int			SavedSpecial;
	int			SavedArgs[5];
	DWORD		SavedFlags;
	fixed_t		SavedAlpha;

};

// phares 3/14/98
//
// Sector list node showing all sectors an object appears in.
//
// There are two threads that flow through these nodes. The first thread
// starts at touching_thinglist in a sector_t and flows through the m_snext
// links to find all mobjs that are entirely or partially in the sector.
// The second thread starts at touching_sectorlist in a AActor and flows
// through the m_tnext links to find all sectors a thing touches. This is
// useful when applying friction or push effects to sectors. These effects
// can be done as thinkers that act upon all objects touching their sectors.
// As an mobj moves through the world, these nodes are created and
// destroyed, with the links changed appropriately.
//
// For the links, NULL means top or end of list.

struct msecnode_t
{
	sector_t			*m_sector;	// a sector containing this object
	AActor				*m_thing;	// this object
	struct msecnode_t	*m_tprev;	// prev msecnode_t for this thing
	struct msecnode_t	*m_tnext;	// next msecnode_t for this thing
	struct msecnode_t	*m_sprev;	// prev msecnode_t for this sector
	struct msecnode_t	*m_snext;	// next msecnode_t for this sector
	bool visited;	// killough 4/4/98, 4/7/98: used in search algorithms
};

struct FPolyNode;
struct FMiniBSP;

//
// The LineSeg.
//
struct seg_t
{
	vertex_t*	v1;
	vertex_t*	v2;
	
	side_t* 	sidedef;
	line_t* 	linedef;

	// Sector references. Could be retrieved from linedef, too.
	sector_t*		frontsector;
	sector_t*		backsector;		// NULL for one-sided lines

	seg_t*			PartnerSeg;
	subsector_t*	Subsector;

	float			sidefrac;		// relative position of seg's ending vertex on owning sidedef
};

struct glsegextra_t
{
	DWORD		 PartnerSeg;
	subsector_t *Subsector;
};

extern seg_t *segs;


//
// A SubSector.
// References a Sector.
// Basically, this is a list of LineSegs indicating the visible walls that
// define (all or some) sides of a convex BSP leaf.
//

enum
{
	SSECF_DEGENERATE = 1,
	SSECF_DRAWN = 2,
	SSECF_POLYORG = 4,
};

struct FPortalCoverage
{
	DWORD *		subsectors;
	int			sscount;
};

struct subsector_t
{
	sector_t	*sector;
	FPolyNode	*polys;
	FMiniBSP	*BSP;
	seg_t		*firstline;
	sector_t	*render_sector;
	DWORD		numlines;
	int			flags;

	void BuildPolyBSP();
	// subsector related GL data
	FLightNode *	lighthead[2];	// Light nodes (blended and additive)
	int				validcount;
	short			mapsection;
	char			hacked;			// 1: is part of a render hack
									// 2: has one-sided walls
	FPortalCoverage	portalcoverage[2];

	// [BL] Constructor to init GZDoom data
	subsector_t() : render_sector(NULL), hacked(0)
	{
		lighthead[0] = lighthead[1] = NULL;
	}
};


	

//
// BSP node.
//
struct node_t
{
	// Partition line.
	fixed_t		x;
	fixed_t		y;
	fixed_t		dx;
	fixed_t		dy;
	fixed_t		bbox[2][4];		// Bounding box for each child.
	float		len;
	union
	{
		void	*children[2];	// If bit 0 is set, it's a subsector.
		int		intchildren[2];	// Used by nodebuilder.
	};
};


// An entire BSP tree.

struct FMiniBSP
{
	bool bDirty;

	TArray<node_t> Nodes;
	TArray<seg_t> Segs;
	TArray<subsector_t> Subsectors;
	TArray<vertex_t> Verts;
};



//
// OTHER TYPES
//

typedef BYTE lighttable_t;	// This could be wider for >8 bit display.

// This encapsulates the fields of vissprite_t that can be altered by AlterWeaponSprite
struct visstyle_t
{
	// [BB] Dummy variable to stop wallhacks.
	BYTE			dummy;
	lighttable_t	*colormap;
	fixed_t			alpha;
	FRenderStyle	RenderStyle;
	// [BB]
	bool bFixedColormap;
};


#endif
