Y Sorting
This commit is contained in:
11
.vscode/settings.json
vendored
11
.vscode/settings.json
vendored
@ -10,6 +10,15 @@
|
|||||||
"type_traits": "cpp",
|
"type_traits": "cpp",
|
||||||
"xmemory": "cpp",
|
"xmemory": "cpp",
|
||||||
"xtr1common": "cpp",
|
"xtr1common": "cpp",
|
||||||
"chrono": "cpp"
|
"chrono": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"list": "cpp",
|
||||||
|
"vector": "cpp",
|
||||||
|
"xhash": "cpp",
|
||||||
|
"xtree": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"span": "cpp",
|
||||||
|
"xstring": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,24 +1,58 @@
|
|||||||
#include <Game.hpp>
|
#include <Game.hpp>
|
||||||
|
|
||||||
|
#include <IAEngine/Input.hpp>
|
||||||
#include <IAEngine/Rendering/Camera.hpp>
|
#include <IAEngine/Rendering/Camera.hpp>
|
||||||
|
#include <IAEngine/Components/SpriteRenderer.hpp>
|
||||||
|
|
||||||
#include <IACore/File.hpp>
|
#include <IACore/File.hpp>
|
||||||
|
|
||||||
namespace ia::iae::game
|
namespace ia::iae::game
|
||||||
{
|
{
|
||||||
RefPtr<iae::Scene> scene;
|
RefPtr<Scene> scene;
|
||||||
|
|
||||||
Texture g_tex;
|
RefPtr<Node> g_player;
|
||||||
Texture g_tex2;
|
|
||||||
|
|
||||||
VOID Game::Initialize()
|
VOID Game::Initialize()
|
||||||
{
|
{
|
||||||
scene = Engine::CreateScene();
|
scene = Engine::CreateScene();
|
||||||
|
scene->YSortingEnabled() = true;
|
||||||
Engine::ChangeScene(scene);
|
Engine::ChangeScene(scene);
|
||||||
|
|
||||||
const auto d = File::ReadToVector("Graphics/1.jpg");
|
g_player = MakeRefPtr<Node>();
|
||||||
g_tex = Engine::CreateTexture(d.data(), d.size());
|
{
|
||||||
g_tex2 = Engine::CreateTexture(d.data(), d.size());
|
const auto t = g_player->AddComponent<SpriteRendererComponent>();
|
||||||
|
t->AddAnimation({
|
||||||
|
.ShouldLoop = true,
|
||||||
|
.Keys = {
|
||||||
|
SpriteRendererComponent::AnimationKeyFrame {
|
||||||
|
.Scale = {0.1f, 0.1f, 0.1f},
|
||||||
|
.Texture = Engine::CreateTexture(File::ReadToVector("Graphics/green.png")),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
t->BakeAnimations();
|
||||||
|
}
|
||||||
|
g_player->SetLocalPosition({200, 200, 0});
|
||||||
|
|
||||||
|
const auto obstacle = MakeRefPtr<Node>();
|
||||||
|
{
|
||||||
|
const auto t = obstacle->AddComponent<SpriteRendererComponent>();
|
||||||
|
t->AddAnimation({
|
||||||
|
.ShouldLoop = true,
|
||||||
|
.Keys = {
|
||||||
|
SpriteRendererComponent::AnimationKeyFrame {
|
||||||
|
.Scale = {0.25f, 0.25f, 0.1f},
|
||||||
|
.Texture = Engine::CreateTexture(File::ReadToVector("Graphics/red.png")),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
t->BakeAnimations();
|
||||||
|
}
|
||||||
|
obstacle->SortOffset() = 20;
|
||||||
|
obstacle->SetLocalPosition({150, 100, 0});
|
||||||
|
|
||||||
|
scene->AddNode(g_player);
|
||||||
|
scene->AddNode(obstacle);
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID Game::Terminate()
|
VOID Game::Terminate()
|
||||||
@ -27,8 +61,6 @@ namespace ia::iae::game
|
|||||||
|
|
||||||
VOID Game::Update()
|
VOID Game::Update()
|
||||||
{
|
{
|
||||||
g_tex.Draw({200.0f, 150.0f, 10.0f}, {1.0f, 1.0f, 1.0f}, 0.0f, false, false, {1.0f, 1.0f, 1.0f, 1.0f});
|
g_player->SetLocalPosition(g_player->GetLocalPosition() + glm::vec3{Input::GetDirectionalInput(), 0.0f});
|
||||||
g_tex2.Draw({300.0f, 150.0f, 15.0f}, {1.0f, 1.0f, 1.0f}, 0.0f, false, false, {1.0f, 0.0f, 1.0f, 1.0f});
|
|
||||||
//iae::Renderer::GetCamera()->Position().x += 0.1f;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ namespace ia::iae
|
|||||||
{
|
{
|
||||||
const auto t = m_tileGrid.m_tileTextures[x + (y * m_tileGrid.TileCountX)];
|
const auto t = m_tileGrid.m_tileTextures[x + (y * m_tileGrid.TileCountX)];
|
||||||
if (t != INVALID_HANDLE)
|
if (t != INVALID_HANDLE)
|
||||||
m_textures[t]->Draw(p + m_tileGrid.Position, {1.0f, 1.0f, 1.0f}, 0.0f, false, false, {1.0f, 1.0f, 1.0f, 1.0f});
|
m_textures[t]->Draw(m_node->SortOffset(), p + m_tileGrid.Position, {1.0f, 1.0f, 1.0f}, 0.0f, false, false, {1.0f, 1.0f, 1.0f, 1.0f});
|
||||||
p.x += m_tileGrid.TileWidth;
|
p.x += m_tileGrid.TileWidth;
|
||||||
}
|
}
|
||||||
p.x = m_node->GetPosition().x;
|
p.x = m_node->GetPosition().x;
|
||||||
|
|||||||
@ -66,8 +66,8 @@ namespace ia::iae
|
|||||||
VOID SpriteRendererComponent::Draw()
|
VOID SpriteRendererComponent::Draw()
|
||||||
{
|
{
|
||||||
const auto &animFrame = m_currentAnimationState;
|
const auto &animFrame = m_currentAnimationState;
|
||||||
if(!animFrame.Texture) return;
|
animFrame.Texture.Draw(
|
||||||
animFrame.Texture->Draw(
|
m_node->SortOffset(),
|
||||||
m_node->GetPosition() + animFrame.Position, m_node->GetScale() * animFrame.Scale,
|
m_node->GetPosition() + animFrame.Position, m_node->GetScale() * animFrame.Scale,
|
||||||
m_node->GetRotation().z + animFrame.Rotation.z, m_isFlippedH, m_isFlippedV, animFrame.ColorOverlay);
|
m_node->GetRotation().z + animFrame.Rotation.z, m_isFlippedH, m_isFlippedV, animFrame.ColorOverlay);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,7 @@ namespace ia::iae
|
|||||||
VOID TextureRendererComponent::Draw()
|
VOID TextureRendererComponent::Draw()
|
||||||
{
|
{
|
||||||
m_texture->Draw(
|
m_texture->Draw(
|
||||||
|
m_node->SortOffset(),
|
||||||
m_node->GetPosition() + m_position, m_node->GetScale(),
|
m_node->GetPosition() + m_position, m_node->GetScale(),
|
||||||
m_node->GetRotation().z, false, false, glm::vec4{1.0f, 1.0f, 1.0f, 1.0f});
|
m_node->GetRotation().z, false, false, glm::vec4{1.0f, 1.0f, 1.0f, 1.0f});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,6 +144,11 @@ namespace ia::iae
|
|||||||
{
|
{
|
||||||
g_activeScene = scene;
|
g_activeScene = scene;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Scene* Engine::GetActiveScene()
|
||||||
|
{
|
||||||
|
return g_activeScene.get();
|
||||||
|
}
|
||||||
} // namespace ia::iae
|
} // namespace ia::iae
|
||||||
|
|
||||||
namespace ia::iae
|
namespace ia::iae
|
||||||
|
|||||||
@ -37,8 +37,8 @@ namespace ia::iae
|
|||||||
g_quadMeshVertexBuffer.reset();
|
g_quadMeshVertexBuffer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID QuadMesh::Draw(IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation)
|
VOID QuadMesh::Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation)
|
||||||
{
|
{
|
||||||
Renderer::Draw(position, scale, rotation, g_quadMeshVertexBuffer->GetHandle(), 6);
|
Renderer::Draw(sortOffset, position, scale, rotation, g_quadMeshVertexBuffer->GetHandle(), 6);
|
||||||
}
|
}
|
||||||
} // namespace ia::iae
|
} // namespace ia::iae
|
||||||
@ -102,8 +102,8 @@ namespace ia::iae
|
|||||||
.format = SDL_GPU_TEXTUREFORMAT_D16_UNORM,
|
.format = SDL_GPU_TEXTUREFORMAT_D16_UNORM,
|
||||||
.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER |
|
.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER |
|
||||||
SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET,
|
SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET,
|
||||||
.width = (UINT32)s_width,
|
.width = (UINT32) s_width,
|
||||||
.height = (UINT32)s_height,
|
.height = (UINT32) s_height,
|
||||||
.layer_count_or_depth = 1,
|
.layer_count_or_depth = 1,
|
||||||
.num_levels = 1,
|
.num_levels = 1,
|
||||||
.sample_count = SDL_GPU_SAMPLECOUNT_1};
|
.sample_count = SDL_GPU_SAMPLECOUNT_1};
|
||||||
@ -234,27 +234,28 @@ namespace ia::iae
|
|||||||
SDL_PushGPUFragmentUniformData(g_cmdBuffer, 0, &textureState, sizeof(textureState));
|
SDL_PushGPUFragmentUniformData(g_cmdBuffer, 0, &textureState, sizeof(textureState));
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID SetModelTransformMatrix(IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation)
|
VOID SetModelTransformMatrix(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation)
|
||||||
{
|
{
|
||||||
matModel = glm::translate(glm::mat4(1.0f), position);
|
const auto depthTestOffset = sortOffset + (Engine::GetActiveScene()->YSortingEnabled() ? position.y : 0);
|
||||||
|
matModel = glm::translate(glm::mat4(1.0f), {position.x, position.y, position.z + depthTestOffset});
|
||||||
matModel = glm::rotate(matModel, rotation, glm::vec3(0.0f, 0.0f, 1.0f));
|
matModel = glm::rotate(matModel, rotation, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||||
matModel = glm::scale(matModel, scale);
|
matModel = glm::scale(matModel, scale);
|
||||||
SDL_PushGPUVertexUniformData(g_cmdBuffer, 2, &matModel, sizeof(matModel));
|
SDL_PushGPUVertexUniformData(g_cmdBuffer, 2, &matModel, sizeof(matModel));
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID Renderer::Draw(IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation,
|
VOID Renderer::Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation,
|
||||||
IN Handle vertexBufferHandle, IN INT32 vertexCount)
|
IN Handle vertexBufferHandle, IN INT32 vertexCount)
|
||||||
{
|
{
|
||||||
SetModelTransformMatrix(position, scale, rotation);
|
SetModelTransformMatrix(sortOffset, position, scale, rotation);
|
||||||
SDL_GPUBufferBinding bindings[] = {{.buffer = (SDL_GPUBuffer *) vertexBufferHandle, .offset = 0}};
|
SDL_GPUBufferBinding bindings[] = {{.buffer = (SDL_GPUBuffer *) vertexBufferHandle, .offset = 0}};
|
||||||
SDL_BindGPUVertexBuffers(g_renderPass, 0, bindings, 1);
|
SDL_BindGPUVertexBuffers(g_renderPass, 0, bindings, 1);
|
||||||
SDL_DrawGPUPrimitives(g_renderPass, vertexCount, 1, 0, 0);
|
SDL_DrawGPUPrimitives(g_renderPass, vertexCount, 1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID Renderer::Draw(IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation,
|
VOID Renderer::Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation,
|
||||||
IN Handle vertexBufferHandle, IN Handle indexBufferHandle, IN INT32 indexCount)
|
IN Handle vertexBufferHandle, IN Handle indexBufferHandle, IN INT32 indexCount)
|
||||||
{
|
{
|
||||||
SetModelTransformMatrix(position, scale, rotation);
|
SetModelTransformMatrix(sortOffset, position, scale, rotation);
|
||||||
SDL_GPUBufferBinding bindings[] = {{.buffer = (SDL_GPUBuffer *) vertexBufferHandle, .offset = 0},
|
SDL_GPUBufferBinding bindings[] = {{.buffer = (SDL_GPUBuffer *) vertexBufferHandle, .offset = 0},
|
||||||
{.buffer = (SDL_GPUBuffer *) indexBufferHandle, .offset = 0}};
|
{.buffer = (SDL_GPUBuffer *) indexBufferHandle, .offset = 0}};
|
||||||
SDL_BindGPUVertexBuffers(g_renderPass, 0, bindings, 1);
|
SDL_BindGPUVertexBuffers(g_renderPass, 0, bindings, 1);
|
||||||
|
|||||||
@ -35,10 +35,10 @@ namespace ia::iae
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID Texture::Draw(IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation, IN BOOL flipH,
|
VOID Texture::Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation, IN BOOL flipH,
|
||||||
IN BOOL flipV, IN CONST glm::vec4 &colorOverlay) CONST
|
IN BOOL flipV, IN CONST glm::vec4 &colorOverlay) CONST
|
||||||
{
|
{
|
||||||
Renderer::BindTexture(m_handle, flipV, flipH, colorOverlay);
|
Renderer::BindTexture(m_handle, flipV, flipH, colorOverlay);
|
||||||
QuadMesh::Draw(position, m_size * scale, rotation);
|
QuadMesh::Draw(sortOffset, position, m_size * scale, rotation);
|
||||||
}
|
}
|
||||||
} // namespace ia::iae
|
} // namespace ia::iae
|
||||||
@ -32,7 +32,7 @@ namespace ia::iae
|
|||||||
glm::vec3 Scale{1.0f, 1.0f, 1.0f};
|
glm::vec3 Scale{1.0f, 1.0f, 1.0f};
|
||||||
glm::vec4 ColorOverlay{1.0f, 1.0f, 1.0f, 1.0f};
|
glm::vec4 ColorOverlay{1.0f, 1.0f, 1.0f, 1.0f};
|
||||||
BOOL ShouldInterpolate{};
|
BOOL ShouldInterpolate{};
|
||||||
RefPtr<Texture> Texture;
|
Texture Texture;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Animation
|
struct Animation
|
||||||
|
|||||||
@ -48,6 +48,7 @@ namespace ia::iae
|
|||||||
STATIC BOOL ShouldClose();
|
STATIC BOOL ShouldClose();
|
||||||
|
|
||||||
STATIC VOID ChangeScene(IN RefPtr<Scene> scene);
|
STATIC VOID ChangeScene(IN RefPtr<Scene> scene);
|
||||||
|
STATIC Scene* GetActiveScene();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
STATIC RefPtr<Scene> CreateScene();
|
STATIC RefPtr<Scene> CreateScene();
|
||||||
|
|||||||
@ -27,7 +27,7 @@ namespace ia::iae
|
|||||||
class Node : public Transform<Node>
|
class Node : public Transform<Node>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VIRTUAL VOID OnAdded(IN Scene* scene);
|
VIRTUAL VOID OnAdded(IN Scene *scene);
|
||||||
VIRTUAL VOID OnRemoved();
|
VIRTUAL VOID OnRemoved();
|
||||||
|
|
||||||
VIRTUAL VOID Draw();
|
VIRTUAL VOID Draw();
|
||||||
@ -38,19 +38,24 @@ namespace ia::iae
|
|||||||
public:
|
public:
|
||||||
VOID AddChild(IN RefPtr<Node> node);
|
VOID AddChild(IN RefPtr<Node> node);
|
||||||
|
|
||||||
template<typename _component_type>
|
template<typename _component_type> _component_type *AddComponent();
|
||||||
_component_type* AddComponent();
|
|
||||||
|
|
||||||
template<typename _component_type>
|
template<typename _component_type> _component_type *GetComponent();
|
||||||
_component_type* GetComponent();
|
|
||||||
|
|
||||||
CONST Vector<RefPtr<IComponent>>& GetComponents() CONST
|
CONST Vector<RefPtr<IComponent>> &GetComponents() CONST
|
||||||
{
|
{
|
||||||
return m_components;
|
return m_components;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
FLOAT32 &SortOffset()
|
||||||
|
{
|
||||||
|
return m_sortOffset;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Scene* m_scene{};
|
Scene *m_scene{};
|
||||||
|
FLOAT32 m_sortOffset{};
|
||||||
BOOL m_isEnabled{true};
|
BOOL m_isEnabled{true};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -62,21 +67,19 @@ namespace ia::iae
|
|||||||
VOID AddComponent(IN RefPtr<IComponent> component);
|
VOID AddComponent(IN RefPtr<IComponent> component);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename _component_type>
|
template<typename _component_type> _component_type *Node::AddComponent()
|
||||||
_component_type* Node::AddComponent()
|
|
||||||
{
|
{
|
||||||
const auto c = MakeRefPtr<_component_type>(this);
|
const auto c = MakeRefPtr<_component_type>(this);
|
||||||
AddComponent(c);
|
AddComponent(c);
|
||||||
return c.get();
|
return c.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename _component_type>
|
template<typename _component_type> _component_type *Node::GetComponent()
|
||||||
_component_type* Node::GetComponent()
|
|
||||||
{
|
{
|
||||||
for(auto& c: m_components)
|
for (auto &c : m_components)
|
||||||
{
|
{
|
||||||
_component_type* comp = dynamic_cast<_component_type*>(c.get());
|
_component_type *comp = dynamic_cast<_component_type *>(c.get());
|
||||||
if(comp)
|
if (comp)
|
||||||
return comp;
|
return comp;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@ -26,6 +26,6 @@ namespace ia::iae
|
|||||||
STATIC VOID Initialize();
|
STATIC VOID Initialize();
|
||||||
STATIC VOID Terminate();
|
STATIC VOID Terminate();
|
||||||
|
|
||||||
STATIC VOID Draw(IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation);
|
STATIC VOID Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation);
|
||||||
};
|
};
|
||||||
} // namespace ia::iae
|
} // namespace ia::iae
|
||||||
@ -46,8 +46,8 @@ namespace ia::iae
|
|||||||
|
|
||||||
STATIC VOID BindTexture(IN Handle handle, IN BOOL flipV, IN BOOL flipH, IN CONST glm::vec4& colorOverlay);
|
STATIC VOID BindTexture(IN Handle handle, IN BOOL flipV, IN BOOL flipH, IN CONST glm::vec4& colorOverlay);
|
||||||
|
|
||||||
STATIC VOID Draw(IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation, IN Handle vertexBufferHandle, IN INT32 vertexCount);
|
STATIC VOID Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation, IN Handle vertexBufferHandle, IN INT32 vertexCount);
|
||||||
STATIC VOID Draw(IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation, IN Handle vertexBufferHandle, IN Handle indexBufferHandle, IN INT32 indexCount);
|
STATIC VOID Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation, IN Handle vertexBufferHandle, IN Handle indexBufferHandle, IN INT32 indexCount);
|
||||||
|
|
||||||
STATIC Camera2D* GetCamera();
|
STATIC Camera2D* GetCamera();
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,13 @@ namespace ia::iae
|
|||||||
return m_nodes;
|
return m_nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL &YSortingEnabled()
|
||||||
|
{
|
||||||
|
return m_ySortingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
BOOL m_ySortingEnabled{false};
|
||||||
Vector<RefPtr<Node>> m_nodes;
|
Vector<RefPtr<Node>> m_nodes;
|
||||||
};
|
};
|
||||||
} // namespace ia::iae
|
} // namespace ia::iae
|
||||||
@ -28,7 +28,7 @@ namespace ia::iae
|
|||||||
~Texture();
|
~Texture();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VOID Draw(IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation, IN BOOL flipH,
|
VOID Draw(IN FLOAT32 sortOffset, IN CONST glm::vec3 &position, IN CONST glm::vec3 &scale, IN FLOAT32 rotation, IN BOOL flipH,
|
||||||
IN BOOL flipV, IN CONST glm::vec4 &colorOverlay) CONST;
|
IN BOOL flipV, IN CONST glm::vec4 &colorOverlay) CONST;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
Reference in New Issue
Block a user