mirror of
https://github.com/celisej567/source-engine.git
synced 2026-01-04 18:09:53 +03:00
1
This commit is contained in:
523
hammer/mapclass.h
Normal file
523
hammer/mapclass.h
Normal file
@@ -0,0 +1,523 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
//
|
||||
// Purpose: Defines a common class for all objects in the world object tree.
|
||||
//
|
||||
// Pointers to other objects in the object tree may be stored, but
|
||||
// they should be set via UpdateDependency rather than directly. This
|
||||
// insures proper linkage to the other object so that if it moves, is
|
||||
// removed from the world, or changes in any other way, the dependent
|
||||
// object is properly notified.
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#ifndef MAPCLASS_H
|
||||
#define MAPCLASS_H
|
||||
#ifdef _WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "tier0/basetypes.h"
|
||||
|
||||
#pragma warning(push, 1)
|
||||
#pragma warning(disable:4701 4702 4530)
|
||||
#include <fstream>
|
||||
#pragma warning(pop)
|
||||
#include "BoundBox.h"
|
||||
#include "MapPoint.h"
|
||||
#include "utlvector.h"
|
||||
#include "visgroup.h"
|
||||
#include "fgdlib/wckeyvalues.h"
|
||||
#include "tier1/smartptr.h"
|
||||
|
||||
|
||||
class Box3D;
|
||||
class CBaseTool;
|
||||
class CChunkFile;
|
||||
class GDclass;
|
||||
class CMapClass;
|
||||
class CMapEntity;
|
||||
class CMapSolid;
|
||||
class CMapView2D;
|
||||
class CMapViewLogical;
|
||||
class CMapWorld;
|
||||
class CPoint;
|
||||
class CRender3D;
|
||||
class CSaveInfo;
|
||||
class CSSolid;
|
||||
class CVisGroupList;
|
||||
class CMapFaceList;
|
||||
|
||||
struct MapError;
|
||||
|
||||
enum ChunkFileResult_t;
|
||||
|
||||
struct MapObjectPair_t
|
||||
{
|
||||
CMapClass *pObject1;
|
||||
CMapClass *pObject2;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure used for returning hits when calling ObjectsAt.
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef struct HitInfo_s
|
||||
{
|
||||
CMapClass *pObject; // Pointer to the CMapAtom that was clicked on.
|
||||
unsigned int uData; // Additional data provided by the CMapAtom object.
|
||||
unsigned int nDepth; // Depth value of the object that was clicked on.
|
||||
VMatrix m_LocalMatrix;
|
||||
} HitInfo_t;
|
||||
|
||||
|
||||
//
|
||||
// Passed into PrepareSelection to control what gets selected.
|
||||
//
|
||||
enum SelectMode_t
|
||||
{
|
||||
selectGroups = 0, // select groups, ungrouped entities, and ungrouped solids
|
||||
selectObjects, // select entities and solids not in entities
|
||||
selectSolids, // select point entities, solids in entities, solids
|
||||
};
|
||||
|
||||
enum VisGroupSelection
|
||||
{
|
||||
AUTO = 0,
|
||||
USER
|
||||
};
|
||||
|
||||
// helper macro for linked lists as pointers
|
||||
#define FOR_EACH_OBJ( listName, iteratorName ) \
|
||||
for( int iteratorName=0; iteratorName<(listName).Count(); iteratorName++)
|
||||
|
||||
|
||||
|
||||
typedef const char * MAPCLASSTYPE;
|
||||
typedef BOOL (*ENUMMAPCHILDRENPROC)(CMapClass *, unsigned int dwParam);
|
||||
typedef CUtlVector<CMapClass*> CMapObjectList;
|
||||
|
||||
|
||||
#define MAX_ENUM_CHILD_DEPTH 16
|
||||
|
||||
|
||||
struct EnumChildrenStackEntry_t
|
||||
{
|
||||
CMapClass *pParent;
|
||||
int pos;
|
||||
};
|
||||
|
||||
|
||||
struct EnumChildrenPos_t
|
||||
{
|
||||
EnumChildrenStackEntry_t Stack[MAX_ENUM_CHILD_DEPTH];
|
||||
int nDepth;
|
||||
};
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MAPCLASSTYPE Type;
|
||||
CMapClass * (*pfnNew)();
|
||||
} MCMSTRUCT;
|
||||
|
||||
|
||||
// This is a reference-counted class that holds a pointer to an object.
|
||||
// When the object goes away, it can set the pointer in here to NULL
|
||||
// and anyone else who holds a reference to this can know that the
|
||||
// object has gone away. It's similar to the EHANDLEs in the engine,
|
||||
// except there's no finite list of objects that's managed anywhere.
|
||||
template<class T>
|
||||
class CSafeObject
|
||||
{
|
||||
public:
|
||||
static CSmartPtr< CSafeObject< T > > Create( T *pObject )
|
||||
{
|
||||
CSafeObject<T> *pRet = new CSafeObject<T>( pObject );
|
||||
return CSmartPtr< CSafeObject< T> >( pRet );
|
||||
}
|
||||
|
||||
void AddRef()
|
||||
{
|
||||
++m_RefCount;
|
||||
}
|
||||
void Release()
|
||||
{
|
||||
--m_RefCount;
|
||||
if ( m_RefCount <= 0 )
|
||||
delete this;
|
||||
}
|
||||
int GetRefCount() const
|
||||
{
|
||||
return m_RefCount;
|
||||
}
|
||||
|
||||
public:
|
||||
T *m_pObject;
|
||||
|
||||
private:
|
||||
CSafeObject( T *pObject )
|
||||
{
|
||||
m_RefCount = 0;
|
||||
m_pObject = pObject;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_RefCount; // This object goes away when all smart pointers to it go away.
|
||||
};
|
||||
|
||||
|
||||
class CMapClass : public CMapPoint
|
||||
{
|
||||
public:
|
||||
//
|
||||
// Construction/destruction:
|
||||
//
|
||||
CMapClass(void);
|
||||
virtual ~CMapClass(void);
|
||||
|
||||
const CSmartPtr< CSafeObject< CMapClass > >& GetSafeObjectSmartPtr();
|
||||
|
||||
inline int GetID(void);
|
||||
inline void SetID(int nID);
|
||||
virtual size_t GetSize(void);
|
||||
|
||||
//
|
||||
// Can belong to one or more visgroups:
|
||||
//
|
||||
virtual void AddVisGroup(CVisGroup *pVisGroup);
|
||||
int GetVisGroupCount(void);
|
||||
CVisGroup *GetVisGroup(int nIndex);
|
||||
void RemoveAllVisGroups(void);
|
||||
void RemoveVisGroup(CVisGroup *pVisGroup);
|
||||
int IsInVisGroup(CVisGroup *pVisGroup);
|
||||
void SetColorVisGroup(CVisGroup *pVisGroup);
|
||||
virtual bool UpdateObjectColor();
|
||||
|
||||
//
|
||||
// Can be tracked in the Undo/Redo system:
|
||||
//
|
||||
inline void SetTemporary(bool bTemporary) { m_bTemporary = bTemporary; }
|
||||
inline bool IsTemporary(void) const { return m_bTemporary; }
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned ID : 28;
|
||||
unsigned Types : 4; // 0 - copy, 1 - relationship, 2 - delete
|
||||
} Kept;
|
||||
|
||||
unsigned int dwKept;
|
||||
};
|
||||
|
||||
//
|
||||
// Has children:
|
||||
//
|
||||
virtual void AddChild(CMapClass *pChild);
|
||||
virtual void CopyChildrenFrom(CMapClass *pobj, bool bUpdateDependencies);
|
||||
virtual void RemoveAllChildren(void);
|
||||
virtual void RemoveChild(CMapClass *pChild, bool bUpdateBounds = true);
|
||||
virtual void UpdateChild(CMapClass *pChild);
|
||||
|
||||
inline int GetChildCount(void) { return( m_Children.Count()); }
|
||||
inline const CMapObjectList *GetChildren() { return &m_Children; }
|
||||
|
||||
CMapClass *GetFirstDescendent(EnumChildrenPos_t &pos);
|
||||
CMapClass *GetNextDescendent(EnumChildrenPos_t &pos);
|
||||
|
||||
virtual CMapClass *GetParent(void)
|
||||
{
|
||||
Assert( (m_pParent == NULL) || (dynamic_cast<CMapClass*>(m_pParent) != NULL) );
|
||||
return( (CMapClass*)m_pParent);
|
||||
}
|
||||
|
||||
virtual void SetParent(CMapAtom *pParent)
|
||||
{
|
||||
Assert( (pParent == NULL) || (dynamic_cast<CMapClass*>(pParent) != NULL) );
|
||||
UpdateParent((CMapClass*)pParent);
|
||||
}
|
||||
|
||||
const CMapObjectList *GetDependents() { return &m_Dependents; }
|
||||
|
||||
virtual void FindTargetNames( CUtlVector< const char * > &Names ) { }
|
||||
virtual void ReplaceTargetname(const char *szOldName, const char *szNewName);
|
||||
|
||||
//
|
||||
// Notifications.
|
||||
//
|
||||
virtual void OnAddToWorld(CMapWorld *pWorld);
|
||||
virtual void OnClone(CMapClass *pClone, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList);
|
||||
virtual void OnPreClone(CMapClass *pClone, CMapWorld *pWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList);
|
||||
virtual void OnPrePaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList);
|
||||
virtual void OnPaste(CMapClass *pCopy, CMapWorld *pSourceWorld, CMapWorld *pDestWorld, const CMapObjectList &OriginalList, CMapObjectList &NewList);
|
||||
virtual void OnNotifyDependent(CMapClass *pObject, Notify_Dependent_t eNotifyType);
|
||||
virtual void OnParentKeyChanged(const char* key, const char* value) {}
|
||||
virtual void OnRemoveFromWorld(CMapWorld *pWorld, bool bNotifyChildren);
|
||||
virtual void OnUndoRedo(void) {}
|
||||
|
||||
virtual bool OnApply( void ) { return true; }
|
||||
|
||||
//
|
||||
// Bounds calculation and intersection functions.
|
||||
//
|
||||
virtual void CalcBounds(BOOL bFullUpdate = FALSE);
|
||||
|
||||
void GetCullBox(Vector &mins, Vector &maxs);
|
||||
void SetCullBoxFromFaceList( CMapFaceList *pFaces );
|
||||
void GetBoundingBox( Vector &mins, Vector &maxs );
|
||||
void SetBoundingBoxFromFaceList( CMapFaceList *pFaces );
|
||||
|
||||
void GetRender2DBox(Vector &mins, Vector &maxs);
|
||||
|
||||
// NOTE: Logical position is in global space
|
||||
virtual void SetLogicalPosition( const Vector2D &vecPosition ) {}
|
||||
virtual const Vector2D& GetLogicalPosition( );
|
||||
|
||||
// NOTE: Logical bounds is in global space
|
||||
virtual void GetRenderLogicalBox( Vector2D &mins, Vector2D &maxs );
|
||||
|
||||
// HACK: temp stuff to ease the transition to not inheriting from BoundBox
|
||||
void GetBoundsCenter(Vector &vecCenter) { m_Render2DBox.GetBoundsCenter(vecCenter); }
|
||||
void GetBoundsSize(Vector &vecSize) { m_Render2DBox.GetBoundsSize(vecSize); }
|
||||
inline bool IsInsideBox(Vector const &Mins, Vector const &Maxs) const { return(m_Render2DBox.IsInsideBox(Mins, Maxs)); }
|
||||
inline bool IsIntersectingBox(const Vector &vecMins, const Vector& vecMaxs) const { return(m_Render2DBox.IsIntersectingBox(vecMins, vecMaxs)); }
|
||||
|
||||
virtual CMapClass *PrepareSelection(SelectMode_t eSelectMode);
|
||||
|
||||
void PostUpdate(Notify_Dependent_t eNotifyType);
|
||||
static void UpdateAllDependencies(CMapClass *pObject);
|
||||
|
||||
void SetOrigin(Vector& origin);
|
||||
|
||||
// hierarchy
|
||||
virtual void UpdateAnimation( float animTime ) {}
|
||||
virtual bool GetTransformMatrix( VMatrix& matrix );
|
||||
|
||||
virtual MAPCLASSTYPE GetType(void) = 0;
|
||||
virtual BOOL IsMapClass(MAPCLASSTYPE Type) = 0;
|
||||
virtual bool IsWorld() { return false; }
|
||||
|
||||
virtual CMapClass *Copy(bool bUpdateDependencies);
|
||||
virtual CMapClass *CopyFrom(CMapClass *pFrom, bool bUpdateDependencies);
|
||||
|
||||
virtual bool HitTest2D(CMapView2D *pView, const Vector2D &point, HitInfo_t &HitData);
|
||||
virtual bool HitTestLogical(CMapViewLogical *pView, const Vector2D &point, HitInfo_t &HitData);
|
||||
|
||||
// Objects that can be clicked on and activated as tools implement this and return a CBaseTool-derived object.
|
||||
virtual CBaseTool *GetToolObject(int nHitData, bool bAttachObject) { return NULL; }
|
||||
|
||||
//
|
||||
// Can be serialized:
|
||||
//
|
||||
virtual ChunkFileResult_t SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo);
|
||||
virtual ChunkFileResult_t SaveEditorData(CChunkFile *pFile);
|
||||
|
||||
virtual bool ShouldSerialize(void) { return true; }
|
||||
virtual int SerializeRMF(std::fstream &File, BOOL bRMF);
|
||||
virtual int SerializeMAP(std::fstream &File, BOOL bRMF);
|
||||
virtual void PostloadWorld(CMapWorld *pWorld);
|
||||
virtual void PresaveWorld(void) {}
|
||||
bool PostloadVisGroups( bool bIsLoading );
|
||||
|
||||
virtual bool IsGroup(void) { return false; }
|
||||
virtual bool IsScaleable(void) { return false; }
|
||||
virtual bool IsClutter(void) { return false; } // Whether this object should be hidden when the user hides helpers.
|
||||
virtual bool IsCulledByCordon(const Vector &vecMins, const Vector &vecMaxs); // Whether this object is hidden based on its own intersection with the cordon, independent of its parent's intersection.
|
||||
virtual bool IsEditable( void );
|
||||
virtual bool ShouldSnapToHalfGrid() { return false; }
|
||||
virtual bool IsSolid( ) { return false; }
|
||||
|
||||
// searching
|
||||
virtual CMapEntity *FindChildByKeyValue( const char* key, const char* value, bool *bIsInInstance = NULL, VMatrix *InstanceMatrix = NULL );
|
||||
|
||||
// HACK: get the world that this object is contained within.
|
||||
static CMapWorld *GetWorldObject(CMapAtom *pStart);
|
||||
|
||||
virtual const char* GetDescription() { return ""; }
|
||||
|
||||
BOOL EnumChildren(ENUMMAPCHILDRENPROC pfn, unsigned int dwParam = 0, MAPCLASSTYPE Type = NULL);
|
||||
BOOL EnumChildrenRecurseGroupsOnly(ENUMMAPCHILDRENPROC pfn, unsigned int dwParam, MAPCLASSTYPE Type = NULL);
|
||||
BOOL IsChildOf(CMapAtom *pObject);
|
||||
|
||||
virtual bool ShouldAppearInLightingPreview(void)
|
||||
{
|
||||
return true; //false;
|
||||
}
|
||||
|
||||
virtual bool ShouldAppearInRaytracedLightingPreview(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool IsVisible(void) { return(m_bVisible); }
|
||||
void SetVisible(bool bVisible);
|
||||
|
||||
inline bool IsVisGroupShown(void) { return m_bVisGroupShown && m_bVisGroupAutoShown; }
|
||||
void VisGroupShow(bool bShow, VisGroupSelection eVisGroup = USER);
|
||||
bool CheckVisibility(bool bIsLoading = false);
|
||||
|
||||
//
|
||||
// Visible2D functions are used only for hiding solids being morphed. Remove?
|
||||
//
|
||||
bool IsVisible2D(void) { return m_bVisible2D; }
|
||||
void SetVisible2D(bool bVisible2D) { m_bVisible2D = bVisible2D; }
|
||||
|
||||
// Is this class potentially visible in 2D visio view?
|
||||
virtual bool IsLogical(void) { return false; }
|
||||
|
||||
// Is this class actually visible in 2D visio view?
|
||||
virtual bool IsVisibleLogical(void) { return false; }
|
||||
|
||||
//
|
||||
// Overridden to set the render color of each of our children.
|
||||
//
|
||||
virtual void SetRenderColor(unsigned char red, unsigned char green, unsigned char blue);
|
||||
virtual void SetRenderColor(color32 rgbColor);
|
||||
|
||||
//
|
||||
// Can be rendered:
|
||||
//
|
||||
virtual void Render2D(CRender2D *pRender);
|
||||
virtual void Render3D(CRender3D *pRender);
|
||||
virtual void RenderLogical( CRender2D *pRender ) {}
|
||||
virtual bool RenderPreload(CRender3D *pRender, bool bNewContext);
|
||||
inline int GetRenderFrame(void) { return(m_nRenderFrame); }
|
||||
inline void SetRenderFrame(int nRenderFrame) { m_nRenderFrame = nRenderFrame; }
|
||||
|
||||
SelectionState_t SetSelectionState(SelectionState_t eSelectionState);
|
||||
|
||||
//
|
||||
// Has a set of editor-specific properties that are loaded from the VMF file.
|
||||
// The keys are freed after being handled by the map post-load code.
|
||||
//
|
||||
int GetEditorKeyCount(void);
|
||||
const char *GetEditorKey(int nIndex);
|
||||
const char *GetEditorKeyValue(int nIndex);
|
||||
const char *GetEditorKeyValue(const char *szKey);
|
||||
void RemoveEditorKeys(void);
|
||||
void SetEditorKeyValue(const char *szKey, const char *szValue);
|
||||
|
||||
virtual void InstanceMoved( void );
|
||||
|
||||
public:
|
||||
|
||||
// Set to true while loading a VMF file so it can delay certain calls like UpdateBounds.
|
||||
// Drastically speeds up load times.
|
||||
static bool s_bLoadingVMF;
|
||||
|
||||
protected:
|
||||
|
||||
//
|
||||
// Implements CMapAtom transformation interface:
|
||||
//
|
||||
virtual void DoTransform(const VMatrix &matrix);
|
||||
|
||||
//
|
||||
// Serialization callbacks.
|
||||
//
|
||||
static ChunkFileResult_t LoadEditorCallback(CChunkFile *pFile, CMapClass *pObject);
|
||||
static ChunkFileResult_t LoadEditorKeyCallback(const char *szKey, const char *szValue, CMapClass *pObject);
|
||||
|
||||
//
|
||||
// Has a list of objects that must be notified if it changes size or position.
|
||||
//
|
||||
void AddDependent(CMapClass *pDependent);
|
||||
void NotifyDependents(Notify_Dependent_t eNotifyType);
|
||||
void RemoveDependent(CMapClass *pDependent);
|
||||
virtual void UpdateDependencies(CMapWorld *pWorld, CMapClass *pObject) {};
|
||||
CMapClass *UpdateDependency(CMapClass *pOldAttached, CMapClass *pNewAttached);
|
||||
|
||||
void UpdateParent(CMapClass *pNewParent);
|
||||
|
||||
void SetBoxFromFaceList( CMapFaceList *pFaces, BoundBox &Box );
|
||||
|
||||
CSmartPtr< CSafeObject< CMapClass > > m_pSafeObject;
|
||||
|
||||
BoundBox m_CullBox; // Our bounds for culling in the 3D views and intersecting with the cordon.
|
||||
BoundBox m_BoundingBox; // Our bounds for brushes / entities themselves. This size may be smaller than m_CullBox ( i.e. spheres are not included )
|
||||
BoundBox m_Render2DBox; // Our bounds for rendering in the 2D views.
|
||||
|
||||
CMapObjectList m_Children; // Each object can have many children. Children usually transform with their parents, etc.
|
||||
CMapObjectList m_Dependents; // Objects that this object should notify if it changes.
|
||||
|
||||
int m_nID; // This object's unique ID.
|
||||
bool m_bTemporary; // Whether to track this object for Undo/Redo.
|
||||
int m_nRenderFrame; // Frame counter used to avoid rendering the same object twice in a 3D frame.
|
||||
|
||||
bool m_bVisible2D; // Whether this object is visible in the 2D view. Currently only used for morphing.
|
||||
bool m_bVisible; // Whether this object is currently visible in the 2D and 3D views based on ALL factors: visgroups, cordon, etc.
|
||||
|
||||
bool m_bVisGroupShown; // Whether this object is shown or hidden by user visgroups. Kept separate from m_bVisible so we can
|
||||
// reflect this state in the visgroups list independent of the cordon, hide entities state, etc.
|
||||
|
||||
bool m_bVisGroupAutoShown; // Whether this object is shown or hidden by auto visgroups.
|
||||
|
||||
CVisGroupList m_VisGroups; // Visgroups to which this object belongs, EMPTY if none.
|
||||
CVisGroup *m_pColorVisGroup; // The visgroup from which we get our color, NULL if none.
|
||||
|
||||
WCKeyValuesT<WCKVBase_Vector> *m_pEditorKeys; // Temporary storage for keys loaded from the "editor" chunk of the VMF file, freed after loading.
|
||||
|
||||
friend class CTrackEntry; // Friends with Undo/Redo system so that parentage can be changed.
|
||||
friend void FixHiddenObject(MapError *pError); // So that the Check for Problems dialog can fix visgroups problems.
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Returns this object's unique ID.
|
||||
//-----------------------------------------------------------------------------
|
||||
int CMapClass::GetID(void)
|
||||
{
|
||||
return(m_nID);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Sets this object's unique ID.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CMapClass::SetID(int nID)
|
||||
{
|
||||
m_nID = nID;
|
||||
}
|
||||
|
||||
class CMapClassManager
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~CMapClassManager();
|
||||
CMapClassManager(MAPCLASSTYPE Type, CMapClass * (*pfnNew)());
|
||||
|
||||
static CMapClass * CreateObject(MAPCLASSTYPE Type);
|
||||
};
|
||||
|
||||
|
||||
#define MAPCLASS_TYPE(class_name) \
|
||||
(class_name::__Type)
|
||||
|
||||
|
||||
#define IMPLEMENT_MAPCLASS(class_name) \
|
||||
char * class_name::__Type = #class_name; \
|
||||
MAPCLASSTYPE class_name::GetType() { return __Type; } \
|
||||
BOOL class_name::IsMapClass(MAPCLASSTYPE Type) \
|
||||
{ return (Type == __Type) ? TRUE : FALSE; } \
|
||||
CMapClass * class_name##_CreateObject() \
|
||||
{ return new class_name; } \
|
||||
CMapClassManager mcm_##class_name(class_name::__Type, \
|
||||
class_name##_CreateObject);
|
||||
|
||||
|
||||
#define DECLARE_MAPCLASS(class_name,class_base) \
|
||||
typedef class_base BaseClass; \
|
||||
static char * __Type; \
|
||||
virtual MAPCLASSTYPE GetType(); \
|
||||
virtual BOOL IsMapClass(MAPCLASSTYPE Type);
|
||||
|
||||
|
||||
class CCheckFaceInfo
|
||||
{
|
||||
public:
|
||||
|
||||
CCheckFaceInfo() { iPoint = -1; }
|
||||
char szDescription[128];
|
||||
int iPoint;
|
||||
};
|
||||
|
||||
|
||||
#endif // MAPCLASS_H
|
||||
Reference in New Issue
Block a user