2018-12-28 08:17:10 +00:00
//-----------------------------------------------------------------------------
//
// Copyright 1993-1996 id Software
// Copyright 1994-1996 Raven Software
// Copyright 1999-2016 Randy Heit
// Copyright 2002-2018 Christoph Oelckers
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//-----------------------------------------------------------------------------
//
// DESCRIPTION:
//
//-----------------------------------------------------------------------------
# include <math.h>
# ifdef _MSC_VER
# include <malloc.h> // for alloca()
# endif
2021-10-30 08:49:12 +00:00
2018-12-28 08:17:10 +00:00
# include "d_player.h"
# include "m_argv.h"
# include "g_game.h"
2020-04-11 11:36:23 +00:00
# include "filesystem.h"
2018-12-28 08:17:10 +00:00
# include "p_local.h"
# include "p_effect.h"
# include "p_terrain.h"
# include "nodebuild.h"
# include "p_lnspec.h"
# include "c_console.h"
# include "p_acs.h"
# include "announcer.h"
# include "wi_stuff.h"
2020-04-11 10:56:55 +00:00
# include "engineerrors.h"
2018-12-28 08:17:10 +00:00
# include "gi.h"
# include "p_conversation.h"
# include "a_keys.h"
# include "s_sndseq.h"
# include "sbar.h"
# include "p_setup.h"
# include "r_data/r_interpolate.h"
# include "r_sky.h"
# include "cmdlib.h"
# include "md5.h"
# include "po_man.h"
2019-07-14 19:09:49 +00:00
# include "swrenderer/r_renderer.h"
2018-12-28 08:17:10 +00:00
# include "p_blockmap.h"
# include "r_utility.h"
# include "p_spec.h"
# include "g_levellocals.h"
# include "c_dispatch.h"
# include "a_dynlight.h"
# include "events.h"
# include "p_destructible.h"
# include "types.h"
# include "i_time.h"
2020-04-11 17:21:41 +00:00
# include "vm.h"
2019-01-05 09:04:27 +00:00
# include "a_specialspot.h"
2018-12-28 08:17:10 +00:00
# include "maploader/maploader.h"
2019-01-06 09:24:27 +00:00
# include "p_acs.h"
2019-01-26 14:21:20 +00:00
# include "am_map.h"
2019-01-31 18:38:04 +00:00
# include "i_system.h"
2019-01-31 23:30:21 +00:00
# include "v_video.h"
2019-01-06 09:24:27 +00:00
# include "fragglescript/t_script.h"
2019-08-23 15:15:19 +00:00
# include "s_music.h"
2020-04-11 16:54:54 +00:00
# include "animations.h"
2020-04-11 17:00:07 +00:00
# include "texturemanager.h"
2020-04-18 11:13:57 +00:00
# include "p_lnspec.h"
2020-04-27 21:53:26 +00:00
# include "d_main.h"
2018-12-28 08:17:10 +00:00
extern AActor * SpawnMapThing ( int index , FMapThing * mthing , int position ) ;
extern unsigned int R_OldBlend ;
//===========================================================================
//
// P_PrecacheLevel
//
// Preloads all relevant graphics for the Level->
//
//===========================================================================
void hw_PrecacheTexture ( uint8_t * texhitlist , TMap < PClassActor * , bool > & actorhitlist ) ;
static void AddToList ( uint8_t * hitlist , FTextureID texid , int bitmask )
{
if ( hitlist [ texid . GetIndex ( ) ] & bitmask ) return ; // already done, no need to process everything again.
hitlist [ texid . GetIndex ( ) ] | = ( uint8_t ) bitmask ;
2019-12-23 11:17:16 +00:00
const auto addAnimations = [ hitlist , bitmask ] ( const FTextureID texid )
2018-12-28 08:17:10 +00:00
{
2022-07-17 08:14:37 +00:00
for ( auto & anim : TexAnim . GetAnimations ( ) )
2018-12-28 08:17:10 +00:00
{
2022-07-17 08:14:37 +00:00
if ( texid = = anim . BasePic | | ( ! anim . bDiscrete & & anim . BasePic < texid & & texid < anim . BasePic + anim . NumFrames ) )
2018-12-28 08:17:10 +00:00
{
2022-07-17 08:14:37 +00:00
for ( int i = anim . BasePic . GetIndex ( ) ; i < anim . BasePic . GetIndex ( ) + anim . NumFrames ; i + + )
2019-12-23 11:17:16 +00:00
{
hitlist [ i ] | = ( uint8_t ) bitmask ;
}
2018-12-28 08:17:10 +00:00
}
}
2019-12-23 11:17:16 +00:00
} ;
addAnimations ( texid ) ;
2018-12-28 08:17:10 +00:00
2020-04-11 16:54:54 +00:00
auto switchdef = TexAnim . FindSwitch ( texid ) ;
2018-12-28 08:17:10 +00:00
if ( switchdef )
{
2019-12-23 11:17:16 +00:00
const FSwitchDef * const pair = switchdef - > PairDef ;
const uint16_t numFrames = switchdef - > NumFrames ;
const uint16_t pairNumFrames = pair - > NumFrames ;
for ( int i = 0 ; i < numFrames ; i + + )
2018-12-28 08:17:10 +00:00
{
hitlist [ switchdef - > frames [ i ] . Texture . GetIndex ( ) ] | = ( uint8_t ) bitmask ;
}
2019-12-23 11:17:16 +00:00
for ( int i = 0 ; i < pairNumFrames ; i + + )
{
hitlist [ pair - > frames [ i ] . Texture . GetIndex ( ) ] | = ( uint8_t ) bitmask ;
}
if ( numFrames = = 1 & & pairNumFrames = = 1 )
2018-12-28 08:17:10 +00:00
{
2019-12-23 11:17:16 +00:00
// Switch can still be animated via BOOM binary definition from ANIMATED lump
addAnimations ( switchdef - > frames [ 0 ] . Texture ) ;
addAnimations ( pair - > frames [ 0 ] . Texture ) ;
2018-12-28 08:17:10 +00:00
}
}
2020-04-11 16:54:54 +00:00
auto adoor = TexAnim . FindAnimatedDoor ( texid ) ;
2018-12-28 08:17:10 +00:00
if ( adoor )
{
for ( int i = 0 ; i < adoor - > NumTextureFrames ; i + + )
{
hitlist [ adoor - > TextureFrames [ i ] . GetIndex ( ) ] | = ( uint8_t ) bitmask ;
}
}
}
static void PrecacheLevel ( FLevelLocals * Level )
{
if ( demoplayback )
return ;
int i ;
TMap < PClassActor * , bool > actorhitlist ;
int cnt = TexMan . NumTextures ( ) ;
TArray < uint8_t > hitlist ( cnt , true ) ;
memset ( hitlist . Data ( ) , 0 , cnt ) ;
AActor * actor ;
2019-01-27 23:55:21 +00:00
auto iterator = Level - > GetThinkerIterator < AActor > ( ) ;
2018-12-28 08:17:10 +00:00
while ( ( actor = iterator . Next ( ) ) )
{
actorhitlist [ actor - > GetClass ( ) ] = true ;
}
for ( auto n : gameinfo . PrecachedClasses )
{
PClassActor * cls = PClass : : FindActor ( n ) ;
if ( cls ! = nullptr ) actorhitlist [ cls ] = true ;
}
for ( unsigned i = 0 ; i < Level - > info - > PrecacheClasses . Size ( ) ; i + + )
{
// Level->info can only store names, no class pointers.
PClassActor * cls = PClass : : FindActor ( Level - > info - > PrecacheClasses [ i ] ) ;
if ( cls ! = nullptr ) actorhitlist [ cls ] = true ;
}
for ( i = Level - > sectors . Size ( ) - 1 ; i > = 0 ; i - - )
{
AddToList ( hitlist . Data ( ) , Level - > sectors [ i ] . GetTexture ( sector_t : : floor ) , FTextureManager : : HIT_Flat ) ;
AddToList ( hitlist . Data ( ) , Level - > sectors [ i ] . GetTexture ( sector_t : : ceiling ) , FTextureManager : : HIT_Flat ) ;
}
for ( i = Level - > sides . Size ( ) - 1 ; i > = 0 ; i - - )
{
2020-04-18 11:13:57 +00:00
auto & sd = Level - > sides [ i ] ;
int hitflag = FTextureManager : : HIT_Wall ;
// Only precache skyboxes when this is actually used as a sky transfer.
if ( sd . linedef - > sidedef [ 0 ] = = & sd & & sd . linedef - > special = = Static_Init & & sd . linedef - > args [ 1 ] = = Init_TransferSky ) hitflag | = FTextureManager : : HIT_Sky ;
AddToList ( hitlist . Data ( ) , sd . GetTexture ( side_t : : top ) , hitflag ) ;
AddToList ( hitlist . Data ( ) , sd . GetTexture ( side_t : : mid ) , FTextureManager : : HIT_Wall ) ;
AddToList ( hitlist . Data ( ) , sd . GetTexture ( side_t : : bottom ) , hitflag ) ;
2018-12-28 08:17:10 +00:00
}
2019-01-29 03:44:44 +00:00
if ( Level - > skytexture1 . isValid ( ) )
2018-12-28 08:17:10 +00:00
{
2019-01-29 03:44:44 +00:00
AddToList ( hitlist . Data ( ) , Level - > skytexture1 , FTextureManager : : HIT_Sky ) ;
2018-12-28 08:17:10 +00:00
}
2019-01-29 03:44:44 +00:00
if ( Level - > skytexture2 . isValid ( ) )
2018-12-28 08:17:10 +00:00
{
2019-01-29 03:44:44 +00:00
AddToList ( hitlist . Data ( ) , Level - > skytexture2 , FTextureManager : : HIT_Sky ) ;
2018-12-28 08:17:10 +00:00
}
2021-07-12 10:27:49 +00:00
static const BITFIELD checkForTextureFlags = FTextureManager : : TEXMAN_Overridable | FTextureManager : : TEXMAN_TryAny | FTextureManager : : TEXMAN_ReturnFirst | FTextureManager : : TEXMAN_DontCreate ;
2018-12-28 08:17:10 +00:00
for ( auto n : gameinfo . PrecachedTextures )
{
2021-07-12 10:27:49 +00:00
FTextureID tex = TexMan . CheckForTexture ( n , ETextureType : : Wall , checkForTextureFlags ) ;
2018-12-28 08:17:10 +00:00
if ( tex . Exists ( ) ) AddToList ( hitlist . Data ( ) , tex , FTextureManager : : HIT_Wall ) ;
}
for ( unsigned i = 0 ; i < Level - > info - > PrecacheTextures . Size ( ) ; i + + )
{
2021-07-12 10:27:49 +00:00
FTextureID tex = TexMan . CheckForTexture ( Level - > info - > PrecacheTextures [ i ] , ETextureType : : Wall , checkForTextureFlags ) ;
2018-12-28 08:17:10 +00:00
if ( tex . Exists ( ) ) AddToList ( hitlist . Data ( ) , tex , FTextureManager : : HIT_Wall ) ;
}
// This is just a temporary solution, until the hardware renderer's texture manager is in a better state.
if ( ! V_IsHardwareRenderer ( ) )
SWRenderer - > Precache ( hitlist . Data ( ) , actorhitlist ) ;
else
hw_PrecacheTexture ( hitlist . Data ( ) , actorhitlist ) ;
}
2018-12-31 11:42:03 +00:00
//============================================================================
//
// clears all portal data for a new level start
//
//============================================================================
void FLevelLocals : : ClearPortals ( )
{
Displacements . Create ( 1 ) ;
linePortals . Clear ( ) ;
linkedPortals . Clear ( ) ;
sectorPortals . Resize ( 2 ) ;
PortalBlockmap . Clear ( ) ;
// The first entry must always be the default skybox. This is what every sector gets by default.
2023-03-26 08:35:33 +00:00
sectorPortals [ 0 ] . Clear ( ) ;
2018-12-31 11:42:03 +00:00
sectorPortals [ 0 ] . mType = PORTS_SKYVIEWPOINT ;
sectorPortals [ 0 ] . mFlags = PORTSF_SKYFLATONLY ;
// The second entry will be the default sky. This is for forcing a regular sky through the skybox picker
2023-03-26 08:35:33 +00:00
sectorPortals [ 1 ] . Clear ( ) ;
2018-12-31 11:42:03 +00:00
sectorPortals [ 1 ] . mType = PORTS_SKYVIEWPOINT ;
sectorPortals [ 1 ] . mFlags = PORTSF_SKYFLATONLY ;
// also clear the render data
for ( auto & sub : subsectors )
{
for ( int j = 0 ; j < 2 ; j + + )
{
if ( sub . portalcoverage [ j ] . subsectors ! = nullptr )
{
delete [ ] sub . portalcoverage [ j ] . subsectors ;
sub . portalcoverage [ j ] . subsectors = nullptr ;
}
}
}
for ( unsigned i = 0 ; i < portalGroups . Size ( ) ; i + + )
{
delete portalGroups [ i ] ;
}
portalGroups . Clear ( ) ;
linePortalSpans . Clear ( ) ;
}
//==========================================================================
//
//
//
//==========================================================================
2022-10-20 14:48:18 +00:00
void FLevelLocals : : ClearLevelData ( bool fullgc )
2018-12-31 11:42:03 +00:00
{
2020-09-02 12:33:30 +00:00
{
auto it = GetThinkerIterator < AActor > ( NAME_None , STAT_TRAVELLING ) ;
for ( AActor * actor = it . Next ( ) ; actor ! = nullptr ; actor = it . Next ( ) )
{
actor - > BlockingLine = nullptr ;
actor - > BlockingFloor = actor - > BlockingCeiling = actor - > Blocking3DFloor = nullptr ;
}
}
2019-02-04 15:27:57 +00:00
interpolator . ClearInterpolations ( ) ; // [RH] Nothing to interpolate on a fresh level.
2022-10-20 14:48:18 +00:00
Thinkers . DestroyAllThinkers ( fullgc ) ;
2019-01-26 14:21:20 +00:00
ClearAllSubsectorLinks ( ) ; // can't be done as part of the polyobj deletion process.
2018-12-31 11:42:03 +00:00
total_monsters = total_items = total_secrets =
- allow the language table to supersede the title patches, if appropriate
For the Doom IWADs the provided font looks almost identical to the characters used on the title patches. So, for any level name that got replaced in some language, it will now check if the retrieved name comes from the default table, and if not, ignore the title patch and print the name with the specified font.
This also required removing the 'en' label from the default table, because with this present, the text would always be picked from 'en' instead of 'default'. Since 'en' and 'default' had the same contents, in any English locale the 'default' table was never hit, so this won't make any difference for the texts being chosen.
Last but not least, wminfo has been made a local variable in G_DoCompleted. There were two places where this was accessed from outside the summary screen or its setup code, and both were incorrect.
2019-02-14 23:29:24 +00:00
killed_monsters = found_items = found_secrets = 0 ;
2019-01-29 23:37:29 +00:00
2019-08-28 18:33:07 +00:00
max_velocity = avg_velocity = 0 ;
2019-01-29 23:37:29 +00:00
for ( int i = 0 ; i < 4 ; i + + )
{
UDMFKeys [ i ] . Clear ( ) ;
}
2019-01-09 01:03:26 +00:00
2019-01-28 22:53:40 +00:00
SN_StopAllSequences ( this ) ;
2019-01-09 01:03:26 +00:00
FStrifeDialogueNode * node ;
while ( StrifeDialogues . Pop ( node ) )
{
delete node ;
}
DialogueRoots . Clear ( ) ;
ClassRoots . Clear ( ) ;
2018-12-31 11:42:03 +00:00
for ( auto & sub : subsectors )
{
if ( sub . BSP ! = nullptr ) delete sub . BSP ;
}
ClearPortals ( ) ;
2019-01-24 00:40:09 +00:00
tagManager . Clear ( ) ;
2019-01-24 18:28:40 +00:00
ClearTIDHashes ( ) ;
2019-02-04 15:27:57 +00:00
if ( SpotState ) SpotState - > Destroy ( ) ;
2019-01-05 09:04:27 +00:00
SpotState = nullptr ;
2019-01-06 09:24:27 +00:00
ACSThinker = nullptr ;
FraggleScriptThinker = nullptr ;
2019-01-05 09:53:06 +00:00
CorpseQueue . Clear ( ) ;
2018-12-31 11:42:03 +00:00
canvasTextureInfo . EmptyList ( ) ;
sections . Clear ( ) ;
segs . Clear ( ) ;
2020-10-04 22:39:21 +00:00
extsectors . Clear ( ) ;
2018-12-31 11:42:03 +00:00
sectors . Clear ( ) ;
linebuffer . Clear ( ) ;
subsectorbuffer . Clear ( ) ;
lines . Clear ( ) ;
sides . Clear ( ) ;
segbuffer . Clear ( ) ;
loadsectors . Clear ( ) ;
loadlines . Clear ( ) ;
loadsides . Clear ( ) ;
vertexes . Clear ( ) ;
nodes . Clear ( ) ;
gamenodes . Reset ( ) ;
subsectors . Clear ( ) ;
gamesubsectors . Reset ( ) ;
rejectmatrix . Clear ( ) ;
Zones . Clear ( ) ;
blockmap . Clear ( ) ;
Polyobjects . Clear ( ) ;
for ( auto & pb : PolyBlockMap )
{
polyblock_t * link = pb ;
while ( link ! = nullptr )
{
polyblock_t * next = link - > next ;
delete link ;
link = next ;
}
}
PolyBlockMap . Reset ( ) ;
deathmatchstarts . Clear ( ) ;
AllPlayerStarts . Clear ( ) ;
memset ( playerstarts , 0 , sizeof ( playerstarts ) ) ;
Scrolls . Clear ( ) ;
2019-01-27 00:49:20 +00:00
if ( automap ) automap - > Destroy ( ) ;
2019-01-31 16:43:12 +00:00
Behaviors . UnloadModules ( ) ;
2019-02-02 16:29:13 +00:00
localEventManager - > Shutdown ( ) ;
2020-04-26 16:54:43 +00:00
if ( aabbTree ) delete aabbTree ;
2022-12-03 19:35:27 +00:00
if ( levelMesh ) delete levelMesh ;
2020-04-26 16:54:43 +00:00
aabbTree = nullptr ;
2022-12-03 19:35:27 +00:00
levelMesh = nullptr ;
2020-06-14 19:33:26 +00:00
if ( screen )
screen - > SetAABBTree ( nullptr ) ;
2018-12-31 11:42:03 +00:00
}
2018-12-28 08:17:10 +00:00
//==========================================================================
//
//
//
//==========================================================================
2022-10-20 14:48:18 +00:00
void P_FreeLevelData ( bool fullgc )
2018-12-28 08:17:10 +00:00
{
R_FreePastViewers ( ) ;
2019-02-01 23:24:43 +00:00
for ( auto Level : AllLevels ( ) )
{
2022-10-20 14:48:18 +00:00
Level - > ClearLevelData ( fullgc ) ;
2019-02-01 23:24:43 +00:00
}
// primaryLevel->FreeSecondaryLevels();
2018-12-28 08:17:10 +00:00
}
//===========================================================================
//
// P_SetupLevel
//
// [RH] position indicates the start spot to spawn at
//
//===========================================================================
2019-01-26 14:21:20 +00:00
void P_SetupLevel ( FLevelLocals * Level , int position , bool newGame )
2018-12-28 08:17:10 +00:00
{
int i ;
2019-01-26 14:21:20 +00:00
Level - > ShaderStartTime = I_msTimeFS ( ) ; // indicate to the shader system that the level just started
2018-12-28 08:17:10 +00:00
// This is motivated as follows:
2019-01-26 14:21:20 +00:00
Level - > maptype = MAPTYPE_UNKNOWN ;
2018-12-28 08:17:10 +00:00
if ( ! savegamerestore )
{
2019-01-26 14:21:20 +00:00
Level - > SetMusicVolume ( Level - > MusicVolume ) ;
2018-12-28 08:17:10 +00:00
for ( i = 0 ; i < MAXPLAYERS ; + + i )
{
2019-01-30 00:15:32 +00:00
Level - > Players [ i ] - > killcount = Level - > Players [ i ] - > secretcount
= Level - > Players [ i ] - > itemcount = 0 ;
2018-12-28 08:17:10 +00:00
}
}
for ( i = 0 ; i < MAXPLAYERS ; + + i )
{
2019-01-30 00:15:32 +00:00
Level - > Players [ i ] - > mo = nullptr ;
2018-12-28 08:17:10 +00:00
}
2020-04-11 16:19:05 +00:00
GPalette . ClearTranslationSlot ( TRANSLATION_LevelScripted ) ;
2020-04-11 10:41:09 +00:00
2018-12-28 08:17:10 +00:00
// Initial height of PointOfView will be set by player think.
2019-02-01 16:02:10 +00:00
auto p = Level - > GetConsolePlayer ( ) ;
if ( p ) p - > viewz = NO_VALUE ;
2018-12-28 08:17:10 +00:00
// Make sure all sounds are stopped before Z_FreeTags.
S_Start ( ) ;
2020-04-11 16:23:15 +00:00
S_ResetMusic ( ) ;
// Don't start the music if loading a savegame, because the music is stored there.
// Don't start the music if revisiting a level in a hub for the same reason.
if ( ! primaryLevel - > IsReentering ( ) )
{
primaryLevel - > SetMusic ( ) ;
}
2018-12-28 08:17:10 +00:00
// [RH] clear out the mid-screen message
C_MidPrint ( nullptr , nullptr ) ;
// Free all level data from the previous map
P_FreeLevelData ( ) ;
2019-01-26 14:21:20 +00:00
MapData * map = P_OpenMapData ( Level - > MapName , true ) ;
2018-12-28 08:17:10 +00:00
if ( map = = nullptr )
{
2019-01-26 14:21:20 +00:00
I_Error ( " Unable to open map '%s' \n " , Level - > MapName . GetChars ( ) ) ;
2018-12-28 08:17:10 +00:00
}
// [ZZ] init per-map static handlers. we need to call this before everything is set up because otherwise scripts don't receive PlayerEntered event
// (which happens at god-knows-what stage in this function, but definitely not the last part, because otherwise it'd work to put E_InitStaticHandlers before the player spawning)
2019-02-06 14:33:19 +00:00
Level - > localEventManager - > InitStaticHandlers ( Level , true ) ;
2018-12-28 08:17:10 +00:00
// generate a checksum for the level, to be included and checked with savegames.
2019-01-26 14:21:20 +00:00
map - > GetChecksum ( Level - > md5 ) ;
2018-12-28 08:17:10 +00:00
// find map num
2019-01-26 14:21:20 +00:00
Level - > lumpnum = map - > lumpnum ;
2018-12-28 08:17:10 +00:00
if ( newGame )
{
2019-02-02 15:43:11 +00:00
Level - > localEventManager - > NewGame ( ) ;
2018-12-28 08:17:10 +00:00
}
2019-01-26 14:21:20 +00:00
MapLoader loader ( Level ) ;
loader . LoadLevel ( map , Level - > MapName . GetChars ( ) , position ) ;
2018-12-28 08:17:10 +00:00
delete map ;
// if deathmatch, randomly spawn the active players
if ( deathmatch )
{
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
2019-01-30 00:15:32 +00:00
if ( Level - > PlayerInGame ( i ) )
2018-12-28 08:17:10 +00:00
{
2019-01-30 00:15:32 +00:00
Level - > Players [ i ] - > mo = nullptr ;
2019-01-28 01:44:05 +00:00
Level - > DeathMatchSpawnPlayer ( i ) ;
2018-12-28 08:17:10 +00:00
}
}
}
// the same, but for random single/coop player starts
2019-01-26 14:21:20 +00:00
else if ( Level - > flags2 & LEVEL2_RANDOMPLAYERSTARTS )
2018-12-28 08:17:10 +00:00
{
for ( i = 0 ; i < MAXPLAYERS ; + + i )
{
2019-01-30 00:15:32 +00:00
if ( Level - > PlayerInGame ( i ) )
2018-12-28 08:17:10 +00:00
{
2019-01-30 00:15:32 +00:00
Level - > Players [ i ] - > mo = nullptr ;
2019-01-28 01:44:05 +00:00
FPlayerStart * mthing = Level - > PickPlayerStart ( i ) ;
2019-01-28 02:02:25 +00:00
Level - > SpawnPlayer ( mthing , i , ( Level - > flags2 & LEVEL2_PRERAISEWEAPON ) ? SPF_WEAPONFULLYUP : 0 ) ;
2018-12-28 08:17:10 +00:00
}
}
}
// [SP] move unfriendly players around
// horribly hacky - yes, this needs rewritten.
2019-01-26 14:21:20 +00:00
if ( Level - > deathmatchstarts . Size ( ) > 0 )
2018-12-28 08:17:10 +00:00
{
for ( i = 0 ; i < MAXPLAYERS ; + + i )
{
2019-01-30 00:15:32 +00:00
auto p = Level - > Players [ i ] ;
if ( Level - > PlayerInGame ( i ) & & p - > mo ! = nullptr )
2018-12-28 08:17:10 +00:00
{
2019-01-30 00:15:32 +00:00
if ( ! ( p - > mo - > flags & MF_FRIENDLY ) )
2018-12-28 08:17:10 +00:00
{
2019-01-30 00:15:32 +00:00
AActor * oldSpawn = p - > mo ;
2019-01-28 01:44:05 +00:00
Level - > DeathMatchSpawnPlayer ( i ) ;
2018-12-28 08:17:10 +00:00
oldSpawn - > Destroy ( ) ;
}
}
}
}
// Don't count monsters in end-of-level sectors if option is on
if ( dmflags2 & DF2_NOCOUNTENDMONST )
{
2019-01-27 23:55:21 +00:00
auto it = Level - > GetThinkerIterator < AActor > ( ) ;
2018-12-28 08:17:10 +00:00
AActor * mo ;
while ( ( mo = it . Next ( ) ) )
{
if ( mo - > flags & MF_COUNTKILL )
{
if ( mo - > Sector - > damageamount > 0 & & ( mo - > Sector - > Flags & ( SECF_ENDGODMODE | SECF_ENDLEVEL ) ) = = ( SECF_ENDGODMODE | SECF_ENDLEVEL ) )
{
mo - > ClearCounters ( ) ;
}
}
}
}
2019-01-27 23:55:21 +00:00
T_PreprocessScripts ( Level ) ; // preprocess FraggleScript scripts
2018-12-28 08:17:10 +00:00
// build subsector connect matrix
// UNUSED P_ConnectSubsectors ();
R_OldBlend = 0xffffffff ;
// [RH] Remove all particles
2019-01-29 00:09:02 +00:00
P_ClearParticles ( Level ) ;
2018-12-28 08:17:10 +00:00
// preload graphics and sounds
if ( precache )
{
2019-01-27 23:55:21 +00:00
PrecacheLevel ( Level ) ;
S_PrecacheLevel ( Level ) ;
2018-12-28 08:17:10 +00:00
}
if ( deathmatch )
{
AnnounceGameStart ( ) ;
}
// This check was previously done at run time each time the heightsec was checked.
// However, since 3D floors are static data, we can easily precalculate this and store it in the sector's flags for quick access.
2019-01-26 14:21:20 +00:00
for ( auto & s : Level - > sectors )
2018-12-28 08:17:10 +00:00
{
if ( s . heightsec ! = nullptr )
{
// If any of these 3D floors render their planes, ignore heightsec.
for ( auto & ff : s . e - > XFloor . ffloors )
{
if ( ( ff - > flags & ( FF_EXISTS | FF_RENDERPLANES ) ) = = ( FF_EXISTS | FF_RENDERPLANES ) )
{
s . MoreFlags | = SECMF_IGNOREHEIGHTSEC ; // mark the heightsec inactive.
}
}
}
}
// Create a backup of the map data so the savegame code can toss out all fields that haven't changed in order to reduce processing time and file size.
// Note that we want binary identity here, so assignment is not sufficient because it won't initialize any padding bytes.
// Note that none of these structures may contain non POD fields anyway.
2019-01-26 14:21:20 +00:00
Level - > loadsectors . Resize ( Level - > sectors . Size ( ) ) ;
memcpy ( & Level - > loadsectors [ 0 ] , & Level - > sectors [ 0 ] , Level - > sectors . Size ( ) * sizeof ( Level - > sectors [ 0 ] ) ) ;
Level - > loadlines . Resize ( Level - > lines . Size ( ) ) ;
memcpy ( & Level - > loadlines [ 0 ] , & Level - > lines [ 0 ] , Level - > lines . Size ( ) * sizeof ( Level - > lines [ 0 ] ) ) ;
Level - > loadsides . Resize ( Level - > sides . Size ( ) ) ;
memcpy ( & Level - > loadsides [ 0 ] , & Level - > sides [ 0 ] , Level - > sides . Size ( ) * sizeof ( Level - > sides [ 0 ] ) ) ;
Level - > automap = AM_Create ( Level ) ;
Level - > automap - > LevelInit ( ) ;
2019-01-29 18:28:22 +00:00
Level - > SetCompatLineOnSide ( true ) ;
2019-01-27 18:16:14 +00:00
// [RH] Start lightning, if MAPINFO tells us to
if ( Level - > flags & LEVEL_STARTLIGHTNING )
{
Level - > StartLightning ( ) ;
}
2019-10-16 22:11:05 +00:00
auto it = Level - > GetThinkerIterator < AActor > ( ) ;
AActor * ac ;
2021-09-21 18:31:10 +00:00
Level - > flags3 | = LEVEL3_LIGHTCREATED ;
2019-10-16 22:11:05 +00:00
// Initial setup of the dynamic lights.
while ( ( ac = it . Next ( ) ) )
{
ac - > SetDynamicLights ( ) ;
}
2018-12-28 08:17:10 +00:00
}
//
// P_Init
//
void P_Init ( )
{
P_InitEffects ( ) ; // [RH]
P_InitTerrainTypes ( ) ;
P_InitKeyMessages ( ) ;
R_InitSprites ( ) ;
}
2019-10-07 18:28:55 +00:00
void P_Shutdown ( )
2019-01-30 01:15:48 +00:00
{
2019-02-01 23:24:43 +00:00
for ( auto Level : AllLevels ( ) )
{
Level - > Thinkers . DestroyThinkersInList ( STAT_STATIC ) ;
}
2018-12-28 08:17:10 +00:00
P_FreeLevelData ( ) ;
// [ZZ] delete global event handlers
2019-02-02 15:43:11 +00:00
staticEventManager . Shutdown ( ) ; // clear out the handlers before starting the engine shutdown
2018-12-28 08:17:10 +00:00
ST_Clear ( ) ;
for ( auto & p : players )
{
if ( p . psprites ! = nullptr ) p . psprites - > Destroy ( ) ;
}
}
//==========================================================================
//
// dumpgeometry
//
//==========================================================================
CCMD ( dumpgeometry )
{
2019-01-28 01:41:29 +00:00
for ( auto Level : AllLevels ( ) )
2018-12-28 08:17:10 +00:00
{
2019-01-28 01:41:29 +00:00
Printf ( " Geometry for %s \n " , Level - > MapName . GetChars ( ) ) ;
for ( auto & sector : Level - > sectors )
2018-12-28 08:17:10 +00:00
{
2019-01-28 01:41:29 +00:00
Printf ( PRINT_LOG , " Sector %d \n " , sector . sectornum ) ;
for ( int j = 0 ; j < sector . subsectorcount ; j + + )
2018-12-28 08:17:10 +00:00
{
2019-01-28 01:41:29 +00:00
subsector_t * sub = sector . subsectors [ j ] ;
Printf ( PRINT_LOG , " Subsector %d - real sector = %d - %s \n " , int ( sub - > Index ( ) ) , sub - > sector - > sectornum , sub - > hacked & 1 ? " hacked " : " " ) ;
for ( uint32_t k = 0 ; k < sub - > numlines ; k + + )
2018-12-28 08:17:10 +00:00
{
2019-01-28 01:41:29 +00:00
seg_t * seg = sub - > firstline + k ;
if ( seg - > linedef )
{
Printf ( PRINT_LOG , " (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, linedef %d, side %d " ,
2019-01-28 22:53:40 +00:00
seg - > v1 - > fX ( ) , seg - > v1 - > fY ( ) , seg - > v2 - > fX ( ) , seg - > v2 - > fY ( ) ,
seg - > Index ( ) , seg - > linedef - > Index ( ) , seg - > sidedef ! = seg - > linedef - > sidedef [ 0 ] ) ;
2019-01-28 01:41:29 +00:00
}
else
{
Printf ( PRINT_LOG , " (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, miniseg " ,
2019-01-28 22:53:40 +00:00
seg - > v1 - > fX ( ) , seg - > v1 - > fY ( ) , seg - > v2 - > fX ( ) , seg - > v2 - > fY ( ) , seg - > Index ( ) ) ;
2019-01-28 01:41:29 +00:00
}
if ( seg - > PartnerSeg )
{
subsector_t * sub2 = seg - > PartnerSeg - > Subsector ;
Printf ( PRINT_LOG , " , back sector = %d, real back sector = %d " , sub2 - > render_sector - > sectornum , seg - > PartnerSeg - > frontsector - > sectornum ) ;
}
else if ( seg - > backsector )
{
Printf ( PRINT_LOG , " , back sector = %d (no partnerseg) " , seg - > backsector - > sectornum ) ;
}
Printf ( PRINT_LOG , " \n " ) ;
2018-12-28 08:17:10 +00:00
}
}
}
}
}
2018-12-28 14:51:32 +00:00
//==========================================================================
//
//
//
//==========================================================================
CCMD ( listmapsections )
{
2019-01-28 01:41:29 +00:00
for ( auto Level : AllLevels ( ) )
2018-12-28 14:51:32 +00:00
{
2019-01-28 01:41:29 +00:00
Printf ( " Map sections for %s: \n " , Level - > MapName . GetChars ( ) ) ;
for ( int i = 0 ; i < 100 ; i + + )
2018-12-28 14:51:32 +00:00
{
2019-01-28 01:41:29 +00:00
for ( auto & sub : Level - > subsectors )
2018-12-28 14:51:32 +00:00
{
2019-01-28 01:41:29 +00:00
if ( sub . mapsection = = i )
{
Printf ( " Mapsection %d, sector %d, line %d \n " , i , sub . render_sector - > Index ( ) , sub . firstline - > linedef - > Index ( ) ) ;
break ;
}
2018-12-28 14:51:32 +00:00
}
}
}
}
2019-01-25 00:26:16 +00:00
//==========================================================================
//
//
//
//==========================================================================
CUSTOM_CVAR ( Bool , forcewater , false , CVAR_ARCHIVE | CVAR_SERVERINFO )
{
2019-01-28 01:41:29 +00:00
if ( gamestate = = GS_LEVEL ) for ( auto Level : AllLevels ( ) )
2019-01-25 00:26:16 +00:00
{
for ( auto & sec : Level - > sectors )
{
sector_t * hsec = sec . GetHeightSec ( ) ;
if ( hsec & & ! ( hsec - > MoreFlags & SECMF_UNDERWATER ) )
{
if ( self )
{
hsec - > MoreFlags | = SECMF_FORCEDUNDERWATER ;
}
else
{
hsec - > MoreFlags & = ~ SECMF_FORCEDUNDERWATER ;
}
}
}
}
}