Main menu finished
This commit is contained in:
+1
-1
Submodule assets updated: e09ebf5a29...26839b66e2
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef Events_FlagCaptured_h__
|
||||||
|
#define Events_FlagCaptured_h__
|
||||||
|
|
||||||
|
#include "Entity.h"
|
||||||
|
#include "EventBroker.h"
|
||||||
|
|
||||||
|
namespace Events
|
||||||
|
{
|
||||||
|
struct FlagCaptured : Event
|
||||||
|
{
|
||||||
|
EntityID Player;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Events_FlagCaptured_h__
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef Events_FlagDropped_h__
|
||||||
|
#define Events_FlagDropped_h__
|
||||||
|
|
||||||
|
#include "Entity.h"
|
||||||
|
#include "EventBroker.h"
|
||||||
|
|
||||||
|
namespace Events
|
||||||
|
{
|
||||||
|
struct FlagDropped : Event
|
||||||
|
{
|
||||||
|
EntityID Player;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Events_FlagDropped_h__
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef Events_FlagPickup_h__
|
||||||
|
#define Events_FlagPickup_h__
|
||||||
|
|
||||||
|
#include "Entity.h"
|
||||||
|
#include "EventBroker.h"
|
||||||
|
|
||||||
|
namespace Events
|
||||||
|
{
|
||||||
|
struct FlagPickup : Event
|
||||||
|
{
|
||||||
|
EntityID Player;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Events_FlagPickup_h__
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef Events_FlagReturned_h__
|
||||||
|
#define Events_FlagReturned_h__
|
||||||
|
|
||||||
|
#include "Entity.h"
|
||||||
|
#include "EventBroker.h"
|
||||||
|
|
||||||
|
namespace Events
|
||||||
|
{
|
||||||
|
struct FlagReturned : Event
|
||||||
|
{
|
||||||
|
EntityID Player;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Events_FlagReturned_h__
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef Events_GameOver_h__
|
||||||
|
#define Events_GameOver_h__
|
||||||
|
|
||||||
|
#include "Entity.h"
|
||||||
|
#include "EventBroker.h"
|
||||||
|
|
||||||
|
namespace Events
|
||||||
|
{
|
||||||
|
struct GameOver : Event
|
||||||
|
{
|
||||||
|
EntityID Winner;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Events_GameOver_h__
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef Events_GameStart_h__
|
||||||
|
#define Events_GameStart_h__
|
||||||
|
|
||||||
|
#include "Entity.h"
|
||||||
|
#include "EventBroker.h"
|
||||||
|
|
||||||
|
namespace Events
|
||||||
|
{
|
||||||
|
struct GameStart : Event
|
||||||
|
{
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Events_GameStart_h__
|
||||||
+45
-15
@@ -36,6 +36,7 @@ public:
|
|||||||
: EventBroker(eventBroker)
|
: EventBroker(eventBroker)
|
||||||
, ResourceManager(resourceManager)
|
, ResourceManager(resourceManager)
|
||||||
, Rectangle()
|
, Rectangle()
|
||||||
|
, m_Parent(nullptr)
|
||||||
, m_Name("UIParent")
|
, m_Name("UIParent")
|
||||||
, m_Layer(0)
|
, m_Layer(0)
|
||||||
, m_Hidden(false)
|
, m_Hidden(false)
|
||||||
@@ -46,12 +47,28 @@ public:
|
|||||||
: m_Name(name)
|
: m_Name(name)
|
||||||
, m_Layer(0)
|
, m_Layer(0)
|
||||||
, m_Hidden(false)
|
, m_Hidden(false)
|
||||||
{ SetParent(std::shared_ptr<Frame>(parent)); Initialize(); }
|
{ SetParent(parent); Initialize(); }
|
||||||
|
|
||||||
|
~Frame()
|
||||||
|
{
|
||||||
|
/*for (auto layer : m_Children)
|
||||||
|
{
|
||||||
|
for (auto child : layer.second)
|
||||||
|
{
|
||||||
|
delete child.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_Parent)
|
||||||
|
{
|
||||||
|
m_Parent->RemoveChild(this);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
::RenderQueuePair RenderQueue;
|
::RenderQueuePair RenderQueue;
|
||||||
|
|
||||||
std::shared_ptr<Frame> Parent() const { return m_Parent; }
|
Frame* Parent() const { return m_Parent; }
|
||||||
void SetParent(std::shared_ptr<Frame> parent)
|
void SetParent(Frame* parent)
|
||||||
{
|
{
|
||||||
if (parent == nullptr)
|
if (parent == nullptr)
|
||||||
{
|
{
|
||||||
@@ -62,13 +79,13 @@ public:
|
|||||||
Width = parent->Width;
|
Width = parent->Width;
|
||||||
Height = parent->Height;
|
Height = parent->Height;
|
||||||
m_Layer = parent->Layer() + 1;
|
m_Layer = parent->Layer() + 1;
|
||||||
parent->AddChild(std::shared_ptr<Frame>(this));
|
parent->AddChild(this);
|
||||||
m_Parent = parent;
|
m_Parent = parent;
|
||||||
EventBroker = parent->EventBroker;
|
EventBroker = parent->EventBroker;
|
||||||
ResourceManager = parent->ResourceManager;
|
ResourceManager = parent->ResourceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddChild(std::shared_ptr<Frame> child)
|
void AddChild(Frame* child)
|
||||||
{
|
{
|
||||||
m_Children[child->m_Layer].insert(std::make_pair(child->Name(), child));
|
m_Children[child->m_Layer].insert(std::make_pair(child->Name(), child));
|
||||||
if (m_Parent)
|
if (m_Parent)
|
||||||
@@ -77,7 +94,19 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef std::map<std::string, std::shared_ptr<Frame>>::const_iterator FrameChildrenIterator;
|
void RemoveChild(Frame* child)
|
||||||
|
{
|
||||||
|
auto it = m_Children.find(child->m_Layer);
|
||||||
|
if (it != m_Children.end())
|
||||||
|
{
|
||||||
|
m_Children.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_Parent)
|
||||||
|
{
|
||||||
|
m_Parent->RemoveChild(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string Name() const { return m_Name; }
|
std::string Name() const { return m_Name; }
|
||||||
void SetName(std::string val) { m_Name = val; }
|
void SetName(std::string val) { m_Name = val; }
|
||||||
@@ -89,8 +118,14 @@ public:
|
|||||||
else
|
else
|
||||||
return m_Hidden;
|
return m_Hidden;
|
||||||
}
|
}
|
||||||
void Hide() { m_Hidden = true; }
|
|
||||||
void Show() { m_Hidden = false; }
|
bool Visible() const
|
||||||
|
{
|
||||||
|
return !Hidden();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Hide() { m_Hidden = true; }
|
||||||
|
virtual void Show() { m_Hidden = false; }
|
||||||
|
|
||||||
int Left() const override
|
int Left() const override
|
||||||
{
|
{
|
||||||
@@ -144,9 +179,6 @@ public:
|
|||||||
|
|
||||||
void UpdateLayered(double dt)
|
void UpdateLayered(double dt)
|
||||||
{
|
{
|
||||||
if (this->Hidden())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Update ourselves
|
// Update ourselves
|
||||||
this->Update(dt);
|
this->Update(dt);
|
||||||
|
|
||||||
@@ -157,8 +189,6 @@ public:
|
|||||||
for (auto &pairChild : children)
|
for (auto &pairChild : children)
|
||||||
{
|
{
|
||||||
auto child = pairChild.second;
|
auto child = pairChild.second;
|
||||||
if (child->Hidden())
|
|
||||||
continue;
|
|
||||||
child->Update(dt);
|
child->Update(dt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,8 +230,8 @@ protected:
|
|||||||
int m_Layer;
|
int m_Layer;
|
||||||
bool m_Hidden;
|
bool m_Hidden;
|
||||||
|
|
||||||
std::shared_ptr<Frame> m_Parent;
|
Frame* m_Parent;
|
||||||
typedef std::multimap<std::string, std::shared_ptr<Frame>> Children_t; // name -> frame
|
typedef std::multimap<std::string, Frame*> Children_t; // name -> frame
|
||||||
std::map<int, Children_t> m_Children; // layer -> Children_t
|
std::map<int, Children_t> m_Children; // layer -> Children_t
|
||||||
|
|
||||||
virtual bool OnKeyDown(const Events::KeyDown &event) { return false; }
|
virtual bool OnKeyDown(const Events::KeyDown &event) { return false; }
|
||||||
|
|||||||
+35
-11
@@ -2,13 +2,15 @@
|
|||||||
#define GUI_GameFrame_h__
|
#define GUI_GameFrame_h__
|
||||||
|
|
||||||
#include "GUI/Frame.h"
|
#include "GUI/Frame.h"
|
||||||
|
#include "GUI/MainMenuFrame.h"
|
||||||
#include "GUI/WorldFrame.h"
|
#include "GUI/WorldFrame.h"
|
||||||
#include "GUI/Viewport.h"
|
#include "GUI/Viewport.h"
|
||||||
#include "GUI/TextureFrame.h"
|
#include "GUI/TextureFrame.h"
|
||||||
#include "GUI/PlayerHUD.h"
|
#include "GUI/PlayerHUD.h"
|
||||||
#include "VehicleSelection.h"
|
|
||||||
|
|
||||||
#include "GameWorld.h"
|
#include "GameWorld.h"
|
||||||
|
#include "Events/GameStart.h"
|
||||||
|
#include "Events/GameOver.h"
|
||||||
|
|
||||||
namespace GUI
|
namespace GUI
|
||||||
{
|
{
|
||||||
@@ -18,37 +20,59 @@ class GameFrame : public Frame
|
|||||||
public:
|
public:
|
||||||
GameFrame(Frame* parent, std::string name)
|
GameFrame(Frame* parent, std::string name)
|
||||||
: Frame(parent, name)
|
: Frame(parent, name)
|
||||||
|
, m_GameOver(false)
|
||||||
{
|
{
|
||||||
|
EVENT_SUBSCRIBE_MEMBER(m_EGameStart, &GUI::GameFrame::OnGameStart);
|
||||||
|
EVENT_SUBSCRIBE_MEMBER(m_EGameOver, &GUI::GameFrame::OnGameOver);
|
||||||
|
|
||||||
m_World = std::make_shared<GameWorld>(EventBroker, ResourceManager);
|
m_World = std::make_shared<GameWorld>(EventBroker, ResourceManager);
|
||||||
auto worldFrame = new WorldFrame(this, "GameWorldFrame", m_World);
|
m_WorldFrame = new WorldFrame(this, "GameWorldFrame", m_World);
|
||||||
{
|
{
|
||||||
vp1 = new Viewport(worldFrame, "Viewport1", m_World);
|
vp1 = new Viewport(m_WorldFrame, "Viewport1", m_World);
|
||||||
vp1->X = 0;
|
vp1->X = 0;
|
||||||
vp1->Width = this->Width / 2.f;
|
vp1->Width = this->Width / 2.f;
|
||||||
//new PlayerHUD(vp1, "PlayerHUD", m_World, 1);
|
new PlayerHUD(vp1, "PlayerHUD", m_World, 1);
|
||||||
new VehicleSelection(vp1, "VehicleSelection", m_World, 1);
|
|
||||||
|
|
||||||
vp2 = new Viewport(worldFrame, "Viewport2", m_World);
|
vp2 = new Viewport(m_WorldFrame, "Viewport2", m_World);
|
||||||
vp2->X = vp1->Right();
|
vp2->X = vp1->Right();
|
||||||
vp2->Width = this->Width / 2.f;
|
vp2->Width = this->Width / 2.f;
|
||||||
//new PlayerHUD(vp2, "PlayerHUD", m_World, 2);
|
new PlayerHUD(vp2, "PlayerHUD", m_World, 2);
|
||||||
new VehicleSelection(vp2, "VehicleSelection", m_World, 2);
|
|
||||||
|
|
||||||
|
m_FreeCamViewport = new Viewport(m_WorldFrame, "ViewportFreeCam", m_World);
|
||||||
m_FreeCamViewport = new Viewport(worldFrame, "ViewportFreeCam", m_World);
|
|
||||||
m_FreeCamViewport->Hide();
|
m_FreeCamViewport->Hide();
|
||||||
}
|
}
|
||||||
|
m_WorldFrame->Hide();
|
||||||
|
m_MainMenuFrame = new MainMenuFrame(this, "MainMenu");
|
||||||
|
|
||||||
m_World->Initialize();
|
m_World->Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EventRelay<Frame, Events::KeyUp> m_EKeyUp;
|
EventRelay<Frame, Events::GameStart> m_EGameStart;
|
||||||
|
bool OnGameStart(const Events::GameStart &event)
|
||||||
|
{
|
||||||
|
m_WorldFrame->Show();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
EventRelay<Frame, Events::GameOver> m_EGameOver;
|
||||||
|
bool OnGameOver(const Events::GameOver &event)
|
||||||
|
{
|
||||||
|
m_GameOver = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<GameWorld> m_World;
|
std::shared_ptr<GameWorld> m_World;
|
||||||
|
|
||||||
|
MainMenuFrame* m_MainMenuFrame;
|
||||||
|
WorldFrame* m_WorldFrame;
|
||||||
Viewport* vp1;
|
Viewport* vp1;
|
||||||
Viewport* vp2;
|
Viewport* vp2;
|
||||||
Viewport* m_FreeCamViewport;
|
Viewport* m_FreeCamViewport;
|
||||||
|
|
||||||
|
bool m_GameOver;
|
||||||
|
|
||||||
bool OnKeyUp(const Events::KeyUp &event) override
|
bool OnKeyUp(const Events::KeyUp &event) override
|
||||||
{
|
{
|
||||||
if (event.KeyCode == GLFW_KEY_1)
|
if (event.KeyCode == GLFW_KEY_1)
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef GUI_GameOverFrame_h__
|
||||||
|
#define GUI_GameOverFrame_h__
|
||||||
|
|
||||||
|
#include "GUI/Frame.h"
|
||||||
|
#include "GUI/HealthOverlay.h"
|
||||||
|
|
||||||
|
namespace GUI
|
||||||
|
{
|
||||||
|
|
||||||
|
class GameOverFrame : public Frame
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GameOverFrame(Frame* parent, std::string name)
|
||||||
|
: Frame(parent, name)
|
||||||
|
{
|
||||||
|
//Hide();
|
||||||
|
m_Background = new TextureFrame(this, "Background");
|
||||||
|
m_Background->SetTexture("Textures/GUI/black.png");
|
||||||
|
m_Background->SetColor(glm::vec4(1.f, 1.f, 1.f, 0.6f));
|
||||||
|
m_Text = new TextureFrame(this, "Message");
|
||||||
|
m_Text->SetTexture("Textures/GUI/GloriousVictory.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Show(bool victory)
|
||||||
|
{
|
||||||
|
if (victory)
|
||||||
|
m_Text->SetTexture("Textures/GUI/GloriousVictory.png");
|
||||||
|
else
|
||||||
|
m_Text->SetTexture("Textures/GUI/ShamefulDefeat.png");
|
||||||
|
|
||||||
|
Frame::Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool m_Victory;
|
||||||
|
TextureFrame* m_Background;
|
||||||
|
TextureFrame* m_Text;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GUI_GameOverFrame_h__
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
#ifndef GUI_MainMenuFrame_h__
|
||||||
|
#define GUI_MainMenuFrame_h__
|
||||||
|
|
||||||
|
#include "GUI/Frame.h"
|
||||||
|
#include "GUI/HealthOverlay.h"
|
||||||
|
|
||||||
|
#include "Events/GameStart.h"
|
||||||
|
|
||||||
|
namespace GUI
|
||||||
|
{
|
||||||
|
|
||||||
|
class MainMenuFrame : public Frame
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MainMenuFrame(Frame* parent, std::string name)
|
||||||
|
: Frame(parent, name)
|
||||||
|
{
|
||||||
|
m_CurrentSelection = 0;
|
||||||
|
|
||||||
|
//Hide();
|
||||||
|
m_Background = new TextureFrame(this, "Background");
|
||||||
|
m_Background->SetTexture("Textures/GUI/MainMenu/BackGround.png");
|
||||||
|
|
||||||
|
m_Buttons = new TextureFrame(this, "Message");
|
||||||
|
m_Buttons->SetTexture("Textures/GUI/MainMenu/Buttons.png");
|
||||||
|
m_CurrentCoordinate = -m_SelectionCoordinates[0] * Scale();
|
||||||
|
m_TargetCoordinate = m_CurrentCoordinate;
|
||||||
|
m_Buttons->X = m_CurrentCoordinate.x;
|
||||||
|
m_Buttons->Y = m_CurrentCoordinate.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(double dt) override
|
||||||
|
{
|
||||||
|
if (Hidden())
|
||||||
|
return;
|
||||||
|
|
||||||
|
glm::vec2 posDiff = m_TargetCoordinate - m_CurrentCoordinate;
|
||||||
|
m_CurrentCoordinate += posDiff * 4.f * (float)dt;
|
||||||
|
|
||||||
|
m_Buttons->X = m_CurrentCoordinate.x;
|
||||||
|
m_Buttons->Y = m_CurrentCoordinate.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool m_Victory;
|
||||||
|
TextureFrame* m_Background;
|
||||||
|
TextureFrame* m_Buttons;
|
||||||
|
|
||||||
|
int m_CurrentSelection;
|
||||||
|
static glm::vec2 m_SelectionCoordinates[2];
|
||||||
|
glm::vec2 m_CurrentCoordinate;
|
||||||
|
glm::vec2 m_TargetCoordinate;
|
||||||
|
|
||||||
|
bool OnCommand(const Events::InputCommand &event) override
|
||||||
|
{
|
||||||
|
if (Hidden())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const float deadzone = 0.5f;
|
||||||
|
|
||||||
|
if (event.Command == "interface_horizontal")
|
||||||
|
{
|
||||||
|
if (event.Value > deadzone)
|
||||||
|
m_CurrentSelection++;
|
||||||
|
else if (event.Value < -deadzone)
|
||||||
|
m_CurrentSelection--;
|
||||||
|
}
|
||||||
|
m_CurrentSelection = (m_CurrentSelection < 0) ? 0 : ((m_CurrentSelection > 1) ? 1 : m_CurrentSelection);
|
||||||
|
m_TargetCoordinate = -m_SelectionCoordinates[m_CurrentSelection] * Scale();
|
||||||
|
|
||||||
|
if (event.Command == "interface_confirm" && event.Value > 0)
|
||||||
|
{
|
||||||
|
// Play
|
||||||
|
if (m_CurrentSelection == 0)
|
||||||
|
{
|
||||||
|
Hide();
|
||||||
|
Events::GameStart e;
|
||||||
|
EventBroker->Publish(e);
|
||||||
|
}
|
||||||
|
// Quit
|
||||||
|
else if (m_CurrentSelection == 1)
|
||||||
|
{
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
glm::vec2 MainMenuFrame::m_SelectionCoordinates[2] = {
|
||||||
|
glm::vec2(0, 0),
|
||||||
|
glm::vec2(193, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GUI_MainMenuFrame_h__
|
||||||
@@ -3,6 +3,11 @@
|
|||||||
|
|
||||||
#include "GUI/Frame.h"
|
#include "GUI/Frame.h"
|
||||||
#include "GUI/HealthOverlay.h"
|
#include "GUI/HealthOverlay.h"
|
||||||
|
#include "GUI/VehicleSelection.h"
|
||||||
|
#include "GUI/GameOverFrame.h"
|
||||||
|
|
||||||
|
#include "Events/GameOver.h"
|
||||||
|
#include "Components/Player.h"
|
||||||
|
|
||||||
namespace GUI
|
namespace GUI
|
||||||
{
|
{
|
||||||
@@ -15,7 +20,10 @@ namespace GUI
|
|||||||
, m_World(world)
|
, m_World(world)
|
||||||
, m_PlayerID(playerID)
|
, m_PlayerID(playerID)
|
||||||
{
|
{
|
||||||
|
EVENT_SUBSCRIBE_MEMBER(m_EGameOver, &GUI::PlayerHUD::OnGameOver)
|
||||||
|
|
||||||
m_HealthOverlay = new HealthOverlay(this, "HealthOverlay", m_World, m_PlayerID);
|
m_HealthOverlay = new HealthOverlay(this, "HealthOverlay", m_World, m_PlayerID);
|
||||||
|
|
||||||
m_Crosshair = new TextureFrame(this, "Crosshair");
|
m_Crosshair = new TextureFrame(this, "Crosshair");
|
||||||
m_Crosshair->Width = 45;
|
m_Crosshair->Width = 45;
|
||||||
m_Crosshair->Height = 45;
|
m_Crosshair->Height = 45;
|
||||||
@@ -23,6 +31,10 @@ namespace GUI
|
|||||||
m_Crosshair->Y = this->Height / 2.f - m_Crosshair->Height / 2.f;
|
m_Crosshair->Y = this->Height / 2.f - m_Crosshair->Height / 2.f;
|
||||||
m_Crosshair->SetTexture("Textures/GUI/crosshair.png");
|
m_Crosshair->SetTexture("Textures/GUI/crosshair.png");
|
||||||
m_Crosshair->SetColor(glm::vec4(1.f, 1.f, 1.f, 0.75f));
|
m_Crosshair->SetColor(glm::vec4(1.f, 1.f, 1.f, 0.75f));
|
||||||
|
|
||||||
|
m_VehicleSelection = new VehicleSelection(this, "VehicleSelection", m_World, m_PlayerID);
|
||||||
|
|
||||||
|
m_GameOverFrame = new GameOverFrame(this, "GameOver");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -31,6 +43,42 @@ namespace GUI
|
|||||||
|
|
||||||
HealthOverlay* m_HealthOverlay;
|
HealthOverlay* m_HealthOverlay;
|
||||||
TextureFrame* m_Crosshair;
|
TextureFrame* m_Crosshair;
|
||||||
|
VehicleSelection* m_VehicleSelection;
|
||||||
|
GameOverFrame* m_GameOverFrame;
|
||||||
|
|
||||||
|
EventRelay<Frame, Events::GameOver> m_EGameOver;
|
||||||
|
bool OnGameOver(const Events::GameOver &event)
|
||||||
|
{
|
||||||
|
auto playerComponent = m_World->GetComponent<Components::Player>(event.Winner);
|
||||||
|
if (!playerComponent)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Winner is not a valid player!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_PlayerID == playerComponent->ID)
|
||||||
|
m_GameOverFrame->Show(true);
|
||||||
|
else
|
||||||
|
m_GameOverFrame->Show(false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OnCommand(const Events::InputCommand &event)
|
||||||
|
{
|
||||||
|
if (event.PlayerID != m_PlayerID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (event.Command == "interface_confirm" && event.Value > 0)
|
||||||
|
{
|
||||||
|
if (m_GameOverFrame->Visible())
|
||||||
|
{
|
||||||
|
m_GameOverFrame->Hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ namespace GUI
|
|||||||
|
|
||||||
void Update(double dt) override
|
void Update(double dt) override
|
||||||
{
|
{
|
||||||
|
if (Hidden())
|
||||||
|
return;
|
||||||
|
|
||||||
glm::vec2 posDiff = m_TargetCoordinate - m_CurrentCoordinate;
|
glm::vec2 posDiff = m_TargetCoordinate - m_CurrentCoordinate;
|
||||||
m_CurrentCoordinate += posDiff * 2.f * (float)dt;
|
m_CurrentCoordinate += posDiff * 2.f * (float)dt;
|
||||||
|
|
||||||
@@ -77,28 +80,30 @@ namespace GUI
|
|||||||
if (event.PlayerID != m_PlayerID)
|
if (event.PlayerID != m_PlayerID)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
const float deadzone = 0.5f;
|
||||||
|
|
||||||
// Menu movement
|
// Menu movement
|
||||||
if (event.Command == "interface_horizontal" || event.Command == "interface_vertical")
|
if (event.Command == "interface_horizontal" || event.Command == "interface_vertical")
|
||||||
{
|
{
|
||||||
// Horizontal scrolling
|
// Horizontal scrolling
|
||||||
if (event.Command == "interface_horizontal")
|
if (event.Command == "interface_horizontal")
|
||||||
{
|
{
|
||||||
if (event.Value > 0)
|
if (event.Value > deadzone)
|
||||||
m_CurrentSelection++;
|
m_CurrentSelection++;
|
||||||
else if (event.Value < 0)
|
else if (event.Value < -deadzone)
|
||||||
m_CurrentSelection--;
|
m_CurrentSelection--;
|
||||||
}
|
}
|
||||||
// Vertical scrolling to switch between "rows" in an intuitive way
|
// Vertical scrolling to switch between "rows" in an intuitive way
|
||||||
else if (event.Command == "interface_vertical")
|
else if (event.Command == "interface_vertical")
|
||||||
{
|
{
|
||||||
if (event.Value > 0)
|
if (event.Value > deadzone)
|
||||||
{
|
{
|
||||||
if (m_CurrentSelection == 0)
|
if (m_CurrentSelection == 0)
|
||||||
m_CurrentSelection++;
|
m_CurrentSelection++;
|
||||||
else if (m_CurrentSelection == 3)
|
else if (m_CurrentSelection == 3)
|
||||||
m_CurrentSelection--;
|
m_CurrentSelection--;
|
||||||
}
|
}
|
||||||
else if (event.Value < 0)
|
else if (event.Value < -deadzone)
|
||||||
{
|
{
|
||||||
if (m_CurrentSelection == 1)
|
if (m_CurrentSelection == 1)
|
||||||
m_CurrentSelection--;
|
m_CurrentSelection--;
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ private:
|
|||||||
auto itpair = m_Children[m_Layer + 1].equal_range(event.ViewportFrame);
|
auto itpair = m_Children[m_Layer + 1].equal_range(event.ViewportFrame);
|
||||||
for (auto it = itpair.first; it != itpair.second; ++it)
|
for (auto it = itpair.first; it != itpair.second; ++it)
|
||||||
{
|
{
|
||||||
auto viewportFrame = std::dynamic_pointer_cast<GUI::Viewport>(it->second);
|
auto viewportFrame = dynamic_cast<GUI::Viewport*>(it->second);
|
||||||
if (!viewportFrame)
|
if (!viewportFrame)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
+37
-3
@@ -6,29 +6,63 @@
|
|||||||
#include "EventBroker.h"
|
#include "EventBroker.h"
|
||||||
#include "Events/InputCommand.h"
|
#include "Events/InputCommand.h"
|
||||||
#include "Events/MouseMove.h"
|
#include "Events/MouseMove.h"
|
||||||
|
#include "Events/GameStart.h"
|
||||||
|
#include "Events/GameOver.h"
|
||||||
|
|
||||||
template <typename EventContext>
|
template <typename EventContext>
|
||||||
class InputController
|
class InputController
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InputController(std::shared_ptr<::EventBroker> eventBroker)
|
InputController(std::shared_ptr<::EventBroker> eventBroker)
|
||||||
: EventBroker(eventBroker) { Initialize(); }
|
: EventBroker(eventBroker)
|
||||||
|
, InGame(true)
|
||||||
|
{ Initialize(); }
|
||||||
|
|
||||||
virtual void Initialize()
|
virtual void Initialize()
|
||||||
{
|
{
|
||||||
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &InputController::OnCommand);
|
EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &InputController::_OnCommand);
|
||||||
EVENT_SUBSCRIBE_MEMBER(m_EMouseMove, &InputController::OnMouseMove);
|
EVENT_SUBSCRIBE_MEMBER(m_EMouseMove, &InputController::_OnMouseMove);
|
||||||
|
EVENT_SUBSCRIBE_MEMBER(m_EGameStart, &InputController::OnGameStart);
|
||||||
|
EVENT_SUBSCRIBE_MEMBER(m_EGameOver, &InputController::OnGameOver);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool OnCommand(const Events::InputCommand &event) { return false; }
|
virtual bool OnCommand(const Events::InputCommand &event) { return false; }
|
||||||
virtual bool OnMouseMove(const Events::MouseMove &event) { return false; }
|
virtual bool OnMouseMove(const Events::MouseMove &event) { return false; }
|
||||||
|
virtual bool OnGameStart(const Events::GameStart &event)
|
||||||
|
{
|
||||||
|
InGame = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
virtual bool OnGameOver(const Events::GameOver &event)
|
||||||
|
{
|
||||||
|
InGame = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<::EventBroker> EventBroker;
|
std::shared_ptr<::EventBroker> EventBroker;
|
||||||
|
bool InGame;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EventRelay<EventContext, Events::InputCommand> m_EInputCommand;
|
EventRelay<EventContext, Events::InputCommand> m_EInputCommand;
|
||||||
EventRelay<EventContext, Events::MouseMove> m_EMouseMove;
|
EventRelay<EventContext, Events::MouseMove> m_EMouseMove;
|
||||||
|
EventRelay<EventContext, Events::GameStart> m_EGameStart;
|
||||||
|
EventRelay<EventContext, Events::GameOver> m_EGameOver;
|
||||||
|
|
||||||
|
virtual bool _OnCommand(const Events::InputCommand &event)
|
||||||
|
{
|
||||||
|
if (InGame || event.Value == 0)
|
||||||
|
return OnCommand(event);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
virtual bool _OnMouseMove(const Events::MouseMove &event)
|
||||||
|
{
|
||||||
|
if (InGame)
|
||||||
|
return OnMouseMove(event);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // InputController_h__
|
#endif // InputController_h__
|
||||||
|
|||||||
+1
-1
@@ -93,7 +93,7 @@ Model::Model(std::shared_ptr<ResourceManager> rm, OBJ &obj)
|
|||||||
if (Vertices.size() > 0)
|
if (Vertices.size() > 0)
|
||||||
{
|
{
|
||||||
CreateTangents();
|
CreateTangents();
|
||||||
getSimilarVertexIndex();
|
//getSimilarVertexIndex();
|
||||||
CreateBuffers(Vertices, Normals, TangentNormals, BiTangentNormals, TextureCoords);
|
CreateBuffers(Vertices, Normals, TangentNormals, BiTangentNormals, TextureCoords);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#include "PrecompiledHeader.h"
|
||||||
|
#include "GameStateSystem.h"
|
||||||
|
#include "World.h"
|
||||||
|
|
||||||
|
void Systems::GameStateSystem::Initialize()
|
||||||
|
{
|
||||||
|
EVENT_SUBSCRIBE_MEMBER(m_EFlagCaptured, &Systems::GameStateSystem::OnFlagCaptured);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Systems::GameStateSystem::OnFlagCaptured(const Events::FlagCaptured &event)
|
||||||
|
{
|
||||||
|
m_InGame = false;
|
||||||
|
|
||||||
|
Events::GameOver e;
|
||||||
|
e.Winner = event.Player;
|
||||||
|
EventBroker->Publish(e);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef GameStateSystem_h__
|
||||||
|
#define GameStateSystem_h__
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "System.h"
|
||||||
|
#include "Components/Transform.h"
|
||||||
|
#include "Components/Player.h"
|
||||||
|
#include "Events/FlagCaptured.h"
|
||||||
|
#include "Events/GameOver.h"
|
||||||
|
|
||||||
|
namespace Systems
|
||||||
|
{
|
||||||
|
|
||||||
|
class GameStateSystem : public System
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
GameStateSystem(World* world, std::shared_ptr<::EventBroker> eventBroker, std::shared_ptr<::ResourceManager> resourceManager)
|
||||||
|
: System(world, eventBroker, resourceManager)
|
||||||
|
, m_InGame(true)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void Initialize() override;
|
||||||
|
|
||||||
|
bool InGame() const { return m_InGame; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_InGame;
|
||||||
|
|
||||||
|
EventRelay<GameStateSystem, Events::FlagCaptured> m_EFlagCaptured;
|
||||||
|
bool OnFlagCaptured(const Events::FlagCaptured &event);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GameStateSystem_h__
|
||||||
@@ -48,6 +48,7 @@
|
|||||||
#include "Components/Input.h"
|
#include "Components/Input.h"
|
||||||
|
|
||||||
#include "Events/SetViewportCamera.h"
|
#include "Events/SetViewportCamera.h"
|
||||||
|
#include "Events/GameOver.h"
|
||||||
|
|
||||||
namespace Systems
|
namespace Systems
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -113,6 +113,7 @@
|
|||||||
<ClCompile Include="..\..\src\Systems\DebugSystem.cpp" />
|
<ClCompile Include="..\..\src\Systems\DebugSystem.cpp" />
|
||||||
<ClCompile Include="..\..\src\Systems\FollowSystem.cpp" />
|
<ClCompile Include="..\..\src\Systems\FollowSystem.cpp" />
|
||||||
<ClCompile Include="..\..\src\Systems\FreeSteeringSystem.cpp" />
|
<ClCompile Include="..\..\src\Systems\FreeSteeringSystem.cpp" />
|
||||||
|
<ClCompile Include="..\..\src\Systems\GameStateSystem.cpp" />
|
||||||
<ClCompile Include="..\..\src\Systems\GarageSystem.cpp" />
|
<ClCompile Include="..\..\src\Systems\GarageSystem.cpp" />
|
||||||
<ClCompile Include="..\..\src\Systems\HelicopterSteeringSystem.cpp" />
|
<ClCompile Include="..\..\src\Systems\HelicopterSteeringSystem.cpp" />
|
||||||
<ClCompile Include="..\..\src\Systems\InputSystem.cpp" />
|
<ClCompile Include="..\..\src\Systems\InputSystem.cpp" />
|
||||||
@@ -192,8 +193,14 @@
|
|||||||
<ClInclude Include="..\..\src\Events\Damage.h" />
|
<ClInclude Include="..\..\src\Events\Damage.h" />
|
||||||
<ClInclude Include="..\..\src\Events\DisableCollisions.h" />
|
<ClInclude Include="..\..\src\Events\DisableCollisions.h" />
|
||||||
<ClInclude Include="..\..\src\Events\EnableCollisions.h" />
|
<ClInclude Include="..\..\src\Events\EnableCollisions.h" />
|
||||||
|
<ClInclude Include="..\..\src\Events\FlagCaptured.h" />
|
||||||
|
<ClInclude Include="..\..\src\Events\FlagDropped.h" />
|
||||||
|
<ClInclude Include="..\..\src\Events\FlagPickup.h" />
|
||||||
|
<ClInclude Include="..\..\src\Events\FlagReturned.h" />
|
||||||
|
<ClInclude Include="..\..\src\Events\GameOver.h" />
|
||||||
<ClInclude Include="..\..\src\Events\GamepadAxis.h" />
|
<ClInclude Include="..\..\src\Events\GamepadAxis.h" />
|
||||||
<ClInclude Include="..\..\src\Events\GamepadButton.h" />
|
<ClInclude Include="..\..\src\Events\GamepadButton.h" />
|
||||||
|
<ClInclude Include="..\..\src\Events\GameStart.h" />
|
||||||
<ClInclude Include="..\..\src\Events\InputCommand.h" />
|
<ClInclude Include="..\..\src\Events\InputCommand.h" />
|
||||||
<ClInclude Include="..\..\src\Events\KeyDown.h" />
|
<ClInclude Include="..\..\src\Events\KeyDown.h" />
|
||||||
<ClInclude Include="..\..\src\Events\KeyUp.h" />
|
<ClInclude Include="..\..\src\Events\KeyUp.h" />
|
||||||
@@ -218,7 +225,9 @@
|
|||||||
<ClInclude Include="..\..\src\GUI\Frame.h" />
|
<ClInclude Include="..\..\src\GUI\Frame.h" />
|
||||||
<ClInclude Include="..\..\src\EventBroker.h" />
|
<ClInclude Include="..\..\src\EventBroker.h" />
|
||||||
<ClInclude Include="..\..\src\GUI\GameFrame.h" />
|
<ClInclude Include="..\..\src\GUI\GameFrame.h" />
|
||||||
|
<ClInclude Include="..\..\src\GUI\GameOverFrame.h" />
|
||||||
<ClInclude Include="..\..\src\GUI\HealthOverlay.h" />
|
<ClInclude Include="..\..\src\GUI\HealthOverlay.h" />
|
||||||
|
<ClInclude Include="..\..\src\GUI\MainMenuFrame.h" />
|
||||||
<ClInclude Include="..\..\src\GUI\PlayerHUD.h" />
|
<ClInclude Include="..\..\src\GUI\PlayerHUD.h" />
|
||||||
<ClInclude Include="..\..\src\GUI\VehicleSelection.h" />
|
<ClInclude Include="..\..\src\GUI\VehicleSelection.h" />
|
||||||
<ClInclude Include="..\..\src\GUI\WorldFrame.h" />
|
<ClInclude Include="..\..\src\GUI\WorldFrame.h" />
|
||||||
@@ -241,6 +250,7 @@
|
|||||||
<ClInclude Include="..\..\src\Systems\DebugSystem.h" />
|
<ClInclude Include="..\..\src\Systems\DebugSystem.h" />
|
||||||
<ClInclude Include="..\..\src\Systems\FollowSystem.h" />
|
<ClInclude Include="..\..\src\Systems\FollowSystem.h" />
|
||||||
<ClInclude Include="..\..\src\Systems\FreeSteeringSystem.h" />
|
<ClInclude Include="..\..\src\Systems\FreeSteeringSystem.h" />
|
||||||
|
<ClInclude Include="..\..\src\Systems\GameStateSystem.h" />
|
||||||
<ClInclude Include="..\..\src\Systems\GarageSystem.h" />
|
<ClInclude Include="..\..\src\Systems\GarageSystem.h" />
|
||||||
<ClInclude Include="..\..\src\Systems\HelicopterSteeringSystem.h" />
|
<ClInclude Include="..\..\src\Systems\HelicopterSteeringSystem.h" />
|
||||||
<ClInclude Include="..\..\src\Systems\InputSystem.h" />
|
<ClInclude Include="..\..\src\Systems\InputSystem.h" />
|
||||||
|
|||||||
@@ -78,15 +78,14 @@
|
|||||||
<ClCompile Include="..\..\src\Systems\FollowSystem.cpp">
|
<ClCompile Include="..\..\src\Systems\FollowSystem.cpp">
|
||||||
<Filter>Game\Systems</Filter>
|
<Filter>Game\Systems</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\Systems\WallSystem.cpp">
|
|
||||||
<Filter>Gameplay\Systems</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\..\src\Systems\GarageSystem.cpp">
|
<ClCompile Include="..\..\src\Systems\GarageSystem.cpp">
|
||||||
<Filter>Game\Systems</Filter>
|
<Filter>Game\Systems</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\Sound.cpp">
|
<ClCompile Include="..\..\src\Sound.cpp">
|
||||||
<Filter>Audio</Filter>
|
<Filter>Audio</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\Systems\WallSystem.cpp" />
|
||||||
|
<ClCompile Include="..\..\src\Systems\GameStateSystem.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Filter Include="Util">
|
<Filter Include="Util">
|
||||||
@@ -526,9 +525,6 @@
|
|||||||
<ClInclude Include="..\..\src\Components\BlendMap.h">
|
<ClInclude Include="..\..\src\Components\BlendMap.h">
|
||||||
<Filter>Rendering\Components</Filter>
|
<Filter>Rendering\Components</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\Systems\WallSystem.h">
|
|
||||||
<Filter>Gameplay\Systems</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\Events\LeaveTrigger.h">
|
<ClInclude Include="..\..\src\Events\LeaveTrigger.h">
|
||||||
<Filter>Physics\Events</Filter>
|
<Filter>Physics\Events</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -565,9 +561,6 @@
|
|||||||
<ClInclude Include="..\..\src\GUI\VehicleSelection.h">
|
<ClInclude Include="..\..\src\GUI\VehicleSelection.h">
|
||||||
<Filter>GUI</Filter>
|
<Filter>GUI</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\Events\OnDead.h">
|
|
||||||
<Filter>Gameplay\Events</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\Events\SpawnVehicle.h">
|
<ClInclude Include="..\..\src\Events\SpawnVehicle.h">
|
||||||
<Filter>Game\Events</Filter>
|
<Filter>Game\Events</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -577,6 +570,35 @@
|
|||||||
<ClInclude Include="..\..\src\Components\SpawnPoint.h">
|
<ClInclude Include="..\..\src\Components\SpawnPoint.h">
|
||||||
<Filter>Game\Components</Filter>
|
<Filter>Game\Components</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\Events\OnDead.h" />
|
||||||
|
<ClInclude Include="..\..\src\Systems\WallSystem.h" />
|
||||||
|
<ClInclude Include="..\..\src\GUI\GameOverFrame.h">
|
||||||
|
<Filter>GUI</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\Systems\GameStateSystem.h">
|
||||||
|
<Filter>Game\Systems</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\Events\FlagPickup.h">
|
||||||
|
<Filter>Game\Events</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\Events\FlagCaptured.h">
|
||||||
|
<Filter>Game\Events</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\Events\FlagDropped.h">
|
||||||
|
<Filter>Game\Events</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\Events\FlagReturned.h">
|
||||||
|
<Filter>Game\Events</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\Events\GameOver.h">
|
||||||
|
<Filter>Game\Events</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\GUI\MainMenuFrame.h">
|
||||||
|
<Filter>GUI</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\src\Events\GameStart.h">
|
||||||
|
<Filter>Game\Events</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\..\src\Shaders\Fragment2.glsl">
|
<None Include="..\..\src\Shaders\Fragment2.glsl">
|
||||||
|
|||||||
Reference in New Issue
Block a user