mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-11-10 23:01:59 +00:00
- made the automap an object.
This simplifies handling a lot because it allows to make the level the owner of its map.
This commit is contained in:
parent
625e9f2525
commit
05830455e7
14 changed files with 830 additions and 782 deletions
1433
src/am_map.cpp
1433
src/am_map.cpp
File diff suppressed because it is too large
Load diff
53
src/am_map.h
53
src/am_map.h
|
@ -22,31 +22,44 @@
|
|||
#ifndef __AMMAP_H__
|
||||
#define __AMMAP_H__
|
||||
|
||||
#include "dobject.h"
|
||||
|
||||
struct event_t;
|
||||
class FSerializer;
|
||||
struct FLevelLocals;
|
||||
|
||||
class DAutomapBase : public DObject
|
||||
{
|
||||
DECLARE_ABSTRACT_CLASS(DAutomapBase, DObject);
|
||||
public:
|
||||
FLevelLocals *Level; // temporary location so that it can be set from the outside.
|
||||
|
||||
// Called by main loop.
|
||||
virtual bool Responder(event_t* ev, bool last) = 0;
|
||||
|
||||
// Called by main loop.
|
||||
virtual void Ticker(void) = 0;
|
||||
|
||||
// Called by main loop,
|
||||
// called instead of view drawer if automap active.
|
||||
virtual void Drawer(int bottom) = 0;
|
||||
|
||||
virtual void NewResolution() = 0;
|
||||
virtual void LevelInit() = 0;
|
||||
virtual void UpdateShowAllLines() = 0;
|
||||
virtual void GoBig() = 0;
|
||||
virtual void ResetFollowLocation() = 0;
|
||||
virtual int addMark() = 0;
|
||||
virtual bool clearMarks() = 0;
|
||||
virtual DVector2 GetPosition() = 0;
|
||||
virtual void startDisplay() = 0;
|
||||
|
||||
};
|
||||
|
||||
void AM_StaticInit();
|
||||
void AM_ClearColorsets(); // reset data for a restart.
|
||||
|
||||
// Called by main loop.
|
||||
bool AM_Responder (event_t* ev, bool last);
|
||||
|
||||
// Called by main loop.
|
||||
void AM_Ticker (void);
|
||||
|
||||
// Called by main loop,
|
||||
// called instead of view drawer if automap active.
|
||||
void AM_Drawer (int bottom);
|
||||
|
||||
// Called to force the automap to quit
|
||||
// if the level is completed while it is up.
|
||||
void AM_Stop (void);
|
||||
|
||||
void AM_NewResolution ();
|
||||
void AM_ToggleMap ();
|
||||
void AM_LevelInit ();
|
||||
void AM_SerializeMarkers(FSerializer &arc);
|
||||
|
||||
DAutomapBase *AM_Create(FLevelLocals *Level);
|
||||
void AM_Stop();
|
||||
void AM_ToggleMap();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -758,7 +758,7 @@ void D_Display ()
|
|||
screen->DrawBlend(viewsec);
|
||||
if (automapactive)
|
||||
{
|
||||
AM_Drawer (hud_althud? viewheight : StatusBar->GetTopOfStatusbar());
|
||||
currentUILevel->automap->Drawer (hud_althud? viewheight : StatusBar->GetTopOfStatusbar());
|
||||
}
|
||||
|
||||
// for timing the statusbar code.
|
||||
|
|
|
@ -955,7 +955,7 @@ bool G_Responder (event_t *ev)
|
|||
{
|
||||
if (ST_Responder (ev))
|
||||
return true; // status window ate it
|
||||
if (!viewactive && AM_Responder (ev, false))
|
||||
if (!viewactive && currentUILevel->automap->Responder (ev, false))
|
||||
return true; // automap ate it
|
||||
}
|
||||
else if (gamestate == GS_FINALE)
|
||||
|
@ -986,7 +986,7 @@ bool G_Responder (event_t *ev)
|
|||
// the events *last* so that any bound keys get precedence.
|
||||
|
||||
if (gamestate == GS_LEVEL && viewactive)
|
||||
return AM_Responder (ev, true);
|
||||
return currentUILevel->automap->Responder (ev, true);
|
||||
|
||||
return (ev->type == EV_KeyDown ||
|
||||
ev->type == EV_Mouse);
|
||||
|
@ -1179,7 +1179,7 @@ void G_Ticker ()
|
|||
{
|
||||
case GS_LEVEL:
|
||||
P_Ticker ();
|
||||
AM_Ticker ();
|
||||
currentUILevel->automap->Ticker ();
|
||||
break;
|
||||
|
||||
case GS_TITLELEVEL:
|
||||
|
|
|
@ -1019,9 +1019,8 @@ void G_DoLoadLevel (int position, bool autosave, bool newGame)
|
|||
E_NewGame(EventHandlerType::Global);
|
||||
}
|
||||
|
||||
P_SetupLevel (level.MapName, position, newGame);
|
||||
P_SetupLevel (&level, position, newGame);
|
||||
|
||||
AM_LevelInit();
|
||||
|
||||
// [RH] Start lightning, if MAPINFO tells us to
|
||||
if (level.flags & LEVEL_STARTLIGHTNING)
|
||||
|
@ -1126,6 +1125,7 @@ void G_DoLoadLevel (int position, bool autosave, bool newGame)
|
|||
{
|
||||
I_Error("no start for player %d found.", pnumerr);
|
||||
}
|
||||
P_ResetSightCounters(true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1452,8 +1452,6 @@ int G_FinishTravel ()
|
|||
|
||||
void FLevelLocals::Init()
|
||||
{
|
||||
level_info_t *info;
|
||||
|
||||
BaseBlendA = 0.0f; // Remove underwater blend effect, if any
|
||||
|
||||
gravity = sv_gravity * 35/TICRATE;
|
||||
|
@ -1465,7 +1463,6 @@ void FLevelLocals::Init()
|
|||
|
||||
info = FindLevelInfo (MapName);
|
||||
|
||||
info = info;
|
||||
skyspeed1 = info->skyspeed1;
|
||||
skyspeed2 = info->skyspeed2;
|
||||
skytexture1 = TexMan.GetTextureID(info->SkyPic1, ETextureType::Wall, FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_ReturnFirst);
|
||||
|
@ -1976,6 +1973,7 @@ void FLevelLocals::Mark()
|
|||
GC::Mark(SpotState);
|
||||
GC::Mark(FraggleScriptThinker);
|
||||
GC::Mark(ACSThinker);
|
||||
GC::Mark(automap);
|
||||
canvasTextureInfo.Mark();
|
||||
for (auto &c : CorpseQueue)
|
||||
{
|
||||
|
|
|
@ -87,6 +87,7 @@ class DACSThinker;
|
|||
class DFraggleThinker;
|
||||
class DSpotState;
|
||||
struct FStrifeDialogueNode;
|
||||
class DAutomapBase;
|
||||
|
||||
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
|
||||
|
@ -235,10 +236,10 @@ public:
|
|||
{
|
||||
return FLineIdIterator(tagManager, tag);
|
||||
}
|
||||
template<class T> TThinkerIterator<T> GetThinkerIterator(FName subtype = NAME_None)
|
||||
template<class T> TThinkerIterator<T> GetThinkerIterator(FName subtype = NAME_None, int statnum = MAX_STATNUM+1)
|
||||
{
|
||||
if (subtype == NAME_None) return TThinkerIterator<T>();
|
||||
else return TThinkerIterator<T>(subtype);
|
||||
if (subtype == NAME_None) return TThinkerIterator<T>(statnum);
|
||||
else return TThinkerIterator<T>(subtype, statnum);
|
||||
}
|
||||
FActorIterator GetActorIterator(int tid)
|
||||
{
|
||||
|
@ -357,6 +358,7 @@ public:
|
|||
|
||||
static const int BODYQUESIZE = 32;
|
||||
TObjPtr<AActor*> bodyque[BODYQUESIZE];
|
||||
TObjPtr<DAutomapBase*> automap;
|
||||
int bodyqueslot;
|
||||
|
||||
int NumMapSections;
|
||||
|
|
|
@ -1075,3 +1075,4 @@ xx(lightflags)
|
|||
xx(lighttype)
|
||||
xx(InternalDynamicLight)
|
||||
xx(_a_chase_default)
|
||||
xx(MapMarker)
|
|
@ -965,7 +965,8 @@ void G_SerializeLevel(FSerializer &arc, FLevelLocals *Level, bool hubload)
|
|||
("fragglethinker", Level->FraggleScriptThinker)
|
||||
("acsthinker", Level->ACSThinker)
|
||||
("impactdecalcount", Level->ImpactDecalCount)
|
||||
("scrolls", Level->Scrolls);
|
||||
("scrolls", Level->Scrolls)
|
||||
("automap", Level->automap);
|
||||
|
||||
|
||||
// Hub transitions must keep the current total time
|
||||
|
@ -988,7 +989,10 @@ void G_SerializeLevel(FSerializer &arc, FLevelLocals *Level, bool hubload)
|
|||
arc("zones", Level->Zones);
|
||||
arc("lineportals", Level->linePortals);
|
||||
arc("sectorportals", Level->sectorPortals);
|
||||
if (arc.isReading()) Level->FinalizePortals();
|
||||
if (arc.isReading())
|
||||
{
|
||||
Level->FinalizePortals();
|
||||
}
|
||||
|
||||
// [ZZ] serialize health groups
|
||||
P_SerializeHealthGroups(arc);
|
||||
|
@ -998,12 +1002,12 @@ void G_SerializeLevel(FSerializer &arc, FLevelLocals *Level, bool hubload)
|
|||
arc("polyobjs", Level->Polyobjects);
|
||||
SerializeSubsectors(arc, "subsectors");
|
||||
StatusBar->SerializeMessages(arc);
|
||||
AM_SerializeMarkers(arc);
|
||||
FRemapTable::StaticSerializeTranslations(arc);
|
||||
Level->canvasTextureInfo.Serialize(arc);
|
||||
P_SerializePlayers(Level, arc, hubload);
|
||||
P_SerializeSounds(arc);
|
||||
|
||||
// Regenerate some data that wasn't saved
|
||||
if (arc.isReading())
|
||||
{
|
||||
for (auto &sec : Level->sectors)
|
||||
|
@ -1012,12 +1016,17 @@ void G_SerializeLevel(FSerializer &arc, FLevelLocals *Level, bool hubload)
|
|||
}
|
||||
for (int i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
if (playeringame[i] && players[i].mo != NULL)
|
||||
if (playeringame[i] && players[i].mo != nullptr)
|
||||
{
|
||||
FWeaponSlots::SetupWeaponSlots(players[i].mo);
|
||||
}
|
||||
}
|
||||
AActor::RecreateAllAttachedLights();
|
||||
InitPortalGroups(Level);
|
||||
|
||||
Level->automap->Level = Level; // Temporary workaround. At the moment this cannot be deserialized yet.
|
||||
Level->automap->UpdateShowAllLines();
|
||||
|
||||
}
|
||||
AActor::RecreateAllAttachedLights();
|
||||
InitPortalGroups(Level);
|
||||
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
#include "a_specialspot.h"
|
||||
#include "maploader/maploader.h"
|
||||
#include "p_acs.h"
|
||||
#include "am_map.h"
|
||||
#include "fragglescript/t_script.h"
|
||||
|
||||
void P_ClearUDMFKeys();
|
||||
|
@ -260,6 +261,8 @@ void FLevelLocals::ClearPortals()
|
|||
|
||||
void FLevelLocals::ClearLevelData()
|
||||
{
|
||||
ClearAllSubsectorLinks(); // can't be done as part of the polyobj deletion process.
|
||||
|
||||
total_monsters = total_items = total_secrets =
|
||||
killed_monsters = found_items = found_secrets =
|
||||
wminfo.maxfrags = 0;
|
||||
|
@ -348,7 +351,6 @@ void P_FreeLevelData ()
|
|||
P_ClearUDMFKeys();
|
||||
|
||||
interpolator.ClearInterpolations(); // [RH] Nothing to interpolate on a fresh level.
|
||||
level.ClearAllSubsectorLinks(); // can't be done as part of the polyobj deletion process.
|
||||
SN_StopAllSequences ();
|
||||
DThinker::DestroyAllThinkers ();
|
||||
|
||||
|
@ -363,20 +365,20 @@ void P_FreeLevelData ()
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
void P_SetupLevel(const char *lumpname, int position, bool newGame)
|
||||
void P_SetupLevel(FLevelLocals *Level, int position, bool newGame)
|
||||
{
|
||||
int i;
|
||||
|
||||
level.ShaderStartTime = I_msTimeFS(); // indicate to the shader system that the level just started
|
||||
Level->ShaderStartTime = I_msTimeFS(); // indicate to the shader system that the level just started
|
||||
|
||||
// This is motivated as follows:
|
||||
|
||||
level.maptype = MAPTYPE_UNKNOWN;
|
||||
Level->maptype = MAPTYPE_UNKNOWN;
|
||||
wminfo.partime = 180;
|
||||
|
||||
if (!savegamerestore)
|
||||
{
|
||||
level.SetMusicVolume(level.MusicVolume);
|
||||
Level->SetMusicVolume(Level->MusicVolume);
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
players[i].killcount = players[i].secretcount
|
||||
|
@ -411,10 +413,10 @@ void P_SetupLevel(const char *lumpname, int position, bool newGame)
|
|||
// Free all level data from the previous map
|
||||
P_FreeLevelData();
|
||||
|
||||
MapData *map = P_OpenMapData(lumpname, true);
|
||||
MapData *map = P_OpenMapData(Level->MapName, true);
|
||||
if (map == nullptr)
|
||||
{
|
||||
I_Error("Unable to open map '%s'\n", lumpname);
|
||||
I_Error("Unable to open map '%s'\n", Level->MapName.GetChars());
|
||||
}
|
||||
|
||||
// [ZZ] init per-map static handlers. we need to call this before everything is set up because otherwise scripts don't receive PlayerEntered event
|
||||
|
@ -422,17 +424,17 @@ void P_SetupLevel(const char *lumpname, int position, bool newGame)
|
|||
E_InitStaticHandlers(true);
|
||||
|
||||
// generate a checksum for the level, to be included and checked with savegames.
|
||||
map->GetChecksum(level.md5);
|
||||
map->GetChecksum(Level->md5);
|
||||
// find map num
|
||||
level.lumpnum = map->lumpnum;
|
||||
Level->lumpnum = map->lumpnum;
|
||||
|
||||
if (newGame)
|
||||
{
|
||||
E_NewGame(EventHandlerType::PerMap);
|
||||
}
|
||||
|
||||
MapLoader loader(&level);
|
||||
loader.LoadLevel(map, lumpname, position);
|
||||
MapLoader loader(Level);
|
||||
loader.LoadLevel(map, Level->MapName.GetChars(), position);
|
||||
delete map;
|
||||
|
||||
// if deathmatch, randomly spawn the active players
|
||||
|
@ -448,7 +450,7 @@ void P_SetupLevel(const char *lumpname, int position, bool newGame)
|
|||
}
|
||||
}
|
||||
// the same, but for random single/coop player starts
|
||||
else if (level.flags2 & LEVEL2_RANDOMPLAYERSTARTS)
|
||||
else if (Level->flags2 & LEVEL2_RANDOMPLAYERSTARTS)
|
||||
{
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
|
@ -456,14 +458,14 @@ void P_SetupLevel(const char *lumpname, int position, bool newGame)
|
|||
{
|
||||
players[i].mo = nullptr;
|
||||
FPlayerStart *mthing = G_PickPlayerStart(i);
|
||||
P_SpawnPlayer(mthing, i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
|
||||
P_SpawnPlayer(mthing, i, (Level->flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [SP] move unfriendly players around
|
||||
// horribly hacky - yes, this needs rewritten.
|
||||
if (level.deathmatchstarts.Size() > 0)
|
||||
if (Level->deathmatchstarts.Size() > 0)
|
||||
{
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
|
@ -522,7 +524,7 @@ void P_SetupLevel(const char *lumpname, int position, bool newGame)
|
|||
|
||||
// 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.
|
||||
for (auto &s : level.sectors)
|
||||
for (auto &s : Level->sectors)
|
||||
{
|
||||
if (s.heightsec != nullptr)
|
||||
{
|
||||
|
@ -537,17 +539,18 @@ void P_SetupLevel(const char *lumpname, int position, bool newGame)
|
|||
}
|
||||
}
|
||||
|
||||
P_ResetSightCounters(true);
|
||||
|
||||
// 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.
|
||||
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->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();
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -146,7 +146,7 @@ bool P_CheckMapData(const char * mapname);
|
|||
// [RH] The only parameter used is mapname, so I removed playermask and skill.
|
||||
// On September 1, 1998, I added the position to indicate which set
|
||||
// of single-player start spots should be spawned in the level.
|
||||
void P_SetupLevel (const char *mapname, int position, bool newGame);
|
||||
void P_SetupLevel (FLevelLocals *Level, int position, bool newGame);
|
||||
|
||||
void P_FreeLevelData();
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "d_player.h"
|
||||
#include "p_setup.h"
|
||||
#include "i_music.h"
|
||||
#include "am_map.h"
|
||||
|
||||
DVector2 AM_GetPosition();
|
||||
int Net_GetLatency(int *ld, int *ad);
|
||||
|
@ -2529,15 +2530,17 @@ DEFINE_ACTION_FUNCTION_NATIVE(FLevelLocals, FormatMapName, FormatMapName)
|
|||
ACTION_RETURN_STRING(rets);
|
||||
}
|
||||
|
||||
static void GetAutomapPosition(DVector2 *pos)
|
||||
static void GetAutomapPosition(FLevelLocals *self, DVector2 *pos)
|
||||
{
|
||||
*pos = AM_GetPosition();
|
||||
*pos = self->automap->GetPosition();
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(FLevelLocals, GetAutomapPosition, GetAutomapPosition)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
ACTION_RETURN_VEC2(AM_GetPosition());
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FLevelLocals);
|
||||
DVector2 result;
|
||||
GetAutomapPosition(self, &result);
|
||||
ACTION_RETURN_VEC2(result);
|
||||
}
|
||||
|
||||
static int ZGetUDMFInt(FLevelLocals *self, int type, int index, int key)
|
||||
|
|
|
@ -66,6 +66,8 @@
|
|||
#include "r_videoscale.h"
|
||||
#include "i_time.h"
|
||||
#include "version.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "am_map.h"
|
||||
|
||||
EXTERN_CVAR(Bool, cl_capfps)
|
||||
EXTERN_CVAR(Int, menu_resolution_custom_width)
|
||||
|
@ -592,7 +594,8 @@ void V_OutputResized (int width, int height)
|
|||
C_NewModeAdjust();
|
||||
// Reload crosshair if transitioned to a different size
|
||||
ST_LoadCrosshair(true);
|
||||
AM_NewResolution();
|
||||
if (currentUILevel && currentUILevel->automap)
|
||||
currentUILevel->automap->NewResolution();
|
||||
}
|
||||
|
||||
void V_CalcCleanFacs (int designwidth, int designheight, int realwidth, int realheight, int *cleanx, int *cleany, int *_cx1, int *_cx2)
|
||||
|
|
|
@ -687,7 +687,7 @@ struct LevelLocals native
|
|||
native static void StartSlideshow(Name whichone = 'none');
|
||||
native static void WorldDone();
|
||||
native static void RemoveAllBots(bool fromlist);
|
||||
native static Vector2 GetAutomapPosition();
|
||||
native ui Vector2 GetAutomapPosition();
|
||||
native void SetInterMusic(String nextmap);
|
||||
native String FormatMapName(int mapnamecolor);
|
||||
native bool IsJumpingAllowed() const;
|
||||
|
@ -722,8 +722,8 @@ struct LevelLocals native
|
|||
return String.Format("%02d:%02d:%02d", sec / 3600, (sec % 3600) / 60, sec % 60);
|
||||
}
|
||||
|
||||
native bool CreateCeiling(sector sec, int type, line ln, double speed, double speed2, double height = 0, int crush = -1, int silent = 0, int change = 0, int crushmode = crushDoom);
|
||||
native bool CreateFloor(sector sec, EFloor floortype, line ln, double speed, double height = 0, int crush = -1, int change = 0, bool crushmode = false, bool hereticlower = false);
|
||||
native bool CreateCeiling(sector sec, int type, line ln, double speed, double speed2, double height = 0, int crush = -1, int silent = 0, int change = 0, int crushmode = 0 /*Floor.crushDoom*/);
|
||||
native bool CreateFloor(sector sec, int floortype, line ln, double speed, double height = 0, int crush = -1, int change = 0, bool crushmode = false, bool hereticlower = false);
|
||||
|
||||
}
|
||||
|
||||
|
@ -926,7 +926,7 @@ class Floor : MovingFloor native
|
|||
genFloorChg
|
||||
};
|
||||
|
||||
deprecated("3.8") static bool CreateFloor(sector sec, EFloor floortype, line ln, double speed, double height = 0, int crush = -1, int change = 0, bool crushmode = false, bool hereticlower = false)
|
||||
deprecated("3.8") static bool CreateFloor(sector sec, int floortype, line ln, double speed, double height = 0, int crush = -1, int change = 0, bool crushmode = false, bool hereticlower = false)
|
||||
{
|
||||
return level.CreateFloor(sec, floortype, ln, speed, height, crush, change, crushmode, hereticlower);
|
||||
}
|
||||
|
|
|
@ -31,11 +31,6 @@
|
|||
**
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
DVector2 AM_GetPosition();
|
||||
*/
|
||||
|
||||
class AltHud ui
|
||||
{
|
||||
TextureID tnt1a0;
|
||||
|
|
Loading…
Reference in a new issue