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-29 00:09:02 +00:00
|
|
|
#include "p_effect.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"
|
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-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
|
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-01-29 01:16:36 +00:00
|
|
|
FLevelLocals() : Behaviors(this), tagManager(this) {}
|
2019-01-28 00:33:52 +00:00
|
|
|
|
2019-01-24 23:30:55 +00:00
|
|
|
friend class MapLoader;
|
|
|
|
|
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);
|
2018-12-31 11:42:03 +00:00
|
|
|
void ClearLevelData();
|
|
|
|
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-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-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:
|
|
|
|
line_t *FindPortalDestination(line_t *src, int tag);
|
|
|
|
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();
|
|
|
|
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 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-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-26 14:21:20 +00:00
|
|
|
if (subtype == NAME_None) return TThinkerIterator<T>(statnum);
|
|
|
|
else return TThinkerIterator<T>(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)
|
|
|
|
{
|
|
|
|
return TThinkerIterator<T>(subtype, statnum, prev);
|
|
|
|
}
|
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;
|
|
|
|
DThinker::FreshThinkers[statnum].AddTail(thinker);
|
|
|
|
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
|
|
|
|
|
|
|
void SetMusic()
|
|
|
|
{
|
|
|
|
if (cdtrack == 0 || !S_ChangeCDMusic(cdtrack, cdid))
|
|
|
|
S_ChangeMusic(Music, musicorder);
|
|
|
|
}
|
2019-01-27 00:49:20 +00:00
|
|
|
|
2019-01-28 21:04:33 +00:00
|
|
|
TArray<vertex_t> vertexes;
|
|
|
|
TArray<sector_t> sectors;
|
|
|
|
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;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
// [ZZ] Destructible geometry information
|
|
|
|
TMap<int, FHealthGroup> healthGroups;
|
|
|
|
|
|
|
|
FBlockmap blockmap;
|
|
|
|
TArray<polyblock_t *> PolyBlockMap;
|
|
|
|
|
|
|
|
// 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-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;
|
|
|
|
|
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
|
|
|
|
int maptime; // time in the map
|
|
|
|
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
|
|
|
|
FString F1Pic;
|
|
|
|
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];
|
2019-01-27 00:49:20 +00:00
|
|
|
TObjPtr<DAutomapBase*> automap = nullptr;
|
2018-12-19 17:41:53 +00:00
|
|
|
int bodyqueslot;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
double gravity;
|
|
|
|
double aircontrol;
|
|
|
|
double airfriction;
|
|
|
|
int airsupply;
|
|
|
|
int DefaultEnvironment; // Default sound environment.
|
|
|
|
|
2019-01-28 22:53:40 +00:00
|
|
|
int ActiveSequences;
|
|
|
|
DSeqNode *SequenceListHead;
|
|
|
|
|
2019-01-29 00:09:02 +00:00
|
|
|
// [RH] particle globals
|
|
|
|
uint32_t ActiveParticles;
|
|
|
|
uint32_t InactiveParticles;
|
|
|
|
TArray<particle_t> Particles;
|
|
|
|
TArray<uint16_t> ParticlesInSubsec;
|
|
|
|
|
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-01-28 19:15:48 +00:00
|
|
|
uint8_t 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;
|
|
|
|
TObjPtr<DFraggleThinker *> FraggleScriptThinker = nullptr;
|
2019-01-05 20:59:34 +00:00
|
|
|
TObjPtr<DACSThinker*> ACSThinker = nullptr;
|
|
|
|
|
2019-01-05 09:04:27 +00:00
|
|
|
TObjPtr<DSpotState *> SpotState = nullptr;
|
2018-04-01 16:45:27 +00:00
|
|
|
|
2017-01-08 17:45:30 +00:00
|
|
|
bool IsJumpingAllowed() const;
|
|
|
|
bool IsCrouchingAllowed() const;
|
|
|
|
bool IsFreelookAllowed() const;
|
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-01-25 17:16:18 +00:00
|
|
|
extern FLevelLocals *currentUILevel; // level for which to display the user interface. This will always be the one the current consoleplayer is in.
|
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
|
|
|
|
{
|
2019-01-24 23:42:55 +00:00
|
|
|
return 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
|
|
|
|
{
|
2019-01-24 23:42:55 +00:00
|
|
|
return 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
|
|
|
|
{
|
2019-01-24 23:42:55 +00:00
|
|
|
return 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
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
return TArrayView<FLevelLocals *>(¤tUILevel, 1);
|
|
|
|
}
|