engine: reject mbf21 and shit24 wads. there is no way to know if it is safe to ignore...
[k8vavoom.git] / source / psim / p_levelinfo.cpp
blob032f6cd95b6a75fabc51fd43abbfaeb65100525b
1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** Copyright (C) 1999-2006 Jānis Legzdiņš
11 //** Copyright (C) 2018-2023 Ketmar Dark
12 //**
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.
16 //**
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.
21 //**
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/>.
24 //**
25 //**************************************************************************
26 #include "../gamedefs.h"
27 #include "../server/server.h"
28 #include "../server/sv_local.h"
29 #include "../server/sv_save.h"
30 #ifdef CLIENT
31 # include "../client/client.h" /* for `cl` */
32 #endif
33 #include "../language.h"
34 #include "../infostr.h"
35 #include "p_entity.h"
36 #include "p_levelinfo.h"
37 #include "p_player.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 () {
68 Super::PostCtor();
69 Level = this;
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;
96 SkyBox = Info.SkyBox;
98 FadeTable = Info.FadeTable;
99 Fade = Info.Fade;
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,
143 const TVec *org)
145 if (Sector) {
146 if (Sector->SectorFlags&sector_t::SF_Silent) return;
147 const int oid = (int)(ptrdiff_t)(Sector-XLevel->Sectors)+(org ? (SNDORG_SectorOrg<<24) : (SNDORG_Sector<<24));
148 TVec sorg;
149 if (org) {
150 sorg = *org;
152 #ifdef CLIENT
153 else if (cl) {
154 sorg = XLevel->CalcSectorSoundOrigin(Sector, cl->ViewOrg);
156 #endif
157 else {
158 sorg = Sector->soundorg;
159 sorg.z = (Sector->floor.minz+Sector->floor.maxz)*0.5f+8.0f;
162 TVec sorg = (org ? *org : Sector->soundorg);
163 if (!org) {
164 if (!Sector->isInnerPObj()) {
165 // normal sector
166 sorg.z = (Sector->floor.minz+Sector->floor.maxz)*0.5f+8.0f;
167 } else {
168 // 3d pobj
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);
176 } else {
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) {
199 if (Sector) {
200 if (Sector->SectorFlags&sector_t::SF_Silent) return;
201 TVec sorg;
202 #ifdef CLIENT
203 if (cl) {
204 sorg = XLevel->CalcSectorSoundOrigin(Sector, cl->ViewOrg);
205 } else
206 #endif
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);
212 } else {
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;
235 if (!Poly) return;
236 bool seenNonSilent = false;
237 for (auto &&it : Poly->SubFirst()) {
238 if (!(it.sub()->sector->SectorFlags&sector_t::SF_Silent)) {
239 seenNonSilent = true;
240 break;
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) {
254 if (!poly) return;
255 StopSoundSequence(poly->index+(SNDORG_PolyObj<<24));
259 //==========================================================================
261 // VLevelInfo::ExitLevel
263 //==========================================================================
264 void VLevelInfo::ExitLevel (int Position) {
265 LeavePosition = Position;
266 completed = true;
267 // just in case
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
282 ExitLevel(Position);
283 return;
286 LeavePosition = Position;
287 completed = true;
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) {
310 int Map = InMap;
311 int Position = InPosition;
312 if (Map == -1 && Position == -1) {
313 if (!svs.deathmatch) {
314 if (G_CheckWantExitText()) {
315 LeavePosition = Position; // just in case
316 completed = true;
317 return;
319 // otherwise, jump straight into intermission
320 G_StartClientFinale();
321 return;
323 Map = 1;
324 Position = 0;
326 NextMap = P_GetMapLumpNameByLevelNum(Map);
328 LeavePosition = Position;
329 completed = true;
333 //==========================================================================
335 // VLevelInfo::FindMobjFromTID
337 //==========================================================================
338 VEntity *VLevelInfo::FindMobjFromTID (int tid, VEntity *Prev) {
339 if (tid) {
340 for (VEntity *E = (Prev ? Prev->TIDHashNext : TIDHash[VLevelInfo::TIDHashBucket(tid)]); E; E = E->TIDHashNext) {
341 if (!E->IsGoingToDie() && E->TID == tid) return E;
344 return nullptr;
348 //==========================================================================
350 // VLevelInfo::ChangeMusic
352 //==========================================================================
353 void VLevelInfo::ChangeMusic (VName SongName) {
354 SongLump = SongName;
358 //==========================================================================
360 // VLevelInfo::GetLevelName
362 //==========================================================================
363 VStr VLevelInfo::GetLevelName () const {
364 return (LevelInfoFlags&LIF_LookupName ? GLanguage[*LevelName] : LevelName);
368 //==========================================================================
370 // VLevelInfo::FindFreeTID
372 // see `UniqueTID`
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;
379 if (tidstart == 0) {
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
386 tidstart = 1;
387 } else {
388 tidstart = 1; // 0 is used
390 // linear search
391 while (limit-- > 0) {
392 if (!IsTIDUsed(tidstart, true)) return tidstart;
393 ++tidstart;
394 if (tidstart == 0x1fffffff) return 0; // arbitrary limit
395 //if (tidstart == 0x8000) return 0; // arbitrary limit
397 return 0;
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;
412 return false;
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);
429 if (sky1tid > 0) {
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) {
446 VLightParams lpar;
447 VEntity *Ent;
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) {
460 VLightParams lpar;
461 VEntity *Ent;
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) {
473 VLightParams lpar;
474 VEntity *Ent;
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) {
487 P_GET_VEC(Origin);
488 P_GET_REF(VEntity, Ent);
489 P_GET_SELF;
490 Self->XLevel->MoveStaticLightByOwner(Ent, Origin);
493 IMPLEMENT_FUNCTION(VLevelInfo, RemoveStaticLightByOwner) {
494 P_GET_REF(VEntity, Ent);
495 P_GET_SELF;
496 Self->XLevel->RemoveStaticLightByOwner(Ent);
499 IMPLEMENT_FUNCTION(VLevelInfo, SectorStartSequence) {
500 P_GET_INT(ModeNum);
501 P_GET_NAME(name);
502 P_GET_PTR(sector_t, sec);
503 P_GET_SELF;
504 Self->SectorStartSequence(sec, name, ModeNum);
507 IMPLEMENT_FUNCTION(VLevelInfo, SectorStopSequence) {
508 P_GET_PTR(sector_t, sec);
509 P_GET_SELF;
510 Self->SectorStopSequence(sec);
513 IMPLEMENT_FUNCTION(VLevelInfo, PolyobjStartSequence) {
514 P_GET_INT(ModeNum);
515 P_GET_NAME(name);
516 P_GET_PTR(polyobj_t, poly);
517 P_GET_SELF;
518 Self->PolyobjStartSequence(poly, name, ModeNum);
521 IMPLEMENT_FUNCTION(VLevelInfo, PolyobjStopSequence) {
522 P_GET_PTR(polyobj_t, poly);
523 P_GET_SELF;
524 Self->PolyobjStopSequence(poly);
527 IMPLEMENT_FUNCTION(VLevelInfo, ExitLevel) {
528 P_GET_INT(Position);
529 P_GET_SELF;
530 Self->ExitLevel(Position);
533 IMPLEMENT_FUNCTION(VLevelInfo, SecretExitLevel) {
534 P_GET_INT(Position);
535 P_GET_SELF;
536 Self->SecretExitLevel(Position);
539 IMPLEMENT_FUNCTION(VLevelInfo, Completed) {
540 P_GET_INT(SaveAngle);
541 P_GET_INT(pos);
542 P_GET_INT(map);
543 P_GET_SELF;
544 Self->Completed(map, pos, SaveAngle);
547 IMPLEMENT_FUNCTION(VLevelInfo, IsSwitchTexture) {
548 P_GET_INT(texid);
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) {
554 line_t *line;
555 VEntity *act;
556 int SideNum;
557 int useAgain;
558 VName DefaultSound;
559 vuint8 *pQuest;
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);
563 bool Quest = false;
564 bool Ret = Self->ChangeSwitchTexture(line, act, SideNum, useAgain, DefaultSound, Quest, porg);
565 if (pQuest) *pQuest = Quest;
566 RET_BOOL(Ret);
569 IMPLEMENT_FUNCTION(VLevelInfo, FindMobjFromTID) {
570 P_GET_REF(VEntity, Prev);
571 P_GET_INT(tid);
572 P_GET_SELF;
573 RET_REF(Self->FindMobjFromTID(tid, Prev));
576 IMPLEMENT_FUNCTION(VLevelInfo, AutoSave) {
577 P_GET_BOOL_OPT(checkpoint, false);
578 P_GET_SELF;
579 if (Self->Game->NetMode == NM_Standalone) SV_AutoSave(checkpoint);
582 IMPLEMENT_FUNCTION(VLevelInfo, ChangeMusic) {
583 P_GET_NAME(SongName);
584 P_GET_SELF;
585 Self->ChangeMusic(SongName);
588 IMPLEMENT_FUNCTION(VLevelInfo, FindFreeTID) {
589 P_GET_INT_OPT(limit, 0);
590 P_GET_INT_OPT(tidstart, 0);
591 P_GET_SELF;
592 RET_INT(Self->FindFreeTID(tidstart, limit));
595 IMPLEMENT_FUNCTION(VLevelInfo, IsTIDUsed) {
596 P_GET_INT(tid);
597 P_GET_SELF;
598 RET_BOOL(Self->IsTIDUsed(tid));
601 // native final void ChangeSky (string skytex1, optional string skytex2/*=skytex1*/);
602 IMPLEMENT_FUNCTION(VLevelInfo, ChangeSky) {
603 VStr skytex1;
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) {
613 vobjGetParamSelf();
614 RET_STR(Self->GetLevelName());
617 // compat getters
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); }