1 //**************************************************************************
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
13 //** This program is free software: you can redistribute it and/or modify
14 //** it under the terms of the GNU General Public License as published by
15 //** the Free Software Foundation, version 3 of the License ONLY.
17 //** This program is distributed in the hope that it will be useful,
18 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 //** GNU General Public License for more details.
22 //** You should have received a copy of the GNU General Public License
23 //** along with this program. If not, see <http://www.gnu.org/licenses/>.
25 //**************************************************************************
26 #include "../gamedefs.h"
27 #include "../server/server.h"
28 #include "../server/sv_local.h"
29 #include "../server/sv_save.h"
31 # include "../client/client.h" /* for `cl` */
33 #include "../language.h"
34 #include "../infostr.h"
36 #include "p_levelinfo.h"
40 VCvarB
compat_shorttex("compat_shorttex", false, "Compatibility: shorttex", 0);
41 VCvarB
compat_stairs("compat_stairs", false, "Compatibility: stairs", 0);
42 VCvarI
compat_limitpain("compat_limitpain", "0", "Compatibility: limit number of skulls from Pain Elementals? (0:map default; 1:always; 2: never)", CVAR_Archive
);
43 VCvarI
compat_nopassover("compat_nopassover", "0", "Compatibility: infinitely tall monsters? (0:map default; 1:always; 2: never)", CVAR_Archive
);
44 VCvarI
compat_notossdrops("compat_notossdrops", "0", "Compatibility: toss dropped items? (0:map default; 1:always; 2: never)", CVAR_Archive
);
45 VCvarB
compat_useblocking("compat_useblocking", false, "Compatibility: useblocking", 0);
46 VCvarB
compat_nodoorlight("compat_nodoorlight", false, "Compatibility: nodoorlight", 0);
47 VCvarB
compat_ravenscroll("compat_ravenscroll", false, "Compatibility: ravenscroll", 0);
48 VCvarB
compat_soundtarget("compat_soundtarget", false, "Compatibility: soundtarget", 0);
49 VCvarB
compat_dehhealth("compat_dehhealth", false, "Compatibility: dehhealth", 0);
50 VCvarB
compat_trace("compat_trace", false, "Compatibility: trace", 0);
51 VCvarB
compat_dropoff("compat_dropoff", false, "Compatibility: dropoff", 0);
52 VCvarB
compat_boomscroll("compat_boomscroll", false, "Compatibility: boomscroll", 0);
53 VCvarB
compat_invisibility("compat_invisibility", false, "Compatibility: invisibility", 0);
56 IMPLEMENT_CLASS(V
, LevelInfo
)
58 static VCvarF
sv_gravity("sv_gravity", "800", "Gravity value.", 0/*|CVAR_ServerInfo*/);
59 static VCvarF
sv_aircontrol("sv_aircontrol", "0.00390625", "Air control value.", 0/*|CVAR_ServerInfo*/);
62 //==========================================================================
64 // VLevelInfo::PostCtor
66 //==========================================================================
67 void VLevelInfo::PostCtor () {
70 //GCon->Logf("** falling damage: flags=0x%08x", (unsigned)LevelInfoFlags);
74 //==========================================================================
76 // VLevelInfo::SetMapInfo
78 //==========================================================================
79 void VLevelInfo::SetMapInfo (VLevel
*InLevel
, const VMapInfo
&Info
) {
80 const VClusterDef
*CInfo
= P_GetClusterDef(Info
.Cluster
);
82 LevelName
= Info
.Name
;
83 LevelNum
= Info
.LevelNum
;
84 Cluster
= Info
.Cluster
;
86 NextMap
= Info
.NextMap
;
87 SecretMap
= Info
.SecretMap
;
89 ParTime
= Info
.ParTime
;
90 SuckTime
= Info
.SuckTime
;
92 Sky1Texture
= Info
.Sky1Texture
;
93 Sky2Texture
= Info
.Sky2Texture
;
94 Sky1ScrollDelta
= Info
.Sky1ScrollDelta
;
95 Sky2ScrollDelta
= Info
.Sky2ScrollDelta
;
98 FadeTable
= Info
.FadeTable
;
100 OutsideFog
= Info
.OutsideFog
;
102 SongLump
= Info
.SongLump
;
104 Gravity
= (Info
.Gravity
? Info
.Gravity
: sv_gravity
)*DEFAULT_GRAVITY
/800.0f
;
105 AirControl
= (Info
.AirControl
? Info
.AirControl
: sv_aircontrol
);
107 Infighting
= Info
.Infighting
;
108 SpecialActions
= Info
.SpecialActions
;
110 // copy flags from mapinfo
111 //GCon->Logf("*** level info flags: 0x%08x", (unsigned)Info.Flags);
112 LevelInfoFlags
= Info
.Flags
;
113 LevelInfoFlags2
= Info
.Flags2
;
115 // doom format maps use strict monster activation by default
116 //k8: it seems that UDMF too (let's hope it won't break Heretic/Hexen UDMF maps)
117 if (!(LevelInfoFlags2
&LIF2_HaveMonsterActivation
)) {
118 if (!(XLevel
->LevelFlags
&VLevel::LF_Extended
) || (XLevel
->LevelFlags
&VLevel::LF_TextMap
)) {
119 LevelInfoFlags2
&= ~LIF2_LaxMonsterActivation
;
123 if (CInfo
->Flags
&CLUSTERF_Hub
) LevelInfoFlags2
|= LIF2_ClusterHub
;
125 // no auto sequences flag sets all sectors to use sequence 0 by default
126 if (Info
.Flags
&VLevelInfo::LIF_NoAutoSndSeq
) {
127 for (auto &&sec
: XLevel
->allSectors()) sec
.seqType
= 0;
130 GGameInfo
->eventTranslateSpecialActions(InLevel
, this);
132 eventAfterSetMapInfo(InLevel
);
136 //==========================================================================
138 // VLevelInfo::SectorStartSound
140 //==========================================================================
141 void VLevelInfo::SectorStartSound (const sector_t
*Sector
, int SoundId
,
142 int Channel
, float Volume
, float Attenuation
,
146 if (Sector
->SectorFlags
§or_t::SF_Silent
) return;
147 const int oid
= (int)(ptrdiff_t)(Sector
-XLevel
->Sectors
)+(org
? (SNDORG_SectorOrg
<<24) : (SNDORG_Sector
<<24));
154 sorg
= XLevel
->CalcSectorSoundOrigin(Sector
, cl
->ViewOrg
);
158 sorg
= Sector
->soundorg
;
159 sorg
.z
= (Sector
->floor
.minz
+Sector
->floor
.maxz
)*0.5f
+8.0f
;
162 TVec sorg = (org ? *org : Sector->soundorg);
164 if (!Sector->isInnerPObj()) {
166 sorg.z = (Sector->floor.minz+Sector->floor.maxz)*0.5f+8.0f;
169 sorg = Sector->ownpobj->startSpot;
173 if (Attenuation
<= 0.0f
) Attenuation
= 1.0f
; //WARNING! zero attenuation means "local sound"
174 // disable random pitch
175 StartSound(sorg
, oid
, SoundId
, Channel
, Volume
, Attenuation
, false, 0.0f
);
177 // disable random pitch
178 StartSound((org
? *org
: TVec(0.0f
, 0.0f
, 0.0f
)), 0, SoundId
, Channel
, Volume
, Attenuation
, false, 0.0f
);
183 //==========================================================================
185 // VLevelInfo::SectorStopSound
187 //==========================================================================
188 void VLevelInfo::SectorStopSound (const sector_t
*sector
, int channel
) {
189 if (sector
) StopSound((int)(ptrdiff_t)(sector
-&XLevel
->Sectors
[0])+(SNDORG_Sector
<<24), channel
);
193 //==========================================================================
195 // VLevelInfo::SectorStartSequence
197 //==========================================================================
198 void VLevelInfo::SectorStartSequence (const sector_t
*Sector
, VName Name
, int ModeNum
) {
200 if (Sector
->SectorFlags
§or_t::SF_Silent
) return;
204 sorg
= XLevel
->CalcSectorSoundOrigin(Sector
, cl
->ViewOrg
);
208 sorg
= Sector
->soundorg
;
209 sorg
.z
= (Sector
->floor
.minz
+Sector
->floor
.maxz
)*0.5f
+8.0f
;
211 StartSoundSequence(sorg
, (int)(ptrdiff_t)(Sector
-XLevel
->Sectors
)+(SNDORG_Sector
<<24), Name
, ModeNum
);
213 StartSoundSequence(TVec(0, 0, 0), 0, Name
, ModeNum
);
218 //==========================================================================
220 // VLevelInfo::SectorStopSequence
222 //==========================================================================
223 void VLevelInfo::SectorStopSequence (const sector_t
*sector
) {
224 if (sector
) StopSoundSequence((int)(ptrdiff_t)(sector
-XLevel
->Sectors
)+(SNDORG_Sector
<<24));
228 //==========================================================================
230 // VLevelInfo::PolyobjStartSequence
232 //==========================================================================
233 void VLevelInfo::PolyobjStartSequence (const polyobj_t
*Poly
, VName Name
, int ModeNum
) {
234 //if (!Poly || !Poly->GetSubsector() || !Poly->GetSubsector()->sector) return;
236 bool seenNonSilent
= false;
237 for (auto &&it
: Poly
->SubFirst()) {
238 if (!(it
.sub()->sector
->SectorFlags
§or_t::SF_Silent
)) {
239 seenNonSilent
= true;
243 if (!seenNonSilent
) return;
244 StartSoundSequence(Poly
->startSpot
, Poly
->index
+(SNDORG_PolyObj
<<24), Name
, ModeNum
);
248 //==========================================================================
250 // VLevelInfo::PolyobjStopSequence
252 //==========================================================================
253 void VLevelInfo::PolyobjStopSequence (const polyobj_t
*poly
) {
255 StopSoundSequence(poly
->index
+(SNDORG_PolyObj
<<24));
259 //==========================================================================
261 // VLevelInfo::ExitLevel
263 //==========================================================================
264 void VLevelInfo::ExitLevel (int Position
) {
265 LeavePosition
= Position
;
268 for (int i
= 0; i
< MAXPLAYERS
; ++i
) {
269 if (Game
->Players
[i
]) Game
->Players
[i
]->PlayerFlags
&= ~VBasePlayer::PF_ExitedViaSecret
;
274 //==========================================================================
276 // VLevelInfo::SecretExitLevel
278 //==========================================================================
279 void VLevelInfo::SecretExitLevel (int Position
) {
280 if (SecretMap
== NAME_None
) {
281 // no secret map, use normal exit
286 LeavePosition
= Position
;
289 NextMap
= SecretMap
; // go to secret level
291 for (int i
= 0; i
< MAXPLAYERS
; ++i
) {
292 if (Game
->Players
[i
]) {
293 Game
->Players
[i
]->PlayerFlags
|= VBasePlayer::PF_DidSecret
|VBasePlayer::PF_ExitedViaSecret
;
299 //==========================================================================
301 // VLevelInfo::Completed
303 // starts intermission routine, which is used only during hub exits,
304 // and DeathMatch games.
306 // FIXME: `SaveAngle` is used in `LNSPEC_TeleportNewMap`
308 //==========================================================================
309 void VLevelInfo::Completed (int InMap
, int InPosition
, int SaveAngle
) {
311 int Position
= InPosition
;
312 if (Map
== -1 && Position
== -1) {
313 if (!svs
.deathmatch
) {
314 if (G_CheckWantExitText()) {
315 LeavePosition
= Position
; // just in case
319 // otherwise, jump straight into intermission
320 G_StartClientFinale();
326 NextMap
= P_GetMapLumpNameByLevelNum(Map
);
328 LeavePosition
= Position
;
333 //==========================================================================
335 // VLevelInfo::FindMobjFromTID
337 //==========================================================================
338 VEntity
*VLevelInfo::FindMobjFromTID (int tid
, VEntity
*Prev
) {
340 for (VEntity
*E
= (Prev
? Prev
->TIDHashNext
: TIDHash
[VLevelInfo::TIDHashBucket(tid
)]); E
; E
= E
->TIDHashNext
) {
341 if (!E
->IsGoingToDie() && E
->TID
== tid
) return E
;
348 //==========================================================================
350 // VLevelInfo::ChangeMusic
352 //==========================================================================
353 void VLevelInfo::ChangeMusic (VName SongName
) {
358 //==========================================================================
360 // VLevelInfo::GetLevelName
362 //==========================================================================
363 VStr
VLevelInfo::GetLevelName () const {
364 return (LevelInfoFlags
&LIF_LookupName
? GLanguage
[*LevelName
] : LevelName
);
368 //==========================================================================
370 // VLevelInfo::FindFreeTID
374 //==========================================================================
375 int VLevelInfo::FindFreeTID (int tidstart
, int limit
) const {
376 if (tidstart
<= 0) tidstart
= 0;
377 if (limit
< 0) return 0;
378 if (limit
== 0) limit
= 0x7fffffff;
380 // do several random hits, then linear search
381 for (int rndtry
= 1024; rndtry
; --rndtry
) {
382 do { tidstart
= GenRandomU31()&0x7fff; } while (tidstart
== 0);
383 if (!IsTIDUsed(tidstart
, true)) return tidstart
;
385 // fallback to linear search
388 tidstart
= 1; // 0 is used
391 while (limit
-- > 0) {
392 if (!IsTIDUsed(tidstart
, true)) return tidstart
;
394 if (tidstart
== 0x1fffffff) return 0; // arbitrary limit
395 //if (tidstart == 0x8000) return 0; // arbitrary limit
401 //==========================================================================
403 // VLevelInfo::FindFreeTID
405 //==========================================================================
406 bool VLevelInfo::IsTIDUsed (int tid
, bool allowdead
) const {
407 if (tid
== 0) return true; // this is "self"
408 for (VEntity
*E
= Level
->TIDHash
[VLevelInfo::TIDHashBucket(tid
)]; E
; E
= E
->TIDHashNext
) {
409 if (!allowdead
&& E
->IsGoingToDie()) continue;
410 if (E
->TID
== tid
) return true;
416 //==========================================================================
418 // VLevelInfo::ChangeSky
420 //==========================================================================
421 void VLevelInfo::ChangeSky (VStr skytex1
, VStr skytex2
) {
422 // allow loading new skies as map textures
423 //int sky1tid = GTextureManager.NumForName(GetName8(sp[-2]), TEXTYPE_Wall, true, true);
424 //int sky2tid = GTextureManager.NumForName(GetName8(sp[-1]), TEXTYPE_Wall, true, true);
425 int sky1tid
= GTextureManager
.FindOrLoadFullyNamedTextureAsMapTexture(skytex1
, nullptr, TEXTYPE_Wall
, true);
426 int sky2tid
= GTextureManager
.FindOrLoadFullyNamedTextureAsMapTexture(skytex2
, nullptr, TEXTYPE_Wall
, true);
428 GCon->Logf("NEW SKY: %s (%d) %s (%d)", *GetName8(sp[-2]), sky1tid, *GetName8(sp[-1]), sky2tid);
430 VTexture *tex = GTextureManager(sky1tid);
431 GCon->Logf(" <%s> %s", *tex->Name, VTexture::TexTypeToStr(tex->Type));
434 if (sky1tid
> 0) Sky1Texture
= sky1tid
;
435 if (sky2tid
> 0) Sky2Texture
= sky2tid
;
439 //==========================================================================
441 // VLevelInfo natives
443 //==========================================================================
444 // native final void AddStaticLight (Entity ent, TVec origin, float radius, optional TVec coneDirection, optional float coneAngle, optional int flags);
445 IMPLEMENT_FUNCTION(VLevelInfo
, AddStaticLight
) {
448 VOptParamVec
ConeDir(TVec(0, 0, 0));
449 VOptParamFloat
ConeAngle(0);
450 VOptParamInt
Flags(0);
451 vobjGetParamSelf(Ent
, lpar
.Origin
, lpar
.Radius
, ConeDir
, ConeAngle
, Flags
);
452 if (ConeDir
.specified
) lpar
.coneDirection
= ConeDir
;
453 if (ConeAngle
.specified
) lpar
.coneAngle
= ConeAngle
;
454 lpar
.Color
= 0xffffffffu
;
455 Self
->XLevel
->AddStaticLightRGB(Ent
, lpar
, Flags
);
458 // native final void AddStaticLightRGB (Entity ent, TVec origin, float radius, int color, optional TVec coneDirection, optional float coneAngle, optional int flags);
459 IMPLEMENT_FUNCTION(VLevelInfo
, AddStaticLightRGB
) {
462 VOptParamVec
ConeDir(TVec(0, 0, 0));
463 VOptParamFloat
ConeAngle(0);
464 VOptParamInt
Flags(0);
465 vobjGetParamSelf(Ent
, lpar
.Origin
, lpar
.Radius
, lpar
.Color
, ConeDir
, ConeAngle
, Flags
);
466 if (ConeDir
.specified
) lpar
.coneDirection
= ConeDir
;
467 if (ConeAngle
.specified
) lpar
.coneAngle
= ConeAngle
;
468 Self
->XLevel
->AddStaticLightRGB(Ent
, lpar
, Flags
);
471 // native final void AddStaticLightRGBSector (Entity ent, TVec origin, sector_t *sector, float scale, int color, optional TVec coneDirection, optional float coneAngle, optional int flags);
472 IMPLEMENT_FUNCTION(VLevelInfo
, AddStaticLightRGBSector
) {
475 VOptParamVec
ConeDir(TVec(0, 0, 0));
476 VOptParamFloat
ConeAngle(0);
477 VOptParamInt
Flags(0);
478 vobjGetParamSelf(Ent
, lpar
.Origin
, lpar
.LevelSector
, lpar
.LevelScale
, lpar
.Color
, ConeDir
, ConeAngle
, Flags
);
479 if (ConeDir
.specified
) lpar
.coneDirection
= ConeDir
;
480 if (ConeAngle
.specified
) lpar
.coneAngle
= ConeAngle
;
481 if (lpar
.LevelSector
) {
482 Self
->XLevel
->AddStaticLightRGB(Ent
, lpar
, Flags
);
486 IMPLEMENT_FUNCTION(VLevelInfo
, MoveStaticLightByOwner
) {
488 P_GET_REF(VEntity
, Ent
);
490 Self
->XLevel
->MoveStaticLightByOwner(Ent
, Origin
);
493 IMPLEMENT_FUNCTION(VLevelInfo
, RemoveStaticLightByOwner
) {
494 P_GET_REF(VEntity
, Ent
);
496 Self
->XLevel
->RemoveStaticLightByOwner(Ent
);
499 IMPLEMENT_FUNCTION(VLevelInfo
, SectorStartSequence
) {
502 P_GET_PTR(sector_t
, sec
);
504 Self
->SectorStartSequence(sec
, name
, ModeNum
);
507 IMPLEMENT_FUNCTION(VLevelInfo
, SectorStopSequence
) {
508 P_GET_PTR(sector_t
, sec
);
510 Self
->SectorStopSequence(sec
);
513 IMPLEMENT_FUNCTION(VLevelInfo
, PolyobjStartSequence
) {
516 P_GET_PTR(polyobj_t
, poly
);
518 Self
->PolyobjStartSequence(poly
, name
, ModeNum
);
521 IMPLEMENT_FUNCTION(VLevelInfo
, PolyobjStopSequence
) {
522 P_GET_PTR(polyobj_t
, poly
);
524 Self
->PolyobjStopSequence(poly
);
527 IMPLEMENT_FUNCTION(VLevelInfo
, ExitLevel
) {
530 Self
->ExitLevel(Position
);
533 IMPLEMENT_FUNCTION(VLevelInfo
, SecretExitLevel
) {
536 Self
->SecretExitLevel(Position
);
539 IMPLEMENT_FUNCTION(VLevelInfo
, Completed
) {
540 P_GET_INT(SaveAngle
);
544 Self
->Completed(map
, pos
, SaveAngle
);
547 IMPLEMENT_FUNCTION(VLevelInfo
, IsSwitchTexture
) {
549 RET_BOOL(VLevelInfo::IsSwitchTexture(texid
));
552 //native final bool ChangeSwitchTexture (line_t *line, Entity Activator, int SideNum, int useAgain, name DefaultSound, out ubyte Quest, optional const TVec org);
553 IMPLEMENT_FUNCTION(VLevelInfo
, ChangeSwitchTexture
) {
560 VOptParamVec
org(TVec(0.0f
, 0.0f
, 0.0f
));
561 vobjGetParamSelf(line
, act
, SideNum
, useAgain
, DefaultSound
, pQuest
, org
);
562 const TVec
*porg
= (org
.specified
? &org
.value
: nullptr);
564 bool Ret
= Self
->ChangeSwitchTexture(line
, act
, SideNum
, useAgain
, DefaultSound
, Quest
, porg
);
565 if (pQuest
) *pQuest
= Quest
;
569 IMPLEMENT_FUNCTION(VLevelInfo
, FindMobjFromTID
) {
570 P_GET_REF(VEntity
, Prev
);
573 RET_REF(Self
->FindMobjFromTID(tid
, Prev
));
576 IMPLEMENT_FUNCTION(VLevelInfo
, AutoSave
) {
577 P_GET_BOOL_OPT(checkpoint
, false);
579 if (Self
->Game
->NetMode
== NM_Standalone
) SV_AutoSave(checkpoint
);
582 IMPLEMENT_FUNCTION(VLevelInfo
, ChangeMusic
) {
583 P_GET_NAME(SongName
);
585 Self
->ChangeMusic(SongName
);
588 IMPLEMENT_FUNCTION(VLevelInfo
, FindFreeTID
) {
589 P_GET_INT_OPT(limit
, 0);
590 P_GET_INT_OPT(tidstart
, 0);
592 RET_INT(Self
->FindFreeTID(tidstart
, limit
));
595 IMPLEMENT_FUNCTION(VLevelInfo
, IsTIDUsed
) {
598 RET_BOOL(Self
->IsTIDUsed(tid
));
601 // native final void ChangeSky (string skytex1, optional string skytex2/*=skytex1*/);
602 IMPLEMENT_FUNCTION(VLevelInfo
, ChangeSky
) {
604 VOptParamStr
skytex2(VStr::EmptyString
);
605 vobjGetParamSelf(skytex1
, skytex2
);
606 if (!skytex2
.specified
) skytex2
= skytex1
;
607 Self
->ChangeSky(skytex1
, skytex2
);
610 // because `LevelName` may require translation
611 //native final string GetLevelName ();
612 IMPLEMENT_FUNCTION(VLevelInfo
, GetLevelName
) {
614 RET_STR(Self
->GetLevelName());
618 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatShortTex
) { vobjGetParamSelf(); RET_BOOL(Self
? (!!(Self
->LevelInfoFlags2
&LIF2_CompatShortTex
) || compat_shorttex
.asBool()) : false); }
619 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatStairs
) { vobjGetParamSelf(); RET_BOOL(Self
? (!!(Self
->LevelInfoFlags2
&LIF2_CompatStairs
) || compat_stairs
.asBool()) : false); }
620 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatLimitPain
) { vobjGetParamSelf(); RET_BOOL(Self
? Self
->GetLimitPain() : false); }
621 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatNoPassOver
) { vobjGetParamSelf(); RET_BOOL(Self
? Self
->GetNoPassOver() : false); }
622 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatNoTossDrops
) { vobjGetParamSelf(); RET_BOOL(Self
? Self
->GetNoTossDrops() : false); }
623 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatUseBlocking
) { vobjGetParamSelf(); RET_BOOL(Self
? (!!(Self
->LevelInfoFlags2
&LIF2_CompatUseBlocking
) || compat_useblocking
.asBool()) : false); }
624 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatNoDoorLight
) { vobjGetParamSelf(); RET_BOOL(Self
? (!!(Self
->LevelInfoFlags2
&LIF2_CompatNoDoorLight
) || compat_nodoorlight
.asBool()) : false); }
625 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatRavenScroll
) { vobjGetParamSelf(); RET_BOOL(Self
? (!!(Self
->LevelInfoFlags2
&LIF2_CompatRavenScroll
) || compat_ravenscroll
.asBool()) : false); }
626 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatSoundTarget
) { vobjGetParamSelf(); RET_BOOL(Self
? (!!(Self
->LevelInfoFlags2
&LIF2_CompatSoundTarget
) || compat_soundtarget
.asBool()) : false); }
627 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatDehHealth
) { vobjGetParamSelf(); RET_BOOL(Self
? (!!(Self
->LevelInfoFlags2
&LIF2_CompatDehHealth
) || compat_dehhealth
.asBool()) : false); }
628 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatTrace
) { vobjGetParamSelf(); RET_BOOL(Self
? (!!(Self
->LevelInfoFlags2
&LIF2_CompatTrace
) || compat_trace
.asBool()) : false); }
629 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatDropOff
) { vobjGetParamSelf(); RET_BOOL(Self
? (!!(Self
->LevelInfoFlags2
&LIF2_CompatDropOff
) || compat_dropoff
.asBool()) : false); }
630 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatBoomScroll
) { vobjGetParamSelf(); RET_BOOL(Self
? (!!(Self
->LevelInfoFlags2
&LIF2_CompatBoomScroll
) || compat_boomscroll
.asBool()) : false); }
631 IMPLEMENT_FUNCTION(VLevelInfo
, get_CompatInvisibility
) { vobjGetParamSelf(); RET_BOOL(Self
? (!!(Self
->LevelInfoFlags2
&LIF2_CompatInvisibility
) || compat_invisibility
.asBool()) : false); }