New collision detection
This commit is contained in:
parent
258ce51cfe
commit
d2cf54556e
@ -1,4 +1,5 @@
|
||||
#include "sdlpp_circlecolider.hpp"
|
||||
#include "sdlpp_geometry.hpp"
|
||||
|
||||
namespace SDLPP {
|
||||
CircleColider::CircleColider( double x, double y, double rad )
|
||||
@ -9,39 +10,25 @@ CircleColider::CircleColider( const Vec2D< double > ¢er, double rad )
|
||||
|
||||
bool CircleColider::colidesWith( const SDLPP::CollisionPolygon &other ) const {
|
||||
if ( other.isCircle() ) {
|
||||
int otherRad = ( other.rightmost() - other.leftmost() ) / 2;
|
||||
/* check if distance of centers is lower than radiuses added together */
|
||||
int otherRad =
|
||||
dynamic_cast< const SDLPP::CircleColider & >( other ).getRadius();
|
||||
int thisRad = getRadius();
|
||||
int totalDist = otherRad + thisRad;
|
||||
int xdiff = other.leftmost() + otherRad - ( leftmost() + thisRad );
|
||||
int ydiff = other.topmost() + otherRad - ( topmost() + thisRad );
|
||||
int xdiff = other.getX() - getX();
|
||||
int ydiff = other.getY() - getY();
|
||||
return ( xdiff * xdiff + ydiff * ydiff ) <= totalDist * totalDist;
|
||||
} else if ( other.isInfinite() ) {
|
||||
return infinityIntersection( other, *this );
|
||||
}
|
||||
int rad = rad_;
|
||||
int centerx = getX();
|
||||
int centery = getY();
|
||||
if ( other.topmost() <= centery && other.bottommost() >= centery ) {
|
||||
return other.leftmost() <= rightmost() &&
|
||||
other.rightmost() >= leftmost();
|
||||
} else if ( other.leftmost() <= centerx && other.rightmost() >= centerx ) {
|
||||
return other.topmost() <= bottommost() &&
|
||||
other.bottommost() >= topmost();
|
||||
/* other is not a circle */
|
||||
int rad = getRadius();
|
||||
for ( auto &line : other.getLines() ) {
|
||||
auto distance = pointLineDistance( position, line );
|
||||
if ( distance * distance <= rad * rad )
|
||||
return true;
|
||||
}
|
||||
int pointx = 0, pointy = 0;
|
||||
if ( centerx > other.rightmost() ) {
|
||||
pointx = other.rightmost();
|
||||
} else {
|
||||
pointx = other.leftmost();
|
||||
}
|
||||
if ( centery < other.topmost() ) {
|
||||
pointy = other.topmost();
|
||||
} else {
|
||||
pointy = other.bottommost();
|
||||
}
|
||||
int distancesquared = ( pointx - centerx ) * ( pointx - centerx ) +
|
||||
( pointy - centery ) * ( pointy - centery );
|
||||
return distancesquared <= rad * rad;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CircleColider::isCircle() const {
|
||||
@ -65,8 +52,8 @@ void CircleColider::updateCollision( int x, int y, int w, int h ) {
|
||||
rad_ = original_rad * w;
|
||||
}
|
||||
|
||||
void CircleColider::render( Renderer &renderer,
|
||||
const std::tuple< int, int, int, int > &color ) {
|
||||
void CircleColider::render( Renderer &renderer, const SDL_Color &color,
|
||||
const SDL_Color &outline_color ) {
|
||||
std::vector< int > rect = { leftmost(), topmost(), rightmost(),
|
||||
bottommost() };
|
||||
auto center_x = getX();
|
||||
@ -76,14 +63,13 @@ void CircleColider::render( Renderer &renderer,
|
||||
auto xdiff = center_x - i;
|
||||
auto xdist = xdiff * xdiff;
|
||||
auto allowed_rad = sqrt( radsq - xdist );
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(),
|
||||
std::get< 0 >( color ), std::get< 1 >( color ),
|
||||
std::get< 2 >( color ), 0x40 );
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(), color.r, color.g,
|
||||
color.b, color.a );
|
||||
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
|
||||
center_y - allowed_rad, i, center_y + allowed_rad );
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(),
|
||||
std::get< 0 >( color ), std::get< 1 >( color ),
|
||||
std::get< 2 >( color ), 0x80 );
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(), outline_color.r,
|
||||
outline_color.g, outline_color.b,
|
||||
outline_color.a );
|
||||
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
|
||||
center_y - allowed_rad, i,
|
||||
center_y - allowed_rad + 2 );
|
||||
@ -91,39 +77,18 @@ void CircleColider::render( Renderer &renderer,
|
||||
center_y + allowed_rad, i,
|
||||
center_y + allowed_rad - 2 );
|
||||
}
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(), 0xFF, 0, 0, 0xFF );
|
||||
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y,
|
||||
center_x + rad_, center_y );
|
||||
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y, center_x,
|
||||
center_y + rad_ );
|
||||
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y,
|
||||
center_x - rad_, center_y );
|
||||
SDL_RenderDrawLine( renderer.getRendererPtr(), center_x, center_y, center_x,
|
||||
center_y - rad_ );
|
||||
}
|
||||
|
||||
void CircleColider::render( Renderer &renderer, const SDL_Color &color ) {
|
||||
auto input_color = color;
|
||||
auto outline_color = color;
|
||||
input_color.a = 0x40;
|
||||
outline_color.a = 0x80;
|
||||
render( renderer, input_color, outline_color );
|
||||
}
|
||||
|
||||
void CircleColider::render( Renderer &renderer ) {
|
||||
std::vector< int > rect = { leftmost(), topmost(), rightmost(),
|
||||
bottommost() };
|
||||
auto center_x = getX();
|
||||
auto center_y = getY();
|
||||
auto radsq = rad_ * rad_;
|
||||
for ( int i = rect[0]; i <= rect[2]; i++ ) {
|
||||
auto xdiff = center_x - i;
|
||||
auto xdist = xdiff * xdiff;
|
||||
auto allowed_rad = sqrt( radsq - xdist );
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_color.r,
|
||||
sdl_color.g, sdl_color.b, sdl_color.a );
|
||||
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
|
||||
center_y - allowed_rad, i, center_y + allowed_rad );
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_outline.r,
|
||||
sdl_outline.g, sdl_outline.b, sdl_outline.a );
|
||||
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
|
||||
center_y - allowed_rad, i,
|
||||
center_y - allowed_rad + 2 );
|
||||
SDL_RenderDrawLine( renderer.getRendererPtr(), i,
|
||||
center_y + allowed_rad, i,
|
||||
center_y + allowed_rad - 2 );
|
||||
}
|
||||
render( renderer, sdl_color, sdl_outline );
|
||||
}
|
||||
|
||||
int CircleColider::getRadius() const {
|
||||
@ -133,4 +98,8 @@ int CircleColider::getRadius() const {
|
||||
std::shared_ptr< CollisionPolygon > CircleColider::copySelf() {
|
||||
return std::make_shared< CircleColider >( *this );
|
||||
}
|
||||
|
||||
std::vector< Line< int > > CircleColider::getLines() const {
|
||||
return {};
|
||||
}
|
||||
} // namespace SDLPP
|
||||
|
@ -21,11 +21,12 @@ public:
|
||||
virtual int rightmost() const override;
|
||||
|
||||
virtual void updateCollision( int x, int y, int w, int h ) override;
|
||||
virtual void
|
||||
render( Renderer &renderer,
|
||||
const std::tuple< int, int, int, int > &color ) override;
|
||||
virtual void render( Renderer &renderer, const SDL_Color &color,
|
||||
const SDL_Color &outline_color ) override;
|
||||
virtual void render( Renderer &renderer, const SDL_Color &color ) override;
|
||||
virtual void render( Renderer &renderer ) override;
|
||||
virtual std::shared_ptr<CollisionPolygon> copySelf() override;
|
||||
virtual std::vector<Line<int>> getLines() const override;
|
||||
|
||||
private:
|
||||
int getRadius() const;
|
||||
|
@ -28,7 +28,7 @@ CircleRender::CircleRender( Vec2D< double > center, double rad,
|
||||
|
||||
CircleRender::CircleRender( Vec2D< double > center, double rad,
|
||||
std::shared_ptr< Renderer > &r,
|
||||
std::shared_ptr< Texture > &t )
|
||||
std::shared_ptr< Texture > &/*UNUSED*/ )
|
||||
: CircleRender( center, rad, r ) {
|
||||
throw "I don't support textures yet!!!";
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "sdlpp_collision.hpp"
|
||||
#include "sdlpp_geometry.hpp"
|
||||
|
||||
namespace SDLPP {
|
||||
CollisionPolygon::CollisionPolygon( double x, double y )
|
||||
@ -53,8 +54,14 @@ bool infinityIntersection( const SDLPP::CollisionPolygon &infinite,
|
||||
|
||||
bool intersects( const SDLPP::CollisionPolygon &p1,
|
||||
const SDLPP::CollisionPolygon &p2 ) {
|
||||
return !(
|
||||
p1.rightmost() < p2.leftmost() || p2.rightmost() < p1.leftmost() ||
|
||||
p1.topmost() > p2.bottommost() || p2.topmost() > p1.bottommost() );
|
||||
if(p1.rightmost() < p2.leftmost() || p2.rightmost() < p1.leftmost() || p1.bottommost() < p2.topmost() || p2.bottommost() < p1.topmost())
|
||||
return false;
|
||||
for(auto &line : p1.getLines()) {
|
||||
for(auto &line2 : p2.getLines()) {
|
||||
if(linesCross(line, line2))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace SDLPP
|
||||
|
@ -4,7 +4,9 @@
|
||||
#include "sdlpp_common.hpp"
|
||||
#include "sdlpp_renderer.hpp"
|
||||
#include "sdlpp_vector.hpp"
|
||||
#include "sdlpp_line.hpp"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace SDLPP {
|
||||
class SDLPPSCOPE CollisionPolygon {
|
||||
@ -21,14 +23,16 @@ public:
|
||||
virtual int leftmost() const = 0;
|
||||
virtual int rightmost() const = 0;
|
||||
virtual void updateCollision( int x, int y, int w, int h );
|
||||
virtual void render( Renderer &renderer,
|
||||
const std::tuple< int, int, int, int > &color ) = 0;
|
||||
virtual void render( Renderer &renderer, const SDL_Color &color,
|
||||
const SDL_Color &outline_color ) = 0;
|
||||
virtual void render( Renderer &renderer, const SDL_Color &color ) = 0;
|
||||
virtual void render( Renderer &renderer ) = 0;
|
||||
int getX() const;
|
||||
int getY() const;
|
||||
void setColor( const std::string &color );
|
||||
void setOutlineColor( const std::string &color );
|
||||
virtual std::shared_ptr< CollisionPolygon > copySelf() = 0;
|
||||
virtual std::vector< Line< int > > getLines() const = 0;
|
||||
|
||||
protected:
|
||||
Vec2D< double > original;
|
||||
|
@ -5,10 +5,12 @@
|
||||
#include "sdlpp_vector.hpp"
|
||||
#include "sdlpp_line.hpp"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
namespace SDLPP {
|
||||
template < typename T >
|
||||
Vec2D<T> pointProjectionOnLine( const Vec2D<T> &point, const Line<T> &line ) {
|
||||
Vec2D< T > pointProjectionOnLine( const Vec2D< T > &point,
|
||||
const Line< T > &line ) {
|
||||
/* from here -
|
||||
* https://stackoverflow.com/questions/849211/shortest-distance-between-a-point-and-a-line-segment
|
||||
*/
|
||||
@ -25,6 +27,45 @@ template<typename T>
|
||||
double pointLineDistance( const Vec2D< T > &point, const Line< T > &line ) {
|
||||
return vecDistance( point, pointProjectionOnLine( point, line ) );
|
||||
}
|
||||
template < typename T >
|
||||
int _determinant( const Line< T > &line, const Vec2D< T > &point ) {
|
||||
auto res = ( line.getEnd().getX() - line.getStart().getX() ) *
|
||||
( point.getY() - line.getStart().getY() ) -
|
||||
( line.getEnd().getY() - line.getStart().getY() ) *
|
||||
( point.getX() - line.getStart().getX() );
|
||||
if ( res < 0 )
|
||||
return -1;
|
||||
if ( res > 0 )
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
template < typename T >
|
||||
bool linesCross( const Line< T > &a, const Line< T > &b ) {
|
||||
auto det_a_s = _determinant( a, b.getStart() );
|
||||
auto det_a_e = _determinant( a, b.getEnd() );
|
||||
if ( det_a_s == det_a_e && det_a_s != 0 )
|
||||
return false;
|
||||
|
||||
// det_a_s == det_a_e == 0, means that lines have the same vector,
|
||||
// we need to find out if they overlap
|
||||
if ( det_a_s == det_a_e ) {
|
||||
if ( a.getStart().getX() == a.getEnd().getX() ) {
|
||||
// vertical lines
|
||||
return a.bottomost().getY() > b.topmost().getY() &&
|
||||
a.topmost().getY() < b.bottomost().getY();
|
||||
} else {
|
||||
// horizontal lines
|
||||
return a.leftmost().getX() < b.rightmost().getX() &&
|
||||
a.rightmost().getX() > b.leftmost().getX();
|
||||
}
|
||||
}
|
||||
|
||||
auto det_b_s = _determinant( b, a.getStart() );
|
||||
auto det_b_e = _determinant( b, a.getEnd() );
|
||||
if ( det_b_s == det_b_e )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
} // namespace SDLPP
|
||||
|
||||
#endif
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "sdlpp_common.hpp"
|
||||
#include "sdlpp_vector.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace SDLPP {
|
||||
template<typename T>
|
||||
@ -11,14 +12,38 @@ public:
|
||||
Line() = delete;
|
||||
~Line() = default;
|
||||
Line( const Vec2D<T> &start, const Vec2D<T> &end )
|
||||
: _start( start ), _end( end ) {}
|
||||
: _start( start ), _end( end ) {
|
||||
updateMost();
|
||||
}
|
||||
Line( T x_1, T y_1, T x_2, T y_2 )
|
||||
: _start( { x_1, y_1 } ), _end( { x_2, y_2 } ) {}
|
||||
: Line( { x_1, y_1 }, { x_2, y_2 } ) {}
|
||||
Line( const Vec2D<T> &start, const Vec2D<T> &end, bool infinite )
|
||||
: _start( start ), _end( end ), _infinite( infinite ) {}
|
||||
: Line( start, end ), _infinite( infinite ) {}
|
||||
Line( T x_1, T y_1, T x_2, T y_2, bool infinite )
|
||||
: _start( { x_1, y_1 } ), _end( { x_2, y_2 } ),
|
||||
_infinite( infinite ) {}
|
||||
: Line( { x_1, y_1 }, { x_2, y_2 }, infinite ) {}
|
||||
Line(const Line &input) : Line(input.getStart(), input.getEnd()) {}
|
||||
Line &operator=(const Line &input) {
|
||||
_start = input.getStart();
|
||||
_end = input.getEnd();
|
||||
updateMost();
|
||||
return *this;
|
||||
}
|
||||
void updateMost() {
|
||||
if(_start.getY() < _end.getY()) {
|
||||
top = &_start;
|
||||
bottom = &_end;
|
||||
} else {
|
||||
top = &_end;
|
||||
bottom = &_start;
|
||||
}
|
||||
if(_start.getX() < _end.getX()) {
|
||||
left = &_start;
|
||||
right = &_end;
|
||||
} else {
|
||||
left = &_end;
|
||||
right = &_start;
|
||||
}
|
||||
}
|
||||
const Vec2D<T> &getStart() const {
|
||||
return _start;
|
||||
}
|
||||
@ -41,10 +66,26 @@ public:
|
||||
_start += vec;
|
||||
_end += vec;
|
||||
}
|
||||
const Vec2D<T> &topmost() const {
|
||||
return *top;
|
||||
}
|
||||
const Vec2D<T> &bottomost() const {
|
||||
return *bottom;
|
||||
}
|
||||
const Vec2D<T> &leftmost() const {
|
||||
return *left;
|
||||
}
|
||||
const Vec2D<T> &rightmost() const {
|
||||
return *right;
|
||||
}
|
||||
|
||||
private:
|
||||
Vec2D<T> _start;
|
||||
Vec2D<T> _end;
|
||||
Vec2D<T> *top = nullptr;
|
||||
Vec2D<T> *bottom = nullptr;
|
||||
Vec2D<T> *left = nullptr;
|
||||
Vec2D<T> *right = nullptr;
|
||||
bool _infinite = false;
|
||||
};
|
||||
} // namespace SDLPP
|
||||
|
@ -62,29 +62,27 @@ void RectColider::updateCollision( int x, int y, int w, int h ) {
|
||||
_size_pixel = Vec2D< int >( width() * w, height() * h );
|
||||
}
|
||||
|
||||
void RectColider::render( Renderer &renderer,
|
||||
const std::tuple< int, int, int, int > &color ) {
|
||||
void RectColider::render( Renderer &renderer, const SDL_Color &color,
|
||||
const SDL_Color &outline_color ) {
|
||||
auto rect = getRect();
|
||||
// outline with desired color at 50% opacity
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(), std::get< 0 >( color ),
|
||||
std::get< 1 >( color ), std::get< 2 >( color ),
|
||||
0x80 );
|
||||
SDL_RenderDrawRect( renderer.getRendererPtr(), &rect );
|
||||
// fill with desired color at 25% opacity
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(), std::get< 0 >( color ),
|
||||
std::get< 1 >( color ), std::get< 2 >( color ),
|
||||
0x40 );
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(), color.r, color.g,
|
||||
color.b, color.a );
|
||||
SDL_RenderFillRect( renderer.getRendererPtr(), &rect );
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(), outline_color.r,
|
||||
outline_color.g, outline_color.b, outline_color.a );
|
||||
SDL_RenderDrawRect( renderer.getRendererPtr(), &rect );
|
||||
}
|
||||
|
||||
void RectColider::render( Renderer &renderer, const SDL_Color &color ) {
|
||||
auto input_color = color;
|
||||
auto outline_color = color;
|
||||
input_color.a = 0x40;
|
||||
outline_color.a = 0x80;
|
||||
render( renderer, input_color, outline_color );
|
||||
}
|
||||
|
||||
void RectColider::render( Renderer &renderer ) {
|
||||
auto rect = getRect();
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_color.r, sdl_color.g,
|
||||
sdl_color.b, sdl_color.a );
|
||||
SDL_RenderFillRect( renderer.getRendererPtr(), &rect );
|
||||
SDL_SetRenderDrawColor( renderer.getRendererPtr(), sdl_outline.r,
|
||||
sdl_outline.g, sdl_outline.b, sdl_outline.a );
|
||||
SDL_RenderDrawRect( renderer.getRendererPtr(), &rect );
|
||||
render( renderer, sdl_color, sdl_outline );
|
||||
}
|
||||
|
||||
SDL_Rect RectColider::getRect() {
|
||||
@ -111,14 +109,14 @@ std::shared_ptr< CollisionPolygon > RectColider::copySelf() {
|
||||
return std::make_shared< RectColider >( *this );
|
||||
}
|
||||
|
||||
std::vector< Line< double > > RectColider::getLines() {
|
||||
std::vector< Line< double > > ret{};
|
||||
ret.emplace_back( original, original + Vec2D< double >( width(), 0 ) );
|
||||
ret.emplace_back( original + Vec2D< double >( width(), 0 ),
|
||||
original + _size );
|
||||
ret.emplace_back( original, original + Vec2D< double >( 0, height() ) );
|
||||
ret.emplace_back( original + Vec2D< double >( 0, height() ),
|
||||
original + _size );
|
||||
std::vector< Line< int > > RectColider::getLines() const {
|
||||
std::vector< Line< int > > ret{};
|
||||
ret.emplace_back( position, position + Vec2D< int >( pixel_width(), 0 ) );
|
||||
ret.emplace_back( position + Vec2D< int >( pixel_width(), 0 ),
|
||||
position + _size_pixel );
|
||||
ret.emplace_back( position, position + Vec2D< int >( 0, pixel_height() ) );
|
||||
ret.emplace_back( position + Vec2D< int >( 0, pixel_height() ),
|
||||
position + _size_pixel );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -22,12 +22,12 @@ public:
|
||||
virtual int leftmost() const override;
|
||||
virtual int rightmost() const override;
|
||||
virtual void updateCollision( int x, int y, int w, int h ) override;
|
||||
virtual void
|
||||
render( Renderer &renderer,
|
||||
const std::tuple< int, int, int, int > &color ) override;
|
||||
virtual void render( Renderer &renderer, const SDL_Color &color,
|
||||
const SDL_Color &outline_color ) override;
|
||||
virtual void render( Renderer &renderer, const SDL_Color &color ) override;
|
||||
virtual void render( Renderer &renderer ) override;
|
||||
virtual std::shared_ptr< CollisionPolygon > copySelf() override;
|
||||
virtual std::vector<Line<double>> getLines();
|
||||
virtual std::vector< Line< int > > getLines() const override;
|
||||
|
||||
private:
|
||||
SDL_Rect getRect();
|
||||
|
@ -137,7 +137,7 @@ bool RenderObject::getKilled() {
|
||||
return kill;
|
||||
}
|
||||
void RenderObject::setColiderColor( const std::string &color ) {
|
||||
colider_color = getColorsHEX( color );
|
||||
colider_color = getSDLColorHEX( color );
|
||||
}
|
||||
|
||||
void RenderObject::animate( int ticks ) {
|
||||
|
@ -114,7 +114,7 @@ protected:
|
||||
uint64_t id = -1;
|
||||
bool hidden = false;
|
||||
bool kill = false;
|
||||
std::tuple< int, int, int, int > colider_color = { 0x00, 0xFF, 0xFF, 0xFF };
|
||||
SDL_Color colider_color = { 0x00, 0xFF, 0xFF, 0xFF };
|
||||
uint64_t scene_id = -1;
|
||||
bool permanent = false;
|
||||
bool is_static = true;
|
||||
|
@ -10,7 +10,7 @@ OUTPUTFLAG = -Fo
|
||||
else
|
||||
UNAME_S := $(shell uname -s)
|
||||
CXX ?= g++
|
||||
CXXFLAGS = -std=c++14 -Wall -Wextra -pedantic -O2 -DDEBUG -DFEATURE -g -fsanitize=address
|
||||
CXXFLAGS = -std=c++14 -Wall -Wextra -pedantic -O2 -DDEBUG -DFEATURE #-g -fsanitize=address
|
||||
OBJEXT = o
|
||||
LDFLAGS ?= -lSDL2 -lSDL2_image -lSDL2_gfx -lSDL2_ttf -pthread
|
||||
OUTPUTFLAG = -o
|
||||
|
Loading…
Reference in New Issue
Block a user