2017-04-17 11:33:19 +00:00
/*
* * g_levellocals . h
* * The static data for a level
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright 1998 - 2016 Randy Heit
* * Copyright 2005 - 2017 Christoph Oelckers
* * All rights reserved .
* *
* * 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 .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
2017-01-08 17:45:30 +00:00
# pragma once
2017-03-17 11:49:43 +00:00
# include "doomdata.h"
2017-01-08 17:45:30 +00:00
# include "g_level.h"
# include "r_defs.h"
2018-11-30 10:22:34 +00:00
# include "r_sky.h"
2017-03-10 01:22:42 +00:00
# include "portal.h"
2017-03-17 13:24:21 +00:00
# include "p_blockmap.h"
2018-10-28 22:08:38 +00:00
# include "p_local.h"
2018-12-28 09:08:39 +00:00
# include "po_man.h"
2019-01-05 17:19:35 +00:00
# include "p_acs.h"
2019-01-23 23:43:43 +00:00
# include "p_tags.h"
2019-01-26 07:28:45 +00:00
# include "p_spec.h"
2019-01-23 23:43:43 +00:00
# include "actor.h"
2019-01-30 00:38:18 +00:00
# include "b_bot.h"
2019-01-29 00:09:02 +00:00
# include "p_effect.h"
2019-01-30 00:15:32 +00:00
# include "d_player.h"
2018-10-28 22:08:38 +00:00
# include "p_destructible.h"
2018-11-06 23:53:44 +00:00
# include "r_data/r_sections.h"
2018-12-10 23:01:45 +00:00
# include "r_data/r_canvastexture.h"
2019-01-28 17:26:14 +00:00
# include "r_data/r_interpolate.h"
2020-04-29 15:36:58 +00:00
# include "doom_aabbtree.h"
2022-06-05 21:24:53 +00:00
# include "doom_levelmesh.h"
2017-01-08 17:45:30 +00:00
2019-01-24 23:30:55 +00:00
//============================================================================
//
// This is used to mark processed portals for some collection functions.
//
//============================================================================
struct FPortalBits
{
TArray < uint32_t > data ;
void setSize ( int num )
{
data . Resize ( ( num + 31 ) / 32 ) ;
clear ( ) ;
}
void clear ( )
{
memset ( & data [ 0 ] , 0 , data . Size ( ) * sizeof ( uint32_t ) ) ;
}
void setBit ( int group )
{
data [ group > > 5 ] | = ( 1 < < ( group & 31 ) ) ;
}
int getBit ( int group )
{
return data [ group > > 5 ] & ( 1 < < ( group & 31 ) ) ;
}
} ;
2019-01-05 20:59:34 +00:00
class DACSThinker ;
2019-01-05 08:40:03 +00:00
class DFraggleThinker ;
2019-01-05 09:04:27 +00:00
class DSpotState ;
2019-01-28 22:53:40 +00:00
class DSeqNode ;
2019-01-09 01:03:26 +00:00
struct FStrifeDialogueNode ;
2019-01-26 14:21:20 +00:00
class DAutomapBase ;
2019-01-29 01:39:14 +00:00
struct wbstartstruct_t ;
2019-01-29 22:45:14 +00:00
class DSectorMarker ;
2019-02-02 00:14:51 +00:00
struct FTranslator ;
2019-02-02 15:43:11 +00:00
struct EventManager ;
2019-01-09 01:03:26 +00:00
typedef TMap < int , int > FDialogueIDMap ; // maps dialogue IDs to dialogue array index (for ACS)
typedef TMap < FName , int > FDialogueMap ; // maps actor class names to dialogue array index
2019-01-29 23:37:29 +00:00
typedef TMap < int , FUDMFKeys > FUDMFKeyMap ;
2022-04-10 06:54:45 +00:00
class DIntermissionController ;
2017-11-15 16:06:21 +00:00
2019-01-28 21:04:33 +00:00
struct FLevelLocals
2018-12-19 17:41:53 +00:00
{
2019-01-29 19:15:06 +00:00
void * level ;
void * Level ; // bug catchers.
2019-02-02 16:29:13 +00:00
FLevelLocals ( ) ;
~ FLevelLocals ( ) ;
2019-01-28 00:33:52 +00:00
2019-02-24 08:57:46 +00:00
void * operator new ( size_t blocksize )
{
// Null the allocated memory before running the constructor.
// If we later allocate secondary levels they need to behave exactly like a global variable, i.e. start nulled.
auto block = : : operator new ( blocksize ) ;
memset ( block , 0 , blocksize ) ;
return block ;
}
2019-01-24 23:30:55 +00:00
friend class MapLoader ;
2022-04-10 06:54:45 +00:00
DIntermissionController * CreateIntermission ( ) ;
2018-12-19 17:41:53 +00:00
void Tick ( ) ;
void Mark ( ) ;
void AddScroller ( int secnum ) ;
void SetInterMusic ( const char * nextmap ) ;
void SetMusicVolume ( float v ) ;
2022-10-20 14:48:18 +00:00
void ClearLevelData ( bool fullgc = true ) ;
2018-12-31 11:42:03 +00:00
void ClearPortals ( ) ;
2019-01-23 23:02:51 +00:00
bool CheckIfExitIsGood ( AActor * self , level_info_t * newmap ) ;
2019-01-24 00:05:07 +00:00
void FormatMapName ( FString & mapname , const char * mapnamecolor ) ;
void ClearAllSubsectorLinks ( ) ;
2019-01-24 00:40:09 +00:00
void TranslateLineDef ( line_t * ld , maplinedef_t * mld , int lineindexforid = - 1 ) ;
2019-02-02 00:14:51 +00:00
int TranslateSectorSpecial ( int special ) ;
2019-01-24 18:28:40 +00:00
bool IsTIDUsed ( int tid ) ;
int FindUniqueTID ( int start_tid , int limit ) ;
2019-01-09 01:03:26 +00:00
int GetConversation ( int conv_id ) ;
int GetConversation ( FName classname ) ;
void SetConversation ( int convid , PClassActor * Class , int dlgindex ) ;
int FindNode ( const FStrifeDialogueNode * node ) ;
2019-01-28 13:31:23 +00:00
int GetInfighting ( ) ;
2019-01-29 18:28:22 +00:00
void SetCompatLineOnSide ( bool state ) ;
2019-03-23 14:28:10 +00:00
int GetCompatibility ( int mask ) ;
int GetCompatibility2 ( int mask ) ;
void ApplyCompatibility ( ) ;
void ApplyCompatibility2 ( ) ;
2019-01-25 18:46:03 +00:00
void Init ( ) ;
2019-01-24 00:40:09 +00:00
2019-01-24 23:30:55 +00:00
private :
2020-07-21 17:53:27 +00:00
bool ShouldDoIntermission ( cluster_info_t * nextcluster , cluster_info_t * thiscluster ) ;
2021-03-05 15:09:13 +00:00
line_t * FindPortalDestination ( line_t * src , int tag , int matchtype = - 1 ) ;
2019-01-24 23:30:55 +00:00
void BuildPortalBlockmap ( ) ;
void UpdatePortal ( FLinePortal * port ) ;
void CollectLinkedPortals ( ) ;
void CreateLinkedPortals ( ) ;
bool ChangePortalLine ( line_t * line , int destid ) ;
void AddDisplacementForPortal ( FSectorPortal * portal ) ;
void AddDisplacementForPortal ( FLinePortal * portal ) ;
bool ConnectPortalGroups ( ) ;
2019-02-23 14:21:54 +00:00
void SerializePlayers ( FSerializer & arc , bool skipload ) ;
void CopyPlayer ( player_t * dst , player_t * src , const char * name ) ;
void ReadOnePlayer ( FSerializer & arc , bool skipload ) ;
void ReadMultiplePlayers ( FSerializer & arc , int numPlayers , int numPlayersNow , bool skipload ) ;
void SerializeSounds ( FSerializer & arc ) ;
2020-05-02 13:20:37 +00:00
void PlayerSpawnPickClass ( int playernum ) ;
2019-02-23 14:21:54 +00:00
2019-01-24 23:30:55 +00:00
public :
2019-01-26 20:23:19 +00:00
void SnapshotLevel ( ) ;
void UnSnapshotLevel ( bool hubLoad ) ;
2019-01-24 23:30:55 +00:00
void FinalizePortals ( ) ;
bool ChangePortal ( line_t * ln , int thisid , int destid ) ;
unsigned GetSkyboxPortal ( AActor * actor ) ;
unsigned GetPortal ( int type , int plane , sector_t * orgsec , sector_t * destsec , const DVector2 & displacement ) ;
unsigned GetStackPortal ( AActor * point , int plane ) ;
DVector2 GetPortalOffsetPosition ( double x , double y , double dx , double dy ) ;
bool CollectConnectedGroups ( int startgroup , const DVector3 & position , double upperz , double checkradius , FPortalGroupArray & out ) ;
2019-01-26 07:28:45 +00:00
void ActivateInStasisPlat ( int tag ) ;
bool CreateCeiling ( sector_t * sec , DCeiling : : ECeiling type , line_t * line , int tag , double speed , double speed2 , double height , int crush , int silent , int change , DCeiling : : ECrushMode hexencrush ) ;
void ActivateInStasisCeiling ( int tag ) ;
bool CreateFloor ( sector_t * sec , DFloor : : EFloor floortype , line_t * line , double speed , double height , int crush , int change , bool hexencrush , bool hereticlower ) ;
2019-01-26 20:23:19 +00:00
void DoDeferedScripts ( ) ;
2019-01-27 00:49:20 +00:00
void AdjustPusher ( int tag , int magnitude , int angle , bool wind ) ;
2019-01-27 13:49:51 +00:00
int Massacre ( bool baddies = false , FName cls = NAME_None ) ;
2019-01-27 15:08:22 +00:00
AActor * SpawnMapThing ( FMapThing * mthing , int position ) ;
AActor * SpawnMapThing ( int index , FMapThing * mt , int position ) ;
2019-01-28 02:02:25 +00:00
AActor * SpawnPlayer ( FPlayerStart * mthing , int playernum , int flags = 0 ) ;
2019-01-27 18:16:14 +00:00
void StartLightning ( ) ;
void ForceLightning ( int mode ) ;
2019-01-27 20:59:19 +00:00
void ClearDynamic3DFloorData ( ) ;
void WorldDone ( void ) ;
void AirControlChanged ( ) ;
AActor * SelectTeleDest ( int tid , int tag , bool norandom ) ;
bool AlignFlat ( int linenum , int side , int fc ) ;
void ReplaceTextures ( const char * fromname , const char * toname , int flags ) ;
bool EV_Thing_Spawn ( int tid , AActor * source , int type , DAngle angle , bool fog , int newtid ) ;
bool EV_Thing_Move ( int tid , AActor * source , int mapspot , bool fog ) ;
bool EV_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 ) ;
int EV_Thing_Damage ( int tid , AActor * whofor0 , int amount , FName type ) ;
2019-01-26 07:28:45 +00:00
bool EV_DoPlat ( int tag , line_t * line , DPlat : : EPlatType type , double height , double speed , int delay , int lip , int change ) ;
void EV_StopPlat ( int tag , bool remove ) ;
bool EV_DoPillar ( DPillar : : EPillar type , line_t * line , int tag , double speed , double height , double height2 , int crush , bool hexencrush ) ;
bool EV_DoDoor ( DDoor : : EVlDoor type , line_t * line , AActor * thing , int tag , double speed , int delay , int lock , int lightTag , bool boomgen = false , int topcountdown = 0 ) ;
bool EV_SlidingDoor ( line_t * line , AActor * thing , int tag , int speed , int delay , DAnimatedDoor : : EADType type ) ;
bool EV_DoCeiling ( DCeiling : : ECeiling type , line_t * line , int tag , double speed , double speed2 , double height , int crush , int silent , int change , DCeiling : : ECrushMode hexencrush = DCeiling : : ECrushMode : : crushDoom ) ;
bool EV_CeilingCrushStop ( int tag , bool remove ) ;
bool EV_StopCeiling ( int tag , line_t * line ) ;
bool EV_BuildStairs ( int tag , DFloor : : EStair type , line_t * line , double stairsize , double speed , int delay , int reset , int igntxt , int usespecials ) ;
bool EV_DoFloor ( DFloor : : EFloor floortype , line_t * line , int tag , double speed , double height , int crush , int change , bool hexencrush , bool hereticlower = false ) ;
bool EV_FloorCrushStop ( int tag , line_t * line ) ;
bool EV_StopFloor ( int tag , line_t * line ) ;
bool EV_DoDonut ( int tag , line_t * line , double pillarspeed , double slimespeed ) ;
bool EV_DoElevator ( line_t * line , DElevator : : EElevator type , double speed , double height , int tag ) ;
bool EV_StartWaggle ( int tag , line_t * line , int height , int speed , int offset , int timer , bool ceiling ) ;
bool EV_DoChange ( line_t * line , EChange changetype , int tag ) ;
2019-01-26 16:55:15 +00:00
void EV_StartLightFlickering ( int tag , int upper , int lower ) ;
void EV_StartLightStrobing ( int tag , int upper , int lower , int utics , int ltics ) ;
void EV_StartLightStrobing ( int tag , int utics , int ltics ) ;
void EV_TurnTagLightsOff ( int tag ) ;
void EV_LightTurnOn ( int tag , int bright ) ;
void EV_LightTurnOnPartway ( int tag , double frac ) ;
void EV_LightChange ( int tag , int value ) ;
void EV_StartLightGlowing ( int tag , int upper , int lower , int tics ) ;
void EV_StartLightFading ( int tag , int value , int tics ) ;
void EV_StopLightEffect ( int tag ) ;
2019-01-27 20:59:19 +00:00
bool EV_Teleport ( int tid , int tag , line_t * line , int side , AActor * thing , int flags ) ;
bool EV_SilentLineTeleport ( line_t * line , int side , AActor * thing , int id , INTBOOL reverse ) ;
bool EV_TeleportOther ( int other_tid , int dest_tid , bool fog ) ;
bool EV_TeleportGroup ( int group_tid , AActor * victim , int source_tid , int dest_tid , bool moveSource , bool fog ) ;
bool EV_TeleportSector ( int tag , int source_tid , int dest_tid , bool fog , int group_tid ) ;
2019-01-26 16:55:15 +00:00
void RecalculateDrawnSubsectors ( ) ;
FSerializer & SerializeSubsectors ( FSerializer & arc , const char * key ) ;
void SpawnExtraPlayers ( ) ;
void Serialize ( FSerializer & arc , bool hubload ) ;
2019-01-28 13:06:19 +00:00
DThinker * FirstThinker ( int statnum ) ;
2019-01-26 16:55:15 +00:00
2019-01-28 01:44:05 +00:00
// g_Game
void PlayerReborn ( int player ) ;
bool CheckSpot ( int playernum , FPlayerStart * mthing ) ;
void DoReborn ( int playernum , bool freshbot ) ;
void QueueBody ( AActor * body ) ;
double PlayersRangeFromSpot ( FPlayerStart * spot ) ;
FPlayerStart * SelectFarthestDeathmatchSpot ( size_t selections ) ;
FPlayerStart * SelectRandomDeathmatchSpot ( int playernum , unsigned int selections ) ;
void DeathMatchSpawnPlayer ( int playernum ) ;
FPlayerStart * PickPlayerStart ( int playernum , int flags = 0 ) ;
2019-01-29 01:39:14 +00:00
bool DoCompleted ( FString nextlevel , wbstartstruct_t & wminfo ) ;
2019-01-29 15:11:23 +00:00
void StartTravel ( ) ;
int FinishTravel ( ) ;
2019-01-29 22:45:14 +00:00
void ChangeLevel ( const char * levelname , int position , int flags , int nextSkill = - 1 ) ;
const char * GetSecretExitMap ( ) ;
void ExitLevel ( int position , bool keepFacing ) ;
void SecretExitLevel ( int position ) ;
2019-01-29 20:19:16 +00:00
void DoLoadLevel ( const FString & nextmapname , int position , bool autosave , bool newGame ) ;
2019-01-28 01:44:05 +00:00
2019-01-29 23:27:05 +00:00
void DeleteAllAttachedLights ( ) ;
void RecreateAllAttachedLights ( ) ;
2019-01-28 00:25:52 +00:00
2019-01-24 23:30:55 +00:00
private :
// Work data for CollectConnectedGroups.
FPortalBits processMask ;
TArray < FLinePortal * > foundPortals ;
TArray < int > groupsToCheck ;
public :
2019-01-23 23:43:43 +00:00
FSectorTagIterator GetSectorTagIterator ( int tag )
{
2019-01-24 00:53:05 +00:00
return FSectorTagIterator ( tagManager , tag ) ;
2019-01-23 23:43:43 +00:00
}
FSectorTagIterator GetSectorTagIterator ( int tag , line_t * line )
{
2019-01-24 00:53:05 +00:00
return FSectorTagIterator ( tagManager , tag , line ) ;
2019-01-23 23:43:43 +00:00
}
FLineIdIterator GetLineIdIterator ( int tag )
{
2019-01-24 00:53:05 +00:00
return FLineIdIterator ( tagManager , tag ) ;
2019-01-23 23:43:43 +00:00
}
2019-01-26 14:21:20 +00:00
template < class T > TThinkerIterator < T > GetThinkerIterator ( FName subtype = NAME_None , int statnum = MAX_STATNUM + 1 )
2019-01-23 23:43:43 +00:00
{
2019-01-30 18:09:21 +00:00
if ( subtype = = NAME_None ) return TThinkerIterator < T > ( this , statnum ) ;
else return TThinkerIterator < T > ( this , subtype , statnum ) ;
2019-01-23 23:43:43 +00:00
}
2019-01-27 23:55:21 +00:00
template < class T > TThinkerIterator < T > GetThinkerIterator ( FName subtype , int statnum , AActor * prev )
{
2019-01-30 18:09:21 +00:00
return TThinkerIterator < T > ( this , subtype , statnum , prev ) ;
2019-01-27 23:55:21 +00:00
}
2019-01-23 23:43:43 +00:00
FActorIterator GetActorIterator ( int tid )
{
2019-01-24 18:28:40 +00:00
return FActorIterator ( TIDHash , tid ) ;
2019-01-23 23:43:43 +00:00
}
2019-01-24 17:50:22 +00:00
FActorIterator GetActorIterator ( int tid , AActor * start )
{
2019-01-24 18:28:40 +00:00
return FActorIterator ( TIDHash , tid , start ) ;
2019-01-24 17:50:22 +00:00
}
2019-01-23 23:43:43 +00:00
NActorIterator GetActorIterator ( FName type , int tid )
{
2019-01-24 18:28:40 +00:00
return NActorIterator ( TIDHash , type , tid ) ;
2019-01-23 23:43:43 +00:00
}
2019-01-25 18:04:40 +00:00
AActor * SingleActorFromTID ( int tid , AActor * defactor )
{
return tid = = 0 ? defactor : GetActorIterator ( tid ) . Next ( ) ;
}
2019-01-23 23:43:43 +00:00
bool SectorHasTags ( sector_t * sector )
{
return tagManager . SectorHasTags ( sector ) ;
}
bool SectorHasTag ( sector_t * sector , int tag )
{
return tagManager . SectorHasTag ( sector , tag ) ;
}
bool SectorHasTag ( int sector , int tag )
{
return tagManager . SectorHasTag ( sector , tag ) ;
}
2019-01-24 00:40:09 +00:00
int GetFirstSectorTag ( const sector_t * sect ) const
{
return tagManager . GetFirstSectorTag ( sect ) ;
}
int GetFirstSectorTag ( int i ) const
{
return tagManager . GetFirstSectorTag ( i ) ;
}
int GetFirstLineId ( const line_t * sect ) const
{
return tagManager . GetFirstLineID ( sect ) ;
}
2019-01-23 23:43:43 +00:00
bool LineHasId ( int line , int tag )
{
return tagManager . LineHasID ( line , tag ) ;
}
2019-01-24 00:40:09 +00:00
bool LineHasId ( line_t * line , int tag )
{
return tagManager . LineHasID ( line , tag ) ;
}
int FindFirstSectorFromTag ( int tag )
{
auto it = GetSectorTagIterator ( tag ) ;
return it . Next ( ) ;
}
int FindFirstLineFromID ( int tag )
{
auto it = GetLineIdIterator ( tag ) ;
return it . Next ( ) ;
}
2019-01-28 19:15:48 +00:00
int isFrozen ( )
{
return frozenstate ;
}
2019-01-29 00:30:41 +00:00
private : // The engine should never ever access subsectors of the game nodes. This is only needed for actually implementing PointInSector.
subsector_t * PointInSubsector ( double x , double y ) ;
public :
sector_t * PointInSectorBuggy ( double x , double y ) ;
subsector_t * PointInRenderSubsector ( fixed_t x , fixed_t y ) ;
2019-01-23 23:43:43 +00:00
sector_t * PointInSector ( const DVector2 & pos )
{
2019-01-29 00:30:41 +00:00
return PointInSubsector ( pos . X , pos . Y ) - > sector ;
2019-01-23 23:43:43 +00:00
}
2019-01-29 00:30:41 +00:00
sector_t * PointInSector ( double x , double y )
{
return PointInSubsector ( x , y ) - > sector ;
}
subsector_t * PointInRenderSubsector ( const DVector2 & pos )
{
return PointInRenderSubsector ( FloatToFixed ( pos . X ) , FloatToFixed ( pos . Y ) ) ;
}
2019-01-24 00:05:07 +00:00
FPolyObj * GetPolyobj ( int polyNum )
{
auto index = Polyobjects . FindEx ( [ = ] ( const auto & poly ) { return poly . tag = = polyNum ; } ) ;
return index = = Polyobjects . Size ( ) ? nullptr : & Polyobjects [ index ] ;
}
2018-12-19 17:41:53 +00:00
2019-01-24 18:28:40 +00:00
void ClearTIDHashes ( )
{
memset ( TIDHash , 0 , sizeof ( TIDHash ) ) ;
}
2019-01-26 08:01:40 +00:00
bool CheckReject ( sector_t * s1 , sector_t * s2 )
{
if ( rejectmatrix . Size ( ) > 0 )
{
int pnum = int ( s1 - > Index ( ) ) * sectors . Size ( ) + int ( s2 - > Index ( ) ) ;
return ! ( rejectmatrix [ pnum > > 3 ] & ( 1 < < ( pnum & 7 ) ) ) ;
}
return true ;
}
2019-01-27 12:08:54 +00:00
DThinker * CreateThinker ( PClass * cls , int statnum = STAT_DEFAULT )
2019-01-27 00:49:20 +00:00
{
2019-01-27 12:08:54 +00:00
DThinker * thinker = static_cast < DThinker * > ( cls - > CreateNew ( ) ) ;
assert ( thinker - > IsKindOf ( RUNTIME_CLASS ( DThinker ) ) ) ;
2019-01-27 00:49:20 +00:00
thinker - > ObjectFlags | = OF_JustSpawned ;
2019-01-30 01:15:48 +00:00
Thinkers . Link ( thinker , statnum ) ;
2019-01-27 00:49:20 +00:00
thinker - > Level = this ;
return thinker ;
}
2019-01-27 12:08:54 +00:00
template < typename T , typename . . . Args >
T * CreateThinker ( Args & & . . . args )
2019-01-27 00:49:20 +00:00
{
2019-01-27 12:08:54 +00:00
auto thinker = static_cast < T * > ( CreateThinker ( RUNTIME_CLASS ( T ) , T : : DEFAULT_STAT ) ) ;
thinker - > Construct ( std : : forward < Args > ( args ) . . . ) ;
2019-01-27 00:49:20 +00:00
return thinker ;
}
2019-01-28 00:25:52 +00:00
2019-08-23 15:15:19 +00:00
void SetMusic ( ) ;
2019-01-28 21:04:33 +00:00
TArray < vertex_t > vertexes ;
TArray < sector_t > sectors ;
2020-10-04 22:39:21 +00:00
TArray < extsector_t > extsectors ; // container for non-trivial sector information. sector_t must be trivially copyable for *_fakeflat to work as intended.
2019-01-28 21:04:33 +00:00
TArray < line_t * > linebuffer ; // contains the line lists for the sectors.
TArray < subsector_t * > subsectorbuffer ; // contains the subsector lists for the sectors.
TArray < line_t > lines ;
TArray < side_t > sides ;
TArray < seg_t * > segbuffer ; // contains the seg links for the sidedefs.
TArray < seg_t > segs ;
TArray < subsector_t > subsectors ;
TArray < node_t > nodes ;
TArray < subsector_t > gamesubsectors ;
TArray < node_t > gamenodes ;
node_t * headgamenode ;
TArray < uint8_t > rejectmatrix ;
TArray < zone_t > Zones ;
TArray < FPolyObj > Polyobjects ;
TArray < FSectorPortal > sectorPortals ;
TArray < FLinePortal > linePortals ;
2021-09-24 06:51:48 +00:00
// Lightmaps
TArray < LightmapSurface > LMSurfaces ;
TArray < float > LMTexCoords ;
int LMTextureCount = 0 ;
int LMTextureSize = 0 ;
2021-09-24 22:13:25 +00:00
TArray < uint16_t > LMTextureData ;
2021-09-25 16:53:18 +00:00
TArray < LightProbe > LightProbes ;
2021-10-15 02:21:18 +00:00
int LPMinX = 0 ;
int LPMinY = 0 ;
int LPWidth = 0 ;
int LPHeight = 0 ;
static const int LPCellSize = 32 ;
TArray < LightProbeCell > LPCells ;
2021-09-24 06:51:48 +00:00
2019-01-28 21:04:33 +00:00
// Portal information.
FDisplacementTable Displacements ;
FPortalBlockmap PortalBlockmap ;
TArray < FLinePortal * > linkedPortals ; // only the linked portals, this is used to speed up looking for them in P_CollectConnectedGroups.
TArray < FSectorPortalGroup * > portalGroups ;
TArray < FLinePortalSpan > linePortalSpans ;
FSectionContainer sections ;
FCanvasTextureInfo canvasTextureInfo ;
2019-02-02 15:43:11 +00:00
EventManager * localEventManager = nullptr ;
2020-04-26 16:54:43 +00:00
DoomLevelAABBTree * aabbTree = nullptr ;
2022-06-05 21:24:53 +00:00
DoomLevelMesh * levelMesh = nullptr ;
2019-01-28 21:04:33 +00:00
// [ZZ] Destructible geometry information
TMap < int , FHealthGroup > healthGroups ;
FBlockmap blockmap ;
TArray < polyblock_t * > PolyBlockMap ;
2019-01-29 23:37:29 +00:00
FUDMFKeyMap UDMFKeys [ 4 ] ;
2019-01-28 21:04:33 +00:00
// These are copies of the loaded map data that get used by the savegame code to skip unaltered fields
// Without such a mechanism the savegame format would become too slow and large because more than 80-90% are normally still unaltered.
TArray < sector_t > loadsectors ;
TArray < line_t > loadlines ;
TArray < side_t > loadsides ;
// Maintain single and multi player starting spots.
TArray < FPlayerStart > deathmatchstarts ;
FPlayerStart playerstarts [ MAXPLAYERS ] ;
TArray < FPlayerStart > AllPlayerStarts ;
FBehaviorContainer Behaviors ;
AActor * TIDHash [ 128 ] ;
TArray < FStrifeDialogueNode * > StrifeDialogues ;
FDialogueIDMap DialogueRoots ;
FDialogueMap ClassRoots ;
2019-01-30 00:38:18 +00:00
FCajunMaster BotInfo ;
2019-01-26 08:01:40 +00:00
2019-01-29 18:28:22 +00:00
int ii_compatflags = 0 ;
int ii_compatflags2 = 0 ;
int ib_compatflags = 0 ;
int i_compatflags = 0 ;
int i_compatflags2 = 0 ;
2019-01-10 01:00:58 +00:00
DSectorMarker * SectorMarker ;
2018-12-19 17:41:53 +00:00
uint8_t md5 [ 16 ] ; // for savegame validation. If the MD5 does not match the savegame won't be loaded.
int time ; // time in the hub
2019-01-29 23:37:29 +00:00
int maptime ; // time in the map
2018-12-19 17:41:53 +00:00
int totaltime ; // time in the game
int starttime ;
int partime ;
int sucktime ;
2018-12-24 09:18:25 +00:00
uint32_t spawnindex ;
2018-12-19 17:41:53 +00:00
level_info_t * info ;
int cluster ;
int clusterflags ;
int levelnum ;
int lumpnum ;
FString LevelName ;
FString MapName ; // the lump name (E1M1, MAP01, etc)
FString NextMap ; // go here when using the regular exit
FString NextSecretMap ; // map to go to when used secret exit
2019-08-02 07:11:40 +00:00
FString AuthorName ;
2018-12-19 17:41:53 +00:00
FString F1Pic ;
2019-02-02 00:14:51 +00:00
FTranslator * Translator ;
2018-12-19 17:41:53 +00:00
EMapType maptype ;
2019-01-28 00:37:21 +00:00
FTagManager tagManager ;
2019-01-28 17:26:14 +00:00
FInterpolator interpolator ;
2018-12-19 17:41:53 +00:00
uint64_t ShaderStartTime = 0 ; // tell the shader system when we started the level (forces a timer restart)
static const int BODYQUESIZE = 32 ;
TObjPtr < AActor * > bodyque [ BODYQUESIZE ] ;
2022-06-06 13:19:31 +00:00
TObjPtr < DAutomapBase * > automap = MakeObjPtr < DAutomapBase * > ( nullptr ) ;
2018-12-19 17:41:53 +00:00
int bodyqueslot ;
2019-01-30 00:15:32 +00:00
// For now this merely points to the global player array, but with this in place, access to this array can be moved over to the level.
2019-02-01 16:02:10 +00:00
// As things progress each level needs to be able to point to different players, even if they are just null if the second level is merely a skybox or camera target.
// But even if it got a real player, the level will not own it - the player merely links to the level.
2019-01-30 00:15:32 +00:00
// This should also be made a real object eventually.
player_t * Players [ MAXPLAYERS ] ;
// This is to allow refactoring without refactoring the data right away.
bool PlayerInGame ( int pnum )
{
return playeringame [ pnum ] ;
}
// This needs to be done better, but for now it should be good enough.
bool PlayerInGame ( player_t * player )
{
for ( int i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( player = = Players [ i ] ) return PlayerInGame ( i ) ;
}
return false ;
}
int PlayerNum ( player_t * player )
{
for ( int i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( player = = Players [ i ] ) return i ;
}
return - 1 ;
}
bool isPrimaryLevel ( ) const
{
return true ;
}
2019-02-01 16:02:10 +00:00
// Gets the console player without having the calling code be aware of the level's state.
player_t * GetConsolePlayer ( ) const
{
return isPrimaryLevel ( ) ? Players [ consoleplayer ] : nullptr ;
}
bool isConsolePlayer ( AActor * mo ) const
{
auto p = GetConsolePlayer ( ) ;
if ( ! p ) return false ;
return p - > mo = = mo ;
}
bool isCamera ( AActor * mo ) const
{
auto p = GetConsolePlayer ( ) ;
if ( ! p ) return false ;
return p - > camera = = mo ;
}
2018-12-19 17:41:53 +00:00
2022-02-14 19:00:06 +00:00
bool MBF21Enabled ( ) const
{
// The affected features only are a problem with Doom format maps - the flag should have no effect in Hexen and UDMF format.
return ! ( i_compatflags2 & COMPATF2_NOMBF21 ) | | maptype ! = MAPTYPE_DOOM ;
}
2018-12-19 17:41:53 +00:00
int NumMapSections ;
2017-01-14 15:05:40 +00:00
2017-03-08 17:47:52 +00:00
uint32_t flags ;
uint32_t flags2 ;
uint32_t flags3 ;
2017-01-08 17:45:30 +00:00
2017-03-08 17:47:52 +00:00
uint32_t fadeto ; // The color the palette fades to (usually black)
uint32_t outsidefog ; // The fog for sectors with sky ceilings
2017-01-08 17:45:30 +00:00
2017-03-08 17:47:52 +00:00
uint32_t hazardcolor ; // what color strife hazard blends the screen color as
uint32_t hazardflash ; // what color strife hazard flashes the screen color as
2017-03-05 14:58:07 +00:00
2017-01-08 17:45:30 +00:00
FString Music ;
int musicorder ;
int cdtrack ;
unsigned int cdid ;
FTextureID skytexture1 ;
FTextureID skytexture2 ;
float skyspeed1 ; // Scrolling speed of sky textures, in pixels per ms
float skyspeed2 ;
2019-01-29 03:44:44 +00:00
double sky1pos , sky2pos ;
float hw_sky1pos , hw_sky2pos ;
bool skystretch ;
2017-01-08 17:45:30 +00:00
int total_secrets ;
int found_secrets ;
int total_items ;
int found_items ;
int total_monsters ;
int killed_monsters ;
2019-08-28 18:33:07 +00:00
double max_velocity ;
double avg_velocity ;
2017-01-08 17:45:30 +00:00
double gravity ;
double aircontrol ;
double airfriction ;
int airsupply ;
int DefaultEnvironment ; // Default sound environment.
2019-01-28 22:53:40 +00:00
DSeqNode * SequenceListHead ;
2019-01-29 00:09:02 +00:00
// [RH] particle globals
2022-11-11 00:59:30 +00:00
uint32_t OldestParticle ; // [MC] Oldest particle for replacing with PS_REPLACE
2019-01-29 00:09:02 +00:00
uint32_t ActiveParticles ;
uint32_t InactiveParticles ;
TArray < particle_t > Particles ;
TArray < uint16_t > ParticlesInSubsec ;
2019-01-30 18:09:21 +00:00
FThinkerCollection Thinkers ;
2019-01-29 00:09:02 +00:00
2017-01-08 17:45:30 +00:00
TArray < DVector2 > Scrolls ; // NULL if no DScrollers in this level
2017-03-08 17:47:52 +00:00
int8_t WallVertLight ; // Light diffs for vert/horiz walls
int8_t WallHorizLight ;
2017-01-08 17:45:30 +00:00
bool FromSnapshot ; // The current map was restored from a snapshot
2018-05-01 07:02:24 +00:00
bool HasHeightSecs ; // true if some Transfer_Heights effects are present in the map. If this is false, some checks in the renderer can be shortcut.
2018-05-03 19:27:45 +00:00
bool HasDynamicLights ; // Another render optimization for maps with no lights at all.
2019-02-08 19:04:45 +00:00
int frozenstate ;
2017-01-08 17:45:30 +00:00
double teamdamage ;
2017-03-14 12:54:24 +00:00
// former OpenGL-exclusive properties that should also be usable by the true color software renderer.
int fogdensity ;
int outsidefogdensity ;
int skyfog ;
2018-11-24 19:32:12 +00:00
FName deathsequence ;
2017-12-29 07:42:03 +00:00
float pixelstretch ;
2018-03-24 12:06:37 +00:00
float MusicVolume ;
2017-03-14 12:54:24 +00:00
2018-04-01 16:45:27 +00:00
// Hardware render stuff that can either be set via CVAR or MAPINFO
2019-01-06 08:00:52 +00:00
ELightMode lightMode ;
2018-04-01 16:45:27 +00:00
bool brightfog ;
bool lightadditivesurfaces ;
bool notexturefill ;
2019-01-08 23:04:28 +00:00
int ImpactDecalCount ;
2019-01-01 18:35:55 +00:00
FDynamicLight * lights ;
2019-01-05 08:40:03 +00:00
// links to global game objects
2019-01-05 09:04:27 +00:00
TArray < TObjPtr < AActor * > > CorpseQueue ;
2022-06-06 13:19:31 +00:00
TObjPtr < DFraggleThinker * > FraggleScriptThinker = MakeObjPtr < DFraggleThinker * > ( nullptr ) ;
TObjPtr < DACSThinker * > ACSThinker = MakeObjPtr < DACSThinker * > ( nullptr ) ;
2019-01-05 20:59:34 +00:00
2022-06-06 13:19:31 +00:00
TObjPtr < DSpotState * > SpotState = MakeObjPtr < DSpotState * > ( nullptr ) ;
2018-04-01 16:45:27 +00:00
2019-02-23 14:21:54 +00:00
//==========================================================================
//
//
//==========================================================================
bool IsJumpingAllowed ( ) const
{
if ( dmflags & DF_NO_JUMP )
return false ;
if ( dmflags & DF_YES_JUMP )
return true ;
return ! ( flags & LEVEL_JUMP_NO ) ;
}
//==========================================================================
//
//
//==========================================================================
bool IsCrouchingAllowed ( ) const
{
if ( dmflags & DF_NO_CROUCH )
return false ;
if ( dmflags & DF_YES_CROUCH )
return true ;
return ! ( flags & LEVEL_CROUCH_NO ) ;
}
//==========================================================================
//
//
//==========================================================================
bool IsFreelookAllowed ( ) const
{
if ( dmflags & DF_NO_FREELOOK )
return false ;
if ( dmflags & DF_YES_FREELOOK )
return true ;
return ! ( flags & LEVEL_FREELOOK_NO ) ;
}
2017-03-17 00:42:37 +00:00
node_t * HeadNode ( ) const
{
2018-12-15 08:40:39 +00:00
return nodes . Size ( ) = = 0 ? nullptr : & nodes [ nodes . Size ( ) - 1 ] ;
2017-03-17 00:42:37 +00:00
}
node_t * HeadGamenode ( ) const
{
2017-03-17 11:09:38 +00:00
return headgamenode ;
2017-03-17 00:42:37 +00:00
}
2018-03-24 12:06:37 +00:00
2018-06-11 08:55:49 +00:00
// Returns true if level is loaded from saved game or is being revisited as a part of a hub
bool IsReentering ( ) const
{
2018-12-15 08:40:39 +00:00
return savegamerestore
2018-06-11 08:55:49 +00:00
| | ( info ! = nullptr & & info - > Snapshot . mBuffer ! = nullptr & & info - > isValid ( ) ) ;
}
2017-01-08 17:45:30 +00:00
} ;
2018-12-19 17:41:53 +00:00
2017-01-08 17:45:30 +00:00
extern FLevelLocals level ;
2019-02-01 23:24:43 +00:00
extern FLevelLocals * primaryLevel ; // level for which to display the user interface. This will always be the one the current consoleplayer is in.
2019-02-01 21:02:16 +00:00
extern FLevelLocals * currentVMLevel ;
2017-01-08 17:45:30 +00:00
2017-01-14 15:05:40 +00:00
inline FSectorPortal * line_t : : GetTransferredPortal ( )
{
2019-01-27 23:55:21 +00:00
auto Level = GetLevel ( ) ;
return portaltransferred > = Level - > sectorPortals . Size ( ) ? ( FSectorPortal * ) nullptr : & Level - > sectorPortals [ portaltransferred ] ;
2017-01-14 15:05:40 +00:00
}
inline FSectorPortal * sector_t : : GetPortal ( int plane )
{
2019-01-27 16:35:50 +00:00
return & Level - > sectorPortals [ Portals [ plane ] ] ;
2017-01-14 15:05:40 +00:00
}
inline double sector_t : : GetPortalPlaneZ ( int plane )
{
2019-01-27 16:35:50 +00:00
return Level - > sectorPortals [ Portals [ plane ] ] . mPlaneZ ;
2017-01-14 15:05:40 +00:00
}
inline DVector2 sector_t : : GetPortalDisplacement ( int plane )
{
2019-01-27 16:35:50 +00:00
return Level - > sectorPortals [ Portals [ plane ] ] . mDisplacement ;
2017-01-14 15:05:40 +00:00
}
inline int sector_t : : GetPortalType ( int plane )
{
2019-01-27 16:35:50 +00:00
return Level - > sectorPortals [ Portals [ plane ] ] . mType ;
2017-01-14 15:05:40 +00:00
}
inline int sector_t : : GetOppositePortalGroup ( int plane )
{
2019-01-27 16:35:50 +00:00
return Level - > sectorPortals [ Portals [ plane ] ] . mDestination - > PortalGroup ;
2017-01-14 15:05:40 +00:00
}
2017-03-10 01:22:42 +00:00
inline bool sector_t : : PortalBlocksView ( int plane )
{
if ( GetPortalType ( plane ) ! = PORTS_LINKEDPORTAL ) return false ;
return ! ! ( planes [ plane ] . Flags & ( PLANEF_NORENDER | PLANEF_DISABLED | PLANEF_OBSTRUCTED ) ) ;
}
inline bool sector_t : : PortalBlocksSight ( int plane )
{
return PLANEF_LINKED ! = ( planes [ plane ] . Flags & ( PLANEF_NORENDER | PLANEF_NOPASS | PLANEF_DISABLED | PLANEF_OBSTRUCTED | PLANEF_LINKED ) ) ;
}
inline bool sector_t : : PortalBlocksMovement ( int plane )
{
return PLANEF_LINKED ! = ( planes [ plane ] . Flags & ( PLANEF_NOPASS | PLANEF_DISABLED | PLANEF_OBSTRUCTED | PLANEF_LINKED ) ) ;
}
inline bool sector_t : : PortalBlocksSound ( int plane )
{
return PLANEF_LINKED ! = ( planes [ plane ] . Flags & ( PLANEF_BLOCKSOUND | PLANEF_DISABLED | PLANEF_OBSTRUCTED | PLANEF_LINKED ) ) ;
}
inline bool sector_t : : PortalIsLinked ( int plane )
{
return ( GetPortalType ( plane ) = = PORTS_LINKEDPORTAL ) ;
}
2019-01-24 23:42:55 +00:00
inline FLevelLocals * line_t : : GetLevel ( ) const
{
2019-01-27 23:55:21 +00:00
return frontsector - > Level ;
2019-01-24 23:42:55 +00:00
}
2017-03-10 01:22:42 +00:00
inline FLinePortal * line_t : : getPortal ( ) const
{
2021-03-05 09:04:18 +00:00
return portalindex = = UINT_MAX & & portalindex > = GetLevel ( ) - > linePortals . Size ( ) ? ( FLinePortal * ) nullptr : & GetLevel ( ) - > linePortals [ portalindex ] ;
2017-03-10 01:22:42 +00:00
}
// returns true if the portal is crossable by actors
inline bool line_t : : isLinePortal ( ) const
{
2021-03-05 09:04:18 +00:00
return portalindex = = UINT_MAX & & portalindex > = GetLevel ( ) - > linePortals . Size ( ) ? false : ! ! ( GetLevel ( ) - > linePortals [ portalindex ] . mFlags & PORTF_PASSABLE ) ;
2017-03-10 01:22:42 +00:00
}
// returns true if the portal needs to be handled by the renderer
inline bool line_t : : isVisualPortal ( ) const
{
2021-03-05 09:04:18 +00:00
return portalindex = = UINT_MAX & & portalindex > = GetLevel ( ) - > linePortals . Size ( ) ? false : ! ! ( GetLevel ( ) - > linePortals [ portalindex ] . mFlags & PORTF_VISIBLE ) ;
2017-03-10 01:22:42 +00:00
}
inline line_t * line_t : : getPortalDestination ( ) const
{
2019-01-24 23:42:55 +00:00
return portalindex > = GetLevel ( ) - > linePortals . Size ( ) ? ( line_t * ) nullptr : GetLevel ( ) - > linePortals [ portalindex ] . mDestination ;
2017-03-10 01:22:42 +00:00
}
2023-01-15 20:29:23 +00:00
inline int line_t : : getPortalFlags ( ) const
{
return portalindex > = GetLevel ( ) - > linePortals . Size ( ) ? 0 : GetLevel ( ) - > linePortals [ portalindex ] . mFlags ;
}
2017-03-10 01:22:42 +00:00
inline int line_t : : getPortalAlignment ( ) const
{
2019-01-24 23:42:55 +00:00
return portalindex > = GetLevel ( ) - > linePortals . Size ( ) ? 0 : GetLevel ( ) - > linePortals [ portalindex ] . mAlign ;
2017-03-10 01:22:42 +00:00
}
2018-11-30 10:22:34 +00:00
2023-01-15 20:29:23 +00:00
inline int line_t : : getPortalType ( ) const
{
return portalindex > = GetLevel ( ) - > linePortals . Size ( ) ? 0 : GetLevel ( ) - > linePortals [ portalindex ] . mType ;
}
inline DVector2 line_t : : getPortalDisplacement ( ) const
{
return portalindex > = GetLevel ( ) - > linePortals . Size ( ) ? DVector2 ( 0. , 0. ) : GetLevel ( ) - > linePortals [ portalindex ] . mDisplacement ;
}
inline DAngle line_t : : getPortalAngleDiff ( ) const
{
return portalindex > = GetLevel ( ) - > linePortals . Size ( ) ? DAngle : : fromDeg ( 0. ) : GetLevel ( ) - > linePortals [ portalindex ] . mAngleDiff ;
}
2018-11-30 10:22:34 +00:00
inline bool line_t : : hitSkyWall ( AActor * mo ) const
{
return backsector & &
backsector - > GetTexture ( sector_t : : ceiling ) = = skyflatnum & &
mo - > Z ( ) > = backsector - > ceilingplane . ZatPoint ( mo - > PosRelative ( this ) ) ;
}
2019-01-27 15:08:22 +00:00
2019-01-28 01:41:29 +00:00
// This must later be extended to return an array with all levels.
// It is meant for code that needs to iterate over all levels to make some global changes, e.g. configuation CCMDs.
inline TArrayView < FLevelLocals * > AllLevels ( )
{
2019-02-01 23:24:43 +00:00
return TArrayView < FLevelLocals * > ( & primaryLevel , 1 ) ;
2019-01-28 01:41:29 +00:00
}